@opencode-ai/ai 0.0.0-beta-17492

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 (194) hide show
  1. package/README.md +392 -0
  2. package/dist/cache-policy.d.ts +2 -0
  3. package/dist/cache-policy.js +121 -0
  4. package/dist/image-client.d.ts +19 -0
  5. package/dist/image-client.js +19 -0
  6. package/dist/image.d.ts +110 -0
  7. package/dist/image.js +102 -0
  8. package/dist/index.d.ts +18 -0
  9. package/dist/index.js +12 -0
  10. package/dist/llm.d.ts +230 -0
  11. package/dist/llm.js +77 -0
  12. package/dist/protocols/anthropic-messages.d.ts +633 -0
  13. package/dist/protocols/anthropic-messages.js +860 -0
  14. package/dist/protocols/bedrock-converse.d.ts +533 -0
  15. package/dist/protocols/bedrock-converse.js +584 -0
  16. package/dist/protocols/bedrock-event-stream.d.ts +9 -0
  17. package/dist/protocols/bedrock-event-stream.js +73 -0
  18. package/dist/protocols/gemini.d.ts +300 -0
  19. package/dist/protocols/gemini.js +512 -0
  20. package/dist/protocols/google-images.d.ts +30 -0
  21. package/dist/protocols/google-images.js +191 -0
  22. package/dist/protocols/index.d.ts +10 -0
  23. package/dist/protocols/index.js +10 -0
  24. package/dist/protocols/open-responses-channel.d.ts +26 -0
  25. package/dist/protocols/open-responses-channel.js +120 -0
  26. package/dist/protocols/open-responses.d.ts +951 -0
  27. package/dist/protocols/open-responses.js +873 -0
  28. package/dist/protocols/openai-chat.d.ts +795 -0
  29. package/dist/protocols/openai-chat.js +718 -0
  30. package/dist/protocols/openai-compatible-chat.d.ts +107 -0
  31. package/dist/protocols/openai-compatible-chat.js +20 -0
  32. package/dist/protocols/openai-compatible-responses.d.ts +96 -0
  33. package/dist/protocols/openai-compatible-responses.js +17 -0
  34. package/dist/protocols/openai-images.d.ts +32 -0
  35. package/dist/protocols/openai-images.js +188 -0
  36. package/dist/protocols/openai-responses-channel.d.ts +12 -0
  37. package/dist/protocols/openai-responses-channel.js +142 -0
  38. package/dist/protocols/openai-responses.d.ts +596 -0
  39. package/dist/protocols/openai-responses.js +208 -0
  40. package/dist/protocols/shared.d.ts +194 -0
  41. package/dist/protocols/shared.js +238 -0
  42. package/dist/protocols/utils/bedrock-auth.d.ts +21 -0
  43. package/dist/protocols/utils/bedrock-auth.js +43 -0
  44. package/dist/protocols/utils/bedrock-cache.d.ts +15 -0
  45. package/dist/protocols/utils/bedrock-cache.js +29 -0
  46. package/dist/protocols/utils/bedrock-media.d.ts +49 -0
  47. package/dist/protocols/utils/bedrock-media.js +70 -0
  48. package/dist/protocols/utils/cache.d.ts +6 -0
  49. package/dist/protocols/utils/cache.js +8 -0
  50. package/dist/protocols/utils/gemini-tool-schema.d.ts +2 -0
  51. package/dist/protocols/utils/gemini-tool-schema.js +103 -0
  52. package/dist/protocols/utils/image-input.d.ts +21 -0
  53. package/dist/protocols/utils/image-input.js +22 -0
  54. package/dist/protocols/utils/lifecycle.d.ts +20 -0
  55. package/dist/protocols/utils/lifecycle.js +68 -0
  56. package/dist/protocols/utils/open-responses-options.d.ts +21 -0
  57. package/dist/protocols/utils/open-responses-options.js +41 -0
  58. package/dist/protocols/utils/openai-image.d.ts +5 -0
  59. package/dist/protocols/utils/openai-image.js +18 -0
  60. package/dist/protocols/utils/openai-options.d.ts +14 -0
  61. package/dist/protocols/utils/openai-options.js +14 -0
  62. package/dist/protocols/utils/tool-schema.d.ts +8 -0
  63. package/dist/protocols/utils/tool-schema.js +81 -0
  64. package/dist/protocols/utils/tool-stream.d.ts +639 -0
  65. package/dist/protocols/utils/tool-stream.js +141 -0
  66. package/dist/protocols/xai-images.d.ts +26 -0
  67. package/dist/protocols/xai-images.js +111 -0
  68. package/dist/protocols/zai-images.d.ts +22 -0
  69. package/dist/protocols/zai-images.js +84 -0
  70. package/dist/protocols.d.ts +1 -0
  71. package/dist/protocols.js +1 -0
  72. package/dist/provider-error.d.ts +14 -0
  73. package/dist/provider-error.js +130 -0
  74. package/dist/provider-package.d.ts +15 -0
  75. package/dist/provider-package.js +1 -0
  76. package/dist/provider.d.ts +23 -0
  77. package/dist/provider.js +2 -0
  78. package/dist/providers/amazon-bedrock/mantle/chat.d.ts +2 -0
  79. package/dist/providers/amazon-bedrock/mantle/chat.js +1 -0
  80. package/dist/providers/amazon-bedrock/mantle/responses.d.ts +2 -0
  81. package/dist/providers/amazon-bedrock/mantle/responses.js +1 -0
  82. package/dist/providers/amazon-bedrock/mantle.d.ts +2 -0
  83. package/dist/providers/amazon-bedrock/mantle.js +1 -0
  84. package/dist/providers/amazon-bedrock-mantle.d.ts +247 -0
  85. package/dist/providers/amazon-bedrock-mantle.js +68 -0
  86. package/dist/providers/amazon-bedrock.d.ts +172 -0
  87. package/dist/providers/amazon-bedrock.js +41 -0
  88. package/dist/providers/anthropic-compatible.d.ts +218 -0
  89. package/dist/providers/anthropic-compatible.js +45 -0
  90. package/dist/providers/anthropic.d.ts +216 -0
  91. package/dist/providers/anthropic.js +40 -0
  92. package/dist/providers/azure/chat.d.ts +2 -0
  93. package/dist/providers/azure/chat.js +1 -0
  94. package/dist/providers/azure/responses.d.ts +2 -0
  95. package/dist/providers/azure/responses.js +1 -0
  96. package/dist/providers/azure.d.ts +248 -0
  97. package/dist/providers/azure.js +90 -0
  98. package/dist/providers/cloudflare.d.ts +338 -0
  99. package/dist/providers/cloudflare.js +87 -0
  100. package/dist/providers/google-vertex/chat.d.ts +2 -0
  101. package/dist/providers/google-vertex/chat.js +1 -0
  102. package/dist/providers/google-vertex/gemini.d.ts +2 -0
  103. package/dist/providers/google-vertex/gemini.js +1 -0
  104. package/dist/providers/google-vertex/messages.d.ts +2 -0
  105. package/dist/providers/google-vertex/messages.js +1 -0
  106. package/dist/providers/google-vertex/responses.d.ts +2 -0
  107. package/dist/providers/google-vertex/responses.js +1 -0
  108. package/dist/providers/google-vertex-chat.d.ts +131 -0
  109. package/dist/providers/google-vertex-chat.js +50 -0
  110. package/dist/providers/google-vertex-messages.d.ts +215 -0
  111. package/dist/providers/google-vertex-messages.js +75 -0
  112. package/dist/providers/google-vertex-responses.d.ts +122 -0
  113. package/dist/providers/google-vertex-responses.js +51 -0
  114. package/dist/providers/google-vertex-shared.d.ts +22 -0
  115. package/dist/providers/google-vertex-shared.js +61 -0
  116. package/dist/providers/google-vertex.d.ts +115 -0
  117. package/dist/providers/google-vertex.js +64 -0
  118. package/dist/providers/google.d.ts +112 -0
  119. package/dist/providers/google.js +43 -0
  120. package/dist/providers/index.d.ts +18 -0
  121. package/dist/providers/index.js +18 -0
  122. package/dist/providers/open-responses-options.d.ts +16 -0
  123. package/dist/providers/open-responses-options.js +1 -0
  124. package/dist/providers/openai/chat.d.ts +2 -0
  125. package/dist/providers/openai/chat.js +1 -0
  126. package/dist/providers/openai/responses.d.ts +2 -0
  127. package/dist/providers/openai/responses.js +1 -0
  128. package/dist/providers/openai-compatible/responses.d.ts +1 -0
  129. package/dist/providers/openai-compatible/responses.js +1 -0
  130. package/dist/providers/openai-compatible-profile.d.ts +43 -0
  131. package/dist/providers/openai-compatible-profile.js +12 -0
  132. package/dist/providers/openai-compatible-responses.d.ts +120 -0
  133. package/dist/providers/openai-compatible-responses.js +33 -0
  134. package/dist/providers/openai-compatible.d.ts +167 -0
  135. package/dist/providers/openai-compatible.js +55 -0
  136. package/dist/providers/openai-options.d.ts +21 -0
  137. package/dist/providers/openai-options.js +41 -0
  138. package/dist/providers/openai.d.ts +264 -0
  139. package/dist/providers/openai.js +83 -0
  140. package/dist/providers/openrouter.d.ts +537 -0
  141. package/dist/providers/openrouter.js +117 -0
  142. package/dist/providers/xai.d.ts +249 -0
  143. package/dist/providers/xai.js +79 -0
  144. package/dist/providers/zai.d.ts +24 -0
  145. package/dist/providers/zai.js +21 -0
  146. package/dist/providers.d.ts +1 -0
  147. package/dist/providers.js +1 -0
  148. package/dist/route/auth-options.d.ts +34 -0
  149. package/dist/route/auth-options.js +14 -0
  150. package/dist/route/auth.d.ts +51 -0
  151. package/dist/route/auth.js +89 -0
  152. package/dist/route/client.d.ts +348 -0
  153. package/dist/route/client.js +214 -0
  154. package/dist/route/endpoint.d.ts +28 -0
  155. package/dist/route/endpoint.js +21 -0
  156. package/dist/route/executor.d.ts +23 -0
  157. package/dist/route/executor.js +227 -0
  158. package/dist/route/framing.d.ts +23 -0
  159. package/dist/route/framing.js +4 -0
  160. package/dist/route/index.d.ts +16 -0
  161. package/dist/route/index.js +9 -0
  162. package/dist/route/protocol.d.ts +77 -0
  163. package/dist/route/protocol.js +17 -0
  164. package/dist/route/transport/http.d.ts +34 -0
  165. package/dist/route/transport/http.js +63 -0
  166. package/dist/route/transport/index.d.ts +39 -0
  167. package/dist/route/transport/index.js +4 -0
  168. package/dist/route/transport/websocket-channel.d.ts +56 -0
  169. package/dist/route/transport/websocket-channel.js +1 -0
  170. package/dist/route/transport/websocket.d.ts +55 -0
  171. package/dist/route/transport/websocket.js +326 -0
  172. package/dist/route.d.ts +1 -0
  173. package/dist/route.js +1 -0
  174. package/dist/schema/errors.d.ts +159 -0
  175. package/dist/schema/errors.js +155 -0
  176. package/dist/schema/events.d.ts +4587 -0
  177. package/dist/schema/events.js +492 -0
  178. package/dist/schema/ids.d.ts +30 -0
  179. package/dist/schema/ids.js +19 -0
  180. package/dist/schema/index.d.ts +5 -0
  181. package/dist/schema/index.js +5 -0
  182. package/dist/schema/messages.d.ts +402 -0
  183. package/dist/schema/messages.js +244 -0
  184. package/dist/schema/options.d.ts +159 -0
  185. package/dist/schema/options.js +202 -0
  186. package/dist/testing.d.ts +1493 -0
  187. package/dist/testing.js +88 -0
  188. package/dist/tool-runtime.d.ts +15 -0
  189. package/dist/tool-runtime.js +54 -0
  190. package/dist/tool.d.ts +135 -0
  191. package/dist/tool.js +66 -0
  192. package/dist/utils/record.d.ts +2 -0
  193. package/dist/utils/record.js +2 -0
  194. package/package.json +47 -0
@@ -0,0 +1,141 @@
1
+ import { Effect } from "effect";
2
+ import { AIError, LLMEvent } from "../../schema/index.js";
3
+ import { eventError, parseToolInput } from "../shared.js";
4
+ /** Create empty accumulator state for one provider stream. */
5
+ export const empty = () => ({});
6
+ const withTool = (tools, key, tool) => {
7
+ return { ...tools, [key]: tool };
8
+ };
9
+ const withoutTool = (tools, key) => {
10
+ const next = { ...tools };
11
+ delete next[key];
12
+ return next;
13
+ };
14
+ const inputStart = (tool) => LLMEvent.toolInputStart({
15
+ id: tool.id,
16
+ name: tool.name,
17
+ providerExecuted: tool.providerExecuted ? true : undefined,
18
+ providerMetadata: tool.providerMetadata,
19
+ });
20
+ const inputDelta = (tool, text) => LLMEvent.toolInputDelta({
21
+ id: tool.id,
22
+ name: tool.name,
23
+ text,
24
+ });
25
+ const toolCall = (route, tool, inputOverride) => {
26
+ const raw = inputOverride ?? tool.input;
27
+ return parseToolInput(route, tool.name, raw).pipe(Effect.map((input) => LLMEvent.toolCall({
28
+ id: tool.id,
29
+ name: tool.name,
30
+ input,
31
+ providerExecuted: tool.providerExecuted ? true : undefined,
32
+ providerMetadata: tool.providerMetadata,
33
+ })), Effect.catch((error) => tool.providerExecuted
34
+ ? Effect.fail(error)
35
+ : Effect.succeed(LLMEvent.toolInputError({
36
+ id: tool.id,
37
+ name: tool.name,
38
+ raw,
39
+ }))));
40
+ };
41
+ const finishEvents = (tool, event) => event.type === "tool-input-error"
42
+ ? [event]
43
+ : [LLMEvent.toolInputEnd({ id: tool.id, name: tool.name, providerMetadata: tool.providerMetadata }), event];
44
+ /** Store the updated tool and produce the optional public delta event. */
45
+ const appendTool = (tools, key, tool, text) => {
46
+ const events = [];
47
+ if (!tools[key])
48
+ events.push(inputStart(tool));
49
+ if (text.length > 0)
50
+ events.push(inputDelta(tool, text));
51
+ return {
52
+ tools: withTool(tools, key, tool),
53
+ tool,
54
+ events,
55
+ };
56
+ };
57
+ export const isError = (result) => result instanceof AIError;
58
+ /**
59
+ * Register a tool call whose start event arrived before any argument deltas.
60
+ * Used by Anthropic `content_block_start`, Bedrock `contentBlockStart`, and
61
+ * OpenAI Responses `response.output_item.added`.
62
+ */
63
+ export const start = (tools, key, tool) => withTool(tools, key, { ...tool, input: tool.input ?? "" });
64
+ /**
65
+ * Append a streamed argument delta, starting the tool if this provider encodes
66
+ * identity on the first delta instead of a separate start event. OpenAI Chat has
67
+ * this shape: `tool_calls[].index` is the stream key, and `id` / `name` may only
68
+ * appear on the first delta for that index.
69
+ */
70
+ export const appendOrStart = (route, tools, key, delta, missingToolMessage) => {
71
+ const current = tools[key];
72
+ const id = current?.id ?? delta.id;
73
+ const name = current?.name ?? delta.name;
74
+ if (!id || !name)
75
+ return eventError(route, missingToolMessage);
76
+ const tool = {
77
+ id,
78
+ name,
79
+ input: `${current?.input ?? ""}${delta.text}`,
80
+ providerExecuted: current?.providerExecuted,
81
+ providerMetadata: current?.providerMetadata,
82
+ };
83
+ if (current && delta.text.length === 0 && current.id === id && current.name === name)
84
+ return { tools, tool: current, events: [] };
85
+ return appendTool(tools, key, tool, delta.text);
86
+ };
87
+ /**
88
+ * Append argument text to a tool that must already have been started. This keeps
89
+ * protocols honest when their stream grammar promises a start event before any
90
+ * argument delta.
91
+ */
92
+ export const appendExisting = (route, tools, key, text, missingToolMessage) => {
93
+ const current = tools[key];
94
+ if (!current)
95
+ return eventError(route, missingToolMessage);
96
+ if (text.length === 0)
97
+ return { tools, tool: current, events: [] };
98
+ return appendTool(tools, key, { ...current, input: `${current.input}${text}` }, text);
99
+ };
100
+ /**
101
+ * Finalize one pending tool call: parse the accumulated raw JSON, remove it
102
+ * from state, and return either a call or a non-executable local input error.
103
+ * Missing keys are a no-op because some providers emit stop events for
104
+ * non-tool content blocks.
105
+ */
106
+ export const finish = (route, tools, key) => Effect.gen(function* () {
107
+ const tool = tools[key];
108
+ if (!tool)
109
+ return { tools };
110
+ return {
111
+ tools: withoutTool(tools, key),
112
+ events: finishEvents(tool, yield* toolCall(route, tool)),
113
+ };
114
+ });
115
+ /**
116
+ * Finalize one pending tool call with an authoritative final input string.
117
+ * OpenAI Responses can send accumulated deltas and then repeat the completed
118
+ * arguments on `response.output_item.done`; the final value wins.
119
+ */
120
+ export const finishWithInput = (route, tools, key, input) => Effect.gen(function* () {
121
+ const tool = tools[key];
122
+ if (!tool)
123
+ return { tools };
124
+ return {
125
+ tools: withoutTool(tools, key),
126
+ events: finishEvents(tool, yield* toolCall(route, tool, input)),
127
+ };
128
+ });
129
+ /**
130
+ * Finalize every pending tool call at once. OpenAI Chat has this shape: it does
131
+ * not emit per-tool stop events, so all accumulated calls finish independently
132
+ * when the choice receives a terminal `finish_reason`.
133
+ */
134
+ export const finishAll = (route, tools) => Effect.gen(function* () {
135
+ const pending = Object.values(tools).filter((tool) => tool !== undefined);
136
+ return {
137
+ tools: empty(),
138
+ events: yield* Effect.forEach(pending, (tool) => toolCall(route, tool).pipe(Effect.map((event) => finishEvents(tool, event)))).pipe(Effect.map((events) => events.flat())),
139
+ };
140
+ });
141
+ export * as ToolStream from "./tool-stream.js";
@@ -0,0 +1,26 @@
1
+ import { ImageModel } from "../image.js";
2
+ import { type Definition as AuthDefinition } from "../route/auth.js";
3
+ import { type HttpOptions } from "../schema/index.js";
4
+ export declare const DEFAULT_BASE_URL = "https://api.x.ai/v1";
5
+ export declare const PATH = "/images/generations";
6
+ export declare const EDIT_PATH = "/images/edits";
7
+ export type XAIImageString<Known extends string> = Known | (string & {});
8
+ export type XAIImageOptions = {
9
+ readonly n?: number;
10
+ readonly aspectRatio?: XAIImageString<"1:1" | "3:4" | "4:3" | "9:16" | "16:9" | "2:3" | "3:2" | "9:19.5" | "19.5:9" | "9:20" | "20:9" | "1:2" | "2:1" | "auto">;
11
+ readonly aspect_ratio?: XAIImageString<"1:1" | "3:4" | "4:3" | "9:16" | "16:9" | "2:3" | "3:2" | "9:19.5" | "19.5:9" | "9:20" | "20:9" | "1:2" | "2:1" | "auto">;
12
+ readonly resolution?: XAIImageString<"1k" | "2k">;
13
+ readonly responseFormat?: XAIImageString<"url" | "b64_json">;
14
+ readonly response_format?: XAIImageString<"url" | "b64_json">;
15
+ } & Record<string, unknown>;
16
+ export interface ModelInput {
17
+ readonly id: string;
18
+ readonly auth: AuthDefinition;
19
+ readonly baseURL?: string;
20
+ readonly headers?: Record<string, string>;
21
+ readonly http?: HttpOptions;
22
+ }
23
+ export declare const model: (input: ModelInput) => ImageModel<XAIImageOptions>;
24
+ export declare const XAIImages: {
25
+ readonly model: (input: ModelInput) => ImageModel<XAIImageOptions>;
26
+ };
@@ -0,0 +1,111 @@
1
+ import { Effect, Encoding, Schema } from "effect";
2
+ import { Headers, HttpClientRequest } from "effect/unstable/http";
3
+ import { GeneratedImage, ImageModel, ImageResponse } from "../image.js";
4
+ import { Auth } from "../route/auth.js";
5
+ import { InvalidProviderOutputReason, AIError, Usage, mergeHttpOptions, mergeJsonRecords, } from "../schema/index.js";
6
+ import { ProviderShared, optionalNull } from "./shared.js";
7
+ import { ImageInputs } from "./utils/image-input.js";
8
+ const ADAPTER = "xai-images";
9
+ export const DEFAULT_BASE_URL = "https://api.x.ai/v1";
10
+ export const PATH = "/images/generations";
11
+ export const EDIT_PATH = "/images/edits";
12
+ const XAIImageResponse = Schema.Struct({
13
+ data: Schema.Array(Schema.Struct({
14
+ b64_json: optionalNull(Schema.String),
15
+ url: optionalNull(Schema.String),
16
+ revised_prompt: optionalNull(Schema.String),
17
+ mime_type: optionalNull(Schema.String),
18
+ })),
19
+ usage: Schema.optional(Schema.Unknown),
20
+ });
21
+ const nativeOptions = (options) => {
22
+ if (!options)
23
+ return undefined;
24
+ const { aspectRatio, responseFormat, ...native } = options;
25
+ return {
26
+ aspect_ratio: aspectRatio,
27
+ response_format: responseFormat,
28
+ ...native,
29
+ };
30
+ };
31
+ const invalidOutput = (message) => new AIError({
32
+ module: ADAPTER,
33
+ method: "generate",
34
+ reason: new InvalidProviderOutputReason({ message, route: ADAPTER }),
35
+ });
36
+ const applyQuery = (url, query) => {
37
+ if (!query)
38
+ return url;
39
+ const next = new URL(url);
40
+ Object.entries(query).forEach(([key, value]) => next.searchParams.set(key, value));
41
+ return next.toString();
42
+ };
43
+ export const model = (input) => {
44
+ const route = {
45
+ id: ADAPTER,
46
+ generate: Effect.fn("XAIImages.generate")(function* (request, execute) {
47
+ const http = mergeHttpOptions(request.model.http, request.http);
48
+ const imageReferences = (request.images ?? []).map((image) => {
49
+ if (image.type === "bytes")
50
+ return { url: ImageInputs.dataUrl(image), type: "image_url" };
51
+ if (image.type === "url")
52
+ return { url: image.url, type: "image_url" };
53
+ if (image.type === "file-id")
54
+ return { file_id: image.id };
55
+ return undefined;
56
+ });
57
+ if (imageReferences.some((image) => image === undefined))
58
+ return yield* ImageInputs.invalid(ADAPTER, "xAI Images accepts image URLs, data URLs, bytes, and file IDs");
59
+ const requestBody = mergeJsonRecords({
60
+ model: request.model.id,
61
+ prompt: request.prompt,
62
+ image: imageReferences.length === 1 ? imageReferences[0] : undefined,
63
+ images: imageReferences.length > 1 ? imageReferences : undefined,
64
+ }, nativeOptions(request.options), http?.body);
65
+ const text = ProviderShared.encodeJson(requestBody);
66
+ const url = applyQuery(`${(input.baseURL ?? DEFAULT_BASE_URL).replace(/\/$/, "")}${imageReferences.length === 0 ? PATH : EDIT_PATH}`, http?.query);
67
+ const headers = yield* Auth.toEffect(input.auth)({
68
+ request,
69
+ method: "POST",
70
+ url,
71
+ body: text,
72
+ headers: Headers.fromInput({ ...input.headers, ...http?.headers }),
73
+ });
74
+ const response = yield* execute(HttpClientRequest.post(url).pipe(HttpClientRequest.setHeaders(headers), HttpClientRequest.bodyText(text, "application/json")));
75
+ const payload = yield* response.json.pipe(Effect.mapError(() => invalidOutput("Failed to read the xAI Images response")));
76
+ const decoded = yield* Schema.decodeUnknownEffect(XAIImageResponse)(payload).pipe(Effect.mapError(() => invalidOutput("xAI Images returned an invalid response")));
77
+ const images = yield* Effect.forEach(decoded.data, (item, index) => {
78
+ const mediaType = item.mime_type ?? "application/octet-stream";
79
+ if (item.b64_json)
80
+ return Effect.fromResult(Encoding.decodeBase64(item.b64_json)).pipe(Effect.mapError(() => invalidOutput(`xAI Images result ${index} contains invalid base64 data`)), Effect.map((data) => new GeneratedImage({
81
+ mediaType,
82
+ data,
83
+ providerMetadata: item.revised_prompt === undefined || item.revised_prompt === null
84
+ ? undefined
85
+ : { xai: { revisedPrompt: item.revised_prompt } },
86
+ })));
87
+ if (item.url)
88
+ return Effect.succeed(new GeneratedImage({
89
+ mediaType,
90
+ data: item.url,
91
+ providerMetadata: item.revised_prompt === undefined || item.revised_prompt === null
92
+ ? undefined
93
+ : { xai: { revisedPrompt: item.revised_prompt } },
94
+ }));
95
+ return Effect.fail(invalidOutput(`xAI Images result ${index} has neither image data nor a URL`));
96
+ });
97
+ if (images.length === 0)
98
+ return yield* invalidOutput("xAI Images returned no images");
99
+ const usage = ProviderShared.isRecord(decoded.usage) ? decoded.usage : undefined;
100
+ return new ImageResponse({
101
+ images,
102
+ usage: usage === undefined ? undefined : new Usage({ providerMetadata: { xai: usage } }),
103
+ providerMetadata: usage === undefined ? undefined : { xai: { usage } },
104
+ });
105
+ }),
106
+ };
107
+ return ImageModel.make({ id: input.id, provider: "xai", route, http: input.http });
108
+ };
109
+ export const XAIImages = {
110
+ model,
111
+ };
@@ -0,0 +1,22 @@
1
+ import { ImageModel } from "../image.js";
2
+ import { type Definition as AuthDefinition } from "../route/auth.js";
3
+ import { type HttpOptions } from "../schema/index.js";
4
+ export declare const DEFAULT_BASE_URL = "https://api.z.ai/api/paas/v4";
5
+ export declare const PATH = "/images/generations";
6
+ export type ZAIImageString<Known extends string> = Known | (string & {});
7
+ export type ZAIImageOptions = {
8
+ readonly size?: ZAIImageString<"1024x1024" | "768x1344" | "864x1152" | "1344x768" | "1152x864" | "1440x720" | "720x1440">;
9
+ readonly quality?: ZAIImageString<"hd" | "standard">;
10
+ readonly userID?: string;
11
+ } & Record<string, unknown>;
12
+ export interface ModelInput {
13
+ readonly id: string;
14
+ readonly auth: AuthDefinition;
15
+ readonly baseURL?: string;
16
+ readonly headers?: Record<string, string>;
17
+ readonly http?: HttpOptions;
18
+ }
19
+ export declare const model: (input: ModelInput) => ImageModel<ZAIImageOptions>;
20
+ export declare const ZAIImages: {
21
+ readonly model: (input: ModelInput) => ImageModel<ZAIImageOptions>;
22
+ };
@@ -0,0 +1,84 @@
1
+ import { Effect, Schema } from "effect";
2
+ import { Headers, HttpClientRequest } from "effect/unstable/http";
3
+ import { GeneratedImage, ImageModel, ImageResponse } from "../image.js";
4
+ import { Auth } from "../route/auth.js";
5
+ import { InvalidProviderOutputReason, AIError, mergeHttpOptions, mergeJsonRecords, } from "../schema/index.js";
6
+ import { ProviderShared } from "./shared.js";
7
+ import { ImageInputs } from "./utils/image-input.js";
8
+ const ADAPTER = "zai-images";
9
+ export const DEFAULT_BASE_URL = "https://api.z.ai/api/paas/v4";
10
+ export const PATH = "/images/generations";
11
+ const ZAIImageResponse = Schema.Struct({
12
+ created: Schema.optional(Schema.Int),
13
+ id: Schema.optional(Schema.String),
14
+ request_id: Schema.optional(Schema.String),
15
+ data: Schema.Array(Schema.Struct({ url: Schema.String })),
16
+ content_filter: Schema.optional(Schema.Array(Schema.Struct({
17
+ role: Schema.optional(Schema.String),
18
+ level: Schema.optional(Schema.Number),
19
+ }))),
20
+ });
21
+ const nativeOptions = (options) => {
22
+ if (!options)
23
+ return undefined;
24
+ const { userID, ...native } = options;
25
+ return {
26
+ user_id: userID,
27
+ ...native,
28
+ };
29
+ };
30
+ const invalidOutput = (message) => new AIError({
31
+ module: ADAPTER,
32
+ method: "generate",
33
+ reason: new InvalidProviderOutputReason({ message, route: ADAPTER }),
34
+ });
35
+ const applyQuery = (url, query) => {
36
+ if (!query)
37
+ return url;
38
+ const next = new URL(url);
39
+ Object.entries(query).forEach(([key, value]) => next.searchParams.set(key, value));
40
+ return next.toString();
41
+ };
42
+ export const model = (input) => {
43
+ const route = {
44
+ id: ADAPTER,
45
+ generate: Effect.fn("ZAIImages.generate")(function* (request, execute) {
46
+ if ((request.images?.length ?? 0) > 0)
47
+ return yield* ImageInputs.invalid(ADAPTER, "Z.ai hosted image generation does not support image inputs");
48
+ const http = mergeHttpOptions(request.model.http, request.http);
49
+ const requestBody = mergeJsonRecords({ model: request.model.id, prompt: request.prompt }, nativeOptions(request.options), http?.body);
50
+ const text = ProviderShared.encodeJson(requestBody);
51
+ const url = applyQuery(`${(input.baseURL ?? DEFAULT_BASE_URL).replace(/\/$/, "")}${PATH}`, http?.query);
52
+ const headers = yield* Auth.toEffect(input.auth)({
53
+ request,
54
+ method: "POST",
55
+ url,
56
+ body: text,
57
+ headers: Headers.fromInput({ ...input.headers, ...http?.headers }),
58
+ });
59
+ const response = yield* execute(HttpClientRequest.post(url).pipe(HttpClientRequest.setHeaders(headers), HttpClientRequest.bodyText(text, "application/json")));
60
+ const payload = yield* response.json.pipe(Effect.mapError(() => invalidOutput("Failed to read the Z.ai Images response")));
61
+ const decoded = yield* Schema.decodeUnknownEffect(ZAIImageResponse)(payload).pipe(Effect.mapError(() => invalidOutput("Z.ai Images returned an invalid response")));
62
+ if (decoded.data.length === 0)
63
+ return yield* invalidOutput("Z.ai Images returned no images");
64
+ return new ImageResponse({
65
+ images: decoded.data.map((item) => new GeneratedImage({
66
+ mediaType: "application/octet-stream",
67
+ data: item.url,
68
+ })),
69
+ providerMetadata: {
70
+ zai: {
71
+ created: decoded.created,
72
+ id: decoded.id,
73
+ requestID: decoded.request_id,
74
+ contentFilter: decoded.content_filter,
75
+ },
76
+ },
77
+ });
78
+ }),
79
+ };
80
+ return ImageModel.make({ id: input.id, provider: "zai", route, http: input.http });
81
+ };
82
+ export const ZAIImages = {
83
+ model,
84
+ };
@@ -0,0 +1 @@
1
+ export * from "./protocols/index.js";
@@ -0,0 +1 @@
1
+ export * from "./protocols/index.js";
@@ -0,0 +1,14 @@
1
+ import { AIError, type HttpContext, type HttpRateLimitDetails, type ProviderMetadata } from "./schema/index.js";
2
+ export declare const isContextOverflow: (message: string) => boolean;
3
+ export declare const isPayloadTooLarge: (message: string) => boolean;
4
+ export declare const isContextOverflowFailure: (failure: unknown) => boolean;
5
+ export interface ProviderFailure {
6
+ readonly message: string;
7
+ readonly status?: number | undefined;
8
+ readonly code?: string | undefined;
9
+ readonly retryAfterMs?: number | undefined;
10
+ readonly rateLimit?: HttpRateLimitDetails | undefined;
11
+ readonly http?: HttpContext | undefined;
12
+ readonly providerMetadata?: ProviderMetadata | undefined;
13
+ }
14
+ export declare function classifyProviderFailure(input: ProviderFailure): AIError["reason"];
@@ -0,0 +1,130 @@
1
+ import { Option, Schema } from "effect";
2
+ import { AuthenticationReason, ContentPolicyReason, InvalidRequestReason, AIError, ProviderErrorEvent, ProviderInternalReason, QuotaExceededReason, RateLimitReason, UnknownProviderReason, } from "./schema/index.js";
3
+ const patterns = [
4
+ /prompt is too long/i,
5
+ /input is too long for requested model/i,
6
+ /exceeds the context window/i,
7
+ /exceeds (?:the )?(?:model'?s )?maximum context length(?: of [\d,]+ tokens?|\s*\([\d,]+\))/i,
8
+ /input token count.*exceeds the maximum/i,
9
+ /tokens in request more than max tokens allowed/i,
10
+ /maximum prompt length is \d+/i,
11
+ /reduce the length of the messages/i,
12
+ /maximum context length is \d+ tokens/i,
13
+ /exceeds (?:the )?maximum allowed input length of [\d,]+ tokens?/i,
14
+ /input \(\d+ tokens\) is longer than the model'?s context length \(\d+ tokens\)/i,
15
+ /exceeds the limit of \d+/i,
16
+ /exceeds the available context size/i,
17
+ /greater than the context length/i,
18
+ /context window exceeds limit/i,
19
+ /exceeded model token limit/i,
20
+ /context[_ ]length[_ ]exceeded/i,
21
+ /context length is only \d+ tokens/i,
22
+ /input length.*exceeds.*context length/i,
23
+ /prompt too long; exceeded (?:max )?context length/i,
24
+ /too large for model with \d+ maximum context length/i,
25
+ /prompt has [\d,]+ tokens?, but the configured context size is [\d,]+ tokens?/i,
26
+ /model_context_window_exceeded/i,
27
+ /too many tokens/i,
28
+ /token limit exceeded/i,
29
+ ];
30
+ const payloadPatterns = [/request_too_large/i, /request entity too large/i, /payload too large/i, /request too large/i];
31
+ const exclusions = [/^(throttling error|service unavailable):/i, /rate limit/i, /too many requests/i];
32
+ export const isContextOverflow = (message) => !exclusions.some((pattern) => pattern.test(message)) &&
33
+ (patterns.some((pattern) => pattern.test(message)) || /^400\s*(status code)?\s*\(no body\)/i.test(message));
34
+ export const isPayloadTooLarge = (message) => payloadPatterns.some((pattern) => pattern.test(message));
35
+ export const isContextOverflowFailure = (failure) => failure instanceof AIError
36
+ ? failure.reason._tag === "InvalidRequest" && failure.reason.classification === "context-overflow"
37
+ : Schema.is(ProviderErrorEvent)(failure) && failure.classification === "context-overflow";
38
+ const decodeJson = Schema.decodeUnknownOption(Schema.UnknownFromJsonString);
39
+ const QUOTA_CODES = new Set(["insufficient_quota", "usage_not_included", "billing_error"]);
40
+ const SERVER_CODES = new Set([
41
+ "api_error",
42
+ "internal_error",
43
+ "internalserverexception",
44
+ "modelstreamerrorexception",
45
+ "overloaded_error",
46
+ "server_error",
47
+ "server_is_overloaded",
48
+ "slow_down",
49
+ "serviceunavailableexception",
50
+ ]);
51
+ const INVALID_REQUEST_CODES = new Set(["invalid_prompt", "invalid_request_error", "validationexception"]);
52
+ const RATE_LIMIT_TEXT = /rate increased too quickly|rate[-_\s]?limit|too[_\s]?many[_\s]?requests/i;
53
+ const QUOTA_TEXT = /insufficient[-_\s]?quota|quota[-_\s]?exceeded/i;
54
+ const CONTENT_POLICY_TEXT = /content[-_\s]?policy|content_filter|safety/i;
55
+ // Keep HTTP failures and provider-reported stream failures on one typed path so
56
+ // session retry policy never needs provider-specific string matching.
57
+ export function classifyProviderFailure(input) {
58
+ const body = input.http?.body ?? "";
59
+ const codes = [input.code, ...providerCodes(body), ...providerCodes(input.message)]
60
+ .filter((code) => code !== undefined)
61
+ .map((code) => code.toLowerCase());
62
+ const text = body || input.message;
63
+ const common = { message: input.message, providerMetadata: input.providerMetadata, http: input.http };
64
+ const clientScoped = input.status === undefined || (input.status >= 400 && input.status < 500);
65
+ if (clientScoped &&
66
+ (codes.includes("context_length_exceeded") ||
67
+ codes.includes("model_context_window_exceeded") ||
68
+ isContextOverflow(text)))
69
+ return new InvalidRequestReason({ ...common, classification: "context-overflow" });
70
+ if (input.status === 413 || isPayloadTooLarge(text))
71
+ return new InvalidRequestReason({ ...common, classification: "payload-too-large" });
72
+ if (CONTENT_POLICY_TEXT.test(text))
73
+ return new ContentPolicyReason(common);
74
+ if (codes.some((code) => QUOTA_CODES.has(code)) || (input.status === 429 && QUOTA_TEXT.test(text)))
75
+ return new QuotaExceededReason(common);
76
+ if (input.status === 401)
77
+ return new AuthenticationReason({ ...common, kind: "invalid" });
78
+ if (input.status === 403)
79
+ return new AuthenticationReason({ ...common, kind: "insufficient-permissions" });
80
+ if (codes.includes("authentication_error"))
81
+ return new AuthenticationReason({ ...common, kind: "invalid" });
82
+ if (codes.includes("permission_error"))
83
+ return new AuthenticationReason({ ...common, kind: "insufficient-permissions" });
84
+ if (codes.some((code) => code.includes("rate_limit") || code === "too_many_requests" || code === "throttlingexception"))
85
+ return new RateLimitReason({
86
+ ...common,
87
+ retryAfterMs: input.retryAfterMs,
88
+ rateLimit: input.rateLimit,
89
+ });
90
+ if (RATE_LIMIT_TEXT.test(text))
91
+ return new RateLimitReason({
92
+ ...common,
93
+ retryAfterMs: input.retryAfterMs,
94
+ rateLimit: input.rateLimit,
95
+ });
96
+ if (codes.some((code) => SERVER_CODES.has(code) || code.includes("exhausted") || code.includes("unavailable")))
97
+ return new ProviderInternalReason({
98
+ ...common,
99
+ status: input.status,
100
+ retryAfterMs: input.retryAfterMs,
101
+ });
102
+ if (input.status === 429) {
103
+ return new RateLimitReason({
104
+ ...common,
105
+ retryAfterMs: input.retryAfterMs,
106
+ rateLimit: input.rateLimit,
107
+ });
108
+ }
109
+ if (input.status === 408 || input.status === 409 || (input.status !== undefined && input.status >= 500))
110
+ return new ProviderInternalReason({
111
+ ...common,
112
+ status: input.status,
113
+ retryAfterMs: input.retryAfterMs,
114
+ });
115
+ if (codes.some((code) => INVALID_REQUEST_CODES.has(code)))
116
+ return new InvalidRequestReason(common);
117
+ if (input.status === 400 || input.status === 404 || input.status === 413 || input.status === 422)
118
+ return new InvalidRequestReason(common);
119
+ return new UnknownProviderReason({ ...common, status: input.status });
120
+ }
121
+ function providerCodes(value) {
122
+ const decoded = Option.getOrUndefined(decodeJson(value));
123
+ if (!isRecord(decoded))
124
+ return [];
125
+ const error = isRecord(decoded.error) ? decoded.error : undefined;
126
+ return [decoded.code, error?.code, error?.type].filter((value) => typeof value === "string");
127
+ }
128
+ function isRecord(value) {
129
+ return typeof value === "object" && value !== null;
130
+ }
@@ -0,0 +1,15 @@
1
+ import type { LanguageModel, ProviderOptions } from "./schema/index.js";
2
+ export interface Settings extends Readonly<Record<string, unknown>> {
3
+ readonly baseURL?: string;
4
+ readonly headers?: Readonly<Record<string, string>>;
5
+ readonly body?: Readonly<Record<string, unknown>>;
6
+ readonly limits?: {
7
+ readonly context: number;
8
+ readonly input?: number;
9
+ readonly output: number;
10
+ };
11
+ }
12
+ export interface Definition<ProviderSettings extends Settings = Settings, Options extends ProviderOptions = ProviderOptions> {
13
+ readonly model: (modelID: string, settings: ProviderSettings) => LanguageModel<Options>;
14
+ }
15
+ export * as ProviderPackage from "./provider-package.js";
@@ -0,0 +1 @@
1
+ export * as ProviderPackage from "./provider-package.js";
@@ -0,0 +1,23 @@
1
+ import type { LanguageModel, ModelID, ProviderID } from "./schema/index.js";
2
+ export type LanguageModelOptions = Pick<LanguageModel.Input, "defaults" | "compatibility">;
3
+ /**
4
+ * Advanced structural provider definition helper. Built-in providers should
5
+ * prefer explicit `configure(options).model(id)` facades so deployment config is
6
+ * chosen before model selection. The optional `apis` map remains for external
7
+ * structural providers that expose multiple route selectors behind one provider.
8
+ */
9
+ export type LanguageModelFactory<Options extends LanguageModelOptions = LanguageModelOptions> = (id: string | ModelID, options?: Options) => LanguageModel;
10
+ type AnyLanguageModelFactory = (...args: never[]) => LanguageModel;
11
+ export interface Definition<Factory extends AnyLanguageModelFactory = LanguageModelFactory> {
12
+ readonly id: ProviderID;
13
+ readonly model: Factory;
14
+ readonly apis?: Record<string, AnyLanguageModelFactory>;
15
+ }
16
+ type DefinitionShape = {
17
+ readonly id: ProviderID;
18
+ readonly model: (...args: never[]) => LanguageModel;
19
+ readonly apis?: Record<string, (...args: never[]) => LanguageModel>;
20
+ };
21
+ type NoExtraFields<Input, Shape> = Input & Record<Exclude<keyof Input, keyof Shape>, never>;
22
+ export declare const make: <DefinitionType extends DefinitionShape>(definition: NoExtraFields<DefinitionType, DefinitionShape>) => NoExtraFields<DefinitionType, DefinitionShape>;
23
+ export * as Provider from "./provider.js";
@@ -0,0 +1,2 @@
1
+ export const make = (definition) => definition;
2
+ export * as Provider from "./provider.js";
@@ -0,0 +1,2 @@
1
+ export { chatModel as model } from "../../amazon-bedrock-mantle.js";
2
+ export type { Settings } from "../../amazon-bedrock-mantle.js";
@@ -0,0 +1 @@
1
+ export { chatModel as model } from "../../amazon-bedrock-mantle.js";
@@ -0,0 +1,2 @@
1
+ export { responsesModel as model } from "../../amazon-bedrock-mantle.js";
2
+ export type { Settings } from "../../amazon-bedrock-mantle.js";
@@ -0,0 +1 @@
1
+ export { responsesModel as model } from "../../amazon-bedrock-mantle.js";
@@ -0,0 +1,2 @@
1
+ export { chatModel as model } from "../amazon-bedrock-mantle.js";
2
+ export type { Settings } from "../amazon-bedrock-mantle.js";
@@ -0,0 +1 @@
1
+ export { chatModel as model } from "../amazon-bedrock-mantle.js";