@providerprotocol/ai 0.0.6 → 0.0.8

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.
@@ -3,7 +3,7 @@ import type { Message } from '../../types/messages.ts';
3
3
  import type { StreamEvent } from '../../types/stream.ts';
4
4
  import type { Tool, ToolCall } from '../../types/tool.ts';
5
5
  import type { TokenUsage } from '../../types/turn.ts';
6
- import type { ContentBlock, TextBlock, ImageBlock } from '../../types/content.ts';
6
+ import type { ContentBlock, TextBlock, ImageBlock, AssistantContent } from '../../types/content.ts';
7
7
  import {
8
8
  AssistantMessage,
9
9
  isUserMessage,
@@ -16,11 +16,13 @@ import type {
16
16
  OpenAIResponsesInputItem,
17
17
  OpenAIResponsesContentPart,
18
18
  OpenAIResponsesTool,
19
+ OpenAIResponsesToolUnion,
19
20
  OpenAIResponsesResponse,
20
21
  OpenAIResponsesStreamEvent,
21
22
  OpenAIResponsesOutputItem,
22
23
  OpenAIResponsesMessageOutput,
23
24
  OpenAIResponsesFunctionCallOutput,
25
+ OpenAIResponsesImageGenerationOutput,
24
26
  } from './types.ts';
25
27
 
26
28
  /**
@@ -36,16 +38,23 @@ export function transformRequest(
36
38
  ): OpenAIResponsesRequest {
37
39
  const params = request.params ?? ({} as OpenAIResponsesParams);
38
40
 
41
+ // Extract built-in tools from params before spreading
42
+ const builtInTools = params.tools as OpenAIResponsesToolUnion[] | undefined;
43
+ const { tools: _paramsTools, ...restParams } = params;
44
+
39
45
  // Spread params to pass through all fields, then set required fields
40
46
  const openaiRequest: OpenAIResponsesRequest = {
41
- ...params,
47
+ ...restParams,
42
48
  model: modelId,
43
49
  input: transformInputItems(request.messages, request.system),
44
50
  };
45
51
 
46
- // Tools come from request, not params
47
- if (request.tools && request.tools.length > 0) {
48
- openaiRequest.tools = request.tools.map(transformTool);
52
+ // Merge tools: UPP function tools from request + built-in tools from params
53
+ const functionTools: OpenAIResponsesToolUnion[] = request.tools?.map(transformTool) ?? [];
54
+ const allTools: OpenAIResponsesToolUnion[] = [...functionTools, ...(builtInTools ?? [])];
55
+
56
+ if (allTools.length > 0) {
57
+ openaiRequest.tools = allTools;
49
58
  }
50
59
 
51
60
  // Structured output via text.format (overrides params.text if set)
@@ -276,8 +285,8 @@ function transformTool(tool: Tool): OpenAIResponsesTool {
276
285
  * Transform OpenAI Responses API response to UPP LLMResponse
277
286
  */
278
287
  export function transformResponse(data: OpenAIResponsesResponse): LLMResponse {
279
- // Extract text content and tool calls from output items
280
- const textContent: TextBlock[] = [];
288
+ // Extract content and tool calls from output items
289
+ const content: AssistantContent[] = [];
281
290
  const toolCalls: ToolCall[] = [];
282
291
  const functionCallItems: Array<{
283
292
  id: string;
@@ -291,20 +300,19 @@ export function transformResponse(data: OpenAIResponsesResponse): LLMResponse {
291
300
  for (const item of data.output) {
292
301
  if (item.type === 'message') {
293
302
  const messageItem = item as OpenAIResponsesMessageOutput;
294
- for (const content of messageItem.content) {
295
- if (content.type === 'output_text') {
296
- textContent.push({ type: 'text', text: content.text });
303
+ for (const part of messageItem.content) {
304
+ if (part.type === 'output_text') {
305
+ content.push({ type: 'text', text: part.text });
297
306
  // Try to parse as JSON for structured output (native JSON mode)
298
- // Only set data if text is valid JSON
299
307
  if (structuredData === undefined) {
300
308
  try {
301
- structuredData = JSON.parse(content.text);
309
+ structuredData = JSON.parse(part.text);
302
310
  } catch {
303
311
  // Not valid JSON - that's fine, might not be structured output
304
312
  }
305
313
  }
306
- } else if (content.type === 'refusal') {
307
- textContent.push({ type: 'text', text: content.refusal });
314
+ } else if (part.type === 'refusal') {
315
+ content.push({ type: 'text', text: part.refusal });
308
316
  hadRefusal = true;
309
317
  }
310
318
  }
@@ -327,11 +335,20 @@ export function transformResponse(data: OpenAIResponsesResponse): LLMResponse {
327
335
  name: functionCall.name,
328
336
  arguments: functionCall.arguments,
329
337
  });
338
+ } else if (item.type === 'image_generation_call') {
339
+ const imageGen = item as OpenAIResponsesImageGenerationOutput;
340
+ if (imageGen.result) {
341
+ content.push({
342
+ type: 'image',
343
+ mimeType: 'image/png',
344
+ source: { type: 'base64', data: imageGen.result },
345
+ } as ImageBlock);
346
+ }
330
347
  }
331
348
  }
332
349
 
333
350
  const message = new AssistantMessage(
334
- textContent,
351
+ content,
335
352
  toolCalls.length > 0 ? toolCalls : undefined,
336
353
  {
337
354
  id: data.id,
@@ -339,7 +356,6 @@ export function transformResponse(data: OpenAIResponsesResponse): LLMResponse {
339
356
  openai: {
340
357
  model: data.model,
341
358
  status: data.status,
342
- // Store response_id for multi-turn tool calling
343
359
  response_id: data.id,
344
360
  functionCallItems:
345
361
  functionCallItems.length > 0 ? functionCallItems : undefined,