@opencode-ai/ai 0.0.0-next-15910 → 0.0.0-next-15911
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 +43 -1
- package/dist/image.d.ts +37 -0
- package/dist/image.js +31 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/protocols/google-images.d.ts +1 -3
- package/dist/protocols/google-images.js +23 -6
- package/dist/protocols/openai-compatible-responses.d.ts +3 -3
- package/dist/protocols/openai-images.d.ts +3 -1
- package/dist/protocols/openai-images.js +123 -37
- package/dist/protocols/openai-responses.d.ts +18 -18
- package/dist/protocols/utils/image-input.d.ts +21 -0
- package/dist/protocols/utils/image-input.js +22 -0
- package/dist/protocols/xai-images.d.ts +1 -0
- package/dist/protocols/xai-images.js +20 -2
- package/dist/protocols/zai-images.js +3 -0
- package/dist/providers/azure.d.ts +3 -3
- package/dist/providers/github-copilot.d.ts +3 -3
- package/dist/providers/google-vertex-responses.d.ts +3 -3
- package/dist/providers/openai-compatible-responses.d.ts +3 -3
- package/dist/providers/openai.d.ts +6 -6
- package/dist/providers/xai.d.ts +3 -3
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -29,7 +29,7 @@ Run `LLMClient.stream(request)` instead of `generate` when you want incremental
|
|
|
29
29
|
Use `Image.generate` with an image model for direct asset generation:
|
|
30
30
|
|
|
31
31
|
```ts
|
|
32
|
-
import { Image } from "@opencode-ai/ai"
|
|
32
|
+
import { Image, ImageInput } from "@opencode-ai/ai"
|
|
33
33
|
import { OpenAI } from "@opencode-ai/ai/providers"
|
|
34
34
|
|
|
35
35
|
const program = Effect.gen(function* () {
|
|
@@ -49,6 +49,48 @@ const program = Effect.gen(function* () {
|
|
|
49
49
|
})
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
+
Pass ordered image inputs to the same method for editing, composition, or image-conditioned generation:
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
const response =
|
|
56
|
+
yield *
|
|
57
|
+
Image.generate({
|
|
58
|
+
model,
|
|
59
|
+
prompt: "Combine these product photos into one studio scene",
|
|
60
|
+
images: [
|
|
61
|
+
ImageInput.bytes(firstBytes, "image/png"),
|
|
62
|
+
ImageInput.url("https://example.com/second.webp"),
|
|
63
|
+
ImageInput.file("file_123"),
|
|
64
|
+
],
|
|
65
|
+
options,
|
|
66
|
+
http,
|
|
67
|
+
})
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
`ImageInput.fileUri(uri, mediaType)` represents provider file URIs such as Gemini Files. Raw strings are not
|
|
71
|
+
accepted as image inputs, avoiding ambiguity between base64, URLs, and provider IDs. Empty or omitted `images`
|
|
72
|
+
uses text-to-image generation; a non-empty array selects the provider's edit behavior without enforcing provider
|
|
73
|
+
image-count limits locally. `images` is the only common image-editing field. OpenAI uses multipart for byte/data-URL
|
|
74
|
+
edits and its JSON reference body for URL or file-ID edits. Its provider-specific `options.mask` accepts an
|
|
75
|
+
`ImageInput` for inpainting:
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
yield *
|
|
79
|
+
Image.generate({
|
|
80
|
+
model: OpenAI.configure({ apiKey }).image("gpt-image-2"),
|
|
81
|
+
prompt,
|
|
82
|
+
images: [ImageInput.bytes(sourceBytes, "image/png")],
|
|
83
|
+
options: { mask: ImageInput.bytes(maskBytes, "image/png") },
|
|
84
|
+
})
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
The OpenAI adapter extracts this helper value into the edit request's native `mask` field rather than passing the
|
|
88
|
+
tagged `ImageInput` object through as an ordinary option. On multipart requests, `http.body` can override option
|
|
89
|
+
fields but not structural `model`, `prompt`, `image[]`, or `mask` fields, and the transport owns the multipart
|
|
90
|
+
`Content-Type` boundary. For JSON requests, `http.body` remains the final raw-native overlay. Gemini does not fetch
|
|
91
|
+
public HTTP URLs, and hosted Z.ai image generation does not accept image inputs. These cases fail with
|
|
92
|
+
`InvalidRequest` before network I/O.
|
|
93
|
+
|
|
52
94
|
Provider-native image options belong to each request. Raw `http.body` fields have final precedence over them:
|
|
53
95
|
|
|
54
96
|
```ts
|
package/dist/image.d.ts
CHANGED
|
@@ -28,9 +28,46 @@ export declare namespace ImageModel {
|
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
export declare const ImageModelSchema: Schema.declare<ImageModel<ImageOptions>, ImageModel<ImageOptions>>;
|
|
31
|
+
export declare const ImageInputSchema: Schema.toTaggedUnion<"type", readonly [Schema.Struct<{
|
|
32
|
+
readonly type: Schema.Literal<"bytes">;
|
|
33
|
+
readonly data: Schema.Uint8Array;
|
|
34
|
+
readonly mediaType: Schema.String;
|
|
35
|
+
}>, Schema.Struct<{
|
|
36
|
+
readonly type: Schema.Literal<"url">;
|
|
37
|
+
readonly url: Schema.String;
|
|
38
|
+
}>, Schema.Struct<{
|
|
39
|
+
readonly type: Schema.Literal<"file-id">;
|
|
40
|
+
readonly id: Schema.String;
|
|
41
|
+
}>, Schema.Struct<{
|
|
42
|
+
readonly type: Schema.Literal<"file-uri">;
|
|
43
|
+
readonly uri: Schema.String;
|
|
44
|
+
readonly mediaType: Schema.String;
|
|
45
|
+
}>]>;
|
|
46
|
+
export type ImageInput = Schema.Schema.Type<typeof ImageInputSchema>;
|
|
47
|
+
export declare const ImageInput: {
|
|
48
|
+
readonly bytes: (data: Uint8Array, mediaType: string) => ImageInput;
|
|
49
|
+
readonly url: (url: string) => ImageInput;
|
|
50
|
+
readonly file: (id: string) => ImageInput;
|
|
51
|
+
readonly fileUri: (uri: string, mediaType: string) => ImageInput;
|
|
52
|
+
};
|
|
31
53
|
declare const ImageRequest_base: Schema.Class<ImageRequest, Schema.Struct<{
|
|
32
54
|
readonly model: Schema.declare<ImageModel<ImageOptions>, ImageModel<ImageOptions>>;
|
|
33
55
|
readonly prompt: Schema.String;
|
|
56
|
+
readonly images: Schema.optional<Schema.$Array<Schema.toTaggedUnion<"type", readonly [Schema.Struct<{
|
|
57
|
+
readonly type: Schema.Literal<"bytes">;
|
|
58
|
+
readonly data: Schema.Uint8Array;
|
|
59
|
+
readonly mediaType: Schema.String;
|
|
60
|
+
}>, Schema.Struct<{
|
|
61
|
+
readonly type: Schema.Literal<"url">;
|
|
62
|
+
readonly url: Schema.String;
|
|
63
|
+
}>, Schema.Struct<{
|
|
64
|
+
readonly type: Schema.Literal<"file-id">;
|
|
65
|
+
readonly id: Schema.String;
|
|
66
|
+
}>, Schema.Struct<{
|
|
67
|
+
readonly type: Schema.Literal<"file-uri">;
|
|
68
|
+
readonly uri: Schema.String;
|
|
69
|
+
readonly mediaType: Schema.String;
|
|
70
|
+
}>]>>>;
|
|
34
71
|
readonly options: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;
|
|
35
72
|
readonly http: Schema.optional<typeof HttpOptions>;
|
|
36
73
|
}>, {}>;
|
package/dist/image.js
CHANGED
|
@@ -24,9 +24,40 @@ export class ImageModel {
|
|
|
24
24
|
export const ImageModelSchema = Schema.declare((value) => value instanceof ImageModel, {
|
|
25
25
|
expected: "Image.Model",
|
|
26
26
|
});
|
|
27
|
+
const ImageBytesInput = Schema.Struct({
|
|
28
|
+
type: Schema.Literal("bytes"),
|
|
29
|
+
data: Schema.Uint8Array,
|
|
30
|
+
mediaType: Schema.String,
|
|
31
|
+
});
|
|
32
|
+
const ImageUrlInput = Schema.Struct({
|
|
33
|
+
type: Schema.Literal("url"),
|
|
34
|
+
url: Schema.String,
|
|
35
|
+
});
|
|
36
|
+
const ImageFileIDInput = Schema.Struct({
|
|
37
|
+
type: Schema.Literal("file-id"),
|
|
38
|
+
id: Schema.String,
|
|
39
|
+
});
|
|
40
|
+
const ImageFileURIInput = Schema.Struct({
|
|
41
|
+
type: Schema.Literal("file-uri"),
|
|
42
|
+
uri: Schema.String,
|
|
43
|
+
mediaType: Schema.String,
|
|
44
|
+
});
|
|
45
|
+
export const ImageInputSchema = Schema.Union([
|
|
46
|
+
ImageBytesInput,
|
|
47
|
+
ImageUrlInput,
|
|
48
|
+
ImageFileIDInput,
|
|
49
|
+
ImageFileURIInput,
|
|
50
|
+
]).pipe(Schema.toTaggedUnion("type"));
|
|
51
|
+
export const ImageInput = {
|
|
52
|
+
bytes: (data, mediaType) => ({ type: "bytes", data, mediaType }),
|
|
53
|
+
url: (url) => ({ type: "url", url }),
|
|
54
|
+
file: (id) => ({ type: "file-id", id }),
|
|
55
|
+
fileUri: (uri, mediaType) => ({ type: "file-uri", uri, mediaType }),
|
|
56
|
+
};
|
|
27
57
|
export class ImageRequest extends Schema.Class("Image.Request")({
|
|
28
58
|
model: ImageModelSchema,
|
|
29
59
|
prompt: Schema.String,
|
|
60
|
+
images: Schema.optional(Schema.Array(ImageInputSchema)),
|
|
30
61
|
options: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)),
|
|
31
62
|
http: Schema.optional(HttpOptions),
|
|
32
63
|
}) {
|
package/dist/index.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ export { ProviderPackage } from "./provider-package";
|
|
|
6
6
|
export { isContextOverflow, isContextOverflowFailure } from "./provider-error";
|
|
7
7
|
export type { RouteModelInput, RouteRoutedModelInput, Interface as LLMClientShape, Service as LLMClientService, } from "./route/client";
|
|
8
8
|
export * from "./schema";
|
|
9
|
-
export { GeneratedImage, ImageModel, ImageRequest, ImageResponse } from "./image";
|
|
9
|
+
export { GeneratedImage, ImageInput, ImageInputSchema, ImageModel, ImageRequest, ImageResponse } from "./image";
|
|
10
10
|
export type { ImageModelOptions, ImageOptions, ImageRequestFor, ImageRequestInput, ImageRoute } from "./image";
|
|
11
11
|
export { Image } from "./image";
|
|
12
12
|
export { Tool, ToolFailure, toDefinitions } from "./tool";
|
package/dist/index.js
CHANGED
|
@@ -5,7 +5,7 @@ export { Provider } from "./provider";
|
|
|
5
5
|
export { ProviderPackage } from "./provider-package";
|
|
6
6
|
export { isContextOverflow, isContextOverflowFailure } from "./provider-error";
|
|
7
7
|
export * from "./schema";
|
|
8
|
-
export { GeneratedImage, ImageModel, ImageRequest, ImageResponse } from "./image";
|
|
8
|
+
export { GeneratedImage, ImageInput, ImageInputSchema, ImageModel, ImageRequest, ImageResponse } from "./image";
|
|
9
9
|
export { Image } from "./image";
|
|
10
10
|
export { Tool, ToolFailure, toDefinitions } from "./tool";
|
|
11
11
|
export { ToolRuntime } from "./tool-runtime";
|
|
@@ -13,9 +13,7 @@ export type GoogleImageOptions = {
|
|
|
13
13
|
export type GoogleImageBody = Record<string, unknown> & {
|
|
14
14
|
readonly contents: ReadonlyArray<{
|
|
15
15
|
readonly role: "user";
|
|
16
|
-
readonly parts: ReadonlyArray<
|
|
17
|
-
readonly text: string;
|
|
18
|
-
}>;
|
|
16
|
+
readonly parts: ReadonlyArray<Record<string, unknown>>;
|
|
19
17
|
}>;
|
|
20
18
|
readonly generationConfig: Record<string, unknown>;
|
|
21
19
|
};
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { Effect, Encoding, Schema } from "effect";
|
|
2
2
|
import { Headers, HttpClientRequest } from "effect/unstable/http";
|
|
3
|
-
import { GeneratedImage, ImageModel, ImageResponse } from "../image";
|
|
3
|
+
import { GeneratedImage, ImageModel, ImageResponse, } from "../image";
|
|
4
4
|
import { Auth } from "../route/auth";
|
|
5
5
|
import { InvalidProviderOutputReason, LLMError, Usage, mergeHttpOptions, mergeJsonRecords, } from "../schema";
|
|
6
6
|
import { ProviderShared } from "./shared";
|
|
7
|
+
import { ImageInputs } from "./utils/image-input";
|
|
7
8
|
const ADAPTER = "google-images";
|
|
8
9
|
export const DEFAULT_BASE_URL = "https://generativelanguage.googleapis.com/v1beta";
|
|
9
10
|
const GoogleUsage = Schema.StructWithRest(Schema.Struct({
|
|
@@ -57,10 +58,6 @@ const nativeOptions = (options) => {
|
|
|
57
58
|
thinkingConfig: Object.values(thinkingConfig).some((value) => value !== undefined) ? thinkingConfig : undefined,
|
|
58
59
|
}, native) ?? { responseModalities: ["IMAGE"] });
|
|
59
60
|
};
|
|
60
|
-
const body = (request, overlay) => mergeJsonRecords({
|
|
61
|
-
contents: [{ role: "user", parts: [{ text: request.prompt }] }],
|
|
62
|
-
generationConfig: nativeOptions(request.options),
|
|
63
|
-
}, overlay);
|
|
64
61
|
const invalidOutput = (message, providerMetadata) => new LLMError({
|
|
65
62
|
module: ADAPTER,
|
|
66
63
|
method: "generate",
|
|
@@ -77,8 +74,13 @@ export const model = (input) => {
|
|
|
77
74
|
const route = {
|
|
78
75
|
id: ADAPTER,
|
|
79
76
|
generate: Effect.fn("GoogleImages.generate")(function* (request, execute) {
|
|
77
|
+
const imageParts = yield* Effect.forEach(request.images ?? [], googleImagePart);
|
|
80
78
|
const http = mergeHttpOptions(request.model.http, request.http);
|
|
81
|
-
const
|
|
79
|
+
const requestBody = mergeJsonRecords({
|
|
80
|
+
contents: [{ role: "user", parts: [{ text: request.prompt }, ...imageParts] }],
|
|
81
|
+
generationConfig: nativeOptions(request.options),
|
|
82
|
+
}, http?.body);
|
|
83
|
+
const text = ProviderShared.encodeJson(requestBody);
|
|
82
84
|
const url = applyQuery(`${(input.baseURL ?? DEFAULT_BASE_URL).replace(/\/$/, "")}/models/${request.model.id}:generateContent`, http?.query);
|
|
83
85
|
const headers = yield* Auth.toEffect(input.auth)({
|
|
84
86
|
request,
|
|
@@ -169,6 +171,21 @@ export const model = (input) => {
|
|
|
169
171
|
};
|
|
170
172
|
return ImageModel.make({ id: input.id, provider: "google", route, http: input.http });
|
|
171
173
|
};
|
|
174
|
+
const googleImagePart = (image) => {
|
|
175
|
+
if (image.type === "bytes")
|
|
176
|
+
return Effect.succeed({ inlineData: { mimeType: image.mediaType, data: Encoding.encodeBase64(image.data) } });
|
|
177
|
+
if (image.type === "file-uri")
|
|
178
|
+
return Effect.succeed({ fileData: { mimeType: image.mediaType, fileUri: image.uri } });
|
|
179
|
+
if (image.type === "url")
|
|
180
|
+
return ImageInputs.decodeDataUrl(image.url, ADAPTER).pipe(Effect.flatMap((decoded) => {
|
|
181
|
+
if (decoded === undefined)
|
|
182
|
+
return Effect.fail(ImageInputs.invalid(ADAPTER, "Google generateContent does not fetch public image URLs; use bytes, a data URL, or a Gemini file URI"));
|
|
183
|
+
return Effect.succeed({
|
|
184
|
+
inlineData: { mimeType: decoded.mediaType, data: Encoding.encodeBase64(decoded.data) },
|
|
185
|
+
});
|
|
186
|
+
}));
|
|
187
|
+
return Effect.fail(ImageInputs.invalid(ADAPTER, "Google generateContent requires Gemini file URIs rather than provider file IDs"));
|
|
188
|
+
};
|
|
172
189
|
export const GoogleImages = {
|
|
173
190
|
model,
|
|
174
191
|
};
|
|
@@ -71,13 +71,13 @@ export declare const route: Route<{
|
|
|
71
71
|
} | {
|
|
72
72
|
readonly type: "image_generation";
|
|
73
73
|
readonly output_format?: "png" | "jpeg" | "webp" | undefined;
|
|
74
|
+
readonly size?: string | undefined;
|
|
75
|
+
readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
|
|
76
|
+
readonly background?: "auto" | "opaque" | "transparent" | undefined;
|
|
74
77
|
readonly output_compression?: number | undefined;
|
|
75
78
|
readonly action?: "generate" | "auto" | "edit" | undefined;
|
|
76
|
-
readonly background?: "auto" | "opaque" | "transparent" | undefined;
|
|
77
79
|
readonly input_fidelity?: "low" | "high" | undefined;
|
|
78
80
|
readonly partial_images?: number | undefined;
|
|
79
|
-
readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
|
|
80
|
-
readonly size?: string | undefined;
|
|
81
81
|
})[] | undefined;
|
|
82
82
|
readonly temperature?: number | undefined;
|
|
83
83
|
readonly tool_choice?: "required" | "none" | "auto" | import("effect/Schema").Struct.ReadonlySide<{
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { ImageModel } from "../image";
|
|
1
|
+
import { ImageModel, type ImageInput } from "../image";
|
|
2
2
|
import { type Definition as AuthDefinition } from "../route/auth";
|
|
3
3
|
import { type HttpOptions } from "../schema";
|
|
4
4
|
export declare const DEFAULT_BASE_URL = "https://api.openai.com/v1";
|
|
5
5
|
export declare const PATH = "/images/generations";
|
|
6
|
+
export declare const EDIT_PATH = "/images/edits";
|
|
6
7
|
export type OpenAIImageString<Known extends string> = Known | (string & {});
|
|
7
8
|
export type OpenAIImageOptions = {
|
|
9
|
+
readonly mask?: ImageInput;
|
|
8
10
|
readonly n?: number;
|
|
9
11
|
readonly size?: OpenAIImageString<"auto" | "256x256" | "512x512" | "1024x1024" | "1536x1024" | "1024x1536" | "1792x1024" | "1024x1792">;
|
|
10
12
|
readonly quality?: OpenAIImageString<"auto" | "low" | "medium" | "high" | "standard" | "hd">;
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { Effect, Encoding, Schema } from "effect";
|
|
2
|
-
import { Headers, HttpClientRequest } from "effect/unstable/http";
|
|
3
|
-
import { ImageModel, GeneratedImage, ImageResponse } from "../image";
|
|
2
|
+
import { Headers, HttpClientRequest, HttpClientResponse } from "effect/unstable/http";
|
|
3
|
+
import { ImageModel, GeneratedImage, ImageResponse, } from "../image";
|
|
4
4
|
import { Auth } from "../route/auth";
|
|
5
5
|
import { InvalidProviderOutputReason, LLMError, Usage, mergeHttpOptions, mergeJsonRecords, } from "../schema";
|
|
6
6
|
import { ProviderShared } from "./shared";
|
|
7
|
+
import { ImageInputs } from "./utils/image-input";
|
|
7
8
|
import { OpenAIImage } from "./utils/openai-image";
|
|
8
9
|
const ADAPTER = "openai-images";
|
|
9
10
|
export const DEFAULT_BASE_URL = "https://api.openai.com/v1";
|
|
10
11
|
export const PATH = "/images/generations";
|
|
12
|
+
export const EDIT_PATH = "/images/edits";
|
|
11
13
|
const OpenAIImageResponse = Schema.Struct({
|
|
12
14
|
data: Schema.Array(Schema.Struct({
|
|
13
15
|
b64_json: Schema.optional(Schema.String),
|
|
@@ -26,7 +28,7 @@ const OpenAIImageResponse = Schema.Struct({
|
|
|
26
28
|
const nativeOptions = (options) => {
|
|
27
29
|
if (!options)
|
|
28
30
|
return undefined;
|
|
29
|
-
const { outputFormat, outputCompression, ...native } = options;
|
|
31
|
+
const { mask: _, outputFormat, outputCompression, ...native } = options;
|
|
30
32
|
return {
|
|
31
33
|
output_format: outputFormat,
|
|
32
34
|
output_compression: outputCompression,
|
|
@@ -49,10 +51,85 @@ export const model = (input) => {
|
|
|
49
51
|
const route = {
|
|
50
52
|
id: ADAPTER,
|
|
51
53
|
generate: Effect.fn("OpenAIImages.generate")(function* (request, execute) {
|
|
54
|
+
const mask = request.options?.mask;
|
|
55
|
+
if (mask !== undefined && (request.images?.length ?? 0) === 0)
|
|
56
|
+
return yield* ImageInputs.invalid(ADAPTER, "An OpenAI image mask requires at least one input image");
|
|
52
57
|
const http = mergeHttpOptions(request.model.http, request.http);
|
|
53
|
-
const
|
|
58
|
+
const sourceImages = request.images ?? [];
|
|
59
|
+
const multipartImages = yield* Effect.forEach(sourceImages, (image) => {
|
|
60
|
+
if (image.type === "bytes")
|
|
61
|
+
return Effect.succeed({ data: image.data, mediaType: image.mediaType });
|
|
62
|
+
if (image.type === "url")
|
|
63
|
+
return ImageInputs.decodeDataUrl(image.url, ADAPTER);
|
|
64
|
+
return Effect.succeed(undefined);
|
|
65
|
+
});
|
|
66
|
+
const multipartMask = mask === undefined
|
|
67
|
+
? undefined
|
|
68
|
+
: mask.type === "bytes"
|
|
69
|
+
? { data: mask.data, mediaType: mask.mediaType }
|
|
70
|
+
: mask.type === "url"
|
|
71
|
+
? yield* ImageInputs.decodeDataUrl(mask.url, ADAPTER)
|
|
72
|
+
: undefined;
|
|
73
|
+
const useMultipart = sourceImages.length > 0 &&
|
|
74
|
+
multipartImages.every((image) => image !== undefined) &&
|
|
75
|
+
(mask === undefined || multipartMask !== undefined);
|
|
76
|
+
const path = sourceImages.length === 0 ? PATH : EDIT_PATH;
|
|
77
|
+
const url = applyQuery(`${(input.baseURL ?? DEFAULT_BASE_URL).replace(/\/$/, "")}${path}`, http?.query);
|
|
78
|
+
if (useMultipart) {
|
|
79
|
+
const form = new FormData();
|
|
80
|
+
form.append("model", request.model.id);
|
|
81
|
+
form.append("prompt", request.prompt);
|
|
82
|
+
Object.entries(mergeJsonRecords(nativeOptions(request.options), http?.body) ?? {}).forEach(([key, value]) => {
|
|
83
|
+
if (["model", "prompt", "image", "image[]", "images", "mask"].includes(key))
|
|
84
|
+
return;
|
|
85
|
+
form.append(key, typeof value === "string" ? value : ProviderShared.encodeJson(value));
|
|
86
|
+
});
|
|
87
|
+
multipartImages.forEach((image, index) => {
|
|
88
|
+
if (image === undefined)
|
|
89
|
+
return;
|
|
90
|
+
form.append("image[]", imageBlob(image.data, image.mediaType), `image-${index}`);
|
|
91
|
+
});
|
|
92
|
+
if (multipartMask !== undefined)
|
|
93
|
+
form.append("mask", imageBlob(multipartMask.data, multipartMask.mediaType), "mask");
|
|
94
|
+
const headers = yield* Auth.toEffect(input.auth)({
|
|
95
|
+
request,
|
|
96
|
+
method: "POST",
|
|
97
|
+
url,
|
|
98
|
+
body: "[multipart/form-data]",
|
|
99
|
+
headers: Headers.remove(Headers.fromInput({ ...input.headers, ...http?.headers }), "content-type"),
|
|
100
|
+
});
|
|
101
|
+
const response = yield* execute(HttpClientRequest.post(url).pipe(HttpClientRequest.setHeaders(headers), HttpClientRequest.bodyFormData(form)));
|
|
102
|
+
return yield* parseResponse(response, request.options, http?.body);
|
|
103
|
+
}
|
|
104
|
+
const references = sourceImages.map((image) => {
|
|
105
|
+
if (image.type === "bytes")
|
|
106
|
+
return { image_url: ImageInputs.dataUrl(image) };
|
|
107
|
+
if (image.type === "url")
|
|
108
|
+
return { image_url: image.url };
|
|
109
|
+
if (image.type === "file-id")
|
|
110
|
+
return { file_id: image.id };
|
|
111
|
+
return undefined;
|
|
112
|
+
});
|
|
113
|
+
if (references.some((image) => image === undefined))
|
|
114
|
+
return yield* ImageInputs.invalid(ADAPTER, "OpenAI Images accepts image URLs, data URLs, bytes, and file IDs");
|
|
115
|
+
const maskReference = mask === undefined
|
|
116
|
+
? undefined
|
|
117
|
+
: mask.type === "bytes"
|
|
118
|
+
? { image_url: ImageInputs.dataUrl(mask) }
|
|
119
|
+
: mask.type === "url"
|
|
120
|
+
? { image_url: mask.url }
|
|
121
|
+
: mask.type === "file-id"
|
|
122
|
+
? { file_id: mask.id }
|
|
123
|
+
: undefined;
|
|
124
|
+
if (mask !== undefined && maskReference === undefined)
|
|
125
|
+
return yield* ImageInputs.invalid(ADAPTER, "OpenAI Images accepts masks as URLs, data URLs, bytes, or file IDs");
|
|
126
|
+
const requestBody = mergeJsonRecords({
|
|
127
|
+
model: request.model.id,
|
|
128
|
+
prompt: request.prompt,
|
|
129
|
+
images: references.length === 0 ? undefined : references,
|
|
130
|
+
mask: maskReference,
|
|
131
|
+
}, nativeOptions(request.options), http?.body);
|
|
54
132
|
const text = ProviderShared.encodeJson(requestBody);
|
|
55
|
-
const url = applyQuery(`${(input.baseURL ?? DEFAULT_BASE_URL).replace(/\/$/, "")}${PATH}`, http?.query);
|
|
56
133
|
const headers = yield* Auth.toEffect(input.auth)({
|
|
57
134
|
request,
|
|
58
135
|
method: "POST",
|
|
@@ -61,42 +138,51 @@ export const model = (input) => {
|
|
|
61
138
|
headers: Headers.fromInput({ ...input.headers, ...http?.headers }),
|
|
62
139
|
});
|
|
63
140
|
const response = yield* execute(HttpClientRequest.post(url).pipe(HttpClientRequest.setHeaders(headers), HttpClientRequest.bodyText(text, "application/json")));
|
|
64
|
-
|
|
65
|
-
const decoded = yield* Schema.decodeUnknownEffect(OpenAIImageResponse)(payload).pipe(Effect.mapError(() => invalidOutput("OpenAI Images returned an invalid response")));
|
|
66
|
-
const format = decoded.output_format ?? (typeof requestBody.output_format === "string" ? requestBody.output_format : "png");
|
|
67
|
-
const images = yield* Effect.forEach(decoded.data, (item, index) => {
|
|
68
|
-
if (item.b64_json)
|
|
69
|
-
return Effect.fromResult(Encoding.decodeBase64(item.b64_json)).pipe(Effect.mapError(() => invalidOutput(`OpenAI Images result ${index} contains invalid base64 data`)), Effect.map((data) => new GeneratedImage({
|
|
70
|
-
mediaType: `image/${format}`,
|
|
71
|
-
data,
|
|
72
|
-
providerMetadata: item.revised_prompt === undefined ? undefined : { openai: { revisedPrompt: item.revised_prompt } },
|
|
73
|
-
})));
|
|
74
|
-
if (item.url)
|
|
75
|
-
return Effect.succeed(new GeneratedImage({
|
|
76
|
-
mediaType: `image/${format}`,
|
|
77
|
-
data: item.url,
|
|
78
|
-
providerMetadata: item.revised_prompt === undefined ? undefined : { openai: { revisedPrompt: item.revised_prompt } },
|
|
79
|
-
}));
|
|
80
|
-
return Effect.fail(invalidOutput(`OpenAI Images result ${index} has neither image data nor a URL`));
|
|
81
|
-
});
|
|
82
|
-
if (images.length === 0)
|
|
83
|
-
return yield* invalidOutput("OpenAI Images returned no images");
|
|
84
|
-
return new ImageResponse({
|
|
85
|
-
images,
|
|
86
|
-
usage: decoded.usage === undefined
|
|
87
|
-
? undefined
|
|
88
|
-
: new Usage({
|
|
89
|
-
inputTokens: decoded.usage.input_tokens,
|
|
90
|
-
outputTokens: decoded.usage.output_tokens,
|
|
91
|
-
totalTokens: decoded.usage.total_tokens,
|
|
92
|
-
providerMetadata: { openai: decoded.usage },
|
|
93
|
-
}),
|
|
94
|
-
providerMetadata: { openai: { outputFormat: format } },
|
|
95
|
-
});
|
|
141
|
+
return yield* parseResponse(response, request.options, http?.body);
|
|
96
142
|
}),
|
|
97
143
|
};
|
|
98
144
|
return ImageModel.make({ id: input.id, provider: "openai", route, http: input.http });
|
|
99
145
|
};
|
|
146
|
+
const parseResponse = Effect.fn("OpenAIImages.parseResponse")(function* (response, options, overlay) {
|
|
147
|
+
const payload = yield* response.json.pipe(Effect.mapError(() => invalidOutput("Failed to read the OpenAI Images response")));
|
|
148
|
+
const decoded = yield* Schema.decodeUnknownEffect(OpenAIImageResponse)(payload).pipe(Effect.mapError(() => invalidOutput("OpenAI Images returned an invalid response")));
|
|
149
|
+
const requestBody = mergeJsonRecords(nativeOptions(options), overlay);
|
|
150
|
+
const format = decoded.output_format ?? (typeof requestBody?.output_format === "string" ? requestBody.output_format : "png");
|
|
151
|
+
const images = yield* Effect.forEach(decoded.data, (item, index) => {
|
|
152
|
+
if (item.b64_json)
|
|
153
|
+
return Effect.fromResult(Encoding.decodeBase64(item.b64_json)).pipe(Effect.mapError(() => invalidOutput(`OpenAI Images result ${index} contains invalid base64 data`)), Effect.map((data) => new GeneratedImage({
|
|
154
|
+
mediaType: `image/${format}`,
|
|
155
|
+
data,
|
|
156
|
+
providerMetadata: item.revised_prompt === undefined ? undefined : { openai: { revisedPrompt: item.revised_prompt } },
|
|
157
|
+
})));
|
|
158
|
+
if (item.url)
|
|
159
|
+
return Effect.succeed(new GeneratedImage({
|
|
160
|
+
mediaType: `image/${format}`,
|
|
161
|
+
data: item.url,
|
|
162
|
+
providerMetadata: item.revised_prompt === undefined ? undefined : { openai: { revisedPrompt: item.revised_prompt } },
|
|
163
|
+
}));
|
|
164
|
+
return Effect.fail(invalidOutput(`OpenAI Images result ${index} has neither image data nor a URL`));
|
|
165
|
+
});
|
|
166
|
+
if (images.length === 0)
|
|
167
|
+
return yield* invalidOutput("OpenAI Images returned no images");
|
|
168
|
+
return new ImageResponse({
|
|
169
|
+
images,
|
|
170
|
+
usage: decoded.usage === undefined
|
|
171
|
+
? undefined
|
|
172
|
+
: new Usage({
|
|
173
|
+
inputTokens: decoded.usage.input_tokens,
|
|
174
|
+
outputTokens: decoded.usage.output_tokens,
|
|
175
|
+
totalTokens: decoded.usage.total_tokens,
|
|
176
|
+
providerMetadata: { openai: decoded.usage },
|
|
177
|
+
}),
|
|
178
|
+
providerMetadata: { openai: { outputFormat: format } },
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
const imageBlob = (data, mediaType) => {
|
|
182
|
+
const buffer = new ArrayBuffer(data.byteLength);
|
|
183
|
+
new Uint8Array(buffer).set(data);
|
|
184
|
+
return new Blob([buffer], { type: mediaType });
|
|
185
|
+
};
|
|
100
186
|
export const OpenAIImages = {
|
|
101
187
|
model,
|
|
102
188
|
};
|
|
@@ -165,13 +165,13 @@ export declare const protocol: Protocol<{
|
|
|
165
165
|
} | {
|
|
166
166
|
readonly type: "image_generation";
|
|
167
167
|
readonly output_format?: "png" | "jpeg" | "webp" | undefined;
|
|
168
|
+
readonly size?: string | undefined;
|
|
169
|
+
readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
|
|
170
|
+
readonly background?: "auto" | "opaque" | "transparent" | undefined;
|
|
168
171
|
readonly output_compression?: number | undefined;
|
|
169
172
|
readonly action?: "generate" | "auto" | "edit" | undefined;
|
|
170
|
-
readonly background?: "auto" | "opaque" | "transparent" | undefined;
|
|
171
173
|
readonly input_fidelity?: "low" | "high" | undefined;
|
|
172
174
|
readonly partial_images?: number | undefined;
|
|
173
|
-
readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
|
|
174
|
-
readonly size?: string | undefined;
|
|
175
175
|
})[] | undefined;
|
|
176
176
|
readonly temperature?: number | undefined;
|
|
177
177
|
readonly tool_choice?: "required" | "none" | "auto" | Schema.Struct.ReadonlySide<{
|
|
@@ -317,13 +317,13 @@ export declare const httpTransport: HttpTransport.HttpJsonTransport<{
|
|
|
317
317
|
} | {
|
|
318
318
|
readonly type: "image_generation";
|
|
319
319
|
readonly output_format?: "png" | "jpeg" | "webp" | undefined;
|
|
320
|
+
readonly size?: string | undefined;
|
|
321
|
+
readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
|
|
322
|
+
readonly background?: "auto" | "opaque" | "transparent" | undefined;
|
|
320
323
|
readonly output_compression?: number | undefined;
|
|
321
324
|
readonly action?: "generate" | "auto" | "edit" | undefined;
|
|
322
|
-
readonly background?: "auto" | "opaque" | "transparent" | undefined;
|
|
323
325
|
readonly input_fidelity?: "low" | "high" | undefined;
|
|
324
326
|
readonly partial_images?: number | undefined;
|
|
325
|
-
readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
|
|
326
|
-
readonly size?: string | undefined;
|
|
327
327
|
})[] | undefined;
|
|
328
328
|
readonly temperature?: number | undefined;
|
|
329
329
|
readonly tool_choice?: "required" | "none" | "auto" | Schema.Struct.ReadonlySide<{
|
|
@@ -406,13 +406,13 @@ export declare const route: Route<{
|
|
|
406
406
|
} | {
|
|
407
407
|
readonly type: "image_generation";
|
|
408
408
|
readonly output_format?: "png" | "jpeg" | "webp" | undefined;
|
|
409
|
+
readonly size?: string | undefined;
|
|
410
|
+
readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
|
|
411
|
+
readonly background?: "auto" | "opaque" | "transparent" | undefined;
|
|
409
412
|
readonly output_compression?: number | undefined;
|
|
410
413
|
readonly action?: "generate" | "auto" | "edit" | undefined;
|
|
411
|
-
readonly background?: "auto" | "opaque" | "transparent" | undefined;
|
|
412
414
|
readonly input_fidelity?: "low" | "high" | undefined;
|
|
413
415
|
readonly partial_images?: number | undefined;
|
|
414
|
-
readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
|
|
415
|
-
readonly size?: string | undefined;
|
|
416
416
|
})[] | undefined;
|
|
417
417
|
readonly temperature?: number | undefined;
|
|
418
418
|
readonly tool_choice?: "required" | "none" | "auto" | Schema.Struct.ReadonlySide<{
|
|
@@ -495,13 +495,13 @@ export declare const webSocketTransport: import("../route/transport/websocket").
|
|
|
495
495
|
} | {
|
|
496
496
|
readonly type: "image_generation";
|
|
497
497
|
readonly output_format?: "png" | "jpeg" | "webp" | undefined;
|
|
498
|
+
readonly size?: string | undefined;
|
|
499
|
+
readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
|
|
500
|
+
readonly background?: "auto" | "opaque" | "transparent" | undefined;
|
|
498
501
|
readonly output_compression?: number | undefined;
|
|
499
502
|
readonly action?: "generate" | "auto" | "edit" | undefined;
|
|
500
|
-
readonly background?: "auto" | "opaque" | "transparent" | undefined;
|
|
501
503
|
readonly input_fidelity?: "low" | "high" | undefined;
|
|
502
504
|
readonly partial_images?: number | undefined;
|
|
503
|
-
readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
|
|
504
|
-
readonly size?: string | undefined;
|
|
505
505
|
})[] | undefined;
|
|
506
506
|
readonly temperature?: number | undefined;
|
|
507
507
|
readonly tool_choice?: "required" | "none" | "auto" | Schema.Struct.ReadonlySide<{
|
|
@@ -584,13 +584,13 @@ export declare const webSocketTransport: import("../route/transport/websocket").
|
|
|
584
584
|
} | {
|
|
585
585
|
readonly type: "image_generation";
|
|
586
586
|
readonly output_format?: "png" | "jpeg" | "webp" | undefined;
|
|
587
|
+
readonly size?: string | undefined;
|
|
588
|
+
readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
|
|
589
|
+
readonly background?: "auto" | "opaque" | "transparent" | undefined;
|
|
587
590
|
readonly output_compression?: number | undefined;
|
|
588
591
|
readonly action?: "generate" | "auto" | "edit" | undefined;
|
|
589
|
-
readonly background?: "auto" | "opaque" | "transparent" | undefined;
|
|
590
592
|
readonly input_fidelity?: "low" | "high" | undefined;
|
|
591
593
|
readonly partial_images?: number | undefined;
|
|
592
|
-
readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
|
|
593
|
-
readonly size?: string | undefined;
|
|
594
594
|
})[] | undefined;
|
|
595
595
|
readonly temperature?: number | undefined;
|
|
596
596
|
readonly tool_choice?: "required" | "none" | "auto" | Schema.Struct.ReadonlySide<{
|
|
@@ -673,13 +673,13 @@ export declare const webSocketRoute: Route<{
|
|
|
673
673
|
} | {
|
|
674
674
|
readonly type: "image_generation";
|
|
675
675
|
readonly output_format?: "png" | "jpeg" | "webp" | undefined;
|
|
676
|
+
readonly size?: string | undefined;
|
|
677
|
+
readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
|
|
678
|
+
readonly background?: "auto" | "opaque" | "transparent" | undefined;
|
|
676
679
|
readonly output_compression?: number | undefined;
|
|
677
680
|
readonly action?: "generate" | "auto" | "edit" | undefined;
|
|
678
|
-
readonly background?: "auto" | "opaque" | "transparent" | undefined;
|
|
679
681
|
readonly input_fidelity?: "low" | "high" | undefined;
|
|
680
682
|
readonly partial_images?: number | undefined;
|
|
681
|
-
readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
|
|
682
|
-
readonly size?: string | undefined;
|
|
683
683
|
})[] | undefined;
|
|
684
684
|
readonly temperature?: number | undefined;
|
|
685
685
|
readonly tool_choice?: "required" | "none" | "auto" | Schema.Struct.ReadonlySide<{
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import type { ImageInput } from "../../image";
|
|
3
|
+
import { LLMError } from "../../schema";
|
|
4
|
+
export declare const dataUrl: (input: Extract<ImageInput, {
|
|
5
|
+
readonly type: "bytes";
|
|
6
|
+
}>) => string;
|
|
7
|
+
export declare const decodeDataUrl: (url: string, module: string) => Effect.Effect<{
|
|
8
|
+
readonly mediaType: string;
|
|
9
|
+
readonly data: Uint8Array;
|
|
10
|
+
} | undefined, LLMError>;
|
|
11
|
+
export declare const invalidImageInput: (module: string, message: string) => LLMError;
|
|
12
|
+
export declare const ImageInputs: {
|
|
13
|
+
readonly dataUrl: (input: Extract<ImageInput, {
|
|
14
|
+
readonly type: "bytes";
|
|
15
|
+
}>) => string;
|
|
16
|
+
readonly decodeDataUrl: (url: string, module: string) => Effect.Effect<{
|
|
17
|
+
readonly mediaType: string;
|
|
18
|
+
readonly data: Uint8Array;
|
|
19
|
+
} | undefined, LLMError>;
|
|
20
|
+
readonly invalid: (module: string, message: string) => LLMError;
|
|
21
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Effect, Encoding } from "effect";
|
|
2
|
+
import { InvalidRequestReason, LLMError } from "../../schema";
|
|
3
|
+
const invalid = (module, message) => new LLMError({
|
|
4
|
+
module,
|
|
5
|
+
method: "generate",
|
|
6
|
+
reason: new InvalidRequestReason({ message }),
|
|
7
|
+
});
|
|
8
|
+
export const dataUrl = (input) => `data:${input.mediaType};base64,${Encoding.encodeBase64(input.data)}`;
|
|
9
|
+
export const decodeDataUrl = (url, module) => {
|
|
10
|
+
if (!url.startsWith("data:"))
|
|
11
|
+
return Effect.succeed(undefined);
|
|
12
|
+
const match = /^data:([^;,]+);base64,(.*)$/s.exec(url);
|
|
13
|
+
if (!match)
|
|
14
|
+
return Effect.fail(invalid(module, "Image data URLs must contain a MIME type and base64 data"));
|
|
15
|
+
return Effect.fromResult(Encoding.decodeBase64(match[2])).pipe(Effect.mapError(() => invalid(module, "Image data URL contains invalid base64 data")), Effect.map((data) => ({ mediaType: match[1], data })));
|
|
16
|
+
};
|
|
17
|
+
export const invalidImageInput = invalid;
|
|
18
|
+
export const ImageInputs = {
|
|
19
|
+
dataUrl,
|
|
20
|
+
decodeDataUrl,
|
|
21
|
+
invalid: invalidImageInput,
|
|
22
|
+
};
|
|
@@ -3,6 +3,7 @@ import { type Definition as AuthDefinition } from "../route/auth";
|
|
|
3
3
|
import { type HttpOptions } from "../schema";
|
|
4
4
|
export declare const DEFAULT_BASE_URL = "https://api.x.ai/v1";
|
|
5
5
|
export declare const PATH = "/images/generations";
|
|
6
|
+
export declare const EDIT_PATH = "/images/edits";
|
|
6
7
|
export type XAIImageString<Known extends string> = Known | (string & {});
|
|
7
8
|
export type XAIImageOptions = {
|
|
8
9
|
readonly n?: number;
|
|
@@ -4,9 +4,11 @@ import { GeneratedImage, ImageModel, ImageResponse } from "../image";
|
|
|
4
4
|
import { Auth } from "../route/auth";
|
|
5
5
|
import { InvalidProviderOutputReason, LLMError, Usage, mergeHttpOptions, mergeJsonRecords, } from "../schema";
|
|
6
6
|
import { ProviderShared, optionalNull } from "./shared";
|
|
7
|
+
import { ImageInputs } from "./utils/image-input";
|
|
7
8
|
const ADAPTER = "xai-images";
|
|
8
9
|
export const DEFAULT_BASE_URL = "https://api.x.ai/v1";
|
|
9
10
|
export const PATH = "/images/generations";
|
|
11
|
+
export const EDIT_PATH = "/images/edits";
|
|
10
12
|
const XAIImageResponse = Schema.Struct({
|
|
11
13
|
data: Schema.Array(Schema.Struct({
|
|
12
14
|
b64_json: optionalNull(Schema.String),
|
|
@@ -43,9 +45,25 @@ export const model = (input) => {
|
|
|
43
45
|
id: ADAPTER,
|
|
44
46
|
generate: Effect.fn("XAIImages.generate")(function* (request, execute) {
|
|
45
47
|
const http = mergeHttpOptions(request.model.http, request.http);
|
|
46
|
-
const
|
|
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);
|
|
47
65
|
const text = ProviderShared.encodeJson(requestBody);
|
|
48
|
-
const url = applyQuery(`${(input.baseURL ?? DEFAULT_BASE_URL).replace(/\/$/, "")}${PATH}`, http?.query);
|
|
66
|
+
const url = applyQuery(`${(input.baseURL ?? DEFAULT_BASE_URL).replace(/\/$/, "")}${imageReferences.length === 0 ? PATH : EDIT_PATH}`, http?.query);
|
|
49
67
|
const headers = yield* Auth.toEffect(input.auth)({
|
|
50
68
|
request,
|
|
51
69
|
method: "POST",
|
|
@@ -4,6 +4,7 @@ import { GeneratedImage, ImageModel, ImageResponse } from "../image";
|
|
|
4
4
|
import { Auth } from "../route/auth";
|
|
5
5
|
import { InvalidProviderOutputReason, LLMError, mergeHttpOptions, mergeJsonRecords } from "../schema";
|
|
6
6
|
import { ProviderShared } from "./shared";
|
|
7
|
+
import { ImageInputs } from "./utils/image-input";
|
|
7
8
|
const ADAPTER = "zai-images";
|
|
8
9
|
export const DEFAULT_BASE_URL = "https://api.z.ai/api/paas/v4";
|
|
9
10
|
export const PATH = "/images/generations";
|
|
@@ -42,6 +43,8 @@ export const model = (input) => {
|
|
|
42
43
|
const route = {
|
|
43
44
|
id: ADAPTER,
|
|
44
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");
|
|
45
48
|
const http = mergeHttpOptions(request.model.http, request.http);
|
|
46
49
|
const requestBody = mergeJsonRecords({ model: request.model.id, prompt: request.prompt }, nativeOptions(request.options), http?.body);
|
|
47
50
|
const text = ProviderShared.encodeJson(requestBody);
|
|
@@ -150,13 +150,13 @@ export declare const routes: (RouteDef<{
|
|
|
150
150
|
} | {
|
|
151
151
|
readonly type: "image_generation";
|
|
152
152
|
readonly output_format?: "png" | "jpeg" | "webp" | undefined;
|
|
153
|
+
readonly size?: string | undefined;
|
|
154
|
+
readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
|
|
155
|
+
readonly background?: "auto" | "opaque" | "transparent" | undefined;
|
|
153
156
|
readonly output_compression?: number | undefined;
|
|
154
157
|
readonly action?: "generate" | "auto" | "edit" | undefined;
|
|
155
|
-
readonly background?: "auto" | "opaque" | "transparent" | undefined;
|
|
156
158
|
readonly input_fidelity?: "low" | "high" | undefined;
|
|
157
159
|
readonly partial_images?: number | undefined;
|
|
158
|
-
readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
|
|
159
|
-
readonly size?: string | undefined;
|
|
160
160
|
})[] | undefined;
|
|
161
161
|
readonly temperature?: number | undefined;
|
|
162
162
|
readonly tool_choice?: "required" | "none" | "auto" | import("effect/Schema").Struct.ReadonlySide<{
|
|
@@ -138,13 +138,13 @@ export declare const routes: (import("../route").Route<{
|
|
|
138
138
|
} | {
|
|
139
139
|
readonly type: "image_generation";
|
|
140
140
|
readonly output_format?: "png" | "jpeg" | "webp" | undefined;
|
|
141
|
+
readonly size?: string | undefined;
|
|
142
|
+
readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
|
|
143
|
+
readonly background?: "auto" | "opaque" | "transparent" | undefined;
|
|
141
144
|
readonly output_compression?: number | undefined;
|
|
142
145
|
readonly action?: "generate" | "auto" | "edit" | undefined;
|
|
143
|
-
readonly background?: "auto" | "opaque" | "transparent" | undefined;
|
|
144
146
|
readonly input_fidelity?: "low" | "high" | undefined;
|
|
145
147
|
readonly partial_images?: number | undefined;
|
|
146
|
-
readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
|
|
147
|
-
readonly size?: string | undefined;
|
|
148
148
|
})[] | undefined;
|
|
149
149
|
readonly temperature?: number | undefined;
|
|
150
150
|
readonly tool_choice?: "required" | "none" | "auto" | import("effect/Schema").Struct.ReadonlySide<{
|
|
@@ -82,13 +82,13 @@ export declare const routes: import("../route").Route<{
|
|
|
82
82
|
} | {
|
|
83
83
|
readonly type: "image_generation";
|
|
84
84
|
readonly output_format?: "png" | "jpeg" | "webp" | undefined;
|
|
85
|
+
readonly size?: string | undefined;
|
|
86
|
+
readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
|
|
87
|
+
readonly background?: "auto" | "opaque" | "transparent" | undefined;
|
|
85
88
|
readonly output_compression?: number | undefined;
|
|
86
89
|
readonly action?: "generate" | "auto" | "edit" | undefined;
|
|
87
|
-
readonly background?: "auto" | "opaque" | "transparent" | undefined;
|
|
88
90
|
readonly input_fidelity?: "low" | "high" | undefined;
|
|
89
91
|
readonly partial_images?: number | undefined;
|
|
90
|
-
readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
|
|
91
|
-
readonly size?: string | undefined;
|
|
92
92
|
})[] | undefined;
|
|
93
93
|
readonly temperature?: number | undefined;
|
|
94
94
|
readonly tool_choice?: "required" | "none" | "auto" | import("effect/Schema").Struct.ReadonlySide<{
|
|
@@ -80,13 +80,13 @@ export declare const routes: import("../route").Route<{
|
|
|
80
80
|
} | {
|
|
81
81
|
readonly type: "image_generation";
|
|
82
82
|
readonly output_format?: "png" | "jpeg" | "webp" | undefined;
|
|
83
|
+
readonly size?: string | undefined;
|
|
84
|
+
readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
|
|
85
|
+
readonly background?: "auto" | "opaque" | "transparent" | undefined;
|
|
83
86
|
readonly output_compression?: number | undefined;
|
|
84
87
|
readonly action?: "generate" | "auto" | "edit" | undefined;
|
|
85
|
-
readonly background?: "auto" | "opaque" | "transparent" | undefined;
|
|
86
88
|
readonly input_fidelity?: "low" | "high" | undefined;
|
|
87
89
|
readonly partial_images?: number | undefined;
|
|
88
|
-
readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
|
|
89
|
-
readonly size?: string | undefined;
|
|
90
90
|
})[] | undefined;
|
|
91
91
|
readonly temperature?: number | undefined;
|
|
92
92
|
readonly tool_choice?: "required" | "none" | "auto" | import("effect/Schema").Struct.ReadonlySide<{
|
|
@@ -136,13 +136,13 @@ export declare const routes: (Route<{
|
|
|
136
136
|
} | {
|
|
137
137
|
readonly type: "image_generation";
|
|
138
138
|
readonly output_format?: "png" | "jpeg" | "webp" | undefined;
|
|
139
|
+
readonly size?: string | undefined;
|
|
140
|
+
readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
|
|
141
|
+
readonly background?: "auto" | "opaque" | "transparent" | undefined;
|
|
139
142
|
readonly output_compression?: number | undefined;
|
|
140
143
|
readonly action?: "generate" | "auto" | "edit" | undefined;
|
|
141
|
-
readonly background?: "auto" | "opaque" | "transparent" | undefined;
|
|
142
144
|
readonly input_fidelity?: "low" | "high" | undefined;
|
|
143
145
|
readonly partial_images?: number | undefined;
|
|
144
|
-
readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
|
|
145
|
-
readonly size?: string | undefined;
|
|
146
146
|
})[] | undefined;
|
|
147
147
|
readonly temperature?: number | undefined;
|
|
148
148
|
readonly tool_choice?: "required" | "none" | "auto" | import("effect/Schema").Struct.ReadonlySide<{
|
|
@@ -224,13 +224,13 @@ export declare const routes: (Route<{
|
|
|
224
224
|
} | {
|
|
225
225
|
readonly type: "image_generation";
|
|
226
226
|
readonly output_format?: "png" | "jpeg" | "webp" | undefined;
|
|
227
|
+
readonly size?: string | undefined;
|
|
228
|
+
readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
|
|
229
|
+
readonly background?: "auto" | "opaque" | "transparent" | undefined;
|
|
227
230
|
readonly output_compression?: number | undefined;
|
|
228
231
|
readonly action?: "generate" | "auto" | "edit" | undefined;
|
|
229
|
-
readonly background?: "auto" | "opaque" | "transparent" | undefined;
|
|
230
232
|
readonly input_fidelity?: "low" | "high" | undefined;
|
|
231
233
|
readonly partial_images?: number | undefined;
|
|
232
|
-
readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
|
|
233
|
-
readonly size?: string | undefined;
|
|
234
234
|
})[] | undefined;
|
|
235
235
|
readonly temperature?: number | undefined;
|
|
236
236
|
readonly tool_choice?: "required" | "none" | "auto" | import("effect/Schema").Struct.ReadonlySide<{
|
package/dist/providers/xai.d.ts
CHANGED
|
@@ -135,13 +135,13 @@ export declare const routes: (import("../route").Route<{
|
|
|
135
135
|
} | {
|
|
136
136
|
readonly type: "image_generation";
|
|
137
137
|
readonly output_format?: "png" | "jpeg" | "webp" | undefined;
|
|
138
|
+
readonly size?: string | undefined;
|
|
139
|
+
readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
|
|
140
|
+
readonly background?: "auto" | "opaque" | "transparent" | undefined;
|
|
138
141
|
readonly output_compression?: number | undefined;
|
|
139
142
|
readonly action?: "generate" | "auto" | "edit" | undefined;
|
|
140
|
-
readonly background?: "auto" | "opaque" | "transparent" | undefined;
|
|
141
143
|
readonly input_fidelity?: "low" | "high" | undefined;
|
|
142
144
|
readonly partial_images?: number | undefined;
|
|
143
|
-
readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
|
|
144
|
-
readonly size?: string | undefined;
|
|
145
145
|
})[] | undefined;
|
|
146
146
|
readonly temperature?: number | undefined;
|
|
147
147
|
readonly tool_choice?: "required" | "none" | "auto" | import("effect/Schema").Struct.ReadonlySide<{
|
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-15911",
|
|
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-15911",
|
|
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-15911",
|
|
39
39
|
"aws4fetch": "1.0.20",
|
|
40
40
|
"effect": "4.0.0-beta.98",
|
|
41
41
|
"google-auth-library": "10.5.0"
|