@yourgpt/llm-sdk 2.6.0-beta.0 → 2.6.0-beta.2

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 (64) hide show
  1. package/README.md +189 -0
  2. package/dist/adapters/index.d.mts +11 -13
  3. package/dist/adapters/index.d.ts +11 -13
  4. package/dist/adapters/index.js +610 -186
  5. package/dist/adapters/index.mjs +610 -186
  6. package/dist/audio-BBSwnjo8.d.mts +92 -0
  7. package/dist/audio-BBSwnjo8.d.ts +92 -0
  8. package/dist/{base-tNgbBaSo.d.mts → base-BICaQaRS.d.mts} +8 -3
  9. package/dist/{base-C58Dsr9p.d.ts → base-CpRPB8u8.d.ts} +8 -3
  10. package/dist/fallback/index.d.mts +4 -4
  11. package/dist/fallback/index.d.ts +4 -4
  12. package/dist/fallback/index.js +75 -39
  13. package/dist/fallback/index.mjs +75 -39
  14. package/dist/index.d.mts +18 -35
  15. package/dist/index.d.ts +18 -35
  16. package/dist/index.js +304 -88
  17. package/dist/index.mjs +302 -89
  18. package/dist/providers/anthropic/index.d.mts +3 -3
  19. package/dist/providers/anthropic/index.d.ts +3 -3
  20. package/dist/providers/anthropic/index.js +123 -7
  21. package/dist/providers/anthropic/index.mjs +123 -7
  22. package/dist/providers/azure/index.d.mts +3 -3
  23. package/dist/providers/azure/index.d.ts +3 -3
  24. package/dist/providers/azure/index.js +101 -4
  25. package/dist/providers/azure/index.mjs +101 -4
  26. package/dist/providers/fireworks/index.d.mts +3 -3
  27. package/dist/providers/fireworks/index.d.ts +3 -3
  28. package/dist/providers/fireworks/index.js +559 -178
  29. package/dist/providers/fireworks/index.mjs +559 -178
  30. package/dist/providers/google/index.d.mts +3 -3
  31. package/dist/providers/google/index.d.ts +3 -3
  32. package/dist/providers/google/index.js +559 -178
  33. package/dist/providers/google/index.mjs +559 -178
  34. package/dist/providers/ollama/index.d.mts +4 -4
  35. package/dist/providers/ollama/index.d.ts +4 -4
  36. package/dist/providers/ollama/index.js +87 -2
  37. package/dist/providers/ollama/index.mjs +87 -2
  38. package/dist/providers/openai/index.d.mts +25 -23
  39. package/dist/providers/openai/index.d.ts +25 -23
  40. package/dist/providers/openai/index.js +1397 -481
  41. package/dist/providers/openai/index.mjs +1396 -482
  42. package/dist/providers/openrouter/index.d.mts +3 -3
  43. package/dist/providers/openrouter/index.d.ts +3 -3
  44. package/dist/providers/openrouter/index.js +559 -178
  45. package/dist/providers/openrouter/index.mjs +559 -178
  46. package/dist/providers/togetherai/index.d.mts +3 -3
  47. package/dist/providers/togetherai/index.d.ts +3 -3
  48. package/dist/providers/togetherai/index.js +559 -178
  49. package/dist/providers/togetherai/index.mjs +559 -178
  50. package/dist/providers/xai/index.d.mts +3 -3
  51. package/dist/providers/xai/index.d.ts +3 -3
  52. package/dist/providers/xai/index.js +559 -178
  53. package/dist/providers/xai/index.mjs +559 -178
  54. package/dist/{types-B6dhnguR.d.mts → types-C3RgRbEC.d.mts} +11 -4
  55. package/dist/{types-CCxPmkmK.d.ts → types-DCFqkJAy.d.ts} +11 -4
  56. package/dist/{types-BQ31QIsA.d.ts → types-DHQEMFHA.d.mts} +3 -11
  57. package/dist/{types-BkQCSiIt.d.mts → types-Dg1Bcg1k.d.mts} +127 -3
  58. package/dist/{types-BkQCSiIt.d.ts → types-Dg1Bcg1k.d.ts} +127 -3
  59. package/dist/{types-BSSiJW2o.d.mts → types-Dikj3En4.d.ts} +3 -11
  60. package/dist/yourgpt/index.d.mts +1 -1
  61. package/dist/yourgpt/index.d.ts +1 -1
  62. package/docs/multimodal-and-audio.md +170 -0
  63. package/package.json +117 -49
  64. package/LICENSE +0 -21
@@ -1,5 +1,78 @@
1
1
  import { z } from 'zod';
2
2
 
3
+ /** OpenAI reasoning controls. Omit to retain the model's default effort. */
4
+ type OpenAIReasoningEffort = "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max";
5
+ interface OpenAIParameterOptions {
6
+ reasoningEffort?: OpenAIReasoningEffort;
7
+ /** Use passthrough for compatible servers with different model rules. */
8
+ parameterPolicy?: "openai" | "passthrough";
9
+ /** Receives omitted-option warnings; defaults to console.warn. */
10
+ onWarning?: (message: string) => void;
11
+ }
12
+
13
+ /** Responses-only options. Omitted values retain the API's defaults. */
14
+ interface OpenAIResponsesOptions {
15
+ store?: boolean;
16
+ /** Send only new input after this stored response; do not also resend its history. */
17
+ previousResponseId?: string;
18
+ include?: string[];
19
+ reasoningSummary?: "auto" | "concise" | "detailed";
20
+ promptCacheKey?: string;
21
+ /** Legacy retention option for models that support it. */
22
+ promptCacheRetention?: "in_memory" | "24h";
23
+ /** GPT-5.6+ cache mode and minimum lifetime; provider/model support varies. */
24
+ promptCacheOptions?: {
25
+ mode?: "implicit" | "explicit";
26
+ ttl?: "30m";
27
+ };
28
+ }
29
+ interface OpenAITransportOptions {
30
+ /** auto preserves existing routing; responses also works without function tools. */
31
+ api?: "auto" | "chat" | "responses";
32
+ responses?: OpenAIResponsesOptions;
33
+ }
34
+ /** Opaque provider items: retain their complete contents and order for replay. */
35
+ interface OpenAIResponseItem {
36
+ type: string;
37
+ [key: string]: unknown;
38
+ }
39
+ interface OpenAIResponseMetadata {
40
+ id?: string;
41
+ model?: string;
42
+ status?: string;
43
+ output: OpenAIResponseItem[];
44
+ usage?: Record<string, unknown>;
45
+ store?: boolean;
46
+ refusals: string[];
47
+ }
48
+ interface ProviderMetadata {
49
+ openai?: {
50
+ response?: OpenAIResponseMetadata;
51
+ };
52
+ [provider: string]: unknown;
53
+ }
54
+
55
+ /** Explicit model-facing output; the application's raw tool result stays separate. */
56
+ type ToolOutputPart = {
57
+ type: "text";
58
+ text: string;
59
+ } | {
60
+ type: "image";
61
+ data: string | Uint8Array;
62
+ mediaType: string;
63
+ } | {
64
+ type: "image";
65
+ url: string;
66
+ } | {
67
+ type: "image";
68
+ fileId: string;
69
+ provider: "openai";
70
+ };
71
+ interface ToolModelOutput {
72
+ type: "content";
73
+ content: ToolOutputPart[];
74
+ }
75
+
3
76
  /**
4
77
  * Stream event types for llm-sdk
5
78
  * These types are used internally by the SDK for streaming responses
@@ -32,6 +105,7 @@ interface MessageDeltaEvent extends BaseEvent {
32
105
  * Message finished streaming
33
106
  */
34
107
  interface MessageEndEvent extends BaseEvent {
108
+ providerMetadata?: ProviderMetadata;
35
109
  type: "message:end";
36
110
  }
37
111
  /**
@@ -77,6 +151,7 @@ interface ActionArgsEvent extends BaseEvent {
77
151
  * Action execution completed
78
152
  */
79
153
  interface ActionEndEvent extends BaseEvent {
154
+ modelOutput?: ToolModelOutput;
80
155
  type: "action:end";
81
156
  id: string;
82
157
  name?: string;
@@ -90,6 +165,12 @@ interface ErrorEvent extends BaseEvent {
90
165
  type: "error";
91
166
  message: string;
92
167
  code?: string;
168
+ status?: number;
169
+ providerCode?: string;
170
+ param?: string;
171
+ requestId?: string;
172
+ /** In-process cause, omitted from serialized error events. */
173
+ cause?: unknown;
93
174
  }
94
175
  /**
95
176
  * Tool call information
@@ -107,6 +188,7 @@ interface ToolCallInfo {
107
188
  * Assistant message with tool calls
108
189
  */
109
190
  interface AssistantToolMessage {
191
+ providerMetadata?: ProviderMetadata;
110
192
  role: "assistant";
111
193
  content: string | null;
112
194
  tool_calls: Array<{
@@ -182,6 +264,8 @@ interface CitationEvent extends BaseEvent {
182
264
  * Message format for done event (API format with snake_case)
183
265
  */
184
266
  interface DoneEventMessage {
267
+ modelOutput?: ToolModelOutput;
268
+ providerMetadata?: ProviderMetadata;
185
269
  role: "assistant" | "tool";
186
270
  content: string | null;
187
271
  tool_calls?: Array<{
@@ -216,6 +300,7 @@ interface ThreadCreatedEvent extends BaseEvent {
216
300
  * Stream completed
217
301
  */
218
302
  interface DoneEvent extends BaseEvent {
303
+ providerMetadata?: ProviderMetadata;
219
304
  type: "done";
220
305
  requiresAction?: boolean;
221
306
  messages?: DoneEventMessage[];
@@ -249,6 +334,7 @@ type ResponseFormat = {
249
334
  * LLM configuration
250
335
  */
251
336
  interface LLMConfig {
337
+ reasoningEffort?: OpenAIReasoningEffort;
252
338
  temperature?: number;
253
339
  maxTokens?: number;
254
340
  responseFormat?: ResponseFormat;
@@ -293,6 +379,8 @@ interface MessageMetadata {
293
379
  * Message type (simplified for llm-sdk)
294
380
  */
295
381
  interface Message {
382
+ modelOutput?: ToolModelOutput;
383
+ providerMetadata?: ProviderMetadata;
296
384
  id: string;
297
385
  thread_id?: string;
298
386
  role: MessageRole;
@@ -363,7 +451,10 @@ interface AIContent {
363
451
  type?: "text" | "image";
364
452
  text?: string;
365
453
  mediaType?: string;
366
- data?: string;
454
+ data?: string | Uint8Array;
455
+ url?: string;
456
+ fileId?: string;
457
+ provider?: "openai";
367
458
  summary?: string;
368
459
  details?: string;
369
460
  }
@@ -379,6 +470,7 @@ interface ToolInputSchema {
379
470
  * Tool definition
380
471
  */
381
472
  interface ToolDefinition<TParams = Record<string, unknown>> {
473
+ toModelOutput?: (result: unknown) => ToolModelOutput | undefined;
382
474
  name: string;
383
475
  description: string;
384
476
  location: ToolLocation;
@@ -608,11 +700,14 @@ interface UserMessage {
608
700
  content: string | UserContentPart[];
609
701
  }
610
702
  interface AssistantMessage {
703
+ /** Provider state retained for continuation and inspection. */
704
+ providerMetadata?: ProviderMetadata;
611
705
  role: "assistant";
612
706
  content: string | null;
613
707
  toolCalls?: ToolCall[];
614
708
  }
615
709
  interface ToolMessage {
710
+ modelOutput?: ToolModelOutput;
616
711
  role: "tool";
617
712
  toolCallId: string;
618
713
  content: string;
@@ -633,6 +728,7 @@ interface ImagePart {
633
728
  mimeType?: string;
634
729
  }
635
730
  interface FilePart {
731
+ filename?: string;
636
732
  type: "file";
637
733
  /** Base64 data or URL */
638
734
  data: string;
@@ -649,6 +745,8 @@ interface Tool<TParams = unknown, TResult = unknown> {
649
745
  parameters: z.ZodType<TParams>;
650
746
  /** Execute function */
651
747
  execute: (params: TParams, context: ToolContext) => Promise<TResult>;
748
+ /** Map the raw result to explicit text/image content for the model. */
749
+ toModelOutput?: (result: TResult) => ToolModelOutput | undefined;
652
750
  /**
653
751
  * Hide this tool's execution from the chat UI.
654
752
  * When true, tool calls and results won't be displayed to the user,
@@ -683,6 +781,7 @@ interface ToolCall {
683
781
  * Tool execution result
684
782
  */
685
783
  interface ToolResult {
784
+ modelOutput?: ToolModelOutput;
686
785
  /** Tool call ID this result corresponds to */
687
786
  toolCallId: string;
688
787
  /** Result data (will be JSON stringified for LLM) */
@@ -692,10 +791,14 @@ interface ToolResult {
692
791
  * Parameters for model.doGenerate() and model.doStream()
693
792
  */
694
793
  interface DoGenerateParams {
794
+ /** OpenAI endpoint and Responses options. */
795
+ openai?: OpenAITransportOptions;
695
796
  /** Messages to send to LLM */
696
797
  messages: CoreMessage[];
697
798
  /** Tools available to the LLM (already formatted for provider) */
698
799
  tools?: unknown[];
800
+ /** OpenAI reasoning effort. Omit to preserve the model default. */
801
+ reasoningEffort?: OpenAIReasoningEffort;
699
802
  /** Temperature (0-2) */
700
803
  temperature?: number;
701
804
  /** Maximum tokens to generate */
@@ -709,6 +812,8 @@ interface DoGenerateParams {
709
812
  * Result from model.doGenerate()
710
813
  */
711
814
  interface DoGenerateResult {
815
+ /** Provider state retained for continuation and inspection. */
816
+ providerMetadata?: ProviderMetadata;
712
817
  /** Generated text content */
713
818
  text: string;
714
819
  /** Tool calls requested by the LLM */
@@ -745,11 +850,14 @@ interface ToolCallChunk {
745
850
  toolCall: ToolCall;
746
851
  }
747
852
  interface ToolResultChunk {
853
+ modelOutput?: ToolModelOutput;
748
854
  type: "tool-result";
749
855
  toolCallId: string;
750
856
  result: unknown;
751
857
  }
752
858
  interface FinishChunk {
859
+ /** Provider state retained for continuation and inspection. */
860
+ providerMetadata?: ProviderMetadata;
753
861
  type: "finish";
754
862
  finishReason: FinishReason;
755
863
  usage?: TokenUsage;
@@ -762,6 +870,8 @@ interface ErrorChunk {
762
870
  * Parameters for generateText()
763
871
  */
764
872
  interface GenerateTextParams {
873
+ /** OpenAI endpoint and Responses options. */
874
+ openai?: OpenAITransportOptions;
765
875
  /** Language model to use */
766
876
  model: LanguageModel;
767
877
  /** Simple prompt (converted to user message) */
@@ -771,9 +881,11 @@ interface GenerateTextParams {
771
881
  /** Full message history */
772
882
  messages?: CoreMessage[];
773
883
  /** Tools available to the LLM */
774
- tools?: Record<string, Tool>;
884
+ tools?: Record<string, Tool<any, any>>;
775
885
  /** Maximum agentic steps (tool call loops) */
776
886
  maxSteps?: number;
887
+ /** OpenAI reasoning effort. Omit to preserve the model default. */
888
+ reasoningEffort?: OpenAIReasoningEffort;
777
889
  /** Temperature (0-2) */
778
890
  temperature?: number;
779
891
  /** Maximum tokens to generate */
@@ -787,6 +899,8 @@ interface GenerateTextParams {
787
899
  * Result from generateText()
788
900
  */
789
901
  interface GenerateTextResult {
902
+ /** Provider state retained for continuation and inspection. */
903
+ providerMetadata?: ProviderMetadata;
790
904
  /** Final text output */
791
905
  text: string;
792
906
  /** Token usage */
@@ -808,6 +922,8 @@ interface GenerateTextResult {
808
922
  * A single step in the generation process
809
923
  */
810
924
  interface GenerateStep {
925
+ /** Provider state retained for continuation and inspection. */
926
+ providerMetadata?: ProviderMetadata;
811
927
  /** Text generated in this step */
812
928
  text: string;
813
929
  /** Tool calls made in this step */
@@ -827,6 +943,11 @@ type StreamTextParams = GenerateTextParams;
827
943
  * Result from streamText()
828
944
  */
829
945
  interface StreamTextResult {
946
+ /** Final conversation, including opaque provider state for replay. */
947
+ readonly response: Promise<{
948
+ messages: CoreMessage[];
949
+ }>;
950
+ readonly providerMetadata: Promise<ProviderMetadata | undefined>;
830
951
  /** Async iterable of text chunks only */
831
952
  textStream: AsyncIterable<string>;
832
953
  /** Async iterable of all stream parts */
@@ -863,6 +984,7 @@ type StreamPart = {
863
984
  type: "tool-result";
864
985
  toolCallId: string;
865
986
  result: unknown;
987
+ modelOutput?: ToolModelOutput;
866
988
  } | {
867
989
  type: "step-start";
868
990
  step: number;
@@ -870,10 +992,12 @@ type StreamPart = {
870
992
  type: "step-finish";
871
993
  step: number;
872
994
  finishReason: FinishReason;
995
+ providerMetadata?: ProviderMetadata;
873
996
  } | {
874
997
  type: "finish";
875
998
  finishReason: FinishReason;
876
999
  usage: TokenUsage;
1000
+ providerMetadata?: ProviderMetadata;
877
1001
  } | {
878
1002
  type: "error";
879
1003
  error: Error;
@@ -960,4 +1084,4 @@ interface StorageFile {
960
1084
  filename?: string;
961
1085
  }
962
1086
 
963
- export { type ToolExecution as $, type ActionDefinition as A, type ToolResultChunk as B, type CoreMessage as C, type DoneEventMessage as D, type FinishChunk as E, type FilePart as F, type GenerateTextParams as G, type ErrorChunk as H, type ImagePart as I, type TokenUsage as J, type KnowledgeBaseConfig as K, type LanguageModel as L, type Message as M, type FinishReason as N, type ResponseOptions as O, type ProviderToolRuntimeOptions as P, type StorageFile as Q, type ResponseFormat as R, type StreamTextParams as S, type ToolContext as T, type UserMessage as U, DEFAULT_CAPABILITIES as V, type WebSearchConfig as W, type LLMConfig as X, type ToolLocation as Y, type UnifiedToolCall as Z, type UnifiedToolResult as _, type GenerateTextResult as a, type OpenAIToolSelectionHints as a0, type AnthropicToolSelectionHints as a1, type ToolNativeProviderHints as a2, type OpenAIProviderToolOptions as a3, type AnthropicProviderToolOptions as a4, type Citation as a5, type MessageAttachment as a6, type StreamTextResult as b, type Tool as c, type ToolDefinition as d, type ToolProfile as e, type StorageAdapter as f, type StreamEvent as g, type ToolCallInfo as h, type TokenUsageRaw as i, type ToolResponse as j, type StorageMessage as k, type ModelCapabilities as l, type DoGenerateParams as m, type DoGenerateResult as n, type SystemMessage as o, type AssistantMessage as p, type ToolMessage as q, type UserContentPart as r, type TextPart as s, type ToolCall as t, type ToolResult as u, type GenerateStep as v, type StreamPart as w, type StreamChunk as x, type TextDeltaChunk as y, type ToolCallChunk as z };
1087
+ export { type LLMConfig as $, type ActionDefinition as A, type StreamChunk as B, type CoreMessage as C, type DoneEventMessage as D, type TextDeltaChunk as E, type FilePart as F, type GenerateTextParams as G, type ToolCallChunk as H, type ImagePart as I, type ToolResultChunk as J, type KnowledgeBaseConfig as K, type LanguageModel as L, type Message as M, type FinishChunk as N, type OpenAITransportOptions as O, type ProviderMetadata as P, type ErrorChunk as Q, type ResponseFormat as R, type StreamTextParams as S, type ToolContext as T, type UserMessage as U, type TokenUsage as V, type WebSearchConfig as W, type FinishReason as X, type ResponseOptions as Y, type StorageFile as Z, DEFAULT_CAPABILITIES as _, type GenerateTextResult as a, type ToolLocation as a0, type UnifiedToolCall as a1, type UnifiedToolResult as a2, type ToolExecution as a3, type OpenAIToolSelectionHints as a4, type AnthropicToolSelectionHints as a5, type ToolNativeProviderHints as a6, type OpenAIProviderToolOptions as a7, type AnthropicProviderToolOptions as a8, type Citation as a9, type OpenAIResponsesOptions as aa, type OpenAIResponseItem as ab, type OpenAIResponseMetadata as ac, type ToolOutputPart as ad, type OpenAIParameterOptions as ae, type MessageAttachment as af, type StreamTextResult as b, type ToolModelOutput as c, type Tool as d, type ToolDefinition as e, type ToolProfile as f, type StorageAdapter as g, type OpenAIReasoningEffort as h, type StreamEvent as i, type ToolCallInfo as j, type TokenUsageRaw as k, type ProviderToolRuntimeOptions as l, type ToolResponse as m, type StorageMessage as n, type ModelCapabilities as o, type DoGenerateParams as p, type DoGenerateResult as q, type SystemMessage as r, type AssistantMessage as s, type ToolMessage as t, type UserContentPart as u, type TextPart as v, type ToolCall as w, type ToolResult as x, type GenerateStep as y, type StreamPart as z };
@@ -1,13 +1,5 @@
1
- import { d as ToolDefinition, Z as UnifiedToolCall, _ as UnifiedToolResult } from './types-BkQCSiIt.mjs';
2
- import { L as LLMAdapter } from './base-tNgbBaSo.mjs';
3
-
4
- /**
5
- * Provider Types
6
- *
7
- * Defines interfaces for:
8
- * 1. Provider Formatters (for tool transformations in agent loop)
9
- * 2. Multi-provider architecture (AIProvider, capabilities, configs)
10
- */
1
+ import { ae as OpenAIParameterOptions, O as OpenAITransportOptions, e as ToolDefinition, a1 as UnifiedToolCall, a2 as UnifiedToolResult } from './types-Dg1Bcg1k.js';
2
+ import { L as LLMAdapter } from './base-CpRPB8u8.js';
11
3
 
12
4
  /**
13
5
  * Provider formatter interface
@@ -259,7 +251,7 @@ interface BaseProviderConfig {
259
251
  /**
260
252
  * OpenAI provider configuration
261
253
  */
262
- interface OpenAIProviderConfig extends BaseProviderConfig {
254
+ interface OpenAIProviderConfig extends BaseProviderConfig, OpenAIParameterOptions, OpenAITransportOptions {
263
255
  /** OpenAI organization ID */
264
256
  organization?: string;
265
257
  /** OpenAI project ID */
@@ -1,4 +1,4 @@
1
- import { k as StorageMessage, f as StorageAdapter, Q as StorageFile } from '../types-BkQCSiIt.mjs';
1
+ import { n as StorageMessage, g as StorageAdapter, Z as StorageFile } from '../types-Dg1Bcg1k.mjs';
2
2
  import 'zod';
3
3
 
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { k as StorageMessage, f as StorageAdapter, Q as StorageFile } from '../types-BkQCSiIt.js';
1
+ import { n as StorageMessage, g as StorageAdapter, Z as StorageFile } from '../types-Dg1Bcg1k.js';
2
2
  import 'zod';
3
3
 
4
4
  /**
@@ -0,0 +1,170 @@
1
+ # Image tool results and recorded audio
2
+
3
+ These APIs are additive. Chat configuration, ordinary JSON/string tool results, and the existing screenshot `addAsUserMessage` attachment path retain their behavior. Native image tool output requires OpenAI **Responses** or Anthropic. TTS and recorded-file STT currently have an OpenAI implementation; other providers can implement the separate `SpeechModel` / `TranscriptionModel` contracts.
4
+
5
+ ## Tools that return images
6
+
7
+ A tool still receives schema-validated JSON arguments. Pass an authorized image ID, URL, or crop reference in those arguments; resolve the actual bytes inside your handler. Do not ask the model to reproduce image base64 in function arguments.
8
+
9
+ ```ts
10
+ import { generateText, tool } from "@yourgpt/llm-sdk";
11
+ import { openai } from "@yourgpt/llm-sdk/openai";
12
+ import { z } from "zod";
13
+
14
+ const result = await generateText({
15
+ model: openai("gpt-5.2", { api: "responses", responses: { store: false } }),
16
+ prompt: "Inspect image sample.",
17
+ maxSteps: 3,
18
+ tools: {
19
+ inspect_image: tool({
20
+ description: "Read an image the caller is authorized to access",
21
+ parameters: z.object({ imageId: z.string() }),
22
+ execute: async ({ imageId }) => loadAuthorizedImage(imageId),
23
+ toModelOutput: (image) => ({
24
+ type: "content",
25
+ content: [
26
+ { type: "text", text: image.caption },
27
+ { type: "image", data: image.bytes, mediaType: "image/png" },
28
+ ],
29
+ }),
30
+ }),
31
+ },
32
+ });
33
+ // result.toolResults keeps the raw handler result for your application.
34
+ // result.response.messages retains the model-facing content for the next turn.
35
+ ```
36
+
37
+ `toModelOutput` is a synchronous mapper, called once when a tool completes. It is also available on Runtime and Copilot tool definitions. For existing Runtime/Copilot handlers, returning `{ success: true, _aiContent: [...] }` uses the same native path. Explicit `toModelOutput` takes precedence. MCP image results keep their accompanying text in order.
38
+
39
+ Supported image parts:
40
+
41
+ | Part | OpenAI Responses | Anthropic |
42
+ | ----------------------------------------------- | ----------------------------------- | --------- |
43
+ | `{ type: 'image', data, mediaType }` | Yes | Yes |
44
+ | `{ type: 'image', url: 'https://…' }` | Yes | Yes |
45
+ | `{ type: 'image', fileId, provider: 'openai' }` | Yes, with an accessible OpenAI file | Rejected |
46
+
47
+ `data` accepts `Uint8Array`, canonical base64, or a base64 data URL matching `mediaType`. Supported media types: PNG, JPEG, WebP, GIF. Images are limited to 20 MiB each; provider/model aggregate limits can be smaller. The SDK does not download URLs or upload files implicitly. URL fetching happens at the selected provider. File references remain scoped to that provider/account.
48
+
49
+ OpenAI Chat Completions, Gemini, Ollama, Azure Chat and other compatible endpoints reject native image tool output clearly. Text-only mapped content still works. Custom OpenAI endpoints require explicit Responses selection and their own compatibility validation. Arbitrary array-shaped JSON results are never assumed to contain images.
50
+
51
+ Native output is carried as `modelOutput` next to the tool message's text. Preserve it when storing/restoring messages, along with `tool_call_id` / `toolCallId` and assistant tool calls. The Runtime storage helper places it at `StorageMessage.metadata.modelOutput`; a custom loader must map it back to `modelOutput`. Copilot's serialized `UIMessage` history preserves it directly, including server results in streaming execution metadata. Do not truncate image base64 or substitute it with text. Context pruning can remove complete older tool exchanges.
52
+
53
+ If an existing screenshot handler explicitly uses `addAsUserMessage`, it continues to add an attachment. When native model output is also supplied, the native output takes precedence and no extra attachment turn is created. General image generation is separate and is not implemented here.
54
+
55
+ ## Speech generation on the server
56
+
57
+ ```ts
58
+ import { generateSpeech } from "@yourgpt/llm-sdk";
59
+ import { openaiSpeech } from "@yourgpt/llm-sdk/openai";
60
+
61
+ const speech = await generateSpeech({
62
+ model: openaiSpeech("gpt-4o-mini-tts"), // reads server OPENAI_API_KEY
63
+ text: "Hello from Copilot.",
64
+ voice: "alloy",
65
+ outputFormat: "wav",
66
+ instructions: "Speak clearly and warmly.",
67
+ speed: 1,
68
+ signal: request.signal,
69
+ });
70
+ return new Response(new Uint8Array(speech.audio.uint8Array), {
71
+ headers: {
72
+ "Content-Type": speech.audio.mediaType,
73
+ "Cache-Control": "no-store",
74
+ },
75
+ });
76
+ ```
77
+
78
+ Returns audio bytes, a lazy `audio.base64` getter, media type/format, warnings, and provider/model/request metadata. Usage is optional and is not invented when the endpoint does not return it. This helper buffers the audio; it is not a realtime voice session or streamed audio API.
79
+
80
+ Formats: MP3, Opus/Ogg, AAC, FLAC, WAV, and raw PCM. OpenAI PCM is 24 kHz, signed 16-bit little-endian without a container header; use WAV/MP3 for ordinary browser playback. Text/instructions are bounded at 4096 characters, speed at 0.25–4. `tts-1` and `tts-1-hd` reject instructions. Unsupported options fail explicitly. Responses are bounded at 25 MB and checked for a matching audio header; the helper does not perform full codec decoding.
81
+
82
+ Browser playback, from a user click:
83
+
84
+ ```ts
85
+ const response = await fetch("/api/speech", {
86
+ method: "POST",
87
+ headers: { "Content-Type": "application/json" },
88
+ body: JSON.stringify({ text: "Hello from Copilot." }),
89
+ });
90
+ if (!response.ok) throw new Error("Speech generation failed");
91
+ const url = URL.createObjectURL(await response.blob());
92
+ const player = new Audio(url);
93
+ const release = () => URL.revokeObjectURL(url);
94
+ player.addEventListener("ended", release, { once: true });
95
+ player.addEventListener("error", release, { once: true });
96
+ try {
97
+ await player.play();
98
+ } catch (error) {
99
+ release();
100
+ throw error;
101
+ }
102
+ // On component disposal: player.pause(); release();
103
+ ```
104
+
105
+ Label generated speech as AI-generated. Keep provider keys and authentication/rate limiting in your server route.
106
+
107
+ ## File transcription on the server
108
+
109
+ ```ts
110
+ import { transcribe } from "@yourgpt/llm-sdk";
111
+ import { openaiTranscription } from "@yourgpt/llm-sdk/openai";
112
+
113
+ // POST /api/transcribe, behind your application's authentication.
114
+ const form = await request.formData();
115
+ const file = form.get("file");
116
+ if (!(file instanceof Blob))
117
+ return new Response("Audio file required", { status: 400 });
118
+ const transcript = await transcribe({
119
+ model: openaiTranscription("gpt-4o-mini-transcribe"),
120
+ audio: file,
121
+ language: "en",
122
+ signal: request.signal,
123
+ });
124
+ return Response.json(transcript);
125
+ ```
126
+
127
+ Accepts a Blob, bytes/ArrayBuffer, or base64; bytes/base64 require `mediaType`. A filename is generated from the type unless supplied. No remote URL fetch occurs. Encoded files are limited to 25 MB and require a matching container header and filename extension. Accepted containers: MP3/MPEG/MPGA, MP4/M4A, WAV, WebM, Ogg, and FLAC. Raw PCM belongs to a separately configured realtime transcription protocol.
128
+
129
+ `text`, provider metadata and usage are returned. Segments, word timestamps, speakers, language and duration are preserved when supplied by the provider. An empty transcript is a valid outcome for silence; missing/non-string text is an invalid response.
130
+
131
+ For timestamps use `openaiTranscription('whisper-1')` with `providerOptions: { openai: { timestampGranularities: ['word', 'segment'] } }`. For speaker labels use `openaiTranscription('gpt-4o-transcribe-diarize')`; it selects `diarized_json` and automatic chunking. Diarization does not accept prompts or timestamp granularity options. Compatible GPT-4o transcription models can request `includeLogprobs: true`. The initial slice does not expose all options of the newer `gpt-transcribe` model (for example keyword lists or multiple language hints).
132
+
133
+ Both audio helpers accept `signal`, preserve cancellation, and expose `AudioAPIError` with a stable `code`, HTTP `status` and provider `requestId` when available. They do not log audio/text or retain provider error bodies. There are no automatic chargeable retries.
134
+
135
+ ## Copilot microphone recording and review
136
+
137
+ `VoiceRecorder` is framework-independent and exported from `@yourgpt/copilot-sdk/core`. It makes no permission request until `start()` is called. Call it from a user gesture on HTTPS/localhost.
138
+
139
+ ```ts
140
+ import { VoiceRecorder, transcribeRecording } from "@yourgpt/copilot-sdk/core";
141
+
142
+ const recorder = new VoiceRecorder({
143
+ onStateChange: (state) => updateRecordingIndicator(state),
144
+ onError: (error) => showError(error.message),
145
+ });
146
+ let upload: AbortController | undefined;
147
+
148
+ // Record button:
149
+ await recorder.start();
150
+
151
+ // Stop button:
152
+ const recording = await recorder.stop();
153
+ upload = new AbortController();
154
+ const transcript = await transcribeRecording(recording, {
155
+ url: "/api/transcribe",
156
+ signal: upload.signal,
157
+ });
158
+ setChatDraft(transcript.text); // Review/edit, then submit through the usual chat flow.
159
+ // transcript.provenance identifies recorded audio and the server provider/model.
160
+
161
+ // Cancel button or component disposal:
162
+ upload?.abort();
163
+ recorder.dispose();
164
+ ```
165
+
166
+ The recorder negotiates supported WebM/Opus, MP4, or Ogg/Opus containers. Stop, cancel, permission races, recorder errors and disposal release microphone tracks. Defaults: 20 MB and 60 seconds; exceeding either discards the recording and invokes `onError`. No automatic message submission, voice cloning, telephony, live transcription, or bidirectional voice call is included.
167
+
168
+ ## Verification sources
169
+
170
+ Provider formats were checked against [OpenAI function outputs](https://developers.openai.com/api/docs/guides/function-calling), [Anthropic tool results](https://platform.claude.com/docs/en/agents-and-tools/tool-use/handle-tool-calls), [OpenAI speech](https://developers.openai.com/api/reference/resources/audio/subresources/speech/methods/create), and [OpenAI transcription](https://developers.openai.com/api/reference/resources/audio/subresources/transcriptions/methods/create). Vercel comparisons use its [tool output guidance](https://ai-sdk.dev/docs/ai-sdk-core/tools-and-tool-calling), [speech](https://ai-sdk.dev/docs/ai-sdk-core/speech), [transcription](https://ai-sdk.dev/docs/ai-sdk-core/transcription), and source checkout `8a5233b15b1e69573d63faee6cd0014559252ee4` (2026-10-15).