@opencode/ai 2.0.16 → 2.0.17

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 (185) hide show
  1. package/README.md +187 -129
  2. package/dist/ai-client.d.ts +8 -0
  3. package/dist/ai-client.js +12 -0
  4. package/dist/experimental/evaluation-client.d.ts +3 -3
  5. package/dist/experimental/evaluation-client.js +1 -1
  6. package/dist/experimental/evaluation.js +1 -1
  7. package/dist/generation.d.ts +6 -10
  8. package/dist/generation.js +17 -20
  9. package/dist/image-client.d.ts +59 -24
  10. package/dist/image-client.js +16 -40
  11. package/dist/image.d.ts +15 -27
  12. package/dist/image.js +4 -15
  13. package/dist/index.d.ts +2 -1
  14. package/dist/index.js +1 -0
  15. package/dist/llm.d.ts +7 -5
  16. package/dist/llm.js +10 -4
  17. package/dist/media-client.d.ts +30 -0
  18. package/dist/media-client.js +51 -0
  19. package/dist/media-model.d.ts +9 -10
  20. package/dist/media-model.js +10 -12
  21. package/dist/media.js +2 -2
  22. package/dist/promise.d.ts +68 -30
  23. package/dist/promise.js +52 -31
  24. package/dist/protocols/alibaba-chat.js +4 -1
  25. package/dist/protocols/alibaba-messages.d.ts +1 -1
  26. package/dist/protocols/alibaba-messages.js +6 -4
  27. package/dist/protocols/anthropic-messages.d.ts +34 -34
  28. package/dist/protocols/anthropic-messages.js +14 -8
  29. package/dist/protocols/assemblyai-transcription.d.ts +2 -1
  30. package/dist/protocols/assemblyai-transcription.js +14 -16
  31. package/dist/protocols/bedrock-converse.d.ts +7 -0
  32. package/dist/protocols/bedrock-converse.js +37 -11
  33. package/dist/protocols/bfl-images.d.ts +7 -1
  34. package/dist/protocols/bfl-images.js +36 -36
  35. package/dist/protocols/cartesia-speech.d.ts +18 -2
  36. package/dist/protocols/cartesia-speech.js +10 -16
  37. package/dist/protocols/deepgram-speech.d.ts +19 -3
  38. package/dist/protocols/deepgram-speech.js +11 -12
  39. package/dist/protocols/deepgram-transcription.d.ts +2 -1
  40. package/dist/protocols/deepgram-transcription.js +9 -13
  41. package/dist/protocols/elevenlabs-speech.d.ts +19 -3
  42. package/dist/protocols/elevenlabs-speech.js +9 -13
  43. package/dist/protocols/fal-images.d.ts +2 -1
  44. package/dist/protocols/fal-images.js +30 -40
  45. package/dist/protocols/fal-video.d.ts +2 -2
  46. package/dist/protocols/fal-video.js +9 -24
  47. package/dist/protocols/gemini.d.ts +13 -13
  48. package/dist/protocols/gemini.js +16 -7
  49. package/dist/protocols/google-images.d.ts +3 -3
  50. package/dist/protocols/google-images.js +11 -21
  51. package/dist/protocols/google-speech.d.ts +16 -0
  52. package/dist/protocols/google-speech.js +20 -16
  53. package/dist/protocols/google-transcription.d.ts +2 -1
  54. package/dist/protocols/google-transcription.js +8 -20
  55. package/dist/protocols/google-video.d.ts +2 -2
  56. package/dist/protocols/google-video.js +14 -30
  57. package/dist/protocols/meta-images.d.ts +3 -4
  58. package/dist/protocols/meta-images.js +12 -21
  59. package/dist/protocols/meta-messages.d.ts +5 -5
  60. package/dist/protocols/meta-responses.js +6 -4
  61. package/dist/protocols/open-responses.d.ts +4 -3
  62. package/dist/protocols/open-responses.js +4 -8
  63. package/dist/protocols/openai-chat.js +3 -4
  64. package/dist/protocols/openai-images.d.ts +7 -5
  65. package/dist/protocols/openai-images.js +65 -66
  66. package/dist/protocols/openai-responses.d.ts +10 -10
  67. package/dist/protocols/openai-responses.js +31 -30
  68. package/dist/protocols/openai-speech.d.ts +21 -1
  69. package/dist/protocols/openai-speech.js +11 -12
  70. package/dist/protocols/openai-transcription.d.ts +4 -0
  71. package/dist/protocols/openai-transcription.js +44 -32
  72. package/dist/protocols/replicate-images.js +11 -17
  73. package/dist/protocols/runway-video.d.ts +3 -3
  74. package/dist/protocols/runway-video.js +11 -17
  75. package/dist/protocols/shared.d.ts +11 -17
  76. package/dist/protocols/shared.js +11 -40
  77. package/dist/protocols/stability-images.d.ts +2 -1
  78. package/dist/protocols/stability-images.js +24 -36
  79. package/dist/protocols/utils/fal-queue.d.ts +1 -3
  80. package/dist/protocols/utils/fal-queue.js +4 -6
  81. package/dist/protocols/utils/media-input.d.ts +15 -1
  82. package/dist/protocols/utils/media-input.js +22 -0
  83. package/dist/protocols/utils/responses-checkpoint.js +3 -7
  84. package/dist/protocols/utils/responses-compaction.d.ts +3 -1
  85. package/dist/protocols/utils/responses-compaction.js +16 -3
  86. package/dist/protocols/utils/speech-stream.d.ts +3 -3
  87. package/dist/protocols/utils/speech-stream.js +1 -4
  88. package/dist/protocols/utils/tool-schema.d.ts +2 -2
  89. package/dist/protocols/utils/tool-schema.js +29 -9
  90. package/dist/protocols/xai-images.d.ts +4 -4
  91. package/dist/protocols/xai-images.js +14 -29
  92. package/dist/protocols/xai-responses.js +1 -1
  93. package/dist/protocols/xai-video.js +12 -18
  94. package/dist/protocols/zai-images.d.ts +2 -2
  95. package/dist/protocols/zai-images.js +11 -14
  96. package/dist/protocols/zai-messages.d.ts +1 -1
  97. package/dist/provider-error.js +7 -1
  98. package/dist/providers/alibaba.d.ts +1 -1
  99. package/dist/providers/amazon-bedrock.d.ts +2 -0
  100. package/dist/providers/amazon-bedrock.js +1 -0
  101. package/dist/providers/anthropic-compatible.d.ts +5 -5
  102. package/dist/providers/anthropic.d.ts +5 -5
  103. package/dist/providers/assemblyai.d.ts +1 -1
  104. package/dist/providers/assemblyai.js +5 -10
  105. package/dist/providers/azure.d.ts +4 -4
  106. package/dist/providers/azure.js +2 -2
  107. package/dist/providers/black-forest-labs.d.ts +1 -1
  108. package/dist/providers/black-forest-labs.js +5 -10
  109. package/dist/providers/cartesia.d.ts +1 -1
  110. package/dist/providers/cartesia.js +5 -10
  111. package/dist/providers/cloudflare-ai-gateway.d.ts +14 -14
  112. package/dist/providers/deepgram.d.ts +1 -1
  113. package/dist/providers/deepgram.js +6 -12
  114. package/dist/providers/elevenlabs.d.ts +1 -1
  115. package/dist/providers/elevenlabs.js +5 -10
  116. package/dist/providers/fal.d.ts +1 -1
  117. package/dist/providers/fal.js +5 -12
  118. package/dist/providers/google-vertex-messages.d.ts +5 -5
  119. package/dist/providers/google-vertex.d.ts +3 -3
  120. package/dist/providers/google.d.ts +3 -3
  121. package/dist/providers/google.js +7 -12
  122. package/dist/providers/meta.d.ts +8 -8
  123. package/dist/providers/meta.js +4 -8
  124. package/dist/providers/minimax.d.ts +5 -5
  125. package/dist/providers/moonshot.d.ts +5 -5
  126. package/dist/providers/openai-options.d.ts +3 -9
  127. package/dist/providers/openai-options.js +4 -7
  128. package/dist/providers/openai.d.ts +9 -10
  129. package/dist/providers/openai.js +11 -12
  130. package/dist/providers/opencode-zen.js +1 -1
  131. package/dist/providers/openrouter.d.ts +6 -7
  132. package/dist/providers/openrouter.js +10 -5
  133. package/dist/providers/replicate.d.ts +1 -1
  134. package/dist/providers/replicate.js +5 -10
  135. package/dist/providers/runway.d.ts +1 -1
  136. package/dist/providers/runway.js +5 -10
  137. package/dist/providers/stability.d.ts +1 -1
  138. package/dist/providers/stability.js +6 -11
  139. package/dist/providers/typesafe-ai.js +1 -1
  140. package/dist/providers/vercel-ai-gateway.js +1 -1
  141. package/dist/providers/xai.js +5 -10
  142. package/dist/providers/zai-coding-plan.d.ts +1 -1
  143. package/dist/providers/zai.js +4 -8
  144. package/dist/route/auth.d.ts +1 -1
  145. package/dist/route/auth.js +14 -13
  146. package/dist/route/client.d.ts +9 -7
  147. package/dist/route/client.js +6 -8
  148. package/dist/route/endpoint.d.ts +1 -0
  149. package/dist/route/endpoint.js +2 -2
  150. package/dist/route/executor-service.d.ts +4 -2
  151. package/dist/route/executor-service.js +2 -1
  152. package/dist/route/executor.d.ts +3 -1
  153. package/dist/route/executor.js +7 -0
  154. package/dist/route/framing.d.ts +10 -1
  155. package/dist/route/framing.js +45 -5
  156. package/dist/route/index.d.ts +1 -1
  157. package/dist/route/media-protocol.d.ts +49 -35
  158. package/dist/route/media-protocol.js +77 -65
  159. package/dist/route/media.d.ts +10 -16
  160. package/dist/route/media.js +46 -62
  161. package/dist/route/protocol.d.ts +3 -1
  162. package/dist/schema/options.d.ts +6 -3
  163. package/dist/schema/options.js +7 -3
  164. package/dist/speech-client.d.ts +62 -17
  165. package/dist/speech-client.js +17 -21
  166. package/dist/speech.d.ts +178 -21
  167. package/dist/speech.js +13 -8
  168. package/dist/testing.d.ts +2 -2
  169. package/dist/transcription-client.d.ts +78 -24
  170. package/dist/transcription-client.js +9 -40
  171. package/dist/transcription.d.ts +15 -27
  172. package/dist/transcription.js +4 -10
  173. package/dist/utils/json.d.ts +4 -0
  174. package/dist/utils/json.js +4 -0
  175. package/dist/utils/media-type.d.ts +2 -1
  176. package/dist/utils/media-type.js +3 -1
  177. package/dist/video-client.d.ts +55 -24
  178. package/dist/video-client.js +16 -36
  179. package/dist/video.d.ts +18 -26
  180. package/dist/video.js +15 -16
  181. package/package.json +3 -3
  182. package/dist/protocols/utils/meta-image.d.ts +0 -2
  183. package/dist/protocols/utils/meta-image.js +0 -13
  184. package/dist/protocols/utils/openai-image.d.ts +0 -5
  185. package/dist/protocols/utils/openai-image.js +0 -18
@@ -11,11 +11,11 @@ export declare class Service extends Service_base {
11
11
  }
12
12
  export declare const evaluate: <Options extends EvaluationOptions, const Questions extends EvaluationQuestions>(request: EvaluationRequestFor<Options, Questions>) => Effect.Effect<EvaluationResponseFor<Questions>, AIError, Service>;
13
13
  export declare const layer: Layer.Layer<Service, never, RequestExecutor.Service>;
14
- export declare const fetchLayer: Layer.Layer<Service, never, never>;
14
+ export declare const fetchLayer: Layer.Layer<import("../route/executor-service.js").RequestExecutorService | Service, never, never>;
15
15
  export declare const EvaluationClient: {
16
16
  readonly Service: typeof Service;
17
- readonly layer: Layer.Layer<Service, never, RequestExecutor.Service>;
18
- readonly fetchLayer: Layer.Layer<Service, never, never>;
17
+ readonly layer: Layer.Layer<Service, never, import("../route/executor-service.js").RequestExecutorService>;
18
+ readonly fetchLayer: Layer.Layer<import("../route/executor-service.js").RequestExecutorService | Service, never, never>;
19
19
  readonly evaluate: <Options extends EvaluationOptions, const Questions extends EvaluationQuestions>(request: EvaluationRequestFor<Options, Questions>) => Effect.Effect<EvaluationResponseFor<Questions>, AIError, Service>;
20
20
  };
21
21
  export {};
@@ -55,7 +55,7 @@ export const layer = Layer.effect(Service, Effect.gen(function* () {
55
55
  })),
56
56
  });
57
57
  }));
58
- export const fetchLayer = layer.pipe(Layer.provide(RequestExecutor.fetchLayer));
58
+ export const fetchLayer = layer.pipe(Layer.provideMerge(RequestExecutor.fetchLayer));
59
59
  export const EvaluationClient = {
60
60
  Service,
61
61
  layer,
@@ -94,7 +94,7 @@ export function request(input) {
94
94
  return new EvaluationRequest({
95
95
  ...input,
96
96
  model: input.model,
97
- http: input.http === undefined ? undefined : HttpOptions.make(input.http),
97
+ http: HttpOptions.make(input.http),
98
98
  });
99
99
  }
100
100
  export function run(input) {
@@ -1,4 +1,4 @@
1
- import { Duration, Effect, Schedule, Schema, Stream } from "effect";
1
+ import { Duration, Effect, Schema, Stream } from "effect";
2
2
  import { AIError } from "./schema/errors.js";
3
3
  export declare const Status: Schema.Literals<readonly ["queued", "running", "completed", "failed", "cancelled", "expired"]>;
4
4
  export type Status = Schema.Schema.Type<typeof Status>;
@@ -9,7 +9,6 @@ export interface Snapshot {
9
9
  /** Normalized 0..1 when the provider reports progress. */
10
10
  readonly progress?: number;
11
11
  readonly position?: number;
12
- readonly expiresAt?: number;
13
12
  }
14
13
  /**
15
14
  * Route-owned generation operations for one generation. The media route decodes its serializable token once (from the
@@ -19,14 +18,10 @@ export interface Route<Response> {
19
18
  readonly status: Effect.Effect<Snapshot, AIError>;
20
19
  readonly result: Effect.Effect<Response, AIError>;
21
20
  readonly cancel?: Effect.Effect<void, AIError>;
22
- /** Provider polling hint (e.g. `openai-poll-after-ms`) that overrides the default interval for the next poll. */
23
- readonly pollHint?: (snapshot: Snapshot) => Duration.Duration | undefined;
24
21
  }
25
22
  export interface Poll {
26
23
  readonly interval?: Duration.Input;
27
24
  readonly timeout?: Duration.Input;
28
- /** Full override of the polling schedule; `interval` and `pollHint` are ignored when supplied. */
29
- readonly schedule?: Schedule.Schedule<unknown, Snapshot>;
30
25
  }
31
26
  export interface AwaitOptions {
32
27
  readonly poll?: Poll;
@@ -49,6 +44,7 @@ export type Event = Observation | {
49
44
  readonly id: string;
50
45
  readonly status: Status;
51
46
  };
47
+ export declare const isTerminal: (status: Status) => boolean;
52
48
  export declare class Generation<Response> {
53
49
  readonly route: Route<Response>;
54
50
  /** Route-owned serializable JSON; pass it to the modality's `resume` from another process. */
@@ -57,7 +53,6 @@ export declare class Generation<Response> {
57
53
  readonly status: Status;
58
54
  readonly progress?: number;
59
55
  readonly position?: number;
60
- readonly expiresAt?: number;
61
56
  constructor(route: Route<Response>,
62
57
  /** Route-owned serializable JSON; pass it to the modality's `resume` from another process. */
63
58
  token: unknown, snapshot: Snapshot);
@@ -70,9 +65,10 @@ export declare class Generation<Response> {
70
65
  await(options?: AwaitOptions): Effect.Effect<Response, AIError>;
71
66
  cancel(): Effect.Effect<void, AIError>;
72
67
  /**
73
- * Status observations as a stream, ending after the first terminal observation. Each poll is bounded by the time
74
- * remaining until `poll.timeout`, so a hung status request fails the stream instead of stalling it. (`Stream.interruptWhen`
75
- * would express this directly but deadlocks under `TestClock` when the source completes while the timer sleeps.)
68
+ * Status observations as a stream, ending after the first terminal observation. Each poll and each sleep between polls
69
+ * is bounded by the time remaining until `poll.timeout`, so a hung status request or a long interval fails the stream at
70
+ * the deadline instead of stalling it. (`Stream.interruptWhen` would express this directly but deadlocks under
71
+ * `TestClock` when the source completes while the timer sleeps.)
76
72
  */
77
73
  events(options?: AwaitOptions): Stream.Stream<Event, AIError>;
78
74
  private event;
@@ -14,6 +14,7 @@ export const ProgressEvent = Schema.Struct({
14
14
  progress: Schema.optional(Schema.Number),
15
15
  }).annotate({ identifier: "Generation.Event.Progress" });
16
16
  const TERMINAL = new Set(["completed", "failed", "cancelled", "expired"]);
17
+ export const isTerminal = (status) => TERMINAL.has(status);
17
18
  export class Generation {
18
19
  route;
19
20
  token;
@@ -21,7 +22,6 @@ export class Generation {
21
22
  status;
22
23
  progress;
23
24
  position;
24
- expiresAt;
25
25
  constructor(route,
26
26
  /** Route-owned serializable JSON; pass it to the modality's `resume` from another process. */
27
27
  token, snapshot) {
@@ -31,7 +31,6 @@ export class Generation {
31
31
  this.status = snapshot.status;
32
32
  this.progress = snapshot.progress;
33
33
  this.position = snapshot.position;
34
- this.expiresAt = snapshot.expiresAt;
35
34
  }
36
35
  get snapshot() {
37
36
  return {
@@ -39,11 +38,10 @@ export class Generation {
39
38
  status: this.status,
40
39
  progress: this.progress,
41
40
  position: this.position,
42
- expiresAt: this.expiresAt,
43
41
  };
44
42
  }
45
43
  get terminal() {
46
- return TERMINAL.has(this.status);
44
+ return isTerminal(this.status);
47
45
  }
48
46
  refresh() {
49
47
  return this.route.status.pipe(Effect.map((snapshot) => new Generation(this.route, this.token, snapshot)));
@@ -64,9 +62,10 @@ export class Generation {
64
62
  return this.route.cancel ?? Effect.void;
65
63
  }
66
64
  /**
67
- * Status observations as a stream, ending after the first terminal observation. Each poll is bounded by the time
68
- * remaining until `poll.timeout`, so a hung status request fails the stream instead of stalling it. (`Stream.interruptWhen`
69
- * would express this directly but deadlocks under `TestClock` when the source completes while the timer sleeps.)
65
+ * Status observations as a stream, ending after the first terminal observation. Each poll and each sleep between polls
66
+ * is bounded by the time remaining until `poll.timeout`, so a hung status request or a long interval fails the stream at
67
+ * the deadline instead of stalling it. (`Stream.interruptWhen` would express this directly but deadlocks under
68
+ * `TestClock` when the source completes while the timer sleeps.)
70
69
  */
71
70
  events(options) {
72
71
  if (this.terminal)
@@ -74,11 +73,16 @@ export class Generation {
74
73
  const timeout = Duration.fromInputUnsafe(options?.poll?.timeout ?? DEFAULT_POLL_TIMEOUT);
75
74
  return Stream.unwrap(Clock.currentTimeMillis.pipe(Effect.map((start) => {
76
75
  const deadline = start + Duration.toMillis(timeout);
77
- const refresh = Clock.currentTimeMillis.pipe(Effect.flatMap((now) => this.refresh().pipe(Effect.timeoutOrElse({
78
- duration: Duration.millis(Math.max(0, deadline - now)),
79
- orElse: () => this.timeoutError(timeout),
80
- }))));
81
- return Stream.fromEffectSchedule(refresh, this.schedule(options?.poll)).pipe(Stream.takeUntil((generation) => generation.terminal), Stream.map((generation) => generation.event()));
76
+ // Fail before polling once the deadline has passed: a fast status request could otherwise win the zero-budget
77
+ // race and schedule another zero-delay poll.
78
+ const refresh = Clock.currentTimeMillis.pipe(Effect.flatMap((now) => now >= deadline
79
+ ? this.timeoutError(timeout)
80
+ : this.refresh().pipe(Effect.timeoutOrElse({
81
+ duration: Duration.millis(deadline - now),
82
+ orElse: () => this.timeoutError(timeout),
83
+ }))));
84
+ const schedule = this.schedule(options?.poll).pipe(Schedule.modifyDelay((meta) => Effect.succeed(Duration.min(meta.duration, Duration.millis(Math.max(0, deadline - meta.now))))));
85
+ return Stream.fromEffectSchedule(refresh, schedule).pipe(Stream.takeUntil((generation) => generation.terminal), Stream.map((generation) => generation.event()));
82
86
  })));
83
87
  }
84
88
  event() {
@@ -100,14 +104,7 @@ export class Generation {
100
104
  return this.refresh().pipe(Effect.repeat({ schedule: this.schedule(poll), until: (generation) => generation.terminal }));
101
105
  }
102
106
  schedule(poll) {
103
- if (poll?.schedule)
104
- return poll.schedule.pipe(Schedule.setInputType());
105
- const interval = poll?.interval ?? DEFAULT_POLL_INTERVAL;
106
- const pollHint = this.route.pollHint;
107
- const spaced = Schedule.spaced(interval).pipe(Schedule.setInputType());
108
- if (!pollHint)
109
- return spaced;
110
- return spaced.pipe(Schedule.modifyDelay((metadata) => Effect.succeed(pollHint(metadata.input.snapshot) ?? interval)));
107
+ return Schedule.spaced(poll?.interval ?? DEFAULT_POLL_INTERVAL);
111
108
  }
112
109
  }
113
110
  export const resultEvents = (generation, expand, options) => generation.events(options).pipe(Stream.filter((event) => event.type !== "generation-finished"), Stream.concat(Stream.fromIterableEffect(Effect.map(generation.result(), expand))));
@@ -1,28 +1,63 @@
1
- import { Context, Effect, Layer, Stream } from "effect";
2
- import type { AwaitOptions, Generation } from "./generation.js";
3
- import { RequestExecutor } from "./route/executor.js";
4
- import type { AIError } from "./schema/index.js";
5
- import { type ImageEvent, type ImageModel, type ImageOptions, type ImageRequestFor, type ImageResponse } from "./image.js";
6
- export interface Interface {
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>;
1
+ import { Context } from "effect";
2
+ import { MediaClient } from "./media-client.js";
3
+ import { type ImageEvent, type ImageRequestFor, type ImageResponse } from "./image.js";
4
+ export type Interface = MediaClient.Interface<ImageRequestFor, ImageEvent, ImageResponse>;
5
+ declare const ImageClientService_base: Context.ServiceClass<ImageClientService, "@opencode/ImageClient", Interface>;
6
+ export declare class ImageClientService extends ImageClientService_base {
11
7
  }
12
- declare const Service_base: Context.ServiceClass<Service, "@opencode/ImageClient", Interface>;
13
- export declare class Service extends Service_base {
14
- }
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>;
19
- export declare const layer: Layer.Layer<Service, never, RequestExecutor.Service>;
8
+ export declare const Service: typeof ImageClientService;
9
+ export type Service = ImageClientService;
20
10
  export declare const ImageClient: {
21
- readonly Service: typeof Service;
22
- readonly layer: Layer.Layer<Service, never, RequestExecutor.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>;
11
+ readonly layer: import("effect/Layer").Layer<ImageClientService, never, import("./route/executor-service.js").RequestExecutorService>;
12
+ readonly generate: (request: ImageRequestFor, options?: import("./generation.js").AwaitOptions) => import("effect/Effect").Effect<ImageResponse, import("./index.js").AIError, ImageClientService>;
13
+ readonly stream: (request: ImageRequestFor, options?: import("./generation.js").AwaitOptions) => import("effect/Stream").Stream<import("./generation.js").Observation | {
14
+ readonly type: "image-partial";
15
+ readonly index: number;
16
+ readonly image: import("./media.js").Asset;
17
+ } | {
18
+ readonly type: "image";
19
+ readonly index: number;
20
+ readonly image: import("./media.js").Asset;
21
+ } | {
22
+ readonly type: "finish";
23
+ readonly providerMetadata?: {
24
+ readonly [x: string]: {
25
+ readonly [x: string]: unknown;
26
+ };
27
+ } | undefined;
28
+ readonly usage?: {
29
+ readonly type: "tokens";
30
+ readonly input?: number | undefined;
31
+ readonly output?: number | undefined;
32
+ readonly total?: number | undefined;
33
+ readonly details?: {
34
+ readonly [x: string]: unknown;
35
+ } | undefined;
36
+ } | {
37
+ readonly type: "seconds";
38
+ readonly seconds: number;
39
+ } | {
40
+ readonly type: "characters";
41
+ readonly characters: number;
42
+ } | {
43
+ readonly type: "credits";
44
+ readonly credits: number;
45
+ } | {
46
+ readonly type: "compute";
47
+ readonly seconds: number;
48
+ } | undefined;
49
+ readonly notices?: readonly {
50
+ readonly type: "other" | "moderated" | "filtered";
51
+ readonly message: string;
52
+ readonly providerMetadata?: {
53
+ readonly [x: string]: {
54
+ readonly [x: string]: unknown;
55
+ };
56
+ } | undefined;
57
+ }[] | undefined;
58
+ }, import("./index.js").AIError, ImageClientService>;
59
+ readonly start: (request: ImageRequestFor) => import("effect/Effect").Effect<import("./generation.js").Generation<ImageResponse>, import("./index.js").AIError, ImageClientService>;
60
+ readonly resume: (model: import("./image.js").ImageModel<import("./media-model.js").MediaModel.Options>, token: unknown) => import("effect/Effect").Effect<import("./generation.js").Generation<ImageResponse>, import("./index.js").AIError, ImageClientService>;
61
+ readonly Service: typeof ImageClientService;
27
62
  };
28
63
  export {};
@@ -1,44 +1,20 @@
1
- import { Context, Effect, Layer, Stream } from "effect";
2
- import { RequestExecutor } from "./route/executor.js";
3
- import { MediaRoute } from "./route/media.js";
4
- import { responseEvents, } from "./image.js";
5
- export class Service extends Context.Service()("@opencode/ImageClient") {
1
+ import { Context } from "effect";
2
+ import { MediaClient } from "./media-client.js";
3
+ import { ImageOutputEvent, ImageFinishEvent, } from "./image.js";
4
+ export class ImageClientService extends Context.Service()("@opencode/ImageClient") {
6
5
  }
7
- export const generate = (request, options) => Effect.gen(function* () {
8
- const client = yield* Service;
9
- return yield* client.generate(request, options);
10
- });
11
- export const stream = (request, options) => Stream.unwrap(Effect.gen(function* () {
12
- const client = yield* Service;
13
- return client.stream(request, options);
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
- });
23
- export const layer = Layer.effect(Service, Effect.gen(function* () {
24
- const executor = yield* RequestExecutor.Service;
25
- const dispatch = MediaRoute.dispatch({
26
- modality: "image",
27
- execute: executor.execute,
28
- responseEvents,
29
- });
30
- return Service.of({
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),
35
- });
36
- }));
6
+ export const Service = ImageClientService;
37
7
  export const ImageClient = {
38
8
  Service,
39
- layer,
40
- generate,
41
- stream,
42
- start,
43
- resume,
9
+ ...MediaClient.make(Service, {
10
+ modality: "image",
11
+ responseEvents: (response) => [
12
+ ...response.images.map((image, index) => ImageOutputEvent.make({ index, image })),
13
+ ImageFinishEvent.make({
14
+ usage: response.usage,
15
+ notices: response.notices,
16
+ providerMetadata: response.providerMetadata,
17
+ }),
18
+ ],
19
+ }),
44
20
  };
package/dist/image.d.ts CHANGED
@@ -3,33 +3,25 @@ import { Generation, type AwaitOptions } from "./generation.js";
3
3
  import { Media } from "./media.js";
4
4
  import { MediaModel } from "./media-model.js";
5
5
  import { MediaRoute } from "./route/media.js";
6
- import type { MediaProtocol } from "./route/media-protocol.js";
7
- import { AIError, HttpOptions } from "./schema/index.js";
6
+ import { AIError, HttpOptions, type OpenString } from "./schema/index.js";
8
7
  import { Service } from "./image-client.js";
9
- export type ImageOptions = Record<string, unknown>;
10
- export type ImageRoute<Options extends ImageOptions = ImageOptions> = MediaRoute.AnyRoute<ImageRequestFor<Options>, ImageEvent, ImageResponse>;
11
- export declare class ImageModel<Options extends ImageOptions = ImageOptions> extends MediaModel<ImageRoute<Options>, Options> {
8
+ export type ImageOptions = MediaModel.Options;
9
+ export type ImageRoute = MediaRoute.AnyRoute<ImageRequestFor, ImageEvent, ImageResponse>;
10
+ export declare class ImageModel<Options extends ImageOptions = ImageOptions> extends MediaModel<ImageRoute, Options> {
12
11
  protected readonly _ImageModel: void;
13
- static make<Options extends ImageOptions = ImageOptions>(input: MediaModel.Input<ImageRoute<Options>>): ImageModel<Options>;
14
12
  /** The number of type arguments selects the kind: `<Options>`, `<Options, Frame, State>`, or `<Options, Token>`. */
15
- static fromRoute<Options extends ImageOptions>(route: ImageModel.InlineRouteInput<Options>, input: MediaRoute.ModelInput): ImageModel<Options>;
16
- static fromRoute<Options extends ImageOptions, Frame, State>(route: ImageModel.StreamRouteInput<Options, Frame, State>, input: MediaRoute.ModelInput): ImageModel<Options>;
17
- static fromRoute<Options extends ImageOptions, Token>(route: ImageModel.QueuedRouteInput<Options, Token>, input: MediaRoute.ModelInput): ImageModel<Options>;
13
+ static fromRoute<Options extends ImageOptions>(route: MediaModel.InlineRouteInput<ImageRequestFor<Options>, ImageResponse>, input: MediaRoute.ModelInput): ImageModel<Options>;
14
+ static fromRoute<Options extends ImageOptions, Frame, State>(route: MediaModel.StreamRouteInput<ImageRequestFor<Options>, ImageEvent, Frame, State>, input: MediaRoute.ModelInput): ImageModel<Options>;
15
+ static fromRoute<Options extends ImageOptions, Token>(route: MediaModel.QueuedRouteInput<ImageRequestFor<Options>, ImageResponse, Token>, input: MediaRoute.ModelInput): ImageModel<Options>;
18
16
  }
19
- export declare namespace ImageModel {
20
- type InlineRouteInput<Options extends ImageOptions = ImageOptions> = MediaModel.RouteInput<ImageRequestFor<Options>, MediaProtocol.Inline<ImageRequestFor<Options>, ImageResponse>>;
21
- type StreamRouteInput<Options extends ImageOptions = ImageOptions, Frame = unknown, State = unknown> = MediaModel.RouteInput<MediaProtocol.Addressed<ImageRequestFor<Options>>, MediaProtocol.Streamed<ImageRequestFor<Options>, ImageEvent, Frame, State>>;
22
- type QueuedRouteInput<Options extends ImageOptions = ImageOptions, Token = unknown> = MediaModel.RouteInput<ImageRequestFor<Options>, MediaProtocol.Queued<ImageRequestFor<Options>, ImageResponse, Token>>;
23
- type RouteInput<Options extends ImageOptions = ImageOptions, Frame = unknown, State = unknown, Token = unknown> = MediaModel.AnyRouteInput<ImageRequestFor<Options>, ImageEvent, ImageResponse, Frame, State, Token>;
24
- }
25
- export declare const ImageModelSchema: Schema.declare<ImageModel<ImageOptions>, ImageModel<ImageOptions>>;
17
+ export declare const ImageModelSchema: Schema.declare<ImageModel<MediaModel.Options>, ImageModel<MediaModel.Options>>;
26
18
  export type ImageSize = `${number}x${number}`;
27
19
  export declare const ImageSize: Schema.declare<`${number}x${number}`, `${number}x${number}`>;
28
20
  export type ImageAspectRatio = Media.AspectRatio;
29
21
  export declare const ImageAspectRatio: Schema.declare<`${number}:${number}`, `${number}:${number}`>;
30
- export type ImageFormat = "png" | "jpeg" | "webp" | (string & {});
22
+ export type ImageFormat = OpenString<"png" | "jpeg" | "webp">;
31
23
  declare const ImageRequest_base: Schema.Class<ImageRequest, Schema.Struct<{
32
- readonly model: Schema.declare<ImageModel<ImageOptions>, ImageModel<ImageOptions>>;
24
+ readonly model: Schema.declare<ImageModel<MediaModel.Options>, ImageModel<MediaModel.Options>>;
33
25
  readonly prompt: Schema.String;
34
26
  /** Edit sources or style/subject references, in order. */
35
27
  readonly images: Schema.optional<Schema.$Array<Schema.decodeTo<Schema.declare<Media.Asset, Media.Asset>, Schema.Struct<{
@@ -1418,22 +1410,18 @@ export declare const ImageEvent: Schema.Union<readonly [Schema.Struct<{
1418
1410
  };
1419
1411
  };
1420
1412
  export type ImageEvent = Schema.Schema.Type<typeof imageEventTagged>;
1421
- export declare const responseEvents: (response: ImageResponse) => ReadonlyArray<ImageEvent>;
1422
1413
  export declare function request<const Model extends ImageModel>(input: ImageRequestInput<Model>): ImageRequestFor<ImageModelOptions<Model>>;
1423
1414
  export declare function request(input: ImageRequest): ImageRequest;
1424
- export declare function generate<const Model extends ImageModel>(input: ImageRequestInput<Model>, options?: AwaitOptions): Effect.Effect<ImageResponse, AIError, Service>;
1425
- export declare function generate(input: ImageRequest, options?: AwaitOptions): Effect.Effect<ImageResponse, AIError, Service>;
1426
- export declare function stream<const Model extends ImageModel>(input: ImageRequestInput<Model>, options?: AwaitOptions): Stream.Stream<ImageEvent, AIError, Service>;
1427
- export declare function stream(input: ImageRequest, options?: AwaitOptions): Stream.Stream<ImageEvent, AIError, Service>;
1415
+ export declare function generate<const Model extends ImageModel>(input: ImageRequest | ImageRequestInput<Model>, options?: AwaitOptions): Effect.Effect<ImageResponse, AIError, Service>;
1416
+ export declare function stream<const Model extends ImageModel>(input: ImageRequest | ImageRequestInput<Model>, options?: AwaitOptions): Stream.Stream<ImageEvent, AIError, Service>;
1428
1417
  /** Inline and streaming routes fail with `UnsupportedOperation`. */
1429
- export declare function start<const Model extends ImageModel>(input: ImageRequestInput<Model>): Effect.Effect<Generation<ImageResponse>, AIError, Service>;
1430
- export declare function start(input: ImageRequest): Effect.Effect<Generation<ImageResponse>, AIError, Service>;
1431
- export declare const resume: <Options extends ImageOptions>(model: ImageModel<Options>, token: unknown) => Effect.Effect<Generation<ImageResponse>, AIError, Service>;
1418
+ export declare function start<const Model extends ImageModel>(input: ImageRequest | ImageRequestInput<Model>): Effect.Effect<Generation<ImageResponse>, AIError, Service>;
1419
+ export declare const resume: (model: ImageModel, token: unknown) => Effect.Effect<Generation<ImageResponse>, AIError, Service>;
1432
1420
  export declare const Image: {
1433
1421
  readonly request: typeof request;
1434
1422
  readonly generate: typeof generate;
1435
1423
  readonly stream: typeof stream;
1436
1424
  readonly start: typeof start;
1437
- readonly resume: <Options extends ImageOptions>(model: ImageModel<Options>, token: unknown) => Effect.Effect<Generation<ImageResponse>, AIError, Service>;
1425
+ readonly resume: (model: ImageModel, token: unknown) => Effect.Effect<Generation<ImageResponse>, AIError, Service>;
1438
1426
  };
1439
1427
  export {};
package/dist/image.js CHANGED
@@ -1,20 +1,17 @@
1
1
  import { Effect, Schema, Stream } from "effect";
2
2
  import { Generation, ProgressEvent, QueuedEvent } from "./generation.js";
3
3
  import { Media } from "./media.js";
4
- import { MediaModel, composeAnyRoute, tryRequest } from "./media-model.js";
4
+ import { MediaModel, composeRoute, tryRequest } from "./media-model.js";
5
5
  import { MediaRoute } from "./route/media.js";
6
6
  import { AIError, HttpOptions, MediaUsage, ProviderMetadata } from "./schema/index.js";
7
7
  import { ImageClient, Service } from "./image-client.js";
8
8
  export class ImageModel extends MediaModel {
9
- static make(input) {
10
- return new ImageModel(input);
11
- }
12
9
  static fromRoute(route, input) {
13
10
  return new ImageModel({
14
11
  id: input.id,
15
- provider: route.provider,
12
+ provider: route.protocol.provider,
16
13
  http: input.http,
17
- route: composeAnyRoute(route, input, collectResponse),
14
+ route: composeRoute(route, input, collectResponse),
18
15
  });
19
16
  }
20
17
  }
@@ -85,14 +82,6 @@ export const ImageEvent = Object.assign(imageEventTagged, {
85
82
  finish: imageEventTagged.guards.finish,
86
83
  },
87
84
  });
88
- export const responseEvents = (response) => [
89
- ...response.images.map((image, index) => ImageOutputEvent.make({ index, image })),
90
- ImageFinishEvent.make({
91
- usage: response.usage,
92
- notices: response.notices,
93
- providerMetadata: response.providerMetadata,
94
- }),
95
- ];
96
85
  const collectResponse = (events) => {
97
86
  const finish = events.find(ImageEvent.is.finish);
98
87
  // Every image protocol's `finish` emits the terminal event or fails, so a completed stream always has one.
@@ -110,7 +99,7 @@ export function request(input) {
110
99
  return input;
111
100
  return new ImageRequest({
112
101
  ...input,
113
- http: input.http === undefined ? undefined : HttpOptions.make(input.http),
102
+ http: HttpOptions.make(input.http),
114
103
  });
115
104
  }
116
105
  const requestEffect = (input) => tryRequest(() => request(input));
package/dist/index.d.ts CHANGED
@@ -1,10 +1,11 @@
1
+ export { AIClient } from "./ai-client.js";
1
2
  export { LLMClient } from "./route/client.js";
2
3
  export { ImageClient } from "./image-client.js";
3
4
  export { Auth } from "./route/auth.js";
4
5
  export { Provider } from "./provider.js";
5
6
  export { ProviderPackage } from "./provider-package.js";
6
7
  export { isContextOverflow, isContextOverflowFailure } from "./provider-error.js";
7
- export type { RouteLanguageModelInput, RouteRoutedLanguageModelInput, Interface as LLMClientShape, Service as LLMClientService, } from "./route/client.js";
8
+ export type { RouteLanguageModelInput, RouteRoutedLanguageModelInput, Interface as LLMClientShape, LLMClientService, } from "./route/client.js";
8
9
  export * from "./schema/index.js";
9
10
  export { ImageAspectRatio, ImageEvent, ImageModel, ImageModelSchema, ImageRequest, ImageResponse, ImageSize, } from "./image.js";
10
11
  export type { ImageFormat, ImageModelOptions, ImageOptions, ImageRequestFor, ImageRequestInput, ImageRoute, } from "./image.js";
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ export { AIClient } from "./ai-client.js";
1
2
  export { LLMClient } from "./route/client.js";
2
3
  export { ImageClient } from "./image-client.js";
3
4
  export { Auth } from "./route/auth.js";
package/dist/llm.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { Effect, JsonSchema, Schema } from "effect";
2
- import { Service } from "./route/client.js";
3
- import { GenerationOptions, HttpOptions, AIError, LLMRequest, LLMResponse, Message, LanguageModel, SystemPart, ToolChoice, type ContentPart, type LanguageModelProviderOptions, type ToolEntryInput } from "./schema/index.js";
1
+ import { Effect, JsonSchema, Schema, Stream } from "effect";
2
+ import { Service, type StreamOptions } from "./route/client.js";
3
+ import { GenerationOptions, HttpOptions, AIError, LLMEvent, LLMRequest, LLMResponse, Message, LanguageModel, SystemPart, ToolChoice, type ContentPart, type LanguageModelProviderOptions, type ToolEntryInput } from "./schema/index.js";
4
4
  import { type ToolSchema } from "./tool.js";
5
5
  /** Input accepted by `LLM.request`, normalized into the canonical `LLMRequest` class. */
6
6
  export type RequestInput<SelectedLanguageModel extends LanguageModel = LanguageModel> = Omit<ConstructorParameters<typeof LLMRequest>[0], "model" | "system" | "messages" | "tools" | "toolChoice" | "generation" | "http" | "providerOptions"> & {
@@ -14,8 +14,10 @@ export type RequestInput<SelectedLanguageModel extends LanguageModel = LanguageM
14
14
  readonly providerOptions?: NoInfer<LanguageModelProviderOptions<SelectedLanguageModel>>;
15
15
  readonly http?: HttpOptions.Input;
16
16
  };
17
- export declare const generate: typeof import("./route/client.js").generate;
18
- export declare const stream: typeof import("./route/client.js").stream;
17
+ export declare function generate<const Model extends LanguageModel>(input: RequestInput<Model>, options?: StreamOptions): Effect.Effect<LLMResponse, AIError, Service>;
18
+ export declare function generate(input: LLMRequest, options?: StreamOptions): Effect.Effect<LLMResponse, AIError, Service>;
19
+ export declare function stream<const Model extends LanguageModel>(input: RequestInput<Model>, options?: StreamOptions): Stream.Stream<LLMEvent, AIError, Service>;
20
+ export declare function stream(input: LLMRequest, options?: StreamOptions): Stream.Stream<LLMEvent, AIError, Service>;
19
21
  export declare const request: <const SelectedLanguageModel extends LanguageModel>(input: RequestInput<SelectedLanguageModel>) => LLMRequest<SelectedLanguageModel>;
20
22
  type GenerateObjectBase<SelectedLanguageModel extends LanguageModel = LanguageModel> = Omit<RequestInput<SelectedLanguageModel>, "tools" | "toolChoice">;
21
23
  export declare class GenerateObjectResponse<T> {
package/dist/llm.js CHANGED
@@ -1,9 +1,15 @@
1
- import { Effect, JsonSchema, Schema } from "effect";
1
+ import { Effect, JsonSchema, Schema, Stream } from "effect";
2
+ import { tryRequest } from "./media-model.js";
2
3
  import { LLMClient, Service } from "./route/client.js";
3
4
  import { GenerationOptions, HttpOptions, InvalidProviderOutputError, AIError, LLMEvent, LLMRequest, LLMResponse, Message, LanguageModel, SystemPart, ToolChoice, ToolEntry, } from "./schema/index.js";
4
5
  import { make as makeTool, toDefinitions } from "./tool.js";
5
- export const generate = LLMClient.generate;
6
- export const stream = LLMClient.stream;
6
+ export function generate(input, options) {
7
+ return requestEffect(input).pipe(Effect.flatMap((request) => LLMClient.generate(request, options)));
8
+ }
9
+ export function stream(input, options) {
10
+ return Stream.unwrap(requestEffect(input).pipe(Effect.map((request) => LLMClient.stream(request, options))));
11
+ }
12
+ const requestEffect = (input) => input instanceof LLMRequest ? Effect.succeed(input) : tryRequest(() => request(input));
7
13
  export const request = (input) => {
8
14
  const { system: requestSystem, prompt, messages, tools, toolChoice: requestToolChoice, generation: requestGeneration, providerOptions: requestProviderOptions, http: requestHttp, ...rest } = input;
9
15
  return new LLMRequest({
@@ -14,7 +20,7 @@ export const request = (input) => {
14
20
  toolChoice: requestToolChoice ? ToolChoice.make(requestToolChoice) : undefined,
15
21
  generation: requestGeneration === undefined ? undefined : GenerationOptions.make(requestGeneration),
16
22
  providerOptions: requestProviderOptions,
17
- http: requestHttp === undefined ? undefined : HttpOptions.make(requestHttp),
23
+ http: HttpOptions.make(requestHttp),
18
24
  });
19
25
  };
20
26
  const GENERATE_OBJECT_TOOL_NAME = "generate_object";
@@ -0,0 +1,30 @@
1
+ import { type Context, Effect, Layer, Stream } from "effect";
2
+ import { type AwaitOptions, type Generation, type Observation } from "./generation.js";
3
+ import type { MediaRoute } from "./route/media.js";
4
+ import { AIError } from "./schema/index.js";
5
+ /** A media request whose model carries the route that executes it. */
6
+ export interface RoutedRequest<Self extends MediaRoute.MediaRequest, Event, Response> extends MediaRoute.MediaRequest {
7
+ readonly model: MediaRoute.MediaRequest["model"] & {
8
+ readonly route: MediaRoute.AnyRoute<Self, Event, Response>;
9
+ };
10
+ }
11
+ /** `start` and `resume` fail with `UnsupportedOperation` on inline and stream routes. */
12
+ export interface Interface<Req extends RoutedRequest<Req, Event, Response>, Event, Response> {
13
+ readonly generate: (request: Req, options?: AwaitOptions) => Effect.Effect<Response, AIError>;
14
+ readonly stream: (request: Req, options?: AwaitOptions) => Stream.Stream<Event | Observation, AIError>;
15
+ readonly start: (request: Req) => Effect.Effect<Generation<Response>, AIError>;
16
+ readonly resume: (model: Req["model"], token: unknown) => Effect.Effect<Generation<Response>, AIError>;
17
+ }
18
+ /** One modality's layer and service accessors, dispatching each request on its route's `kind`. */
19
+ export declare const make: <Self, Req extends RoutedRequest<Req, Event, Response>, Event, Response>(service: Context.Service<Self, Interface<Req, Event, Response>>, input: {
20
+ readonly modality: string;
21
+ /** A completed response expanded into the streaming event shape. */
22
+ readonly responseEvents: (response: Response) => ReadonlyArray<Event>;
23
+ }) => {
24
+ layer: Layer.Layer<Self, never, import("./route/executor-service.js").RequestExecutorService>;
25
+ generate: (request: Req, options?: AwaitOptions) => Effect.Effect<Response, AIError, Self>;
26
+ stream: (request: Req, options?: AwaitOptions) => Stream.Stream<Observation | Event, AIError, Exclude<Self, import("effect/Scope").Scope>>;
27
+ start: (request: Req) => Effect.Effect<Generation<Response>, AIError, Self>;
28
+ resume: (model: Req["model"], token: unknown) => Effect.Effect<Generation<Response>, AIError, Self>;
29
+ };
30
+ export * as MediaClient from "./media-client.js";
@@ -0,0 +1,51 @@
1
+ import { Effect, Layer, Stream } from "effect";
2
+ import { resultEvents } from "./generation.js";
3
+ import { RequestExecutor } from "./route/executor.js";
4
+ import { AIError, UnsupportedOperationError } from "./schema/index.js";
5
+ /** One modality's layer and service accessors, dispatching each request on its route's `kind`. */
6
+ export const make = (service, input) => ({
7
+ layer: Layer.effect(service, Effect.gen(function* () {
8
+ const executor = yield* RequestExecutor.Service;
9
+ const notQueued = (route, operation) => new AIError({
10
+ reason: new UnsupportedOperationError({
11
+ operation: `${input.modality}.${operation}`,
12
+ provider: route.provider,
13
+ route: route.id,
14
+ message: `${route.provider}/${route.id} is not a queued route; use generate or stream`,
15
+ }),
16
+ });
17
+ const start = (request) => {
18
+ const route = request.model.route;
19
+ if (route.kind !== "queued")
20
+ return Effect.fail(notQueued(route, "start"));
21
+ return route.start(request, executor.execute);
22
+ };
23
+ return service.of({
24
+ start,
25
+ resume: (model, token) => {
26
+ if (model.route.kind !== "queued")
27
+ return Effect.fail(notQueued(model.route, "resume"));
28
+ return model.route.resume(model, token, executor.execute);
29
+ },
30
+ generate: (request, options) => {
31
+ const route = request.model.route;
32
+ if (route.kind !== "queued")
33
+ return route.generate(request, executor.execute);
34
+ return start(request).pipe(Effect.flatMap((generation) => generation.await(options)));
35
+ },
36
+ stream: (request, options) => {
37
+ const route = request.model.route;
38
+ if (route.kind === "stream")
39
+ return route.stream(request, executor.execute);
40
+ if (route.kind === "queued")
41
+ return Stream.unwrap(start(request).pipe(Effect.map((generation) => resultEvents(generation, input.responseEvents, options))));
42
+ return Stream.fromIterableEffect(Effect.map(route.generate(request, executor.execute), input.responseEvents));
43
+ },
44
+ });
45
+ })),
46
+ generate: (request, options) => service.use((client) => client.generate(request, options)),
47
+ stream: (request, options) => Stream.unwrap(service.useSync((client) => client.stream(request, options))),
48
+ start: (request) => service.use((client) => client.start(request)),
49
+ resume: (model, token) => service.use((client) => client.resume(model, token)),
50
+ });
51
+ export * as MediaClient from "./media-client.js";