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