🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

@opencode-ai/ai

Package Overview
Dependencies
Maintainers
2
Versions
129
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@opencode-ai/ai - npm Package Compare versions

Comparing version
0.0.0-next-15898
to
0.0.0-next-15910
+32
dist/protocols/google-images.d.ts
import { ImageModel } from "../image";
import { type Definition as AuthDefinition } from "../route/auth";
import { type HttpOptions } from "../schema";
export declare const DEFAULT_BASE_URL = "https://generativelanguage.googleapis.com/v1beta";
export type GoogleImageString<Known extends string> = Known | (string & {});
export type GoogleImageOptions = {
readonly aspectRatio?: GoogleImageString<"1:1" | "2:3" | "3:2" | "3:4" | "4:3" | "4:5" | "5:4" | "9:16" | "16:9" | "21:9">;
readonly imageSize?: GoogleImageString<"1K" | "2K" | "4K">;
readonly seed?: number;
readonly thinkingLevel?: GoogleImageString<"MINIMAL" | "LOW" | "MEDIUM" | "HIGH">;
readonly includeThoughts?: boolean;
} & Record<string, unknown>;
export type GoogleImageBody = Record<string, unknown> & {
readonly contents: ReadonlyArray<{
readonly role: "user";
readonly parts: ReadonlyArray<{
readonly text: string;
}>;
}>;
readonly generationConfig: Record<string, unknown>;
};
export interface ModelInput {
readonly id: string;
readonly auth: AuthDefinition;
readonly baseURL?: string;
readonly headers?: Record<string, string>;
readonly http?: HttpOptions;
}
export declare const model: (input: ModelInput) => ImageModel<GoogleImageOptions>;
export declare const GoogleImages: {
readonly model: (input: ModelInput) => ImageModel<GoogleImageOptions>;
};
import { Effect, Encoding, Schema } from "effect";
import { Headers, HttpClientRequest } from "effect/unstable/http";
import { GeneratedImage, ImageModel, ImageResponse } from "../image";
import { Auth } from "../route/auth";
import { InvalidProviderOutputReason, LLMError, Usage, mergeHttpOptions, mergeJsonRecords, } from "../schema";
import { ProviderShared } from "./shared";
const ADAPTER = "google-images";
export const DEFAULT_BASE_URL = "https://generativelanguage.googleapis.com/v1beta";
const GoogleUsage = Schema.StructWithRest(Schema.Struct({
cachedContentTokenCount: Schema.optional(Schema.Number),
thoughtsTokenCount: Schema.optional(Schema.Number),
promptTokenCount: Schema.optional(Schema.Number),
candidatesTokenCount: Schema.optional(Schema.Number),
totalTokenCount: Schema.optional(Schema.Number),
promptTokensDetails: Schema.optional(Schema.Unknown),
candidatesTokensDetails: Schema.optional(Schema.Unknown),
}), [Schema.Record(Schema.String, Schema.Unknown)]);
const GoogleImageResponse = Schema.Struct({
candidates: Schema.optional(Schema.Array(Schema.Struct({
index: Schema.optional(Schema.Number),
content: Schema.optional(Schema.Struct({
parts: Schema.Array(Schema.Struct({
text: Schema.optional(Schema.String),
thought: Schema.optional(Schema.Boolean),
thoughtSignature: Schema.optional(Schema.String),
inlineData: Schema.optional(Schema.Struct({
mimeType: Schema.String,
data: Schema.String,
})),
})),
})),
finishReason: Schema.optional(Schema.String),
finishMessage: Schema.optional(Schema.String),
safetyRatings: Schema.optional(Schema.Unknown),
citationMetadata: Schema.optional(Schema.Unknown),
groundingMetadata: Schema.optional(Schema.Unknown),
}))),
usageMetadata: Schema.optional(GoogleUsage),
modelVersion: Schema.optional(Schema.String),
responseId: Schema.optional(Schema.String),
promptFeedback: Schema.optional(Schema.Unknown),
});
const nativeOptions = (options) => {
const { aspectRatio, imageSize, seed, thinkingLevel, includeThoughts, ...native } = options ?? {};
const image = {
aspectRatio,
imageSize,
};
const thinkingConfig = {
thinkingLevel,
includeThoughts,
};
return (mergeJsonRecords({
responseModalities: ["IMAGE"],
imageConfig: Object.values(image).some((value) => value !== undefined) ? image : undefined,
seed,
thinkingConfig: Object.values(thinkingConfig).some((value) => value !== undefined) ? thinkingConfig : undefined,
}, native) ?? { responseModalities: ["IMAGE"] });
};
const body = (request, overlay) => mergeJsonRecords({
contents: [{ role: "user", parts: [{ text: request.prompt }] }],
generationConfig: nativeOptions(request.options),
}, overlay);
const invalidOutput = (message, providerMetadata) => new LLMError({
module: ADAPTER,
method: "generate",
reason: new InvalidProviderOutputReason({ message, route: ADAPTER, providerMetadata }),
});
const applyQuery = (url, query) => {
if (!query)
return url;
const next = new URL(url);
Object.entries(query).forEach(([key, value]) => next.searchParams.set(key, value));
return next.toString();
};
export const model = (input) => {
const route = {
id: ADAPTER,
generate: Effect.fn("GoogleImages.generate")(function* (request, execute) {
const http = mergeHttpOptions(request.model.http, request.http);
const text = ProviderShared.encodeJson(body(request, http?.body));
const url = applyQuery(`${(input.baseURL ?? DEFAULT_BASE_URL).replace(/\/$/, "")}/models/${request.model.id}:generateContent`, http?.query);
const headers = yield* Auth.toEffect(input.auth)({
request,
method: "POST",
url,
body: text,
headers: Headers.fromInput({ ...input.headers, ...http?.headers }),
});
const response = yield* execute(HttpClientRequest.post(url).pipe(HttpClientRequest.setHeaders(headers), HttpClientRequest.bodyText(text, "application/json")));
const payload = yield* response.json.pipe(Effect.mapError(() => invalidOutput("Failed to read the Google Images response")));
const decoded = yield* Schema.decodeUnknownEffect(GoogleImageResponse)(payload).pipe(Effect.mapError(() => invalidOutput("Google Images returned an invalid response")));
const candidates = decoded.candidates ?? [];
const candidateMetadata = candidates.map((candidate, candidateIndex) => ({
index: candidate.index ?? candidateIndex,
finishReason: candidate.finishReason,
finishMessage: candidate.finishMessage,
safetyRatings: candidate.safetyRatings,
citationMetadata: candidate.citationMetadata,
groundingMetadata: candidate.groundingMetadata,
parts: (candidate.content?.parts ?? []).map((part) => part.inlineData === undefined
? {
type: "text",
text: part.text,
thought: part.thought,
thoughtSignature: part.thoughtSignature,
}
: {
type: "inlineData",
mediaType: part.inlineData.mimeType,
thought: part.thought,
thoughtSignature: part.thoughtSignature,
}),
}));
const encoded = candidates.flatMap((candidate, candidateIndex) => (candidate.content?.parts ?? []).flatMap((part, partIndex) => part.inlineData === undefined || part.thought === true
? []
: [{ candidate, candidateIndex, partIndex, inlineData: part.inlineData }]));
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({
mediaType: item.inlineData.mimeType,
data,
providerMetadata: {
google: {
candidateIndex: item.candidate.index ?? item.candidateIndex,
partIndex: item.partIndex,
finishReason: item.candidate.finishReason,
safetyRatings: item.candidate.safetyRatings,
citationMetadata: item.candidate.citationMetadata,
groundingMetadata: item.candidate.groundingMetadata,
thoughtSignature: item.candidate.content?.parts[item.partIndex]?.thoughtSignature,
},
},
}))));
if (images.length === 0) {
const finishReasons = candidates.flatMap((candidate) => candidate.finishReason === undefined ? [] : [candidate.finishReason]);
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`, {
google: {
promptFeedback: decoded.promptFeedback,
candidates: candidateMetadata,
},
});
}
const usage = decoded.usageMetadata;
const outputTokens = usage?.candidatesTokenCount === undefined
? undefined
: usage.candidatesTokenCount + (usage.thoughtsTokenCount ?? 0);
return new ImageResponse({
images,
usage: usage === undefined
? undefined
: new Usage({
inputTokens: usage.promptTokenCount,
outputTokens,
nonCachedInputTokens: ProviderShared.subtractTokens(usage.promptTokenCount, usage.cachedContentTokenCount),
cacheReadInputTokens: usage.cachedContentTokenCount,
reasoningTokens: usage.thoughtsTokenCount,
totalTokens: ProviderShared.totalTokens(usage.promptTokenCount, outputTokens, usage.totalTokenCount),
providerMetadata: { google: usage },
}),
providerMetadata: {
google: {
modelVersion: decoded.modelVersion,
responseId: decoded.responseId,
promptFeedback: decoded.promptFeedback,
candidates: candidateMetadata,
},
},
});
}),
};
return ImageModel.make({ id: input.id, provider: "google", route, http: input.http });
};
export const GoogleImages = {
model,
};
import { ImageModel } from "../image";
import { type Definition as AuthDefinition } from "../route/auth";
import { type HttpOptions } from "../schema";
export declare const DEFAULT_BASE_URL = "https://api.x.ai/v1";
export declare const PATH = "/images/generations";
export type XAIImageString<Known extends string> = Known | (string & {});
export type XAIImageOptions = {
readonly n?: number;
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">;
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">;
readonly resolution?: XAIImageString<"1k" | "2k">;
readonly responseFormat?: XAIImageString<"url" | "b64_json">;
readonly response_format?: XAIImageString<"url" | "b64_json">;
} & Record<string, unknown>;
export interface ModelInput {
readonly id: string;
readonly auth: AuthDefinition;
readonly baseURL?: string;
readonly headers?: Record<string, string>;
readonly http?: HttpOptions;
}
export declare const model: (input: ModelInput) => ImageModel<XAIImageOptions>;
export declare const XAIImages: {
readonly model: (input: ModelInput) => ImageModel<XAIImageOptions>;
};
import { Effect, Encoding, Schema } from "effect";
import { Headers, HttpClientRequest } from "effect/unstable/http";
import { GeneratedImage, ImageModel, ImageResponse } from "../image";
import { Auth } from "../route/auth";
import { InvalidProviderOutputReason, LLMError, Usage, mergeHttpOptions, mergeJsonRecords, } from "../schema";
import { ProviderShared, optionalNull } from "./shared";
const ADAPTER = "xai-images";
export const DEFAULT_BASE_URL = "https://api.x.ai/v1";
export const PATH = "/images/generations";
const XAIImageResponse = Schema.Struct({
data: Schema.Array(Schema.Struct({
b64_json: optionalNull(Schema.String),
url: optionalNull(Schema.String),
revised_prompt: optionalNull(Schema.String),
mime_type: optionalNull(Schema.String),
})),
usage: Schema.optional(Schema.Unknown),
});
const nativeOptions = (options) => {
if (!options)
return undefined;
const { aspectRatio, responseFormat, ...native } = options;
return {
aspect_ratio: aspectRatio,
response_format: responseFormat,
...native,
};
};
const invalidOutput = (message) => new LLMError({
module: ADAPTER,
method: "generate",
reason: new InvalidProviderOutputReason({ message, route: ADAPTER }),
});
const applyQuery = (url, query) => {
if (!query)
return url;
const next = new URL(url);
Object.entries(query).forEach(([key, value]) => next.searchParams.set(key, value));
return next.toString();
};
export const model = (input) => {
const route = {
id: ADAPTER,
generate: Effect.fn("XAIImages.generate")(function* (request, execute) {
const http = mergeHttpOptions(request.model.http, request.http);
const requestBody = mergeJsonRecords({ model: request.model.id, prompt: request.prompt }, nativeOptions(request.options), http?.body);
const text = ProviderShared.encodeJson(requestBody);
const url = applyQuery(`${(input.baseURL ?? DEFAULT_BASE_URL).replace(/\/$/, "")}${PATH}`, http?.query);
const headers = yield* Auth.toEffect(input.auth)({
request,
method: "POST",
url,
body: text,
headers: Headers.fromInput({ ...input.headers, ...http?.headers }),
});
const response = yield* execute(HttpClientRequest.post(url).pipe(HttpClientRequest.setHeaders(headers), HttpClientRequest.bodyText(text, "application/json")));
const payload = yield* response.json.pipe(Effect.mapError(() => invalidOutput("Failed to read the xAI Images response")));
const decoded = yield* Schema.decodeUnknownEffect(XAIImageResponse)(payload).pipe(Effect.mapError(() => invalidOutput("xAI Images returned an invalid response")));
const images = yield* Effect.forEach(decoded.data, (item, index) => {
const mediaType = item.mime_type ?? "application/octet-stream";
if (item.b64_json)
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({
mediaType,
data,
providerMetadata: item.revised_prompt === undefined || item.revised_prompt === null
? undefined
: { xai: { revisedPrompt: item.revised_prompt } },
})));
if (item.url)
return Effect.succeed(new GeneratedImage({
mediaType,
data: item.url,
providerMetadata: item.revised_prompt === undefined || item.revised_prompt === null
? undefined
: { xai: { revisedPrompt: item.revised_prompt } },
}));
return Effect.fail(invalidOutput(`xAI Images result ${index} has neither image data nor a URL`));
});
if (images.length === 0)
return yield* invalidOutput("xAI Images returned no images");
const usage = ProviderShared.isRecord(decoded.usage) ? decoded.usage : undefined;
return new ImageResponse({
images,
usage: usage === undefined ? undefined : new Usage({ providerMetadata: { xai: usage } }),
providerMetadata: usage === undefined ? undefined : { xai: { usage } },
});
}),
};
return ImageModel.make({ id: input.id, provider: "xai", route, http: input.http });
};
export const XAIImages = {
model,
};
import { ImageModel } from "../image";
import { type Definition as AuthDefinition } from "../route/auth";
import { type HttpOptions } from "../schema";
export declare const DEFAULT_BASE_URL = "https://api.z.ai/api/paas/v4";
export declare const PATH = "/images/generations";
export type ZAIImageString<Known extends string> = Known | (string & {});
export type ZAIImageOptions = {
readonly size?: ZAIImageString<"1024x1024" | "768x1344" | "864x1152" | "1344x768" | "1152x864" | "1440x720" | "720x1440">;
readonly quality?: ZAIImageString<"hd" | "standard">;
readonly userID?: string;
} & Record<string, unknown>;
export interface ModelInput {
readonly id: string;
readonly auth: AuthDefinition;
readonly baseURL?: string;
readonly headers?: Record<string, string>;
readonly http?: HttpOptions;
}
export declare const model: (input: ModelInput) => ImageModel<ZAIImageOptions>;
export declare const ZAIImages: {
readonly model: (input: ModelInput) => ImageModel<ZAIImageOptions>;
};
import { Effect, Schema } from "effect";
import { Headers, HttpClientRequest } from "effect/unstable/http";
import { GeneratedImage, ImageModel, ImageResponse } from "../image";
import { Auth } from "../route/auth";
import { InvalidProviderOutputReason, LLMError, mergeHttpOptions, mergeJsonRecords } from "../schema";
import { ProviderShared } from "./shared";
const ADAPTER = "zai-images";
export const DEFAULT_BASE_URL = "https://api.z.ai/api/paas/v4";
export const PATH = "/images/generations";
const ZAIImageResponse = Schema.Struct({
created: Schema.optional(Schema.Int),
id: Schema.optional(Schema.String),
request_id: Schema.optional(Schema.String),
data: Schema.Array(Schema.Struct({ url: Schema.String })),
content_filter: Schema.optional(Schema.Array(Schema.Struct({
role: Schema.optional(Schema.String),
level: Schema.optional(Schema.Number),
}))),
});
const nativeOptions = (options) => {
if (!options)
return undefined;
const { userID, ...native } = options;
return {
user_id: userID,
...native,
};
};
const invalidOutput = (message) => new LLMError({
module: ADAPTER,
method: "generate",
reason: new InvalidProviderOutputReason({ message, route: ADAPTER }),
});
const applyQuery = (url, query) => {
if (!query)
return url;
const next = new URL(url);
Object.entries(query).forEach(([key, value]) => next.searchParams.set(key, value));
return next.toString();
};
export const model = (input) => {
const route = {
id: ADAPTER,
generate: Effect.fn("ZAIImages.generate")(function* (request, execute) {
const http = mergeHttpOptions(request.model.http, request.http);
const requestBody = mergeJsonRecords({ model: request.model.id, prompt: request.prompt }, nativeOptions(request.options), http?.body);
const text = ProviderShared.encodeJson(requestBody);
const url = applyQuery(`${(input.baseURL ?? DEFAULT_BASE_URL).replace(/\/$/, "")}${PATH}`, http?.query);
const headers = yield* Auth.toEffect(input.auth)({
request,
method: "POST",
url,
body: text,
headers: Headers.fromInput({ ...input.headers, ...http?.headers }),
});
const response = yield* execute(HttpClientRequest.post(url).pipe(HttpClientRequest.setHeaders(headers), HttpClientRequest.bodyText(text, "application/json")));
const payload = yield* response.json.pipe(Effect.mapError(() => invalidOutput("Failed to read the Z.ai Images response")));
const decoded = yield* Schema.decodeUnknownEffect(ZAIImageResponse)(payload).pipe(Effect.mapError(() => invalidOutput("Z.ai Images returned an invalid response")));
if (decoded.data.length === 0)
return yield* invalidOutput("Z.ai Images returned no images");
return new ImageResponse({
images: decoded.data.map((item) => new GeneratedImage({
mediaType: "application/octet-stream",
data: item.url,
})),
providerMetadata: {
zai: {
created: decoded.created,
id: decoded.id,
requestID: decoded.request_id,
contentFilter: decoded.content_filter,
},
},
});
}),
};
return ImageModel.make({ id: input.id, provider: "zai", route, http: input.http });
};
export const ZAIImages = {
model,
};
import { type ProviderAuthOption } from "../route/auth-options";
import { HttpOptions, type ModelID } from "../schema";
export declare const id: string & import("effect/Brand").Brand<"LLM.ProviderID">;
export type Config = ProviderAuthOption<"optional"> & {
readonly baseURL?: string;
readonly headers?: Record<string, string>;
readonly http?: HttpOptions.Input;
};
export type { ZAIImageOptions } from "../protocols/zai-images";
export declare const configure: (input?: Config) => {
id: string & import("effect/Brand").Brand<"LLM.ProviderID">;
image: (modelID: string | ModelID) => import("..").ImageModel<import("./zai").ZAIImageOptions>;
configure: (input?: Config) => /*elided*/ any;
};
export declare const provider: {
id: string & import("effect/Brand").Brand<"LLM.ProviderID">;
image: (modelID: string | ModelID) => import("..").ImageModel<import("./zai").ZAIImageOptions>;
configure: (input?: Config) => {
id: string & import("effect/Brand").Brand<"LLM.ProviderID">;
image: (modelID: string | ModelID) => import("..").ImageModel<import("./zai").ZAIImageOptions>;
configure: /*elided*/ any;
};
};
export declare const image: (modelID: string | ModelID) => import("..").ImageModel<import("./zai").ZAIImageOptions>;
import { ZAIImages } from "../protocols/zai-images";
import { AuthOptions } from "../route/auth-options";
import { HttpOptions, ProviderID } from "../schema";
export const id = ProviderID.make("zai");
const auth = (options) => AuthOptions.bearer(options, "ZAI_API_KEY");
export const configure = (input = {}) => {
const image = (modelID) => ZAIImages.model({
id: modelID,
auth: auth(input),
baseURL: input.baseURL,
headers: input.headers,
http: input.http === undefined ? undefined : HttpOptions.make(input.http),
});
return {
id,
image,
configure,
};
};
export const provider = configure();
export const image = provider.image;
+4
-4
import { Context, Effect, Layer } from "effect";
import { RequestExecutor } from "./route/executor";
import type { ImageRequest, ImageResponse } from "./image";
import type { ImageOptions, ImageRequestFor, ImageResponse } from "./image";
import type { LLMError } from "./schema";
export type Execute = RequestExecutor.Interface["execute"];
export interface Interface {
readonly generate: (request: ImageRequest) => Effect.Effect<ImageResponse, LLMError>;
readonly generate: <Options extends ImageOptions>(request: ImageRequestFor<Options>) => Effect.Effect<ImageResponse, LLMError>;
}

@@ -12,3 +12,3 @@ declare const Service_base: Context.ServiceClass<Service, "@opencode/ImageClient", Interface>;

}
export declare const generate: (request: ImageRequest) => Effect.Effect<ImageResponse, LLMError>;
export declare const generate: <Options extends ImageOptions>(request: ImageRequestFor<Options>) => Effect.Effect<ImageResponse, LLMError>;
export declare const layer: Layer.Layer<Service, never, RequestExecutor.Service>;

@@ -18,4 +18,4 @@ export declare const ImageClient: {

readonly layer: Layer.Layer<Service, never, RequestExecutor.Service>;
readonly generate: (request: ImageRequest) => Effect.Effect<ImageResponse, LLMError>;
readonly generate: <Options extends ImageOptions>(request: ImageRequestFor<Options>) => Effect.Effect<ImageResponse, LLMError>;
};
export {};
import { Effect, Schema } from "effect";
import { HttpOptions, LLMError, ModelID, ProviderID, Usage } from "./schema";
import { type Execute as ImageExecute } from "./image-client";
export interface ImageRoute {
import { Service, type Execute as ImageExecute } from "./image-client";
export interface ImageRoute<Options extends ImageOptions = ImageOptions> {
readonly id: string;
readonly generate: (request: ImageRequest, execute: ImageExecute) => Effect.Effect<ImageResponse, LLMError>;
readonly generate: (request: ImageRequestFor<Options>, execute: ImageExecute) => Effect.Effect<ImageResponse, LLMError>;
}
export declare class ImageModel {
export type ImageOptions = Record<string, unknown>;
export declare class ImageModel<Options extends ImageOptions = ImageOptions> {
protected readonly _Options: (options: Options) => Options;
readonly id: ModelID;
readonly provider: ProviderID;
readonly route: ImageRoute;
readonly defaults?: ImageModelDefaults;
constructor(input: ImageModel.Input);
static make(input: ImageModel.MakeInput): ImageModel;
readonly route: ImageRoute<Options>;
readonly http?: HttpOptions;
constructor(input: ImageModel.Input<Options>);
static make<Options extends ImageOptions = ImageOptions>(input: ImageModel.MakeInput<Options>): ImageModel<Options>;
}
export declare namespace ImageModel {
interface Input {
interface Input<Options extends ImageOptions = ImageOptions> {
readonly id: ModelID;
readonly provider: ProviderID;
readonly route: ImageRoute;
readonly defaults?: ImageModelDefaults;
readonly route: ImageRoute<Options>;
readonly http?: HttpOptions;
}
interface MakeInput extends Omit<Input, "id" | "provider"> {
interface MakeInput<Options extends ImageOptions = ImageOptions> extends Omit<Input<Options>, "id" | "provider"> {
readonly id: string | ModelID;

@@ -28,31 +30,22 @@ readonly provider: string | ProviderID;

}
export interface ImageModelDefaults {
readonly providerOptions?: Record<string, Record<string, unknown>>;
readonly http?: HttpOptions;
}
export declare const ImageModelSchema: Schema.declare<ImageModel, ImageModel>;
export declare const ImageSize: Schema.Struct<{
readonly width: Schema.Int;
readonly height: Schema.Int;
}>;
export type ImageSize = Schema.Schema.Type<typeof ImageSize>;
export declare const ImageModelSchema: Schema.declare<ImageModel<ImageOptions>, ImageModel<ImageOptions>>;
declare const ImageRequest_base: Schema.Class<ImageRequest, Schema.Struct<{
readonly model: Schema.declare<ImageModel, ImageModel>;
readonly model: Schema.declare<ImageModel<ImageOptions>, ImageModel<ImageOptions>>;
readonly prompt: Schema.String;
readonly count: Schema.optional<Schema.Int>;
readonly size: Schema.optional<Schema.Struct<{
readonly width: Schema.Int;
readonly height: Schema.Int;
}>>;
readonly aspectRatio: Schema.optional<Schema.String>;
readonly seed: Schema.optional<Schema.Number>;
readonly providerOptions: Schema.optional<Schema.$Record<Schema.String, Schema.$Record<Schema.String, Schema.Unknown>>>;
readonly options: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;
readonly http: Schema.optional<typeof HttpOptions>;
readonly metadata: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;
}>, {}>;
export declare class ImageRequest extends ImageRequest_base {
protected readonly _ImageRequest: void;
}
export type ImageRequestInput = Omit<ConstructorParameters<typeof ImageRequest>[0], "http"> & {
export type ImageRequestFor<Options extends ImageOptions = ImageOptions> = Omit<ImageRequest, "model" | "options"> & {
readonly model: ImageModel<Options>;
readonly options?: Options;
};
export type ImageModelOptions<Model> = Model extends ImageModel<infer Options> ? Options : never;
export type ImageRequestInput<Model extends object = ImageModel> = Omit<ConstructorParameters<typeof ImageRequest>[0], "model" | "options" | "http"> & {
readonly model: Model;
readonly options?: NoInfer<ImageModelOptions<Model>>;
readonly http?: HttpOptions.Input;
};
} & (Model extends ImageModel<ImageModelOptions<Model>> ? unknown : never);
declare const GeneratedImage_base: Schema.Class<GeneratedImage, Schema.Struct<{

@@ -73,8 +66,10 @@ readonly mediaType: Schema.String;

}
export declare const request: (input: ImageRequest | ImageRequestInput) => ImageRequest;
export declare const generate: (input: ImageRequest | ImageRequestInput) => Effect.Effect<ImageResponse, LLMError, never>;
export declare function request<const Model extends object>(input: ImageRequestInput<Model>): ImageRequestFor<ImageModelOptions<Model>>;
export declare function request(input: ImageRequest): ImageRequest;
export declare function generate<const Model extends object>(input: ImageRequestInput<Model>): Effect.Effect<ImageResponse, LLMError, Service>;
export declare function generate(input: ImageRequest): Effect.Effect<ImageResponse, LLMError, Service>;
export declare const Image: {
readonly request: (input: ImageRequest | ImageRequestInput) => ImageRequest;
readonly generate: (input: ImageRequest | ImageRequestInput) => Effect.Effect<ImageResponse, LLMError, never>;
readonly request: typeof request;
readonly generate: typeof generate;
};
export {};
import { Effect, Schema } from "effect";
import { HttpOptions, InvalidRequestReason, LLMError, ModelID, ProviderID, ProviderMetadata, Usage } from "./schema";
import { ImageClient } from "./image-client";
import { ImageClient, Service } from "./image-client";
export class ImageModel {

@@ -8,3 +8,3 @@ id;

route;
defaults;
http;
constructor(input) {

@@ -14,3 +14,3 @@ this.id = input.id;

this.route = input.route;
this.defaults = input.defaults;
this.http = input.http;
}

@@ -22,3 +22,3 @@ static make(input) {

route: input.route,
defaults: input.defaults,
http: input.http,
});

@@ -30,16 +30,7 @@ }

});
export const ImageSize = Schema.Struct({
width: Schema.Int.check(Schema.isGreaterThanOrEqualTo(1)),
height: Schema.Int.check(Schema.isGreaterThanOrEqualTo(1)),
}).annotate({ identifier: "Image.Size" });
export class ImageRequest extends Schema.Class("Image.Request")({
model: ImageModelSchema,
prompt: Schema.String,
count: Schema.optional(Schema.Int.check(Schema.isGreaterThanOrEqualTo(1))),
size: Schema.optional(ImageSize),
aspectRatio: Schema.optional(Schema.String),
seed: Schema.optional(Schema.Number),
providerOptions: Schema.optional(Schema.Record(Schema.String, Schema.Record(Schema.String, Schema.Unknown))),
options: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)),
http: Schema.optional(HttpOptions),
metadata: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)),
}) {

@@ -62,3 +53,3 @@ }

}
export const request = (input) => {
export function request(input) {
if (input instanceof ImageRequest)

@@ -68,13 +59,16 @@ return input;

...input,
model: input.model,
http: input.http === undefined ? undefined : HttpOptions.make(input.http),
});
};
export const generate = (input) => Effect.try({
try: () => request(input),
catch: (error) => new LLMError({
module: "Image",
method: "generate",
reason: new InvalidRequestReason({ message: error instanceof Error ? error.message : String(error) }),
}),
}).pipe(Effect.flatMap(ImageClient.generate));
}
export function generate(input) {
return Effect.try({
try: () => (input instanceof ImageRequest ? input : request(input)),
catch: (error) => new LLMError({
module: "Image",
method: "generate",
reason: new InvalidRequestReason({ message: error instanceof Error ? error.message : String(error) }),
}),
}).pipe(Effect.flatMap((request) => ImageClient.generate(request)));
}
export const Image = {

@@ -81,0 +75,0 @@ request,

@@ -9,4 +9,4 @@ export { LLMClient } from "./route/client";

export * from "./schema";
export { GeneratedImage, ImageModel, ImageRequest, ImageResponse, ImageSize } from "./image";
export type { ImageModelDefaults, ImageRequestInput, ImageRoute } from "./image";
export { GeneratedImage, ImageModel, ImageRequest, ImageResponse } from "./image";
export type { ImageModelOptions, ImageOptions, ImageRequestFor, ImageRequestInput, ImageRoute } from "./image";
export { Image } from "./image";

@@ -13,0 +13,0 @@ export { Tool, ToolFailure, toDefinitions } from "./tool";

@@ -8,3 +8,3 @@ export { LLMClient } from "./route/client";

export * from "./schema";
export { GeneratedImage, ImageModel, ImageRequest, ImageResponse, ImageSize } from "./image";
export { GeneratedImage, ImageModel, ImageRequest, ImageResponse } from "./image";
export { Image } from "./image";

@@ -11,0 +11,0 @@ export { Tool, ToolFailure, toDefinitions } from "./tool";

@@ -73,10 +73,10 @@ import { Route, type RouteRoutedModelInput } from "../route/client";

readonly type: "image_generation";
readonly size?: string | undefined;
readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
readonly background?: "auto" | "opaque" | "transparent" | undefined;
readonly output_format?: "png" | "jpeg" | "webp" | undefined;
readonly output_compression?: number | undefined;
readonly action?: "generate" | "auto" | "edit" | undefined;
readonly background?: "auto" | "opaque" | "transparent" | undefined;
readonly input_fidelity?: "low" | "high" | undefined;
readonly partial_images?: number | undefined;
readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
readonly size?: string | undefined;
})[] | undefined;

@@ -83,0 +83,0 @@ readonly temperature?: number | undefined;

@@ -1,25 +0,20 @@

import { Schema } from "effect";
import { ImageModel, type ImageModelDefaults } from "../image";
import { ImageModel } from "../image";
import { type Definition as AuthDefinition } from "../route/auth";
import { type HttpOptions } from "../schema";
export declare const DEFAULT_BASE_URL = "https://api.openai.com/v1";
export declare const PATH = "/images/generations";
export interface OpenAIImageOptions {
readonly quality?: "auto" | "low" | "medium" | "high";
readonly background?: "auto" | "opaque" | "transparent";
readonly moderation?: "auto" | "low";
readonly outputFormat?: "png" | "jpeg" | "webp";
export type OpenAIImageString<Known extends string> = Known | (string & {});
export type OpenAIImageOptions = {
readonly n?: number;
readonly size?: OpenAIImageString<"auto" | "256x256" | "512x512" | "1024x1024" | "1536x1024" | "1024x1536" | "1792x1024" | "1024x1792">;
readonly quality?: OpenAIImageString<"auto" | "low" | "medium" | "high" | "standard" | "hd">;
readonly background?: OpenAIImageString<"auto" | "opaque" | "transparent">;
readonly moderation?: OpenAIImageString<"auto" | "low">;
readonly outputFormat?: OpenAIImageString<"png" | "jpeg" | "webp">;
readonly outputCompression?: number;
}
declare const OpenAIImageBody: Schema.Struct<{
readonly model: Schema.String;
readonly prompt: Schema.String;
readonly n: Schema.optional<Schema.Int>;
readonly size: Schema.optional<Schema.String>;
readonly quality: Schema.optional<Schema.Literals<readonly ["auto", "low", "medium", "high"]>>;
readonly background: Schema.optional<Schema.Literals<readonly ["auto", "opaque", "transparent"]>>;
readonly moderation: Schema.optional<Schema.Literals<readonly ["auto", "low"]>>;
readonly output_format: Schema.optional<Schema.Literals<readonly ["png", "jpeg", "webp"]>>;
readonly output_compression: Schema.optional<Schema.Int>;
}>;
export type OpenAIImageBody = Schema.Schema.Type<typeof OpenAIImageBody>;
} & Record<string, unknown>;
export type OpenAIImageBody = Record<string, unknown> & {
readonly model: string;
readonly prompt: string;
};
export interface ModelInput {

@@ -30,8 +25,7 @@ readonly id: string;

readonly headers?: Record<string, string>;
readonly defaults?: ImageModelDefaults;
readonly http?: HttpOptions;
}
export declare const model: (input: ModelInput) => ImageModel;
export declare const model: (input: ModelInput) => ImageModel<OpenAIImageOptions>;
export declare const OpenAIImages: {
readonly model: (input: ModelInput) => ImageModel;
readonly model: (input: ModelInput) => ImageModel<OpenAIImageOptions>;
};
export {};
import { Effect, Encoding, Schema } from "effect";
import { Headers, HttpClientRequest } from "effect/unstable/http";
import { ImageModel, GeneratedImage, ImageResponse, } from "../image";
import { ImageModel, GeneratedImage, ImageResponse } from "../image";
import { Auth } from "../route/auth";
import { InvalidProviderOutputReason, LLMError, Usage, mergeHttpOptions, mergeJsonRecords } from "../schema";
import { InvalidProviderOutputReason, LLMError, Usage, mergeHttpOptions, mergeJsonRecords, } from "../schema";
import { ProviderShared } from "./shared";

@@ -11,13 +11,2 @@ import { OpenAIImage } from "./utils/openai-image";

export const PATH = "/images/generations";
const OpenAIImageBody = Schema.Struct({
model: Schema.String,
prompt: Schema.String,
n: Schema.optional(Schema.Int.check(Schema.isGreaterThanOrEqualTo(1))),
size: Schema.optional(Schema.String),
quality: Schema.optional(Schema.Literals(["auto", "low", "medium", "high"])),
background: Schema.optional(Schema.Literals(["auto", "opaque", "transparent"])),
moderation: Schema.optional(Schema.Literals(["auto", "low"])),
output_format: Schema.optional(Schema.Literals(["png", "jpeg", "webp"])),
output_compression: Schema.optional(Schema.Int.check(Schema.isBetween({ minimum: 0, maximum: 100 }))),
});
const OpenAIImageResponse = Schema.Struct({

@@ -38,18 +27,10 @@ data: Schema.Array(Schema.Struct({

});
const providerOptions = (request) => ({
...request.model.defaults?.providerOptions?.openai,
...request.providerOptions?.openai,
});
const body = (request) => {
const options = providerOptions(request);
const nativeOptions = (options) => {
if (!options)
return undefined;
const { outputFormat, outputCompression, ...native } = options;
return {
model: request.model.id,
prompt: request.prompt,
n: request.count,
size: request.size === undefined ? undefined : `${request.size.width}x${request.size.height}`,
quality: options.quality,
background: options.background,
moderation: options.moderation,
output_format: options.outputFormat,
output_compression: options.outputCompression,
output_format: outputFormat,
output_compression: outputCompression,
...native,
};

@@ -69,21 +50,2 @@ };

};
const PROTOCOL_BODY_FIELDS = new Set([
"model",
"prompt",
"n",
"size",
"quality",
"background",
"moderation",
"output_format",
"output_compression",
]);
const bodyWithOverlay = Effect.fn("OpenAIImages.bodyWithOverlay")(function* (imageBody, overlay) {
if (!overlay)
return imageBody;
const reserved = Object.keys(overlay).filter((key) => PROTOCOL_BODY_FIELDS.has(key));
if (reserved.length > 0)
return yield* ProviderShared.invalidRequest(`http.body cannot overlay protocol-owned field(s): ${reserved.join(", ")}`);
return mergeJsonRecords(imageBody, overlay) ?? imageBody;
});
export const model = (input) => {

@@ -93,10 +55,5 @@ const route = {

generate: Effect.fn("OpenAIImages.generate")(function* (request, execute) {
if (request.aspectRatio !== undefined)
return yield* ProviderShared.invalidRequest("OpenAI Images does not support the common aspectRatio option");
if (request.seed !== undefined)
return yield* ProviderShared.invalidRequest("OpenAI Images does not support the common seed option");
const requestBody = yield* ProviderShared.validateWith(Schema.decodeUnknownEffect(OpenAIImageBody))(body(request));
const http = mergeHttpOptions(request.model.defaults?.http, request.http);
const overlaidBody = yield* bodyWithOverlay(requestBody, http?.body);
const text = ProviderShared.encodeJson(overlaidBody);
const http = mergeHttpOptions(request.model.http, request.http);
const requestBody = mergeJsonRecords({ model: request.model.id, prompt: request.prompt }, nativeOptions(request.options), http?.body);
const text = ProviderShared.encodeJson(requestBody);
const url = applyQuery(`${(input.baseURL ?? DEFAULT_BASE_URL).replace(/\/$/, "")}${PATH}`, http?.query);

@@ -113,3 +70,3 @@ const headers = yield* Auth.toEffect(input.auth)({

const decoded = yield* Schema.decodeUnknownEffect(OpenAIImageResponse)(payload).pipe(Effect.mapError(() => invalidOutput("OpenAI Images returned an invalid response")));
const format = decoded.output_format ?? providerOptions(request).outputFormat ?? "png";
const format = decoded.output_format ?? (typeof requestBody.output_format === "string" ? requestBody.output_format : "png");
const images = yield* Effect.forEach(decoded.data, (item, index) => {

@@ -146,3 +103,3 @@ if (item.b64_json)

};
return ImageModel.make({ id: input.id, provider: "openai", route, defaults: input.defaults });
return ImageModel.make({ id: input.id, provider: "openai", route, http: input.http });
};

@@ -149,0 +106,0 @@ export const OpenAIImages = {

@@ -167,10 +167,10 @@ import { Schema } from "effect";

readonly type: "image_generation";
readonly size?: string | undefined;
readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
readonly background?: "auto" | "opaque" | "transparent" | undefined;
readonly output_format?: "png" | "jpeg" | "webp" | undefined;
readonly output_compression?: number | undefined;
readonly action?: "generate" | "auto" | "edit" | undefined;
readonly background?: "auto" | "opaque" | "transparent" | undefined;
readonly input_fidelity?: "low" | "high" | undefined;
readonly partial_images?: number | undefined;
readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
readonly size?: string | undefined;
})[] | undefined;

@@ -320,10 +320,10 @@ readonly temperature?: number | undefined;

readonly type: "image_generation";
readonly size?: string | undefined;
readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
readonly background?: "auto" | "opaque" | "transparent" | undefined;
readonly output_format?: "png" | "jpeg" | "webp" | undefined;
readonly output_compression?: number | undefined;
readonly action?: "generate" | "auto" | "edit" | undefined;
readonly background?: "auto" | "opaque" | "transparent" | undefined;
readonly input_fidelity?: "low" | "high" | undefined;
readonly partial_images?: number | undefined;
readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
readonly size?: string | undefined;
})[] | undefined;

@@ -410,10 +410,10 @@ readonly temperature?: number | undefined;

readonly type: "image_generation";
readonly size?: string | undefined;
readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
readonly background?: "auto" | "opaque" | "transparent" | undefined;
readonly output_format?: "png" | "jpeg" | "webp" | undefined;
readonly output_compression?: number | undefined;
readonly action?: "generate" | "auto" | "edit" | undefined;
readonly background?: "auto" | "opaque" | "transparent" | undefined;
readonly input_fidelity?: "low" | "high" | undefined;
readonly partial_images?: number | undefined;
readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
readonly size?: string | undefined;
})[] | undefined;

@@ -500,10 +500,10 @@ readonly temperature?: number | undefined;

readonly type: "image_generation";
readonly size?: string | undefined;
readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
readonly background?: "auto" | "opaque" | "transparent" | undefined;
readonly output_format?: "png" | "jpeg" | "webp" | undefined;
readonly output_compression?: number | undefined;
readonly action?: "generate" | "auto" | "edit" | undefined;
readonly background?: "auto" | "opaque" | "transparent" | undefined;
readonly input_fidelity?: "low" | "high" | undefined;
readonly partial_images?: number | undefined;
readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
readonly size?: string | undefined;
})[] | undefined;

@@ -590,10 +590,10 @@ readonly temperature?: number | undefined;

readonly type: "image_generation";
readonly size?: string | undefined;
readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
readonly background?: "auto" | "opaque" | "transparent" | undefined;
readonly output_format?: "png" | "jpeg" | "webp" | undefined;
readonly output_compression?: number | undefined;
readonly action?: "generate" | "auto" | "edit" | undefined;
readonly background?: "auto" | "opaque" | "transparent" | undefined;
readonly input_fidelity?: "low" | "high" | undefined;
readonly partial_images?: number | undefined;
readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
readonly size?: string | undefined;
})[] | undefined;

@@ -680,10 +680,10 @@ readonly temperature?: number | undefined;

readonly type: "image_generation";
readonly size?: string | undefined;
readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
readonly background?: "auto" | "opaque" | "transparent" | undefined;
readonly output_format?: "png" | "jpeg" | "webp" | undefined;
readonly output_compression?: number | undefined;
readonly action?: "generate" | "auto" | "edit" | undefined;
readonly background?: "auto" | "opaque" | "transparent" | undefined;
readonly input_fidelity?: "low" | "high" | undefined;
readonly partial_images?: number | undefined;
readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
readonly size?: string | undefined;
})[] | undefined;

@@ -690,0 +690,0 @@ readonly temperature?: number | undefined;

@@ -152,10 +152,10 @@ import { type AtLeastOne, type ProviderAuthOption } from "../route/auth-options";

readonly type: "image_generation";
readonly size?: string | undefined;
readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
readonly background?: "auto" | "opaque" | "transparent" | undefined;
readonly output_format?: "png" | "jpeg" | "webp" | undefined;
readonly output_compression?: number | undefined;
readonly action?: "generate" | "auto" | "edit" | undefined;
readonly background?: "auto" | "opaque" | "transparent" | undefined;
readonly input_fidelity?: "low" | "high" | undefined;
readonly partial_images?: number | undefined;
readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
readonly size?: string | undefined;
})[] | undefined;

@@ -162,0 +162,0 @@ readonly temperature?: number | undefined;

@@ -140,10 +140,10 @@ import { type ProviderAuthOption } from "../route/auth-options";

readonly type: "image_generation";
readonly size?: string | undefined;
readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
readonly background?: "auto" | "opaque" | "transparent" | undefined;
readonly output_format?: "png" | "jpeg" | "webp" | undefined;
readonly output_compression?: number | undefined;
readonly action?: "generate" | "auto" | "edit" | undefined;
readonly background?: "auto" | "opaque" | "transparent" | undefined;
readonly input_fidelity?: "low" | "high" | undefined;
readonly partial_images?: number | undefined;
readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
readonly size?: string | undefined;
})[] | undefined;

@@ -150,0 +150,0 @@ readonly temperature?: number | undefined;

@@ -84,10 +84,10 @@ import type { ProviderPackage } from "../provider-package";

readonly type: "image_generation";
readonly size?: string | undefined;
readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
readonly background?: "auto" | "opaque" | "transparent" | undefined;
readonly output_format?: "png" | "jpeg" | "webp" | undefined;
readonly output_compression?: number | undefined;
readonly action?: "generate" | "auto" | "edit" | undefined;
readonly background?: "auto" | "opaque" | "transparent" | undefined;
readonly input_fidelity?: "low" | "high" | undefined;
readonly partial_images?: number | undefined;
readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
readonly size?: string | undefined;
})[] | undefined;

@@ -94,0 +94,0 @@ readonly temperature?: number | undefined;

@@ -5,2 +5,3 @@ import type { RouteDefaultsInput } from "../route/client";

import { type ModelID, type ProviderOptions } from "../schema";
export type { GoogleImageOptions } from "../protocols/google-images";
export declare const id: string & import("effect/Brand").Brand<"LLM.ProviderID">;

@@ -73,2 +74,3 @@ export declare const routes: import("../route").Route<{

model: (modelID: string | ModelID) => import("..").Model;
image: (modelID: string | ModelID) => import("..").ImageModel<import("./google").GoogleImageOptions>;
configure: (input?: Config) => /*elided*/ any;

@@ -79,5 +81,7 @@ };

model: (modelID: string | ModelID) => import("..").Model;
image: (modelID: string | ModelID) => import("..").ImageModel<import("./google").GoogleImageOptions>;
configure: (input?: Config) => {
id: string & import("effect/Brand").Brand<"LLM.ProviderID">;
model: (modelID: string | ModelID) => import("..").Model;
image: (modelID: string | ModelID) => import("..").ImageModel<import("./google").GoogleImageOptions>;
configure: /*elided*/ any;

@@ -87,1 +91,2 @@ };

export declare const model: ProviderPackage.Definition<Settings>["model"];
export declare const image: (modelID: string | ModelID) => import("..").ImageModel<import("./google").GoogleImageOptions>;
import { Auth } from "../route/auth";
import { ProviderID } from "../schema";
import * as Gemini from "../protocols/gemini";
import { HttpOptions, ProviderID, mergeHttpOptions } from "../schema";
import { Gemini } from "../protocols/gemini";
import { GoogleImages } from "../protocols/google-images";
export const id = ProviderID.make("google");

@@ -19,5 +20,13 @@ export const routes = [Gemini.route];

const route = configuredRoute(input);
const image = (modelID) => GoogleImages.model({
id: modelID,
auth: auth(input),
baseURL: input.baseURL,
headers: input.headers,
http: mergeHttpOptions(input.http === undefined ? undefined : HttpOptions.make(input.http)),
});
return {
id,
model: (modelID) => route.model({ id: modelID }),
image,
configure,

@@ -35,1 +44,2 @@ };

}).model(modelID);
export const image = provider.image;

@@ -18,1 +18,2 @@ export * as Anthropic from "./anthropic";

export * as XAI from "./xai";
export * as ZAI from "./zai";

@@ -18,1 +18,2 @@ export * as Anthropic from "./anthropic";

export * as XAI from "./xai";
export * as ZAI from "./zai";

@@ -82,10 +82,10 @@ import type { ProviderPackage } from "../provider-package";

readonly type: "image_generation";
readonly size?: string | undefined;
readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
readonly background?: "auto" | "opaque" | "transparent" | undefined;
readonly output_format?: "png" | "jpeg" | "webp" | undefined;
readonly output_compression?: number | undefined;
readonly action?: "generate" | "auto" | "edit" | undefined;
readonly background?: "auto" | "opaque" | "transparent" | undefined;
readonly input_fidelity?: "low" | "high" | undefined;
readonly partial_images?: number | undefined;
readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
readonly size?: string | undefined;
})[] | undefined;

@@ -92,0 +92,0 @@ readonly temperature?: number | undefined;

@@ -6,3 +6,3 @@ import { type ProviderAuthOption } from "../route/auth-options";

import { type OpenAIProviderOptionsInput } from "./openai-options";
import { type OpenAIImageOptions } from "../protocols/openai-images";
import { type OpenAIImageString } from "../protocols/openai-images";
export type { OpenAIOptionsInput, OpenAIResponseIncludable } from "./openai-options";

@@ -139,10 +139,10 @@ export type { OpenAIImageOptions } from "../protocols/openai-images";

readonly type: "image_generation";
readonly size?: string | undefined;
readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
readonly background?: "auto" | "opaque" | "transparent" | undefined;
readonly output_format?: "png" | "jpeg" | "webp" | undefined;
readonly output_compression?: number | undefined;
readonly action?: "generate" | "auto" | "edit" | undefined;
readonly background?: "auto" | "opaque" | "transparent" | undefined;
readonly input_fidelity?: "low" | "high" | undefined;
readonly partial_images?: number | undefined;
readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
readonly size?: string | undefined;
})[] | undefined;

@@ -228,10 +228,10 @@ readonly temperature?: number | undefined;

readonly type: "image_generation";
readonly size?: string | undefined;
readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
readonly background?: "auto" | "opaque" | "transparent" | undefined;
readonly output_format?: "png" | "jpeg" | "webp" | undefined;
readonly output_compression?: number | undefined;
readonly action?: "generate" | "auto" | "edit" | undefined;
readonly background?: "auto" | "opaque" | "transparent" | undefined;
readonly input_fidelity?: "low" | "high" | undefined;
readonly partial_images?: number | undefined;
readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
readonly size?: string | undefined;
})[] | undefined;

@@ -257,16 +257,12 @@ readonly temperature?: number | undefined;

readonly providerOptions?: OpenAIProviderOptionsInput;
readonly image?: ImageConfig;
};
export interface ImageConfig {
readonly providerOptions?: OpenAIImageOptions;
}
export interface ImageGenerationOptions {
readonly action?: "auto" | "generate" | "edit";
readonly background?: "auto" | "opaque" | "transparent";
readonly inputFidelity?: "low" | "high";
readonly action?: OpenAIImageString<"auto" | "generate" | "edit">;
readonly background?: OpenAIImageString<"auto" | "opaque" | "transparent">;
readonly inputFidelity?: OpenAIImageString<"low" | "high">;
readonly outputCompression?: number;
readonly outputFormat?: "png" | "jpeg" | "webp";
readonly outputFormat?: OpenAIImageString<"png" | "jpeg" | "webp">;
readonly partialImages?: number;
readonly quality?: "auto" | "low" | "medium" | "high";
readonly size?: string;
readonly quality?: OpenAIImageString<"auto" | "low" | "medium" | "high" | "standard" | "hd">;
readonly size?: OpenAIImageString<"auto" | "256x256" | "512x512" | "1024x1024" | "1536x1024" | "1024x1536" | "1792x1024" | "1024x1792">;
}

@@ -289,3 +285,3 @@ export declare const imageGeneration: (options?: ImageGenerationOptions) => ToolDefinition;

chat: (id: string | ModelID) => import("..").Model;
image: (modelID: string | ModelID) => import("..").ImageModel;
image: (modelID: string | ModelID) => import("..").ImageModel<import("./openai").OpenAIImageOptions>;
configure: (input?: Config) => /*elided*/ any;

@@ -299,3 +295,3 @@ };

chat: (id: string | ModelID) => import("..").Model;
image: (modelID: string | ModelID) => import("..").ImageModel;
image: (modelID: string | ModelID) => import("..").ImageModel<import("./openai").OpenAIImageOptions>;
configure: (input?: Config) => {

@@ -307,3 +303,3 @@ id: string & import("effect/Brand").Brand<"LLM.ProviderID">;

chat: (id: string | ModelID) => import("..").Model;
image: (modelID: string | ModelID) => import("..").ImageModel;
image: (modelID: string | ModelID) => import("..").ImageModel<import("./openai").OpenAIImageOptions>;
configure: /*elided*/ any;

@@ -317,2 +313,2 @@ };

export declare const chat: (id: string | ModelID) => import("..").Model;
export declare const image: (modelID: string | ModelID) => import("..").ImageModel;
export declare const image: (modelID: string | ModelID) => import("..").ImageModel<import("./openai").OpenAIImageOptions>;

@@ -29,3 +29,3 @@ import { AuthOptions } from "../route/auth-options";

const defaults = (input) => {
const { apiKey: _, auth: _auth, baseURL: _baseURL, queryParams: _queryParams, image: _image, ...rest } = input;
const { apiKey: _, auth: _auth, baseURL: _baseURL, queryParams: _queryParams, ...rest } = input;
return rest;

@@ -50,6 +50,3 @@ };

headers: input.headers,
defaults: {
providerOptions: input.image?.providerOptions === undefined ? undefined : { openai: { ...input.image.providerOptions } },
http: mergeHttpOptions(input.http === undefined ? undefined : HttpOptions.make(input.http), input.queryParams === undefined ? undefined : new HttpOptions({ query: input.queryParams })),
},
http: mergeHttpOptions(input.http === undefined ? undefined : HttpOptions.make(input.http), input.queryParams === undefined ? undefined : new HttpOptions({ query: input.queryParams })),
});

@@ -56,0 +53,0 @@ return {

@@ -8,2 +8,3 @@ import { type ProviderAuthOption } from "../route/auth-options";

};
export type { XAIImageOptions } from "../protocols/xai-images";
export declare const routes: (import("../route").Route<{

@@ -137,10 +138,10 @@ readonly messages: readonly (import("effect/Schema").Struct.ReadonlySide<{

readonly type: "image_generation";
readonly size?: string | undefined;
readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
readonly background?: "auto" | "opaque" | "transparent" | undefined;
readonly output_format?: "png" | "jpeg" | "webp" | undefined;
readonly output_compression?: number | undefined;
readonly action?: "generate" | "auto" | "edit" | undefined;
readonly background?: "auto" | "opaque" | "transparent" | undefined;
readonly input_fidelity?: "low" | "high" | undefined;
readonly partial_images?: number | undefined;
readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
readonly size?: string | undefined;
})[] | undefined;

@@ -167,2 +168,3 @@ readonly temperature?: number | undefined;

chat: (modelID: string | ModelID) => import("..").Model;
image: (modelID: string | ModelID) => import("..").ImageModel<import("./xai").XAIImageOptions>;
configure: (input?: ModelOptions) => /*elided*/ any;

@@ -175,2 +177,3 @@ };

chat: (modelID: string | ModelID) => import("..").Model;
image: (modelID: string | ModelID) => import("..").ImageModel<import("./xai").XAIImageOptions>;
configure: (input?: ModelOptions) => {

@@ -181,2 +184,3 @@ id: string & import("effect/Brand").Brand<"LLM.ProviderID">;

chat: (modelID: string | ModelID) => import("..").Model;
image: (modelID: string | ModelID) => import("..").ImageModel<import("./xai").XAIImageOptions>;
configure: /*elided*/ any;

@@ -188,1 +192,2 @@ };

export declare const chat: (modelID: string | ModelID) => import("..").Model;
export declare const image: (modelID: string | ModelID) => import("..").ImageModel<import("./xai").XAIImageOptions>;
import { AuthOptions } from "../route/auth-options";
import { ProviderID } from "../schema";
import { HttpOptions, ProviderID } from "../schema";
import * as OpenAICompatibleProfiles from "./openai-compatible-profile";
import * as OpenAICompatibleChat from "../protocols/openai-compatible-chat";
import * as OpenAIResponses from "../protocols/openai-responses";
import { XAIImages } from "../protocols/xai-images";
export const id = ProviderID.make("xai");

@@ -32,2 +33,9 @@ export const routes = [OpenAIResponses.route, OpenAICompatibleChat.route];

const chat = (modelID) => chatRoute.model({ id: modelID });
const image = (modelID) => XAIImages.model({
id: modelID,
auth: auth(input),
baseURL: input.baseURL ?? OpenAICompatibleProfiles.profiles.xai.baseURL,
headers: input.headers,
http: input.http === undefined ? undefined : HttpOptions.make(input.http),
});
return {

@@ -38,2 +46,3 @@ id,

chat,
image,
configure,

@@ -46,1 +55,2 @@ };

export const chat = provider.chat;
export const image = provider.image;
{
"$schema": "https://json.schemastore.org/package.json",
"version": "0.0.0-next-15898",
"version": "0.0.0-next-15910",
"name": "@opencode-ai/ai",

@@ -10,3 +10,3 @@ "type": "module",

"test": "bun test --timeout 30000 --only-failures",
"typecheck": "tsgo --noEmit",
"typecheck": "tsgo --noEmit && tsgo --noEmit -p tsconfig.types.json",
"build": "tsc -p tsconfig.build.json"

@@ -30,3 +30,3 @@ },

"@effect/platform-node": "4.0.0-beta.98",
"@opencode-ai/http-recorder": "0.0.0-next-15898",
"@opencode-ai/http-recorder": "0.0.0-next-15910",
"@tsconfig/bun": "1.0.9",

@@ -40,3 +40,3 @@ "@types/bun": "1.3.13",

"@smithy/util-utf8": "4.2.2",
"@opencode-ai/schema": "0.0.0-next-15898",
"@opencode-ai/schema": "0.0.0-next-15910",
"aws4fetch": "1.0.20",

@@ -43,0 +43,0 @@ "effect": "4.0.0-beta.98",

@@ -39,5 +39,9 @@ # @opencode-ai/ai

prompt: "A robot tending a rooftop garden",
count: 2,
size: { width: 1024, height: 1024 },
providerOptions: { openai: { quality: "high", outputFormat: "webp" } },
options: {
n: 2,
size: "1024x1024",
quality: "high", // inferred from the OpenAI image model
outputFormat: "webp",
future_option: true, // unknown native options pass through unchanged
},
})

@@ -49,2 +53,83 @@

Provider-native image options belong to each request. Raw `http.body` fields have final precedence over them:
```ts
const model = OpenAI.configure({ apiKey }).image("gpt-image-2")
yield *
Image.generate({
model,
prompt,
options: { quality: "medium" },
http,
})
```
xAI image models use the same request API with xAI-native controls:
```ts
yield *
Image.generate({
model: XAI.configure({ apiKey }).image("any-model-id"),
prompt,
options: {
n: 2,
aspectRatio: "16:9",
resolution: "1k",
responseFormat: "b64_json",
future_option: true,
},
http,
})
```
Google's current Gemini image models use the same direct API:
```ts
import { Google } from "@opencode-ai/ai/providers"
const googleProgram = Effect.gen(function* () {
const response = yield* Image.generate({
model: Google.configure({ apiKey }).image("any-model-id"),
prompt: "A robot tending a rooftop garden",
options: {
aspectRatio: "16:9",
imageSize: "2K",
seed: 42,
thinkingLevel: "HIGH",
includeThoughts: true,
futureOption: true,
},
http,
})
return response.images
})
```
Google image options are request-scoped and inferred from the selected model. Known fields autocomplete while
future string values and arbitrary native Gemini `generationConfig` fields remain available. Native fields override
their mapped aliases, and `http.body` is the final deep overlay. The selected model ID is sent to Gemini
`generateContent` without a local allowlist.
Z.ai image models infer open Z.ai-native options from the selected model:
```ts
yield *
Image.generate({
model: ZAI.configure({ apiKey }).image("any-model-id"),
prompt,
options: {
quality: "hd",
userID: "user-123",
future_option: true,
},
http,
})
```
Z.ai does not include trustworthy MIME metadata for output URLs, so generated images use
`application/octet-stream`. Output URLs expire after 30 days; download and persist them promptly if they must
remain available.
Conversational image generation remains part of the LLM interaction. OpenAI Responses exposes it through its hosted image tool:

@@ -150,3 +235,3 @@

Included providers: OpenAI, Anthropic, Google (Gemini), Google Vertex Gemini and Anthropic, Amazon Bedrock, Azure OpenAI, Cloudflare AI Gateway, Cloudflare Workers AI, GitHub Copilot, OpenRouter, xAI, plus generic OpenAI-compatible Chat and Responses entrypoints and an Anthropic Messages-compatible entrypoint.
Included providers: OpenAI, Anthropic, Google (Gemini), Google Vertex Gemini and Anthropic, Amazon Bedrock, Azure OpenAI, Cloudflare AI Gateway, Cloudflare Workers AI, GitHub Copilot, OpenRouter, xAI, Z.ai, plus generic OpenAI-compatible Chat and Responses entrypoints and an Anthropic Messages-compatible entrypoint.

@@ -153,0 +238,0 @@ ### Package-like entrypoints