@vfarcic/dot-ai 0.108.0 → 0.110.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.
@@ -4,7 +4,7 @@
4
4
  * Implements AIProvider interface using Anthropic SDK directly.
5
5
  * Supports streaming for long operations and debug logging.
6
6
  */
7
- import { AIProvider, AIResponse, AIProviderConfig, AITool, ToolExecutor, ToolLoopConfig, AgenticResult } from '../ai-provider.interface';
7
+ import { AIProvider, AIResponse, AIProviderConfig, ToolLoopConfig, AgenticResult } from '../ai-provider.interface';
8
8
  export declare class AnthropicProvider implements AIProvider {
9
9
  private client;
10
10
  private apiKey;
@@ -15,6 +15,10 @@ export declare class AnthropicProvider implements AIProvider {
15
15
  getProviderType(): string;
16
16
  getDefaultModel(): string;
17
17
  isInitialized(): boolean;
18
+ /**
19
+ * Helper method to log debug information if debug mode is enabled
20
+ */
21
+ private logDebugIfEnabled;
18
22
  sendMessage(message: string, operation?: string): Promise<AIResponse>;
19
23
  /**
20
24
  * Agentic tool loop implementation
@@ -37,8 +41,5 @@ export declare class AnthropicProvider implements AIProvider {
37
41
  * See PRD #136 for full architecture analysis and decision rationale.
38
42
  */
39
43
  toolLoop(config: ToolLoopConfig): Promise<AgenticResult>;
40
- sendMessageWithTools(message: string, tools: AITool[], toolExecutor: ToolExecutor, operation?: string): Promise<AIResponse & {
41
- toolCalls?: any[];
42
- }>;
43
44
  }
44
45
  //# sourceMappingURL=anthropic-provider.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"anthropic-provider.d.ts","sourceRoot":"","sources":["../../../src/core/providers/anthropic-provider.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EACL,UAAU,EACV,UAAU,EACV,gBAAgB,EAChB,MAAM,EACN,YAAY,EACZ,cAAc,EACd,aAAa,EACd,MAAM,0BAA0B,CAAC;AAGlC,qBAAa,iBAAkB,YAAW,UAAU;IAClD,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,SAAS,CAAU;gBAEf,MAAM,EAAE,gBAAgB;IAWpC,OAAO,CAAC,cAAc;IAStB,eAAe,IAAI,MAAM;IAIzB,eAAe,IAAI,MAAM;IAIzB,aAAa,IAAI,OAAO;IAIlB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,GAAE,MAAkB,GAAG,OAAO,CAAC,UAAU,CAAC;IAwDtF;;;;;;;;;;;;;;;;;;;OAmBG;IACG,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;IAgIxD,oBAAoB,CACxB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EAAE,EACf,YAAY,EAAE,YAAY,EAC1B,SAAS,GAAE,MAAoB,GAC9B,OAAO,CAAC,UAAU,GAAG;QAAE,SAAS,CAAC,EAAE,GAAG,EAAE,CAAA;KAAE,CAAC;CAqE/C"}
1
+ {"version":3,"file":"anthropic-provider.d.ts","sourceRoot":"","sources":["../../../src/core/providers/anthropic-provider.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EACL,UAAU,EACV,UAAU,EACV,gBAAgB,EAChB,cAAc,EACd,aAAa,EACd,MAAM,0BAA0B,CAAC;AAGlC,qBAAa,iBAAkB,YAAW,UAAU;IAClD,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,SAAS,CAAU;gBAEf,MAAM,EAAE,gBAAgB;IAWpC,OAAO,CAAC,cAAc;IAStB,eAAe,IAAI,MAAM;IAIzB,eAAe,IAAI,MAAM;IAIzB,aAAa,IAAI,OAAO;IAIxB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAqBnB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,GAAE,MAAkB,GAAG,OAAO,CAAC,UAAU,CAAC;IAoDtF;;;;;;;;;;;;;;;;;;;OAmBG;IACG,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;CAmO/D"}
@@ -43,6 +43,24 @@ class AnthropicProvider {
43
43
  isInitialized() {
44
44
  return this.client !== undefined;
45
45
  }
46
+ /**
47
+ * Helper method to log debug information if debug mode is enabled
48
+ */
49
+ logDebugIfEnabled(operation, prompt, response, durationMs) {
50
+ if (!this.debugMode)
51
+ return;
52
+ const debugId = (0, provider_debug_utils_1.generateDebugId)(operation);
53
+ (0, provider_debug_utils_1.debugLogInteraction)(debugId, prompt, response, operation, this.getProviderType(), this.model, this.debugMode);
54
+ // Use logMetrics for sendMessage calls (simple token structure, no extended metrics)
55
+ (0, provider_debug_utils_1.logMetrics)(operation, this.getProviderType(), {
56
+ totalTokens: {
57
+ input: response.usage.input_tokens,
58
+ output: response.usage.output_tokens,
59
+ cacheCreation: response.usage.cache_creation_input_tokens,
60
+ cacheRead: response.usage.cache_read_input_tokens
61
+ }
62
+ }, durationMs, this.debugMode);
63
+ }
46
64
  async sendMessage(message, operation = 'generic') {
47
65
  if (!this.client) {
48
66
  throw new Error('Anthropic client not initialized');
@@ -81,11 +99,7 @@ class AnthropicProvider {
81
99
  };
82
100
  const durationMs = Date.now() - startTime;
83
101
  // Debug log the interaction if enabled
84
- if (this.debugMode) {
85
- const debugId = (0, provider_debug_utils_1.generateDebugId)(operation);
86
- (0, provider_debug_utils_1.debugLogInteraction)(debugId, message, response, operation, this.getProviderType(), this.model, this.debugMode);
87
- (0, provider_debug_utils_1.logMetrics)(operation, this.getProviderType(), response.usage, durationMs, this.debugMode);
88
- }
102
+ this.logDebugIfEnabled(operation, message, response, durationMs);
89
103
  return response;
90
104
  }
91
105
  catch (error) {
@@ -117,82 +131,142 @@ class AnthropicProvider {
117
131
  throw new Error('Anthropic client not initialized');
118
132
  }
119
133
  const startTime = Date.now();
120
- // Convert AITool[] to Anthropic Tool format
121
- const tools = config.tools.map(t => ({
122
- name: t.name,
123
- description: t.description,
124
- input_schema: t.inputSchema
125
- }));
126
- // Initialize conversation history
127
- const initialContent = config.systemPrompt + '\n\n' + config.userMessage;
134
+ // Convert AITool[] to Anthropic Tool format with caching on last tool
135
+ const tools = config.tools.map((t, index) => {
136
+ const tool = {
137
+ name: t.name,
138
+ description: t.description,
139
+ input_schema: t.inputSchema
140
+ };
141
+ // Add cache control to the last tool to cache the entire tools array
142
+ if (index === config.tools.length - 1) {
143
+ tool.cache_control = { type: 'ephemeral' };
144
+ }
145
+ return tool;
146
+ });
147
+ // Separate system prompt with caching from user message
148
+ const systemPrompt = [
149
+ {
150
+ type: 'text',
151
+ text: config.systemPrompt,
152
+ cache_control: { type: 'ephemeral' }
153
+ }
154
+ ];
155
+ // Initialize conversation history with just the user message
128
156
  const conversationHistory = [
129
157
  {
130
158
  role: 'user',
131
- content: initialContent
159
+ content: config.userMessage
132
160
  }
133
161
  ];
134
162
  let iterations = 0;
135
163
  const toolCallsExecuted = [];
136
- const totalTokens = { input: 0, output: 0 };
164
+ const totalTokens = { input: 0, output: 0, cacheCreation: 0, cacheRead: 0 };
137
165
  const maxIterations = config.maxIterations || 20;
166
+ const operation = config.operation || 'tool-loop';
138
167
  try {
139
168
  while (iterations < maxIterations) {
140
169
  iterations++;
141
- // Call Anthropic API with tools
170
+ const iterationStartTime = Date.now();
171
+ // Call Anthropic API with tools and cached system prompt
142
172
  const response = await this.client.messages.create({
143
173
  model: this.model,
144
174
  max_tokens: 4096,
175
+ system: systemPrompt,
145
176
  messages: conversationHistory,
146
177
  tools: tools
147
178
  });
148
- // Track token usage
179
+ // Track token usage including cache metrics
149
180
  totalTokens.input += response.usage.input_tokens;
150
181
  totalTokens.output += response.usage.output_tokens;
182
+ // Track cache usage (available when prompt caching is used)
183
+ if ('cache_creation_input_tokens' in response.usage) {
184
+ totalTokens.cacheCreation += response.usage.cache_creation_input_tokens || 0;
185
+ }
186
+ if ('cache_read_input_tokens' in response.usage) {
187
+ totalTokens.cacheRead += response.usage.cache_read_input_tokens || 0;
188
+ }
189
+ // Debug log this iteration if enabled
190
+ if (this.debugMode) {
191
+ const currentPrompt = conversationHistory.map(m => typeof m.content === 'string' ? m.content : JSON.stringify(m.content, null, 2)).join('\n\n---\n\n');
192
+ const aiResponse = {
193
+ content: response.content.map(c => c.type === 'text' ? c.text : `[${c.type}]`).join('\n'),
194
+ usage: {
195
+ input_tokens: response.usage.input_tokens,
196
+ output_tokens: response.usage.output_tokens,
197
+ cache_creation_input_tokens: response.usage.cache_creation_input_tokens,
198
+ cache_read_input_tokens: response.usage.cache_read_input_tokens
199
+ }
200
+ };
201
+ const iterationDurationMs = Date.now() - iterationStartTime;
202
+ this.logDebugIfEnabled(`${operation}-iter${iterations}`, currentPrompt, aiResponse, iterationDurationMs);
203
+ }
151
204
  // Check if AI wants to use tools
152
205
  const toolUses = response.content.filter((c) => c.type === 'tool_use');
153
206
  if (toolUses.length === 0) {
154
207
  // AI is done - extract final text message
155
208
  const textContent = response.content.find((c) => c.type === 'text');
156
- const result = {
209
+ return (0, provider_debug_utils_1.createAndLogAgenticResult)({
157
210
  finalMessage: textContent?.text || '',
158
211
  iterations,
159
212
  toolCallsExecuted,
160
- totalTokens
161
- };
162
- // Log metrics for the entire tool loop
163
- const durationMs = Date.now() - startTime;
164
- if (this.debugMode) {
165
- const operation = config.operation || 'tool-loop';
166
- (0, provider_debug_utils_1.logMetrics)(operation, this.getProviderType(), {
167
- input_tokens: totalTokens.input,
168
- output_tokens: totalTokens.output
169
- }, durationMs, this.debugMode);
170
- }
171
- return result;
213
+ totalTokens: {
214
+ input: totalTokens.input,
215
+ output: totalTokens.output,
216
+ cacheCreation: totalTokens.cacheCreation,
217
+ cacheRead: totalTokens.cacheRead
218
+ },
219
+ status: 'success',
220
+ completionReason: 'investigation_complete',
221
+ modelVersion: this.model,
222
+ operation: `${operation}-summary`,
223
+ sdk: this.getProviderType(),
224
+ startTime,
225
+ debugMode: this.debugMode
226
+ });
172
227
  }
173
- // Execute all requested tools
228
+ // Execute all requested tools in parallel
174
229
  const toolResults = [];
175
- for (const toolUse of toolUses) {
230
+ // Create promises for parallel execution
231
+ const toolExecutionPromises = toolUses.map(async (toolUse) => {
176
232
  try {
177
233
  const result = await config.toolExecutor(toolUse.name, toolUse.input);
234
+ return {
235
+ success: true,
236
+ toolUse,
237
+ result
238
+ };
239
+ }
240
+ catch (error) {
241
+ return {
242
+ success: false,
243
+ toolUse,
244
+ error: error instanceof Error ? error.message : String(error)
245
+ };
246
+ }
247
+ });
248
+ // Execute all tools simultaneously
249
+ const executionResults = await Promise.all(toolExecutionPromises);
250
+ // Process results and build tool_result blocks
251
+ for (const executionResult of executionResults) {
252
+ if (executionResult.success) {
178
253
  toolCallsExecuted.push({
179
- tool: toolUse.name,
180
- input: toolUse.input,
181
- output: result
254
+ tool: executionResult.toolUse.name,
255
+ input: executionResult.toolUse.input,
256
+ output: executionResult.result
182
257
  });
183
258
  toolResults.push({
184
259
  type: 'tool_result',
185
- tool_use_id: toolUse.id,
186
- content: JSON.stringify(result)
260
+ tool_use_id: executionResult.toolUse.id,
261
+ content: JSON.stringify(executionResult.result)
187
262
  });
188
263
  }
189
- catch (error) {
264
+ else {
190
265
  // Feed error back to AI as tool result
191
- const errorMessage = error instanceof Error ? error.message : String(error);
192
266
  toolResults.push({
193
267
  type: 'tool_result',
194
- tool_use_id: toolUse.id,
195
- content: JSON.stringify({ error: errorMessage }),
268
+ tool_use_id: executionResult.toolUse.id,
269
+ content: JSON.stringify({ error: executionResult.error }),
196
270
  is_error: true
197
271
  });
198
272
  }
@@ -204,77 +278,46 @@ class AnthropicProvider {
204
278
  config.onIteration(iterations, toolCallsExecuted);
205
279
  }
206
280
  }
207
- throw new Error(`Tool loop exceeded max iterations (${maxIterations})`);
208
- }
209
- catch (error) {
210
- if (error instanceof Error && error.message.includes('exceeded max iterations')) {
211
- throw error;
212
- }
213
- throw new Error(`Anthropic tool loop error: ${error}`);
214
- }
215
- }
216
- async sendMessageWithTools(message, tools, toolExecutor, operation = 'tool-call') {
217
- if (!this.client) {
218
- throw new Error('Anthropic client not initialized');
219
- }
220
- // Convert AITool[] to Anthropic Tool format
221
- const anthropicTools = tools.map(t => ({
222
- name: t.name,
223
- description: t.description,
224
- input_schema: t.inputSchema
225
- }));
226
- try {
227
- // Single API call with tools
228
- const response = await this.client.messages.create({
229
- model: this.model,
230
- max_tokens: 4096,
231
- messages: [{ role: 'user', content: message }],
232
- tools: anthropicTools
233
- });
234
- const toolCalls = [];
235
- let textContent = '';
236
- // Process response content
237
- for (const block of response.content) {
238
- if (block.type === 'text') {
239
- textContent += block.text;
240
- }
241
- else if (block.type === 'tool_use') {
242
- // Execute the tool
243
- try {
244
- const result = await toolExecutor(block.name, block.input);
245
- toolCalls.push({
246
- tool: block.name,
247
- input: block.input,
248
- output: result
249
- });
250
- }
251
- catch (error) {
252
- const errorMessage = error instanceof Error ? error.message : String(error);
253
- toolCalls.push({
254
- tool: block.name,
255
- input: block.input,
256
- error: errorMessage
257
- });
258
- }
259
- }
260
- }
261
- const aiResponse = {
262
- content: textContent,
263
- usage: {
264
- input_tokens: response.usage.input_tokens,
265
- output_tokens: response.usage.output_tokens
281
+ // Reached max iterations without completion
282
+ return (0, provider_debug_utils_1.createAndLogAgenticResult)({
283
+ finalMessage: `Investigation incomplete - reached maximum ${maxIterations} iterations`,
284
+ iterations,
285
+ toolCallsExecuted,
286
+ totalTokens: {
287
+ input: totalTokens.input,
288
+ output: totalTokens.output,
289
+ cacheCreation: totalTokens.cacheCreation,
290
+ cacheRead: totalTokens.cacheRead
266
291
  },
267
- toolCalls: toolCalls.length > 0 ? toolCalls : undefined
268
- };
269
- // Debug log if enabled
270
- if (this.debugMode) {
271
- const debugId = (0, provider_debug_utils_1.generateDebugId)(operation);
272
- (0, provider_debug_utils_1.debugLogInteraction)(debugId, message, aiResponse, operation, this.getProviderType(), this.model, this.debugMode);
273
- }
274
- return aiResponse;
292
+ status: 'failed',
293
+ completionReason: 'max_iterations',
294
+ modelVersion: this.model,
295
+ operation: `${operation}-max-iterations`,
296
+ sdk: this.getProviderType(),
297
+ startTime,
298
+ debugMode: this.debugMode
299
+ });
275
300
  }
276
301
  catch (error) {
277
- throw new Error(`Anthropic tool call error: ${error}`);
302
+ // Return error result with extended metrics
303
+ return (0, provider_debug_utils_1.createAndLogAgenticResult)({
304
+ finalMessage: `Error during investigation: ${error instanceof Error ? error.message : String(error)}`,
305
+ iterations,
306
+ toolCallsExecuted,
307
+ totalTokens: {
308
+ input: totalTokens.input,
309
+ output: totalTokens.output,
310
+ cacheCreation: totalTokens.cacheCreation,
311
+ cacheRead: totalTokens.cacheRead
312
+ },
313
+ status: 'failed',
314
+ completionReason: 'error',
315
+ modelVersion: this.model,
316
+ operation: `${operation}-error`,
317
+ sdk: this.getProviderType(),
318
+ startTime,
319
+ debugMode: this.debugMode
320
+ });
278
321
  }
279
322
  }
280
323
  }
@@ -4,7 +4,7 @@
4
4
  * Common functions for logging metrics and debugging AI interactions
5
5
  * when DEBUG_DOT_AI=true
6
6
  */
7
- import { AIResponse } from '../ai-provider.interface';
7
+ import { AIResponse, AgenticResult } from '../ai-provider.interface';
8
8
  /**
9
9
  * Create debug directory if it doesn't exist
10
10
  */
@@ -15,11 +15,54 @@ export declare function ensureDebugDirectory(): string;
15
15
  export declare function generateDebugId(operation: string): string;
16
16
  /**
17
17
  * Log metrics for token usage and execution time when DEBUG_DOT_AI=true
18
+ *
19
+ * PRD #143 Decision 5: Extended metrics for model comparison analysis
18
20
  */
19
- export declare function logMetrics(operation: string, provider: string, usage: {
20
- input_tokens: number;
21
- output_tokens: number;
21
+ export declare function logMetrics(operation: string, sdk: string, result: {
22
+ totalTokens: {
23
+ input: number;
24
+ output: number;
25
+ cacheCreation?: number;
26
+ cacheRead?: number;
27
+ };
28
+ iterations?: number;
29
+ toolCallsExecuted?: Array<{
30
+ tool: string;
31
+ input: any;
32
+ output: any;
33
+ }>;
34
+ status?: string;
35
+ completionReason?: string;
36
+ modelVersion?: string;
22
37
  }, durationMs: number, debugMode: boolean): void;
38
+ /**
39
+ * Create AgenticResult and log metrics in one step
40
+ * Reduces code duplication across providers
41
+ *
42
+ * PRD #143 Decision 5: Standardized metrics logging
43
+ */
44
+ export declare function createAndLogAgenticResult(config: {
45
+ finalMessage: string;
46
+ iterations: number;
47
+ toolCallsExecuted: Array<{
48
+ tool: string;
49
+ input: any;
50
+ output: any;
51
+ }>;
52
+ totalTokens: {
53
+ input: number;
54
+ output: number;
55
+ cacheCreation: number;
56
+ cacheRead: number;
57
+ };
58
+ status: 'success' | 'failed' | 'timeout' | 'parse_error';
59
+ completionReason: 'investigation_complete' | 'max_iterations' | 'parse_failure' | 'model_stopped' | 'error';
60
+ modelVersion: string;
61
+ operation: string;
62
+ sdk: string;
63
+ startTime: number;
64
+ debugMode: boolean;
65
+ }): AgenticResult;
23
66
  /**
24
67
  * Save AI interaction for debugging when DEBUG_DOT_AI=true
25
68
  */
@@ -1 +1 @@
1
- {"version":3,"file":"provider-debug-utils.d.ts","sourceRoot":"","sources":["../../../src/core/providers/provider-debug-utils.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAEtD;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAM7C;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAKzD;AAED;;GAEG;AACH,wBAAgB,UAAU,CACxB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,EACtD,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,OAAO,GACjB,IAAI,CAoBN;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,UAAU,EACpB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,OAAO,GACjB,IAAI,CAkCN"}
1
+ {"version":3,"file":"provider-debug-utils.d.ts","sourceRoot":"","sources":["../../../src/core/providers/provider-debug-utils.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAErE;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAM7C;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAKzD;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CACxB,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,EACX,MAAM,EAAE;IACN,WAAW,EAAE;QACX,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;IACF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iBAAiB,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,GAAG,CAAC;QAAC,MAAM,EAAE,GAAG,CAAA;KAAE,CAAC,CAAC;IACrE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,EACD,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,OAAO,GACjB,IAAI,CA2DN;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE;IAChD,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,GAAG,CAAC;QAAC,MAAM,EAAE,GAAG,CAAA;KAAE,CAAC,CAAC;IACpE,WAAW,EAAE;QACX,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,aAAa,EAAE,MAAM,CAAC;QACtB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,MAAM,EAAE,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,aAAa,CAAC;IACzD,gBAAgB,EAAE,wBAAwB,GAAG,gBAAgB,GAAG,eAAe,GAAG,eAAe,GAAG,OAAO,CAAC;IAC5G,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,OAAO,CAAC;CACpB,GAAG,aAAa,CAiBhB;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,UAAU,EACpB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,OAAO,GACjB,IAAI,CAkCN"}
@@ -42,6 +42,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
42
42
  exports.ensureDebugDirectory = ensureDebugDirectory;
43
43
  exports.generateDebugId = generateDebugId;
44
44
  exports.logMetrics = logMetrics;
45
+ exports.createAndLogAgenticResult = createAndLogAgenticResult;
45
46
  exports.debugLogInteraction = debugLogInteraction;
46
47
  const fs = __importStar(require("fs"));
47
48
  const path = __importStar(require("path"));
@@ -67,27 +68,86 @@ function generateDebugId(operation) {
67
68
  }
68
69
  /**
69
70
  * Log metrics for token usage and execution time when DEBUG_DOT_AI=true
71
+ *
72
+ * PRD #143 Decision 5: Extended metrics for model comparison analysis
70
73
  */
71
- function logMetrics(operation, provider, usage, durationMs, debugMode) {
74
+ function logMetrics(operation, sdk, result, durationMs, debugMode) {
72
75
  if (!debugMode)
73
76
  return;
74
77
  try {
75
78
  const debugDir = ensureDebugDirectory();
76
79
  const metricsFile = path.join(debugDir, 'metrics.jsonl');
77
- const entry = JSON.stringify({
80
+ const entry = {
78
81
  timestamp: new Date().toISOString(),
79
- provider,
82
+ sdk,
80
83
  operation,
81
- inputTokens: usage.input_tokens,
82
- outputTokens: usage.output_tokens,
84
+ inputTokens: result.totalTokens.input,
85
+ outputTokens: result.totalTokens.output,
83
86
  durationMs
84
- }) + '\n';
85
- fs.appendFileSync(metricsFile, entry);
87
+ };
88
+ // Add cache metrics if present
89
+ if (result.totalTokens.cacheCreation !== undefined) {
90
+ entry.cacheCreationTokens = result.totalTokens.cacheCreation;
91
+ }
92
+ if (result.totalTokens.cacheRead !== undefined) {
93
+ entry.cacheReadTokens = result.totalTokens.cacheRead;
94
+ }
95
+ // Calculate cache hit rate (percentage)
96
+ if (result.totalTokens.cacheRead !== undefined && result.totalTokens.input > 0) {
97
+ entry.cacheHitRate = Math.round((result.totalTokens.cacheRead / result.totalTokens.input) * 100);
98
+ }
99
+ // Add extended metrics (PRD #143 Decision 5)
100
+ if (result.iterations !== undefined) {
101
+ entry.iterationCount = result.iterations;
102
+ }
103
+ if (result.toolCallsExecuted) {
104
+ entry.toolCallCount = result.toolCallsExecuted.length;
105
+ // Extract unique tool names
106
+ const uniqueTools = [...new Set(result.toolCallsExecuted.map(tc => tc.tool))];
107
+ entry.uniqueToolsUsed = uniqueTools;
108
+ }
109
+ if (result.status) {
110
+ entry.status = result.status;
111
+ }
112
+ if (result.completionReason) {
113
+ entry.completionReason = result.completionReason;
114
+ }
115
+ if (result.modelVersion) {
116
+ entry.modelVersion = result.modelVersion;
117
+ }
118
+ // Manual annotation placeholders (populate after test analysis)
119
+ entry.manualNotes = '';
120
+ entry.failureReason = '';
121
+ entry.qualityIssues = [];
122
+ entry.comparisonNotes = '';
123
+ fs.appendFileSync(metricsFile, JSON.stringify(entry) + '\n');
86
124
  }
87
125
  catch (error) {
88
126
  console.warn('Failed to log metrics:', error);
89
127
  }
90
128
  }
129
+ /**
130
+ * Create AgenticResult and log metrics in one step
131
+ * Reduces code duplication across providers
132
+ *
133
+ * PRD #143 Decision 5: Standardized metrics logging
134
+ */
135
+ function createAndLogAgenticResult(config) {
136
+ const result = {
137
+ finalMessage: config.finalMessage,
138
+ iterations: config.iterations,
139
+ toolCallsExecuted: config.toolCallsExecuted,
140
+ totalTokens: config.totalTokens,
141
+ status: config.status,
142
+ completionReason: config.completionReason,
143
+ modelVersion: config.modelVersion
144
+ };
145
+ const durationMs = Date.now() - config.startTime;
146
+ if (config.debugMode) {
147
+ logMetrics(config.operation, config.sdk, result, durationMs, config.debugMode);
148
+ }
149
+ return result;
150
+ }
91
151
  /**
92
152
  * Save AI interaction for debugging when DEBUG_DOT_AI=true
93
153
  */
@@ -4,7 +4,7 @@
4
4
  * Implements AIProvider interface using Vercel AI SDK.
5
5
  * Supports OpenAI and Google Gemini providers through unified interface.
6
6
  */
7
- import { AIProvider, AIResponse, AIProviderConfig, AITool, ToolExecutor, ToolLoopConfig, AgenticResult } from '../ai-provider.interface';
7
+ import { AIProvider, AIResponse, AIProviderConfig, ToolLoopConfig, AgenticResult } from '../ai-provider.interface';
8
8
  export declare class VercelProvider implements AIProvider {
9
9
  private providerType;
10
10
  private model;
@@ -17,31 +17,21 @@ export declare class VercelProvider implements AIProvider {
17
17
  getProviderType(): string;
18
18
  getDefaultModel(): string;
19
19
  isInitialized(): boolean;
20
+ private logDebugIfEnabled;
20
21
  sendMessage(message: string, operation?: string): Promise<AIResponse>;
21
22
  /**
22
- * Agentic tool loop - NOT IMPLEMENTED for Vercel provider
23
+ * Agentic tool loop using Vercel AI SDK
23
24
  *
24
- * This method is intentionally not implemented because:
25
- * 1. toolLoop() is currently NOT USED anywhere in the codebase (see PRD #136)
26
- * 2. JSON-based agentic loops already provide equivalent functionality
27
- * 3. No current workflows require SDK-managed tool loops
25
+ * Implements multi-turn tool calling using generateText with maxSteps.
26
+ * The Vercel AI SDK handles the conversation loop automatically.
28
27
  *
29
- * If future requirements necessitate toolLoop() for non-Anthropic providers,
30
- * this would need to be implemented using Vercel AI SDK's tool calling API.
28
+ * Provider-specific caching:
29
+ * - Anthropic: Manual cache control via providerOptions
30
+ * - OpenAI: Automatic caching (no code changes needed)
31
+ * - Google: Check Gemini caching capabilities
31
32
  *
32
- * See AnthropicProvider.toolLoop() and PRD #136 for implementation reference.
33
+ * See PRD #143 Milestone 2.5 for Vercel provider implementation details.
33
34
  */
34
- toolLoop(_config: ToolLoopConfig): Promise<AgenticResult>;
35
- /**
36
- * Single-shot tool calling - NOT IMPLEMENTED for Vercel provider
37
- *
38
- * Same reasoning as toolLoop(): not currently needed in codebase.
39
- * JSON-based approach achieves same functionality without SDK overhead.
40
- *
41
- * See AnthropicProvider.sendMessageWithTools() for reference implementation.
42
- */
43
- sendMessageWithTools(_message: string, _tools: AITool[], _toolExecutor: ToolExecutor, _operation?: string): Promise<AIResponse & {
44
- toolCalls?: any[];
45
- }>;
35
+ toolLoop(config: ToolLoopConfig): Promise<AgenticResult>;
46
36
  }
47
37
  //# sourceMappingURL=vercel-provider.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"vercel-provider.d.ts","sourceRoot":"","sources":["../../../src/core/providers/vercel-provider.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,EACL,UAAU,EACV,UAAU,EACV,gBAAgB,EAChB,MAAM,EACN,YAAY,EACZ,cAAc,EACd,aAAa,EACd,MAAM,0BAA0B,CAAC;AAclC,qBAAa,cAAe,YAAW,UAAU;IAC/C,OAAO,CAAC,YAAY,CAAoB;IACxC,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,SAAS,CAAU;IAC3B,OAAO,CAAC,aAAa,CAAM;gBAEf,MAAM,EAAE,gBAAgB;IAUpC,OAAO,CAAC,qBAAqB;IAU7B,OAAO,CAAC,eAAe;IAgCvB,eAAe,IAAI,MAAM;IAIzB,eAAe,IAAI,MAAM;IAIzB,aAAa,IAAI,OAAO;IAIlB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,GAAE,MAAkB,GAAG,OAAO,CAAC,UAAU,CAAC;IAuCtF;;;;;;;;;;;;OAYG;IACG,QAAQ,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;IAQ/D;;;;;;;OAOG;IACG,oBAAoB,CACxB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EAAE,EAChB,aAAa,EAAE,YAAY,EAC3B,UAAU,GAAE,MAAoB,GAC/B,OAAO,CAAC,UAAU,GAAG;QAAE,SAAS,CAAC,EAAE,GAAG,EAAE,CAAA;KAAE,CAAC;CAO/C"}
1
+ {"version":3,"file":"vercel-provider.d.ts","sourceRoot":"","sources":["../../../src/core/providers/vercel-provider.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,EACL,UAAU,EACV,UAAU,EACV,gBAAgB,EAChB,cAAc,EACd,aAAa,EACd,MAAM,0BAA0B,CAAC;AAclC,qBAAa,cAAe,YAAW,UAAU;IAC/C,OAAO,CAAC,YAAY,CAAoB;IACxC,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,SAAS,CAAU;IAC3B,OAAO,CAAC,aAAa,CAAM;gBAEf,MAAM,EAAE,gBAAgB;IAUpC,OAAO,CAAC,qBAAqB;IAU7B,OAAO,CAAC,eAAe;IAgCvB,eAAe,IAAI,MAAM;IAIzB,eAAe,IAAI,MAAM;IAIzB,aAAa,IAAI,OAAO;IAIxB,OAAO,CAAC,iBAAiB;IAqBnB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,GAAE,MAAkB,GAAG,OAAO,CAAC,UAAU,CAAC;IA8CtF;;;;;;;;;;;;OAYG;IACG,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;CA0P/D"}