Skip to content

Chat Provider Client

GodeX needs a clean boundary between provider-specific logic (specs, hooks, capability declarations) and the raw HTTP transport that actually talks to upstream APIs. ChatProviderClient occupies exactly this boundary. It takes a provider name, a base URL, an API key, and an optional timeout, and exposes two methods -- request() and stream() -- that delegate to the generated ChatApi class and wrap every error into a ProviderError with an appropriate domain code.

This layer is why provider implementations never deal with raw fetch errors or SSE parsing. The client absorbs the complexity of timeout detection, response body extraction, and status-code classification, so each provider spec only needs to declare what it supports, not how HTTP works.

At a Glance

ComponentFilePurpose
ChatProviderClientchat-provider-client.tsTyped HTTP client with error wrapping
ChatApichat-api.tsDecorator-generated API class (@post methods)
chatApi factorychat-api.ts:42-53Creates a ChatApi instance with Bearer auth and timeout
wrapProviderErrorchat-provider-client.ts:47-96Converts unknown errors to ProviderError
assertProviderChatRequestchat-request-guard.tsValidates patched request shape before sending
Example clientexample/client.tsReference implementation

Class Diagram

ChatProviderClient

The constructor (chat-provider-client.ts:22-25) creates a ChatApi instance via the chatApi factory, passing through baseURL, apiKey, and timeout. The provider name is stored for error context.

request(body)

Sends a non-streaming Chat Completions request (chat-provider-client.ts:27-33):

  1. Calls this.api.chatCompletions(body).
  2. On failure, calls wrapProviderError(err, this.provider) and throws the result.

stream(body)

Sends a streaming Chat Completions request (chat-provider-client.ts:35-44):

  1. Forces stream: true on the body.
  2. Calls this.api.streamChatCompletions(body).
  3. On failure, wraps the error the same way as request().

ChatApi and the chatApi Factory

ChatApi is a decorator-generated class (chat-api.ts:23-40) with two methods:

MethodEndpointResult Handling
chatCompletionsPOST chat/completionsDefault JSON extraction
streamChatCompletionsPOST chat/completionsJsonStreamResultExtractor for SSE parsing

The chatApi factory (chat-api.ts:42-53) constructs a Fetcher with:

  • baseURL from options
  • timeout from options
  • Authorization: Bearer {apiKey} header

The JsonStreamResultExtractor (stream-result-extractor.ts:13-17) uses a DoneDetector that checks for the [DONE] SSE sentinel to know when the stream has terminated.

Error Wrapping

The wrapProviderError function (chat-provider-client.ts:47-96) classifies errors into domain codes:

Error Code Classification

HTTP StatusDomain CodeMeaning
408PROVIDER_UPSTREAM_TIMEOUTRequest timed out (fetch-level or upstream)
429PROVIDER_UPSTREAM_RATE_LIMITUpstream rate limit hit
500-599PROVIDER_UPSTREAM_SERVER_ERRORUpstream server error
OtherPROVIDER_UPSTREAM_ERRORGeneric upstream failure
No responsePROVIDER_UPSTREAM_ERRORNetwork-level failure (DNS, connection refused, etc.)

For ExchangeError instances, the wrapper also attempts to extract the response body as JSON and reads the error.message field for a human-readable message (chat-provider-client.ts:62-83). If parsing fails, safeResponseJson returns null gracefully (chat-provider-client.ts:113-122).

Request Lifecycle

Example Provider

The example provider at src/providers/example/client.ts shows the minimal usage pattern. createExampleProviderEdge (client.ts:11-26) calls createProviderEdge with the EXAMPLE_PROVIDER_SPEC and optional transport overrides. In a real provider, the transport functions would be methods on a ChatProviderClient instance rather than injected directly.

ChatRequestGuard

Before sending, assertProviderChatRequest (chat-request-guard.ts:5-27) validates that the request object has a non-empty model string and a messages array. This guard is called by every provider's patchRequest hook to catch malformed requests early, before they reach the network layer.

Cross-references

  • ProviderSpec Contract -- the spec that declares the endpoint and auth configuration consumed by chatApi
  • Provider Hooks -- the patchRequest hooks that run before ChatProviderClient receives the body

References