noumen 0.2.0 → 0.4.0

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.
Files changed (83) hide show
  1. package/README.md +95 -16
  2. package/dist/a2a/index.d.ts +5 -5
  3. package/dist/a2a/index.js +3 -3
  4. package/dist/a2a/index.js.map +1 -1
  5. package/dist/acp/index.d.ts +5 -5
  6. package/dist/acp/index.js +4 -4
  7. package/dist/acp/index.js.map +1 -1
  8. package/dist/{agent-BrkbZyOT.d.ts → agent-1nFVUP9E.d.ts} +319 -15
  9. package/dist/{cache-DVqaCX8v.d.ts → cache-DsRqxx6v.d.ts} +1 -1
  10. package/dist/{chunk-BGG2E6JD.js → chunk-3HEYCV26.js} +1 -1
  11. package/dist/chunk-3SK5GCI6.js +75 -0
  12. package/dist/chunk-3SK5GCI6.js.map +1 -0
  13. package/dist/{chunk-NBDFQYUZ.js → chunk-4HW6LN6D.js} +4784 -2411
  14. package/dist/chunk-4HW6LN6D.js.map +1 -0
  15. package/dist/{chunk-7ZMN7XJE.js → chunk-5JN4SPI7.js} +6 -6
  16. package/dist/chunk-5JN4SPI7.js.map +1 -0
  17. package/dist/{chunk-CPFHEPW4.js → chunk-CS6WNDCF.js} +73 -41
  18. package/dist/chunk-CS6WNDCF.js.map +1 -0
  19. package/dist/chunk-EKOGVTBT.js +472 -0
  20. package/dist/chunk-EKOGVTBT.js.map +1 -0
  21. package/dist/{chunk-KY6ZPWHO.js → chunk-HEQQQGK5.js} +47 -28
  22. package/dist/chunk-HEQQQGK5.js.map +1 -0
  23. package/dist/{chunk-QTJ7VTJY.js → chunk-HL6JCRZJ.js} +1599 -481
  24. package/dist/chunk-HL6JCRZJ.js.map +1 -0
  25. package/dist/chunk-L3L3FG5T.js +16 -0
  26. package/dist/chunk-L3L3FG5T.js.map +1 -0
  27. package/dist/cli/index.js +36 -30
  28. package/dist/cli/index.js.map +1 -1
  29. package/dist/client/index.d.ts +2 -2
  30. package/dist/{headless-Q7XHHZIW.js → headless-FFU2DESQ.js} +3 -4
  31. package/dist/headless-FFU2DESQ.js.map +1 -0
  32. package/dist/index.d.ts +218 -68
  33. package/dist/index.js +37 -23
  34. package/dist/lsp/index.d.ts +4 -4
  35. package/dist/mcp/index.d.ts +5 -5
  36. package/dist/mcp/index.js +2 -1
  37. package/dist/mcp/index.js.map +1 -1
  38. package/dist/{provider-factory-34MSWJZ3.js → provider-factory-KCLIF34X.js} +2 -2
  39. package/dist/providers/anthropic.d.ts +2 -2
  40. package/dist/providers/anthropic.js +5 -3
  41. package/dist/providers/anthropic.js.map +1 -1
  42. package/dist/providers/bedrock.d.ts +2 -2
  43. package/dist/providers/bedrock.js +5 -3
  44. package/dist/providers/bedrock.js.map +1 -1
  45. package/dist/providers/gemini.d.ts +2 -1
  46. package/dist/providers/gemini.js +133 -95
  47. package/dist/providers/gemini.js.map +1 -1
  48. package/dist/providers/ollama.d.ts +13 -0
  49. package/dist/{ollama-YNXAYP3R.js → providers/ollama.js} +6 -4
  50. package/dist/providers/ollama.js.map +1 -0
  51. package/dist/providers/openai.d.ts +4 -1
  52. package/dist/providers/openai.js +2 -1
  53. package/dist/providers/openrouter.d.ts +1 -1
  54. package/dist/providers/openrouter.js +2 -1
  55. package/dist/providers/openrouter.js.map +1 -1
  56. package/dist/providers/vertex.d.ts +4 -2
  57. package/dist/providers/vertex.js +6 -3
  58. package/dist/providers/vertex.js.map +1 -1
  59. package/dist/{resolve-XM52G7YE.js → resolve-4JA2BBDA.js} +2 -2
  60. package/dist/server/index.d.ts +35 -20
  61. package/dist/server/index.js +276 -207
  62. package/dist/server/index.js.map +1 -1
  63. package/dist/{server-Cg1yWGaV.d.ts → server-CHMxuWKq.d.ts} +1 -1
  64. package/dist/{types-DwdzmXfs.d.ts → types-CD0rUKKT.d.ts} +2 -0
  65. package/dist/{types-3c88cRKH.d.ts → types-LrU4LRmX.d.ts} +28 -0
  66. package/dist/{types-CwKKucOF.d.ts → types-RPKUTu1k.d.ts} +27 -2
  67. package/dist/uuid-RVN2T26F.js +8 -0
  68. package/dist/uuid-RVN2T26F.js.map +1 -0
  69. package/dist/zod-7YXKWYMC.js +12 -0
  70. package/dist/zod-7YXKWYMC.js.map +1 -0
  71. package/package.json +22 -13
  72. package/dist/chunk-2ZTGQLYK.js +0 -356
  73. package/dist/chunk-2ZTGQLYK.js.map +0 -1
  74. package/dist/chunk-7ZMN7XJE.js.map +0 -1
  75. package/dist/chunk-CPFHEPW4.js.map +0 -1
  76. package/dist/chunk-KY6ZPWHO.js.map +0 -1
  77. package/dist/chunk-NBDFQYUZ.js.map +0 -1
  78. package/dist/chunk-QTJ7VTJY.js.map +0 -1
  79. package/dist/headless-Q7XHHZIW.js.map +0 -1
  80. package/dist/ollama-YNXAYP3R.js.map +0 -1
  81. /package/dist/{chunk-BGG2E6JD.js.map → chunk-3HEYCV26.js.map} +0 -0
  82. /package/dist/{provider-factory-34MSWJZ3.js.map → provider-factory-KCLIF34X.js.map} +0 -0
  83. /package/dist/{resolve-XM52G7YE.js.map → resolve-4JA2BBDA.js.map} +0 -0
@@ -37,12 +37,12 @@ async function resolveProvider(input, opts) {
37
37
  case "anthropic": {
38
38
  if (!key) throw new Error("Anthropic requires an API key. Set ANTHROPIC_API_KEY or pass apiKey.");
39
39
  const { AnthropicProvider } = await import("./providers/anthropic.js");
40
- return new AnthropicProvider({ apiKey: key, model: opts?.model });
40
+ return new AnthropicProvider({ apiKey: key, model: opts?.model, baseURL: opts?.baseURL, cacheControl: { enabled: true } });
41
41
  }
42
42
  case "gemini": {
43
43
  if (!key) throw new Error("Gemini requires an API key. Set GEMINI_API_KEY or pass apiKey.");
44
44
  const { GeminiProvider } = await import("./providers/gemini.js");
45
- return new GeminiProvider({ apiKey: key, model: opts?.model });
45
+ return new GeminiProvider({ apiKey: key, model: opts?.model, baseURL: opts?.baseURL });
46
46
  }
47
47
  case "openrouter": {
48
48
  if (!key) throw new Error("OpenRouter requires an API key. Set OPENROUTER_API_KEY or pass apiKey.");
@@ -51,14 +51,14 @@ async function resolveProvider(input, opts) {
51
51
  }
52
52
  case "bedrock": {
53
53
  const { BedrockAnthropicProvider } = await import("./providers/bedrock.js");
54
- return new BedrockAnthropicProvider({ model: opts?.model });
54
+ return new BedrockAnthropicProvider({ model: opts?.model, baseURL: opts?.baseURL });
55
55
  }
56
56
  case "vertex": {
57
57
  const { VertexAnthropicProvider } = await import("./providers/vertex.js");
58
- return new VertexAnthropicProvider({ model: opts?.model });
58
+ return new VertexAnthropicProvider({ model: opts?.model, baseURL: opts?.baseURL });
59
59
  }
60
60
  case "ollama": {
61
- const { OllamaProvider } = await import("./ollama-YNXAYP3R.js");
61
+ const { OllamaProvider } = await import("./providers/ollama.js");
62
62
  const base = opts?.baseURL ?? (process.env.OLLAMA_HOST ? `${process.env.OLLAMA_HOST.replace(/\/+$/, "")}/v1` : void 0);
63
63
  return new OllamaProvider({ model: opts?.model, baseURL: base });
64
64
  }
@@ -91,4 +91,4 @@ export {
91
91
  resolveProvider,
92
92
  detectProvider
93
93
  };
94
- //# sourceMappingURL=chunk-7ZMN7XJE.js.map
94
+ //# sourceMappingURL=chunk-5JN4SPI7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/providers/resolve.ts"],"sourcesContent":["import type { AIProvider } from \"./types.js\";\n\nexport type ProviderName =\n | \"openai\"\n | \"anthropic\"\n | \"gemini\"\n | \"openrouter\"\n | \"bedrock\"\n | \"vertex\"\n | \"ollama\";\n\nconst ENV_KEY_MAP: Record<string, string> = {\n openai: \"OPENAI_API_KEY\",\n anthropic: \"ANTHROPIC_API_KEY\",\n gemini: \"GEMINI_API_KEY\",\n openrouter: \"OPENROUTER_API_KEY\",\n};\n\nexport const DEFAULT_MODELS: Record<string, string> = {\n openai: \"gpt-5.4\",\n anthropic: \"claude-opus-4.6\",\n gemini: \"gemini-2.5-flash\",\n openrouter: \"anthropic/claude-opus-4.6\",\n bedrock: \"us.anthropic.claude-opus-4.6-v1:0\",\n vertex: \"claude-opus-4.6\",\n ollama: \"qwen2.5-coder:32b\",\n};\n\nexport const SUPPORTED_PROVIDERS: ProviderName[] = Object.keys(DEFAULT_MODELS) as ProviderName[];\n\nfunction getProviderEnvKey(name: string): string | undefined {\n const envVar = ENV_KEY_MAP[name];\n return envVar ? process.env[envVar] : undefined;\n}\n\nexport interface ResolveProviderOptions {\n apiKey?: string;\n model?: string;\n baseURL?: string;\n}\n\n/**\n * Resolve a provider from a name string or pass through an AIProvider instance.\n * API key resolution order:\n * 1. Explicit apiKey option\n * 2. Provider-specific env var (OPENAI_API_KEY, etc.)\n * 3. NOUMEN_API_KEY generic env var\n */\nexport async function resolveProvider(\n input: AIProvider | ProviderName,\n opts?: ResolveProviderOptions,\n): Promise<AIProvider> {\n if (typeof input !== \"string\") return input;\n\n const name = input;\n if (!SUPPORTED_PROVIDERS.includes(name)) {\n throw new Error(\n `Unknown provider \"${name}\". Supported: ${SUPPORTED_PROVIDERS.join(\", \")}`,\n );\n }\n\n const key =\n opts?.apiKey ??\n getProviderEnvKey(name) ??\n process.env.NOUMEN_API_KEY;\n\n switch (name) {\n case \"openai\": {\n if (!key) throw new Error(\"OpenAI requires an API key. Set OPENAI_API_KEY or pass apiKey.\");\n const { OpenAIProvider } = await import(\"./openai.js\");\n return new OpenAIProvider({ apiKey: key, model: opts?.model, baseURL: opts?.baseURL });\n }\n case \"anthropic\": {\n if (!key) throw new Error(\"Anthropic requires an API key. Set ANTHROPIC_API_KEY or pass apiKey.\");\n const { AnthropicProvider } = await import(\"./anthropic.js\");\n return new AnthropicProvider({ apiKey: key, model: opts?.model, baseURL: opts?.baseURL, cacheControl: { enabled: true } });\n }\n case \"gemini\": {\n if (!key) throw new Error(\"Gemini requires an API key. Set GEMINI_API_KEY or pass apiKey.\");\n const { GeminiProvider } = await import(\"./gemini.js\");\n return new GeminiProvider({ apiKey: key, model: opts?.model, baseURL: opts?.baseURL });\n }\n case \"openrouter\": {\n if (!key) throw new Error(\"OpenRouter requires an API key. Set OPENROUTER_API_KEY or pass apiKey.\");\n const { OpenRouterProvider } = await import(\"./openrouter.js\");\n return new OpenRouterProvider({ apiKey: key, model: opts?.model, appName: \"noumen\" });\n }\n case \"bedrock\": {\n const { BedrockAnthropicProvider } = await import(\"./bedrock.js\");\n return new BedrockAnthropicProvider({ model: opts?.model, baseURL: opts?.baseURL });\n }\n case \"vertex\": {\n const { VertexAnthropicProvider } = await import(\"./vertex.js\");\n return new VertexAnthropicProvider({ model: opts?.model, baseURL: opts?.baseURL });\n }\n case \"ollama\": {\n const { OllamaProvider } = await import(\"./ollama.js\");\n const base = opts?.baseURL ?? (process.env.OLLAMA_HOST\n ? `${process.env.OLLAMA_HOST.replace(/\\/+$/, \"\")}/v1`\n : undefined);\n return new OllamaProvider({ model: opts?.model, baseURL: base });\n }\n default:\n throw new Error(`Unhandled provider: ${name}`);\n }\n}\n\n/**\n * Auto-detect provider from available environment variables.\n */\nexport async function detectProvider(): Promise<ProviderName | undefined> {\n if (process.env.ANTHROPIC_API_KEY) return \"anthropic\";\n if (process.env.OPENAI_API_KEY) return \"openai\";\n if (process.env.GEMINI_API_KEY) return \"gemini\";\n if (process.env.OPENROUTER_API_KEY) return \"openrouter\";\n if (process.env.AWS_ACCESS_KEY_ID || process.env.AWS_PROFILE) return \"bedrock\";\n if (process.env.GOOGLE_APPLICATION_CREDENTIALS || process.env.GCLOUD_PROJECT) return \"vertex\";\n if (process.env.OLLAMA_HOST) return \"ollama\";\n\n try {\n const host = process.env.OLLAMA_HOST ?? \"http://localhost:11434\";\n const res = await fetch(`${host.replace(/\\/+$/, \"\")}/api/tags`, {\n signal: AbortSignal.timeout(500),\n });\n if (res.ok) return \"ollama\";\n } catch { /* not running */ }\n\n return undefined;\n}\n"],"mappings":"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","names":[]}
@@ -1,16 +1,23 @@
1
+ import {
2
+ ChatStreamError
3
+ } from "./chunk-L3L3FG5T.js";
4
+
1
5
  // src/providers/openai.ts
2
6
  import OpenAI from "openai";
3
7
  var O_SERIES_PATTERN = /^o[1-9]/;
4
8
  var OpenAIProvider = class _OpenAIProvider {
5
9
  client;
6
10
  defaultModel;
11
+ compatMode;
7
12
  constructor(opts) {
8
13
  this.client = new OpenAI({
9
14
  apiKey: opts.apiKey ?? "not-needed",
10
15
  baseURL: opts.baseURL,
11
- defaultHeaders: opts.defaultHeaders
16
+ defaultHeaders: opts.defaultHeaders,
17
+ maxRetries: 0
12
18
  });
13
19
  this.defaultModel = opts.model ?? "gpt-5.4";
20
+ this.compatMode = opts.compatMode ?? false;
14
21
  }
15
22
  async *chat(params) {
16
23
  const messages = this.buildMessages(params.system, params.messages);
@@ -24,10 +31,13 @@ var OpenAIProvider = class _OpenAIProvider {
24
31
  function: t.function
25
32
  })),
26
33
  stream: true,
27
- stream_options: { include_usage: true }
34
+ ...this.compatMode ? {} : { stream_options: { include_usage: true } }
28
35
  };
29
- if (isOSeries && params.thinking?.type === "enabled") {
30
- createParams.reasoning_effort = "high";
36
+ if (isOSeries) {
37
+ createParams.max_completion_tokens = params.max_tokens ?? 16384;
38
+ if (params.thinking?.type === "enabled") {
39
+ createParams.reasoning_effort = "high";
40
+ }
31
41
  } else {
32
42
  createParams.max_tokens = params.max_tokens;
33
43
  createParams.temperature = params.temperature;
@@ -46,44 +56,66 @@ var OpenAIProvider = class _OpenAIProvider {
46
56
  type: "json_object"
47
57
  };
48
58
  }
49
- const stream = await this.client.chat.completions.create(createParams);
50
- for await (const chunk of stream) {
51
- const usage = chunk.usage;
52
- let mappedUsage;
53
- if (usage) {
54
- const u = usage;
55
- const promptDetails = u.prompt_tokens_details;
56
- const completionDetails = u.completion_tokens_details;
57
- mappedUsage = {
58
- prompt_tokens: usage.prompt_tokens,
59
- completion_tokens: usage.completion_tokens,
60
- total_tokens: usage.total_tokens,
61
- cache_read_tokens: promptDetails?.cached_tokens,
62
- thinking_tokens: completionDetails?.reasoning_tokens
59
+ try {
60
+ const stream = await this.client.chat.completions.create(createParams, {
61
+ ...params.signal ? { signal: params.signal } : {}
62
+ });
63
+ for await (const chunk of stream) {
64
+ const usage = chunk.usage;
65
+ let mappedUsage;
66
+ if (usage) {
67
+ const u = usage;
68
+ const promptDetails = u.prompt_tokens_details;
69
+ const completionDetails = u.completion_tokens_details;
70
+ mappedUsage = {
71
+ prompt_tokens: usage.prompt_tokens,
72
+ completion_tokens: usage.completion_tokens,
73
+ total_tokens: usage.total_tokens,
74
+ cache_read_tokens: promptDetails?.cached_tokens,
75
+ cache_creation_tokens: promptDetails?.cache_creation_tokens,
76
+ thinking_tokens: completionDetails?.reasoning_tokens
77
+ };
78
+ }
79
+ const choices = chunk.choices ?? [];
80
+ if (choices.length === 0 && mappedUsage) {
81
+ yield { id: chunk.id, model: chunk.model, choices: [], usage: mappedUsage };
82
+ continue;
83
+ }
84
+ yield {
85
+ id: chunk.id,
86
+ model: chunk.model,
87
+ choices: choices.map((c) => ({
88
+ index: c.index,
89
+ delta: {
90
+ role: c.delta.role,
91
+ content: c.delta.content,
92
+ thinking_content: c.delta.reasoning_content,
93
+ tool_calls: c.delta.tool_calls?.map((tc) => ({
94
+ index: tc.index,
95
+ id: tc.id,
96
+ type: tc.type,
97
+ function: tc.function ? {
98
+ name: tc.function.name,
99
+ arguments: tc.function.arguments
100
+ } : void 0
101
+ }))
102
+ },
103
+ finish_reason: c.finish_reason
104
+ })),
105
+ usage: mappedUsage
63
106
  };
64
107
  }
65
- yield {
66
- id: chunk.id,
67
- model: chunk.model,
68
- choices: chunk.choices.map((c) => ({
69
- index: c.index,
70
- delta: {
71
- role: c.delta.role,
72
- content: c.delta.content,
73
- tool_calls: c.delta.tool_calls?.map((tc) => ({
74
- index: tc.index,
75
- id: tc.id,
76
- type: tc.type,
77
- function: tc.function ? {
78
- name: tc.function.name,
79
- arguments: tc.function.arguments
80
- } : void 0
81
- }))
82
- },
83
- finish_reason: c.finish_reason
84
- })),
85
- usage: mappedUsage
86
- };
108
+ } catch (err) {
109
+ if (err instanceof ChatStreamError) throw err;
110
+ const apiErr = err;
111
+ throw new ChatStreamError(
112
+ err instanceof Error ? err.message : String(err),
113
+ {
114
+ status: apiErr.status,
115
+ retryAfter: apiErr.headers?.get?.("retry-after") ?? apiErr.headers?.["retry-after"] ?? void 0,
116
+ cause: err
117
+ }
118
+ );
87
119
  }
88
120
  }
89
121
  static contentPartsToOpenAI(parts) {
@@ -136,4 +168,4 @@ var OpenAIProvider = class _OpenAIProvider {
136
168
  export {
137
169
  OpenAIProvider
138
170
  };
139
- //# sourceMappingURL=chunk-CPFHEPW4.js.map
171
+ //# sourceMappingURL=chunk-CS6WNDCF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/providers/openai.ts"],"sourcesContent":["import OpenAI from \"openai\";\nimport type {\n AIProvider,\n ChatParams,\n ChatStreamChunk,\n} from \"./types.js\";\nimport { ChatStreamError } from \"./types.js\";\nimport type { ChatMessage, ContentPart } from \"../session/types.js\";\n\nexport interface OpenAIProviderOptions {\n apiKey?: string;\n baseURL?: string;\n model?: string;\n defaultHeaders?: Record<string, string | undefined>;\n /** When true, omits `stream_options` that some OpenAI-compatible endpoints don't support. */\n compatMode?: boolean;\n}\n\nconst O_SERIES_PATTERN = /^o[1-9]/;\n\nexport class OpenAIProvider implements AIProvider {\n private client: OpenAI;\n private defaultModel: string;\n protected compatMode: boolean;\n\n constructor(opts: OpenAIProviderOptions) {\n this.client = new OpenAI({\n apiKey: opts.apiKey ?? \"not-needed\",\n baseURL: opts.baseURL,\n defaultHeaders: opts.defaultHeaders,\n maxRetries: 0,\n });\n this.defaultModel = opts.model ?? \"gpt-5.4\";\n this.compatMode = opts.compatMode ?? false;\n }\n\n async *chat(params: ChatParams): AsyncIterable<ChatStreamChunk> {\n const messages = this.buildMessages(params.system, params.messages);\n const model = params.model ?? this.defaultModel;\n const isOSeries = O_SERIES_PATTERN.test(model);\n\n const createParams: OpenAI.ChatCompletionCreateParamsStreaming = {\n model,\n messages: messages as unknown as OpenAI.ChatCompletionMessageParam[],\n tools: params.tools?.map((t) => ({\n type: \"function\" as const,\n function: t.function,\n })),\n stream: true,\n ...(this.compatMode ? {} : { stream_options: { include_usage: true } }),\n };\n\n if (isOSeries) {\n (createParams as unknown as Record<string, unknown>).max_completion_tokens = params.max_tokens ?? 16384;\n if (params.thinking?.type === \"enabled\") {\n (createParams as unknown as Record<string, unknown>).reasoning_effort = \"high\";\n }\n } else {\n createParams.max_tokens = params.max_tokens;\n createParams.temperature = params.temperature;\n }\n\n if (params.outputFormat?.type === \"json_schema\") {\n (createParams as unknown as Record<string, unknown>).response_format = {\n type: \"json_schema\",\n json_schema: {\n name: params.outputFormat.name ?? \"response\",\n schema: params.outputFormat.schema,\n strict: params.outputFormat.strict ?? false,\n },\n };\n } else if (params.outputFormat?.type === \"json_object\") {\n (createParams as unknown as Record<string, unknown>).response_format = {\n type: \"json_object\",\n };\n }\n\n try {\n const stream = await this.client.chat.completions.create(createParams, {\n ...(params.signal ? { signal: params.signal } : {}),\n });\n\n for await (const chunk of stream) {\n const usage = chunk.usage;\n let mappedUsage: ChatStreamChunk[\"usage\"] | undefined;\n if (usage) {\n const u = usage as unknown as Record<string, unknown>;\n const promptDetails = u.prompt_tokens_details as Record<string, unknown> | undefined;\n const completionDetails = u.completion_tokens_details as Record<string, unknown> | undefined;\n mappedUsage = {\n prompt_tokens: usage.prompt_tokens,\n completion_tokens: usage.completion_tokens,\n total_tokens: usage.total_tokens,\n cache_read_tokens: promptDetails?.cached_tokens as number | undefined,\n cache_creation_tokens: promptDetails?.cache_creation_tokens as number | undefined,\n thinking_tokens: completionDetails?.reasoning_tokens as number | undefined,\n };\n }\n\n const choices = chunk.choices ?? [];\n if (choices.length === 0 && mappedUsage) {\n yield { id: chunk.id, model: chunk.model, choices: [], usage: mappedUsage };\n continue;\n }\n\n yield {\n id: chunk.id,\n model: chunk.model,\n choices: choices.map((c) => ({\n index: c.index,\n delta: {\n role: c.delta.role as \"assistant\" | undefined,\n content: c.delta.content,\n thinking_content: (c.delta as Record<string, unknown>).reasoning_content as string | undefined,\n tool_calls: c.delta.tool_calls?.map((tc) => ({\n index: tc.index,\n id: tc.id,\n type: tc.type as \"function\" | undefined,\n function: tc.function\n ? {\n name: tc.function.name,\n arguments: tc.function.arguments,\n }\n : undefined,\n })),\n },\n finish_reason: c.finish_reason,\n })),\n usage: mappedUsage,\n };\n }\n } catch (err: unknown) {\n if (err instanceof ChatStreamError) throw err;\n const apiErr = err as { status?: number; headers?: Record<string, string> & { get?(k: string): string | null } };\n throw new ChatStreamError(\n err instanceof Error ? err.message : String(err),\n {\n status: apiErr.status,\n retryAfter: apiErr.headers?.get?.(\"retry-after\") ?? apiErr.headers?.[\"retry-after\"] ?? undefined,\n cause: err,\n },\n );\n }\n }\n\n private static contentPartsToOpenAI(\n parts: ContentPart[],\n ): Array<Record<string, unknown>> {\n return parts.map((part) => {\n if (part.type === \"text\") {\n return { type: \"text\", text: part.text };\n }\n if (part.type === \"image\") {\n return {\n type: \"image_url\",\n image_url: { url: `data:${part.media_type};base64,${part.data}` },\n };\n }\n // image_url\n return { type: \"image_url\", image_url: { url: part.url } };\n });\n }\n\n private buildMessages(\n system: string | undefined,\n messages: ChatMessage[],\n ): Array<Record<string, unknown>> {\n const result: Array<Record<string, unknown>> = [];\n if (system) {\n result.push({ role: \"system\", content: system });\n }\n for (const msg of messages) {\n if (msg.role === \"tool\") {\n const content = Array.isArray(msg.content)\n ? OpenAIProvider.contentPartsToOpenAI(msg.content as ContentPart[])\n : msg.content;\n result.push({\n role: \"tool\",\n tool_call_id: msg.tool_call_id,\n content,\n });\n } else if (msg.role === \"assistant\") {\n const entry: Record<string, unknown> = {\n role: \"assistant\",\n content: msg.content,\n };\n if (msg.tool_calls) {\n entry.tool_calls = msg.tool_calls;\n }\n result.push(entry);\n } else if (msg.role === \"user\") {\n const content = Array.isArray(msg.content)\n ? OpenAIProvider.contentPartsToOpenAI(msg.content as ContentPart[])\n : msg.content;\n result.push({ role: \"user\", content });\n } else {\n result.push({ role: msg.role, content: msg.content });\n }\n }\n return result;\n }\n}\n"],"mappings":"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","names":[]}