zz_

app/agent/chatBackend.ts

zzmq.dev@gmail.com/zz / app / agent / chatBackend.ts @ 5e5720fb Download
Ladder Summary
  • file The file defines the public contract between the generic tool‑calling loop and any underlying LLM provider by exporting the **ChatMessage** type (the shape of messages, including tool call metadata) and the **ChatBackend** interface (which any backend must implement with a `name` string and a `chat(messages, tools, onPartial?)` method that returns a `Promise<ChatMessage>`). These exported types are the only symbols other code imports to interact with a local Ollama model, Gemini API, or future providers.
// The one seam between the generic tool-calling loop (agent/loop.ts)
// and whichever model actually answers -- a local Ollama model or a
// remote API (Gemini today). The loop only ever talks to this
// interface, never to a provider's own wire format directly, so it
// doesn't care which one is running underneath.
import type { ToolDef } from "./tools.js";

export interface ChatMessage {
  role: "system" | "user" | "assistant" | "tool";
  content: string;
  tool_calls?: {
    function: { name: string; arguments: Record<string, unknown> };
    /**
     * A backend's own opaque per-call metadata, round-tripped back to
     * that same backend unchanged on the next turn -- needed for
     * Gemini specifically, whose API rejects a reconstructed
     * functionCall that's missing the `thoughtSignature` it returned
     * the first time (confirmed directly: a real 400 "Function call is
     * missing a thought_signature" error). Other backends (Ollama)
     * never set or read this.
     */
    raw?: unknown;
  }[];
  /** Which tool this result is for, on a "tool"-role message -- Ollama doesn't need it (matches by array order), Gemini's functionResponse does (matches by name). */
  name?: string;
}

export interface ChatBackend {
  name: string;
  /**
   * One turn: given the full message history and the tools available,
   * returns the model's reply (content and/or tool_calls). `onPartial`,
   * if the backend supports streaming, is called repeatedly with the
   * accumulated text-so-far.
   */
  chat(messages: ChatMessage[], tools: ToolDef[], onPartial?: (textSoFar: string) => void): Promise<ChatMessage>;
}