@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 +47 -0
- package/dist/protocols/google-images.d.ts +32 -0
- package/dist/protocols/google-images.js +174 -0
- package/dist/protocols/xai-images.d.ts +25 -0
- package/dist/protocols/xai-images.js +93 -0
- package/dist/providers/google.d.ts +5 -0
- package/dist/providers/google.js +12 -2
- package/dist/providers/xai.d.ts +5 -0
- package/dist/providers/xai.js +11 -1
- package/package.json +3 -3
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>;
|
package/dist/providers/google.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Auth } from "../route/auth";
|
|
2
|
-
import { ProviderID } from "../schema";
|
|
3
|
-
import
|
|
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;
|
package/dist/providers/xai.d.ts
CHANGED
|
@@ -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>;
|
package/dist/providers/xai.js
CHANGED
|
@@ -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-
|
|
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-
|
|
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-
|
|
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"
|