@24klynx/llm 0.1.0 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -1,9 +1,9 @@
1
- import { ContentBlock } from "@lynx/core";
1
+ import { ContentBlock } from "@24klynx/core";
2
2
 
3
3
  //#region src/types.d.ts
4
4
  /**
5
5
  * A chat message with a role and content, matching the OpenAI-compatible wire format.
6
- * Unlike {@link import('@lynx/core').Message}, this is a wire-format type for the LLM API.
6
+ * Unlike {@link import('@24klynx/core').Message}, this is a wire-format type for the LLM API.
7
7
  */
8
8
  interface ChatMessage {
9
9
  /** Who sent this message. */
@@ -51,7 +51,7 @@ interface LlmProvider {
51
51
  listModels(): ModelInfo[];
52
52
  /**
53
53
  * Return the static capability blob for a model.
54
- * Throws {@link import('@lynx/core').LlmError} if the model is unknown.
54
+ * Throws {@link import('@24klynx/core').LlmError} if the model is unknown.
55
55
  */
56
56
  getCapability(modelId: string): ProviderCapability;
57
57
  /**
@@ -233,7 +233,7 @@ declare function withRetry<T>(fn: () => Promise<T>, opts?: RetryOptions): Promis
233
233
  /**
234
234
  * Return the capability blob for `provider/modelId`.
235
235
  * Returns `undefined` for unknown models — callers should fall back
236
- * or throw a {@link import('@lynx/core').ConfigError}.
236
+ * or throw a {@link import('@24klynx/core').ConfigError}.
237
237
  */
238
238
  declare function getCapability(providerId: string, modelId: string): ProviderCapability | undefined;
239
239
  /**
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { LlmAuthError, LlmContextOverflowError, LlmError, LlmRateLimitError } from "@lynx/core";
1
+ import { LlmAuthError, LlmContextOverflowError, LlmError, LlmRateLimitError } from "@24klynx/core";
2
2
  //#region src/stream.ts
3
3
  /**
4
4
  * Stream\<T\> — pull‑based AsyncIterator backed by an internal queue.
@@ -244,7 +244,7 @@ const CAPABILITIES = {
244
244
  /**
245
245
  * Return the capability blob for `provider/modelId`.
246
246
  * Returns `undefined` for unknown models — callers should fall back
247
- * or throw a {@link import('@lynx/core').ConfigError}.
247
+ * or throw a {@link import('@24klynx/core').ConfigError}.
248
248
  */
249
249
  function getCapability(providerId, modelId) {
250
250
  return CAPABILITIES[`${providerId}/${modelId}`];
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["anyErr","DEFAULT_BASE_URL","MODELS","flushActiveToolCall","processToolCallDeltas","processSseLine","handleHttpError","DEFAULT_BASE_URL","MODELS","handleHttpError"],"sources":["../src/stream.ts","../src/retry.ts","../src/capabilities.ts","../src/fallback.ts","../src/tool-format.ts","../src/providers/deepseek.ts","../src/providers/openai.ts","../src/providers/anthropic.ts"],"sourcesContent":["/**\n * Stream\\<T\\> — pull‑based AsyncIterator backed by an internal queue.\n *\n * Producers call `enqueue()` / `done()` / `error()`.\n * Consumers use `for await … of`.\n *\n * This is deliberately a class (not EventEmitter) so the consumer\n * controls back‑pressure — when nobody is iterating, values stay in\n * the queue until `maxQueueSize` is reached.\n */\n\n// ── Constants ──────────────────────────────────────\n\n/** Drop the oldest item when the queue exceeds this size. */\nconst DEFAULT_MAX_QUEUE = 256;\n\n// ── Public API ─────────────────────────────────────\n\n/**\n * A cold, single‑consumer async iterator.\n *\n * ```ts\n * const stream = new Stream<string>();\n * producer(stream);\n * for await (const chunk of stream) {\n * console.log(chunk);\n * }\n * ```\n */\nexport class Stream<T> implements AsyncIterator<T>, AsyncIterable<T> {\n private _queue: T[] = [];\n private _resolvers: Array<(value: IteratorResult<T>) => void> = [];\n private _done = false;\n private _error: Error | null = null;\n private readonly _maxQueue: number;\n\n constructor(maxQueueSize = DEFAULT_MAX_QUEUE) {\n this._maxQueue = maxQueueSize;\n }\n\n // ── Producer API ────────────────────────────────\n\n /** Push a value into the stream. */\n enqueue(value: T): void {\n if (this._done || this._error) return;\n\n if (this._resolvers.length > 0) {\n const resolve = this._resolvers.shift()!;\n resolve({ value, done: false });\n } else {\n this._queue.push(value);\n // Prevent unbounded growth\n if (this._queue.length > this._maxQueue) {\n this._queue.shift();\n }\n }\n }\n\n /** Signal that the stream has finished normally. */\n done(): void {\n if (this._done) return;\n this._done = true;\n // Resolve all pending awaiters with done: true\n for (const resolve of this._resolvers) {\n resolve({ value: undefined, done: true });\n }\n this._resolvers = [];\n }\n\n /** Propagate an error to the consumer. */\n error(err: Error): void {\n if (this._done || this._error) return;\n this._error = err;\n this._done = true;\n // The error is thrown from next(), but we can't throw through pending\n // resolvers. We resolve them with done: true and set the error flag\n // so the next next() call will throw.\n for (const resolve of this._resolvers) {\n resolve({ value: undefined, done: true });\n }\n this._resolvers = [];\n }\n\n // ── Consumer API ────────────────────────────────\n\n [Symbol.asyncIterator](): AsyncIterator<T> {\n return this;\n }\n\n next(): Promise<IteratorResult<T>> {\n // If the stream errored, reject\n if (this._error) {\n return Promise.reject(this._error);\n }\n\n // If we're done and the queue is empty, return done\n if (this._done && this._queue.length === 0) {\n return Promise.resolve({ value: undefined, done: true });\n }\n\n // If there's a queued value, return it immediately\n if (this._queue.length > 0) {\n const value = this._queue.shift()!;\n return Promise.resolve({ value, done: false });\n }\n\n // Wait for the next enqueue/done/error\n return new Promise((resolve) => {\n this._resolvers.push(resolve);\n });\n }\n}\n","/**\n * withRetry — exponential backoff with jitter and Retry‑After respect.\n *\n * Used by every LLM provider for transient errors (429, 529, ECONNRESET, etc.).\n *\n * Algorithm:\n * delay = min(baseMs * 2^(attempt-1), maxMs)\n * jitter = delay * uniform(0.75, 1.25)\n * final = min(jitter, maxMs)\n */\n\nimport { LlmError, LlmRateLimitError } from \"@lynx/core\";\n\n// ── Constants ──────────────────────────────────────\n\nconst BASE_DELAY_MS = 500;\nconst MAX_DELAY_MS = 32_000;\nconst MAX_RETRIES = 10;\n\n// ── Types ──────────────────────────────────────────\n\nexport interface RetryOptions {\n /** Base delay for the first retry (default 500ms). */\n baseMs?: number;\n /** Hard ceiling on delay (default 32s). */\n maxMs?: number;\n /** Maximum number of retries before giving up (default 10). */\n maxRetries?: number;\n /** Function that returns true if this error is retryable. */\n isRetryable?: (err: Error) => boolean;\n /**\n * Whether this request is in the foreground (user-facing).\n * Foreground requests retry on 529; background requests discard\n * immediately to prevent cascade amplification. Defaults to true.\n */\n isForeground?: boolean;\n}\n\n// ── Helpers ────────────────────────────────────────\n\n/** Uniform random in [lo, hi]. */\nfunction jitter(lo: number, hi: number): number {\n return lo + Math.random() * (hi - lo);\n}\n\n/** Extract Retry‑After seconds from HTTP headers if present. */\nfunction parseRetryAfter(err: Error): number | null {\n const anyErr = err as Error & { headers?: unknown };\n const headers = anyErr.headers as Record<string, string> | undefined;\n if (!headers) return null;\n\n const raw = headers[\"retry-after\"];\n if (!raw) return null;\n\n // Try integer seconds first\n const secs = Number.parseInt(raw, 10);\n if (!Number.isNaN(secs)) return secs;\n\n // Try HTTP‑date (rare but valid)\n const parsed = Date.parse(raw);\n if (!Number.isNaN(parsed)) {\n return Math.max(0, Math.ceil((parsed - Date.now()) / 1000));\n }\n\n return null;\n}\n\n// ── Public API ─────────────────────────────────────\n\n/**\n * Execute `fn` with exponential backoff.\n *\n * Only retries on transient errors. Non‑retryable errors are re‑thrown\n * immediately.\n *\n * ```ts\n * const result = await withRetry(() => fetchFromApi(), {\n * baseMs: 500,\n * maxMs: 32_000,\n * });\n * ```\n */\nexport async function withRetry<T>(fn: () => Promise<T>, opts: RetryOptions = {}): Promise<T> {\n const baseMs = opts.baseMs ?? BASE_DELAY_MS;\n const maxMs = opts.maxMs ?? MAX_DELAY_MS;\n const maxRetries = opts.maxRetries ?? MAX_RETRIES;\n const isRetryable =\n opts.isRetryable ??\n ((err) => {\n if (err instanceof LlmRateLimitError) return true;\n // Always retry transient errors\n if (err instanceof LlmError && err.retryable) return true;\n // Retry network errors\n const code = (err as NodeJS.ErrnoException).code;\n if (code === \"ECONNRESET\" || code === \"EPIPE\" || code === \"ETIMEDOUT\") return true;\n return false;\n });\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n return await fn();\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n\n // If this is the last attempt or the error is not retryable, give up\n if (attempt === maxRetries || !isRetryable(error)) {\n throw error;\n }\n\n // 529 overload — only retry if this is in the foreground.\n // Background tasks drop to prevent cascade amplification.\n const anyErr = error as Error & { status?: number };\n if (anyErr.status === 529 && opts.isForeground === false) {\n throw error;\n }\n\n // Respect Retry‑After header\n const retryAfter = parseRetryAfter(error);\n const delay = retryAfter ? retryAfter * 1000 : Math.min(baseMs * Math.pow(2, attempt), maxMs);\n\n const jittered = Math.min(delay * jitter(0.75, 1.25), maxMs);\n\n await new Promise((resolve) => setTimeout(resolve, jittered));\n }\n }\n\n // Unreachable — the loop above always throws or returns\n throw new LlmError(\"withRetry exhausted all attempts\", {\n category: \"llm\",\n recoverable: false,\n retryable: false,\n diagnosticHint: \"llm_retry_exhausted\",\n });\n}\n","/**\n * Provider capability metadata table.\n *\n * Every model known to Lynx is registered here with its static capabilities.\n * The router uses this table to decide which model can handle a request\n * (e.g. a request with images requires `supportsVision: true`).\n */\n\nimport type { ProviderCapability } from \"./types.js\";\n\n/**\n * Lookup table keyed by \"provider/modelId\".\n *\n * New models can be added here without code changes —\n * capabilities are pure data, not behaviour.\n */\nconst CAPABILITIES: Record<string, ProviderCapability> = {\n // DeepSeek\n \"deepseek/deepseek-chat\": {\n contextWindow: 128_000,\n maxOutput: 8_192,\n supportsReasoning: false,\n supportsStreaming: true,\n supportsToolUse: true,\n supportsVision: false,\n promptCacheEnabled: true, // implicit prefix caching\n },\n \"deepseek/deepseek-reasoner\": {\n contextWindow: 128_000,\n maxOutput: 8_192,\n supportsReasoning: true,\n supportsStreaming: true,\n supportsToolUse: false,\n supportsVision: false,\n promptCacheEnabled: true,\n },\n\n // OpenAI\n \"openai/gpt-4o\": {\n contextWindow: 128_000,\n maxOutput: 16_384,\n supportsReasoning: false,\n supportsStreaming: true,\n supportsToolUse: true,\n supportsVision: true,\n promptCacheEnabled: false, // No prompt caching for OpenAI (as of now)\n },\n \"openai/gpt-4o-mini\": {\n contextWindow: 128_000,\n maxOutput: 16_384,\n supportsReasoning: false,\n supportsStreaming: true,\n supportsToolUse: true,\n supportsVision: true,\n promptCacheEnabled: false,\n },\n \"openai/o3-mini\": {\n contextWindow: 200_000,\n maxOutput: 100_000,\n supportsReasoning: true,\n supportsStreaming: true,\n supportsToolUse: false,\n supportsVision: false,\n promptCacheEnabled: false,\n },\n\n // Anthropic\n \"anthropic/claude-opus-4-8\": {\n contextWindow: 200_000,\n maxOutput: 32_000,\n supportsReasoning: true,\n supportsStreaming: true,\n supportsToolUse: true,\n supportsVision: true,\n promptCacheEnabled: true,\n },\n \"anthropic/claude-sonnet-4-6\": {\n contextWindow: 200_000,\n maxOutput: 16_384,\n supportsReasoning: true,\n supportsStreaming: true,\n supportsToolUse: true,\n supportsVision: true,\n promptCacheEnabled: true,\n },\n \"anthropic/claude-haiku-4-5\": {\n contextWindow: 200_000,\n maxOutput: 8_192,\n supportsReasoning: false,\n supportsStreaming: true,\n supportsToolUse: true,\n supportsVision: true,\n promptCacheEnabled: true,\n },\n};\n\n/**\n * Return the capability blob for `provider/modelId`.\n * Returns `undefined` for unknown models — callers should fall back\n * or throw a {@link import('@lynx/core').ConfigError}.\n */\nexport function getCapability(providerId: string, modelId: string): ProviderCapability | undefined {\n return CAPABILITIES[`${providerId}/${modelId}`];\n}\n\n/**\n * Return every known capability entry.\n * Used by the model picker UI in the TUI layer.\n */\nexport function listCapabilities(): Array<{ key: string } & ProviderCapability> {\n return Object.entries(CAPABILITIES).map(([key, cap]) => ({ key, ...cap }));\n}\n","/**\n * Model/Provider fallback logic.\n *\n * When a provider returns a transient error (rate‑limit, overload) the\n * fallback engine tries the next candidate in the configured chain.\n * Permanent errors (auth) skip the provider entirely and move to the next.\n *\n * The primary use‑case is keeping the agent loop running when the\n * preferred model is temporarily unavailable.\n */\n\nimport { LlmRateLimitError, LlmAuthError } from \"@lynx/core\";\n\n// ── Types ────────────────────────────────────────────\n\n/** A candidate provider+model pair in the fallback chain. */\nexport interface ModelCandidate {\n provider: string;\n model: string;\n}\n\n/** Record of a single fallback attempt for diagnostics. */\nexport interface FallbackAttempt {\n provider: string;\n model: string;\n error: string;\n code?: string;\n /** HTTP status code if available. */\n status?: number;\n}\n\n/** Result of a fallback run — which candidate succeeded and what was tried. */\nexport interface FallbackResult<T> {\n result: T;\n provider: string;\n model: string;\n attempts: FallbackAttempt[];\n}\n\n/** Error thrown when every candidate in the chain has been exhausted. */\nexport class FallbackExhaustedError extends Error {\n readonly attempts: FallbackAttempt[];\n\n constructor(attempts: FallbackAttempt[]) {\n const summary = attempts.map((a) => ` ${a.provider}/${a.model}: ${a.error}`).join(\"\\n\");\n super(`All fallback candidates exhausted:\\n${summary}`);\n this.name = \"FallbackExhaustedError\";\n this.attempts = attempts;\n }\n}\n\n// ── Helpers ──────────────────────────────────────────\n\n/**\n * Determine whether an error is transient (worth retrying with a\n * different provider) or permanent (skip this provider).\n */\nfunction isTransient(err: unknown): boolean {\n if (err instanceof LlmRateLimitError) return true;\n if (err instanceof LlmAuthError) return false;\n // Network errors are transient\n if (err instanceof Error && \"code\" in err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === \"ECONNRESET\" || code === \"EPIPE\" || code === \"ETIMEDOUT\" || code === \"ENOTFOUND\") {\n return true;\n }\n }\n // Default: treat unknown errors as transient (safer to try another provider)\n return true;\n}\n\n// ── Public API ───────────────────────────────────────\n\n/**\n * Run `fn` with automatic fallback across a chain of model candidates.\n *\n * `fn` is called with (provider, model) and should return a result.\n * If `fn` throws, the error is classified — transient errors move to the\n * next candidate, permanent errors skip the provider, and fatal errors\n * (like user abort) are re‑thrown immediately.\n */\nexport async function runWithFallback<T>(\n candidates: ModelCandidate[],\n fn: (candidate: ModelCandidate) => Promise<T>,\n opts?: { signal?: AbortSignal },\n): Promise<FallbackResult<T>> {\n if (candidates.length === 0) {\n throw new FallbackExhaustedError([]);\n }\n\n const attempts: FallbackAttempt[] = [];\n\n for (const candidate of candidates) {\n // Respect external abort signal\n if (opts?.signal?.aborted) {\n throw new DOMException(\"Aborted\", \"AbortError\");\n }\n\n try {\n const result = await fn(candidate);\n return { result, provider: candidate.provider, model: candidate.model, attempts };\n } catch (err) {\n // User‑initiated abort — stop immediately\n if (err instanceof DOMException && err.name === \"AbortError\") {\n throw err;\n }\n\n const message = err instanceof Error ? err.message : String(err);\n const status = (err as Record<string, unknown>).status as number | undefined;\n const code = (err as Record<string, unknown>).code as string | undefined;\n\n attempts.push({\n provider: candidate.provider,\n model: candidate.model,\n error: message,\n code,\n status,\n });\n\n // Permanent errors — skip this provider, try next\n if (!isTransient(err)) {\n continue;\n }\n\n // Transient — try next candidate\n continue;\n }\n }\n\n throw new FallbackExhaustedError(attempts);\n}\n\n/**\n * Build a fallback chain from a primary model and a list of fallback refs.\n *\n * Fallback refs use the format `\"provider/model\"` (e.g. `\"openai/gpt-4o-mini\"`).\n * If the provider prefix is omitted the primary's provider is used.\n */\nexport function buildCandidateChain(\n primaryProvider: string,\n primaryModel: string,\n fallbackRefs: string[],\n): ModelCandidate[] {\n const chain: ModelCandidate[] = [{ provider: primaryProvider, model: primaryModel }];\n\n for (const ref of fallbackRefs) {\n const slashIdx = ref.indexOf(\"/\");\n if (slashIdx > 0) {\n chain.push({ provider: ref.slice(0, slashIdx), model: ref.slice(slashIdx + 1) });\n } else {\n chain.push({ provider: primaryProvider, model: ref });\n }\n }\n\n return chain;\n}\n","/**\n * Wire-format normalization for OpenAI-compatible APIs.\n *\n * Lynx uses its own internal types ({@link import('@lynx/tools').ToolDescriptor},\n * {@link import('@lynx/core').ContentBlock}) that don't exactly match the\n * OpenAI/DeepSeek wire format. This module maps between them.\n */\n\nimport type { ChatMessage } from \"./types.js\";\n\n// ── Tool normalization ────────────────────────────\n\n/** Raw shape we receive from the agent (ToolDescriptor-like). */\ninterface RawTool {\n name?: unknown;\n description?: unknown;\n inputSchema?: unknown;\n}\n\n/** OpenAI-compatible tool definition. */\ninterface OpenAiTool {\n type: \"function\";\n function: {\n name: string;\n description: string;\n parameters: Record<string, unknown>;\n };\n}\n\n/**\n * Convert Lynx tool descriptors to OpenAI-compatible format.\n *\n * Wraps each tool with `type: \"function\"` and maps `inputSchema` → `parameters`.\n * Tools already in OpenAI format pass through unchanged.\n */\nexport function normalizeOpenAiTools(tools: unknown[]): OpenAiTool[] {\n return tools.map((tool) => {\n const raw = tool as RawTool;\n if (typeof raw === \"object\" && raw !== null && (raw as { type?: string }).type === \"function\") {\n return tool as OpenAiTool;\n }\n return {\n type: \"function\" as const,\n function: {\n name: String(raw.name ?? \"unknown\"),\n description: String(raw.description ?? \"\"),\n parameters: (raw.inputSchema as Record<string, unknown>) ?? {},\n },\n };\n });\n}\n\n// ── Wire-format message types ─────────────────────\n\n/** A message in OpenAI wire format — what the API actually expects. */\nexport interface OpenAiMessage {\n role: \"system\" | \"user\" | \"assistant\" | \"tool\";\n content: string | OpenAiContentBlock[] | null;\n tool_call_id?: string;\n tool_calls?: Array<{\n id: string;\n type: \"function\";\n function: { name: string; arguments: string };\n }>;\n}\n\ninterface OpenAiContentBlock {\n type: \"text\";\n text: string;\n}\n\n// ── Content block types for recognition ───────────\n\ninterface LynxContentBlock {\n type: string;\n text?: string;\n id?: string;\n name?: string;\n input?: Record<string, unknown>;\n toolUseId?: string;\n content?: string;\n isError?: boolean;\n}\n\n// ── Message normalization ─────────────────────────\n\n/**\n * Convert Lynx ChatMessage[] to OpenAI wire-format messages.\n *\n * Key transformations:\n * - `tool_result` content blocks → `role: \"tool\"` messages with string content\n * - `tool_use` blocks in assistant messages → `tool_calls` array\n * - `reasoning` blocks are stripped (not sent back to the API)\n */\nexport function normalizeOpenAiMessages(messages: ChatMessage[]): OpenAiMessage[] {\n const out: OpenAiMessage[] = [];\n\n for (const msg of messages) {\n // Simple string content — pass through with role\n if (typeof msg.content === \"string\") {\n out.push({ role: msg.role as OpenAiMessage[\"role\"], content: msg.content });\n continue;\n }\n\n const blocks = msg.content as LynxContentBlock[];\n\n // Check for tool_result blocks → convert to tool-role messages\n const toolResults = blocks.filter((b) => b.type === \"tool_result\");\n const nonToolBlocks = blocks.filter((b) => b.type !== \"tool_result\");\n\n // Emit tool result messages\n for (const tr of toolResults) {\n out.push({\n role: \"tool\",\n tool_call_id: tr.toolUseId ?? \"unknown\",\n content: tr.content ?? \"\",\n });\n }\n\n // Handle remaining blocks by role\n if (nonToolBlocks.length > 0) {\n const normalized = normalizeContentBlocks(nonToolBlocks, msg.role);\n\n if (msg.role === \"assistant\") {\n const textBlocks = normalized.filter((b) => b.type === \"text\");\n const toolUses = blocks.filter((b) => b.type === \"tool_use\");\n const assistantMsg: OpenAiMessage = {\n role: \"assistant\",\n content: textBlocks.length > 0 ? textBlocks : null,\n };\n if (toolUses.length > 0) {\n assistantMsg.tool_calls = toolUses.map((tu) => ({\n id: tu.id ?? \"unknown\",\n type: \"function\" as const,\n function: {\n name: tu.name ?? \"unknown\",\n arguments: JSON.stringify(tu.input ?? {}),\n },\n }));\n }\n out.push(assistantMsg);\n } else {\n // User or system message — strip unrecognized blocks, keep text\n const textBlocks = normalized.filter((b) => b.type === \"text\");\n if (textBlocks.length > 0) {\n out.push({\n role: msg.role as OpenAiMessage[\"role\"],\n content:\n textBlocks.length === 1 && msg.role !== \"system\"\n ? (textBlocks[0]!.text ?? \"\")\n : textBlocks,\n });\n }\n }\n }\n }\n\n return out;\n}\n\n/** Keep only text blocks, strip reasoning/tool_use/tool_result from content. */\nfunction normalizeContentBlocks(blocks: LynxContentBlock[], _role: string): OpenAiContentBlock[] {\n return blocks\n .filter((b) => b.type === \"text\" && typeof b.text === \"string\")\n .map((b) => ({ type: \"text\" as const, text: b.text! }));\n}\n","/**\n * DeepSeek provider — SSE streaming via the /v1/chat/completions endpoint.\n *\n * DeepSeek is API‑compatible with OpenAI so the wire format is the same.\n * The key difference: DeepSeek has implicit prefix caching on every request\n * (no cache_control blocks needed).\n */\n\nimport { LlmError, LlmRateLimitError, LlmAuthError, LlmContextOverflowError } from \"@lynx/core\";\nimport type { LlmProvider, ModelInfo, ProviderCapability, StreamEvent } from \"../types.js\";\nimport { getCapability } from \"../capabilities.js\";\nimport { withRetry } from \"../retry.js\";\nimport { normalizeOpenAiTools, normalizeOpenAiMessages } from \"../tool-format.js\";\n\n// ── Configuration ──────────────────────────────────\n\nexport interface DeepSeekConfig {\n /** API key read from auth store. */\n apiKey: string;\n /** Base URL (defaults to https://api.deepseek.com). */\n baseUrl?: string;\n /** Request timeout in ms (default 120s). */\n timeoutMs?: number;\n}\n\nconst DEFAULT_BASE_URL = \"https://api.deepseek.com\";\nconst DEFAULT_TIMEOUT_MS = 120_000;\n\n// ── Models ─────────────────────────────────────────\n\nconst MODELS: ModelInfo[] = [\n { id: \"deepseek-chat\", label: \"DeepSeek Chat (V3)\", contextWindow: 128_000, maxOutput: 8_192 },\n {\n id: \"deepseek-reasoner\",\n label: \"DeepSeek Reasoner (R1)\",\n contextWindow: 128_000,\n maxOutput: 8_192,\n },\n];\n\n// ── Helpers ────────────────────────────────────────\n\n/** Flush active tool call buffer, yielding tool_use_end or error events. */\nfunction flushActiveToolCall(call: { id: string; name: string; buffer: string }): StreamEvent[] {\n try {\n const input = JSON.parse(call.buffer);\n return [{ type: \"tool_use_end\", callId: call.id, input }];\n } catch {\n return [\n {\n type: \"error\",\n code: \"TOOL_PARSE_ERROR\",\n message: `Failed to parse tool input for ${call.name}`,\n },\n ];\n }\n}\n\n/** Process tool_calls delta array, returning events and updated active call state. */\nfunction processToolCallDeltas(\n toolCalls: Array<Record<string, unknown>>,\n activeCall: { id: string; name: string; buffer: string } | null,\n): { events: StreamEvent[]; activeCall: typeof activeCall } {\n const events: StreamEvent[] = [];\n let currentCall = activeCall;\n\n for (const tc of toolCalls) {\n const tcId = tc.id as string | undefined;\n const fn = tc.function as { name?: string; arguments?: string } | undefined;\n\n if (tcId && fn?.name) {\n // Flush previous tool call if any (silently ignore parse failures)\n if (currentCall) {\n try {\n const input = JSON.parse(currentCall.buffer);\n events.push({ type: \"tool_use_end\", callId: currentCall.id, input });\n } catch {\n // ignore parse failure, keep accumulating\n }\n }\n currentCall = { id: tcId, name: fn.name, buffer: \"\" };\n events.push({ type: \"tool_use_start\", callId: tcId, name: fn.name });\n }\n\n if (fn?.arguments && currentCall) {\n currentCall.buffer += fn.arguments;\n events.push({ type: \"tool_use_delta\", callId: currentCall.id, text: fn.arguments });\n }\n }\n\n return { events, activeCall: currentCall };\n}\n\n/** SSE stream processing state. */\ninterface SseState {\n activeToolCall: { id: string; name: string; buffer: string } | null;\n textIndex: number;\n reasoningIndex: number;\n}\n\n/** Process a single SSE line, returning events to yield and updated state. */\nfunction processSseLine(\n line: string,\n state: SseState,\n): { events: StreamEvent[]; state: SseState; done: boolean } {\n const trimmed = line.trim();\n if (!trimmed || !trimmed.startsWith(\"data:\")) {\n return { events: [], state, done: false };\n }\n\n const data = trimmed.slice(5).trim();\n\n if (data === \"[DONE]\") {\n const events: StreamEvent[] = [];\n if (state.activeToolCall) {\n events.push(...flushActiveToolCall(state.activeToolCall));\n }\n events.push({ type: \"done\" });\n return { events, state: { ...state, activeToolCall: null }, done: true };\n }\n\n let chunk: { choices?: Array<{ delta?: Record<string, unknown> }> };\n try {\n chunk = JSON.parse(data);\n } catch {\n return { events: [], state, done: false };\n }\n\n const choice = chunk.choices?.[0];\n if (!choice?.delta) {\n return { events: [], state, done: false };\n }\n\n const delta = choice.delta;\n const events: StreamEvent[] = [];\n let { textIndex, reasoningIndex, activeToolCall } = state;\n\n // Reasoning content (DeepSeek R1)\n if (delta.reasoning_content && typeof delta.reasoning_content === \"string\") {\n events.push({\n type: \"reasoning_delta\",\n index: reasoningIndex++,\n text: delta.reasoning_content,\n });\n }\n\n // Regular text content\n if (delta.content && typeof delta.content === \"string\") {\n events.push({\n type: \"text_delta\",\n index: textIndex++,\n text: delta.content,\n });\n }\n\n // Tool call handling\n if (delta.tool_calls) {\n const tcResult = processToolCallDeltas(\n delta.tool_calls as Array<Record<string, unknown>>,\n activeToolCall,\n );\n events.push(...tcResult.events);\n activeToolCall = tcResult.activeCall;\n }\n\n return { events, state: { textIndex, reasoningIndex, activeToolCall }, done: false };\n}\n\n// ── Provider ───────────────────────────────────────\n\n/**\n * Create a DeepSeek provider instance.\n *\n * Each call to `stream()` opens a fresh HTTP connection.\n * The provider itself is stateless — the API key is the only configuration.\n */\nexport function createDeepSeekProvider(config: DeepSeekConfig): LlmProvider {\n const baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;\n const _timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n\n const provider: LlmProvider = {\n id: \"deepseek\",\n\n listModels(): ModelInfo[] {\n return MODELS;\n },\n\n getCapability(modelId: string): ProviderCapability {\n const cap = getCapability(\"deepseek\", modelId);\n if (!cap) {\n throw new LlmError(`Unknown DeepSeek model: ${modelId}`, {\n category: \"llm\",\n recoverable: false,\n retryable: false,\n diagnosticHint: \"llm_unknown_model\",\n });\n }\n return cap;\n },\n\n async *stream(\n ...[modelId, messages, systemPrompt, tools, signal]: Parameters<LlmProvider[\"stream\"]>\n ): AsyncGenerator<StreamEvent, void, void> {\n const systemMessages = systemPrompt ? [{ role: \"system\", content: systemPrompt }] : [];\n\n const body = {\n model: modelId,\n messages: [...systemMessages, ...normalizeOpenAiMessages(messages)],\n stream: true,\n ...(tools.length > 0 ? { tools: normalizeOpenAiTools(tools) } : {}),\n };\n\n async function makeRequest(): Promise<Response> {\n const controller = new AbortController();\n const onAbort = () => controller.abort();\n signal.addEventListener(\"abort\", onAbort, { once: true });\n\n try {\n const response = await fetch(`${baseUrl}/v1/chat/completions`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${config.apiKey}`,\n },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n await handleHttpError(response);\n }\n\n return response;\n } finally {\n signal.removeEventListener(\"abort\", onAbort);\n }\n }\n\n const response = await withRetry(makeRequest, {\n baseMs: 500,\n maxMs: 32_000,\n });\n\n if (!response.body) {\n throw new LlmError(\"DeepSeek returned empty response body\", {\n category: \"llm\",\n recoverable: true,\n retryable: true,\n });\n }\n\n // Parse SSE stream\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n let textIndex = 0;\n let reasoningIndex = 0;\n let activeToolCall: { id: string; name: string; buffer: string } | null = null;\n\n try {\n while (true) {\n if (signal.aborted) break;\n\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n\n // Process complete SSE lines\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() ?? \"\"; // keep incomplete line in buffer\n\n for (const line of lines) {\n const result = processSseLine(line, { activeToolCall, textIndex, reasoningIndex });\n for (const event of result.events) yield event;\n if (result.done) return;\n activeToolCall = result.state.activeToolCall;\n textIndex = result.state.textIndex;\n reasoningIndex = result.state.reasoningIndex;\n }\n }\n\n // Stream ended without [DONE]\n if (activeToolCall) {\n for (const event of flushActiveToolCall(activeToolCall)) yield event;\n }\n yield { type: \"done\" };\n } finally {\n reader.releaseLock();\n }\n },\n };\n\n return provider;\n}\n\n// ── HTTP error mapper ──────────────────────────────\n\nasync function handleHttpError(response: Response): Promise<never> {\n let body: string;\n try {\n body = await response.text();\n } catch {\n body = \"\";\n }\n\n const status = response.status;\n\n if (status === 401 || status === 403) {\n throw new LlmAuthError(`DeepSeek auth failed (${status}): ${body}`);\n }\n\n if (status === 429) {\n throw new LlmRateLimitError(`DeepSeek rate limited (429): ${body}`);\n }\n\n if (status === 529) {\n const err = new LlmRateLimitError(`DeepSeek overloaded (529): ${body}`);\n (err as unknown as Record<string, unknown>).status = 529;\n throw err;\n }\n\n if (status === 400 && body.includes(\"context\")) {\n throw new LlmContextOverflowError(`DeepSeek context overflow: ${body}`);\n }\n\n throw new LlmError(`DeepSeek HTTP ${status}: ${body}`, {\n category: \"llm\",\n recoverable: status >= 500,\n retryable: status >= 500 || status === 429,\n });\n}\n","/**\n * OpenAI provider — SSE streaming via the /v1/chat/completions endpoint.\n *\n * The API shape is identical to DeepSeek (both follow the OpenAI spec),\n * but the provider metadata and defaults differ.\n */\n\nimport { LlmError, LlmRateLimitError, LlmAuthError, LlmContextOverflowError } from \"@lynx/core\";\nimport type { LlmProvider, ModelInfo, ProviderCapability, StreamEvent } from \"../types.js\";\nimport { getCapability } from \"../capabilities.js\";\nimport { withRetry } from \"../retry.js\";\nimport { normalizeOpenAiTools, normalizeOpenAiMessages } from \"../tool-format.js\";\n\n// ── Configuration ──────────────────────────────────\n\nexport interface OpenAiConfig {\n apiKey: string;\n baseUrl?: string;\n timeoutMs?: number;\n}\n\nconst DEFAULT_BASE_URL = \"https://api.openai.com\";\nconst _DEFAULT_TIMEOUT_MS = 120_000;\n\n// ── Models ─────────────────────────────────────────\n\nconst MODELS: ModelInfo[] = [\n { id: \"gpt-4o\", label: \"GPT-4o\", contextWindow: 128_000, maxOutput: 16_384 },\n { id: \"gpt-4o-mini\", label: \"GPT-4o Mini\", contextWindow: 128_000, maxOutput: 16_384 },\n { id: \"o3-mini\", label: \"o3-mini\", contextWindow: 200_000, maxOutput: 100_000 },\n];\n\n// ── Helpers ────────────────────────────────────────\n\n/** Flush active tool call buffer, yielding tool_use_end or error events. */\nfunction flushActiveToolCall(call: { id: string; name: string; buffer: string }): StreamEvent[] {\n try {\n const input = JSON.parse(call.buffer);\n return [{ type: \"tool_use_end\", callId: call.id, input }];\n } catch {\n return [\n {\n type: \"error\",\n code: \"TOOL_PARSE_ERROR\",\n message: `Failed to parse tool input for ${call.name}`,\n },\n ];\n }\n}\n\n/** Process tool_calls delta array, returning events and updated active call state. */\nfunction processToolCallDeltas(\n toolCalls: Array<Record<string, unknown>>,\n activeCall: { id: string; name: string; buffer: string } | null,\n): { events: StreamEvent[]; activeCall: typeof activeCall } {\n const events: StreamEvent[] = [];\n let currentCall = activeCall;\n\n for (const tc of toolCalls) {\n const tcId = tc.id as string | undefined;\n const fn = tc.function as { name?: string; arguments?: string } | undefined;\n\n if (tcId && fn?.name) {\n if (currentCall) {\n try {\n const input = JSON.parse(currentCall.buffer);\n events.push({ type: \"tool_use_end\", callId: currentCall.id, input });\n } catch {\n /* keep accumulating */\n }\n }\n currentCall = { id: tcId, name: fn.name, buffer: \"\" };\n events.push({ type: \"tool_use_start\", callId: tcId, name: fn.name });\n }\n\n if (fn?.arguments && currentCall) {\n currentCall.buffer += fn.arguments;\n events.push({ type: \"tool_use_delta\", callId: currentCall.id, text: fn.arguments });\n }\n }\n\n return { events, activeCall: currentCall };\n}\n\n/** SSE stream processing state. */\ninterface SseState {\n activeToolCall: { id: string; name: string; buffer: string } | null;\n textIndex: number;\n reasoningIndex: number;\n}\n\n/** Process a single SSE line, returning events to yield and updated state. */\nfunction processSseLine(\n line: string,\n state: SseState,\n): { events: StreamEvent[]; state: SseState; done: boolean } {\n const trimmed = line.trim();\n if (!trimmed || !trimmed.startsWith(\"data:\")) {\n return { events: [], state, done: false };\n }\n\n const data = trimmed.slice(5).trim();\n\n if (data === \"[DONE]\") {\n const events: StreamEvent[] = [];\n if (state.activeToolCall) {\n events.push(...flushActiveToolCall(state.activeToolCall));\n }\n events.push({ type: \"done\" });\n return { events, state: { ...state, activeToolCall: null }, done: true };\n }\n\n let chunk: { choices?: Array<{ delta?: Record<string, unknown> }> };\n try {\n chunk = JSON.parse(data);\n } catch {\n return { events: [], state, done: false };\n }\n\n const choice = chunk.choices?.[0];\n if (!choice?.delta) {\n return { events: [], state, done: false };\n }\n\n const delta = choice.delta;\n const events: StreamEvent[] = [];\n let { textIndex, reasoningIndex, activeToolCall } = state;\n\n if (delta.reasoning_content && typeof delta.reasoning_content === \"string\") {\n events.push({\n type: \"reasoning_delta\",\n index: reasoningIndex++,\n text: delta.reasoning_content,\n });\n }\n\n if (delta.content && typeof delta.content === \"string\") {\n events.push({ type: \"text_delta\", index: textIndex++, text: delta.content });\n }\n\n if (delta.tool_calls) {\n const tcResult = processToolCallDeltas(\n delta.tool_calls as Array<Record<string, unknown>>,\n activeToolCall,\n );\n events.push(...tcResult.events);\n activeToolCall = tcResult.activeCall;\n }\n\n return { events, state: { textIndex, reasoningIndex, activeToolCall }, done: false };\n}\n\n// ── Provider ───────────────────────────────────────\n\n/**\n * Create an OpenAI provider instance.\n *\n * Same SSE parsing logic as DeepSeek — both follow the same wire protocol.\n * The only differences are the base URL, models, and capability metadata.\n */\nexport function createOpenAiProvider(config: OpenAiConfig): LlmProvider {\n const baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;\n const provider: LlmProvider = {\n id: \"openai\",\n\n listModels(): ModelInfo[] {\n return MODELS;\n },\n\n getCapability(modelId: string): ProviderCapability {\n const cap = getCapability(\"openai\", modelId);\n if (!cap) {\n throw new LlmError(`Unknown OpenAI model: ${modelId}`, {\n category: \"llm\",\n recoverable: false,\n retryable: false,\n diagnosticHint: \"llm_unknown_model\",\n });\n }\n return cap;\n },\n\n async *stream(\n ...[modelId, messages, systemPrompt, tools, signal]: Parameters<LlmProvider[\"stream\"]>\n ): AsyncGenerator<StreamEvent, void, void> {\n const systemMessages = systemPrompt ? [{ role: \"system\", content: systemPrompt }] : [];\n\n const body = {\n model: modelId,\n messages: [...systemMessages, ...normalizeOpenAiMessages(messages)],\n stream: true,\n ...(tools.length > 0 ? { tools: normalizeOpenAiTools(tools) } : {}),\n };\n\n async function makeRequest(): Promise<Response> {\n const controller = new AbortController();\n const onAbort = () => controller.abort();\n signal.addEventListener(\"abort\", onAbort, { once: true });\n\n try {\n const response = await fetch(`${baseUrl}/v1/chat/completions`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${config.apiKey}`,\n },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n await handleHttpError(response);\n }\n\n return response;\n } finally {\n signal.removeEventListener(\"abort\", onAbort);\n }\n }\n\n const response = await withRetry(makeRequest, {\n baseMs: 500,\n maxMs: 32_000,\n });\n\n if (!response.body) {\n throw new LlmError(\"OpenAI returned empty response body\", {\n category: \"llm\",\n recoverable: true,\n retryable: true,\n });\n }\n\n // Parse SSE stream (same logic as DeepSeek, extracted for clarity)\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n let textIndex = 0;\n let reasoningIndex = 0;\n let activeToolCall: { id: string; name: string; buffer: string } | null = null;\n\n try {\n while (true) {\n if (signal.aborted) break;\n\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() ?? \"\";\n\n for (const line of lines) {\n const result = processSseLine(line, { activeToolCall, textIndex, reasoningIndex });\n for (const event of result.events) yield event;\n if (result.done) return;\n activeToolCall = result.state.activeToolCall;\n textIndex = result.state.textIndex;\n reasoningIndex = result.state.reasoningIndex;\n }\n }\n\n if (activeToolCall) {\n for (const event of flushActiveToolCall(activeToolCall)) yield event;\n }\n yield { type: \"done\" };\n } finally {\n reader.releaseLock();\n }\n },\n };\n\n return provider;\n}\n\n// ── HTTP error mapper ──────────────────────────────\n\nasync function handleHttpError(response: Response): Promise<never> {\n let body: string;\n try {\n body = await response.text();\n } catch {\n body = \"\";\n }\n\n const status = response.status;\n\n if (status === 401 || status === 403) {\n throw new LlmAuthError(`OpenAI auth failed (${status}): ${body}`);\n }\n\n if (status === 429) {\n throw new LlmRateLimitError(`OpenAI rate limited (429): ${body}`);\n }\n\n if (status === 529) {\n throw new LlmRateLimitError(`OpenAI overloaded (529): ${body}`, 529);\n }\n\n if (status === 400 && body.includes(\"context\")) {\n throw new LlmContextOverflowError(`OpenAI context overflow: ${body}`);\n }\n\n throw new LlmError(`OpenAI HTTP ${status}: ${body}`, {\n category: \"llm\",\n recoverable: status >= 500,\n retryable: status >= 500 || status === 429,\n });\n}\n","/**\n * Anthropic provider — SSE streaming via the /v1/messages endpoint.\n *\n * Translates Lynx's OpenAI‑compatible internal format to Anthropic's\n * native message/tool format, then maps Anthropic SSE events back to\n * Lynx StreamEvent types. This is the reverse of what Claude Code's\n * Codex adapter does (Anthropic → OpenAI translation).\n */\n\nimport { LlmError, LlmRateLimitError, LlmAuthError, LlmContextOverflowError } from \"@lynx/core\";\nimport type { LlmProvider, ModelInfo, ProviderCapability, StreamEvent } from \"../types.js\";\nimport { getCapability } from \"../capabilities.js\";\nimport { withRetry } from \"../retry.js\";\n\n// ── Configuration ──────────────────────────────────\n\nexport interface AnthropicConfig {\n /** API key from ANTHROPIC_API_KEY env var. */\n apiKey: string;\n /** Base URL (defaults to https://api.anthropic.com). */\n baseUrl?: string;\n /** Request timeout in ms (default 120s). */\n timeoutMs?: number;\n}\n\nconst DEFAULT_BASE_URL = \"https://api.anthropic.com\";\nconst ANTHROPIC_VERSION = \"2023-06-01\";\nconst DEFAULT_TIMEOUT_MS = 120_000;\n\n// ── Models ─────────────────────────────────────────\n\nconst MODELS: ModelInfo[] = [\n {\n id: \"claude-opus-4-8\",\n label: \"Claude Opus 4.8\",\n contextWindow: 200_000,\n maxOutput: 32_000,\n },\n {\n id: \"claude-sonnet-4-6\",\n label: \"Claude Sonnet 4.6\",\n contextWindow: 200_000,\n maxOutput: 16_384,\n },\n {\n id: \"claude-haiku-4-5\",\n label: \"Claude Haiku 4.5\",\n contextWindow: 200_000,\n maxOutput: 8_192,\n },\n];\n\n// ── Wire‑format types ──────────────────────────────\n\n/** Anthropic content block types. */\ntype AnthropicContentBlock =\n | { type: \"text\"; text: string }\n | { type: \"tool_use\"; id: string; name: string; input: Record<string, unknown> }\n | { type: \"tool_result\"; tool_use_id: string; content: string; is_error?: boolean };\n\n/** Anthropic message format. */\ninterface AnthropicMessage {\n role: \"user\" | \"assistant\";\n content: string | AnthropicContentBlock[];\n}\n\n/** Anthropic tool definition. */\ninterface AnthropicTool {\n name: string;\n description: string;\n input_schema: Record<string, unknown>;\n}\n\n/** Internal type for message content blocks arriving via Lynx ChatMessage. */\ninterface LynxContentBlock {\n type: string;\n text?: string;\n id?: string;\n name?: string;\n input?: Record<string, unknown>;\n toolUseId?: string;\n content?: string;\n isError?: boolean;\n}\n\n/** OpenAI‑format tool_calls entry. */\ninterface OpenAiToolCall {\n id: string;\n type: \"function\";\n function: { name: string; arguments: string };\n}\n\n/** Lynx ChatMessage shape (what normalizeOpenAiMessages maps TO, so we receive FROM). */\ninterface LynxChatMessage {\n role: \"system\" | \"user\" | \"assistant\" | \"tool\";\n content: string | LynxContentBlock[] | null;\n tool_call_id?: string;\n tool_calls?: OpenAiToolCall[];\n}\n\n// ── Translation: Lynx messages → Anthropic messages ─\n\n/**\n * Convert Lynx OpenAI‑compatible ChatMessage[] to Anthropic Message[].\n * System‑role messages are extracted into a separate string array\n * (Anthropic treats system as a top‑level parameter, not a message).\n */\nfunction translateMessages(chatMessages: LynxChatMessage[]): {\n messages: AnthropicMessage[];\n systemLines: string[];\n} {\n const messages: AnthropicMessage[] = [];\n const systemLines: string[] = [];\n\n for (const msg of chatMessages) {\n if (msg.role === \"system\") {\n if (typeof msg.content === \"string\") {\n systemLines.push(msg.content);\n } else if (Array.isArray(msg.content)) {\n for (const block of msg.content) {\n if (block.type === \"text\" && block.text) systemLines.push(block.text);\n }\n }\n continue;\n }\n\n if (msg.role === \"user\") {\n messages.push({ role: \"user\", content: translateUserContent(msg) });\n } else if (msg.role === \"assistant\") {\n messages.push({ role: \"assistant\", content: translateAssistantContent(msg) });\n } else if (msg.role === \"tool\") {\n // OpenAI tool‑role → Anthropic user message with tool_result block\n messages.push({\n role: \"user\",\n content: [\n {\n type: \"tool_result\",\n tool_use_id: msg.tool_call_id ?? \"unknown\",\n content: typeof msg.content === \"string\" ? msg.content : JSON.stringify(msg.content),\n },\n ],\n });\n }\n }\n\n return { messages, systemLines };\n}\n\n/** Convert a Lynx user message to Anthropic content. */\nfunction translateUserContent(msg: LynxChatMessage): string | AnthropicContentBlock[] {\n if (typeof msg.content === \"string\") return msg.content;\n if (!msg.content || msg.content.length === 0) return \"\";\n\n const blocks: AnthropicContentBlock[] = [];\n for (const block of msg.content as LynxContentBlock[]) {\n if (block.type === \"text\" && block.text) {\n blocks.push({ type: \"text\", text: block.text });\n } else if (block.type === \"tool_result\") {\n blocks.push({\n type: \"tool_result\",\n tool_use_id: block.toolUseId ?? \"unknown\",\n content: typeof block.content === \"string\" ? block.content : JSON.stringify(block.content),\n is_error: block.isError,\n });\n }\n }\n return blocks.length > 0 ? blocks : \"\";\n}\n\n/** Convert a Lynx assistant message to Anthropic content. */\nfunction translateAssistantContent(msg: LynxChatMessage): string | AnthropicContentBlock[] {\n const blocks: AnthropicContentBlock[] = [];\n\n // Text blocks from content array\n if (Array.isArray(msg.content)) {\n for (const block of msg.content as LynxContentBlock[]) {\n if (block.type === \"text\" && block.text) {\n blocks.push({ type: \"text\", text: block.text });\n } else if (block.type === \"tool_use\" && block.id && block.name) {\n blocks.push({\n type: \"tool_use\",\n id: block.id,\n name: block.name,\n input: block.input ?? {},\n });\n }\n }\n } else if (typeof msg.content === \"string\" && msg.content) {\n blocks.push({ type: \"text\", text: msg.content });\n }\n\n // Tool calls from OpenAI format\n if (msg.tool_calls) {\n for (const tc of msg.tool_calls) {\n let input: Record<string, unknown> = {};\n try {\n input = JSON.parse(tc.function.arguments);\n } catch {\n input = {};\n }\n blocks.push({\n type: \"tool_use\",\n id: tc.id,\n name: tc.function.name,\n input,\n });\n }\n }\n\n return blocks.length > 0 ? blocks : \"\";\n}\n\n// ── Translation: Lynx tools → Anthropic tools ──────\n\n/** Convert Lynx/OpenAI‑format tool descriptors to Anthropic format. */\nfunction translateTools(tools: unknown[]): AnthropicTool[] {\n return tools.map((tool) => {\n const raw = tool as Record<string, unknown>;\n return {\n name: (raw.name as string) ?? \"unknown\",\n description: (raw.description as string) ?? \"\",\n input_schema:\n (raw.inputSchema as Record<string, unknown>) ??\n ((raw.function as Record<string, unknown>)?.parameters as Record<string, unknown>) ??\n {},\n };\n });\n}\n\n// ── SSE streaming parser ───────────────────────────\n\n/** State for tracking streaming content blocks. */\ninterface StreamState {\n activeToolCall: { id: string; name: string; buffer: string } | null;\n activeToolCallIndex: number;\n textIndex: number;\n toolUseCount: number;\n /** Set of block indices that are tool_use blocks (to detect when text blocks end). */\n toolUseIndices: Set<number>;\n}\n\n/**\n * Parse a single Anthropic SSE event line and return mapped Lynx events.\n * Anthropic SSE format: `event: <type>\\ndata: <json>\\n\\n`\n */\nfunction processAnthropicEvent(\n eventType: string,\n data: string,\n state: StreamState,\n): { events: StreamEvent[]; state: StreamState } {\n const events: StreamEvent[] = [];\n let nextState = state;\n\n if (eventType === \"message_start\") {\n return { events, state: nextState };\n }\n\n if (eventType === \"ping\") {\n return { events, state: nextState };\n }\n\n if (eventType === \"content_block_start\") {\n const parsed = JSON.parse(data) as {\n type: \"content_block_start\";\n index: number;\n content_block: { type: string; id?: string; name?: string };\n };\n const block = parsed.content_block;\n\n if (block.type === \"tool_use\" && block.id && block.name) {\n nextState = {\n ...state,\n activeToolCall: { id: block.id, name: block.name, buffer: \"\" },\n activeToolCallIndex: parsed.index,\n toolUseIndices: new Set(state.toolUseIndices).add(parsed.index),\n };\n events.push({ type: \"tool_use_start\", callId: block.id, name: block.name });\n }\n // text block start — no event needed, deltas carry the content\n return { events, state: nextState };\n }\n\n if (eventType === \"content_block_delta\") {\n const parsed = JSON.parse(data) as {\n type: \"content_block_delta\";\n index: number;\n delta: { type: string; text?: string; partial_json?: string; thinking?: string };\n };\n const delta = parsed.delta;\n\n if (delta.type === \"text_delta\" && delta.text) {\n events.push({\n type: \"text_delta\",\n index: state.textIndex++,\n text: delta.text,\n });\n } else if (delta.type === \"input_json_delta\" && delta.partial_json && state.activeToolCall) {\n nextState = {\n ...state,\n activeToolCall: {\n ...state.activeToolCall,\n buffer: state.activeToolCall.buffer + delta.partial_json,\n },\n };\n events.push({\n type: \"tool_use_delta\",\n callId: state.activeToolCall.id,\n text: delta.partial_json,\n });\n } else if (delta.type === \"thinking_delta\" && delta.thinking) {\n events.push({\n type: \"reasoning_delta\",\n index: 0,\n text: delta.thinking,\n });\n }\n\n return { events, state: nextState };\n }\n\n if (eventType === \"content_block_stop\") {\n // Parse accumulated JSON for active tool call on this block index\n const parsed = JSON.parse(data) as { type: \"content_block_stop\"; index: number };\n if (\n state.activeToolCall &&\n parsed.index === state.activeToolCallIndex &&\n state.activeToolCall.buffer\n ) {\n try {\n const input = JSON.parse(state.activeToolCall.buffer);\n events.push({\n type: \"tool_use_end\",\n callId: state.activeToolCall.id,\n input: input as Record<string, unknown>,\n });\n nextState = {\n ...state,\n toolUseCount: state.toolUseCount + 1,\n activeToolCall: null,\n };\n } catch {\n events.push({\n type: \"error\",\n code: \"TOOL_PARSE_ERROR\",\n message: `解析 ${state.activeToolCall.name} 的工具输入失败`,\n });\n }\n }\n return { events, state: nextState };\n }\n\n if (eventType === \"message_delta\") {\n return { events, state: nextState };\n }\n\n if (eventType === \"message_stop\") {\n events.push({ type: \"done\" });\n return { events, state: nextState };\n }\n\n return { events, state: nextState };\n}\n\n// ── Provider factory ───────────────────────────────\n\n/**\n * Create an Anthropic provider instance.\n *\n * Each call to `stream()` opens a fresh HTTP connection.\n * The provider itself is stateless — the API key is the only configuration.\n */\nexport function createAnthropicProvider(config: AnthropicConfig): LlmProvider {\n const baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;\n const timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const apiKey = config.apiKey;\n\n const provider: LlmProvider = {\n id: \"anthropic\",\n\n listModels(): ModelInfo[] {\n return MODELS;\n },\n\n getCapability(modelId: string): ProviderCapability {\n const cap = getCapability(\"anthropic\", modelId);\n if (!cap) {\n throw new LlmError(`未知 Anthropic 模型: ${modelId}`, {\n category: \"llm\",\n recoverable: false,\n retryable: false,\n diagnosticHint: \"llm_unknown_model\",\n });\n }\n return cap;\n },\n\n async *stream(\n ...[modelId, messages, systemPrompt, tools, signal]: Parameters<LlmProvider[\"stream\"]>\n ): AsyncGenerator<StreamEvent, void, void> {\n // Translate system prompt + system messages → Anthropic system parameter\n const translated = translateMessages(messages as LynxChatMessage[]);\n const allSystemLines = systemPrompt\n ? [systemPrompt, ...translated.systemLines]\n : translated.systemLines;\n const systemParam = allSystemLines.length > 0 ? allSystemLines.join(\"\\n\\n\") : undefined;\n\n const body = {\n model: modelId,\n max_tokens: 8192,\n messages: translated.messages,\n ...(systemParam ? { system: systemParam } : {}),\n ...(tools.length > 0 ? { tools: translateTools(tools) } : {}),\n stream: true,\n };\n\n async function makeRequest(): Promise<Response> {\n const controller = new AbortController();\n const onAbort = () => controller.abort();\n signal.addEventListener(\"abort\", onAbort, { once: true });\n\n try {\n const response = await fetch(`${baseUrl}/v1/messages`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-api-key\": apiKey,\n \"anthropic-version\": ANTHROPIC_VERSION,\n },\n body: JSON.stringify(body),\n signal: AbortSignal.any\n ? AbortSignal.any([signal, controller.signal])\n : controller.signal,\n });\n\n if (!response.ok) {\n await handleHttpError(response);\n }\n\n return response;\n } finally {\n signal.removeEventListener(\"abort\", onAbort);\n }\n }\n\n const response = await withRetry(makeRequest, {\n baseMs: 500,\n maxMs: 32_000,\n });\n\n if (!response.body) {\n throw new LlmError(\"Anthropic 返回了空响应体\", {\n category: \"llm\",\n recoverable: true,\n retryable: true,\n });\n }\n\n // Parse Anthropic SSE stream\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n let eventType = \"\";\n let state: StreamState = {\n activeToolCall: null,\n activeToolCallIndex: -1,\n textIndex: 0,\n toolUseCount: 0,\n toolUseIndices: new Set(),\n };\n\n try {\n while (true) {\n if (signal.aborted) break;\n\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n\n // Process complete SSE events — Anthropic uses two-line format:\n // event: <type>\n // data: <json>\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() ?? \"\"; // keep incomplete line\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (trimmed.startsWith(\"event: \")) {\n eventType = trimmed.slice(7).trim();\n } else if (trimmed.startsWith(\"data: \") && eventType) {\n const data = trimmed.slice(6).trim();\n const result = processAnthropicEvent(eventType, data, state);\n for (const event of result.events) yield event;\n state = result.state;\n }\n }\n }\n\n // Stream ended without message_stop\n if (state.activeToolCall && state.activeToolCall.buffer) {\n try {\n const input = JSON.parse(state.activeToolCall.buffer);\n yield {\n type: \"tool_use_end\",\n callId: state.activeToolCall.id,\n input: input as Record<string, unknown>,\n };\n } catch {\n // ignore — partial tool call at stream end\n }\n }\n yield { type: \"done\" };\n } finally {\n reader.releaseLock();\n }\n },\n };\n\n return provider;\n}\n\n// ── HTTP error mapper ──────────────────────────────\n\nasync function handleHttpError(response: Response): Promise<never> {\n let body: string;\n try {\n body = await response.text();\n } catch {\n body = \"\";\n }\n\n const status = response.status;\n\n if (status === 401 || status === 403) {\n throw new LlmAuthError(`Anthropic 认证失败 (${status}): ${body}`);\n }\n\n if (status === 429) {\n throw new LlmRateLimitError(`Anthropic 速率限制 (429): ${body}`);\n }\n\n if (status === 529) {\n const err = new LlmRateLimitError(`Anthropic 过载 (529): ${body}`);\n (err as unknown as Record<string, unknown>).status = 529;\n throw err;\n }\n\n if (status === 400 && body.includes(\"context\")) {\n throw new LlmContextOverflowError(`Anthropic 上下文溢出: ${body}`);\n }\n\n throw new LlmError(`Anthropic HTTP ${status}: ${body}`, {\n category: \"llm\",\n recoverable: status >= 500,\n retryable: status >= 500 || status === 429,\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["anyErr","DEFAULT_BASE_URL","MODELS","flushActiveToolCall","processToolCallDeltas","processSseLine","handleHttpError","DEFAULT_BASE_URL","MODELS","handleHttpError"],"sources":["../src/stream.ts","../src/retry.ts","../src/capabilities.ts","../src/fallback.ts","../src/tool-format.ts","../src/providers/deepseek.ts","../src/providers/openai.ts","../src/providers/anthropic.ts"],"sourcesContent":["/**\n * Stream\\<T\\> — pull‑based AsyncIterator backed by an internal queue.\n *\n * Producers call `enqueue()` / `done()` / `error()`.\n * Consumers use `for await … of`.\n *\n * This is deliberately a class (not EventEmitter) so the consumer\n * controls back‑pressure — when nobody is iterating, values stay in\n * the queue until `maxQueueSize` is reached.\n */\n\n// ── Constants ──────────────────────────────────────\n\n/** Drop the oldest item when the queue exceeds this size. */\nconst DEFAULT_MAX_QUEUE = 256;\n\n// ── Public API ─────────────────────────────────────\n\n/**\n * A cold, single‑consumer async iterator.\n *\n * ```ts\n * const stream = new Stream<string>();\n * producer(stream);\n * for await (const chunk of stream) {\n * console.log(chunk);\n * }\n * ```\n */\nexport class Stream<T> implements AsyncIterator<T>, AsyncIterable<T> {\n private _queue: T[] = [];\n private _resolvers: Array<(value: IteratorResult<T>) => void> = [];\n private _done = false;\n private _error: Error | null = null;\n private readonly _maxQueue: number;\n\n constructor(maxQueueSize = DEFAULT_MAX_QUEUE) {\n this._maxQueue = maxQueueSize;\n }\n\n // ── Producer API ────────────────────────────────\n\n /** Push a value into the stream. */\n enqueue(value: T): void {\n if (this._done || this._error) return;\n\n if (this._resolvers.length > 0) {\n const resolve = this._resolvers.shift()!;\n resolve({ value, done: false });\n } else {\n this._queue.push(value);\n // Prevent unbounded growth\n if (this._queue.length > this._maxQueue) {\n this._queue.shift();\n }\n }\n }\n\n /** Signal that the stream has finished normally. */\n done(): void {\n if (this._done) return;\n this._done = true;\n // Resolve all pending awaiters with done: true\n for (const resolve of this._resolvers) {\n resolve({ value: undefined, done: true });\n }\n this._resolvers = [];\n }\n\n /** Propagate an error to the consumer. */\n error(err: Error): void {\n if (this._done || this._error) return;\n this._error = err;\n this._done = true;\n // The error is thrown from next(), but we can't throw through pending\n // resolvers. We resolve them with done: true and set the error flag\n // so the next next() call will throw.\n for (const resolve of this._resolvers) {\n resolve({ value: undefined, done: true });\n }\n this._resolvers = [];\n }\n\n // ── Consumer API ────────────────────────────────\n\n [Symbol.asyncIterator](): AsyncIterator<T> {\n return this;\n }\n\n next(): Promise<IteratorResult<T>> {\n // If the stream errored, reject\n if (this._error) {\n return Promise.reject(this._error);\n }\n\n // If we're done and the queue is empty, return done\n if (this._done && this._queue.length === 0) {\n return Promise.resolve({ value: undefined, done: true });\n }\n\n // If there's a queued value, return it immediately\n if (this._queue.length > 0) {\n const value = this._queue.shift()!;\n return Promise.resolve({ value, done: false });\n }\n\n // Wait for the next enqueue/done/error\n return new Promise((resolve) => {\n this._resolvers.push(resolve);\n });\n }\n}\n","/**\n * withRetry — exponential backoff with jitter and Retry‑After respect.\n *\n * Used by every LLM provider for transient errors (429, 529, ECONNRESET, etc.).\n *\n * Algorithm:\n * delay = min(baseMs * 2^(attempt-1), maxMs)\n * jitter = delay * uniform(0.75, 1.25)\n * final = min(jitter, maxMs)\n */\n\nimport { LlmError, LlmRateLimitError } from \"@24klynx/core\";\n\n// ── Constants ──────────────────────────────────────\n\nconst BASE_DELAY_MS = 500;\nconst MAX_DELAY_MS = 32_000;\nconst MAX_RETRIES = 10;\n\n// ── Types ──────────────────────────────────────────\n\nexport interface RetryOptions {\n /** Base delay for the first retry (default 500ms). */\n baseMs?: number;\n /** Hard ceiling on delay (default 32s). */\n maxMs?: number;\n /** Maximum number of retries before giving up (default 10). */\n maxRetries?: number;\n /** Function that returns true if this error is retryable. */\n isRetryable?: (err: Error) => boolean;\n /**\n * Whether this request is in the foreground (user-facing).\n * Foreground requests retry on 529; background requests discard\n * immediately to prevent cascade amplification. Defaults to true.\n */\n isForeground?: boolean;\n}\n\n// ── Helpers ────────────────────────────────────────\n\n/** Uniform random in [lo, hi]. */\nfunction jitter(lo: number, hi: number): number {\n return lo + Math.random() * (hi - lo);\n}\n\n/** Extract Retry‑After seconds from HTTP headers if present. */\nfunction parseRetryAfter(err: Error): number | null {\n const anyErr = err as Error & { headers?: unknown };\n const headers = anyErr.headers as Record<string, string> | undefined;\n if (!headers) return null;\n\n const raw = headers[\"retry-after\"];\n if (!raw) return null;\n\n // Try integer seconds first\n const secs = Number.parseInt(raw, 10);\n if (!Number.isNaN(secs)) return secs;\n\n // Try HTTP‑date (rare but valid)\n const parsed = Date.parse(raw);\n if (!Number.isNaN(parsed)) {\n return Math.max(0, Math.ceil((parsed - Date.now()) / 1000));\n }\n\n return null;\n}\n\n// ── Public API ─────────────────────────────────────\n\n/**\n * Execute `fn` with exponential backoff.\n *\n * Only retries on transient errors. Non‑retryable errors are re‑thrown\n * immediately.\n *\n * ```ts\n * const result = await withRetry(() => fetchFromApi(), {\n * baseMs: 500,\n * maxMs: 32_000,\n * });\n * ```\n */\nexport async function withRetry<T>(fn: () => Promise<T>, opts: RetryOptions = {}): Promise<T> {\n const baseMs = opts.baseMs ?? BASE_DELAY_MS;\n const maxMs = opts.maxMs ?? MAX_DELAY_MS;\n const maxRetries = opts.maxRetries ?? MAX_RETRIES;\n const isRetryable =\n opts.isRetryable ??\n ((err) => {\n if (err instanceof LlmRateLimitError) return true;\n // Always retry transient errors\n if (err instanceof LlmError && err.retryable) return true;\n // Retry network errors\n const code = (err as NodeJS.ErrnoException).code;\n if (code === \"ECONNRESET\" || code === \"EPIPE\" || code === \"ETIMEDOUT\") return true;\n return false;\n });\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n return await fn();\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n\n // If this is the last attempt or the error is not retryable, give up\n if (attempt === maxRetries || !isRetryable(error)) {\n throw error;\n }\n\n // 529 overload — only retry if this is in the foreground.\n // Background tasks drop to prevent cascade amplification.\n const anyErr = error as Error & { status?: number };\n if (anyErr.status === 529 && opts.isForeground === false) {\n throw error;\n }\n\n // Respect Retry‑After header\n const retryAfter = parseRetryAfter(error);\n const delay = retryAfter ? retryAfter * 1000 : Math.min(baseMs * Math.pow(2, attempt), maxMs);\n\n const jittered = Math.min(delay * jitter(0.75, 1.25), maxMs);\n\n await new Promise((resolve) => setTimeout(resolve, jittered));\n }\n }\n\n // Unreachable — the loop above always throws or returns\n throw new LlmError(\"withRetry exhausted all attempts\", {\n category: \"llm\",\n recoverable: false,\n retryable: false,\n diagnosticHint: \"llm_retry_exhausted\",\n });\n}\n","/**\n * Provider capability metadata table.\n *\n * Every model known to Lynx is registered here with its static capabilities.\n * The router uses this table to decide which model can handle a request\n * (e.g. a request with images requires `supportsVision: true`).\n */\n\nimport type { ProviderCapability } from \"./types.js\";\n\n/**\n * Lookup table keyed by \"provider/modelId\".\n *\n * New models can be added here without code changes —\n * capabilities are pure data, not behaviour.\n */\nconst CAPABILITIES: Record<string, ProviderCapability> = {\n // DeepSeek\n \"deepseek/deepseek-chat\": {\n contextWindow: 128_000,\n maxOutput: 8_192,\n supportsReasoning: false,\n supportsStreaming: true,\n supportsToolUse: true,\n supportsVision: false,\n promptCacheEnabled: true, // implicit prefix caching\n },\n \"deepseek/deepseek-reasoner\": {\n contextWindow: 128_000,\n maxOutput: 8_192,\n supportsReasoning: true,\n supportsStreaming: true,\n supportsToolUse: false,\n supportsVision: false,\n promptCacheEnabled: true,\n },\n\n // OpenAI\n \"openai/gpt-4o\": {\n contextWindow: 128_000,\n maxOutput: 16_384,\n supportsReasoning: false,\n supportsStreaming: true,\n supportsToolUse: true,\n supportsVision: true,\n promptCacheEnabled: false, // No prompt caching for OpenAI (as of now)\n },\n \"openai/gpt-4o-mini\": {\n contextWindow: 128_000,\n maxOutput: 16_384,\n supportsReasoning: false,\n supportsStreaming: true,\n supportsToolUse: true,\n supportsVision: true,\n promptCacheEnabled: false,\n },\n \"openai/o3-mini\": {\n contextWindow: 200_000,\n maxOutput: 100_000,\n supportsReasoning: true,\n supportsStreaming: true,\n supportsToolUse: false,\n supportsVision: false,\n promptCacheEnabled: false,\n },\n\n // Anthropic\n \"anthropic/claude-opus-4-8\": {\n contextWindow: 200_000,\n maxOutput: 32_000,\n supportsReasoning: true,\n supportsStreaming: true,\n supportsToolUse: true,\n supportsVision: true,\n promptCacheEnabled: true,\n },\n \"anthropic/claude-sonnet-4-6\": {\n contextWindow: 200_000,\n maxOutput: 16_384,\n supportsReasoning: true,\n supportsStreaming: true,\n supportsToolUse: true,\n supportsVision: true,\n promptCacheEnabled: true,\n },\n \"anthropic/claude-haiku-4-5\": {\n contextWindow: 200_000,\n maxOutput: 8_192,\n supportsReasoning: false,\n supportsStreaming: true,\n supportsToolUse: true,\n supportsVision: true,\n promptCacheEnabled: true,\n },\n};\n\n/**\n * Return the capability blob for `provider/modelId`.\n * Returns `undefined` for unknown models — callers should fall back\n * or throw a {@link import('@24klynx/core').ConfigError}.\n */\nexport function getCapability(providerId: string, modelId: string): ProviderCapability | undefined {\n return CAPABILITIES[`${providerId}/${modelId}`];\n}\n\n/**\n * Return every known capability entry.\n * Used by the model picker UI in the TUI layer.\n */\nexport function listCapabilities(): Array<{ key: string } & ProviderCapability> {\n return Object.entries(CAPABILITIES).map(([key, cap]) => ({ key, ...cap }));\n}\n","/**\n * Model/Provider fallback logic.\n *\n * When a provider returns a transient error (rate‑limit, overload) the\n * fallback engine tries the next candidate in the configured chain.\n * Permanent errors (auth) skip the provider entirely and move to the next.\n *\n * The primary use‑case is keeping the agent loop running when the\n * preferred model is temporarily unavailable.\n */\n\nimport { LlmRateLimitError, LlmAuthError } from \"@24klynx/core\";\n\n// ── Types ────────────────────────────────────────────\n\n/** A candidate provider+model pair in the fallback chain. */\nexport interface ModelCandidate {\n provider: string;\n model: string;\n}\n\n/** Record of a single fallback attempt for diagnostics. */\nexport interface FallbackAttempt {\n provider: string;\n model: string;\n error: string;\n code?: string;\n /** HTTP status code if available. */\n status?: number;\n}\n\n/** Result of a fallback run — which candidate succeeded and what was tried. */\nexport interface FallbackResult<T> {\n result: T;\n provider: string;\n model: string;\n attempts: FallbackAttempt[];\n}\n\n/** Error thrown when every candidate in the chain has been exhausted. */\nexport class FallbackExhaustedError extends Error {\n readonly attempts: FallbackAttempt[];\n\n constructor(attempts: FallbackAttempt[]) {\n const summary = attempts.map((a) => ` ${a.provider}/${a.model}: ${a.error}`).join(\"\\n\");\n super(`All fallback candidates exhausted:\\n${summary}`);\n this.name = \"FallbackExhaustedError\";\n this.attempts = attempts;\n }\n}\n\n// ── Helpers ──────────────────────────────────────────\n\n/**\n * Determine whether an error is transient (worth retrying with a\n * different provider) or permanent (skip this provider).\n */\nfunction isTransient(err: unknown): boolean {\n if (err instanceof LlmRateLimitError) return true;\n if (err instanceof LlmAuthError) return false;\n // Network errors are transient\n if (err instanceof Error && \"code\" in err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === \"ECONNRESET\" || code === \"EPIPE\" || code === \"ETIMEDOUT\" || code === \"ENOTFOUND\") {\n return true;\n }\n }\n // Default: treat unknown errors as transient (safer to try another provider)\n return true;\n}\n\n// ── Public API ───────────────────────────────────────\n\n/**\n * Run `fn` with automatic fallback across a chain of model candidates.\n *\n * `fn` is called with (provider, model) and should return a result.\n * If `fn` throws, the error is classified — transient errors move to the\n * next candidate, permanent errors skip the provider, and fatal errors\n * (like user abort) are re‑thrown immediately.\n */\nexport async function runWithFallback<T>(\n candidates: ModelCandidate[],\n fn: (candidate: ModelCandidate) => Promise<T>,\n opts?: { signal?: AbortSignal },\n): Promise<FallbackResult<T>> {\n if (candidates.length === 0) {\n throw new FallbackExhaustedError([]);\n }\n\n const attempts: FallbackAttempt[] = [];\n\n for (const candidate of candidates) {\n // Respect external abort signal\n if (opts?.signal?.aborted) {\n throw new DOMException(\"Aborted\", \"AbortError\");\n }\n\n try {\n const result = await fn(candidate);\n return { result, provider: candidate.provider, model: candidate.model, attempts };\n } catch (err) {\n // User‑initiated abort — stop immediately\n if (err instanceof DOMException && err.name === \"AbortError\") {\n throw err;\n }\n\n const message = err instanceof Error ? err.message : String(err);\n const status = (err as Record<string, unknown>).status as number | undefined;\n const code = (err as Record<string, unknown>).code as string | undefined;\n\n attempts.push({\n provider: candidate.provider,\n model: candidate.model,\n error: message,\n code,\n status,\n });\n\n // Permanent errors — skip this provider, try next\n if (!isTransient(err)) {\n continue;\n }\n\n // Transient — try next candidate\n continue;\n }\n }\n\n throw new FallbackExhaustedError(attempts);\n}\n\n/**\n * Build a fallback chain from a primary model and a list of fallback refs.\n *\n * Fallback refs use the format `\"provider/model\"` (e.g. `\"openai/gpt-4o-mini\"`).\n * If the provider prefix is omitted the primary's provider is used.\n */\nexport function buildCandidateChain(\n primaryProvider: string,\n primaryModel: string,\n fallbackRefs: string[],\n): ModelCandidate[] {\n const chain: ModelCandidate[] = [{ provider: primaryProvider, model: primaryModel }];\n\n for (const ref of fallbackRefs) {\n const slashIdx = ref.indexOf(\"/\");\n if (slashIdx > 0) {\n chain.push({ provider: ref.slice(0, slashIdx), model: ref.slice(slashIdx + 1) });\n } else {\n chain.push({ provider: primaryProvider, model: ref });\n }\n }\n\n return chain;\n}\n","/**\n * Wire-format normalization for OpenAI-compatible APIs.\n *\n * Lynx uses its own internal types ({@link import('@24klynx/tools').ToolDescriptor},\n * {@link import('@24klynx/core').ContentBlock}) that don't exactly match the\n * OpenAI/DeepSeek wire format. This module maps between them.\n */\n\nimport type { ChatMessage } from \"./types.js\";\n\n// ── Tool normalization ────────────────────────────\n\n/** Raw shape we receive from the agent (ToolDescriptor-like). */\ninterface RawTool {\n name?: unknown;\n description?: unknown;\n inputSchema?: unknown;\n}\n\n/** OpenAI-compatible tool definition. */\ninterface OpenAiTool {\n type: \"function\";\n function: {\n name: string;\n description: string;\n parameters: Record<string, unknown>;\n };\n}\n\n/**\n * Convert Lynx tool descriptors to OpenAI-compatible format.\n *\n * Wraps each tool with `type: \"function\"` and maps `inputSchema` → `parameters`.\n * Tools already in OpenAI format pass through unchanged.\n */\nexport function normalizeOpenAiTools(tools: unknown[]): OpenAiTool[] {\n return tools.map((tool) => {\n const raw = tool as RawTool;\n if (typeof raw === \"object\" && raw !== null && (raw as { type?: string }).type === \"function\") {\n return tool as OpenAiTool;\n }\n return {\n type: \"function\" as const,\n function: {\n name: String(raw.name ?? \"unknown\"),\n description: String(raw.description ?? \"\"),\n parameters: (raw.inputSchema as Record<string, unknown>) ?? {},\n },\n };\n });\n}\n\n// ── Wire-format message types ─────────────────────\n\n/** A message in OpenAI wire format — what the API actually expects. */\nexport interface OpenAiMessage {\n role: \"system\" | \"user\" | \"assistant\" | \"tool\";\n content: string | OpenAiContentBlock[] | null;\n tool_call_id?: string;\n tool_calls?: Array<{\n id: string;\n type: \"function\";\n function: { name: string; arguments: string };\n }>;\n}\n\ninterface OpenAiContentBlock {\n type: \"text\";\n text: string;\n}\n\n// ── Content block types for recognition ───────────\n\ninterface LynxContentBlock {\n type: string;\n text?: string;\n id?: string;\n name?: string;\n input?: Record<string, unknown>;\n toolUseId?: string;\n content?: string;\n isError?: boolean;\n}\n\n// ── Message normalization ─────────────────────────\n\n/**\n * Convert Lynx ChatMessage[] to OpenAI wire-format messages.\n *\n * Key transformations:\n * - `tool_result` content blocks → `role: \"tool\"` messages with string content\n * - `tool_use` blocks in assistant messages → `tool_calls` array\n * - `reasoning` blocks are stripped (not sent back to the API)\n */\nexport function normalizeOpenAiMessages(messages: ChatMessage[]): OpenAiMessage[] {\n const out: OpenAiMessage[] = [];\n\n for (const msg of messages) {\n // Simple string content — pass through with role\n if (typeof msg.content === \"string\") {\n out.push({ role: msg.role as OpenAiMessage[\"role\"], content: msg.content });\n continue;\n }\n\n const blocks = msg.content as LynxContentBlock[];\n\n // Check for tool_result blocks → convert to tool-role messages\n const toolResults = blocks.filter((b) => b.type === \"tool_result\");\n const nonToolBlocks = blocks.filter((b) => b.type !== \"tool_result\");\n\n // Emit tool result messages\n for (const tr of toolResults) {\n out.push({\n role: \"tool\",\n tool_call_id: tr.toolUseId ?? \"unknown\",\n content: tr.content ?? \"\",\n });\n }\n\n // Handle remaining blocks by role\n if (nonToolBlocks.length > 0) {\n const normalized = normalizeContentBlocks(nonToolBlocks, msg.role);\n\n if (msg.role === \"assistant\") {\n const textBlocks = normalized.filter((b) => b.type === \"text\");\n const toolUses = blocks.filter((b) => b.type === \"tool_use\");\n const assistantMsg: OpenAiMessage = {\n role: \"assistant\",\n content: textBlocks.length > 0 ? textBlocks : null,\n };\n if (toolUses.length > 0) {\n assistantMsg.tool_calls = toolUses.map((tu) => ({\n id: tu.id ?? \"unknown\",\n type: \"function\" as const,\n function: {\n name: tu.name ?? \"unknown\",\n arguments: JSON.stringify(tu.input ?? {}),\n },\n }));\n }\n out.push(assistantMsg);\n } else {\n // User or system message — strip unrecognized blocks, keep text\n const textBlocks = normalized.filter((b) => b.type === \"text\");\n if (textBlocks.length > 0) {\n out.push({\n role: msg.role as OpenAiMessage[\"role\"],\n content:\n textBlocks.length === 1 && msg.role !== \"system\"\n ? (textBlocks[0]!.text ?? \"\")\n : textBlocks,\n });\n }\n }\n }\n }\n\n return out;\n}\n\n/** Keep only text blocks, strip reasoning/tool_use/tool_result from content. */\nfunction normalizeContentBlocks(blocks: LynxContentBlock[], _role: string): OpenAiContentBlock[] {\n return blocks\n .filter((b) => b.type === \"text\" && typeof b.text === \"string\")\n .map((b) => ({ type: \"text\" as const, text: b.text! }));\n}\n","/**\n * DeepSeek provider — SSE streaming via the /v1/chat/completions endpoint.\n *\n * DeepSeek is API‑compatible with OpenAI so the wire format is the same.\n * The key difference: DeepSeek has implicit prefix caching on every request\n * (no cache_control blocks needed).\n */\n\nimport { LlmError, LlmRateLimitError, LlmAuthError, LlmContextOverflowError } from \"@24klynx/core\";\nimport type { LlmProvider, ModelInfo, ProviderCapability, StreamEvent } from \"../types.js\";\nimport { getCapability } from \"../capabilities.js\";\nimport { withRetry } from \"../retry.js\";\nimport { normalizeOpenAiTools, normalizeOpenAiMessages } from \"../tool-format.js\";\n\n// ── Configuration ──────────────────────────────────\n\nexport interface DeepSeekConfig {\n /** API key read from auth store. */\n apiKey: string;\n /** Base URL (defaults to https://api.deepseek.com). */\n baseUrl?: string;\n /** Request timeout in ms (default 120s). */\n timeoutMs?: number;\n}\n\nconst DEFAULT_BASE_URL = \"https://api.deepseek.com\";\nconst DEFAULT_TIMEOUT_MS = 120_000;\n\n// ── Models ─────────────────────────────────────────\n\nconst MODELS: ModelInfo[] = [\n { id: \"deepseek-chat\", label: \"DeepSeek Chat (V3)\", contextWindow: 128_000, maxOutput: 8_192 },\n {\n id: \"deepseek-reasoner\",\n label: \"DeepSeek Reasoner (R1)\",\n contextWindow: 128_000,\n maxOutput: 8_192,\n },\n];\n\n// ── Helpers ────────────────────────────────────────\n\n/** Flush active tool call buffer, yielding tool_use_end or error events. */\nfunction flushActiveToolCall(call: { id: string; name: string; buffer: string }): StreamEvent[] {\n try {\n const input = JSON.parse(call.buffer);\n return [{ type: \"tool_use_end\", callId: call.id, input }];\n } catch {\n return [\n {\n type: \"error\",\n code: \"TOOL_PARSE_ERROR\",\n message: `Failed to parse tool input for ${call.name}`,\n },\n ];\n }\n}\n\n/** Process tool_calls delta array, returning events and updated active call state. */\nfunction processToolCallDeltas(\n toolCalls: Array<Record<string, unknown>>,\n activeCall: { id: string; name: string; buffer: string } | null,\n): { events: StreamEvent[]; activeCall: typeof activeCall } {\n const events: StreamEvent[] = [];\n let currentCall = activeCall;\n\n for (const tc of toolCalls) {\n const tcId = tc.id as string | undefined;\n const fn = tc.function as { name?: string; arguments?: string } | undefined;\n\n if (tcId && fn?.name) {\n // Flush previous tool call if any (silently ignore parse failures)\n if (currentCall) {\n try {\n const input = JSON.parse(currentCall.buffer);\n events.push({ type: \"tool_use_end\", callId: currentCall.id, input });\n } catch {\n // ignore parse failure, keep accumulating\n }\n }\n currentCall = { id: tcId, name: fn.name, buffer: \"\" };\n events.push({ type: \"tool_use_start\", callId: tcId, name: fn.name });\n }\n\n if (fn?.arguments && currentCall) {\n currentCall.buffer += fn.arguments;\n events.push({ type: \"tool_use_delta\", callId: currentCall.id, text: fn.arguments });\n }\n }\n\n return { events, activeCall: currentCall };\n}\n\n/** SSE stream processing state. */\ninterface SseState {\n activeToolCall: { id: string; name: string; buffer: string } | null;\n textIndex: number;\n reasoningIndex: number;\n}\n\n/** Process a single SSE line, returning events to yield and updated state. */\nfunction processSseLine(\n line: string,\n state: SseState,\n): { events: StreamEvent[]; state: SseState; done: boolean } {\n const trimmed = line.trim();\n if (!trimmed || !trimmed.startsWith(\"data:\")) {\n return { events: [], state, done: false };\n }\n\n const data = trimmed.slice(5).trim();\n\n if (data === \"[DONE]\") {\n const events: StreamEvent[] = [];\n if (state.activeToolCall) {\n events.push(...flushActiveToolCall(state.activeToolCall));\n }\n events.push({ type: \"done\" });\n return { events, state: { ...state, activeToolCall: null }, done: true };\n }\n\n let chunk: { choices?: Array<{ delta?: Record<string, unknown> }> };\n try {\n chunk = JSON.parse(data);\n } catch {\n return { events: [], state, done: false };\n }\n\n const choice = chunk.choices?.[0];\n if (!choice?.delta) {\n return { events: [], state, done: false };\n }\n\n const delta = choice.delta;\n const events: StreamEvent[] = [];\n let { textIndex, reasoningIndex, activeToolCall } = state;\n\n // Reasoning content (DeepSeek R1)\n if (delta.reasoning_content && typeof delta.reasoning_content === \"string\") {\n events.push({\n type: \"reasoning_delta\",\n index: reasoningIndex++,\n text: delta.reasoning_content,\n });\n }\n\n // Regular text content\n if (delta.content && typeof delta.content === \"string\") {\n events.push({\n type: \"text_delta\",\n index: textIndex++,\n text: delta.content,\n });\n }\n\n // Tool call handling\n if (delta.tool_calls) {\n const tcResult = processToolCallDeltas(\n delta.tool_calls as Array<Record<string, unknown>>,\n activeToolCall,\n );\n events.push(...tcResult.events);\n activeToolCall = tcResult.activeCall;\n }\n\n return { events, state: { textIndex, reasoningIndex, activeToolCall }, done: false };\n}\n\n// ── Provider ───────────────────────────────────────\n\n/**\n * Create a DeepSeek provider instance.\n *\n * Each call to `stream()` opens a fresh HTTP connection.\n * The provider itself is stateless — the API key is the only configuration.\n */\nexport function createDeepSeekProvider(config: DeepSeekConfig): LlmProvider {\n const baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;\n const _timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n\n const provider: LlmProvider = {\n id: \"deepseek\",\n\n listModels(): ModelInfo[] {\n return MODELS;\n },\n\n getCapability(modelId: string): ProviderCapability {\n const cap = getCapability(\"deepseek\", modelId);\n if (!cap) {\n throw new LlmError(`Unknown DeepSeek model: ${modelId}`, {\n category: \"llm\",\n recoverable: false,\n retryable: false,\n diagnosticHint: \"llm_unknown_model\",\n });\n }\n return cap;\n },\n\n async *stream(\n ...[modelId, messages, systemPrompt, tools, signal]: Parameters<LlmProvider[\"stream\"]>\n ): AsyncGenerator<StreamEvent, void, void> {\n const systemMessages = systemPrompt ? [{ role: \"system\", content: systemPrompt }] : [];\n\n const body = {\n model: modelId,\n messages: [...systemMessages, ...normalizeOpenAiMessages(messages)],\n stream: true,\n ...(tools.length > 0 ? { tools: normalizeOpenAiTools(tools) } : {}),\n };\n\n async function makeRequest(): Promise<Response> {\n const controller = new AbortController();\n const onAbort = () => controller.abort();\n signal.addEventListener(\"abort\", onAbort, { once: true });\n\n try {\n const response = await fetch(`${baseUrl}/v1/chat/completions`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${config.apiKey}`,\n },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n await handleHttpError(response);\n }\n\n return response;\n } finally {\n signal.removeEventListener(\"abort\", onAbort);\n }\n }\n\n const response = await withRetry(makeRequest, {\n baseMs: 500,\n maxMs: 32_000,\n });\n\n if (!response.body) {\n throw new LlmError(\"DeepSeek returned empty response body\", {\n category: \"llm\",\n recoverable: true,\n retryable: true,\n });\n }\n\n // Parse SSE stream\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n let textIndex = 0;\n let reasoningIndex = 0;\n let activeToolCall: { id: string; name: string; buffer: string } | null = null;\n\n try {\n while (true) {\n if (signal.aborted) break;\n\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n\n // Process complete SSE lines\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() ?? \"\"; // keep incomplete line in buffer\n\n for (const line of lines) {\n const result = processSseLine(line, { activeToolCall, textIndex, reasoningIndex });\n for (const event of result.events) yield event;\n if (result.done) return;\n activeToolCall = result.state.activeToolCall;\n textIndex = result.state.textIndex;\n reasoningIndex = result.state.reasoningIndex;\n }\n }\n\n // Stream ended without [DONE]\n if (activeToolCall) {\n for (const event of flushActiveToolCall(activeToolCall)) yield event;\n }\n yield { type: \"done\" };\n } finally {\n reader.releaseLock();\n }\n },\n };\n\n return provider;\n}\n\n// ── HTTP error mapper ──────────────────────────────\n\nasync function handleHttpError(response: Response): Promise<never> {\n let body: string;\n try {\n body = await response.text();\n } catch {\n body = \"\";\n }\n\n const status = response.status;\n\n if (status === 401 || status === 403) {\n throw new LlmAuthError(`DeepSeek auth failed (${status}): ${body}`);\n }\n\n if (status === 429) {\n throw new LlmRateLimitError(`DeepSeek rate limited (429): ${body}`);\n }\n\n if (status === 529) {\n const err = new LlmRateLimitError(`DeepSeek overloaded (529): ${body}`);\n (err as unknown as Record<string, unknown>).status = 529;\n throw err;\n }\n\n if (status === 400 && body.includes(\"context\")) {\n throw new LlmContextOverflowError(`DeepSeek context overflow: ${body}`);\n }\n\n throw new LlmError(`DeepSeek HTTP ${status}: ${body}`, {\n category: \"llm\",\n recoverable: status >= 500,\n retryable: status >= 500 || status === 429,\n });\n}\n","/**\n * OpenAI provider — SSE streaming via the /v1/chat/completions endpoint.\n *\n * The API shape is identical to DeepSeek (both follow the OpenAI spec),\n * but the provider metadata and defaults differ.\n */\n\nimport { LlmError, LlmRateLimitError, LlmAuthError, LlmContextOverflowError } from \"@24klynx/core\";\nimport type { LlmProvider, ModelInfo, ProviderCapability, StreamEvent } from \"../types.js\";\nimport { getCapability } from \"../capabilities.js\";\nimport { withRetry } from \"../retry.js\";\nimport { normalizeOpenAiTools, normalizeOpenAiMessages } from \"../tool-format.js\";\n\n// ── Configuration ──────────────────────────────────\n\nexport interface OpenAiConfig {\n apiKey: string;\n baseUrl?: string;\n timeoutMs?: number;\n}\n\nconst DEFAULT_BASE_URL = \"https://api.openai.com\";\nconst _DEFAULT_TIMEOUT_MS = 120_000;\n\n// ── Models ─────────────────────────────────────────\n\nconst MODELS: ModelInfo[] = [\n { id: \"gpt-4o\", label: \"GPT-4o\", contextWindow: 128_000, maxOutput: 16_384 },\n { id: \"gpt-4o-mini\", label: \"GPT-4o Mini\", contextWindow: 128_000, maxOutput: 16_384 },\n { id: \"o3-mini\", label: \"o3-mini\", contextWindow: 200_000, maxOutput: 100_000 },\n];\n\n// ── Helpers ────────────────────────────────────────\n\n/** Flush active tool call buffer, yielding tool_use_end or error events. */\nfunction flushActiveToolCall(call: { id: string; name: string; buffer: string }): StreamEvent[] {\n try {\n const input = JSON.parse(call.buffer);\n return [{ type: \"tool_use_end\", callId: call.id, input }];\n } catch {\n return [\n {\n type: \"error\",\n code: \"TOOL_PARSE_ERROR\",\n message: `Failed to parse tool input for ${call.name}`,\n },\n ];\n }\n}\n\n/** Process tool_calls delta array, returning events and updated active call state. */\nfunction processToolCallDeltas(\n toolCalls: Array<Record<string, unknown>>,\n activeCall: { id: string; name: string; buffer: string } | null,\n): { events: StreamEvent[]; activeCall: typeof activeCall } {\n const events: StreamEvent[] = [];\n let currentCall = activeCall;\n\n for (const tc of toolCalls) {\n const tcId = tc.id as string | undefined;\n const fn = tc.function as { name?: string; arguments?: string } | undefined;\n\n if (tcId && fn?.name) {\n if (currentCall) {\n try {\n const input = JSON.parse(currentCall.buffer);\n events.push({ type: \"tool_use_end\", callId: currentCall.id, input });\n } catch {\n /* keep accumulating */\n }\n }\n currentCall = { id: tcId, name: fn.name, buffer: \"\" };\n events.push({ type: \"tool_use_start\", callId: tcId, name: fn.name });\n }\n\n if (fn?.arguments && currentCall) {\n currentCall.buffer += fn.arguments;\n events.push({ type: \"tool_use_delta\", callId: currentCall.id, text: fn.arguments });\n }\n }\n\n return { events, activeCall: currentCall };\n}\n\n/** SSE stream processing state. */\ninterface SseState {\n activeToolCall: { id: string; name: string; buffer: string } | null;\n textIndex: number;\n reasoningIndex: number;\n}\n\n/** Process a single SSE line, returning events to yield and updated state. */\nfunction processSseLine(\n line: string,\n state: SseState,\n): { events: StreamEvent[]; state: SseState; done: boolean } {\n const trimmed = line.trim();\n if (!trimmed || !trimmed.startsWith(\"data:\")) {\n return { events: [], state, done: false };\n }\n\n const data = trimmed.slice(5).trim();\n\n if (data === \"[DONE]\") {\n const events: StreamEvent[] = [];\n if (state.activeToolCall) {\n events.push(...flushActiveToolCall(state.activeToolCall));\n }\n events.push({ type: \"done\" });\n return { events, state: { ...state, activeToolCall: null }, done: true };\n }\n\n let chunk: { choices?: Array<{ delta?: Record<string, unknown> }> };\n try {\n chunk = JSON.parse(data);\n } catch {\n return { events: [], state, done: false };\n }\n\n const choice = chunk.choices?.[0];\n if (!choice?.delta) {\n return { events: [], state, done: false };\n }\n\n const delta = choice.delta;\n const events: StreamEvent[] = [];\n let { textIndex, reasoningIndex, activeToolCall } = state;\n\n if (delta.reasoning_content && typeof delta.reasoning_content === \"string\") {\n events.push({\n type: \"reasoning_delta\",\n index: reasoningIndex++,\n text: delta.reasoning_content,\n });\n }\n\n if (delta.content && typeof delta.content === \"string\") {\n events.push({ type: \"text_delta\", index: textIndex++, text: delta.content });\n }\n\n if (delta.tool_calls) {\n const tcResult = processToolCallDeltas(\n delta.tool_calls as Array<Record<string, unknown>>,\n activeToolCall,\n );\n events.push(...tcResult.events);\n activeToolCall = tcResult.activeCall;\n }\n\n return { events, state: { textIndex, reasoningIndex, activeToolCall }, done: false };\n}\n\n// ── Provider ───────────────────────────────────────\n\n/**\n * Create an OpenAI provider instance.\n *\n * Same SSE parsing logic as DeepSeek — both follow the same wire protocol.\n * The only differences are the base URL, models, and capability metadata.\n */\nexport function createOpenAiProvider(config: OpenAiConfig): LlmProvider {\n const baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;\n const provider: LlmProvider = {\n id: \"openai\",\n\n listModels(): ModelInfo[] {\n return MODELS;\n },\n\n getCapability(modelId: string): ProviderCapability {\n const cap = getCapability(\"openai\", modelId);\n if (!cap) {\n throw new LlmError(`Unknown OpenAI model: ${modelId}`, {\n category: \"llm\",\n recoverable: false,\n retryable: false,\n diagnosticHint: \"llm_unknown_model\",\n });\n }\n return cap;\n },\n\n async *stream(\n ...[modelId, messages, systemPrompt, tools, signal]: Parameters<LlmProvider[\"stream\"]>\n ): AsyncGenerator<StreamEvent, void, void> {\n const systemMessages = systemPrompt ? [{ role: \"system\", content: systemPrompt }] : [];\n\n const body = {\n model: modelId,\n messages: [...systemMessages, ...normalizeOpenAiMessages(messages)],\n stream: true,\n ...(tools.length > 0 ? { tools: normalizeOpenAiTools(tools) } : {}),\n };\n\n async function makeRequest(): Promise<Response> {\n const controller = new AbortController();\n const onAbort = () => controller.abort();\n signal.addEventListener(\"abort\", onAbort, { once: true });\n\n try {\n const response = await fetch(`${baseUrl}/v1/chat/completions`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${config.apiKey}`,\n },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n await handleHttpError(response);\n }\n\n return response;\n } finally {\n signal.removeEventListener(\"abort\", onAbort);\n }\n }\n\n const response = await withRetry(makeRequest, {\n baseMs: 500,\n maxMs: 32_000,\n });\n\n if (!response.body) {\n throw new LlmError(\"OpenAI returned empty response body\", {\n category: \"llm\",\n recoverable: true,\n retryable: true,\n });\n }\n\n // Parse SSE stream (same logic as DeepSeek, extracted for clarity)\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n let textIndex = 0;\n let reasoningIndex = 0;\n let activeToolCall: { id: string; name: string; buffer: string } | null = null;\n\n try {\n while (true) {\n if (signal.aborted) break;\n\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() ?? \"\";\n\n for (const line of lines) {\n const result = processSseLine(line, { activeToolCall, textIndex, reasoningIndex });\n for (const event of result.events) yield event;\n if (result.done) return;\n activeToolCall = result.state.activeToolCall;\n textIndex = result.state.textIndex;\n reasoningIndex = result.state.reasoningIndex;\n }\n }\n\n if (activeToolCall) {\n for (const event of flushActiveToolCall(activeToolCall)) yield event;\n }\n yield { type: \"done\" };\n } finally {\n reader.releaseLock();\n }\n },\n };\n\n return provider;\n}\n\n// ── HTTP error mapper ──────────────────────────────\n\nasync function handleHttpError(response: Response): Promise<never> {\n let body: string;\n try {\n body = await response.text();\n } catch {\n body = \"\";\n }\n\n const status = response.status;\n\n if (status === 401 || status === 403) {\n throw new LlmAuthError(`OpenAI auth failed (${status}): ${body}`);\n }\n\n if (status === 429) {\n throw new LlmRateLimitError(`OpenAI rate limited (429): ${body}`);\n }\n\n if (status === 529) {\n throw new LlmRateLimitError(`OpenAI overloaded (529): ${body}`, 529);\n }\n\n if (status === 400 && body.includes(\"context\")) {\n throw new LlmContextOverflowError(`OpenAI context overflow: ${body}`);\n }\n\n throw new LlmError(`OpenAI HTTP ${status}: ${body}`, {\n category: \"llm\",\n recoverable: status >= 500,\n retryable: status >= 500 || status === 429,\n });\n}\n","/**\n * Anthropic provider — SSE streaming via the /v1/messages endpoint.\n *\n * Translates Lynx's OpenAI‑compatible internal format to Anthropic's\n * native message/tool format, then maps Anthropic SSE events back to\n * Lynx StreamEvent types. This is the reverse of what Claude Code's\n * Codex adapter does (Anthropic → OpenAI translation).\n */\n\nimport { LlmError, LlmRateLimitError, LlmAuthError, LlmContextOverflowError } from \"@24klynx/core\";\nimport type { LlmProvider, ModelInfo, ProviderCapability, StreamEvent } from \"../types.js\";\nimport { getCapability } from \"../capabilities.js\";\nimport { withRetry } from \"../retry.js\";\n\n// ── Configuration ──────────────────────────────────\n\nexport interface AnthropicConfig {\n /** API key from ANTHROPIC_API_KEY env var. */\n apiKey: string;\n /** Base URL (defaults to https://api.anthropic.com). */\n baseUrl?: string;\n /** Request timeout in ms (default 120s). */\n timeoutMs?: number;\n}\n\nconst DEFAULT_BASE_URL = \"https://api.anthropic.com\";\nconst ANTHROPIC_VERSION = \"2023-06-01\";\nconst DEFAULT_TIMEOUT_MS = 120_000;\n\n// ── Models ─────────────────────────────────────────\n\nconst MODELS: ModelInfo[] = [\n {\n id: \"claude-opus-4-8\",\n label: \"Claude Opus 4.8\",\n contextWindow: 200_000,\n maxOutput: 32_000,\n },\n {\n id: \"claude-sonnet-4-6\",\n label: \"Claude Sonnet 4.6\",\n contextWindow: 200_000,\n maxOutput: 16_384,\n },\n {\n id: \"claude-haiku-4-5\",\n label: \"Claude Haiku 4.5\",\n contextWindow: 200_000,\n maxOutput: 8_192,\n },\n];\n\n// ── Wire‑format types ──────────────────────────────\n\n/** Anthropic content block types. */\ntype AnthropicContentBlock =\n | { type: \"text\"; text: string }\n | { type: \"tool_use\"; id: string; name: string; input: Record<string, unknown> }\n | { type: \"tool_result\"; tool_use_id: string; content: string; is_error?: boolean };\n\n/** Anthropic message format. */\ninterface AnthropicMessage {\n role: \"user\" | \"assistant\";\n content: string | AnthropicContentBlock[];\n}\n\n/** Anthropic tool definition. */\ninterface AnthropicTool {\n name: string;\n description: string;\n input_schema: Record<string, unknown>;\n}\n\n/** Internal type for message content blocks arriving via Lynx ChatMessage. */\ninterface LynxContentBlock {\n type: string;\n text?: string;\n id?: string;\n name?: string;\n input?: Record<string, unknown>;\n toolUseId?: string;\n content?: string;\n isError?: boolean;\n}\n\n/** OpenAI‑format tool_calls entry. */\ninterface OpenAiToolCall {\n id: string;\n type: \"function\";\n function: { name: string; arguments: string };\n}\n\n/** Lynx ChatMessage shape (what normalizeOpenAiMessages maps TO, so we receive FROM). */\ninterface LynxChatMessage {\n role: \"system\" | \"user\" | \"assistant\" | \"tool\";\n content: string | LynxContentBlock[] | null;\n tool_call_id?: string;\n tool_calls?: OpenAiToolCall[];\n}\n\n// ── Translation: Lynx messages → Anthropic messages ─\n\n/**\n * Convert Lynx OpenAI‑compatible ChatMessage[] to Anthropic Message[].\n * System‑role messages are extracted into a separate string array\n * (Anthropic treats system as a top‑level parameter, not a message).\n */\nfunction translateMessages(chatMessages: LynxChatMessage[]): {\n messages: AnthropicMessage[];\n systemLines: string[];\n} {\n const messages: AnthropicMessage[] = [];\n const systemLines: string[] = [];\n\n for (const msg of chatMessages) {\n if (msg.role === \"system\") {\n if (typeof msg.content === \"string\") {\n systemLines.push(msg.content);\n } else if (Array.isArray(msg.content)) {\n for (const block of msg.content) {\n if (block.type === \"text\" && block.text) systemLines.push(block.text);\n }\n }\n continue;\n }\n\n if (msg.role === \"user\") {\n messages.push({ role: \"user\", content: translateUserContent(msg) });\n } else if (msg.role === \"assistant\") {\n messages.push({ role: \"assistant\", content: translateAssistantContent(msg) });\n } else if (msg.role === \"tool\") {\n // OpenAI tool‑role → Anthropic user message with tool_result block\n messages.push({\n role: \"user\",\n content: [\n {\n type: \"tool_result\",\n tool_use_id: msg.tool_call_id ?? \"unknown\",\n content: typeof msg.content === \"string\" ? msg.content : JSON.stringify(msg.content),\n },\n ],\n });\n }\n }\n\n return { messages, systemLines };\n}\n\n/** Convert a Lynx user message to Anthropic content. */\nfunction translateUserContent(msg: LynxChatMessage): string | AnthropicContentBlock[] {\n if (typeof msg.content === \"string\") return msg.content;\n if (!msg.content || msg.content.length === 0) return \"\";\n\n const blocks: AnthropicContentBlock[] = [];\n for (const block of msg.content as LynxContentBlock[]) {\n if (block.type === \"text\" && block.text) {\n blocks.push({ type: \"text\", text: block.text });\n } else if (block.type === \"tool_result\") {\n blocks.push({\n type: \"tool_result\",\n tool_use_id: block.toolUseId ?? \"unknown\",\n content: typeof block.content === \"string\" ? block.content : JSON.stringify(block.content),\n is_error: block.isError,\n });\n }\n }\n return blocks.length > 0 ? blocks : \"\";\n}\n\n/** Convert a Lynx assistant message to Anthropic content. */\nfunction translateAssistantContent(msg: LynxChatMessage): string | AnthropicContentBlock[] {\n const blocks: AnthropicContentBlock[] = [];\n\n // Text blocks from content array\n if (Array.isArray(msg.content)) {\n for (const block of msg.content as LynxContentBlock[]) {\n if (block.type === \"text\" && block.text) {\n blocks.push({ type: \"text\", text: block.text });\n } else if (block.type === \"tool_use\" && block.id && block.name) {\n blocks.push({\n type: \"tool_use\",\n id: block.id,\n name: block.name,\n input: block.input ?? {},\n });\n }\n }\n } else if (typeof msg.content === \"string\" && msg.content) {\n blocks.push({ type: \"text\", text: msg.content });\n }\n\n // Tool calls from OpenAI format\n if (msg.tool_calls) {\n for (const tc of msg.tool_calls) {\n let input: Record<string, unknown> = {};\n try {\n input = JSON.parse(tc.function.arguments);\n } catch {\n input = {};\n }\n blocks.push({\n type: \"tool_use\",\n id: tc.id,\n name: tc.function.name,\n input,\n });\n }\n }\n\n return blocks.length > 0 ? blocks : \"\";\n}\n\n// ── Translation: Lynx tools → Anthropic tools ──────\n\n/** Convert Lynx/OpenAI‑format tool descriptors to Anthropic format. */\nfunction translateTools(tools: unknown[]): AnthropicTool[] {\n return tools.map((tool) => {\n const raw = tool as Record<string, unknown>;\n return {\n name: (raw.name as string) ?? \"unknown\",\n description: (raw.description as string) ?? \"\",\n input_schema:\n (raw.inputSchema as Record<string, unknown>) ??\n ((raw.function as Record<string, unknown>)?.parameters as Record<string, unknown>) ??\n {},\n };\n });\n}\n\n// ── SSE streaming parser ───────────────────────────\n\n/** State for tracking streaming content blocks. */\ninterface StreamState {\n activeToolCall: { id: string; name: string; buffer: string } | null;\n activeToolCallIndex: number;\n textIndex: number;\n toolUseCount: number;\n /** Set of block indices that are tool_use blocks (to detect when text blocks end). */\n toolUseIndices: Set<number>;\n}\n\n/**\n * Parse a single Anthropic SSE event line and return mapped Lynx events.\n * Anthropic SSE format: `event: <type>\\ndata: <json>\\n\\n`\n */\nfunction processAnthropicEvent(\n eventType: string,\n data: string,\n state: StreamState,\n): { events: StreamEvent[]; state: StreamState } {\n const events: StreamEvent[] = [];\n let nextState = state;\n\n if (eventType === \"message_start\") {\n return { events, state: nextState };\n }\n\n if (eventType === \"ping\") {\n return { events, state: nextState };\n }\n\n if (eventType === \"content_block_start\") {\n const parsed = JSON.parse(data) as {\n type: \"content_block_start\";\n index: number;\n content_block: { type: string; id?: string; name?: string };\n };\n const block = parsed.content_block;\n\n if (block.type === \"tool_use\" && block.id && block.name) {\n nextState = {\n ...state,\n activeToolCall: { id: block.id, name: block.name, buffer: \"\" },\n activeToolCallIndex: parsed.index,\n toolUseIndices: new Set(state.toolUseIndices).add(parsed.index),\n };\n events.push({ type: \"tool_use_start\", callId: block.id, name: block.name });\n }\n // text block start — no event needed, deltas carry the content\n return { events, state: nextState };\n }\n\n if (eventType === \"content_block_delta\") {\n const parsed = JSON.parse(data) as {\n type: \"content_block_delta\";\n index: number;\n delta: { type: string; text?: string; partial_json?: string; thinking?: string };\n };\n const delta = parsed.delta;\n\n if (delta.type === \"text_delta\" && delta.text) {\n events.push({\n type: \"text_delta\",\n index: state.textIndex++,\n text: delta.text,\n });\n } else if (delta.type === \"input_json_delta\" && delta.partial_json && state.activeToolCall) {\n nextState = {\n ...state,\n activeToolCall: {\n ...state.activeToolCall,\n buffer: state.activeToolCall.buffer + delta.partial_json,\n },\n };\n events.push({\n type: \"tool_use_delta\",\n callId: state.activeToolCall.id,\n text: delta.partial_json,\n });\n } else if (delta.type === \"thinking_delta\" && delta.thinking) {\n events.push({\n type: \"reasoning_delta\",\n index: 0,\n text: delta.thinking,\n });\n }\n\n return { events, state: nextState };\n }\n\n if (eventType === \"content_block_stop\") {\n // Parse accumulated JSON for active tool call on this block index\n const parsed = JSON.parse(data) as { type: \"content_block_stop\"; index: number };\n if (\n state.activeToolCall &&\n parsed.index === state.activeToolCallIndex &&\n state.activeToolCall.buffer\n ) {\n try {\n const input = JSON.parse(state.activeToolCall.buffer);\n events.push({\n type: \"tool_use_end\",\n callId: state.activeToolCall.id,\n input: input as Record<string, unknown>,\n });\n nextState = {\n ...state,\n toolUseCount: state.toolUseCount + 1,\n activeToolCall: null,\n };\n } catch {\n events.push({\n type: \"error\",\n code: \"TOOL_PARSE_ERROR\",\n message: `解析 ${state.activeToolCall.name} 的工具输入失败`,\n });\n }\n }\n return { events, state: nextState };\n }\n\n if (eventType === \"message_delta\") {\n return { events, state: nextState };\n }\n\n if (eventType === \"message_stop\") {\n events.push({ type: \"done\" });\n return { events, state: nextState };\n }\n\n return { events, state: nextState };\n}\n\n// ── Provider factory ───────────────────────────────\n\n/**\n * Create an Anthropic provider instance.\n *\n * Each call to `stream()` opens a fresh HTTP connection.\n * The provider itself is stateless — the API key is the only configuration.\n */\nexport function createAnthropicProvider(config: AnthropicConfig): LlmProvider {\n const baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;\n const timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const apiKey = config.apiKey;\n\n const provider: LlmProvider = {\n id: \"anthropic\",\n\n listModels(): ModelInfo[] {\n return MODELS;\n },\n\n getCapability(modelId: string): ProviderCapability {\n const cap = getCapability(\"anthropic\", modelId);\n if (!cap) {\n throw new LlmError(`未知 Anthropic 模型: ${modelId}`, {\n category: \"llm\",\n recoverable: false,\n retryable: false,\n diagnosticHint: \"llm_unknown_model\",\n });\n }\n return cap;\n },\n\n async *stream(\n ...[modelId, messages, systemPrompt, tools, signal]: Parameters<LlmProvider[\"stream\"]>\n ): AsyncGenerator<StreamEvent, void, void> {\n // Translate system prompt + system messages → Anthropic system parameter\n const translated = translateMessages(messages as LynxChatMessage[]);\n const allSystemLines = systemPrompt\n ? [systemPrompt, ...translated.systemLines]\n : translated.systemLines;\n const systemParam = allSystemLines.length > 0 ? allSystemLines.join(\"\\n\\n\") : undefined;\n\n const body = {\n model: modelId,\n max_tokens: 8192,\n messages: translated.messages,\n ...(systemParam ? { system: systemParam } : {}),\n ...(tools.length > 0 ? { tools: translateTools(tools) } : {}),\n stream: true,\n };\n\n async function makeRequest(): Promise<Response> {\n const controller = new AbortController();\n const onAbort = () => controller.abort();\n signal.addEventListener(\"abort\", onAbort, { once: true });\n\n try {\n const response = await fetch(`${baseUrl}/v1/messages`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-api-key\": apiKey,\n \"anthropic-version\": ANTHROPIC_VERSION,\n },\n body: JSON.stringify(body),\n signal: AbortSignal.any\n ? AbortSignal.any([signal, controller.signal])\n : controller.signal,\n });\n\n if (!response.ok) {\n await handleHttpError(response);\n }\n\n return response;\n } finally {\n signal.removeEventListener(\"abort\", onAbort);\n }\n }\n\n const response = await withRetry(makeRequest, {\n baseMs: 500,\n maxMs: 32_000,\n });\n\n if (!response.body) {\n throw new LlmError(\"Anthropic 返回了空响应体\", {\n category: \"llm\",\n recoverable: true,\n retryable: true,\n });\n }\n\n // Parse Anthropic SSE stream\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n let eventType = \"\";\n let state: StreamState = {\n activeToolCall: null,\n activeToolCallIndex: -1,\n textIndex: 0,\n toolUseCount: 0,\n toolUseIndices: new Set(),\n };\n\n try {\n while (true) {\n if (signal.aborted) break;\n\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n\n // Process complete SSE events — Anthropic uses two-line format:\n // event: <type>\n // data: <json>\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() ?? \"\"; // keep incomplete line\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (trimmed.startsWith(\"event: \")) {\n eventType = trimmed.slice(7).trim();\n } else if (trimmed.startsWith(\"data: \") && eventType) {\n const data = trimmed.slice(6).trim();\n const result = processAnthropicEvent(eventType, data, state);\n for (const event of result.events) yield event;\n state = result.state;\n }\n }\n }\n\n // Stream ended without message_stop\n if (state.activeToolCall && state.activeToolCall.buffer) {\n try {\n const input = JSON.parse(state.activeToolCall.buffer);\n yield {\n type: \"tool_use_end\",\n callId: state.activeToolCall.id,\n input: input as Record<string, unknown>,\n };\n } catch {\n // ignore — partial tool call at stream end\n }\n }\n yield { type: \"done\" };\n } finally {\n reader.releaseLock();\n }\n },\n };\n\n return provider;\n}\n\n// ── HTTP error mapper ──────────────────────────────\n\nasync function handleHttpError(response: Response): Promise<never> {\n let body: string;\n try {\n body = await response.text();\n } catch {\n body = \"\";\n }\n\n const status = response.status;\n\n if (status === 401 || status === 403) {\n throw new LlmAuthError(`Anthropic 认证失败 (${status}): ${body}`);\n }\n\n if (status === 429) {\n throw new LlmRateLimitError(`Anthropic 速率限制 (429): ${body}`);\n }\n\n if (status === 529) {\n const err = new LlmRateLimitError(`Anthropic 过载 (529): ${body}`);\n (err as unknown as Record<string, unknown>).status = 529;\n throw err;\n }\n\n if (status === 400 && body.includes(\"context\")) {\n throw new LlmContextOverflowError(`Anthropic 上下文溢出: ${body}`);\n }\n\n throw new LlmError(`Anthropic HTTP ${status}: ${body}`, {\n category: \"llm\",\n recoverable: status >= 500,\n retryable: status >= 500 || status === 429,\n });\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,10 @@
1
1
  {
2
2
  "name": "@24klynx/llm",
3
- "version": "0.1.0",
3
+ "version": "0.1.4",
4
4
  "description": "LLM abstraction layer — Stream, providers, retry, fallback",
5
+ "files": [
6
+ "dist"
7
+ ],
5
8
  "type": "module",
6
9
  "main": "./dist/index.mjs",
7
10
  "types": "./dist/index.d.mts",
@@ -11,16 +14,13 @@
11
14
  "types": "./dist/index.d.mts"
12
15
  }
13
16
  },
14
- "dependencies": {
15
- "@anthropic-ai/sdk": "^0.101.0",
16
- "@24klynx/core": "0.1.0"
17
- },
18
- "files": [
19
- "dist"
20
- ],
21
17
  "publishConfig": {
22
18
  "access": "public"
23
19
  },
20
+ "dependencies": {
21
+ "@anthropic-ai/sdk": "^0.101.0",
22
+ "@24klynx/core": "0.1.4"
23
+ },
24
24
  "scripts": {
25
25
  "build": "tsdown --config-loader tsx",
26
26
  "test": "vitest run --passWithNoTests",