@24klynx/llm 0.1.2 → 0.1.5
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/LICENSE +21 -0
- package/dist/index.d.mts +6 -0
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +47 -152
- package/dist/index.mjs.map +1 -1
- package/package.json +8 -8
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 24K
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.d.mts
CHANGED
|
@@ -308,6 +308,8 @@ interface DeepSeekConfig {
|
|
|
308
308
|
baseUrl?: string;
|
|
309
309
|
/** Request timeout in ms (default 120s). */
|
|
310
310
|
timeoutMs?: number;
|
|
311
|
+
/** Max output tokens per request (default 8192). */
|
|
312
|
+
maxTokens?: number;
|
|
311
313
|
}
|
|
312
314
|
/**
|
|
313
315
|
* Create a DeepSeek provider instance.
|
|
@@ -322,6 +324,8 @@ interface OpenAiConfig {
|
|
|
322
324
|
apiKey: string;
|
|
323
325
|
baseUrl?: string;
|
|
324
326
|
timeoutMs?: number;
|
|
327
|
+
/** Max output tokens per request (default 8192). */
|
|
328
|
+
maxTokens?: number;
|
|
325
329
|
}
|
|
326
330
|
/**
|
|
327
331
|
* Create an OpenAI provider instance.
|
|
@@ -339,6 +343,8 @@ interface AnthropicConfig {
|
|
|
339
343
|
baseUrl?: string;
|
|
340
344
|
/** Request timeout in ms (default 120s). */
|
|
341
345
|
timeoutMs?: number;
|
|
346
|
+
/** Max output tokens per request (default 8192). */
|
|
347
|
+
maxTokens?: number;
|
|
342
348
|
}
|
|
343
349
|
/**
|
|
344
350
|
* Create an Anthropic provider instance.
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/stream.ts","../src/retry.ts","../src/capabilities.ts","../src/fallback.ts","../src/providers/deepseek.ts","../src/providers/openai.ts","../src/providers/anthropic.ts"],"mappings":";;;;;;AAoBuB;UAJN,WAAA;EAYS;EAVxB,IAAA;EAUwB;EARxB,OAAA,EAAS,YAAY;AAAA;;;;UAQN,SAAA;EAeA;EAbf,EAAA;;EAEA,KAAA;EAYA;EAVA,aAAA;EAYA;EAVA,SAAA;AAAA;;;;AAekB;UARH,kBAAA;EACf,aAAA;EACA,SAAA;EACA,iBAAA;EACA,iBAAA;EACA,eAAA;EACA,cAAA;EAyCkB;EAvClB,kBAAA;AAAA;;;;;;;UASe,WAAA;EAwBf;EAAA,SAtBS,EAAA;EAwBG;EArBZ,UAAA,IAAc,SAAA;EAsBZ;;;;EAhBF,aAAA,CAAc,OAAA,WAAkB,kBAAA;EAmBd;;AAAW;AAmB/B;;;;;;;;EAzBE,MAAA,CACE,OAAA,UACA,QAAA,EAAU,WAAA,IACV,YAAA,UACA,KAAA,aACA,MAAA,EAAQ,WAAA,GACP,cAAA,CAAe,WAAA;AAAA;;;;;;;;;;;;;;;KAmBR,WAAA,GACR,cAAA,GACA,mBAAA,GACA,iBAAA,GACA,iBAAA,GACA,eAAA,GACA,eAAA,GACA,cAAA,GACA,UAAA,GACA,SAAA;AAGJ;AAAA,UAAiB,cAAA;EACf,IAAA;EAD6B;EAG7B,KAAA;EAAA;EAEA,IAAA;AAAA;AAAI;AAAA,UAIW,mBAAA;EACf,IAAA;EACA,KAAA;EACA,IAAA;AAAA;;UAIe,iBAAA;EACf,IAAA;EALI;EAOJ,MAAA;EAHgC;EAKhC,IAAA;AAAA;;UAIe,iBAAA;EACf,IAAA;EACA,MAAA;EANI;EAQJ,IAAA;AAAA;;UAIe,eAAA;EACf,IAAA;EACA,MAAA;EANA;EAQA,KAAA,EAAO,MAAM;AAAA;AAJf;AAAA,UAQiB,eAAA;EACf,IAAA;EACA,SAAA;EACA,OAAA;EACA,OAAA;AAAA;;UAIe,UAAA;EACf,IAAA;EACA,IAAA;EACA,OAAA;AAAA;;UAIe,cAAA;EACf,IAAA;EAbA;EAeA,OAAA;EAdO;EAgBP,SAAA;EAZe;EAcf,UAAA;AAAA;;UAIe,SAAA;EACf,IAAA;EAhBA;EAkBA,WAAW;AAAA;;;;;;AA/Kb;;;;;;;;AAIuB;AAQvB;;;;;;;;;cCCa,MAAA,eAAqB,aAAA,CAAc,CAAA,GAAI,aAAA,CAAc,CAAA;EAAA,QACxD,MAAA;EAAA,QACA,UAAA;EAAA,QACA,KAAA;EAAA,QACA,MAAA;EAAA,iBACS,SAAA;cAEL,YAAA;EDUZ;ECHA,OAAA,CAAQ,KAAA,EAAO,CAAA;EDKf;ECWA,IAAA;EDRA;ECmBA,KAAA,CAAM,GAAA,EAAK,KAAA;EAAA,CAeV,MAAA,CAAO,aAAA,KAAkB,aAAA,CAAc,CAAA;EAIxC,IAAA,IAAQ,OAAA,CAAQ,cAAA,CAAe,CAAA;AAAA;;;;;;ADzEjC;;;;;;;UEKiB,YAAA;EFDM;EEGrB,MAAA;EFKwB;EEHxB,KAAA;EFGwB;EEDxB,UAAA;EFKA;EEHA,WAAA,IAAe,GAAA,EAAK,KAAK;EFOzB;;AAAS;AAOX;;EERE,YAAA;AAAA;;;;;;;;;AFgBkB;AASpB;;;;iBEsBsB,SAAA,IAAa,EAAA,QAAU,OAAA,CAAQ,CAAA,GAAI,IAAA,GAAM,YAAA,GAAoB,OAAA,CAAQ,CAAA;;;;;;AF9DpE;AAQvB;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/stream.ts","../src/retry.ts","../src/capabilities.ts","../src/fallback.ts","../src/providers/deepseek.ts","../src/providers/openai.ts","../src/providers/anthropic.ts"],"mappings":"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"}
|
package/dist/index.mjs
CHANGED
|
@@ -239,6 +239,15 @@ const CAPABILITIES = {
|
|
|
239
239
|
supportsToolUse: true,
|
|
240
240
|
supportsVision: true,
|
|
241
241
|
promptCacheEnabled: true
|
|
242
|
+
},
|
|
243
|
+
"anthropic/deepseek-v4-pro": {
|
|
244
|
+
contextWindow: 2e5,
|
|
245
|
+
maxOutput: 32e3,
|
|
246
|
+
supportsReasoning: true,
|
|
247
|
+
supportsStreaming: true,
|
|
248
|
+
supportsToolUse: true,
|
|
249
|
+
supportsVision: false,
|
|
250
|
+
promptCacheEnabled: true
|
|
242
251
|
}
|
|
243
252
|
};
|
|
244
253
|
/**
|
|
@@ -441,28 +450,9 @@ function normalizeContentBlocks(blocks, _role) {
|
|
|
441
450
|
}));
|
|
442
451
|
}
|
|
443
452
|
//#endregion
|
|
444
|
-
//#region src/providers/
|
|
445
|
-
/**
|
|
446
|
-
* DeepSeek provider — SSE streaming via the /v1/chat/completions endpoint.
|
|
447
|
-
*
|
|
448
|
-
* DeepSeek is API‑compatible with OpenAI so the wire format is the same.
|
|
449
|
-
* The key difference: DeepSeek has implicit prefix caching on every request
|
|
450
|
-
* (no cache_control blocks needed).
|
|
451
|
-
*/
|
|
452
|
-
const DEFAULT_BASE_URL$2 = "https://api.deepseek.com";
|
|
453
|
-
const MODELS$2 = [{
|
|
454
|
-
id: "deepseek-chat",
|
|
455
|
-
label: "DeepSeek Chat (V3)",
|
|
456
|
-
contextWindow: 128e3,
|
|
457
|
-
maxOutput: 8192
|
|
458
|
-
}, {
|
|
459
|
-
id: "deepseek-reasoner",
|
|
460
|
-
label: "DeepSeek Reasoner (R1)",
|
|
461
|
-
contextWindow: 128e3,
|
|
462
|
-
maxOutput: 8192
|
|
463
|
-
}];
|
|
453
|
+
//#region src/providers/sse-parser.ts
|
|
464
454
|
/** Flush active tool call buffer, yielding tool_use_end or error events. */
|
|
465
|
-
function flushActiveToolCall
|
|
455
|
+
function flushActiveToolCall(call) {
|
|
466
456
|
try {
|
|
467
457
|
const input = JSON.parse(call.buffer);
|
|
468
458
|
return [{
|
|
@@ -479,7 +469,7 @@ function flushActiveToolCall$1(call) {
|
|
|
479
469
|
}
|
|
480
470
|
}
|
|
481
471
|
/** Process tool_calls delta array, returning events and updated active call state. */
|
|
482
|
-
function processToolCallDeltas
|
|
472
|
+
function processToolCallDeltas(toolCalls, activeCall) {
|
|
483
473
|
const events = [];
|
|
484
474
|
let currentCall = activeCall;
|
|
485
475
|
for (const tc of toolCalls) {
|
|
@@ -520,7 +510,7 @@ function processToolCallDeltas$1(toolCalls, activeCall) {
|
|
|
520
510
|
};
|
|
521
511
|
}
|
|
522
512
|
/** Process a single SSE line, returning events to yield and updated state. */
|
|
523
|
-
function processSseLine
|
|
513
|
+
function processSseLine(line, state) {
|
|
524
514
|
const trimmed = line.trim();
|
|
525
515
|
if (!trimmed || !trimmed.startsWith("data:")) return {
|
|
526
516
|
events: [],
|
|
@@ -530,7 +520,7 @@ function processSseLine$1(line, state) {
|
|
|
530
520
|
const data = trimmed.slice(5).trim();
|
|
531
521
|
if (data === "[DONE]") {
|
|
532
522
|
const events = [];
|
|
533
|
-
if (state.activeToolCall) events.push(...flushActiveToolCall
|
|
523
|
+
if (state.activeToolCall) events.push(...flushActiveToolCall(state.activeToolCall));
|
|
534
524
|
events.push({ type: "done" });
|
|
535
525
|
return {
|
|
536
526
|
events,
|
|
@@ -571,7 +561,7 @@ function processSseLine$1(line, state) {
|
|
|
571
561
|
text: delta.content
|
|
572
562
|
});
|
|
573
563
|
if (delta.tool_calls) {
|
|
574
|
-
const tcResult = processToolCallDeltas
|
|
564
|
+
const tcResult = processToolCallDeltas(delta.tool_calls, activeToolCall);
|
|
575
565
|
events.push(...tcResult.events);
|
|
576
566
|
activeToolCall = tcResult.activeCall;
|
|
577
567
|
}
|
|
@@ -585,6 +575,27 @@ function processSseLine$1(line, state) {
|
|
|
585
575
|
done: false
|
|
586
576
|
};
|
|
587
577
|
}
|
|
578
|
+
//#endregion
|
|
579
|
+
//#region src/providers/deepseek.ts
|
|
580
|
+
/**
|
|
581
|
+
* DeepSeek provider — SSE streaming via the /v1/chat/completions endpoint.
|
|
582
|
+
*
|
|
583
|
+
* DeepSeek is API‑compatible with OpenAI so the wire format is the same.
|
|
584
|
+
* The key difference: DeepSeek has implicit prefix caching on every request
|
|
585
|
+
* (no cache_control blocks needed).
|
|
586
|
+
*/
|
|
587
|
+
const DEFAULT_BASE_URL$2 = "https://api.deepseek.com";
|
|
588
|
+
const MODELS$2 = [{
|
|
589
|
+
id: "deepseek-chat",
|
|
590
|
+
label: "DeepSeek Chat (V3)",
|
|
591
|
+
contextWindow: 128e3,
|
|
592
|
+
maxOutput: 8192
|
|
593
|
+
}, {
|
|
594
|
+
id: "deepseek-reasoner",
|
|
595
|
+
label: "DeepSeek Reasoner (R1)",
|
|
596
|
+
contextWindow: 128e3,
|
|
597
|
+
maxOutput: 8192
|
|
598
|
+
}];
|
|
588
599
|
/**
|
|
589
600
|
* Create a DeepSeek provider instance.
|
|
590
601
|
*
|
|
@@ -616,6 +627,7 @@ function createDeepSeekProvider(config) {
|
|
|
616
627
|
role: "system",
|
|
617
628
|
content: systemPrompt
|
|
618
629
|
}] : [], ...normalizeOpenAiMessages(messages)],
|
|
630
|
+
max_tokens: config.maxTokens ?? 8192,
|
|
619
631
|
stream: true,
|
|
620
632
|
...tools.length > 0 ? { tools: normalizeOpenAiTools(tools) } : {}
|
|
621
633
|
};
|
|
@@ -663,7 +675,7 @@ function createDeepSeekProvider(config) {
|
|
|
663
675
|
const lines = buffer.split("\n");
|
|
664
676
|
buffer = lines.pop() ?? "";
|
|
665
677
|
for (const line of lines) {
|
|
666
|
-
const result = processSseLine
|
|
678
|
+
const result = processSseLine(line, {
|
|
667
679
|
activeToolCall,
|
|
668
680
|
textIndex,
|
|
669
681
|
reasoningIndex
|
|
@@ -675,7 +687,7 @@ function createDeepSeekProvider(config) {
|
|
|
675
687
|
reasoningIndex = result.state.reasoningIndex;
|
|
676
688
|
}
|
|
677
689
|
}
|
|
678
|
-
if (activeToolCall) for (const event of flushActiveToolCall
|
|
690
|
+
if (activeToolCall) for (const event of flushActiveToolCall(activeToolCall)) yield event;
|
|
679
691
|
yield { type: "done" };
|
|
680
692
|
} finally {
|
|
681
693
|
reader.releaseLock();
|
|
@@ -734,130 +746,6 @@ const MODELS$1 = [
|
|
|
734
746
|
maxOutput: 1e5
|
|
735
747
|
}
|
|
736
748
|
];
|
|
737
|
-
/** Flush active tool call buffer, yielding tool_use_end or error events. */
|
|
738
|
-
function flushActiveToolCall(call) {
|
|
739
|
-
try {
|
|
740
|
-
const input = JSON.parse(call.buffer);
|
|
741
|
-
return [{
|
|
742
|
-
type: "tool_use_end",
|
|
743
|
-
callId: call.id,
|
|
744
|
-
input
|
|
745
|
-
}];
|
|
746
|
-
} catch {
|
|
747
|
-
return [{
|
|
748
|
-
type: "error",
|
|
749
|
-
code: "TOOL_PARSE_ERROR",
|
|
750
|
-
message: `Failed to parse tool input for ${call.name}`
|
|
751
|
-
}];
|
|
752
|
-
}
|
|
753
|
-
}
|
|
754
|
-
/** Process tool_calls delta array, returning events and updated active call state. */
|
|
755
|
-
function processToolCallDeltas(toolCalls, activeCall) {
|
|
756
|
-
const events = [];
|
|
757
|
-
let currentCall = activeCall;
|
|
758
|
-
for (const tc of toolCalls) {
|
|
759
|
-
const tcId = tc.id;
|
|
760
|
-
const fn = tc.function;
|
|
761
|
-
if (tcId && fn?.name) {
|
|
762
|
-
if (currentCall) try {
|
|
763
|
-
const input = JSON.parse(currentCall.buffer);
|
|
764
|
-
events.push({
|
|
765
|
-
type: "tool_use_end",
|
|
766
|
-
callId: currentCall.id,
|
|
767
|
-
input
|
|
768
|
-
});
|
|
769
|
-
} catch {}
|
|
770
|
-
currentCall = {
|
|
771
|
-
id: tcId,
|
|
772
|
-
name: fn.name,
|
|
773
|
-
buffer: ""
|
|
774
|
-
};
|
|
775
|
-
events.push({
|
|
776
|
-
type: "tool_use_start",
|
|
777
|
-
callId: tcId,
|
|
778
|
-
name: fn.name
|
|
779
|
-
});
|
|
780
|
-
}
|
|
781
|
-
if (fn?.arguments && currentCall) {
|
|
782
|
-
currentCall.buffer += fn.arguments;
|
|
783
|
-
events.push({
|
|
784
|
-
type: "tool_use_delta",
|
|
785
|
-
callId: currentCall.id,
|
|
786
|
-
text: fn.arguments
|
|
787
|
-
});
|
|
788
|
-
}
|
|
789
|
-
}
|
|
790
|
-
return {
|
|
791
|
-
events,
|
|
792
|
-
activeCall: currentCall
|
|
793
|
-
};
|
|
794
|
-
}
|
|
795
|
-
/** Process a single SSE line, returning events to yield and updated state. */
|
|
796
|
-
function processSseLine(line, state) {
|
|
797
|
-
const trimmed = line.trim();
|
|
798
|
-
if (!trimmed || !trimmed.startsWith("data:")) return {
|
|
799
|
-
events: [],
|
|
800
|
-
state,
|
|
801
|
-
done: false
|
|
802
|
-
};
|
|
803
|
-
const data = trimmed.slice(5).trim();
|
|
804
|
-
if (data === "[DONE]") {
|
|
805
|
-
const events = [];
|
|
806
|
-
if (state.activeToolCall) events.push(...flushActiveToolCall(state.activeToolCall));
|
|
807
|
-
events.push({ type: "done" });
|
|
808
|
-
return {
|
|
809
|
-
events,
|
|
810
|
-
state: {
|
|
811
|
-
...state,
|
|
812
|
-
activeToolCall: null
|
|
813
|
-
},
|
|
814
|
-
done: true
|
|
815
|
-
};
|
|
816
|
-
}
|
|
817
|
-
let chunk;
|
|
818
|
-
try {
|
|
819
|
-
chunk = JSON.parse(data);
|
|
820
|
-
} catch {
|
|
821
|
-
return {
|
|
822
|
-
events: [],
|
|
823
|
-
state,
|
|
824
|
-
done: false
|
|
825
|
-
};
|
|
826
|
-
}
|
|
827
|
-
const choice = chunk.choices?.[0];
|
|
828
|
-
if (!choice?.delta) return {
|
|
829
|
-
events: [],
|
|
830
|
-
state,
|
|
831
|
-
done: false
|
|
832
|
-
};
|
|
833
|
-
const delta = choice.delta;
|
|
834
|
-
const events = [];
|
|
835
|
-
let { textIndex, reasoningIndex, activeToolCall } = state;
|
|
836
|
-
if (delta.reasoning_content && typeof delta.reasoning_content === "string") events.push({
|
|
837
|
-
type: "reasoning_delta",
|
|
838
|
-
index: reasoningIndex++,
|
|
839
|
-
text: delta.reasoning_content
|
|
840
|
-
});
|
|
841
|
-
if (delta.content && typeof delta.content === "string") events.push({
|
|
842
|
-
type: "text_delta",
|
|
843
|
-
index: textIndex++,
|
|
844
|
-
text: delta.content
|
|
845
|
-
});
|
|
846
|
-
if (delta.tool_calls) {
|
|
847
|
-
const tcResult = processToolCallDeltas(delta.tool_calls, activeToolCall);
|
|
848
|
-
events.push(...tcResult.events);
|
|
849
|
-
activeToolCall = tcResult.activeCall;
|
|
850
|
-
}
|
|
851
|
-
return {
|
|
852
|
-
events,
|
|
853
|
-
state: {
|
|
854
|
-
textIndex,
|
|
855
|
-
reasoningIndex,
|
|
856
|
-
activeToolCall
|
|
857
|
-
},
|
|
858
|
-
done: false
|
|
859
|
-
};
|
|
860
|
-
}
|
|
861
749
|
/**
|
|
862
750
|
* Create an OpenAI provider instance.
|
|
863
751
|
*
|
|
@@ -888,6 +776,7 @@ function createOpenAiProvider(config) {
|
|
|
888
776
|
role: "system",
|
|
889
777
|
content: systemPrompt
|
|
890
778
|
}] : [], ...normalizeOpenAiMessages(messages)],
|
|
779
|
+
max_tokens: config.maxTokens ?? 8192,
|
|
891
780
|
stream: true,
|
|
892
781
|
...tools.length > 0 ? { tools: normalizeOpenAiTools(tools) } : {}
|
|
893
782
|
};
|
|
@@ -1003,6 +892,12 @@ const MODELS = [
|
|
|
1003
892
|
label: "Claude Haiku 4.5",
|
|
1004
893
|
contextWindow: 2e5,
|
|
1005
894
|
maxOutput: 8192
|
|
895
|
+
},
|
|
896
|
+
{
|
|
897
|
+
id: "deepseek-v4-pro",
|
|
898
|
+
label: "DeepSeek V4 Pro",
|
|
899
|
+
contextWindow: 2e5,
|
|
900
|
+
maxOutput: 32e3
|
|
1006
901
|
}
|
|
1007
902
|
];
|
|
1008
903
|
/**
|
|
@@ -1248,7 +1143,7 @@ function createAnthropicProvider(config) {
|
|
|
1248
1143
|
const systemParam = allSystemLines.length > 0 ? allSystemLines.join("\n\n") : void 0;
|
|
1249
1144
|
const body = {
|
|
1250
1145
|
model: modelId,
|
|
1251
|
-
max_tokens: 8192,
|
|
1146
|
+
max_tokens: config.maxTokens ?? 8192,
|
|
1252
1147
|
messages: translated.messages,
|
|
1253
1148
|
...systemParam ? { system: systemParam } : {},
|
|
1254
1149
|
...tools.length > 0 ? { tools: translateTools(tools) } : {},
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["anyErr","DEFAULT_BASE_URL","MODELS","flushActiveToolCall","processToolCallDeltas","processSseLine","handleHttpError","DEFAULT_BASE_URL","MODELS","handleHttpError"],"sources":["../src/stream.ts","../src/retry.ts","../src/capabilities.ts","../src/fallback.ts","../src/tool-format.ts","../src/providers/deepseek.ts","../src/providers/openai.ts","../src/providers/anthropic.ts"],"sourcesContent":["/**\n * Stream\\<T\\> — pull‑based AsyncIterator backed by an internal queue.\n *\n * Producers call `enqueue()` / `done()` / `error()`.\n * Consumers use `for await … of`.\n *\n * This is deliberately a class (not EventEmitter) so the consumer\n * controls back‑pressure — when nobody is iterating, values stay in\n * the queue until `maxQueueSize` is reached.\n */\n\n// ── Constants ──────────────────────────────────────\n\n/** Drop the oldest item when the queue exceeds this size. */\nconst DEFAULT_MAX_QUEUE = 256;\n\n// ── Public API ─────────────────────────────────────\n\n/**\n * A cold, single‑consumer async iterator.\n *\n * ```ts\n * const stream = new Stream<string>();\n * producer(stream);\n * for await (const chunk of stream) {\n * console.log(chunk);\n * }\n * ```\n */\nexport class Stream<T> implements AsyncIterator<T>, AsyncIterable<T> {\n private _queue: T[] = [];\n private _resolvers: Array<(value: IteratorResult<T>) => void> = [];\n private _done = false;\n private _error: Error | null = null;\n private readonly _maxQueue: number;\n\n constructor(maxQueueSize = DEFAULT_MAX_QUEUE) {\n this._maxQueue = maxQueueSize;\n }\n\n // ── Producer API ────────────────────────────────\n\n /** Push a value into the stream. */\n enqueue(value: T): void {\n if (this._done || this._error) return;\n\n if (this._resolvers.length > 0) {\n const resolve = this._resolvers.shift()!;\n resolve({ value, done: false });\n } else {\n this._queue.push(value);\n // Prevent unbounded growth\n if (this._queue.length > this._maxQueue) {\n this._queue.shift();\n }\n }\n }\n\n /** Signal that the stream has finished normally. */\n done(): void {\n if (this._done) return;\n this._done = true;\n // Resolve all pending awaiters with done: true\n for (const resolve of this._resolvers) {\n resolve({ value: undefined, done: true });\n }\n this._resolvers = [];\n }\n\n /** Propagate an error to the consumer. */\n error(err: Error): void {\n if (this._done || this._error) return;\n this._error = err;\n this._done = true;\n // The error is thrown from next(), but we can't throw through pending\n // resolvers. We resolve them with done: true and set the error flag\n // so the next next() call will throw.\n for (const resolve of this._resolvers) {\n resolve({ value: undefined, done: true });\n }\n this._resolvers = [];\n }\n\n // ── Consumer API ────────────────────────────────\n\n [Symbol.asyncIterator](): AsyncIterator<T> {\n return this;\n }\n\n next(): Promise<IteratorResult<T>> {\n // If the stream errored, reject\n if (this._error) {\n return Promise.reject(this._error);\n }\n\n // If we're done and the queue is empty, return done\n if (this._done && this._queue.length === 0) {\n return Promise.resolve({ value: undefined, done: true });\n }\n\n // If there's a queued value, return it immediately\n if (this._queue.length > 0) {\n const value = this._queue.shift()!;\n return Promise.resolve({ value, done: false });\n }\n\n // Wait for the next enqueue/done/error\n return new Promise((resolve) => {\n this._resolvers.push(resolve);\n });\n }\n}\n","/**\n * withRetry — exponential backoff with jitter and Retry‑After respect.\n *\n * Used by every LLM provider for transient errors (429, 529, ECONNRESET, etc.).\n *\n * Algorithm:\n * delay = min(baseMs * 2^(attempt-1), maxMs)\n * jitter = delay * uniform(0.75, 1.25)\n * final = min(jitter, maxMs)\n */\n\nimport { LlmError, LlmRateLimitError } from \"@24klynx/core\";\n\n// ── Constants ──────────────────────────────────────\n\nconst BASE_DELAY_MS = 500;\nconst MAX_DELAY_MS = 32_000;\nconst MAX_RETRIES = 10;\n\n// ── Types ──────────────────────────────────────────\n\nexport interface RetryOptions {\n /** Base delay for the first retry (default 500ms). */\n baseMs?: number;\n /** Hard ceiling on delay (default 32s). */\n maxMs?: number;\n /** Maximum number of retries before giving up (default 10). */\n maxRetries?: number;\n /** Function that returns true if this error is retryable. */\n isRetryable?: (err: Error) => boolean;\n /**\n * Whether this request is in the foreground (user-facing).\n * Foreground requests retry on 529; background requests discard\n * immediately to prevent cascade amplification. Defaults to true.\n */\n isForeground?: boolean;\n}\n\n// ── Helpers ────────────────────────────────────────\n\n/** Uniform random in [lo, hi]. */\nfunction jitter(lo: number, hi: number): number {\n return lo + Math.random() * (hi - lo);\n}\n\n/** Extract Retry‑After seconds from HTTP headers if present. */\nfunction parseRetryAfter(err: Error): number | null {\n const anyErr = err as Error & { headers?: unknown };\n const headers = anyErr.headers as Record<string, string> | undefined;\n if (!headers) return null;\n\n const raw = headers[\"retry-after\"];\n if (!raw) return null;\n\n // Try integer seconds first\n const secs = Number.parseInt(raw, 10);\n if (!Number.isNaN(secs)) return secs;\n\n // Try HTTP‑date (rare but valid)\n const parsed = Date.parse(raw);\n if (!Number.isNaN(parsed)) {\n return Math.max(0, Math.ceil((parsed - Date.now()) / 1000));\n }\n\n return null;\n}\n\n// ── Public API ─────────────────────────────────────\n\n/**\n * Execute `fn` with exponential backoff.\n *\n * Only retries on transient errors. Non‑retryable errors are re‑thrown\n * immediately.\n *\n * ```ts\n * const result = await withRetry(() => fetchFromApi(), {\n * baseMs: 500,\n * maxMs: 32_000,\n * });\n * ```\n */\nexport async function withRetry<T>(fn: () => Promise<T>, opts: RetryOptions = {}): Promise<T> {\n const baseMs = opts.baseMs ?? BASE_DELAY_MS;\n const maxMs = opts.maxMs ?? MAX_DELAY_MS;\n const maxRetries = opts.maxRetries ?? MAX_RETRIES;\n const isRetryable =\n opts.isRetryable ??\n ((err) => {\n if (err instanceof LlmRateLimitError) return true;\n // Always retry transient errors\n if (err instanceof LlmError && err.retryable) return true;\n // Retry network errors\n const code = (err as NodeJS.ErrnoException).code;\n if (code === \"ECONNRESET\" || code === \"EPIPE\" || code === \"ETIMEDOUT\") return true;\n return false;\n });\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n return await fn();\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n\n // If this is the last attempt or the error is not retryable, give up\n if (attempt === maxRetries || !isRetryable(error)) {\n throw error;\n }\n\n // 529 overload — only retry if this is in the foreground.\n // Background tasks drop to prevent cascade amplification.\n const anyErr = error as Error & { status?: number };\n if (anyErr.status === 529 && opts.isForeground === false) {\n throw error;\n }\n\n // Respect Retry‑After header\n const retryAfter = parseRetryAfter(error);\n const delay = retryAfter ? retryAfter * 1000 : Math.min(baseMs * Math.pow(2, attempt), maxMs);\n\n const jittered = Math.min(delay * jitter(0.75, 1.25), maxMs);\n\n await new Promise((resolve) => setTimeout(resolve, jittered));\n }\n }\n\n // Unreachable — the loop above always throws or returns\n throw new LlmError(\"withRetry exhausted all attempts\", {\n category: \"llm\",\n recoverable: false,\n retryable: false,\n diagnosticHint: \"llm_retry_exhausted\",\n });\n}\n","/**\n * Provider capability metadata table.\n *\n * Every model known to Lynx is registered here with its static capabilities.\n * The router uses this table to decide which model can handle a request\n * (e.g. a request with images requires `supportsVision: true`).\n */\n\nimport type { ProviderCapability } from \"./types.js\";\n\n/**\n * Lookup table keyed by \"provider/modelId\".\n *\n * New models can be added here without code changes —\n * capabilities are pure data, not behaviour.\n */\nconst CAPABILITIES: Record<string, ProviderCapability> = {\n // DeepSeek\n \"deepseek/deepseek-chat\": {\n contextWindow: 128_000,\n maxOutput: 8_192,\n supportsReasoning: false,\n supportsStreaming: true,\n supportsToolUse: true,\n supportsVision: false,\n promptCacheEnabled: true, // implicit prefix caching\n },\n \"deepseek/deepseek-reasoner\": {\n contextWindow: 128_000,\n maxOutput: 8_192,\n supportsReasoning: true,\n supportsStreaming: true,\n supportsToolUse: false,\n supportsVision: false,\n promptCacheEnabled: true,\n },\n\n // OpenAI\n \"openai/gpt-4o\": {\n contextWindow: 128_000,\n maxOutput: 16_384,\n supportsReasoning: false,\n supportsStreaming: true,\n supportsToolUse: true,\n supportsVision: true,\n promptCacheEnabled: false, // No prompt caching for OpenAI (as of now)\n },\n \"openai/gpt-4o-mini\": {\n contextWindow: 128_000,\n maxOutput: 16_384,\n supportsReasoning: false,\n supportsStreaming: true,\n supportsToolUse: true,\n supportsVision: true,\n promptCacheEnabled: false,\n },\n \"openai/o3-mini\": {\n contextWindow: 200_000,\n maxOutput: 100_000,\n supportsReasoning: true,\n supportsStreaming: true,\n supportsToolUse: false,\n supportsVision: false,\n promptCacheEnabled: false,\n },\n\n // Anthropic\n \"anthropic/claude-opus-4-8\": {\n contextWindow: 200_000,\n maxOutput: 32_000,\n supportsReasoning: true,\n supportsStreaming: true,\n supportsToolUse: true,\n supportsVision: true,\n promptCacheEnabled: true,\n },\n \"anthropic/claude-sonnet-4-6\": {\n contextWindow: 200_000,\n maxOutput: 16_384,\n supportsReasoning: true,\n supportsStreaming: true,\n supportsToolUse: true,\n supportsVision: true,\n promptCacheEnabled: true,\n },\n \"anthropic/claude-haiku-4-5\": {\n contextWindow: 200_000,\n maxOutput: 8_192,\n supportsReasoning: false,\n supportsStreaming: true,\n supportsToolUse: true,\n supportsVision: true,\n promptCacheEnabled: true,\n },\n};\n\n/**\n * Return the capability blob for `provider/modelId`.\n * Returns `undefined` for unknown models — callers should fall back\n * or throw a {@link import('@24klynx/core').ConfigError}.\n */\nexport function getCapability(providerId: string, modelId: string): ProviderCapability | undefined {\n return CAPABILITIES[`${providerId}/${modelId}`];\n}\n\n/**\n * Return every known capability entry.\n * Used by the model picker UI in the TUI layer.\n */\nexport function listCapabilities(): Array<{ key: string } & ProviderCapability> {\n return Object.entries(CAPABILITIES).map(([key, cap]) => ({ key, ...cap }));\n}\n","/**\n * Model/Provider fallback logic.\n *\n * When a provider returns a transient error (rate‑limit, overload) the\n * fallback engine tries the next candidate in the configured chain.\n * Permanent errors (auth) skip the provider entirely and move to the next.\n *\n * The primary use‑case is keeping the agent loop running when the\n * preferred model is temporarily unavailable.\n */\n\nimport { LlmRateLimitError, LlmAuthError } from \"@24klynx/core\";\n\n// ── Types ────────────────────────────────────────────\n\n/** A candidate provider+model pair in the fallback chain. */\nexport interface ModelCandidate {\n provider: string;\n model: string;\n}\n\n/** Record of a single fallback attempt for diagnostics. */\nexport interface FallbackAttempt {\n provider: string;\n model: string;\n error: string;\n code?: string;\n /** HTTP status code if available. */\n status?: number;\n}\n\n/** Result of a fallback run — which candidate succeeded and what was tried. */\nexport interface FallbackResult<T> {\n result: T;\n provider: string;\n model: string;\n attempts: FallbackAttempt[];\n}\n\n/** Error thrown when every candidate in the chain has been exhausted. */\nexport class FallbackExhaustedError extends Error {\n readonly attempts: FallbackAttempt[];\n\n constructor(attempts: FallbackAttempt[]) {\n const summary = attempts.map((a) => ` ${a.provider}/${a.model}: ${a.error}`).join(\"\\n\");\n super(`All fallback candidates exhausted:\\n${summary}`);\n this.name = \"FallbackExhaustedError\";\n this.attempts = attempts;\n }\n}\n\n// ── Helpers ──────────────────────────────────────────\n\n/**\n * Determine whether an error is transient (worth retrying with a\n * different provider) or permanent (skip this provider).\n */\nfunction isTransient(err: unknown): boolean {\n if (err instanceof LlmRateLimitError) return true;\n if (err instanceof LlmAuthError) return false;\n // Network errors are transient\n if (err instanceof Error && \"code\" in err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === \"ECONNRESET\" || code === \"EPIPE\" || code === \"ETIMEDOUT\" || code === \"ENOTFOUND\") {\n return true;\n }\n }\n // Default: treat unknown errors as transient (safer to try another provider)\n return true;\n}\n\n// ── Public API ───────────────────────────────────────\n\n/**\n * Run `fn` with automatic fallback across a chain of model candidates.\n *\n * `fn` is called with (provider, model) and should return a result.\n * If `fn` throws, the error is classified — transient errors move to the\n * next candidate, permanent errors skip the provider, and fatal errors\n * (like user abort) are re‑thrown immediately.\n */\nexport async function runWithFallback<T>(\n candidates: ModelCandidate[],\n fn: (candidate: ModelCandidate) => Promise<T>,\n opts?: { signal?: AbortSignal },\n): Promise<FallbackResult<T>> {\n if (candidates.length === 0) {\n throw new FallbackExhaustedError([]);\n }\n\n const attempts: FallbackAttempt[] = [];\n\n for (const candidate of candidates) {\n // Respect external abort signal\n if (opts?.signal?.aborted) {\n throw new DOMException(\"Aborted\", \"AbortError\");\n }\n\n try {\n const result = await fn(candidate);\n return { result, provider: candidate.provider, model: candidate.model, attempts };\n } catch (err) {\n // User‑initiated abort — stop immediately\n if (err instanceof DOMException && err.name === \"AbortError\") {\n throw err;\n }\n\n const message = err instanceof Error ? err.message : String(err);\n const status = (err as Record<string, unknown>).status as number | undefined;\n const code = (err as Record<string, unknown>).code as string | undefined;\n\n attempts.push({\n provider: candidate.provider,\n model: candidate.model,\n error: message,\n code,\n status,\n });\n\n // Permanent errors — skip this provider, try next\n if (!isTransient(err)) {\n continue;\n }\n\n // Transient — try next candidate\n continue;\n }\n }\n\n throw new FallbackExhaustedError(attempts);\n}\n\n/**\n * Build a fallback chain from a primary model and a list of fallback refs.\n *\n * Fallback refs use the format `\"provider/model\"` (e.g. `\"openai/gpt-4o-mini\"`).\n * If the provider prefix is omitted the primary's provider is used.\n */\nexport function buildCandidateChain(\n primaryProvider: string,\n primaryModel: string,\n fallbackRefs: string[],\n): ModelCandidate[] {\n const chain: ModelCandidate[] = [{ provider: primaryProvider, model: primaryModel }];\n\n for (const ref of fallbackRefs) {\n const slashIdx = ref.indexOf(\"/\");\n if (slashIdx > 0) {\n chain.push({ provider: ref.slice(0, slashIdx), model: ref.slice(slashIdx + 1) });\n } else {\n chain.push({ provider: primaryProvider, model: ref });\n }\n }\n\n return chain;\n}\n","/**\n * Wire-format normalization for OpenAI-compatible APIs.\n *\n * Lynx uses its own internal types ({@link import('@24klynx/tools').ToolDescriptor},\n * {@link import('@24klynx/core').ContentBlock}) that don't exactly match the\n * OpenAI/DeepSeek wire format. This module maps between them.\n */\n\nimport type { ChatMessage } from \"./types.js\";\n\n// ── Tool normalization ────────────────────────────\n\n/** Raw shape we receive from the agent (ToolDescriptor-like). */\ninterface RawTool {\n name?: unknown;\n description?: unknown;\n inputSchema?: unknown;\n}\n\n/** OpenAI-compatible tool definition. */\ninterface OpenAiTool {\n type: \"function\";\n function: {\n name: string;\n description: string;\n parameters: Record<string, unknown>;\n };\n}\n\n/**\n * Convert Lynx tool descriptors to OpenAI-compatible format.\n *\n * Wraps each tool with `type: \"function\"` and maps `inputSchema` → `parameters`.\n * Tools already in OpenAI format pass through unchanged.\n */\nexport function normalizeOpenAiTools(tools: unknown[]): OpenAiTool[] {\n return tools.map((tool) => {\n const raw = tool as RawTool;\n if (typeof raw === \"object\" && raw !== null && (raw as { type?: string }).type === \"function\") {\n return tool as OpenAiTool;\n }\n return {\n type: \"function\" as const,\n function: {\n name: String(raw.name ?? \"unknown\"),\n description: String(raw.description ?? \"\"),\n parameters: (raw.inputSchema as Record<string, unknown>) ?? {},\n },\n };\n });\n}\n\n// ── Wire-format message types ─────────────────────\n\n/** A message in OpenAI wire format — what the API actually expects. */\nexport interface OpenAiMessage {\n role: \"system\" | \"user\" | \"assistant\" | \"tool\";\n content: string | OpenAiContentBlock[] | null;\n tool_call_id?: string;\n tool_calls?: Array<{\n id: string;\n type: \"function\";\n function: { name: string; arguments: string };\n }>;\n}\n\ninterface OpenAiContentBlock {\n type: \"text\";\n text: string;\n}\n\n// ── Content block types for recognition ───────────\n\ninterface LynxContentBlock {\n type: string;\n text?: string;\n id?: string;\n name?: string;\n input?: Record<string, unknown>;\n toolUseId?: string;\n content?: string;\n isError?: boolean;\n}\n\n// ── Message normalization ─────────────────────────\n\n/**\n * Convert Lynx ChatMessage[] to OpenAI wire-format messages.\n *\n * Key transformations:\n * - `tool_result` content blocks → `role: \"tool\"` messages with string content\n * - `tool_use` blocks in assistant messages → `tool_calls` array\n * - `reasoning` blocks are stripped (not sent back to the API)\n */\nexport function normalizeOpenAiMessages(messages: ChatMessage[]): OpenAiMessage[] {\n const out: OpenAiMessage[] = [];\n\n for (const msg of messages) {\n // Simple string content — pass through with role\n if (typeof msg.content === \"string\") {\n out.push({ role: msg.role as OpenAiMessage[\"role\"], content: msg.content });\n continue;\n }\n\n const blocks = msg.content as LynxContentBlock[];\n\n // Check for tool_result blocks → convert to tool-role messages\n const toolResults = blocks.filter((b) => b.type === \"tool_result\");\n const nonToolBlocks = blocks.filter((b) => b.type !== \"tool_result\");\n\n // Emit tool result messages\n for (const tr of toolResults) {\n out.push({\n role: \"tool\",\n tool_call_id: tr.toolUseId ?? \"unknown\",\n content: tr.content ?? \"\",\n });\n }\n\n // Handle remaining blocks by role\n if (nonToolBlocks.length > 0) {\n const normalized = normalizeContentBlocks(nonToolBlocks, msg.role);\n\n if (msg.role === \"assistant\") {\n const textBlocks = normalized.filter((b) => b.type === \"text\");\n const toolUses = blocks.filter((b) => b.type === \"tool_use\");\n const assistantMsg: OpenAiMessage = {\n role: \"assistant\",\n content: textBlocks.length > 0 ? textBlocks : null,\n };\n if (toolUses.length > 0) {\n assistantMsg.tool_calls = toolUses.map((tu) => ({\n id: tu.id ?? \"unknown\",\n type: \"function\" as const,\n function: {\n name: tu.name ?? \"unknown\",\n arguments: JSON.stringify(tu.input ?? {}),\n },\n }));\n }\n out.push(assistantMsg);\n } else {\n // User or system message — strip unrecognized blocks, keep text\n const textBlocks = normalized.filter((b) => b.type === \"text\");\n if (textBlocks.length > 0) {\n out.push({\n role: msg.role as OpenAiMessage[\"role\"],\n content:\n textBlocks.length === 1 && msg.role !== \"system\"\n ? (textBlocks[0]!.text ?? \"\")\n : textBlocks,\n });\n }\n }\n }\n }\n\n return out;\n}\n\n/** Keep only text blocks, strip reasoning/tool_use/tool_result from content. */\nfunction normalizeContentBlocks(blocks: LynxContentBlock[], _role: string): OpenAiContentBlock[] {\n return blocks\n .filter((b) => b.type === \"text\" && typeof b.text === \"string\")\n .map((b) => ({ type: \"text\" as const, text: b.text! }));\n}\n","/**\n * DeepSeek provider — SSE streaming via the /v1/chat/completions endpoint.\n *\n * DeepSeek is API‑compatible with OpenAI so the wire format is the same.\n * The key difference: DeepSeek has implicit prefix caching on every request\n * (no cache_control blocks needed).\n */\n\nimport { LlmError, LlmRateLimitError, LlmAuthError, LlmContextOverflowError } from \"@24klynx/core\";\nimport type { LlmProvider, ModelInfo, ProviderCapability, StreamEvent } from \"../types.js\";\nimport { getCapability } from \"../capabilities.js\";\nimport { withRetry } from \"../retry.js\";\nimport { normalizeOpenAiTools, normalizeOpenAiMessages } from \"../tool-format.js\";\n\n// ── Configuration ──────────────────────────────────\n\nexport interface DeepSeekConfig {\n /** API key read from auth store. */\n apiKey: string;\n /** Base URL (defaults to https://api.deepseek.com). */\n baseUrl?: string;\n /** Request timeout in ms (default 120s). */\n timeoutMs?: number;\n}\n\nconst DEFAULT_BASE_URL = \"https://api.deepseek.com\";\nconst DEFAULT_TIMEOUT_MS = 120_000;\n\n// ── Models ─────────────────────────────────────────\n\nconst MODELS: ModelInfo[] = [\n { id: \"deepseek-chat\", label: \"DeepSeek Chat (V3)\", contextWindow: 128_000, maxOutput: 8_192 },\n {\n id: \"deepseek-reasoner\",\n label: \"DeepSeek Reasoner (R1)\",\n contextWindow: 128_000,\n maxOutput: 8_192,\n },\n];\n\n// ── Helpers ────────────────────────────────────────\n\n/** Flush active tool call buffer, yielding tool_use_end or error events. */\nfunction flushActiveToolCall(call: { id: string; name: string; buffer: string }): StreamEvent[] {\n try {\n const input = JSON.parse(call.buffer);\n return [{ type: \"tool_use_end\", callId: call.id, input }];\n } catch {\n return [\n {\n type: \"error\",\n code: \"TOOL_PARSE_ERROR\",\n message: `Failed to parse tool input for ${call.name}`,\n },\n ];\n }\n}\n\n/** Process tool_calls delta array, returning events and updated active call state. */\nfunction processToolCallDeltas(\n toolCalls: Array<Record<string, unknown>>,\n activeCall: { id: string; name: string; buffer: string } | null,\n): { events: StreamEvent[]; activeCall: typeof activeCall } {\n const events: StreamEvent[] = [];\n let currentCall = activeCall;\n\n for (const tc of toolCalls) {\n const tcId = tc.id as string | undefined;\n const fn = tc.function as { name?: string; arguments?: string } | undefined;\n\n if (tcId && fn?.name) {\n // Flush previous tool call if any (silently ignore parse failures)\n if (currentCall) {\n try {\n const input = JSON.parse(currentCall.buffer);\n events.push({ type: \"tool_use_end\", callId: currentCall.id, input });\n } catch {\n // ignore parse failure, keep accumulating\n }\n }\n currentCall = { id: tcId, name: fn.name, buffer: \"\" };\n events.push({ type: \"tool_use_start\", callId: tcId, name: fn.name });\n }\n\n if (fn?.arguments && currentCall) {\n currentCall.buffer += fn.arguments;\n events.push({ type: \"tool_use_delta\", callId: currentCall.id, text: fn.arguments });\n }\n }\n\n return { events, activeCall: currentCall };\n}\n\n/** SSE stream processing state. */\ninterface SseState {\n activeToolCall: { id: string; name: string; buffer: string } | null;\n textIndex: number;\n reasoningIndex: number;\n}\n\n/** Process a single SSE line, returning events to yield and updated state. */\nfunction processSseLine(\n line: string,\n state: SseState,\n): { events: StreamEvent[]; state: SseState; done: boolean } {\n const trimmed = line.trim();\n if (!trimmed || !trimmed.startsWith(\"data:\")) {\n return { events: [], state, done: false };\n }\n\n const data = trimmed.slice(5).trim();\n\n if (data === \"[DONE]\") {\n const events: StreamEvent[] = [];\n if (state.activeToolCall) {\n events.push(...flushActiveToolCall(state.activeToolCall));\n }\n events.push({ type: \"done\" });\n return { events, state: { ...state, activeToolCall: null }, done: true };\n }\n\n let chunk: { choices?: Array<{ delta?: Record<string, unknown> }> };\n try {\n chunk = JSON.parse(data);\n } catch {\n return { events: [], state, done: false };\n }\n\n const choice = chunk.choices?.[0];\n if (!choice?.delta) {\n return { events: [], state, done: false };\n }\n\n const delta = choice.delta;\n const events: StreamEvent[] = [];\n let { textIndex, reasoningIndex, activeToolCall } = state;\n\n // Reasoning content (DeepSeek R1)\n if (delta.reasoning_content && typeof delta.reasoning_content === \"string\") {\n events.push({\n type: \"reasoning_delta\",\n index: reasoningIndex++,\n text: delta.reasoning_content,\n });\n }\n\n // Regular text content\n if (delta.content && typeof delta.content === \"string\") {\n events.push({\n type: \"text_delta\",\n index: textIndex++,\n text: delta.content,\n });\n }\n\n // Tool call handling\n if (delta.tool_calls) {\n const tcResult = processToolCallDeltas(\n delta.tool_calls as Array<Record<string, unknown>>,\n activeToolCall,\n );\n events.push(...tcResult.events);\n activeToolCall = tcResult.activeCall;\n }\n\n return { events, state: { textIndex, reasoningIndex, activeToolCall }, done: false };\n}\n\n// ── Provider ───────────────────────────────────────\n\n/**\n * Create a DeepSeek provider instance.\n *\n * Each call to `stream()` opens a fresh HTTP connection.\n * The provider itself is stateless — the API key is the only configuration.\n */\nexport function createDeepSeekProvider(config: DeepSeekConfig): LlmProvider {\n const baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;\n const _timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n\n const provider: LlmProvider = {\n id: \"deepseek\",\n\n listModels(): ModelInfo[] {\n return MODELS;\n },\n\n getCapability(modelId: string): ProviderCapability {\n const cap = getCapability(\"deepseek\", modelId);\n if (!cap) {\n throw new LlmError(`Unknown DeepSeek model: ${modelId}`, {\n category: \"llm\",\n recoverable: false,\n retryable: false,\n diagnosticHint: \"llm_unknown_model\",\n });\n }\n return cap;\n },\n\n async *stream(\n ...[modelId, messages, systemPrompt, tools, signal]: Parameters<LlmProvider[\"stream\"]>\n ): AsyncGenerator<StreamEvent, void, void> {\n const systemMessages = systemPrompt ? [{ role: \"system\", content: systemPrompt }] : [];\n\n const body = {\n model: modelId,\n messages: [...systemMessages, ...normalizeOpenAiMessages(messages)],\n stream: true,\n ...(tools.length > 0 ? { tools: normalizeOpenAiTools(tools) } : {}),\n };\n\n async function makeRequest(): Promise<Response> {\n const controller = new AbortController();\n const onAbort = () => controller.abort();\n signal.addEventListener(\"abort\", onAbort, { once: true });\n\n try {\n const response = await fetch(`${baseUrl}/v1/chat/completions`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${config.apiKey}`,\n },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n await handleHttpError(response);\n }\n\n return response;\n } finally {\n signal.removeEventListener(\"abort\", onAbort);\n }\n }\n\n const response = await withRetry(makeRequest, {\n baseMs: 500,\n maxMs: 32_000,\n });\n\n if (!response.body) {\n throw new LlmError(\"DeepSeek returned empty response body\", {\n category: \"llm\",\n recoverable: true,\n retryable: true,\n });\n }\n\n // Parse SSE stream\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n let textIndex = 0;\n let reasoningIndex = 0;\n let activeToolCall: { id: string; name: string; buffer: string } | null = null;\n\n try {\n while (true) {\n if (signal.aborted) break;\n\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n\n // Process complete SSE lines\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() ?? \"\"; // keep incomplete line in buffer\n\n for (const line of lines) {\n const result = processSseLine(line, { activeToolCall, textIndex, reasoningIndex });\n for (const event of result.events) yield event;\n if (result.done) return;\n activeToolCall = result.state.activeToolCall;\n textIndex = result.state.textIndex;\n reasoningIndex = result.state.reasoningIndex;\n }\n }\n\n // Stream ended without [DONE]\n if (activeToolCall) {\n for (const event of flushActiveToolCall(activeToolCall)) yield event;\n }\n yield { type: \"done\" };\n } finally {\n reader.releaseLock();\n }\n },\n };\n\n return provider;\n}\n\n// ── HTTP error mapper ──────────────────────────────\n\nasync function handleHttpError(response: Response): Promise<never> {\n let body: string;\n try {\n body = await response.text();\n } catch {\n body = \"\";\n }\n\n const status = response.status;\n\n if (status === 401 || status === 403) {\n throw new LlmAuthError(`DeepSeek auth failed (${status}): ${body}`);\n }\n\n if (status === 429) {\n throw new LlmRateLimitError(`DeepSeek rate limited (429): ${body}`);\n }\n\n if (status === 529) {\n const err = new LlmRateLimitError(`DeepSeek overloaded (529): ${body}`);\n (err as unknown as Record<string, unknown>).status = 529;\n throw err;\n }\n\n if (status === 400 && body.includes(\"context\")) {\n throw new LlmContextOverflowError(`DeepSeek context overflow: ${body}`);\n }\n\n throw new LlmError(`DeepSeek HTTP ${status}: ${body}`, {\n category: \"llm\",\n recoverable: status >= 500,\n retryable: status >= 500 || status === 429,\n });\n}\n","/**\n * OpenAI provider — SSE streaming via the /v1/chat/completions endpoint.\n *\n * The API shape is identical to DeepSeek (both follow the OpenAI spec),\n * but the provider metadata and defaults differ.\n */\n\nimport { LlmError, LlmRateLimitError, LlmAuthError, LlmContextOverflowError } from \"@24klynx/core\";\nimport type { LlmProvider, ModelInfo, ProviderCapability, StreamEvent } from \"../types.js\";\nimport { getCapability } from \"../capabilities.js\";\nimport { withRetry } from \"../retry.js\";\nimport { normalizeOpenAiTools, normalizeOpenAiMessages } from \"../tool-format.js\";\n\n// ── Configuration ──────────────────────────────────\n\nexport interface OpenAiConfig {\n apiKey: string;\n baseUrl?: string;\n timeoutMs?: number;\n}\n\nconst DEFAULT_BASE_URL = \"https://api.openai.com\";\nconst _DEFAULT_TIMEOUT_MS = 120_000;\n\n// ── Models ─────────────────────────────────────────\n\nconst MODELS: ModelInfo[] = [\n { id: \"gpt-4o\", label: \"GPT-4o\", contextWindow: 128_000, maxOutput: 16_384 },\n { id: \"gpt-4o-mini\", label: \"GPT-4o Mini\", contextWindow: 128_000, maxOutput: 16_384 },\n { id: \"o3-mini\", label: \"o3-mini\", contextWindow: 200_000, maxOutput: 100_000 },\n];\n\n// ── Helpers ────────────────────────────────────────\n\n/** Flush active tool call buffer, yielding tool_use_end or error events. */\nfunction flushActiveToolCall(call: { id: string; name: string; buffer: string }): StreamEvent[] {\n try {\n const input = JSON.parse(call.buffer);\n return [{ type: \"tool_use_end\", callId: call.id, input }];\n } catch {\n return [\n {\n type: \"error\",\n code: \"TOOL_PARSE_ERROR\",\n message: `Failed to parse tool input for ${call.name}`,\n },\n ];\n }\n}\n\n/** Process tool_calls delta array, returning events and updated active call state. */\nfunction processToolCallDeltas(\n toolCalls: Array<Record<string, unknown>>,\n activeCall: { id: string; name: string; buffer: string } | null,\n): { events: StreamEvent[]; activeCall: typeof activeCall } {\n const events: StreamEvent[] = [];\n let currentCall = activeCall;\n\n for (const tc of toolCalls) {\n const tcId = tc.id as string | undefined;\n const fn = tc.function as { name?: string; arguments?: string } | undefined;\n\n if (tcId && fn?.name) {\n if (currentCall) {\n try {\n const input = JSON.parse(currentCall.buffer);\n events.push({ type: \"tool_use_end\", callId: currentCall.id, input });\n } catch {\n /* keep accumulating */\n }\n }\n currentCall = { id: tcId, name: fn.name, buffer: \"\" };\n events.push({ type: \"tool_use_start\", callId: tcId, name: fn.name });\n }\n\n if (fn?.arguments && currentCall) {\n currentCall.buffer += fn.arguments;\n events.push({ type: \"tool_use_delta\", callId: currentCall.id, text: fn.arguments });\n }\n }\n\n return { events, activeCall: currentCall };\n}\n\n/** SSE stream processing state. */\ninterface SseState {\n activeToolCall: { id: string; name: string; buffer: string } | null;\n textIndex: number;\n reasoningIndex: number;\n}\n\n/** Process a single SSE line, returning events to yield and updated state. */\nfunction processSseLine(\n line: string,\n state: SseState,\n): { events: StreamEvent[]; state: SseState; done: boolean } {\n const trimmed = line.trim();\n if (!trimmed || !trimmed.startsWith(\"data:\")) {\n return { events: [], state, done: false };\n }\n\n const data = trimmed.slice(5).trim();\n\n if (data === \"[DONE]\") {\n const events: StreamEvent[] = [];\n if (state.activeToolCall) {\n events.push(...flushActiveToolCall(state.activeToolCall));\n }\n events.push({ type: \"done\" });\n return { events, state: { ...state, activeToolCall: null }, done: true };\n }\n\n let chunk: { choices?: Array<{ delta?: Record<string, unknown> }> };\n try {\n chunk = JSON.parse(data);\n } catch {\n return { events: [], state, done: false };\n }\n\n const choice = chunk.choices?.[0];\n if (!choice?.delta) {\n return { events: [], state, done: false };\n }\n\n const delta = choice.delta;\n const events: StreamEvent[] = [];\n let { textIndex, reasoningIndex, activeToolCall } = state;\n\n if (delta.reasoning_content && typeof delta.reasoning_content === \"string\") {\n events.push({\n type: \"reasoning_delta\",\n index: reasoningIndex++,\n text: delta.reasoning_content,\n });\n }\n\n if (delta.content && typeof delta.content === \"string\") {\n events.push({ type: \"text_delta\", index: textIndex++, text: delta.content });\n }\n\n if (delta.tool_calls) {\n const tcResult = processToolCallDeltas(\n delta.tool_calls as Array<Record<string, unknown>>,\n activeToolCall,\n );\n events.push(...tcResult.events);\n activeToolCall = tcResult.activeCall;\n }\n\n return { events, state: { textIndex, reasoningIndex, activeToolCall }, done: false };\n}\n\n// ── Provider ───────────────────────────────────────\n\n/**\n * Create an OpenAI provider instance.\n *\n * Same SSE parsing logic as DeepSeek — both follow the same wire protocol.\n * The only differences are the base URL, models, and capability metadata.\n */\nexport function createOpenAiProvider(config: OpenAiConfig): LlmProvider {\n const baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;\n const provider: LlmProvider = {\n id: \"openai\",\n\n listModels(): ModelInfo[] {\n return MODELS;\n },\n\n getCapability(modelId: string): ProviderCapability {\n const cap = getCapability(\"openai\", modelId);\n if (!cap) {\n throw new LlmError(`Unknown OpenAI model: ${modelId}`, {\n category: \"llm\",\n recoverable: false,\n retryable: false,\n diagnosticHint: \"llm_unknown_model\",\n });\n }\n return cap;\n },\n\n async *stream(\n ...[modelId, messages, systemPrompt, tools, signal]: Parameters<LlmProvider[\"stream\"]>\n ): AsyncGenerator<StreamEvent, void, void> {\n const systemMessages = systemPrompt ? [{ role: \"system\", content: systemPrompt }] : [];\n\n const body = {\n model: modelId,\n messages: [...systemMessages, ...normalizeOpenAiMessages(messages)],\n stream: true,\n ...(tools.length > 0 ? { tools: normalizeOpenAiTools(tools) } : {}),\n };\n\n async function makeRequest(): Promise<Response> {\n const controller = new AbortController();\n const onAbort = () => controller.abort();\n signal.addEventListener(\"abort\", onAbort, { once: true });\n\n try {\n const response = await fetch(`${baseUrl}/v1/chat/completions`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${config.apiKey}`,\n },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n await handleHttpError(response);\n }\n\n return response;\n } finally {\n signal.removeEventListener(\"abort\", onAbort);\n }\n }\n\n const response = await withRetry(makeRequest, {\n baseMs: 500,\n maxMs: 32_000,\n });\n\n if (!response.body) {\n throw new LlmError(\"OpenAI returned empty response body\", {\n category: \"llm\",\n recoverable: true,\n retryable: true,\n });\n }\n\n // Parse SSE stream (same logic as DeepSeek, extracted for clarity)\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n let textIndex = 0;\n let reasoningIndex = 0;\n let activeToolCall: { id: string; name: string; buffer: string } | null = null;\n\n try {\n while (true) {\n if (signal.aborted) break;\n\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() ?? \"\";\n\n for (const line of lines) {\n const result = processSseLine(line, { activeToolCall, textIndex, reasoningIndex });\n for (const event of result.events) yield event;\n if (result.done) return;\n activeToolCall = result.state.activeToolCall;\n textIndex = result.state.textIndex;\n reasoningIndex = result.state.reasoningIndex;\n }\n }\n\n if (activeToolCall) {\n for (const event of flushActiveToolCall(activeToolCall)) yield event;\n }\n yield { type: \"done\" };\n } finally {\n reader.releaseLock();\n }\n },\n };\n\n return provider;\n}\n\n// ── HTTP error mapper ──────────────────────────────\n\nasync function handleHttpError(response: Response): Promise<never> {\n let body: string;\n try {\n body = await response.text();\n } catch {\n body = \"\";\n }\n\n const status = response.status;\n\n if (status === 401 || status === 403) {\n throw new LlmAuthError(`OpenAI auth failed (${status}): ${body}`);\n }\n\n if (status === 429) {\n throw new LlmRateLimitError(`OpenAI rate limited (429): ${body}`);\n }\n\n if (status === 529) {\n throw new LlmRateLimitError(`OpenAI overloaded (529): ${body}`, 529);\n }\n\n if (status === 400 && body.includes(\"context\")) {\n throw new LlmContextOverflowError(`OpenAI context overflow: ${body}`);\n }\n\n throw new LlmError(`OpenAI HTTP ${status}: ${body}`, {\n category: \"llm\",\n recoverable: status >= 500,\n retryable: status >= 500 || status === 429,\n });\n}\n","/**\n * Anthropic provider — SSE streaming via the /v1/messages endpoint.\n *\n * Translates Lynx's OpenAI‑compatible internal format to Anthropic's\n * native message/tool format, then maps Anthropic SSE events back to\n * Lynx StreamEvent types. This is the reverse of what Claude Code's\n * Codex adapter does (Anthropic → OpenAI translation).\n */\n\nimport { LlmError, LlmRateLimitError, LlmAuthError, LlmContextOverflowError } from \"@24klynx/core\";\nimport type { LlmProvider, ModelInfo, ProviderCapability, StreamEvent } from \"../types.js\";\nimport { getCapability } from \"../capabilities.js\";\nimport { withRetry } from \"../retry.js\";\n\n// ── Configuration ──────────────────────────────────\n\nexport interface AnthropicConfig {\n /** API key from ANTHROPIC_API_KEY env var. */\n apiKey: string;\n /** Base URL (defaults to https://api.anthropic.com). */\n baseUrl?: string;\n /** Request timeout in ms (default 120s). */\n timeoutMs?: number;\n}\n\nconst DEFAULT_BASE_URL = \"https://api.anthropic.com\";\nconst ANTHROPIC_VERSION = \"2023-06-01\";\nconst DEFAULT_TIMEOUT_MS = 120_000;\n\n// ── Models ─────────────────────────────────────────\n\nconst MODELS: ModelInfo[] = [\n {\n id: \"claude-opus-4-8\",\n label: \"Claude Opus 4.8\",\n contextWindow: 200_000,\n maxOutput: 32_000,\n },\n {\n id: \"claude-sonnet-4-6\",\n label: \"Claude Sonnet 4.6\",\n contextWindow: 200_000,\n maxOutput: 16_384,\n },\n {\n id: \"claude-haiku-4-5\",\n label: \"Claude Haiku 4.5\",\n contextWindow: 200_000,\n maxOutput: 8_192,\n },\n];\n\n// ── Wire‑format types ──────────────────────────────\n\n/** Anthropic content block types. */\ntype AnthropicContentBlock =\n | { type: \"text\"; text: string }\n | { type: \"tool_use\"; id: string; name: string; input: Record<string, unknown> }\n | { type: \"tool_result\"; tool_use_id: string; content: string; is_error?: boolean };\n\n/** Anthropic message format. */\ninterface AnthropicMessage {\n role: \"user\" | \"assistant\";\n content: string | AnthropicContentBlock[];\n}\n\n/** Anthropic tool definition. */\ninterface AnthropicTool {\n name: string;\n description: string;\n input_schema: Record<string, unknown>;\n}\n\n/** Internal type for message content blocks arriving via Lynx ChatMessage. */\ninterface LynxContentBlock {\n type: string;\n text?: string;\n id?: string;\n name?: string;\n input?: Record<string, unknown>;\n toolUseId?: string;\n content?: string;\n isError?: boolean;\n}\n\n/** OpenAI‑format tool_calls entry. */\ninterface OpenAiToolCall {\n id: string;\n type: \"function\";\n function: { name: string; arguments: string };\n}\n\n/** Lynx ChatMessage shape (what normalizeOpenAiMessages maps TO, so we receive FROM). */\ninterface LynxChatMessage {\n role: \"system\" | \"user\" | \"assistant\" | \"tool\";\n content: string | LynxContentBlock[] | null;\n tool_call_id?: string;\n tool_calls?: OpenAiToolCall[];\n}\n\n// ── Translation: Lynx messages → Anthropic messages ─\n\n/**\n * Convert Lynx OpenAI‑compatible ChatMessage[] to Anthropic Message[].\n * System‑role messages are extracted into a separate string array\n * (Anthropic treats system as a top‑level parameter, not a message).\n */\nfunction translateMessages(chatMessages: LynxChatMessage[]): {\n messages: AnthropicMessage[];\n systemLines: string[];\n} {\n const messages: AnthropicMessage[] = [];\n const systemLines: string[] = [];\n\n for (const msg of chatMessages) {\n if (msg.role === \"system\") {\n if (typeof msg.content === \"string\") {\n systemLines.push(msg.content);\n } else if (Array.isArray(msg.content)) {\n for (const block of msg.content) {\n if (block.type === \"text\" && block.text) systemLines.push(block.text);\n }\n }\n continue;\n }\n\n if (msg.role === \"user\") {\n messages.push({ role: \"user\", content: translateUserContent(msg) });\n } else if (msg.role === \"assistant\") {\n messages.push({ role: \"assistant\", content: translateAssistantContent(msg) });\n } else if (msg.role === \"tool\") {\n // OpenAI tool‑role → Anthropic user message with tool_result block\n messages.push({\n role: \"user\",\n content: [\n {\n type: \"tool_result\",\n tool_use_id: msg.tool_call_id ?? \"unknown\",\n content: typeof msg.content === \"string\" ? msg.content : JSON.stringify(msg.content),\n },\n ],\n });\n }\n }\n\n return { messages, systemLines };\n}\n\n/** Convert a Lynx user message to Anthropic content. */\nfunction translateUserContent(msg: LynxChatMessage): string | AnthropicContentBlock[] {\n if (typeof msg.content === \"string\") return msg.content;\n if (!msg.content || msg.content.length === 0) return \"\";\n\n const blocks: AnthropicContentBlock[] = [];\n for (const block of msg.content as LynxContentBlock[]) {\n if (block.type === \"text\" && block.text) {\n blocks.push({ type: \"text\", text: block.text });\n } else if (block.type === \"tool_result\") {\n blocks.push({\n type: \"tool_result\",\n tool_use_id: block.toolUseId ?? \"unknown\",\n content: typeof block.content === \"string\" ? block.content : JSON.stringify(block.content),\n is_error: block.isError,\n });\n }\n }\n return blocks.length > 0 ? blocks : \"\";\n}\n\n/** Convert a Lynx assistant message to Anthropic content. */\nfunction translateAssistantContent(msg: LynxChatMessage): string | AnthropicContentBlock[] {\n const blocks: AnthropicContentBlock[] = [];\n\n // Text blocks from content array\n if (Array.isArray(msg.content)) {\n for (const block of msg.content as LynxContentBlock[]) {\n if (block.type === \"text\" && block.text) {\n blocks.push({ type: \"text\", text: block.text });\n } else if (block.type === \"tool_use\" && block.id && block.name) {\n blocks.push({\n type: \"tool_use\",\n id: block.id,\n name: block.name,\n input: block.input ?? {},\n });\n }\n }\n } else if (typeof msg.content === \"string\" && msg.content) {\n blocks.push({ type: \"text\", text: msg.content });\n }\n\n // Tool calls from OpenAI format\n if (msg.tool_calls) {\n for (const tc of msg.tool_calls) {\n let input: Record<string, unknown> = {};\n try {\n input = JSON.parse(tc.function.arguments);\n } catch {\n input = {};\n }\n blocks.push({\n type: \"tool_use\",\n id: tc.id,\n name: tc.function.name,\n input,\n });\n }\n }\n\n return blocks.length > 0 ? blocks : \"\";\n}\n\n// ── Translation: Lynx tools → Anthropic tools ──────\n\n/** Convert Lynx/OpenAI‑format tool descriptors to Anthropic format. */\nfunction translateTools(tools: unknown[]): AnthropicTool[] {\n return tools.map((tool) => {\n const raw = tool as Record<string, unknown>;\n return {\n name: (raw.name as string) ?? \"unknown\",\n description: (raw.description as string) ?? \"\",\n input_schema:\n (raw.inputSchema as Record<string, unknown>) ??\n ((raw.function as Record<string, unknown>)?.parameters as Record<string, unknown>) ??\n {},\n };\n });\n}\n\n// ── SSE streaming parser ───────────────────────────\n\n/** State for tracking streaming content blocks. */\ninterface StreamState {\n activeToolCall: { id: string; name: string; buffer: string } | null;\n activeToolCallIndex: number;\n textIndex: number;\n toolUseCount: number;\n /** Set of block indices that are tool_use blocks (to detect when text blocks end). */\n toolUseIndices: Set<number>;\n}\n\n/**\n * Parse a single Anthropic SSE event line and return mapped Lynx events.\n * Anthropic SSE format: `event: <type>\\ndata: <json>\\n\\n`\n */\nfunction processAnthropicEvent(\n eventType: string,\n data: string,\n state: StreamState,\n): { events: StreamEvent[]; state: StreamState } {\n const events: StreamEvent[] = [];\n let nextState = state;\n\n if (eventType === \"message_start\") {\n return { events, state: nextState };\n }\n\n if (eventType === \"ping\") {\n return { events, state: nextState };\n }\n\n if (eventType === \"content_block_start\") {\n const parsed = JSON.parse(data) as {\n type: \"content_block_start\";\n index: number;\n content_block: { type: string; id?: string; name?: string };\n };\n const block = parsed.content_block;\n\n if (block.type === \"tool_use\" && block.id && block.name) {\n nextState = {\n ...state,\n activeToolCall: { id: block.id, name: block.name, buffer: \"\" },\n activeToolCallIndex: parsed.index,\n toolUseIndices: new Set(state.toolUseIndices).add(parsed.index),\n };\n events.push({ type: \"tool_use_start\", callId: block.id, name: block.name });\n }\n // text block start — no event needed, deltas carry the content\n return { events, state: nextState };\n }\n\n if (eventType === \"content_block_delta\") {\n const parsed = JSON.parse(data) as {\n type: \"content_block_delta\";\n index: number;\n delta: { type: string; text?: string; partial_json?: string; thinking?: string };\n };\n const delta = parsed.delta;\n\n if (delta.type === \"text_delta\" && delta.text) {\n events.push({\n type: \"text_delta\",\n index: state.textIndex++,\n text: delta.text,\n });\n } else if (delta.type === \"input_json_delta\" && delta.partial_json && state.activeToolCall) {\n nextState = {\n ...state,\n activeToolCall: {\n ...state.activeToolCall,\n buffer: state.activeToolCall.buffer + delta.partial_json,\n },\n };\n events.push({\n type: \"tool_use_delta\",\n callId: state.activeToolCall.id,\n text: delta.partial_json,\n });\n } else if (delta.type === \"thinking_delta\" && delta.thinking) {\n events.push({\n type: \"reasoning_delta\",\n index: 0,\n text: delta.thinking,\n });\n }\n\n return { events, state: nextState };\n }\n\n if (eventType === \"content_block_stop\") {\n // Parse accumulated JSON for active tool call on this block index\n const parsed = JSON.parse(data) as { type: \"content_block_stop\"; index: number };\n if (\n state.activeToolCall &&\n parsed.index === state.activeToolCallIndex &&\n state.activeToolCall.buffer\n ) {\n try {\n const input = JSON.parse(state.activeToolCall.buffer);\n events.push({\n type: \"tool_use_end\",\n callId: state.activeToolCall.id,\n input: input as Record<string, unknown>,\n });\n nextState = {\n ...state,\n toolUseCount: state.toolUseCount + 1,\n activeToolCall: null,\n };\n } catch {\n events.push({\n type: \"error\",\n code: \"TOOL_PARSE_ERROR\",\n message: `解析 ${state.activeToolCall.name} 的工具输入失败`,\n });\n }\n }\n return { events, state: nextState };\n }\n\n if (eventType === \"message_delta\") {\n return { events, state: nextState };\n }\n\n if (eventType === \"message_stop\") {\n events.push({ type: \"done\" });\n return { events, state: nextState };\n }\n\n return { events, state: nextState };\n}\n\n// ── Provider factory ───────────────────────────────\n\n/**\n * Create an Anthropic provider instance.\n *\n * Each call to `stream()` opens a fresh HTTP connection.\n * The provider itself is stateless — the API key is the only configuration.\n */\nexport function createAnthropicProvider(config: AnthropicConfig): LlmProvider {\n const baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;\n const timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const apiKey = config.apiKey;\n\n const provider: LlmProvider = {\n id: \"anthropic\",\n\n listModels(): ModelInfo[] {\n return MODELS;\n },\n\n getCapability(modelId: string): ProviderCapability {\n const cap = getCapability(\"anthropic\", modelId);\n if (!cap) {\n throw new LlmError(`未知 Anthropic 模型: ${modelId}`, {\n category: \"llm\",\n recoverable: false,\n retryable: false,\n diagnosticHint: \"llm_unknown_model\",\n });\n }\n return cap;\n },\n\n async *stream(\n ...[modelId, messages, systemPrompt, tools, signal]: Parameters<LlmProvider[\"stream\"]>\n ): AsyncGenerator<StreamEvent, void, void> {\n // Translate system prompt + system messages → Anthropic system parameter\n const translated = translateMessages(messages as LynxChatMessage[]);\n const allSystemLines = systemPrompt\n ? [systemPrompt, ...translated.systemLines]\n : translated.systemLines;\n const systemParam = allSystemLines.length > 0 ? allSystemLines.join(\"\\n\\n\") : undefined;\n\n const body = {\n model: modelId,\n max_tokens: 8192,\n messages: translated.messages,\n ...(systemParam ? { system: systemParam } : {}),\n ...(tools.length > 0 ? { tools: translateTools(tools) } : {}),\n stream: true,\n };\n\n async function makeRequest(): Promise<Response> {\n const controller = new AbortController();\n const onAbort = () => controller.abort();\n signal.addEventListener(\"abort\", onAbort, { once: true });\n\n try {\n const response = await fetch(`${baseUrl}/v1/messages`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-api-key\": apiKey,\n \"anthropic-version\": ANTHROPIC_VERSION,\n },\n body: JSON.stringify(body),\n signal: AbortSignal.any\n ? AbortSignal.any([signal, controller.signal])\n : controller.signal,\n });\n\n if (!response.ok) {\n await handleHttpError(response);\n }\n\n return response;\n } finally {\n signal.removeEventListener(\"abort\", onAbort);\n }\n }\n\n const response = await withRetry(makeRequest, {\n baseMs: 500,\n maxMs: 32_000,\n });\n\n if (!response.body) {\n throw new LlmError(\"Anthropic 返回了空响应体\", {\n category: \"llm\",\n recoverable: true,\n retryable: true,\n });\n }\n\n // Parse Anthropic SSE stream\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n let eventType = \"\";\n let state: StreamState = {\n activeToolCall: null,\n activeToolCallIndex: -1,\n textIndex: 0,\n toolUseCount: 0,\n toolUseIndices: new Set(),\n };\n\n try {\n while (true) {\n if (signal.aborted) break;\n\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n\n // Process complete SSE events — Anthropic uses two-line format:\n // event: <type>\n // data: <json>\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() ?? \"\"; // keep incomplete line\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (trimmed.startsWith(\"event: \")) {\n eventType = trimmed.slice(7).trim();\n } else if (trimmed.startsWith(\"data: \") && eventType) {\n const data = trimmed.slice(6).trim();\n const result = processAnthropicEvent(eventType, data, state);\n for (const event of result.events) yield event;\n state = result.state;\n }\n }\n }\n\n // Stream ended without message_stop\n if (state.activeToolCall && state.activeToolCall.buffer) {\n try {\n const input = JSON.parse(state.activeToolCall.buffer);\n yield {\n type: \"tool_use_end\",\n callId: state.activeToolCall.id,\n input: input as Record<string, unknown>,\n };\n } catch {\n // ignore — partial tool call at stream end\n }\n }\n yield { type: \"done\" };\n } finally {\n reader.releaseLock();\n }\n },\n };\n\n return provider;\n}\n\n// ── HTTP error mapper ──────────────────────────────\n\nasync function handleHttpError(response: Response): Promise<never> {\n let body: string;\n try {\n body = await response.text();\n } catch {\n body = \"\";\n }\n\n const status = response.status;\n\n if (status === 401 || status === 403) {\n throw new LlmAuthError(`Anthropic 认证失败 (${status}): ${body}`);\n }\n\n if (status === 429) {\n throw new LlmRateLimitError(`Anthropic 速率限制 (429): ${body}`);\n }\n\n if (status === 529) {\n const err = new LlmRateLimitError(`Anthropic 过载 (529): ${body}`);\n (err as unknown as Record<string, unknown>).status = 529;\n throw err;\n }\n\n if (status === 400 && body.includes(\"context\")) {\n throw new LlmContextOverflowError(`Anthropic 上下文溢出: ${body}`);\n }\n\n throw new LlmError(`Anthropic HTTP ${status}: ${body}`, {\n category: \"llm\",\n recoverable: status >= 500,\n retryable: status >= 500 || status === 429,\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["anyErr","DEFAULT_BASE_URL","MODELS","handleHttpError","DEFAULT_BASE_URL","MODELS","handleHttpError"],"sources":["../src/stream.ts","../src/retry.ts","../src/capabilities.ts","../src/fallback.ts","../src/tool-format.ts","../src/providers/sse-parser.ts","../src/providers/deepseek.ts","../src/providers/openai.ts","../src/providers/anthropic.ts"],"sourcesContent":["/**\n * Stream\\<T\\> — pull‑based AsyncIterator backed by an internal queue.\n *\n * Producers call `enqueue()` / `done()` / `error()`.\n * Consumers use `for await … of`.\n *\n * This is deliberately a class (not EventEmitter) so the consumer\n * controls back‑pressure — when nobody is iterating, values stay in\n * the queue until `maxQueueSize` is reached.\n */\n\n// ── Constants ──────────────────────────────────────\n\n/** Drop the oldest item when the queue exceeds this size. */\nconst DEFAULT_MAX_QUEUE = 256;\n\n// ── Public API ─────────────────────────────────────\n\n/**\n * A cold, single‑consumer async iterator.\n *\n * ```ts\n * const stream = new Stream<string>();\n * producer(stream);\n * for await (const chunk of stream) {\n * console.log(chunk);\n * }\n * ```\n */\nexport class Stream<T> implements AsyncIterator<T>, AsyncIterable<T> {\n private _queue: T[] = [];\n private _resolvers: Array<(value: IteratorResult<T>) => void> = [];\n private _done = false;\n private _error: Error | null = null;\n private readonly _maxQueue: number;\n\n constructor(maxQueueSize = DEFAULT_MAX_QUEUE) {\n this._maxQueue = maxQueueSize;\n }\n\n // ── Producer API ────────────────────────────────\n\n /** Push a value into the stream. */\n enqueue(value: T): void {\n if (this._done || this._error) return;\n\n if (this._resolvers.length > 0) {\n const resolve = this._resolvers.shift()!;\n resolve({ value, done: false });\n } else {\n this._queue.push(value);\n // Prevent unbounded growth\n if (this._queue.length > this._maxQueue) {\n this._queue.shift();\n }\n }\n }\n\n /** Signal that the stream has finished normally. */\n done(): void {\n if (this._done) return;\n this._done = true;\n // Resolve all pending awaiters with done: true\n for (const resolve of this._resolvers) {\n resolve({ value: undefined, done: true });\n }\n this._resolvers = [];\n }\n\n /** Propagate an error to the consumer. */\n error(err: Error): void {\n if (this._done || this._error) return;\n this._error = err;\n this._done = true;\n // The error is thrown from next(), but we can't throw through pending\n // resolvers. We resolve them with done: true and set the error flag\n // so the next next() call will throw.\n for (const resolve of this._resolvers) {\n resolve({ value: undefined, done: true });\n }\n this._resolvers = [];\n }\n\n // ── Consumer API ────────────────────────────────\n\n [Symbol.asyncIterator](): AsyncIterator<T> {\n return this;\n }\n\n next(): Promise<IteratorResult<T>> {\n // If the stream errored, reject\n if (this._error) {\n return Promise.reject(this._error);\n }\n\n // If we're done and the queue is empty, return done\n if (this._done && this._queue.length === 0) {\n return Promise.resolve({ value: undefined, done: true });\n }\n\n // If there's a queued value, return it immediately\n if (this._queue.length > 0) {\n const value = this._queue.shift()!;\n return Promise.resolve({ value, done: false });\n }\n\n // Wait for the next enqueue/done/error\n return new Promise((resolve) => {\n this._resolvers.push(resolve);\n });\n }\n}\n","/**\n * withRetry — exponential backoff with jitter and Retry‑After respect.\n *\n * Used by every LLM provider for transient errors (429, 529, ECONNRESET, etc.).\n *\n * Algorithm:\n * delay = min(baseMs * 2^(attempt-1), maxMs)\n * jitter = delay * uniform(0.75, 1.25)\n * final = min(jitter, maxMs)\n */\n\nimport { LlmError, LlmRateLimitError } from \"@24klynx/core\";\n\n// ── Constants ──────────────────────────────────────\n\nconst BASE_DELAY_MS = 500;\nconst MAX_DELAY_MS = 32_000;\nconst MAX_RETRIES = 10;\n\n// ── Types ──────────────────────────────────────────\n\nexport interface RetryOptions {\n /** Base delay for the first retry (default 500ms). */\n baseMs?: number;\n /** Hard ceiling on delay (default 32s). */\n maxMs?: number;\n /** Maximum number of retries before giving up (default 10). */\n maxRetries?: number;\n /** Function that returns true if this error is retryable. */\n isRetryable?: (err: Error) => boolean;\n /**\n * Whether this request is in the foreground (user-facing).\n * Foreground requests retry on 529; background requests discard\n * immediately to prevent cascade amplification. Defaults to true.\n */\n isForeground?: boolean;\n}\n\n// ── Helpers ────────────────────────────────────────\n\n/** Uniform random in [lo, hi]. */\nfunction jitter(lo: number, hi: number): number {\n return lo + Math.random() * (hi - lo);\n}\n\n/** Extract Retry‑After seconds from HTTP headers if present. */\nfunction parseRetryAfter(err: Error): number | null {\n const anyErr = err as Error & { headers?: unknown };\n const headers = anyErr.headers as Record<string, string> | undefined;\n if (!headers) return null;\n\n const raw = headers[\"retry-after\"];\n if (!raw) return null;\n\n // Try integer seconds first\n const secs = Number.parseInt(raw, 10);\n if (!Number.isNaN(secs)) return secs;\n\n // Try HTTP‑date (rare but valid)\n const parsed = Date.parse(raw);\n if (!Number.isNaN(parsed)) {\n return Math.max(0, Math.ceil((parsed - Date.now()) / 1000));\n }\n\n return null;\n}\n\n// ── Public API ─────────────────────────────────────\n\n/**\n * Execute `fn` with exponential backoff.\n *\n * Only retries on transient errors. Non‑retryable errors are re‑thrown\n * immediately.\n *\n * ```ts\n * const result = await withRetry(() => fetchFromApi(), {\n * baseMs: 500,\n * maxMs: 32_000,\n * });\n * ```\n */\nexport async function withRetry<T>(fn: () => Promise<T>, opts: RetryOptions = {}): Promise<T> {\n const baseMs = opts.baseMs ?? BASE_DELAY_MS;\n const maxMs = opts.maxMs ?? MAX_DELAY_MS;\n const maxRetries = opts.maxRetries ?? MAX_RETRIES;\n const isRetryable =\n opts.isRetryable ??\n ((err) => {\n if (err instanceof LlmRateLimitError) return true;\n // Always retry transient errors\n if (err instanceof LlmError && err.retryable) return true;\n // Retry network errors\n const code = (err as NodeJS.ErrnoException).code;\n if (code === \"ECONNRESET\" || code === \"EPIPE\" || code === \"ETIMEDOUT\") return true;\n return false;\n });\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n return await fn();\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n\n // If this is the last attempt or the error is not retryable, give up\n if (attempt === maxRetries || !isRetryable(error)) {\n throw error;\n }\n\n // 529 overload — only retry if this is in the foreground.\n // Background tasks drop to prevent cascade amplification.\n const anyErr = error as Error & { status?: number };\n if (anyErr.status === 529 && opts.isForeground === false) {\n throw error;\n }\n\n // Respect Retry‑After header\n const retryAfter = parseRetryAfter(error);\n const delay = retryAfter ? retryAfter * 1000 : Math.min(baseMs * Math.pow(2, attempt), maxMs);\n\n const jittered = Math.min(delay * jitter(0.75, 1.25), maxMs);\n\n await new Promise((resolve) => setTimeout(resolve, jittered));\n }\n }\n\n // Unreachable — the loop above always throws or returns\n throw new LlmError(\"withRetry exhausted all attempts\", {\n category: \"llm\",\n recoverable: false,\n retryable: false,\n diagnosticHint: \"llm_retry_exhausted\",\n });\n}\n","/**\n * Provider capability metadata table.\n *\n * Every model known to Lynx is registered here with its static capabilities.\n * The router uses this table to decide which model can handle a request\n * (e.g. a request with images requires `supportsVision: true`).\n */\n\nimport type { ProviderCapability } from \"./types.js\";\n\n/**\n * Lookup table keyed by \"provider/modelId\".\n *\n * New models can be added here without code changes —\n * capabilities are pure data, not behaviour.\n */\nconst CAPABILITIES: Record<string, ProviderCapability> = {\n // DeepSeek\n \"deepseek/deepseek-chat\": {\n contextWindow: 128_000,\n maxOutput: 8_192,\n supportsReasoning: false,\n supportsStreaming: true,\n supportsToolUse: true,\n supportsVision: false,\n promptCacheEnabled: true, // implicit prefix caching\n },\n \"deepseek/deepseek-reasoner\": {\n contextWindow: 128_000,\n maxOutput: 8_192,\n supportsReasoning: true,\n supportsStreaming: true,\n supportsToolUse: false,\n supportsVision: false,\n promptCacheEnabled: true,\n },\n\n // OpenAI\n \"openai/gpt-4o\": {\n contextWindow: 128_000,\n maxOutput: 16_384,\n supportsReasoning: false,\n supportsStreaming: true,\n supportsToolUse: true,\n supportsVision: true,\n promptCacheEnabled: false, // No prompt caching for OpenAI (as of now)\n },\n \"openai/gpt-4o-mini\": {\n contextWindow: 128_000,\n maxOutput: 16_384,\n supportsReasoning: false,\n supportsStreaming: true,\n supportsToolUse: true,\n supportsVision: true,\n promptCacheEnabled: false,\n },\n \"openai/o3-mini\": {\n contextWindow: 200_000,\n maxOutput: 100_000,\n supportsReasoning: true,\n supportsStreaming: true,\n supportsToolUse: false,\n supportsVision: false,\n promptCacheEnabled: false,\n },\n\n // Anthropic\n \"anthropic/claude-opus-4-8\": {\n contextWindow: 200_000,\n maxOutput: 32_000,\n supportsReasoning: true,\n supportsStreaming: true,\n supportsToolUse: true,\n supportsVision: true,\n promptCacheEnabled: true,\n },\n \"anthropic/claude-sonnet-4-6\": {\n contextWindow: 200_000,\n maxOutput: 16_384,\n supportsReasoning: true,\n supportsStreaming: true,\n supportsToolUse: true,\n supportsVision: true,\n promptCacheEnabled: true,\n },\n \"anthropic/claude-haiku-4-5\": {\n contextWindow: 200_000,\n maxOutput: 8_192,\n supportsReasoning: false,\n supportsStreaming: true,\n supportsToolUse: true,\n supportsVision: true,\n promptCacheEnabled: true,\n },\n\n // DeepSeek via Anthropic-compatible endpoint (api.deepseek.com/anthropic)\n \"anthropic/deepseek-v4-pro\": {\n contextWindow: 200_000,\n maxOutput: 32_000,\n supportsReasoning: true,\n supportsStreaming: true,\n supportsToolUse: true,\n supportsVision: false,\n promptCacheEnabled: true,\n },\n};\n\n/**\n * Return the capability blob for `provider/modelId`.\n * Returns `undefined` for unknown models — callers should fall back\n * or throw a {@link import('@24klynx/core').ConfigError}.\n */\nexport function getCapability(providerId: string, modelId: string): ProviderCapability | undefined {\n return CAPABILITIES[`${providerId}/${modelId}`];\n}\n\n/**\n * Return every known capability entry.\n * Used by the model picker UI in the TUI layer.\n */\nexport function listCapabilities(): Array<{ key: string } & ProviderCapability> {\n return Object.entries(CAPABILITIES).map(([key, cap]) => ({ key, ...cap }));\n}\n","/**\n * Model/Provider fallback logic.\n *\n * When a provider returns a transient error (rate‑limit, overload) the\n * fallback engine tries the next candidate in the configured chain.\n * Permanent errors (auth) skip the provider entirely and move to the next.\n *\n * The primary use‑case is keeping the agent loop running when the\n * preferred model is temporarily unavailable.\n */\n\nimport { LlmRateLimitError, LlmAuthError } from \"@24klynx/core\";\n\n// ── Types ────────────────────────────────────────────\n\n/** A candidate provider+model pair in the fallback chain. */\nexport interface ModelCandidate {\n provider: string;\n model: string;\n}\n\n/** Record of a single fallback attempt for diagnostics. */\nexport interface FallbackAttempt {\n provider: string;\n model: string;\n error: string;\n code?: string;\n /** HTTP status code if available. */\n status?: number;\n}\n\n/** Result of a fallback run — which candidate succeeded and what was tried. */\nexport interface FallbackResult<T> {\n result: T;\n provider: string;\n model: string;\n attempts: FallbackAttempt[];\n}\n\n/** Error thrown when every candidate in the chain has been exhausted. */\nexport class FallbackExhaustedError extends Error {\n readonly attempts: FallbackAttempt[];\n\n constructor(attempts: FallbackAttempt[]) {\n const summary = attempts.map((a) => ` ${a.provider}/${a.model}: ${a.error}`).join(\"\\n\");\n super(`All fallback candidates exhausted:\\n${summary}`);\n this.name = \"FallbackExhaustedError\";\n this.attempts = attempts;\n }\n}\n\n// ── Helpers ──────────────────────────────────────────\n\n/**\n * Determine whether an error is transient (worth retrying with a\n * different provider) or permanent (skip this provider).\n */\nfunction isTransient(err: unknown): boolean {\n if (err instanceof LlmRateLimitError) return true;\n if (err instanceof LlmAuthError) return false;\n // Network errors are transient\n if (err instanceof Error && \"code\" in err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === \"ECONNRESET\" || code === \"EPIPE\" || code === \"ETIMEDOUT\" || code === \"ENOTFOUND\") {\n return true;\n }\n }\n // Default: treat unknown errors as transient (safer to try another provider)\n return true;\n}\n\n// ── Public API ───────────────────────────────────────\n\n/**\n * Run `fn` with automatic fallback across a chain of model candidates.\n *\n * `fn` is called with (provider, model) and should return a result.\n * If `fn` throws, the error is classified — transient errors move to the\n * next candidate, permanent errors skip the provider, and fatal errors\n * (like user abort) are re‑thrown immediately.\n */\nexport async function runWithFallback<T>(\n candidates: ModelCandidate[],\n fn: (candidate: ModelCandidate) => Promise<T>,\n opts?: { signal?: AbortSignal },\n): Promise<FallbackResult<T>> {\n if (candidates.length === 0) {\n throw new FallbackExhaustedError([]);\n }\n\n const attempts: FallbackAttempt[] = [];\n\n for (const candidate of candidates) {\n // Respect external abort signal\n if (opts?.signal?.aborted) {\n throw new DOMException(\"Aborted\", \"AbortError\");\n }\n\n try {\n const result = await fn(candidate);\n return { result, provider: candidate.provider, model: candidate.model, attempts };\n } catch (err) {\n // User‑initiated abort — stop immediately\n if (err instanceof DOMException && err.name === \"AbortError\") {\n throw err;\n }\n\n const message = err instanceof Error ? err.message : String(err);\n const status = (err as Record<string, unknown>).status as number | undefined;\n const code = (err as Record<string, unknown>).code as string | undefined;\n\n attempts.push({\n provider: candidate.provider,\n model: candidate.model,\n error: message,\n code,\n status,\n });\n\n // Permanent errors — skip this provider, try next\n if (!isTransient(err)) {\n continue;\n }\n\n // Transient — try next candidate\n continue;\n }\n }\n\n throw new FallbackExhaustedError(attempts);\n}\n\n/**\n * Build a fallback chain from a primary model and a list of fallback refs.\n *\n * Fallback refs use the format `\"provider/model\"` (e.g. `\"openai/gpt-4o-mini\"`).\n * If the provider prefix is omitted the primary's provider is used.\n */\nexport function buildCandidateChain(\n primaryProvider: string,\n primaryModel: string,\n fallbackRefs: string[],\n): ModelCandidate[] {\n const chain: ModelCandidate[] = [{ provider: primaryProvider, model: primaryModel }];\n\n for (const ref of fallbackRefs) {\n const slashIdx = ref.indexOf(\"/\");\n if (slashIdx > 0) {\n chain.push({ provider: ref.slice(0, slashIdx), model: ref.slice(slashIdx + 1) });\n } else {\n chain.push({ provider: primaryProvider, model: ref });\n }\n }\n\n return chain;\n}\n","/**\n * Wire-format normalization for OpenAI-compatible APIs.\n *\n * Lynx uses its own internal types ({@link import('@24klynx/tools').ToolDescriptor},\n * {@link import('@24klynx/core').ContentBlock}) that don't exactly match the\n * OpenAI/DeepSeek wire format. This module maps between them.\n */\n\nimport type { ChatMessage } from \"./types.js\";\n\n// ── Tool normalization ────────────────────────────\n\n/** Raw shape we receive from the agent (ToolDescriptor-like). */\ninterface RawTool {\n name?: unknown;\n description?: unknown;\n inputSchema?: unknown;\n}\n\n/** OpenAI-compatible tool definition. */\ninterface OpenAiTool {\n type: \"function\";\n function: {\n name: string;\n description: string;\n parameters: Record<string, unknown>;\n };\n}\n\n/**\n * Convert Lynx tool descriptors to OpenAI-compatible format.\n *\n * Wraps each tool with `type: \"function\"` and maps `inputSchema` → `parameters`.\n * Tools already in OpenAI format pass through unchanged.\n */\nexport function normalizeOpenAiTools(tools: unknown[]): OpenAiTool[] {\n return tools.map((tool) => {\n const raw = tool as RawTool;\n if (typeof raw === \"object\" && raw !== null && (raw as { type?: string }).type === \"function\") {\n return tool as OpenAiTool;\n }\n return {\n type: \"function\" as const,\n function: {\n name: String(raw.name ?? \"unknown\"),\n description: String(raw.description ?? \"\"),\n parameters: (raw.inputSchema as Record<string, unknown>) ?? {},\n },\n };\n });\n}\n\n// ── Wire-format message types ─────────────────────\n\n/** A message in OpenAI wire format — what the API actually expects. */\nexport interface OpenAiMessage {\n role: \"system\" | \"user\" | \"assistant\" | \"tool\";\n content: string | OpenAiContentBlock[] | null;\n tool_call_id?: string;\n tool_calls?: Array<{\n id: string;\n type: \"function\";\n function: { name: string; arguments: string };\n }>;\n}\n\ninterface OpenAiContentBlock {\n type: \"text\";\n text: string;\n}\n\n// ── Content block types for recognition ───────────\n\ninterface LynxContentBlock {\n type: string;\n text?: string;\n id?: string;\n name?: string;\n input?: Record<string, unknown>;\n toolUseId?: string;\n content?: string;\n isError?: boolean;\n}\n\n// ── Message normalization ─────────────────────────\n\n/**\n * Convert Lynx ChatMessage[] to OpenAI wire-format messages.\n *\n * Key transformations:\n * - `tool_result` content blocks → `role: \"tool\"` messages with string content\n * - `tool_use` blocks in assistant messages → `tool_calls` array\n * - `reasoning` blocks are stripped (not sent back to the API)\n */\nexport function normalizeOpenAiMessages(messages: ChatMessage[]): OpenAiMessage[] {\n const out: OpenAiMessage[] = [];\n\n for (const msg of messages) {\n // Simple string content — pass through with role\n if (typeof msg.content === \"string\") {\n out.push({ role: msg.role as OpenAiMessage[\"role\"], content: msg.content });\n continue;\n }\n\n const blocks = msg.content as LynxContentBlock[];\n\n // Check for tool_result blocks → convert to tool-role messages\n const toolResults = blocks.filter((b) => b.type === \"tool_result\");\n const nonToolBlocks = blocks.filter((b) => b.type !== \"tool_result\");\n\n // Emit tool result messages\n for (const tr of toolResults) {\n out.push({\n role: \"tool\",\n tool_call_id: tr.toolUseId ?? \"unknown\",\n content: tr.content ?? \"\",\n });\n }\n\n // Handle remaining blocks by role\n if (nonToolBlocks.length > 0) {\n const normalized = normalizeContentBlocks(nonToolBlocks, msg.role);\n\n if (msg.role === \"assistant\") {\n const textBlocks = normalized.filter((b) => b.type === \"text\");\n const toolUses = blocks.filter((b) => b.type === \"tool_use\");\n const assistantMsg: OpenAiMessage = {\n role: \"assistant\",\n content: textBlocks.length > 0 ? textBlocks : null,\n };\n if (toolUses.length > 0) {\n assistantMsg.tool_calls = toolUses.map((tu) => ({\n id: tu.id ?? \"unknown\",\n type: \"function\" as const,\n function: {\n name: tu.name ?? \"unknown\",\n arguments: JSON.stringify(tu.input ?? {}),\n },\n }));\n }\n out.push(assistantMsg);\n } else {\n // User or system message — strip unrecognized blocks, keep text\n const textBlocks = normalized.filter((b) => b.type === \"text\");\n if (textBlocks.length > 0) {\n out.push({\n role: msg.role as OpenAiMessage[\"role\"],\n content:\n textBlocks.length === 1 && msg.role !== \"system\"\n ? (textBlocks[0]!.text ?? \"\")\n : textBlocks,\n });\n }\n }\n }\n }\n\n return out;\n}\n\n/** Keep only text blocks, strip reasoning/tool_use/tool_result from content. */\nfunction normalizeContentBlocks(blocks: LynxContentBlock[], _role: string): OpenAiContentBlock[] {\n return blocks\n .filter((b) => b.type === \"text\" && typeof b.text === \"string\")\n .map((b) => ({ type: \"text\" as const, text: b.text! }));\n}\n","/**\n * sse-parser — 共享 SSE 流解析逻辑。\n *\n * DeepSeek 和 OpenAI 使用相同的 OpenAI‑compatible SSE 协议,\n * 此模块提取 tool_use 增量解析和 SSE 行处理逻辑,\n * 避免在两份 Provider 中重复。\n */\nimport type { StreamEvent } from \"../types.js\";\n\n// ── flushActiveToolCall ──────────────────────────────\n\n/** Flush active tool call buffer, yielding tool_use_end or error events. */\nexport function flushActiveToolCall(call: {\n id: string;\n name: string;\n buffer: string;\n}): StreamEvent[] {\n try {\n const input = JSON.parse(call.buffer);\n return [{ type: \"tool_use_end\", callId: call.id, input }];\n } catch {\n return [\n {\n type: \"error\",\n code: \"TOOL_PARSE_ERROR\",\n message: `Failed to parse tool input for ${call.name}`,\n },\n ];\n }\n}\n\n// ── processToolCallDeltas ────────────────────────────\n\n/** Process tool_calls delta array, returning events and updated active call state. */\nexport function processToolCallDeltas(\n toolCalls: Array<Record<string, unknown>>,\n activeCall: { id: string; name: string; buffer: string } | null,\n): { events: StreamEvent[]; activeCall: typeof activeCall } {\n const events: StreamEvent[] = [];\n let currentCall = activeCall;\n\n for (const tc of toolCalls) {\n const tcId = tc.id as string | undefined;\n const fn = tc.function as { name?: string; arguments?: string } | undefined;\n\n if (tcId && fn?.name) {\n // Flush previous tool call if any (silently ignore parse failures)\n if (currentCall) {\n try {\n const input = JSON.parse(currentCall.buffer);\n events.push({ type: \"tool_use_end\", callId: currentCall.id, input });\n } catch {\n // ignore parse failure, keep accumulating\n }\n }\n currentCall = { id: tcId, name: fn.name, buffer: \"\" };\n events.push({ type: \"tool_use_start\", callId: tcId, name: fn.name });\n }\n\n if (fn?.arguments && currentCall) {\n currentCall.buffer += fn.arguments;\n events.push({ type: \"tool_use_delta\", callId: currentCall.id, text: fn.arguments });\n }\n }\n\n return { events, activeCall: currentCall };\n}\n\n// ── SseState ─────────────────────────────────────────\n\n/** SSE stream processing state. */\nexport interface SseState {\n activeToolCall: { id: string; name: string; buffer: string } | null;\n textIndex: number;\n reasoningIndex: number;\n}\n\n// ── processSseLine ───────────────────────────────────\n\n/** Process a single SSE line, returning events to yield and updated state. */\nexport function processSseLine(\n line: string,\n state: SseState,\n): { events: StreamEvent[]; state: SseState; done: boolean } {\n const trimmed = line.trim();\n if (!trimmed || !trimmed.startsWith(\"data:\")) {\n return { events: [], state, done: false };\n }\n\n const data = trimmed.slice(5).trim();\n\n if (data === \"[DONE]\") {\n const events: StreamEvent[] = [];\n if (state.activeToolCall) {\n events.push(...flushActiveToolCall(state.activeToolCall));\n }\n events.push({ type: \"done\" });\n return { events, state: { ...state, activeToolCall: null }, done: true };\n }\n\n let chunk: { choices?: Array<{ delta?: Record<string, unknown> }> };\n try {\n chunk = JSON.parse(data);\n } catch {\n return { events: [], state, done: false };\n }\n\n const choice = chunk.choices?.[0];\n if (!choice?.delta) {\n return { events: [], state, done: false };\n }\n\n const delta = choice.delta;\n const events: StreamEvent[] = [];\n let { textIndex, reasoningIndex, activeToolCall } = state;\n\n // Reasoning content (DeepSeek R1 / OpenAI o-series)\n if (delta.reasoning_content && typeof delta.reasoning_content === \"string\") {\n events.push({\n type: \"reasoning_delta\",\n index: reasoningIndex++,\n text: delta.reasoning_content,\n });\n }\n\n // Regular text content\n if (delta.content && typeof delta.content === \"string\") {\n events.push({\n type: \"text_delta\",\n index: textIndex++,\n text: delta.content,\n });\n }\n\n // Tool call handling\n if (delta.tool_calls) {\n const tcResult = processToolCallDeltas(\n delta.tool_calls as Array<Record<string, unknown>>,\n activeToolCall,\n );\n events.push(...tcResult.events);\n activeToolCall = tcResult.activeCall;\n }\n\n return { events, state: { textIndex, reasoningIndex, activeToolCall }, done: false };\n}\n","/**\n * DeepSeek provider — SSE streaming via the /v1/chat/completions endpoint.\n *\n * DeepSeek is API‑compatible with OpenAI so the wire format is the same.\n * The key difference: DeepSeek has implicit prefix caching on every request\n * (no cache_control blocks needed).\n */\n\nimport { LlmError, LlmRateLimitError, LlmAuthError, LlmContextOverflowError } from \"@24klynx/core\";\nimport type { LlmProvider, ModelInfo, ProviderCapability, StreamEvent } from \"../types.js\";\nimport { getCapability } from \"../capabilities.js\";\nimport { withRetry } from \"../retry.js\";\nimport { normalizeOpenAiTools, normalizeOpenAiMessages } from \"../tool-format.js\";\nimport { flushActiveToolCall, processSseLine } from \"./sse-parser.js\";\n\n// ── Configuration ──────────────────────────────────\n\nexport interface DeepSeekConfig {\n /** API key read from auth store. */\n apiKey: string;\n /** Base URL (defaults to https://api.deepseek.com). */\n baseUrl?: string;\n /** Request timeout in ms (default 120s). */\n timeoutMs?: number;\n /** Max output tokens per request (default 8192). */\n maxTokens?: number;\n}\n\nconst DEFAULT_BASE_URL = \"https://api.deepseek.com\";\nconst DEFAULT_TIMEOUT_MS = 120_000;\n\n// ── Models ─────────────────────────────────────────\n\nconst MODELS: ModelInfo[] = [\n { id: \"deepseek-chat\", label: \"DeepSeek Chat (V3)\", contextWindow: 128_000, maxOutput: 8_192 },\n {\n id: \"deepseek-reasoner\",\n label: \"DeepSeek Reasoner (R1)\",\n contextWindow: 128_000,\n maxOutput: 8_192,\n },\n];\n\n// ── Provider ───────────────────────────────────────\n\n/**\n * Create a DeepSeek provider instance.\n *\n * Each call to `stream()` opens a fresh HTTP connection.\n * The provider itself is stateless — the API key is the only configuration.\n */\nexport function createDeepSeekProvider(config: DeepSeekConfig): LlmProvider {\n const baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;\n const _timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n\n const provider: LlmProvider = {\n id: \"deepseek\",\n\n listModels(): ModelInfo[] {\n return MODELS;\n },\n\n getCapability(modelId: string): ProviderCapability {\n const cap = getCapability(\"deepseek\", modelId);\n if (!cap) {\n throw new LlmError(`Unknown DeepSeek model: ${modelId}`, {\n category: \"llm\",\n recoverable: false,\n retryable: false,\n diagnosticHint: \"llm_unknown_model\",\n });\n }\n return cap;\n },\n\n async *stream(\n ...[modelId, messages, systemPrompt, tools, signal]: Parameters<LlmProvider[\"stream\"]>\n ): AsyncGenerator<StreamEvent, void, void> {\n const systemMessages = systemPrompt ? [{ role: \"system\", content: systemPrompt }] : [];\n\n const body = {\n model: modelId,\n messages: [...systemMessages, ...normalizeOpenAiMessages(messages)],\n max_tokens: config.maxTokens ?? 8192,\n stream: true,\n ...(tools.length > 0 ? { tools: normalizeOpenAiTools(tools) } : {}),\n };\n\n async function makeRequest(): Promise<Response> {\n const controller = new AbortController();\n const onAbort = () => controller.abort();\n signal.addEventListener(\"abort\", onAbort, { once: true });\n\n try {\n const response = await fetch(`${baseUrl}/v1/chat/completions`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${config.apiKey}`,\n },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n await handleHttpError(response);\n }\n\n return response;\n } finally {\n signal.removeEventListener(\"abort\", onAbort);\n }\n }\n\n const response = await withRetry(makeRequest, {\n baseMs: 500,\n maxMs: 32_000,\n });\n\n if (!response.body) {\n throw new LlmError(\"DeepSeek returned empty response body\", {\n category: \"llm\",\n recoverable: true,\n retryable: true,\n });\n }\n\n // Parse SSE stream\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n let textIndex = 0;\n let reasoningIndex = 0;\n let activeToolCall: { id: string; name: string; buffer: string } | null = null;\n\n try {\n while (true) {\n if (signal.aborted) break;\n\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n\n // Process complete SSE lines\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() ?? \"\"; // keep incomplete line in buffer\n\n for (const line of lines) {\n const result = processSseLine(line, { activeToolCall, textIndex, reasoningIndex });\n for (const event of result.events) yield event;\n if (result.done) return;\n activeToolCall = result.state.activeToolCall;\n textIndex = result.state.textIndex;\n reasoningIndex = result.state.reasoningIndex;\n }\n }\n\n // Stream ended without [DONE]\n if (activeToolCall) {\n for (const event of flushActiveToolCall(activeToolCall)) yield event;\n }\n yield { type: \"done\" };\n } finally {\n reader.releaseLock();\n }\n },\n };\n\n return provider;\n}\n\n// ── HTTP error mapper ──────────────────────────────\n\nasync function handleHttpError(response: Response): Promise<never> {\n let body: string;\n try {\n body = await response.text();\n } catch {\n body = \"\";\n }\n\n const status = response.status;\n\n if (status === 401 || status === 403) {\n throw new LlmAuthError(`DeepSeek auth failed (${status}): ${body}`);\n }\n\n if (status === 429) {\n throw new LlmRateLimitError(`DeepSeek rate limited (429): ${body}`);\n }\n\n if (status === 529) {\n const err = new LlmRateLimitError(`DeepSeek overloaded (529): ${body}`);\n (err as unknown as Record<string, unknown>).status = 529;\n throw err;\n }\n\n if (status === 400 && body.includes(\"context\")) {\n throw new LlmContextOverflowError(`DeepSeek context overflow: ${body}`);\n }\n\n throw new LlmError(`DeepSeek HTTP ${status}: ${body}`, {\n category: \"llm\",\n recoverable: status >= 500,\n retryable: status >= 500 || status === 429,\n });\n}\n","/**\n * OpenAI provider — SSE streaming via the /v1/chat/completions endpoint.\n *\n * The API shape is identical to DeepSeek (both follow the OpenAI spec),\n * but the provider metadata and defaults differ.\n */\n\nimport { LlmError, LlmRateLimitError, LlmAuthError, LlmContextOverflowError } from \"@24klynx/core\";\nimport type { LlmProvider, ModelInfo, ProviderCapability, StreamEvent } from \"../types.js\";\nimport { getCapability } from \"../capabilities.js\";\nimport { withRetry } from \"../retry.js\";\nimport { normalizeOpenAiTools, normalizeOpenAiMessages } from \"../tool-format.js\";\nimport { flushActiveToolCall, processSseLine } from \"./sse-parser.js\";\n\n// ── Configuration ──────────────────────────────────\n\nexport interface OpenAiConfig {\n apiKey: string;\n baseUrl?: string;\n timeoutMs?: number;\n /** Max output tokens per request (default 8192). */\n maxTokens?: number;\n}\n\nconst DEFAULT_BASE_URL = \"https://api.openai.com\";\nconst _DEFAULT_TIMEOUT_MS = 120_000;\n\n// ── Models ─────────────────────────────────────────\n\nconst MODELS: ModelInfo[] = [\n { id: \"gpt-4o\", label: \"GPT-4o\", contextWindow: 128_000, maxOutput: 16_384 },\n { id: \"gpt-4o-mini\", label: \"GPT-4o Mini\", contextWindow: 128_000, maxOutput: 16_384 },\n { id: \"o3-mini\", label: \"o3-mini\", contextWindow: 200_000, maxOutput: 100_000 },\n];\n\n// ── Provider ───────────────────────────────────────\n\n/**\n * Create an OpenAI provider instance.\n *\n * Same SSE parsing logic as DeepSeek — both follow the same wire protocol.\n * The only differences are the base URL, models, and capability metadata.\n */\nexport function createOpenAiProvider(config: OpenAiConfig): LlmProvider {\n const baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;\n const provider: LlmProvider = {\n id: \"openai\",\n\n listModels(): ModelInfo[] {\n return MODELS;\n },\n\n getCapability(modelId: string): ProviderCapability {\n const cap = getCapability(\"openai\", modelId);\n if (!cap) {\n throw new LlmError(`Unknown OpenAI model: ${modelId}`, {\n category: \"llm\",\n recoverable: false,\n retryable: false,\n diagnosticHint: \"llm_unknown_model\",\n });\n }\n return cap;\n },\n\n async *stream(\n ...[modelId, messages, systemPrompt, tools, signal]: Parameters<LlmProvider[\"stream\"]>\n ): AsyncGenerator<StreamEvent, void, void> {\n const systemMessages = systemPrompt ? [{ role: \"system\", content: systemPrompt }] : [];\n\n const body = {\n model: modelId,\n messages: [...systemMessages, ...normalizeOpenAiMessages(messages)],\n max_tokens: config.maxTokens ?? 8192,\n stream: true,\n ...(tools.length > 0 ? { tools: normalizeOpenAiTools(tools) } : {}),\n };\n\n async function makeRequest(): Promise<Response> {\n const controller = new AbortController();\n const onAbort = () => controller.abort();\n signal.addEventListener(\"abort\", onAbort, { once: true });\n\n try {\n const response = await fetch(`${baseUrl}/v1/chat/completions`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${config.apiKey}`,\n },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n await handleHttpError(response);\n }\n\n return response;\n } finally {\n signal.removeEventListener(\"abort\", onAbort);\n }\n }\n\n const response = await withRetry(makeRequest, {\n baseMs: 500,\n maxMs: 32_000,\n });\n\n if (!response.body) {\n throw new LlmError(\"OpenAI returned empty response body\", {\n category: \"llm\",\n recoverable: true,\n retryable: true,\n });\n }\n\n // Parse SSE stream (same logic as DeepSeek, extracted for clarity)\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n let textIndex = 0;\n let reasoningIndex = 0;\n let activeToolCall: { id: string; name: string; buffer: string } | null = null;\n\n try {\n while (true) {\n if (signal.aborted) break;\n\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() ?? \"\";\n\n for (const line of lines) {\n const result = processSseLine(line, { activeToolCall, textIndex, reasoningIndex });\n for (const event of result.events) yield event;\n if (result.done) return;\n activeToolCall = result.state.activeToolCall;\n textIndex = result.state.textIndex;\n reasoningIndex = result.state.reasoningIndex;\n }\n }\n\n if (activeToolCall) {\n for (const event of flushActiveToolCall(activeToolCall)) yield event;\n }\n yield { type: \"done\" };\n } finally {\n reader.releaseLock();\n }\n },\n };\n\n return provider;\n}\n\n// ── HTTP error mapper ──────────────────────────────\n\nasync function handleHttpError(response: Response): Promise<never> {\n let body: string;\n try {\n body = await response.text();\n } catch {\n body = \"\";\n }\n\n const status = response.status;\n\n if (status === 401 || status === 403) {\n throw new LlmAuthError(`OpenAI auth failed (${status}): ${body}`);\n }\n\n if (status === 429) {\n throw new LlmRateLimitError(`OpenAI rate limited (429): ${body}`);\n }\n\n if (status === 529) {\n throw new LlmRateLimitError(`OpenAI overloaded (529): ${body}`, 529);\n }\n\n if (status === 400 && body.includes(\"context\")) {\n throw new LlmContextOverflowError(`OpenAI context overflow: ${body}`);\n }\n\n throw new LlmError(`OpenAI HTTP ${status}: ${body}`, {\n category: \"llm\",\n recoverable: status >= 500,\n retryable: status >= 500 || status === 429,\n });\n}\n","/**\n * Anthropic provider — SSE streaming via the /v1/messages endpoint.\n *\n * Translates Lynx's OpenAI‑compatible internal format to Anthropic's\n * native message/tool format, then maps Anthropic SSE events back to\n * Lynx StreamEvent types. This is the reverse of what Claude Code's\n * Codex adapter does (Anthropic → OpenAI translation).\n */\n\nimport { LlmError, LlmRateLimitError, LlmAuthError, LlmContextOverflowError } from \"@24klynx/core\";\nimport type { LlmProvider, ModelInfo, ProviderCapability, StreamEvent } from \"../types.js\";\nimport { getCapability } from \"../capabilities.js\";\nimport { withRetry } from \"../retry.js\";\n\n// ── Configuration ──────────────────────────────────\n\nexport interface AnthropicConfig {\n /** API key from ANTHROPIC_API_KEY env var. */\n apiKey: string;\n /** Base URL (defaults to https://api.anthropic.com). */\n baseUrl?: string;\n /** Request timeout in ms (default 120s). */\n timeoutMs?: number;\n /** Max output tokens per request (default 8192). */\n maxTokens?: number;\n}\n\nconst DEFAULT_BASE_URL = \"https://api.anthropic.com\";\nconst ANTHROPIC_VERSION = \"2023-06-01\";\nconst DEFAULT_TIMEOUT_MS = 120_000;\n\n// ── Models ─────────────────────────────────────────\n\nconst MODELS: ModelInfo[] = [\n {\n id: \"claude-opus-4-8\",\n label: \"Claude Opus 4.8\",\n contextWindow: 200_000,\n maxOutput: 32_000,\n },\n {\n id: \"claude-sonnet-4-6\",\n label: \"Claude Sonnet 4.6\",\n contextWindow: 200_000,\n maxOutput: 16_384,\n },\n {\n id: \"claude-haiku-4-5\",\n label: \"Claude Haiku 4.5\",\n contextWindow: 200_000,\n maxOutput: 8_192,\n },\n\n // DeepSeek via Anthropic-compatible endpoint\n {\n id: \"deepseek-v4-pro\",\n label: \"DeepSeek V4 Pro\",\n contextWindow: 200_000,\n maxOutput: 32_000,\n },\n];\n\n// ── Wire‑format types ──────────────────────────────\n\n/** Anthropic content block types. */\ntype AnthropicContentBlock =\n | { type: \"text\"; text: string }\n | { type: \"tool_use\"; id: string; name: string; input: Record<string, unknown> }\n | { type: \"tool_result\"; tool_use_id: string; content: string; is_error?: boolean };\n\n/** Anthropic message format. */\ninterface AnthropicMessage {\n role: \"user\" | \"assistant\";\n content: string | AnthropicContentBlock[];\n}\n\n/** Anthropic tool definition. */\ninterface AnthropicTool {\n name: string;\n description: string;\n input_schema: Record<string, unknown>;\n}\n\n/** Internal type for message content blocks arriving via Lynx ChatMessage. */\ninterface LynxContentBlock {\n type: string;\n text?: string;\n id?: string;\n name?: string;\n input?: Record<string, unknown>;\n toolUseId?: string;\n content?: string;\n isError?: boolean;\n}\n\n/** OpenAI‑format tool_calls entry. */\ninterface OpenAiToolCall {\n id: string;\n type: \"function\";\n function: { name: string; arguments: string };\n}\n\n/** Lynx ChatMessage shape (what normalizeOpenAiMessages maps TO, so we receive FROM). */\ninterface LynxChatMessage {\n role: \"system\" | \"user\" | \"assistant\" | \"tool\";\n content: string | LynxContentBlock[] | null;\n tool_call_id?: string;\n tool_calls?: OpenAiToolCall[];\n}\n\n// ── Translation: Lynx messages → Anthropic messages ─\n\n/**\n * Convert Lynx OpenAI‑compatible ChatMessage[] to Anthropic Message[].\n * System‑role messages are extracted into a separate string array\n * (Anthropic treats system as a top‑level parameter, not a message).\n */\nfunction translateMessages(chatMessages: LynxChatMessage[]): {\n messages: AnthropicMessage[];\n systemLines: string[];\n} {\n const messages: AnthropicMessage[] = [];\n const systemLines: string[] = [];\n\n for (const msg of chatMessages) {\n if (msg.role === \"system\") {\n if (typeof msg.content === \"string\") {\n systemLines.push(msg.content);\n } else if (Array.isArray(msg.content)) {\n for (const block of msg.content) {\n if (block.type === \"text\" && block.text) systemLines.push(block.text);\n }\n }\n continue;\n }\n\n if (msg.role === \"user\") {\n messages.push({ role: \"user\", content: translateUserContent(msg) });\n } else if (msg.role === \"assistant\") {\n messages.push({ role: \"assistant\", content: translateAssistantContent(msg) });\n } else if (msg.role === \"tool\") {\n // OpenAI tool‑role → Anthropic user message with tool_result block\n messages.push({\n role: \"user\",\n content: [\n {\n type: \"tool_result\",\n tool_use_id: msg.tool_call_id ?? \"unknown\",\n content: typeof msg.content === \"string\" ? msg.content : JSON.stringify(msg.content),\n },\n ],\n });\n }\n }\n\n return { messages, systemLines };\n}\n\n/** Convert a Lynx user message to Anthropic content. */\nfunction translateUserContent(msg: LynxChatMessage): string | AnthropicContentBlock[] {\n if (typeof msg.content === \"string\") return msg.content;\n if (!msg.content || msg.content.length === 0) return \"\";\n\n const blocks: AnthropicContentBlock[] = [];\n for (const block of msg.content as LynxContentBlock[]) {\n if (block.type === \"text\" && block.text) {\n blocks.push({ type: \"text\", text: block.text });\n } else if (block.type === \"tool_result\") {\n blocks.push({\n type: \"tool_result\",\n tool_use_id: block.toolUseId ?? \"unknown\",\n content: typeof block.content === \"string\" ? block.content : JSON.stringify(block.content),\n is_error: block.isError,\n });\n }\n }\n return blocks.length > 0 ? blocks : \"\";\n}\n\n/** Convert a Lynx assistant message to Anthropic content. */\nfunction translateAssistantContent(msg: LynxChatMessage): string | AnthropicContentBlock[] {\n const blocks: AnthropicContentBlock[] = [];\n\n // Text blocks from content array\n if (Array.isArray(msg.content)) {\n for (const block of msg.content as LynxContentBlock[]) {\n if (block.type === \"text\" && block.text) {\n blocks.push({ type: \"text\", text: block.text });\n } else if (block.type === \"tool_use\" && block.id && block.name) {\n blocks.push({\n type: \"tool_use\",\n id: block.id,\n name: block.name,\n input: block.input ?? {},\n });\n }\n }\n } else if (typeof msg.content === \"string\" && msg.content) {\n blocks.push({ type: \"text\", text: msg.content });\n }\n\n // Tool calls from OpenAI format\n if (msg.tool_calls) {\n for (const tc of msg.tool_calls) {\n let input: Record<string, unknown> = {};\n try {\n input = JSON.parse(tc.function.arguments);\n } catch {\n input = {};\n }\n blocks.push({\n type: \"tool_use\",\n id: tc.id,\n name: tc.function.name,\n input,\n });\n }\n }\n\n return blocks.length > 0 ? blocks : \"\";\n}\n\n// ── Translation: Lynx tools → Anthropic tools ──────\n\n/** Convert Lynx/OpenAI‑format tool descriptors to Anthropic format. */\nfunction translateTools(tools: unknown[]): AnthropicTool[] {\n return tools.map((tool) => {\n const raw = tool as Record<string, unknown>;\n return {\n name: (raw.name as string) ?? \"unknown\",\n description: (raw.description as string) ?? \"\",\n input_schema:\n (raw.inputSchema as Record<string, unknown>) ??\n ((raw.function as Record<string, unknown>)?.parameters as Record<string, unknown>) ??\n {},\n };\n });\n}\n\n// ── SSE streaming parser ───────────────────────────\n\n/** State for tracking streaming content blocks. */\ninterface StreamState {\n activeToolCall: { id: string; name: string; buffer: string } | null;\n activeToolCallIndex: number;\n textIndex: number;\n toolUseCount: number;\n /** Set of block indices that are tool_use blocks (to detect when text blocks end). */\n toolUseIndices: Set<number>;\n}\n\n/**\n * Parse a single Anthropic SSE event line and return mapped Lynx events.\n * Anthropic SSE format: `event: <type>\\ndata: <json>\\n\\n`\n */\nfunction processAnthropicEvent(\n eventType: string,\n data: string,\n state: StreamState,\n): { events: StreamEvent[]; state: StreamState } {\n const events: StreamEvent[] = [];\n let nextState = state;\n\n if (eventType === \"message_start\") {\n return { events, state: nextState };\n }\n\n if (eventType === \"ping\") {\n return { events, state: nextState };\n }\n\n if (eventType === \"content_block_start\") {\n const parsed = JSON.parse(data) as {\n type: \"content_block_start\";\n index: number;\n content_block: { type: string; id?: string; name?: string };\n };\n const block = parsed.content_block;\n\n if (block.type === \"tool_use\" && block.id && block.name) {\n nextState = {\n ...state,\n activeToolCall: { id: block.id, name: block.name, buffer: \"\" },\n activeToolCallIndex: parsed.index,\n toolUseIndices: new Set(state.toolUseIndices).add(parsed.index),\n };\n events.push({ type: \"tool_use_start\", callId: block.id, name: block.name });\n }\n // text block start — no event needed, deltas carry the content\n return { events, state: nextState };\n }\n\n if (eventType === \"content_block_delta\") {\n const parsed = JSON.parse(data) as {\n type: \"content_block_delta\";\n index: number;\n delta: { type: string; text?: string; partial_json?: string; thinking?: string };\n };\n const delta = parsed.delta;\n\n if (delta.type === \"text_delta\" && delta.text) {\n events.push({\n type: \"text_delta\",\n index: state.textIndex++,\n text: delta.text,\n });\n } else if (delta.type === \"input_json_delta\" && delta.partial_json && state.activeToolCall) {\n nextState = {\n ...state,\n activeToolCall: {\n ...state.activeToolCall,\n buffer: state.activeToolCall.buffer + delta.partial_json,\n },\n };\n events.push({\n type: \"tool_use_delta\",\n callId: state.activeToolCall.id,\n text: delta.partial_json,\n });\n } else if (delta.type === \"thinking_delta\" && delta.thinking) {\n events.push({\n type: \"reasoning_delta\",\n index: 0,\n text: delta.thinking,\n });\n }\n\n return { events, state: nextState };\n }\n\n if (eventType === \"content_block_stop\") {\n // Parse accumulated JSON for active tool call on this block index\n const parsed = JSON.parse(data) as { type: \"content_block_stop\"; index: number };\n if (\n state.activeToolCall &&\n parsed.index === state.activeToolCallIndex &&\n state.activeToolCall.buffer\n ) {\n try {\n const input = JSON.parse(state.activeToolCall.buffer);\n events.push({\n type: \"tool_use_end\",\n callId: state.activeToolCall.id,\n input: input as Record<string, unknown>,\n });\n nextState = {\n ...state,\n toolUseCount: state.toolUseCount + 1,\n activeToolCall: null,\n };\n } catch {\n events.push({\n type: \"error\",\n code: \"TOOL_PARSE_ERROR\",\n message: `解析 ${state.activeToolCall.name} 的工具输入失败`,\n });\n }\n }\n return { events, state: nextState };\n }\n\n if (eventType === \"message_delta\") {\n return { events, state: nextState };\n }\n\n if (eventType === \"message_stop\") {\n events.push({ type: \"done\" });\n return { events, state: nextState };\n }\n\n return { events, state: nextState };\n}\n\n// ── Provider factory ───────────────────────────────\n\n/**\n * Create an Anthropic provider instance.\n *\n * Each call to `stream()` opens a fresh HTTP connection.\n * The provider itself is stateless — the API key is the only configuration.\n */\nexport function createAnthropicProvider(config: AnthropicConfig): LlmProvider {\n const baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;\n const timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const apiKey = config.apiKey;\n\n const provider: LlmProvider = {\n id: \"anthropic\",\n\n listModels(): ModelInfo[] {\n return MODELS;\n },\n\n getCapability(modelId: string): ProviderCapability {\n const cap = getCapability(\"anthropic\", modelId);\n if (!cap) {\n throw new LlmError(`未知 Anthropic 模型: ${modelId}`, {\n category: \"llm\",\n recoverable: false,\n retryable: false,\n diagnosticHint: \"llm_unknown_model\",\n });\n }\n return cap;\n },\n\n async *stream(\n ...[modelId, messages, systemPrompt, tools, signal]: Parameters<LlmProvider[\"stream\"]>\n ): AsyncGenerator<StreamEvent, void, void> {\n // Translate system prompt + system messages → Anthropic system parameter\n const translated = translateMessages(messages as LynxChatMessage[]);\n const allSystemLines = systemPrompt\n ? [systemPrompt, ...translated.systemLines]\n : translated.systemLines;\n const systemParam = allSystemLines.length > 0 ? allSystemLines.join(\"\\n\\n\") : undefined;\n\n const body = {\n model: modelId,\n max_tokens: config.maxTokens ?? 8192,\n messages: translated.messages,\n ...(systemParam ? { system: systemParam } : {}),\n ...(tools.length > 0 ? { tools: translateTools(tools) } : {}),\n stream: true,\n };\n\n async function makeRequest(): Promise<Response> {\n const controller = new AbortController();\n const onAbort = () => controller.abort();\n signal.addEventListener(\"abort\", onAbort, { once: true });\n\n try {\n const response = await fetch(`${baseUrl}/v1/messages`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-api-key\": apiKey,\n \"anthropic-version\": ANTHROPIC_VERSION,\n },\n body: JSON.stringify(body),\n signal: AbortSignal.any\n ? AbortSignal.any([signal, controller.signal])\n : controller.signal,\n });\n\n if (!response.ok) {\n await handleHttpError(response);\n }\n\n return response;\n } finally {\n signal.removeEventListener(\"abort\", onAbort);\n }\n }\n\n const response = await withRetry(makeRequest, {\n baseMs: 500,\n maxMs: 32_000,\n });\n\n if (!response.body) {\n throw new LlmError(\"Anthropic 返回了空响应体\", {\n category: \"llm\",\n recoverable: true,\n retryable: true,\n });\n }\n\n // Parse Anthropic SSE stream\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n let eventType = \"\";\n let state: StreamState = {\n activeToolCall: null,\n activeToolCallIndex: -1,\n textIndex: 0,\n toolUseCount: 0,\n toolUseIndices: new Set(),\n };\n\n try {\n while (true) {\n if (signal.aborted) break;\n\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n\n // Process complete SSE events — Anthropic uses two-line format:\n // event: <type>\n // data: <json>\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() ?? \"\"; // keep incomplete line\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (trimmed.startsWith(\"event: \")) {\n eventType = trimmed.slice(7).trim();\n } else if (trimmed.startsWith(\"data: \") && eventType) {\n const data = trimmed.slice(6).trim();\n const result = processAnthropicEvent(eventType, data, state);\n for (const event of result.events) yield event;\n state = result.state;\n }\n }\n }\n\n // Stream ended without message_stop\n if (state.activeToolCall && state.activeToolCall.buffer) {\n try {\n const input = JSON.parse(state.activeToolCall.buffer);\n yield {\n type: \"tool_use_end\",\n callId: state.activeToolCall.id,\n input: input as Record<string, unknown>,\n };\n } catch {\n // ignore — partial tool call at stream end\n }\n }\n yield { type: \"done\" };\n } finally {\n reader.releaseLock();\n }\n },\n };\n\n return provider;\n}\n\n// ── HTTP error mapper ──────────────────────────────\n\nasync function handleHttpError(response: Response): Promise<never> {\n let body: string;\n try {\n body = await response.text();\n } catch {\n body = \"\";\n }\n\n const status = response.status;\n\n if (status === 401 || status === 403) {\n throw new LlmAuthError(`Anthropic 认证失败 (${status}): ${body}`);\n }\n\n if (status === 429) {\n throw new LlmRateLimitError(`Anthropic 速率限制 (429): ${body}`);\n }\n\n if (status === 529) {\n const err = new LlmRateLimitError(`Anthropic 过载 (529): ${body}`);\n (err as unknown as Record<string, unknown>).status = 529;\n throw err;\n }\n\n if (status === 400 && body.includes(\"context\")) {\n throw new LlmContextOverflowError(`Anthropic 上下文溢出: ${body}`);\n }\n\n throw new LlmError(`Anthropic HTTP ${status}: ${body}`, {\n category: \"llm\",\n recoverable: status >= 500,\n retryable: status >= 500 || status === 429,\n });\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@24klynx/llm",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "LLM abstraction layer — Stream, providers, retry, fallback",
|
|
5
|
+
"files": [
|
|
6
|
+
"dist"
|
|
7
|
+
],
|
|
5
8
|
"type": "module",
|
|
6
9
|
"main": "./dist/index.mjs",
|
|
7
10
|
"types": "./dist/index.d.mts",
|
|
@@ -11,16 +14,13 @@
|
|
|
11
14
|
"types": "./dist/index.d.mts"
|
|
12
15
|
}
|
|
13
16
|
},
|
|
14
|
-
"dependencies": {
|
|
15
|
-
"@anthropic-ai/sdk": "^0.101.0",
|
|
16
|
-
"@24klynx/core": "0.1.2"
|
|
17
|
-
},
|
|
18
|
-
"files": [
|
|
19
|
-
"dist"
|
|
20
|
-
],
|
|
21
17
|
"publishConfig": {
|
|
22
18
|
"access": "public"
|
|
23
19
|
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@anthropic-ai/sdk": "^0.101.0",
|
|
22
|
+
"@24klynx/core": "0.1.5"
|
|
23
|
+
},
|
|
24
24
|
"scripts": {
|
|
25
25
|
"build": "tsdown --config-loader tsx",
|
|
26
26
|
"test": "vitest run --passWithNoTests",
|