Skip to content

Refactor AIGateway::Client (and the other service clients)

Problem

The AiGateway::Client as of today is in a bad shape that it doesn't correctly reflect the interfaces of AI Gateway. It's tailored only for /v1/chat/agent and now it's spilling the business logic as endpoint_url parameter. For instance, ::Gitlab::Llm::AiGateway::Client::DEFAULT_INSTANT_MODEL and Gitlab::Llm::AiGateway::Client::ConnectionError doesn't make sense at all as it varies per feature. This is something we need to follow-up to correct the client interface.

From client perspective, their concern is which services, models or features to access, and low-level stuff like authentication and connection protocols should be abstracted under the client libraries. Like demonstrated in typical client libraries (e.g. Google Cloud libraries) and AiGateway::DocsClient, we should introduce a new client per service. This means we should have DuoChatClient, DocsSearchClient, VertexAIProxyClient, AnthrpicProxyClient etc instead of the single AiGateway::Client class. It should be relatively straight-forward to have the shared module for handling AI Gateway specific low-level stuff and plug it into these clients. Imagine, as a client, you would want to use a "BigQuery" service to create a dataset, not "Google Cloud Platform" to create a dataset (As that's too broad, you wouldn't have idea in which service the dataset will be created).

Proposal

One idea would be to have a corresponding client class to the cloud connector services. So duo_chat service => DuoChatClient#completion/#stream/#get_docs etc.

pseudo code:

# ee/lib/gitlab/duo/services/code_suggestions/client.rb

module Gitlab
  module Duo
    module Services
      module CodeSuggestions
        include Gitlab::Duo::Concerns::AiGateway # plugin AI Gateway specific concerns

        class Client
          def get_access_token
            # business logic
          end

          private

          # This should go to Cloud Connectoer specific concern
          def cloud_connector_service
            ::CloudConnector::AvailableServices.find_by_name(service_name)
          end

          def service_name
            self.class.name.split('::')[-2].underscore
          end

          def access_token
            cloud_connector_service.access_token(user)
          end
        end
      end
    end
  end
end

Further reading

previous proposal

The following discussions from !153215 (merged) should be addressed:

  • @jessieay started a discussion: (+3 comments)

    Question: does this mean that a DB record for the add-on is required to test this locally?

    I know we've had some issues in the recent past with a cloud connector not being seeded in GDK and errors being raised as a result so we added these troubleshooting docs. So I wanted to check on whether that was the case here, too

  • @jessieay started a discussion:

    Question (non-blocking): I do not see this in the list of UPs in the AI Gateway, is that expected? cc @shinya.maeda

  • @jessieay started a discussion:

    Suggestion (non-blocking): interpolate or use host and project (currently unused)

  • @jessieay started a discussion:

    Suggestion (non-blocking): interpolate or use host and project (currently unused)

  • @jessieay started a discussion:

    Suggestion (non-blocking): interpolate or use host and project (currently unused)

  • @jessieay started a discussion:

    Suggestion (non-blocking): interpolate or use host and project (currently unused)

  • @jessieay started a discussion:

    Suggestion (non-blocking): interpolate or use host and project (currently unused)

  • @jessieay started a discussion:

    Suggestion (non-blocking): using a real UP here means that if this UP changes we will need to make changes to this test file. We had this issue with tests for StageCheck when moving duo_chat to GA.

    It would be preferable to use a dummy value so that even if our real UPs change this test can remain the same.

Edited by Shinya Maeda