@nemesis-oss/ollama-sdk 1.3.0 → 1.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.
- package/CHANGELOG.md +43 -212
- package/README.md +391 -18
- package/dist/compose-BR70yb4W.d.cts +164 -0
- package/dist/compose-DfAqKAFj.d.ts +164 -0
- package/dist/generated-api.cjs +454 -0
- package/dist/generated-api.cjs.map +1 -0
- package/dist/generated-api.d.cts +1651 -0
- package/dist/generated-api.d.ts +1651 -0
- package/dist/generated-api.js +428 -0
- package/dist/generated-api.js.map +1 -0
- package/dist/generated-models-schemas.cjs +328 -0
- package/dist/generated-models-schemas.cjs.map +1 -0
- package/dist/generated-models-schemas.d.cts +463 -0
- package/dist/generated-models-schemas.d.ts +463 -0
- package/dist/generated-models-schemas.js +291 -0
- package/dist/generated-models-schemas.js.map +1 -0
- package/dist/generated-runtime.cjs +145 -0
- package/dist/generated-runtime.cjs.map +1 -0
- package/dist/generated-runtime.d.cts +2 -0
- package/dist/generated-runtime.d.ts +2 -0
- package/dist/generated-runtime.js +143 -0
- package/dist/generated-runtime.js.map +1 -0
- package/dist/index-2RIErNT5.d.cts +583 -0
- package/dist/index-DIQ8NUbU.d.ts +583 -0
- package/dist/index.cjs +1886 -154
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +817 -453
- package/dist/index.d.ts +817 -453
- package/dist/index.js +1877 -155
- package/dist/index.js.map +1 -1
- package/dist/mcp/tools.json +1058 -0
- package/dist/mcp-generated.cjs +386 -0
- package/dist/mcp-generated.cjs.map +1 -0
- package/dist/mcp-generated.d.cts +35 -0
- package/dist/mcp-generated.d.ts +35 -0
- package/dist/mcp-generated.js +381 -0
- package/dist/mcp-generated.js.map +1 -0
- package/dist/mcp-http.cjs +100 -0
- package/dist/mcp-http.cjs.map +1 -0
- package/dist/mcp-http.d.cts +48 -0
- package/dist/mcp-http.d.ts +48 -0
- package/dist/mcp-http.js +96 -0
- package/dist/mcp-http.js.map +1 -0
- package/dist/mcp-stdio.cjs +49 -0
- package/dist/mcp-stdio.cjs.map +1 -0
- package/dist/mcp-stdio.d.cts +34 -0
- package/dist/mcp-stdio.d.ts +34 -0
- package/dist/mcp-stdio.js +47 -0
- package/dist/mcp-stdio.js.map +1 -0
- package/dist/skills.cjs.map +1 -1
- package/dist/skills.d.cts +3 -2
- package/dist/skills.d.ts +3 -2
- package/dist/skills.js.map +1 -1
- package/dist/types-B9_axjum.d.cts +66 -0
- package/dist/types-B9_axjum.d.ts +66 -0
- package/dist/{compose-CDWq8oxJ.d.cts → types-BQhgW-du.d.cts} +68 -171
- package/dist/{compose-CDWq8oxJ.d.ts → types-BQhgW-du.d.ts} +68 -171
- package/package.json +96 -5
package/dist/index.cjs
CHANGED
|
@@ -154,6 +154,26 @@ var OllamaModelRoutingError = class extends OllamaClientError {
|
|
|
154
154
|
this.availableModels = options.availableModels;
|
|
155
155
|
}
|
|
156
156
|
};
|
|
157
|
+
var OllamaIncompatibleModelError = class extends OllamaClientError {
|
|
158
|
+
model;
|
|
159
|
+
capability;
|
|
160
|
+
reportedCapabilities;
|
|
161
|
+
constructor(message, options) {
|
|
162
|
+
super(message, { ...options, code: "incompatible_model", retryable: false });
|
|
163
|
+
this.model = options.model;
|
|
164
|
+
this.capability = options.capability;
|
|
165
|
+
this.reportedCapabilities = options.reportedCapabilities;
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
var OllamaAgentMaxToolCallsError = class extends OllamaClientError {
|
|
169
|
+
maxToolCalls;
|
|
170
|
+
toolCallsExecuted;
|
|
171
|
+
constructor(message, options) {
|
|
172
|
+
super(message, { ...options, code: "agent_max_tool_calls_exceeded", retryable: false });
|
|
173
|
+
this.maxToolCalls = options.maxToolCalls;
|
|
174
|
+
this.toolCallsExecuted = options.toolCallsExecuted;
|
|
175
|
+
}
|
|
176
|
+
};
|
|
157
177
|
var OllamaAgentMaxIterationsError = class extends OllamaClientError {
|
|
158
178
|
maxIterations;
|
|
159
179
|
constructor(message, options) {
|
|
@@ -452,6 +472,28 @@ var EndpointRegistry = class {
|
|
|
452
472
|
}
|
|
453
473
|
};
|
|
454
474
|
|
|
475
|
+
// src/middleware.ts
|
|
476
|
+
function composeMiddleware(middlewares, finalHandler) {
|
|
477
|
+
return function runPipeline(initialRequest) {
|
|
478
|
+
let index = -1;
|
|
479
|
+
function dispatch2(i, currentRequest) {
|
|
480
|
+
if (i <= index) {
|
|
481
|
+
return Promise.reject(new Error("next() called multiple times in middleware"));
|
|
482
|
+
}
|
|
483
|
+
index = i;
|
|
484
|
+
const fn = middlewares[i];
|
|
485
|
+
if (i === middlewares.length || !fn) {
|
|
486
|
+
return finalHandler(currentRequest);
|
|
487
|
+
}
|
|
488
|
+
return fn({
|
|
489
|
+
request: currentRequest,
|
|
490
|
+
next: () => dispatch2(i + 1, currentRequest)
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
return dispatch2(0, initialRequest);
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
|
|
455
497
|
// src/streaming/ndjson.ts
|
|
456
498
|
async function* parseNdjsonStream(stream) {
|
|
457
499
|
const reader = stream.getReader();
|
|
@@ -480,6 +522,99 @@ async function* parseNdjsonStream(stream) {
|
|
|
480
522
|
yield JSON.parse(buffer.trim());
|
|
481
523
|
}
|
|
482
524
|
} finally {
|
|
525
|
+
try {
|
|
526
|
+
await reader.cancel();
|
|
527
|
+
} catch {
|
|
528
|
+
}
|
|
529
|
+
reader.releaseLock();
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
// src/streaming/sse.ts
|
|
534
|
+
function dispatch(event, data, id, retry) {
|
|
535
|
+
if (data.length === 0) return void 0;
|
|
536
|
+
const joined = data.join("\n");
|
|
537
|
+
return {
|
|
538
|
+
...event !== void 0 ? { event } : {},
|
|
539
|
+
data: joined.endsWith("\n") ? joined.slice(0, -1) : joined,
|
|
540
|
+
...id !== void 0 ? { id } : {},
|
|
541
|
+
...retry !== void 0 ? { retry } : {}
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
async function* parseSseStream(stream) {
|
|
545
|
+
const reader = stream.getReader();
|
|
546
|
+
const decoder = new TextDecoder();
|
|
547
|
+
let buffer = "";
|
|
548
|
+
let eventType;
|
|
549
|
+
let data = [];
|
|
550
|
+
let lastEventId;
|
|
551
|
+
let retry;
|
|
552
|
+
const processLine = (line) => {
|
|
553
|
+
if (line.length === 0) {
|
|
554
|
+
const event = dispatch(eventType, data, lastEventId, retry);
|
|
555
|
+
eventType = void 0;
|
|
556
|
+
data = [];
|
|
557
|
+
retry = void 0;
|
|
558
|
+
return event;
|
|
559
|
+
}
|
|
560
|
+
if (line.startsWith(":")) return void 0;
|
|
561
|
+
const separator = line.indexOf(":");
|
|
562
|
+
const field = separator === -1 ? line : line.slice(0, separator);
|
|
563
|
+
let value = separator === -1 ? "" : line.slice(separator + 1);
|
|
564
|
+
if (value.startsWith(" ")) value = value.slice(1);
|
|
565
|
+
switch (field) {
|
|
566
|
+
case "event":
|
|
567
|
+
eventType = value;
|
|
568
|
+
break;
|
|
569
|
+
case "data":
|
|
570
|
+
data.push(value);
|
|
571
|
+
break;
|
|
572
|
+
case "id":
|
|
573
|
+
if (!value.includes("\0")) lastEventId = value;
|
|
574
|
+
break;
|
|
575
|
+
case "retry": {
|
|
576
|
+
if (/^\d+$/.test(value)) {
|
|
577
|
+
const parsed = Number(value);
|
|
578
|
+
if (Number.isSafeInteger(parsed)) retry = parsed;
|
|
579
|
+
}
|
|
580
|
+
break;
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
return void 0;
|
|
584
|
+
};
|
|
585
|
+
try {
|
|
586
|
+
while (true) {
|
|
587
|
+
const { done, value } = await reader.read();
|
|
588
|
+
if (done) break;
|
|
589
|
+
buffer += decoder.decode(value, { stream: true });
|
|
590
|
+
while (true) {
|
|
591
|
+
const lf = buffer.indexOf("\n");
|
|
592
|
+
const cr = buffer.indexOf("\r");
|
|
593
|
+
if (lf === -1 && cr === -1) break;
|
|
594
|
+
if (cr !== -1 && lf === -1 && cr === buffer.length - 1) break;
|
|
595
|
+
const lineEnd = lf === -1 ? cr : cr === -1 ? lf : Math.min(lf, cr);
|
|
596
|
+
let separatorLength = 1;
|
|
597
|
+
if (buffer[lineEnd] === "\r" && buffer[lineEnd + 1] === "\n") {
|
|
598
|
+
separatorLength = 2;
|
|
599
|
+
}
|
|
600
|
+
const line = buffer.slice(0, lineEnd);
|
|
601
|
+
buffer = buffer.slice(lineEnd + separatorLength);
|
|
602
|
+
const event = processLine(line);
|
|
603
|
+
if (event) yield event;
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
buffer += decoder.decode();
|
|
607
|
+
if (buffer.length > 0) {
|
|
608
|
+
const event = processLine(buffer);
|
|
609
|
+
if (event) yield event;
|
|
610
|
+
}
|
|
611
|
+
const terminal = dispatch(eventType, data, lastEventId, retry);
|
|
612
|
+
if (terminal) yield terminal;
|
|
613
|
+
} finally {
|
|
614
|
+
try {
|
|
615
|
+
await reader.cancel();
|
|
616
|
+
} catch {
|
|
617
|
+
}
|
|
483
618
|
reader.releaseLock();
|
|
484
619
|
}
|
|
485
620
|
}
|
|
@@ -564,16 +699,36 @@ function httpSpanAttributes(method, url) {
|
|
|
564
699
|
[ATTR_SERVER_PORT]: target.port ? Number(target.port) : void 0
|
|
565
700
|
};
|
|
566
701
|
}
|
|
702
|
+
var requestSequence = 0;
|
|
703
|
+
function createRequestId() {
|
|
704
|
+
requestSequence += 1;
|
|
705
|
+
return `ollama-http-${requestSequence}`;
|
|
706
|
+
}
|
|
707
|
+
function sameHeaders(actual, expected) {
|
|
708
|
+
const actualEntries = Object.fromEntries(
|
|
709
|
+
Object.entries(actual).map(([key, value]) => [key.toLowerCase(), value])
|
|
710
|
+
);
|
|
711
|
+
const expectedEntries = Object.fromEntries(expected.entries());
|
|
712
|
+
const actualKeys = Object.keys(actualEntries);
|
|
713
|
+
const expectedKeys = Object.keys(expectedEntries);
|
|
714
|
+
return actualKeys.length === expectedKeys.length && expectedKeys.every((key) => actualEntries[key] === expectedEntries[key]);
|
|
715
|
+
}
|
|
567
716
|
var HttpClient = class {
|
|
568
717
|
baseUrl;
|
|
569
718
|
apiKey;
|
|
570
719
|
defaultHeaders;
|
|
571
720
|
fetchImpl;
|
|
721
|
+
middleware;
|
|
722
|
+
onLifecycleEvent;
|
|
723
|
+
requestId;
|
|
572
724
|
constructor(options) {
|
|
573
725
|
this.baseUrl = options.baseUrl.replace(/\/+$/, "");
|
|
574
726
|
this.apiKey = options.apiKey;
|
|
575
727
|
this.defaultHeaders = options.headers ?? {};
|
|
576
728
|
this.fetchImpl = options.fetch ?? globalThis.fetch;
|
|
729
|
+
this.middleware = options.middleware ?? [];
|
|
730
|
+
this.onLifecycleEvent = options.onLifecycleEvent;
|
|
731
|
+
this.requestId = options.requestId;
|
|
577
732
|
}
|
|
578
733
|
buildHeaders(customHeaders) {
|
|
579
734
|
const headers = {
|
|
@@ -586,6 +741,131 @@ var HttpClient = class {
|
|
|
586
741
|
}
|
|
587
742
|
return headers;
|
|
588
743
|
}
|
|
744
|
+
async fetchWithMiddleware(request) {
|
|
745
|
+
const requestId = this.requestId ?? createRequestId();
|
|
746
|
+
const startedAt = Date.now();
|
|
747
|
+
let rawResponse;
|
|
748
|
+
this.onLifecycleEvent?.({
|
|
749
|
+
type: "start",
|
|
750
|
+
requestId,
|
|
751
|
+
method: request.method,
|
|
752
|
+
url: request.url,
|
|
753
|
+
timestamp: startedAt
|
|
754
|
+
});
|
|
755
|
+
const finalHandler = async (req) => {
|
|
756
|
+
const init = {
|
|
757
|
+
method: req.method,
|
|
758
|
+
headers: req.headers,
|
|
759
|
+
...req.body !== void 0 ? { body: req.body } : {},
|
|
760
|
+
...req.signal !== void 0 ? { signal: req.signal } : {}
|
|
761
|
+
};
|
|
762
|
+
const response = await this.fetchImpl(req.url, init);
|
|
763
|
+
rawResponse = response;
|
|
764
|
+
const responseHeaders = response.headers && typeof response.headers.entries === "function" ? Object.fromEntries(response.headers.entries()) : {};
|
|
765
|
+
return {
|
|
766
|
+
status: response.status,
|
|
767
|
+
headers: responseHeaders,
|
|
768
|
+
body: response
|
|
769
|
+
};
|
|
770
|
+
};
|
|
771
|
+
try {
|
|
772
|
+
if (this.middleware.length === 0) {
|
|
773
|
+
const response2 = await this.fetchImpl(request.url, {
|
|
774
|
+
method: request.method,
|
|
775
|
+
headers: request.headers,
|
|
776
|
+
...request.body !== void 0 ? { body: request.body } : {},
|
|
777
|
+
...request.signal !== void 0 ? { signal: request.signal } : {}
|
|
778
|
+
});
|
|
779
|
+
if (response2.status >= 200 && response2.status < 300) {
|
|
780
|
+
this.onLifecycleEvent?.({
|
|
781
|
+
type: "success",
|
|
782
|
+
requestId,
|
|
783
|
+
durationMs: Date.now() - startedAt,
|
|
784
|
+
status: response2.status,
|
|
785
|
+
timestamp: Date.now()
|
|
786
|
+
});
|
|
787
|
+
} else {
|
|
788
|
+
this.onLifecycleEvent?.({
|
|
789
|
+
type: "error",
|
|
790
|
+
requestId,
|
|
791
|
+
durationMs: Date.now() - startedAt,
|
|
792
|
+
error: mapError(new Error(`HTTP ${response2.status}`), {
|
|
793
|
+
request: { method: request.method, url: request.url },
|
|
794
|
+
response: { status: response2.status }
|
|
795
|
+
}),
|
|
796
|
+
timestamp: Date.now()
|
|
797
|
+
});
|
|
798
|
+
}
|
|
799
|
+
return response2;
|
|
800
|
+
}
|
|
801
|
+
const pipeline = composeMiddleware(this.middleware, finalHandler);
|
|
802
|
+
const context = await pipeline(request);
|
|
803
|
+
let response;
|
|
804
|
+
if (rawResponse !== void 0 && context.body === rawResponse) {
|
|
805
|
+
response = rawResponse;
|
|
806
|
+
if (context.status !== response.status || !sameHeaders(context.headers, response.headers)) {
|
|
807
|
+
const cloned = response.clone();
|
|
808
|
+
response = new Response(cloned.body, {
|
|
809
|
+
status: context.status,
|
|
810
|
+
headers: context.headers
|
|
811
|
+
});
|
|
812
|
+
}
|
|
813
|
+
} else if (context.body instanceof Response) {
|
|
814
|
+
if (context.status === context.body.status && sameHeaders(context.headers, context.body.headers)) {
|
|
815
|
+
response = context.body;
|
|
816
|
+
} else {
|
|
817
|
+
const cloned = context.body.clone();
|
|
818
|
+
response = new Response(cloned.body, {
|
|
819
|
+
status: context.status,
|
|
820
|
+
headers: context.headers
|
|
821
|
+
});
|
|
822
|
+
}
|
|
823
|
+
} else if (context.body === void 0 || typeof context.body === "string" || context.body instanceof ArrayBuffer || ArrayBuffer.isView(context.body) || context.body instanceof Blob || context.body instanceof ReadableStream) {
|
|
824
|
+
response = new Response(context.body, {
|
|
825
|
+
status: context.status,
|
|
826
|
+
headers: context.headers
|
|
827
|
+
});
|
|
828
|
+
} else {
|
|
829
|
+
response = new Response(JSON.stringify(context.body), {
|
|
830
|
+
status: context.status,
|
|
831
|
+
headers: context.headers
|
|
832
|
+
});
|
|
833
|
+
}
|
|
834
|
+
if (context.status >= 200 && context.status < 300) {
|
|
835
|
+
this.onLifecycleEvent?.({
|
|
836
|
+
type: "success",
|
|
837
|
+
requestId,
|
|
838
|
+
durationMs: Date.now() - startedAt,
|
|
839
|
+
status: context.status,
|
|
840
|
+
timestamp: Date.now()
|
|
841
|
+
});
|
|
842
|
+
} else {
|
|
843
|
+
this.onLifecycleEvent?.({
|
|
844
|
+
type: "error",
|
|
845
|
+
requestId,
|
|
846
|
+
durationMs: Date.now() - startedAt,
|
|
847
|
+
error: mapError(new Error(`HTTP ${context.status}`), {
|
|
848
|
+
request: { method: request.method, url: request.url },
|
|
849
|
+
response: { status: context.status, headers: context.headers }
|
|
850
|
+
}),
|
|
851
|
+
timestamp: Date.now()
|
|
852
|
+
});
|
|
853
|
+
}
|
|
854
|
+
return response;
|
|
855
|
+
} catch (error) {
|
|
856
|
+
const mapped = mapError(error, {
|
|
857
|
+
request: { method: request.method, url: request.url }
|
|
858
|
+
});
|
|
859
|
+
this.onLifecycleEvent?.({
|
|
860
|
+
type: "error",
|
|
861
|
+
requestId,
|
|
862
|
+
durationMs: Date.now() - startedAt,
|
|
863
|
+
error: mapped,
|
|
864
|
+
timestamp: Date.now()
|
|
865
|
+
});
|
|
866
|
+
throw mapped;
|
|
867
|
+
}
|
|
868
|
+
}
|
|
589
869
|
async request(options) {
|
|
590
870
|
const url = `${this.baseUrl}${options.path}`;
|
|
591
871
|
const method = options.method ?? (options.body !== void 0 || options.rawBody !== void 0 ? "POST" : "GET");
|
|
@@ -599,13 +879,13 @@ var HttpClient = class {
|
|
|
599
879
|
async (span) => {
|
|
600
880
|
try {
|
|
601
881
|
const bodyInit = options.rawBody !== void 0 ? options.rawBody : options.body !== void 0 ? JSON.stringify(options.body) : void 0;
|
|
602
|
-
const
|
|
882
|
+
const response = await this.fetchWithMiddleware({
|
|
883
|
+
url,
|
|
603
884
|
method,
|
|
604
885
|
headers,
|
|
605
886
|
...bodyInit !== void 0 ? { body: bodyInit } : {},
|
|
606
887
|
...options.signal !== void 0 ? { signal: options.signal } : {}
|
|
607
|
-
};
|
|
608
|
-
const response = await this.fetchImpl(url, init);
|
|
888
|
+
});
|
|
609
889
|
span?.setAttribute(ATTR_HTTP_RESPONSE_STATUS_CODE, response.status);
|
|
610
890
|
if (!response.ok) {
|
|
611
891
|
let errorBody;
|
|
@@ -623,6 +903,9 @@ var HttpClient = class {
|
|
|
623
903
|
if (options.method === "HEAD" || response.status === 204) {
|
|
624
904
|
return void 0;
|
|
625
905
|
}
|
|
906
|
+
if (options.raw === true) {
|
|
907
|
+
return response;
|
|
908
|
+
}
|
|
626
909
|
if (typeof response.text === "function") {
|
|
627
910
|
const text = await response.text();
|
|
628
911
|
return text ? JSON.parse(text) : void 0;
|
|
@@ -637,6 +920,79 @@ var HttpClient = class {
|
|
|
637
920
|
}
|
|
638
921
|
);
|
|
639
922
|
}
|
|
923
|
+
/**
|
|
924
|
+
* Opens an event-stream response and exposes parsed SSE events.
|
|
925
|
+
*
|
|
926
|
+
* This is intentionally schema-agnostic so OpenAI and Anthropic compatibility layers
|
|
927
|
+
* can decode their provider-specific event payloads without duplicating transport logic.
|
|
928
|
+
*/
|
|
929
|
+
async requestSseStream(options) {
|
|
930
|
+
const url = `${this.baseUrl}${options.path}`;
|
|
931
|
+
const method = options.method ?? "POST";
|
|
932
|
+
const headers = {
|
|
933
|
+
...this.buildHeaders(options.headers),
|
|
934
|
+
Accept: "text/event-stream"
|
|
935
|
+
};
|
|
936
|
+
return withSpan(
|
|
937
|
+
`${method} ${routeTemplate(options.path)}`,
|
|
938
|
+
httpSpanAttributes(method, url),
|
|
939
|
+
async (span) => {
|
|
940
|
+
try {
|
|
941
|
+
const controller = new AbortController();
|
|
942
|
+
const removeAbortListener = options.signal !== void 0 ? (() => {
|
|
943
|
+
if (options.signal.aborted) {
|
|
944
|
+
controller.abort(options.signal.reason);
|
|
945
|
+
} else {
|
|
946
|
+
const onAbort = () => controller.abort(options.signal?.reason);
|
|
947
|
+
options.signal.addEventListener("abort", onAbort, { once: true });
|
|
948
|
+
return () => options.signal?.removeEventListener("abort", onAbort);
|
|
949
|
+
}
|
|
950
|
+
return void 0;
|
|
951
|
+
})() : void 0;
|
|
952
|
+
const response = await this.fetchWithMiddleware({
|
|
953
|
+
url,
|
|
954
|
+
method,
|
|
955
|
+
headers,
|
|
956
|
+
...options.body !== void 0 ? { body: JSON.stringify(options.body) } : {},
|
|
957
|
+
signal: controller.signal
|
|
958
|
+
});
|
|
959
|
+
span?.setAttribute(ATTR_HTTP_RESPONSE_STATUS_CODE, response.status);
|
|
960
|
+
if (!response.ok) {
|
|
961
|
+
removeAbortListener?.();
|
|
962
|
+
const errorText = await response.text();
|
|
963
|
+
throw mapError(new Error(errorText || `HTTP ${response.status}`), {
|
|
964
|
+
request: { method, url },
|
|
965
|
+
response: { status: response.status, body: errorText }
|
|
966
|
+
});
|
|
967
|
+
}
|
|
968
|
+
if (!response.body) {
|
|
969
|
+
removeAbortListener?.();
|
|
970
|
+
throw mapError(new Error("Response body is null, cannot stream SSE"), {
|
|
971
|
+
request: { method, url }
|
|
972
|
+
});
|
|
973
|
+
}
|
|
974
|
+
const stream = (async function* () {
|
|
975
|
+
try {
|
|
976
|
+
yield* parseSseStream(response.body);
|
|
977
|
+
} finally {
|
|
978
|
+
removeAbortListener?.();
|
|
979
|
+
}
|
|
980
|
+
})();
|
|
981
|
+
return {
|
|
982
|
+
[Symbol.asyncIterator]() {
|
|
983
|
+
return stream[Symbol.asyncIterator]();
|
|
984
|
+
},
|
|
985
|
+
abort: () => {
|
|
986
|
+
removeAbortListener?.();
|
|
987
|
+
controller.abort();
|
|
988
|
+
}
|
|
989
|
+
};
|
|
990
|
+
} catch (err) {
|
|
991
|
+
throw mapError(err, { request: { method, url } });
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
);
|
|
995
|
+
}
|
|
640
996
|
async requestStream(options) {
|
|
641
997
|
const url = `${this.baseUrl}${options.path}`;
|
|
642
998
|
const method = options.method ?? "POST";
|
|
@@ -646,15 +1002,27 @@ var HttpClient = class {
|
|
|
646
1002
|
httpSpanAttributes(method, url),
|
|
647
1003
|
async (span) => {
|
|
648
1004
|
try {
|
|
649
|
-
const
|
|
1005
|
+
const controller = new AbortController();
|
|
1006
|
+
const removeAbortListener = options.signal !== void 0 ? (() => {
|
|
1007
|
+
if (options.signal.aborted) {
|
|
1008
|
+
controller.abort(options.signal.reason);
|
|
1009
|
+
} else {
|
|
1010
|
+
const onAbort = () => controller.abort(options.signal?.reason);
|
|
1011
|
+
options.signal.addEventListener("abort", onAbort, { once: true });
|
|
1012
|
+
return () => options.signal?.removeEventListener("abort", onAbort);
|
|
1013
|
+
}
|
|
1014
|
+
return void 0;
|
|
1015
|
+
})() : void 0;
|
|
1016
|
+
const response = await this.fetchWithMiddleware({
|
|
1017
|
+
url,
|
|
650
1018
|
method,
|
|
651
1019
|
headers,
|
|
652
1020
|
...options.body !== void 0 ? { body: JSON.stringify(options.body) } : {},
|
|
653
|
-
|
|
654
|
-
};
|
|
655
|
-
const response = await this.fetchImpl(url, init);
|
|
1021
|
+
signal: controller.signal
|
|
1022
|
+
});
|
|
656
1023
|
span?.setAttribute(ATTR_HTTP_RESPONSE_STATUS_CODE, response.status);
|
|
657
1024
|
if (!response.ok) {
|
|
1025
|
+
removeAbortListener?.();
|
|
658
1026
|
const errorText = await response.text();
|
|
659
1027
|
throw mapError(new Error(errorText || `HTTP ${response.status}`), {
|
|
660
1028
|
request: { method, url },
|
|
@@ -662,6 +1030,7 @@ var HttpClient = class {
|
|
|
662
1030
|
});
|
|
663
1031
|
}
|
|
664
1032
|
if (!response.body) {
|
|
1033
|
+
removeAbortListener?.();
|
|
665
1034
|
throw mapError(new Error("Response body is null, cannot stream"), {
|
|
666
1035
|
request: { method, url }
|
|
667
1036
|
});
|
|
@@ -669,7 +1038,17 @@ var HttpClient = class {
|
|
|
669
1038
|
const stream = parseNdjsonStream(response.body);
|
|
670
1039
|
const abortable = {
|
|
671
1040
|
[Symbol.asyncIterator]() {
|
|
672
|
-
return
|
|
1041
|
+
return (async function* () {
|
|
1042
|
+
try {
|
|
1043
|
+
yield* stream;
|
|
1044
|
+
} finally {
|
|
1045
|
+
removeAbortListener?.();
|
|
1046
|
+
}
|
|
1047
|
+
})()[Symbol.asyncIterator]();
|
|
1048
|
+
},
|
|
1049
|
+
abort: () => {
|
|
1050
|
+
removeAbortListener?.();
|
|
1051
|
+
controller.abort();
|
|
673
1052
|
}
|
|
674
1053
|
};
|
|
675
1054
|
return abortable;
|
|
@@ -682,12 +1061,14 @@ var HttpClient = class {
|
|
|
682
1061
|
};
|
|
683
1062
|
|
|
684
1063
|
// src/providers/health-check.ts
|
|
685
|
-
async function checkEndpointHealth(endpoint, fetchImpl, timeoutMs = 5e3) {
|
|
1064
|
+
async function checkEndpointHealth(endpoint, fetchImpl, timeoutMs = 5e3, middleware, onLifecycleEvent) {
|
|
686
1065
|
const client = new HttpClient({
|
|
687
1066
|
baseUrl: endpoint.baseUrl,
|
|
688
1067
|
apiKey: endpoint.apiKey,
|
|
689
1068
|
headers: endpoint.headers,
|
|
690
|
-
fetch: fetchImpl
|
|
1069
|
+
fetch: fetchImpl,
|
|
1070
|
+
middleware,
|
|
1071
|
+
onLifecycleEvent
|
|
691
1072
|
});
|
|
692
1073
|
const startTime = Date.now();
|
|
693
1074
|
const controller = new AbortController();
|
|
@@ -733,13 +1114,24 @@ function inferRuntimeMode(baseUrl) {
|
|
|
733
1114
|
return "unknown";
|
|
734
1115
|
}
|
|
735
1116
|
}
|
|
736
|
-
|
|
1117
|
+
function extractContextLength(modelInfo) {
|
|
1118
|
+
if (!modelInfo) return void 0;
|
|
1119
|
+
for (const [key, value] of Object.entries(modelInfo)) {
|
|
1120
|
+
if (key.endsWith(".context_length") && typeof value === "number" && Number.isFinite(value) && value > 0) {
|
|
1121
|
+
return value;
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
return void 0;
|
|
1125
|
+
}
|
|
1126
|
+
async function detectModelCapabilities(http, model, signal) {
|
|
737
1127
|
const showRes = await http.request({
|
|
738
1128
|
path: "/api/show",
|
|
739
|
-
body: { model }
|
|
1129
|
+
body: { model },
|
|
1130
|
+
...signal !== void 0 ? { signal } : {}
|
|
740
1131
|
});
|
|
741
1132
|
const reported = showRes.capabilities ?? [];
|
|
742
1133
|
const reportedSet = new Set(reported.map((c) => c.toLowerCase()));
|
|
1134
|
+
const contextLength = extractContextLength(showRes.model_info);
|
|
743
1135
|
return {
|
|
744
1136
|
model,
|
|
745
1137
|
reported,
|
|
@@ -748,6 +1140,8 @@ async function detectModelCapabilities(http, model) {
|
|
|
748
1140
|
supportsEmbedding: reportedSet.has("embedding"),
|
|
749
1141
|
supportsCompletion: reportedSet.has("completion") || !reportedSet.has("embedding"),
|
|
750
1142
|
supportsThinking: reportedSet.has("thinking"),
|
|
1143
|
+
...showRes.thinking !== void 0 ? { thinking: showRes.thinking } : {},
|
|
1144
|
+
...contextLength !== void 0 ? { contextLength } : {},
|
|
751
1145
|
supportsStreaming: true,
|
|
752
1146
|
supportsStructuredOutputRequest: inferRuntimeMode(http.baseUrl) !== "cloud"
|
|
753
1147
|
};
|
|
@@ -872,6 +1266,7 @@ function extractUsage(raw) {
|
|
|
872
1266
|
}
|
|
873
1267
|
return {
|
|
874
1268
|
promptTokens,
|
|
1269
|
+
...raw.prompt_eval_cached_count !== void 0 ? { cachedPromptTokens: raw.prompt_eval_cached_count } : {},
|
|
875
1270
|
completionTokens,
|
|
876
1271
|
totalTokens,
|
|
877
1272
|
...tokensPerSecond !== void 0 ? { tokensPerSecond } : {},
|
|
@@ -895,7 +1290,7 @@ function ensureToolCallIds(toolCalls) {
|
|
|
895
1290
|
|
|
896
1291
|
// src/streaming/stream.ts
|
|
897
1292
|
var OllamaStream = class {
|
|
898
|
-
constructor(source, mapChunk, aggregate, initial) {
|
|
1293
|
+
constructor(source, mapChunk, aggregate, initial, signal) {
|
|
899
1294
|
this.source = source;
|
|
900
1295
|
this.mapChunk = mapChunk;
|
|
901
1296
|
this.aggregate = aggregate;
|
|
@@ -904,6 +1299,15 @@ var OllamaStream = class {
|
|
|
904
1299
|
this.resolveFinal = resolve;
|
|
905
1300
|
this.rejectFinal = reject;
|
|
906
1301
|
});
|
|
1302
|
+
if (signal !== void 0) {
|
|
1303
|
+
const onAbort = () => this.abort();
|
|
1304
|
+
if (signal.aborted) {
|
|
1305
|
+
onAbort();
|
|
1306
|
+
} else {
|
|
1307
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
1308
|
+
this.removeAbortListener = () => signal.removeEventListener("abort", onAbort);
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
907
1311
|
}
|
|
908
1312
|
source;
|
|
909
1313
|
mapChunk;
|
|
@@ -914,11 +1318,15 @@ var OllamaStream = class {
|
|
|
914
1318
|
finalResultPromise;
|
|
915
1319
|
resolveFinal;
|
|
916
1320
|
rejectFinal;
|
|
1321
|
+
removeAbortListener;
|
|
917
1322
|
get finalResult() {
|
|
918
1323
|
return this.finalResultPromise;
|
|
919
1324
|
}
|
|
920
1325
|
abort() {
|
|
1326
|
+
this.removeAbortListener?.();
|
|
1327
|
+
this.removeAbortListener = void 0;
|
|
921
1328
|
this.source.abort?.();
|
|
1329
|
+
this.rejectFinal(new OllamaAbortError("Ollama stream aborted"));
|
|
922
1330
|
}
|
|
923
1331
|
on(type, listener) {
|
|
924
1332
|
if (this.mode === "iterator") {
|
|
@@ -958,7 +1366,11 @@ var OllamaStream = class {
|
|
|
958
1366
|
}
|
|
959
1367
|
}
|
|
960
1368
|
}
|
|
1369
|
+
this.removeAbortListener?.();
|
|
1370
|
+
this.removeAbortListener = void 0;
|
|
961
1371
|
} catch (error) {
|
|
1372
|
+
this.removeAbortListener?.();
|
|
1373
|
+
this.removeAbortListener = void 0;
|
|
962
1374
|
const mapped = mapError(error);
|
|
963
1375
|
this.emit({ type: "error", data: { error: mapped } });
|
|
964
1376
|
this.rejectFinal(mapped);
|
|
@@ -972,12 +1384,14 @@ var OllamaStream = class {
|
|
|
972
1384
|
}
|
|
973
1385
|
this.mode = "iterator";
|
|
974
1386
|
let accumulated = this.initial;
|
|
1387
|
+
let completed = false;
|
|
975
1388
|
try {
|
|
976
1389
|
for await (const chunk of this.source) {
|
|
977
1390
|
accumulated = this.aggregate(accumulated, chunk);
|
|
978
1391
|
const events = this.mapChunk(chunk, accumulated);
|
|
979
1392
|
for (const event of events) {
|
|
980
1393
|
if (event.type === "done") {
|
|
1394
|
+
completed = true;
|
|
981
1395
|
this.resolveFinal(event.data.result);
|
|
982
1396
|
}
|
|
983
1397
|
yield event;
|
|
@@ -987,6 +1401,13 @@ var OllamaStream = class {
|
|
|
987
1401
|
const mapped = mapError(error);
|
|
988
1402
|
this.rejectFinal(mapped);
|
|
989
1403
|
yield { type: "error", data: { error: mapped } };
|
|
1404
|
+
} finally {
|
|
1405
|
+
this.removeAbortListener?.();
|
|
1406
|
+
this.removeAbortListener = void 0;
|
|
1407
|
+
if (!completed) {
|
|
1408
|
+
this.source.abort?.();
|
|
1409
|
+
this.rejectFinal(new OllamaAbortError("Ollama stream ended before completion"));
|
|
1410
|
+
}
|
|
990
1411
|
}
|
|
991
1412
|
}
|
|
992
1413
|
};
|
|
@@ -1040,13 +1461,13 @@ function withStableToolCallIds(source) {
|
|
|
1040
1461
|
}
|
|
1041
1462
|
return Object.assign(generator(), { abort: source.abort });
|
|
1042
1463
|
}
|
|
1043
|
-
function normalizeChatStream(source) {
|
|
1464
|
+
function normalizeChatStream(source, signal) {
|
|
1044
1465
|
const initial = {
|
|
1045
1466
|
message: { role: "assistant", content: "" },
|
|
1046
1467
|
model: "",
|
|
1047
1468
|
done: false
|
|
1048
1469
|
};
|
|
1049
|
-
return new OllamaStream(withStableToolCallIds(source), mapChatChunk, aggregateChat, initial);
|
|
1470
|
+
return new OllamaStream(withStableToolCallIds(source), mapChatChunk, aggregateChat, initial, signal);
|
|
1050
1471
|
}
|
|
1051
1472
|
function aggregateGenerate(accumulated, chunk) {
|
|
1052
1473
|
return {
|
|
@@ -1056,6 +1477,10 @@ function aggregateGenerate(accumulated, chunk) {
|
|
|
1056
1477
|
doneReason: chunk.done ? chunk.done_reason : accumulated.doneReason,
|
|
1057
1478
|
totalDurationMs: chunk.done && chunk.total_duration !== void 0 ? chunk.total_duration / NANOS_PER_MS : accumulated.totalDurationMs,
|
|
1058
1479
|
usage: chunk.done ? extractUsage(chunk) : accumulated.usage,
|
|
1480
|
+
thinking: chunk.thinking !== void 0 ? (accumulated.thinking ?? "") + chunk.thinking : accumulated.thinking,
|
|
1481
|
+
image: chunk.image ?? accumulated.image,
|
|
1482
|
+
completed: chunk.completed ?? accumulated.completed,
|
|
1483
|
+
total: chunk.total ?? accumulated.total,
|
|
1059
1484
|
raw: chunk
|
|
1060
1485
|
};
|
|
1061
1486
|
}
|
|
@@ -1073,9 +1498,9 @@ function mapGenerateChunk(chunk, aggregated) {
|
|
|
1073
1498
|
}
|
|
1074
1499
|
return events;
|
|
1075
1500
|
}
|
|
1076
|
-
function normalizeGenerateStream(source) {
|
|
1501
|
+
function normalizeGenerateStream(source, signal) {
|
|
1077
1502
|
const initial = { response: "", model: "", done: false };
|
|
1078
|
-
return new OllamaStream(source, mapGenerateChunk, aggregateGenerate, initial);
|
|
1503
|
+
return new OllamaStream(source, mapGenerateChunk, aggregateGenerate, initial, signal);
|
|
1079
1504
|
}
|
|
1080
1505
|
function aggregateProgress(_accumulated, chunk) {
|
|
1081
1506
|
return {
|
|
@@ -1095,9 +1520,9 @@ function mapProgressChunk(chunk, aggregated) {
|
|
|
1095
1520
|
}
|
|
1096
1521
|
return events;
|
|
1097
1522
|
}
|
|
1098
|
-
function normalizeProgressStream(source) {
|
|
1523
|
+
function normalizeProgressStream(source, signal) {
|
|
1099
1524
|
const initial = { status: "", done: false };
|
|
1100
|
-
return new OllamaStream(source, mapProgressChunk, aggregateProgress, initial);
|
|
1525
|
+
return new OllamaStream(source, mapProgressChunk, aggregateProgress, initial, signal);
|
|
1101
1526
|
}
|
|
1102
1527
|
|
|
1103
1528
|
// src/transport/backoff.ts
|
|
@@ -1123,8 +1548,33 @@ function isRetryableDefault(error) {
|
|
|
1123
1548
|
}
|
|
1124
1549
|
return false;
|
|
1125
1550
|
}
|
|
1126
|
-
|
|
1127
|
-
|
|
1551
|
+
function abortError(signal, fallbackMessage) {
|
|
1552
|
+
const reason = signal?.reason;
|
|
1553
|
+
if (reason instanceof OllamaClientError) return reason;
|
|
1554
|
+
return new OllamaAbortError(
|
|
1555
|
+
reason instanceof Error && reason.message ? reason.message : fallbackMessage,
|
|
1556
|
+
{ cause: reason }
|
|
1557
|
+
);
|
|
1558
|
+
}
|
|
1559
|
+
function sleep(ms, signal) {
|
|
1560
|
+
return new Promise((resolve, reject) => {
|
|
1561
|
+
if (signal?.aborted) {
|
|
1562
|
+
reject(abortError(signal, "Retry backoff aborted"));
|
|
1563
|
+
return;
|
|
1564
|
+
}
|
|
1565
|
+
const timerId = setTimeout(() => {
|
|
1566
|
+
signal?.removeEventListener("abort", onAbort);
|
|
1567
|
+
resolve();
|
|
1568
|
+
}, ms);
|
|
1569
|
+
const onAbort = () => {
|
|
1570
|
+
if (timerId !== void 0) clearTimeout(timerId);
|
|
1571
|
+
signal?.removeEventListener("abort", onAbort);
|
|
1572
|
+
reject(abortError(signal, "Retry backoff aborted"));
|
|
1573
|
+
};
|
|
1574
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
1575
|
+
});
|
|
1576
|
+
}
|
|
1577
|
+
async function withRetry(operation, config = DEFAULT_RETRY_CONFIG, signal) {
|
|
1128
1578
|
let attempt = 0;
|
|
1129
1579
|
while (true) {
|
|
1130
1580
|
try {
|
|
@@ -1137,7 +1587,7 @@ async function withRetry(operation, config = DEFAULT_RETRY_CONFIG) {
|
|
|
1137
1587
|
}
|
|
1138
1588
|
const delayMs = calculateBackoff(attempt, config.backoff);
|
|
1139
1589
|
config.onRetry?.(error, attempt, delayMs);
|
|
1140
|
-
await sleep(delayMs);
|
|
1590
|
+
await sleep(delayMs, signal);
|
|
1141
1591
|
attempt += 1;
|
|
1142
1592
|
}
|
|
1143
1593
|
}
|
|
@@ -1203,7 +1653,7 @@ var ModelsClient = class {
|
|
|
1203
1653
|
body: { ...request, stream: true },
|
|
1204
1654
|
signal
|
|
1205
1655
|
});
|
|
1206
|
-
return normalizeProgressStream(stream);
|
|
1656
|
+
return normalizeProgressStream(stream, signal);
|
|
1207
1657
|
},
|
|
1208
1658
|
{ ...request, singleEndpoint: true, holdUntil: (stream) => stream.finalResult }
|
|
1209
1659
|
);
|
|
@@ -1226,7 +1676,7 @@ var ModelsClient = class {
|
|
|
1226
1676
|
body: { ...request, stream: true },
|
|
1227
1677
|
signal
|
|
1228
1678
|
});
|
|
1229
|
-
return normalizeProgressStream(stream);
|
|
1679
|
+
return normalizeProgressStream(stream, signal);
|
|
1230
1680
|
},
|
|
1231
1681
|
{ ...request, singleEndpoint: true, holdUntil: (stream) => stream.finalResult }
|
|
1232
1682
|
);
|
|
@@ -1320,77 +1770,963 @@ var ModelsClient = class {
|
|
|
1320
1770
|
};
|
|
1321
1771
|
|
|
1322
1772
|
// src/integrations/openai.ts
|
|
1323
|
-
var
|
|
1324
|
-
|
|
1325
|
-
|
|
1773
|
+
var OpenAIResponsesStreamError = class extends OllamaClientError {
|
|
1774
|
+
responsePayload;
|
|
1775
|
+
constructor(message, responsePayload) {
|
|
1776
|
+
super(message, { code: "openai_responses_stream_error", retryable: false, response: { body: responsePayload } });
|
|
1777
|
+
this.responsePayload = responsePayload;
|
|
1326
1778
|
}
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1779
|
+
};
|
|
1780
|
+
var OpenAIChatCompletionStream = class {
|
|
1781
|
+
constructor(source, signal) {
|
|
1782
|
+
this.source = source;
|
|
1783
|
+
this.finalResultPromise = new Promise((resolve, reject) => {
|
|
1784
|
+
this.resolveFinal = resolve;
|
|
1785
|
+
this.rejectFinal = reject;
|
|
1333
1786
|
});
|
|
1787
|
+
if (signal !== void 0) {
|
|
1788
|
+
const onAbort = () => this.abort();
|
|
1789
|
+
if (signal.aborted) onAbort();
|
|
1790
|
+
else {
|
|
1791
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
1792
|
+
this.removeAbortListener = () => signal.removeEventListener("abort", onAbort);
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1334
1795
|
}
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
signal
|
|
1343
|
-
});
|
|
1796
|
+
source;
|
|
1797
|
+
finalResultPromise;
|
|
1798
|
+
resolveFinal;
|
|
1799
|
+
rejectFinal;
|
|
1800
|
+
removeAbortListener;
|
|
1801
|
+
get finalResult() {
|
|
1802
|
+
return this.finalResultPromise;
|
|
1344
1803
|
}
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
});
|
|
1804
|
+
abort() {
|
|
1805
|
+
this.removeAbortListener?.();
|
|
1806
|
+
this.removeAbortListener = void 0;
|
|
1807
|
+
this.source.abort?.();
|
|
1808
|
+
this.rejectFinal(new OllamaAbortError("OpenAI Chat Completion stream aborted"));
|
|
1351
1809
|
}
|
|
1352
|
-
async
|
|
1353
|
-
|
|
1810
|
+
async *[Symbol.asyncIterator]() {
|
|
1811
|
+
const choices = /* @__PURE__ */ new Map();
|
|
1812
|
+
let id = "";
|
|
1813
|
+
let model = "";
|
|
1814
|
+
let created = 0;
|
|
1815
|
+
let systemFingerprint;
|
|
1816
|
+
let usage;
|
|
1817
|
+
let completed = false;
|
|
1818
|
+
try {
|
|
1819
|
+
for await (const event of this.source) {
|
|
1820
|
+
if (event.data === "[DONE]") break;
|
|
1821
|
+
let chunk;
|
|
1822
|
+
try {
|
|
1823
|
+
chunk = JSON.parse(event.data);
|
|
1824
|
+
} catch (error) {
|
|
1825
|
+
throw new Error("Failed to parse OpenAI chat completion SSE payload", { cause: error });
|
|
1826
|
+
}
|
|
1827
|
+
id = chunk.id || id;
|
|
1828
|
+
model = chunk.model || model;
|
|
1829
|
+
created = chunk.created || created;
|
|
1830
|
+
if (chunk.system_fingerprint !== void 0) systemFingerprint = chunk.system_fingerprint;
|
|
1831
|
+
if (chunk.usage !== null && chunk.usage !== void 0) usage = chunk.usage;
|
|
1832
|
+
for (const choice of chunk.choices) {
|
|
1833
|
+
const existing = choices.get(choice.index);
|
|
1834
|
+
const delta = choice.delta;
|
|
1835
|
+
if (!existing) {
|
|
1836
|
+
const toolCalls = delta.tool_calls?.map((toolCall) => ({
|
|
1837
|
+
id: toolCall.id ?? "",
|
|
1838
|
+
type: "function",
|
|
1839
|
+
function: {
|
|
1840
|
+
name: toolCall.function?.name ?? "",
|
|
1841
|
+
arguments: toolCall.function?.arguments ?? ""
|
|
1842
|
+
}
|
|
1843
|
+
}));
|
|
1844
|
+
choices.set(choice.index, {
|
|
1845
|
+
message: {
|
|
1846
|
+
role: delta.role ?? "assistant",
|
|
1847
|
+
content: delta.content ?? "",
|
|
1848
|
+
...delta.reasoning !== void 0 && delta.reasoning !== null ? { reasoning: delta.reasoning } : {},
|
|
1849
|
+
...toolCalls?.length ? { tool_calls: toolCalls } : {}
|
|
1850
|
+
},
|
|
1851
|
+
finish_reason: choice.finish_reason ?? "",
|
|
1852
|
+
...choice.logprobs !== void 0 ? { logprobs: choice.logprobs } : {}
|
|
1853
|
+
});
|
|
1854
|
+
continue;
|
|
1855
|
+
}
|
|
1856
|
+
const priorToolCalls = [...existing.message.tool_calls ?? []];
|
|
1857
|
+
for (const toolCall of delta.tool_calls ?? []) {
|
|
1858
|
+
const position = toolCall.index;
|
|
1859
|
+
const current = priorToolCalls[position];
|
|
1860
|
+
if (current) {
|
|
1861
|
+
priorToolCalls[position] = {
|
|
1862
|
+
...current,
|
|
1863
|
+
...toolCall.id !== void 0 ? { id: toolCall.id } : {},
|
|
1864
|
+
function: {
|
|
1865
|
+
name: current.function?.name ?? toolCall.function?.name ?? "",
|
|
1866
|
+
arguments: (current.function?.arguments ?? "") + (toolCall.function?.arguments ?? "")
|
|
1867
|
+
}
|
|
1868
|
+
};
|
|
1869
|
+
} else {
|
|
1870
|
+
priorToolCalls[position] = {
|
|
1871
|
+
id: toolCall.id ?? "",
|
|
1872
|
+
type: "function",
|
|
1873
|
+
function: {
|
|
1874
|
+
name: toolCall.function?.name ?? "",
|
|
1875
|
+
arguments: toolCall.function?.arguments ?? ""
|
|
1876
|
+
}
|
|
1877
|
+
};
|
|
1878
|
+
}
|
|
1879
|
+
}
|
|
1880
|
+
choices.set(choice.index, {
|
|
1881
|
+
message: {
|
|
1882
|
+
...existing.message,
|
|
1883
|
+
...delta.content !== void 0 && delta.content !== null ? { content: existing.message.content + delta.content } : {},
|
|
1884
|
+
...delta.reasoning !== void 0 && delta.reasoning !== null ? { reasoning: (existing.message.reasoning ?? "") + delta.reasoning } : {},
|
|
1885
|
+
...priorToolCalls.length ? { tool_calls: priorToolCalls } : {}
|
|
1886
|
+
},
|
|
1887
|
+
finish_reason: choice.finish_reason ?? existing.finish_reason,
|
|
1888
|
+
...choice.logprobs !== void 0 ? { logprobs: choice.logprobs } : {}
|
|
1889
|
+
});
|
|
1890
|
+
}
|
|
1891
|
+
yield chunk;
|
|
1892
|
+
}
|
|
1893
|
+
const response = {
|
|
1894
|
+
id,
|
|
1895
|
+
object: "chat.completion",
|
|
1896
|
+
created,
|
|
1897
|
+
model,
|
|
1898
|
+
...systemFingerprint !== void 0 ? { system_fingerprint: systemFingerprint } : {},
|
|
1899
|
+
choices: [...choices.entries()].sort(([a], [b]) => a - b).map(([index, choice]) => ({
|
|
1900
|
+
index,
|
|
1901
|
+
message: choice.message,
|
|
1902
|
+
finish_reason: choice.finish_reason,
|
|
1903
|
+
...choice.logprobs !== void 0 ? { logprobs: choice.logprobs } : {}
|
|
1904
|
+
})),
|
|
1905
|
+
...usage !== void 0 ? { usage } : {}
|
|
1906
|
+
};
|
|
1907
|
+
completed = true;
|
|
1908
|
+
this.removeAbortListener?.();
|
|
1909
|
+
this.removeAbortListener = void 0;
|
|
1910
|
+
this.resolveFinal(response);
|
|
1911
|
+
} catch (error) {
|
|
1912
|
+
this.removeAbortListener?.();
|
|
1913
|
+
this.removeAbortListener = void 0;
|
|
1914
|
+
this.rejectFinal(error);
|
|
1915
|
+
throw error;
|
|
1916
|
+
} finally {
|
|
1917
|
+
if (!completed) {
|
|
1918
|
+
this.source.abort?.();
|
|
1919
|
+
this.rejectFinal(new OllamaAbortError("OpenAI Chat Completion stream ended before completion"));
|
|
1920
|
+
}
|
|
1921
|
+
}
|
|
1354
1922
|
}
|
|
1355
1923
|
};
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
http;
|
|
1363
|
-
async createMessage(request, signal) {
|
|
1364
|
-
return this.http.request({
|
|
1365
|
-
path: "/v1/messages",
|
|
1366
|
-
body: request,
|
|
1367
|
-
signal
|
|
1924
|
+
var OpenAICompletionStream = class {
|
|
1925
|
+
constructor(source, signal) {
|
|
1926
|
+
this.source = source;
|
|
1927
|
+
this.finalResultPromise = new Promise((resolve, reject) => {
|
|
1928
|
+
this.resolveFinal = resolve;
|
|
1929
|
+
this.rejectFinal = reject;
|
|
1368
1930
|
});
|
|
1931
|
+
if (signal !== void 0) {
|
|
1932
|
+
const onAbort = () => this.abort();
|
|
1933
|
+
if (signal.aborted) onAbort();
|
|
1934
|
+
else {
|
|
1935
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
1936
|
+
this.removeAbortListener = () => signal.removeEventListener("abort", onAbort);
|
|
1937
|
+
}
|
|
1938
|
+
}
|
|
1369
1939
|
}
|
|
1370
|
-
|
|
1371
|
-
|
|
1940
|
+
source;
|
|
1941
|
+
finalResultPromise;
|
|
1942
|
+
resolveFinal;
|
|
1943
|
+
rejectFinal;
|
|
1944
|
+
removeAbortListener;
|
|
1945
|
+
get finalResult() {
|
|
1946
|
+
return this.finalResultPromise;
|
|
1372
1947
|
}
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
const bufferCtor = globalThis.Buffer;
|
|
1379
|
-
if (bufferCtor !== void 0) return bufferCtor.from(image).toString("base64");
|
|
1380
|
-
const CHUNK_SIZE = 32768;
|
|
1381
|
-
let binary = "";
|
|
1382
|
-
for (let i = 0; i < image.length; i += CHUNK_SIZE) {
|
|
1383
|
-
binary += String.fromCharCode(...image.subarray(i, i + CHUNK_SIZE));
|
|
1948
|
+
abort() {
|
|
1949
|
+
this.removeAbortListener?.();
|
|
1950
|
+
this.removeAbortListener = void 0;
|
|
1951
|
+
this.source.abort?.();
|
|
1952
|
+
this.rejectFinal(new OllamaAbortError("OpenAI Completion stream aborted"));
|
|
1384
1953
|
}
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1954
|
+
async *[Symbol.asyncIterator]() {
|
|
1955
|
+
const texts = /* @__PURE__ */ new Map();
|
|
1956
|
+
let id = "";
|
|
1957
|
+
let model = "";
|
|
1958
|
+
let created = 0;
|
|
1959
|
+
let usage;
|
|
1960
|
+
let completed = false;
|
|
1961
|
+
try {
|
|
1962
|
+
for await (const event of this.source) {
|
|
1963
|
+
if (event.data === "[DONE]") break;
|
|
1964
|
+
const chunk = JSON.parse(event.data);
|
|
1965
|
+
id = chunk.id || id;
|
|
1966
|
+
model = chunk.model || model;
|
|
1967
|
+
created = chunk.created || created;
|
|
1968
|
+
if (chunk.usage !== null && chunk.usage !== void 0) usage = chunk.usage;
|
|
1969
|
+
for (const choice of chunk.choices) {
|
|
1970
|
+
const existing = texts.get(choice.index);
|
|
1971
|
+
texts.set(choice.index, {
|
|
1972
|
+
text: (existing?.text ?? "") + (choice.text ?? ""),
|
|
1973
|
+
index: choice.index,
|
|
1974
|
+
finish_reason: choice.finish_reason ?? existing?.finish_reason ?? null,
|
|
1975
|
+
...choice.logprobs !== void 0 ? { logprobs: choice.logprobs } : {}
|
|
1976
|
+
});
|
|
1977
|
+
}
|
|
1978
|
+
yield chunk;
|
|
1979
|
+
}
|
|
1980
|
+
const response = {
|
|
1981
|
+
id,
|
|
1982
|
+
object: "text_completion",
|
|
1983
|
+
created,
|
|
1984
|
+
model,
|
|
1985
|
+
choices: [...texts.values()].sort((a, b) => a.index - b.index),
|
|
1986
|
+
...usage !== void 0 ? { usage } : {}
|
|
1987
|
+
};
|
|
1988
|
+
completed = true;
|
|
1989
|
+
this.removeAbortListener?.();
|
|
1990
|
+
this.removeAbortListener = void 0;
|
|
1991
|
+
this.resolveFinal(response);
|
|
1992
|
+
} catch (error) {
|
|
1993
|
+
this.removeAbortListener?.();
|
|
1994
|
+
this.removeAbortListener = void 0;
|
|
1995
|
+
this.rejectFinal(error);
|
|
1996
|
+
throw error;
|
|
1997
|
+
} finally {
|
|
1998
|
+
if (!completed) {
|
|
1999
|
+
this.source.abort?.();
|
|
2000
|
+
this.rejectFinal(new OllamaAbortError("OpenAI Completion stream ended before completion"));
|
|
2001
|
+
}
|
|
2002
|
+
}
|
|
2003
|
+
}
|
|
2004
|
+
};
|
|
2005
|
+
var OpenAIResponsesStream = class {
|
|
2006
|
+
constructor(source, signal) {
|
|
2007
|
+
this.source = source;
|
|
2008
|
+
this.finalResultPromise = new Promise((resolve, reject) => {
|
|
2009
|
+
this.resolveFinal = resolve;
|
|
2010
|
+
this.rejectFinal = reject;
|
|
2011
|
+
});
|
|
2012
|
+
if (signal !== void 0) {
|
|
2013
|
+
const onAbort = () => this.abort();
|
|
2014
|
+
if (signal.aborted) onAbort();
|
|
2015
|
+
else {
|
|
2016
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
2017
|
+
this.removeAbortListener = () => signal.removeEventListener("abort", onAbort);
|
|
2018
|
+
}
|
|
2019
|
+
}
|
|
2020
|
+
}
|
|
2021
|
+
source;
|
|
2022
|
+
finalResultPromise;
|
|
2023
|
+
resolveFinal;
|
|
2024
|
+
rejectFinal;
|
|
2025
|
+
removeAbortListener;
|
|
2026
|
+
get finalResult() {
|
|
2027
|
+
return this.finalResultPromise;
|
|
2028
|
+
}
|
|
2029
|
+
abort() {
|
|
2030
|
+
this.removeAbortListener?.();
|
|
2031
|
+
this.removeAbortListener = void 0;
|
|
2032
|
+
this.source.abort?.();
|
|
2033
|
+
this.rejectFinal(new OllamaAbortError("OpenAI Responses stream aborted"));
|
|
2034
|
+
}
|
|
2035
|
+
async *[Symbol.asyncIterator]() {
|
|
2036
|
+
let finalResponse;
|
|
2037
|
+
let responseMeta;
|
|
2038
|
+
let terminalFailure;
|
|
2039
|
+
let terminalIncomplete;
|
|
2040
|
+
const outputs = /* @__PURE__ */ new Map();
|
|
2041
|
+
const canonicalOutputItems = /* @__PURE__ */ new Map();
|
|
2042
|
+
let completed = false;
|
|
2043
|
+
const ensureState = (outputIndex, itemId, kind) => {
|
|
2044
|
+
const existing = outputs.get(outputIndex);
|
|
2045
|
+
if (existing) {
|
|
2046
|
+
if (itemId) existing.itemId = itemId;
|
|
2047
|
+
existing.kind = kind;
|
|
2048
|
+
return existing;
|
|
2049
|
+
}
|
|
2050
|
+
const state = { itemId, kind, content: /* @__PURE__ */ new Map(), summary: /* @__PURE__ */ new Map(), arguments: "", reasoningText: "" };
|
|
2051
|
+
outputs.set(outputIndex, state);
|
|
2052
|
+
return state;
|
|
2053
|
+
};
|
|
2054
|
+
const applyOutputItem = (outputIndex, item) => {
|
|
2055
|
+
const kind = item.type === "message" ? "message" : item.type === "function_call" ? "function_call" : "reasoning";
|
|
2056
|
+
const state = ensureState(outputIndex, item.id ?? "", kind);
|
|
2057
|
+
state.itemId = item.id ?? state.itemId;
|
|
2058
|
+
state.status = item.status;
|
|
2059
|
+
if (item.type === "message") {
|
|
2060
|
+
state.role = item.role;
|
|
2061
|
+
state.phase = item.phase;
|
|
2062
|
+
state.content.clear();
|
|
2063
|
+
item.content.forEach((p, i) => state.content.set(i, p));
|
|
2064
|
+
} else if (item.type === "function_call") {
|
|
2065
|
+
state.callId = item.call_id;
|
|
2066
|
+
state.name = item.name;
|
|
2067
|
+
state.arguments = item.arguments;
|
|
2068
|
+
} else {
|
|
2069
|
+
state.summary.clear();
|
|
2070
|
+
item.summary?.forEach((p, i) => state.summary.set(i, p.text));
|
|
2071
|
+
state.reasoningText = item.text ?? "";
|
|
2072
|
+
}
|
|
2073
|
+
};
|
|
2074
|
+
const applyContentPart = (outputIndex, itemId, contentIndex, part) => {
|
|
2075
|
+
const state = ensureState(outputIndex, itemId, "message");
|
|
2076
|
+
state.content.set(contentIndex, part);
|
|
2077
|
+
};
|
|
2078
|
+
const reconstructedOutput = () => [...outputs.entries()].sort(([a], [b]) => a - b).map(([outputIndex, state]) => {
|
|
2079
|
+
const canonical = canonicalOutputItems.get(outputIndex);
|
|
2080
|
+
if (canonical) return canonical;
|
|
2081
|
+
if (state.kind === "message") return { type: "message", ...state.itemId ? { id: state.itemId } : {}, ...state.status ? { status: state.status } : {}, role: state.role ?? "assistant", ...state.phase ? { phase: state.phase } : {}, content: [...state.content.entries()].sort(([a], [b]) => a - b).map(([, p]) => p) };
|
|
2082
|
+
if (state.kind === "function_call") return { type: "function_call", ...state.itemId ? { id: state.itemId } : {}, ...state.callId ? { call_id: state.callId } : {}, ...state.status ? { status: state.status } : {}, name: state.name ?? "", arguments: state.arguments };
|
|
2083
|
+
return { type: "reasoning", ...state.itemId ? { id: state.itemId } : {}, ...state.status ? { status: state.status } : {}, ...state.summary.size ? { summary: [...state.summary.entries()].sort(([a], [b]) => a - b).map(([, text]) => ({ type: "summary_text", text })) } : {}, ...state.reasoningText ? { text: state.reasoningText } : {} };
|
|
2084
|
+
});
|
|
2085
|
+
try {
|
|
2086
|
+
for await (const event of this.source) {
|
|
2087
|
+
if (event.data === "[DONE]") break;
|
|
2088
|
+
let payload;
|
|
2089
|
+
try {
|
|
2090
|
+
payload = JSON.parse(event.data);
|
|
2091
|
+
} catch (error) {
|
|
2092
|
+
throw new Error("Failed to parse OpenAI Responses SSE payload", { cause: error });
|
|
2093
|
+
}
|
|
2094
|
+
switch (payload.type) {
|
|
2095
|
+
case "response.created":
|
|
2096
|
+
case "response.in_progress":
|
|
2097
|
+
case "response.queued":
|
|
2098
|
+
responseMeta = payload.response;
|
|
2099
|
+
break;
|
|
2100
|
+
case "response.completed":
|
|
2101
|
+
case "response.done":
|
|
2102
|
+
finalResponse = payload.response;
|
|
2103
|
+
break;
|
|
2104
|
+
case "response.failed":
|
|
2105
|
+
terminalFailure = payload.response;
|
|
2106
|
+
finalResponse = terminalFailure;
|
|
2107
|
+
break;
|
|
2108
|
+
case "response.incomplete":
|
|
2109
|
+
terminalIncomplete = payload.response;
|
|
2110
|
+
finalResponse = terminalIncomplete;
|
|
2111
|
+
break;
|
|
2112
|
+
case "response.output_item.added": {
|
|
2113
|
+
const e = payload;
|
|
2114
|
+
applyOutputItem(e.output_index, e.item);
|
|
2115
|
+
break;
|
|
2116
|
+
}
|
|
2117
|
+
case "response.output_item.done": {
|
|
2118
|
+
const e = payload;
|
|
2119
|
+
applyOutputItem(e.output_index, e.item);
|
|
2120
|
+
canonicalOutputItems.set(e.output_index, e.item);
|
|
2121
|
+
break;
|
|
2122
|
+
}
|
|
2123
|
+
case "response.content_part.added":
|
|
2124
|
+
case "response.content_part.done": {
|
|
2125
|
+
const e = payload;
|
|
2126
|
+
if (e.part.type === "output_text" && "text" in e.part && typeof e.part.text === "string" || e.part.type === "refusal" && "refusal" in e.part && typeof e.part.refusal === "string") {
|
|
2127
|
+
applyContentPart(e.output_index, e.item_id, e.content_index, e.part);
|
|
2128
|
+
}
|
|
2129
|
+
break;
|
|
2130
|
+
}
|
|
2131
|
+
case "response.output_text.delta": {
|
|
2132
|
+
const e = payload;
|
|
2133
|
+
const s = ensureState(e.output_index, e.item_id, "message");
|
|
2134
|
+
const p = s.content.get(e.content_index);
|
|
2135
|
+
s.content.set(e.content_index, { type: "output_text", text: (p?.type === "output_text" ? p.text : "") + e.delta });
|
|
2136
|
+
break;
|
|
2137
|
+
}
|
|
2138
|
+
case "response.output_text.done": {
|
|
2139
|
+
const e = payload;
|
|
2140
|
+
const s = ensureState(e.output_index, e.item_id, "message");
|
|
2141
|
+
s.content.set(e.content_index, { type: "output_text", text: e.text });
|
|
2142
|
+
break;
|
|
2143
|
+
}
|
|
2144
|
+
case "response.refusal.delta": {
|
|
2145
|
+
const e = payload;
|
|
2146
|
+
const s = ensureState(e.output_index, e.item_id, "message");
|
|
2147
|
+
const p = s.content.get(e.content_index);
|
|
2148
|
+
s.content.set(e.content_index, { type: "refusal", refusal: (p?.type === "refusal" ? p.refusal : "") + e.delta });
|
|
2149
|
+
break;
|
|
2150
|
+
}
|
|
2151
|
+
case "response.refusal.done": {
|
|
2152
|
+
const e = payload;
|
|
2153
|
+
const s = ensureState(e.output_index, e.item_id, "message");
|
|
2154
|
+
s.content.set(e.content_index, { type: "refusal", refusal: e.refusal });
|
|
2155
|
+
break;
|
|
2156
|
+
}
|
|
2157
|
+
case "response.function_call_arguments.delta": {
|
|
2158
|
+
const e = payload;
|
|
2159
|
+
const s = ensureState(e.output_index, e.item_id, "function_call");
|
|
2160
|
+
s.arguments += e.delta;
|
|
2161
|
+
break;
|
|
2162
|
+
}
|
|
2163
|
+
case "response.function_call_arguments.done": {
|
|
2164
|
+
const e = payload;
|
|
2165
|
+
const s = ensureState(e.output_index, e.item_id, "function_call");
|
|
2166
|
+
s.arguments = e.arguments;
|
|
2167
|
+
if (e.name !== void 0) s.name = e.name;
|
|
2168
|
+
break;
|
|
2169
|
+
}
|
|
2170
|
+
case "response.reasoning_summary_part.added":
|
|
2171
|
+
case "response.reasoning_summary_part.done": {
|
|
2172
|
+
const e = payload;
|
|
2173
|
+
const s = ensureState(e.output_index, e.item_id, "reasoning");
|
|
2174
|
+
s.summary.set(e.summary_index, e.part.text);
|
|
2175
|
+
break;
|
|
2176
|
+
}
|
|
2177
|
+
case "response.reasoning_summary_text.delta": {
|
|
2178
|
+
const e = payload;
|
|
2179
|
+
const s = ensureState(e.output_index, e.item_id, "reasoning");
|
|
2180
|
+
s.summary.set(e.summary_index, (s.summary.get(e.summary_index) ?? "") + e.delta);
|
|
2181
|
+
break;
|
|
2182
|
+
}
|
|
2183
|
+
case "response.reasoning_summary_text.done": {
|
|
2184
|
+
const e = payload;
|
|
2185
|
+
const s = ensureState(e.output_index, e.item_id, "reasoning");
|
|
2186
|
+
s.summary.set(e.summary_index, e.text);
|
|
2187
|
+
break;
|
|
2188
|
+
}
|
|
2189
|
+
case "response.reasoning_text.delta": {
|
|
2190
|
+
const e = payload;
|
|
2191
|
+
const s = ensureState(e.output_index, e.item_id, "reasoning");
|
|
2192
|
+
s.reasoningText += e.delta;
|
|
2193
|
+
break;
|
|
2194
|
+
}
|
|
2195
|
+
case "response.reasoning_text.done": {
|
|
2196
|
+
const e = payload;
|
|
2197
|
+
const s = ensureState(e.output_index, e.item_id, "reasoning");
|
|
2198
|
+
s.reasoningText = e.text;
|
|
2199
|
+
break;
|
|
2200
|
+
}
|
|
2201
|
+
default:
|
|
2202
|
+
break;
|
|
2203
|
+
}
|
|
2204
|
+
yield payload;
|
|
2205
|
+
}
|
|
2206
|
+
if (terminalFailure || terminalIncomplete) {
|
|
2207
|
+
const response = terminalFailure ?? terminalIncomplete;
|
|
2208
|
+
const status = terminalFailure ? "failed" : "incomplete";
|
|
2209
|
+
const detail = response.error?.message ?? response.incomplete_details?.reason ?? "unknown error";
|
|
2210
|
+
throw new OpenAIResponsesStreamError("OpenAI Responses stream " + status + ": " + detail, response);
|
|
2211
|
+
}
|
|
2212
|
+
if (!finalResponse) {
|
|
2213
|
+
if (!responseMeta) throw new Error("OpenAI Responses stream ended without a terminal response payload");
|
|
2214
|
+
if (responseMeta.status === "failed" || responseMeta.status === "incomplete") {
|
|
2215
|
+
const detail = responseMeta.error?.message ?? responseMeta.incomplete_details?.reason ?? "unknown error";
|
|
2216
|
+
throw new OpenAIResponsesStreamError("OpenAI Responses stream " + responseMeta.status + ": " + detail, responseMeta);
|
|
2217
|
+
}
|
|
2218
|
+
finalResponse = { ...responseMeta, output: reconstructedOutput(), status: "completed" };
|
|
2219
|
+
} else if (finalResponse.status === "failed" || finalResponse.status === "incomplete") {
|
|
2220
|
+
const detail = finalResponse.error?.message ?? finalResponse.incomplete_details?.reason ?? "unknown error";
|
|
2221
|
+
throw new OpenAIResponsesStreamError("OpenAI Responses stream " + finalResponse.status + ": " + detail, finalResponse);
|
|
2222
|
+
}
|
|
2223
|
+
if (finalResponse.output.length === 0 && outputs.size > 0) finalResponse = { ...finalResponse, output: reconstructedOutput() };
|
|
2224
|
+
completed = true;
|
|
2225
|
+
this.removeAbortListener?.();
|
|
2226
|
+
this.removeAbortListener = void 0;
|
|
2227
|
+
this.resolveFinal(finalResponse);
|
|
2228
|
+
} catch (error) {
|
|
2229
|
+
this.removeAbortListener?.();
|
|
2230
|
+
this.removeAbortListener = void 0;
|
|
2231
|
+
this.rejectFinal(error);
|
|
2232
|
+
throw error;
|
|
2233
|
+
} finally {
|
|
2234
|
+
if (!completed) {
|
|
2235
|
+
this.source.abort?.();
|
|
2236
|
+
this.rejectFinal(new OllamaAbortError("OpenAI Responses stream ended before completion"));
|
|
2237
|
+
}
|
|
2238
|
+
}
|
|
2239
|
+
}
|
|
2240
|
+
};
|
|
2241
|
+
var OpenAICompatClient = class {
|
|
2242
|
+
constructor(http, runner) {
|
|
2243
|
+
this.http = http;
|
|
2244
|
+
this.runner = runner;
|
|
2245
|
+
}
|
|
2246
|
+
http;
|
|
2247
|
+
runner;
|
|
2248
|
+
request(operation, model, signal, holdUntil) {
|
|
2249
|
+
if (this.runner) {
|
|
2250
|
+
return this.runner(
|
|
2251
|
+
(http, runnerSignal) => operation(http, runnerSignal),
|
|
2252
|
+
{
|
|
2253
|
+
...model !== void 0 ? { model } : {},
|
|
2254
|
+
...signal !== void 0 ? { signal } : {},
|
|
2255
|
+
...holdUntil !== void 0 ? { holdUntil } : {}
|
|
2256
|
+
}
|
|
2257
|
+
);
|
|
2258
|
+
}
|
|
2259
|
+
return operation(this.http, signal);
|
|
2260
|
+
}
|
|
2261
|
+
async createChatCompletion(request, signal) {
|
|
2262
|
+
if (request.stream) {
|
|
2263
|
+
return this.request(
|
|
2264
|
+
(http, requestSignal) => http.requestSseStream({
|
|
2265
|
+
path: "/v1/chat/completions",
|
|
2266
|
+
body: request,
|
|
2267
|
+
signal: requestSignal
|
|
2268
|
+
}).then((source) => new OpenAIChatCompletionStream(source, requestSignal)),
|
|
2269
|
+
request.model,
|
|
2270
|
+
signal,
|
|
2271
|
+
(stream) => stream.finalResult
|
|
2272
|
+
);
|
|
2273
|
+
}
|
|
2274
|
+
return this.request(
|
|
2275
|
+
(http, requestSignal) => http.request({
|
|
2276
|
+
path: "/v1/chat/completions",
|
|
2277
|
+
body: request,
|
|
2278
|
+
signal: requestSignal
|
|
2279
|
+
}),
|
|
2280
|
+
request.model,
|
|
2281
|
+
signal
|
|
2282
|
+
);
|
|
2283
|
+
}
|
|
2284
|
+
async chatCompletions(request, signal) {
|
|
2285
|
+
return this.createChatCompletion(request, signal);
|
|
2286
|
+
}
|
|
2287
|
+
async listModels(signal) {
|
|
2288
|
+
return this.request(
|
|
2289
|
+
(http, requestSignal) => http.request({
|
|
2290
|
+
path: "/v1/models",
|
|
2291
|
+
method: "GET",
|
|
2292
|
+
signal: requestSignal
|
|
2293
|
+
}),
|
|
2294
|
+
void 0,
|
|
2295
|
+
signal
|
|
2296
|
+
);
|
|
2297
|
+
}
|
|
2298
|
+
async retrieveModel(model, signal) {
|
|
2299
|
+
return this.request(
|
|
2300
|
+
(http, requestSignal) => http.request({
|
|
2301
|
+
path: `/v1/models/${encodeURIComponent(model)}`,
|
|
2302
|
+
method: "GET",
|
|
2303
|
+
signal: requestSignal
|
|
2304
|
+
}),
|
|
2305
|
+
model,
|
|
2306
|
+
signal
|
|
2307
|
+
);
|
|
2308
|
+
}
|
|
2309
|
+
async getModel(model, signal) {
|
|
2310
|
+
return this.retrieveModel(model, signal);
|
|
2311
|
+
}
|
|
2312
|
+
async createCompletion(request, signal) {
|
|
2313
|
+
if (request.stream) {
|
|
2314
|
+
return this.request(
|
|
2315
|
+
(http, requestSignal) => http.requestSseStream({
|
|
2316
|
+
path: "/v1/completions",
|
|
2317
|
+
body: request,
|
|
2318
|
+
signal: requestSignal
|
|
2319
|
+
}).then((source) => new OpenAICompletionStream(source, requestSignal)),
|
|
2320
|
+
request.model,
|
|
2321
|
+
signal,
|
|
2322
|
+
(stream) => stream.finalResult
|
|
2323
|
+
);
|
|
2324
|
+
}
|
|
2325
|
+
return this.request(
|
|
2326
|
+
(http, requestSignal) => http.request({
|
|
2327
|
+
path: "/v1/completions",
|
|
2328
|
+
body: request,
|
|
2329
|
+
signal: requestSignal
|
|
2330
|
+
}),
|
|
2331
|
+
request.model,
|
|
2332
|
+
signal
|
|
2333
|
+
);
|
|
2334
|
+
}
|
|
2335
|
+
async completions(request, signal) {
|
|
2336
|
+
return this.createCompletion(request, signal);
|
|
2337
|
+
}
|
|
2338
|
+
async createEmbedding(request, signal) {
|
|
2339
|
+
return this.request(
|
|
2340
|
+
(http, requestSignal) => http.request({
|
|
2341
|
+
path: "/v1/embeddings",
|
|
2342
|
+
body: request,
|
|
2343
|
+
signal: requestSignal
|
|
2344
|
+
}),
|
|
2345
|
+
request.model,
|
|
2346
|
+
signal
|
|
2347
|
+
);
|
|
2348
|
+
}
|
|
2349
|
+
async embeddings(request, signal) {
|
|
2350
|
+
return this.createEmbedding(request, signal);
|
|
2351
|
+
}
|
|
2352
|
+
async createResponses(request, signal) {
|
|
2353
|
+
if (request.stream) {
|
|
2354
|
+
return this.request(
|
|
2355
|
+
(http, requestSignal) => http.requestSseStream({
|
|
2356
|
+
path: "/v1/responses",
|
|
2357
|
+
body: request,
|
|
2358
|
+
signal: requestSignal
|
|
2359
|
+
}).then((source) => new OpenAIResponsesStream(source, requestSignal)),
|
|
2360
|
+
request.model,
|
|
2361
|
+
signal,
|
|
2362
|
+
(stream) => stream.finalResult
|
|
2363
|
+
);
|
|
2364
|
+
}
|
|
2365
|
+
return this.request(
|
|
2366
|
+
(http, requestSignal) => http.request({
|
|
2367
|
+
path: "/v1/responses",
|
|
2368
|
+
body: request,
|
|
2369
|
+
signal: requestSignal
|
|
2370
|
+
}),
|
|
2371
|
+
request.model,
|
|
2372
|
+
signal
|
|
2373
|
+
);
|
|
2374
|
+
}
|
|
2375
|
+
async responses(request, signal) {
|
|
2376
|
+
return this.createResponses(request, signal);
|
|
2377
|
+
}
|
|
2378
|
+
};
|
|
2379
|
+
|
|
2380
|
+
// src/integrations/anthropic.ts
|
|
2381
|
+
function isAnthropicKnownStreamEvent(event) {
|
|
2382
|
+
switch (event.type) {
|
|
2383
|
+
case "message_start":
|
|
2384
|
+
case "content_block_start":
|
|
2385
|
+
case "content_block_delta":
|
|
2386
|
+
case "content_block_stop":
|
|
2387
|
+
case "message_delta":
|
|
2388
|
+
case "message_stop":
|
|
2389
|
+
case "ping":
|
|
2390
|
+
case "error":
|
|
2391
|
+
return true;
|
|
2392
|
+
default:
|
|
2393
|
+
return false;
|
|
2394
|
+
}
|
|
2395
|
+
}
|
|
2396
|
+
function parseAnthropicEvent(event) {
|
|
2397
|
+
return JSON.parse(event.data);
|
|
2398
|
+
}
|
|
2399
|
+
function mergeContentBlock(current, delta) {
|
|
2400
|
+
if (delta.type === "text_delta" && current.type === "text") {
|
|
2401
|
+
return { ...current, text: current.text + delta.text };
|
|
2402
|
+
}
|
|
2403
|
+
if (delta.type === "input_json_delta" && current.type === "tool_use") {
|
|
2404
|
+
return current;
|
|
2405
|
+
}
|
|
2406
|
+
if (delta.type === "thinking_delta" && current.type === "thinking") {
|
|
2407
|
+
return { ...current, thinking: current.thinking + delta.thinking };
|
|
2408
|
+
}
|
|
2409
|
+
if (delta.type === "signature_delta" && current.type === "thinking") {
|
|
2410
|
+
return { ...current, signature: (current.signature ?? "") + delta.signature };
|
|
2411
|
+
}
|
|
2412
|
+
return current;
|
|
2413
|
+
}
|
|
2414
|
+
function sanitizeAnthropicTextBlock(block) {
|
|
2415
|
+
if (block.cache_control === void 0) return block;
|
|
2416
|
+
const { cache_control: _cacheControl, ...sanitized } = block;
|
|
2417
|
+
return sanitized;
|
|
2418
|
+
}
|
|
2419
|
+
function sanitizeAnthropicContentBlock(block) {
|
|
2420
|
+
if (block.type === "text") {
|
|
2421
|
+
return sanitizeAnthropicTextBlock(block);
|
|
2422
|
+
}
|
|
2423
|
+
if (block.type === "tool_result" && Array.isArray(block.content)) {
|
|
2424
|
+
return {
|
|
2425
|
+
...block,
|
|
2426
|
+
content: block.content.map(sanitizeAnthropicTextBlock)
|
|
2427
|
+
};
|
|
2428
|
+
}
|
|
2429
|
+
return block;
|
|
2430
|
+
}
|
|
2431
|
+
function sanitizeAnthropicRequest(request) {
|
|
2432
|
+
const messages = request.messages.map((message) => {
|
|
2433
|
+
if (typeof message.content === "string") return message;
|
|
2434
|
+
return {
|
|
2435
|
+
...message,
|
|
2436
|
+
content: message.content.map(sanitizeAnthropicContentBlock)
|
|
2437
|
+
};
|
|
2438
|
+
});
|
|
2439
|
+
const system = Array.isArray(request.system) ? request.system.map(sanitizeAnthropicTextBlock) : request.system;
|
|
2440
|
+
const {
|
|
2441
|
+
tool_choice: _toolChoice,
|
|
2442
|
+
metadata: _metadata,
|
|
2443
|
+
...sanitized
|
|
2444
|
+
} = request;
|
|
2445
|
+
return {
|
|
2446
|
+
...sanitized,
|
|
2447
|
+
messages,
|
|
2448
|
+
...system !== void 0 ? { system } : {}
|
|
2449
|
+
};
|
|
2450
|
+
}
|
|
2451
|
+
var AnthropicMessagesStream = class {
|
|
2452
|
+
constructor(source, signal) {
|
|
2453
|
+
this.source = source;
|
|
2454
|
+
this.finalResultPromise = new Promise((resolve, reject) => {
|
|
2455
|
+
this.resolveFinal = resolve;
|
|
2456
|
+
this.rejectFinal = reject;
|
|
2457
|
+
});
|
|
2458
|
+
if (signal !== void 0) {
|
|
2459
|
+
const onAbort = () => this.abort();
|
|
2460
|
+
if (signal.aborted) onAbort();
|
|
2461
|
+
else {
|
|
2462
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
2463
|
+
this.removeAbortListener = () => signal.removeEventListener("abort", onAbort);
|
|
2464
|
+
}
|
|
2465
|
+
}
|
|
2466
|
+
}
|
|
2467
|
+
source;
|
|
2468
|
+
finalResultPromise;
|
|
2469
|
+
resolveFinal;
|
|
2470
|
+
rejectFinal;
|
|
2471
|
+
removeAbortListener;
|
|
2472
|
+
get finalResult() {
|
|
2473
|
+
return this.finalResultPromise;
|
|
2474
|
+
}
|
|
2475
|
+
abort() {
|
|
2476
|
+
this.removeAbortListener?.();
|
|
2477
|
+
this.removeAbortListener = void 0;
|
|
2478
|
+
this.source.abort?.();
|
|
2479
|
+
this.rejectFinal(new OllamaAbortError("Anthropic Messages stream aborted"));
|
|
2480
|
+
}
|
|
2481
|
+
async *[Symbol.asyncIterator]() {
|
|
2482
|
+
let message;
|
|
2483
|
+
const blocks = /* @__PURE__ */ new Map();
|
|
2484
|
+
const toolInputJson = /* @__PURE__ */ new Map();
|
|
2485
|
+
let completed = false;
|
|
2486
|
+
try {
|
|
2487
|
+
for await (const rawEvent of this.source) {
|
|
2488
|
+
let event;
|
|
2489
|
+
try {
|
|
2490
|
+
event = parseAnthropicEvent(rawEvent);
|
|
2491
|
+
} catch (error) {
|
|
2492
|
+
throw new Error("Failed to parse Anthropic message SSE payload", { cause: error });
|
|
2493
|
+
}
|
|
2494
|
+
if (!isAnthropicKnownStreamEvent(event)) {
|
|
2495
|
+
yield event;
|
|
2496
|
+
continue;
|
|
2497
|
+
}
|
|
2498
|
+
if (event.type === "error") {
|
|
2499
|
+
throw new Error(event.error.message);
|
|
2500
|
+
}
|
|
2501
|
+
if (event.type === "message_start") {
|
|
2502
|
+
message = {
|
|
2503
|
+
...event.message,
|
|
2504
|
+
content: [],
|
|
2505
|
+
...event.message.usage !== void 0 ? { usage: event.message.usage } : {}
|
|
2506
|
+
};
|
|
2507
|
+
} else if (event.type === "content_block_start") {
|
|
2508
|
+
blocks.set(event.index, event.content_block);
|
|
2509
|
+
} else if (event.type === "content_block_delta") {
|
|
2510
|
+
const current = blocks.get(event.index);
|
|
2511
|
+
if (event.delta.type === "input_json_delta") {
|
|
2512
|
+
toolInputJson.set(event.index, (toolInputJson.get(event.index) ?? "") + event.delta.partial_json);
|
|
2513
|
+
}
|
|
2514
|
+
if (current) blocks.set(event.index, mergeContentBlock(current, event.delta));
|
|
2515
|
+
} else if (event.type === "content_block_stop") {
|
|
2516
|
+
const current = blocks.get(event.index);
|
|
2517
|
+
if (current?.type === "tool_use") {
|
|
2518
|
+
const rawJson = toolInputJson.get(event.index);
|
|
2519
|
+
if (rawJson !== void 0 && rawJson !== "") {
|
|
2520
|
+
try {
|
|
2521
|
+
const parsed = JSON.parse(rawJson);
|
|
2522
|
+
blocks.set(event.index, { ...current, input: parsed });
|
|
2523
|
+
} catch {
|
|
2524
|
+
blocks.set(event.index, current);
|
|
2525
|
+
}
|
|
2526
|
+
}
|
|
2527
|
+
}
|
|
2528
|
+
} else if (event.type === "message_delta") {
|
|
2529
|
+
if (message) {
|
|
2530
|
+
message = {
|
|
2531
|
+
...message,
|
|
2532
|
+
stop_reason: event.delta.stop_reason !== void 0 && event.delta.stop_reason !== null ? event.delta.stop_reason : message.stop_reason,
|
|
2533
|
+
stop_sequence: event.delta.stop_sequence ?? message.stop_sequence,
|
|
2534
|
+
...event.usage !== void 0 ? {
|
|
2535
|
+
usage: {
|
|
2536
|
+
input_tokens: event.usage.input_tokens ?? message.usage?.input_tokens ?? 0,
|
|
2537
|
+
output_tokens: event.usage.output_tokens,
|
|
2538
|
+
...event.usage.cache_creation_input_tokens !== void 0 ? { cache_creation_input_tokens: event.usage.cache_creation_input_tokens } : message.usage?.cache_creation_input_tokens !== void 0 ? { cache_creation_input_tokens: message.usage.cache_creation_input_tokens } : {},
|
|
2539
|
+
...event.usage.cache_read_input_tokens !== void 0 ? { cache_read_input_tokens: event.usage.cache_read_input_tokens } : message.usage?.cache_read_input_tokens !== void 0 ? { cache_read_input_tokens: message.usage.cache_read_input_tokens } : {},
|
|
2540
|
+
...event.usage.cache_creation !== void 0 ? { cache_creation: event.usage.cache_creation } : message.usage?.cache_creation !== void 0 ? { cache_creation: message.usage.cache_creation } : {}
|
|
2541
|
+
}
|
|
2542
|
+
} : {}
|
|
2543
|
+
};
|
|
2544
|
+
}
|
|
2545
|
+
} else if (event.type === "message_stop" && message) {
|
|
2546
|
+
message = {
|
|
2547
|
+
...message,
|
|
2548
|
+
content: [...blocks.entries()].sort(([a], [b]) => a - b).map(([, block]) => block)
|
|
2549
|
+
};
|
|
2550
|
+
}
|
|
2551
|
+
yield event;
|
|
2552
|
+
}
|
|
2553
|
+
if (!message) {
|
|
2554
|
+
throw new Error("Anthropic Messages stream ended without message_start");
|
|
2555
|
+
}
|
|
2556
|
+
const final = {
|
|
2557
|
+
...message,
|
|
2558
|
+
content: [...blocks.entries()].sort(([a], [b]) => a - b).map(([, block]) => block)
|
|
2559
|
+
};
|
|
2560
|
+
completed = true;
|
|
2561
|
+
this.removeAbortListener?.();
|
|
2562
|
+
this.removeAbortListener = void 0;
|
|
2563
|
+
this.resolveFinal(final);
|
|
2564
|
+
} catch (error) {
|
|
2565
|
+
this.removeAbortListener?.();
|
|
2566
|
+
this.removeAbortListener = void 0;
|
|
2567
|
+
this.rejectFinal(error);
|
|
2568
|
+
throw error;
|
|
2569
|
+
} finally {
|
|
2570
|
+
if (!completed) {
|
|
2571
|
+
this.source.abort?.();
|
|
2572
|
+
this.rejectFinal(new OllamaAbortError("Anthropic Messages stream ended before completion"));
|
|
2573
|
+
}
|
|
2574
|
+
}
|
|
2575
|
+
}
|
|
2576
|
+
};
|
|
2577
|
+
var AnthropicCompatClient = class {
|
|
2578
|
+
constructor(http, runner) {
|
|
2579
|
+
this.http = http;
|
|
2580
|
+
this.runner = runner;
|
|
2581
|
+
}
|
|
2582
|
+
http;
|
|
2583
|
+
runner;
|
|
2584
|
+
request(operation, model, signal, holdUntil) {
|
|
2585
|
+
if (this.runner) {
|
|
2586
|
+
return this.runner(
|
|
2587
|
+
(http, runnerSignal) => operation(http, runnerSignal),
|
|
2588
|
+
{
|
|
2589
|
+
model,
|
|
2590
|
+
...signal !== void 0 ? { signal } : {},
|
|
2591
|
+
...holdUntil !== void 0 ? { holdUntil } : {}
|
|
2592
|
+
}
|
|
2593
|
+
);
|
|
2594
|
+
}
|
|
2595
|
+
return operation(this.http, signal);
|
|
2596
|
+
}
|
|
2597
|
+
async createMessage(request, signal) {
|
|
2598
|
+
if (request.stream) {
|
|
2599
|
+
return this.request(
|
|
2600
|
+
(http, requestSignal) => http.requestSseStream({
|
|
2601
|
+
path: "/v1/messages",
|
|
2602
|
+
body: sanitizeAnthropicRequest(request),
|
|
2603
|
+
headers: { "anthropic-version": "2023-06-01" },
|
|
2604
|
+
signal: requestSignal
|
|
2605
|
+
}).then((source) => new AnthropicMessagesStream(source, requestSignal)),
|
|
2606
|
+
request.model,
|
|
2607
|
+
signal,
|
|
2608
|
+
(stream) => stream.finalResult
|
|
2609
|
+
);
|
|
2610
|
+
}
|
|
2611
|
+
return this.request(
|
|
2612
|
+
(http, requestSignal) => http.request({
|
|
2613
|
+
path: "/v1/messages",
|
|
2614
|
+
body: sanitizeAnthropicRequest(request),
|
|
2615
|
+
headers: { "anthropic-version": "2023-06-01" },
|
|
2616
|
+
signal: requestSignal
|
|
2617
|
+
}),
|
|
2618
|
+
request.model,
|
|
2619
|
+
signal
|
|
2620
|
+
);
|
|
2621
|
+
}
|
|
2622
|
+
async messages(request, signal) {
|
|
2623
|
+
if (request.stream) {
|
|
2624
|
+
return this.createMessage(request, signal);
|
|
2625
|
+
}
|
|
2626
|
+
return this.createMessage(request, signal);
|
|
2627
|
+
}
|
|
2628
|
+
};
|
|
2629
|
+
|
|
2630
|
+
// src/generated/runtime/runtime.ts
|
|
2631
|
+
function compareVersions(a, b) {
|
|
2632
|
+
const pa = a.split(".").map((s) => Number.parseInt(s, 10) ?? 0);
|
|
2633
|
+
const pb = b.split(".").map((s) => Number.parseInt(s, 10) ?? 0);
|
|
2634
|
+
for (let i = 0; i < Math.max(pa.length, pb.length); i += 1) {
|
|
2635
|
+
const va = pa[i] ?? 0;
|
|
2636
|
+
const vb = pb[i] ?? 0;
|
|
2637
|
+
if (va !== vb) return va - vb;
|
|
2638
|
+
}
|
|
2639
|
+
return 0;
|
|
2640
|
+
}
|
|
2641
|
+
function assertOperationAllowed(operation, options) {
|
|
2642
|
+
const localMode = options.localMode ?? true;
|
|
2643
|
+
if (!localMode && !operation.environment.cloud) {
|
|
2644
|
+
throw new OllamaGenericClientError(
|
|
2645
|
+
`Operation ${operation.operationId} (${operation.method} ${operation.path}) is local-only and not supported in cloud mode.`
|
|
2646
|
+
);
|
|
2647
|
+
}
|
|
2648
|
+
if (operation.constraints?.minOllamaVersion && options.serverVersion && compareVersions(options.serverVersion, operation.constraints.minOllamaVersion) < 0) {
|
|
2649
|
+
throw new OllamaGenericClientError(
|
|
2650
|
+
`Operation ${operation.operationId} (${operation.method} ${operation.path}) requires Ollama >= ${operation.constraints.minOllamaVersion} (server reports ${options.serverVersion}).`
|
|
2651
|
+
);
|
|
2652
|
+
}
|
|
2653
|
+
}
|
|
2654
|
+
function shouldStream(req) {
|
|
2655
|
+
if (req.body && typeof req.body === "object" && "stream" in req.body) {
|
|
2656
|
+
const explicit = req.body.stream;
|
|
2657
|
+
if (typeof explicit === "boolean") return explicit;
|
|
2658
|
+
}
|
|
2659
|
+
return Boolean(req.streamingDefault ?? req.operation.transport.streamingDefault);
|
|
2660
|
+
}
|
|
2661
|
+
function buildBody(req) {
|
|
2662
|
+
if (req.body === void 0) return void 0;
|
|
2663
|
+
if (typeof req.body !== "object" || req.body === null) return req.body;
|
|
2664
|
+
const body = req.body;
|
|
2665
|
+
if (req.operation.transport.streaming && req.operation.transport.streamingDefault && body.stream === void 0) {
|
|
2666
|
+
return { ...body, stream: true };
|
|
2667
|
+
}
|
|
2668
|
+
return body;
|
|
2669
|
+
}
|
|
2670
|
+
var OllamaRuntime = class {
|
|
2671
|
+
constructor(options) {
|
|
2672
|
+
this.options = options;
|
|
2673
|
+
}
|
|
2674
|
+
options;
|
|
2675
|
+
/**
|
|
2676
|
+
* Invoke an operation. Called by every generated API method.
|
|
2677
|
+
*
|
|
2678
|
+
* Returns a `Promise<T>` for non-streaming operations, or an
|
|
2679
|
+
* `AsyncGenerator<T>` for streaming operations. The generated API
|
|
2680
|
+
* class's overloads pick the right return type at compile time.
|
|
2681
|
+
*/
|
|
2682
|
+
async invoke(req) {
|
|
2683
|
+
assertOperationAllowed(req.operation, this.options);
|
|
2684
|
+
const body = buildBody(req);
|
|
2685
|
+
const streaming = shouldStream(req);
|
|
2686
|
+
const httpReq = {
|
|
2687
|
+
path: req.operation.path,
|
|
2688
|
+
method: req.operation.method,
|
|
2689
|
+
...body !== void 0 ? { body } : {},
|
|
2690
|
+
...req.signal !== void 0 ? { signal: req.signal } : {},
|
|
2691
|
+
// For streaming calls, request the raw Response so we can pipe it
|
|
2692
|
+
// through parseNdjsonStream directly. This inherits middleware,
|
|
2693
|
+
// retry, telemetry, and error-mapping from HttpClient — the
|
|
2694
|
+
// previous Wave 3 implementation bypassed them with a direct
|
|
2695
|
+
// `fetch()` call.
|
|
2696
|
+
...streaming ? { raw: true } : {}
|
|
2697
|
+
};
|
|
2698
|
+
if (!streaming) {
|
|
2699
|
+
return await this.options.http.request(httpReq);
|
|
2700
|
+
}
|
|
2701
|
+
const response = await this.options.http.request(httpReq);
|
|
2702
|
+
if (!response.body) {
|
|
2703
|
+
throw new OllamaGenericClientError(
|
|
2704
|
+
`Operation ${req.operation.operationId}: streaming response had no body.`
|
|
2705
|
+
);
|
|
2706
|
+
}
|
|
2707
|
+
return parseNdjsonStream(response.body);
|
|
2708
|
+
}
|
|
2709
|
+
};
|
|
2710
|
+
|
|
2711
|
+
// src/utils.ts
|
|
2712
|
+
async function encodeImage(image) {
|
|
2713
|
+
if (typeof image === "string") return image;
|
|
2714
|
+
const bufferCtor = globalThis.Buffer;
|
|
2715
|
+
if (bufferCtor !== void 0) return bufferCtor.from(image).toString("base64");
|
|
2716
|
+
const CHUNK_SIZE = 32768;
|
|
2717
|
+
let binary = "";
|
|
2718
|
+
for (let i = 0; i < image.length; i += CHUNK_SIZE) {
|
|
2719
|
+
binary += String.fromCharCode(...image.subarray(i, i + CHUNK_SIZE));
|
|
2720
|
+
}
|
|
2721
|
+
return btoa(binary);
|
|
2722
|
+
}
|
|
2723
|
+
async function encodeImages(images) {
|
|
2724
|
+
if (images === void 0) return void 0;
|
|
2725
|
+
return Promise.all(images.map((image) => encodeImage(image)));
|
|
2726
|
+
}
|
|
2727
|
+
async function withEncodedImages(req) {
|
|
2728
|
+
if (!req.images?.some((image) => image instanceof Uint8Array)) return req;
|
|
2729
|
+
return { ...req, images: await encodeImages(req.images) };
|
|
1394
2730
|
}
|
|
1395
2731
|
async function withEncodedMessageImages(messages) {
|
|
1396
2732
|
if (!messages.some((message) => message.images?.some((image) => image instanceof Uint8Array)))
|
|
@@ -1399,6 +2735,11 @@ async function withEncodedMessageImages(messages) {
|
|
|
1399
2735
|
}
|
|
1400
2736
|
|
|
1401
2737
|
// src/client.ts
|
|
2738
|
+
var logicalRequestSequence = 0;
|
|
2739
|
+
function createLogicalRequestId() {
|
|
2740
|
+
logicalRequestSequence += 1;
|
|
2741
|
+
return `ollama-request-${logicalRequestSequence}`;
|
|
2742
|
+
}
|
|
1402
2743
|
var OllamaClient = class {
|
|
1403
2744
|
registry;
|
|
1404
2745
|
models;
|
|
@@ -1407,6 +2748,8 @@ var OllamaClient = class {
|
|
|
1407
2748
|
failoverCodes;
|
|
1408
2749
|
fetchImpl;
|
|
1409
2750
|
logger;
|
|
2751
|
+
middleware;
|
|
2752
|
+
onLifecycleEvent;
|
|
1410
2753
|
/**
|
|
1411
2754
|
* API key for Ollama's hosted web tools (`webSearch`/`webFetch`), which always target
|
|
1412
2755
|
* `OLLAMA_CLOUD_BASE_URL` regardless of `config.endpoints` — an Ollama account API key
|
|
@@ -1427,17 +2770,66 @@ var OllamaClient = class {
|
|
|
1427
2770
|
...config.headers !== void 0 ? { headers: config.headers } : {}
|
|
1428
2771
|
}
|
|
1429
2772
|
]);
|
|
1430
|
-
this.registry = new EndpointRegistry(
|
|
2773
|
+
this.registry = new EndpointRegistry(
|
|
2774
|
+
[...endpoints, ...credentialEndpoints],
|
|
2775
|
+
config.endpointHealth
|
|
2776
|
+
);
|
|
1431
2777
|
this.timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
1432
2778
|
this.failoverCodes = new Set(config.failoverOn ?? DEFAULT_FAILOVER_CODES);
|
|
1433
2779
|
this.fetchImpl = config.fetch ?? globalThis.fetch;
|
|
1434
2780
|
this.logger = config.logger ?? (config.debug ? createConsoleLogger() : NOOP_LOGGER);
|
|
2781
|
+
this.middleware = config.middleware;
|
|
2782
|
+
this.onLifecycleEvent = config.onLifecycleEvent;
|
|
1435
2783
|
this.retryConfig = typeof config.retries === "number" ? { ...DEFAULT_RETRY_CONFIG, maxRetries: config.retries } : { ...DEFAULT_RETRY_CONFIG, ...config.retries };
|
|
1436
2784
|
this.models = new ModelsClient((op, opts) => this.executeWithFailover(op, opts));
|
|
1437
2785
|
}
|
|
1438
2786
|
get modelsClient() {
|
|
1439
2787
|
return this.models;
|
|
1440
2788
|
}
|
|
2789
|
+
/**
|
|
2790
|
+
* Return a generated {@link OllamaRuntime} that shares this client's
|
|
2791
|
+
* transport (HttpClient + middleware + retry + telemetry).
|
|
2792
|
+
*
|
|
2793
|
+
* Wave 8 (ADR 0018) bridge: lets callers mix the existing `OllamaClient`
|
|
2794
|
+
* API with the generated `NativeApi` / `OpenAIApi` / `AnthropicApi`
|
|
2795
|
+
* surface without configuring two separate HttpClient instances.
|
|
2796
|
+
*
|
|
2797
|
+
* The returned runtime is bound to the registry's first candidate
|
|
2798
|
+
* endpoint (or the resolved single-endpoint config if no `endpoints`
|
|
2799
|
+
* array was provided). For multi-endpoint configs that need per-call
|
|
2800
|
+
* routing, prefer constructing `OllamaRuntime` directly with a
|
|
2801
|
+
* specific HttpClient:
|
|
2802
|
+
*
|
|
2803
|
+
* ```ts
|
|
2804
|
+
* const http = new HttpClient({ baseUrl: 'http://host-a:11434' });
|
|
2805
|
+
* const runtime = new OllamaRuntime({ http });
|
|
2806
|
+
* const api = new NativeApi(runtime);
|
|
2807
|
+
* ```
|
|
2808
|
+
*
|
|
2809
|
+
* The runtime is cached on first call — subsequent calls return the
|
|
2810
|
+
* same instance.
|
|
2811
|
+
*/
|
|
2812
|
+
get runtime() {
|
|
2813
|
+
if (this._runtime === void 0) {
|
|
2814
|
+
const candidates = this.registry.candidates();
|
|
2815
|
+
const endpoint = candidates[0];
|
|
2816
|
+
const baseUrl = endpoint?.baseUrl ?? "http://localhost:11434";
|
|
2817
|
+
const http = new HttpClient({
|
|
2818
|
+
baseUrl,
|
|
2819
|
+
...endpoint?.apiKey !== void 0 ? { apiKey: endpoint.apiKey } : {},
|
|
2820
|
+
...endpoint?.headers !== void 0 ? { headers: endpoint.headers } : {},
|
|
2821
|
+
fetch: this.fetchImpl,
|
|
2822
|
+
...this.middleware !== void 0 ? { middleware: this.middleware } : {},
|
|
2823
|
+
...this.onLifecycleEvent !== void 0 ? { onLifecycleEvent: this.onLifecycleEvent } : {}
|
|
2824
|
+
});
|
|
2825
|
+
this._runtime = new OllamaRuntime({
|
|
2826
|
+
http,
|
|
2827
|
+
localMode: inferRuntimeMode(baseUrl) === "local"
|
|
2828
|
+
});
|
|
2829
|
+
}
|
|
2830
|
+
return this._runtime;
|
|
2831
|
+
}
|
|
2832
|
+
_runtime;
|
|
1441
2833
|
/**
|
|
1442
2834
|
* Fail-fast guard for `format` (structured output) requests: throws before any network
|
|
1443
2835
|
* call if the candidate endpoint is inferred as Ollama Cloud, which does not currently
|
|
@@ -1455,6 +2847,8 @@ var OllamaClient = class {
|
|
|
1455
2847
|
}
|
|
1456
2848
|
async executeWithFailover(operation, options) {
|
|
1457
2849
|
const timeout = createTimeoutSignal(options?.timeoutMs ?? this.timeoutMs, options?.signal);
|
|
2850
|
+
const requestId = createLogicalRequestId();
|
|
2851
|
+
let deferTimeoutCancel = false;
|
|
1458
2852
|
try {
|
|
1459
2853
|
let lastError;
|
|
1460
2854
|
for (; ; ) {
|
|
@@ -1481,7 +2875,10 @@ var OllamaClient = class {
|
|
|
1481
2875
|
baseUrl: endpoint.baseUrl,
|
|
1482
2876
|
...endpoint.apiKey !== void 0 ? { apiKey: endpoint.apiKey } : {},
|
|
1483
2877
|
...endpoint.headers !== void 0 ? { headers: endpoint.headers } : {},
|
|
1484
|
-
fetch: this.fetchImpl
|
|
2878
|
+
fetch: this.fetchImpl,
|
|
2879
|
+
middleware: this.middleware,
|
|
2880
|
+
onLifecycleEvent: this.onLifecycleEvent,
|
|
2881
|
+
requestId
|
|
1485
2882
|
});
|
|
1486
2883
|
this.registry.acquire(endpoint.name);
|
|
1487
2884
|
let holdPromise;
|
|
@@ -1492,10 +2889,30 @@ var OllamaClient = class {
|
|
|
1492
2889
|
[ATTR_OLLAMA_ENDPOINT_NAME]: endpoint.name,
|
|
1493
2890
|
[ATTR_OLLAMA_ENDPOINT_ATTEMPT]: attemptIndex
|
|
1494
2891
|
},
|
|
1495
|
-
() => withRetry(
|
|
2892
|
+
() => withRetry(
|
|
2893
|
+
() => operation(http, timeout.signal),
|
|
2894
|
+
{
|
|
2895
|
+
...this.retryConfig,
|
|
2896
|
+
onRetry: (error, attempt, delayMs) => {
|
|
2897
|
+
this.retryConfig.onRetry?.(error, attempt, delayMs);
|
|
2898
|
+
this.onLifecycleEvent?.({
|
|
2899
|
+
type: "retry",
|
|
2900
|
+
requestId,
|
|
2901
|
+
attempt: attempt + 1,
|
|
2902
|
+
error,
|
|
2903
|
+
delayMs,
|
|
2904
|
+
timestamp: Date.now()
|
|
2905
|
+
});
|
|
2906
|
+
}
|
|
2907
|
+
},
|
|
2908
|
+
timeout.signal
|
|
2909
|
+
)
|
|
1496
2910
|
);
|
|
1497
2911
|
this.registry.reportSuccess(endpoint.name);
|
|
1498
|
-
if (options?.holdUntil)
|
|
2912
|
+
if (options?.holdUntil) {
|
|
2913
|
+
holdPromise = options.holdUntil(result);
|
|
2914
|
+
deferTimeoutCancel = true;
|
|
2915
|
+
}
|
|
1499
2916
|
return result;
|
|
1500
2917
|
} catch (err) {
|
|
1501
2918
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
@@ -1506,7 +2923,10 @@ var OllamaClient = class {
|
|
|
1506
2923
|
throw error;
|
|
1507
2924
|
} finally {
|
|
1508
2925
|
if (holdPromise) {
|
|
1509
|
-
void holdPromise.catch(() => void 0).finally(() =>
|
|
2926
|
+
void holdPromise.catch(() => void 0).finally(() => {
|
|
2927
|
+
timeout.cancel();
|
|
2928
|
+
this.registry.release(endpoint.name);
|
|
2929
|
+
});
|
|
1510
2930
|
} else {
|
|
1511
2931
|
this.registry.release(endpoint.name);
|
|
1512
2932
|
}
|
|
@@ -1515,11 +2935,18 @@ var OllamaClient = class {
|
|
|
1515
2935
|
throw lastError ?? new Error("No healthy Ollama endpoints available");
|
|
1516
2936
|
}
|
|
1517
2937
|
} finally {
|
|
1518
|
-
timeout.cancel();
|
|
2938
|
+
if (!deferTimeoutCancel) timeout.cancel();
|
|
1519
2939
|
}
|
|
1520
2940
|
}
|
|
1521
2941
|
async chat(req) {
|
|
1522
|
-
const
|
|
2942
|
+
const encodedMessages = await withEncodedMessageImages(req.messages);
|
|
2943
|
+
const messages = encodedMessages.map((message) => {
|
|
2944
|
+
if (message.role === "tool" && message.tool_call_id !== void 0) {
|
|
2945
|
+
const { tool_call_id: _toolCallId, ...nativeMessage } = message;
|
|
2946
|
+
return nativeMessage;
|
|
2947
|
+
}
|
|
2948
|
+
return message;
|
|
2949
|
+
});
|
|
1523
2950
|
if (req.stream) {
|
|
1524
2951
|
return this.executeWithFailover(
|
|
1525
2952
|
async (http, signal) => {
|
|
@@ -1530,7 +2957,7 @@ var OllamaClient = class {
|
|
|
1530
2957
|
body: { ...req, messages, stream: true },
|
|
1531
2958
|
signal
|
|
1532
2959
|
});
|
|
1533
|
-
return normalizeChatStream(stream);
|
|
2960
|
+
return normalizeChatStream(stream, signal);
|
|
1534
2961
|
},
|
|
1535
2962
|
{
|
|
1536
2963
|
signal: req.signal,
|
|
@@ -1594,7 +3021,7 @@ var OllamaClient = class {
|
|
|
1594
3021
|
body: { ...encodedReq, stream: true },
|
|
1595
3022
|
signal
|
|
1596
3023
|
});
|
|
1597
|
-
return normalizeGenerateStream(stream);
|
|
3024
|
+
return normalizeGenerateStream(stream, signal);
|
|
1598
3025
|
},
|
|
1599
3026
|
{
|
|
1600
3027
|
signal: encodedReq.signal,
|
|
@@ -1714,30 +3141,59 @@ var OllamaClient = class {
|
|
|
1714
3141
|
*/
|
|
1715
3142
|
async executeCloudRequest(operation, options) {
|
|
1716
3143
|
const timeout = createTimeoutSignal(options.timeoutMs ?? this.timeoutMs, options.signal);
|
|
3144
|
+
const requestId = createLogicalRequestId();
|
|
1717
3145
|
try {
|
|
1718
3146
|
const http = new HttpClient({
|
|
1719
3147
|
baseUrl: OLLAMA_CLOUD_BASE_URL,
|
|
1720
3148
|
apiKey: this.cloudApiKey,
|
|
1721
|
-
fetch: this.fetchImpl
|
|
3149
|
+
fetch: this.fetchImpl,
|
|
3150
|
+
middleware: this.middleware,
|
|
3151
|
+
onLifecycleEvent: this.onLifecycleEvent,
|
|
3152
|
+
requestId
|
|
1722
3153
|
});
|
|
1723
|
-
return await withRetry(
|
|
3154
|
+
return await withRetry(
|
|
3155
|
+
() => operation(http, timeout.signal),
|
|
3156
|
+
{
|
|
3157
|
+
...this.retryConfig,
|
|
3158
|
+
onRetry: (error, attempt, delayMs) => {
|
|
3159
|
+
this.retryConfig.onRetry?.(error, attempt, delayMs);
|
|
3160
|
+
this.onLifecycleEvent?.({
|
|
3161
|
+
type: "retry",
|
|
3162
|
+
requestId,
|
|
3163
|
+
attempt: attempt + 1,
|
|
3164
|
+
error,
|
|
3165
|
+
delayMs,
|
|
3166
|
+
timestamp: Date.now()
|
|
3167
|
+
});
|
|
3168
|
+
}
|
|
3169
|
+
},
|
|
3170
|
+
timeout.signal
|
|
3171
|
+
);
|
|
1724
3172
|
} finally {
|
|
1725
3173
|
timeout.cancel();
|
|
1726
3174
|
}
|
|
1727
3175
|
}
|
|
1728
3176
|
// --- Capabilities & Health ---
|
|
1729
|
-
capabilities(model) {
|
|
1730
|
-
return this.executeWithFailover(
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
3177
|
+
capabilities(model, signal) {
|
|
3178
|
+
return this.executeWithFailover(
|
|
3179
|
+
(http, runnerSignal) => detectModelCapabilities(http, model, runnerSignal),
|
|
3180
|
+
{
|
|
3181
|
+
singleEndpoint: true,
|
|
3182
|
+
model,
|
|
3183
|
+
...signal !== void 0 ? { signal } : {}
|
|
3184
|
+
}
|
|
3185
|
+
);
|
|
1734
3186
|
}
|
|
1735
3187
|
runtimeMode() {
|
|
1736
3188
|
const ep = this.registry.candidates()[0];
|
|
1737
3189
|
return inferRuntimeMode(ep?.baseUrl ?? DEFAULT_BASE_URL);
|
|
1738
3190
|
}
|
|
1739
3191
|
healthCheck() {
|
|
1740
|
-
return Promise.all(
|
|
3192
|
+
return Promise.all(
|
|
3193
|
+
this.registry.list().map(
|
|
3194
|
+
(ep) => checkEndpointHealth(ep, this.fetchImpl, 5e3, this.middleware, this.onLifecycleEvent)
|
|
3195
|
+
)
|
|
3196
|
+
);
|
|
1741
3197
|
}
|
|
1742
3198
|
endpointStatus() {
|
|
1743
3199
|
return this.registry.status();
|
|
@@ -1750,8 +3206,11 @@ var OllamaClient = class {
|
|
|
1750
3206
|
baseUrl: ep?.baseUrl ?? DEFAULT_BASE_URL,
|
|
1751
3207
|
apiKey: ep?.apiKey,
|
|
1752
3208
|
headers: ep?.headers,
|
|
1753
|
-
fetch: this.fetchImpl
|
|
1754
|
-
|
|
3209
|
+
fetch: this.fetchImpl,
|
|
3210
|
+
middleware: this.middleware,
|
|
3211
|
+
onLifecycleEvent: this.onLifecycleEvent
|
|
3212
|
+
}),
|
|
3213
|
+
(op, opts) => this.executeWithFailover(op, opts)
|
|
1755
3214
|
);
|
|
1756
3215
|
}
|
|
1757
3216
|
get anthropic() {
|
|
@@ -1761,34 +3220,15 @@ var OllamaClient = class {
|
|
|
1761
3220
|
baseUrl: ep?.baseUrl ?? DEFAULT_BASE_URL,
|
|
1762
3221
|
apiKey: ep?.apiKey,
|
|
1763
3222
|
headers: ep?.headers,
|
|
1764
|
-
fetch: this.fetchImpl
|
|
1765
|
-
|
|
3223
|
+
fetch: this.fetchImpl,
|
|
3224
|
+
middleware: this.middleware,
|
|
3225
|
+
onLifecycleEvent: this.onLifecycleEvent
|
|
3226
|
+
}),
|
|
3227
|
+
(op, opts) => this.executeWithFailover(op, opts)
|
|
1766
3228
|
);
|
|
1767
3229
|
}
|
|
1768
3230
|
};
|
|
1769
3231
|
|
|
1770
|
-
// src/middleware.ts
|
|
1771
|
-
function composeMiddleware(middlewares, finalHandler) {
|
|
1772
|
-
return function runPipeline(initialRequest) {
|
|
1773
|
-
let index = -1;
|
|
1774
|
-
function dispatch(i, currentRequest) {
|
|
1775
|
-
if (i <= index) {
|
|
1776
|
-
return Promise.reject(new Error("next() called multiple times in middleware"));
|
|
1777
|
-
}
|
|
1778
|
-
index = i;
|
|
1779
|
-
const fn = middlewares[i];
|
|
1780
|
-
if (i === middlewares.length || !fn) {
|
|
1781
|
-
return finalHandler(currentRequest);
|
|
1782
|
-
}
|
|
1783
|
-
return fn({
|
|
1784
|
-
request: currentRequest,
|
|
1785
|
-
next: () => dispatch(i + 1, currentRequest)
|
|
1786
|
-
});
|
|
1787
|
-
}
|
|
1788
|
-
return dispatch(0, initialRequest);
|
|
1789
|
-
};
|
|
1790
|
-
}
|
|
1791
|
-
|
|
1792
3232
|
// src/streaming/adapters.ts
|
|
1793
3233
|
function toTextStream(stream) {
|
|
1794
3234
|
const iterator = stream[Symbol.asyncIterator]();
|
|
@@ -2152,28 +3592,234 @@ var ToolRegistry = class {
|
|
|
2152
3592
|
};
|
|
2153
3593
|
}
|
|
2154
3594
|
};
|
|
2155
|
-
|
|
3595
|
+
|
|
3596
|
+
// src/mcp/json-schema.ts
|
|
3597
|
+
function label(path) {
|
|
3598
|
+
return path.length === 0 ? "value" : "value" + path.map((part) => typeof part === "number" ? "[" + part + "]" : "." + part).join("");
|
|
3599
|
+
}
|
|
3600
|
+
function equalJson(a, b) {
|
|
3601
|
+
if (Object.is(a, b)) return true;
|
|
3602
|
+
if (typeof a !== typeof b || a === null || b === null) return false;
|
|
3603
|
+
if (Array.isArray(a) || Array.isArray(b)) {
|
|
3604
|
+
return Array.isArray(a) && Array.isArray(b) && a.length === b.length && a.every((item, i) => equalJson(item, b[i]));
|
|
3605
|
+
}
|
|
3606
|
+
if (typeof a === "object" && typeof b === "object") {
|
|
3607
|
+
const ar = a;
|
|
3608
|
+
const br = b;
|
|
3609
|
+
const ak = Object.keys(ar).sort();
|
|
3610
|
+
const bk = Object.keys(br).sort();
|
|
3611
|
+
return ak.length === bk.length && ak.every((key, i) => key === bk[i] && equalJson(ar[key], br[key]));
|
|
3612
|
+
}
|
|
3613
|
+
return false;
|
|
3614
|
+
}
|
|
3615
|
+
function matchesType(value, type) {
|
|
3616
|
+
switch (type) {
|
|
3617
|
+
case "null":
|
|
3618
|
+
return value === null;
|
|
3619
|
+
case "boolean":
|
|
3620
|
+
return typeof value === "boolean";
|
|
3621
|
+
case "object":
|
|
3622
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3623
|
+
case "array":
|
|
3624
|
+
return Array.isArray(value);
|
|
3625
|
+
case "string":
|
|
3626
|
+
return typeof value === "string";
|
|
3627
|
+
case "number":
|
|
3628
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
3629
|
+
case "integer":
|
|
3630
|
+
return typeof value === "number" && Number.isInteger(value);
|
|
3631
|
+
default:
|
|
3632
|
+
return true;
|
|
3633
|
+
}
|
|
3634
|
+
}
|
|
3635
|
+
function validate(value, schema, path) {
|
|
3636
|
+
if (schema === true || schema === void 0) return [];
|
|
3637
|
+
if (schema === false) return [{ path, message: label(path) + " is not allowed by the schema" }];
|
|
3638
|
+
if (typeof schema !== "object" || schema === null || Array.isArray(schema)) return [];
|
|
3639
|
+
const s = schema;
|
|
3640
|
+
const issues = [];
|
|
3641
|
+
if (s["nullable"] === true && value === null) return issues;
|
|
3642
|
+
const type = s["type"];
|
|
3643
|
+
if (typeof type === "string" && !matchesType(value, type)) {
|
|
3644
|
+
return [{ path, message: label(path) + " must be " + type }];
|
|
3645
|
+
}
|
|
3646
|
+
if (Array.isArray(type) && !type.some((t) => typeof t === "string" && matchesType(value, t))) {
|
|
3647
|
+
return [{ path, message: label(path) + " must match one of: " + type.join(", ") }];
|
|
3648
|
+
}
|
|
3649
|
+
if ("const" in s && !equalJson(value, s["const"])) issues.push({ path, message: label(path) + " must equal the schema const value" });
|
|
3650
|
+
const enumValues = s["enum"];
|
|
3651
|
+
if (Array.isArray(enumValues) && !enumValues.some((item) => equalJson(value, item))) issues.push({ path, message: label(path) + " must be one of the allowed enum values" });
|
|
3652
|
+
const anyOf = s["anyOf"];
|
|
3653
|
+
if (Array.isArray(anyOf) && !anyOf.some((candidate) => validate(value, candidate, path).length === 0)) issues.push({ path, message: label(path) + " must satisfy at least one anyOf schema" });
|
|
3654
|
+
const oneOf = s["oneOf"];
|
|
3655
|
+
if (Array.isArray(oneOf)) {
|
|
3656
|
+
const matches = oneOf.filter((candidate) => validate(value, candidate, path).length === 0).length;
|
|
3657
|
+
if (matches !== 1) issues.push({ path, message: label(path) + " must satisfy exactly one oneOf schema" });
|
|
3658
|
+
}
|
|
3659
|
+
const allOf = s["allOf"];
|
|
3660
|
+
if (Array.isArray(allOf)) for (const candidate of allOf) issues.push(...validate(value, candidate, path));
|
|
3661
|
+
if ("not" in s && validate(value, s["not"], path).length === 0) issues.push({ path, message: label(path) + " must not satisfy the not schema" });
|
|
3662
|
+
if (typeof value === "string") {
|
|
3663
|
+
const min = s["minLength"];
|
|
3664
|
+
const max = s["maxLength"];
|
|
3665
|
+
if (typeof min === "number" && value.length < min) issues.push({ path, message: label(path) + " must have at least " + min + " characters" });
|
|
3666
|
+
if (typeof max === "number" && value.length > max) issues.push({ path, message: label(path) + " must have at most " + max + " characters" });
|
|
3667
|
+
if (typeof s["pattern"] === "string") {
|
|
3668
|
+
try {
|
|
3669
|
+
if (!new RegExp(s["pattern"]).test(value)) issues.push({ path, message: label(path) + " does not match the required pattern" });
|
|
3670
|
+
} catch {
|
|
3671
|
+
}
|
|
3672
|
+
}
|
|
3673
|
+
}
|
|
3674
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
3675
|
+
const numericRules = [
|
|
3676
|
+
["minimum", (n, b) => n >= b, "must be greater than or equal to"],
|
|
3677
|
+
["maximum", (n, b) => n <= b, "must be less than or equal to"],
|
|
3678
|
+
["exclusiveMinimum", (n, b) => n > b, "must be greater than"],
|
|
3679
|
+
["exclusiveMaximum", (n, b) => n < b, "must be less than"]
|
|
3680
|
+
];
|
|
3681
|
+
for (const [keyword, check, phrase] of numericRules) {
|
|
3682
|
+
const bound = s[keyword];
|
|
3683
|
+
if (typeof bound === "number" && !check(value, bound)) issues.push({ path, message: label(path) + " " + phrase + " " + bound });
|
|
3684
|
+
}
|
|
3685
|
+
const multipleOf = s["multipleOf"];
|
|
3686
|
+
if (typeof multipleOf === "number" && multipleOf > 0) {
|
|
3687
|
+
const quotient = value / multipleOf;
|
|
3688
|
+
if (Math.abs(quotient - Math.round(quotient)) > 1e-12) issues.push({ path, message: label(path) + " must be a multiple of " + multipleOf });
|
|
3689
|
+
}
|
|
3690
|
+
}
|
|
3691
|
+
if (Array.isArray(value)) {
|
|
3692
|
+
const min = s["minItems"];
|
|
3693
|
+
const max = s["maxItems"];
|
|
3694
|
+
if (typeof min === "number" && value.length < min) issues.push({ path, message: label(path) + " must contain at least " + min + " items" });
|
|
3695
|
+
if (typeof max === "number" && value.length > max) issues.push({ path, message: label(path) + " must contain at most " + max + " items" });
|
|
3696
|
+
if (s["uniqueItems"] === true) {
|
|
3697
|
+
for (let i = 0; i < value.length; i += 1) if (value.slice(0, i).some((previous) => equalJson(previous, value[i]))) {
|
|
3698
|
+
issues.push({ path: [...path, i], message: label([...path, i]) + " must be unique" });
|
|
3699
|
+
break;
|
|
3700
|
+
}
|
|
3701
|
+
}
|
|
3702
|
+
if ("items" in s) value.forEach((item, i) => issues.push(...validate(item, s["items"], [...path, i])));
|
|
3703
|
+
if (Array.isArray(s["prefixItems"])) s["prefixItems"].forEach((itemSchema, i) => {
|
|
3704
|
+
if (i < value.length) issues.push(...validate(value[i], itemSchema, [...path, i]));
|
|
3705
|
+
});
|
|
3706
|
+
}
|
|
3707
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
3708
|
+
const object = value;
|
|
3709
|
+
const required = s["required"];
|
|
3710
|
+
if (Array.isArray(required)) {
|
|
3711
|
+
for (const key of required) if (typeof key === "string" && !(key in object)) issues.push({ path: [...path, key], message: label([...path, key]) + " is required" });
|
|
3712
|
+
}
|
|
3713
|
+
const properties = s["properties"];
|
|
3714
|
+
if (typeof properties === "object" && properties !== null && !Array.isArray(properties)) {
|
|
3715
|
+
const propertySchemas = properties;
|
|
3716
|
+
for (const [key, propertySchema] of Object.entries(propertySchemas)) if (key in object) issues.push(...validate(object[key], propertySchema, [...path, key]));
|
|
3717
|
+
const additional = s["additionalProperties"];
|
|
3718
|
+
for (const key of Object.keys(object)) {
|
|
3719
|
+
if (key in propertySchemas) continue;
|
|
3720
|
+
if (additional === false) issues.push({ path: [...path, key], message: label([...path, key]) + " is not allowed" });
|
|
3721
|
+
else if (additional !== true && additional !== void 0) issues.push(...validate(object[key], additional, [...path, key]));
|
|
3722
|
+
}
|
|
3723
|
+
}
|
|
3724
|
+
const min = s["minProperties"];
|
|
3725
|
+
const max = s["maxProperties"];
|
|
3726
|
+
if (typeof min === "number" && Object.keys(object).length < min) issues.push({ path, message: label(path) + " must contain at least " + min + " properties" });
|
|
3727
|
+
if (typeof max === "number" && Object.keys(object).length > max) issues.push({ path, message: label(path) + " must contain at most " + max + " properties" });
|
|
3728
|
+
}
|
|
3729
|
+
return issues;
|
|
3730
|
+
}
|
|
3731
|
+
function validateJsonSchema(value, schema) {
|
|
3732
|
+
return validate(value, schema, []);
|
|
3733
|
+
}
|
|
3734
|
+
|
|
3735
|
+
// src/mcp/mcp-tools.ts
|
|
3736
|
+
function resolveMaxPages(options) {
|
|
3737
|
+
const maxPages = options.maxPages ?? 64;
|
|
3738
|
+
if (!Number.isInteger(maxPages) || maxPages <= 0) {
|
|
3739
|
+
throw new RangeError("MCP maxPages must be a positive integer");
|
|
3740
|
+
}
|
|
3741
|
+
return maxPages;
|
|
3742
|
+
}
|
|
3743
|
+
async function listAllMcpTools(mcpClient, options = {}, signal) {
|
|
3744
|
+
const maxPages = resolveMaxPages(options);
|
|
3745
|
+
const tools = [];
|
|
3746
|
+
const seenCursors = /* @__PURE__ */ new Set();
|
|
3747
|
+
let cursor;
|
|
3748
|
+
for (let pageNumber = 1; pageNumber <= maxPages; pageNumber += 1) {
|
|
3749
|
+
const params = cursor !== void 0 ? { cursor } : void 0;
|
|
3750
|
+
const requestOptions = signal !== void 0 ? { signal } : void 0;
|
|
3751
|
+
const page = await mcpClient.listTools(params, requestOptions);
|
|
3752
|
+
tools.push(...page.tools);
|
|
3753
|
+
if (page.nextCursor === void 0) {
|
|
3754
|
+
return tools;
|
|
3755
|
+
}
|
|
3756
|
+
if (seenCursors.has(page.nextCursor)) {
|
|
3757
|
+
throw new Error("MCP tools/list returned a repeated nextCursor");
|
|
3758
|
+
}
|
|
3759
|
+
seenCursors.add(page.nextCursor);
|
|
3760
|
+
cursor = page.nextCursor;
|
|
3761
|
+
}
|
|
3762
|
+
throw new Error("MCP tools/list exceeded maxPages (" + maxPages + ")");
|
|
3763
|
+
}
|
|
3764
|
+
function formatMcpToolResult(result) {
|
|
3765
|
+
const parts = result.content.map((block) => {
|
|
3766
|
+
if (block.type === "text" && typeof block.text === "string") {
|
|
3767
|
+
return block.text;
|
|
3768
|
+
}
|
|
3769
|
+
try {
|
|
3770
|
+
return JSON.stringify(block);
|
|
3771
|
+
} catch {
|
|
3772
|
+
return `[${block.type}]`;
|
|
3773
|
+
}
|
|
3774
|
+
});
|
|
3775
|
+
if (result.structuredContent !== void 0) {
|
|
3776
|
+
try {
|
|
3777
|
+
parts.push(JSON.stringify(result.structuredContent));
|
|
3778
|
+
} catch {
|
|
3779
|
+
}
|
|
3780
|
+
}
|
|
3781
|
+
if (result.isError) {
|
|
3782
|
+
parts.unshift("[MCP tool error]");
|
|
3783
|
+
}
|
|
3784
|
+
return parts.join("\n");
|
|
3785
|
+
}
|
|
3786
|
+
function createInputValidator(inputSchema) {
|
|
3787
|
+
return zod.z.custom().superRefine((value, ctx) => {
|
|
3788
|
+
const issues = validateJsonSchema(value, inputSchema);
|
|
3789
|
+
for (const issue of issues) {
|
|
3790
|
+
ctx.addIssue({
|
|
3791
|
+
code: zod.z.ZodIssueCode.custom,
|
|
3792
|
+
path: [...issue.path],
|
|
3793
|
+
message: issue.message
|
|
3794
|
+
});
|
|
3795
|
+
}
|
|
3796
|
+
});
|
|
3797
|
+
}
|
|
3798
|
+
function convertMcpDescriptorToTool(descriptor, mcpClient, namePrefix = "", resultMode = "text") {
|
|
2156
3799
|
const toolName = `${namePrefix}${descriptor.name}`;
|
|
2157
|
-
const inputSchema = descriptor.inputSchema ?? { properties: {} };
|
|
3800
|
+
const inputSchema = descriptor.inputSchema ?? { type: "object", properties: {} };
|
|
2158
3801
|
const properties = inputSchema["properties"] ?? {};
|
|
2159
3802
|
const required = inputSchema["required"] ?? [];
|
|
3803
|
+
const parameters = {
|
|
3804
|
+
...inputSchema,
|
|
3805
|
+
type: "object",
|
|
3806
|
+
properties,
|
|
3807
|
+
...required.length > 0 ? { required } : {}
|
|
3808
|
+
};
|
|
2160
3809
|
return {
|
|
2161
3810
|
name: toolName,
|
|
2162
3811
|
description: descriptor.description ?? "",
|
|
2163
|
-
schema:
|
|
2164
|
-
execute: async (args) => {
|
|
3812
|
+
schema: createInputValidator(inputSchema),
|
|
3813
|
+
execute: async (args, context) => {
|
|
2165
3814
|
try {
|
|
2166
|
-
const result = await mcpClient.callTool(
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
});
|
|
2175
|
-
}
|
|
2176
|
-
return result.content.map((b) => b.type === "text" && b.text ? b.text : `[${b.type}]`).join("\n");
|
|
3815
|
+
const result = await mcpClient.callTool(
|
|
3816
|
+
{
|
|
3817
|
+
name: descriptor.name,
|
|
3818
|
+
arguments: args
|
|
3819
|
+
},
|
|
3820
|
+
context.signal !== void 0 ? { signal: context.signal } : void 0
|
|
3821
|
+
);
|
|
3822
|
+
return resultMode === "structured" ? result : formatMcpToolResult(result);
|
|
2177
3823
|
} catch (err) {
|
|
2178
3824
|
if (err instanceof OllamaMcpError) throw err;
|
|
2179
3825
|
throw new OllamaMcpError(`Failed calling MCP tool "${descriptor.name}"`, {
|
|
@@ -2188,19 +3834,15 @@ function convertMcpDescriptorToTool(descriptor, mcpClient, namePrefix = "") {
|
|
|
2188
3834
|
function: {
|
|
2189
3835
|
name: toolName,
|
|
2190
3836
|
description: descriptor.description ?? "",
|
|
2191
|
-
parameters
|
|
2192
|
-
type: "object",
|
|
2193
|
-
properties,
|
|
2194
|
-
...required.length > 0 ? { required } : {}
|
|
2195
|
-
}
|
|
3837
|
+
parameters
|
|
2196
3838
|
}
|
|
2197
3839
|
}
|
|
2198
3840
|
};
|
|
2199
3841
|
}
|
|
2200
|
-
async function loadMcpTools(mcpClient, options = {}) {
|
|
3842
|
+
async function loadMcpTools(mcpClient, options = {}, signal) {
|
|
2201
3843
|
try {
|
|
2202
|
-
const
|
|
2203
|
-
return tools.map((t) => convertMcpDescriptorToTool(t, mcpClient, options.namePrefix));
|
|
3844
|
+
const tools = await listAllMcpTools(mcpClient, options, signal);
|
|
3845
|
+
return tools.map((t) => convertMcpDescriptorToTool(t, mcpClient, options.namePrefix, options.resultMode));
|
|
2204
3846
|
} catch (err) {
|
|
2205
3847
|
throw new OllamaMcpError("Failed listing MCP tools from client", {
|
|
2206
3848
|
mcpMethod: "listTools",
|
|
@@ -2208,11 +3850,48 @@ async function loadMcpTools(mcpClient, options = {}) {
|
|
|
2208
3850
|
});
|
|
2209
3851
|
}
|
|
2210
3852
|
}
|
|
2211
|
-
async function registerMcpTools(registry, mcpClient, options = {}) {
|
|
2212
|
-
const tools = await loadMcpTools(mcpClient, options);
|
|
3853
|
+
async function registerMcpTools(registry, mcpClient, options = {}, signal) {
|
|
3854
|
+
const tools = await loadMcpTools(mcpClient, options, signal);
|
|
2213
3855
|
registry.registerMany(tools);
|
|
2214
3856
|
}
|
|
2215
3857
|
|
|
3858
|
+
// src/mcp/bridge.ts
|
|
3859
|
+
var McpBridge = class _McpBridge {
|
|
3860
|
+
constructor(client, options = {}) {
|
|
3861
|
+
this.client = client;
|
|
3862
|
+
this.options = options;
|
|
3863
|
+
}
|
|
3864
|
+
client;
|
|
3865
|
+
options;
|
|
3866
|
+
static toOllamaTools(tools, options = {}) {
|
|
3867
|
+
const prefix = options.namePrefix ?? "";
|
|
3868
|
+
return tools.map((tool) => ({
|
|
3869
|
+
type: "function",
|
|
3870
|
+
function: {
|
|
3871
|
+
name: prefix + tool.name,
|
|
3872
|
+
description: tool.description ?? "",
|
|
3873
|
+
parameters: {
|
|
3874
|
+
...tool.inputSchema ?? { type: "object", properties: {} },
|
|
3875
|
+
type: "object",
|
|
3876
|
+
properties: (tool.inputSchema ?? {})["properties"] ?? {}
|
|
3877
|
+
}
|
|
3878
|
+
}
|
|
3879
|
+
}));
|
|
3880
|
+
}
|
|
3881
|
+
async listTools(signal) {
|
|
3882
|
+
return listAllMcpTools(this.client, this.options, signal);
|
|
3883
|
+
}
|
|
3884
|
+
async definitions(signal) {
|
|
3885
|
+
return _McpBridge.toOllamaTools(await this.listTools(signal), this.options);
|
|
3886
|
+
}
|
|
3887
|
+
async loadTools(signal) {
|
|
3888
|
+
return loadMcpTools(this.client, this.options, signal);
|
|
3889
|
+
}
|
|
3890
|
+
async register(registry, signal) {
|
|
3891
|
+
await registry.registerMany(await this.loadTools(signal));
|
|
3892
|
+
}
|
|
3893
|
+
};
|
|
3894
|
+
|
|
2216
3895
|
// src/skills/frontmatter.ts
|
|
2217
3896
|
function parseYamlValue(raw) {
|
|
2218
3897
|
const trimmed = raw.trim();
|
|
@@ -2314,12 +3993,24 @@ var Agent = class {
|
|
|
2314
3993
|
client;
|
|
2315
3994
|
tools;
|
|
2316
3995
|
maxIterations;
|
|
3996
|
+
maxToolCalls;
|
|
2317
3997
|
hooks;
|
|
3998
|
+
validateToolCapability;
|
|
3999
|
+
toolContextSize;
|
|
2318
4000
|
constructor(client, config = {}) {
|
|
2319
4001
|
this.client = client;
|
|
2320
4002
|
this.tools = config.tools;
|
|
2321
4003
|
this.maxIterations = config.maxIterations ?? 10;
|
|
4004
|
+
this.maxToolCalls = config.maxToolCalls;
|
|
2322
4005
|
this.hooks = config.hooks;
|
|
4006
|
+
if (this.maxToolCalls !== void 0 && (!Number.isInteger(this.maxToolCalls) || this.maxToolCalls < 0)) {
|
|
4007
|
+
throw new RangeError("Agent maxToolCalls must be a non-negative integer");
|
|
4008
|
+
}
|
|
4009
|
+
this.validateToolCapability = config.validateToolCapability ?? true;
|
|
4010
|
+
this.toolContextSize = config.toolContextSize ?? 32768;
|
|
4011
|
+
if (!Number.isInteger(this.toolContextSize) || this.toolContextSize <= 0) {
|
|
4012
|
+
throw new RangeError("Agent toolContextSize must be a positive integer");
|
|
4013
|
+
}
|
|
2323
4014
|
}
|
|
2324
4015
|
run(input) {
|
|
2325
4016
|
return withSpan(
|
|
@@ -2337,6 +4028,30 @@ var Agent = class {
|
|
|
2337
4028
|
const history = [...input.messages];
|
|
2338
4029
|
const turns = [];
|
|
2339
4030
|
const toolDefs = this.tools?.definitions();
|
|
4031
|
+
const hasTools = toolDefs !== void 0 && toolDefs.length > 0;
|
|
4032
|
+
let capabilities;
|
|
4033
|
+
if (hasTools && this.validateToolCapability && this.client.capabilities) {
|
|
4034
|
+
capabilities = await this.client.capabilities(input.model, input.signal);
|
|
4035
|
+
if (!capabilities.supportsTools) {
|
|
4036
|
+
throw new OllamaIncompatibleModelError(
|
|
4037
|
+
`Model "${input.model}" does not advertise the "tools" capability.`,
|
|
4038
|
+
{
|
|
4039
|
+
model: input.model,
|
|
4040
|
+
capability: "tools",
|
|
4041
|
+
reportedCapabilities: capabilities.reported
|
|
4042
|
+
}
|
|
4043
|
+
);
|
|
4044
|
+
}
|
|
4045
|
+
}
|
|
4046
|
+
const shouldAutoSizeContext = hasTools && this.validateToolCapability && capabilities !== void 0 && input.options?.num_ctx === void 0;
|
|
4047
|
+
const effectiveOptions = shouldAutoSizeContext ? {
|
|
4048
|
+
...input.options ?? {},
|
|
4049
|
+
num_ctx: Math.min(
|
|
4050
|
+
this.toolContextSize,
|
|
4051
|
+
capabilities?.contextLength ?? this.toolContextSize
|
|
4052
|
+
)
|
|
4053
|
+
} : input.options;
|
|
4054
|
+
let toolCallsExecuted = 0;
|
|
2340
4055
|
for (let iteration = 1; iteration <= this.maxIterations; iteration++) {
|
|
2341
4056
|
const outcome = await withSpan(
|
|
2342
4057
|
"ollama.agent.turn",
|
|
@@ -2347,7 +4062,7 @@ var Agent = class {
|
|
|
2347
4062
|
model: input.model,
|
|
2348
4063
|
messages: history,
|
|
2349
4064
|
...toolDefs !== void 0 ? { tools: toolDefs } : {},
|
|
2350
|
-
...
|
|
4065
|
+
...effectiveOptions !== void 0 ? { options: effectiveOptions } : {},
|
|
2351
4066
|
...input.think !== void 0 ? { think: input.think } : {},
|
|
2352
4067
|
stream: false,
|
|
2353
4068
|
...input.signal !== void 0 ? { signal: input.signal } : {}
|
|
@@ -2362,17 +4077,24 @@ var Agent = class {
|
|
|
2362
4077
|
this.hooks?.onTurnEnd?.(finalTurn);
|
|
2363
4078
|
return { done: true, finalMessage: assistantMessage };
|
|
2364
4079
|
}
|
|
4080
|
+
if (this.maxToolCalls !== void 0 && toolCallsExecuted + toolCalls.length > this.maxToolCalls) {
|
|
4081
|
+
throw new OllamaAgentMaxToolCallsError(
|
|
4082
|
+
`Agent exceeded max tool calls (${this.maxToolCalls})`,
|
|
4083
|
+
{ maxToolCalls: this.maxToolCalls, toolCallsExecuted }
|
|
4084
|
+
);
|
|
4085
|
+
}
|
|
2365
4086
|
for (const tc of toolCalls) {
|
|
2366
4087
|
this.hooks?.onToolCallStart?.(tc);
|
|
2367
4088
|
}
|
|
2368
4089
|
const toolResults = await this.tools.executeToolCalls(toolCalls, {
|
|
2369
4090
|
signal: input.signal
|
|
2370
4091
|
});
|
|
4092
|
+
toolCallsExecuted += toolCalls.length;
|
|
2371
4093
|
for (const res of toolResults) {
|
|
2372
4094
|
this.hooks?.onToolCallEnd?.(res);
|
|
2373
4095
|
history.push({
|
|
2374
4096
|
role: "tool",
|
|
2375
|
-
|
|
4097
|
+
tool_name: res.toolName,
|
|
2376
4098
|
content: res.outputString
|
|
2377
4099
|
});
|
|
2378
4100
|
}
|
|
@@ -2412,6 +4134,7 @@ exports.ATTR_SERVER_PORT = ATTR_SERVER_PORT;
|
|
|
2412
4134
|
exports.ATTR_URL_FULL = ATTR_URL_FULL;
|
|
2413
4135
|
exports.Agent = Agent;
|
|
2414
4136
|
exports.AnthropicCompatClient = AnthropicCompatClient;
|
|
4137
|
+
exports.AnthropicMessagesStream = AnthropicMessagesStream;
|
|
2415
4138
|
exports.DEFAULT_BACKOFF = DEFAULT_BACKOFF;
|
|
2416
4139
|
exports.DEFAULT_BASE_URL = DEFAULT_BASE_URL;
|
|
2417
4140
|
exports.DEFAULT_FAILOVER_CODES = DEFAULT_FAILOVER_CODES;
|
|
@@ -2420,6 +4143,7 @@ exports.DEFAULT_TIMEOUT_MS = DEFAULT_TIMEOUT_MS;
|
|
|
2420
4143
|
exports.EndpointRegistry = EndpointRegistry;
|
|
2421
4144
|
exports.GEN_AI_SYSTEM_OLLAMA = GEN_AI_SYSTEM_OLLAMA;
|
|
2422
4145
|
exports.HttpClient = HttpClient;
|
|
4146
|
+
exports.McpBridge = McpBridge;
|
|
2423
4147
|
exports.ModelsClient = ModelsClient;
|
|
2424
4148
|
exports.NANOS_PER_MS = NANOS_PER_MS;
|
|
2425
4149
|
exports.NANOS_PER_SECOND = NANOS_PER_SECOND;
|
|
@@ -2427,10 +4151,12 @@ exports.NOOP_LOGGER = NOOP_LOGGER;
|
|
|
2427
4151
|
exports.OLLAMA_CLOUD_BASE_URL = OLLAMA_CLOUD_BASE_URL;
|
|
2428
4152
|
exports.OllamaAbortError = OllamaAbortError;
|
|
2429
4153
|
exports.OllamaAgentMaxIterationsError = OllamaAgentMaxIterationsError;
|
|
4154
|
+
exports.OllamaAgentMaxToolCallsError = OllamaAgentMaxToolCallsError;
|
|
2430
4155
|
exports.OllamaAuthError = OllamaAuthError;
|
|
2431
4156
|
exports.OllamaClient = OllamaClient;
|
|
2432
4157
|
exports.OllamaClientError = OllamaClientError;
|
|
2433
4158
|
exports.OllamaGenericClientError = OllamaGenericClientError;
|
|
4159
|
+
exports.OllamaIncompatibleModelError = OllamaIncompatibleModelError;
|
|
2434
4160
|
exports.OllamaMcpError = OllamaMcpError;
|
|
2435
4161
|
exports.OllamaModelRoutingError = OllamaModelRoutingError;
|
|
2436
4162
|
exports.OllamaNetworkError = OllamaNetworkError;
|
|
@@ -2445,7 +4171,11 @@ exports.OllamaTimeoutError = OllamaTimeoutError;
|
|
|
2445
4171
|
exports.OllamaToolTimeoutError = OllamaToolTimeoutError;
|
|
2446
4172
|
exports.OllamaToolValidationError = OllamaToolValidationError;
|
|
2447
4173
|
exports.OllamaUnsupportedCapabilityError = OllamaUnsupportedCapabilityError;
|
|
4174
|
+
exports.OpenAIChatCompletionStream = OpenAIChatCompletionStream;
|
|
2448
4175
|
exports.OpenAICompatClient = OpenAICompatClient;
|
|
4176
|
+
exports.OpenAICompletionStream = OpenAICompletionStream;
|
|
4177
|
+
exports.OpenAIResponsesStream = OpenAIResponsesStream;
|
|
4178
|
+
exports.OpenAIResponsesStreamError = OpenAIResponsesStreamError;
|
|
2449
4179
|
exports.QuotaManager = QuotaManager;
|
|
2450
4180
|
exports.ToolRegistry = ToolRegistry;
|
|
2451
4181
|
exports.addSpanEvent = addSpanEvent;
|
|
@@ -2461,6 +4191,7 @@ exports.detectModelCapabilities = detectModelCapabilities;
|
|
|
2461
4191
|
exports.encodeImage = encodeImage;
|
|
2462
4192
|
exports.extractUsage = extractUsage;
|
|
2463
4193
|
exports.inferRuntimeMode = inferRuntimeMode;
|
|
4194
|
+
exports.listAllMcpTools = listAllMcpTools;
|
|
2464
4195
|
exports.listAvailableModels = listAvailableModels;
|
|
2465
4196
|
exports.loadMcpTools = loadMcpTools;
|
|
2466
4197
|
exports.mapError = mapError;
|
|
@@ -2469,6 +4200,7 @@ exports.normalizeGenerateStream = normalizeGenerateStream;
|
|
|
2469
4200
|
exports.normalizeProgressStream = normalizeProgressStream;
|
|
2470
4201
|
exports.parseFrontmatter = parseFrontmatter;
|
|
2471
4202
|
exports.parseNdjsonStream = parseNdjsonStream;
|
|
4203
|
+
exports.parseSseStream = parseSseStream;
|
|
2472
4204
|
exports.parseStructuredOutput = parseStructuredOutput;
|
|
2473
4205
|
exports.registerMcpTools = registerMcpTools;
|
|
2474
4206
|
exports.resolveCredentialEndpoints = resolveCredentialEndpoints;
|