@providerprotocol/ai 0.0.9 → 0.0.11

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/src/core/llm.ts CHANGED
@@ -23,7 +23,11 @@ import {
23
23
  isAssistantMessage,
24
24
  } from '../types/messages.ts';
25
25
  import { createTurn, aggregateUsage, emptyUsage } from '../types/turn.ts';
26
- import { createStreamResult } from '../types/stream.ts';
26
+ import {
27
+ createStreamResult,
28
+ toolExecutionStart,
29
+ toolExecutionEnd,
30
+ } from '../types/stream.ts';
27
31
  import { generateShortId } from '../utils/id.ts';
28
32
 
29
33
  /**
@@ -413,14 +417,21 @@ function executeStream<TParams>(
413
417
  );
414
418
  }
415
419
 
416
- // Execute tools
420
+ // Execute tools with event emission
421
+ const toolEvents: StreamEvent[] = [];
417
422
  const results = await executeTools(
418
423
  response.message,
419
424
  tools,
420
425
  toolStrategy,
421
- toolExecutions
426
+ toolExecutions,
427
+ (event) => toolEvents.push(event)
422
428
  );
423
429
 
430
+ // Yield tool execution events
431
+ for (const event of toolEvents) {
432
+ yield event;
433
+ }
434
+
424
435
  // Add tool results
425
436
  allMessages.push(new ToolResultMessage(results));
426
437
 
@@ -467,7 +478,8 @@ async function executeTools(
467
478
  message: AssistantMessage,
468
479
  tools: Tool[],
469
480
  toolStrategy: LLMOptions<unknown>['toolStrategy'],
470
- executions: ToolExecution[]
481
+ executions: ToolExecution[],
482
+ onEvent?: (event: StreamEvent) => void
471
483
  ): Promise<ToolResult[]> {
472
484
  const toolCalls = message.toolCalls ?? [];
473
485
  const results: ToolResult[] = [];
@@ -476,7 +488,7 @@ async function executeTools(
476
488
  const toolMap = new Map(tools.map((t) => [t.name, t]));
477
489
 
478
490
  // Execute tools (in parallel)
479
- const promises = toolCalls.map(async (call) => {
491
+ const promises = toolCalls.map(async (call, index) => {
480
492
  const tool = toolMap.get(call.toolName);
481
493
  if (!tool) {
482
494
  return {
@@ -488,6 +500,9 @@ async function executeTools(
488
500
 
489
501
  const startTime = Date.now();
490
502
 
503
+ // Emit start event
504
+ onEvent?.(toolExecutionStart(call.toolCallId, tool.name, startTime, index));
505
+
491
506
  // Notify strategy
492
507
  await toolStrategy?.onToolCall?.(tool, call.arguments);
493
508
 
@@ -495,6 +510,8 @@ async function executeTools(
495
510
  if (toolStrategy?.onBeforeCall) {
496
511
  const shouldRun = await toolStrategy.onBeforeCall(tool, call.arguments);
497
512
  if (!shouldRun) {
513
+ const endTime = Date.now();
514
+ onEvent?.(toolExecutionEnd(call.toolCallId, tool.name, 'Tool execution skipped', true, endTime, index));
498
515
  return {
499
516
  toolCallId: call.toolCallId,
500
517
  result: 'Tool execution skipped',
@@ -515,17 +532,20 @@ async function executeTools(
515
532
  }
516
533
 
517
534
  if (!approved) {
535
+ const endTime = Date.now();
518
536
  const execution: ToolExecution = {
519
537
  toolName: tool.name,
520
538
  toolCallId: call.toolCallId,
521
539
  arguments: call.arguments,
522
540
  result: 'Tool execution denied',
523
541
  isError: true,
524
- duration: Date.now() - startTime,
542
+ duration: endTime - startTime,
525
543
  approved: false,
526
544
  };
527
545
  executions.push(execution);
528
546
 
547
+ onEvent?.(toolExecutionEnd(call.toolCallId, tool.name, 'Tool execution denied by approval handler', true, endTime, index));
548
+
529
549
  return {
530
550
  toolCallId: call.toolCallId,
531
551
  result: 'Tool execution denied by approval handler',
@@ -536,6 +556,7 @@ async function executeTools(
536
556
  // Execute tool
537
557
  try {
538
558
  const result = await tool.run(call.arguments);
559
+ const endTime = Date.now();
539
560
 
540
561
  await toolStrategy?.onAfterCall?.(tool, call.arguments, result);
541
562
 
@@ -545,17 +566,20 @@ async function executeTools(
545
566
  arguments: call.arguments,
546
567
  result,
547
568
  isError: false,
548
- duration: Date.now() - startTime,
569
+ duration: endTime - startTime,
549
570
  approved,
550
571
  };
551
572
  executions.push(execution);
552
573
 
574
+ onEvent?.(toolExecutionEnd(call.toolCallId, tool.name, result, false, endTime, index));
575
+
553
576
  return {
554
577
  toolCallId: call.toolCallId,
555
578
  result,
556
579
  isError: false,
557
580
  };
558
581
  } catch (error) {
582
+ const endTime = Date.now();
559
583
  await toolStrategy?.onError?.(tool, call.arguments, error as Error);
560
584
 
561
585
  const errorMessage = error instanceof Error ? error.message : String(error);
@@ -566,11 +590,13 @@ async function executeTools(
566
590
  arguments: call.arguments,
567
591
  result: errorMessage,
568
592
  isError: true,
569
- duration: Date.now() - startTime,
593
+ duration: endTime - startTime,
570
594
  approved,
571
595
  };
572
596
  executions.push(execution);
573
597
 
598
+ onEvent?.(toolExecutionEnd(call.toolCallId, tool.name, errorMessage, true, endTime, index));
599
+
574
600
  return {
575
601
  toolCallId: call.toolCallId,
576
602
  result: errorMessage,
@@ -20,6 +20,7 @@ export type {
20
20
  OpenAIAudioConfig,
21
21
  OpenAIWebSearchOptions,
22
22
  OpenAIWebSearchUserLocation,
23
+ OpenAICompletionsWebSearchUserLocation,
23
24
  // Built-in tool types
24
25
  OpenAIBuiltInTool,
25
26
  OpenAIWebSearchTool,
@@ -156,6 +156,7 @@ export type {
156
156
  OpenAIAudioConfig,
157
157
  OpenAIWebSearchOptions,
158
158
  OpenAIWebSearchUserLocation,
159
+ OpenAICompletionsWebSearchUserLocation,
159
160
  // Built-in tool types
160
161
  OpenAIBuiltInTool,
161
162
  OpenAIWebSearchTool,
@@ -27,8 +27,8 @@ export interface OpenAIAudioConfig {
27
27
  // ============================================
28
28
 
29
29
  /**
30
- * User location for web search context
31
- * Requires type: 'approximate' with location fields at the same level
30
+ * User location for web search context (Responses API format)
31
+ * Fields are at the same level as type
32
32
  */
33
33
  export interface OpenAIWebSearchUserLocation {
34
34
  /** Location type - must be 'approximate' */
@@ -43,8 +43,29 @@ export interface OpenAIWebSearchUserLocation {
43
43
  timezone?: string;
44
44
  }
45
45
 
46
+ /**
47
+ * User location for web search context (Chat Completions API format)
48
+ * Fields are nested under 'approximate' object
49
+ */
50
+ export interface OpenAICompletionsWebSearchUserLocation {
51
+ /** Location type - must be 'approximate' */
52
+ type: 'approximate';
53
+ /** Approximate location details */
54
+ approximate: {
55
+ /** City name */
56
+ city?: string;
57
+ /** ISO 3166-1 country code (e.g., "US") */
58
+ country?: string;
59
+ /** Region/state name */
60
+ region?: string;
61
+ /** IANA timezone (e.g., "America/New_York") */
62
+ timezone?: string;
63
+ };
64
+ }
65
+
46
66
  /**
47
67
  * Web search options for Chat Completions API
68
+ * Use with gpt-5-search-api-* models
48
69
  */
49
70
  export interface OpenAIWebSearchOptions {
50
71
  /**
@@ -53,7 +74,7 @@ export interface OpenAIWebSearchOptions {
53
74
  */
54
75
  search_context_size?: 'low' | 'medium' | 'high';
55
76
  /** User location for localizing search results */
56
- user_location?: OpenAIWebSearchUserLocation | null;
77
+ user_location?: OpenAICompletionsWebSearchUserLocation | null;
57
78
  }
58
79
 
59
80
  /**
@@ -10,6 +10,8 @@ export type StreamEventType =
10
10
  | 'audio_delta'
11
11
  | 'video_delta'
12
12
  | 'tool_call_delta'
13
+ | 'tool_execution_start'
14
+ | 'tool_execution_end'
13
15
  | 'message_start'
14
16
  | 'message_stop'
15
17
  | 'content_block_start'
@@ -24,6 +26,12 @@ export interface EventDelta {
24
26
  toolCallId?: string;
25
27
  toolName?: string;
26
28
  argumentsJson?: string;
29
+ /** Tool execution result (for tool_execution_end) */
30
+ result?: unknown;
31
+ /** Whether tool execution errored (for tool_execution_end) */
32
+ isError?: boolean;
33
+ /** Timestamp in ms (for tool_execution_start/end) */
34
+ timestamp?: number;
27
35
  }
28
36
 
29
37
  /**
@@ -144,3 +152,37 @@ export function contentBlockStop(index: number): StreamEvent {
144
152
  delta: {},
145
153
  };
146
154
  }
155
+
156
+ /**
157
+ * Create a tool execution start event
158
+ */
159
+ export function toolExecutionStart(
160
+ toolCallId: string,
161
+ toolName: string,
162
+ timestamp: number,
163
+ index = 0
164
+ ): StreamEvent {
165
+ return {
166
+ type: 'tool_execution_start',
167
+ index,
168
+ delta: { toolCallId, toolName, timestamp },
169
+ };
170
+ }
171
+
172
+ /**
173
+ * Create a tool execution end event
174
+ */
175
+ export function toolExecutionEnd(
176
+ toolCallId: string,
177
+ toolName: string,
178
+ result: unknown,
179
+ isError: boolean,
180
+ timestamp: number,
181
+ index = 0
182
+ ): StreamEvent {
183
+ return {
184
+ type: 'tool_execution_end',
185
+ index,
186
+ delta: { toolCallId, toolName, result, isError, timestamp },
187
+ };
188
+ }