UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

61 lines (60 loc) • 4.14 kB
import type { Brain } from "../core/types.js"; import { type StreamEngineConfig } from "./stream-engine.js"; export interface OpenAIBrainConfig extends StreamEngineConfig { /** Base URL of your OpenAI-compatible gateway (without /chat/completions). Overridden by model.baseUrl if set. */ baseUrl?: string; /** API key. Overridden by per-call options.apiKey / getApiKeyAndHeaders. */ apiKey?: string; /** Extra headers merged into every request. */ headers?: Record<string, string>; /** Inject a custom fetch (tests / proxies). Absent ⇒ the engine's default transport; the contract for * both cases is `resolveFetch` (transport.ts). */ fetchImpl?: typeof fetch; /** * For weaker models that emit tool calls as TEXT (`<tool_call>{...}</tool_call>` / fenced json) * instead of native tool calls: promote them to real tool calls. Off by default. Native models * don't need this. */ repairTextToolCalls?: boolean; /** * Replay assistant `reasoning_content` back to the provider on follow-up requests (design/46). * **On by default**, and **conditional**: thinking is replayed only on prior assistant **tool-call** * turns — DeepSeek V4 **requires** it there (else it errors/degrades), while non-tool turns ignore it, * so replaying there only bloats the context window (prefix-cache cuts the *cost* of that bloat but not * the *window occupancy* → earlier compaction). Replaying just the tool-call turns is correctness- * equivalent and leaner. Set `false` to never replay (e.g. a provider that rejects the field). * Anthropic uses `createAnthropicBrain`, which always replays thinking blocks with their signature. */ replayThinking?: boolean; /** * Detect **degenerate repetition** (the model looping on the same char/phrase forever) mid-stream * and cut it off — the turn ends `error` ("degenerate repetition") so the loop stops paying and a * team member's looped output is treated as a failed turn, not a real statement. Default true. */ detectRepetition?: boolean; } /** * Wire-field inference for the per-reply output cap when the model declares no explicit * `compat.maxTokensField`. Newer stock-OpenAI model families REJECT `max_tokens` with a 400 * ("Unsupported parameter: 'max_tokens' is not supported with this model. Use 'max_completion_tokens' * instead."): the o-series reasoning models (o1/o3/o4 and their -mini/-pro/dated variants), the gpt-5 * family, and chatgpt-4o. Everything else — older OpenAI models AND the long tail of third-party * OpenAI-COMPATIBLE endpoints (vLLM, DeepSeek, llama.cpp, gateways) — keeps `max_tokens`, because many * compat endpoints only recognize that spelling. Heuristic rules (deliberately conservative — an id the * table can't confidently classify keeps the widely-supported `max_tokens`): * - `gpt-5…` → max_completion_tokens (gpt-5, gpt-5-mini, gpt-5.x, …) * - `chatgpt-4o…` → max_completion_tokens (chatgpt-4o-latest) * - `o1|o3|o4` (+ `-`/`.` suffix or bare) → max_completion_tokens (o1-preview, o3-mini, o4-mini, …) * - anything else → max_tokens (gpt-4o/gpt-4.x accept both; third-party ids often ONLY this) * An explicit `compat.maxTokensField` always wins over this inference. Exported so shells constructing * Models from bare ids (e.g. the TB entry) can bake the same inference into compat metadata. */ export declare function inferMaxTokensField(modelId: string): "max_completion_tokens" | "max_tokens"; /** * Create a Brain that talks to an OpenAI-compatible chat-completions endpoint (your model gateway). * Translates the unified Context into an OpenAI request and the streamed response back into the unified * AssistantMessageEvent protocol, including native tool calls. The connect/retry/timeout/read-loop * machinery lives in the shared {@link runStreamingBrain} engine (design/32); this adapter owns the * OpenAI request shaping + chat-completions SSE parsing + finalization. */ export declare function createOpenAIBrain(config?: OpenAIBrainConfig): Brain;