@vfarcic/dot-ai 0.125.0 → 0.126.0
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/core/capability-scan-workflow.d.ts +4 -9
- package/dist/core/capability-scan-workflow.d.ts.map +1 -1
- package/dist/core/capability-scan-workflow.js +203 -455
- package/dist/core/discovery.d.ts.map +1 -1
- package/dist/core/discovery.js +4 -3
- package/dist/core/embedding-service.d.ts +9 -36
- package/dist/core/embedding-service.d.ts.map +1 -1
- package/dist/core/embedding-service.js +137 -253
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.js +2 -2
- package/dist/core/kubernetes-utils.d.ts +1 -0
- package/dist/core/kubernetes-utils.d.ts.map +1 -1
- package/dist/core/kubernetes-utils.js +53 -48
- package/dist/core/providers/anthropic-provider.d.ts.map +1 -1
- package/dist/core/providers/anthropic-provider.js +352 -282
- package/dist/core/providers/vercel-provider.d.ts.map +1 -1
- package/dist/core/providers/vercel-provider.js +389 -351
- package/dist/core/tracing/ai-tracing.d.ts +80 -0
- package/dist/core/tracing/ai-tracing.d.ts.map +1 -0
- package/dist/core/tracing/ai-tracing.js +122 -0
- package/dist/core/tracing/config.d.ts +15 -0
- package/dist/core/tracing/config.d.ts.map +1 -0
- package/dist/core/tracing/config.js +133 -0
- package/dist/core/tracing/http-tracing.d.ts +28 -0
- package/dist/core/tracing/http-tracing.d.ts.map +1 -0
- package/dist/core/tracing/http-tracing.js +119 -0
- package/dist/core/tracing/index.d.ts +14 -0
- package/dist/core/tracing/index.d.ts.map +1 -0
- package/dist/core/tracing/index.js +40 -0
- package/dist/core/tracing/k8s-tracing.d.ts +57 -0
- package/dist/core/tracing/k8s-tracing.d.ts.map +1 -0
- package/dist/core/tracing/k8s-tracing.js +155 -0
- package/dist/core/tracing/qdrant-tracing.d.ts +68 -0
- package/dist/core/tracing/qdrant-tracing.d.ts.map +1 -0
- package/dist/core/tracing/qdrant-tracing.js +102 -0
- package/dist/core/tracing/tool-tracing.d.ts +31 -0
- package/dist/core/tracing/tool-tracing.d.ts.map +1 -0
- package/dist/core/tracing/tool-tracing.js +76 -0
- package/dist/core/tracing/tracer.d.ts +21 -0
- package/dist/core/tracing/tracer.d.ts.map +1 -0
- package/dist/core/tracing/tracer.js +215 -0
- package/dist/core/tracing/types.d.ts +86 -0
- package/dist/core/tracing/types.d.ts.map +1 -0
- package/dist/core/tracing/types.js +41 -0
- package/dist/core/vector-db-service.d.ts.map +1 -1
- package/dist/core/vector-db-service.js +238 -163
- package/dist/interfaces/mcp.d.ts.map +1 -1
- package/dist/interfaces/mcp.js +71 -43
- package/dist/mcp/server.js +12 -2
- package/dist/tools/organizational-data.d.ts.map +1 -1
- package/dist/tools/organizational-data.js +2 -4
- package/dist/tools/version.d.ts +12 -1
- package/dist/tools/version.d.ts.map +1 -1
- package/dist/tools/version.js +24 -4
- package/package.json +9 -1
|
@@ -17,6 +17,7 @@ const deepseek_1 = require("@ai-sdk/deepseek");
|
|
|
17
17
|
const ai_sdk_provider_1 = require("@openrouter/ai-sdk-provider");
|
|
18
18
|
const provider_debug_utils_1 = require("./provider-debug-utils");
|
|
19
19
|
const model_config_1 = require("../model-config");
|
|
20
|
+
const ai_tracing_1 = require("../tracing/ai-tracing");
|
|
20
21
|
// Get all supported provider keys dynamically from CURRENT_MODELS
|
|
21
22
|
const SUPPORTED_PROVIDERS = Object.keys(model_config_1.CURRENT_MODELS);
|
|
22
23
|
class VercelProvider {
|
|
@@ -30,7 +31,7 @@ class VercelProvider {
|
|
|
30
31
|
this.apiKey = config.apiKey;
|
|
31
32
|
this.providerType = config.provider;
|
|
32
33
|
this.model = config.model || this.getDefaultModel();
|
|
33
|
-
this.debugMode = config.debugMode ??
|
|
34
|
+
this.debugMode = config.debugMode ?? process.env.DEBUG_DOT_AI === 'true';
|
|
34
35
|
this.baseURL = config.baseURL; // PRD #194: Store custom endpoint URL
|
|
35
36
|
this.validateConfiguration();
|
|
36
37
|
this.initializeModel();
|
|
@@ -50,7 +51,7 @@ class VercelProvider {
|
|
|
50
51
|
case 'openai':
|
|
51
52
|
case 'openai_pro':
|
|
52
53
|
provider = (0, openai_1.createOpenAI)({
|
|
53
|
-
apiKey: this.apiKey
|
|
54
|
+
apiKey: this.apiKey,
|
|
54
55
|
});
|
|
55
56
|
break;
|
|
56
57
|
case 'google':
|
|
@@ -64,8 +65,8 @@ class VercelProvider {
|
|
|
64
65
|
// Enable 1M token context window for Claude Sonnet 4 (5x increase from 200K)
|
|
65
66
|
// Required for models like claude-sonnet-4-5-20250929
|
|
66
67
|
headers: {
|
|
67
|
-
'anthropic-beta': 'context-1m-2025-08-07'
|
|
68
|
-
}
|
|
68
|
+
'anthropic-beta': 'context-1m-2025-08-07',
|
|
69
|
+
},
|
|
69
70
|
});
|
|
70
71
|
break;
|
|
71
72
|
case 'xai':
|
|
@@ -77,14 +78,14 @@ class VercelProvider {
|
|
|
77
78
|
break;
|
|
78
79
|
case 'deepseek':
|
|
79
80
|
provider = (0, deepseek_1.createDeepSeek)({
|
|
80
|
-
apiKey: this.apiKey
|
|
81
|
+
apiKey: this.apiKey,
|
|
81
82
|
});
|
|
82
83
|
break;
|
|
83
84
|
case 'openrouter':
|
|
84
85
|
// PRD #194: OpenRouter custom endpoint support
|
|
85
86
|
// Use dedicated OpenRouter provider for proper format conversion
|
|
86
87
|
provider = (0, ai_sdk_provider_1.createOpenRouter)({
|
|
87
|
-
apiKey: this.apiKey
|
|
88
|
+
apiKey: this.apiKey,
|
|
88
89
|
});
|
|
89
90
|
break;
|
|
90
91
|
case 'custom':
|
|
@@ -95,7 +96,7 @@ class VercelProvider {
|
|
|
95
96
|
}
|
|
96
97
|
provider = (0, openai_1.createOpenAI)({
|
|
97
98
|
apiKey: this.apiKey,
|
|
98
|
-
baseURL: this.baseURL
|
|
99
|
+
baseURL: this.baseURL,
|
|
99
100
|
});
|
|
100
101
|
break;
|
|
101
102
|
default:
|
|
@@ -130,97 +131,115 @@ class VercelProvider {
|
|
|
130
131
|
// Return the actual debug file names created
|
|
131
132
|
return {
|
|
132
133
|
promptFile: `${debugId}_prompt.md`,
|
|
133
|
-
responseFile: `${debugId}_response.md
|
|
134
|
+
responseFile: `${debugId}_response.md`,
|
|
134
135
|
};
|
|
135
136
|
}
|
|
136
137
|
async sendMessage(message, operation = 'generic', evaluationContext) {
|
|
137
138
|
if (!this.isInitialized()) {
|
|
138
139
|
throw new Error(`${this.providerType} provider not initialized`);
|
|
139
140
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
(0, provider_debug_utils_1.debugLogInteraction)(debugId, message, response, operation, this.getProviderType(), this.model, this.debugMode);
|
|
160
|
-
// PRD #154: Always use new evaluation dataset system
|
|
161
|
-
const evaluationMetrics = {
|
|
162
|
-
// Core execution data
|
|
163
|
-
operation,
|
|
164
|
-
sdk: this.getProviderType(),
|
|
165
|
-
inputTokens: response.usage.input_tokens,
|
|
166
|
-
outputTokens: response.usage.output_tokens,
|
|
167
|
-
durationMs,
|
|
168
|
-
// Required fields
|
|
169
|
-
iterationCount: 1,
|
|
170
|
-
toolCallCount: 0,
|
|
171
|
-
status: 'completed',
|
|
172
|
-
completionReason: 'stop',
|
|
173
|
-
modelVersion: this.model,
|
|
174
|
-
// Required evaluation context - NO DEFAULTS, must be provided
|
|
175
|
-
test_scenario: operation,
|
|
176
|
-
ai_response_summary: response.content,
|
|
177
|
-
user_intent: evaluationContext?.user_intent || '',
|
|
178
|
-
interaction_id: evaluationContext?.interaction_id || '',
|
|
179
|
-
// Optional performance data
|
|
180
|
-
...(response.usage.cache_creation_input_tokens && { cacheCreationTokens: response.usage.cache_creation_input_tokens }),
|
|
181
|
-
...(response.usage.cache_read_input_tokens && { cacheReadTokens: response.usage.cache_read_input_tokens })
|
|
141
|
+
return await (0, ai_tracing_1.withAITracing)({
|
|
142
|
+
provider: this.providerType,
|
|
143
|
+
model: this.model,
|
|
144
|
+
operation: 'chat',
|
|
145
|
+
}, async () => {
|
|
146
|
+
const startTime = Date.now();
|
|
147
|
+
try {
|
|
148
|
+
// Use Vercel AI SDK generateText
|
|
149
|
+
// Note: maxOutputTokens not specified - provider will use model's natural maximum
|
|
150
|
+
const result = await (0, ai_1.generateText)({
|
|
151
|
+
model: this.modelInstance,
|
|
152
|
+
prompt: message,
|
|
153
|
+
});
|
|
154
|
+
const response = {
|
|
155
|
+
content: result.text,
|
|
156
|
+
usage: {
|
|
157
|
+
input_tokens: (result.totalUsage || result.usage).inputTokens || 0,
|
|
158
|
+
output_tokens: (result.totalUsage || result.usage).outputTokens || 0,
|
|
159
|
+
},
|
|
182
160
|
};
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
161
|
+
const durationMs = Date.now() - startTime;
|
|
162
|
+
// Debug log the interaction if enabled
|
|
163
|
+
if (this.debugMode) {
|
|
164
|
+
const debugId = (0, provider_debug_utils_1.generateDebugId)(operation);
|
|
165
|
+
(0, provider_debug_utils_1.debugLogInteraction)(debugId, message, response, operation, this.getProviderType(), this.model, this.debugMode);
|
|
166
|
+
// PRD #154: Always use new evaluation dataset system
|
|
167
|
+
const evaluationMetrics = {
|
|
168
|
+
// Core execution data
|
|
169
|
+
operation,
|
|
170
|
+
sdk: this.getProviderType(),
|
|
171
|
+
inputTokens: response.usage.input_tokens,
|
|
172
|
+
outputTokens: response.usage.output_tokens,
|
|
173
|
+
durationMs,
|
|
174
|
+
// Required fields
|
|
175
|
+
iterationCount: 1,
|
|
176
|
+
toolCallCount: 0,
|
|
177
|
+
status: 'completed',
|
|
178
|
+
completionReason: 'stop',
|
|
179
|
+
modelVersion: this.model,
|
|
180
|
+
// Required evaluation context - NO DEFAULTS, must be provided
|
|
181
|
+
test_scenario: operation,
|
|
182
|
+
ai_response_summary: response.content,
|
|
183
|
+
user_intent: evaluationContext?.user_intent || '',
|
|
184
|
+
interaction_id: evaluationContext?.interaction_id || '',
|
|
185
|
+
// Optional performance data
|
|
186
|
+
...(response.usage.cache_creation_input_tokens && {
|
|
187
|
+
cacheCreationTokens: response.usage.cache_creation_input_tokens,
|
|
188
|
+
}),
|
|
189
|
+
...(response.usage.cache_read_input_tokens && {
|
|
190
|
+
cacheReadTokens: response.usage.cache_read_input_tokens,
|
|
191
|
+
}),
|
|
192
|
+
};
|
|
193
|
+
// Calculate cache hit rate if applicable
|
|
194
|
+
if (response.usage.cache_read_input_tokens &&
|
|
195
|
+
response.usage.input_tokens > 0) {
|
|
196
|
+
evaluationMetrics.cacheHitRate = Math.round((response.usage.cache_read_input_tokens /
|
|
197
|
+
response.usage.input_tokens) *
|
|
198
|
+
100);
|
|
199
|
+
}
|
|
200
|
+
(0, provider_debug_utils_1.logEvaluationDataset)(evaluationMetrics, this.debugMode);
|
|
186
201
|
}
|
|
187
|
-
|
|
202
|
+
return response;
|
|
188
203
|
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
}
|
|
220
|
-
|
|
204
|
+
catch (error) {
|
|
205
|
+
// Log the prompt that caused the error for debugging
|
|
206
|
+
if (this.debugMode) {
|
|
207
|
+
const debugId = (0, provider_debug_utils_1.generateDebugId)(operation);
|
|
208
|
+
(0, provider_debug_utils_1.debugLogPromptOnly)(debugId, message, operation, this.getProviderType(), this.model, this.debugMode);
|
|
209
|
+
}
|
|
210
|
+
// Generate dataset for failed AI interaction
|
|
211
|
+
if (this.debugMode && evaluationContext) {
|
|
212
|
+
const failureMetrics = {
|
|
213
|
+
operation,
|
|
214
|
+
user_intent: evaluationContext.user_intent || '',
|
|
215
|
+
ai_response_summary: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
216
|
+
durationMs: Date.now() - startTime,
|
|
217
|
+
inputTokens: 0,
|
|
218
|
+
outputTokens: 0,
|
|
219
|
+
iterationCount: 0,
|
|
220
|
+
toolCallCount: 0,
|
|
221
|
+
status: 'failed',
|
|
222
|
+
completionReason: 'error',
|
|
223
|
+
sdk: this.getProviderType(),
|
|
224
|
+
modelVersion: this.model,
|
|
225
|
+
test_scenario: operation,
|
|
226
|
+
interaction_id: evaluationContext.interaction_id || (0, provider_debug_utils_1.generateDebugId)(operation),
|
|
227
|
+
failure_analysis: {
|
|
228
|
+
failure_type: 'error',
|
|
229
|
+
failure_reason: `${this.providerType} API error: ${error instanceof Error ? error.message : String(error)}`,
|
|
230
|
+
time_to_failure: Date.now() - startTime,
|
|
231
|
+
},
|
|
232
|
+
};
|
|
233
|
+
(0, provider_debug_utils_1.logEvaluationDataset)(failureMetrics, this.debugMode);
|
|
234
|
+
}
|
|
235
|
+
throw new Error(`${this.providerType} API error: ${error}`);
|
|
221
236
|
}
|
|
222
|
-
|
|
223
|
-
|
|
237
|
+
}, (response) => ({
|
|
238
|
+
inputTokens: response.usage.input_tokens,
|
|
239
|
+
outputTokens: response.usage.output_tokens,
|
|
240
|
+
cacheReadTokens: response.usage.cache_read_input_tokens,
|
|
241
|
+
cacheCreationTokens: response.usage.cache_creation_input_tokens,
|
|
242
|
+
}));
|
|
224
243
|
}
|
|
225
244
|
/**
|
|
226
245
|
* Agentic tool loop using Vercel AI SDK
|
|
@@ -239,285 +258,304 @@ class VercelProvider {
|
|
|
239
258
|
if (!this.isInitialized()) {
|
|
240
259
|
throw new Error(`${this.providerType} provider not initialized`);
|
|
241
260
|
}
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
const
|
|
249
|
-
const
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
261
|
+
return await (0, ai_tracing_1.withAITracing)({
|
|
262
|
+
provider: this.providerType,
|
|
263
|
+
model: this.model,
|
|
264
|
+
operation: 'tool_loop',
|
|
265
|
+
}, async () => {
|
|
266
|
+
const startTime = Date.now();
|
|
267
|
+
const maxIterations = config.maxIterations || 20;
|
|
268
|
+
const operation = config.operation || 'tool-loop';
|
|
269
|
+
// Convert AITool[] to Vercel AI SDK tool format
|
|
270
|
+
const tools = {};
|
|
271
|
+
for (let i = 0; i < config.tools.length; i++) {
|
|
272
|
+
const aiTool = config.tools[i];
|
|
273
|
+
const isLastTool = i === config.tools.length - 1;
|
|
274
|
+
const toolDef = (0, ai_1.tool)({
|
|
275
|
+
description: aiTool.description,
|
|
276
|
+
inputSchema: (0, ai_1.jsonSchema)(aiTool.inputSchema),
|
|
277
|
+
execute: async (input) => {
|
|
278
|
+
return await config.toolExecutor(aiTool.name, input);
|
|
279
|
+
},
|
|
280
|
+
});
|
|
281
|
+
// Add cache control ONLY to last tool for Anthropic (max 4 cache breakpoints)
|
|
282
|
+
// This caches the system prompt + all tools together
|
|
283
|
+
if ((this.providerType === 'anthropic' ||
|
|
284
|
+
this.providerType === 'anthropic_haiku') &&
|
|
285
|
+
isLastTool) {
|
|
286
|
+
toolDef.providerOptions = {
|
|
287
|
+
anthropic: {
|
|
288
|
+
cacheControl: { type: 'ephemeral' },
|
|
289
|
+
},
|
|
290
|
+
};
|
|
255
291
|
}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
};
|
|
292
|
+
// TODO: Check if Google Gemini supports caching in future SDK versions
|
|
293
|
+
// Google Gemini may have caching capabilities - research providerOptions.google syntax
|
|
294
|
+
// if (this.providerType === 'google' && isLastTool) {
|
|
295
|
+
// (toolDef as any).providerOptions = {
|
|
296
|
+
// google: { /* caching config if available */ }
|
|
297
|
+
// };
|
|
298
|
+
// }
|
|
299
|
+
tools[aiTool.name] = toolDef;
|
|
265
300
|
}
|
|
266
|
-
//
|
|
267
|
-
//
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
301
|
+
// Build messages array with system prompt caching for Anthropic
|
|
302
|
+
// Anthropic caching requires system messages in messages array with providerOptions
|
|
303
|
+
const messages = [];
|
|
304
|
+
let systemParam;
|
|
305
|
+
if (this.providerType === 'anthropic' ||
|
|
306
|
+
this.providerType === 'anthropic_haiku') {
|
|
307
|
+
// For Anthropic: Put system in messages array with cacheControl
|
|
308
|
+
messages.push({
|
|
309
|
+
role: 'system',
|
|
310
|
+
content: config.systemPrompt,
|
|
311
|
+
providerOptions: {
|
|
312
|
+
anthropic: {
|
|
313
|
+
cacheControl: { type: 'ephemeral' },
|
|
314
|
+
},
|
|
315
|
+
},
|
|
316
|
+
});
|
|
317
|
+
// Don't use system parameter for Anthropic when caching
|
|
318
|
+
systemParam = undefined;
|
|
319
|
+
}
|
|
320
|
+
else {
|
|
321
|
+
// For OpenAI/Google: Use system parameter (string)
|
|
322
|
+
systemParam = config.systemPrompt;
|
|
323
|
+
}
|
|
324
|
+
// Add user message
|
|
281
325
|
messages.push({
|
|
282
|
-
role: '
|
|
283
|
-
content: config.
|
|
284
|
-
providerOptions: {
|
|
285
|
-
anthropic: {
|
|
286
|
-
cacheControl: { type: 'ephemeral' }
|
|
287
|
-
}
|
|
288
|
-
}
|
|
326
|
+
role: 'user',
|
|
327
|
+
content: config.userMessage,
|
|
289
328
|
});
|
|
290
|
-
//
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
//
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
// Then add the conversation history if available
|
|
336
|
-
if (result.response?.messages) {
|
|
337
|
-
finalPrompt += result.response.messages
|
|
338
|
-
.map(msg => {
|
|
339
|
-
if (typeof msg.content === 'string') {
|
|
340
|
-
return `${msg.role}: ${msg.content}`;
|
|
341
|
-
}
|
|
342
|
-
else if (Array.isArray(msg.content)) {
|
|
343
|
-
const contentParts = msg.content.map(part => {
|
|
344
|
-
if (part.type === 'text') {
|
|
345
|
-
return part.text;
|
|
346
|
-
}
|
|
347
|
-
else if (part.type === 'tool-call') {
|
|
348
|
-
return `[TOOL_USE: ${part.toolName}]`;
|
|
349
|
-
}
|
|
350
|
-
else if (part.type === 'tool-result') {
|
|
351
|
-
const resultData = part.output || part.result || part.content;
|
|
352
|
-
if (typeof resultData === 'string') {
|
|
353
|
-
return `[TOOL_RESULT: ${part.toolName}]\n${resultData}`;
|
|
329
|
+
// TODO: Check if Google Gemini supports system prompt caching in future SDK versions
|
|
330
|
+
// if (this.providerType === 'google') {
|
|
331
|
+
// messages.unshift({
|
|
332
|
+
// role: 'system',
|
|
333
|
+
// content: config.systemPrompt,
|
|
334
|
+
// providerOptions: {
|
|
335
|
+
// google: { /* caching config if available */ }
|
|
336
|
+
// }
|
|
337
|
+
// });
|
|
338
|
+
// systemParam = undefined;
|
|
339
|
+
// }
|
|
340
|
+
try {
|
|
341
|
+
// Use Vercel AI SDK's generateText with stopWhen for automatic loop
|
|
342
|
+
// Default is stepCountIs(1) - we need to increase for multi-step investigation
|
|
343
|
+
// Note: maxOutputTokens not specified - provider will use model's natural maximum
|
|
344
|
+
const generateConfig = {
|
|
345
|
+
model: this.modelInstance,
|
|
346
|
+
messages,
|
|
347
|
+
tools,
|
|
348
|
+
stopWhen: (0, ai_1.stepCountIs)(maxIterations),
|
|
349
|
+
};
|
|
350
|
+
// Add system parameter for non-Anthropic providers
|
|
351
|
+
if (systemParam) {
|
|
352
|
+
generateConfig.system = systemParam;
|
|
353
|
+
}
|
|
354
|
+
const result = await (0, ai_1.generateText)(generateConfig);
|
|
355
|
+
// Log raw response immediately after generation (before any processing)
|
|
356
|
+
let debugFiles = null;
|
|
357
|
+
if (this.debugMode) {
|
|
358
|
+
// Build the full conversation context like Anthropic provider does
|
|
359
|
+
let finalPrompt = `System: ${config.systemPrompt}\n\n`;
|
|
360
|
+
// Always include the original user intent first
|
|
361
|
+
finalPrompt += `user: ${config.userMessage}\n\n`;
|
|
362
|
+
// Then add the conversation history if available
|
|
363
|
+
if (result.response?.messages) {
|
|
364
|
+
finalPrompt += result.response.messages
|
|
365
|
+
.map(msg => {
|
|
366
|
+
if (typeof msg.content === 'string') {
|
|
367
|
+
return `${msg.role}: ${msg.content}`;
|
|
368
|
+
}
|
|
369
|
+
else if (Array.isArray(msg.content)) {
|
|
370
|
+
const contentParts = msg.content
|
|
371
|
+
.map(part => {
|
|
372
|
+
if (part.type === 'text') {
|
|
373
|
+
return part.text;
|
|
354
374
|
}
|
|
355
|
-
else if (
|
|
356
|
-
return `[
|
|
375
|
+
else if (part.type === 'tool-call') {
|
|
376
|
+
return `[TOOL_USE: ${part.toolName}]`;
|
|
357
377
|
}
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
378
|
+
else if (part.type === 'tool-result') {
|
|
379
|
+
const resultData = part.output ||
|
|
380
|
+
part.result ||
|
|
381
|
+
part.content;
|
|
382
|
+
if (typeof resultData === 'string') {
|
|
383
|
+
return `[TOOL_RESULT: ${part.toolName}]\n${resultData}`;
|
|
384
|
+
}
|
|
385
|
+
else if (resultData) {
|
|
386
|
+
return `[TOOL_RESULT: ${part.toolName}]\n${JSON.stringify(resultData, null, 2)}`;
|
|
387
|
+
}
|
|
388
|
+
return `[TOOL_RESULT: ${part.toolName}]`;
|
|
389
|
+
}
|
|
390
|
+
return `[${part.type}]`;
|
|
391
|
+
})
|
|
392
|
+
.join(' ');
|
|
393
|
+
return `${msg.role}: ${contentParts}`;
|
|
394
|
+
}
|
|
395
|
+
return `${msg.role}: [complex_content]`;
|
|
396
|
+
})
|
|
397
|
+
.join('\n\n');
|
|
398
|
+
}
|
|
399
|
+
// Create raw response content that includes ALL data from result
|
|
400
|
+
let rawResponseContent = `# RAW RESPONSE DATA\n\n`;
|
|
401
|
+
rawResponseContent += `**result.text**: ${result.text || '[EMPTY]'}\n\n`;
|
|
402
|
+
if (result.steps && result.steps.length > 0) {
|
|
403
|
+
rawResponseContent += `**Steps (${result.steps.length})**:\n`;
|
|
404
|
+
result.steps.forEach((step, i) => {
|
|
405
|
+
rawResponseContent += `\nStep ${i + 1}:\n`;
|
|
406
|
+
rawResponseContent += `- text: ${step.text || '[EMPTY]'}\n`;
|
|
407
|
+
if (step.toolCalls) {
|
|
408
|
+
rawResponseContent += `- toolCalls: ${step.toolCalls.length}\n`;
|
|
409
|
+
}
|
|
410
|
+
if (step.toolResults) {
|
|
411
|
+
rawResponseContent += `- toolResults: ${step.toolResults.length}\n`;
|
|
412
|
+
}
|
|
413
|
+
});
|
|
414
|
+
rawResponseContent += '\n';
|
|
415
|
+
}
|
|
416
|
+
// Add the last step's text for easy access
|
|
417
|
+
let lastStepText = '';
|
|
418
|
+
if (result.steps && result.steps.length > 0) {
|
|
419
|
+
for (let i = result.steps.length - 1; i >= 0; i--) {
|
|
420
|
+
if (result.steps[i].text && result.steps[i].text.trim()) {
|
|
421
|
+
lastStepText = result.steps[i].text;
|
|
422
|
+
break;
|
|
423
|
+
}
|
|
381
424
|
}
|
|
382
|
-
}
|
|
383
|
-
rawResponseContent += '\n
|
|
425
|
+
}
|
|
426
|
+
rawResponseContent += `**Last step with text**: ${lastStepText || '[NONE]'}\n\n`;
|
|
427
|
+
const usage = result.totalUsage || result.usage;
|
|
428
|
+
const rawAiResponse = {
|
|
429
|
+
content: rawResponseContent,
|
|
430
|
+
usage: {
|
|
431
|
+
input_tokens: usage.inputTokens || 0,
|
|
432
|
+
output_tokens: usage.outputTokens || 0,
|
|
433
|
+
cache_creation_input_tokens: 0,
|
|
434
|
+
cache_read_input_tokens: 0,
|
|
435
|
+
},
|
|
436
|
+
};
|
|
437
|
+
debugFiles = this.logDebugIfEnabled(`${operation}-raw`, finalPrompt, rawAiResponse);
|
|
384
438
|
}
|
|
385
|
-
//
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
for (
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
439
|
+
// Extract tool call history from steps
|
|
440
|
+
const toolCallsExecuted = [];
|
|
441
|
+
for (const step of result.steps || []) {
|
|
442
|
+
for (const toolCall of step.toolCalls || []) {
|
|
443
|
+
const toolResult = step.toolResults?.find((tr) => tr.toolCallId === toolCall.toolCallId);
|
|
444
|
+
toolCallsExecuted.push({
|
|
445
|
+
tool: toolCall.toolName,
|
|
446
|
+
input: toolCall.args,
|
|
447
|
+
output: toolResult?.result,
|
|
448
|
+
});
|
|
393
449
|
}
|
|
394
450
|
}
|
|
395
|
-
|
|
451
|
+
// Normalize token metrics across providers
|
|
452
|
+
// NOTE: Vercel AI SDK had token reporting bugs that were fixed in PR #8945 (merged Sept 26, 2025)
|
|
453
|
+
// - GitHub Issue #8349: cache tokens only reflected last step, not summed across all steps
|
|
454
|
+
// - GitHub Issue #8795: Token reporting issues with Anthropic provider (streaming)
|
|
455
|
+
// Our version (5.0.60, released Oct 2, 2025) includes these fixes.
|
|
456
|
+
// However, testing still shows ~70% fewer tokens reported vs Anthropic native SDK.
|
|
457
|
+
// Root cause: We were using result.usage (final step only) instead of result.totalUsage (sum of all steps)!
|
|
396
458
|
const usage = result.totalUsage || result.usage;
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
cache_creation_input_tokens: 0,
|
|
403
|
-
cache_read_input_tokens: 0
|
|
404
|
-
}
|
|
405
|
-
};
|
|
406
|
-
debugFiles = this.logDebugIfEnabled(`${operation}-raw`, finalPrompt, rawAiResponse);
|
|
407
|
-
}
|
|
408
|
-
// Extract tool call history from steps
|
|
409
|
-
const toolCallsExecuted = [];
|
|
410
|
-
for (const step of result.steps || []) {
|
|
411
|
-
for (const toolCall of step.toolCalls || []) {
|
|
412
|
-
const toolResult = step.toolResults?.find((tr) => tr.toolCallId === toolCall.toolCallId);
|
|
413
|
-
toolCallsExecuted.push({
|
|
414
|
-
tool: toolCall.toolName,
|
|
415
|
-
input: toolCall.args,
|
|
416
|
-
output: toolResult?.result
|
|
417
|
-
});
|
|
459
|
+
let cacheReadTokens = 0;
|
|
460
|
+
let cacheCreationTokens = 0;
|
|
461
|
+
// Anthropic via Vercel uses cachedInputTokens (confirmed in AI SDK 5+)
|
|
462
|
+
if (usage.cachedInputTokens) {
|
|
463
|
+
cacheReadTokens = usage.cachedInputTokens;
|
|
418
464
|
}
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
// TODO: Check if Google Gemini reports cache metrics in future SDK versions
|
|
446
|
-
// Google Gemini may return cache-related metrics - check usage object structure
|
|
447
|
-
// Possible fields: cachedTokens, cacheHits, or provider-specific naming
|
|
448
|
-
// Add normalization logic here when Gemini caching is confirmed
|
|
449
|
-
// Extract final text from the last step (result.text might be empty if last step had tool calls)
|
|
450
|
-
let finalText = result.text;
|
|
451
|
-
if (!finalText || finalText.trim().length === 0) {
|
|
452
|
-
// If result.text is empty, find the last text response from steps
|
|
453
|
-
for (let i = (result.steps || []).length - 1; i >= 0; i--) {
|
|
454
|
-
const step = result.steps[i];
|
|
455
|
-
if (step.text && step.text.trim().length > 0) {
|
|
456
|
-
finalText = step.text;
|
|
457
|
-
break;
|
|
465
|
+
// OpenAI uses cached_tokens or cachedTokens (automatic caching, no config needed)
|
|
466
|
+
if ('cachedTokens' in usage || usage.cached_tokens) {
|
|
467
|
+
cacheReadTokens =
|
|
468
|
+
usage.cachedTokens || usage.cached_tokens || 0;
|
|
469
|
+
}
|
|
470
|
+
// Anthropic native SDK uses separate cache_creation and cache_read fields
|
|
471
|
+
if (usage.cache_creation_input_tokens) {
|
|
472
|
+
cacheCreationTokens = usage.cache_creation_input_tokens;
|
|
473
|
+
}
|
|
474
|
+
if (usage.cache_read_input_tokens) {
|
|
475
|
+
cacheReadTokens = usage.cache_read_input_tokens;
|
|
476
|
+
}
|
|
477
|
+
// TODO: Check if Google Gemini reports cache metrics in future SDK versions
|
|
478
|
+
// Google Gemini may return cache-related metrics - check usage object structure
|
|
479
|
+
// Possible fields: cachedTokens, cacheHits, or provider-specific naming
|
|
480
|
+
// Add normalization logic here when Gemini caching is confirmed
|
|
481
|
+
// Extract final text from the last step (result.text might be empty if last step had tool calls)
|
|
482
|
+
let finalText = result.text;
|
|
483
|
+
if (!finalText || finalText.trim().length === 0) {
|
|
484
|
+
// If result.text is empty, find the last text response from steps
|
|
485
|
+
for (let i = (result.steps || []).length - 1; i >= 0; i--) {
|
|
486
|
+
const step = result.steps[i];
|
|
487
|
+
if (step.text && step.text.trim().length > 0) {
|
|
488
|
+
finalText = step.text;
|
|
489
|
+
break;
|
|
490
|
+
}
|
|
458
491
|
}
|
|
459
492
|
}
|
|
493
|
+
// Log processed summary response (keep existing functionality)
|
|
494
|
+
if (this.debugMode && debugFiles === null) {
|
|
495
|
+
// Only log summary if we haven't already logged raw response
|
|
496
|
+
let finalPrompt = `System: ${config.systemPrompt}\n\nuser: ${config.userMessage}`;
|
|
497
|
+
const aiResponse = {
|
|
498
|
+
content: finalText || '',
|
|
499
|
+
usage: {
|
|
500
|
+
input_tokens: usage.inputTokens || 0,
|
|
501
|
+
output_tokens: usage.outputTokens || 0,
|
|
502
|
+
cache_creation_input_tokens: cacheCreationTokens,
|
|
503
|
+
cache_read_input_tokens: cacheReadTokens,
|
|
504
|
+
},
|
|
505
|
+
};
|
|
506
|
+
debugFiles = this.logDebugIfEnabled(`${operation}-summary`, finalPrompt, aiResponse);
|
|
507
|
+
}
|
|
508
|
+
return (0, provider_debug_utils_1.createAndLogAgenticResult)({
|
|
509
|
+
finalMessage: finalText || '',
|
|
510
|
+
iterations: result.steps?.length || 1,
|
|
511
|
+
toolCallsExecuted,
|
|
512
|
+
totalTokens: {
|
|
513
|
+
input: usage.inputTokens || 0,
|
|
514
|
+
output: usage.outputTokens || 0,
|
|
515
|
+
cacheCreation: cacheCreationTokens,
|
|
516
|
+
cacheRead: cacheReadTokens,
|
|
517
|
+
},
|
|
518
|
+
status: 'success',
|
|
519
|
+
completionReason: 'investigation_complete',
|
|
520
|
+
modelVersion: this.model,
|
|
521
|
+
operation: `${operation}-summary`,
|
|
522
|
+
sdk: this.getProviderType(),
|
|
523
|
+
startTime,
|
|
524
|
+
debugMode: this.debugMode,
|
|
525
|
+
debugFiles,
|
|
526
|
+
evaluationContext: config.evaluationContext,
|
|
527
|
+
interaction_id: config.interaction_id,
|
|
528
|
+
});
|
|
460
529
|
}
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
|
|
530
|
+
catch (error) {
|
|
531
|
+
// Return error result with extended metrics
|
|
532
|
+
return (0, provider_debug_utils_1.createAndLogAgenticResult)({
|
|
533
|
+
finalMessage: `Error during investigation: ${error instanceof Error ? error.message : String(error)}`,
|
|
534
|
+
iterations: 0,
|
|
535
|
+
toolCallsExecuted: [],
|
|
536
|
+
totalTokens: {
|
|
537
|
+
input: 0,
|
|
538
|
+
output: 0,
|
|
539
|
+
cacheCreation: 0,
|
|
540
|
+
cacheRead: 0,
|
|
541
|
+
},
|
|
542
|
+
status: 'failed',
|
|
543
|
+
completionReason: 'error',
|
|
544
|
+
modelVersion: this.model,
|
|
545
|
+
operation: `${operation}-error`,
|
|
546
|
+
sdk: this.getProviderType(),
|
|
547
|
+
startTime,
|
|
548
|
+
debugMode: this.debugMode,
|
|
549
|
+
evaluationContext: config.evaluationContext,
|
|
550
|
+
interaction_id: config.interaction_id,
|
|
551
|
+
});
|
|
475
552
|
}
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
output: usage.outputTokens || 0,
|
|
483
|
-
cacheCreation: cacheCreationTokens,
|
|
484
|
-
cacheRead: cacheReadTokens
|
|
485
|
-
},
|
|
486
|
-
status: 'success',
|
|
487
|
-
completionReason: 'investigation_complete',
|
|
488
|
-
modelVersion: this.model,
|
|
489
|
-
operation: `${operation}-summary`,
|
|
490
|
-
sdk: this.getProviderType(),
|
|
491
|
-
startTime,
|
|
492
|
-
debugMode: this.debugMode,
|
|
493
|
-
debugFiles,
|
|
494
|
-
evaluationContext: config.evaluationContext,
|
|
495
|
-
interaction_id: config.interaction_id
|
|
496
|
-
});
|
|
497
|
-
}
|
|
498
|
-
catch (error) {
|
|
499
|
-
// Return error result with extended metrics
|
|
500
|
-
return (0, provider_debug_utils_1.createAndLogAgenticResult)({
|
|
501
|
-
finalMessage: `Error during investigation: ${error instanceof Error ? error.message : String(error)}`,
|
|
502
|
-
iterations: 0,
|
|
503
|
-
toolCallsExecuted: [],
|
|
504
|
-
totalTokens: {
|
|
505
|
-
input: 0,
|
|
506
|
-
output: 0,
|
|
507
|
-
cacheCreation: 0,
|
|
508
|
-
cacheRead: 0
|
|
509
|
-
},
|
|
510
|
-
status: 'failed',
|
|
511
|
-
completionReason: 'error',
|
|
512
|
-
modelVersion: this.model,
|
|
513
|
-
operation: `${operation}-error`,
|
|
514
|
-
sdk: this.getProviderType(),
|
|
515
|
-
startTime,
|
|
516
|
-
debugMode: this.debugMode,
|
|
517
|
-
evaluationContext: config.evaluationContext,
|
|
518
|
-
interaction_id: config.interaction_id
|
|
519
|
-
});
|
|
520
|
-
}
|
|
553
|
+
}, (result) => ({
|
|
554
|
+
inputTokens: result.totalTokens.input,
|
|
555
|
+
outputTokens: result.totalTokens.output,
|
|
556
|
+
cacheReadTokens: result.totalTokens.cacheRead,
|
|
557
|
+
cacheCreationTokens: result.totalTokens.cacheCreation,
|
|
558
|
+
}));
|
|
521
559
|
}
|
|
522
560
|
}
|
|
523
561
|
exports.VercelProvider = VercelProvider;
|