@deepstrike/sdk 0.2.52 → 0.2.60

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 (148) hide show
  1. package/README.md +28 -28
  2. package/dist/agent-ir.d.ts +103 -0
  3. package/dist/agent-ir.js +134 -0
  4. package/dist/agent.d.ts +67 -0
  5. package/dist/agent.js +36 -0
  6. package/dist/collaboration/harness.js +1 -1
  7. package/dist/collaboration/modes/creator-verifier.d.ts +2 -7
  8. package/dist/collaboration/modes/creator-verifier.js +4 -6
  9. package/dist/collaboration/pool.d.ts +8 -20
  10. package/dist/collaboration/pool.js +27 -97
  11. package/dist/compat/anthropic/mcp.d.ts +15 -0
  12. package/dist/compat/anthropic/mcp.js +10 -0
  13. package/dist/compat/openai/agent.d.ts +34 -0
  14. package/dist/compat/openai/agent.js +24 -0
  15. package/dist/governance.d.ts +1 -17
  16. package/dist/governance.js +1 -34
  17. package/dist/guardrail.d.ts +6 -0
  18. package/dist/guardrail.js +1 -0
  19. package/dist/handoff-target.d.ts +12 -0
  20. package/dist/handoff-target.js +1 -0
  21. package/dist/harness/manifest.js +0 -4
  22. package/dist/index.d.ts +19 -7
  23. package/dist/index.js +8 -5
  24. package/dist/kernel.d.ts +2 -20
  25. package/dist/knowledge/public.d.ts +29 -0
  26. package/dist/knowledge/public.js +1 -0
  27. package/dist/mcp-server.d.ts +28 -0
  28. package/dist/mcp-server.js +1 -0
  29. package/dist/memory/agent.d.ts +2 -2
  30. package/dist/memory/agent.js +2 -2
  31. package/dist/memory/durable.d.ts +16 -0
  32. package/dist/memory/durable.js +46 -0
  33. package/dist/memory/in-memory-store.d.ts +9 -7
  34. package/dist/memory/in-memory-store.js +8 -2
  35. package/dist/memory/protocols.d.ts +22 -4
  36. package/dist/memory/public.d.ts +4 -3
  37. package/dist/memory/public.js +3 -2
  38. package/dist/providers/anthropic-adapter.d.ts +47 -0
  39. package/dist/providers/anthropic-adapter.js +444 -0
  40. package/dist/providers/anthropic-compatible.d.ts +2 -3
  41. package/dist/providers/anthropic-compatible.js +8 -5
  42. package/dist/providers/anthropic.d.ts +19 -23
  43. package/dist/providers/anthropic.js +169 -395
  44. package/dist/providers/base.js +43 -3
  45. package/dist/providers/capability-router.d.ts +29 -0
  46. package/dist/providers/capability-router.js +43 -0
  47. package/dist/providers/catalog.d.ts +16 -4
  48. package/dist/providers/catalog.js +112 -21
  49. package/dist/providers/content-normalization.d.ts +57 -0
  50. package/dist/providers/content-normalization.js +238 -0
  51. package/dist/providers/content-policy.d.ts +16 -0
  52. package/dist/providers/content-policy.js +39 -0
  53. package/dist/providers/credentials.d.ts +83 -0
  54. package/dist/providers/credentials.js +190 -0
  55. package/dist/providers/endpoints.d.ts +137 -0
  56. package/dist/providers/endpoints.js +128 -0
  57. package/dist/providers/factories.js +9 -3
  58. package/dist/providers/gemini-adapter.d.ts +33 -0
  59. package/dist/providers/gemini-adapter.js +272 -0
  60. package/dist/providers/gemini.d.ts +16 -3
  61. package/dist/providers/gemini.js +97 -195
  62. package/dist/providers/model-catalog.d.ts +37 -0
  63. package/dist/providers/model-catalog.js +62 -0
  64. package/dist/providers/model-registry.d.ts +98 -0
  65. package/dist/providers/model-registry.js +315 -0
  66. package/dist/providers/ollama-adapter.d.ts +65 -0
  67. package/dist/providers/ollama-adapter.js +188 -0
  68. package/dist/providers/ollama.d.ts +9 -4
  69. package/dist/providers/ollama.js +96 -109
  70. package/dist/providers/openai-chat-dialects.d.ts +154 -0
  71. package/dist/providers/openai-chat-dialects.js +179 -0
  72. package/dist/providers/openai-chat.d.ts +46 -18
  73. package/dist/providers/openai-chat.js +418 -51
  74. package/dist/providers/openai-responses-adapter.d.ts +42 -0
  75. package/dist/providers/openai-responses-adapter.js +341 -0
  76. package/dist/providers/openai-responses.d.ts +14 -33
  77. package/dist/providers/openai-responses.js +131 -264
  78. package/dist/providers/openai.d.ts +29 -76
  79. package/dist/providers/openai.js +195 -292
  80. package/dist/providers/protocol-adapter.d.ts +34 -0
  81. package/dist/providers/protocol-adapter.js +9 -0
  82. package/dist/providers/protocol-capabilities.d.ts +34 -0
  83. package/dist/providers/protocol-capabilities.js +44 -0
  84. package/dist/providers/provider-error.d.ts +31 -0
  85. package/dist/providers/provider-error.js +149 -0
  86. package/dist/providers/public.d.ts +26 -3
  87. package/dist/providers/public.js +13 -1
  88. package/dist/providers/registry.d.ts +7 -6
  89. package/dist/providers/registry.js +47 -20
  90. package/dist/providers/request-plan.d.ts +86 -0
  91. package/dist/providers/request-plan.js +175 -0
  92. package/dist/providers/usage-normalizer.d.ts +48 -0
  93. package/dist/providers/usage-normalizer.js +96 -0
  94. package/dist/providers/vendor-profiles.d.ts +2 -15
  95. package/dist/providers/vendor-profiles.js +14 -60
  96. package/dist/runtime/canonical-kernel-step.d.ts +1 -2
  97. package/dist/runtime/canonical-kernel-step.js +47 -12
  98. package/dist/runtime/context-policy.d.ts +10 -12
  99. package/dist/runtime/context-policy.js +6 -8
  100. package/dist/runtime/durable-content.d.ts +50 -0
  101. package/dist/runtime/durable-content.js +159 -0
  102. package/dist/runtime/execution-plane.d.ts +2 -2
  103. package/dist/runtime/execution-plane.js +2 -2
  104. package/dist/runtime/kernel-event-log.js +0 -1
  105. package/dist/runtime/kernel-step.d.ts +0 -1
  106. package/dist/runtime/kernel-step.js +4 -2
  107. package/dist/runtime/mcp-proxy-plane.d.ts +25 -1
  108. package/dist/runtime/mcp-proxy-plane.js +44 -6
  109. package/dist/runtime/output-schema.d.ts +1 -2
  110. package/dist/runtime/provider-replay.d.ts +1 -1
  111. package/dist/runtime/provider-replay.js +12 -27
  112. package/dist/runtime/reactive-session.d.ts +1 -1
  113. package/dist/runtime/reactive-session.js +2 -3
  114. package/dist/runtime/run-group.d.ts +1 -1
  115. package/dist/runtime/runner.d.ts +26 -40
  116. package/dist/runtime/runner.js +145 -63
  117. package/dist/runtime/session-log.d.ts +8 -1
  118. package/dist/runtime/session-log.js +42 -2
  119. package/dist/runtime/session-repair.d.ts +1 -1
  120. package/dist/runtime/session-repair.js +1 -1
  121. package/dist/runtime/sub-agent-orchestrator.d.ts +1 -1
  122. package/dist/runtime/sub-agent-orchestrator.js +8 -11
  123. package/dist/runtime/workflow-control-flow.d.ts +0 -4
  124. package/dist/runtime/workflow-control-flow.js +0 -16
  125. package/dist/session.d.ts +11 -0
  126. package/dist/session.js +1 -0
  127. package/dist/skill.d.ts +17 -0
  128. package/dist/skill.js +16 -0
  129. package/dist/skills/loader.d.ts +3 -0
  130. package/dist/tools/errors.d.ts +1 -3
  131. package/dist/tools/errors.js +1 -3
  132. package/dist/tools/index.d.ts +3 -0
  133. package/dist/types/agent.d.ts +21 -9
  134. package/dist/types/agent.js +30 -4
  135. package/dist/types.d.ts +128 -13
  136. package/package.json +3 -3
  137. package/dist/providers/deepseek.d.ts +0 -46
  138. package/dist/providers/deepseek.js +0 -97
  139. package/dist/providers/glm.d.ts +0 -25
  140. package/dist/providers/glm.js +0 -48
  141. package/dist/providers/kimi.d.ts +0 -23
  142. package/dist/providers/kimi.js +0 -30
  143. package/dist/providers/minimax.d.ts +0 -49
  144. package/dist/providers/minimax.js +0 -98
  145. package/dist/providers/profiles.d.ts +0 -1992
  146. package/dist/providers/profiles.js +0 -796
  147. package/dist/providers/qwen.d.ts +0 -38
  148. package/dist/providers/qwen.js +0 -97
@@ -0,0 +1,188 @@
1
+ import { projectToolOutputToText } from "./content-normalization.js";
2
+ import { normalizeToolCall } from "./base.js";
3
+ import { ProtocolResponseError, OLLAMA_PROTOCOL_CAPABILITIES, } from "./protocol-adapter.js";
4
+ function messageContent(message) {
5
+ const text = [];
6
+ const images = [];
7
+ for (const item of message.blocks) {
8
+ if (item.type === "tool_result") {
9
+ text.push(projectToolOutputToText(item.blocks));
10
+ }
11
+ else if (item.type === "text") {
12
+ text.push(item.text);
13
+ }
14
+ else if (item.type === "image" && item.source.kind === "base64") {
15
+ images.push(item.source.data);
16
+ text.push("[image]");
17
+ }
18
+ else {
19
+ throw new ProtocolResponseError("ollama-chat", `cannot serialize ${item.type}`);
20
+ }
21
+ }
22
+ return {
23
+ role: message.role,
24
+ content: text.join("\n"),
25
+ ...(images.length ? { images } : {}),
26
+ };
27
+ }
28
+ function requestExtensions(extensions) {
29
+ const blocked = new Set([
30
+ "model",
31
+ "messages",
32
+ "tools",
33
+ "stream",
34
+ "__deepstrikeThinkingEnabled",
35
+ "degradeMissingReasoningReplay",
36
+ ]);
37
+ return Object.fromEntries(Object.entries(extensions).filter(([key]) => !blocked.has(key)));
38
+ }
39
+ function validCount(raw, field) {
40
+ const value = raw[field];
41
+ if (value === undefined)
42
+ return undefined;
43
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
44
+ throw new ProtocolResponseError("ollama-chat", `${field} must be a non-negative finite number`);
45
+ }
46
+ return value;
47
+ }
48
+ export class OllamaNdjsonDecoder {
49
+ buffer = "";
50
+ push(text) {
51
+ this.buffer += text;
52
+ const lines = this.buffer.split("\n");
53
+ this.buffer = lines.pop() ?? "";
54
+ return this.parse(lines);
55
+ }
56
+ finish(text = "") {
57
+ this.buffer += text;
58
+ const tail = this.buffer;
59
+ this.buffer = "";
60
+ return this.parse(tail ? [tail] : []);
61
+ }
62
+ parse(lines) {
63
+ const chunks = [];
64
+ for (const line of lines) {
65
+ if (!line.trim())
66
+ continue;
67
+ try {
68
+ const value = JSON.parse(line);
69
+ if (value && typeof value === "object" && !Array.isArray(value)) {
70
+ chunks.push(value);
71
+ }
72
+ }
73
+ catch {
74
+ // Preserve the established Ollama behavior: malformed complete lines are skipped.
75
+ }
76
+ }
77
+ return chunks;
78
+ }
79
+ }
80
+ export class OllamaAdapter {
81
+ protocol = "ollama-chat";
82
+ protocolCapabilities = OLLAMA_PROTOCOL_CAPABILITIES;
83
+ buildRequest(input) {
84
+ const messages = [];
85
+ if (input.context.systemText) {
86
+ messages.push({ role: "system", content: input.context.systemText });
87
+ }
88
+ const turns = input.context.stateTurn
89
+ ? [...input.context.turns, input.context.stateTurn]
90
+ : input.context.turns;
91
+ messages.push(...turns.map(messageContent));
92
+ return {
93
+ ...requestExtensions(input.extensions),
94
+ model: input.resolved.identity.modelId,
95
+ messages,
96
+ ...(input.tools.length ? {
97
+ tools: input.tools.map(tool => ({
98
+ type: "function",
99
+ function: {
100
+ name: tool.name,
101
+ description: tool.description,
102
+ parameters: JSON.parse(tool.parameters),
103
+ },
104
+ })),
105
+ } : {}),
106
+ };
107
+ }
108
+ decodeComplete(raw, _input) {
109
+ return {
110
+ message: {
111
+ role: "assistant",
112
+ content: raw.message?.content ?? "",
113
+ },
114
+ };
115
+ }
116
+ createStreamState(input) {
117
+ return { input: input.input, pendingToolCalls: new Map() };
118
+ }
119
+ pushStreamChunk(chunk, state) {
120
+ const events = [];
121
+ if (chunk.message?.content) {
122
+ events.push({ type: "text_delta", delta: chunk.message.content });
123
+ }
124
+ for (const call of chunk.message?.tool_calls ?? []) {
125
+ const normalized = normalizeToolCall("", call.function.name, call.function.arguments);
126
+ if (!normalized)
127
+ continue;
128
+ const key = `${normalized.name}:${normalized.arguments}`;
129
+ if (!state.pendingToolCalls.has(key)) {
130
+ state.pendingToolCalls.set(key, {
131
+ id: `call_${state.pendingToolCalls.size + 1}`,
132
+ name: normalized.name,
133
+ arguments: JSON.parse(normalized.arguments),
134
+ });
135
+ }
136
+ }
137
+ if (chunk.done)
138
+ state.finalChunk = chunk;
139
+ return { events };
140
+ }
141
+ finishStream(state, final) {
142
+ const terminal = final ?? state.finalChunk;
143
+ const events = Array.from(state.pendingToolCalls.values(), call => ({ type: "tool_call", ...call }));
144
+ const usage = this.normalizeUsage(terminal);
145
+ if (usage) {
146
+ const rawStopReason = terminal?.done_reason;
147
+ const stopReason = this.normalizeStopReason(rawStopReason);
148
+ events.push({
149
+ type: "usage",
150
+ totalTokens: usage.inputTokens + usage.outputTokens,
151
+ inputTokens: usage.inputTokens,
152
+ outputTokens: usage.outputTokens,
153
+ providerUsage: usage,
154
+ ...(stopReason ? { stopReason } : {}),
155
+ ...(rawStopReason ? { rawStopReason } : {}),
156
+ });
157
+ }
158
+ return { events };
159
+ }
160
+ normalizeUsage(raw) {
161
+ if (raw === undefined || raw === null)
162
+ return undefined;
163
+ if (typeof raw !== "object" || Array.isArray(raw)) {
164
+ throw new ProtocolResponseError("ollama-chat", "usage source must be an object");
165
+ }
166
+ const record = raw;
167
+ const inputTokens = validCount(record, "prompt_eval_count");
168
+ const outputTokens = validCount(record, "eval_count");
169
+ if (inputTokens === undefined && outputTokens === undefined)
170
+ return undefined;
171
+ return {
172
+ inputTokens: inputTokens ?? 0,
173
+ outputTokens: outputTokens ?? 0,
174
+ };
175
+ }
176
+ normalizeStopReason(raw) {
177
+ if (raw === undefined)
178
+ return undefined;
179
+ switch (raw) {
180
+ case "stop": return "end_turn";
181
+ case "length": return "max_tokens";
182
+ default: return "other";
183
+ }
184
+ }
185
+ createNdjsonDecoder() {
186
+ return new OllamaNdjsonDecoder();
187
+ }
188
+ }
@@ -1,12 +1,17 @@
1
1
  import type { Message, RenderedContext, ToolSchema, StreamEvent, LLMProvider, RuntimePolicy } from "../types.js";
2
+ import { type CanonicalAdapterInput } from "./content-normalization.js";
3
+ type ResolvedOllamaRuntime = CanonicalAdapterInput["resolved"];
2
4
  export declare class OllamaProvider implements LLMProvider {
3
5
  private readonly model;
4
6
  private readonly baseUrl;
5
- constructor(model?: string, baseUrl?: string);
7
+ private readonly resolvedRuntimePolicy;
8
+ private resolvedRuntime?;
9
+ private readonly adapter;
10
+ constructor(model?: string, baseUrl?: string, resolvedRuntimePolicy?: RuntimePolicy, resolvedRuntime?: ResolvedOllamaRuntime | undefined);
6
11
  runtimePolicy(): RuntimePolicy;
7
- private toOllamaMessages;
8
- private buildTools;
9
- private requestExtensions;
12
+ bindResolvedRuntime(resolved: ResolvedOllamaRuntime): void;
13
+ private adapterInput;
10
14
  complete(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>): Promise<Message>;
11
15
  stream(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>): AsyncIterable<StreamEvent>;
12
16
  }
17
+ export {};
@@ -1,129 +1,116 @@
1
- import { normalizeToolCall, omitExtensionKeys, turnsWithStateAppended, UnsupportedModalityError } from "./base.js";
2
- // Prefix-based policy for local models (first match wins)
3
- const OLLAMA_PREFIX_POLICIES = [
4
- ["deepseek-r1", { maxTurns: 40 }],
5
- ["qwq", { maxTurns: 35 }],
6
- ["llama3.3", { maxTurns: 25 }],
7
- ["llama3.2", { maxTurns: 20 }],
8
- ["llama3.1", { maxTurns: 20 }],
9
- ["llama3", { maxTurns: 20 }],
10
- ["mistral", { maxTurns: 20 }],
11
- ["gemma2", { maxTurns: 20 }],
12
- ["phi4", { maxTurns: 20 }],
13
- ["phi3", { maxTurns: 15 }],
14
- ["codellama", { maxTurns: 20 }],
15
- ];
1
+ import { normalizeCanonicalAdapterInput, } from "./content-normalization.js";
2
+ import { endpointProfiles } from "./endpoints.js";
3
+ import { OllamaAdapter } from "./ollama-adapter.js";
4
+ import { classifyProviderError, ProviderError } from "./provider-error.js";
16
5
  export class OllamaProvider {
17
6
  model;
18
7
  baseUrl;
19
- constructor(model = "llama3", baseUrl = "http://localhost:11434") {
8
+ resolvedRuntimePolicy;
9
+ resolvedRuntime;
10
+ adapter = new OllamaAdapter();
11
+ constructor(model = "llama3", baseUrl = "http://localhost:11434", resolvedRuntimePolicy = {}, resolvedRuntime) {
20
12
  this.model = model;
21
13
  this.baseUrl = baseUrl;
14
+ this.resolvedRuntimePolicy = resolvedRuntimePolicy;
15
+ this.resolvedRuntime = resolvedRuntime;
22
16
  }
23
17
  runtimePolicy() {
24
- const m = this.model.toLowerCase();
25
- for (const [prefix, policy] of OLLAMA_PREFIX_POLICIES) {
26
- if (m.startsWith(prefix))
27
- return policy;
28
- }
29
- return { maxTurns: 20 };
18
+ return this.resolvedRuntimePolicy;
30
19
  }
31
- toOllamaMessages(context) {
32
- const result = [];
33
- if (context.systemText)
34
- result.push({ role: "system", content: context.systemText });
35
- for (const m of turnsWithStateAppended(context)) {
36
- const images = [];
37
- if (m.contentParts?.length) {
38
- for (const p of m.contentParts) {
39
- if (p.type === "image" && p.data)
40
- images.push(p.data);
41
- else if (p.type === "audio")
42
- throw new UnsupportedModalityError("audio", "ollama");
43
- }
44
- }
45
- result.push({ role: m.role, content: m.content, ...(images.length ? { images } : {}) });
20
+ bindResolvedRuntime(resolved) {
21
+ if (resolved.identity.protocol !== "ollama-chat"
22
+ || resolved.identity.modelId !== this.model) {
23
+ throw new Error("OllamaProvider received a mismatched resolved runtime");
46
24
  }
47
- return result;
25
+ this.resolvedRuntime = resolved;
48
26
  }
49
- buildTools(tools) {
50
- return tools.map(t => ({
51
- type: "function",
52
- function: { name: t.name, description: t.description, parameters: JSON.parse(t.parameters) },
53
- }));
54
- }
55
- requestExtensions(extensions) {
56
- return omitExtensionKeys(extensions, ["model", "messages", "tools", "stream"]);
27
+ adapterInput(context, tools, extensions) {
28
+ const resolved = this.resolvedRuntime ?? {
29
+ identity: {
30
+ providerId: "ollama",
31
+ modelId: this.model,
32
+ endpointId: "ollama.local",
33
+ protocol: "ollama-chat",
34
+ },
35
+ model: { id: `ollama/${this.model}`, providerId: "ollama", kind: "generation", intrinsic: {} },
36
+ endpoint: endpointProfiles["ollama.local"],
37
+ adapter: this,
38
+ effectiveCapabilities: {
39
+ inputModalities: Object.fromEntries(["text", "image", "audio", "video", "file"].map(modality => [modality, { state: modality === "audio" || modality === "video" || modality === "file" ? "unsupported" : "unknown", evidence: [] }])),
40
+ outputModalities: Object.fromEntries(["text", "image", "audio", "embedding"].map(modality => [modality, { state: "unknown", evidence: [] }])),
41
+ tools: { state: "unknown", evidence: [] },
42
+ reasoning: { state: "unknown", evidence: [] },
43
+ parallelToolCalls: { state: "unknown", evidence: [] },
44
+ structuredOutput: { state: "unknown", evidence: [] },
45
+ promptCaching: { state: "unknown", evidence: [] },
46
+ nativeTokenCounting: { state: "unknown", evidence: [] },
47
+ mediaForms: Object.fromEntries(["imageUrl", "imageBase64", "fileId", "audioUrl", "audioBase64"].map(form => [form, { state: "unknown", evidence: [] }])),
48
+ },
49
+ };
50
+ return normalizeCanonicalAdapterInput({ context, tools, resolved, extensions });
57
51
  }
58
52
  async complete(context, tools, extensions) {
59
- const resp = await fetch(`${this.baseUrl}/api/chat`, {
60
- method: "POST",
61
- headers: { "Content-Type": "application/json" },
62
- body: JSON.stringify({
63
- ...this.requestExtensions(extensions),
64
- model: this.model,
65
- messages: this.toOllamaMessages(context),
66
- ...(tools.length ? { tools: this.buildTools(tools) } : {}),
67
- stream: false,
68
- }),
69
- });
70
- if (!resp.ok)
71
- throw new Error(`Ollama error: ${resp.status}`);
72
- const data = await resp.json();
73
- return { role: "assistant", content: data.message.content };
53
+ try {
54
+ const input = this.adapterInput(context, tools, extensions);
55
+ const body = { ...this.adapter.buildRequest(input), stream: false };
56
+ const resp = await fetch(`${this.baseUrl}/api/chat`, {
57
+ method: "POST",
58
+ headers: { "Content-Type": "application/json" },
59
+ body: JSON.stringify(body),
60
+ });
61
+ if (!resp.ok)
62
+ throw ollamaHttpError(resp.status);
63
+ const data = await resp.json();
64
+ return this.adapter.decodeComplete(data, { input }).message;
65
+ }
66
+ catch (error) {
67
+ throw classifyProviderError("ollama", error);
68
+ }
74
69
  }
75
70
  async *stream(context, tools, extensions) {
76
- const resp = await fetch(`${this.baseUrl}/api/chat`, {
77
- method: "POST",
78
- headers: { "Content-Type": "application/json" },
79
- body: JSON.stringify({
80
- ...this.requestExtensions(extensions),
81
- model: this.model,
82
- messages: this.toOllamaMessages(context),
83
- ...(tools.length ? { tools: this.buildTools(tools) } : {}),
84
- stream: true,
85
- }),
86
- });
87
- if (!resp.ok)
88
- throw new Error(`Ollama error: ${resp.status}`);
89
- const reader = resp.body.getReader();
90
- const decoder = new TextDecoder();
91
- let buf = "";
92
- const pendingToolCalls = new Map();
93
- while (true) {
94
- const { done, value } = await reader.read();
95
- if (done)
96
- break;
97
- buf += decoder.decode(value, { stream: true });
98
- const lines = buf.split("\n");
99
- buf = lines.pop() ?? "";
100
- for (const line of lines) {
101
- if (!line.trim())
102
- continue;
103
- try {
104
- const chunk = JSON.parse(line);
105
- if (chunk.message?.content)
106
- yield { type: "text_delta", delta: chunk.message.content };
107
- for (const tc of chunk.message?.tool_calls ?? []) {
108
- const norm = normalizeToolCall("", tc.function.name, tc.function.arguments);
109
- if (!norm)
110
- continue;
111
- const args = JSON.parse(norm.arguments);
112
- const key = `${norm.name}:${norm.arguments}`;
113
- if (!pendingToolCalls.has(key)) {
114
- pendingToolCalls.set(key, {
115
- id: `call_${pendingToolCalls.size + 1}`,
116
- name: norm.name,
117
- arguments: args,
118
- });
119
- }
120
- }
71
+ try {
72
+ const input = this.adapterInput(context, tools, extensions);
73
+ const body = { ...this.adapter.buildRequest(input), stream: true };
74
+ const resp = await fetch(`${this.baseUrl}/api/chat`, {
75
+ method: "POST",
76
+ headers: { "Content-Type": "application/json" },
77
+ body: JSON.stringify(body),
78
+ });
79
+ if (!resp.ok)
80
+ throw ollamaHttpError(resp.status);
81
+ if (!resp.body) {
82
+ throw new ProviderError({
83
+ provider: "ollama",
84
+ kind: "protocol",
85
+ retryable: false,
86
+ message: "Ollama stream response has no body",
87
+ });
88
+ }
89
+ const reader = resp.body.getReader();
90
+ const decoder = new TextDecoder();
91
+ const ndjson = this.adapter.createNdjsonDecoder();
92
+ const state = this.adapter.createStreamState({ input });
93
+ while (true) {
94
+ const { done, value } = await reader.read();
95
+ if (done)
96
+ break;
97
+ for (const chunk of ndjson.push(decoder.decode(value, { stream: true }))) {
98
+ for (const event of this.adapter.pushStreamChunk(chunk, state).events)
99
+ yield event;
121
100
  }
122
- catch { /* skip malformed lines */ }
123
101
  }
102
+ for (const chunk of ndjson.finish(decoder.decode())) {
103
+ for (const event of this.adapter.pushStreamChunk(chunk, state).events)
104
+ yield event;
105
+ }
106
+ for (const event of this.adapter.finishStream(state, state.finalChunk).events)
107
+ yield event;
124
108
  }
125
- for (const tc of pendingToolCalls.values()) {
126
- yield { type: "tool_call", id: tc.id, name: tc.name, arguments: tc.arguments };
109
+ catch (error) {
110
+ throw classifyProviderError("ollama", error);
127
111
  }
128
112
  }
129
113
  }
114
+ function ollamaHttpError(status) {
115
+ return classifyProviderError("ollama", Object.assign(new Error(`Ollama error: ${status}`), { status }));
116
+ }
@@ -0,0 +1,154 @@
1
+ import type { ProviderDescriptor, ProviderReplay, ToolCall } from "../types.js";
2
+ import type { EndpointProfileId, ProviderId } from "./endpoints.js";
3
+ export interface OpenAIChatTurnReasoning {
4
+ reasoningContent: string;
5
+ reasoningDetails?: unknown;
6
+ nativeToolCalls: unknown[];
7
+ }
8
+ export type OpenAIChatReplayStrategy = "none" | "generic_stream" | "deepseek" | "minimax";
9
+ export interface OpenAIChatWireDialect {
10
+ readonly id: string;
11
+ readonly providerId: ProviderId;
12
+ readonly endpointId: EndpointProfileId;
13
+ readonly descriptor: {
14
+ reasoning: ProviderDescriptor["reasoning"];
15
+ };
16
+ readonly prepareExtensions: (extensions: Readonly<Record<string, unknown>>) => Record<string, unknown>;
17
+ readonly serverTools?: (extensions: Readonly<Record<string, unknown>>) => unknown[];
18
+ readonly cacheKey: "openai" | "none";
19
+ readonly inlineThinkingTags: boolean;
20
+ readonly exposeReasoning: (extensions: Readonly<Record<string, unknown>>) => boolean;
21
+ readonly requireReasoningReplay: (extensions: Readonly<Record<string, unknown>>) => boolean;
22
+ readonly replay: OpenAIChatReplayStrategy;
23
+ }
24
+ export declare const openAIChatDialects: {
25
+ readonly openai: {
26
+ readonly id: "openai";
27
+ readonly providerId: "openai";
28
+ readonly endpointId: "openai.chat";
29
+ readonly descriptor: {
30
+ readonly reasoning: {
31
+ supported: boolean;
32
+ preserveAcrossToolTurns: boolean;
33
+ requiresReplayForToolTurns?: boolean;
34
+ };
35
+ };
36
+ readonly prepareExtensions: (extensions: Readonly<Record<string, unknown>>) => Record<string, unknown>;
37
+ readonly cacheKey: "openai";
38
+ readonly inlineThinkingTags: true;
39
+ readonly exposeReasoning: () => boolean;
40
+ readonly requireReasoningReplay: () => boolean;
41
+ readonly replay: "generic_stream";
42
+ };
43
+ readonly deepseek: {
44
+ readonly id: "deepseek";
45
+ readonly providerId: "deepseek";
46
+ readonly endpointId: "deepseek.openai";
47
+ readonly descriptor: {
48
+ readonly reasoning: {
49
+ supported: boolean;
50
+ preserveAcrossToolTurns: boolean;
51
+ requiresReplayForToolTurns?: boolean;
52
+ };
53
+ };
54
+ readonly prepareExtensions: (extensions: Readonly<Record<string, unknown>>) => {
55
+ __deepstrikeThinkingEnabled: boolean;
56
+ reasoning_effort: string;
57
+ extra_body: {
58
+ thinking: {
59
+ type: string;
60
+ };
61
+ };
62
+ degradeMissingReasoningReplay?: boolean | undefined;
63
+ };
64
+ readonly cacheKey: "none";
65
+ readonly inlineThinkingTags: false;
66
+ readonly exposeReasoning: (extensions: Readonly<Record<string, unknown>>) => boolean;
67
+ readonly requireReasoningReplay: (extensions: Readonly<Record<string, unknown>>) => boolean;
68
+ readonly replay: "deepseek";
69
+ };
70
+ readonly kimi: {
71
+ readonly id: "kimi";
72
+ readonly providerId: "kimi";
73
+ readonly endpointId: "kimi.openai";
74
+ readonly descriptor: {
75
+ readonly reasoning: {
76
+ supported: boolean;
77
+ preserveAcrossToolTurns: boolean;
78
+ requiresReplayForToolTurns?: boolean;
79
+ };
80
+ };
81
+ readonly prepareExtensions: (extensions: Readonly<Record<string, unknown>>) => Record<string, unknown>;
82
+ readonly cacheKey: "openai";
83
+ readonly inlineThinkingTags: true;
84
+ readonly exposeReasoning: () => boolean;
85
+ readonly requireReasoningReplay: () => boolean;
86
+ readonly replay: "generic_stream";
87
+ };
88
+ readonly qwen: {
89
+ readonly id: "qwen";
90
+ readonly providerId: "qwen";
91
+ readonly endpointId: "qwen.dashscope";
92
+ readonly descriptor: {
93
+ readonly reasoning: {
94
+ supported: boolean;
95
+ preserveAcrossToolTurns: boolean;
96
+ requiresReplayForToolTurns?: boolean;
97
+ };
98
+ };
99
+ readonly prepareExtensions: (extensions: Readonly<Record<string, unknown>>) => {
100
+ extra_body?: Record<string, unknown> | undefined;
101
+ };
102
+ readonly cacheKey: "none";
103
+ readonly inlineThinkingTags: false;
104
+ readonly exposeReasoning: () => boolean;
105
+ readonly requireReasoningReplay: () => boolean;
106
+ readonly replay: "generic_stream";
107
+ };
108
+ readonly glm: {
109
+ readonly id: "glm";
110
+ readonly providerId: "glm";
111
+ readonly endpointId: "glm.openai";
112
+ readonly descriptor: {
113
+ readonly reasoning: {
114
+ supported: boolean;
115
+ preserveAcrossToolTurns: boolean;
116
+ requiresReplayForToolTurns?: boolean;
117
+ };
118
+ };
119
+ readonly prepareExtensions: (extensions: Readonly<Record<string, unknown>>) => Record<string, unknown>;
120
+ readonly serverTools: (extensions: Readonly<Record<string, unknown>>) => {
121
+ type: string;
122
+ web_search: object;
123
+ }[];
124
+ readonly cacheKey: "openai";
125
+ readonly inlineThinkingTags: true;
126
+ readonly exposeReasoning: () => boolean;
127
+ readonly requireReasoningReplay: () => boolean;
128
+ readonly replay: "generic_stream";
129
+ };
130
+ readonly minimax: {
131
+ readonly id: "minimax";
132
+ readonly providerId: "minimax";
133
+ readonly endpointId: "minimax.openai";
134
+ readonly descriptor: {
135
+ readonly reasoning: {
136
+ supported: boolean;
137
+ preserveAcrossToolTurns: boolean;
138
+ requiresReplayForToolTurns?: boolean;
139
+ };
140
+ };
141
+ readonly prepareExtensions: (extensions: Readonly<Record<string, unknown>>) => {
142
+ __deepstrikeThinkingEnabled: boolean;
143
+ reasoning_split: boolean;
144
+ degradeMissingReasoningReplay?: boolean | undefined;
145
+ };
146
+ readonly cacheKey: "none";
147
+ readonly inlineThinkingTags: false;
148
+ readonly exposeReasoning: (extensions: Readonly<Record<string, unknown>>) => boolean;
149
+ readonly requireReasoningReplay: (extensions: Readonly<Record<string, unknown>>) => boolean;
150
+ readonly replay: "minimax";
151
+ };
152
+ };
153
+ export type OpenAIChatDialectId = keyof typeof openAIChatDialects;
154
+ export declare function replayForTurn(dialect: OpenAIChatWireDialect, phase: "complete" | "stream", model: string, content: string, toolCalls: ToolCall[], reasoning: OpenAIChatTurnReasoning): ProviderReplay | undefined;