@stackfactor/agent-utils 1.2.7 → 1.2.8
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/cjs/langChain.d.ts
CHANGED
|
@@ -15,8 +15,9 @@ declare const _default: {
|
|
|
15
15
|
createAgent: (name: string, modelName: string, systemPrompt: string, tools: any[], responseFormat: any, config: any) => any;
|
|
16
16
|
runAgent: (agent: any, prompt: string, config: any, onProgress?: Function | null, usageTracker?: UsageTracker | null) => Promise<any>;
|
|
17
17
|
runPromptWithModel: (modelName: string, config: any, prompt: any, onProgressReport: any, minPercent?: number, maxPercent?: number, expectsJsonResponse?: boolean, schema?: any, agentName?: string, tools?: any[], usageTracker?: UsageTracker | null) => Promise<any>;
|
|
18
|
-
runPromptWithModelForImageGeneration: (modelName: string, config: any, prompt: string, options?: any) => Promise<any>;
|
|
18
|
+
runPromptWithModelForImageGeneration: (modelName: string, config: any, prompt: string, options?: any, usageTracker?: UsageTracker | null) => Promise<any>;
|
|
19
19
|
throwErrorIfNotSuccessful: (response: any) => string;
|
|
20
|
+
updateUsageTrackerForCharacters: (tracker: UsageTracker | null | undefined, modelName: string, characterCount: number, config: any) => void;
|
|
20
21
|
validateModel: (selectedModel: string, supportedModels: string[]) => string;
|
|
21
22
|
};
|
|
22
23
|
export default _default;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"langChain.d.ts","sourceRoot":"","sources":["../../src/langChain.ts"],"names":[],"mappings":"AA2BA;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE;QAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC;CACxC,CAAC;;0CAE2C,GAAG,KAAG,IAAI;
|
|
1
|
+
{"version":3,"file":"langChain.d.ts","sourceRoot":"","sources":["../../src/langChain.ts"],"names":[],"mappings":"AA2BA;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE;QAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC;CACxC,CAAC;;0CAE2C,GAAG,KAAG,IAAI;wBA+jB/C,MAAM,aACD,MAAM,gBACH,MAAM,SACb,GAAG,EAAE,kBACI,GAAG,UACX,GAAG,KACV,GAAG;sBA0BG,GAAG,UACF,MAAM,UACN,GAAG,eACC,QAAQ,GAAG,IAAI,iBACb,YAAY,GAAG,IAAI,KAChC,OAAO,CAAC,GAAG,CAAC;oCA4XF,MAAM,UACT,GAAG,UACH,GAAG,oBACO,GAAG,eACT,MAAM,eACN,MAAM,wBACG,OAAO,WACpB,GAAG,cACA,MAAM,UACV,GAAG,EAAE,iBACE,YAAY,GAAG,IAAI,KAChC,OAAO,CAAC,GAAG,CAAC;sDAusBF,MAAM,UACT,GAAG,UACH,MAAM,YACL,GAAG,iBACE,YAAY,GAAG,IAAI,KAChC,OAAO,CAAC,GAAG,CAAC;0CA3+B8B,GAAG,KAAG,MAAM;+CApjB9C,YAAY,GAAG,IAAI,GAAG,SAAS,aAC7B,MAAM,kBACD,MAAM,UACd,GAAG,KACV,IAAI;mCAokBU,MAAM,mBACJ,MAAM,EAAE,KACxB,MAAM;;AAkhCT,wBASE"}
|
package/dist/cjs/langChain.js
CHANGED
|
@@ -68,6 +68,101 @@ const updateUsageTracker = (tracker, modelName, usage, config) => {
|
|
|
68
68
|
tracker.tokens[inputKey] = (tracker.tokens[inputKey] || 0) + inputTokens;
|
|
69
69
|
tracker.tokens[outputKey] = (tracker.tokens[outputKey] || 0) + outputTokens;
|
|
70
70
|
};
|
|
71
|
+
/**
|
|
72
|
+
* Adds a single image-generation call's usage to the caller-supplied tracker.
|
|
73
|
+
* Extracts token counts from provider-specific response shapes: OpenAI returns
|
|
74
|
+
* `response.usage` with `input_tokens_details.{text_tokens,image_tokens}` and
|
|
75
|
+
* `output_tokens`; Google returns `response.usageMetadata` with
|
|
76
|
+
* `promptTokensDetails` (modality-keyed) and `candidatesTokenCount` for the
|
|
77
|
+
* generated image. Reads rates from `<model>-input-token-costs`,
|
|
78
|
+
* `<model>-image-input-token-costs`, and `<model>-image-output-token-costs`.
|
|
79
|
+
* No-ops when the tracker or response usage info is absent.
|
|
80
|
+
*/
|
|
81
|
+
const updateImageUsageTracker = (tracker, modelName, response, config, provider) => {
|
|
82
|
+
if (!tracker || !modelName || !response)
|
|
83
|
+
return;
|
|
84
|
+
if (typeof tracker.cost !== "number")
|
|
85
|
+
tracker.cost = 0;
|
|
86
|
+
if (!tracker.tokens || typeof tracker.tokens !== "object")
|
|
87
|
+
tracker.tokens = {};
|
|
88
|
+
let textInputTokens = 0;
|
|
89
|
+
let imageInputTokens = 0;
|
|
90
|
+
let imageOutputTokens = 0;
|
|
91
|
+
if (provider === "openai") {
|
|
92
|
+
const usage = response.usage;
|
|
93
|
+
if (!usage)
|
|
94
|
+
return;
|
|
95
|
+
const details = usage.input_tokens_details || {};
|
|
96
|
+
textInputTokens = details.text_tokens || 0;
|
|
97
|
+
imageInputTokens = details.image_tokens || 0;
|
|
98
|
+
// Older shapes report a single input_tokens without modality breakdown.
|
|
99
|
+
if (!textInputTokens && !imageInputTokens && usage.input_tokens) {
|
|
100
|
+
textInputTokens = usage.input_tokens;
|
|
101
|
+
}
|
|
102
|
+
imageOutputTokens = usage.output_tokens || 0;
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
const um = response.usageMetadata;
|
|
106
|
+
if (!um)
|
|
107
|
+
return;
|
|
108
|
+
const promptDetails = Array.isArray(um.promptTokensDetails)
|
|
109
|
+
? um.promptTokensDetails
|
|
110
|
+
: [];
|
|
111
|
+
for (const d of promptDetails) {
|
|
112
|
+
const modality = String(d?.modality || "").toUpperCase();
|
|
113
|
+
const count = Number(d?.tokenCount) || 0;
|
|
114
|
+
if (modality === "IMAGE")
|
|
115
|
+
imageInputTokens += count;
|
|
116
|
+
else
|
|
117
|
+
textInputTokens += count;
|
|
118
|
+
}
|
|
119
|
+
if (!textInputTokens && !imageInputTokens) {
|
|
120
|
+
textInputTokens = Number(um.promptTokenCount) || 0;
|
|
121
|
+
}
|
|
122
|
+
imageOutputTokens = Number(um.candidatesTokenCount) || 0;
|
|
123
|
+
}
|
|
124
|
+
const textInputRate = getModelRate(modelName, config, "input-token");
|
|
125
|
+
const imageInputRate = getModelRate(modelName, config, "image-input-token");
|
|
126
|
+
const imageOutputRate = getModelRate(modelName, config, "image-output-token");
|
|
127
|
+
const addedCost = (textInputTokens / 1_000_000) * textInputRate +
|
|
128
|
+
(imageInputTokens / 1_000_000) * imageInputRate +
|
|
129
|
+
(imageOutputTokens / 1_000_000) * imageOutputRate;
|
|
130
|
+
if (Number.isFinite(addedCost) && addedCost > 0)
|
|
131
|
+
tracker.cost += addedCost;
|
|
132
|
+
if (textInputTokens > 0) {
|
|
133
|
+
const key = `${modelName}_inputTokens`;
|
|
134
|
+
tracker.tokens[key] = (tracker.tokens[key] || 0) + textInputTokens;
|
|
135
|
+
}
|
|
136
|
+
if (imageInputTokens > 0) {
|
|
137
|
+
const key = `${modelName}_imageInputTokens`;
|
|
138
|
+
tracker.tokens[key] = (tracker.tokens[key] || 0) + imageInputTokens;
|
|
139
|
+
}
|
|
140
|
+
if (imageOutputTokens > 0) {
|
|
141
|
+
const key = `${modelName}_imageOutputTokens`;
|
|
142
|
+
tracker.tokens[key] = (tracker.tokens[key] || 0) + imageOutputTokens;
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
/**
|
|
146
|
+
* Adds character-billed usage (e.g. ElevenLabs TTS) to the caller-supplied
|
|
147
|
+
* tracker. There is no provider response to parse — billing is deterministic
|
|
148
|
+
* from the input character count. Reads the rate from
|
|
149
|
+
* `<model>-character-costs` (USD per million characters) and accumulates
|
|
150
|
+
* under `<model>_characters`. No-ops on missing tracker / model / count.
|
|
151
|
+
*/
|
|
152
|
+
const updateUsageTrackerForCharacters = (tracker, modelName, characterCount, config) => {
|
|
153
|
+
if (!tracker || !modelName || !characterCount)
|
|
154
|
+
return;
|
|
155
|
+
if (typeof tracker.cost !== "number")
|
|
156
|
+
tracker.cost = 0;
|
|
157
|
+
if (!tracker.tokens || typeof tracker.tokens !== "object")
|
|
158
|
+
tracker.tokens = {};
|
|
159
|
+
const rate = getModelRate(modelName, config, "character");
|
|
160
|
+
const addedCost = (characterCount / 1_000_000) * rate;
|
|
161
|
+
if (Number.isFinite(addedCost) && addedCost > 0)
|
|
162
|
+
tracker.cost += addedCost;
|
|
163
|
+
const key = `${modelName}_characters`;
|
|
164
|
+
tracker.tokens[key] = (tracker.tokens[key] || 0) + characterCount;
|
|
165
|
+
};
|
|
71
166
|
/**
|
|
72
167
|
* Extracts a normalized token-usage object from a single LangChain `invoke()`
|
|
73
168
|
* response. Reads from `usage_metadata` first (standardized in LangChain v1),
|
|
@@ -1170,7 +1265,7 @@ const getImageModelProvider = (modelName) => {
|
|
|
1170
1265
|
* @returns An object with `url`, `b64_json`, and `revisedPrompt` for a single image,
|
|
1171
1266
|
* or `{ images: [...] }` for multiple images
|
|
1172
1267
|
*/
|
|
1173
|
-
const generateImageWithOpenAI = async (modelName, config, prompt, options) => {
|
|
1268
|
+
const generateImageWithOpenAI = async (modelName, config, prompt, options, usageTracker = null) => {
|
|
1174
1269
|
const { size = "1024x1024", style = "vivid", responseFormat = "url", n = 1, } = options;
|
|
1175
1270
|
if (!config.openAIAPIKey) {
|
|
1176
1271
|
throw errorHandling_js_1.default.create(const_js_1.default.HTTP_CODES.BAD_REQUEST, "OpenAI API key is required for OpenAI image generation");
|
|
@@ -1218,6 +1313,7 @@ const generateImageWithOpenAI = async (modelName, config, prompt, options) => {
|
|
|
1218
1313
|
}
|
|
1219
1314
|
}
|
|
1220
1315
|
const response = await openai.images.generate(requestParams);
|
|
1316
|
+
updateImageUsageTracker(usageTracker, modelName, response, config, "openai");
|
|
1221
1317
|
// Format response based on number of images
|
|
1222
1318
|
if (n === 1) {
|
|
1223
1319
|
const imageData = response.data[0];
|
|
@@ -1255,7 +1351,7 @@ const generateImageWithOpenAI = async (modelName, config, prompt, options) => {
|
|
|
1255
1351
|
* @returns A single image descriptor `{ b64_json, mimeType }` when one image is
|
|
1256
1352
|
* requested, or `{ images: [...] }` for multiple images
|
|
1257
1353
|
*/
|
|
1258
|
-
const generateImageWithGoogle = async (modelName, config, prompt, options) => {
|
|
1354
|
+
const generateImageWithGoogle = async (modelName, config, prompt, options, usageTracker = null) => {
|
|
1259
1355
|
const { aspectRatio = "1:1", numberOfImages = 1, negativePrompt = "", } = options;
|
|
1260
1356
|
if (!config.googleAPIKey) {
|
|
1261
1357
|
throw errorHandling_js_1.default.create(const_js_1.default.HTTP_CODES.BAD_REQUEST, "Google API key is required for Google image generation");
|
|
@@ -1316,6 +1412,7 @@ const generateImageWithGoogle = async (modelName, config, prompt, options) => {
|
|
|
1316
1412
|
},
|
|
1317
1413
|
};
|
|
1318
1414
|
const response = await ai.models.generateContent(req);
|
|
1415
|
+
updateImageUsageTracker(usageTracker, modelName, response, config, "google");
|
|
1319
1416
|
// Extract images from response
|
|
1320
1417
|
const images = [];
|
|
1321
1418
|
const candidates = response.candidates || [];
|
|
@@ -1358,7 +1455,7 @@ const generateImageWithGoogle = async (modelName, config, prompt, options) => {
|
|
|
1358
1455
|
* and `generateImageWithGoogle` for full option sets); defaults to `{}`
|
|
1359
1456
|
* @returns The generated image data object returned by the provider-specific function
|
|
1360
1457
|
*/
|
|
1361
|
-
const runPromptWithModelForImageGeneration = async (modelName, config, prompt, options = {}) => {
|
|
1458
|
+
const runPromptWithModelForImageGeneration = async (modelName, config, prompt, options = {}, usageTracker = null) => {
|
|
1362
1459
|
const provider = getImageModelProvider(modelName);
|
|
1363
1460
|
if (!provider) {
|
|
1364
1461
|
throw errorHandling_js_1.default.create(const_js_1.default.HTTP_CODES.BAD_REQUEST, `Unable to determine provider for model: ${modelName}. Model name should start with 'gpt-image-', 'gemini-', or 'imagen-'.`);
|
|
@@ -1367,10 +1464,10 @@ const runPromptWithModelForImageGeneration = async (modelName, config, prompt, o
|
|
|
1367
1464
|
try {
|
|
1368
1465
|
let result;
|
|
1369
1466
|
if (provider === "openai") {
|
|
1370
|
-
result = await generateImageWithOpenAI(modelName, config, prompt, options);
|
|
1467
|
+
result = await generateImageWithOpenAI(modelName, config, prompt, options, usageTracker);
|
|
1371
1468
|
}
|
|
1372
1469
|
else if (provider === "google") {
|
|
1373
|
-
result = await generateImageWithGoogle(modelName, config, prompt, options);
|
|
1470
|
+
result = await generateImageWithGoogle(modelName, config, prompt, options, usageTracker);
|
|
1374
1471
|
}
|
|
1375
1472
|
const endTime = Date.now();
|
|
1376
1473
|
const duration = endTime - startTime;
|
|
@@ -1398,5 +1495,6 @@ exports.default = {
|
|
|
1398
1495
|
runPromptWithModel,
|
|
1399
1496
|
runPromptWithModelForImageGeneration,
|
|
1400
1497
|
throwErrorIfNotSuccessful,
|
|
1498
|
+
updateUsageTrackerForCharacters,
|
|
1401
1499
|
validateModel,
|
|
1402
1500
|
};
|
package/dist/esm/langChain.d.ts
CHANGED
|
@@ -15,8 +15,9 @@ declare const _default: {
|
|
|
15
15
|
createAgent: (name: string, modelName: string, systemPrompt: string, tools: any[], responseFormat: any, config: any) => any;
|
|
16
16
|
runAgent: (agent: any, prompt: string, config: any, onProgress?: Function | null, usageTracker?: UsageTracker | null) => Promise<any>;
|
|
17
17
|
runPromptWithModel: (modelName: string, config: any, prompt: any, onProgressReport: any, minPercent?: number, maxPercent?: number, expectsJsonResponse?: boolean, schema?: any, agentName?: string, tools?: any[], usageTracker?: UsageTracker | null) => Promise<any>;
|
|
18
|
-
runPromptWithModelForImageGeneration: (modelName: string, config: any, prompt: string, options?: any) => Promise<any>;
|
|
18
|
+
runPromptWithModelForImageGeneration: (modelName: string, config: any, prompt: string, options?: any, usageTracker?: UsageTracker | null) => Promise<any>;
|
|
19
19
|
throwErrorIfNotSuccessful: (response: any) => string;
|
|
20
|
+
updateUsageTrackerForCharacters: (tracker: UsageTracker | null | undefined, modelName: string, characterCount: number, config: any) => void;
|
|
20
21
|
validateModel: (selectedModel: string, supportedModels: string[]) => string;
|
|
21
22
|
};
|
|
22
23
|
export default _default;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"langChain.d.ts","sourceRoot":"","sources":["../../src/langChain.ts"],"names":[],"mappings":"AA2BA;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE;QAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC;CACxC,CAAC;;0CAE2C,GAAG,KAAG,IAAI;
|
|
1
|
+
{"version":3,"file":"langChain.d.ts","sourceRoot":"","sources":["../../src/langChain.ts"],"names":[],"mappings":"AA2BA;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE;QAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC;CACxC,CAAC;;0CAE2C,GAAG,KAAG,IAAI;wBA+jB/C,MAAM,aACD,MAAM,gBACH,MAAM,SACb,GAAG,EAAE,kBACI,GAAG,UACX,GAAG,KACV,GAAG;sBA0BG,GAAG,UACF,MAAM,UACN,GAAG,eACC,QAAQ,GAAG,IAAI,iBACb,YAAY,GAAG,IAAI,KAChC,OAAO,CAAC,GAAG,CAAC;oCA4XF,MAAM,UACT,GAAG,UACH,GAAG,oBACO,GAAG,eACT,MAAM,eACN,MAAM,wBACG,OAAO,WACpB,GAAG,cACA,MAAM,UACV,GAAG,EAAE,iBACE,YAAY,GAAG,IAAI,KAChC,OAAO,CAAC,GAAG,CAAC;sDAusBF,MAAM,UACT,GAAG,UACH,MAAM,YACL,GAAG,iBACE,YAAY,GAAG,IAAI,KAChC,OAAO,CAAC,GAAG,CAAC;0CA3+B8B,GAAG,KAAG,MAAM;+CApjB9C,YAAY,GAAG,IAAI,GAAG,SAAS,aAC7B,MAAM,kBACD,MAAM,UACd,GAAG,KACV,IAAI;mCAokBU,MAAM,mBACJ,MAAM,EAAE,KACxB,MAAM;;AAkhCT,wBASE"}
|
package/dist/esm/langChain.js
CHANGED
|
@@ -63,6 +63,101 @@ const updateUsageTracker = (tracker, modelName, usage, config) => {
|
|
|
63
63
|
tracker.tokens[inputKey] = (tracker.tokens[inputKey] || 0) + inputTokens;
|
|
64
64
|
tracker.tokens[outputKey] = (tracker.tokens[outputKey] || 0) + outputTokens;
|
|
65
65
|
};
|
|
66
|
+
/**
|
|
67
|
+
* Adds a single image-generation call's usage to the caller-supplied tracker.
|
|
68
|
+
* Extracts token counts from provider-specific response shapes: OpenAI returns
|
|
69
|
+
* `response.usage` with `input_tokens_details.{text_tokens,image_tokens}` and
|
|
70
|
+
* `output_tokens`; Google returns `response.usageMetadata` with
|
|
71
|
+
* `promptTokensDetails` (modality-keyed) and `candidatesTokenCount` for the
|
|
72
|
+
* generated image. Reads rates from `<model>-input-token-costs`,
|
|
73
|
+
* `<model>-image-input-token-costs`, and `<model>-image-output-token-costs`.
|
|
74
|
+
* No-ops when the tracker or response usage info is absent.
|
|
75
|
+
*/
|
|
76
|
+
const updateImageUsageTracker = (tracker, modelName, response, config, provider) => {
|
|
77
|
+
if (!tracker || !modelName || !response)
|
|
78
|
+
return;
|
|
79
|
+
if (typeof tracker.cost !== "number")
|
|
80
|
+
tracker.cost = 0;
|
|
81
|
+
if (!tracker.tokens || typeof tracker.tokens !== "object")
|
|
82
|
+
tracker.tokens = {};
|
|
83
|
+
let textInputTokens = 0;
|
|
84
|
+
let imageInputTokens = 0;
|
|
85
|
+
let imageOutputTokens = 0;
|
|
86
|
+
if (provider === "openai") {
|
|
87
|
+
const usage = response.usage;
|
|
88
|
+
if (!usage)
|
|
89
|
+
return;
|
|
90
|
+
const details = usage.input_tokens_details || {};
|
|
91
|
+
textInputTokens = details.text_tokens || 0;
|
|
92
|
+
imageInputTokens = details.image_tokens || 0;
|
|
93
|
+
// Older shapes report a single input_tokens without modality breakdown.
|
|
94
|
+
if (!textInputTokens && !imageInputTokens && usage.input_tokens) {
|
|
95
|
+
textInputTokens = usage.input_tokens;
|
|
96
|
+
}
|
|
97
|
+
imageOutputTokens = usage.output_tokens || 0;
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
const um = response.usageMetadata;
|
|
101
|
+
if (!um)
|
|
102
|
+
return;
|
|
103
|
+
const promptDetails = Array.isArray(um.promptTokensDetails)
|
|
104
|
+
? um.promptTokensDetails
|
|
105
|
+
: [];
|
|
106
|
+
for (const d of promptDetails) {
|
|
107
|
+
const modality = String(d?.modality || "").toUpperCase();
|
|
108
|
+
const count = Number(d?.tokenCount) || 0;
|
|
109
|
+
if (modality === "IMAGE")
|
|
110
|
+
imageInputTokens += count;
|
|
111
|
+
else
|
|
112
|
+
textInputTokens += count;
|
|
113
|
+
}
|
|
114
|
+
if (!textInputTokens && !imageInputTokens) {
|
|
115
|
+
textInputTokens = Number(um.promptTokenCount) || 0;
|
|
116
|
+
}
|
|
117
|
+
imageOutputTokens = Number(um.candidatesTokenCount) || 0;
|
|
118
|
+
}
|
|
119
|
+
const textInputRate = getModelRate(modelName, config, "input-token");
|
|
120
|
+
const imageInputRate = getModelRate(modelName, config, "image-input-token");
|
|
121
|
+
const imageOutputRate = getModelRate(modelName, config, "image-output-token");
|
|
122
|
+
const addedCost = (textInputTokens / 1_000_000) * textInputRate +
|
|
123
|
+
(imageInputTokens / 1_000_000) * imageInputRate +
|
|
124
|
+
(imageOutputTokens / 1_000_000) * imageOutputRate;
|
|
125
|
+
if (Number.isFinite(addedCost) && addedCost > 0)
|
|
126
|
+
tracker.cost += addedCost;
|
|
127
|
+
if (textInputTokens > 0) {
|
|
128
|
+
const key = `${modelName}_inputTokens`;
|
|
129
|
+
tracker.tokens[key] = (tracker.tokens[key] || 0) + textInputTokens;
|
|
130
|
+
}
|
|
131
|
+
if (imageInputTokens > 0) {
|
|
132
|
+
const key = `${modelName}_imageInputTokens`;
|
|
133
|
+
tracker.tokens[key] = (tracker.tokens[key] || 0) + imageInputTokens;
|
|
134
|
+
}
|
|
135
|
+
if (imageOutputTokens > 0) {
|
|
136
|
+
const key = `${modelName}_imageOutputTokens`;
|
|
137
|
+
tracker.tokens[key] = (tracker.tokens[key] || 0) + imageOutputTokens;
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
/**
|
|
141
|
+
* Adds character-billed usage (e.g. ElevenLabs TTS) to the caller-supplied
|
|
142
|
+
* tracker. There is no provider response to parse — billing is deterministic
|
|
143
|
+
* from the input character count. Reads the rate from
|
|
144
|
+
* `<model>-character-costs` (USD per million characters) and accumulates
|
|
145
|
+
* under `<model>_characters`. No-ops on missing tracker / model / count.
|
|
146
|
+
*/
|
|
147
|
+
const updateUsageTrackerForCharacters = (tracker, modelName, characterCount, config) => {
|
|
148
|
+
if (!tracker || !modelName || !characterCount)
|
|
149
|
+
return;
|
|
150
|
+
if (typeof tracker.cost !== "number")
|
|
151
|
+
tracker.cost = 0;
|
|
152
|
+
if (!tracker.tokens || typeof tracker.tokens !== "object")
|
|
153
|
+
tracker.tokens = {};
|
|
154
|
+
const rate = getModelRate(modelName, config, "character");
|
|
155
|
+
const addedCost = (characterCount / 1_000_000) * rate;
|
|
156
|
+
if (Number.isFinite(addedCost) && addedCost > 0)
|
|
157
|
+
tracker.cost += addedCost;
|
|
158
|
+
const key = `${modelName}_characters`;
|
|
159
|
+
tracker.tokens[key] = (tracker.tokens[key] || 0) + characterCount;
|
|
160
|
+
};
|
|
66
161
|
/**
|
|
67
162
|
* Extracts a normalized token-usage object from a single LangChain `invoke()`
|
|
68
163
|
* response. Reads from `usage_metadata` first (standardized in LangChain v1),
|
|
@@ -1165,7 +1260,7 @@ const getImageModelProvider = (modelName) => {
|
|
|
1165
1260
|
* @returns An object with `url`, `b64_json`, and `revisedPrompt` for a single image,
|
|
1166
1261
|
* or `{ images: [...] }` for multiple images
|
|
1167
1262
|
*/
|
|
1168
|
-
const generateImageWithOpenAI = async (modelName, config, prompt, options) => {
|
|
1263
|
+
const generateImageWithOpenAI = async (modelName, config, prompt, options, usageTracker = null) => {
|
|
1169
1264
|
const { size = "1024x1024", style = "vivid", responseFormat = "url", n = 1, } = options;
|
|
1170
1265
|
if (!config.openAIAPIKey) {
|
|
1171
1266
|
throw errorHandlingHelper.create(constants.HTTP_CODES.BAD_REQUEST, "OpenAI API key is required for OpenAI image generation");
|
|
@@ -1213,6 +1308,7 @@ const generateImageWithOpenAI = async (modelName, config, prompt, options) => {
|
|
|
1213
1308
|
}
|
|
1214
1309
|
}
|
|
1215
1310
|
const response = await openai.images.generate(requestParams);
|
|
1311
|
+
updateImageUsageTracker(usageTracker, modelName, response, config, "openai");
|
|
1216
1312
|
// Format response based on number of images
|
|
1217
1313
|
if (n === 1) {
|
|
1218
1314
|
const imageData = response.data[0];
|
|
@@ -1250,7 +1346,7 @@ const generateImageWithOpenAI = async (modelName, config, prompt, options) => {
|
|
|
1250
1346
|
* @returns A single image descriptor `{ b64_json, mimeType }` when one image is
|
|
1251
1347
|
* requested, or `{ images: [...] }` for multiple images
|
|
1252
1348
|
*/
|
|
1253
|
-
const generateImageWithGoogle = async (modelName, config, prompt, options) => {
|
|
1349
|
+
const generateImageWithGoogle = async (modelName, config, prompt, options, usageTracker = null) => {
|
|
1254
1350
|
const { aspectRatio = "1:1", numberOfImages = 1, negativePrompt = "", } = options;
|
|
1255
1351
|
if (!config.googleAPIKey) {
|
|
1256
1352
|
throw errorHandlingHelper.create(constants.HTTP_CODES.BAD_REQUEST, "Google API key is required for Google image generation");
|
|
@@ -1311,6 +1407,7 @@ const generateImageWithGoogle = async (modelName, config, prompt, options) => {
|
|
|
1311
1407
|
},
|
|
1312
1408
|
};
|
|
1313
1409
|
const response = await ai.models.generateContent(req);
|
|
1410
|
+
updateImageUsageTracker(usageTracker, modelName, response, config, "google");
|
|
1314
1411
|
// Extract images from response
|
|
1315
1412
|
const images = [];
|
|
1316
1413
|
const candidates = response.candidates || [];
|
|
@@ -1353,7 +1450,7 @@ const generateImageWithGoogle = async (modelName, config, prompt, options) => {
|
|
|
1353
1450
|
* and `generateImageWithGoogle` for full option sets); defaults to `{}`
|
|
1354
1451
|
* @returns The generated image data object returned by the provider-specific function
|
|
1355
1452
|
*/
|
|
1356
|
-
const runPromptWithModelForImageGeneration = async (modelName, config, prompt, options = {}) => {
|
|
1453
|
+
const runPromptWithModelForImageGeneration = async (modelName, config, prompt, options = {}, usageTracker = null) => {
|
|
1357
1454
|
const provider = getImageModelProvider(modelName);
|
|
1358
1455
|
if (!provider) {
|
|
1359
1456
|
throw errorHandlingHelper.create(constants.HTTP_CODES.BAD_REQUEST, `Unable to determine provider for model: ${modelName}. Model name should start with 'gpt-image-', 'gemini-', or 'imagen-'.`);
|
|
@@ -1362,10 +1459,10 @@ const runPromptWithModelForImageGeneration = async (modelName, config, prompt, o
|
|
|
1362
1459
|
try {
|
|
1363
1460
|
let result;
|
|
1364
1461
|
if (provider === "openai") {
|
|
1365
|
-
result = await generateImageWithOpenAI(modelName, config, prompt, options);
|
|
1462
|
+
result = await generateImageWithOpenAI(modelName, config, prompt, options, usageTracker);
|
|
1366
1463
|
}
|
|
1367
1464
|
else if (provider === "google") {
|
|
1368
|
-
result = await generateImageWithGoogle(modelName, config, prompt, options);
|
|
1465
|
+
result = await generateImageWithGoogle(modelName, config, prompt, options, usageTracker);
|
|
1369
1466
|
}
|
|
1370
1467
|
const endTime = Date.now();
|
|
1371
1468
|
const duration = endTime - startTime;
|
|
@@ -1393,5 +1490,6 @@ export default {
|
|
|
1393
1490
|
runPromptWithModel,
|
|
1394
1491
|
runPromptWithModelForImageGeneration,
|
|
1395
1492
|
throwErrorIfNotSuccessful,
|
|
1493
|
+
updateUsageTrackerForCharacters,
|
|
1396
1494
|
validateModel,
|
|
1397
1495
|
};
|