@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.
- package/assets/project-setup/templates/.github/CODEOWNERS.hbs +25 -0
- package/assets/project-setup/templates/.github/FUNDING.yml.hbs +35 -0
- package/assets/project-setup/templates/.github/ISSUE_TEMPLATE/bug_report.yml.hbs +175 -0
- package/assets/project-setup/templates/.github/ISSUE_TEMPLATE/config.yml.hbs +32 -0
- package/assets/project-setup/templates/.github/ISSUE_TEMPLATE/feature_request.yml.hbs +134 -0
- package/assets/project-setup/templates/.github/PULL_REQUEST_TEMPLATE.md.hbs +172 -0
- package/assets/project-setup/templates/.github/labeler.yml.hbs +123 -0
- package/assets/project-setup/templates/.github/release.yml.hbs +51 -0
- package/assets/project-setup/templates/.github/workflows/labeler.yml.hbs +21 -0
- package/assets/project-setup/templates/.github/workflows/scorecard.yml.hbs +71 -0
- package/assets/project-setup/templates/.github/workflows/stale.yml.hbs +57 -0
- package/dist/core/ai-provider-factory.d.ts.map +1 -1
- package/dist/core/ai-provider-factory.js +22 -5
- package/dist/core/ai-provider.interface.d.ts +16 -0
- package/dist/core/ai-provider.interface.d.ts.map +1 -1
- 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 -456
- package/dist/core/discovery.d.ts +6 -0
- package/dist/core/discovery.d.ts.map +1 -1
- package/dist/core/discovery.js +39 -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 -246
- 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/model-config.d.ts +2 -0
- package/dist/core/model-config.d.ts.map +1 -1
- package/dist/core/model-config.js +3 -1
- package/dist/core/providers/anthropic-provider.d.ts.map +1 -1
- package/dist/core/providers/anthropic-provider.js +352 -282
- package/dist/core/providers/provider-debug-utils.d.ts +4 -0
- package/dist/core/providers/provider-debug-utils.d.ts.map +1 -1
- package/dist/core/providers/provider-debug-utils.js +25 -3
- package/dist/core/providers/vercel-provider.d.ts +1 -0
- package/dist/core/providers/vercel-provider.d.ts.map +1 -1
- package/dist/core/providers/vercel-provider.js +412 -345
- 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/unified-creation-session.js +1 -1
- 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 +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 ??
|
|
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)({
|
|
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)({
|
|
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
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
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
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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
|
-
|
|
202
|
+
return response;
|
|
164
203
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
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
|
-
|
|
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
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
const
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
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
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
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
|
-
//
|
|
238
|
-
//
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
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: '
|
|
254
|
-
content: config.
|
|
255
|
-
providerOptions: {
|
|
256
|
-
anthropic: {
|
|
257
|
-
cacheControl: { type: 'ephemeral' }
|
|
258
|
-
}
|
|
259
|
-
}
|
|
326
|
+
role: 'user',
|
|
327
|
+
content: config.userMessage,
|
|
260
328
|
});
|
|
261
|
-
//
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
//
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
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 (
|
|
327
|
-
return `[
|
|
375
|
+
else if (part.type === 'tool-call') {
|
|
376
|
+
return `[TOOL_USE: ${part.toolName}]`;
|
|
328
377
|
}
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
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
|
-
//
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
for (
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
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
|
-
|
|
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
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
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
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
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
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
|
|
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
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
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;
|