@p-sw/brainbox 0.1.2-alpha.10 → 0.1.2-alpha.11
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/dist/index.js +157 -218
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -550,7 +550,7 @@ class LLMExecutor {
|
|
|
550
550
|
});
|
|
551
551
|
};
|
|
552
552
|
if (conv.provider === id.provider) {
|
|
553
|
-
return
|
|
553
|
+
return build(conv.provider);
|
|
554
554
|
}
|
|
555
555
|
const modelToProvider = {
|
|
556
556
|
[conv.model]: conv.provider,
|
|
@@ -561,7 +561,7 @@ class LLMExecutor {
|
|
|
561
561
|
[id.provider]: build(id.provider)
|
|
562
562
|
};
|
|
563
563
|
const fallback = instances[conv.provider];
|
|
564
|
-
return
|
|
564
|
+
return new class extends LLMExecutor {
|
|
565
565
|
providerName = "dispatch";
|
|
566
566
|
models = {
|
|
567
567
|
conversation: conv.model,
|
|
@@ -575,126 +575,21 @@ class LLMExecutor {
|
|
|
575
575
|
const exec = instances[modelToProvider[model] ?? ""] ?? fallback;
|
|
576
576
|
return exec.chatWithTools(model, options);
|
|
577
577
|
}
|
|
578
|
-
}
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
function formatPayload(value) {
|
|
582
|
-
if (typeof value === "string")
|
|
583
|
-
return value;
|
|
584
|
-
try {
|
|
585
|
-
return JSON.stringify(value, null, 2);
|
|
586
|
-
} catch {
|
|
587
|
-
return String(value);
|
|
578
|
+
};
|
|
588
579
|
}
|
|
589
580
|
}
|
|
590
|
-
function
|
|
581
|
+
function resolveLlmCaller(options, fallback = "llm") {
|
|
591
582
|
return options.caller ?? options.jsonSchemaName ?? fallback;
|
|
592
583
|
}
|
|
593
|
-
function
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
jsonSchemaName: jsonMode ? options.jsonSchemaName : undefined
|
|
602
|
-
}, "call");
|
|
603
|
-
const requestBody = [
|
|
604
|
-
`provider: ${inner.providerName}`,
|
|
605
|
-
`model: ${model}`,
|
|
606
|
-
`caller: ${caller}`,
|
|
607
|
-
`reasoningEffort: ${options.reasoningEffort ?? "-"}`,
|
|
608
|
-
`jsonSchemaName: ${jsonMode ? options.jsonSchemaName : "-"}`,
|
|
609
|
-
"",
|
|
610
|
-
"--- instruction ---",
|
|
611
|
-
options.instruction,
|
|
612
|
-
"",
|
|
613
|
-
"--- message ---",
|
|
614
|
-
options.message,
|
|
615
|
-
...jsonMode ? ["", "--- jsonSchema ---", formatPayload(options.jsonSchema)] : []
|
|
616
|
-
].join(`
|
|
617
|
-
`);
|
|
618
|
-
try {
|
|
619
|
-
const result = await inner.call(model, options);
|
|
620
|
-
if (isLlmLogEnabled()) {
|
|
621
|
-
writeLlmExchange(caller, [
|
|
622
|
-
"======== REQUEST ========",
|
|
623
|
-
requestBody,
|
|
624
|
-
"",
|
|
625
|
-
"======== RESPONSE ========",
|
|
626
|
-
formatPayload(result),
|
|
627
|
-
""
|
|
628
|
-
].join(`
|
|
629
|
-
`));
|
|
630
|
-
}
|
|
631
|
-
return result;
|
|
632
|
-
} catch (err) {
|
|
633
|
-
const reason = err instanceof Error ? err.message : String(err);
|
|
634
|
-
if (isLlmLogEnabled()) {
|
|
635
|
-
writeLlmExchange(caller, [
|
|
636
|
-
"======== REQUEST ========",
|
|
637
|
-
requestBody,
|
|
638
|
-
"",
|
|
639
|
-
"======== ERROR ========",
|
|
640
|
-
reason,
|
|
641
|
-
""
|
|
642
|
-
].join(`
|
|
643
|
-
`));
|
|
644
|
-
}
|
|
645
|
-
throw err;
|
|
646
|
-
}
|
|
647
|
-
}
|
|
648
|
-
async chatWithTools(model, options) {
|
|
649
|
-
const caller = resolveCaller(options, "chat");
|
|
650
|
-
const requestBody = [
|
|
651
|
-
`provider: ${inner.providerName}`,
|
|
652
|
-
`model: ${model}`,
|
|
653
|
-
`caller: ${caller}`,
|
|
654
|
-
`reasoningEffort: ${options.reasoningEffort ?? "-"}`,
|
|
655
|
-
`parallelToolCalls: ${options.parallelToolCalls ?? false}`,
|
|
656
|
-
"",
|
|
657
|
-
"--- instruction ---",
|
|
658
|
-
options.instruction,
|
|
659
|
-
"",
|
|
660
|
-
"--- messages ---",
|
|
661
|
-
formatPayload(options.messages),
|
|
662
|
-
"",
|
|
663
|
-
"--- tools ---",
|
|
664
|
-
formatPayload(options.tools)
|
|
665
|
-
].join(`
|
|
584
|
+
function logLlmWire(caller, request, response) {
|
|
585
|
+
if (!isLlmLogEnabled())
|
|
586
|
+
return;
|
|
587
|
+
writeLlmExchange(caller, `======== REQUEST ========
|
|
588
|
+
${request}
|
|
589
|
+
|
|
590
|
+
======== RESPONSE ========
|
|
591
|
+
${response}
|
|
666
592
|
`);
|
|
667
|
-
try {
|
|
668
|
-
const result = await inner.chatWithTools(model, options);
|
|
669
|
-
if (isLlmLogEnabled()) {
|
|
670
|
-
writeLlmExchange(caller, [
|
|
671
|
-
"======== REQUEST ========",
|
|
672
|
-
requestBody,
|
|
673
|
-
"",
|
|
674
|
-
"======== RESPONSE ========",
|
|
675
|
-
formatPayload(result),
|
|
676
|
-
""
|
|
677
|
-
].join(`
|
|
678
|
-
`));
|
|
679
|
-
}
|
|
680
|
-
return result;
|
|
681
|
-
} catch (err) {
|
|
682
|
-
const reason = err instanceof Error ? err.message : String(err);
|
|
683
|
-
if (isLlmLogEnabled()) {
|
|
684
|
-
writeLlmExchange(caller, [
|
|
685
|
-
"======== REQUEST ========",
|
|
686
|
-
requestBody,
|
|
687
|
-
"",
|
|
688
|
-
"======== ERROR ========",
|
|
689
|
-
reason,
|
|
690
|
-
""
|
|
691
|
-
].join(`
|
|
692
|
-
`));
|
|
693
|
-
}
|
|
694
|
-
throw err;
|
|
695
|
-
}
|
|
696
|
-
}
|
|
697
|
-
};
|
|
698
593
|
}
|
|
699
594
|
function listProviderNames() {
|
|
700
595
|
return LLMExecutor.listProviderNames();
|
|
@@ -764,27 +659,27 @@ class OpenRouterExecutor extends LLMExecutor {
|
|
|
764
659
|
async call(model, options) {
|
|
765
660
|
const jsonMode = "jsonSchemaName" in options;
|
|
766
661
|
log3.debug(`call: model=${model} jsonSchema=${jsonMode ? options.jsonSchemaName : "-"} msgLen=${options.message.length}`);
|
|
767
|
-
const
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
662
|
+
const chatRequest = {
|
|
663
|
+
model,
|
|
664
|
+
messages: [
|
|
665
|
+
{ role: "system", content: options.instruction },
|
|
666
|
+
{ role: "user", content: options.message }
|
|
667
|
+
],
|
|
668
|
+
reasoning: {
|
|
669
|
+
effort: options.reasoningEffort ?? (model === this.models.identity ? REASONING_EFFORT_MAP.medium : REASONING_EFFORT_MAP.none)
|
|
670
|
+
},
|
|
671
|
+
responseFormat: jsonMode ? {
|
|
672
|
+
type: "json_schema",
|
|
673
|
+
jsonSchema: {
|
|
674
|
+
name: options.jsonSchemaName,
|
|
675
|
+
schema: options.jsonSchema,
|
|
676
|
+
strict: true
|
|
677
|
+
}
|
|
678
|
+
} : { type: "text" },
|
|
679
|
+
stream: false
|
|
680
|
+
};
|
|
681
|
+
const result = await this.client.chat.send({ chatRequest });
|
|
682
|
+
logLlmWire(resolveLlmCaller(options), JSON.stringify(chatRequest), JSON.stringify(result));
|
|
788
683
|
const raw = result.choices[0]?.message?.content;
|
|
789
684
|
const content = typeof raw === "string" ? stripThinkTags(raw) : raw;
|
|
790
685
|
if (!content) {
|
|
@@ -796,22 +691,22 @@ class OpenRouterExecutor extends LLMExecutor {
|
|
|
796
691
|
}
|
|
797
692
|
async chatWithTools(model, options) {
|
|
798
693
|
log3.debug(`chatWithTools: model=${model} msgs=${options.messages.length} tools=${options.tools.length}`);
|
|
799
|
-
const
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
694
|
+
const chatRequest = {
|
|
695
|
+
model,
|
|
696
|
+
messages: [
|
|
697
|
+
{ role: "system", content: options.instruction },
|
|
698
|
+
...options.messages.map(toOrMessage)
|
|
699
|
+
],
|
|
700
|
+
reasoning: {
|
|
701
|
+
effort: options.reasoningEffort ?? (model === this.models.identity ? REASONING_EFFORT_MAP.medium : REASONING_EFFORT_MAP.none)
|
|
702
|
+
},
|
|
703
|
+
responseFormat: { type: "text" },
|
|
704
|
+
tools: options.tools.map(toOrTool),
|
|
705
|
+
parallelToolCalls: options.parallelToolCalls ?? false,
|
|
706
|
+
stream: false
|
|
707
|
+
};
|
|
708
|
+
const result = await this.client.chat.send({ chatRequest });
|
|
709
|
+
logLlmWire(resolveLlmCaller(options), JSON.stringify(chatRequest), JSON.stringify(result));
|
|
815
710
|
const choice = result.choices[0];
|
|
816
711
|
if (!choice) {
|
|
817
712
|
log3.debug(`chatWithTools: no choice in response`);
|
|
@@ -925,7 +820,7 @@ class OpenAICompatibleExecutor extends LLMExecutor {
|
|
|
925
820
|
}
|
|
926
821
|
return url.toString();
|
|
927
822
|
}
|
|
928
|
-
async sendRequest(body, reasoningEffort) {
|
|
823
|
+
async sendRequest(body, reasoningEffort, caller) {
|
|
929
824
|
const modelName = body["model"];
|
|
930
825
|
const modelStr = typeof modelName === "string" ? modelName : "";
|
|
931
826
|
const url = this.buildRequestUrl(modelStr, reasoningEffort);
|
|
@@ -935,21 +830,34 @@ class OpenAICompatibleExecutor extends LLMExecutor {
|
|
|
935
830
|
...authHeader,
|
|
936
831
|
...this.defaultHeaders
|
|
937
832
|
};
|
|
833
|
+
const requestRaw = JSON.stringify(body);
|
|
938
834
|
const res = await fetch(url, {
|
|
939
835
|
method: "POST",
|
|
940
836
|
headers,
|
|
941
|
-
body:
|
|
837
|
+
body: requestRaw
|
|
942
838
|
});
|
|
839
|
+
const responseRaw = await res.text().catch(() => "");
|
|
840
|
+
logLlmWire(caller, requestRaw, responseRaw);
|
|
943
841
|
if (!res.ok) {
|
|
944
|
-
|
|
945
|
-
log4.error(`${this.providerName}: HTTP ${res.status} ${res.statusText} body=${text.slice(0, 500)}`);
|
|
842
|
+
log4.error(`${this.providerName}: HTTP ${res.status} ${res.statusText} body=${responseRaw.slice(0, 500)}`);
|
|
946
843
|
throw new Error(`${this.providerName} request failed: ${res.status} ${res.statusText}`);
|
|
947
844
|
}
|
|
948
|
-
|
|
845
|
+
let data;
|
|
846
|
+
try {
|
|
847
|
+
data = JSON.parse(responseRaw);
|
|
848
|
+
} catch {
|
|
849
|
+
throw new Error(`${this.providerName}: invalid JSON response`);
|
|
850
|
+
}
|
|
949
851
|
if (data.error) {
|
|
950
852
|
log4.error(`${this.providerName}: API error ${data.error.type ?? ""} ${data.error.message ?? ""}`);
|
|
951
853
|
throw new Error(`${this.providerName} API error: ${data.error.message ?? "unknown"}`);
|
|
952
854
|
}
|
|
855
|
+
const baseCode = data.base_resp?.status_code;
|
|
856
|
+
if (typeof baseCode === "number" && baseCode !== 0) {
|
|
857
|
+
const msg = data.base_resp?.status_msg?.trim() || `status_code ${baseCode}`;
|
|
858
|
+
log4.error(`${this.providerName}: base_resp ${baseCode} ${msg}`);
|
|
859
|
+
throw new Error(`${this.providerName} API error: ${msg}`);
|
|
860
|
+
}
|
|
953
861
|
return data;
|
|
954
862
|
}
|
|
955
863
|
async call(model, options) {
|
|
@@ -965,12 +873,15 @@ class OpenAICompatibleExecutor extends LLMExecutor {
|
|
|
965
873
|
responseFormat: jsonMode ? buildResponseFormat(options.jsonSchemaName, options.jsonSchema) : undefined,
|
|
966
874
|
reasoningEffort: reasoning
|
|
967
875
|
});
|
|
968
|
-
const data = await this.sendRequest(body, options.reasoningEffort);
|
|
969
|
-
const
|
|
876
|
+
const data = await this.sendRequest(body, options.reasoningEffort, resolveLlmCaller(options));
|
|
877
|
+
const choice = data.choices?.[0];
|
|
878
|
+
const raw = choice?.message?.content;
|
|
970
879
|
const content = typeof raw === "string" ? stripThinkTags(raw) : raw;
|
|
971
880
|
if (!content) {
|
|
972
|
-
|
|
973
|
-
|
|
881
|
+
const finish = choice?.finish_reason ?? "no-choice";
|
|
882
|
+
const reasoningLen = typeof choice?.message?.reasoning_content === "string" ? choice.message.reasoning_content.length : 0;
|
|
883
|
+
log4.debug(`call: empty content in choice 0 finish_reason=${finish} reasoning_len=${reasoningLen} rawType=${raw === null ? "null" : typeof raw}`);
|
|
884
|
+
throw new Error(reasoningLen > 0 ? `Empty response from model (finish_reason=${finish}; reasoning present but no content)` : "Empty response from model");
|
|
974
885
|
}
|
|
975
886
|
log4.debug(`call: response ${content.length} chars`);
|
|
976
887
|
return jsonMode ? parseModelJson(content) : content;
|
|
@@ -988,7 +899,7 @@ class OpenAICompatibleExecutor extends LLMExecutor {
|
|
|
988
899
|
parallelToolCalls: options.parallelToolCalls ?? false,
|
|
989
900
|
reasoningEffort: reasoning
|
|
990
901
|
});
|
|
991
|
-
const data = await this.sendRequest(body, options.reasoningEffort);
|
|
902
|
+
const data = await this.sendRequest(body, options.reasoningEffort, resolveLlmCaller(options));
|
|
992
903
|
const choice = data.choices?.[0];
|
|
993
904
|
if (!choice) {
|
|
994
905
|
log4.debug(`chatWithTools: no choice in response`);
|
|
@@ -1290,10 +1201,15 @@ class ZenMuxExecutor extends OpenAICompatibleExecutor {
|
|
|
1290
1201
|
// src/provider/providers/minimax.ts
|
|
1291
1202
|
class MiniMaxBase extends OpenAICompatibleExecutor {
|
|
1292
1203
|
buildBody(opts) {
|
|
1204
|
+
const jsonMode = opts.responseFormat !== undefined;
|
|
1293
1205
|
const body = super.buildBody(opts);
|
|
1294
1206
|
delete body["reasoning_effort"];
|
|
1207
|
+
delete body["response_format"];
|
|
1208
|
+
delete body["parallel_tool_calls"];
|
|
1295
1209
|
body["reasoning_split"] = true;
|
|
1296
|
-
body["
|
|
1210
|
+
body["max_completion_tokens"] = 16384;
|
|
1211
|
+
const wantThink = !jsonMode && opts.reasoningEffort !== undefined && opts.reasoningEffort !== "none";
|
|
1212
|
+
body["thinking"] = wantThink ? { type: "adaptive" } : { type: "disabled" };
|
|
1297
1213
|
return body;
|
|
1298
1214
|
}
|
|
1299
1215
|
}
|
|
@@ -1501,21 +1417,23 @@ class CloudflareWorkersExecutor extends LLMExecutor {
|
|
|
1501
1417
|
const accountId = readAuthString(opts.auth, "accountId", "CLOUDFLARE_ACCOUNT_ID");
|
|
1502
1418
|
this.baseURL = accountId ? `https://api.cloudflare.com/client/v4/accounts/${accountId}/ai/run` : "https://api.cloudflare.com/client/v4/accounts/__cf_account_id__/ai/run";
|
|
1503
1419
|
}
|
|
1504
|
-
async run(model, body) {
|
|
1420
|
+
async run(model, body, caller) {
|
|
1505
1421
|
const url = `${this.baseURL}/${encodeURIComponent(model)}`;
|
|
1422
|
+
const requestRaw = JSON.stringify(body);
|
|
1506
1423
|
const res = await fetch(url, {
|
|
1507
1424
|
method: "POST",
|
|
1508
1425
|
headers: {
|
|
1509
1426
|
Authorization: `Bearer ${this.apiKey}`,
|
|
1510
1427
|
"Content-Type": "application/json"
|
|
1511
1428
|
},
|
|
1512
|
-
body:
|
|
1429
|
+
body: requestRaw
|
|
1513
1430
|
});
|
|
1431
|
+
const responseRaw = await res.text().catch(() => "");
|
|
1432
|
+
logLlmWire(caller, requestRaw, responseRaw);
|
|
1514
1433
|
if (!res.ok) {
|
|
1515
|
-
|
|
1516
|
-
throw new Error(`cloudflare-workers request failed: ${res.status} ${res.statusText} body=${text.slice(0, 500)}`);
|
|
1434
|
+
throw new Error(`cloudflare-workers request failed: ${res.status} ${res.statusText} body=${responseRaw.slice(0, 500)}`);
|
|
1517
1435
|
}
|
|
1518
|
-
return
|
|
1436
|
+
return JSON.parse(responseRaw);
|
|
1519
1437
|
}
|
|
1520
1438
|
async call(model, options) {
|
|
1521
1439
|
const jsonMode = "jsonSchemaName" in options;
|
|
@@ -1536,7 +1454,7 @@ class CloudflareWorkersExecutor extends LLMExecutor {
|
|
|
1536
1454
|
if (reasoning !== "none") {
|
|
1537
1455
|
body["reasoning_effort"] = reasoning;
|
|
1538
1456
|
}
|
|
1539
|
-
const data = await this.run(model, body);
|
|
1457
|
+
const data = await this.run(model, body, resolveLlmCaller(options));
|
|
1540
1458
|
if (data.errors && data.errors.length > 0) {
|
|
1541
1459
|
throw new Error(`cloudflare-workers API error: ${data.errors.map((e) => e.message).join("; ")}`);
|
|
1542
1460
|
}
|
|
@@ -1565,7 +1483,7 @@ class CloudflareWorkersExecutor extends LLMExecutor {
|
|
|
1565
1483
|
if (reasoning !== "none") {
|
|
1566
1484
|
body["reasoning_effort"] = reasoning;
|
|
1567
1485
|
}
|
|
1568
|
-
const data = await this.run(model, body);
|
|
1486
|
+
const data = await this.run(model, body, resolveLlmCaller(options));
|
|
1569
1487
|
if (data.errors && data.errors.length > 0) {
|
|
1570
1488
|
throw new Error(`cloudflare-workers API error: ${data.errors.map((e) => e.message).join("; ")}`);
|
|
1571
1489
|
}
|
|
@@ -1732,7 +1650,8 @@ class AnthropicExecutor extends LLMExecutor {
|
|
|
1732
1650
|
this.baseURL = (extra.baseURL ?? readAuthString(opts.auth, "baseURL", "ANTHROPIC_BASE_URL") ?? "https://api.anthropic.com").replace(/\/v1\/?$/, "");
|
|
1733
1651
|
this.apiVersion = extra.apiVersion ?? "2023-06-01";
|
|
1734
1652
|
}
|
|
1735
|
-
async send(body) {
|
|
1653
|
+
async send(body, caller) {
|
|
1654
|
+
const requestRaw = JSON.stringify(body);
|
|
1736
1655
|
const res = await fetch(`${this.baseURL}/v1/messages`, {
|
|
1737
1656
|
method: "POST",
|
|
1738
1657
|
headers: {
|
|
@@ -1740,13 +1659,14 @@ class AnthropicExecutor extends LLMExecutor {
|
|
|
1740
1659
|
"anthropic-version": this.apiVersion,
|
|
1741
1660
|
"Content-Type": "application/json"
|
|
1742
1661
|
},
|
|
1743
|
-
body:
|
|
1662
|
+
body: requestRaw
|
|
1744
1663
|
});
|
|
1664
|
+
const responseRaw = await res.text().catch(() => "");
|
|
1665
|
+
logLlmWire(caller, requestRaw, responseRaw);
|
|
1745
1666
|
if (!res.ok) {
|
|
1746
|
-
|
|
1747
|
-
throw new Error(`anthropic request failed: ${res.status} ${res.statusText} body=${text.slice(0, 500)}`);
|
|
1667
|
+
throw new Error(`anthropic request failed: ${res.status} ${res.statusText} body=${responseRaw.slice(0, 500)}`);
|
|
1748
1668
|
}
|
|
1749
|
-
return
|
|
1669
|
+
return JSON.parse(responseRaw);
|
|
1750
1670
|
}
|
|
1751
1671
|
async call(model, options) {
|
|
1752
1672
|
const jsonMode = "jsonSchemaName" in options;
|
|
@@ -1768,7 +1688,7 @@ class AnthropicExecutor extends LLMExecutor {
|
|
|
1768
1688
|
budget_tokens: budget
|
|
1769
1689
|
};
|
|
1770
1690
|
}
|
|
1771
|
-
const data = await this.send(body);
|
|
1691
|
+
const data = await this.send(body, resolveLlmCaller(options));
|
|
1772
1692
|
if (data.error) {
|
|
1773
1693
|
throw new Error(`anthropic API error: ${data.error.message ?? "unknown"}`);
|
|
1774
1694
|
}
|
|
@@ -1799,7 +1719,7 @@ class AnthropicExecutor extends LLMExecutor {
|
|
|
1799
1719
|
budget_tokens: budget
|
|
1800
1720
|
};
|
|
1801
1721
|
}
|
|
1802
|
-
const data = await this.send(body);
|
|
1722
|
+
const data = await this.send(body, resolveLlmCaller(options));
|
|
1803
1723
|
if (data.error) {
|
|
1804
1724
|
throw new Error(`anthropic API error: ${data.error.message ?? "unknown"}`);
|
|
1805
1725
|
}
|
|
@@ -1962,7 +1882,7 @@ class BedrockExecutor extends LLMExecutor {
|
|
|
1962
1882
|
this.sessionToken = readAuthString(opts.auth, "sessionToken", "AWS_SESSION_TOKEN");
|
|
1963
1883
|
this.baseURL = `https://bedrock-runtime.${this.region}.amazonaws.com`;
|
|
1964
1884
|
}
|
|
1965
|
-
async invoke(model, body) {
|
|
1885
|
+
async invoke(model, body, caller) {
|
|
1966
1886
|
const bodyStr = JSON.stringify(body);
|
|
1967
1887
|
const path = `/model/${encodeURIComponent(model)}/invoke`;
|
|
1968
1888
|
const headers = signRequest({
|
|
@@ -1981,11 +1901,12 @@ class BedrockExecutor extends LLMExecutor {
|
|
|
1981
1901
|
headers,
|
|
1982
1902
|
body: bodyStr
|
|
1983
1903
|
});
|
|
1904
|
+
const responseRaw = await res.text().catch(() => "");
|
|
1905
|
+
logLlmWire(caller, bodyStr, responseRaw);
|
|
1984
1906
|
if (!res.ok) {
|
|
1985
|
-
|
|
1986
|
-
throw new Error(`bedrock request failed: ${res.status} ${res.statusText} body=${text.slice(0, 500)}`);
|
|
1907
|
+
throw new Error(`bedrock request failed: ${res.status} ${res.statusText} body=${responseRaw.slice(0, 500)}`);
|
|
1987
1908
|
}
|
|
1988
|
-
return
|
|
1909
|
+
return JSON.parse(responseRaw);
|
|
1989
1910
|
}
|
|
1990
1911
|
async call(model, options) {
|
|
1991
1912
|
const jsonMode = "jsonSchemaName" in options;
|
|
@@ -2000,7 +1921,7 @@ class BedrockExecutor extends LLMExecutor {
|
|
|
2000
1921
|
system: options.instruction,
|
|
2001
1922
|
messages: [{ role: "user", content: options.message }]
|
|
2002
1923
|
};
|
|
2003
|
-
const data = await this.invoke(model, body);
|
|
1924
|
+
const data = await this.invoke(model, body, resolveLlmCaller(options));
|
|
2004
1925
|
const { text } = extractAnthropicContent(data);
|
|
2005
1926
|
if (!text) {
|
|
2006
1927
|
throw new Error("Empty response from model");
|
|
@@ -2021,7 +1942,7 @@ class BedrockExecutor extends LLMExecutor {
|
|
|
2021
1942
|
messages: msgs,
|
|
2022
1943
|
tools: options.tools.map(toAnthropicTool2)
|
|
2023
1944
|
};
|
|
2024
|
-
const data = await this.invoke(model, body);
|
|
1945
|
+
const data = await this.invoke(model, body, resolveLlmCaller(options));
|
|
2025
1946
|
const { text, toolCalls } = extractAnthropicContent(data);
|
|
2026
1947
|
return { message: { content: text || undefined, toolCalls } };
|
|
2027
1948
|
}
|
|
@@ -2137,21 +2058,23 @@ class VertexExecutor extends LLMExecutor {
|
|
|
2137
2058
|
this.region = extra.region ?? readAuthString(opts.auth, "region", "GOOGLE_CLOUD_REGION") ?? "us-central1";
|
|
2138
2059
|
this.baseURL = this.project ? `https://${this.region}-aiplatform.googleapis.com/v1/projects/${this.project}/locations/${this.region}/publishers/google/models` : "https://us-central1-aiplatform.googleapis.com/v1";
|
|
2139
2060
|
}
|
|
2140
|
-
async generate(model, body) {
|
|
2061
|
+
async generate(model, body, caller) {
|
|
2141
2062
|
const url = `${this.baseURL}/${encodeURIComponent(model)}:generateContent`;
|
|
2063
|
+
const requestRaw = JSON.stringify(body);
|
|
2142
2064
|
const res = await fetch(url, {
|
|
2143
2065
|
method: "POST",
|
|
2144
2066
|
headers: {
|
|
2145
2067
|
Authorization: `Bearer ${this.apiKey}`,
|
|
2146
2068
|
"Content-Type": "application/json"
|
|
2147
2069
|
},
|
|
2148
|
-
body:
|
|
2070
|
+
body: requestRaw
|
|
2149
2071
|
});
|
|
2072
|
+
const responseRaw = await res.text().catch(() => "");
|
|
2073
|
+
logLlmWire(caller, requestRaw, responseRaw);
|
|
2150
2074
|
if (!res.ok) {
|
|
2151
|
-
|
|
2152
|
-
throw new Error(`vertex request failed: ${res.status} ${res.statusText} body=${text.slice(0, 500)}`);
|
|
2075
|
+
throw new Error(`vertex request failed: ${res.status} ${res.statusText} body=${responseRaw.slice(0, 500)}`);
|
|
2153
2076
|
}
|
|
2154
|
-
return
|
|
2077
|
+
return JSON.parse(responseRaw);
|
|
2155
2078
|
}
|
|
2156
2079
|
async call(model, options) {
|
|
2157
2080
|
const jsonMode = "jsonSchemaName" in options;
|
|
@@ -2172,7 +2095,7 @@ class VertexExecutor extends LLMExecutor {
|
|
|
2172
2095
|
responseSchema: options.jsonSchema
|
|
2173
2096
|
};
|
|
2174
2097
|
}
|
|
2175
|
-
const data = await this.generate(model, body);
|
|
2098
|
+
const data = await this.generate(model, body, resolveLlmCaller(options));
|
|
2176
2099
|
if (data.error) {
|
|
2177
2100
|
throw new Error(`vertex API error: ${data.error.message ?? "unknown"}`);
|
|
2178
2101
|
}
|
|
@@ -2191,7 +2114,7 @@ class VertexExecutor extends LLMExecutor {
|
|
|
2191
2114
|
systemInstruction: { parts: [{ text: options.instruction }] },
|
|
2192
2115
|
tools: options.tools.length > 0 ? [toGeminiTools(options.tools)] : undefined
|
|
2193
2116
|
};
|
|
2194
|
-
const data = await this.generate(model, body);
|
|
2117
|
+
const data = await this.generate(model, body, resolveLlmCaller(options));
|
|
2195
2118
|
if (data.error) {
|
|
2196
2119
|
throw new Error(`vertex API error: ${data.error.message ?? "unknown"}`);
|
|
2197
2120
|
}
|
|
@@ -2267,20 +2190,22 @@ class GitLabDuoExecutor extends LLMExecutor {
|
|
|
2267
2190
|
const extra = parsed.success ? parsed.data : {};
|
|
2268
2191
|
this.baseURL = (extra.baseURL ?? readAuthString(opts.auth, "baseURL", "GITLAB_BASE_URL") ?? "https://gitlab.com/api/v4/ai/llm/proxy").replace(/\/+$/, "");
|
|
2269
2192
|
}
|
|
2270
|
-
async send(body) {
|
|
2193
|
+
async send(body, caller) {
|
|
2194
|
+
const requestRaw = JSON.stringify(body);
|
|
2271
2195
|
const res = await fetch(this.baseURL, {
|
|
2272
2196
|
method: "POST",
|
|
2273
2197
|
headers: {
|
|
2274
2198
|
Authorization: `Bearer ${this.apiKey}`,
|
|
2275
2199
|
"Content-Type": "application/json"
|
|
2276
2200
|
},
|
|
2277
|
-
body:
|
|
2201
|
+
body: requestRaw
|
|
2278
2202
|
});
|
|
2203
|
+
const responseRaw = await res.text().catch(() => "");
|
|
2204
|
+
logLlmWire(caller, requestRaw, responseRaw);
|
|
2279
2205
|
if (!res.ok) {
|
|
2280
|
-
|
|
2281
|
-
throw new Error(`gitlab-duo request failed: ${res.status} ${res.statusText} body=${text.slice(0, 500)}`);
|
|
2206
|
+
throw new Error(`gitlab-duo request failed: ${res.status} ${res.statusText} body=${responseRaw.slice(0, 500)}`);
|
|
2282
2207
|
}
|
|
2283
|
-
return
|
|
2208
|
+
return JSON.parse(responseRaw);
|
|
2284
2209
|
}
|
|
2285
2210
|
async call(model, options) {
|
|
2286
2211
|
const jsonMode = "jsonSchemaName" in options;
|
|
@@ -2293,7 +2218,7 @@ class GitLabDuoExecutor extends LLMExecutor {
|
|
|
2293
2218
|
{ role: "user", content: options.message }
|
|
2294
2219
|
]
|
|
2295
2220
|
};
|
|
2296
|
-
const data = await this.send(body);
|
|
2221
|
+
const data = await this.send(body, resolveLlmCaller(options));
|
|
2297
2222
|
if (data.error) {
|
|
2298
2223
|
throw new Error(`gitlab-duo API error: ${data.error.message ?? "unknown"}`);
|
|
2299
2224
|
}
|
|
@@ -2314,7 +2239,7 @@ class GitLabDuoExecutor extends LLMExecutor {
|
|
|
2314
2239
|
],
|
|
2315
2240
|
tools: options.tools.map(toTool)
|
|
2316
2241
|
};
|
|
2317
|
-
const data = await this.send(body);
|
|
2242
|
+
const data = await this.send(body, resolveLlmCaller(options));
|
|
2318
2243
|
if (data.error) {
|
|
2319
2244
|
throw new Error(`gitlab-duo API error: ${data.error.message ?? "unknown"}`);
|
|
2320
2245
|
}
|
|
@@ -2382,20 +2307,22 @@ class SnowflakeCortexExecutor extends LLMExecutor {
|
|
|
2382
2307
|
const account = extra.account ?? readAuthString(opts.auth, "account", "SNOWFLAKE_ACCOUNT") ?? "";
|
|
2383
2308
|
this.baseURL = account ? `https://${account}.snowflakecomputing.com/api/v2/cortex/inference:complete` : "https://__account__.snowflakecomputing.com/api/v2/cortex/inference:complete";
|
|
2384
2309
|
}
|
|
2385
|
-
async send(body) {
|
|
2310
|
+
async send(body, caller) {
|
|
2311
|
+
const requestRaw = JSON.stringify(body);
|
|
2386
2312
|
const res = await fetch(this.baseURL, {
|
|
2387
2313
|
method: "POST",
|
|
2388
2314
|
headers: {
|
|
2389
2315
|
Authorization: `Bearer ${this.apiKey}`,
|
|
2390
2316
|
"Content-Type": "application/json"
|
|
2391
2317
|
},
|
|
2392
|
-
body:
|
|
2318
|
+
body: requestRaw
|
|
2393
2319
|
});
|
|
2320
|
+
const responseRaw = await res.text().catch(() => "");
|
|
2321
|
+
logLlmWire(caller, requestRaw, responseRaw);
|
|
2394
2322
|
if (!res.ok) {
|
|
2395
|
-
|
|
2396
|
-
throw new Error(`snowflake-cortex request failed: ${res.status} ${res.statusText} body=${text.slice(0, 500)}`);
|
|
2323
|
+
throw new Error(`snowflake-cortex request failed: ${res.status} ${res.statusText} body=${responseRaw.slice(0, 500)}`);
|
|
2397
2324
|
}
|
|
2398
|
-
return
|
|
2325
|
+
return JSON.parse(responseRaw);
|
|
2399
2326
|
}
|
|
2400
2327
|
async call(model, options) {
|
|
2401
2328
|
const jsonMode = "jsonSchemaName" in options;
|
|
@@ -2412,7 +2339,7 @@ class SnowflakeCortexExecutor extends LLMExecutor {
|
|
|
2412
2339
|
if (reasoning !== "none") {
|
|
2413
2340
|
body["reasoning_effort"] = reasoning;
|
|
2414
2341
|
}
|
|
2415
|
-
const data = await this.send(body);
|
|
2342
|
+
const data = await this.send(body, resolveLlmCaller(options));
|
|
2416
2343
|
if (data.error) {
|
|
2417
2344
|
throw new Error(`snowflake-cortex API error: ${data.error.message ?? "unknown"}`);
|
|
2418
2345
|
}
|
|
@@ -2433,7 +2360,7 @@ class SnowflakeCortexExecutor extends LLMExecutor {
|
|
|
2433
2360
|
],
|
|
2434
2361
|
tools: options.tools.map(toCortexTool)
|
|
2435
2362
|
};
|
|
2436
|
-
const data = await this.send(body);
|
|
2363
|
+
const data = await this.send(body, resolveLlmCaller(options));
|
|
2437
2364
|
if (data.error) {
|
|
2438
2365
|
throw new Error(`snowflake-cortex API error: ${data.error.message ?? "unknown"}`);
|
|
2439
2366
|
}
|
|
@@ -2822,7 +2749,11 @@ class Brain {
|
|
|
2822
2749
|
async regenerateSchedules() {
|
|
2823
2750
|
const today = new Date;
|
|
2824
2751
|
const nextMonth = new Date(today.getFullYear(), today.getMonth() + 1, today.getDate());
|
|
2825
|
-
await this.createMonthlySchedule(nextMonth);
|
|
2752
|
+
const monthly = await this.createMonthlySchedule(nextMonth);
|
|
2753
|
+
if (!monthly) {
|
|
2754
|
+
log7.debug(`regenerateSchedules: skip daily — monthly schedule generation failed`);
|
|
2755
|
+
return;
|
|
2756
|
+
}
|
|
2826
2757
|
const tomorrow = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 1);
|
|
2827
2758
|
await this.createDailySchedule(tomorrow);
|
|
2828
2759
|
await this.createDailySchedule(today);
|
|
@@ -3704,7 +3635,7 @@ class BaseChannel {
|
|
|
3704
3635
|
static all() {
|
|
3705
3636
|
return Array.from(BaseChannel.activeChannels.values());
|
|
3706
3637
|
}
|
|
3707
|
-
static
|
|
3638
|
+
static forceDo(brainId, action) {
|
|
3708
3639
|
const channel = BaseChannel.activeChannels.get(brainId);
|
|
3709
3640
|
if (!channel) {
|
|
3710
3641
|
return {
|
|
@@ -3713,12 +3644,20 @@ class BaseChannel {
|
|
|
3713
3644
|
};
|
|
3714
3645
|
}
|
|
3715
3646
|
const displayName = channel.brain.brainbase.displayName;
|
|
3716
|
-
logger.info(`do ${action}:
|
|
3717
|
-
|
|
3718
|
-
|
|
3719
|
-
|
|
3720
|
-
|
|
3721
|
-
|
|
3647
|
+
logger.info(`do ${action}: queued for "${displayName}" (${brainId})`);
|
|
3648
|
+
(async () => {
|
|
3649
|
+
try {
|
|
3650
|
+
if (action === "generateSchedule") {
|
|
3651
|
+
await channel.regenerateSchedules();
|
|
3652
|
+
} else {
|
|
3653
|
+
await channel.runSleepMemory(true);
|
|
3654
|
+
}
|
|
3655
|
+
logger.success(`do ${action}: done for "${displayName}" (${brainId})`);
|
|
3656
|
+
} catch (error) {
|
|
3657
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
3658
|
+
logger.error(`do ${action}: failed for "${displayName}" (${brainId}): ${reason}`);
|
|
3659
|
+
}
|
|
3660
|
+
})();
|
|
3722
3661
|
return { ok: true, displayName };
|
|
3723
3662
|
}
|
|
3724
3663
|
static async view(brainId, thing) {
|
|
@@ -4303,7 +4242,7 @@ defineCommand({
|
|
|
4303
4242
|
if (typeof brainId !== "string" || brainId.trim().length === 0) {
|
|
4304
4243
|
return { ok: false, error: "missing brainId" };
|
|
4305
4244
|
}
|
|
4306
|
-
const result =
|
|
4245
|
+
const result = BaseChannel.forceDo(brainId.trim(), action);
|
|
4307
4246
|
if (!result.ok)
|
|
4308
4247
|
return result;
|
|
4309
4248
|
return {
|
|
@@ -4380,8 +4319,8 @@ async function startChannels() {
|
|
|
4380
4319
|
async function daemon() {
|
|
4381
4320
|
const logDir = join5(config.brainboxRoot, "logs");
|
|
4382
4321
|
logger.configure({ logDir });
|
|
4383
|
-
configureLlmLog(join5(logDir, "llm"));
|
|
4384
|
-
logger.debug(`daemon: boot (
|
|
4322
|
+
configureLlmLog(config.debug ? join5(logDir, "llm") : undefined);
|
|
4323
|
+
logger.debug(`daemon: boot (debug=${config.debug}, logDir=${logDir}, llmLog=${config.debug})`);
|
|
4385
4324
|
const started = await startChannels();
|
|
4386
4325
|
if (started === 0) {
|
|
4387
4326
|
logger.info("No activated brains with channels. Daemon idling.");
|
|
@@ -4559,7 +4498,7 @@ async function doAction(action, brainId) {
|
|
|
4559
4498
|
args: { action, brainId }
|
|
4560
4499
|
});
|
|
4561
4500
|
const name = response.result?.displayName ?? brainId;
|
|
4562
|
-
logger.success(`
|
|
4501
|
+
logger.success(`Successfully sent ${action} for "${name}" (${brainId}).`);
|
|
4563
4502
|
}
|
|
4564
4503
|
async function viewThing(thing, brainId) {
|
|
4565
4504
|
if (!VIEW_THINGS.includes(thing)) {
|