@ljoukov/llm 4.0.1 → 4.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +41 -1
- package/dist/index.cjs +2010 -1047
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -5
- package/dist/index.d.ts +17 -5
- package/dist/index.js +2010 -1047
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/llm.ts
|
|
2
|
-
import { Buffer as
|
|
3
|
-
import { AsyncLocalStorage } from "async_hooks";
|
|
2
|
+
import { Buffer as Buffer4 } from "buffer";
|
|
3
|
+
import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
|
|
4
4
|
import { randomBytes } from "crypto";
|
|
5
5
|
import {
|
|
6
6
|
FinishReason,
|
|
@@ -205,6 +205,16 @@ var OPENAI_GPT_52_PRICING = {
|
|
|
205
205
|
cachedRate: 0.175 / 1e6,
|
|
206
206
|
outputRate: 14 / 1e6
|
|
207
207
|
};
|
|
208
|
+
var OPENAI_GPT_54_PRICING = {
|
|
209
|
+
inputRate: 2.5 / 1e6,
|
|
210
|
+
cachedRate: 0.25 / 1e6,
|
|
211
|
+
outputRate: 15 / 1e6
|
|
212
|
+
};
|
|
213
|
+
var OPENAI_GPT_54_PRIORITY_PRICING = {
|
|
214
|
+
inputRate: 5 / 1e6,
|
|
215
|
+
cachedRate: 0.5 / 1e6,
|
|
216
|
+
outputRate: 30 / 1e6
|
|
217
|
+
};
|
|
208
218
|
var OPENAI_GPT_53_CODEX_PRICING = {
|
|
209
219
|
inputRate: 1.25 / 1e6,
|
|
210
220
|
cachedRate: 0.125 / 1e6,
|
|
@@ -216,6 +226,12 @@ var OPENAI_GPT_5_MINI_PRICING = {
|
|
|
216
226
|
outputRate: 2 / 1e6
|
|
217
227
|
};
|
|
218
228
|
function getOpenAiPricing(modelId) {
|
|
229
|
+
if (modelId.includes("gpt-5.4-fast")) {
|
|
230
|
+
return OPENAI_GPT_54_PRIORITY_PRICING;
|
|
231
|
+
}
|
|
232
|
+
if (modelId.includes("gpt-5.4")) {
|
|
233
|
+
return OPENAI_GPT_54_PRICING;
|
|
234
|
+
}
|
|
219
235
|
if (modelId.includes("gpt-5.3-codex-spark")) {
|
|
220
236
|
return OPENAI_GPT_5_MINI_PRICING;
|
|
221
237
|
}
|
|
@@ -2626,11 +2642,18 @@ async function runOpenAiCall(fn, modelId, runOptions) {
|
|
|
2626
2642
|
}
|
|
2627
2643
|
|
|
2628
2644
|
// src/openai/models.ts
|
|
2629
|
-
var OPENAI_MODEL_IDS = [
|
|
2645
|
+
var OPENAI_MODEL_IDS = [
|
|
2646
|
+
"gpt-5.4",
|
|
2647
|
+
"gpt-5.3-codex",
|
|
2648
|
+
"gpt-5.2",
|
|
2649
|
+
"gpt-5.1-codex-mini"
|
|
2650
|
+
];
|
|
2630
2651
|
function isOpenAiModelId(value) {
|
|
2631
2652
|
return OPENAI_MODEL_IDS.includes(value);
|
|
2632
2653
|
}
|
|
2633
2654
|
var CHATGPT_MODEL_IDS = [
|
|
2655
|
+
"chatgpt-gpt-5.4",
|
|
2656
|
+
"chatgpt-gpt-5.4-fast",
|
|
2634
2657
|
"chatgpt-gpt-5.3-codex",
|
|
2635
2658
|
"chatgpt-gpt-5.3-codex-spark",
|
|
2636
2659
|
"chatgpt-gpt-5.2",
|
|
@@ -2642,9 +2665,387 @@ function isChatGptModelId(value) {
|
|
|
2642
2665
|
function stripChatGptPrefix(model) {
|
|
2643
2666
|
return model.slice("chatgpt-".length);
|
|
2644
2667
|
}
|
|
2668
|
+
function resolveChatGptProviderModel(model) {
|
|
2669
|
+
switch (model) {
|
|
2670
|
+
case "chatgpt-gpt-5.4-fast":
|
|
2671
|
+
return "gpt-5.4";
|
|
2672
|
+
default:
|
|
2673
|
+
return stripChatGptPrefix(model);
|
|
2674
|
+
}
|
|
2675
|
+
}
|
|
2676
|
+
function resolveChatGptServiceTier(model) {
|
|
2677
|
+
return model === "chatgpt-gpt-5.4-fast" ? "priority" : void 0;
|
|
2678
|
+
}
|
|
2679
|
+
|
|
2680
|
+
// src/agentLogging.ts
|
|
2681
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
2682
|
+
import { Buffer as Buffer3 } from "buffer";
|
|
2683
|
+
import { appendFile, mkdir, writeFile } from "fs/promises";
|
|
2684
|
+
import path3 from "path";
|
|
2685
|
+
function toIsoNow() {
|
|
2686
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
2687
|
+
}
|
|
2688
|
+
function toErrorMessage(error) {
|
|
2689
|
+
if (error instanceof Error && error.message) {
|
|
2690
|
+
return error.message;
|
|
2691
|
+
}
|
|
2692
|
+
if (typeof error === "string") {
|
|
2693
|
+
return error;
|
|
2694
|
+
}
|
|
2695
|
+
return String(error);
|
|
2696
|
+
}
|
|
2697
|
+
function isPromiseLike(value) {
|
|
2698
|
+
return (typeof value === "object" || typeof value === "function") && value !== null && typeof value.then === "function";
|
|
2699
|
+
}
|
|
2700
|
+
function normalisePathSegment(value) {
|
|
2701
|
+
const cleaned = value.trim().replace(/[^a-z0-9._-]+/giu, "-").replace(/-{2,}/g, "-").replace(/^-+|-+$/g, "");
|
|
2702
|
+
return cleaned.length > 0 ? cleaned : "segment";
|
|
2703
|
+
}
|
|
2704
|
+
function ensureTrailingNewline(value) {
|
|
2705
|
+
return value.endsWith("\n") ? value : `${value}
|
|
2706
|
+
`;
|
|
2707
|
+
}
|
|
2708
|
+
function redactDataUrlPayload(value) {
|
|
2709
|
+
if (!value.toLowerCase().startsWith("data:")) {
|
|
2710
|
+
return value;
|
|
2711
|
+
}
|
|
2712
|
+
const commaIndex = value.indexOf(",");
|
|
2713
|
+
if (commaIndex < 0) {
|
|
2714
|
+
return value;
|
|
2715
|
+
}
|
|
2716
|
+
return `${value.slice(0, commaIndex + 1)}...`;
|
|
2717
|
+
}
|
|
2718
|
+
function sanitiseLogValue(value, seen = /* @__PURE__ */ new WeakSet()) {
|
|
2719
|
+
if (typeof value === "string") {
|
|
2720
|
+
return redactDataUrlPayload(value);
|
|
2721
|
+
}
|
|
2722
|
+
if (typeof value === "number" || typeof value === "boolean" || value === null || value === void 0) {
|
|
2723
|
+
return value;
|
|
2724
|
+
}
|
|
2725
|
+
if (Array.isArray(value)) {
|
|
2726
|
+
return value.map((entry) => sanitiseLogValue(entry, seen));
|
|
2727
|
+
}
|
|
2728
|
+
if (typeof value !== "object") {
|
|
2729
|
+
return String(value);
|
|
2730
|
+
}
|
|
2731
|
+
if (seen.has(value)) {
|
|
2732
|
+
return "[circular]";
|
|
2733
|
+
}
|
|
2734
|
+
seen.add(value);
|
|
2735
|
+
const record = value;
|
|
2736
|
+
const output = {};
|
|
2737
|
+
const hasInlineMime = typeof record.mimeType === "string" && record.mimeType.trim().length > 0 || typeof record.mime_type === "string" && record.mime_type.trim().length > 0;
|
|
2738
|
+
for (const [key, entryValue] of Object.entries(record)) {
|
|
2739
|
+
if (key === "image_url") {
|
|
2740
|
+
if (typeof entryValue === "string") {
|
|
2741
|
+
output[key] = redactDataUrlPayload(entryValue);
|
|
2742
|
+
continue;
|
|
2743
|
+
}
|
|
2744
|
+
if (entryValue && typeof entryValue === "object") {
|
|
2745
|
+
const nested = entryValue;
|
|
2746
|
+
if (typeof nested.url === "string") {
|
|
2747
|
+
output[key] = {
|
|
2748
|
+
...nested,
|
|
2749
|
+
url: redactDataUrlPayload(nested.url)
|
|
2750
|
+
};
|
|
2751
|
+
continue;
|
|
2752
|
+
}
|
|
2753
|
+
}
|
|
2754
|
+
}
|
|
2755
|
+
if (key === "data" && hasInlineMime && typeof entryValue === "string") {
|
|
2756
|
+
output[key] = `[omitted:${Buffer3.byteLength(entryValue, "utf8")}b]`;
|
|
2757
|
+
continue;
|
|
2758
|
+
}
|
|
2759
|
+
output[key] = sanitiseLogValue(entryValue, seen);
|
|
2760
|
+
}
|
|
2761
|
+
return output;
|
|
2762
|
+
}
|
|
2763
|
+
function serialiseForSnippet(value) {
|
|
2764
|
+
if (typeof value === "string") {
|
|
2765
|
+
return value;
|
|
2766
|
+
}
|
|
2767
|
+
try {
|
|
2768
|
+
return JSON.stringify(sanitiseLogValue(value));
|
|
2769
|
+
} catch {
|
|
2770
|
+
return String(value);
|
|
2771
|
+
}
|
|
2772
|
+
}
|
|
2773
|
+
function formatToolLogSnippet(value) {
|
|
2774
|
+
const compact = serialiseForSnippet(value).replace(/\s+/gu, " ").trim();
|
|
2775
|
+
if (compact.length === 0) {
|
|
2776
|
+
return "<empty>";
|
|
2777
|
+
}
|
|
2778
|
+
const max = 600;
|
|
2779
|
+
if (compact.length <= max) {
|
|
2780
|
+
return compact;
|
|
2781
|
+
}
|
|
2782
|
+
return `${compact.slice(0, max)}...`;
|
|
2783
|
+
}
|
|
2784
|
+
function formatUsd(value) {
|
|
2785
|
+
const amount = typeof value === "number" && Number.isFinite(value) ? Math.max(0, value) : 0;
|
|
2786
|
+
return amount.toFixed(6);
|
|
2787
|
+
}
|
|
2788
|
+
function appendToolCallStreamLog(options) {
|
|
2789
|
+
const event = options.event;
|
|
2790
|
+
if (event.type !== "tool_call") {
|
|
2791
|
+
return;
|
|
2792
|
+
}
|
|
2793
|
+
const callIdSegment = typeof event.callId === "string" && event.callId.trim().length > 0 ? ` callId=${event.callId}` : "";
|
|
2794
|
+
const prefix = [
|
|
2795
|
+
`tool_call_${event.phase}:`,
|
|
2796
|
+
`turn=${event.turn.toString()}`,
|
|
2797
|
+
`index=${event.toolIndex.toString()}`,
|
|
2798
|
+
`tool=${event.toolName}${callIdSegment}`
|
|
2799
|
+
].join(" ");
|
|
2800
|
+
if (event.phase === "started") {
|
|
2801
|
+
options.append(prefix);
|
|
2802
|
+
options.append(`tool_call_input: ${formatToolLogSnippet(sanitiseLogValue(event.input))}`);
|
|
2803
|
+
return;
|
|
2804
|
+
}
|
|
2805
|
+
const durationSegment = typeof event.durationMs === "number" && Number.isFinite(event.durationMs) ? ` durationMs=${Math.max(0, Math.round(event.durationMs)).toString()}` : "";
|
|
2806
|
+
options.append(`${prefix} status=${event.error ? "error" : "ok"}${durationSegment}`);
|
|
2807
|
+
options.append(`tool_call_output: ${formatToolLogSnippet(sanitiseLogValue(event.output))}`);
|
|
2808
|
+
if (typeof event.error === "string" && event.error.trim().length > 0) {
|
|
2809
|
+
options.append(`tool_call_error: ${event.error.trim()}`);
|
|
2810
|
+
}
|
|
2811
|
+
}
|
|
2812
|
+
function appendAgentStreamEventLog(options) {
|
|
2813
|
+
const event = options.event;
|
|
2814
|
+
switch (event.type) {
|
|
2815
|
+
case "delta": {
|
|
2816
|
+
const channelPrefix = event.channel === "thought" ? "thought_delta" : "response_delta";
|
|
2817
|
+
options.append(`${channelPrefix}: ${event.text}`);
|
|
2818
|
+
return;
|
|
2819
|
+
}
|
|
2820
|
+
case "model": {
|
|
2821
|
+
options.append(`model: ${event.modelVersion}`);
|
|
2822
|
+
return;
|
|
2823
|
+
}
|
|
2824
|
+
case "usage": {
|
|
2825
|
+
options.append(
|
|
2826
|
+
[
|
|
2827
|
+
"usage:",
|
|
2828
|
+
`modelVersion=${event.modelVersion}`,
|
|
2829
|
+
`costUsd=${formatUsd(event.costUsd)}`,
|
|
2830
|
+
`tokens=${formatToolLogSnippet(sanitiseLogValue(event.usage))}`
|
|
2831
|
+
].join(" ")
|
|
2832
|
+
);
|
|
2833
|
+
return;
|
|
2834
|
+
}
|
|
2835
|
+
case "blocked": {
|
|
2836
|
+
options.append("blocked");
|
|
2837
|
+
return;
|
|
2838
|
+
}
|
|
2839
|
+
case "tool_call": {
|
|
2840
|
+
appendToolCallStreamLog({
|
|
2841
|
+
event,
|
|
2842
|
+
append: options.append
|
|
2843
|
+
});
|
|
2844
|
+
return;
|
|
2845
|
+
}
|
|
2846
|
+
}
|
|
2847
|
+
}
|
|
2848
|
+
var AgentLoggingSessionImpl = class {
|
|
2849
|
+
workspaceDir;
|
|
2850
|
+
logsRootDir;
|
|
2851
|
+
mirrorToConsole;
|
|
2852
|
+
sink;
|
|
2853
|
+
agentLogPath;
|
|
2854
|
+
ensureReady;
|
|
2855
|
+
pending = /* @__PURE__ */ new Set();
|
|
2856
|
+
lineChain = Promise.resolve();
|
|
2857
|
+
callCounter = 0;
|
|
2858
|
+
constructor(config) {
|
|
2859
|
+
this.workspaceDir = path3.resolve(config.workspaceDir ?? process.cwd());
|
|
2860
|
+
this.logsRootDir = path3.join(path3.dirname(this.workspaceDir), "logs");
|
|
2861
|
+
this.mirrorToConsole = config.mirrorToConsole !== false;
|
|
2862
|
+
this.sink = config.sink;
|
|
2863
|
+
this.agentLogPath = path3.join(this.workspaceDir, "agent.log");
|
|
2864
|
+
this.ensureReady = this.prepare();
|
|
2865
|
+
this.track(this.ensureReady);
|
|
2866
|
+
}
|
|
2867
|
+
async prepare() {
|
|
2868
|
+
await mkdir(this.workspaceDir, { recursive: true });
|
|
2869
|
+
await mkdir(this.logsRootDir, { recursive: true });
|
|
2870
|
+
}
|
|
2871
|
+
track(task) {
|
|
2872
|
+
this.pending.add(task);
|
|
2873
|
+
task.finally(() => {
|
|
2874
|
+
this.pending.delete(task);
|
|
2875
|
+
});
|
|
2876
|
+
}
|
|
2877
|
+
enqueueLineWrite(line) {
|
|
2878
|
+
const next = this.lineChain.then(async () => {
|
|
2879
|
+
await this.ensureReady;
|
|
2880
|
+
await appendFile(this.agentLogPath, `${line}
|
|
2881
|
+
`, "utf8");
|
|
2882
|
+
const sinkResult = this.sink?.append(line);
|
|
2883
|
+
if (isPromiseLike(sinkResult)) {
|
|
2884
|
+
await sinkResult;
|
|
2885
|
+
}
|
|
2886
|
+
});
|
|
2887
|
+
const tracked = next.catch(() => void 0);
|
|
2888
|
+
this.lineChain = tracked;
|
|
2889
|
+
this.track(tracked);
|
|
2890
|
+
}
|
|
2891
|
+
logLine(line) {
|
|
2892
|
+
const timestamped = `${toIsoNow()} ${line}`;
|
|
2893
|
+
if (this.mirrorToConsole) {
|
|
2894
|
+
console.log(timestamped);
|
|
2895
|
+
}
|
|
2896
|
+
this.enqueueLineWrite(timestamped);
|
|
2897
|
+
}
|
|
2898
|
+
startLlmCall(input) {
|
|
2899
|
+
const callNumber = this.callCounter + 1;
|
|
2900
|
+
this.callCounter = callNumber;
|
|
2901
|
+
const timestampSegment = toIsoNow().replace(/[:]/g, "-");
|
|
2902
|
+
const modelSegment = normalisePathSegment(input.modelId);
|
|
2903
|
+
const baseDir = path3.join(
|
|
2904
|
+
this.logsRootDir,
|
|
2905
|
+
`${timestampSegment}-${callNumber.toString().padStart(4, "0")}`,
|
|
2906
|
+
modelSegment
|
|
2907
|
+
);
|
|
2908
|
+
const responsePath = path3.join(baseDir, "response.txt");
|
|
2909
|
+
const thoughtsPath = path3.join(baseDir, "thoughts.txt");
|
|
2910
|
+
const responseMetadataPath = path3.join(baseDir, "response.metadata.json");
|
|
2911
|
+
let chain = this.ensureReady.then(async () => {
|
|
2912
|
+
await mkdir(baseDir, { recursive: true });
|
|
2913
|
+
const requestText = input.requestText.trim().length > 0 ? input.requestText : "<empty request>";
|
|
2914
|
+
await writeFile(
|
|
2915
|
+
path3.join(baseDir, "request.txt"),
|
|
2916
|
+
ensureTrailingNewline(requestText),
|
|
2917
|
+
"utf8"
|
|
2918
|
+
);
|
|
2919
|
+
const requestMetadata = {
|
|
2920
|
+
capturedAt: toIsoNow(),
|
|
2921
|
+
provider: input.provider,
|
|
2922
|
+
modelId: input.modelId,
|
|
2923
|
+
...input.requestMetadata ? { request: sanitiseLogValue(input.requestMetadata) } : {}
|
|
2924
|
+
};
|
|
2925
|
+
await writeFile(
|
|
2926
|
+
path3.join(baseDir, "request.metadata.json"),
|
|
2927
|
+
`${JSON.stringify(requestMetadata, null, 2)}
|
|
2928
|
+
`,
|
|
2929
|
+
"utf8"
|
|
2930
|
+
);
|
|
2931
|
+
const usedNames = /* @__PURE__ */ new Set();
|
|
2932
|
+
for (const attachment of input.attachments ?? []) {
|
|
2933
|
+
let filename = normalisePathSegment(attachment.filename);
|
|
2934
|
+
if (!filename.includes(".")) {
|
|
2935
|
+
filename = `${filename}.bin`;
|
|
2936
|
+
}
|
|
2937
|
+
const ext = path3.extname(filename);
|
|
2938
|
+
const base = ext.length > 0 ? filename.slice(0, -ext.length) : filename;
|
|
2939
|
+
let candidate = filename;
|
|
2940
|
+
let duplicateIndex = 2;
|
|
2941
|
+
while (usedNames.has(candidate)) {
|
|
2942
|
+
candidate = `${base}-${duplicateIndex.toString()}${ext}`;
|
|
2943
|
+
duplicateIndex += 1;
|
|
2944
|
+
}
|
|
2945
|
+
usedNames.add(candidate);
|
|
2946
|
+
await writeFile(path3.join(baseDir, candidate), attachment.bytes);
|
|
2947
|
+
}
|
|
2948
|
+
}).catch(() => void 0);
|
|
2949
|
+
this.track(chain);
|
|
2950
|
+
let closed = false;
|
|
2951
|
+
const enqueue = (operation) => {
|
|
2952
|
+
const next = chain.then(operation);
|
|
2953
|
+
const tracked = next.catch(() => void 0);
|
|
2954
|
+
chain = tracked;
|
|
2955
|
+
this.track(tracked);
|
|
2956
|
+
};
|
|
2957
|
+
return {
|
|
2958
|
+
appendThoughtDelta: (text) => {
|
|
2959
|
+
if (closed || text.length === 0) {
|
|
2960
|
+
return;
|
|
2961
|
+
}
|
|
2962
|
+
enqueue(async () => {
|
|
2963
|
+
await appendFile(thoughtsPath, text, "utf8");
|
|
2964
|
+
});
|
|
2965
|
+
},
|
|
2966
|
+
appendResponseDelta: (text) => {
|
|
2967
|
+
if (closed || text.length === 0) {
|
|
2968
|
+
return;
|
|
2969
|
+
}
|
|
2970
|
+
enqueue(async () => {
|
|
2971
|
+
await appendFile(responsePath, text, "utf8");
|
|
2972
|
+
});
|
|
2973
|
+
},
|
|
2974
|
+
complete: (metadata) => {
|
|
2975
|
+
if (closed) {
|
|
2976
|
+
return;
|
|
2977
|
+
}
|
|
2978
|
+
closed = true;
|
|
2979
|
+
enqueue(async () => {
|
|
2980
|
+
const payload = {
|
|
2981
|
+
capturedAt: toIsoNow(),
|
|
2982
|
+
status: "completed"
|
|
2983
|
+
};
|
|
2984
|
+
if (metadata) {
|
|
2985
|
+
const sanitised = sanitiseLogValue(metadata);
|
|
2986
|
+
if (sanitised && typeof sanitised === "object" && !Array.isArray(sanitised)) {
|
|
2987
|
+
Object.assign(payload, sanitised);
|
|
2988
|
+
} else if (sanitised !== void 0) {
|
|
2989
|
+
payload.metadata = sanitised;
|
|
2990
|
+
}
|
|
2991
|
+
}
|
|
2992
|
+
await writeFile(responseMetadataPath, `${JSON.stringify(payload, null, 2)}
|
|
2993
|
+
`, "utf8");
|
|
2994
|
+
});
|
|
2995
|
+
},
|
|
2996
|
+
fail: (error, metadata) => {
|
|
2997
|
+
if (closed) {
|
|
2998
|
+
return;
|
|
2999
|
+
}
|
|
3000
|
+
closed = true;
|
|
3001
|
+
enqueue(async () => {
|
|
3002
|
+
const payload = {
|
|
3003
|
+
capturedAt: toIsoNow(),
|
|
3004
|
+
status: "failed",
|
|
3005
|
+
error: toErrorMessage(error)
|
|
3006
|
+
};
|
|
3007
|
+
if (metadata) {
|
|
3008
|
+
const sanitised = sanitiseLogValue(metadata);
|
|
3009
|
+
if (sanitised && typeof sanitised === "object" && !Array.isArray(sanitised)) {
|
|
3010
|
+
Object.assign(payload, sanitised);
|
|
3011
|
+
} else if (sanitised !== void 0) {
|
|
3012
|
+
payload.metadata = sanitised;
|
|
3013
|
+
}
|
|
3014
|
+
}
|
|
3015
|
+
await writeFile(responseMetadataPath, `${JSON.stringify(payload, null, 2)}
|
|
3016
|
+
`, "utf8");
|
|
3017
|
+
});
|
|
3018
|
+
}
|
|
3019
|
+
};
|
|
3020
|
+
}
|
|
3021
|
+
async flush() {
|
|
3022
|
+
while (this.pending.size > 0) {
|
|
3023
|
+
await Promise.allSettled([...this.pending]);
|
|
3024
|
+
}
|
|
3025
|
+
if (typeof this.sink?.flush === "function") {
|
|
3026
|
+
try {
|
|
3027
|
+
await this.sink.flush();
|
|
3028
|
+
} catch {
|
|
3029
|
+
}
|
|
3030
|
+
}
|
|
3031
|
+
}
|
|
3032
|
+
};
|
|
3033
|
+
var loggingSessionStorage = new AsyncLocalStorage();
|
|
3034
|
+
function createAgentLoggingSession(config) {
|
|
3035
|
+
return new AgentLoggingSessionImpl(config);
|
|
3036
|
+
}
|
|
3037
|
+
function runWithAgentLoggingSession(session, fn) {
|
|
3038
|
+
if (!session) {
|
|
3039
|
+
return fn();
|
|
3040
|
+
}
|
|
3041
|
+
return loggingSessionStorage.run(session, fn);
|
|
3042
|
+
}
|
|
3043
|
+
function getCurrentAgentLoggingSession() {
|
|
3044
|
+
return loggingSessionStorage.getStore();
|
|
3045
|
+
}
|
|
2645
3046
|
|
|
2646
3047
|
// src/llm.ts
|
|
2647
|
-
var toolCallContextStorage = new
|
|
3048
|
+
var toolCallContextStorage = new AsyncLocalStorage2();
|
|
2648
3049
|
function getCurrentToolCallContext() {
|
|
2649
3050
|
return toolCallContextStorage.getStore() ?? null;
|
|
2650
3051
|
}
|
|
@@ -2936,9 +3337,9 @@ function sanitisePartForLogging(part) {
|
|
|
2936
3337
|
case "inlineData": {
|
|
2937
3338
|
let omittedBytes;
|
|
2938
3339
|
try {
|
|
2939
|
-
omittedBytes =
|
|
3340
|
+
omittedBytes = Buffer4.from(part.data, "base64").byteLength;
|
|
2940
3341
|
} catch {
|
|
2941
|
-
omittedBytes =
|
|
3342
|
+
omittedBytes = Buffer4.byteLength(part.data, "utf8");
|
|
2942
3343
|
}
|
|
2943
3344
|
return {
|
|
2944
3345
|
type: "inlineData",
|
|
@@ -3023,7 +3424,11 @@ function convertLlmContentToGeminiContent(content) {
|
|
|
3023
3424
|
}
|
|
3024
3425
|
function resolveProvider(model) {
|
|
3025
3426
|
if (isChatGptModelId(model)) {
|
|
3026
|
-
return {
|
|
3427
|
+
return {
|
|
3428
|
+
provider: "chatgpt",
|
|
3429
|
+
model: resolveChatGptProviderModel(model),
|
|
3430
|
+
serviceTier: resolveChatGptServiceTier(model)
|
|
3431
|
+
};
|
|
3027
3432
|
}
|
|
3028
3433
|
if (isGeminiTextModelId(model) || isGeminiImageModelId(model)) {
|
|
3029
3434
|
return { provider: "gemini", model };
|
|
@@ -4001,8 +4406,8 @@ function parseOpenAiToolArguments(raw) {
|
|
|
4001
4406
|
function formatZodIssues(issues) {
|
|
4002
4407
|
const messages = [];
|
|
4003
4408
|
for (const issue of issues) {
|
|
4004
|
-
const
|
|
4005
|
-
messages.push(`${
|
|
4409
|
+
const path8 = issue.path.length > 0 ? issue.path.map(String).join(".") : "input";
|
|
4410
|
+
messages.push(`${path8}: ${issue.message}`);
|
|
4006
4411
|
}
|
|
4007
4412
|
return messages.join("; ");
|
|
4008
4413
|
}
|
|
@@ -4388,9 +4793,9 @@ function resolveGeminiThinkingConfig(modelId, thinkingLevel) {
|
|
|
4388
4793
|
}
|
|
4389
4794
|
function decodeInlineDataBuffer(base64) {
|
|
4390
4795
|
try {
|
|
4391
|
-
return
|
|
4796
|
+
return Buffer4.from(base64, "base64");
|
|
4392
4797
|
} catch {
|
|
4393
|
-
return
|
|
4798
|
+
return Buffer4.from(base64, "base64url");
|
|
4394
4799
|
}
|
|
4395
4800
|
}
|
|
4396
4801
|
function extractImages(content) {
|
|
@@ -4407,6 +4812,195 @@ function extractImages(content) {
|
|
|
4407
4812
|
}
|
|
4408
4813
|
return images;
|
|
4409
4814
|
}
|
|
4815
|
+
function resolveAttachmentExtension(mimeType) {
|
|
4816
|
+
const normalized = (mimeType ?? "").trim().toLowerCase();
|
|
4817
|
+
switch (normalized) {
|
|
4818
|
+
case "image/jpeg":
|
|
4819
|
+
return "jpg";
|
|
4820
|
+
case "image/png":
|
|
4821
|
+
return "png";
|
|
4822
|
+
case "image/webp":
|
|
4823
|
+
return "webp";
|
|
4824
|
+
case "image/gif":
|
|
4825
|
+
return "gif";
|
|
4826
|
+
case "image/heic":
|
|
4827
|
+
return "heic";
|
|
4828
|
+
case "image/heif":
|
|
4829
|
+
return "heif";
|
|
4830
|
+
case "application/pdf":
|
|
4831
|
+
return "pdf";
|
|
4832
|
+
case "application/json":
|
|
4833
|
+
return "json";
|
|
4834
|
+
case "text/plain":
|
|
4835
|
+
return "txt";
|
|
4836
|
+
case "text/markdown":
|
|
4837
|
+
return "md";
|
|
4838
|
+
default: {
|
|
4839
|
+
const slashIndex = normalized.indexOf("/");
|
|
4840
|
+
if (slashIndex >= 0) {
|
|
4841
|
+
const subtype = normalized.slice(slashIndex + 1).split("+")[0] ?? "";
|
|
4842
|
+
const cleaned = subtype.replace(/[^a-z0-9]+/giu, "");
|
|
4843
|
+
if (cleaned.length > 0) {
|
|
4844
|
+
return cleaned;
|
|
4845
|
+
}
|
|
4846
|
+
}
|
|
4847
|
+
return "bin";
|
|
4848
|
+
}
|
|
4849
|
+
}
|
|
4850
|
+
}
|
|
4851
|
+
function decodeDataUrlAttachment(value, basename) {
|
|
4852
|
+
const trimmed = value.trim();
|
|
4853
|
+
if (!trimmed.toLowerCase().startsWith("data:")) {
|
|
4854
|
+
return null;
|
|
4855
|
+
}
|
|
4856
|
+
const commaIndex = trimmed.indexOf(",");
|
|
4857
|
+
if (commaIndex < 0) {
|
|
4858
|
+
return null;
|
|
4859
|
+
}
|
|
4860
|
+
const header = trimmed.slice(5, commaIndex);
|
|
4861
|
+
const payload = trimmed.slice(commaIndex + 1);
|
|
4862
|
+
const isBase64 = /;base64(?:;|$)/iu.test(header);
|
|
4863
|
+
const mimeType = (header.split(";")[0] ?? "application/octet-stream").trim().toLowerCase();
|
|
4864
|
+
try {
|
|
4865
|
+
const bytes = isBase64 ? Buffer4.from(payload, "base64") : Buffer4.from(decodeURIComponent(payload), "utf8");
|
|
4866
|
+
return {
|
|
4867
|
+
filename: `${basename}.${resolveAttachmentExtension(mimeType)}`,
|
|
4868
|
+
bytes
|
|
4869
|
+
};
|
|
4870
|
+
} catch {
|
|
4871
|
+
return null;
|
|
4872
|
+
}
|
|
4873
|
+
}
|
|
4874
|
+
function collectPayloadAttachments(value, options) {
|
|
4875
|
+
if (typeof value === "string") {
|
|
4876
|
+
const attachment = decodeDataUrlAttachment(
|
|
4877
|
+
value,
|
|
4878
|
+
`${options.prefix}-${options.counter.toString()}`
|
|
4879
|
+
);
|
|
4880
|
+
if (attachment) {
|
|
4881
|
+
options.attachments.push(attachment);
|
|
4882
|
+
options.counter += 1;
|
|
4883
|
+
}
|
|
4884
|
+
return;
|
|
4885
|
+
}
|
|
4886
|
+
if (!value || typeof value !== "object") {
|
|
4887
|
+
return;
|
|
4888
|
+
}
|
|
4889
|
+
if (options.seen.has(value)) {
|
|
4890
|
+
return;
|
|
4891
|
+
}
|
|
4892
|
+
options.seen.add(value);
|
|
4893
|
+
if (Array.isArray(value)) {
|
|
4894
|
+
for (const entry of value) {
|
|
4895
|
+
collectPayloadAttachments(entry, options);
|
|
4896
|
+
}
|
|
4897
|
+
return;
|
|
4898
|
+
}
|
|
4899
|
+
const record = value;
|
|
4900
|
+
const mimeType = typeof record.mimeType === "string" ? record.mimeType : void 0;
|
|
4901
|
+
if (typeof record.data === "string" && mimeType) {
|
|
4902
|
+
try {
|
|
4903
|
+
options.attachments.push({
|
|
4904
|
+
filename: `${options.prefix}-${options.counter.toString()}.${resolveAttachmentExtension(mimeType)}`,
|
|
4905
|
+
bytes: decodeInlineDataBuffer(record.data)
|
|
4906
|
+
});
|
|
4907
|
+
options.counter += 1;
|
|
4908
|
+
} catch {
|
|
4909
|
+
}
|
|
4910
|
+
}
|
|
4911
|
+
for (const entry of Object.values(record)) {
|
|
4912
|
+
collectPayloadAttachments(entry, options);
|
|
4913
|
+
}
|
|
4914
|
+
}
|
|
4915
|
+
function serialiseRequestPayloadForLogging(value) {
|
|
4916
|
+
try {
|
|
4917
|
+
return `${JSON.stringify(sanitiseLogValue(value), null, 2)}
|
|
4918
|
+
`;
|
|
4919
|
+
} catch {
|
|
4920
|
+
return `${String(value)}
|
|
4921
|
+
`;
|
|
4922
|
+
}
|
|
4923
|
+
}
|
|
4924
|
+
function startLlmCallLoggerFromContents(options) {
|
|
4925
|
+
const session = getCurrentAgentLoggingSession();
|
|
4926
|
+
if (!session) {
|
|
4927
|
+
return void 0;
|
|
4928
|
+
}
|
|
4929
|
+
const attachments = [];
|
|
4930
|
+
const sections = [];
|
|
4931
|
+
for (const [messageIndex, message] of options.contents.entries()) {
|
|
4932
|
+
sections.push(`### message_${(messageIndex + 1).toString()} role=${message.role}`);
|
|
4933
|
+
for (const [partIndex, part] of message.parts.entries()) {
|
|
4934
|
+
if (part.type === "text") {
|
|
4935
|
+
const channel = part.thought === true ? "thought" : "response";
|
|
4936
|
+
sections.push(`[text:${channel}]`);
|
|
4937
|
+
sections.push(part.text);
|
|
4938
|
+
continue;
|
|
4939
|
+
}
|
|
4940
|
+
const filename = `message-${(messageIndex + 1).toString()}-part-${(partIndex + 1).toString()}.${resolveAttachmentExtension(part.mimeType)}`;
|
|
4941
|
+
attachments.push({
|
|
4942
|
+
filename,
|
|
4943
|
+
bytes: decodeInlineDataBuffer(part.data)
|
|
4944
|
+
});
|
|
4945
|
+
sections.push(
|
|
4946
|
+
`[inlineData] file=${filename} mime=${part.mimeType ?? "application/octet-stream"} bytes=${attachments[attachments.length - 1]?.bytes.byteLength ?? 0}`
|
|
4947
|
+
);
|
|
4948
|
+
}
|
|
4949
|
+
sections.push("");
|
|
4950
|
+
}
|
|
4951
|
+
return session.startLlmCall({
|
|
4952
|
+
provider: options.provider,
|
|
4953
|
+
modelId: options.request.model,
|
|
4954
|
+
requestText: sections.join("\n").trim(),
|
|
4955
|
+
requestMetadata: {
|
|
4956
|
+
model: options.request.model,
|
|
4957
|
+
input: options.contents.map((content) => ({
|
|
4958
|
+
role: content.role,
|
|
4959
|
+
parts: content.parts.map((part) => sanitisePartForLogging(part))
|
|
4960
|
+
})),
|
|
4961
|
+
...options.request.instructions ? {
|
|
4962
|
+
instructions: options.request.instructions
|
|
4963
|
+
} : {},
|
|
4964
|
+
...options.request.tools ? { tools: options.request.tools } : {},
|
|
4965
|
+
...options.request.responseMimeType ? {
|
|
4966
|
+
responseMimeType: options.request.responseMimeType
|
|
4967
|
+
} : {},
|
|
4968
|
+
...options.request.responseJsonSchema ? {
|
|
4969
|
+
responseJsonSchema: sanitiseLogValue(options.request.responseJsonSchema)
|
|
4970
|
+
} : {},
|
|
4971
|
+
...options.request.responseModalities ? { responseModalities: options.request.responseModalities } : {},
|
|
4972
|
+
...options.request.imageAspectRatio ? { imageAspectRatio: options.request.imageAspectRatio } : {},
|
|
4973
|
+
...options.request.imageSize ? { imageSize: options.request.imageSize } : {},
|
|
4974
|
+
...options.request.thinkingLevel ? { thinkingLevel: options.request.thinkingLevel } : {},
|
|
4975
|
+
...options.request.openAiTextFormat ? { openAiTextFormat: sanitiseLogValue(options.request.openAiTextFormat) } : {},
|
|
4976
|
+
...getCurrentToolCallContext() ? { toolContext: getCurrentToolCallContext() } : {}
|
|
4977
|
+
},
|
|
4978
|
+
attachments
|
|
4979
|
+
});
|
|
4980
|
+
}
|
|
4981
|
+
function startLlmCallLoggerFromPayload(options) {
|
|
4982
|
+
const session = getCurrentAgentLoggingSession();
|
|
4983
|
+
if (!session) {
|
|
4984
|
+
return void 0;
|
|
4985
|
+
}
|
|
4986
|
+
const attachments = [];
|
|
4987
|
+
collectPayloadAttachments(options.requestPayload, {
|
|
4988
|
+
prefix: `step-${options.step.toString()}`,
|
|
4989
|
+
attachments,
|
|
4990
|
+
seen: /* @__PURE__ */ new WeakSet(),
|
|
4991
|
+
counter: 1
|
|
4992
|
+
});
|
|
4993
|
+
return session.startLlmCall({
|
|
4994
|
+
provider: options.provider,
|
|
4995
|
+
modelId: options.modelId,
|
|
4996
|
+
requestText: serialiseRequestPayloadForLogging(options.requestPayload),
|
|
4997
|
+
requestMetadata: {
|
|
4998
|
+
step: options.step,
|
|
4999
|
+
...getCurrentToolCallContext() ? { toolContext: getCurrentToolCallContext() } : {}
|
|
5000
|
+
},
|
|
5001
|
+
attachments
|
|
5002
|
+
});
|
|
5003
|
+
}
|
|
4410
5004
|
async function runTextCall(params) {
|
|
4411
5005
|
const { request, queue, abortController } = params;
|
|
4412
5006
|
const providerInfo = resolveProvider(request.model);
|
|
@@ -4416,6 +5010,11 @@ async function runTextCall(params) {
|
|
|
4416
5010
|
if (contents.length === 0) {
|
|
4417
5011
|
throw new Error("LLM call received an empty prompt.");
|
|
4418
5012
|
}
|
|
5013
|
+
const callLogger = startLlmCallLoggerFromContents({
|
|
5014
|
+
provider,
|
|
5015
|
+
request,
|
|
5016
|
+
contents
|
|
5017
|
+
});
|
|
4419
5018
|
let modelVersion = request.model;
|
|
4420
5019
|
let blocked = false;
|
|
4421
5020
|
let grounding;
|
|
@@ -4423,12 +5022,17 @@ async function runTextCall(params) {
|
|
|
4423
5022
|
let responseRole;
|
|
4424
5023
|
let latestUsage;
|
|
4425
5024
|
let responseImages = 0;
|
|
4426
|
-
const pushDelta = (channel,
|
|
4427
|
-
if (!
|
|
5025
|
+
const pushDelta = (channel, text) => {
|
|
5026
|
+
if (!text) {
|
|
4428
5027
|
return;
|
|
4429
5028
|
}
|
|
4430
|
-
responseParts.push({ type: "text", text
|
|
4431
|
-
|
|
5029
|
+
responseParts.push({ type: "text", text, ...channel === "thought" ? { thought: true } : {} });
|
|
5030
|
+
if (channel === "thought") {
|
|
5031
|
+
callLogger?.appendThoughtDelta(text);
|
|
5032
|
+
} else {
|
|
5033
|
+
callLogger?.appendResponseDelta(text);
|
|
5034
|
+
}
|
|
5035
|
+
queue.push({ type: "delta", channel, text });
|
|
4432
5036
|
};
|
|
4433
5037
|
const pushInline = (data, mimeType) => {
|
|
4434
5038
|
if (!data) {
|
|
@@ -4455,263 +5059,295 @@ async function runTextCall(params) {
|
|
|
4455
5059
|
return abortController.signal;
|
|
4456
5060
|
};
|
|
4457
5061
|
const signal = resolveAbortSignal();
|
|
4458
|
-
|
|
4459
|
-
|
|
4460
|
-
|
|
4461
|
-
|
|
4462
|
-
|
|
4463
|
-
|
|
4464
|
-
|
|
4465
|
-
|
|
4466
|
-
|
|
4467
|
-
|
|
4468
|
-
|
|
4469
|
-
|
|
4470
|
-
|
|
4471
|
-
|
|
4472
|
-
|
|
4473
|
-
|
|
4474
|
-
|
|
4475
|
-
|
|
4476
|
-
|
|
4477
|
-
|
|
4478
|
-
|
|
4479
|
-
|
|
4480
|
-
|
|
4481
|
-
|
|
4482
|
-
for await (const event of stream) {
|
|
4483
|
-
switch (event.type) {
|
|
4484
|
-
case "response.output_text.delta": {
|
|
4485
|
-
const delta = event.delta ?? "";
|
|
4486
|
-
pushDelta("response", typeof delta === "string" ? delta : "");
|
|
4487
|
-
break;
|
|
4488
|
-
}
|
|
4489
|
-
case "response.reasoning_summary_text.delta": {
|
|
4490
|
-
const delta = event.delta ?? "";
|
|
4491
|
-
pushDelta("thought", typeof delta === "string" ? delta : "");
|
|
4492
|
-
break;
|
|
4493
|
-
}
|
|
4494
|
-
case "response.refusal.delta": {
|
|
4495
|
-
blocked = true;
|
|
4496
|
-
queue.push({ type: "blocked" });
|
|
4497
|
-
break;
|
|
4498
|
-
}
|
|
4499
|
-
default:
|
|
4500
|
-
break;
|
|
4501
|
-
}
|
|
4502
|
-
}
|
|
4503
|
-
const finalResponse = await stream.finalResponse();
|
|
4504
|
-
modelVersion = typeof finalResponse.model === "string" ? finalResponse.model : request.model;
|
|
4505
|
-
queue.push({ type: "model", modelVersion });
|
|
4506
|
-
if (finalResponse.error) {
|
|
4507
|
-
const message = typeof finalResponse.error.message === "string" ? finalResponse.error.message : "OpenAI response failed";
|
|
4508
|
-
throw new Error(message);
|
|
4509
|
-
}
|
|
4510
|
-
if (finalResponse.status && finalResponse.status !== "completed" && finalResponse.status !== "in_progress") {
|
|
4511
|
-
const detail = finalResponse.incomplete_details?.reason;
|
|
4512
|
-
throw new Error(
|
|
4513
|
-
`OpenAI response status ${finalResponse.status}${detail ? ` (${detail})` : ""}`
|
|
5062
|
+
try {
|
|
5063
|
+
if (provider === "openai") {
|
|
5064
|
+
const openAiInput = toOpenAiInput(contents);
|
|
5065
|
+
const openAiTools = toOpenAiTools(request.tools);
|
|
5066
|
+
const reasoningEffort = resolveOpenAiReasoningEffort(modelForProvider, request.thinkingLevel);
|
|
5067
|
+
const openAiTextConfig = {
|
|
5068
|
+
format: request.openAiTextFormat ?? { type: "text" },
|
|
5069
|
+
verbosity: resolveOpenAiVerbosity(modelForProvider)
|
|
5070
|
+
};
|
|
5071
|
+
const reasoning = {
|
|
5072
|
+
effort: toOpenAiReasoningEffort(reasoningEffort),
|
|
5073
|
+
summary: "detailed"
|
|
5074
|
+
};
|
|
5075
|
+
await runOpenAiCall(async (client) => {
|
|
5076
|
+
const stream = client.responses.stream(
|
|
5077
|
+
{
|
|
5078
|
+
model: modelForProvider,
|
|
5079
|
+
input: openAiInput,
|
|
5080
|
+
reasoning,
|
|
5081
|
+
text: openAiTextConfig,
|
|
5082
|
+
...openAiTools ? { tools: openAiTools } : {},
|
|
5083
|
+
include: ["code_interpreter_call.outputs", "reasoning.encrypted_content"]
|
|
5084
|
+
},
|
|
5085
|
+
{ signal }
|
|
4514
5086
|
);
|
|
4515
|
-
|
|
4516
|
-
|
|
4517
|
-
|
|
4518
|
-
|
|
4519
|
-
|
|
4520
|
-
|
|
4521
|
-
|
|
4522
|
-
|
|
4523
|
-
|
|
4524
|
-
|
|
5087
|
+
for await (const event of stream) {
|
|
5088
|
+
switch (event.type) {
|
|
5089
|
+
case "response.output_text.delta": {
|
|
5090
|
+
const delta = event.delta ?? "";
|
|
5091
|
+
pushDelta("response", typeof delta === "string" ? delta : "");
|
|
5092
|
+
break;
|
|
5093
|
+
}
|
|
5094
|
+
case "response.reasoning_summary_text.delta": {
|
|
5095
|
+
const delta = event.delta ?? "";
|
|
5096
|
+
pushDelta("thought", typeof delta === "string" ? delta : "");
|
|
5097
|
+
break;
|
|
5098
|
+
}
|
|
5099
|
+
case "response.refusal.delta": {
|
|
5100
|
+
blocked = true;
|
|
5101
|
+
queue.push({ type: "blocked" });
|
|
5102
|
+
break;
|
|
5103
|
+
}
|
|
5104
|
+
default:
|
|
5105
|
+
break;
|
|
4525
5106
|
}
|
|
4526
5107
|
}
|
|
4527
|
-
|
|
4528
|
-
|
|
4529
|
-
|
|
4530
|
-
|
|
4531
|
-
|
|
4532
|
-
|
|
4533
|
-
const requestPayload = {
|
|
4534
|
-
model: modelForProvider,
|
|
4535
|
-
store: false,
|
|
4536
|
-
stream: true,
|
|
4537
|
-
instructions: chatGptInput.instructions ?? "You are a helpful assistant.",
|
|
4538
|
-
input: chatGptInput.input,
|
|
4539
|
-
include: ["reasoning.encrypted_content"],
|
|
4540
|
-
reasoning: { effort: toOpenAiReasoningEffort(reasoningEffort), summary: "detailed" },
|
|
4541
|
-
text: {
|
|
4542
|
-
format: request.openAiTextFormat ?? { type: "text" },
|
|
4543
|
-
verbosity: resolveOpenAiVerbosity(request.model)
|
|
4544
|
-
},
|
|
4545
|
-
...openAiTools ? { tools: openAiTools } : {}
|
|
4546
|
-
};
|
|
4547
|
-
let sawResponseDelta = false;
|
|
4548
|
-
let sawThoughtDelta = false;
|
|
4549
|
-
const result = await collectChatGptCodexResponseWithRetry({
|
|
4550
|
-
request: requestPayload,
|
|
4551
|
-
signal,
|
|
4552
|
-
onDelta: (delta) => {
|
|
4553
|
-
if (delta.thoughtDelta) {
|
|
4554
|
-
sawThoughtDelta = true;
|
|
4555
|
-
pushDelta("thought", delta.thoughtDelta);
|
|
5108
|
+
const finalResponse = await stream.finalResponse();
|
|
5109
|
+
modelVersion = typeof finalResponse.model === "string" ? finalResponse.model : request.model;
|
|
5110
|
+
queue.push({ type: "model", modelVersion });
|
|
5111
|
+
if (finalResponse.error) {
|
|
5112
|
+
const message = typeof finalResponse.error.message === "string" ? finalResponse.error.message : "OpenAI response failed";
|
|
5113
|
+
throw new Error(message);
|
|
4556
5114
|
}
|
|
4557
|
-
if (
|
|
4558
|
-
|
|
4559
|
-
|
|
5115
|
+
if (finalResponse.status && finalResponse.status !== "completed" && finalResponse.status !== "in_progress") {
|
|
5116
|
+
const detail = finalResponse.incomplete_details?.reason;
|
|
5117
|
+
throw new Error(
|
|
5118
|
+
`OpenAI response status ${finalResponse.status}${detail ? ` (${detail})` : ""}`
|
|
5119
|
+
);
|
|
4560
5120
|
}
|
|
4561
|
-
|
|
4562
|
-
|
|
4563
|
-
|
|
4564
|
-
|
|
4565
|
-
|
|
4566
|
-
|
|
4567
|
-
|
|
4568
|
-
|
|
4569
|
-
|
|
4570
|
-
|
|
4571
|
-
|
|
4572
|
-
const fallbackText = typeof result.text === "string" ? result.text : "";
|
|
4573
|
-
const fallbackThoughts = typeof result.reasoningSummaryText === "string" && result.reasoningSummaryText.length > 0 ? result.reasoningSummaryText : typeof result.reasoningText === "string" ? result.reasoningText : "";
|
|
4574
|
-
if (!sawThoughtDelta && fallbackThoughts.length > 0) {
|
|
4575
|
-
pushDelta("thought", fallbackThoughts);
|
|
4576
|
-
}
|
|
4577
|
-
if (!sawResponseDelta && fallbackText.length > 0) {
|
|
4578
|
-
pushDelta("response", fallbackText);
|
|
4579
|
-
}
|
|
4580
|
-
} else if (provider === "fireworks") {
|
|
4581
|
-
if (request.tools && request.tools.length > 0) {
|
|
4582
|
-
throw new Error(
|
|
4583
|
-
"Fireworks provider does not support provider-native tools in generateText; use runToolLoop for function tools."
|
|
4584
|
-
);
|
|
4585
|
-
}
|
|
4586
|
-
const fireworksMessages = toFireworksMessages(contents, {
|
|
4587
|
-
responseMimeType: request.responseMimeType,
|
|
4588
|
-
responseJsonSchema: request.responseJsonSchema
|
|
4589
|
-
});
|
|
4590
|
-
await runFireworksCall(async (client) => {
|
|
4591
|
-
const responseFormat = request.responseJsonSchema ? {
|
|
4592
|
-
type: "json_schema",
|
|
4593
|
-
json_schema: {
|
|
4594
|
-
name: "llm-response",
|
|
4595
|
-
schema: request.responseJsonSchema
|
|
5121
|
+
latestUsage = extractOpenAiUsageTokens(finalResponse.usage);
|
|
5122
|
+
if (responseParts.length === 0) {
|
|
5123
|
+
const fallback = extractOpenAiResponseParts(finalResponse);
|
|
5124
|
+
blocked = blocked || fallback.blocked;
|
|
5125
|
+
for (const part of fallback.parts) {
|
|
5126
|
+
if (part.type === "text") {
|
|
5127
|
+
pushDelta(part.thought === true ? "thought" : "response", part.text);
|
|
5128
|
+
} else {
|
|
5129
|
+
pushInline(part.data, part.mimeType);
|
|
5130
|
+
}
|
|
5131
|
+
}
|
|
4596
5132
|
}
|
|
4597
|
-
}
|
|
4598
|
-
|
|
4599
|
-
|
|
4600
|
-
|
|
4601
|
-
|
|
4602
|
-
|
|
5133
|
+
}, modelForProvider);
|
|
5134
|
+
} else if (provider === "chatgpt") {
|
|
5135
|
+
const chatGptInput = toChatGptInput(contents);
|
|
5136
|
+
const reasoningEffort = resolveOpenAiReasoningEffort(request.model, request.thinkingLevel);
|
|
5137
|
+
const openAiTools = toOpenAiTools(request.tools);
|
|
5138
|
+
const requestPayload = {
|
|
5139
|
+
model: modelForProvider,
|
|
5140
|
+
store: false,
|
|
5141
|
+
stream: true,
|
|
5142
|
+
...providerInfo.serviceTier ? { service_tier: providerInfo.serviceTier } : {},
|
|
5143
|
+
instructions: chatGptInput.instructions ?? "You are a helpful assistant.",
|
|
5144
|
+
input: chatGptInput.input,
|
|
5145
|
+
include: ["reasoning.encrypted_content"],
|
|
5146
|
+
reasoning: {
|
|
5147
|
+
effort: toOpenAiReasoningEffort(reasoningEffort),
|
|
5148
|
+
summary: "detailed"
|
|
4603
5149
|
},
|
|
4604
|
-
{
|
|
4605
|
-
|
|
4606
|
-
|
|
4607
|
-
|
|
4608
|
-
|
|
4609
|
-
|
|
4610
|
-
|
|
5150
|
+
text: {
|
|
5151
|
+
format: request.openAiTextFormat ?? { type: "text" },
|
|
5152
|
+
verbosity: resolveOpenAiVerbosity(request.model)
|
|
5153
|
+
},
|
|
5154
|
+
...openAiTools ? { tools: openAiTools } : {}
|
|
5155
|
+
};
|
|
5156
|
+
let sawResponseDelta = false;
|
|
5157
|
+
let sawThoughtDelta = false;
|
|
5158
|
+
const result = await collectChatGptCodexResponseWithRetry({
|
|
5159
|
+
request: requestPayload,
|
|
5160
|
+
signal,
|
|
5161
|
+
onDelta: (delta) => {
|
|
5162
|
+
if (delta.thoughtDelta) {
|
|
5163
|
+
sawThoughtDelta = true;
|
|
5164
|
+
pushDelta("thought", delta.thoughtDelta);
|
|
5165
|
+
}
|
|
5166
|
+
if (delta.textDelta) {
|
|
5167
|
+
sawResponseDelta = true;
|
|
5168
|
+
pushDelta("response", delta.textDelta);
|
|
5169
|
+
}
|
|
5170
|
+
}
|
|
5171
|
+
});
|
|
5172
|
+
blocked = blocked || result.blocked;
|
|
5173
|
+
if (blocked) {
|
|
4611
5174
|
queue.push({ type: "blocked" });
|
|
4612
5175
|
}
|
|
4613
|
-
|
|
4614
|
-
|
|
4615
|
-
|
|
4616
|
-
if (textOutput.length > 0) {
|
|
4617
|
-
pushDelta("response", textOutput);
|
|
5176
|
+
if (result.model) {
|
|
5177
|
+
modelVersion = providerInfo.serviceTier ? request.model : `chatgpt-${result.model}`;
|
|
5178
|
+
queue.push({ type: "model", modelVersion });
|
|
4618
5179
|
}
|
|
4619
|
-
latestUsage =
|
|
4620
|
-
|
|
4621
|
-
|
|
4622
|
-
|
|
4623
|
-
|
|
4624
|
-
|
|
4625
|
-
|
|
4626
|
-
|
|
4627
|
-
|
|
4628
|
-
|
|
4629
|
-
|
|
4630
|
-
|
|
4631
|
-
|
|
4632
|
-
|
|
4633
|
-
|
|
4634
|
-
|
|
4635
|
-
|
|
4636
|
-
|
|
4637
|
-
const geminiTools = toGeminiTools(request.tools);
|
|
4638
|
-
if (geminiTools) {
|
|
4639
|
-
config.tools = geminiTools;
|
|
4640
|
-
}
|
|
4641
|
-
await runGeminiCall(async (client) => {
|
|
4642
|
-
const stream = await client.models.generateContentStream({
|
|
4643
|
-
model: modelForProvider,
|
|
4644
|
-
contents: geminiContents,
|
|
4645
|
-
config
|
|
5180
|
+
latestUsage = extractChatGptUsageTokens(result.usage);
|
|
5181
|
+
const fallbackText = typeof result.text === "string" ? result.text : "";
|
|
5182
|
+
const fallbackThoughts = typeof result.reasoningSummaryText === "string" && result.reasoningSummaryText.length > 0 ? result.reasoningSummaryText : typeof result.reasoningText === "string" ? result.reasoningText : "";
|
|
5183
|
+
if (!sawThoughtDelta && fallbackThoughts.length > 0) {
|
|
5184
|
+
pushDelta("thought", fallbackThoughts);
|
|
5185
|
+
}
|
|
5186
|
+
if (!sawResponseDelta && fallbackText.length > 0) {
|
|
5187
|
+
pushDelta("response", fallbackText);
|
|
5188
|
+
}
|
|
5189
|
+
} else if (provider === "fireworks") {
|
|
5190
|
+
if (request.tools && request.tools.length > 0) {
|
|
5191
|
+
throw new Error(
|
|
5192
|
+
"Fireworks provider does not support provider-native tools in generateText; use runToolLoop for function tools."
|
|
5193
|
+
);
|
|
5194
|
+
}
|
|
5195
|
+
const fireworksMessages = toFireworksMessages(contents, {
|
|
5196
|
+
responseMimeType: request.responseMimeType,
|
|
5197
|
+
responseJsonSchema: request.responseJsonSchema
|
|
4646
5198
|
});
|
|
4647
|
-
|
|
4648
|
-
|
|
4649
|
-
|
|
4650
|
-
|
|
4651
|
-
|
|
4652
|
-
|
|
4653
|
-
|
|
5199
|
+
await runFireworksCall(async (client) => {
|
|
5200
|
+
const responseFormat = request.responseJsonSchema ? {
|
|
5201
|
+
type: "json_schema",
|
|
5202
|
+
json_schema: {
|
|
5203
|
+
name: "llm-response",
|
|
5204
|
+
schema: request.responseJsonSchema
|
|
5205
|
+
}
|
|
5206
|
+
} : request.responseMimeType === "application/json" ? { type: "json_object" } : void 0;
|
|
5207
|
+
const response = await client.chat.completions.create(
|
|
5208
|
+
{
|
|
5209
|
+
model: modelForProvider,
|
|
5210
|
+
messages: fireworksMessages,
|
|
5211
|
+
...responseFormat ? { response_format: responseFormat } : {}
|
|
5212
|
+
},
|
|
5213
|
+
{ signal }
|
|
5214
|
+
);
|
|
5215
|
+
modelVersion = typeof response.model === "string" ? response.model : request.model;
|
|
5216
|
+
queue.push({ type: "model", modelVersion });
|
|
5217
|
+
const choice = Array.isArray(response.choices) ? response.choices[0] : void 0;
|
|
5218
|
+
if (choice?.finish_reason === "content_filter") {
|
|
4654
5219
|
blocked = true;
|
|
4655
5220
|
queue.push({ type: "blocked" });
|
|
4656
5221
|
}
|
|
4657
|
-
|
|
4658
|
-
|
|
4659
|
-
|
|
4660
|
-
|
|
4661
|
-
|
|
4662
|
-
const primary = candidates[0];
|
|
4663
|
-
if (primary && isModerationFinish(primary.finishReason)) {
|
|
4664
|
-
blocked = true;
|
|
4665
|
-
queue.push({ type: "blocked" });
|
|
5222
|
+
const textOutput = extractFireworksMessageText(
|
|
5223
|
+
choice?.message
|
|
5224
|
+
);
|
|
5225
|
+
if (textOutput.length > 0) {
|
|
5226
|
+
pushDelta("response", textOutput);
|
|
4666
5227
|
}
|
|
4667
|
-
|
|
4668
|
-
|
|
4669
|
-
|
|
4670
|
-
|
|
5228
|
+
latestUsage = extractFireworksUsageTokens(response.usage);
|
|
5229
|
+
}, modelForProvider);
|
|
5230
|
+
} else {
|
|
5231
|
+
const geminiContents = contents.map(convertLlmContentToGeminiContent);
|
|
5232
|
+
const thinkingConfig = resolveGeminiThinkingConfig(modelForProvider, request.thinkingLevel);
|
|
5233
|
+
const config = {
|
|
5234
|
+
maxOutputTokens: 32e3,
|
|
5235
|
+
...thinkingConfig ? { thinkingConfig } : {},
|
|
5236
|
+
...request.responseMimeType ? { responseMimeType: request.responseMimeType } : {},
|
|
5237
|
+
...request.responseJsonSchema ? { responseJsonSchema: request.responseJsonSchema } : {},
|
|
5238
|
+
...request.responseModalities ? { responseModalities: Array.from(request.responseModalities) } : {},
|
|
5239
|
+
...request.imageAspectRatio || request.imageSize ? {
|
|
5240
|
+
imageConfig: {
|
|
5241
|
+
...request.imageAspectRatio ? { aspectRatio: request.imageAspectRatio } : {},
|
|
5242
|
+
...request.imageSize ? { imageSize: request.imageSize } : {}
|
|
4671
5243
|
}
|
|
4672
|
-
|
|
4673
|
-
|
|
5244
|
+
} : {}
|
|
5245
|
+
};
|
|
5246
|
+
const geminiTools = toGeminiTools(request.tools);
|
|
5247
|
+
if (geminiTools) {
|
|
5248
|
+
config.tools = geminiTools;
|
|
5249
|
+
}
|
|
5250
|
+
await runGeminiCall(async (client) => {
|
|
5251
|
+
const stream = await client.models.generateContentStream({
|
|
5252
|
+
model: modelForProvider,
|
|
5253
|
+
contents: geminiContents,
|
|
5254
|
+
config
|
|
5255
|
+
});
|
|
5256
|
+
let latestGrounding;
|
|
5257
|
+
for await (const chunk of stream) {
|
|
5258
|
+
if (chunk.modelVersion) {
|
|
5259
|
+
modelVersion = chunk.modelVersion;
|
|
5260
|
+
queue.push({ type: "model", modelVersion });
|
|
4674
5261
|
}
|
|
4675
|
-
|
|
4676
|
-
|
|
4677
|
-
|
|
5262
|
+
if (chunk.promptFeedback?.blockReason) {
|
|
5263
|
+
blocked = true;
|
|
5264
|
+
queue.push({ type: "blocked" });
|
|
4678
5265
|
}
|
|
4679
|
-
|
|
4680
|
-
|
|
4681
|
-
|
|
4682
|
-
|
|
4683
|
-
|
|
5266
|
+
latestUsage = mergeTokenUpdates(
|
|
5267
|
+
latestUsage,
|
|
5268
|
+
extractGeminiUsageTokens(chunk.usageMetadata)
|
|
5269
|
+
);
|
|
5270
|
+
const candidates = chunk.candidates;
|
|
5271
|
+
if (!candidates || candidates.length === 0) {
|
|
5272
|
+
continue;
|
|
5273
|
+
}
|
|
5274
|
+
const primary = candidates[0];
|
|
5275
|
+
if (primary && isModerationFinish(primary.finishReason)) {
|
|
5276
|
+
blocked = true;
|
|
5277
|
+
queue.push({ type: "blocked" });
|
|
5278
|
+
}
|
|
5279
|
+
for (const candidate of candidates) {
|
|
5280
|
+
const candidateContent = candidate.content;
|
|
5281
|
+
if (!candidateContent) {
|
|
5282
|
+
continue;
|
|
5283
|
+
}
|
|
5284
|
+
if (candidate.groundingMetadata) {
|
|
5285
|
+
latestGrounding = candidate.groundingMetadata;
|
|
5286
|
+
}
|
|
5287
|
+
const content2 = convertGeminiContentToLlmContent(candidateContent);
|
|
5288
|
+
if (!responseRole) {
|
|
5289
|
+
responseRole = content2.role;
|
|
5290
|
+
}
|
|
5291
|
+
for (const part of content2.parts) {
|
|
5292
|
+
if (part.type === "text") {
|
|
5293
|
+
pushDelta(part.thought === true ? "thought" : "response", part.text);
|
|
5294
|
+
} else {
|
|
5295
|
+
pushInline(part.data, part.mimeType);
|
|
5296
|
+
}
|
|
4684
5297
|
}
|
|
4685
5298
|
}
|
|
4686
5299
|
}
|
|
4687
|
-
|
|
4688
|
-
|
|
4689
|
-
}
|
|
4690
|
-
|
|
4691
|
-
|
|
4692
|
-
|
|
4693
|
-
|
|
4694
|
-
|
|
4695
|
-
|
|
4696
|
-
|
|
4697
|
-
|
|
4698
|
-
|
|
4699
|
-
|
|
4700
|
-
|
|
4701
|
-
|
|
5300
|
+
grounding = latestGrounding;
|
|
5301
|
+
}, modelForProvider);
|
|
5302
|
+
}
|
|
5303
|
+
const mergedParts = mergeConsecutiveTextParts(responseParts);
|
|
5304
|
+
const content = mergedParts.length > 0 ? { role: responseRole ?? "assistant", parts: mergedParts } : void 0;
|
|
5305
|
+
const { text, thoughts } = extractTextByChannel(content);
|
|
5306
|
+
const costUsd = estimateCallCostUsd({
|
|
5307
|
+
modelId: modelVersion,
|
|
5308
|
+
tokens: latestUsage,
|
|
5309
|
+
responseImages,
|
|
5310
|
+
imageSize: request.imageSize
|
|
5311
|
+
});
|
|
5312
|
+
if (latestUsage) {
|
|
5313
|
+
queue.push({ type: "usage", usage: latestUsage, costUsd, modelVersion });
|
|
5314
|
+
}
|
|
5315
|
+
callLogger?.complete({
|
|
5316
|
+
provider,
|
|
5317
|
+
model: request.model,
|
|
5318
|
+
modelVersion,
|
|
5319
|
+
blocked,
|
|
5320
|
+
costUsd,
|
|
5321
|
+
usage: latestUsage,
|
|
5322
|
+
grounding: grounding ? sanitiseLogValue(grounding) : void 0,
|
|
5323
|
+
responseChars: text.length,
|
|
5324
|
+
thoughtChars: thoughts.length,
|
|
5325
|
+
responseImages
|
|
5326
|
+
});
|
|
5327
|
+
return {
|
|
5328
|
+
provider,
|
|
5329
|
+
model: request.model,
|
|
5330
|
+
modelVersion,
|
|
5331
|
+
content,
|
|
5332
|
+
text,
|
|
5333
|
+
thoughts,
|
|
5334
|
+
blocked,
|
|
5335
|
+
usage: latestUsage,
|
|
5336
|
+
costUsd,
|
|
5337
|
+
grounding
|
|
5338
|
+
};
|
|
5339
|
+
} catch (error) {
|
|
5340
|
+
callLogger?.fail(error, {
|
|
5341
|
+
provider,
|
|
5342
|
+
model: request.model,
|
|
5343
|
+
modelVersion,
|
|
5344
|
+
blocked,
|
|
5345
|
+
usage: latestUsage,
|
|
5346
|
+
partialResponseParts: responseParts.length,
|
|
5347
|
+
responseImages
|
|
5348
|
+
});
|
|
5349
|
+
throw error;
|
|
4702
5350
|
}
|
|
4703
|
-
return {
|
|
4704
|
-
provider,
|
|
4705
|
-
model: request.model,
|
|
4706
|
-
modelVersion,
|
|
4707
|
-
content,
|
|
4708
|
-
text,
|
|
4709
|
-
thoughts,
|
|
4710
|
-
blocked,
|
|
4711
|
-
usage: latestUsage,
|
|
4712
|
-
costUsd,
|
|
4713
|
-
grounding
|
|
4714
|
-
};
|
|
4715
5351
|
}
|
|
4716
5352
|
function streamText(request) {
|
|
4717
5353
|
const queue = createAsyncQueue();
|
|
@@ -5174,6 +5810,23 @@ async function runToolLoop(request) {
|
|
|
5174
5810
|
let modelVersion = request.model;
|
|
5175
5811
|
let usageTokens;
|
|
5176
5812
|
let thoughtDeltaEmitted = false;
|
|
5813
|
+
let blocked = false;
|
|
5814
|
+
const stepRequestPayload = {
|
|
5815
|
+
model: providerInfo.model,
|
|
5816
|
+
input,
|
|
5817
|
+
...previousResponseId ? { previous_response_id: previousResponseId } : {},
|
|
5818
|
+
...openAiTools.length > 0 ? { tools: openAiTools } : {},
|
|
5819
|
+
...openAiTools.length > 0 ? { parallel_tool_calls: true } : {},
|
|
5820
|
+
reasoning,
|
|
5821
|
+
text: textConfig,
|
|
5822
|
+
include: ["reasoning.encrypted_content"]
|
|
5823
|
+
};
|
|
5824
|
+
const stepCallLogger = startLlmCallLoggerFromPayload({
|
|
5825
|
+
provider: "openai",
|
|
5826
|
+
modelId: request.model,
|
|
5827
|
+
requestPayload: stepRequestPayload,
|
|
5828
|
+
step: turn
|
|
5829
|
+
});
|
|
5177
5830
|
const emitEvent = (ev) => {
|
|
5178
5831
|
onEvent?.(ev);
|
|
5179
5832
|
};
|
|
@@ -5182,226 +5835,276 @@ async function runToolLoop(request) {
|
|
|
5182
5835
|
firstModelEventAtMs = Date.now();
|
|
5183
5836
|
}
|
|
5184
5837
|
};
|
|
5185
|
-
|
|
5186
|
-
|
|
5187
|
-
|
|
5188
|
-
|
|
5189
|
-
|
|
5190
|
-
|
|
5191
|
-
|
|
5192
|
-
|
|
5193
|
-
|
|
5194
|
-
|
|
5195
|
-
|
|
5196
|
-
|
|
5197
|
-
|
|
5198
|
-
|
|
5199
|
-
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
|
|
5203
|
-
|
|
5204
|
-
|
|
5205
|
-
|
|
5206
|
-
|
|
5207
|
-
|
|
5208
|
-
|
|
5209
|
-
|
|
5210
|
-
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
|
|
5214
|
-
|
|
5215
|
-
|
|
5216
|
-
|
|
5217
|
-
|
|
5218
|
-
|
|
5219
|
-
|
|
5220
|
-
|
|
5221
|
-
|
|
5222
|
-
|
|
5838
|
+
try {
|
|
5839
|
+
const finalResponse = await runOpenAiCall(
|
|
5840
|
+
async (client) => {
|
|
5841
|
+
const stream = client.responses.stream(
|
|
5842
|
+
{
|
|
5843
|
+
model: providerInfo.model,
|
|
5844
|
+
input,
|
|
5845
|
+
...previousResponseId ? { previous_response_id: previousResponseId } : {},
|
|
5846
|
+
...openAiTools.length > 0 ? { tools: openAiTools } : {},
|
|
5847
|
+
...openAiTools.length > 0 ? { parallel_tool_calls: true } : {},
|
|
5848
|
+
reasoning,
|
|
5849
|
+
text: textConfig,
|
|
5850
|
+
include: ["reasoning.encrypted_content"]
|
|
5851
|
+
},
|
|
5852
|
+
{ signal: abortController.signal }
|
|
5853
|
+
);
|
|
5854
|
+
for await (const event of stream) {
|
|
5855
|
+
markFirstModelEvent();
|
|
5856
|
+
switch (event.type) {
|
|
5857
|
+
case "response.output_text.delta": {
|
|
5858
|
+
const text = typeof event.delta === "string" ? event.delta : "";
|
|
5859
|
+
if (text.length > 0) {
|
|
5860
|
+
stepCallLogger?.appendResponseDelta(text);
|
|
5861
|
+
}
|
|
5862
|
+
emitEvent({
|
|
5863
|
+
type: "delta",
|
|
5864
|
+
channel: "response",
|
|
5865
|
+
text
|
|
5866
|
+
});
|
|
5867
|
+
break;
|
|
5868
|
+
}
|
|
5869
|
+
case "response.reasoning_summary_text.delta": {
|
|
5870
|
+
thoughtDeltaEmitted = true;
|
|
5871
|
+
const text = typeof event.delta === "string" ? event.delta : "";
|
|
5872
|
+
if (text.length > 0) {
|
|
5873
|
+
stepCallLogger?.appendThoughtDelta(text);
|
|
5874
|
+
}
|
|
5875
|
+
emitEvent({
|
|
5876
|
+
type: "delta",
|
|
5877
|
+
channel: "thought",
|
|
5878
|
+
text
|
|
5879
|
+
});
|
|
5880
|
+
break;
|
|
5881
|
+
}
|
|
5882
|
+
case "response.refusal.delta":
|
|
5883
|
+
blocked = true;
|
|
5884
|
+
emitEvent({ type: "blocked" });
|
|
5885
|
+
break;
|
|
5886
|
+
default:
|
|
5887
|
+
break;
|
|
5888
|
+
}
|
|
5889
|
+
}
|
|
5890
|
+
return await stream.finalResponse();
|
|
5891
|
+
},
|
|
5892
|
+
providerInfo.model,
|
|
5893
|
+
{
|
|
5894
|
+
onSettled: (metrics) => {
|
|
5895
|
+
schedulerMetrics = metrics;
|
|
5223
5896
|
}
|
|
5224
5897
|
}
|
|
5225
|
-
|
|
5226
|
-
|
|
5227
|
-
|
|
5228
|
-
{
|
|
5229
|
-
|
|
5230
|
-
|
|
5898
|
+
);
|
|
5899
|
+
modelVersion = typeof finalResponse.model === "string" ? finalResponse.model : request.model;
|
|
5900
|
+
emitEvent({ type: "model", modelVersion });
|
|
5901
|
+
if (finalResponse.error) {
|
|
5902
|
+
const message = typeof finalResponse.error.message === "string" ? finalResponse.error.message : "OpenAI response failed";
|
|
5903
|
+
throw new Error(message);
|
|
5904
|
+
}
|
|
5905
|
+
usageTokens = extractOpenAiUsageTokens(finalResponse.usage);
|
|
5906
|
+
const responseText = extractOpenAiResponseParts(finalResponse).parts.filter((p) => p.type === "text" && p.thought !== true).map((p) => p.text).join("").trim();
|
|
5907
|
+
const reasoningSummary = extractOpenAiReasoningSummary(finalResponse).trim();
|
|
5908
|
+
if (!thoughtDeltaEmitted && reasoningSummary.length > 0) {
|
|
5909
|
+
stepCallLogger?.appendThoughtDelta(reasoningSummary);
|
|
5910
|
+
emitEvent({ type: "delta", channel: "thought", text: reasoningSummary });
|
|
5911
|
+
}
|
|
5912
|
+
const modelCompletedAtMs = Date.now();
|
|
5913
|
+
const stepCostUsd = estimateCallCostUsd({
|
|
5914
|
+
modelId: modelVersion,
|
|
5915
|
+
tokens: usageTokens,
|
|
5916
|
+
responseImages: 0
|
|
5917
|
+
});
|
|
5918
|
+
totalCostUsd += stepCostUsd;
|
|
5919
|
+
if (usageTokens) {
|
|
5920
|
+
emitEvent({ type: "usage", usage: usageTokens, costUsd: stepCostUsd, modelVersion });
|
|
5921
|
+
}
|
|
5922
|
+
const responseToolCalls = extractOpenAiToolCalls(finalResponse.output);
|
|
5923
|
+
const stepToolCalls = [];
|
|
5924
|
+
if (responseToolCalls.length === 0) {
|
|
5925
|
+
const steeringInput2 = steeringInternal?.drainPendingContents() ?? [];
|
|
5926
|
+
const steeringItems2 = steeringInput2.length > 0 ? toOpenAiInput(steeringInput2) : [];
|
|
5927
|
+
finalText = responseText;
|
|
5928
|
+
finalThoughts = reasoningSummary;
|
|
5929
|
+
const stepCompletedAtMs2 = Date.now();
|
|
5930
|
+
const timing2 = buildStepTiming({
|
|
5931
|
+
stepStartedAtMs,
|
|
5932
|
+
stepCompletedAtMs: stepCompletedAtMs2,
|
|
5933
|
+
modelCompletedAtMs,
|
|
5934
|
+
firstModelEventAtMs,
|
|
5935
|
+
schedulerMetrics,
|
|
5936
|
+
toolExecutionMs: 0,
|
|
5937
|
+
waitToolMs: 0
|
|
5938
|
+
});
|
|
5939
|
+
steps.push({
|
|
5940
|
+
step: steps.length + 1,
|
|
5941
|
+
modelVersion,
|
|
5942
|
+
text: responseText || void 0,
|
|
5943
|
+
thoughts: reasoningSummary || void 0,
|
|
5944
|
+
toolCalls: [],
|
|
5945
|
+
usage: usageTokens,
|
|
5946
|
+
costUsd: stepCostUsd,
|
|
5947
|
+
timing: timing2
|
|
5948
|
+
});
|
|
5949
|
+
stepCallLogger?.complete({
|
|
5950
|
+
provider: "openai",
|
|
5951
|
+
model: request.model,
|
|
5952
|
+
modelVersion,
|
|
5953
|
+
step: turn,
|
|
5954
|
+
usage: usageTokens,
|
|
5955
|
+
costUsd: stepCostUsd,
|
|
5956
|
+
blocked,
|
|
5957
|
+
responseChars: responseText.length,
|
|
5958
|
+
thoughtChars: reasoningSummary.length,
|
|
5959
|
+
toolCalls: 0,
|
|
5960
|
+
finalStep: steeringItems2.length === 0
|
|
5961
|
+
});
|
|
5962
|
+
if (steeringItems2.length === 0) {
|
|
5963
|
+
return { text: finalText, thoughts: finalThoughts, steps, totalCostUsd };
|
|
5231
5964
|
}
|
|
5965
|
+
previousResponseId = finalResponse.id;
|
|
5966
|
+
input = steeringItems2;
|
|
5967
|
+
continue;
|
|
5232
5968
|
}
|
|
5233
|
-
|
|
5234
|
-
|
|
5235
|
-
|
|
5236
|
-
|
|
5237
|
-
|
|
5238
|
-
|
|
5239
|
-
|
|
5240
|
-
|
|
5241
|
-
|
|
5242
|
-
|
|
5243
|
-
|
|
5244
|
-
|
|
5245
|
-
|
|
5246
|
-
|
|
5247
|
-
|
|
5248
|
-
|
|
5249
|
-
|
|
5250
|
-
|
|
5251
|
-
|
|
5252
|
-
|
|
5253
|
-
|
|
5254
|
-
|
|
5255
|
-
|
|
5256
|
-
|
|
5257
|
-
|
|
5258
|
-
|
|
5259
|
-
|
|
5260
|
-
|
|
5261
|
-
|
|
5262
|
-
|
|
5263
|
-
|
|
5264
|
-
const
|
|
5969
|
+
const callInputs = responseToolCalls.map((call, index) => {
|
|
5970
|
+
const toolIndex = index + 1;
|
|
5971
|
+
const toolId = buildToolLogId(turn, toolIndex);
|
|
5972
|
+
const toolName = call.name;
|
|
5973
|
+
if (call.kind === "custom") {
|
|
5974
|
+
return {
|
|
5975
|
+
call,
|
|
5976
|
+
toolName,
|
|
5977
|
+
value: call.input,
|
|
5978
|
+
parseError: void 0,
|
|
5979
|
+
toolId,
|
|
5980
|
+
turn,
|
|
5981
|
+
toolIndex
|
|
5982
|
+
};
|
|
5983
|
+
}
|
|
5984
|
+
const { value, error: parseError } = parseOpenAiToolArguments(call.arguments);
|
|
5985
|
+
return { call, toolName, value, parseError, toolId, turn, toolIndex };
|
|
5986
|
+
});
|
|
5987
|
+
for (const entry of callInputs) {
|
|
5988
|
+
emitEvent({
|
|
5989
|
+
type: "tool_call",
|
|
5990
|
+
phase: "started",
|
|
5991
|
+
turn: entry.turn,
|
|
5992
|
+
toolIndex: entry.toolIndex,
|
|
5993
|
+
toolName: entry.toolName,
|
|
5994
|
+
toolId: entry.toolId,
|
|
5995
|
+
callKind: entry.call.kind,
|
|
5996
|
+
callId: entry.call.call_id,
|
|
5997
|
+
input: entry.value
|
|
5998
|
+
});
|
|
5999
|
+
}
|
|
6000
|
+
const callResults = await Promise.all(
|
|
6001
|
+
callInputs.map(async (entry) => {
|
|
6002
|
+
return await toolCallContextStorage.run(
|
|
6003
|
+
{
|
|
6004
|
+
toolName: entry.toolName,
|
|
6005
|
+
toolId: entry.toolId,
|
|
6006
|
+
turn: entry.turn,
|
|
6007
|
+
toolIndex: entry.toolIndex
|
|
6008
|
+
},
|
|
6009
|
+
async () => {
|
|
6010
|
+
const { result, outputPayload } = await executeToolCall({
|
|
6011
|
+
callKind: entry.call.kind,
|
|
6012
|
+
toolName: entry.toolName,
|
|
6013
|
+
tool: request.tools[entry.toolName],
|
|
6014
|
+
rawInput: entry.value,
|
|
6015
|
+
parseError: entry.parseError
|
|
6016
|
+
});
|
|
6017
|
+
return { entry, result, outputPayload };
|
|
6018
|
+
}
|
|
6019
|
+
);
|
|
6020
|
+
})
|
|
6021
|
+
);
|
|
6022
|
+
const toolOutputs = [];
|
|
6023
|
+
let toolExecutionMs = 0;
|
|
6024
|
+
let waitToolMs = 0;
|
|
6025
|
+
for (const { entry, result, outputPayload } of callResults) {
|
|
6026
|
+
stepToolCalls.push({ ...result, callId: entry.call.call_id });
|
|
6027
|
+
const callDurationMs = toToolResultDuration(result);
|
|
6028
|
+
toolExecutionMs += callDurationMs;
|
|
6029
|
+
if (entry.toolName.toLowerCase() === SUBAGENT_WAIT_TOOL_NAME) {
|
|
6030
|
+
waitToolMs += callDurationMs;
|
|
6031
|
+
}
|
|
6032
|
+
emitEvent({
|
|
6033
|
+
type: "tool_call",
|
|
6034
|
+
phase: "completed",
|
|
6035
|
+
turn: entry.turn,
|
|
6036
|
+
toolIndex: entry.toolIndex,
|
|
6037
|
+
toolName: entry.toolName,
|
|
6038
|
+
toolId: entry.toolId,
|
|
6039
|
+
callKind: entry.call.kind,
|
|
6040
|
+
callId: entry.call.call_id,
|
|
6041
|
+
input: entry.value,
|
|
6042
|
+
output: result.output,
|
|
6043
|
+
error: result.error,
|
|
6044
|
+
durationMs: result.durationMs
|
|
6045
|
+
});
|
|
6046
|
+
if (entry.call.kind === "custom") {
|
|
6047
|
+
toolOutputs.push({
|
|
6048
|
+
type: "custom_tool_call_output",
|
|
6049
|
+
call_id: entry.call.call_id,
|
|
6050
|
+
output: toOpenAiToolOutput(outputPayload)
|
|
6051
|
+
});
|
|
6052
|
+
} else {
|
|
6053
|
+
toolOutputs.push({
|
|
6054
|
+
type: "function_call_output",
|
|
6055
|
+
call_id: entry.call.call_id,
|
|
6056
|
+
output: toOpenAiToolOutput(outputPayload)
|
|
6057
|
+
});
|
|
6058
|
+
}
|
|
6059
|
+
}
|
|
6060
|
+
const stepCompletedAtMs = Date.now();
|
|
6061
|
+
const timing = buildStepTiming({
|
|
5265
6062
|
stepStartedAtMs,
|
|
5266
|
-
stepCompletedAtMs
|
|
6063
|
+
stepCompletedAtMs,
|
|
5267
6064
|
modelCompletedAtMs,
|
|
5268
6065
|
firstModelEventAtMs,
|
|
5269
6066
|
schedulerMetrics,
|
|
5270
|
-
toolExecutionMs
|
|
5271
|
-
waitToolMs
|
|
6067
|
+
toolExecutionMs,
|
|
6068
|
+
waitToolMs
|
|
5272
6069
|
});
|
|
5273
6070
|
steps.push({
|
|
5274
6071
|
step: steps.length + 1,
|
|
5275
6072
|
modelVersion,
|
|
5276
6073
|
text: responseText || void 0,
|
|
5277
6074
|
thoughts: reasoningSummary || void 0,
|
|
5278
|
-
toolCalls:
|
|
6075
|
+
toolCalls: stepToolCalls,
|
|
5279
6076
|
usage: usageTokens,
|
|
5280
6077
|
costUsd: stepCostUsd,
|
|
5281
|
-
timing
|
|
6078
|
+
timing
|
|
5282
6079
|
});
|
|
5283
|
-
|
|
5284
|
-
|
|
5285
|
-
|
|
5286
|
-
|
|
5287
|
-
|
|
5288
|
-
|
|
5289
|
-
|
|
5290
|
-
|
|
5291
|
-
|
|
5292
|
-
|
|
5293
|
-
|
|
5294
|
-
|
|
5295
|
-
|
|
5296
|
-
|
|
5297
|
-
toolName,
|
|
5298
|
-
value: call.input,
|
|
5299
|
-
parseError: void 0,
|
|
5300
|
-
toolId,
|
|
5301
|
-
turn,
|
|
5302
|
-
toolIndex
|
|
5303
|
-
};
|
|
5304
|
-
}
|
|
5305
|
-
const { value, error: parseError } = parseOpenAiToolArguments(call.arguments);
|
|
5306
|
-
return { call, toolName, value, parseError, toolId, turn, toolIndex };
|
|
5307
|
-
});
|
|
5308
|
-
for (const entry of callInputs) {
|
|
5309
|
-
emitEvent({
|
|
5310
|
-
type: "tool_call",
|
|
5311
|
-
phase: "started",
|
|
5312
|
-
turn: entry.turn,
|
|
5313
|
-
toolIndex: entry.toolIndex,
|
|
5314
|
-
toolName: entry.toolName,
|
|
5315
|
-
toolId: entry.toolId,
|
|
5316
|
-
callKind: entry.call.kind,
|
|
5317
|
-
callId: entry.call.call_id,
|
|
5318
|
-
input: entry.value
|
|
6080
|
+
const steeringInput = steeringInternal?.drainPendingContents() ?? [];
|
|
6081
|
+
const steeringItems = steeringInput.length > 0 ? toOpenAiInput(steeringInput) : [];
|
|
6082
|
+
stepCallLogger?.complete({
|
|
6083
|
+
provider: "openai",
|
|
6084
|
+
model: request.model,
|
|
6085
|
+
modelVersion,
|
|
6086
|
+
step: turn,
|
|
6087
|
+
usage: usageTokens,
|
|
6088
|
+
costUsd: stepCostUsd,
|
|
6089
|
+
blocked,
|
|
6090
|
+
responseChars: responseText.length,
|
|
6091
|
+
thoughtChars: reasoningSummary.length,
|
|
6092
|
+
toolCalls: stepToolCalls.length,
|
|
6093
|
+
finalStep: false
|
|
5319
6094
|
});
|
|
5320
|
-
|
|
5321
|
-
|
|
5322
|
-
|
|
5323
|
-
|
|
5324
|
-
|
|
5325
|
-
|
|
5326
|
-
|
|
5327
|
-
|
|
5328
|
-
|
|
5329
|
-
|
|
5330
|
-
async () => {
|
|
5331
|
-
const { result, outputPayload } = await executeToolCall({
|
|
5332
|
-
callKind: entry.call.kind,
|
|
5333
|
-
toolName: entry.toolName,
|
|
5334
|
-
tool: request.tools[entry.toolName],
|
|
5335
|
-
rawInput: entry.value,
|
|
5336
|
-
parseError: entry.parseError
|
|
5337
|
-
});
|
|
5338
|
-
return { entry, result, outputPayload };
|
|
5339
|
-
}
|
|
5340
|
-
);
|
|
5341
|
-
})
|
|
5342
|
-
);
|
|
5343
|
-
const toolOutputs = [];
|
|
5344
|
-
let toolExecutionMs = 0;
|
|
5345
|
-
let waitToolMs = 0;
|
|
5346
|
-
for (const { entry, result, outputPayload } of callResults) {
|
|
5347
|
-
stepToolCalls.push({ ...result, callId: entry.call.call_id });
|
|
5348
|
-
const callDurationMs = toToolResultDuration(result);
|
|
5349
|
-
toolExecutionMs += callDurationMs;
|
|
5350
|
-
if (entry.toolName.toLowerCase() === SUBAGENT_WAIT_TOOL_NAME) {
|
|
5351
|
-
waitToolMs += callDurationMs;
|
|
5352
|
-
}
|
|
5353
|
-
emitEvent({
|
|
5354
|
-
type: "tool_call",
|
|
5355
|
-
phase: "completed",
|
|
5356
|
-
turn: entry.turn,
|
|
5357
|
-
toolIndex: entry.toolIndex,
|
|
5358
|
-
toolName: entry.toolName,
|
|
5359
|
-
toolId: entry.toolId,
|
|
5360
|
-
callKind: entry.call.kind,
|
|
5361
|
-
callId: entry.call.call_id,
|
|
5362
|
-
input: entry.value,
|
|
5363
|
-
output: result.output,
|
|
5364
|
-
error: result.error,
|
|
5365
|
-
durationMs: result.durationMs
|
|
6095
|
+
previousResponseId = finalResponse.id;
|
|
6096
|
+
input = steeringItems.length > 0 ? toolOutputs.concat(steeringItems) : toolOutputs;
|
|
6097
|
+
} catch (error) {
|
|
6098
|
+
stepCallLogger?.fail(error, {
|
|
6099
|
+
provider: "openai",
|
|
6100
|
+
model: request.model,
|
|
6101
|
+
modelVersion,
|
|
6102
|
+
step: turn,
|
|
6103
|
+
usage: usageTokens,
|
|
6104
|
+
blocked
|
|
5366
6105
|
});
|
|
5367
|
-
|
|
5368
|
-
toolOutputs.push({
|
|
5369
|
-
type: "custom_tool_call_output",
|
|
5370
|
-
call_id: entry.call.call_id,
|
|
5371
|
-
output: toOpenAiToolOutput(outputPayload)
|
|
5372
|
-
});
|
|
5373
|
-
} else {
|
|
5374
|
-
toolOutputs.push({
|
|
5375
|
-
type: "function_call_output",
|
|
5376
|
-
call_id: entry.call.call_id,
|
|
5377
|
-
output: toOpenAiToolOutput(outputPayload)
|
|
5378
|
-
});
|
|
5379
|
-
}
|
|
6106
|
+
throw error;
|
|
5380
6107
|
}
|
|
5381
|
-
const stepCompletedAtMs = Date.now();
|
|
5382
|
-
const timing = buildStepTiming({
|
|
5383
|
-
stepStartedAtMs,
|
|
5384
|
-
stepCompletedAtMs,
|
|
5385
|
-
modelCompletedAtMs,
|
|
5386
|
-
firstModelEventAtMs,
|
|
5387
|
-
schedulerMetrics,
|
|
5388
|
-
toolExecutionMs,
|
|
5389
|
-
waitToolMs
|
|
5390
|
-
});
|
|
5391
|
-
steps.push({
|
|
5392
|
-
step: steps.length + 1,
|
|
5393
|
-
modelVersion,
|
|
5394
|
-
text: responseText || void 0,
|
|
5395
|
-
thoughts: reasoningSummary || void 0,
|
|
5396
|
-
toolCalls: stepToolCalls,
|
|
5397
|
-
usage: usageTokens,
|
|
5398
|
-
costUsd: stepCostUsd,
|
|
5399
|
-
timing
|
|
5400
|
-
});
|
|
5401
|
-
const steeringInput = steeringInternal?.drainPendingContents() ?? [];
|
|
5402
|
-
const steeringItems = steeringInput.length > 0 ? toOpenAiInput(steeringInput) : [];
|
|
5403
|
-
previousResponseId = finalResponse.id;
|
|
5404
|
-
input = steeringItems.length > 0 ? toolOutputs.concat(steeringItems) : toolOutputs;
|
|
5405
6108
|
}
|
|
5406
6109
|
throw new Error(`Tool loop exceeded max steps (${maxSteps}) without final response.`);
|
|
5407
6110
|
}
|
|
@@ -5419,242 +6122,656 @@ async function runToolLoop(request) {
|
|
|
5419
6122
|
const stepStartedAtMs = Date.now();
|
|
5420
6123
|
let firstModelEventAtMs;
|
|
5421
6124
|
let thoughtDeltaEmitted = false;
|
|
6125
|
+
let sawResponseDelta = false;
|
|
6126
|
+
let modelVersion = request.model;
|
|
6127
|
+
let usageTokens;
|
|
6128
|
+
let responseText = "";
|
|
6129
|
+
let reasoningSummaryText = "";
|
|
5422
6130
|
const markFirstModelEvent = () => {
|
|
5423
6131
|
if (firstModelEventAtMs === void 0) {
|
|
5424
6132
|
firstModelEventAtMs = Date.now();
|
|
5425
6133
|
}
|
|
5426
6134
|
};
|
|
5427
|
-
const
|
|
5428
|
-
|
|
5429
|
-
|
|
5430
|
-
|
|
5431
|
-
|
|
5432
|
-
|
|
5433
|
-
|
|
5434
|
-
|
|
5435
|
-
|
|
5436
|
-
|
|
5437
|
-
|
|
5438
|
-
|
|
5439
|
-
|
|
5440
|
-
|
|
5441
|
-
|
|
5442
|
-
summary: "detailed"
|
|
5443
|
-
},
|
|
5444
|
-
text: { verbosity: resolveOpenAiVerbosity(request.model) }
|
|
6135
|
+
const stepRequestPayload = {
|
|
6136
|
+
model: providerInfo.model,
|
|
6137
|
+
store: false,
|
|
6138
|
+
stream: true,
|
|
6139
|
+
...providerInfo.serviceTier ? { service_tier: providerInfo.serviceTier } : {},
|
|
6140
|
+
instructions: toolLoopInput.instructions ?? "You are a helpful assistant.",
|
|
6141
|
+
input,
|
|
6142
|
+
prompt_cache_key: promptCacheKey,
|
|
6143
|
+
include: ["reasoning.encrypted_content"],
|
|
6144
|
+
tools: openAiTools,
|
|
6145
|
+
tool_choice: "auto",
|
|
6146
|
+
parallel_tool_calls: true,
|
|
6147
|
+
reasoning: {
|
|
6148
|
+
effort: toOpenAiReasoningEffort(reasoningEffort),
|
|
6149
|
+
summary: "detailed"
|
|
5445
6150
|
},
|
|
5446
|
-
|
|
5447
|
-
|
|
5448
|
-
|
|
5449
|
-
|
|
5450
|
-
|
|
5451
|
-
|
|
6151
|
+
text: { verbosity: resolveOpenAiVerbosity(request.model) }
|
|
6152
|
+
};
|
|
6153
|
+
const stepCallLogger = startLlmCallLoggerFromPayload({
|
|
6154
|
+
provider: "chatgpt",
|
|
6155
|
+
modelId: request.model,
|
|
6156
|
+
requestPayload: stepRequestPayload,
|
|
6157
|
+
step: turn
|
|
6158
|
+
});
|
|
6159
|
+
try {
|
|
6160
|
+
const response = await collectChatGptCodexResponseWithRetry({
|
|
6161
|
+
sessionId: conversationId,
|
|
6162
|
+
request: stepRequestPayload,
|
|
6163
|
+
signal: request.signal,
|
|
6164
|
+
onDelta: (delta) => {
|
|
6165
|
+
if (delta.thoughtDelta) {
|
|
6166
|
+
markFirstModelEvent();
|
|
6167
|
+
thoughtDeltaEmitted = true;
|
|
6168
|
+
stepCallLogger?.appendThoughtDelta(delta.thoughtDelta);
|
|
6169
|
+
request.onEvent?.({ type: "delta", channel: "thought", text: delta.thoughtDelta });
|
|
6170
|
+
}
|
|
6171
|
+
if (delta.textDelta) {
|
|
6172
|
+
markFirstModelEvent();
|
|
6173
|
+
sawResponseDelta = true;
|
|
6174
|
+
stepCallLogger?.appendResponseDelta(delta.textDelta);
|
|
6175
|
+
request.onEvent?.({ type: "delta", channel: "response", text: delta.textDelta });
|
|
6176
|
+
}
|
|
5452
6177
|
}
|
|
5453
|
-
|
|
5454
|
-
|
|
5455
|
-
|
|
6178
|
+
});
|
|
6179
|
+
const modelCompletedAtMs = Date.now();
|
|
6180
|
+
modelVersion = response.model && !providerInfo.serviceTier ? `chatgpt-${response.model}` : request.model;
|
|
6181
|
+
usageTokens = extractChatGptUsageTokens(response.usage);
|
|
6182
|
+
const stepCostUsd = estimateCallCostUsd({
|
|
6183
|
+
modelId: modelVersion,
|
|
6184
|
+
tokens: usageTokens,
|
|
6185
|
+
responseImages: 0
|
|
6186
|
+
});
|
|
6187
|
+
totalCostUsd += stepCostUsd;
|
|
6188
|
+
responseText = (response.text ?? "").trim();
|
|
6189
|
+
reasoningSummaryText = (response.reasoningSummaryText ?? "").trim();
|
|
6190
|
+
if (!thoughtDeltaEmitted && reasoningSummaryText.length > 0) {
|
|
6191
|
+
stepCallLogger?.appendThoughtDelta(reasoningSummaryText);
|
|
6192
|
+
request.onEvent?.({ type: "delta", channel: "thought", text: reasoningSummaryText });
|
|
6193
|
+
}
|
|
6194
|
+
if (!sawResponseDelta && responseText.length > 0) {
|
|
6195
|
+
stepCallLogger?.appendResponseDelta(responseText);
|
|
6196
|
+
}
|
|
6197
|
+
const responseToolCalls = response.toolCalls ?? [];
|
|
6198
|
+
if (responseToolCalls.length === 0) {
|
|
6199
|
+
const steeringInput2 = steeringInternal?.drainPendingContents() ?? [];
|
|
6200
|
+
const steeringItems2 = steeringInput2.length > 0 ? toChatGptInput(steeringInput2).input : [];
|
|
6201
|
+
finalText = responseText;
|
|
6202
|
+
finalThoughts = reasoningSummaryText;
|
|
6203
|
+
const stepCompletedAtMs2 = Date.now();
|
|
6204
|
+
const timing2 = buildStepTiming({
|
|
6205
|
+
stepStartedAtMs,
|
|
6206
|
+
stepCompletedAtMs: stepCompletedAtMs2,
|
|
6207
|
+
modelCompletedAtMs,
|
|
6208
|
+
firstModelEventAtMs,
|
|
6209
|
+
toolExecutionMs: 0,
|
|
6210
|
+
waitToolMs: 0
|
|
6211
|
+
});
|
|
6212
|
+
steps.push({
|
|
6213
|
+
step: steps.length + 1,
|
|
6214
|
+
modelVersion,
|
|
6215
|
+
text: responseText || void 0,
|
|
6216
|
+
thoughts: reasoningSummaryText || void 0,
|
|
6217
|
+
toolCalls: [],
|
|
6218
|
+
usage: usageTokens,
|
|
6219
|
+
costUsd: stepCostUsd,
|
|
6220
|
+
timing: timing2
|
|
6221
|
+
});
|
|
6222
|
+
stepCallLogger?.complete({
|
|
6223
|
+
provider: "chatgpt",
|
|
6224
|
+
model: request.model,
|
|
6225
|
+
modelVersion,
|
|
6226
|
+
step: turn,
|
|
6227
|
+
usage: usageTokens,
|
|
6228
|
+
costUsd: stepCostUsd,
|
|
6229
|
+
responseChars: responseText.length,
|
|
6230
|
+
thoughtChars: reasoningSummaryText.length,
|
|
6231
|
+
toolCalls: 0,
|
|
6232
|
+
finalStep: steeringItems2.length === 0
|
|
6233
|
+
});
|
|
6234
|
+
if (steeringItems2.length === 0) {
|
|
6235
|
+
return { text: finalText, thoughts: finalThoughts, steps, totalCostUsd };
|
|
5456
6236
|
}
|
|
6237
|
+
const assistantItem = toChatGptAssistantMessage(responseText);
|
|
6238
|
+
input = assistantItem ? input.concat(assistantItem, steeringItems2) : input.concat(steeringItems2);
|
|
6239
|
+
continue;
|
|
5457
6240
|
}
|
|
5458
|
-
|
|
5459
|
-
|
|
5460
|
-
|
|
5461
|
-
|
|
5462
|
-
|
|
5463
|
-
|
|
5464
|
-
|
|
5465
|
-
|
|
5466
|
-
|
|
5467
|
-
|
|
5468
|
-
|
|
5469
|
-
|
|
5470
|
-
|
|
5471
|
-
|
|
5472
|
-
|
|
5473
|
-
|
|
5474
|
-
|
|
5475
|
-
|
|
5476
|
-
|
|
5477
|
-
|
|
5478
|
-
|
|
5479
|
-
|
|
5480
|
-
|
|
6241
|
+
const toolCalls = [];
|
|
6242
|
+
const toolOutputs = [];
|
|
6243
|
+
const callInputs = responseToolCalls.map((call, index) => {
|
|
6244
|
+
const toolIndex = index + 1;
|
|
6245
|
+
const toolId = buildToolLogId(turn, toolIndex);
|
|
6246
|
+
const toolName = call.name;
|
|
6247
|
+
const { value, error: parseError } = call.kind === "custom" ? { value: call.input, error: void 0 } : parseOpenAiToolArguments(call.arguments);
|
|
6248
|
+
const ids = normalizeChatGptToolIds({
|
|
6249
|
+
callKind: call.kind,
|
|
6250
|
+
callId: call.callId,
|
|
6251
|
+
itemId: call.id
|
|
6252
|
+
});
|
|
6253
|
+
return { call, toolName, value, parseError, ids, toolId, turn, toolIndex };
|
|
6254
|
+
});
|
|
6255
|
+
for (const entry of callInputs) {
|
|
6256
|
+
request.onEvent?.({
|
|
6257
|
+
type: "tool_call",
|
|
6258
|
+
phase: "started",
|
|
6259
|
+
turn: entry.turn,
|
|
6260
|
+
toolIndex: entry.toolIndex,
|
|
6261
|
+
toolName: entry.toolName,
|
|
6262
|
+
toolId: entry.toolId,
|
|
6263
|
+
callKind: entry.call.kind,
|
|
6264
|
+
callId: entry.ids.callId,
|
|
6265
|
+
input: entry.value
|
|
6266
|
+
});
|
|
6267
|
+
}
|
|
6268
|
+
const callResults = await Promise.all(
|
|
6269
|
+
callInputs.map(async (entry) => {
|
|
6270
|
+
return await toolCallContextStorage.run(
|
|
6271
|
+
{
|
|
6272
|
+
toolName: entry.toolName,
|
|
6273
|
+
toolId: entry.toolId,
|
|
6274
|
+
turn: entry.turn,
|
|
6275
|
+
toolIndex: entry.toolIndex
|
|
6276
|
+
},
|
|
6277
|
+
async () => {
|
|
6278
|
+
const { result, outputPayload } = await executeToolCall({
|
|
6279
|
+
callKind: entry.call.kind,
|
|
6280
|
+
toolName: entry.toolName,
|
|
6281
|
+
tool: request.tools[entry.toolName],
|
|
6282
|
+
rawInput: entry.value,
|
|
6283
|
+
parseError: entry.parseError
|
|
6284
|
+
});
|
|
6285
|
+
return { entry, result, outputPayload };
|
|
6286
|
+
}
|
|
6287
|
+
);
|
|
6288
|
+
})
|
|
6289
|
+
);
|
|
6290
|
+
let toolExecutionMs = 0;
|
|
6291
|
+
let waitToolMs = 0;
|
|
6292
|
+
for (const { entry, result, outputPayload } of callResults) {
|
|
6293
|
+
toolCalls.push({ ...result, callId: entry.ids.callId });
|
|
6294
|
+
const callDurationMs = toToolResultDuration(result);
|
|
6295
|
+
toolExecutionMs += callDurationMs;
|
|
6296
|
+
if (entry.toolName.toLowerCase() === SUBAGENT_WAIT_TOOL_NAME) {
|
|
6297
|
+
waitToolMs += callDurationMs;
|
|
6298
|
+
}
|
|
6299
|
+
request.onEvent?.({
|
|
6300
|
+
type: "tool_call",
|
|
6301
|
+
phase: "completed",
|
|
6302
|
+
turn: entry.turn,
|
|
6303
|
+
toolIndex: entry.toolIndex,
|
|
6304
|
+
toolName: entry.toolName,
|
|
6305
|
+
toolId: entry.toolId,
|
|
6306
|
+
callKind: entry.call.kind,
|
|
6307
|
+
callId: entry.ids.callId,
|
|
6308
|
+
input: entry.value,
|
|
6309
|
+
output: result.output,
|
|
6310
|
+
error: result.error,
|
|
6311
|
+
durationMs: result.durationMs
|
|
6312
|
+
});
|
|
6313
|
+
if (entry.call.kind === "custom") {
|
|
6314
|
+
toolOutputs.push({
|
|
6315
|
+
type: "custom_tool_call",
|
|
6316
|
+
id: entry.ids.itemId,
|
|
6317
|
+
call_id: entry.ids.callId,
|
|
6318
|
+
name: entry.toolName,
|
|
6319
|
+
input: entry.call.input,
|
|
6320
|
+
status: "completed"
|
|
6321
|
+
});
|
|
6322
|
+
toolOutputs.push({
|
|
6323
|
+
type: "custom_tool_call_output",
|
|
6324
|
+
call_id: entry.ids.callId,
|
|
6325
|
+
output: toOpenAiToolOutput(outputPayload)
|
|
6326
|
+
});
|
|
6327
|
+
} else {
|
|
6328
|
+
toolOutputs.push({
|
|
6329
|
+
type: "function_call",
|
|
6330
|
+
id: entry.ids.itemId,
|
|
6331
|
+
call_id: entry.ids.callId,
|
|
6332
|
+
name: entry.toolName,
|
|
6333
|
+
arguments: entry.call.arguments,
|
|
6334
|
+
status: "completed"
|
|
6335
|
+
});
|
|
6336
|
+
toolOutputs.push({
|
|
6337
|
+
type: "function_call_output",
|
|
6338
|
+
call_id: entry.ids.callId,
|
|
6339
|
+
output: toOpenAiToolOutput(outputPayload)
|
|
6340
|
+
});
|
|
6341
|
+
}
|
|
6342
|
+
}
|
|
6343
|
+
const stepCompletedAtMs = Date.now();
|
|
6344
|
+
const timing = buildStepTiming({
|
|
5481
6345
|
stepStartedAtMs,
|
|
5482
|
-
stepCompletedAtMs
|
|
6346
|
+
stepCompletedAtMs,
|
|
5483
6347
|
modelCompletedAtMs,
|
|
5484
6348
|
firstModelEventAtMs,
|
|
5485
|
-
toolExecutionMs
|
|
5486
|
-
waitToolMs
|
|
6349
|
+
toolExecutionMs,
|
|
6350
|
+
waitToolMs
|
|
5487
6351
|
});
|
|
5488
6352
|
steps.push({
|
|
5489
6353
|
step: steps.length + 1,
|
|
5490
6354
|
modelVersion,
|
|
5491
6355
|
text: responseText || void 0,
|
|
5492
6356
|
thoughts: reasoningSummaryText || void 0,
|
|
5493
|
-
toolCalls
|
|
6357
|
+
toolCalls,
|
|
5494
6358
|
usage: usageTokens,
|
|
5495
6359
|
costUsd: stepCostUsd,
|
|
5496
|
-
timing
|
|
6360
|
+
timing
|
|
5497
6361
|
});
|
|
5498
|
-
|
|
5499
|
-
|
|
5500
|
-
|
|
5501
|
-
|
|
5502
|
-
|
|
5503
|
-
|
|
5504
|
-
|
|
5505
|
-
|
|
5506
|
-
|
|
5507
|
-
|
|
5508
|
-
|
|
5509
|
-
|
|
5510
|
-
|
|
5511
|
-
const { value, error: parseError } = call.kind === "custom" ? { value: call.input, error: void 0 } : parseOpenAiToolArguments(call.arguments);
|
|
5512
|
-
const ids = normalizeChatGptToolIds({
|
|
5513
|
-
callKind: call.kind,
|
|
5514
|
-
callId: call.callId,
|
|
5515
|
-
itemId: call.id
|
|
6362
|
+
const steeringInput = steeringInternal?.drainPendingContents() ?? [];
|
|
6363
|
+
const steeringItems = steeringInput.length > 0 ? toChatGptInput(steeringInput).input : [];
|
|
6364
|
+
stepCallLogger?.complete({
|
|
6365
|
+
provider: "chatgpt",
|
|
6366
|
+
model: request.model,
|
|
6367
|
+
modelVersion,
|
|
6368
|
+
step: turn,
|
|
6369
|
+
usage: usageTokens,
|
|
6370
|
+
costUsd: stepCostUsd,
|
|
6371
|
+
responseChars: responseText.length,
|
|
6372
|
+
thoughtChars: reasoningSummaryText.length,
|
|
6373
|
+
toolCalls: toolCalls.length,
|
|
6374
|
+
finalStep: false
|
|
5516
6375
|
});
|
|
5517
|
-
|
|
5518
|
-
})
|
|
5519
|
-
|
|
5520
|
-
|
|
5521
|
-
|
|
5522
|
-
|
|
5523
|
-
|
|
5524
|
-
|
|
5525
|
-
toolName: entry.toolName,
|
|
5526
|
-
toolId: entry.toolId,
|
|
5527
|
-
callKind: entry.call.kind,
|
|
5528
|
-
callId: entry.ids.callId,
|
|
5529
|
-
input: entry.value
|
|
6376
|
+
input = steeringItems.length > 0 ? input.concat(toolOutputs, steeringItems) : input.concat(toolOutputs);
|
|
6377
|
+
} catch (error) {
|
|
6378
|
+
stepCallLogger?.fail(error, {
|
|
6379
|
+
provider: "chatgpt",
|
|
6380
|
+
model: request.model,
|
|
6381
|
+
modelVersion,
|
|
6382
|
+
step: turn,
|
|
6383
|
+
usage: usageTokens
|
|
5530
6384
|
});
|
|
6385
|
+
throw error;
|
|
5531
6386
|
}
|
|
5532
|
-
|
|
5533
|
-
|
|
5534
|
-
|
|
5535
|
-
|
|
5536
|
-
|
|
5537
|
-
|
|
5538
|
-
|
|
5539
|
-
toolIndex: entry.toolIndex
|
|
5540
|
-
},
|
|
5541
|
-
async () => {
|
|
5542
|
-
const { result, outputPayload } = await executeToolCall({
|
|
5543
|
-
callKind: entry.call.kind,
|
|
5544
|
-
toolName: entry.toolName,
|
|
5545
|
-
tool: request.tools[entry.toolName],
|
|
5546
|
-
rawInput: entry.value,
|
|
5547
|
-
parseError: entry.parseError
|
|
5548
|
-
});
|
|
5549
|
-
return { entry, result, outputPayload };
|
|
5550
|
-
}
|
|
5551
|
-
);
|
|
5552
|
-
})
|
|
6387
|
+
}
|
|
6388
|
+
throw new Error(`Tool loop exceeded max steps (${maxSteps}) without final response.`);
|
|
6389
|
+
}
|
|
6390
|
+
if (providerInfo.provider === "fireworks") {
|
|
6391
|
+
if (request.modelTools && request.modelTools.length > 0) {
|
|
6392
|
+
throw new Error(
|
|
6393
|
+
"Fireworks provider does not support provider-native modelTools in runToolLoop."
|
|
5553
6394
|
);
|
|
5554
|
-
|
|
5555
|
-
|
|
5556
|
-
|
|
5557
|
-
|
|
5558
|
-
|
|
5559
|
-
|
|
5560
|
-
|
|
5561
|
-
|
|
6395
|
+
}
|
|
6396
|
+
const fireworksTools = buildFireworksToolsFromToolSet(request.tools);
|
|
6397
|
+
const messages = toFireworksMessages(contents);
|
|
6398
|
+
for (let stepIndex = 0; stepIndex < maxSteps; stepIndex += 1) {
|
|
6399
|
+
const turn = stepIndex + 1;
|
|
6400
|
+
const stepStartedAtMs = Date.now();
|
|
6401
|
+
let schedulerMetrics;
|
|
6402
|
+
let modelVersion = request.model;
|
|
6403
|
+
let usageTokens;
|
|
6404
|
+
let responseText = "";
|
|
6405
|
+
let blocked = false;
|
|
6406
|
+
const stepRequestPayload = {
|
|
6407
|
+
model: providerInfo.model,
|
|
6408
|
+
messages,
|
|
6409
|
+
tools: fireworksTools,
|
|
6410
|
+
tool_choice: "auto",
|
|
6411
|
+
parallel_tool_calls: true
|
|
6412
|
+
};
|
|
6413
|
+
const stepCallLogger = startLlmCallLoggerFromPayload({
|
|
6414
|
+
provider: "fireworks",
|
|
6415
|
+
modelId: request.model,
|
|
6416
|
+
requestPayload: stepRequestPayload,
|
|
6417
|
+
step: turn
|
|
6418
|
+
});
|
|
6419
|
+
try {
|
|
6420
|
+
const response = await runFireworksCall(
|
|
6421
|
+
async (client) => {
|
|
6422
|
+
return await client.chat.completions.create(
|
|
6423
|
+
{
|
|
6424
|
+
model: providerInfo.model,
|
|
6425
|
+
messages,
|
|
6426
|
+
tools: fireworksTools,
|
|
6427
|
+
tool_choice: "auto",
|
|
6428
|
+
parallel_tool_calls: true
|
|
6429
|
+
},
|
|
6430
|
+
{ signal: request.signal }
|
|
6431
|
+
);
|
|
6432
|
+
},
|
|
6433
|
+
providerInfo.model,
|
|
6434
|
+
{
|
|
6435
|
+
onSettled: (metrics) => {
|
|
6436
|
+
schedulerMetrics = metrics;
|
|
6437
|
+
}
|
|
6438
|
+
}
|
|
6439
|
+
);
|
|
6440
|
+
const modelCompletedAtMs = Date.now();
|
|
6441
|
+
modelVersion = typeof response.model === "string" ? response.model : request.model;
|
|
6442
|
+
request.onEvent?.({ type: "model", modelVersion });
|
|
6443
|
+
const choice = Array.isArray(response.choices) ? response.choices[0] : void 0;
|
|
6444
|
+
if (choice?.finish_reason === "content_filter") {
|
|
6445
|
+
blocked = true;
|
|
6446
|
+
request.onEvent?.({ type: "blocked" });
|
|
5562
6447
|
}
|
|
5563
|
-
|
|
5564
|
-
|
|
5565
|
-
|
|
5566
|
-
|
|
5567
|
-
|
|
5568
|
-
|
|
5569
|
-
|
|
5570
|
-
|
|
5571
|
-
|
|
5572
|
-
|
|
5573
|
-
|
|
5574
|
-
error: result.error,
|
|
5575
|
-
durationMs: result.durationMs
|
|
6448
|
+
const message = choice?.message;
|
|
6449
|
+
responseText = extractFireworksMessageText(message).trim();
|
|
6450
|
+
if (responseText.length > 0) {
|
|
6451
|
+
stepCallLogger?.appendResponseDelta(responseText);
|
|
6452
|
+
request.onEvent?.({ type: "delta", channel: "response", text: responseText });
|
|
6453
|
+
}
|
|
6454
|
+
usageTokens = extractFireworksUsageTokens(response.usage);
|
|
6455
|
+
const stepCostUsd = estimateCallCostUsd({
|
|
6456
|
+
modelId: modelVersion,
|
|
6457
|
+
tokens: usageTokens,
|
|
6458
|
+
responseImages: 0
|
|
5576
6459
|
});
|
|
5577
|
-
|
|
5578
|
-
|
|
5579
|
-
|
|
5580
|
-
|
|
5581
|
-
|
|
5582
|
-
|
|
5583
|
-
|
|
5584
|
-
status: "completed"
|
|
6460
|
+
totalCostUsd += stepCostUsd;
|
|
6461
|
+
if (usageTokens) {
|
|
6462
|
+
request.onEvent?.({
|
|
6463
|
+
type: "usage",
|
|
6464
|
+
usage: usageTokens,
|
|
6465
|
+
costUsd: stepCostUsd,
|
|
6466
|
+
modelVersion
|
|
5585
6467
|
});
|
|
5586
|
-
|
|
5587
|
-
|
|
5588
|
-
|
|
5589
|
-
|
|
6468
|
+
}
|
|
6469
|
+
const responseToolCalls = extractFireworksToolCalls(message);
|
|
6470
|
+
if (responseToolCalls.length === 0) {
|
|
6471
|
+
const steeringInput2 = steeringInternal?.drainPendingContents() ?? [];
|
|
6472
|
+
const steeringMessages = steeringInput2.length > 0 ? toFireworksMessages(steeringInput2) : [];
|
|
6473
|
+
finalText = responseText;
|
|
6474
|
+
finalThoughts = "";
|
|
6475
|
+
const stepCompletedAtMs2 = Date.now();
|
|
6476
|
+
const timing2 = buildStepTiming({
|
|
6477
|
+
stepStartedAtMs,
|
|
6478
|
+
stepCompletedAtMs: stepCompletedAtMs2,
|
|
6479
|
+
modelCompletedAtMs,
|
|
6480
|
+
schedulerMetrics,
|
|
6481
|
+
toolExecutionMs: 0,
|
|
6482
|
+
waitToolMs: 0
|
|
5590
6483
|
});
|
|
5591
|
-
|
|
5592
|
-
|
|
5593
|
-
|
|
5594
|
-
|
|
5595
|
-
|
|
5596
|
-
|
|
5597
|
-
|
|
5598
|
-
|
|
6484
|
+
steps.push({
|
|
6485
|
+
step: steps.length + 1,
|
|
6486
|
+
modelVersion,
|
|
6487
|
+
text: responseText || void 0,
|
|
6488
|
+
thoughts: void 0,
|
|
6489
|
+
toolCalls: [],
|
|
6490
|
+
usage: usageTokens,
|
|
6491
|
+
costUsd: stepCostUsd,
|
|
6492
|
+
timing: timing2
|
|
5599
6493
|
});
|
|
5600
|
-
|
|
5601
|
-
|
|
5602
|
-
|
|
5603
|
-
|
|
6494
|
+
stepCallLogger?.complete({
|
|
6495
|
+
provider: "fireworks",
|
|
6496
|
+
model: request.model,
|
|
6497
|
+
modelVersion,
|
|
6498
|
+
step: turn,
|
|
6499
|
+
usage: usageTokens,
|
|
6500
|
+
costUsd: stepCostUsd,
|
|
6501
|
+
blocked,
|
|
6502
|
+
responseChars: responseText.length,
|
|
6503
|
+
thoughtChars: 0,
|
|
6504
|
+
toolCalls: 0,
|
|
6505
|
+
finalStep: steeringMessages.length === 0
|
|
5604
6506
|
});
|
|
6507
|
+
if (steeringMessages.length === 0) {
|
|
6508
|
+
return { text: finalText, thoughts: finalThoughts, steps, totalCostUsd };
|
|
6509
|
+
}
|
|
6510
|
+
if (responseText.length > 0) {
|
|
6511
|
+
messages.push({ role: "assistant", content: responseText });
|
|
6512
|
+
}
|
|
6513
|
+
messages.push(...steeringMessages);
|
|
6514
|
+
continue;
|
|
5605
6515
|
}
|
|
5606
|
-
|
|
5607
|
-
|
|
5608
|
-
|
|
5609
|
-
|
|
5610
|
-
|
|
5611
|
-
|
|
5612
|
-
|
|
5613
|
-
|
|
5614
|
-
|
|
5615
|
-
|
|
5616
|
-
|
|
5617
|
-
|
|
5618
|
-
|
|
5619
|
-
|
|
5620
|
-
|
|
5621
|
-
|
|
5622
|
-
|
|
5623
|
-
|
|
5624
|
-
|
|
5625
|
-
|
|
5626
|
-
|
|
5627
|
-
|
|
5628
|
-
|
|
6516
|
+
const stepToolCalls = [];
|
|
6517
|
+
const callInputs = responseToolCalls.map((call, index) => {
|
|
6518
|
+
const toolIndex = index + 1;
|
|
6519
|
+
const toolId = buildToolLogId(turn, toolIndex);
|
|
6520
|
+
const { value, error: parseError } = parseOpenAiToolArguments(call.arguments);
|
|
6521
|
+
return { call, toolName: call.name, value, parseError, toolId, turn, toolIndex };
|
|
6522
|
+
});
|
|
6523
|
+
for (const entry of callInputs) {
|
|
6524
|
+
request.onEvent?.({
|
|
6525
|
+
type: "tool_call",
|
|
6526
|
+
phase: "started",
|
|
6527
|
+
turn: entry.turn,
|
|
6528
|
+
toolIndex: entry.toolIndex,
|
|
6529
|
+
toolName: entry.toolName,
|
|
6530
|
+
toolId: entry.toolId,
|
|
6531
|
+
callKind: "function",
|
|
6532
|
+
callId: entry.call.id,
|
|
6533
|
+
input: entry.value
|
|
6534
|
+
});
|
|
6535
|
+
}
|
|
6536
|
+
const callResults = await Promise.all(
|
|
6537
|
+
callInputs.map(async (entry) => {
|
|
6538
|
+
return await toolCallContextStorage.run(
|
|
6539
|
+
{
|
|
6540
|
+
toolName: entry.toolName,
|
|
6541
|
+
toolId: entry.toolId,
|
|
6542
|
+
turn: entry.turn,
|
|
6543
|
+
toolIndex: entry.toolIndex
|
|
6544
|
+
},
|
|
6545
|
+
async () => {
|
|
6546
|
+
const { result, outputPayload } = await executeToolCall({
|
|
6547
|
+
callKind: "function",
|
|
6548
|
+
toolName: entry.toolName,
|
|
6549
|
+
tool: request.tools[entry.toolName],
|
|
6550
|
+
rawInput: entry.value,
|
|
6551
|
+
parseError: entry.parseError
|
|
6552
|
+
});
|
|
6553
|
+
return { entry, result, outputPayload };
|
|
6554
|
+
}
|
|
6555
|
+
);
|
|
6556
|
+
})
|
|
6557
|
+
);
|
|
6558
|
+
const assistantToolCalls = [];
|
|
6559
|
+
const toolMessages = [];
|
|
6560
|
+
let toolExecutionMs = 0;
|
|
6561
|
+
let waitToolMs = 0;
|
|
6562
|
+
for (const { entry, result, outputPayload } of callResults) {
|
|
6563
|
+
stepToolCalls.push({ ...result, callId: entry.call.id });
|
|
6564
|
+
const callDurationMs = toToolResultDuration(result);
|
|
6565
|
+
toolExecutionMs += callDurationMs;
|
|
6566
|
+
if (entry.toolName.toLowerCase() === SUBAGENT_WAIT_TOOL_NAME) {
|
|
6567
|
+
waitToolMs += callDurationMs;
|
|
6568
|
+
}
|
|
6569
|
+
request.onEvent?.({
|
|
6570
|
+
type: "tool_call",
|
|
6571
|
+
phase: "completed",
|
|
6572
|
+
turn: entry.turn,
|
|
6573
|
+
toolIndex: entry.toolIndex,
|
|
6574
|
+
toolName: entry.toolName,
|
|
6575
|
+
toolId: entry.toolId,
|
|
6576
|
+
callKind: "function",
|
|
6577
|
+
callId: entry.call.id,
|
|
6578
|
+
input: entry.value,
|
|
6579
|
+
output: result.output,
|
|
6580
|
+
error: result.error,
|
|
6581
|
+
durationMs: result.durationMs
|
|
6582
|
+
});
|
|
6583
|
+
assistantToolCalls.push({
|
|
6584
|
+
id: entry.call.id,
|
|
6585
|
+
type: "function",
|
|
6586
|
+
function: {
|
|
6587
|
+
name: entry.toolName,
|
|
6588
|
+
arguments: entry.call.arguments
|
|
6589
|
+
}
|
|
6590
|
+
});
|
|
6591
|
+
toolMessages.push({
|
|
6592
|
+
role: "tool",
|
|
6593
|
+
tool_call_id: entry.call.id,
|
|
6594
|
+
content: mergeToolOutput(outputPayload)
|
|
6595
|
+
});
|
|
6596
|
+
}
|
|
6597
|
+
const stepCompletedAtMs = Date.now();
|
|
6598
|
+
const timing = buildStepTiming({
|
|
6599
|
+
stepStartedAtMs,
|
|
6600
|
+
stepCompletedAtMs,
|
|
6601
|
+
modelCompletedAtMs,
|
|
6602
|
+
schedulerMetrics,
|
|
6603
|
+
toolExecutionMs,
|
|
6604
|
+
waitToolMs
|
|
6605
|
+
});
|
|
6606
|
+
steps.push({
|
|
6607
|
+
step: steps.length + 1,
|
|
6608
|
+
modelVersion,
|
|
6609
|
+
text: responseText || void 0,
|
|
6610
|
+
thoughts: void 0,
|
|
6611
|
+
toolCalls: stepToolCalls,
|
|
6612
|
+
usage: usageTokens,
|
|
6613
|
+
costUsd: stepCostUsd,
|
|
6614
|
+
timing
|
|
6615
|
+
});
|
|
6616
|
+
stepCallLogger?.complete({
|
|
6617
|
+
provider: "fireworks",
|
|
6618
|
+
model: request.model,
|
|
6619
|
+
modelVersion,
|
|
6620
|
+
step: turn,
|
|
6621
|
+
usage: usageTokens,
|
|
6622
|
+
costUsd: stepCostUsd,
|
|
6623
|
+
blocked,
|
|
6624
|
+
responseChars: responseText.length,
|
|
6625
|
+
thoughtChars: 0,
|
|
6626
|
+
toolCalls: stepToolCalls.length,
|
|
6627
|
+
finalStep: false
|
|
6628
|
+
});
|
|
6629
|
+
messages.push({
|
|
6630
|
+
role: "assistant",
|
|
6631
|
+
...responseText.length > 0 ? { content: responseText } : {},
|
|
6632
|
+
tool_calls: assistantToolCalls
|
|
6633
|
+
});
|
|
6634
|
+
messages.push(...toolMessages);
|
|
6635
|
+
const steeringInput = steeringInternal?.drainPendingContents() ?? [];
|
|
6636
|
+
if (steeringInput.length > 0) {
|
|
6637
|
+
messages.push(...toFireworksMessages(steeringInput));
|
|
6638
|
+
}
|
|
6639
|
+
} catch (error) {
|
|
6640
|
+
stepCallLogger?.fail(error, {
|
|
6641
|
+
provider: "fireworks",
|
|
6642
|
+
model: request.model,
|
|
6643
|
+
modelVersion,
|
|
6644
|
+
step: turn,
|
|
6645
|
+
usage: usageTokens,
|
|
6646
|
+
blocked
|
|
6647
|
+
});
|
|
6648
|
+
throw error;
|
|
6649
|
+
}
|
|
5629
6650
|
}
|
|
5630
6651
|
throw new Error(`Tool loop exceeded max steps (${maxSteps}) without final response.`);
|
|
5631
6652
|
}
|
|
5632
|
-
|
|
5633
|
-
|
|
5634
|
-
|
|
5635
|
-
|
|
5636
|
-
|
|
5637
|
-
|
|
5638
|
-
const
|
|
5639
|
-
|
|
5640
|
-
|
|
5641
|
-
|
|
5642
|
-
|
|
5643
|
-
|
|
5644
|
-
|
|
6653
|
+
const geminiFunctionTools = buildGeminiFunctionDeclarations(request.tools);
|
|
6654
|
+
const geminiNativeTools = toGeminiTools(request.modelTools);
|
|
6655
|
+
const geminiTools = geminiNativeTools ? geminiNativeTools.concat(geminiFunctionTools) : geminiFunctionTools;
|
|
6656
|
+
const geminiContents = contents.map(convertLlmContentToGeminiContent);
|
|
6657
|
+
for (let stepIndex = 0; stepIndex < maxSteps; stepIndex += 1) {
|
|
6658
|
+
const turn = stepIndex + 1;
|
|
6659
|
+
const stepStartedAtMs = Date.now();
|
|
6660
|
+
let firstModelEventAtMs;
|
|
6661
|
+
let schedulerMetrics;
|
|
6662
|
+
let modelVersion = request.model;
|
|
6663
|
+
let usageTokens;
|
|
6664
|
+
let responseText = "";
|
|
6665
|
+
let thoughtsText = "";
|
|
6666
|
+
const markFirstModelEvent = () => {
|
|
6667
|
+
if (firstModelEventAtMs === void 0) {
|
|
6668
|
+
firstModelEventAtMs = Date.now();
|
|
6669
|
+
}
|
|
6670
|
+
};
|
|
6671
|
+
const thinkingConfig = resolveGeminiThinkingConfig(request.model, request.thinkingLevel);
|
|
6672
|
+
const config = {
|
|
6673
|
+
maxOutputTokens: 32e3,
|
|
6674
|
+
tools: geminiTools,
|
|
6675
|
+
toolConfig: {
|
|
6676
|
+
functionCallingConfig: {
|
|
6677
|
+
mode: FunctionCallingConfigMode.VALIDATED
|
|
6678
|
+
}
|
|
6679
|
+
},
|
|
6680
|
+
...thinkingConfig ? { thinkingConfig } : {}
|
|
6681
|
+
};
|
|
6682
|
+
const onEvent = request.onEvent;
|
|
6683
|
+
const stepRequestPayload = {
|
|
6684
|
+
model: request.model,
|
|
6685
|
+
contents: geminiContents,
|
|
6686
|
+
config
|
|
6687
|
+
};
|
|
6688
|
+
const stepCallLogger = startLlmCallLoggerFromPayload({
|
|
6689
|
+
provider: "gemini",
|
|
6690
|
+
modelId: request.model,
|
|
6691
|
+
requestPayload: stepRequestPayload,
|
|
6692
|
+
step: turn
|
|
6693
|
+
});
|
|
6694
|
+
try {
|
|
6695
|
+
const response = await runGeminiCall(
|
|
5645
6696
|
async (client) => {
|
|
5646
|
-
|
|
5647
|
-
|
|
5648
|
-
|
|
5649
|
-
|
|
5650
|
-
|
|
5651
|
-
|
|
5652
|
-
|
|
5653
|
-
|
|
5654
|
-
|
|
5655
|
-
);
|
|
6697
|
+
const stream = await client.models.generateContentStream({
|
|
6698
|
+
model: request.model,
|
|
6699
|
+
contents: geminiContents,
|
|
6700
|
+
config
|
|
6701
|
+
});
|
|
6702
|
+
let responseText2 = "";
|
|
6703
|
+
let thoughtsText2 = "";
|
|
6704
|
+
const modelParts = [];
|
|
6705
|
+
const functionCalls = [];
|
|
6706
|
+
const seenFunctionCallIds = /* @__PURE__ */ new Set();
|
|
6707
|
+
const seenFunctionCallKeys = /* @__PURE__ */ new Set();
|
|
6708
|
+
let latestUsageMetadata;
|
|
6709
|
+
let resolvedModelVersion;
|
|
6710
|
+
for await (const chunk of stream) {
|
|
6711
|
+
markFirstModelEvent();
|
|
6712
|
+
if (chunk.modelVersion) {
|
|
6713
|
+
resolvedModelVersion = chunk.modelVersion;
|
|
6714
|
+
onEvent?.({ type: "model", modelVersion: chunk.modelVersion });
|
|
6715
|
+
}
|
|
6716
|
+
if (chunk.usageMetadata) {
|
|
6717
|
+
latestUsageMetadata = chunk.usageMetadata;
|
|
6718
|
+
}
|
|
6719
|
+
const candidates = chunk.candidates;
|
|
6720
|
+
if (!candidates || candidates.length === 0) {
|
|
6721
|
+
continue;
|
|
6722
|
+
}
|
|
6723
|
+
const primary = candidates[0];
|
|
6724
|
+
const parts = primary?.content?.parts;
|
|
6725
|
+
if (!parts || parts.length === 0) {
|
|
6726
|
+
continue;
|
|
6727
|
+
}
|
|
6728
|
+
for (const part of parts) {
|
|
6729
|
+
modelParts.push(part);
|
|
6730
|
+
const call = part.functionCall;
|
|
6731
|
+
if (call) {
|
|
6732
|
+
const id = typeof call.id === "string" ? call.id : "";
|
|
6733
|
+
const shouldAdd = (() => {
|
|
6734
|
+
if (id.length > 0) {
|
|
6735
|
+
if (seenFunctionCallIds.has(id)) {
|
|
6736
|
+
return false;
|
|
6737
|
+
}
|
|
6738
|
+
seenFunctionCallIds.add(id);
|
|
6739
|
+
return true;
|
|
6740
|
+
}
|
|
6741
|
+
const key = JSON.stringify({ name: call.name ?? "", args: call.args ?? null });
|
|
6742
|
+
if (seenFunctionCallKeys.has(key)) {
|
|
6743
|
+
return false;
|
|
6744
|
+
}
|
|
6745
|
+
seenFunctionCallKeys.add(key);
|
|
6746
|
+
return true;
|
|
6747
|
+
})();
|
|
6748
|
+
if (shouldAdd) {
|
|
6749
|
+
functionCalls.push(call);
|
|
6750
|
+
}
|
|
6751
|
+
}
|
|
6752
|
+
if (typeof part.text === "string" && part.text.length > 0) {
|
|
6753
|
+
if (part.thought) {
|
|
6754
|
+
thoughtsText2 += part.text;
|
|
6755
|
+
stepCallLogger?.appendThoughtDelta(part.text);
|
|
6756
|
+
onEvent?.({ type: "delta", channel: "thought", text: part.text });
|
|
6757
|
+
} else {
|
|
6758
|
+
responseText2 += part.text;
|
|
6759
|
+
stepCallLogger?.appendResponseDelta(part.text);
|
|
6760
|
+
onEvent?.({ type: "delta", channel: "response", text: part.text });
|
|
6761
|
+
}
|
|
6762
|
+
}
|
|
6763
|
+
}
|
|
6764
|
+
}
|
|
6765
|
+
return {
|
|
6766
|
+
responseText: responseText2,
|
|
6767
|
+
thoughtsText: thoughtsText2,
|
|
6768
|
+
functionCalls,
|
|
6769
|
+
modelParts,
|
|
6770
|
+
usageMetadata: latestUsageMetadata,
|
|
6771
|
+
modelVersion: resolvedModelVersion ?? request.model
|
|
6772
|
+
};
|
|
5656
6773
|
},
|
|
5657
|
-
|
|
6774
|
+
request.model,
|
|
5658
6775
|
{
|
|
5659
6776
|
onSettled: (metrics) => {
|
|
5660
6777
|
schedulerMetrics = metrics;
|
|
@@ -5662,43 +6779,26 @@ async function runToolLoop(request) {
|
|
|
5662
6779
|
}
|
|
5663
6780
|
);
|
|
5664
6781
|
const modelCompletedAtMs = Date.now();
|
|
5665
|
-
|
|
5666
|
-
|
|
5667
|
-
|
|
5668
|
-
|
|
5669
|
-
request.onEvent?.({ type: "blocked" });
|
|
5670
|
-
}
|
|
5671
|
-
const message = choice?.message;
|
|
5672
|
-
const responseText = extractFireworksMessageText(message).trim();
|
|
5673
|
-
if (responseText.length > 0) {
|
|
5674
|
-
request.onEvent?.({ type: "delta", channel: "response", text: responseText });
|
|
5675
|
-
}
|
|
5676
|
-
const usageTokens = extractFireworksUsageTokens(response.usage);
|
|
6782
|
+
usageTokens = extractGeminiUsageTokens(response.usageMetadata);
|
|
6783
|
+
modelVersion = response.modelVersion ?? request.model;
|
|
6784
|
+
responseText = response.responseText.trim();
|
|
6785
|
+
thoughtsText = response.thoughtsText.trim();
|
|
5677
6786
|
const stepCostUsd = estimateCallCostUsd({
|
|
5678
6787
|
modelId: modelVersion,
|
|
5679
6788
|
tokens: usageTokens,
|
|
5680
6789
|
responseImages: 0
|
|
5681
6790
|
});
|
|
5682
6791
|
totalCostUsd += stepCostUsd;
|
|
5683
|
-
if (
|
|
5684
|
-
request.onEvent?.({
|
|
5685
|
-
type: "usage",
|
|
5686
|
-
usage: usageTokens,
|
|
5687
|
-
costUsd: stepCostUsd,
|
|
5688
|
-
modelVersion
|
|
5689
|
-
});
|
|
5690
|
-
}
|
|
5691
|
-
const responseToolCalls = extractFireworksToolCalls(message);
|
|
5692
|
-
if (responseToolCalls.length === 0) {
|
|
6792
|
+
if (response.functionCalls.length === 0) {
|
|
5693
6793
|
const steeringInput2 = steeringInternal?.drainPendingContents() ?? [];
|
|
5694
|
-
const steeringMessages = steeringInput2.length > 0 ? toFireworksMessages(steeringInput2) : [];
|
|
5695
6794
|
finalText = responseText;
|
|
5696
|
-
finalThoughts =
|
|
6795
|
+
finalThoughts = thoughtsText;
|
|
5697
6796
|
const stepCompletedAtMs2 = Date.now();
|
|
5698
6797
|
const timing2 = buildStepTiming({
|
|
5699
6798
|
stepStartedAtMs,
|
|
5700
6799
|
stepCompletedAtMs: stepCompletedAtMs2,
|
|
5701
6800
|
modelCompletedAtMs,
|
|
6801
|
+
firstModelEventAtMs,
|
|
5702
6802
|
schedulerMetrics,
|
|
5703
6803
|
toolExecutionMs: 0,
|
|
5704
6804
|
waitToolMs: 0
|
|
@@ -5706,31 +6806,65 @@ async function runToolLoop(request) {
|
|
|
5706
6806
|
steps.push({
|
|
5707
6807
|
step: steps.length + 1,
|
|
5708
6808
|
modelVersion,
|
|
5709
|
-
text:
|
|
5710
|
-
thoughts: void 0,
|
|
6809
|
+
text: finalText || void 0,
|
|
6810
|
+
thoughts: finalThoughts || void 0,
|
|
5711
6811
|
toolCalls: [],
|
|
5712
6812
|
usage: usageTokens,
|
|
5713
6813
|
costUsd: stepCostUsd,
|
|
5714
6814
|
timing: timing2
|
|
5715
6815
|
});
|
|
5716
|
-
|
|
6816
|
+
stepCallLogger?.complete({
|
|
6817
|
+
provider: "gemini",
|
|
6818
|
+
model: request.model,
|
|
6819
|
+
modelVersion,
|
|
6820
|
+
step: turn,
|
|
6821
|
+
usage: usageTokens,
|
|
6822
|
+
costUsd: stepCostUsd,
|
|
6823
|
+
responseChars: responseText.length,
|
|
6824
|
+
thoughtChars: thoughtsText.length,
|
|
6825
|
+
toolCalls: 0,
|
|
6826
|
+
finalStep: steeringInput2.length === 0
|
|
6827
|
+
});
|
|
6828
|
+
if (steeringInput2.length === 0) {
|
|
5717
6829
|
return { text: finalText, thoughts: finalThoughts, steps, totalCostUsd };
|
|
5718
6830
|
}
|
|
5719
|
-
|
|
5720
|
-
|
|
6831
|
+
const modelPartsForHistory2 = response.modelParts.filter(
|
|
6832
|
+
(part) => !(typeof part.text === "string" && part.thought === true)
|
|
6833
|
+
);
|
|
6834
|
+
if (modelPartsForHistory2.length > 0) {
|
|
6835
|
+
geminiContents.push({ role: "model", parts: modelPartsForHistory2 });
|
|
6836
|
+
} else if (response.responseText.length > 0) {
|
|
6837
|
+
geminiContents.push({ role: "model", parts: [{ text: response.responseText }] });
|
|
5721
6838
|
}
|
|
5722
|
-
|
|
6839
|
+
geminiContents.push(...steeringInput2.map(convertLlmContentToGeminiContent));
|
|
5723
6840
|
continue;
|
|
5724
6841
|
}
|
|
5725
|
-
const
|
|
5726
|
-
const
|
|
6842
|
+
const toolCalls = [];
|
|
6843
|
+
const modelPartsForHistory = response.modelParts.filter(
|
|
6844
|
+
(part) => !(typeof part.text === "string" && part.thought === true)
|
|
6845
|
+
);
|
|
6846
|
+
if (modelPartsForHistory.length > 0) {
|
|
6847
|
+
geminiContents.push({ role: "model", parts: modelPartsForHistory });
|
|
6848
|
+
} else {
|
|
6849
|
+
const parts = [];
|
|
6850
|
+
if (response.responseText) {
|
|
6851
|
+
parts.push({ text: response.responseText });
|
|
6852
|
+
}
|
|
6853
|
+
for (const call of response.functionCalls) {
|
|
6854
|
+
parts.push({ functionCall: call });
|
|
6855
|
+
}
|
|
6856
|
+
geminiContents.push({ role: "model", parts });
|
|
6857
|
+
}
|
|
6858
|
+
const responseParts = [];
|
|
6859
|
+
const callInputs = response.functionCalls.map((call, index) => {
|
|
5727
6860
|
const toolIndex = index + 1;
|
|
5728
6861
|
const toolId = buildToolLogId(turn, toolIndex);
|
|
5729
|
-
const
|
|
5730
|
-
|
|
6862
|
+
const toolName = call.name ?? "unknown";
|
|
6863
|
+
const rawInput = call.args ?? {};
|
|
6864
|
+
return { call, toolName, rawInput, toolId, turn, toolIndex };
|
|
5731
6865
|
});
|
|
5732
6866
|
for (const entry of callInputs) {
|
|
5733
|
-
|
|
6867
|
+
onEvent?.({
|
|
5734
6868
|
type: "tool_call",
|
|
5735
6869
|
phase: "started",
|
|
5736
6870
|
turn: entry.turn,
|
|
@@ -5739,7 +6873,7 @@ async function runToolLoop(request) {
|
|
|
5739
6873
|
toolId: entry.toolId,
|
|
5740
6874
|
callKind: "function",
|
|
5741
6875
|
callId: entry.call.id,
|
|
5742
|
-
input: entry.
|
|
6876
|
+
input: entry.rawInput
|
|
5743
6877
|
});
|
|
5744
6878
|
}
|
|
5745
6879
|
const callResults = await Promise.all(
|
|
@@ -5756,26 +6890,23 @@ async function runToolLoop(request) {
|
|
|
5756
6890
|
callKind: "function",
|
|
5757
6891
|
toolName: entry.toolName,
|
|
5758
6892
|
tool: request.tools[entry.toolName],
|
|
5759
|
-
rawInput: entry.
|
|
5760
|
-
parseError: entry.parseError
|
|
6893
|
+
rawInput: entry.rawInput
|
|
5761
6894
|
});
|
|
5762
6895
|
return { entry, result, outputPayload };
|
|
5763
6896
|
}
|
|
5764
6897
|
);
|
|
5765
6898
|
})
|
|
5766
6899
|
);
|
|
5767
|
-
const assistantToolCalls = [];
|
|
5768
|
-
const toolMessages = [];
|
|
5769
6900
|
let toolExecutionMs = 0;
|
|
5770
6901
|
let waitToolMs = 0;
|
|
5771
6902
|
for (const { entry, result, outputPayload } of callResults) {
|
|
5772
|
-
|
|
6903
|
+
toolCalls.push({ ...result, callId: entry.call.id });
|
|
5773
6904
|
const callDurationMs = toToolResultDuration(result);
|
|
5774
6905
|
toolExecutionMs += callDurationMs;
|
|
5775
6906
|
if (entry.toolName.toLowerCase() === SUBAGENT_WAIT_TOOL_NAME) {
|
|
5776
6907
|
waitToolMs += callDurationMs;
|
|
5777
6908
|
}
|
|
5778
|
-
|
|
6909
|
+
onEvent?.({
|
|
5779
6910
|
type: "tool_call",
|
|
5780
6911
|
phase: "completed",
|
|
5781
6912
|
turn: entry.turn,
|
|
@@ -5784,30 +6915,26 @@ async function runToolLoop(request) {
|
|
|
5784
6915
|
toolId: entry.toolId,
|
|
5785
6916
|
callKind: "function",
|
|
5786
6917
|
callId: entry.call.id,
|
|
5787
|
-
input: entry.
|
|
6918
|
+
input: entry.rawInput,
|
|
5788
6919
|
output: result.output,
|
|
5789
6920
|
error: result.error,
|
|
5790
6921
|
durationMs: result.durationMs
|
|
5791
6922
|
});
|
|
5792
|
-
|
|
5793
|
-
|
|
5794
|
-
|
|
5795
|
-
function: {
|
|
6923
|
+
const responsePayload = isPlainRecord(outputPayload) ? outputPayload : { output: outputPayload };
|
|
6924
|
+
responseParts.push({
|
|
6925
|
+
functionResponse: {
|
|
5796
6926
|
name: entry.toolName,
|
|
5797
|
-
|
|
6927
|
+
response: responsePayload,
|
|
6928
|
+
...entry.call.id ? { id: entry.call.id } : {}
|
|
5798
6929
|
}
|
|
5799
6930
|
});
|
|
5800
|
-
toolMessages.push({
|
|
5801
|
-
role: "tool",
|
|
5802
|
-
tool_call_id: entry.call.id,
|
|
5803
|
-
content: mergeToolOutput(outputPayload)
|
|
5804
|
-
});
|
|
5805
6931
|
}
|
|
5806
6932
|
const stepCompletedAtMs = Date.now();
|
|
5807
6933
|
const timing = buildStepTiming({
|
|
5808
6934
|
stepStartedAtMs,
|
|
5809
6935
|
stepCompletedAtMs,
|
|
5810
6936
|
modelCompletedAtMs,
|
|
6937
|
+
firstModelEventAtMs,
|
|
5811
6938
|
schedulerMetrics,
|
|
5812
6939
|
toolExecutionMs,
|
|
5813
6940
|
waitToolMs
|
|
@@ -5816,296 +6943,40 @@ async function runToolLoop(request) {
|
|
|
5816
6943
|
step: steps.length + 1,
|
|
5817
6944
|
modelVersion,
|
|
5818
6945
|
text: responseText || void 0,
|
|
5819
|
-
thoughts: void 0,
|
|
5820
|
-
toolCalls
|
|
6946
|
+
thoughts: thoughtsText || void 0,
|
|
6947
|
+
toolCalls,
|
|
5821
6948
|
usage: usageTokens,
|
|
5822
6949
|
costUsd: stepCostUsd,
|
|
5823
6950
|
timing
|
|
5824
6951
|
});
|
|
5825
|
-
|
|
5826
|
-
|
|
5827
|
-
|
|
5828
|
-
|
|
6952
|
+
stepCallLogger?.complete({
|
|
6953
|
+
provider: "gemini",
|
|
6954
|
+
model: request.model,
|
|
6955
|
+
modelVersion,
|
|
6956
|
+
step: turn,
|
|
6957
|
+
usage: usageTokens,
|
|
6958
|
+
costUsd: stepCostUsd,
|
|
6959
|
+
responseChars: responseText.length,
|
|
6960
|
+
thoughtChars: thoughtsText.length,
|
|
6961
|
+
toolCalls: toolCalls.length,
|
|
6962
|
+
finalStep: false
|
|
5829
6963
|
});
|
|
5830
|
-
|
|
6964
|
+
geminiContents.push({ role: "user", parts: responseParts });
|
|
5831
6965
|
const steeringInput = steeringInternal?.drainPendingContents() ?? [];
|
|
5832
6966
|
if (steeringInput.length > 0) {
|
|
5833
|
-
|
|
5834
|
-
}
|
|
5835
|
-
}
|
|
5836
|
-
throw new Error(`Tool loop exceeded max steps (${maxSteps}) without final response.`);
|
|
5837
|
-
}
|
|
5838
|
-
const geminiFunctionTools = buildGeminiFunctionDeclarations(request.tools);
|
|
5839
|
-
const geminiNativeTools = toGeminiTools(request.modelTools);
|
|
5840
|
-
const geminiTools = geminiNativeTools ? geminiNativeTools.concat(geminiFunctionTools) : geminiFunctionTools;
|
|
5841
|
-
const geminiContents = contents.map(convertLlmContentToGeminiContent);
|
|
5842
|
-
for (let stepIndex = 0; stepIndex < maxSteps; stepIndex += 1) {
|
|
5843
|
-
const stepStartedAtMs = Date.now();
|
|
5844
|
-
let firstModelEventAtMs;
|
|
5845
|
-
let schedulerMetrics;
|
|
5846
|
-
const markFirstModelEvent = () => {
|
|
5847
|
-
if (firstModelEventAtMs === void 0) {
|
|
5848
|
-
firstModelEventAtMs = Date.now();
|
|
5849
|
-
}
|
|
5850
|
-
};
|
|
5851
|
-
const thinkingConfig = resolveGeminiThinkingConfig(request.model, request.thinkingLevel);
|
|
5852
|
-
const config = {
|
|
5853
|
-
maxOutputTokens: 32e3,
|
|
5854
|
-
tools: geminiTools,
|
|
5855
|
-
toolConfig: {
|
|
5856
|
-
functionCallingConfig: {
|
|
5857
|
-
mode: FunctionCallingConfigMode.VALIDATED
|
|
5858
|
-
}
|
|
5859
|
-
},
|
|
5860
|
-
...thinkingConfig ? { thinkingConfig } : {}
|
|
5861
|
-
};
|
|
5862
|
-
const onEvent = request.onEvent;
|
|
5863
|
-
const response = await runGeminiCall(
|
|
5864
|
-
async (client) => {
|
|
5865
|
-
const stream = await client.models.generateContentStream({
|
|
5866
|
-
model: request.model,
|
|
5867
|
-
contents: geminiContents,
|
|
5868
|
-
config
|
|
5869
|
-
});
|
|
5870
|
-
let responseText = "";
|
|
5871
|
-
let thoughtsText = "";
|
|
5872
|
-
const modelParts = [];
|
|
5873
|
-
const functionCalls = [];
|
|
5874
|
-
const seenFunctionCallIds = /* @__PURE__ */ new Set();
|
|
5875
|
-
const seenFunctionCallKeys = /* @__PURE__ */ new Set();
|
|
5876
|
-
let latestUsageMetadata;
|
|
5877
|
-
let resolvedModelVersion;
|
|
5878
|
-
for await (const chunk of stream) {
|
|
5879
|
-
markFirstModelEvent();
|
|
5880
|
-
if (chunk.modelVersion) {
|
|
5881
|
-
resolvedModelVersion = chunk.modelVersion;
|
|
5882
|
-
onEvent?.({ type: "model", modelVersion: chunk.modelVersion });
|
|
5883
|
-
}
|
|
5884
|
-
if (chunk.usageMetadata) {
|
|
5885
|
-
latestUsageMetadata = chunk.usageMetadata;
|
|
5886
|
-
}
|
|
5887
|
-
const candidates = chunk.candidates;
|
|
5888
|
-
if (!candidates || candidates.length === 0) {
|
|
5889
|
-
continue;
|
|
5890
|
-
}
|
|
5891
|
-
const primary = candidates[0];
|
|
5892
|
-
const parts = primary?.content?.parts;
|
|
5893
|
-
if (!parts || parts.length === 0) {
|
|
5894
|
-
continue;
|
|
5895
|
-
}
|
|
5896
|
-
for (const part of parts) {
|
|
5897
|
-
modelParts.push(part);
|
|
5898
|
-
const call = part.functionCall;
|
|
5899
|
-
if (call) {
|
|
5900
|
-
const id = typeof call.id === "string" ? call.id : "";
|
|
5901
|
-
const shouldAdd = (() => {
|
|
5902
|
-
if (id.length > 0) {
|
|
5903
|
-
if (seenFunctionCallIds.has(id)) {
|
|
5904
|
-
return false;
|
|
5905
|
-
}
|
|
5906
|
-
seenFunctionCallIds.add(id);
|
|
5907
|
-
return true;
|
|
5908
|
-
}
|
|
5909
|
-
const key = JSON.stringify({ name: call.name ?? "", args: call.args ?? null });
|
|
5910
|
-
if (seenFunctionCallKeys.has(key)) {
|
|
5911
|
-
return false;
|
|
5912
|
-
}
|
|
5913
|
-
seenFunctionCallKeys.add(key);
|
|
5914
|
-
return true;
|
|
5915
|
-
})();
|
|
5916
|
-
if (shouldAdd) {
|
|
5917
|
-
functionCalls.push(call);
|
|
5918
|
-
}
|
|
5919
|
-
}
|
|
5920
|
-
if (typeof part.text === "string" && part.text.length > 0) {
|
|
5921
|
-
if (part.thought) {
|
|
5922
|
-
thoughtsText += part.text;
|
|
5923
|
-
onEvent?.({ type: "delta", channel: "thought", text: part.text });
|
|
5924
|
-
} else {
|
|
5925
|
-
responseText += part.text;
|
|
5926
|
-
onEvent?.({ type: "delta", channel: "response", text: part.text });
|
|
5927
|
-
}
|
|
5928
|
-
}
|
|
5929
|
-
}
|
|
5930
|
-
}
|
|
5931
|
-
return {
|
|
5932
|
-
responseText,
|
|
5933
|
-
thoughtsText,
|
|
5934
|
-
functionCalls,
|
|
5935
|
-
modelParts,
|
|
5936
|
-
usageMetadata: latestUsageMetadata,
|
|
5937
|
-
modelVersion: resolvedModelVersion ?? request.model
|
|
5938
|
-
};
|
|
5939
|
-
},
|
|
5940
|
-
request.model,
|
|
5941
|
-
{
|
|
5942
|
-
onSettled: (metrics) => {
|
|
5943
|
-
schedulerMetrics = metrics;
|
|
5944
|
-
}
|
|
6967
|
+
geminiContents.push(...steeringInput.map(convertLlmContentToGeminiContent));
|
|
5945
6968
|
}
|
|
5946
|
-
)
|
|
5947
|
-
|
|
5948
|
-
|
|
5949
|
-
|
|
5950
|
-
const stepCostUsd = estimateCallCostUsd({
|
|
5951
|
-
modelId: modelVersion,
|
|
5952
|
-
tokens: usageTokens,
|
|
5953
|
-
responseImages: 0
|
|
5954
|
-
});
|
|
5955
|
-
totalCostUsd += stepCostUsd;
|
|
5956
|
-
if (response.functionCalls.length === 0) {
|
|
5957
|
-
const steeringInput2 = steeringInternal?.drainPendingContents() ?? [];
|
|
5958
|
-
finalText = response.responseText.trim();
|
|
5959
|
-
finalThoughts = response.thoughtsText.trim();
|
|
5960
|
-
const stepCompletedAtMs2 = Date.now();
|
|
5961
|
-
const timing2 = buildStepTiming({
|
|
5962
|
-
stepStartedAtMs,
|
|
5963
|
-
stepCompletedAtMs: stepCompletedAtMs2,
|
|
5964
|
-
modelCompletedAtMs,
|
|
5965
|
-
firstModelEventAtMs,
|
|
5966
|
-
schedulerMetrics,
|
|
5967
|
-
toolExecutionMs: 0,
|
|
5968
|
-
waitToolMs: 0
|
|
5969
|
-
});
|
|
5970
|
-
steps.push({
|
|
5971
|
-
step: steps.length + 1,
|
|
6969
|
+
} catch (error) {
|
|
6970
|
+
stepCallLogger?.fail(error, {
|
|
6971
|
+
provider: "gemini",
|
|
6972
|
+
model: request.model,
|
|
5972
6973
|
modelVersion,
|
|
5973
|
-
|
|
5974
|
-
thoughts: finalThoughts || void 0,
|
|
5975
|
-
toolCalls: [],
|
|
6974
|
+
step: turn,
|
|
5976
6975
|
usage: usageTokens,
|
|
5977
|
-
|
|
5978
|
-
|
|
5979
|
-
});
|
|
5980
|
-
if (steeringInput2.length === 0) {
|
|
5981
|
-
return { text: finalText, thoughts: finalThoughts, steps, totalCostUsd };
|
|
5982
|
-
}
|
|
5983
|
-
const modelPartsForHistory2 = response.modelParts.filter(
|
|
5984
|
-
(part) => !(typeof part.text === "string" && part.thought === true)
|
|
5985
|
-
);
|
|
5986
|
-
if (modelPartsForHistory2.length > 0) {
|
|
5987
|
-
geminiContents.push({ role: "model", parts: modelPartsForHistory2 });
|
|
5988
|
-
} else if (response.responseText.length > 0) {
|
|
5989
|
-
geminiContents.push({ role: "model", parts: [{ text: response.responseText }] });
|
|
5990
|
-
}
|
|
5991
|
-
geminiContents.push(...steeringInput2.map(convertLlmContentToGeminiContent));
|
|
5992
|
-
continue;
|
|
5993
|
-
}
|
|
5994
|
-
const toolCalls = [];
|
|
5995
|
-
const modelPartsForHistory = response.modelParts.filter(
|
|
5996
|
-
(part) => !(typeof part.text === "string" && part.thought === true)
|
|
5997
|
-
);
|
|
5998
|
-
if (modelPartsForHistory.length > 0) {
|
|
5999
|
-
geminiContents.push({ role: "model", parts: modelPartsForHistory });
|
|
6000
|
-
} else {
|
|
6001
|
-
const parts = [];
|
|
6002
|
-
if (response.responseText) {
|
|
6003
|
-
parts.push({ text: response.responseText });
|
|
6004
|
-
}
|
|
6005
|
-
for (const call of response.functionCalls) {
|
|
6006
|
-
parts.push({ functionCall: call });
|
|
6007
|
-
}
|
|
6008
|
-
geminiContents.push({ role: "model", parts });
|
|
6009
|
-
}
|
|
6010
|
-
const responseParts = [];
|
|
6011
|
-
const callInputs = response.functionCalls.map((call, index) => {
|
|
6012
|
-
const turn = stepIndex + 1;
|
|
6013
|
-
const toolIndex = index + 1;
|
|
6014
|
-
const toolId = buildToolLogId(turn, toolIndex);
|
|
6015
|
-
const toolName = call.name ?? "unknown";
|
|
6016
|
-
const rawInput = call.args ?? {};
|
|
6017
|
-
return { call, toolName, rawInput, toolId, turn, toolIndex };
|
|
6018
|
-
});
|
|
6019
|
-
for (const entry of callInputs) {
|
|
6020
|
-
onEvent?.({
|
|
6021
|
-
type: "tool_call",
|
|
6022
|
-
phase: "started",
|
|
6023
|
-
turn: entry.turn,
|
|
6024
|
-
toolIndex: entry.toolIndex,
|
|
6025
|
-
toolName: entry.toolName,
|
|
6026
|
-
toolId: entry.toolId,
|
|
6027
|
-
callKind: "function",
|
|
6028
|
-
callId: entry.call.id,
|
|
6029
|
-
input: entry.rawInput
|
|
6030
|
-
});
|
|
6031
|
-
}
|
|
6032
|
-
const callResults = await Promise.all(
|
|
6033
|
-
callInputs.map(async (entry) => {
|
|
6034
|
-
return await toolCallContextStorage.run(
|
|
6035
|
-
{
|
|
6036
|
-
toolName: entry.toolName,
|
|
6037
|
-
toolId: entry.toolId,
|
|
6038
|
-
turn: entry.turn,
|
|
6039
|
-
toolIndex: entry.toolIndex
|
|
6040
|
-
},
|
|
6041
|
-
async () => {
|
|
6042
|
-
const { result, outputPayload } = await executeToolCall({
|
|
6043
|
-
callKind: "function",
|
|
6044
|
-
toolName: entry.toolName,
|
|
6045
|
-
tool: request.tools[entry.toolName],
|
|
6046
|
-
rawInput: entry.rawInput
|
|
6047
|
-
});
|
|
6048
|
-
return { entry, result, outputPayload };
|
|
6049
|
-
}
|
|
6050
|
-
);
|
|
6051
|
-
})
|
|
6052
|
-
);
|
|
6053
|
-
let toolExecutionMs = 0;
|
|
6054
|
-
let waitToolMs = 0;
|
|
6055
|
-
for (const { entry, result, outputPayload } of callResults) {
|
|
6056
|
-
toolCalls.push({ ...result, callId: entry.call.id });
|
|
6057
|
-
const callDurationMs = toToolResultDuration(result);
|
|
6058
|
-
toolExecutionMs += callDurationMs;
|
|
6059
|
-
if (entry.toolName.toLowerCase() === SUBAGENT_WAIT_TOOL_NAME) {
|
|
6060
|
-
waitToolMs += callDurationMs;
|
|
6061
|
-
}
|
|
6062
|
-
onEvent?.({
|
|
6063
|
-
type: "tool_call",
|
|
6064
|
-
phase: "completed",
|
|
6065
|
-
turn: entry.turn,
|
|
6066
|
-
toolIndex: entry.toolIndex,
|
|
6067
|
-
toolName: entry.toolName,
|
|
6068
|
-
toolId: entry.toolId,
|
|
6069
|
-
callKind: "function",
|
|
6070
|
-
callId: entry.call.id,
|
|
6071
|
-
input: entry.rawInput,
|
|
6072
|
-
output: result.output,
|
|
6073
|
-
error: result.error,
|
|
6074
|
-
durationMs: result.durationMs
|
|
6075
|
-
});
|
|
6076
|
-
const responsePayload = isPlainRecord(outputPayload) ? outputPayload : { output: outputPayload };
|
|
6077
|
-
responseParts.push({
|
|
6078
|
-
functionResponse: {
|
|
6079
|
-
name: entry.toolName,
|
|
6080
|
-
response: responsePayload,
|
|
6081
|
-
...entry.call.id ? { id: entry.call.id } : {}
|
|
6082
|
-
}
|
|
6976
|
+
responseChars: responseText.length,
|
|
6977
|
+
thoughtChars: thoughtsText.length
|
|
6083
6978
|
});
|
|
6084
|
-
|
|
6085
|
-
const stepCompletedAtMs = Date.now();
|
|
6086
|
-
const timing = buildStepTiming({
|
|
6087
|
-
stepStartedAtMs,
|
|
6088
|
-
stepCompletedAtMs,
|
|
6089
|
-
modelCompletedAtMs,
|
|
6090
|
-
firstModelEventAtMs,
|
|
6091
|
-
schedulerMetrics,
|
|
6092
|
-
toolExecutionMs,
|
|
6093
|
-
waitToolMs
|
|
6094
|
-
});
|
|
6095
|
-
steps.push({
|
|
6096
|
-
step: steps.length + 1,
|
|
6097
|
-
modelVersion,
|
|
6098
|
-
text: response.responseText.trim() || void 0,
|
|
6099
|
-
thoughts: response.thoughtsText.trim() || void 0,
|
|
6100
|
-
toolCalls,
|
|
6101
|
-
usage: usageTokens,
|
|
6102
|
-
costUsd: stepCostUsd,
|
|
6103
|
-
timing
|
|
6104
|
-
});
|
|
6105
|
-
geminiContents.push({ role: "user", parts: responseParts });
|
|
6106
|
-
const steeringInput = steeringInternal?.drainPendingContents() ?? [];
|
|
6107
|
-
if (steeringInput.length > 0) {
|
|
6108
|
-
geminiContents.push(...steeringInput.map(convertLlmContentToGeminiContent));
|
|
6979
|
+
throw error;
|
|
6109
6980
|
}
|
|
6110
6981
|
}
|
|
6111
6982
|
throw new Error(`Tool loop exceeded max steps (${maxSteps}) without final response.`);
|
|
@@ -6419,6 +7290,7 @@ ${lines}`;
|
|
|
6419
7290
|
|
|
6420
7291
|
// src/agent.ts
|
|
6421
7292
|
import { randomBytes as randomBytes3 } from "crypto";
|
|
7293
|
+
import path7 from "path";
|
|
6422
7294
|
|
|
6423
7295
|
// src/agent/subagents.ts
|
|
6424
7296
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
@@ -6893,26 +7765,26 @@ function resolveInputItemsText(items) {
|
|
|
6893
7765
|
}
|
|
6894
7766
|
const itemType = typeof item.type === "string" ? item.type.trim() : "";
|
|
6895
7767
|
const name = typeof item.name === "string" ? item.name.trim() : "";
|
|
6896
|
-
const
|
|
7768
|
+
const path8 = typeof item.path === "string" ? item.path.trim() : "";
|
|
6897
7769
|
const imageUrl = typeof item.image_url === "string" ? item.image_url.trim() : "";
|
|
6898
7770
|
if (itemType === "image") {
|
|
6899
7771
|
lines.push("[image]");
|
|
6900
7772
|
continue;
|
|
6901
7773
|
}
|
|
6902
|
-
if (itemType === "local_image" &&
|
|
6903
|
-
lines.push(`[local_image:${
|
|
7774
|
+
if (itemType === "local_image" && path8) {
|
|
7775
|
+
lines.push(`[local_image:${path8}]`);
|
|
6904
7776
|
continue;
|
|
6905
7777
|
}
|
|
6906
|
-
if (itemType === "skill" && name &&
|
|
6907
|
-
lines.push(`[skill:$${name}](${
|
|
7778
|
+
if (itemType === "skill" && name && path8) {
|
|
7779
|
+
lines.push(`[skill:$${name}](${path8})`);
|
|
6908
7780
|
continue;
|
|
6909
7781
|
}
|
|
6910
|
-
if (itemType === "mention" && name &&
|
|
6911
|
-
lines.push(`[mention:$${name}](${
|
|
7782
|
+
if (itemType === "mention" && name && path8) {
|
|
7783
|
+
lines.push(`[mention:$${name}](${path8})`);
|
|
6912
7784
|
continue;
|
|
6913
7785
|
}
|
|
6914
|
-
if (
|
|
6915
|
-
lines.push(`[${itemType || "input"}:${
|
|
7786
|
+
if (path8 || imageUrl) {
|
|
7787
|
+
lines.push(`[${itemType || "input"}:${path8 || imageUrl}]`);
|
|
6916
7788
|
continue;
|
|
6917
7789
|
}
|
|
6918
7790
|
if (name) {
|
|
@@ -7037,7 +7909,7 @@ function startRun(agent, options) {
|
|
|
7037
7909
|
setLifecycle(agent, "idle", "input_queued", `Subagent ${agent.id} run interrupted.`);
|
|
7038
7910
|
return;
|
|
7039
7911
|
}
|
|
7040
|
-
const message =
|
|
7912
|
+
const message = toErrorMessage2(error);
|
|
7041
7913
|
agent.lastError = message;
|
|
7042
7914
|
setLifecycle(agent, "failed", "run_failed", `Subagent ${agent.id} failed: ${message}`);
|
|
7043
7915
|
emitBackgroundNotification(agent, options);
|
|
@@ -7217,7 +8089,7 @@ function trimToUndefined(value) {
|
|
|
7217
8089
|
const trimmed = value?.trim();
|
|
7218
8090
|
return trimmed && trimmed.length > 0 ? trimmed : void 0;
|
|
7219
8091
|
}
|
|
7220
|
-
function
|
|
8092
|
+
function toErrorMessage2(error) {
|
|
7221
8093
|
if (error instanceof Error) {
|
|
7222
8094
|
return error.message;
|
|
7223
8095
|
}
|
|
@@ -7230,27 +8102,27 @@ function sleep2(ms) {
|
|
|
7230
8102
|
}
|
|
7231
8103
|
|
|
7232
8104
|
// src/tools/filesystemTools.ts
|
|
7233
|
-
import
|
|
7234
|
-
import { Buffer as
|
|
8105
|
+
import path6 from "path";
|
|
8106
|
+
import { Buffer as Buffer5 } from "buffer";
|
|
7235
8107
|
import { z as z6 } from "zod";
|
|
7236
8108
|
|
|
7237
8109
|
// src/tools/applyPatch.ts
|
|
7238
|
-
import
|
|
8110
|
+
import path5 from "path";
|
|
7239
8111
|
import { z as z5 } from "zod";
|
|
7240
8112
|
|
|
7241
8113
|
// src/tools/filesystem.ts
|
|
7242
8114
|
import { promises as fs3 } from "fs";
|
|
7243
|
-
import
|
|
8115
|
+
import path4 from "path";
|
|
7244
8116
|
var InMemoryAgentFilesystem = class {
|
|
7245
8117
|
#files = /* @__PURE__ */ new Map();
|
|
7246
8118
|
#dirs = /* @__PURE__ */ new Map();
|
|
7247
8119
|
#clock = 0;
|
|
7248
8120
|
constructor(initialFiles = {}) {
|
|
7249
|
-
const root =
|
|
8121
|
+
const root = path4.resolve("/");
|
|
7250
8122
|
this.#dirs.set(root, { mtimeMs: this.#nextMtime() });
|
|
7251
8123
|
for (const [filePath, content] of Object.entries(initialFiles)) {
|
|
7252
|
-
const absolutePath =
|
|
7253
|
-
this.#ensureDirSync(
|
|
8124
|
+
const absolutePath = path4.resolve(filePath);
|
|
8125
|
+
this.#ensureDirSync(path4.dirname(absolutePath));
|
|
7254
8126
|
this.#files.set(absolutePath, {
|
|
7255
8127
|
content,
|
|
7256
8128
|
mtimeMs: this.#nextMtime()
|
|
@@ -7258,7 +8130,7 @@ var InMemoryAgentFilesystem = class {
|
|
|
7258
8130
|
}
|
|
7259
8131
|
}
|
|
7260
8132
|
async readTextFile(filePath) {
|
|
7261
|
-
const absolutePath =
|
|
8133
|
+
const absolutePath = path4.resolve(filePath);
|
|
7262
8134
|
const file = this.#files.get(absolutePath);
|
|
7263
8135
|
if (!file) {
|
|
7264
8136
|
throw createNoSuchFileError("open", absolutePath);
|
|
@@ -7270,24 +8142,24 @@ var InMemoryAgentFilesystem = class {
|
|
|
7270
8142
|
return Buffer.from(content, "utf8");
|
|
7271
8143
|
}
|
|
7272
8144
|
async writeTextFile(filePath, content) {
|
|
7273
|
-
const absolutePath =
|
|
7274
|
-
const parentPath =
|
|
8145
|
+
const absolutePath = path4.resolve(filePath);
|
|
8146
|
+
const parentPath = path4.dirname(absolutePath);
|
|
7275
8147
|
if (!this.#dirs.has(parentPath)) {
|
|
7276
8148
|
throw createNoSuchFileError("open", parentPath);
|
|
7277
8149
|
}
|
|
7278
8150
|
this.#files.set(absolutePath, { content, mtimeMs: this.#nextMtime() });
|
|
7279
8151
|
}
|
|
7280
8152
|
async deleteFile(filePath) {
|
|
7281
|
-
const absolutePath =
|
|
8153
|
+
const absolutePath = path4.resolve(filePath);
|
|
7282
8154
|
if (!this.#files.delete(absolutePath)) {
|
|
7283
8155
|
throw createNoSuchFileError("unlink", absolutePath);
|
|
7284
8156
|
}
|
|
7285
8157
|
}
|
|
7286
8158
|
async ensureDir(directoryPath) {
|
|
7287
|
-
this.#ensureDirSync(
|
|
8159
|
+
this.#ensureDirSync(path4.resolve(directoryPath));
|
|
7288
8160
|
}
|
|
7289
8161
|
async readDir(directoryPath) {
|
|
7290
|
-
const absolutePath =
|
|
8162
|
+
const absolutePath = path4.resolve(directoryPath);
|
|
7291
8163
|
const directory = this.#dirs.get(absolutePath);
|
|
7292
8164
|
if (!directory) {
|
|
7293
8165
|
throw createNoSuchFileError("scandir", absolutePath);
|
|
@@ -7298,10 +8170,10 @@ var InMemoryAgentFilesystem = class {
|
|
|
7298
8170
|
if (dirPath === absolutePath) {
|
|
7299
8171
|
continue;
|
|
7300
8172
|
}
|
|
7301
|
-
if (
|
|
8173
|
+
if (path4.dirname(dirPath) !== absolutePath) {
|
|
7302
8174
|
continue;
|
|
7303
8175
|
}
|
|
7304
|
-
const name =
|
|
8176
|
+
const name = path4.basename(dirPath);
|
|
7305
8177
|
if (seenNames.has(name)) {
|
|
7306
8178
|
continue;
|
|
7307
8179
|
}
|
|
@@ -7314,10 +8186,10 @@ var InMemoryAgentFilesystem = class {
|
|
|
7314
8186
|
});
|
|
7315
8187
|
}
|
|
7316
8188
|
for (const [filePath, fileRecord] of this.#files.entries()) {
|
|
7317
|
-
if (
|
|
8189
|
+
if (path4.dirname(filePath) !== absolutePath) {
|
|
7318
8190
|
continue;
|
|
7319
8191
|
}
|
|
7320
|
-
const name =
|
|
8192
|
+
const name = path4.basename(filePath);
|
|
7321
8193
|
if (seenNames.has(name)) {
|
|
7322
8194
|
continue;
|
|
7323
8195
|
}
|
|
@@ -7333,7 +8205,7 @@ var InMemoryAgentFilesystem = class {
|
|
|
7333
8205
|
return entries;
|
|
7334
8206
|
}
|
|
7335
8207
|
async stat(entryPath) {
|
|
7336
|
-
const absolutePath =
|
|
8208
|
+
const absolutePath = path4.resolve(entryPath);
|
|
7337
8209
|
const file = this.#files.get(absolutePath);
|
|
7338
8210
|
if (file) {
|
|
7339
8211
|
return { kind: "file", mtimeMs: file.mtimeMs };
|
|
@@ -7349,7 +8221,7 @@ var InMemoryAgentFilesystem = class {
|
|
|
7349
8221
|
return Object.fromEntries(entries.map(([filePath, record]) => [filePath, record.content]));
|
|
7350
8222
|
}
|
|
7351
8223
|
#ensureDirSync(directoryPath) {
|
|
7352
|
-
const absolutePath =
|
|
8224
|
+
const absolutePath = path4.resolve(directoryPath);
|
|
7353
8225
|
const parts = [];
|
|
7354
8226
|
let cursor = absolutePath;
|
|
7355
8227
|
for (; ; ) {
|
|
@@ -7357,7 +8229,7 @@ var InMemoryAgentFilesystem = class {
|
|
|
7357
8229
|
break;
|
|
7358
8230
|
}
|
|
7359
8231
|
parts.push(cursor);
|
|
7360
|
-
const parent =
|
|
8232
|
+
const parent = path4.dirname(cursor);
|
|
7361
8233
|
if (parent === cursor) {
|
|
7362
8234
|
break;
|
|
7363
8235
|
}
|
|
@@ -7391,7 +8263,7 @@ function createNodeAgentFilesystem() {
|
|
|
7391
8263
|
const entries = await fs3.readdir(directoryPath, { withFileTypes: true });
|
|
7392
8264
|
const result = [];
|
|
7393
8265
|
for (const entry of entries) {
|
|
7394
|
-
const entryPath =
|
|
8266
|
+
const entryPath = path4.resolve(directoryPath, entry.name);
|
|
7395
8267
|
const stats = await fs3.lstat(entryPath);
|
|
7396
8268
|
result.push({
|
|
7397
8269
|
name: entry.name,
|
|
@@ -7555,7 +8427,7 @@ function createApplyPatchTool(options = {}) {
|
|
|
7555
8427
|
});
|
|
7556
8428
|
}
|
|
7557
8429
|
async function applyPatch(request) {
|
|
7558
|
-
const cwd =
|
|
8430
|
+
const cwd = path5.resolve(request.cwd ?? process.cwd());
|
|
7559
8431
|
const adapter = request.fs ?? createNodeAgentFilesystem();
|
|
7560
8432
|
const allowOutsideCwd = request.allowOutsideCwd === true;
|
|
7561
8433
|
const patchBytes = Buffer.byteLength(request.patch, "utf8");
|
|
@@ -7577,7 +8449,7 @@ async function applyPatch(request) {
|
|
|
7577
8449
|
kind: "add",
|
|
7578
8450
|
path: absolutePath2
|
|
7579
8451
|
});
|
|
7580
|
-
await adapter.ensureDir(
|
|
8452
|
+
await adapter.ensureDir(path5.dirname(absolutePath2));
|
|
7581
8453
|
await adapter.writeTextFile(absolutePath2, operation.content);
|
|
7582
8454
|
added.push(toDisplayPath(absolutePath2, cwd));
|
|
7583
8455
|
continue;
|
|
@@ -7611,7 +8483,7 @@ async function applyPatch(request) {
|
|
|
7611
8483
|
fromPath: absolutePath,
|
|
7612
8484
|
toPath: destinationPath
|
|
7613
8485
|
});
|
|
7614
|
-
await adapter.ensureDir(
|
|
8486
|
+
await adapter.ensureDir(path5.dirname(destinationPath));
|
|
7615
8487
|
await adapter.writeTextFile(destinationPath, next);
|
|
7616
8488
|
await adapter.deleteFile(absolutePath);
|
|
7617
8489
|
modified.push(toDisplayPath(destinationPath, cwd));
|
|
@@ -7642,22 +8514,22 @@ function resolvePatchPath(rawPath, cwd, allowOutsideCwd) {
|
|
|
7642
8514
|
if (trimmed.length === 0) {
|
|
7643
8515
|
throw new Error("apply_patch failed: empty file path");
|
|
7644
8516
|
}
|
|
7645
|
-
const absolutePath =
|
|
8517
|
+
const absolutePath = path5.isAbsolute(trimmed) ? path5.resolve(trimmed) : path5.resolve(cwd, trimmed);
|
|
7646
8518
|
if (!allowOutsideCwd && !isPathInsideCwd(absolutePath, cwd)) {
|
|
7647
8519
|
throw new Error(`apply_patch failed: path "${trimmed}" resolves outside cwd "${cwd}"`);
|
|
7648
8520
|
}
|
|
7649
8521
|
return absolutePath;
|
|
7650
8522
|
}
|
|
7651
8523
|
function isPathInsideCwd(candidatePath, cwd) {
|
|
7652
|
-
const relative =
|
|
7653
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
8524
|
+
const relative = path5.relative(cwd, candidatePath);
|
|
8525
|
+
return relative === "" || !relative.startsWith("..") && !path5.isAbsolute(relative);
|
|
7654
8526
|
}
|
|
7655
8527
|
function toDisplayPath(absolutePath, cwd) {
|
|
7656
|
-
const relative =
|
|
8528
|
+
const relative = path5.relative(cwd, absolutePath);
|
|
7657
8529
|
if (relative === "") {
|
|
7658
8530
|
return ".";
|
|
7659
8531
|
}
|
|
7660
|
-
if (!relative.startsWith("..") && !
|
|
8532
|
+
if (!relative.startsWith("..") && !path5.isAbsolute(relative)) {
|
|
7661
8533
|
return relative;
|
|
7662
8534
|
}
|
|
7663
8535
|
return absolutePath;
|
|
@@ -8424,7 +9296,7 @@ async function readBinaryFile(filesystem, filePath) {
|
|
|
8424
9296
|
return await filesystem.readBinaryFile(filePath);
|
|
8425
9297
|
}
|
|
8426
9298
|
const text = await filesystem.readTextFile(filePath);
|
|
8427
|
-
return
|
|
9299
|
+
return Buffer5.from(text, "utf8");
|
|
8428
9300
|
}
|
|
8429
9301
|
function detectImageMimeType(buffer, filePath) {
|
|
8430
9302
|
if (buffer.length >= 8 && buffer[0] === 137 && buffer[1] === 80 && buffer[2] === 78 && buffer[3] === 71 && buffer[4] === 13 && buffer[5] === 10 && buffer[6] === 26 && buffer[7] === 10) {
|
|
@@ -8442,7 +9314,7 @@ function detectImageMimeType(buffer, filePath) {
|
|
|
8442
9314
|
if (buffer.length >= 12 && buffer.subarray(0, 4).toString("ascii") === "RIFF" && buffer.subarray(8, 12).toString("ascii") === "WEBP") {
|
|
8443
9315
|
return "image/webp";
|
|
8444
9316
|
}
|
|
8445
|
-
const fromExtension = IMAGE_MIME_BY_EXTENSION[
|
|
9317
|
+
const fromExtension = IMAGE_MIME_BY_EXTENSION[path6.extname(filePath).toLowerCase()];
|
|
8446
9318
|
if (fromExtension && SUPPORTED_IMAGE_MIME_TYPES.has(fromExtension)) {
|
|
8447
9319
|
return fromExtension;
|
|
8448
9320
|
}
|
|
@@ -8452,13 +9324,13 @@ function isPdfFile(buffer, filePath) {
|
|
|
8452
9324
|
if (buffer.length >= 5 && buffer.subarray(0, 5).toString("ascii") === "%PDF-") {
|
|
8453
9325
|
return true;
|
|
8454
9326
|
}
|
|
8455
|
-
return
|
|
9327
|
+
return path6.extname(filePath).toLowerCase() === ".pdf";
|
|
8456
9328
|
}
|
|
8457
9329
|
function isValidUtf8(buffer) {
|
|
8458
9330
|
if (buffer.length === 0) {
|
|
8459
9331
|
return true;
|
|
8460
9332
|
}
|
|
8461
|
-
return
|
|
9333
|
+
return Buffer5.from(buffer.toString("utf8"), "utf8").equals(buffer);
|
|
8462
9334
|
}
|
|
8463
9335
|
async function readFileGemini(input, options) {
|
|
8464
9336
|
const runtime = resolveRuntime(options);
|
|
@@ -8490,7 +9362,7 @@ async function writeFileGemini(input, options) {
|
|
|
8490
9362
|
action: "write",
|
|
8491
9363
|
path: filePath
|
|
8492
9364
|
});
|
|
8493
|
-
await runtime.filesystem.ensureDir(
|
|
9365
|
+
await runtime.filesystem.ensureDir(path6.dirname(filePath));
|
|
8494
9366
|
await runtime.filesystem.writeTextFile(filePath, input.content);
|
|
8495
9367
|
return `Successfully wrote file: ${toDisplayPath2(filePath, runtime.cwd)}`;
|
|
8496
9368
|
}
|
|
@@ -8511,7 +9383,7 @@ async function replaceFileContentGemini(input, options) {
|
|
|
8511
9383
|
originalContent = await runtime.filesystem.readTextFile(filePath);
|
|
8512
9384
|
} catch (error) {
|
|
8513
9385
|
if (isNoEntError(error) && oldValue.length === 0) {
|
|
8514
|
-
await runtime.filesystem.ensureDir(
|
|
9386
|
+
await runtime.filesystem.ensureDir(path6.dirname(filePath));
|
|
8515
9387
|
await runtime.filesystem.writeTextFile(filePath, newValue);
|
|
8516
9388
|
return `Successfully wrote new file: ${toDisplayPath2(filePath, runtime.cwd)}`;
|
|
8517
9389
|
}
|
|
@@ -8689,7 +9561,7 @@ async function globFilesGemini(input, options) {
|
|
|
8689
9561
|
});
|
|
8690
9562
|
const matched = [];
|
|
8691
9563
|
for (const filePath of files) {
|
|
8692
|
-
const relativePath = normalizeSlashes(
|
|
9564
|
+
const relativePath = normalizeSlashes(path6.relative(dirPath, filePath));
|
|
8693
9565
|
if (!matcher(relativePath)) {
|
|
8694
9566
|
continue;
|
|
8695
9567
|
}
|
|
@@ -8707,7 +9579,7 @@ async function globFilesGemini(input, options) {
|
|
|
8707
9579
|
}
|
|
8708
9580
|
function resolveRuntime(options) {
|
|
8709
9581
|
return {
|
|
8710
|
-
cwd:
|
|
9582
|
+
cwd: path6.resolve(options.cwd ?? process.cwd()),
|
|
8711
9583
|
filesystem: options.fs ?? createNodeAgentFilesystem(),
|
|
8712
9584
|
allowOutsideCwd: options.allowOutsideCwd === true,
|
|
8713
9585
|
checkAccess: options.checkAccess,
|
|
@@ -8738,13 +9610,13 @@ function mapApplyPatchAction(action) {
|
|
|
8738
9610
|
return "move";
|
|
8739
9611
|
}
|
|
8740
9612
|
function resolvePathWithPolicy(inputPath, cwd, allowOutsideCwd) {
|
|
8741
|
-
const absolutePath =
|
|
9613
|
+
const absolutePath = path6.isAbsolute(inputPath) ? path6.resolve(inputPath) : path6.resolve(cwd, inputPath);
|
|
8742
9614
|
if (allowOutsideCwd || isPathInsideCwd2(absolutePath, cwd)) {
|
|
8743
9615
|
return absolutePath;
|
|
8744
9616
|
}
|
|
8745
|
-
if (
|
|
9617
|
+
if (path6.isAbsolute(inputPath)) {
|
|
8746
9618
|
const sandboxRelativePath = inputPath.replace(/^[/\\]+/, "");
|
|
8747
|
-
const sandboxRootedPath =
|
|
9619
|
+
const sandboxRootedPath = path6.resolve(cwd, sandboxRelativePath);
|
|
8748
9620
|
if (isPathInsideCwd2(sandboxRootedPath, cwd)) {
|
|
8749
9621
|
return sandboxRootedPath;
|
|
8750
9622
|
}
|
|
@@ -8752,25 +9624,25 @@ function resolvePathWithPolicy(inputPath, cwd, allowOutsideCwd) {
|
|
|
8752
9624
|
throw new Error(`path "${inputPath}" resolves outside cwd "${cwd}"`);
|
|
8753
9625
|
}
|
|
8754
9626
|
function isPathInsideCwd2(candidatePath, cwd) {
|
|
8755
|
-
const relative =
|
|
8756
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
9627
|
+
const relative = path6.relative(cwd, candidatePath);
|
|
9628
|
+
return relative === "" || !relative.startsWith("..") && !path6.isAbsolute(relative);
|
|
8757
9629
|
}
|
|
8758
9630
|
function toDisplayPath2(absolutePath, cwd) {
|
|
8759
|
-
const relative =
|
|
9631
|
+
const relative = path6.relative(cwd, absolutePath);
|
|
8760
9632
|
if (relative === "") {
|
|
8761
9633
|
return ".";
|
|
8762
9634
|
}
|
|
8763
|
-
if (!relative.startsWith("..") && !
|
|
9635
|
+
if (!relative.startsWith("..") && !path6.isAbsolute(relative)) {
|
|
8764
9636
|
return relative;
|
|
8765
9637
|
}
|
|
8766
9638
|
return absolutePath;
|
|
8767
9639
|
}
|
|
8768
9640
|
function toSandboxDisplayPath(absolutePath, cwd) {
|
|
8769
|
-
const relative =
|
|
9641
|
+
const relative = path6.relative(cwd, absolutePath);
|
|
8770
9642
|
if (relative === "") {
|
|
8771
9643
|
return "/";
|
|
8772
9644
|
}
|
|
8773
|
-
if (!relative.startsWith("..") && !
|
|
9645
|
+
if (!relative.startsWith("..") && !path6.isAbsolute(relative)) {
|
|
8774
9646
|
return `/${normalizeSlashes(relative)}`;
|
|
8775
9647
|
}
|
|
8776
9648
|
return normalizeSlashes(absolutePath);
|
|
@@ -8886,7 +9758,7 @@ function createGlobMatcher(pattern, caseSensitive = false) {
|
|
|
8886
9758
|
}));
|
|
8887
9759
|
return (candidatePath) => {
|
|
8888
9760
|
const normalizedPath = normalizeSlashes(candidatePath);
|
|
8889
|
-
const basename =
|
|
9761
|
+
const basename = path6.posix.basename(normalizedPath);
|
|
8890
9762
|
return compiled.some(
|
|
8891
9763
|
(entry) => entry.regex.test(entry.applyToBasename ? basename : normalizedPath)
|
|
8892
9764
|
);
|
|
@@ -9002,10 +9874,18 @@ function isNoEntError(error) {
|
|
|
9002
9874
|
// src/agent.ts
|
|
9003
9875
|
async function runAgentLoop(request) {
|
|
9004
9876
|
const telemetry = createAgentTelemetrySession(request.telemetry);
|
|
9877
|
+
const logging = createRootAgentLoggingSession(request);
|
|
9005
9878
|
try {
|
|
9006
|
-
return await
|
|
9879
|
+
return await runWithAgentLoggingSession(logging, async () => {
|
|
9880
|
+
return await runAgentLoopInternal(request, {
|
|
9881
|
+
depth: 0,
|
|
9882
|
+
telemetry,
|
|
9883
|
+
logging
|
|
9884
|
+
});
|
|
9885
|
+
});
|
|
9007
9886
|
} finally {
|
|
9008
9887
|
await telemetry?.flush();
|
|
9888
|
+
await logging?.flush();
|
|
9009
9889
|
}
|
|
9010
9890
|
}
|
|
9011
9891
|
function mergeAbortSignals2(first, second) {
|
|
@@ -9076,9 +9956,11 @@ async function runAgentLoopInternal(request, context) {
|
|
|
9076
9956
|
subagent_tool,
|
|
9077
9957
|
subagents,
|
|
9078
9958
|
telemetry,
|
|
9959
|
+
logging: _logging,
|
|
9079
9960
|
...toolLoopRequest
|
|
9080
9961
|
} = request;
|
|
9081
9962
|
const telemetrySession = context.telemetry ?? createAgentTelemetrySession(telemetry);
|
|
9963
|
+
const loggingSession = context.logging;
|
|
9082
9964
|
const runId = randomRunId();
|
|
9083
9965
|
const startedAtMs = Date.now();
|
|
9084
9966
|
const steeringChannel = toolLoopRequest.steering ?? createToolLoopSteeringChannel();
|
|
@@ -9092,6 +9974,7 @@ async function runAgentLoopInternal(request, context) {
|
|
|
9092
9974
|
model: request.model,
|
|
9093
9975
|
depth: context.depth,
|
|
9094
9976
|
telemetry: telemetrySession,
|
|
9977
|
+
logging: loggingSession,
|
|
9095
9978
|
customTools: customTools ?? {},
|
|
9096
9979
|
filesystemSelection,
|
|
9097
9980
|
subagentSelection,
|
|
@@ -9128,6 +10011,16 @@ async function runAgentLoopInternal(request, context) {
|
|
|
9128
10011
|
filesystemToolsEnabled: Object.keys(filesystemTools).length > 0,
|
|
9129
10012
|
subagentToolsEnabled: resolvedSubagentConfig.enabled
|
|
9130
10013
|
});
|
|
10014
|
+
loggingSession?.logLine(
|
|
10015
|
+
[
|
|
10016
|
+
`[agent:${runId}] run_started`,
|
|
10017
|
+
`depth=${context.depth.toString()}`,
|
|
10018
|
+
`model=${request.model}`,
|
|
10019
|
+
`tools=${Object.keys(mergedTools).length.toString()}`,
|
|
10020
|
+
`filesystemTools=${Object.keys(filesystemTools).length > 0 ? "true" : "false"}`,
|
|
10021
|
+
`subagentTools=${resolvedSubagentConfig.enabled ? "true" : "false"}`
|
|
10022
|
+
].join(" ")
|
|
10023
|
+
);
|
|
9131
10024
|
const sourceOnEvent = toolLoopRequestWithSteering.onEvent;
|
|
9132
10025
|
const includeLlmStreamEvents = telemetrySession?.includeLlmStreamEvents === true;
|
|
9133
10026
|
const wrappedOnEvent = sourceOnEvent || includeLlmStreamEvents ? (event) => {
|
|
@@ -9135,6 +10028,14 @@ async function runAgentLoopInternal(request, context) {
|
|
|
9135
10028
|
if (includeLlmStreamEvents) {
|
|
9136
10029
|
emitTelemetry({ type: "agent.run.stream", event });
|
|
9137
10030
|
}
|
|
10031
|
+
if (loggingSession) {
|
|
10032
|
+
appendAgentStreamEventLog({
|
|
10033
|
+
event,
|
|
10034
|
+
append: (line) => {
|
|
10035
|
+
loggingSession.logLine(`[agent:${runId}] ${line}`);
|
|
10036
|
+
}
|
|
10037
|
+
});
|
|
10038
|
+
}
|
|
9138
10039
|
} : void 0;
|
|
9139
10040
|
try {
|
|
9140
10041
|
const result = await runToolLoop({
|
|
@@ -9152,14 +10053,43 @@ async function runAgentLoopInternal(request, context) {
|
|
|
9152
10053
|
totalCostUsd: result.totalCostUsd,
|
|
9153
10054
|
usage: summarizeResultUsage(result)
|
|
9154
10055
|
});
|
|
10056
|
+
loggingSession?.logLine(
|
|
10057
|
+
[
|
|
10058
|
+
`[agent:${runId}] run_completed`,
|
|
10059
|
+
`status=ok`,
|
|
10060
|
+
`durationMs=${Math.max(0, Date.now() - startedAtMs).toString()}`,
|
|
10061
|
+
`steps=${result.steps.length.toString()}`,
|
|
10062
|
+
`toolCalls=${countToolCalls(result).toString()}`,
|
|
10063
|
+
`totalCostUsd=${(result.totalCostUsd ?? 0).toFixed(6)}`
|
|
10064
|
+
].join(" ")
|
|
10065
|
+
);
|
|
10066
|
+
for (const step of result.steps) {
|
|
10067
|
+
loggingSession?.logLine(
|
|
10068
|
+
[
|
|
10069
|
+
`[agent:${runId}] step_completed`,
|
|
10070
|
+
`step=${step.step.toString()}`,
|
|
10071
|
+
`modelVersion=${step.modelVersion}`,
|
|
10072
|
+
`toolCalls=${step.toolCalls.length.toString()}`,
|
|
10073
|
+
`costUsd=${(step.costUsd ?? 0).toFixed(6)}`
|
|
10074
|
+
].join(" ")
|
|
10075
|
+
);
|
|
10076
|
+
}
|
|
9155
10077
|
return result;
|
|
9156
10078
|
} catch (error) {
|
|
9157
10079
|
emitTelemetry({
|
|
9158
10080
|
type: "agent.run.completed",
|
|
9159
10081
|
success: false,
|
|
9160
10082
|
durationMs: Math.max(0, Date.now() - startedAtMs),
|
|
9161
|
-
error:
|
|
10083
|
+
error: toErrorMessage3(error)
|
|
9162
10084
|
});
|
|
10085
|
+
loggingSession?.logLine(
|
|
10086
|
+
[
|
|
10087
|
+
`[agent:${runId}] run_completed`,
|
|
10088
|
+
`status=error`,
|
|
10089
|
+
`durationMs=${Math.max(0, Date.now() - startedAtMs).toString()}`,
|
|
10090
|
+
`error=${toErrorMessage3(error)}`
|
|
10091
|
+
].join(" ")
|
|
10092
|
+
);
|
|
9163
10093
|
throw error;
|
|
9164
10094
|
} finally {
|
|
9165
10095
|
await subagentController?.closeAll();
|
|
@@ -9230,7 +10160,8 @@ function createSubagentController(params) {
|
|
|
9230
10160
|
{
|
|
9231
10161
|
depth: params.depth + 1,
|
|
9232
10162
|
parentRunId: params.runId,
|
|
9233
|
-
telemetry: params.telemetry
|
|
10163
|
+
telemetry: params.telemetry,
|
|
10164
|
+
logging: params.logging
|
|
9234
10165
|
}
|
|
9235
10166
|
);
|
|
9236
10167
|
}
|
|
@@ -9294,10 +10225,10 @@ function trimToUndefined2(value) {
|
|
|
9294
10225
|
function randomRunId() {
|
|
9295
10226
|
return randomBytes3(8).toString("hex");
|
|
9296
10227
|
}
|
|
9297
|
-
function
|
|
10228
|
+
function toIsoNow2() {
|
|
9298
10229
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
9299
10230
|
}
|
|
9300
|
-
function
|
|
10231
|
+
function toErrorMessage3(error) {
|
|
9301
10232
|
if (error instanceof Error && error.message) {
|
|
9302
10233
|
return error.message;
|
|
9303
10234
|
}
|
|
@@ -9342,9 +10273,41 @@ function summarizeResultUsage(result) {
|
|
|
9342
10273
|
}
|
|
9343
10274
|
return summary;
|
|
9344
10275
|
}
|
|
9345
|
-
function
|
|
10276
|
+
function isPromiseLike2(value) {
|
|
9346
10277
|
return (typeof value === "object" || typeof value === "function") && value !== null && typeof value.then === "function";
|
|
9347
10278
|
}
|
|
10279
|
+
function resolveAgentLoggingSelection(value) {
|
|
10280
|
+
if (value === false) {
|
|
10281
|
+
return void 0;
|
|
10282
|
+
}
|
|
10283
|
+
if (value === void 0 || value === true) {
|
|
10284
|
+
return {
|
|
10285
|
+
mirrorToConsole: true
|
|
10286
|
+
};
|
|
10287
|
+
}
|
|
10288
|
+
return value;
|
|
10289
|
+
}
|
|
10290
|
+
function resolveWorkspaceDirForLogging(request) {
|
|
10291
|
+
const explicitSelection = request.filesystemTool ?? request.filesystem_tool;
|
|
10292
|
+
if (explicitSelection && typeof explicitSelection === "object" && !Array.isArray(explicitSelection)) {
|
|
10293
|
+
const cwd = explicitSelection.options?.cwd;
|
|
10294
|
+
if (typeof cwd === "string" && cwd.trim().length > 0) {
|
|
10295
|
+
return path7.resolve(cwd);
|
|
10296
|
+
}
|
|
10297
|
+
}
|
|
10298
|
+
return process.cwd();
|
|
10299
|
+
}
|
|
10300
|
+
function createRootAgentLoggingSession(request) {
|
|
10301
|
+
const selected = resolveAgentLoggingSelection(request.logging);
|
|
10302
|
+
if (!selected) {
|
|
10303
|
+
return void 0;
|
|
10304
|
+
}
|
|
10305
|
+
return createAgentLoggingSession({
|
|
10306
|
+
...selected,
|
|
10307
|
+
workspaceDir: typeof selected.workspaceDir === "string" && selected.workspaceDir.trim().length > 0 ? path7.resolve(selected.workspaceDir) : resolveWorkspaceDirForLogging(request),
|
|
10308
|
+
mirrorToConsole: selected.mirrorToConsole !== false
|
|
10309
|
+
});
|
|
10310
|
+
}
|
|
9348
10311
|
function isAgentTelemetrySink(value) {
|
|
9349
10312
|
return typeof value === "object" && value !== null && typeof value.emit === "function";
|
|
9350
10313
|
}
|
|
@@ -9375,7 +10338,7 @@ function createAgentTelemetrySession(telemetry) {
|
|
|
9375
10338
|
const emit = (event) => {
|
|
9376
10339
|
try {
|
|
9377
10340
|
const output = config.sink.emit(event);
|
|
9378
|
-
if (
|
|
10341
|
+
if (isPromiseLike2(output)) {
|
|
9379
10342
|
const task = Promise.resolve(output).then(() => void 0).catch(() => void 0);
|
|
9380
10343
|
trackPromise(task);
|
|
9381
10344
|
}
|
|
@@ -9406,7 +10369,7 @@ function createAgentTelemetryEmitter(params) {
|
|
|
9406
10369
|
}
|
|
9407
10370
|
params.session.emit({
|
|
9408
10371
|
...event,
|
|
9409
|
-
timestamp:
|
|
10372
|
+
timestamp: toIsoNow2(),
|
|
9410
10373
|
runId: params.runId,
|
|
9411
10374
|
...params.parentRunId ? { parentRunId: params.parentRunId } : {},
|
|
9412
10375
|
depth: params.depth,
|