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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -36,15 +36,33 @@ const program = Effect.gen(function* () {
36
36
  const response = yield* Image.generate({
37
37
  model: OpenAI.configure({ apiKey: process.env.OPENAI_API_KEY }).image("gpt-image-2"),
38
38
  prompt: "A robot tending a rooftop garden",
39
- count: 2,
40
- size: { width: 1024, height: 1024 },
41
- providerOptions: { openai: { quality: "high", outputFormat: "webp" } },
39
+ options: {
40
+ n: 2,
41
+ size: "1024x1024",
42
+ quality: "high", // inferred from the OpenAI image model
43
+ outputFormat: "webp",
44
+ future_option: true, // unknown native options pass through unchanged
45
+ },
42
46
  })
43
47
 
44
48
  return response.images // GeneratedImage[] with owned bytes or a provider URL
45
49
  })
46
50
  ```
47
51
 
52
+ Provider-native image options belong to each request. Raw `http.body` fields have final precedence over them:
53
+
54
+ ```ts
55
+ const model = OpenAI.configure({ apiKey }).image("gpt-image-2")
56
+
57
+ yield *
58
+ Image.generate({
59
+ model,
60
+ prompt,
61
+ options: { quality: "medium" },
62
+ http,
63
+ })
64
+ ```
65
+
48
66
  Conversational image generation remains part of the LLM interaction. OpenAI Responses exposes it through its hosted image tool:
49
67
 
50
68
  ```ts
@@ -1,19 +1,19 @@
1
1
  import { Context, Effect, Layer } from "effect";
2
2
  import { RequestExecutor } from "./route/executor";
3
- import type { ImageRequest, ImageResponse } from "./image";
3
+ import type { ImageOptions, ImageRequestFor, ImageResponse } from "./image";
4
4
  import type { LLMError } from "./schema";
5
5
  export type Execute = RequestExecutor.Interface["execute"];
6
6
  export interface Interface {
7
- readonly generate: (request: ImageRequest) => Effect.Effect<ImageResponse, LLMError>;
7
+ readonly generate: <Options extends ImageOptions>(request: ImageRequestFor<Options>) => Effect.Effect<ImageResponse, LLMError>;
8
8
  }
9
9
  declare const Service_base: Context.ServiceClass<Service, "@opencode/ImageClient", Interface>;
10
10
  export declare class Service extends Service_base {
11
11
  }
12
- export declare const generate: (request: ImageRequest) => Effect.Effect<ImageResponse, LLMError>;
12
+ export declare const generate: <Options extends ImageOptions>(request: ImageRequestFor<Options>) => Effect.Effect<ImageResponse, LLMError>;
13
13
  export declare const layer: Layer.Layer<Service, never, RequestExecutor.Service>;
14
14
  export declare const ImageClient: {
15
15
  readonly Service: typeof Service;
16
16
  readonly layer: Layer.Layer<Service, never, RequestExecutor.Service>;
17
- readonly generate: (request: ImageRequest) => Effect.Effect<ImageResponse, LLMError>;
17
+ readonly generate: <Options extends ImageOptions>(request: ImageRequestFor<Options>) => Effect.Effect<ImageResponse, LLMError>;
18
18
  };
19
19
  export {};
package/dist/image.d.ts CHANGED
@@ -1,59 +1,52 @@
1
1
  import { Effect, Schema } from "effect";
2
2
  import { HttpOptions, LLMError, ModelID, ProviderID, Usage } from "./schema";
3
- import { type Execute as ImageExecute } from "./image-client";
4
- export interface ImageRoute {
3
+ import { Service, type Execute as ImageExecute } from "./image-client";
4
+ export interface ImageRoute<Options extends ImageOptions = ImageOptions> {
5
5
  readonly id: string;
6
- readonly generate: (request: ImageRequest, execute: ImageExecute) => Effect.Effect<ImageResponse, LLMError>;
6
+ readonly generate: (request: ImageRequestFor<Options>, execute: ImageExecute) => Effect.Effect<ImageResponse, LLMError>;
7
7
  }
8
- export declare class ImageModel {
8
+ export type ImageOptions = Record<string, unknown>;
9
+ export declare class ImageModel<Options extends ImageOptions = ImageOptions> {
10
+ protected readonly _Options: (options: Options) => Options;
9
11
  readonly id: ModelID;
10
12
  readonly provider: ProviderID;
11
- readonly route: ImageRoute;
12
- readonly defaults?: ImageModelDefaults;
13
- constructor(input: ImageModel.Input);
14
- static make(input: ImageModel.MakeInput): ImageModel;
13
+ readonly route: ImageRoute<Options>;
14
+ readonly http?: HttpOptions;
15
+ constructor(input: ImageModel.Input<Options>);
16
+ static make<Options extends ImageOptions = ImageOptions>(input: ImageModel.MakeInput<Options>): ImageModel<Options>;
15
17
  }
16
18
  export declare namespace ImageModel {
17
- interface Input {
19
+ interface Input<Options extends ImageOptions = ImageOptions> {
18
20
  readonly id: ModelID;
19
21
  readonly provider: ProviderID;
20
- readonly route: ImageRoute;
21
- readonly defaults?: ImageModelDefaults;
22
+ readonly route: ImageRoute<Options>;
23
+ readonly http?: HttpOptions;
22
24
  }
23
- interface MakeInput extends Omit<Input, "id" | "provider"> {
25
+ interface MakeInput<Options extends ImageOptions = ImageOptions> extends Omit<Input<Options>, "id" | "provider"> {
24
26
  readonly id: string | ModelID;
25
27
  readonly provider: string | ProviderID;
26
28
  }
27
29
  }
28
- export interface ImageModelDefaults {
29
- readonly providerOptions?: Record<string, Record<string, unknown>>;
30
- readonly http?: HttpOptions;
31
- }
32
- export declare const ImageModelSchema: Schema.declare<ImageModel, ImageModel>;
33
- export declare const ImageSize: Schema.Struct<{
34
- readonly width: Schema.Int;
35
- readonly height: Schema.Int;
36
- }>;
37
- export type ImageSize = Schema.Schema.Type<typeof ImageSize>;
30
+ export declare const ImageModelSchema: Schema.declare<ImageModel<ImageOptions>, ImageModel<ImageOptions>>;
38
31
  declare const ImageRequest_base: Schema.Class<ImageRequest, Schema.Struct<{
39
- readonly model: Schema.declare<ImageModel, ImageModel>;
32
+ readonly model: Schema.declare<ImageModel<ImageOptions>, ImageModel<ImageOptions>>;
40
33
  readonly prompt: Schema.String;
41
- readonly count: Schema.optional<Schema.Int>;
42
- readonly size: Schema.optional<Schema.Struct<{
43
- readonly width: Schema.Int;
44
- readonly height: Schema.Int;
45
- }>>;
46
- readonly aspectRatio: Schema.optional<Schema.String>;
47
- readonly seed: Schema.optional<Schema.Number>;
48
- readonly providerOptions: Schema.optional<Schema.$Record<Schema.String, Schema.$Record<Schema.String, Schema.Unknown>>>;
34
+ readonly options: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;
49
35
  readonly http: Schema.optional<typeof HttpOptions>;
50
- readonly metadata: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;
51
36
  }>, {}>;
52
37
  export declare class ImageRequest extends ImageRequest_base {
38
+ protected readonly _ImageRequest: void;
53
39
  }
54
- export type ImageRequestInput = Omit<ConstructorParameters<typeof ImageRequest>[0], "http"> & {
55
- readonly http?: HttpOptions.Input;
40
+ export type ImageRequestFor<Options extends ImageOptions = ImageOptions> = Omit<ImageRequest, "model" | "options"> & {
41
+ readonly model: ImageModel<Options>;
42
+ readonly options?: Options;
56
43
  };
44
+ export type ImageModelOptions<Model> = Model extends ImageModel<infer Options> ? Options : never;
45
+ export type ImageRequestInput<Model extends object = ImageModel> = Omit<ConstructorParameters<typeof ImageRequest>[0], "model" | "options" | "http"> & {
46
+ readonly model: Model;
47
+ readonly options?: NoInfer<ImageModelOptions<Model>>;
48
+ readonly http?: HttpOptions.Input;
49
+ } & (Model extends ImageModel<ImageModelOptions<Model>> ? unknown : never);
57
50
  declare const GeneratedImage_base: Schema.Class<GeneratedImage, Schema.Struct<{
58
51
  readonly mediaType: Schema.String;
59
52
  readonly data: Schema.Union<readonly [Schema.String, Schema.Uint8Array]>;
@@ -69,10 +62,12 @@ declare const ImageResponse_base: Schema.Class<ImageResponse, Schema.Struct<{
69
62
  export declare class ImageResponse extends ImageResponse_base {
70
63
  get image(): GeneratedImage;
71
64
  }
72
- export declare const request: (input: ImageRequest | ImageRequestInput) => ImageRequest;
73
- export declare const generate: (input: ImageRequest | ImageRequestInput) => Effect.Effect<ImageResponse, LLMError, never>;
65
+ export declare function request<const Model extends object>(input: ImageRequestInput<Model>): ImageRequestFor<ImageModelOptions<Model>>;
66
+ export declare function request(input: ImageRequest): ImageRequest;
67
+ export declare function generate<const Model extends object>(input: ImageRequestInput<Model>): Effect.Effect<ImageResponse, LLMError, Service>;
68
+ export declare function generate(input: ImageRequest): Effect.Effect<ImageResponse, LLMError, Service>;
74
69
  export declare const Image: {
75
- readonly request: (input: ImageRequest | ImageRequestInput) => ImageRequest;
76
- readonly generate: (input: ImageRequest | ImageRequestInput) => Effect.Effect<ImageResponse, LLMError, never>;
70
+ readonly request: typeof request;
71
+ readonly generate: typeof generate;
77
72
  };
78
73
  export {};
package/dist/image.js CHANGED
@@ -1,43 +1,34 @@
1
1
  import { Effect, Schema } from "effect";
2
2
  import { HttpOptions, InvalidRequestReason, LLMError, ModelID, ProviderID, ProviderMetadata, Usage } from "./schema";
3
- import { ImageClient } from "./image-client";
3
+ import { ImageClient, Service } from "./image-client";
4
4
  export class ImageModel {
5
5
  id;
6
6
  provider;
7
7
  route;
8
- defaults;
8
+ http;
9
9
  constructor(input) {
10
10
  this.id = input.id;
11
11
  this.provider = input.provider;
12
12
  this.route = input.route;
13
- this.defaults = input.defaults;
13
+ this.http = input.http;
14
14
  }
15
15
  static make(input) {
16
16
  return new ImageModel({
17
17
  id: ModelID.make(input.id),
18
18
  provider: ProviderID.make(input.provider),
19
19
  route: input.route,
20
- defaults: input.defaults,
20
+ http: input.http,
21
21
  });
22
22
  }
23
23
  }
24
24
  export const ImageModelSchema = Schema.declare((value) => value instanceof ImageModel, {
25
25
  expected: "Image.Model",
26
26
  });
27
- export const ImageSize = Schema.Struct({
28
- width: Schema.Int.check(Schema.isGreaterThanOrEqualTo(1)),
29
- height: Schema.Int.check(Schema.isGreaterThanOrEqualTo(1)),
30
- }).annotate({ identifier: "Image.Size" });
31
27
  export class ImageRequest extends Schema.Class("Image.Request")({
32
28
  model: ImageModelSchema,
33
29
  prompt: Schema.String,
34
- count: Schema.optional(Schema.Int.check(Schema.isGreaterThanOrEqualTo(1))),
35
- size: Schema.optional(ImageSize),
36
- aspectRatio: Schema.optional(Schema.String),
37
- seed: Schema.optional(Schema.Number),
38
- providerOptions: Schema.optional(Schema.Record(Schema.String, Schema.Record(Schema.String, Schema.Unknown))),
30
+ options: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)),
39
31
  http: Schema.optional(HttpOptions),
40
- metadata: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)),
41
32
  }) {
42
33
  }
43
34
  export class GeneratedImage extends Schema.Class("Image.Generated")({
@@ -55,22 +46,25 @@ export class ImageResponse extends Schema.Class("Image.Response")({
55
46
  return this.images[0];
56
47
  }
57
48
  }
58
- export const request = (input) => {
49
+ export function request(input) {
59
50
  if (input instanceof ImageRequest)
60
51
  return input;
61
52
  return new ImageRequest({
62
53
  ...input,
54
+ model: input.model,
63
55
  http: input.http === undefined ? undefined : HttpOptions.make(input.http),
64
56
  });
65
- };
66
- export const generate = (input) => Effect.try({
67
- try: () => request(input),
68
- catch: (error) => new LLMError({
69
- module: "Image",
70
- method: "generate",
71
- reason: new InvalidRequestReason({ message: error instanceof Error ? error.message : String(error) }),
72
- }),
73
- }).pipe(Effect.flatMap(ImageClient.generate));
57
+ }
58
+ export function generate(input) {
59
+ return Effect.try({
60
+ try: () => (input instanceof ImageRequest ? input : request(input)),
61
+ catch: (error) => new LLMError({
62
+ module: "Image",
63
+ method: "generate",
64
+ reason: new InvalidRequestReason({ message: error instanceof Error ? error.message : String(error) }),
65
+ }),
66
+ }).pipe(Effect.flatMap((request) => ImageClient.generate(request)));
67
+ }
74
68
  export const Image = {
75
69
  request,
76
70
  generate,
package/dist/index.d.ts CHANGED
@@ -6,8 +6,8 @@ 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, ImageSize } from "./image";
10
- export type { ImageModelDefaults, ImageRequestInput, ImageRoute } from "./image";
9
+ export { GeneratedImage, ImageModel, ImageRequest, ImageResponse } from "./image";
10
+ export type { ImageModelOptions, ImageOptions, ImageRequestFor, ImageRequestInput, ImageRoute } from "./image";
11
11
  export { Image } from "./image";
12
12
  export { Tool, ToolFailure, toDefinitions } from "./tool";
13
13
  export { ToolRuntime } from "./tool-runtime";
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, ImageSize } from "./image";
8
+ export { GeneratedImage, 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";
@@ -70,14 +70,14 @@ export declare const route: Route<{
70
70
  readonly strict?: boolean | undefined;
71
71
  } | {
72
72
  readonly type: "image_generation";
73
- readonly size?: string | undefined;
74
- readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
75
- readonly background?: "auto" | "opaque" | "transparent" | undefined;
76
73
  readonly output_format?: "png" | "jpeg" | "webp" | undefined;
77
74
  readonly output_compression?: number | undefined;
78
75
  readonly action?: "generate" | "auto" | "edit" | undefined;
76
+ readonly background?: "auto" | "opaque" | "transparent" | undefined;
79
77
  readonly input_fidelity?: "low" | "high" | undefined;
80
78
  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,36 +1,30 @@
1
- import { Schema } from "effect";
2
- import { ImageModel, type ImageModelDefaults } from "../image";
1
+ import { ImageModel } from "../image";
3
2
  import { type Definition as AuthDefinition } from "../route/auth";
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 interface OpenAIImageOptions {
7
- readonly quality?: "auto" | "low" | "medium" | "high";
8
- readonly background?: "auto" | "opaque" | "transparent";
9
- readonly moderation?: "auto" | "low";
10
- readonly outputFormat?: "png" | "jpeg" | "webp";
6
+ export type OpenAIImageString<Known extends string> = Known | (string & {});
7
+ export type OpenAIImageOptions = {
8
+ readonly n?: number;
9
+ readonly size?: OpenAIImageString<"auto" | "256x256" | "512x512" | "1024x1024" | "1536x1024" | "1024x1536" | "1792x1024" | "1024x1792">;
10
+ readonly quality?: OpenAIImageString<"auto" | "low" | "medium" | "high" | "standard" | "hd">;
11
+ readonly background?: OpenAIImageString<"auto" | "opaque" | "transparent">;
12
+ readonly moderation?: OpenAIImageString<"auto" | "low">;
13
+ readonly outputFormat?: OpenAIImageString<"png" | "jpeg" | "webp">;
11
14
  readonly outputCompression?: number;
12
- }
13
- declare const OpenAIImageBody: Schema.Struct<{
14
- readonly model: Schema.String;
15
- readonly prompt: Schema.String;
16
- readonly n: Schema.optional<Schema.Int>;
17
- readonly size: Schema.optional<Schema.String>;
18
- readonly quality: Schema.optional<Schema.Literals<readonly ["auto", "low", "medium", "high"]>>;
19
- readonly background: Schema.optional<Schema.Literals<readonly ["auto", "opaque", "transparent"]>>;
20
- readonly moderation: Schema.optional<Schema.Literals<readonly ["auto", "low"]>>;
21
- readonly output_format: Schema.optional<Schema.Literals<readonly ["png", "jpeg", "webp"]>>;
22
- readonly output_compression: Schema.optional<Schema.Int>;
23
- }>;
24
- export type OpenAIImageBody = Schema.Schema.Type<typeof OpenAIImageBody>;
15
+ } & Record<string, unknown>;
16
+ export type OpenAIImageBody = Record<string, unknown> & {
17
+ readonly model: string;
18
+ readonly prompt: string;
19
+ };
25
20
  export interface ModelInput {
26
21
  readonly id: string;
27
22
  readonly auth: AuthDefinition;
28
23
  readonly baseURL?: string;
29
24
  readonly headers?: Record<string, string>;
30
- readonly defaults?: ImageModelDefaults;
25
+ readonly http?: HttpOptions;
31
26
  }
32
- export declare const model: (input: ModelInput) => ImageModel;
27
+ export declare const model: (input: ModelInput) => ImageModel<OpenAIImageOptions>;
33
28
  export declare const OpenAIImages: {
34
- readonly model: (input: ModelInput) => ImageModel;
29
+ readonly model: (input: ModelInput) => ImageModel<OpenAIImageOptions>;
35
30
  };
36
- export {};
@@ -1,24 +1,13 @@
1
1
  import { Effect, Encoding, Schema } from "effect";
2
2
  import { Headers, HttpClientRequest } from "effect/unstable/http";
3
- import { ImageModel, GeneratedImage, ImageResponse, } from "../image";
3
+ import { ImageModel, GeneratedImage, ImageResponse } from "../image";
4
4
  import { Auth } from "../route/auth";
5
- import { InvalidProviderOutputReason, LLMError, Usage, mergeHttpOptions, mergeJsonRecords } from "../schema";
5
+ import { InvalidProviderOutputReason, LLMError, Usage, mergeHttpOptions, mergeJsonRecords, } from "../schema";
6
6
  import { ProviderShared } from "./shared";
7
7
  import { OpenAIImage } from "./utils/openai-image";
8
8
  const ADAPTER = "openai-images";
9
9
  export const DEFAULT_BASE_URL = "https://api.openai.com/v1";
10
10
  export const PATH = "/images/generations";
11
- const OpenAIImageBody = Schema.Struct({
12
- model: Schema.String,
13
- prompt: Schema.String,
14
- n: Schema.optional(Schema.Int.check(Schema.isGreaterThanOrEqualTo(1))),
15
- size: Schema.optional(Schema.String),
16
- quality: Schema.optional(Schema.Literals(["auto", "low", "medium", "high"])),
17
- background: Schema.optional(Schema.Literals(["auto", "opaque", "transparent"])),
18
- moderation: Schema.optional(Schema.Literals(["auto", "low"])),
19
- output_format: Schema.optional(Schema.Literals(["png", "jpeg", "webp"])),
20
- output_compression: Schema.optional(Schema.Int.check(Schema.isBetween({ minimum: 0, maximum: 100 }))),
21
- });
22
11
  const OpenAIImageResponse = Schema.Struct({
23
12
  data: Schema.Array(Schema.Struct({
24
13
  b64_json: Schema.optional(Schema.String),
@@ -34,22 +23,14 @@ const OpenAIImageResponse = Schema.Struct({
34
23
  output_tokens_details: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)),
35
24
  })),
36
25
  });
37
- const providerOptions = (request) => ({
38
- ...request.model.defaults?.providerOptions?.openai,
39
- ...request.providerOptions?.openai,
40
- });
41
- const body = (request) => {
42
- const options = providerOptions(request);
26
+ const nativeOptions = (options) => {
27
+ if (!options)
28
+ return undefined;
29
+ const { outputFormat, outputCompression, ...native } = options;
43
30
  return {
44
- model: request.model.id,
45
- prompt: request.prompt,
46
- n: request.count,
47
- size: request.size === undefined ? undefined : `${request.size.width}x${request.size.height}`,
48
- quality: options.quality,
49
- background: options.background,
50
- moderation: options.moderation,
51
- output_format: options.outputFormat,
52
- output_compression: options.outputCompression,
31
+ output_format: outputFormat,
32
+ output_compression: outputCompression,
33
+ ...native,
53
34
  };
54
35
  };
55
36
  const invalidOutput = (message) => new LLMError({
@@ -64,37 +45,13 @@ const applyQuery = (url, query) => {
64
45
  Object.entries(query).forEach(([key, value]) => next.searchParams.set(key, value));
65
46
  return next.toString();
66
47
  };
67
- const PROTOCOL_BODY_FIELDS = new Set([
68
- "model",
69
- "prompt",
70
- "n",
71
- "size",
72
- "quality",
73
- "background",
74
- "moderation",
75
- "output_format",
76
- "output_compression",
77
- ]);
78
- const bodyWithOverlay = Effect.fn("OpenAIImages.bodyWithOverlay")(function* (imageBody, overlay) {
79
- if (!overlay)
80
- return imageBody;
81
- const reserved = Object.keys(overlay).filter((key) => PROTOCOL_BODY_FIELDS.has(key));
82
- if (reserved.length > 0)
83
- return yield* ProviderShared.invalidRequest(`http.body cannot overlay protocol-owned field(s): ${reserved.join(", ")}`);
84
- return mergeJsonRecords(imageBody, overlay) ?? imageBody;
85
- });
86
48
  export const model = (input) => {
87
49
  const route = {
88
50
  id: ADAPTER,
89
51
  generate: Effect.fn("OpenAIImages.generate")(function* (request, execute) {
90
- if (request.aspectRatio !== undefined)
91
- return yield* ProviderShared.invalidRequest("OpenAI Images does not support the common aspectRatio option");
92
- if (request.seed !== undefined)
93
- return yield* ProviderShared.invalidRequest("OpenAI Images does not support the common seed option");
94
- const requestBody = yield* ProviderShared.validateWith(Schema.decodeUnknownEffect(OpenAIImageBody))(body(request));
95
- const http = mergeHttpOptions(request.model.defaults?.http, request.http);
96
- const overlaidBody = yield* bodyWithOverlay(requestBody, http?.body);
97
- const text = ProviderShared.encodeJson(overlaidBody);
52
+ const http = mergeHttpOptions(request.model.http, request.http);
53
+ const requestBody = mergeJsonRecords({ model: request.model.id, prompt: request.prompt }, nativeOptions(request.options), http?.body);
54
+ const text = ProviderShared.encodeJson(requestBody);
98
55
  const url = applyQuery(`${(input.baseURL ?? DEFAULT_BASE_URL).replace(/\/$/, "")}${PATH}`, http?.query);
99
56
  const headers = yield* Auth.toEffect(input.auth)({
100
57
  request,
@@ -106,7 +63,7 @@ export const model = (input) => {
106
63
  const response = yield* execute(HttpClientRequest.post(url).pipe(HttpClientRequest.setHeaders(headers), HttpClientRequest.bodyText(text, "application/json")));
107
64
  const payload = yield* response.json.pipe(Effect.mapError(() => invalidOutput("Failed to read the OpenAI Images response")));
108
65
  const decoded = yield* Schema.decodeUnknownEffect(OpenAIImageResponse)(payload).pipe(Effect.mapError(() => invalidOutput("OpenAI Images returned an invalid response")));
109
- const format = decoded.output_format ?? providerOptions(request).outputFormat ?? "png";
66
+ const format = decoded.output_format ?? (typeof requestBody.output_format === "string" ? requestBody.output_format : "png");
110
67
  const images = yield* Effect.forEach(decoded.data, (item, index) => {
111
68
  if (item.b64_json)
112
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({
@@ -138,7 +95,7 @@ export const model = (input) => {
138
95
  });
139
96
  }),
140
97
  };
141
- return ImageModel.make({ id: input.id, provider: "openai", route, defaults: input.defaults });
98
+ return ImageModel.make({ id: input.id, provider: "openai", route, http: input.http });
142
99
  };
143
100
  export const OpenAIImages = {
144
101
  model,
@@ -164,14 +164,14 @@ export declare const protocol: Protocol<{
164
164
  readonly strict?: boolean | undefined;
165
165
  } | {
166
166
  readonly type: "image_generation";
167
- readonly size?: string | undefined;
168
- readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
169
- readonly background?: "auto" | "opaque" | "transparent" | undefined;
170
167
  readonly output_format?: "png" | "jpeg" | "webp" | undefined;
171
168
  readonly output_compression?: number | undefined;
172
169
  readonly action?: "generate" | "auto" | "edit" | undefined;
170
+ readonly background?: "auto" | "opaque" | "transparent" | undefined;
173
171
  readonly input_fidelity?: "low" | "high" | undefined;
174
172
  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<{
@@ -316,14 +316,14 @@ export declare const httpTransport: HttpTransport.HttpJsonTransport<{
316
316
  readonly strict?: boolean | undefined;
317
317
  } | {
318
318
  readonly type: "image_generation";
319
- readonly size?: string | undefined;
320
- readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
321
- readonly background?: "auto" | "opaque" | "transparent" | undefined;
322
319
  readonly output_format?: "png" | "jpeg" | "webp" | undefined;
323
320
  readonly output_compression?: number | undefined;
324
321
  readonly action?: "generate" | "auto" | "edit" | undefined;
322
+ readonly background?: "auto" | "opaque" | "transparent" | undefined;
325
323
  readonly input_fidelity?: "low" | "high" | undefined;
326
324
  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<{
@@ -405,14 +405,14 @@ export declare const route: Route<{
405
405
  readonly strict?: boolean | undefined;
406
406
  } | {
407
407
  readonly type: "image_generation";
408
- readonly size?: string | undefined;
409
- readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
410
- readonly background?: "auto" | "opaque" | "transparent" | undefined;
411
408
  readonly output_format?: "png" | "jpeg" | "webp" | undefined;
412
409
  readonly output_compression?: number | undefined;
413
410
  readonly action?: "generate" | "auto" | "edit" | undefined;
411
+ readonly background?: "auto" | "opaque" | "transparent" | undefined;
414
412
  readonly input_fidelity?: "low" | "high" | undefined;
415
413
  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<{
@@ -494,14 +494,14 @@ export declare const webSocketTransport: import("../route/transport/websocket").
494
494
  readonly strict?: boolean | undefined;
495
495
  } | {
496
496
  readonly type: "image_generation";
497
- readonly size?: string | undefined;
498
- readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
499
- readonly background?: "auto" | "opaque" | "transparent" | undefined;
500
497
  readonly output_format?: "png" | "jpeg" | "webp" | undefined;
501
498
  readonly output_compression?: number | undefined;
502
499
  readonly action?: "generate" | "auto" | "edit" | undefined;
500
+ readonly background?: "auto" | "opaque" | "transparent" | undefined;
503
501
  readonly input_fidelity?: "low" | "high" | undefined;
504
502
  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<{
@@ -583,14 +583,14 @@ export declare const webSocketTransport: import("../route/transport/websocket").
583
583
  readonly strict?: boolean | undefined;
584
584
  } | {
585
585
  readonly type: "image_generation";
586
- readonly size?: string | undefined;
587
- readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
588
- readonly background?: "auto" | "opaque" | "transparent" | undefined;
589
586
  readonly output_format?: "png" | "jpeg" | "webp" | undefined;
590
587
  readonly output_compression?: number | undefined;
591
588
  readonly action?: "generate" | "auto" | "edit" | undefined;
589
+ readonly background?: "auto" | "opaque" | "transparent" | undefined;
592
590
  readonly input_fidelity?: "low" | "high" | undefined;
593
591
  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<{
@@ -672,14 +672,14 @@ export declare const webSocketRoute: Route<{
672
672
  readonly strict?: boolean | undefined;
673
673
  } | {
674
674
  readonly type: "image_generation";
675
- readonly size?: string | undefined;
676
- readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
677
- readonly background?: "auto" | "opaque" | "transparent" | undefined;
678
675
  readonly output_format?: "png" | "jpeg" | "webp" | undefined;
679
676
  readonly output_compression?: number | undefined;
680
677
  readonly action?: "generate" | "auto" | "edit" | undefined;
678
+ readonly background?: "auto" | "opaque" | "transparent" | undefined;
681
679
  readonly input_fidelity?: "low" | "high" | undefined;
682
680
  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<{
@@ -149,14 +149,14 @@ export declare const routes: (RouteDef<{
149
149
  readonly strict?: boolean | undefined;
150
150
  } | {
151
151
  readonly type: "image_generation";
152
- readonly size?: string | undefined;
153
- readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
154
- readonly background?: "auto" | "opaque" | "transparent" | undefined;
155
152
  readonly output_format?: "png" | "jpeg" | "webp" | undefined;
156
153
  readonly output_compression?: number | undefined;
157
154
  readonly action?: "generate" | "auto" | "edit" | undefined;
155
+ readonly background?: "auto" | "opaque" | "transparent" | undefined;
158
156
  readonly input_fidelity?: "low" | "high" | undefined;
159
157
  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<{
@@ -137,14 +137,14 @@ export declare const routes: (import("../route").Route<{
137
137
  readonly strict?: boolean | undefined;
138
138
  } | {
139
139
  readonly type: "image_generation";
140
- readonly size?: string | undefined;
141
- readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
142
- readonly background?: "auto" | "opaque" | "transparent" | undefined;
143
140
  readonly output_format?: "png" | "jpeg" | "webp" | undefined;
144
141
  readonly output_compression?: number | undefined;
145
142
  readonly action?: "generate" | "auto" | "edit" | undefined;
143
+ readonly background?: "auto" | "opaque" | "transparent" | undefined;
146
144
  readonly input_fidelity?: "low" | "high" | undefined;
147
145
  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<{
@@ -81,14 +81,14 @@ export declare const routes: import("../route").Route<{
81
81
  readonly strict?: boolean | undefined;
82
82
  } | {
83
83
  readonly type: "image_generation";
84
- readonly size?: string | undefined;
85
- readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
86
- readonly background?: "auto" | "opaque" | "transparent" | undefined;
87
84
  readonly output_format?: "png" | "jpeg" | "webp" | undefined;
88
85
  readonly output_compression?: number | undefined;
89
86
  readonly action?: "generate" | "auto" | "edit" | undefined;
87
+ readonly background?: "auto" | "opaque" | "transparent" | undefined;
90
88
  readonly input_fidelity?: "low" | "high" | undefined;
91
89
  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<{
@@ -79,14 +79,14 @@ export declare const routes: import("../route").Route<{
79
79
  readonly strict?: boolean | undefined;
80
80
  } | {
81
81
  readonly type: "image_generation";
82
- readonly size?: string | undefined;
83
- readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
84
- readonly background?: "auto" | "opaque" | "transparent" | undefined;
85
82
  readonly output_format?: "png" | "jpeg" | "webp" | undefined;
86
83
  readonly output_compression?: number | undefined;
87
84
  readonly action?: "generate" | "auto" | "edit" | undefined;
85
+ readonly background?: "auto" | "opaque" | "transparent" | undefined;
88
86
  readonly input_fidelity?: "low" | "high" | undefined;
89
87
  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<{
@@ -3,7 +3,7 @@ import type { Route, RouteDefaultsInput } from "../route/client";
3
3
  import type { ProviderPackage } from "../provider-package";
4
4
  import { ToolDefinition, type ModelID } from "../schema";
5
5
  import { type OpenAIProviderOptionsInput } from "./openai-options";
6
- import { type OpenAIImageOptions } from "../protocols/openai-images";
6
+ import { type OpenAIImageString } from "../protocols/openai-images";
7
7
  export type { OpenAIOptionsInput, OpenAIResponseIncludable } from "./openai-options";
8
8
  export type { OpenAIImageOptions } from "../protocols/openai-images";
9
9
  export declare const id: string & import("effect/Brand").Brand<"LLM.ProviderID">;
@@ -135,14 +135,14 @@ export declare const routes: (Route<{
135
135
  readonly strict?: boolean | undefined;
136
136
  } | {
137
137
  readonly type: "image_generation";
138
- readonly size?: string | undefined;
139
- readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
140
- readonly background?: "auto" | "opaque" | "transparent" | undefined;
141
138
  readonly output_format?: "png" | "jpeg" | "webp" | undefined;
142
139
  readonly output_compression?: number | undefined;
143
140
  readonly action?: "generate" | "auto" | "edit" | undefined;
141
+ readonly background?: "auto" | "opaque" | "transparent" | undefined;
144
142
  readonly input_fidelity?: "low" | "high" | undefined;
145
143
  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<{
@@ -223,14 +223,14 @@ export declare const routes: (Route<{
223
223
  readonly strict?: boolean | undefined;
224
224
  } | {
225
225
  readonly type: "image_generation";
226
- readonly size?: string | undefined;
227
- readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
228
- readonly background?: "auto" | "opaque" | "transparent" | undefined;
229
226
  readonly output_format?: "png" | "jpeg" | "webp" | undefined;
230
227
  readonly output_compression?: number | undefined;
231
228
  readonly action?: "generate" | "auto" | "edit" | undefined;
229
+ readonly background?: "auto" | "opaque" | "transparent" | undefined;
232
230
  readonly input_fidelity?: "low" | "high" | undefined;
233
231
  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<{
@@ -251,20 +251,16 @@ export type Config = RouteDefaultsInput & ProviderAuthOption<"optional"> & {
251
251
  readonly baseURL?: string;
252
252
  readonly queryParams?: Record<string, string>;
253
253
  readonly providerOptions?: OpenAIProviderOptionsInput;
254
- readonly image?: ImageConfig;
255
254
  };
256
- export interface ImageConfig {
257
- readonly providerOptions?: OpenAIImageOptions;
258
- }
259
255
  export interface ImageGenerationOptions {
260
- readonly action?: "auto" | "generate" | "edit";
261
- readonly background?: "auto" | "opaque" | "transparent";
262
- readonly inputFidelity?: "low" | "high";
256
+ readonly action?: OpenAIImageString<"auto" | "generate" | "edit">;
257
+ readonly background?: OpenAIImageString<"auto" | "opaque" | "transparent">;
258
+ readonly inputFidelity?: OpenAIImageString<"low" | "high">;
263
259
  readonly outputCompression?: number;
264
- readonly outputFormat?: "png" | "jpeg" | "webp";
260
+ readonly outputFormat?: OpenAIImageString<"png" | "jpeg" | "webp">;
265
261
  readonly partialImages?: number;
266
- readonly quality?: "auto" | "low" | "medium" | "high";
267
- readonly size?: string;
262
+ readonly quality?: OpenAIImageString<"auto" | "low" | "medium" | "high" | "standard" | "hd">;
263
+ readonly size?: OpenAIImageString<"auto" | "256x256" | "512x512" | "1024x1024" | "1536x1024" | "1024x1536" | "1792x1024" | "1024x1792">;
268
264
  }
269
265
  export declare const imageGeneration: (options?: ImageGenerationOptions) => ToolDefinition;
270
266
  export interface Settings extends ProviderPackage.Settings {
@@ -282,7 +278,7 @@ export declare const configure: (input?: Config) => {
282
278
  responses: (id: string | ModelID) => import("..").Model;
283
279
  responsesWebSocket: (id: string | ModelID) => import("..").Model;
284
280
  chat: (id: string | ModelID) => import("..").Model;
285
- image: (modelID: string | ModelID) => import("..").ImageModel;
281
+ image: (modelID: string | ModelID) => import("..").ImageModel<import("./openai").OpenAIImageOptions>;
286
282
  configure: (input?: Config) => /*elided*/ any;
287
283
  };
288
284
  export declare const provider: {
@@ -291,14 +287,14 @@ export declare const provider: {
291
287
  responses: (id: string | ModelID) => import("..").Model;
292
288
  responsesWebSocket: (id: string | ModelID) => import("..").Model;
293
289
  chat: (id: string | ModelID) => import("..").Model;
294
- image: (modelID: string | ModelID) => import("..").ImageModel;
290
+ image: (modelID: string | ModelID) => import("..").ImageModel<import("./openai").OpenAIImageOptions>;
295
291
  configure: (input?: Config) => {
296
292
  id: string & import("effect/Brand").Brand<"LLM.ProviderID">;
297
293
  model: (id: string | ModelID) => import("..").Model;
298
294
  responses: (id: string | ModelID) => import("..").Model;
299
295
  responsesWebSocket: (id: string | ModelID) => import("..").Model;
300
296
  chat: (id: string | ModelID) => import("..").Model;
301
- image: (modelID: string | ModelID) => import("..").ImageModel;
297
+ image: (modelID: string | ModelID) => import("..").ImageModel<import("./openai").OpenAIImageOptions>;
302
298
  configure: /*elided*/ any;
303
299
  };
304
300
  };
@@ -307,4 +303,4 @@ export declare const chatModel: ProviderPackage.Definition<Settings>["model"];
307
303
  export declare const responses: (id: string | ModelID) => import("..").Model;
308
304
  export declare const responsesWebSocket: (id: string | ModelID) => import("..").Model;
309
305
  export declare const chat: (id: string | ModelID) => import("..").Model;
310
- export declare const image: (modelID: string | ModelID) => import("..").ImageModel;
306
+ export declare const image: (modelID: string | ModelID) => import("..").ImageModel<import("./openai").OpenAIImageOptions>;
@@ -26,7 +26,7 @@ export const imageGeneration = (options = {}) => ToolDefinition.make({
26
26
  });
27
27
  const auth = (options) => AuthOptions.bearer(options, "OPENAI_API_KEY");
28
28
  const defaults = (input) => {
29
- const { apiKey: _, auth: _auth, baseURL: _baseURL, queryParams: _queryParams, image: _image, ...rest } = input;
29
+ const { apiKey: _, auth: _auth, baseURL: _baseURL, queryParams: _queryParams, ...rest } = input;
30
30
  return rest;
31
31
  };
32
32
  const configuredRoute = (route, input) => route.with({
@@ -46,10 +46,7 @@ export const configure = (input = {}) => {
46
46
  auth: auth(input),
47
47
  baseURL: input.baseURL,
48
48
  headers: input.headers,
49
- defaults: {
50
- providerOptions: input.image?.providerOptions === undefined ? undefined : { openai: { ...input.image.providerOptions } },
51
- http: mergeHttpOptions(input.http === undefined ? undefined : HttpOptions.make(input.http), input.queryParams === undefined ? undefined : new HttpOptions({ query: input.queryParams })),
52
- },
49
+ http: mergeHttpOptions(input.http === undefined ? undefined : HttpOptions.make(input.http), input.queryParams === undefined ? undefined : new HttpOptions({ query: input.queryParams })),
53
50
  });
54
51
  return {
55
52
  id,
@@ -133,14 +133,14 @@ export declare const routes: (import("../route").Route<{
133
133
  readonly strict?: boolean | undefined;
134
134
  } | {
135
135
  readonly type: "image_generation";
136
- readonly size?: string | undefined;
137
- readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
138
- readonly background?: "auto" | "opaque" | "transparent" | undefined;
139
136
  readonly output_format?: "png" | "jpeg" | "webp" | undefined;
140
137
  readonly output_compression?: number | undefined;
141
138
  readonly action?: "generate" | "auto" | "edit" | undefined;
139
+ readonly background?: "auto" | "opaque" | "transparent" | undefined;
142
140
  readonly input_fidelity?: "low" | "high" | undefined;
143
141
  readonly partial_images?: number | undefined;
142
+ readonly quality?: "low" | "medium" | "high" | "auto" | undefined;
143
+ readonly size?: string | undefined;
144
144
  })[] | undefined;
145
145
  readonly temperature?: number | undefined;
146
146
  readonly tool_choice?: "required" | "none" | "auto" | import("effect/Schema").Struct.ReadonlySide<{
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
- "version": "0.0.0-next-15898",
3
+ "version": "0.0.0-next-15903",
4
4
  "name": "@opencode-ai/ai",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "scripts": {
8
8
  "setup:recording-env": "bun run script/setup-recording-env.ts",
9
9
  "test": "bun test --timeout 30000 --only-failures",
10
- "typecheck": "tsgo --noEmit",
10
+ "typecheck": "tsgo --noEmit && tsgo --noEmit -p tsconfig.types.json",
11
11
  "build": "tsc -p tsconfig.build.json"
12
12
  },
13
13
  "files": [
@@ -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-15898",
29
+ "@opencode-ai/http-recorder": "0.0.0-next-15903",
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-15898",
38
+ "@opencode-ai/schema": "0.0.0-next-15903",
39
39
  "aws4fetch": "1.0.20",
40
40
  "effect": "4.0.0-beta.98",
41
41
  "google-auth-library": "10.5.0"