@opencode/ai 0.0.0-dev-20061 → 0.0.0-dev-20065

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.
Files changed (48) hide show
  1. package/README.md +56 -1
  2. package/dist/image-client.d.ts +14 -7
  3. package/dist/image-client.js +24 -8
  4. package/dist/image.d.ts +392 -12
  5. package/dist/image.js +41 -9
  6. package/dist/media-model.d.ts +3 -1
  7. package/dist/media-model.js +10 -0
  8. package/dist/promise.d.ts +33 -5
  9. package/dist/promise.js +4 -2
  10. package/dist/protocols/bfl-images.d.ts +32 -0
  11. package/dist/protocols/bfl-images.js +153 -0
  12. package/dist/protocols/fal-images.d.ts +24 -0
  13. package/dist/protocols/fal-images.js +114 -0
  14. package/dist/protocols/fal-video.d.ts +0 -10
  15. package/dist/protocols/fal-video.js +8 -59
  16. package/dist/protocols/openai-images.d.ts +124 -3
  17. package/dist/protocols/openai-images.js +102 -35
  18. package/dist/protocols/openai-responses.d.ts +5 -5
  19. package/dist/protocols/replicate-images.d.ts +28 -0
  20. package/dist/protocols/replicate-images.js +133 -0
  21. package/dist/protocols/stability-images.d.ts +38 -0
  22. package/dist/protocols/stability-images.js +148 -0
  23. package/dist/protocols/utils/fal-queue.d.ts +28 -0
  24. package/dist/protocols/utils/fal-queue.js +69 -0
  25. package/dist/protocols/utils/media-input.d.ts +4 -0
  26. package/dist/protocols/utils/media-input.js +4 -0
  27. package/dist/provider-error.js +3 -0
  28. package/dist/providers/azure.d.ts +2 -2
  29. package/dist/providers/black-forest-labs.d.ts +25 -0
  30. package/dist/providers/black-forest-labs.js +28 -0
  31. package/dist/providers/cloudflare-ai-gateway.d.ts +2 -2
  32. package/dist/providers/fal.d.ts +5 -0
  33. package/dist/providers/fal.js +8 -4
  34. package/dist/providers/index.d.ts +3 -0
  35. package/dist/providers/index.js +3 -0
  36. package/dist/providers/openai.d.ts +1 -1
  37. package/dist/providers/replicate.d.ts +25 -0
  38. package/dist/providers/replicate.js +22 -0
  39. package/dist/providers/stability.d.ts +28 -0
  40. package/dist/providers/stability.js +23 -0
  41. package/dist/route/media-protocol.d.ts +3 -1
  42. package/dist/route/media.d.ts +13 -2
  43. package/dist/route/media.js +37 -2
  44. package/dist/transcription-client.d.ts +1 -1
  45. package/dist/transcription-client.js +10 -35
  46. package/dist/transcription.d.ts +2 -2
  47. package/dist/transcription.js +2 -9
  48. package/package.json +3 -3
package/README.md CHANGED
@@ -584,6 +584,61 @@ Z.ai does not include trustworthy MIME metadata for output URLs, so generated im
584
584
  `application/octet-stream` until materialized. Output URLs expire after 30 days; call `asset.materialize()` and
585
585
  persist the bytes promptly if they must remain available.
586
586
 
587
+ ### Partial images
588
+
589
+ OpenAI's GPT image models stream previews. `Image.stream` sends `stream: true` with `partialImages` (0–3, default 2)
590
+ and emits `image-partial` events before each final `image`; `Image.generate` keeps the plain JSON request.
591
+ `dall-e-*` models do not stream and fail typed:
592
+
593
+ ```ts
594
+ yield *
595
+ Image.stream({
596
+ model: openai.image("gpt-image-2"),
597
+ prompt: "A lighthouse at dusk",
598
+ providerOptions: { partialImages: 2 },
599
+ }).pipe(Stream.runForEach((event) => (ImageEvent.is.imagePartial(event) ? showPreview(event.image) : Effect.void)))
600
+ ```
601
+
602
+ The provider may send fewer previews than requested when the final image is ready first.
603
+
604
+ ### Queued image providers
605
+
606
+ Black Forest Labs, fal, Replicate, and Stability's creative upscaler are submit-then-poll routes. `Image.generate`
607
+ and `Image.stream` poll for you (pass `{ poll }` to tune the interval and timeout); `Image.start` returns a
608
+ `Generation` whose `token` is serializable JSON for `Image.resume` in another process:
609
+
610
+ ```ts
611
+ import { BlackForestLabs, Stability } from "@opencode/ai/providers"
612
+
613
+ const bfl = BlackForestLabs.configure({ apiKey: process.env.BFL_API_KEY })
614
+
615
+ const generation = yield * Image.start({ model: bfl.image("flux-2-pro"), prompt, size: "1024x768" })
616
+ persist(generation.token)
617
+
618
+ const resumed = yield * Image.resume(bfl.image("flux-2-pro"), loadToken())
619
+ const response = yield * resumed.await({ poll: { interval: "2 seconds" } })
620
+ ```
621
+
622
+ - **Black Forest Labs** — results are downloaded before returning, because `result.sample` expires in 10 minutes.
623
+ - **Replicate** — inputs are model-defined, so only `prompt` lowers: sizing, count, seed, format, and files go in
624
+ `providerOptions` under the model's names, with files as `Media.Asset` (data URLs up to 256 KB, larger by URL).
625
+ Outputs are removed an hour after the prediction completes. `Prefer: wait=60` in `headers` or `http.headers` holds
626
+ the submission open so a fast prediction costs one result read.
627
+ - **Stability** — `stability.image(id)` generates inline; `stability.upscale()` is the creative upscaler, queued:
628
+
629
+ ```ts
630
+ const stability = Stability.configure({ apiKey: process.env.STABILITY_API_KEY })
631
+ const upscaled =
632
+ yield *
633
+ Image.generate(
634
+ { model: stability.upscale(), prompt: "A lighthouse", images: [yield * Media.file("./small.png")] },
635
+ { poll: { interval: "5 seconds" } },
636
+ )
637
+ ```
638
+
639
+ Imagen is not available: Google shut it down on the Gemini API, and Vertex discontinued the Imagen 4 models on
640
+ 2026-06-30. `Google.image(...)` uses Gemini-native image models.
641
+
587
642
  Conversational image generation remains part of the LLM interaction. OpenAI Responses exposes it through its hosted image tool:
588
643
 
589
644
  ```ts
@@ -836,7 +891,7 @@ const transcript = await generation.await({ poll: { interval: 3_000 } })
836
891
  - **`Message.user(...)` / `Message.assistant(...)` / `Message.tool(...)`** — message constructors from the canonical schema model.
837
892
  - **`LanguageModel.make(...)` / `ToolCallPart.make(...)` / `ToolResultPart.make(...)` / `ToolDefinition.make(...)`** — model and tool-related constructors from the canonical schema model.
838
893
  - **`LLMEvent.is.*`** — typed guards (`is.textDelta`, `is.toolCall`, `is.finish`, …) for filtering streams.
839
- - **`Image.request` / `Image.generate` / `Image.stream`** — generate images through a provider-neutral image request and response model.
894
+ - **`Image.request` / `generate` / `stream` / `start` / `resume`** — images over inline, streaming (partial previews), and queued routes through a provider-neutral request and response model.
840
895
  - **`ImageClient`** — Effect service and layer for image execution, parallel to `LLMClient`.
841
896
  - **`Media`** — the shared asset type (`Media.Asset`, `Media.Source`) and constructors used by messages, tool results, and media requests.
842
897
  - **`Generation`** — provider-neutral handle for an in-flight media generation (`await`, `refresh`, `cancel`, `events`) used by queued media routes.
@@ -1,21 +1,28 @@
1
1
  import { Context, Effect, Layer, Stream } from "effect";
2
+ import type { AwaitOptions, Generation } from "./generation.js";
2
3
  import { RequestExecutor } from "./route/executor.js";
3
4
  import type { AIError } from "./schema/index.js";
4
- import { type ImageEvent, type ImageOptions, type ImageRequestFor, type ImageResponse } from "./image.js";
5
+ import { type ImageEvent, type ImageModel, type ImageOptions, type ImageRequestFor, type ImageResponse } from "./image.js";
5
6
  export interface Interface {
6
- readonly generate: <Options extends ImageOptions>(request: ImageRequestFor<Options>) => Effect.Effect<ImageResponse, AIError>;
7
- readonly stream: <Options extends ImageOptions>(request: ImageRequestFor<Options>) => Stream.Stream<ImageEvent, AIError>;
7
+ readonly generate: <Options extends ImageOptions>(request: ImageRequestFor<Options>, options?: AwaitOptions) => Effect.Effect<ImageResponse, AIError>;
8
+ readonly stream: <Options extends ImageOptions>(request: ImageRequestFor<Options>, options?: AwaitOptions) => Stream.Stream<ImageEvent, AIError>;
9
+ readonly start: <Options extends ImageOptions>(request: ImageRequestFor<Options>) => Effect.Effect<Generation<ImageResponse>, AIError>;
10
+ readonly resume: <Options extends ImageOptions>(model: ImageModel<Options>, token: unknown) => Effect.Effect<Generation<ImageResponse>, AIError>;
8
11
  }
9
12
  declare const Service_base: Context.ServiceClass<Service, "@opencode/ImageClient", Interface>;
10
13
  export declare class Service extends Service_base {
11
14
  }
12
- export declare const generate: <Options extends ImageOptions>(request: ImageRequestFor<Options>) => Effect.Effect<ImageResponse, AIError, Service>;
13
- export declare const stream: <Options extends ImageOptions>(request: ImageRequestFor<Options>) => Stream.Stream<ImageEvent, AIError, Service>;
15
+ export declare const generate: <Options extends ImageOptions>(request: ImageRequestFor<Options>, options?: AwaitOptions) => Effect.Effect<ImageResponse, AIError, Service>;
16
+ export declare const stream: <Options extends ImageOptions>(request: ImageRequestFor<Options>, options?: AwaitOptions) => Stream.Stream<ImageEvent, AIError, Service>;
17
+ export declare const start: <Options extends ImageOptions>(request: ImageRequestFor<Options>) => Effect.Effect<Generation<ImageResponse>, AIError, Service>;
18
+ export declare const resume: <Options extends ImageOptions>(model: ImageModel<Options>, token: unknown) => Effect.Effect<Generation<ImageResponse>, AIError, Service>;
14
19
  export declare const layer: Layer.Layer<Service, never, RequestExecutor.Service>;
15
20
  export declare const ImageClient: {
16
21
  readonly Service: typeof Service;
17
22
  readonly layer: Layer.Layer<Service, never, RequestExecutor.Service>;
18
- readonly generate: <Options extends ImageOptions>(request: ImageRequestFor<Options>) => Effect.Effect<ImageResponse, AIError, Service>;
19
- readonly stream: <Options extends ImageOptions>(request: ImageRequestFor<Options>) => Stream.Stream<ImageEvent, AIError, Service>;
23
+ readonly generate: <Options extends ImageOptions>(request: ImageRequestFor<Options>, options?: AwaitOptions) => Effect.Effect<ImageResponse, AIError, Service>;
24
+ readonly stream: <Options extends ImageOptions>(request: ImageRequestFor<Options>, options?: AwaitOptions) => Stream.Stream<ImageEvent, AIError, Service>;
25
+ readonly start: <Options extends ImageOptions>(request: ImageRequestFor<Options>) => Effect.Effect<Generation<ImageResponse>, AIError, Service>;
26
+ readonly resume: <Options extends ImageOptions>(model: ImageModel<Options>, token: unknown) => Effect.Effect<Generation<ImageResponse>, AIError, Service>;
20
27
  };
21
28
  export {};
@@ -1,23 +1,37 @@
1
1
  import { Context, Effect, Layer, Stream } from "effect";
2
2
  import { RequestExecutor } from "./route/executor.js";
3
+ import { MediaRoute } from "./route/media.js";
3
4
  import { responseEvents, } from "./image.js";
4
5
  export class Service extends Context.Service()("@opencode/ImageClient") {
5
6
  }
6
- export const generate = (request) => Effect.gen(function* () {
7
+ export const generate = (request, options) => Effect.gen(function* () {
7
8
  const client = yield* Service;
8
- return yield* client.generate(request);
9
+ return yield* client.generate(request, options);
9
10
  });
10
- export const stream = (request) => Stream.unwrap(Effect.gen(function* () {
11
+ export const stream = (request, options) => Stream.unwrap(Effect.gen(function* () {
11
12
  const client = yield* Service;
12
- return client.stream(request);
13
+ return client.stream(request, options);
13
14
  }));
15
+ export const start = (request) => Effect.gen(function* () {
16
+ const client = yield* Service;
17
+ return yield* client.start(request);
18
+ });
19
+ export const resume = (model, token) => Effect.gen(function* () {
20
+ const client = yield* Service;
21
+ return yield* client.resume(model, token);
22
+ });
14
23
  export const layer = Layer.effect(Service, Effect.gen(function* () {
15
24
  const executor = yield* RequestExecutor.Service;
16
- const generate = (request) => request.model.route.generate(request, executor.execute);
25
+ const dispatch = MediaRoute.dispatch({
26
+ modality: "image",
27
+ execute: executor.execute,
28
+ responseEvents,
29
+ });
17
30
  return Service.of({
18
- generate,
19
- // Inline routes have no partial frames yet; the stream is the completed response expanded into events.
20
- stream: (request) => Stream.fromIterableEffect(Effect.map(generate(request), responseEvents)),
31
+ start: (request) => dispatch.start(request.model.route, request),
32
+ resume: (model, token) => dispatch.resume(model.route, model, token),
33
+ generate: (request, options) => dispatch.generate(request.model.route, request, options),
34
+ stream: (request, options) => dispatch.stream(request.model.route, request, options),
21
35
  });
22
36
  }));
23
37
  export const ImageClient = {
@@ -25,4 +39,6 @@ export const ImageClient = {
25
39
  layer,
26
40
  generate,
27
41
  stream,
42
+ start,
43
+ resume,
28
44
  };