@agentfield/sdk 0.1.142-rc.7 → 0.1.142-rc.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +9 -1
- package/dist/index.js +101 -27
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -354,6 +354,7 @@ declare class AIClient {
|
|
|
354
354
|
resolveModelChoice(options?: AIRequestOptions): {
|
|
355
355
|
provider: NonNullable<AIConfig['provider']>;
|
|
356
356
|
modelName: string;
|
|
357
|
+
routingProvider: string;
|
|
357
358
|
};
|
|
358
359
|
private buildModel;
|
|
359
360
|
private buildPrompt;
|
|
@@ -609,6 +610,8 @@ declare const USAGE_ENVELOPE_KEY = "__agentfield_usage__";
|
|
|
609
610
|
/** A single LLM (or harness) call usage record. */
|
|
610
611
|
interface CostEntry {
|
|
611
612
|
model: string;
|
|
613
|
+
usageStatus?: 'missing';
|
|
614
|
+
routingProvider?: string;
|
|
612
615
|
inputTokens: number;
|
|
613
616
|
outputTokens: number;
|
|
614
617
|
totalTokens: number;
|
|
@@ -632,6 +635,8 @@ interface CostEntry {
|
|
|
632
635
|
/** Input accepted by {@link CostTracker.record}. */
|
|
633
636
|
interface CostEntryInit {
|
|
634
637
|
model: string;
|
|
638
|
+
usageStatus?: 'missing';
|
|
639
|
+
routingProvider?: string;
|
|
635
640
|
inputTokens?: number;
|
|
636
641
|
outputTokens?: number;
|
|
637
642
|
totalTokens?: number;
|
|
@@ -646,6 +651,8 @@ interface CostEntryInit {
|
|
|
646
651
|
}
|
|
647
652
|
/** Wire form of a single usage entry (snake_case cross-language contract). */
|
|
648
653
|
interface UsageEntryWire {
|
|
654
|
+
usage_status?: 'missing';
|
|
655
|
+
routing_provider?: string;
|
|
649
656
|
source: string;
|
|
650
657
|
provider: string | null;
|
|
651
658
|
model: string;
|
|
@@ -1211,6 +1218,7 @@ declare function buildToolConfig(toolsParam: ToolsOption, agent: Agent): Promise
|
|
|
1211
1218
|
declare function executeToolCallLoop(agent: Agent, prompt: string, toolMap: ToolSet, config: ToolCallConfig, needsLazyHydration: boolean, buildModel: () => any, options?: AIRequestOptions, modelChoice?: {
|
|
1212
1219
|
provider?: string;
|
|
1213
1220
|
modelName?: string;
|
|
1221
|
+
routingProvider?: string;
|
|
1214
1222
|
}): Promise<{
|
|
1215
1223
|
text: string;
|
|
1216
1224
|
trace: ToolCallTrace;
|
|
@@ -1959,7 +1967,7 @@ declare const PROVIDER_SPECS: {
|
|
|
1959
1967
|
};
|
|
1960
1968
|
};
|
|
1961
1969
|
type HarnessProviderName = keyof typeof PROVIDER_SPECS;
|
|
1962
|
-
declare const SUPPORTED_PROVIDER_NAMES: readonly ("aforge" | "claude-code" | "codex" | "
|
|
1970
|
+
declare const SUPPORTED_PROVIDER_NAMES: readonly ("gemini" | "aforge" | "claude-code" | "codex" | "opencode" | "pi" | "omp")[];
|
|
1963
1971
|
interface ProviderHealth {
|
|
1964
1972
|
provider: HarnessProviderName;
|
|
1965
1973
|
binary: string | null;
|
package/dist/index.js
CHANGED
|
@@ -218,8 +218,9 @@ function batchProbeInvocation(command) {
|
|
|
218
218
|
});
|
|
219
219
|
return {
|
|
220
220
|
file: process.env.ComSpec ?? "cmd.exe",
|
|
221
|
-
// /s
|
|
222
|
-
//
|
|
221
|
+
// /s removes the outer quote pair of the fixed template, so each "%VAR%"
|
|
222
|
+
// expansion stays quoted.
|
|
223
|
+
// /v:off disables delayed ! expansion; /d disables AutoRun.
|
|
223
224
|
args: ["/d", "/v:off", "/s", "/c", `"${tokens.join(" ")}"`],
|
|
224
225
|
env
|
|
225
226
|
};
|
|
@@ -227,20 +228,40 @@ function batchProbeInvocation(command) {
|
|
|
227
228
|
async function defaultVersionProbe(command) {
|
|
228
229
|
const { execFile } = await import('child_process');
|
|
229
230
|
return new Promise((resolve3, reject) => {
|
|
230
|
-
const
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
(
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
231
|
+
const onProbeOutput = (error, stdout, stderr) => {
|
|
232
|
+
if (error) {
|
|
233
|
+
reject(error);
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
const output = String(stdout || stderr).trim();
|
|
237
|
+
resolve3(output ? output.split(/\r?\n/, 1)[0] : "unknown");
|
|
238
|
+
};
|
|
239
|
+
if (isWindowsBatchFile(command[0])) {
|
|
240
|
+
for (const value of command) {
|
|
241
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
242
|
+
const codePoint = value.charCodeAt(index);
|
|
243
|
+
if (codePoint === 34 || codePoint < 32) {
|
|
244
|
+
reject(new TypeError(
|
|
245
|
+
`Windows batch probe values must not contain double quotes or control characters; offending code point ${codePoint} in ${JSON.stringify(value)}`
|
|
246
|
+
));
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
240
249
|
}
|
|
241
|
-
const output = String(stdout || stderr).trim();
|
|
242
|
-
resolve3(output ? output.split(/\r?\n/, 1)[0] : "unknown");
|
|
243
250
|
}
|
|
251
|
+
const { file, args, env } = batchProbeInvocation(command);
|
|
252
|
+
execFile(
|
|
253
|
+
file,
|
|
254
|
+
args,
|
|
255
|
+
{ timeout: 2e3, windowsHide: true, windowsVerbatimArguments: true, env },
|
|
256
|
+
onProbeOutput
|
|
257
|
+
);
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
execFile(
|
|
261
|
+
command[0],
|
|
262
|
+
command.slice(1),
|
|
263
|
+
{ timeout: 2e3, windowsHide: true, windowsVerbatimArguments: false },
|
|
264
|
+
onProbeOutput
|
|
244
265
|
);
|
|
245
266
|
});
|
|
246
267
|
}
|
|
@@ -2729,6 +2750,8 @@ var CostTracker = class {
|
|
|
2729
2750
|
record(init) {
|
|
2730
2751
|
this.entries.push({
|
|
2731
2752
|
model: init.model,
|
|
2753
|
+
usageStatus: init.usageStatus,
|
|
2754
|
+
routingProvider: init.routingProvider,
|
|
2732
2755
|
inputTokens: toCount(init.inputTokens),
|
|
2733
2756
|
outputTokens: toCount(init.outputTokens),
|
|
2734
2757
|
totalTokens: toCount(init.totalTokens),
|
|
@@ -2788,6 +2811,8 @@ var CostTracker = class {
|
|
|
2788
2811
|
cost_usd: e.costUsd,
|
|
2789
2812
|
cost_source: e.costSource
|
|
2790
2813
|
});
|
|
2814
|
+
if (e.routingProvider) entries[entries.length - 1].routing_provider = e.routingProvider;
|
|
2815
|
+
if (e.usageStatus) entries[entries.length - 1].usage_status = e.usageStatus;
|
|
2791
2816
|
totalInput += e.inputTokens;
|
|
2792
2817
|
totalOutput += e.outputTokens;
|
|
2793
2818
|
totalTokens += entryTotal;
|
|
@@ -2916,7 +2941,19 @@ function recordAiSdkUsage(params) {
|
|
|
2916
2941
|
const tokens = extractTokenUsage(params.source.totalUsage ?? params.source.usage);
|
|
2917
2942
|
const cost = extractProviderCostUsd(params.source);
|
|
2918
2943
|
const hasTokens = tokens.inputTokens > 0 || tokens.outputTokens > 0 || tokens.totalTokens > 0 || tokens.cacheReadTokens > 0 || tokens.cacheCreationTokens > 0;
|
|
2919
|
-
|
|
2944
|
+
const receipt = params.source.totalUsage ?? params.source.usage;
|
|
2945
|
+
const hasTokenReceipt = isRecord(receipt) && ["inputTokens", "outputTokens", "totalTokens"].some(
|
|
2946
|
+
(key) => typeof receipt[key] === "number" && Number.isFinite(receipt[key])
|
|
2947
|
+
);
|
|
2948
|
+
if (!hasTokens && cost === null && !hasTokenReceipt) {
|
|
2949
|
+
tracker.record({
|
|
2950
|
+
model: params.model,
|
|
2951
|
+
provider: params.provider ?? "unknown",
|
|
2952
|
+
routingProvider: params.routingProvider ?? params.provider ?? "unknown",
|
|
2953
|
+
usageStatus: "missing"
|
|
2954
|
+
});
|
|
2955
|
+
return;
|
|
2956
|
+
}
|
|
2920
2957
|
tracker.record({
|
|
2921
2958
|
model: params.model,
|
|
2922
2959
|
...tokens,
|
|
@@ -2924,6 +2961,7 @@ function recordAiSdkUsage(params) {
|
|
|
2924
2961
|
costSource: cost !== null ? "provider" : null,
|
|
2925
2962
|
reasonerName: params.reasonerName !== void 0 ? params.reasonerName : current?.metadata.reasonerId ?? null,
|
|
2926
2963
|
source: "llm",
|
|
2964
|
+
routingProvider: params.routingProvider ?? params.provider ?? void 0,
|
|
2927
2965
|
provider: params.provider ?? void 0
|
|
2928
2966
|
});
|
|
2929
2967
|
} catch {
|
|
@@ -3154,7 +3192,7 @@ async function executeToolCallLoop(agent, prompt, toolMap, config, needsLazyHydr
|
|
|
3154
3192
|
const usageModel = modelChoice?.modelName ?? options.model;
|
|
3155
3193
|
const recordLoopUsage = (result2) => {
|
|
3156
3194
|
if (usageModel) {
|
|
3157
|
-
recordAiSdkUsage({ source: result2, model: usageModel, provider: modelChoice?.provider });
|
|
3195
|
+
recordAiSdkUsage({ source: result2, model: usageModel, provider: modelChoice?.provider, routingProvider: modelChoice?.routingProvider });
|
|
3158
3196
|
}
|
|
3159
3197
|
};
|
|
3160
3198
|
const trace = {
|
|
@@ -3679,6 +3717,26 @@ function withOpenRouterUsageInclude(baseFetch) {
|
|
|
3679
3717
|
return impl(input, init);
|
|
3680
3718
|
};
|
|
3681
3719
|
}
|
|
3720
|
+
|
|
3721
|
+
// src/usage/routingProvider.ts
|
|
3722
|
+
function routingProvider(provider, endpoint) {
|
|
3723
|
+
if (endpoint !== void 0 && endpoint !== null) {
|
|
3724
|
+
try {
|
|
3725
|
+
const url = new URL(endpoint);
|
|
3726
|
+
if (!["https:", "http:"].includes(url.protocol)) return "unknown";
|
|
3727
|
+
const host = url.hostname.toLowerCase();
|
|
3728
|
+
if (host === "openrouter.ai") return "openrouter";
|
|
3729
|
+
if (host === "api.openai.com") return "openai";
|
|
3730
|
+
if (host === "api.anthropic.com") return "anthropic";
|
|
3731
|
+
if (host === "generativelanguage.googleapis.com") return "google";
|
|
3732
|
+
return "other";
|
|
3733
|
+
} catch {
|
|
3734
|
+
return "unknown";
|
|
3735
|
+
}
|
|
3736
|
+
}
|
|
3737
|
+
const normalized = provider === "gemini" ? "google" : provider?.toLowerCase();
|
|
3738
|
+
return ["openrouter", "openai", "anthropic", "google", "bedrock", "azure", "ollama"].includes(normalized ?? "") ? normalized : "unknown";
|
|
3739
|
+
}
|
|
3682
3740
|
var IMAGE_MIME_TYPES = {
|
|
3683
3741
|
".jpg": "image/jpeg",
|
|
3684
3742
|
".jpeg": "image/jpeg",
|
|
@@ -4003,7 +4061,7 @@ var AIClient = class {
|
|
|
4003
4061
|
};
|
|
4004
4062
|
}
|
|
4005
4063
|
async generate(prompt, options = {}) {
|
|
4006
|
-
const { provider, modelName } = this.resolveModelChoice(options);
|
|
4064
|
+
const { provider, modelName, routingProvider: routingProvider2 } = this.resolveModelChoice(options);
|
|
4007
4065
|
const model = this.buildModel(options);
|
|
4008
4066
|
const requestPrompt = this.buildPrompt(prompt, options.content);
|
|
4009
4067
|
if (options.schema) {
|
|
@@ -4019,7 +4077,7 @@ var AIClient = class {
|
|
|
4019
4077
|
experimental_repairText: async ({ text: text2 }) => repairJsonText(text2)
|
|
4020
4078
|
});
|
|
4021
4079
|
const response2 = await this.withRateLimitRetry(call2);
|
|
4022
|
-
recordAiSdkUsage({ source: response2, model: modelName, provider });
|
|
4080
|
+
recordAiSdkUsage({ source: response2, model: modelName, provider, routingProvider: routingProvider2 });
|
|
4023
4081
|
return response2.object;
|
|
4024
4082
|
}
|
|
4025
4083
|
const call = async () => generateText({
|
|
@@ -4030,23 +4088,38 @@ var AIClient = class {
|
|
|
4030
4088
|
maxOutputTokens: options.maxTokens ?? this.config.maxTokens
|
|
4031
4089
|
});
|
|
4032
4090
|
const response = await this.withRateLimitRetry(call);
|
|
4033
|
-
recordAiSdkUsage({ source: response, model: modelName, provider });
|
|
4091
|
+
recordAiSdkUsage({ source: response, model: modelName, provider, routingProvider: routingProvider2 });
|
|
4034
4092
|
return response.text;
|
|
4035
4093
|
}
|
|
4036
|
-
//
|
|
4037
|
-
//
|
|
4038
|
-
// stream": attaching to them would force full background consumption of a
|
|
4039
|
-
// stream the caller may abandon early, changing stream semantics.
|
|
4094
|
+
// Observe usage only after the caller consumes the stream. Merely creating
|
|
4095
|
+
// the stream must not start background consumption of an abandoned response.
|
|
4040
4096
|
async stream(prompt, options = {}) {
|
|
4041
|
-
const
|
|
4097
|
+
const { provider, modelName, routingProvider: routingProvider2 } = this.resolveModelChoice(options);
|
|
4098
|
+
const tracker = ExecutionContext.getCurrent()?.costTracker;
|
|
4042
4099
|
const streamResult = streamText({
|
|
4043
|
-
model,
|
|
4100
|
+
model: this.buildModel(options),
|
|
4044
4101
|
prompt: this.buildPrompt(prompt, options.content),
|
|
4045
4102
|
system: options.system,
|
|
4046
4103
|
temperature: options.temperature ?? this.config.temperature,
|
|
4047
4104
|
maxOutputTokens: options.maxTokens ?? this.config.maxTokens
|
|
4048
4105
|
});
|
|
4049
|
-
return streamResult.textStream;
|
|
4106
|
+
if (!tracker) return streamResult.textStream;
|
|
4107
|
+
return (async function* () {
|
|
4108
|
+
let completed = false;
|
|
4109
|
+
try {
|
|
4110
|
+
for await (const text2 of streamResult.textStream) yield text2;
|
|
4111
|
+
try {
|
|
4112
|
+
const usage = await streamResult.totalUsage;
|
|
4113
|
+
if (usage) recordAiSdkUsage({ source: { totalUsage: usage }, model: modelName, provider, routingProvider: routingProvider2, tracker });
|
|
4114
|
+
else tracker.record({ model: modelName, provider, routingProvider: routingProvider2, usageStatus: "missing" });
|
|
4115
|
+
} catch {
|
|
4116
|
+
tracker.record({ model: modelName, provider, routingProvider: routingProvider2, usageStatus: "missing" });
|
|
4117
|
+
}
|
|
4118
|
+
completed = true;
|
|
4119
|
+
} finally {
|
|
4120
|
+
if (!completed) tracker.record({ model: modelName, provider, routingProvider: routingProvider2, usageStatus: "missing" });
|
|
4121
|
+
}
|
|
4122
|
+
})();
|
|
4050
4123
|
}
|
|
4051
4124
|
async embed(value, options = {}) {
|
|
4052
4125
|
const model = this.buildEmbeddingModel(options);
|
|
@@ -4083,7 +4156,8 @@ var AIClient = class {
|
|
|
4083
4156
|
resolveModelChoice(options = {}) {
|
|
4084
4157
|
return {
|
|
4085
4158
|
provider: options.provider ?? this.config.provider ?? "openai",
|
|
4086
|
-
modelName: options.model ?? this.config.model ?? "gpt-4o"
|
|
4159
|
+
modelName: options.model ?? this.config.model ?? "gpt-4o",
|
|
4160
|
+
routingProvider: routingProvider(options.provider ?? this.config.provider ?? "openai", this.config.baseUrl)
|
|
4087
4161
|
};
|
|
4088
4162
|
}
|
|
4089
4163
|
buildModel(options) {
|