feat(unstable): Initial implementation of providers#899
feat(unstable): Initial implementation of providers#899benbrandt merged 14 commits intoagentclientprotocol:mainfrom
Conversation
|
@alexhancock @DOsinga @baxen does any of this marry up with the Goose 2.0 design? |
|
thanks for the ping @codefromthecrypt -- I think this should match our approach well and we can switch the custom routes to this when set. Let me have a closer look. Do we thoughts about also selecting/listing models? there's some other things like setting of thinking effort that spring to mind we could consider adding. |
good q @DOsinga so right now it is interesting that model list and parameters are session scoped at the moment. This is interesting because it does allow paring down this to a particular session (e.g what was the default model of a loaded session). However, I've always found it awkward at best for normal "llm stuff". For example, we do a throw-away session, just to learn the models of the implicit provider under the agent. @xtmq I there's a great opportunity here to look at the functions tacked onto session and otherwise and see if we can expose some that are provider scoped to not require a session, notably model listing. |
|
We purposely kept model listing/configuration out of this one to solve the provider config issue for now. We definitely want to look at models as well |
e867bf2 to
d600465
Compare
Implementation of "Configurable LLM Providers" RFD.
Also contains several QoL improvements, we can drop them from this MR.
title: "Configurable LLM Providers"
Elevator pitch
Add the ability for clients to discover and configure agent LLM providers (identified by
id) via dedicated provider methods:providers/listproviders/setproviders/disableThis allows clients to route LLM requests through their own infrastructure (proxies, gateways, or self-hosted models) without agents needing to know about this configuration in advance.
Status quo
ACP does not currently define a standard method for configuring LLM providers.
In practice, provider configuration is usually done via environment variables or agent-specific config files. That creates several problems:
This particularly affects:
Shiny future
Clients will be able to:
Implementation details and plan
Intended flow
sequenceDiagram participant Client participant Agent Client->>Agent: initialize Agent-->>Client: initialize response (agentCapabilities.providers = {}) Client->>Agent: providers/list Agent-->>Client: providers/list response Client->>Agent: providers/set (id = "main") Agent-->>Client: providers/set response Client->>Agent: providers/disable (optional) Agent-->>Client: providers/disable response Client->>Agent: session/newagentCapabilities.providers.providers/listto discover available providers, their current routing targets (or disabled state), supported protocol types, and whether they are required.providers/setto apply new (required) configuration for a specific provider id.providers/disablewhen a non-required provider should be disabled.Capability advertisement
The agent advertises support with an empty object capability:
If
providersis absent, clients must treat provider methods as unsupported.Types
providers/listproviders/setproviders/setupdates the full configuration for one provider id.providers/disableExample exchange
initialize Response:
{ "jsonrpc": "2.0", "id": 0, "result": { "protocolVersion": 1, "agentInfo": { "name": "MyAgent", "version": "2.0.0" }, "agentCapabilities": { "providers": {}, "sessionCapabilities": {} } } }providers/list Request:
{ "jsonrpc": "2.0", "id": 1, "method": "providers/list", "params": {} }providers/list Response:
{ "jsonrpc": "2.0", "id": 1, "result": { "providers": [ { "id": "main", "supported": ["bedrock", "vertex", "azure", "anthropic"], "required": true, "current": { "apiType": "anthropic", "baseUrl": "http://localhost/anthropic" } }, { "id": "openai", "supported": ["openai"], "required": false, "current": null } ] } }providers/set Request:
{ "jsonrpc": "2.0", "id": 2, "method": "providers/set", "params": { "id": "main", "apiType": "anthropic", "baseUrl": "https://llm-gateway.corp.example.com/anthropic/v1", "headers": { "X-Request-Source": "my-ide" } } }providers/set Response:
{ "jsonrpc": "2.0", "id": 2, "result": {} }providers/disable Request:
{ "jsonrpc": "2.0", "id": 3, "method": "providers/disable", "params": { "id": "openai" } }providers/disable Response:
{ "jsonrpc": "2.0", "id": 3, "result": {} }Behavior
agentCapabilities.providers: {}ininitialize. Clients SHOULD only callproviders/*when this capability is present.initialize. Clients SHOULD configure providers before creating or loading sessions. Agents MAY choose not to apply changes to already running sessions, but SHOULD apply them to sessions created or loaded after the change.providers/listreturns configurable providers, their supported protocol types, current effective routing, andrequiredflag. Providers SHOULD remain discoverable in list afterproviders/disable.providers/disablefor providers whererequired: true.providers/list,current: nullmeans the provider is disabled and MUST NOT be used by the agent for LLM calls.providers/setreplaces the full configuration for the targetid(apiType,baseUrl, fullheaders). Ifidis unknown,apiTypeis unsupported for that provider, or params are malformed, agents SHOULD returninvalid_params.providers/disabledisables the target provider at runtime. A disabled provider MUST appear inproviders/listwithcurrent: null. If target provider hasrequired: true, agents MUST returninvalid_params. Disabling an unknownidSHOULD be treated as success (idempotent behavior).Frequently asked questions
What does
nullmean inproviders/list?current: nullmeans the provider is disabled.When disabled, the agent MUST NOT route LLM calls through that provider until the client enables it again with
providers/set.Why is there a
requiredflag?Some providers are mandatory for agent operation and must not be disabled.
requiredlets clients hide or disable the provider-disable action in UI and avoid callingproviders/disablefor those ids.Why not a single
providers/updatemethod for full list replacement?A full-list update means the client must send complete configuration (including
headers) for all providers every time.If the client wants to change only one provider, it may not know headers for the others. In that case it cannot safely build a correct full-list payload.
Also,
providers/listdoes not return headers, so the client cannot simply "take what the agent returned" and send it back with one edit.Per-provider methods (
setanddisable) avoid this problem and keep updates explicit.Why doesn't
providers/listreturn headers?Header values may contain secrets and should not be echoed by the agent.
providers/listis intentionally limited to non-secret routing information (current.apiType,current.baseUrl).Why are
providers/listandproviders/setpayloads different?providers/setacceptsheaders, including secrets, and is write-oriented.providers/listis read-oriented and returns only non-secret routing summary (current) for UI and capability discovery.Why is this separate from
initializeparams?Clients need capability discovery first, then provider discovery, then configuration. A dedicated method family keeps initialization focused on negotiation and leaves provider mutation to explicit steps.
Why not use
session-configwith aprovidercategory instead?session-configis a possible alternative, and we may revisit it as the spec evolves.We did not choose it as the primary approach in this proposal because provider routing here needs dedicated semantics that are difficult to express with today's session config model:
id, each with its own lifecycleapiType,baseUrl, fullheadersmap) rather than simple scalar valuesproviders/list) and disable (providers/disable) semanticsToday,
session-configvalues are effectively string-oriented and do not define a standard multi-value/structured model for this use case.Revision history
providers/removerenamed toproviders/disable, required providers are non-disableable, and disabled state is represented ascurrent: nullproviders/list,providers/set,providers/remove)LlmProtocolan open string type with well-known values; resolve open questions on identifier standardization and model availabilitysetLlmEndpointsmethod with capability advertisement