buildBackendChatModel function

ChatModel<ChatModelOptions> buildBackendChatModel(
  1. ModelBackendSpec spec, {
  2. required String model,
  3. List<Tool<Object>>? tools,
  4. Client? client,
})

Builds a configured dartantic ChatModel for spec.

model is the model id; tools are the tools to offer (names must already be Anthropic-legal — the seam owns lenny's dotted-name encoding). A supplied client is used directly on the swift-infer and Anthropic paths (so the driver owns retry); the OpenAI path wraps it in a transport-retry client.

Implementation

ChatModel<ChatModelOptions> buildBackendChatModel(
  ModelBackendSpec spec, {
  required String model,
  List<Tool>? tools,
  http.Client? client,
}) {
  switch (spec) {
    case SwiftInferBackend s:
      return SwiftInferChatModel(
        name: model,
        baseUrl: s.baseUrl,
        bearerToken: s.bearerToken,
        headers: s.headers,
        tools: tools,
        client: client,
        defaultOptions: s.options,
      );
    case AnthropicBackend a:
      return AnthropicChatModel(
        name: model,
        apiKey: a.apiKey,
        baseUrl: a.baseUrl,
        headers: a.headers,
        tools: tools,
        client: client,
        enableThinking: a.enableThinking,
        // Extended thinking (kept on, per the panel default) imposes two
        // Anthropic constraints, so the default options must respect both:
        //   * tool_choice must NOT force a tool ("Thinking may not be enabled
        //     when tool_choice forces tool use") — so tool_choice:auto, not
        //     :any. Claude reliably calls a tool with thinking on, and the
        //     driver's SchemaRejection retry covers a rare prose-only turn.
        //   * temperature may only be 1 when thinking is enabled — so we omit
        //     a temperature override here (the tuned frontier 0.2 is
        //     incompatible with thinking) and let Anthropic use its default.
        // An explicit [a.options] overrides these defaults; a caller that
        // re-introduces forced tool_choice or a non-1 temperature alongside
        // thinking owns that incompatible combination.
        defaultOptions:
            a.options ??
            const AnthropicChatOptions(toolChoice: AnthropicToolChoice.auto()),
      );
    case OpenAIBackend o:
      // NOTE: dartantic 3.4.1's OpenAI mapper hard-codes tool_choice:null, so
      // the OpenAI tier CANNOT force a tool call the way the old provider did
      // (tool_choice:'required'). The model usually calls a tool given tools +
      // a directive prompt; the driver's SchemaRejection retry covers a miss.
      // Tracked as a dartantic-upstream follow-up. Temperature defaults to the
      // tuned frontier 0.2 (was silently 1.0 after the cutover).
      return OpenAIProvider(
        apiKey: o.apiKey,
        baseUrl: o.baseUrl,
        headers: o.headers ?? const {},
      ).createChatModel(
        name: model,
        tools: tools,
        enableThinking: o.enableThinking,
        temperature: o.temperature ?? 0.2,
        options: o.options,
      );
  }
}