smoltalk 0.5.1 → 0.7.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.
Files changed (77) hide show
  1. package/README.md +67 -10
  2. package/dist/classes/message/UserMessage.d.ts +66 -5
  3. package/dist/classes/message/UserMessage.js +100 -21
  4. package/dist/classes/message/contentParts.d.ts +259 -0
  5. package/dist/classes/message/contentParts.js +44 -0
  6. package/dist/classes/message/index.d.ts +8 -2
  7. package/dist/classes/message/index.js +12 -0
  8. package/dist/classes/message/renderers/AnthropicRenderer.d.ts +43 -0
  9. package/dist/classes/message/renderers/AnthropicRenderer.js +19 -0
  10. package/dist/classes/message/renderers/GoogleRenderer.d.ts +35 -0
  11. package/dist/classes/message/renderers/GoogleRenderer.js +26 -0
  12. package/dist/classes/message/renderers/JSONRenderer.d.ts +8 -0
  13. package/dist/classes/message/renderers/JSONRenderer.js +21 -0
  14. package/dist/classes/message/renderers/OpenAIChatRenderer.d.ts +30 -0
  15. package/dist/classes/message/renderers/OpenAIChatRenderer.js +20 -0
  16. package/dist/classes/message/renderers/OpenAIResponsesRenderer.d.ts +39 -0
  17. package/dist/classes/message/renderers/OpenAIResponsesRenderer.js +23 -0
  18. package/dist/classes/message/renderers/PartRenderer.d.ts +14 -0
  19. package/dist/classes/message/renderers/PartRenderer.js +16 -0
  20. package/dist/client.d.ts +4 -0
  21. package/dist/client.js +37 -16
  22. package/dist/clients/anthropic.d.ts +24 -2
  23. package/dist/clients/anthropic.js +9 -4
  24. package/dist/clients/baseClient.d.ts +7 -0
  25. package/dist/clients/baseClient.js +39 -0
  26. package/dist/clients/deepinfra.d.ts +17 -0
  27. package/dist/clients/deepinfra.js +27 -0
  28. package/dist/clients/google.js +6 -4
  29. package/dist/clients/litellm.d.ts +19 -0
  30. package/dist/clients/litellm.js +32 -0
  31. package/dist/clients/ollama.js +12 -9
  32. package/dist/clients/openai.d.ts +41 -5
  33. package/dist/clients/openai.js +78 -14
  34. package/dist/clients/openaiCompat.d.ts +22 -0
  35. package/dist/clients/openaiCompat.js +33 -0
  36. package/dist/clients/openaiResponses.js +6 -4
  37. package/dist/clients/openrouter.d.ts +23 -0
  38. package/dist/clients/openrouter.js +76 -0
  39. package/dist/clients/resolveAttachments.d.ts +11 -0
  40. package/dist/clients/resolveAttachments.js +108 -0
  41. package/dist/embed/openai.d.ts +7 -1
  42. package/dist/embed/openai.js +8 -2
  43. package/dist/embed.d.ts +18 -4
  44. package/dist/embed.js +32 -4
  45. package/dist/files/BaseFileProvider.d.ts +29 -0
  46. package/dist/files/BaseFileProvider.js +38 -0
  47. package/dist/files/anthropic.d.ts +16 -0
  48. package/dist/files/anthropic.js +20 -0
  49. package/dist/files/google.d.ts +18 -0
  50. package/dist/files/google.js +38 -0
  51. package/dist/files/openai.d.ts +16 -0
  52. package/dist/files/openai.js +23 -0
  53. package/dist/files.d.ts +46 -0
  54. package/dist/files.js +70 -0
  55. package/dist/image/openaiCompat.d.ts +9 -0
  56. package/dist/image/openaiCompat.js +60 -0
  57. package/dist/image.d.ts +13 -2
  58. package/dist/image.js +28 -3
  59. package/dist/index.d.ts +4 -0
  60. package/dist/index.js +3 -0
  61. package/dist/models.d.ts +22 -3
  62. package/dist/models.js +46 -3
  63. package/dist/statelogClient.js +2 -1
  64. package/dist/types.d.ts +28 -18
  65. package/dist/types.js +1 -4
  66. package/dist/util/attachments.d.ts +34 -0
  67. package/dist/util/attachments.js +62 -0
  68. package/dist/util/hostedTools.js +5 -1
  69. package/dist/util/imageRef.d.ts +16 -1
  70. package/dist/util/imageRef.js +95 -10
  71. package/dist/util/modalities.d.ts +2 -0
  72. package/dist/util/modalities.js +31 -0
  73. package/dist/util/provider.d.ts +32 -6
  74. package/dist/util/provider.js +38 -4
  75. package/dist/util/redact.d.ts +8 -0
  76. package/dist/util/redact.js +42 -0
  77. package/package.json +1 -1
package/README.md CHANGED
@@ -8,6 +8,15 @@ Smoltalk exposes a common API to different LLM providers, with built-in cost tra
8
8
  pnpm install smoltalk
9
9
  ```
10
10
 
11
+ > **Upgrading to 0.6.x?** The flat API-key/host fields on `SmolConfig` have
12
+ > been removed in favor of nested `apiKey` and `baseUrl` maps. Migration:
13
+ > ```diff
14
+ > -{ openAiApiKey: "sk-...", googleApiKey: "...", ollamaHost: "http://..." }
15
+ > +{ apiKey: { openAi: "sk-...", google: "..." }, baseUrl: { ollama: "http://..." } }
16
+ > ```
17
+ > Env-var fallbacks are unchanged (`OPENAI_API_KEY`, `GEMINI_API_KEY`,
18
+ > `ANTHROPIC_API_KEY`, `OLLAMA_HOST`).
19
+
11
20
  ## Hello world example
12
21
 
13
22
  ```typescript
@@ -68,8 +77,10 @@ const messages = [
68
77
  const resp = await text({
69
78
  messages,
70
79
  model: "gemini-2.0-flash-lite",
71
- openAiApiKey: process.env.OPENAI_API_KEY || "",
72
- googleApiKey: process.env.GEMINI_API_KEY || "",
80
+ apiKey: {
81
+ openAi: process.env.OPENAI_API_KEY || "",
82
+ google: process.env.GEMINI_API_KEY || "",
83
+ },
73
84
  logLevel: "debug",
74
85
  });
75
86
  ```
@@ -80,8 +91,10 @@ If you want to construct a client once and reuse it across many calls, use `getC
80
91
  import { getClient, userMessage } from "smoltalk";
81
92
 
82
93
  const client = getClient({
83
- openAiApiKey: process.env.OPENAI_API_KEY || "",
84
- googleApiKey: process.env.GEMINI_API_KEY || "",
94
+ apiKey: {
95
+ openAi: process.env.OPENAI_API_KEY || "",
96
+ google: process.env.GEMINI_API_KEY || "",
97
+ },
85
98
  model: "gemini-2.0-flash-lite",
86
99
  });
87
100
 
@@ -147,12 +160,9 @@ A couple of design decisions to note:
147
160
  |--------|------|-------------|
148
161
  | `model` | `ModelName` | **Required.** The model to use (e.g. `"gpt-4o"`, `"gemini-2.0-flash-lite"`). |
149
162
  | `messages` | `Message[]` | **Required.** The conversation messages to send. |
150
- | `openAiApiKey` | `string` | OpenAI API key. |
151
- | `googleApiKey` | `string` | Google Gemini API key. |
152
- | `anthropicApiKey` | `string` | Anthropic API key. |
153
- | `ollamaApiKey` | `string` | Ollama API key (only needed for cloud Ollama). |
154
- | `ollamaHost` | `string` | Ollama host URL (for self-hosted or cloud Ollama). |
155
- | `provider` | `Provider` | Override provider detection. One of `"openai"`, `"openai-responses"`, `"google"`, `"ollama"`, `"anthropic"`, or any provider registered via `registerProvider()`. |
163
+ | `apiKey` | `{ openAi?, google?, anthropic?, ollama?, openRouter?, deepInfra?, liteLlm?, openAiCompat? }` | API keys, nested by provider. Each falls back to its conventional env var (`OPENAI_API_KEY`, `GEMINI_API_KEY`, `ANTHROPIC_API_KEY`, `OPENROUTER_API_KEY`, `DEEPINFRA_API_KEY`, `LITELLM_API_KEY`, `OPENAI_COMPAT_API_KEY`). Ollama has no env-var fallback for the key. |
164
+ | `baseUrl` | `{ ollama?, openRouter?, deepInfra?, liteLlm?, openAiCompat? }` | Custom base URLs. `ollama` defaults to `$OLLAMA_HOST` then `http://localhost:11434`; `openRouter`/`deepInfra` defaults are baked in; `liteLlm`/`openAiCompat` require an explicit URL (or `LITELLM_BASE_URL` / `OPENAI_COMPAT_BASE_URL` env). |
165
+ | `provider` | `Provider` | Override provider detection. One of `"openai"`, `"openai-responses"`, `"google"`, `"ollama"`, `"anthropic"`, `"openrouter"`, `"deepinfra"`, `"litellm"`, `"openai-compat"`, or any provider registered via `registerProvider()`. |
156
166
  | `logLevel` | `LogLevel` | Logging verbosity: `"debug"`, `"info"`, `"warn"`, `"error"`. |
157
167
  | `tools` | `{ name, description?, schema }[]` | Tool definitions. `schema` is a Zod object schema. |
158
168
  | `responseFormat` | `ZodType` | Zod schema for structured output. The response is parsed and validated against this schema. |
@@ -194,6 +204,53 @@ Detects when the model is stuck in a repetitive tool-call loop.
194
204
  | `intervention` | `string` | Action to take: `"remove-tool"`, `"remove-all-tools"`, `"throw-error"`, or `"halt-execution"`. |
195
205
  | `excludeTools` | `string[]` | Tool names to ignore when counting calls. |
196
206
 
207
+ ## Hosted OpenAI-compatible providers
208
+
209
+ Smoltalk ships four built-in providers for hosted open-source models that all
210
+ speak the OpenAI chat-completions shape. Use these when you want to run a
211
+ Llama, GLM, Qwen, etc. via someone else's hosted infrastructure without
212
+ adding a new dependency. You must pass `provider:` explicitly because these
213
+ model ids aren't in the smoltalk registry.
214
+
215
+ | `provider:` | What it is | Required config | Cost source |
216
+ |-------------|------------|-----------------|-------------|
217
+ | `"openrouter"` | OpenRouter.ai aggregator | `apiKey.openRouter` (or `OPENROUTER_API_KEY`) | `usage.cost` (auto-enabled by injecting `usage:{include:true}`) |
218
+ | `"deepinfra"` | DeepInfra hosted models | `apiKey.deepInfra` (or `DEEPINFRA_API_KEY`) | `usage.estimated_cost` |
219
+ | `"litellm"` | Your own LiteLLM proxy | `apiKey.liteLlm` + `baseUrl.liteLlm` (or `LITELLM_API_KEY` / `LITELLM_BASE_URL`) | `x-litellm-response-cost` header (non-stream only) |
220
+ | `"openai-compat"` | Any OpenAI-shape backend (vLLM, TGI, LM Studio…) | `apiKey.openAiCompat` + `baseUrl.openAiCompat` (or `OPENAI_COMPAT_API_KEY` / `OPENAI_COMPAT_BASE_URL`) | Best-effort: reads `usage.cost`/`estimated_cost`/`cost_usd` if present |
221
+
222
+ ```ts
223
+ import { textSync, userMessage } from "smoltalk";
224
+
225
+ const r = await textSync({
226
+ model: "z-ai/glm-5.2",
227
+ provider: "openrouter",
228
+ apiKey: { openRouter: process.env.OPENROUTER_API_KEY! },
229
+ messages: [userMessage("hi")],
230
+ });
231
+ // r.value.cost.totalCost is a real OpenRouter-reported USD cost.
232
+ ```
233
+
234
+ **Capability matrix:**
235
+
236
+ | | chat | embeddings | image generation | `web_search` hosted tool |
237
+ |-----------------|------|------------|------------------|--------------------------|
238
+ | `openrouter` | ✅ | ❌ | ❌ | ✅ (via `:online` / web plugin) |
239
+ | `deepinfra` | ✅ | ✅ | ❌ (uses per-model endpoints, not OpenAI shape) | ❌ |
240
+ | `litellm` | ✅ | ✅ | ✅ (if the upstream model supports it) | ✅ (if upstream supports it) |
241
+ | `openai-compat` | ✅ | ✅ | ✅ (backend-dependent) | depends on backend |
242
+
243
+ Smoltalk surfaces a clear `failure(...)` from `embed()`/`image()` for the
244
+ unsupported combinations rather than silently dropping the call.
245
+
246
+ **Running a local LiteLLM proxy:**
247
+
248
+ ```bash
249
+ pip install 'litellm[proxy]'
250
+ litellm --model openai/gpt-4o
251
+ # In your code: baseUrl: { liteLlm: "http://localhost:4000" }
252
+ ```
253
+
197
254
  ## Refreshing model data
198
255
 
199
256
  Smoltalk ships a baked-in model registry (pricing, context limits, capabilities).
@@ -4,27 +4,88 @@ import { ChatCompletionMessageParam } from "openai/resources";
4
4
  import { Content } from "@google/genai";
5
5
  import { Message } from "ollama";
6
6
  import type { ResponseInputItem } from "openai/resources/responses/responses.js";
7
+ import { UserContent, UserContentInput, UserContentPart } from "./contentParts.js";
7
8
  export declare const UserMessageJSONSchema: z.ZodObject<{
8
9
  role: z.ZodLiteral<"user">;
9
- content: z.ZodString;
10
+ content: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
11
+ type: z.ZodLiteral<"text">;
12
+ text: z.ZodString;
13
+ }, z.core.$strip>, z.ZodObject<{
14
+ type: z.ZodLiteral<"image">;
15
+ source: z.ZodDiscriminatedUnion<[z.ZodObject<{
16
+ kind: z.ZodLiteral<"bytes">;
17
+ data: z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>;
18
+ mimeType: z.ZodString;
19
+ }, z.core.$strip>, z.ZodObject<{
20
+ kind: z.ZodLiteral<"base64">;
21
+ base64: z.ZodString;
22
+ mimeType: z.ZodString;
23
+ }, z.core.$strip>, z.ZodObject<{
24
+ kind: z.ZodLiteral<"path">;
25
+ path: z.ZodString;
26
+ mimeType: z.ZodOptional<z.ZodString>;
27
+ }, z.core.$strip>, z.ZodObject<{
28
+ kind: z.ZodLiteral<"url">;
29
+ url: z.ZodString;
30
+ mimeType: z.ZodOptional<z.ZodString>;
31
+ timeoutMs: z.ZodOptional<z.ZodNumber>;
32
+ }, z.core.$strip>, z.ZodObject<{
33
+ kind: z.ZodLiteral<"providerFile">;
34
+ provider: z.ZodString;
35
+ id: z.ZodString;
36
+ uri: z.ZodOptional<z.ZodString>;
37
+ mimeType: z.ZodOptional<z.ZodString>;
38
+ expiresAt: z.ZodOptional<z.ZodNumber>;
39
+ }, z.core.$strip>], "kind">;
40
+ }, z.core.$strip>, z.ZodObject<{
41
+ type: z.ZodLiteral<"file">;
42
+ source: z.ZodDiscriminatedUnion<[z.ZodObject<{
43
+ kind: z.ZodLiteral<"bytes">;
44
+ data: z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>;
45
+ mimeType: z.ZodString;
46
+ }, z.core.$strip>, z.ZodObject<{
47
+ kind: z.ZodLiteral<"base64">;
48
+ base64: z.ZodString;
49
+ mimeType: z.ZodString;
50
+ }, z.core.$strip>, z.ZodObject<{
51
+ kind: z.ZodLiteral<"path">;
52
+ path: z.ZodString;
53
+ mimeType: z.ZodOptional<z.ZodString>;
54
+ }, z.core.$strip>, z.ZodObject<{
55
+ kind: z.ZodLiteral<"url">;
56
+ url: z.ZodString;
57
+ mimeType: z.ZodOptional<z.ZodString>;
58
+ timeoutMs: z.ZodOptional<z.ZodNumber>;
59
+ }, z.core.$strip>, z.ZodObject<{
60
+ kind: z.ZodLiteral<"providerFile">;
61
+ provider: z.ZodString;
62
+ id: z.ZodString;
63
+ uri: z.ZodOptional<z.ZodString>;
64
+ mimeType: z.ZodOptional<z.ZodString>;
65
+ expiresAt: z.ZodOptional<z.ZodNumber>;
66
+ }, z.core.$strip>], "kind">;
67
+ filename: z.ZodOptional<z.ZodString>;
68
+ }, z.core.$strip>], "type">>]>;
10
69
  name: z.ZodOptional<z.ZodString>;
11
70
  rawData: z.ZodOptional<z.ZodAny>;
12
71
  }, z.core.$strip>;
13
72
  export type UserMessageJSON = z.infer<typeof UserMessageJSONSchema>;
14
73
  export declare class UserMessage extends BaseMessage implements MessageClass {
15
74
  _role: "user";
16
- _content: string;
75
+ _content: UserContent;
17
76
  _name?: string;
18
77
  _rawData?: any;
19
- constructor(content: string, options?: {
78
+ constructor(content: UserContentInput, options?: {
20
79
  name?: string;
21
80
  rawData?: any;
22
81
  });
23
82
  get content(): string;
24
- set content(value: string);
83
+ set content(value: UserContentInput);
25
84
  get role(): "user";
26
85
  get name(): string | undefined;
27
86
  get rawData(): any;
87
+ /** The content parts array, or null when content is a plain string. */
88
+ getContentParts(): UserContentPart[] | null;
28
89
  toJSON(): UserMessageJSON;
29
90
  static fromJSON(json: unknown): UserMessage;
30
91
  toOpenAIMessage(): ChatCompletionMessageParam;
@@ -33,6 +94,6 @@ export declare class UserMessage extends BaseMessage implements MessageClass {
33
94
  toOllamaMessage(): Message;
34
95
  toAnthropicMessage(): {
35
96
  role: "user";
36
- content: string;
97
+ content: string | any[];
37
98
  };
38
99
  }
@@ -1,9 +1,17 @@
1
1
  import { z } from "zod";
2
2
  import { BaseMessage } from "./BaseMessage.js";
3
3
  import { getLogger } from "../../util/logger.js";
4
+ import { UserContentSchema, } from "./contentParts.js";
5
+ import { refToBase64 } from "../../util/attachments.js";
6
+ import { renderParts } from "./renderers/PartRenderer.js";
7
+ import { OpenAIChatRenderer } from "./renderers/OpenAIChatRenderer.js";
8
+ import { OpenAIResponsesRenderer } from "./renderers/OpenAIResponsesRenderer.js";
9
+ import { GoogleRenderer } from "./renderers/GoogleRenderer.js";
10
+ import { AnthropicRenderer } from "./renderers/AnthropicRenderer.js";
11
+ import { JSONRenderer } from "./renderers/JSONRenderer.js";
4
12
  export const UserMessageJSONSchema = z.object({
5
13
  role: z.literal("user"),
6
- content: z.string(),
14
+ content: UserContentSchema,
7
15
  name: z.string().optional(),
8
16
  rawData: z.any().optional(),
9
17
  });
@@ -14,15 +22,15 @@ export class UserMessage extends BaseMessage {
14
22
  _rawData;
15
23
  constructor(content, options = {}) {
16
24
  super();
17
- this._content = content;
25
+ this._content = normalizeUserContent(content);
18
26
  this._name = options.name;
19
27
  this._rawData = options.rawData;
20
28
  }
21
29
  get content() {
22
- return this._content;
30
+ return userContentToText(this._content);
23
31
  }
24
32
  set content(value) {
25
- this._content = value;
33
+ this._content = normalizeUserContent(value);
26
34
  }
27
35
  get role() {
28
36
  return this._role;
@@ -33,10 +41,17 @@ export class UserMessage extends BaseMessage {
33
41
  get rawData() {
34
42
  return this._rawData;
35
43
  }
44
+ /** The content parts array, or null when content is a plain string. */
45
+ getContentParts() {
46
+ if (typeof this._content === "string") {
47
+ return null;
48
+ }
49
+ return this._content;
50
+ }
36
51
  toJSON() {
37
52
  return {
38
53
  role: this.role,
39
- content: this.content,
54
+ content: serializeUserContentForJSON(this._content),
40
55
  name: this.name,
41
56
  };
42
57
  }
@@ -55,29 +70,93 @@ export class UserMessage extends BaseMessage {
55
70
  });
56
71
  }
57
72
  toOpenAIMessage() {
58
- return {
59
- role: this.role,
60
- content: this.content,
61
- name: this.name,
62
- };
73
+ if (typeof this._content === "string") {
74
+ return { role: this.role, content: this._content, name: this.name };
75
+ }
76
+ const parts = renderParts(this._content, new OpenAIChatRenderer());
77
+ return { role: this.role, content: parts, name: this.name };
63
78
  }
64
79
  toOpenAIResponseInputItem() {
65
- return {
66
- type: "message",
67
- role: "user",
68
- content: this.content,
69
- };
80
+ if (typeof this._content === "string") {
81
+ return { type: "message", role: "user", content: this._content };
82
+ }
83
+ const content = renderParts(this._content, new OpenAIResponsesRenderer());
84
+ return { type: "message", role: "user", content };
70
85
  }
71
86
  toGoogleMessage() {
72
- return { role: this.role, parts: [{ text: this.content }] };
87
+ if (typeof this._content === "string") {
88
+ return { role: this.role, parts: [{ text: this._content }] };
89
+ }
90
+ const parts = renderParts(this._content, new GoogleRenderer());
91
+ return { role: this.role, parts };
73
92
  }
74
93
  toOllamaMessage() {
75
- return {
76
- role: this.role,
77
- content: this.content,
78
- };
94
+ if (typeof this._content === "string") {
95
+ return { role: this.role, content: this._content };
96
+ }
97
+ const texts = [];
98
+ const images = [];
99
+ for (const part of this._content) {
100
+ if (part.type === "text") {
101
+ texts.push(part.text);
102
+ continue;
103
+ }
104
+ if (part.type === "image") {
105
+ if (part.source.kind === "providerFile") {
106
+ throw new Error("Ollama does not support provider file references.");
107
+ }
108
+ images.push(refToBase64(part.source).base64);
109
+ continue;
110
+ }
111
+ if (part.source.kind === "providerFile") {
112
+ throw new Error("Ollama does not support provider file references.");
113
+ }
114
+ getLogger().warn("Ollama does not support file attachments; dropping a file part.");
115
+ }
116
+ const message = { role: this.role, content: texts.join("\n") };
117
+ if (images.length > 0) {
118
+ message.images = images;
119
+ }
120
+ return message;
79
121
  }
80
122
  toAnthropicMessage() {
81
- return { role: "user", content: this.content };
123
+ if (typeof this._content === "string") {
124
+ return { role: "user", content: this._content };
125
+ }
126
+ const blocks = renderParts(this._content, new AnthropicRenderer());
127
+ return { role: "user", content: blocks };
128
+ }
129
+ }
130
+ function normalizeUserContent(content) {
131
+ if (typeof content === "string") {
132
+ return content;
133
+ }
134
+ const parts = [];
135
+ for (const part of content) {
136
+ if (typeof part === "string") {
137
+ parts.push({ type: "text", text: part });
138
+ }
139
+ else {
140
+ parts.push(part);
141
+ }
142
+ }
143
+ return parts;
144
+ }
145
+ function userContentToText(content) {
146
+ if (typeof content === "string") {
147
+ return content;
148
+ }
149
+ const texts = [];
150
+ for (const part of content) {
151
+ if (part.type === "text") {
152
+ texts.push(part.text);
153
+ }
154
+ }
155
+ return texts.join("\n");
156
+ }
157
+ function serializeUserContentForJSON(content) {
158
+ if (typeof content === "string") {
159
+ return content;
82
160
  }
161
+ return renderParts(content, new JSONRenderer());
83
162
  }
@@ -0,0 +1,259 @@
1
+ import { z } from "zod";
2
+ import type { ImageRef } from "../../util/imageRef.js";
3
+ export type TextPart = {
4
+ type: "text";
5
+ text: string;
6
+ };
7
+ export type ImagePart = {
8
+ type: "image";
9
+ source: AttachmentSource;
10
+ };
11
+ export type FilePart = {
12
+ type: "file";
13
+ source: AttachmentSource;
14
+ filename?: string;
15
+ };
16
+ export type UserContentPart = TextPart | ImagePart | FilePart;
17
+ /** Normalized user-message content: a plain string or an array of typed parts. */
18
+ export type UserContent = string | UserContentPart[];
19
+ /** What callers may pass: a bare string element is sugar for a text part. */
20
+ export type UserContentInput = string | Array<string | UserContentPart>;
21
+ export declare const TextPartSchema: z.ZodObject<{
22
+ type: z.ZodLiteral<"text">;
23
+ text: z.ZodString;
24
+ }, z.core.$strip>;
25
+ export declare const ImageRefSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
26
+ kind: z.ZodLiteral<"bytes">;
27
+ data: z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>;
28
+ mimeType: z.ZodString;
29
+ }, z.core.$strip>, z.ZodObject<{
30
+ kind: z.ZodLiteral<"base64">;
31
+ base64: z.ZodString;
32
+ mimeType: z.ZodString;
33
+ }, z.core.$strip>, z.ZodObject<{
34
+ kind: z.ZodLiteral<"path">;
35
+ path: z.ZodString;
36
+ mimeType: z.ZodOptional<z.ZodString>;
37
+ }, z.core.$strip>, z.ZodObject<{
38
+ kind: z.ZodLiteral<"url">;
39
+ url: z.ZodString;
40
+ mimeType: z.ZodOptional<z.ZodString>;
41
+ timeoutMs: z.ZodOptional<z.ZodNumber>;
42
+ }, z.core.$strip>], "kind">;
43
+ export type ProviderFileRef = {
44
+ kind: "providerFile";
45
+ provider: string;
46
+ id: string;
47
+ uri?: string;
48
+ mimeType?: string;
49
+ expiresAt?: number;
50
+ };
51
+ export declare const ProviderFileRefSchema: z.ZodObject<{
52
+ kind: z.ZodLiteral<"providerFile">;
53
+ provider: z.ZodString;
54
+ id: z.ZodString;
55
+ uri: z.ZodOptional<z.ZodString>;
56
+ mimeType: z.ZodOptional<z.ZodString>;
57
+ expiresAt: z.ZodOptional<z.ZodNumber>;
58
+ }, z.core.$strip>;
59
+ export type AttachmentSource = ImageRef | ProviderFileRef;
60
+ export declare const AttachmentSourceSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
61
+ kind: z.ZodLiteral<"bytes">;
62
+ data: z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>;
63
+ mimeType: z.ZodString;
64
+ }, z.core.$strip>, z.ZodObject<{
65
+ kind: z.ZodLiteral<"base64">;
66
+ base64: z.ZodString;
67
+ mimeType: z.ZodString;
68
+ }, z.core.$strip>, z.ZodObject<{
69
+ kind: z.ZodLiteral<"path">;
70
+ path: z.ZodString;
71
+ mimeType: z.ZodOptional<z.ZodString>;
72
+ }, z.core.$strip>, z.ZodObject<{
73
+ kind: z.ZodLiteral<"url">;
74
+ url: z.ZodString;
75
+ mimeType: z.ZodOptional<z.ZodString>;
76
+ timeoutMs: z.ZodOptional<z.ZodNumber>;
77
+ }, z.core.$strip>, z.ZodObject<{
78
+ kind: z.ZodLiteral<"providerFile">;
79
+ provider: z.ZodString;
80
+ id: z.ZodString;
81
+ uri: z.ZodOptional<z.ZodString>;
82
+ mimeType: z.ZodOptional<z.ZodString>;
83
+ expiresAt: z.ZodOptional<z.ZodNumber>;
84
+ }, z.core.$strip>], "kind">;
85
+ export declare const ImagePartSchema: z.ZodObject<{
86
+ type: z.ZodLiteral<"image">;
87
+ source: z.ZodDiscriminatedUnion<[z.ZodObject<{
88
+ kind: z.ZodLiteral<"bytes">;
89
+ data: z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>;
90
+ mimeType: z.ZodString;
91
+ }, z.core.$strip>, z.ZodObject<{
92
+ kind: z.ZodLiteral<"base64">;
93
+ base64: z.ZodString;
94
+ mimeType: z.ZodString;
95
+ }, z.core.$strip>, z.ZodObject<{
96
+ kind: z.ZodLiteral<"path">;
97
+ path: z.ZodString;
98
+ mimeType: z.ZodOptional<z.ZodString>;
99
+ }, z.core.$strip>, z.ZodObject<{
100
+ kind: z.ZodLiteral<"url">;
101
+ url: z.ZodString;
102
+ mimeType: z.ZodOptional<z.ZodString>;
103
+ timeoutMs: z.ZodOptional<z.ZodNumber>;
104
+ }, z.core.$strip>, z.ZodObject<{
105
+ kind: z.ZodLiteral<"providerFile">;
106
+ provider: z.ZodString;
107
+ id: z.ZodString;
108
+ uri: z.ZodOptional<z.ZodString>;
109
+ mimeType: z.ZodOptional<z.ZodString>;
110
+ expiresAt: z.ZodOptional<z.ZodNumber>;
111
+ }, z.core.$strip>], "kind">;
112
+ }, z.core.$strip>;
113
+ export declare const FilePartSchema: z.ZodObject<{
114
+ type: z.ZodLiteral<"file">;
115
+ source: z.ZodDiscriminatedUnion<[z.ZodObject<{
116
+ kind: z.ZodLiteral<"bytes">;
117
+ data: z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>;
118
+ mimeType: z.ZodString;
119
+ }, z.core.$strip>, z.ZodObject<{
120
+ kind: z.ZodLiteral<"base64">;
121
+ base64: z.ZodString;
122
+ mimeType: z.ZodString;
123
+ }, z.core.$strip>, z.ZodObject<{
124
+ kind: z.ZodLiteral<"path">;
125
+ path: z.ZodString;
126
+ mimeType: z.ZodOptional<z.ZodString>;
127
+ }, z.core.$strip>, z.ZodObject<{
128
+ kind: z.ZodLiteral<"url">;
129
+ url: z.ZodString;
130
+ mimeType: z.ZodOptional<z.ZodString>;
131
+ timeoutMs: z.ZodOptional<z.ZodNumber>;
132
+ }, z.core.$strip>, z.ZodObject<{
133
+ kind: z.ZodLiteral<"providerFile">;
134
+ provider: z.ZodString;
135
+ id: z.ZodString;
136
+ uri: z.ZodOptional<z.ZodString>;
137
+ mimeType: z.ZodOptional<z.ZodString>;
138
+ expiresAt: z.ZodOptional<z.ZodNumber>;
139
+ }, z.core.$strip>], "kind">;
140
+ filename: z.ZodOptional<z.ZodString>;
141
+ }, z.core.$strip>;
142
+ export declare const UserContentPartSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
143
+ type: z.ZodLiteral<"text">;
144
+ text: z.ZodString;
145
+ }, z.core.$strip>, z.ZodObject<{
146
+ type: z.ZodLiteral<"image">;
147
+ source: z.ZodDiscriminatedUnion<[z.ZodObject<{
148
+ kind: z.ZodLiteral<"bytes">;
149
+ data: z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>;
150
+ mimeType: z.ZodString;
151
+ }, z.core.$strip>, z.ZodObject<{
152
+ kind: z.ZodLiteral<"base64">;
153
+ base64: z.ZodString;
154
+ mimeType: z.ZodString;
155
+ }, z.core.$strip>, z.ZodObject<{
156
+ kind: z.ZodLiteral<"path">;
157
+ path: z.ZodString;
158
+ mimeType: z.ZodOptional<z.ZodString>;
159
+ }, z.core.$strip>, z.ZodObject<{
160
+ kind: z.ZodLiteral<"url">;
161
+ url: z.ZodString;
162
+ mimeType: z.ZodOptional<z.ZodString>;
163
+ timeoutMs: z.ZodOptional<z.ZodNumber>;
164
+ }, z.core.$strip>, z.ZodObject<{
165
+ kind: z.ZodLiteral<"providerFile">;
166
+ provider: z.ZodString;
167
+ id: z.ZodString;
168
+ uri: z.ZodOptional<z.ZodString>;
169
+ mimeType: z.ZodOptional<z.ZodString>;
170
+ expiresAt: z.ZodOptional<z.ZodNumber>;
171
+ }, z.core.$strip>], "kind">;
172
+ }, z.core.$strip>, z.ZodObject<{
173
+ type: z.ZodLiteral<"file">;
174
+ source: z.ZodDiscriminatedUnion<[z.ZodObject<{
175
+ kind: z.ZodLiteral<"bytes">;
176
+ data: z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>;
177
+ mimeType: z.ZodString;
178
+ }, z.core.$strip>, z.ZodObject<{
179
+ kind: z.ZodLiteral<"base64">;
180
+ base64: z.ZodString;
181
+ mimeType: z.ZodString;
182
+ }, z.core.$strip>, z.ZodObject<{
183
+ kind: z.ZodLiteral<"path">;
184
+ path: z.ZodString;
185
+ mimeType: z.ZodOptional<z.ZodString>;
186
+ }, z.core.$strip>, z.ZodObject<{
187
+ kind: z.ZodLiteral<"url">;
188
+ url: z.ZodString;
189
+ mimeType: z.ZodOptional<z.ZodString>;
190
+ timeoutMs: z.ZodOptional<z.ZodNumber>;
191
+ }, z.core.$strip>, z.ZodObject<{
192
+ kind: z.ZodLiteral<"providerFile">;
193
+ provider: z.ZodString;
194
+ id: z.ZodString;
195
+ uri: z.ZodOptional<z.ZodString>;
196
+ mimeType: z.ZodOptional<z.ZodString>;
197
+ expiresAt: z.ZodOptional<z.ZodNumber>;
198
+ }, z.core.$strip>], "kind">;
199
+ filename: z.ZodOptional<z.ZodString>;
200
+ }, z.core.$strip>], "type">;
201
+ export declare const UserContentSchema: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
202
+ type: z.ZodLiteral<"text">;
203
+ text: z.ZodString;
204
+ }, z.core.$strip>, z.ZodObject<{
205
+ type: z.ZodLiteral<"image">;
206
+ source: z.ZodDiscriminatedUnion<[z.ZodObject<{
207
+ kind: z.ZodLiteral<"bytes">;
208
+ data: z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>;
209
+ mimeType: z.ZodString;
210
+ }, z.core.$strip>, z.ZodObject<{
211
+ kind: z.ZodLiteral<"base64">;
212
+ base64: z.ZodString;
213
+ mimeType: z.ZodString;
214
+ }, z.core.$strip>, z.ZodObject<{
215
+ kind: z.ZodLiteral<"path">;
216
+ path: z.ZodString;
217
+ mimeType: z.ZodOptional<z.ZodString>;
218
+ }, z.core.$strip>, z.ZodObject<{
219
+ kind: z.ZodLiteral<"url">;
220
+ url: z.ZodString;
221
+ mimeType: z.ZodOptional<z.ZodString>;
222
+ timeoutMs: z.ZodOptional<z.ZodNumber>;
223
+ }, z.core.$strip>, z.ZodObject<{
224
+ kind: z.ZodLiteral<"providerFile">;
225
+ provider: z.ZodString;
226
+ id: z.ZodString;
227
+ uri: z.ZodOptional<z.ZodString>;
228
+ mimeType: z.ZodOptional<z.ZodString>;
229
+ expiresAt: z.ZodOptional<z.ZodNumber>;
230
+ }, z.core.$strip>], "kind">;
231
+ }, z.core.$strip>, z.ZodObject<{
232
+ type: z.ZodLiteral<"file">;
233
+ source: z.ZodDiscriminatedUnion<[z.ZodObject<{
234
+ kind: z.ZodLiteral<"bytes">;
235
+ data: z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>;
236
+ mimeType: z.ZodString;
237
+ }, z.core.$strip>, z.ZodObject<{
238
+ kind: z.ZodLiteral<"base64">;
239
+ base64: z.ZodString;
240
+ mimeType: z.ZodString;
241
+ }, z.core.$strip>, z.ZodObject<{
242
+ kind: z.ZodLiteral<"path">;
243
+ path: z.ZodString;
244
+ mimeType: z.ZodOptional<z.ZodString>;
245
+ }, z.core.$strip>, z.ZodObject<{
246
+ kind: z.ZodLiteral<"url">;
247
+ url: z.ZodString;
248
+ mimeType: z.ZodOptional<z.ZodString>;
249
+ timeoutMs: z.ZodOptional<z.ZodNumber>;
250
+ }, z.core.$strip>, z.ZodObject<{
251
+ kind: z.ZodLiteral<"providerFile">;
252
+ provider: z.ZodString;
253
+ id: z.ZodString;
254
+ uri: z.ZodOptional<z.ZodString>;
255
+ mimeType: z.ZodOptional<z.ZodString>;
256
+ expiresAt: z.ZodOptional<z.ZodNumber>;
257
+ }, z.core.$strip>], "kind">;
258
+ filename: z.ZodOptional<z.ZodString>;
259
+ }, z.core.$strip>], "type">>]>;
@@ -0,0 +1,44 @@
1
+ import { z } from "zod";
2
+ export const TextPartSchema = z.object({
3
+ type: z.literal("text"),
4
+ text: z.string(),
5
+ });
6
+ export const ImageRefSchema = z.discriminatedUnion("kind", [
7
+ z.object({ kind: z.literal("bytes"), data: z.instanceof(Uint8Array), mimeType: z.string() }),
8
+ z.object({ kind: z.literal("base64"), base64: z.string(), mimeType: z.string() }),
9
+ z.object({ kind: z.literal("path"), path: z.string(), mimeType: z.string().optional() }),
10
+ z.object({
11
+ kind: z.literal("url"),
12
+ url: z.string(),
13
+ mimeType: z.string().optional(),
14
+ timeoutMs: z.number().optional(),
15
+ }),
16
+ ]);
17
+ export const ProviderFileRefSchema = z.object({
18
+ kind: z.literal("providerFile"),
19
+ provider: z.string(),
20
+ id: z.string(),
21
+ uri: z.string().optional(),
22
+ mimeType: z.string().optional(),
23
+ expiresAt: z.number().optional(),
24
+ });
25
+ // Compose from ImageRefSchema so future arms don't need mirroring here.
26
+ export const AttachmentSourceSchema = z.discriminatedUnion("kind", [
27
+ ...ImageRefSchema.options,
28
+ ProviderFileRefSchema,
29
+ ]);
30
+ export const ImagePartSchema = z.object({
31
+ type: z.literal("image"),
32
+ source: AttachmentSourceSchema,
33
+ });
34
+ export const FilePartSchema = z.object({
35
+ type: z.literal("file"),
36
+ source: AttachmentSourceSchema,
37
+ filename: z.string().optional(),
38
+ });
39
+ export const UserContentPartSchema = z.discriminatedUnion("type", [
40
+ TextPartSchema,
41
+ ImagePartSchema,
42
+ FilePartSchema,
43
+ ]);
44
+ export const UserContentSchema = z.union([z.string(), z.array(UserContentPartSchema)]);