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