@opencode-ai/ai 0.0.0-next-15903 → 0.0.0-next-15909

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -63,6 +63,53 @@ yield *
63
63
  })
64
64
  ```
65
65
 
66
+ xAI image models use the same request API with xAI-native controls:
67
+
68
+ ```ts
69
+ yield *
70
+ Image.generate({
71
+ model: XAI.configure({ apiKey }).image("any-model-id"),
72
+ prompt,
73
+ options: {
74
+ n: 2,
75
+ aspectRatio: "16:9",
76
+ resolution: "1k",
77
+ responseFormat: "b64_json",
78
+ future_option: true,
79
+ },
80
+ http,
81
+ })
82
+ ```
83
+
84
+ Google's current Gemini image models use the same direct API:
85
+
86
+ ```ts
87
+ import { Google } from "@opencode-ai/ai/providers"
88
+
89
+ const googleProgram = Effect.gen(function* () {
90
+ const response = yield* Image.generate({
91
+ model: Google.configure({ apiKey }).image("any-model-id"),
92
+ prompt: "A robot tending a rooftop garden",
93
+ options: {
94
+ aspectRatio: "16:9",
95
+ imageSize: "2K",
96
+ seed: 42,
97
+ thinkingLevel: "HIGH",
98
+ includeThoughts: true,
99
+ futureOption: true,
100
+ },
101
+ http,
102
+ })
103
+
104
+ return response.images
105
+ })
106
+ ```
107
+
108
+ Google image options are request-scoped and inferred from the selected model. Known fields autocomplete while
109
+ future string values and arbitrary native Gemini `generationConfig` fields remain available. Native fields override
110
+ their mapped aliases, and `http.body` is the final deep overlay. The selected model ID is sent to Gemini
111
+ `generateContent` without a local allowlist.
112
+
66
113
  Conversational image generation remains part of the LLM interaction. OpenAI Responses exposes it through its hosted image tool:
67
114
 
68
115
  ```ts
@@ -0,0 +1,32 @@
1
+ import { ImageModel } from "../image";
2
+ import { type Definition as AuthDefinition } from "../route/auth";
3
+ import { type HttpOptions } from "../schema";
4
+ export declare const DEFAULT_BASE_URL = "https://generativelanguage.googleapis.com/v1beta";
5
+ export type GoogleImageString<Known extends string> = Known | (string & {});
6
+ export type GoogleImageOptions = {
7
+ readonly aspectRatio?: GoogleImageString<"1:1" | "2:3" | "3:2" | "3:4" | "4:3" | "4:5" | "5:4" | "9:16" | "16:9" | "21:9">;
8
+ readonly imageSize?: GoogleImageString<"1K" | "2K" | "4K">;
9
+ readonly seed?: number;
10
+ readonly thinkingLevel?: GoogleImageString<"MINIMAL" | "LOW" | "MEDIUM" | "HIGH">;
11
+ readonly includeThoughts?: boolean;
12
+ } & Record<string, unknown>;
13
+ export type GoogleImageBody = Record<string, unknown> & {
14
+ readonly contents: ReadonlyArray<{
15
+ readonly role: "user";
16
+ readonly parts: ReadonlyArray<{
17
+ readonly text: string;
18
+ }>;
19
+ }>;
20
+ readonly generationConfig: Record<string, unknown>;
21
+ };
22
+ export interface ModelInput {
23
+ readonly id: string;
24
+ readonly auth: AuthDefinition;
25
+ readonly baseURL?: string;
26
+ readonly headers?: Record<string, string>;
27
+ readonly http?: HttpOptions;
28
+ }
29
+ export declare const model: (input: ModelInput) => ImageModel<GoogleImageOptions>;
30
+ export declare const GoogleImages: {
31
+ readonly model: (input: ModelInput) => ImageModel<GoogleImageOptions>;
32
+ };
@@ -0,0 +1,174 @@
1
+ import { Effect, Encoding, Schema } from "effect";
2
+ import { Headers, HttpClientRequest } from "effect/unstable/http";
3
+ import { GeneratedImage, ImageModel, ImageResponse } from "../image";
4
+ import { Auth } from "../route/auth";
5
+ import { InvalidProviderOutputReason, LLMError, Usage, mergeHttpOptions, mergeJsonRecords, } from "../schema";
6
+ import { ProviderShared } from "./shared";
7
+ const ADAPTER = "google-images";
8
+ export const DEFAULT_BASE_URL = "https://generativelanguage.googleapis.com/v1beta";
9
+ const GoogleUsage = Schema.StructWithRest(Schema.Struct({
10
+ cachedContentTokenCount: Schema.optional(Schema.Number),
11
+ thoughtsTokenCount: Schema.optional(Schema.Number),
12
+ promptTokenCount: Schema.optional(Schema.Number),
13
+ candidatesTokenCount: Schema.optional(Schema.Number),
14
+ totalTokenCount: Schema.optional(Schema.Number),
15
+ promptTokensDetails: Schema.optional(Schema.Unknown),
16
+ candidatesTokensDetails: Schema.optional(Schema.Unknown),
17
+ }), [Schema.Record(Schema.String, Schema.Unknown)]);
18
+ const GoogleImageResponse = Schema.Struct({
19
+ candidates: Schema.optional(Schema.Array(Schema.Struct({
20
+ index: Schema.optional(Schema.Number),
21
+ content: Schema.optional(Schema.Struct({
22
+ parts: Schema.Array(Schema.Struct({
23
+ text: Schema.optional(Schema.String),
24
+ thought: Schema.optional(Schema.Boolean),
25
+ thoughtSignature: Schema.optional(Schema.String),
26
+ inlineData: Schema.optional(Schema.Struct({
27
+ mimeType: Schema.String,
28
+ data: Schema.String,
29
+ })),
30
+ })),
31
+ })),
32
+ finishReason: Schema.optional(Schema.String),
33
+ finishMessage: Schema.optional(Schema.String),
34
+ safetyRatings: Schema.optional(Schema.Unknown),
35
+ citationMetadata: Schema.optional(Schema.Unknown),
36
+ groundingMetadata: Schema.optional(Schema.Unknown),
37
+ }))),
38
+ usageMetadata: Schema.optional(GoogleUsage),
39
+ modelVersion: Schema.optional(Schema.String),
40
+ responseId: Schema.optional(Schema.String),
41
+ promptFeedback: Schema.optional(Schema.Unknown),
42
+ });
43
+ const nativeOptions = (options) => {
44
+ const { aspectRatio, imageSize, seed, thinkingLevel, includeThoughts, ...native } = options ?? {};
45
+ const image = {
46
+ aspectRatio,
47
+ imageSize,
48
+ };
49
+ const thinkingConfig = {
50
+ thinkingLevel,
51
+ includeThoughts,
52
+ };
53
+ return (mergeJsonRecords({
54
+ responseModalities: ["IMAGE"],
55
+ imageConfig: Object.values(image).some((value) => value !== undefined) ? image : undefined,
56
+ seed,
57
+ thinkingConfig: Object.values(thinkingConfig).some((value) => value !== undefined) ? thinkingConfig : undefined,
58
+ }, native) ?? { responseModalities: ["IMAGE"] });
59
+ };
60
+ const body = (request, overlay) => mergeJsonRecords({
61
+ contents: [{ role: "user", parts: [{ text: request.prompt }] }],
62
+ generationConfig: nativeOptions(request.options),
63
+ }, overlay);
64
+ const invalidOutput = (message, providerMetadata) => new LLMError({
65
+ module: ADAPTER,
66
+ method: "generate",
67
+ reason: new InvalidProviderOutputReason({ message, route: ADAPTER, providerMetadata }),
68
+ });
69
+ const applyQuery = (url, query) => {
70
+ if (!query)
71
+ return url;
72
+ const next = new URL(url);
73
+ Object.entries(query).forEach(([key, value]) => next.searchParams.set(key, value));
74
+ return next.toString();
75
+ };
76
+ export const model = (input) => {
77
+ const route = {
78
+ id: ADAPTER,
79
+ generate: Effect.fn("GoogleImages.generate")(function* (request, execute) {
80
+ const http = mergeHttpOptions(request.model.http, request.http);
81
+ const text = ProviderShared.encodeJson(body(request, http?.body));
82
+ const url = applyQuery(`${(input.baseURL ?? DEFAULT_BASE_URL).replace(/\/$/, "")}/models/${request.model.id}:generateContent`, http?.query);
83
+ const headers = yield* Auth.toEffect(input.auth)({
84
+ request,
85
+ method: "POST",
86
+ url,
87
+ body: text,
88
+ headers: Headers.fromInput({ ...input.headers, ...http?.headers }),
89
+ });
90
+ const response = yield* execute(HttpClientRequest.post(url).pipe(HttpClientRequest.setHeaders(headers), HttpClientRequest.bodyText(text, "application/json")));
91
+ const payload = yield* response.json.pipe(Effect.mapError(() => invalidOutput("Failed to read the Google Images response")));
92
+ const decoded = yield* Schema.decodeUnknownEffect(GoogleImageResponse)(payload).pipe(Effect.mapError(() => invalidOutput("Google Images returned an invalid response")));
93
+ const candidates = decoded.candidates ?? [];
94
+ const candidateMetadata = candidates.map((candidate, candidateIndex) => ({
95
+ index: candidate.index ?? candidateIndex,
96
+ finishReason: candidate.finishReason,
97
+ finishMessage: candidate.finishMessage,
98
+ safetyRatings: candidate.safetyRatings,
99
+ citationMetadata: candidate.citationMetadata,
100
+ groundingMetadata: candidate.groundingMetadata,
101
+ parts: (candidate.content?.parts ?? []).map((part) => part.inlineData === undefined
102
+ ? {
103
+ type: "text",
104
+ text: part.text,
105
+ thought: part.thought,
106
+ thoughtSignature: part.thoughtSignature,
107
+ }
108
+ : {
109
+ type: "inlineData",
110
+ mediaType: part.inlineData.mimeType,
111
+ thought: part.thought,
112
+ thoughtSignature: part.thoughtSignature,
113
+ }),
114
+ }));
115
+ const encoded = candidates.flatMap((candidate, candidateIndex) => (candidate.content?.parts ?? []).flatMap((part, partIndex) => part.inlineData === undefined || part.thought === true
116
+ ? []
117
+ : [{ candidate, candidateIndex, partIndex, inlineData: part.inlineData }]));
118
+ const images = yield* Effect.forEach(encoded, (item) => Effect.fromResult(Encoding.decodeBase64(item.inlineData.data)).pipe(Effect.mapError(() => invalidOutput(`Google Images candidate ${item.candidateIndex} part ${item.partIndex} contains invalid base64 data`)), Effect.map((data) => new GeneratedImage({
119
+ mediaType: item.inlineData.mimeType,
120
+ data,
121
+ providerMetadata: {
122
+ google: {
123
+ candidateIndex: item.candidate.index ?? item.candidateIndex,
124
+ partIndex: item.partIndex,
125
+ finishReason: item.candidate.finishReason,
126
+ safetyRatings: item.candidate.safetyRatings,
127
+ citationMetadata: item.candidate.citationMetadata,
128
+ groundingMetadata: item.candidate.groundingMetadata,
129
+ thoughtSignature: item.candidate.content?.parts[item.partIndex]?.thoughtSignature,
130
+ },
131
+ },
132
+ }))));
133
+ if (images.length === 0) {
134
+ const finishReasons = candidates.flatMap((candidate) => candidate.finishReason === undefined ? [] : [candidate.finishReason]);
135
+ return yield* invalidOutput(`Google Images returned no final images${finishReasons.length === 0 ? "" : ` (finish reasons: ${finishReasons.join(", ")})`}; inspect reason.providerMetadata.google for prompt feedback and candidate details`, {
136
+ google: {
137
+ promptFeedback: decoded.promptFeedback,
138
+ candidates: candidateMetadata,
139
+ },
140
+ });
141
+ }
142
+ const usage = decoded.usageMetadata;
143
+ const outputTokens = usage?.candidatesTokenCount === undefined
144
+ ? undefined
145
+ : usage.candidatesTokenCount + (usage.thoughtsTokenCount ?? 0);
146
+ return new ImageResponse({
147
+ images,
148
+ usage: usage === undefined
149
+ ? undefined
150
+ : new Usage({
151
+ inputTokens: usage.promptTokenCount,
152
+ outputTokens,
153
+ nonCachedInputTokens: ProviderShared.subtractTokens(usage.promptTokenCount, usage.cachedContentTokenCount),
154
+ cacheReadInputTokens: usage.cachedContentTokenCount,
155
+ reasoningTokens: usage.thoughtsTokenCount,
156
+ totalTokens: ProviderShared.totalTokens(usage.promptTokenCount, outputTokens, usage.totalTokenCount),
157
+ providerMetadata: { google: usage },
158
+ }),
159
+ providerMetadata: {
160
+ google: {
161
+ modelVersion: decoded.modelVersion,
162
+ responseId: decoded.responseId,
163
+ promptFeedback: decoded.promptFeedback,
164
+ candidates: candidateMetadata,
165
+ },
166
+ },
167
+ });
168
+ }),
169
+ };
170
+ return ImageModel.make({ id: input.id, provider: "google", route, http: input.http });
171
+ };
172
+ export const GoogleImages = {
173
+ model,
174
+ };
@@ -0,0 +1,25 @@
1
+ import { ImageModel } from "../image";
2
+ import { type Definition as AuthDefinition } from "../route/auth";
3
+ import { type HttpOptions } from "../schema";
4
+ export declare const DEFAULT_BASE_URL = "https://api.x.ai/v1";
5
+ export declare const PATH = "/images/generations";
6
+ export type XAIImageString<Known extends string> = Known | (string & {});
7
+ export type XAIImageOptions = {
8
+ readonly n?: number;
9
+ 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">;
10
+ 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">;
11
+ readonly resolution?: XAIImageString<"1k" | "2k">;
12
+ readonly responseFormat?: XAIImageString<"url" | "b64_json">;
13
+ readonly response_format?: XAIImageString<"url" | "b64_json">;
14
+ } & Record<string, unknown>;
15
+ export interface ModelInput {
16
+ readonly id: string;
17
+ readonly auth: AuthDefinition;
18
+ readonly baseURL?: string;
19
+ readonly headers?: Record<string, string>;
20
+ readonly http?: HttpOptions;
21
+ }
22
+ export declare const model: (input: ModelInput) => ImageModel<XAIImageOptions>;
23
+ export declare const XAIImages: {
24
+ readonly model: (input: ModelInput) => ImageModel<XAIImageOptions>;
25
+ };
@@ -0,0 +1,93 @@
1
+ import { Effect, Encoding, Schema } from "effect";
2
+ import { Headers, HttpClientRequest } from "effect/unstable/http";
3
+ import { GeneratedImage, ImageModel, ImageResponse } from "../image";
4
+ import { Auth } from "../route/auth";
5
+ import { InvalidProviderOutputReason, LLMError, Usage, mergeHttpOptions, mergeJsonRecords, } from "../schema";
6
+ import { ProviderShared, optionalNull } from "./shared";
7
+ const ADAPTER = "xai-images";
8
+ export const DEFAULT_BASE_URL = "https://api.x.ai/v1";
9
+ export const PATH = "/images/generations";
10
+ const XAIImageResponse = Schema.Struct({
11
+ data: Schema.Array(Schema.Struct({
12
+ b64_json: optionalNull(Schema.String),
13
+ url: optionalNull(Schema.String),
14
+ revised_prompt: optionalNull(Schema.String),
15
+ mime_type: optionalNull(Schema.String),
16
+ })),
17
+ usage: Schema.optional(Schema.Unknown),
18
+ });
19
+ const nativeOptions = (options) => {
20
+ if (!options)
21
+ return undefined;
22
+ const { aspectRatio, responseFormat, ...native } = options;
23
+ return {
24
+ aspect_ratio: aspectRatio,
25
+ response_format: responseFormat,
26
+ ...native,
27
+ };
28
+ };
29
+ const invalidOutput = (message) => new LLMError({
30
+ module: ADAPTER,
31
+ method: "generate",
32
+ reason: new InvalidProviderOutputReason({ message, route: ADAPTER }),
33
+ });
34
+ const applyQuery = (url, query) => {
35
+ if (!query)
36
+ return url;
37
+ const next = new URL(url);
38
+ Object.entries(query).forEach(([key, value]) => next.searchParams.set(key, value));
39
+ return next.toString();
40
+ };
41
+ export const model = (input) => {
42
+ const route = {
43
+ id: ADAPTER,
44
+ generate: Effect.fn("XAIImages.generate")(function* (request, execute) {
45
+ const http = mergeHttpOptions(request.model.http, request.http);
46
+ const requestBody = mergeJsonRecords({ model: request.model.id, prompt: request.prompt }, nativeOptions(request.options), http?.body);
47
+ const text = ProviderShared.encodeJson(requestBody);
48
+ const url = applyQuery(`${(input.baseURL ?? DEFAULT_BASE_URL).replace(/\/$/, "")}${PATH}`, http?.query);
49
+ const headers = yield* Auth.toEffect(input.auth)({
50
+ request,
51
+ method: "POST",
52
+ url,
53
+ body: text,
54
+ headers: Headers.fromInput({ ...input.headers, ...http?.headers }),
55
+ });
56
+ const response = yield* execute(HttpClientRequest.post(url).pipe(HttpClientRequest.setHeaders(headers), HttpClientRequest.bodyText(text, "application/json")));
57
+ const payload = yield* response.json.pipe(Effect.mapError(() => invalidOutput("Failed to read the xAI Images response")));
58
+ const decoded = yield* Schema.decodeUnknownEffect(XAIImageResponse)(payload).pipe(Effect.mapError(() => invalidOutput("xAI Images returned an invalid response")));
59
+ const images = yield* Effect.forEach(decoded.data, (item, index) => {
60
+ const mediaType = item.mime_type ?? "application/octet-stream";
61
+ if (item.b64_json)
62
+ 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({
63
+ mediaType,
64
+ data,
65
+ providerMetadata: item.revised_prompt === undefined || item.revised_prompt === null
66
+ ? undefined
67
+ : { xai: { revisedPrompt: item.revised_prompt } },
68
+ })));
69
+ if (item.url)
70
+ return Effect.succeed(new GeneratedImage({
71
+ mediaType,
72
+ data: item.url,
73
+ providerMetadata: item.revised_prompt === undefined || item.revised_prompt === null
74
+ ? undefined
75
+ : { xai: { revisedPrompt: item.revised_prompt } },
76
+ }));
77
+ return Effect.fail(invalidOutput(`xAI Images result ${index} has neither image data nor a URL`));
78
+ });
79
+ if (images.length === 0)
80
+ return yield* invalidOutput("xAI Images returned no images");
81
+ const usage = ProviderShared.isRecord(decoded.usage) ? decoded.usage : undefined;
82
+ return new ImageResponse({
83
+ images,
84
+ usage: usage === undefined ? undefined : new Usage({ providerMetadata: { xai: usage } }),
85
+ providerMetadata: usage === undefined ? undefined : { xai: { usage } },
86
+ });
87
+ }),
88
+ };
89
+ return ImageModel.make({ id: input.id, provider: "xai", route, http: input.http });
90
+ };
91
+ export const XAIImages = {
92
+ model,
93
+ };
@@ -2,6 +2,7 @@ import type { RouteDefaultsInput } from "../route/client";
2
2
  import type { ProviderAuthOption } from "../route/auth-options";
3
3
  import type { ProviderPackage } from "../provider-package";
4
4
  import { type ModelID, type ProviderOptions } from "../schema";
5
+ export type { GoogleImageOptions } from "../protocols/google-images";
5
6
  export declare const id: string & import("effect/Brand").Brand<"LLM.ProviderID">;
6
7
  export declare const routes: import("../route").Route<{
7
8
  readonly contents: readonly import("effect/Schema").Struct.ReadonlySide<{
@@ -69,15 +70,19 @@ export interface Settings extends ProviderPackage.Settings {
69
70
  export declare const configure: (input?: Config) => {
70
71
  id: string & import("effect/Brand").Brand<"LLM.ProviderID">;
71
72
  model: (modelID: string | ModelID) => import("..").Model;
73
+ image: (modelID: string | ModelID) => import("..").ImageModel<import("./google").GoogleImageOptions>;
72
74
  configure: (input?: Config) => /*elided*/ any;
73
75
  };
74
76
  export declare const provider: {
75
77
  id: string & import("effect/Brand").Brand<"LLM.ProviderID">;
76
78
  model: (modelID: string | ModelID) => import("..").Model;
79
+ image: (modelID: string | ModelID) => import("..").ImageModel<import("./google").GoogleImageOptions>;
77
80
  configure: (input?: Config) => {
78
81
  id: string & import("effect/Brand").Brand<"LLM.ProviderID">;
79
82
  model: (modelID: string | ModelID) => import("..").Model;
83
+ image: (modelID: string | ModelID) => import("..").ImageModel<import("./google").GoogleImageOptions>;
80
84
  configure: /*elided*/ any;
81
85
  };
82
86
  };
83
87
  export declare const model: ProviderPackage.Definition<Settings>["model"];
88
+ export declare const image: (modelID: string | ModelID) => import("..").ImageModel<import("./google").GoogleImageOptions>;
@@ -1,6 +1,7 @@
1
1
  import { Auth } from "../route/auth";
2
- import { ProviderID } from "../schema";
3
- import * as Gemini from "../protocols/gemini";
2
+ import { HttpOptions, ProviderID, mergeHttpOptions } from "../schema";
3
+ import { Gemini } from "../protocols/gemini";
4
+ import { GoogleImages } from "../protocols/google-images";
4
5
  export const id = ProviderID.make("google");
5
6
  export const routes = [Gemini.route];
6
7
  const auth = (options) => {
@@ -16,9 +17,17 @@ const configuredRoute = (input) => {
16
17
  };
17
18
  export const configure = (input = {}) => {
18
19
  const route = configuredRoute(input);
20
+ const image = (modelID) => GoogleImages.model({
21
+ id: modelID,
22
+ auth: auth(input),
23
+ baseURL: input.baseURL,
24
+ headers: input.headers,
25
+ http: mergeHttpOptions(input.http === undefined ? undefined : HttpOptions.make(input.http)),
26
+ });
19
27
  return {
20
28
  id,
21
29
  model: (modelID) => route.model({ id: modelID }),
30
+ image,
22
31
  configure,
23
32
  };
24
33
  };
@@ -31,3 +40,4 @@ export const model = (modelID, settings) => configure({
31
40
  limits: settings.limits,
32
41
  providerOptions: settings.providerOptions,
33
42
  }).model(modelID);
43
+ export const image = provider.image;
@@ -5,6 +5,7 @@ export declare const id: string & import("effect/Brand").Brand<"LLM.ProviderID">
5
5
  export type ModelOptions = RouteDefaultsInput & ProviderAuthOption<"optional"> & {
6
6
  readonly baseURL?: string;
7
7
  };
8
+ export type { XAIImageOptions } from "../protocols/xai-images";
8
9
  export declare const routes: (import("../route").Route<{
9
10
  readonly messages: readonly (import("effect/Schema").Struct.ReadonlySide<{
10
11
  readonly role: import("effect/Schema").Literal<"system">;
@@ -162,6 +163,7 @@ export declare const configure: (input?: ModelOptions) => {
162
163
  model: (modelID: string | ModelID) => import("..").Model;
163
164
  responses: (modelID: string | ModelID) => import("..").Model;
164
165
  chat: (modelID: string | ModelID) => import("..").Model;
166
+ image: (modelID: string | ModelID) => import("..").ImageModel<import("./xai").XAIImageOptions>;
165
167
  configure: (input?: ModelOptions) => /*elided*/ any;
166
168
  };
167
169
  export declare const provider: {
@@ -169,14 +171,17 @@ export declare const provider: {
169
171
  model: (modelID: string | ModelID) => import("..").Model;
170
172
  responses: (modelID: string | ModelID) => import("..").Model;
171
173
  chat: (modelID: string | ModelID) => import("..").Model;
174
+ image: (modelID: string | ModelID) => import("..").ImageModel<import("./xai").XAIImageOptions>;
172
175
  configure: (input?: ModelOptions) => {
173
176
  id: string & import("effect/Brand").Brand<"LLM.ProviderID">;
174
177
  model: (modelID: string | ModelID) => import("..").Model;
175
178
  responses: (modelID: string | ModelID) => import("..").Model;
176
179
  chat: (modelID: string | ModelID) => import("..").Model;
180
+ image: (modelID: string | ModelID) => import("..").ImageModel<import("./xai").XAIImageOptions>;
177
181
  configure: /*elided*/ any;
178
182
  };
179
183
  };
180
184
  export declare const model: (modelID: string | ModelID) => import("..").Model;
181
185
  export declare const responses: (modelID: string | ModelID) => import("..").Model;
182
186
  export declare const chat: (modelID: string | ModelID) => import("..").Model;
187
+ export declare const image: (modelID: string | ModelID) => import("..").ImageModel<import("./xai").XAIImageOptions>;
@@ -1,8 +1,9 @@
1
1
  import { AuthOptions } from "../route/auth-options";
2
- import { ProviderID } from "../schema";
2
+ import { HttpOptions, ProviderID } from "../schema";
3
3
  import * as OpenAICompatibleProfiles from "./openai-compatible-profile";
4
4
  import * as OpenAICompatibleChat from "../protocols/openai-compatible-chat";
5
5
  import * as OpenAIResponses from "../protocols/openai-responses";
6
+ import { XAIImages } from "../protocols/xai-images";
6
7
  export const id = ProviderID.make("xai");
7
8
  export const routes = [OpenAIResponses.route, OpenAICompatibleChat.route];
8
9
  const auth = (options) => AuthOptions.bearer(options, "XAI_API_KEY");
@@ -29,11 +30,19 @@ export const configure = (input = {}) => {
29
30
  const chatRoute = configuredChatRoute(input);
30
31
  const responses = (modelID) => responsesRoute.model({ id: modelID });
31
32
  const chat = (modelID) => chatRoute.model({ id: modelID });
33
+ const image = (modelID) => XAIImages.model({
34
+ id: modelID,
35
+ auth: auth(input),
36
+ baseURL: input.baseURL ?? OpenAICompatibleProfiles.profiles.xai.baseURL,
37
+ headers: input.headers,
38
+ http: input.http === undefined ? undefined : HttpOptions.make(input.http),
39
+ });
32
40
  return {
33
41
  id,
34
42
  model: responses,
35
43
  responses,
36
44
  chat,
45
+ image,
37
46
  configure,
38
47
  };
39
48
  };
@@ -41,3 +50,4 @@ export const provider = configure();
41
50
  export const model = provider.model;
42
51
  export const responses = provider.responses;
43
52
  export const chat = provider.chat;
53
+ export const image = provider.image;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
- "version": "0.0.0-next-15903",
3
+ "version": "0.0.0-next-15909",
4
4
  "name": "@opencode-ai/ai",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -26,7 +26,7 @@
26
26
  "devDependencies": {
27
27
  "@clack/prompts": "1.0.0-alpha.1",
28
28
  "@effect/platform-node": "4.0.0-beta.98",
29
- "@opencode-ai/http-recorder": "0.0.0-next-15903",
29
+ "@opencode-ai/http-recorder": "0.0.0-next-15909",
30
30
  "@tsconfig/bun": "1.0.9",
31
31
  "@types/bun": "1.3.13",
32
32
  "@typescript/native-preview": "7.0.0-dev.20251207.1",
@@ -35,7 +35,7 @@
35
35
  "dependencies": {
36
36
  "@smithy/eventstream-codec": "4.2.14",
37
37
  "@smithy/util-utf8": "4.2.2",
38
- "@opencode-ai/schema": "0.0.0-next-15903",
38
+ "@opencode-ai/schema": "0.0.0-next-15909",
39
39
  "aws4fetch": "1.0.20",
40
40
  "effect": "4.0.0-beta.98",
41
41
  "google-auth-library": "10.5.0"