@lihuu/dsh-ollama-cloud 0.1.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.
- package/README.md +70 -0
- package/cordis.patch.yml +8 -0
- package/dist/index.js +837 -0
- package/lib/adapter.d.ts +111 -0
- package/lib/index.d.ts +62 -0
- package/lib/serialize.d.ts +44 -0
- package/lib/sse.d.ts +23 -0
- package/lib/translate.d.ts +32 -0
- package/lib/types.d.ts +143 -0
- package/package.json +52 -0
- package/src/adapter.ts +423 -0
- package/src/index.ts +214 -0
- package/src/serialize.ts +215 -0
- package/src/sse.ts +40 -0
- package/src/translate.ts +182 -0
- package/src/types.ts +148 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,837 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { credentialRef } from "@deepseek-ai/dsh-credentials";
|
|
3
|
+
import { assertUsableApiKey, LlmError as LlmError5, resolveRetryPolicy } from "@deepseek-ai/dsh-llm";
|
|
4
|
+
|
|
5
|
+
// src/adapter.ts
|
|
6
|
+
import {
|
|
7
|
+
attributionHeaders,
|
|
8
|
+
contentHasImage as contentHasImage2,
|
|
9
|
+
CONTEXT_WINDOW_EXCEEDED_CODE,
|
|
10
|
+
isContextWindowExceededError,
|
|
11
|
+
isQuotaExceededError,
|
|
12
|
+
LlmAdapter,
|
|
13
|
+
LlmError as LlmError4,
|
|
14
|
+
ProviderRequestId,
|
|
15
|
+
QUOTA_EXCEEDED_CODE,
|
|
16
|
+
ReasoningEffortId
|
|
17
|
+
} from "@deepseek-ai/dsh-llm";
|
|
18
|
+
|
|
19
|
+
// src/serialize.ts
|
|
20
|
+
import { contentHasImage, LlmError } from "@deepseek-ai/dsh-llm";
|
|
21
|
+
function passReasoning(model) {
|
|
22
|
+
return model.includes("deepseek");
|
|
23
|
+
}
|
|
24
|
+
function reasoningEffort(effort) {
|
|
25
|
+
if (effort === "off" || effort === "low" || effort === "high" || effort === "max") {
|
|
26
|
+
return effort;
|
|
27
|
+
}
|
|
28
|
+
throw new LlmError(
|
|
29
|
+
`Ollama does not support reasoning effort "${effort}"`,
|
|
30
|
+
"UNSUPPORTED_REASONING_EFFORT"
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
function resolveThinking(options, defaults) {
|
|
34
|
+
if (options.purpose === "session-title") return { reasoningEffort: "none" };
|
|
35
|
+
const effort = options.reasoningEffort === void 0 ? defaults.reasoningEffort : reasoningEffort(options.reasoningEffort);
|
|
36
|
+
if (defaults.thinking === "disabled" && effort !== void 0 && effort !== "off") {
|
|
37
|
+
throw new LlmError(
|
|
38
|
+
`Ollama deployment does not support reasoning effort "${effort}"`,
|
|
39
|
+
"UNSUPPORTED_REASONING_EFFORT"
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
if (effort === "off") return { reasoningEffort: "none" };
|
|
43
|
+
if (effort === "low" || effort === "high" || effort === "max") {
|
|
44
|
+
return { reasoningEffort: effort };
|
|
45
|
+
}
|
|
46
|
+
return defaults.thinking === "disabled" ? { reasoningEffort: "none" } : {};
|
|
47
|
+
}
|
|
48
|
+
function flattenText(blocks) {
|
|
49
|
+
return blocks.filter((block) => block.type === "text").map((block) => block.text).join("");
|
|
50
|
+
}
|
|
51
|
+
function assertTextOnly(blocks) {
|
|
52
|
+
if (contentHasImage(blocks)) {
|
|
53
|
+
throw new LlmError("The Ollama chat-completions adapter does not support image content yet.", "UNSUPPORTED_CONTENT");
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function serializeAssistant(message, model) {
|
|
57
|
+
const text = flattenText(message.content);
|
|
58
|
+
const reasoning = message.content.filter((block) => block.type === "reasoning").map((block) => block.text).join("");
|
|
59
|
+
const toolCalls = message.content.filter((block) => block.type === "tool-call").map((block) => ({
|
|
60
|
+
id: block.id,
|
|
61
|
+
type: "function",
|
|
62
|
+
function: { name: block.name, arguments: block.arguments }
|
|
63
|
+
}));
|
|
64
|
+
return {
|
|
65
|
+
role: "assistant",
|
|
66
|
+
// Text-less turns send "" — NEVER null. Reasoning-only turns (the model
|
|
67
|
+
// can answer entirely in the reasoning channel) risk a gateway 400, and
|
|
68
|
+
// since the message sits durably in the session log, a null here bricks
|
|
69
|
+
// every later turn of that session.
|
|
70
|
+
content: text,
|
|
71
|
+
// CoT passback only for reasoning-capable models, via the `reasoning`
|
|
72
|
+
// field Ollama accepts on assistant history.
|
|
73
|
+
...passReasoning(model) && reasoning.length > 0 ? { reasoning } : {},
|
|
74
|
+
...toolCalls.length > 0 ? { tool_calls: toolCalls } : {}
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function serializeMessages(model, messages) {
|
|
78
|
+
const wire = [];
|
|
79
|
+
for (const message of messages) {
|
|
80
|
+
assertTextOnly(message.content);
|
|
81
|
+
if (message.role === "system") {
|
|
82
|
+
wire.push({ role: "system", content: flattenText(message.content) });
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
if (message.role === "assistant") {
|
|
86
|
+
wire.push(serializeAssistant(message, model));
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
const toolResults = message.content.filter((block) => block.type === "tool-result");
|
|
90
|
+
const text = flattenText(message.content);
|
|
91
|
+
if (text.length > 0 || toolResults.length === 0) {
|
|
92
|
+
wire.push({ role: "user", content: text });
|
|
93
|
+
}
|
|
94
|
+
for (const result of toolResults) {
|
|
95
|
+
wire.push({
|
|
96
|
+
role: "tool",
|
|
97
|
+
tool_call_id: result.toolCallId,
|
|
98
|
+
// Empty tool output still needs SOME content on the wire.
|
|
99
|
+
content: flattenText(result.content) || "(no output)"
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return wire;
|
|
104
|
+
}
|
|
105
|
+
function requestWithMessages(options, messages, defaults) {
|
|
106
|
+
const tools = options.tools?.map((tool) => ({
|
|
107
|
+
type: "function",
|
|
108
|
+
function: {
|
|
109
|
+
name: tool.name,
|
|
110
|
+
description: tool.description,
|
|
111
|
+
parameters: tool.parameters
|
|
112
|
+
}
|
|
113
|
+
}));
|
|
114
|
+
const resolvedThinking = resolveThinking(options, defaults);
|
|
115
|
+
return {
|
|
116
|
+
model: options.model,
|
|
117
|
+
messages,
|
|
118
|
+
stream: true,
|
|
119
|
+
stream_options: { include_usage: true },
|
|
120
|
+
...resolvedThinking.reasoningEffort !== void 0 ? { reasoning_effort: resolvedThinking.reasoningEffort } : {},
|
|
121
|
+
...tools !== void 0 && tools.length > 0 ? { tools } : {},
|
|
122
|
+
...options.temperature !== void 0 ? { temperature: options.temperature } : {},
|
|
123
|
+
...options.maxTokens === void 0 ? {} : { max_tokens: options.maxTokens },
|
|
124
|
+
...options.stop !== void 0 ? { stop: options.stop } : {}
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
function serializeRequest(options, defaults = {}) {
|
|
128
|
+
const messages = [];
|
|
129
|
+
if (options.system !== void 0) {
|
|
130
|
+
messages.push({ role: "system", content: options.system });
|
|
131
|
+
}
|
|
132
|
+
messages.push(...serializeMessages(options.model, options.messages));
|
|
133
|
+
return requestWithMessages(options, messages, defaults);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// ../../../git/deepseek-harness/node_modules/.pnpm/eventsource-parser@3.1.0/node_modules/eventsource-parser/dist/index.js
|
|
137
|
+
var ParseError = class extends Error {
|
|
138
|
+
constructor(message, options) {
|
|
139
|
+
super(message), this.name = "ParseError", this.type = options.type, this.field = options.field, this.value = options.value, this.line = options.line;
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
var LF = 10;
|
|
143
|
+
var CR = 13;
|
|
144
|
+
var SPACE = 32;
|
|
145
|
+
function noop(_arg) {
|
|
146
|
+
}
|
|
147
|
+
function createParser(config) {
|
|
148
|
+
if (typeof config == "function")
|
|
149
|
+
throw new TypeError(
|
|
150
|
+
"`config` must be an object, got a function instead. Did you mean `createParser({onEvent: fn})`?"
|
|
151
|
+
);
|
|
152
|
+
const { onEvent = noop, onError = noop, onRetry = noop, onComment, maxBufferSize } = config, pendingFragments = [];
|
|
153
|
+
let pendingFragmentsLength = 0, isFirstChunk = true, id, data = "", dataLines = 0, eventType, terminated = false;
|
|
154
|
+
function feed(chunk) {
|
|
155
|
+
if (terminated)
|
|
156
|
+
throw new Error(
|
|
157
|
+
"Cannot feed parser: it was terminated after exceeding the configured max buffer size. Call `reset()` to resume parsing."
|
|
158
|
+
);
|
|
159
|
+
if (isFirstChunk && (isFirstChunk = false, chunk.charCodeAt(0) === 239 && chunk.charCodeAt(1) === 187 && chunk.charCodeAt(2) === 191 && (chunk = chunk.slice(3))), pendingFragments.length === 0) {
|
|
160
|
+
const trailing2 = processLines(chunk);
|
|
161
|
+
trailing2 !== "" && (pendingFragments.push(trailing2), pendingFragmentsLength = trailing2.length), checkBufferSize();
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
if (chunk.indexOf(`
|
|
165
|
+
`) === -1 && chunk.indexOf("\r") === -1) {
|
|
166
|
+
pendingFragments.push(chunk), pendingFragmentsLength += chunk.length, checkBufferSize();
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
pendingFragments.push(chunk);
|
|
170
|
+
const input = pendingFragments.join("");
|
|
171
|
+
pendingFragments.length = 0, pendingFragmentsLength = 0;
|
|
172
|
+
const trailing = processLines(input);
|
|
173
|
+
trailing !== "" && (pendingFragments.push(trailing), pendingFragmentsLength = trailing.length), checkBufferSize();
|
|
174
|
+
}
|
|
175
|
+
function checkBufferSize() {
|
|
176
|
+
maxBufferSize !== void 0 && (pendingFragmentsLength + data.length <= maxBufferSize || (terminated = true, pendingFragments.length = 0, pendingFragmentsLength = 0, id = void 0, data = "", dataLines = 0, eventType = void 0, onError(
|
|
177
|
+
new ParseError(`Buffered data exceeded max buffer size of ${maxBufferSize} characters`, {
|
|
178
|
+
type: "max-buffer-size-exceeded"
|
|
179
|
+
})
|
|
180
|
+
)));
|
|
181
|
+
}
|
|
182
|
+
function processLines(chunk) {
|
|
183
|
+
let searchIndex = 0;
|
|
184
|
+
if (chunk.indexOf("\r") === -1) {
|
|
185
|
+
let lfIndex = chunk.indexOf(`
|
|
186
|
+
`, searchIndex);
|
|
187
|
+
for (; lfIndex !== -1; ) {
|
|
188
|
+
if (searchIndex === lfIndex) {
|
|
189
|
+
dataLines > 0 && onEvent({ id, event: eventType, data }), id = void 0, data = "", dataLines = 0, eventType = void 0, searchIndex = lfIndex + 1, lfIndex = chunk.indexOf(`
|
|
190
|
+
`, searchIndex);
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
const firstCharCode = chunk.charCodeAt(searchIndex);
|
|
194
|
+
if (isDataPrefix(chunk, searchIndex, firstCharCode)) {
|
|
195
|
+
const valueStart = chunk.charCodeAt(searchIndex + 5) === SPACE ? searchIndex + 6 : searchIndex + 5, value = chunk.slice(valueStart, lfIndex);
|
|
196
|
+
if (dataLines === 0 && chunk.charCodeAt(lfIndex + 1) === LF) {
|
|
197
|
+
onEvent({ id, event: eventType, data: value }), id = void 0, data = "", eventType = void 0, searchIndex = lfIndex + 2, lfIndex = chunk.indexOf(`
|
|
198
|
+
`, searchIndex);
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
data = dataLines === 0 ? value : `${data}
|
|
202
|
+
${value}`, dataLines++;
|
|
203
|
+
} else isEventPrefix(chunk, searchIndex, firstCharCode) ? eventType = chunk.slice(
|
|
204
|
+
chunk.charCodeAt(searchIndex + 6) === SPACE ? searchIndex + 7 : searchIndex + 6,
|
|
205
|
+
lfIndex
|
|
206
|
+
) || void 0 : parseLine(chunk, searchIndex, lfIndex);
|
|
207
|
+
searchIndex = lfIndex + 1, lfIndex = chunk.indexOf(`
|
|
208
|
+
`, searchIndex);
|
|
209
|
+
}
|
|
210
|
+
return chunk.slice(searchIndex);
|
|
211
|
+
}
|
|
212
|
+
for (; searchIndex < chunk.length; ) {
|
|
213
|
+
const crIndex = chunk.indexOf("\r", searchIndex), lfIndex = chunk.indexOf(`
|
|
214
|
+
`, searchIndex);
|
|
215
|
+
let lineEnd = -1;
|
|
216
|
+
if (crIndex !== -1 && lfIndex !== -1 ? lineEnd = crIndex < lfIndex ? crIndex : lfIndex : crIndex !== -1 ? crIndex === chunk.length - 1 ? lineEnd = -1 : lineEnd = crIndex : lfIndex !== -1 && (lineEnd = lfIndex), lineEnd === -1)
|
|
217
|
+
break;
|
|
218
|
+
parseLine(chunk, searchIndex, lineEnd), searchIndex = lineEnd + 1, chunk.charCodeAt(searchIndex - 1) === CR && chunk.charCodeAt(searchIndex) === LF && searchIndex++;
|
|
219
|
+
}
|
|
220
|
+
return chunk.slice(searchIndex);
|
|
221
|
+
}
|
|
222
|
+
function parseLine(chunk, start, end) {
|
|
223
|
+
if (start === end) {
|
|
224
|
+
dispatchEvent();
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
const firstCharCode = chunk.charCodeAt(start);
|
|
228
|
+
if (isDataPrefix(chunk, start, firstCharCode)) {
|
|
229
|
+
const valueStart = chunk.charCodeAt(start + 5) === SPACE ? start + 6 : start + 5, value2 = chunk.slice(valueStart, end);
|
|
230
|
+
data = dataLines === 0 ? value2 : `${data}
|
|
231
|
+
${value2}`, dataLines++;
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
if (isEventPrefix(chunk, start, firstCharCode)) {
|
|
235
|
+
eventType = chunk.slice(chunk.charCodeAt(start + 6) === SPACE ? start + 7 : start + 6, end) || void 0;
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
if (firstCharCode === 105 && chunk.charCodeAt(start + 1) === 100 && chunk.charCodeAt(start + 2) === 58) {
|
|
239
|
+
const value2 = chunk.slice(chunk.charCodeAt(start + 3) === SPACE ? start + 4 : start + 3, end);
|
|
240
|
+
id = value2.includes("\0") ? void 0 : value2;
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
if (firstCharCode === 58) {
|
|
244
|
+
if (onComment) {
|
|
245
|
+
const line2 = chunk.slice(start, end);
|
|
246
|
+
onComment(line2.slice(chunk.charCodeAt(start + 1) === SPACE ? 2 : 1));
|
|
247
|
+
}
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
const line = chunk.slice(start, end), fieldSeparatorIndex = line.indexOf(":");
|
|
251
|
+
if (fieldSeparatorIndex === -1) {
|
|
252
|
+
processField(line, "", line);
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
const field = line.slice(0, fieldSeparatorIndex), offset = line.charCodeAt(fieldSeparatorIndex + 1) === SPACE ? 2 : 1, value = line.slice(fieldSeparatorIndex + offset);
|
|
256
|
+
processField(field, value, line);
|
|
257
|
+
}
|
|
258
|
+
function processField(field, value, line) {
|
|
259
|
+
switch (field) {
|
|
260
|
+
case "event":
|
|
261
|
+
eventType = value || void 0;
|
|
262
|
+
break;
|
|
263
|
+
case "data":
|
|
264
|
+
data = dataLines === 0 ? value : `${data}
|
|
265
|
+
${value}`, dataLines++;
|
|
266
|
+
break;
|
|
267
|
+
case "id":
|
|
268
|
+
id = value.includes("\0") ? void 0 : value;
|
|
269
|
+
break;
|
|
270
|
+
case "retry":
|
|
271
|
+
/^\d+$/.test(value) ? onRetry(parseInt(value, 10)) : onError(
|
|
272
|
+
new ParseError(`Invalid \`retry\` value: "${value}"`, {
|
|
273
|
+
type: "invalid-retry",
|
|
274
|
+
value,
|
|
275
|
+
line
|
|
276
|
+
})
|
|
277
|
+
);
|
|
278
|
+
break;
|
|
279
|
+
default:
|
|
280
|
+
onError(
|
|
281
|
+
new ParseError(
|
|
282
|
+
`Unknown field "${field.length > 20 ? `${field.slice(0, 20)}\u2026` : field}"`,
|
|
283
|
+
{ type: "unknown-field", field, value, line }
|
|
284
|
+
)
|
|
285
|
+
);
|
|
286
|
+
break;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
function dispatchEvent() {
|
|
290
|
+
dataLines > 0 && onEvent({
|
|
291
|
+
id,
|
|
292
|
+
event: eventType,
|
|
293
|
+
data
|
|
294
|
+
}), id = void 0, data = "", dataLines = 0, eventType = void 0;
|
|
295
|
+
}
|
|
296
|
+
function reset(options = {}) {
|
|
297
|
+
if (options.consume && pendingFragments.length > 0) {
|
|
298
|
+
const incompleteLine = pendingFragments.join("");
|
|
299
|
+
parseLine(incompleteLine, 0, incompleteLine.length);
|
|
300
|
+
}
|
|
301
|
+
isFirstChunk = true, id = void 0, data = "", dataLines = 0, eventType = void 0, pendingFragments.length = 0, pendingFragmentsLength = 0, terminated = false;
|
|
302
|
+
}
|
|
303
|
+
return { feed, reset };
|
|
304
|
+
}
|
|
305
|
+
function isDataPrefix(chunk, i, firstCharCode) {
|
|
306
|
+
return firstCharCode === 100 && chunk.charCodeAt(i + 1) === 97 && chunk.charCodeAt(i + 2) === 116 && chunk.charCodeAt(i + 3) === 97 && chunk.charCodeAt(i + 4) === 58;
|
|
307
|
+
}
|
|
308
|
+
function isEventPrefix(chunk, i, firstCharCode) {
|
|
309
|
+
return firstCharCode === 101 && chunk.charCodeAt(i + 1) === 118 && chunk.charCodeAt(i + 2) === 101 && chunk.charCodeAt(i + 3) === 110 && chunk.charCodeAt(i + 4) === 116 && chunk.charCodeAt(i + 5) === 58;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// ../../../git/deepseek-harness/node_modules/.pnpm/eventsource-parser@3.1.0/node_modules/eventsource-parser/dist/stream.js
|
|
313
|
+
var EventSourceParserStream = class extends TransformStream {
|
|
314
|
+
constructor({ onError, onRetry, onComment, maxBufferSize } = {}) {
|
|
315
|
+
let parser;
|
|
316
|
+
super({
|
|
317
|
+
start(controller) {
|
|
318
|
+
parser = createParser({
|
|
319
|
+
onEvent: (event) => {
|
|
320
|
+
controller.enqueue(event);
|
|
321
|
+
},
|
|
322
|
+
onError(error) {
|
|
323
|
+
typeof onError == "function" && onError(error), (onError === "terminate" || error.type === "max-buffer-size-exceeded") && controller.error(error);
|
|
324
|
+
},
|
|
325
|
+
onRetry,
|
|
326
|
+
onComment,
|
|
327
|
+
maxBufferSize
|
|
328
|
+
});
|
|
329
|
+
},
|
|
330
|
+
transform(chunk) {
|
|
331
|
+
parser.feed(chunk);
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
// src/sse.ts
|
|
338
|
+
import { LlmError as LlmError2 } from "@deepseek-ai/dsh-llm";
|
|
339
|
+
var DONE = "[DONE]";
|
|
340
|
+
async function* parseSse(stream, onComment) {
|
|
341
|
+
const events = stream.pipeThrough(new TextDecoderStream()).pipeThrough(new EventSourceParserStream({ onComment }));
|
|
342
|
+
for await (const { data } of events) {
|
|
343
|
+
yield data;
|
|
344
|
+
if (data === DONE) return;
|
|
345
|
+
}
|
|
346
|
+
throw new LlmError2("SSE stream ended without [DONE]", "STREAM_CLOSED");
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// src/translate.ts
|
|
350
|
+
import { ToolCallId, EMPTY_RESPONSE_CODE, LlmError as LlmError3 } from "@deepseek-ai/dsh-llm";
|
|
351
|
+
function mapFinishReason(reason) {
|
|
352
|
+
switch (reason) {
|
|
353
|
+
case "stop":
|
|
354
|
+
return { kind: "stop" };
|
|
355
|
+
case "tool_calls":
|
|
356
|
+
return { kind: "tool-calls" };
|
|
357
|
+
case "length":
|
|
358
|
+
return { kind: "max-tokens" };
|
|
359
|
+
default:
|
|
360
|
+
return {
|
|
361
|
+
kind: "error",
|
|
362
|
+
failure: { message: `model stopped: ${reason}`, code: reason.toUpperCase() }
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
function mapUsage(usage) {
|
|
367
|
+
const cacheRead = usage.prompt_tokens_details?.cached_tokens;
|
|
368
|
+
const reasoning = usage.completion_tokens_details?.reasoning_tokens;
|
|
369
|
+
return {
|
|
370
|
+
inputTokens: usage.prompt_tokens - (cacheRead ?? 0),
|
|
371
|
+
outputTokens: usage.completion_tokens,
|
|
372
|
+
...cacheRead !== void 0 ? { cacheReadTokens: cacheRead } : {},
|
|
373
|
+
...reasoning !== void 0 ? { reasoningTokens: reasoning } : {}
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
function closeBlock(block) {
|
|
377
|
+
switch (block.kind) {
|
|
378
|
+
case "text":
|
|
379
|
+
return { type: "text", text: block.text };
|
|
380
|
+
case "reasoning":
|
|
381
|
+
return { type: "reasoning", text: block.text };
|
|
382
|
+
case "tool-call":
|
|
383
|
+
return {
|
|
384
|
+
type: "tool-call",
|
|
385
|
+
id: ToolCallId(block.callId ?? ""),
|
|
386
|
+
name: block.name ?? "",
|
|
387
|
+
arguments: block.text
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
async function* translate(payloads) {
|
|
392
|
+
let nextIndex = 0;
|
|
393
|
+
let textBlock;
|
|
394
|
+
let reasoningBlock;
|
|
395
|
+
const toolBlocks = /* @__PURE__ */ new Map();
|
|
396
|
+
const order = [];
|
|
397
|
+
let pendingFinish;
|
|
398
|
+
let pendingUsage;
|
|
399
|
+
function open(kind) {
|
|
400
|
+
const block = { index: nextIndex++, kind, text: "" };
|
|
401
|
+
order.push(block);
|
|
402
|
+
return block;
|
|
403
|
+
}
|
|
404
|
+
for await (const payload of payloads) {
|
|
405
|
+
if (payload === DONE) {
|
|
406
|
+
for (const block of order) {
|
|
407
|
+
yield { type: "block-end", index: block.index, block: closeBlock(block) };
|
|
408
|
+
}
|
|
409
|
+
if (pendingUsage) yield { type: "usage", usage: pendingUsage };
|
|
410
|
+
const reason = pendingFinish ?? { kind: "stop" };
|
|
411
|
+
yield {
|
|
412
|
+
type: "finish",
|
|
413
|
+
reason: reason.kind === "stop" && order.length === 0 ? {
|
|
414
|
+
kind: "error",
|
|
415
|
+
failure: { message: "model returned a completed response with no content", code: EMPTY_RESPONSE_CODE }
|
|
416
|
+
} : reason
|
|
417
|
+
};
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
let chunk;
|
|
421
|
+
try {
|
|
422
|
+
chunk = JSON.parse(payload);
|
|
423
|
+
} catch {
|
|
424
|
+
throw new LlmError3(`malformed SSE payload: ${payload.slice(0, 120)}`, "MALFORMED_RESPONSE");
|
|
425
|
+
}
|
|
426
|
+
for (const choice of chunk.choices ?? []) {
|
|
427
|
+
const delta = choice.delta;
|
|
428
|
+
const reasoning = delta?.reasoning;
|
|
429
|
+
if (typeof reasoning === "string" && reasoning.length > 0) {
|
|
430
|
+
if (!reasoningBlock) {
|
|
431
|
+
reasoningBlock = open("reasoning");
|
|
432
|
+
yield { type: "block-start", index: reasoningBlock.index, blockType: "reasoning" };
|
|
433
|
+
}
|
|
434
|
+
reasoningBlock.text += reasoning;
|
|
435
|
+
yield { type: "reasoning-delta", index: reasoningBlock.index, text: reasoning };
|
|
436
|
+
}
|
|
437
|
+
const content = delta?.content;
|
|
438
|
+
if (typeof content === "string" && content.length > 0) {
|
|
439
|
+
if (!textBlock) {
|
|
440
|
+
textBlock = open("text");
|
|
441
|
+
yield { type: "block-start", index: textBlock.index, blockType: "text" };
|
|
442
|
+
}
|
|
443
|
+
textBlock.text += content;
|
|
444
|
+
yield { type: "text-delta", index: textBlock.index, text: content };
|
|
445
|
+
}
|
|
446
|
+
for (const call of delta?.tool_calls ?? []) {
|
|
447
|
+
let block = toolBlocks.get(call.index);
|
|
448
|
+
if (!block) {
|
|
449
|
+
block = open("tool-call");
|
|
450
|
+
toolBlocks.set(call.index, block);
|
|
451
|
+
yield { type: "block-start", index: block.index, blockType: "tool-call" };
|
|
452
|
+
}
|
|
453
|
+
if (call.id !== void 0) block.callId = call.id;
|
|
454
|
+
if (call.function?.name !== void 0) block.name = call.function.name;
|
|
455
|
+
const fragment = call.function?.arguments ?? "";
|
|
456
|
+
block.text += fragment;
|
|
457
|
+
yield {
|
|
458
|
+
type: "tool-call-delta",
|
|
459
|
+
index: block.index,
|
|
460
|
+
id: ToolCallId(block.callId ?? ""),
|
|
461
|
+
...block.name !== void 0 ? { name: block.name } : {},
|
|
462
|
+
argumentsDelta: fragment
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
if (typeof choice.finish_reason === "string") {
|
|
466
|
+
pendingFinish = mapFinishReason(choice.finish_reason);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
if (chunk.usage) pendingUsage = mapUsage(chunk.usage);
|
|
470
|
+
}
|
|
471
|
+
throw new LlmError3("SSE payload stream ended without [DONE]", "STREAM_CLOSED");
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// src/adapter.ts
|
|
475
|
+
var DEFAULT_STREAM_IDLE_TIMEOUT_MS = 3e5;
|
|
476
|
+
var DEFAULT_CONTEXT_WINDOW = 1e6;
|
|
477
|
+
var DEFAULT_MAX_TOKENS = 65536;
|
|
478
|
+
var CLOUD_SUFFIX = ":cloud";
|
|
479
|
+
var MAX_TIMER_DELAY_MS = 2147483647;
|
|
480
|
+
var STREAM_IDLE_TIMEOUT_CODE = "LLM_STREAM_IDLE_TIMEOUT";
|
|
481
|
+
var OFF_REASONING_EFFORT = ReasoningEffortId("off");
|
|
482
|
+
var LOW_REASONING_EFFORT = ReasoningEffortId("low");
|
|
483
|
+
var HIGH_REASONING_EFFORT = ReasoningEffortId("high");
|
|
484
|
+
var MAX_REASONING_EFFORT = ReasoningEffortId("max");
|
|
485
|
+
var REASONING_EFFORTS = [
|
|
486
|
+
{ id: OFF_REASONING_EFFORT, name: "Off" },
|
|
487
|
+
{ id: LOW_REASONING_EFFORT, name: "Low" },
|
|
488
|
+
{ id: HIGH_REASONING_EFFORT, name: "High" },
|
|
489
|
+
{ id: MAX_REASONING_EFFORT, name: "Max" }
|
|
490
|
+
];
|
|
491
|
+
var OFF_ONLY_REASONING_EFFORTS = [
|
|
492
|
+
{ id: OFF_REASONING_EFFORT, name: "Off" }
|
|
493
|
+
];
|
|
494
|
+
function normalizeCloud(model) {
|
|
495
|
+
return model.endsWith(CLOUD_SUFFIX) ? model : `${model}${CLOUD_SUFFIX}`;
|
|
496
|
+
}
|
|
497
|
+
var IdleWatchdog = class {
|
|
498
|
+
constructor(upstream, timeoutMs) {
|
|
499
|
+
this.timeoutMs = timeoutMs;
|
|
500
|
+
this.signal = upstream.aborted ? upstream : AbortSignal.any([upstream, this.controller.signal]);
|
|
501
|
+
if (!upstream.aborted) {
|
|
502
|
+
upstream.addEventListener("abort", () => this.stop(), { once: true });
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
timeoutMs;
|
|
506
|
+
controller = new AbortController();
|
|
507
|
+
timer;
|
|
508
|
+
expired = false;
|
|
509
|
+
/** Combined caller + watchdog signal; aborts when either fires. */
|
|
510
|
+
signal;
|
|
511
|
+
get didExpire() {
|
|
512
|
+
return this.expired;
|
|
513
|
+
}
|
|
514
|
+
arm() {
|
|
515
|
+
this.stop();
|
|
516
|
+
this.timer = setTimeout(() => {
|
|
517
|
+
this.expired = true;
|
|
518
|
+
this.controller.abort(new Error(STREAM_IDLE_TIMEOUT_CODE));
|
|
519
|
+
}, this.timeoutMs);
|
|
520
|
+
}
|
|
521
|
+
/** Rearm the idle window; called after each provider read. */
|
|
522
|
+
pulse() {
|
|
523
|
+
this.arm();
|
|
524
|
+
}
|
|
525
|
+
stop() {
|
|
526
|
+
if (this.timer !== void 0) {
|
|
527
|
+
clearTimeout(this.timer);
|
|
528
|
+
this.timer = void 0;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
};
|
|
532
|
+
function modelInfo(provider, model) {
|
|
533
|
+
return {
|
|
534
|
+
provider,
|
|
535
|
+
id: model.id,
|
|
536
|
+
name: model.name ?? model.id,
|
|
537
|
+
...model.description === void 0 ? {} : { description: model.description },
|
|
538
|
+
inputModalities: model.inputModalities ?? ["text"]
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
function providerRetryAfterMs(value) {
|
|
542
|
+
if (value === null) return void 0;
|
|
543
|
+
if (/^\d+$/.test(value)) {
|
|
544
|
+
const delay2 = Number(value) * 1e3;
|
|
545
|
+
return Number.isFinite(delay2) && delay2 > 0 ? delay2 : void 0;
|
|
546
|
+
}
|
|
547
|
+
const delay = Date.parse(value) - Date.now();
|
|
548
|
+
return Number.isFinite(delay) && delay > 0 ? delay : void 0;
|
|
549
|
+
}
|
|
550
|
+
function requestId(headers) {
|
|
551
|
+
const value = headers.get("x-request-id") ?? headers.get("x-ollama-request-id");
|
|
552
|
+
return value === null || value.length === 0 ? void 0 : ProviderRequestId(value);
|
|
553
|
+
}
|
|
554
|
+
function httpErrorCode(status, error) {
|
|
555
|
+
if (status === 401 || status === 403) return "AUTH";
|
|
556
|
+
if (status === 413) return "INVALID_REQUEST";
|
|
557
|
+
const detail = [error?.code, error?.type, error?.message].filter(Boolean).join(" ");
|
|
558
|
+
if (isQuotaExceededError(detail)) return QUOTA_EXCEEDED_CODE;
|
|
559
|
+
if (status === 429) return "RATE_LIMIT";
|
|
560
|
+
if (status === 400) {
|
|
561
|
+
if (isContextWindowExceededError(detail)) return CONTEXT_WINDOW_EXCEEDED_CODE;
|
|
562
|
+
return "INVALID_REQUEST";
|
|
563
|
+
}
|
|
564
|
+
if (status >= 500) return "SERVER";
|
|
565
|
+
return `HTTP_${status}`;
|
|
566
|
+
}
|
|
567
|
+
var OllamaAdapter = class extends LlmAdapter {
|
|
568
|
+
constructor(config) {
|
|
569
|
+
super();
|
|
570
|
+
this.config = config;
|
|
571
|
+
}
|
|
572
|
+
config;
|
|
573
|
+
providerInfo(provider) {
|
|
574
|
+
return { id: provider, name: "Ollama" };
|
|
575
|
+
}
|
|
576
|
+
providerRetryPolicy(_provider) {
|
|
577
|
+
return this.config.options().retryPolicy;
|
|
578
|
+
}
|
|
579
|
+
listModels(provider) {
|
|
580
|
+
return Promise.resolve(this.config.options().models.map((model) => modelInfo(provider, model)));
|
|
581
|
+
}
|
|
582
|
+
resolveModel(provider, model, _signal) {
|
|
583
|
+
const connection = this.config.options();
|
|
584
|
+
const wireModel = normalizeCloud(model);
|
|
585
|
+
const configured = connection.models.find((entry) => entry.id === wireModel);
|
|
586
|
+
const contextWindow = configured?.contextWindow ?? connection.defaultContextWindow;
|
|
587
|
+
return Promise.resolve({
|
|
588
|
+
// An uncatalogued endpoint is safely treated as text-only.
|
|
589
|
+
...configured === void 0 ? { provider, id: wireModel, name: wireModel, inputModalities: ["text"] } : modelInfo(provider, configured),
|
|
590
|
+
context: { contextWindow },
|
|
591
|
+
defaultMaxTokens: configured?.maxTokens ?? connection.maxTokens,
|
|
592
|
+
...connection.defaults.thinking === "disabled" ? {
|
|
593
|
+
reasoning: {
|
|
594
|
+
efforts: OFF_ONLY_REASONING_EFFORTS,
|
|
595
|
+
defaultEffort: OFF_REASONING_EFFORT
|
|
596
|
+
}
|
|
597
|
+
} : {
|
|
598
|
+
reasoning: {
|
|
599
|
+
efforts: REASONING_EFFORTS,
|
|
600
|
+
defaultEffort: connection.defaults.reasoningEffort === "off" ? OFF_REASONING_EFFORT : connection.defaults.reasoningEffort === "low" ? LOW_REASONING_EFFORT : connection.defaults.reasoningEffort === "max" ? MAX_REASONING_EFFORT : HIGH_REASONING_EFFORT
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
});
|
|
604
|
+
}
|
|
605
|
+
async *stream(options) {
|
|
606
|
+
const connection = this.config.options();
|
|
607
|
+
if (options.messages.some((message) => contentHasImage2(message.content))) {
|
|
608
|
+
throw new LlmError4(
|
|
609
|
+
"Ollama image input is not supported yet.",
|
|
610
|
+
"UNSUPPORTED_CONTENT"
|
|
611
|
+
);
|
|
612
|
+
}
|
|
613
|
+
const apiKey = await this.config.resolveApiKey(connection);
|
|
614
|
+
const consumer = new AbortController();
|
|
615
|
+
const upstream = options.signal === void 0 ? consumer.signal : AbortSignal.any([options.signal, consumer.signal]);
|
|
616
|
+
const watchdog = new IdleWatchdog(upstream, connection.streamIdleTimeoutMs);
|
|
617
|
+
const iterator = this.request(
|
|
618
|
+
options,
|
|
619
|
+
watchdog.signal,
|
|
620
|
+
connection,
|
|
621
|
+
apiKey,
|
|
622
|
+
() => watchdog.pulse()
|
|
623
|
+
)[Symbol.asyncIterator]();
|
|
624
|
+
let exhausted = false;
|
|
625
|
+
try {
|
|
626
|
+
while (true) {
|
|
627
|
+
watchdog.pulse();
|
|
628
|
+
const result = await iterator.next();
|
|
629
|
+
if (result.done) {
|
|
630
|
+
exhausted = true;
|
|
631
|
+
return;
|
|
632
|
+
}
|
|
633
|
+
yield result.value;
|
|
634
|
+
}
|
|
635
|
+
} catch (error) {
|
|
636
|
+
if (watchdog.didExpire) {
|
|
637
|
+
throw new LlmError4(
|
|
638
|
+
`Ollama stream idle timeout after ${connection.streamIdleTimeoutMs}ms`,
|
|
639
|
+
"TIMEOUT",
|
|
640
|
+
{ cause: error }
|
|
641
|
+
);
|
|
642
|
+
}
|
|
643
|
+
if (options.signal?.aborted) {
|
|
644
|
+
throw new LlmError4("Ollama request aborted by caller", "ABORTED", { cause: error });
|
|
645
|
+
}
|
|
646
|
+
if (error instanceof LlmError4) throw error;
|
|
647
|
+
throw new LlmError4(`Ollama API stream from ${connection.baseURL} failed`, "TRANSPORT", { cause: error });
|
|
648
|
+
} finally {
|
|
649
|
+
watchdog.stop();
|
|
650
|
+
consumer.abort("Ollama stream consumer stopped");
|
|
651
|
+
if (!exhausted && iterator.return !== void 0) {
|
|
652
|
+
try {
|
|
653
|
+
await iterator.return();
|
|
654
|
+
} catch (_abortedTransportTeardown) {
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
async *request(options, signal, connection, apiKey, onComment) {
|
|
660
|
+
const body = serializeRequest(
|
|
661
|
+
{ ...options, model: normalizeCloud(options.model) },
|
|
662
|
+
connection.defaults
|
|
663
|
+
);
|
|
664
|
+
const payload = JSON.stringify(body);
|
|
665
|
+
const headers = {
|
|
666
|
+
"authorization": `Bearer ${apiKey}`,
|
|
667
|
+
"content-type": "application/json",
|
|
668
|
+
"accept": "text/event-stream",
|
|
669
|
+
...attributionHeaders(),
|
|
670
|
+
...options.sessionId !== void 0 ? { "x-deepseek-harness-session-id": String(options.sessionId) } : {},
|
|
671
|
+
...options.purpose === "compaction" ? { "x-deepseek-harness-compact": "1" } : {}
|
|
672
|
+
};
|
|
673
|
+
let response;
|
|
674
|
+
try {
|
|
675
|
+
response = await fetch(`${connection.baseURL}/chat/completions`, {
|
|
676
|
+
method: "POST",
|
|
677
|
+
headers,
|
|
678
|
+
body: payload,
|
|
679
|
+
signal
|
|
680
|
+
});
|
|
681
|
+
} catch (error) {
|
|
682
|
+
if (signal.aborted) throw error;
|
|
683
|
+
throw new LlmError4(
|
|
684
|
+
`Ollama API request to ${connection.baseURL} failed`,
|
|
685
|
+
"TRANSPORT",
|
|
686
|
+
{ cause: error }
|
|
687
|
+
);
|
|
688
|
+
}
|
|
689
|
+
if (!response.ok) {
|
|
690
|
+
let message = `Ollama API error (HTTP ${response.status})`;
|
|
691
|
+
let providerError;
|
|
692
|
+
try {
|
|
693
|
+
const parsed = await response.json();
|
|
694
|
+
providerError = parsed.error;
|
|
695
|
+
if (providerError?.message) message = providerError.message;
|
|
696
|
+
} catch {
|
|
697
|
+
}
|
|
698
|
+
const delay = providerRetryAfterMs(response.headers.get("retry-after"));
|
|
699
|
+
const id = requestId(response.headers);
|
|
700
|
+
throw new LlmError4(message, httpErrorCode(response.status, providerError), {
|
|
701
|
+
status: response.status,
|
|
702
|
+
...delay === void 0 ? {} : { providerRetryAfterMs: delay },
|
|
703
|
+
...id === void 0 ? {} : { requestId: id }
|
|
704
|
+
});
|
|
705
|
+
}
|
|
706
|
+
if (!response.body) {
|
|
707
|
+
throw new LlmError4("Ollama API returned no response body", "EMPTY_RESPONSE");
|
|
708
|
+
}
|
|
709
|
+
yield* translate(parseSse(response.body, onComment));
|
|
710
|
+
}
|
|
711
|
+
};
|
|
712
|
+
|
|
713
|
+
// src/index.ts
|
|
714
|
+
var name = "llm-ollama-cloud";
|
|
715
|
+
var inject = ["llm"];
|
|
716
|
+
var DEFAULT_API_KEY_ENV = "OLLAMA_CLOUD_API_KEY";
|
|
717
|
+
var PROVIDER = "ollama-cloud-direct";
|
|
718
|
+
var DEFAULT_MODELS = [
|
|
719
|
+
{ id: "deepseek-v4-flash:cloud", name: "DeepSeek-V4-Flash (cloud)", contextWindow: DEFAULT_CONTEXT_WINDOW },
|
|
720
|
+
{ id: "deepseek-v4-pro:cloud", name: "DeepSeek-V4-Pro (cloud)", contextWindow: DEFAULT_CONTEXT_WINDOW },
|
|
721
|
+
{ id: "glm-5.2:cloud", name: "GLM-5.2 (cloud)", contextWindow: DEFAULT_CONTEXT_WINDOW }
|
|
722
|
+
];
|
|
723
|
+
var MODEL_MODALITIES = ["text", "image"];
|
|
724
|
+
var PUBLIC_BASE_URL = "https://ollama.com/v1";
|
|
725
|
+
function resolveModels(models) {
|
|
726
|
+
const seen = /* @__PURE__ */ new Set();
|
|
727
|
+
return (models ?? DEFAULT_MODELS).map((model) => {
|
|
728
|
+
if (model.id.length === 0) throw new Error("llm-ollama-cloud: catalog model ids must be non-empty");
|
|
729
|
+
const id = normalizeCloud(model.id);
|
|
730
|
+
if (model.name !== void 0 && model.name.length === 0) {
|
|
731
|
+
throw new Error(`llm-ollama-cloud: catalog model "${id}" has an empty name`);
|
|
732
|
+
}
|
|
733
|
+
if (model.contextWindow !== void 0 && (!Number.isInteger(model.contextWindow) || model.contextWindow <= 0)) {
|
|
734
|
+
throw new Error(
|
|
735
|
+
`llm-ollama-cloud: catalog model "${id}" contextWindow must be a positive integer`
|
|
736
|
+
);
|
|
737
|
+
}
|
|
738
|
+
if (model.maxTokens !== void 0 && (!Number.isInteger(model.maxTokens) || model.maxTokens <= 0)) {
|
|
739
|
+
throw new Error(
|
|
740
|
+
`llm-ollama-cloud: catalog model "${id}" maxTokens must be a positive integer`
|
|
741
|
+
);
|
|
742
|
+
}
|
|
743
|
+
const inputModalities = model.inputModalities ?? ["text"];
|
|
744
|
+
if (inputModalities.length === 0) {
|
|
745
|
+
throw new Error(`llm-ollama-cloud: catalog model "${id}" inputModalities must not be empty`);
|
|
746
|
+
}
|
|
747
|
+
if (inputModalities.some((modality) => !MODEL_MODALITIES.includes(modality))) {
|
|
748
|
+
throw new Error(
|
|
749
|
+
`llm-ollama-cloud: catalog model "${id}" inputModalities must contain only "text" and "image"`
|
|
750
|
+
);
|
|
751
|
+
}
|
|
752
|
+
if (new Set(inputModalities).size !== inputModalities.length) {
|
|
753
|
+
throw new Error(`llm-ollama-cloud: catalog model "${id}" inputModalities must not contain duplicates`);
|
|
754
|
+
}
|
|
755
|
+
if (seen.has(id)) throw new Error(`llm-ollama-cloud: duplicate catalog model "${id}"`);
|
|
756
|
+
seen.add(id);
|
|
757
|
+
return {
|
|
758
|
+
id,
|
|
759
|
+
...model.name === void 0 ? {} : { name: model.name },
|
|
760
|
+
...model.description === void 0 ? {} : { description: model.description },
|
|
761
|
+
...model.contextWindow === void 0 ? {} : { contextWindow: model.contextWindow },
|
|
762
|
+
...model.maxTokens === void 0 ? {} : { maxTokens: model.maxTokens },
|
|
763
|
+
inputModalities: [...inputModalities]
|
|
764
|
+
};
|
|
765
|
+
});
|
|
766
|
+
}
|
|
767
|
+
function resolveAdapterOptions(config) {
|
|
768
|
+
if (config.thinking === "disabled" && config.reasoningEffort !== void 0 && config.reasoningEffort !== "off") {
|
|
769
|
+
throw new Error('llm-ollama-cloud: only reasoningEffort "off" can be configured when thinking is disabled');
|
|
770
|
+
}
|
|
771
|
+
if (config.defaultContextWindow !== void 0 && (!Number.isInteger(config.defaultContextWindow) || config.defaultContextWindow <= 0)) {
|
|
772
|
+
throw new Error("llm-ollama-cloud: defaultContextWindow must be a positive integer");
|
|
773
|
+
}
|
|
774
|
+
if (config.maxTokens !== void 0 && (!Number.isSafeInteger(config.maxTokens) || config.maxTokens <= 0)) {
|
|
775
|
+
throw new Error("llm-ollama-cloud: maxTokens must be a positive safe integer");
|
|
776
|
+
}
|
|
777
|
+
const streamIdleTimeoutMs = config.streamIdleTimeoutMs ?? DEFAULT_STREAM_IDLE_TIMEOUT_MS;
|
|
778
|
+
if (!Number.isFinite(streamIdleTimeoutMs) || streamIdleTimeoutMs <= 0 || streamIdleTimeoutMs > MAX_TIMER_DELAY_MS) {
|
|
779
|
+
throw new Error(
|
|
780
|
+
`llm-ollama-cloud: streamIdleTimeoutMs must be a positive finite number no greater than ${MAX_TIMER_DELAY_MS}`
|
|
781
|
+
);
|
|
782
|
+
}
|
|
783
|
+
return {
|
|
784
|
+
apiKeyEnv: config.apiKeyEnv ?? DEFAULT_API_KEY_ENV,
|
|
785
|
+
baseURL: config.baseURL ?? PUBLIC_BASE_URL,
|
|
786
|
+
defaults: {
|
|
787
|
+
thinking: config.thinking,
|
|
788
|
+
reasoningEffort: config.reasoningEffort
|
|
789
|
+
},
|
|
790
|
+
maxTokens: config.maxTokens ?? DEFAULT_MAX_TOKENS,
|
|
791
|
+
defaultContextWindow: config.defaultContextWindow ?? DEFAULT_CONTEXT_WINDOW,
|
|
792
|
+
models: resolveModels(config.models),
|
|
793
|
+
streamIdleTimeoutMs,
|
|
794
|
+
retryPolicy: resolveRetryPolicy(config.retryPolicy, "llm-ollama-cloud: retryPolicy")
|
|
795
|
+
};
|
|
796
|
+
}
|
|
797
|
+
function apply(ctx, config) {
|
|
798
|
+
const options = () => resolveAdapterOptions(config);
|
|
799
|
+
const connection = options();
|
|
800
|
+
credentialRef(connection.apiKeyEnv);
|
|
801
|
+
const credentials = ctx.get("credentials");
|
|
802
|
+
const adapter = new OllamaAdapter({
|
|
803
|
+
options,
|
|
804
|
+
resolveApiKey: async (connection2) => {
|
|
805
|
+
const ref = connection2.apiKeyEnv;
|
|
806
|
+
if (credentials !== void 0) {
|
|
807
|
+
const hit = await credentials.resolve(ref);
|
|
808
|
+
if (hit !== void 0 && hit.value.length > 0) {
|
|
809
|
+
return assertUsableApiKey(hit.value, "llm-ollama-cloud", ref);
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
const value = process.env[ref];
|
|
813
|
+
if (value !== void 0 && value.length > 0) {
|
|
814
|
+
return assertUsableApiKey(value, "llm-ollama-cloud", ref);
|
|
815
|
+
}
|
|
816
|
+
throw new LlmError5(
|
|
817
|
+
`llm-ollama-cloud: no API key for provider route "${PROVIDER}"; store ${ref} in the credentials file or export it in the launching environment`,
|
|
818
|
+
"MISSING_CREDENTIAL"
|
|
819
|
+
);
|
|
820
|
+
}
|
|
821
|
+
});
|
|
822
|
+
ctx.llm.registerAdapter([PROVIDER], adapter);
|
|
823
|
+
}
|
|
824
|
+
export {
|
|
825
|
+
DEFAULT_CONTEXT_WINDOW,
|
|
826
|
+
DEFAULT_MAX_TOKENS,
|
|
827
|
+
DEFAULT_STREAM_IDLE_TIMEOUT_MS,
|
|
828
|
+
MAX_TIMER_DELAY_MS,
|
|
829
|
+
OllamaAdapter,
|
|
830
|
+
PUBLIC_BASE_URL,
|
|
831
|
+
apply,
|
|
832
|
+
inject,
|
|
833
|
+
name,
|
|
834
|
+
normalizeCloud,
|
|
835
|
+
resolveAdapterOptions
|
|
836
|
+
};
|
|
837
|
+
//# sourceMappingURL=index.js.map
|