@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.
Files changed (55) hide show
  1. package/dist/core/capability-scan-workflow.d.ts +4 -9
  2. package/dist/core/capability-scan-workflow.d.ts.map +1 -1
  3. package/dist/core/capability-scan-workflow.js +203 -455
  4. package/dist/core/discovery.d.ts.map +1 -1
  5. package/dist/core/discovery.js +4 -3
  6. package/dist/core/embedding-service.d.ts +9 -36
  7. package/dist/core/embedding-service.d.ts.map +1 -1
  8. package/dist/core/embedding-service.js +137 -253
  9. package/dist/core/index.d.ts +1 -1
  10. package/dist/core/index.js +2 -2
  11. package/dist/core/kubernetes-utils.d.ts +1 -0
  12. package/dist/core/kubernetes-utils.d.ts.map +1 -1
  13. package/dist/core/kubernetes-utils.js +53 -48
  14. package/dist/core/providers/anthropic-provider.d.ts.map +1 -1
  15. package/dist/core/providers/anthropic-provider.js +352 -282
  16. package/dist/core/providers/vercel-provider.d.ts.map +1 -1
  17. package/dist/core/providers/vercel-provider.js +389 -351
  18. package/dist/core/tracing/ai-tracing.d.ts +80 -0
  19. package/dist/core/tracing/ai-tracing.d.ts.map +1 -0
  20. package/dist/core/tracing/ai-tracing.js +122 -0
  21. package/dist/core/tracing/config.d.ts +15 -0
  22. package/dist/core/tracing/config.d.ts.map +1 -0
  23. package/dist/core/tracing/config.js +133 -0
  24. package/dist/core/tracing/http-tracing.d.ts +28 -0
  25. package/dist/core/tracing/http-tracing.d.ts.map +1 -0
  26. package/dist/core/tracing/http-tracing.js +119 -0
  27. package/dist/core/tracing/index.d.ts +14 -0
  28. package/dist/core/tracing/index.d.ts.map +1 -0
  29. package/dist/core/tracing/index.js +40 -0
  30. package/dist/core/tracing/k8s-tracing.d.ts +57 -0
  31. package/dist/core/tracing/k8s-tracing.d.ts.map +1 -0
  32. package/dist/core/tracing/k8s-tracing.js +155 -0
  33. package/dist/core/tracing/qdrant-tracing.d.ts +68 -0
  34. package/dist/core/tracing/qdrant-tracing.d.ts.map +1 -0
  35. package/dist/core/tracing/qdrant-tracing.js +102 -0
  36. package/dist/core/tracing/tool-tracing.d.ts +31 -0
  37. package/dist/core/tracing/tool-tracing.d.ts.map +1 -0
  38. package/dist/core/tracing/tool-tracing.js +76 -0
  39. package/dist/core/tracing/tracer.d.ts +21 -0
  40. package/dist/core/tracing/tracer.d.ts.map +1 -0
  41. package/dist/core/tracing/tracer.js +215 -0
  42. package/dist/core/tracing/types.d.ts +86 -0
  43. package/dist/core/tracing/types.d.ts.map +1 -0
  44. package/dist/core/tracing/types.js +41 -0
  45. package/dist/core/vector-db-service.d.ts.map +1 -1
  46. package/dist/core/vector-db-service.js +238 -163
  47. package/dist/interfaces/mcp.d.ts.map +1 -1
  48. package/dist/interfaces/mcp.js +71 -43
  49. package/dist/mcp/server.js +12 -2
  50. package/dist/tools/organizational-data.d.ts.map +1 -1
  51. package/dist/tools/organizational-data.js +2 -4
  52. package/dist/tools/version.d.ts +12 -1
  53. package/dist/tools/version.d.ts.map +1 -1
  54. package/dist/tools/version.js +24 -4
  55. 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 ?? (process.env.DEBUG_DOT_AI === 'true');
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
- const startTime = Date.now();
141
- try {
142
- // Use Vercel AI SDK generateText
143
- // Note: maxOutputTokens not specified - provider will use model's natural maximum
144
- const result = await (0, ai_1.generateText)({
145
- model: this.modelInstance,
146
- prompt: message,
147
- });
148
- const response = {
149
- content: result.text,
150
- usage: {
151
- input_tokens: (result.totalUsage || result.usage).inputTokens || 0,
152
- output_tokens: (result.totalUsage || result.usage).outputTokens || 0
153
- }
154
- };
155
- const durationMs = Date.now() - startTime;
156
- // Debug log the interaction if enabled
157
- if (this.debugMode) {
158
- const debugId = (0, provider_debug_utils_1.generateDebugId)(operation);
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
- // Calculate cache hit rate if applicable
184
- if (response.usage.cache_read_input_tokens && response.usage.input_tokens > 0) {
185
- evaluationMetrics.cacheHitRate = Math.round((response.usage.cache_read_input_tokens / response.usage.input_tokens) * 100);
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
- (0, provider_debug_utils_1.logEvaluationDataset)(evaluationMetrics, this.debugMode);
202
+ return response;
188
203
  }
189
- return response;
190
- }
191
- catch (error) {
192
- // Log the prompt that caused the error for debugging
193
- if (this.debugMode) {
194
- const debugId = (0, provider_debug_utils_1.generateDebugId)(operation);
195
- (0, provider_debug_utils_1.debugLogPromptOnly)(debugId, message, operation, this.getProviderType(), this.model, this.debugMode);
196
- }
197
- // Generate dataset for failed AI interaction
198
- if (this.debugMode && evaluationContext) {
199
- const failureMetrics = {
200
- operation,
201
- user_intent: evaluationContext.user_intent || '',
202
- ai_response_summary: `Error: ${error instanceof Error ? error.message : String(error)}`,
203
- durationMs: Date.now() - startTime,
204
- inputTokens: 0,
205
- outputTokens: 0,
206
- iterationCount: 0,
207
- toolCallCount: 0,
208
- status: 'failed',
209
- completionReason: 'error',
210
- sdk: this.getProviderType(),
211
- modelVersion: this.model,
212
- test_scenario: operation,
213
- interaction_id: evaluationContext.interaction_id || (0, provider_debug_utils_1.generateDebugId)(operation),
214
- failure_analysis: {
215
- failure_type: "error",
216
- failure_reason: `${this.providerType} API error: ${error instanceof Error ? error.message : String(error)}`,
217
- time_to_failure: Date.now() - startTime
218
- }
219
- };
220
- (0, provider_debug_utils_1.logEvaluationDataset)(failureMetrics, this.debugMode);
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
- throw new Error(`${this.providerType} API error: ${error}`);
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
- const startTime = Date.now();
243
- const maxIterations = config.maxIterations || 20;
244
- const operation = config.operation || 'tool-loop';
245
- // Convert AITool[] to Vercel AI SDK tool format
246
- const tools = {};
247
- for (let i = 0; i < config.tools.length; i++) {
248
- const aiTool = config.tools[i];
249
- const isLastTool = i === config.tools.length - 1;
250
- const toolDef = (0, ai_1.tool)({
251
- description: aiTool.description,
252
- inputSchema: (0, ai_1.jsonSchema)(aiTool.inputSchema),
253
- execute: async (input) => {
254
- return await config.toolExecutor(aiTool.name, input);
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
- // Add cache control ONLY to last tool for Anthropic (max 4 cache breakpoints)
258
- // This caches the system prompt + all tools together
259
- if ((this.providerType === 'anthropic' || this.providerType === 'anthropic_haiku') && isLastTool) {
260
- toolDef.providerOptions = {
261
- anthropic: {
262
- cacheControl: { type: 'ephemeral' }
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
- // TODO: Check if Google Gemini supports caching in future SDK versions
267
- // Google Gemini may have caching capabilities - research providerOptions.google syntax
268
- // if (this.providerType === 'google' && isLastTool) {
269
- // (toolDef as any).providerOptions = {
270
- // google: { /* caching config if available */ }
271
- // };
272
- // }
273
- tools[aiTool.name] = toolDef;
274
- }
275
- // Build messages array with system prompt caching for Anthropic
276
- // Anthropic caching requires system messages in messages array with providerOptions
277
- const messages = [];
278
- let systemParam;
279
- if (this.providerType === 'anthropic' || this.providerType === 'anthropic_haiku') {
280
- // For Anthropic: Put system in messages array with cacheControl
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: 'system',
283
- content: config.systemPrompt,
284
- providerOptions: {
285
- anthropic: {
286
- cacheControl: { type: 'ephemeral' }
287
- }
288
- }
326
+ role: 'user',
327
+ content: config.userMessage,
289
328
  });
290
- // Don't use system parameter for Anthropic when caching
291
- systemParam = undefined;
292
- }
293
- else {
294
- // For OpenAI/Google: Use system parameter (string)
295
- systemParam = config.systemPrompt;
296
- }
297
- // Add user message
298
- messages.push({
299
- role: 'user',
300
- content: config.userMessage
301
- });
302
- // TODO: Check if Google Gemini supports system prompt caching in future SDK versions
303
- // if (this.providerType === 'google') {
304
- // messages.unshift({
305
- // role: 'system',
306
- // content: config.systemPrompt,
307
- // providerOptions: {
308
- // google: { /* caching config if available */ }
309
- // }
310
- // });
311
- // systemParam = undefined;
312
- // }
313
- try {
314
- // Use Vercel AI SDK's generateText with stopWhen for automatic loop
315
- // Default is stepCountIs(1) - we need to increase for multi-step investigation
316
- // Note: maxOutputTokens not specified - provider will use model's natural maximum
317
- const generateConfig = {
318
- model: this.modelInstance,
319
- messages,
320
- tools,
321
- stopWhen: (0, ai_1.stepCountIs)(maxIterations)
322
- };
323
- // Add system parameter for non-Anthropic providers
324
- if (systemParam) {
325
- generateConfig.system = systemParam;
326
- }
327
- const result = await (0, ai_1.generateText)(generateConfig);
328
- // Log raw response immediately after generation (before any processing)
329
- let debugFiles = null;
330
- if (this.debugMode) {
331
- // Build the full conversation context like Anthropic provider does
332
- let finalPrompt = `System: ${config.systemPrompt}\n\n`;
333
- // Always include the original user intent first
334
- finalPrompt += `user: ${config.userMessage}\n\n`;
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 (resultData) {
356
- return `[TOOL_RESULT: ${part.toolName}]\n${JSON.stringify(resultData, null, 2)}`;
375
+ else if (part.type === 'tool-call') {
376
+ return `[TOOL_USE: ${part.toolName}]`;
357
377
  }
358
- return `[TOOL_RESULT: ${part.toolName}]`;
359
- }
360
- return `[${part.type}]`;
361
- }).join(' ');
362
- return `${msg.role}: ${contentParts}`;
363
- }
364
- return `${msg.role}: [complex_content]`;
365
- })
366
- .join('\n\n');
367
- }
368
- // Create raw response content that includes ALL data from result
369
- let rawResponseContent = `# RAW RESPONSE DATA\n\n`;
370
- rawResponseContent += `**result.text**: ${result.text || '[EMPTY]'}\n\n`;
371
- if (result.steps && result.steps.length > 0) {
372
- rawResponseContent += `**Steps (${result.steps.length})**:\n`;
373
- result.steps.forEach((step, i) => {
374
- rawResponseContent += `\nStep ${i + 1}:\n`;
375
- rawResponseContent += `- text: ${step.text || '[EMPTY]'}\n`;
376
- if (step.toolCalls) {
377
- rawResponseContent += `- toolCalls: ${step.toolCalls.length}\n`;
378
- }
379
- if (step.toolResults) {
380
- rawResponseContent += `- toolResults: ${step.toolResults.length}\n`;
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
- // Add the last step's text for easy access
386
- let lastStepText = '';
387
- if (result.steps && result.steps.length > 0) {
388
- for (let i = result.steps.length - 1; i >= 0; i--) {
389
- if (result.steps[i].text && result.steps[i].text.trim()) {
390
- lastStepText = result.steps[i].text;
391
- break;
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
- rawResponseContent += `**Last step with text**: ${lastStepText || '[NONE]'}\n\n`;
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
- const rawAiResponse = {
398
- content: rawResponseContent,
399
- usage: {
400
- input_tokens: usage.inputTokens || 0,
401
- output_tokens: usage.outputTokens || 0,
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
- // Normalize token metrics across providers
421
- // NOTE: Vercel AI SDK had token reporting bugs that were fixed in PR #8945 (merged Sept 26, 2025)
422
- // - GitHub Issue #8349: cache tokens only reflected last step, not summed across all steps
423
- // - GitHub Issue #8795: Token reporting issues with Anthropic provider (streaming)
424
- // Our version (5.0.60, released Oct 2, 2025) includes these fixes.
425
- // However, testing still shows ~70% fewer tokens reported vs Anthropic native SDK.
426
- // Root cause: We were using result.usage (final step only) instead of result.totalUsage (sum of all steps)!
427
- const usage = result.totalUsage || result.usage;
428
- let cacheReadTokens = 0;
429
- let cacheCreationTokens = 0;
430
- // Anthropic via Vercel uses cachedInputTokens (confirmed in AI SDK 5+)
431
- if (usage.cachedInputTokens) {
432
- cacheReadTokens = usage.cachedInputTokens;
433
- }
434
- // OpenAI uses cached_tokens or cachedTokens (automatic caching, no config needed)
435
- if ('cachedTokens' in usage || usage.cached_tokens) {
436
- cacheReadTokens = usage.cachedTokens || usage.cached_tokens || 0;
437
- }
438
- // Anthropic native SDK uses separate cache_creation and cache_read fields
439
- if (usage.cache_creation_input_tokens) {
440
- cacheCreationTokens = usage.cache_creation_input_tokens;
441
- }
442
- if (usage.cache_read_input_tokens) {
443
- cacheReadTokens = usage.cache_read_input_tokens;
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
- // Log processed summary response (keep existing functionality)
462
- if (this.debugMode && debugFiles === null) {
463
- // Only log summary if we haven't already logged raw response
464
- let finalPrompt = `System: ${config.systemPrompt}\n\nuser: ${config.userMessage}`;
465
- const aiResponse = {
466
- content: finalText || '',
467
- usage: {
468
- input_tokens: usage.inputTokens || 0,
469
- output_tokens: usage.outputTokens || 0,
470
- cache_creation_input_tokens: cacheCreationTokens,
471
- cache_read_input_tokens: cacheReadTokens
472
- }
473
- };
474
- debugFiles = this.logDebugIfEnabled(`${operation}-summary`, finalPrompt, aiResponse);
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
- return (0, provider_debug_utils_1.createAndLogAgenticResult)({
477
- finalMessage: finalText || '',
478
- iterations: result.steps?.length || 1,
479
- toolCallsExecuted,
480
- totalTokens: {
481
- input: usage.inputTokens || 0,
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;