@opencode/ai 0.0.0-dev-21801 → 0.0.0-dev-21803

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/dist/index.js CHANGED
@@ -1,28 +1,106 @@
1
- export { AIClient } from "./ai-client.js";
2
- export { LLMClient } from "./route/client.js";
3
- export { ImageClient } from "./image-client.js";
4
- export { Auth } from "./route/auth.js";
5
- export { Provider } from "./provider.js";
6
- export { ProviderPackage } from "./provider-package.js";
7
- export { isContextOverflow, isContextOverflowFailure, isRetryable } from "./provider-error.js";
8
- export * from "./schema/index.js";
9
- export { ImageAspectRatio, ImageEvent, ImageModel, ImageModelSchema, ImageRequest, ImageResponse } from "./image.js";
10
- export { Image } from "./image.js";
11
- export { VideoClient } from "./video-client.js";
12
- export { VideoAspectRatio, VideoEvent, VideoFrames, VideoModel, VideoModelSchema, VideoRequest, VideoResponse, } from "./video.js";
13
- export { Video } from "./video.js";
14
- export { SpeechClient } from "./speech-client.js";
15
- export { SpeechEvent, SpeechModel, SpeechModelSchema, SpeechRequest, SpeechResponse, SpeechTimestamp, SpeechVoice, } from "./speech.js";
16
- export { Speech } from "./speech.js";
17
- export { TranscriptionClient } from "./transcription-client.js";
18
- export { TranscriptionEvent, TranscriptionModel, TranscriptionModelSchema, TranscriptionRequest, TranscriptionResponse, TranscriptionSegment, TranscriptionTimestamps, TranscriptionWord, } from "./transcription.js";
19
- export { Transcription } from "./transcription.js";
20
- export { DecisionClient } from "./decision-client.js";
21
- export { ChoiceAnswer, ChoiceQuestion, ChoiceValue, DecisionAnswer, DecisionJsonPart, DecisionMediaPart, DecisionModel, DecisionModelCompatibility, DecisionModelSchema, DecisionPart, DecisionQuestion, DecisionRequest, DecisionResponse, DecisionTextPart, ErrorAnswer, PredicateAnswer, PredicateQuestion, ScoreAnswer, ScoreQuestion, } from "./decision.js";
22
- export { Decision } from "./decision.js";
23
- export { DecisionRoute } from "./route/decision.js";
24
- export { Media } from "./media.js";
25
- export { Generation } from "./generation.js";
26
- export { Tool, ToolFailure, toDefinitions } from "./tool.js";
27
- export { ToolRuntime } from "./tool-runtime.js";
28
- export * as LLM from "./llm.js";
1
+ import { Effect, Layer, ManagedRuntime, Stream } from "effect";
2
+ import { AIClient } from "./ai-client.js";
3
+ import { Decision } from "./decision.js";
4
+ import { Image } from "./image.js";
5
+ import { LLM } from "./effect.js";
6
+ import { Media } from "./media.js";
7
+ import { RequestExecutor } from "./route/executor.js";
8
+ import { AIError, InvalidRequestError, LanguageModel } from "./schema/index.js";
9
+ import { Speech } from "./speech.js";
10
+ import { Transcription, } from "./transcription.js";
11
+ import { fileMediaType } from "./utils/media-type.js";
12
+ import { Video } from "./video.js";
13
+ // Fails with `signal.reason` so aborted calls reject and aborted streams throw like `fetch`: an `AbortError` by default.
14
+ const abortEffect = (signal) => signal === undefined
15
+ ? Effect.never
16
+ : Effect.callback((resume) => {
17
+ if (signal.aborted) {
18
+ resume(Effect.fail(signal.reason));
19
+ return;
20
+ }
21
+ const onAbort = () => resume(Effect.fail(signal.reason));
22
+ signal.addEventListener("abort", onAbort, { once: true });
23
+ return Effect.sync(() => signal.removeEventListener("abort", onAbort));
24
+ });
25
+ export const make = (options = {}) => {
26
+ const runtime = ManagedRuntime.make(AIClient.layerWith(options.layer ?? RequestExecutor.fetchLayer));
27
+ /** Run any package Effect (for example `LLMClient.compact(...)`) inside this runtime. */
28
+ const run = (effect, options) => runtime.runPromise(Effect.raceFirst(effect, abortEffect(options?.signal)));
29
+ const iterate = (stream, options) => Stream.toAsyncIterable(Stream.unwrap(runtime.contextEffect.pipe(Effect.map((context) => stream.pipe(Stream.interruptWhen(abortEffect(options?.signal)), Stream.provideContext(context))))));
30
+ const handle = (generation) => ({
31
+ ...generation.snapshot,
32
+ token: generation.token,
33
+ await: (options) => run(generation.await({ poll: options?.poll }), options),
34
+ events: (options) => iterate(generation.events({ poll: options?.poll }), options),
35
+ result: (options) => run(generation.result(), options),
36
+ refresh: (options) => run(generation.refresh(), options).then(handle),
37
+ cancel: (options) => run(generation.cancel(), options),
38
+ });
39
+ return {
40
+ run,
41
+ /** Decoded asset bytes, downloading `url` sources through the executor. */
42
+ bytes: (asset, options) => run(asset.bytes(), options),
43
+ base64: (asset, options) => run(asset.base64(), options),
44
+ /** Pull a `url` asset into owned bytes before the provider URL expires. */
45
+ materialize: (asset, options) => run(asset.materialize(), options),
46
+ /** Read a file into an asset like `Media.file`: sniffed media type, then the extension's. */
47
+ file: async (path, options) => {
48
+ const { readFile } = await import("node:fs/promises");
49
+ return run(Effect.tryPromise({
50
+ try: (signal) => readFile(path, { signal }),
51
+ catch: (cause) => fileError(`Failed to read media file ${path}`, cause),
52
+ }).pipe(Effect.map((buffer) => {
53
+ const data = new Uint8Array(buffer);
54
+ return Media.bytes(data, fileMediaType(data, path), options);
55
+ })), options);
56
+ },
57
+ /** Write an asset's bytes like `Media.write`, downloading `url` sources through the executor. */
58
+ write: async (asset, path, options) => {
59
+ const { writeFile } = await import("node:fs/promises");
60
+ return run(asset.bytes().pipe(Effect.flatMap((data) => Effect.tryPromise({
61
+ try: (signal) => writeFile(path, data, { signal }),
62
+ catch: (cause) => fileError(`Failed to write media file ${path}`, cause),
63
+ }))), options);
64
+ },
65
+ llm: {
66
+ request: LLM.request,
67
+ generate: (input, options) => run(LLM.generate(input), options),
68
+ stream: (input, options) => iterate(LLM.stream(input), options),
69
+ },
70
+ image: {
71
+ request: Image.request,
72
+ generate: (input, options) => run(Image.generate(input, { poll: options?.poll }), options),
73
+ stream: (input, options) => iterate(Image.stream(input, { poll: options?.poll }), options),
74
+ start: (input, options) => run(Image.start(input), options).then(handle),
75
+ resume: (model, token, options) => run(Image.resume(model, token), options).then(handle),
76
+ },
77
+ video: {
78
+ request: Video.request,
79
+ start: (input, options) => run(Video.start(input), options).then(handle),
80
+ generate: (input, options) => run(Video.generate(input, { poll: options?.poll }), options),
81
+ resume: (model, token, options) => run(Video.resume(model, token), options).then(handle),
82
+ stream: (input, options) => iterate(Video.stream(input, { poll: options?.poll }), options),
83
+ },
84
+ speech: {
85
+ request: Speech.request,
86
+ generate: (input, options) => run(Speech.generate(input), options),
87
+ stream: (input, options) => iterate(Speech.stream(input), options),
88
+ },
89
+ transcription: {
90
+ request: Transcription.request,
91
+ generate: (input, options) => run(Transcription.generate(input, { poll: options?.poll }), options),
92
+ stream: (input, options) => iterate(Transcription.stream(input, { poll: options?.poll }), options),
93
+ start: (input, options) => run(Transcription.start(input), options).then(handle),
94
+ resume: (model, token, options) => run(Transcription.resume(model, token), options).then(handle),
95
+ },
96
+ decision: {
97
+ request: Decision.request,
98
+ generate: (input, options) => run(Decision.generate(input), options),
99
+ },
100
+ dispose: () => runtime.dispose(),
101
+ };
102
+ };
103
+ const fileError = (message, cause) => new AIError({ reason: new InvalidRequestError({ message, cause }) });
104
+ /** Default client over `RequestExecutor.fetchLayer` for scripts; the runtime builds its layer on first use. */
105
+ export const ai = make();
106
+ export * as AI from "./index.js";
@@ -172,15 +172,15 @@ export declare const protocol: Protocol<{
172
172
  } | undefined;
173
173
  readonly top_logprobs?: number | undefined;
174
174
  readonly truncation?: "auto" | "disabled" | undefined;
175
- readonly service_tier?: import("../index.js").OpenString<"auto" | "default" | "flex" | "priority"> | undefined;
175
+ readonly service_tier?: import("../effect.js").OpenString<"auto" | "default" | "flex" | "priority"> | undefined;
176
176
  readonly prompt_cache_key?: string | undefined;
177
- readonly include?: readonly import("../index.js").OpenString<"code_interpreter_call.outputs" | "computer_call_output.output.image_url" | "file_search_call.results" | "message.input_image.image_url" | "message.output_text.logprobs" | "reasoning.encrypted_content" | "web_search_call.action.sources" | "web_search_call.results">[] | undefined;
177
+ readonly include?: readonly import("../effect.js").OpenString<"code_interpreter_call.outputs" | "computer_call_output.output.image_url" | "file_search_call.results" | "message.input_image.image_url" | "message.output_text.logprobs" | "reasoning.encrypted_content" | "web_search_call.action.sources" | "web_search_call.results">[] | undefined;
178
178
  readonly reasoning?: {
179
179
  readonly effort?: OpenResponsesOptions.ReasoningEffort | undefined;
180
180
  readonly summary?: "auto" | "concise" | "detailed" | undefined;
181
181
  } | undefined;
182
182
  readonly text?: {
183
- readonly verbosity?: import("../index.js").OpenString<"high" | "low" | "medium"> | undefined;
183
+ readonly verbosity?: import("../effect.js").OpenString<"high" | "low" | "medium"> | undefined;
184
184
  } | undefined;
185
185
  readonly max_output_tokens?: number | undefined;
186
186
  readonly max_tool_calls?: number | undefined;
@@ -127,7 +127,7 @@ export declare const route: Route<{
127
127
  } | undefined;
128
128
  readonly store?: boolean | undefined;
129
129
  readonly prompt_cache_key?: string | undefined;
130
- readonly reasoning_effort?: import("../index.js").ReasoningEffort | undefined;
130
+ readonly reasoning_effort?: import("../effect.js").ReasoningEffort | undefined;
131
131
  readonly tool_stream?: boolean | undefined;
132
132
  readonly max_completion_tokens?: number | undefined;
133
133
  readonly max_tokens?: number | undefined;
@@ -9,7 +9,7 @@ export declare const route: Route<{
9
9
  readonly input: readonly ({
10
10
  readonly type: "configuration_update";
11
11
  readonly reasoning: {
12
- readonly effort: import("../index.js").ReasoningEffort;
12
+ readonly effort: import("../effect.js").ReasoningEffort;
13
13
  };
14
14
  } | {
15
15
  readonly type: "message";
@@ -166,15 +166,15 @@ export declare const route: Route<{
166
166
  } | undefined;
167
167
  readonly top_logprobs?: number | undefined;
168
168
  readonly truncation?: "auto" | "disabled" | undefined;
169
- readonly service_tier?: import("../index.js").OpenString<"auto" | "default" | "flex" | "priority"> | undefined;
169
+ readonly service_tier?: import("../effect.js").OpenString<"auto" | "default" | "flex" | "priority"> | undefined;
170
170
  readonly prompt_cache_key?: string | undefined;
171
- readonly include?: readonly import("../index.js").OpenString<"code_interpreter_call.outputs" | "computer_call_output.output.image_url" | "file_search_call.results" | "message.input_image.image_url" | "message.output_text.logprobs" | "reasoning.encrypted_content" | "web_search_call.action.sources" | "web_search_call.results">[] | undefined;
171
+ readonly include?: readonly import("../effect.js").OpenString<"code_interpreter_call.outputs" | "computer_call_output.output.image_url" | "file_search_call.results" | "message.input_image.image_url" | "message.output_text.logprobs" | "reasoning.encrypted_content" | "web_search_call.action.sources" | "web_search_call.results">[] | undefined;
172
172
  readonly reasoning?: {
173
- readonly effort?: import("../index.js").ReasoningEffort | undefined;
173
+ readonly effort?: import("../effect.js").ReasoningEffort | undefined;
174
174
  readonly summary?: "auto" | "concise" | "detailed" | undefined;
175
175
  } | undefined;
176
176
  readonly text?: {
177
- readonly verbosity?: import("../index.js").OpenString<"high" | "low" | "medium"> | undefined;
177
+ readonly verbosity?: import("../effect.js").OpenString<"high" | "low" | "medium"> | undefined;
178
178
  } | undefined;
179
179
  readonly max_output_tokens?: number | undefined;
180
180
  readonly max_tool_calls?: number | undefined;
@@ -30,5 +30,5 @@ export declare const nearest: (shape: Shape, sizes: ReadonlyArray<Size>) => Size
30
30
  /** Reports a size more than 2% off the requested aspect or more than √2 per edge off the requested tier. */
31
31
  export declare const notice: (name: string, shape: Shape, size: Size) => Media.Notice | undefined;
32
32
  /** Free width and height that must honor the shape; a size that misses it fails instead of returning a notice. */
33
- export declare const exact: (route: MediaProtocol.Identity, shape: Shape, limits: Limits) => Effect.Effect<undefined, never, never> | Effect.Effect<never, import("../../index.js").AIError, never> | Effect.Effect<Size, never, never>;
33
+ export declare const exact: (route: MediaProtocol.Identity, shape: Shape, limits: Limits) => Effect.Effect<undefined, never, never> | Effect.Effect<never, import("../../effect.js").AIError, never> | Effect.Effect<Size, never, never>;
34
34
  export * as ImageSizing from "./image-sizing.js";
@@ -9,7 +9,7 @@ export declare const Service: typeof SpeechClientService;
9
9
  export type Service = SpeechClientService;
10
10
  export declare const SpeechClient: {
11
11
  readonly layer: import("effect/Layer").Layer<SpeechClientService, never, import("./route/executor-service.js").RequestExecutorService>;
12
- readonly generate: (request: SpeechRequestFor, options?: import("./generation.js").AwaitOptions) => import("effect/Effect").Effect<SpeechResponse, import("./index.js").AIError, SpeechClientService>;
12
+ readonly generate: (request: SpeechRequestFor, options?: import("./generation.js").AwaitOptions) => import("effect/Effect").Effect<SpeechResponse, import("./effect.js").AIError, SpeechClientService>;
13
13
  readonly stream: (request: SpeechRequestFor, options?: import("./generation.js").AwaitOptions) => import("effect/Stream").Stream<{
14
14
  readonly type: "audio-delta";
15
15
  readonly chunk: Uint8Array<ArrayBufferLike>;
@@ -58,11 +58,11 @@ export declare const SpeechClient: {
58
58
  readonly [x: string]: unknown;
59
59
  };
60
60
  } | undefined;
61
- } | import("./generation.js").Observation, import("./index.js").AIError, SpeechClientService>;
62
- readonly start: (request: SpeechRequestFor) => import("effect/Effect").Effect<import("./generation.js").Generation<SpeechResponse>, import("./index.js").AIError, SpeechClientService>;
61
+ } | import("./generation.js").Observation, import("./effect.js").AIError, SpeechClientService>;
62
+ readonly start: (request: SpeechRequestFor) => import("effect/Effect").Effect<import("./generation.js").Generation<SpeechResponse>, import("./effect.js").AIError, SpeechClientService>;
63
63
  readonly resume: (model: import("./speech.js").SpeechModel<{
64
64
  readonly [x: string]: unknown;
65
- }>, token: unknown) => import("effect/Effect").Effect<import("./generation.js").Generation<SpeechResponse>, import("./index.js").AIError, SpeechClientService>;
65
+ }>, token: unknown) => import("effect/Effect").Effect<import("./generation.js").Generation<SpeechResponse>, import("./effect.js").AIError, SpeechClientService>;
66
66
  readonly Service: typeof SpeechClientService;
67
67
  };
68
68
  export {};
@@ -9,7 +9,7 @@ export declare const Service: typeof TranscriptionClientService;
9
9
  export type Service = TranscriptionClientService;
10
10
  export declare const TranscriptionClient: {
11
11
  readonly layer: import("effect/Layer").Layer<TranscriptionClientService, never, import("./route/executor-service.js").RequestExecutorService>;
12
- readonly generate: (request: TranscriptionRequestFor, options?: import("./generation.js").AwaitOptions) => import("effect/Effect").Effect<TranscriptionResponse, import("./index.js").AIError, TranscriptionClientService>;
12
+ readonly generate: (request: TranscriptionRequestFor, options?: import("./generation.js").AwaitOptions) => import("effect/Effect").Effect<TranscriptionResponse, import("./effect.js").AIError, TranscriptionClientService>;
13
13
  readonly stream: (request: TranscriptionRequestFor, options?: import("./generation.js").AwaitOptions) => import("effect/Stream").Stream<{
14
14
  readonly type: "text-delta";
15
15
  readonly delta: string;
@@ -74,11 +74,11 @@ export declare const TranscriptionClient: {
74
74
  };
75
75
  } | undefined;
76
76
  readonly type: "finish";
77
- } | import("./generation.js").Observation, import("./index.js").AIError, TranscriptionClientService>;
78
- readonly start: (request: TranscriptionRequestFor) => import("effect/Effect").Effect<import("./generation.js").Generation<TranscriptionResponse>, import("./index.js").AIError, TranscriptionClientService>;
77
+ } | import("./generation.js").Observation, import("./effect.js").AIError, TranscriptionClientService>;
78
+ readonly start: (request: TranscriptionRequestFor) => import("effect/Effect").Effect<import("./generation.js").Generation<TranscriptionResponse>, import("./effect.js").AIError, TranscriptionClientService>;
79
79
  readonly resume: (model: import("./transcription.js").TranscriptionModel<{
80
80
  readonly [x: string]: unknown;
81
- }>, token: unknown) => import("effect/Effect").Effect<import("./generation.js").Generation<TranscriptionResponse>, import("./index.js").AIError, TranscriptionClientService>;
81
+ }>, token: unknown) => import("effect/Effect").Effect<import("./generation.js").Generation<TranscriptionResponse>, import("./effect.js").AIError, TranscriptionClientService>;
82
82
  readonly Service: typeof TranscriptionClientService;
83
83
  };
84
84
  export {};
@@ -9,7 +9,7 @@ export declare const Service: typeof VideoClientService;
9
9
  export type Service = VideoClientService;
10
10
  export declare const VideoClient: {
11
11
  readonly layer: import("effect/Layer").Layer<VideoClientService, never, import("./route/executor-service.js").RequestExecutorService>;
12
- readonly generate: (request: VideoRequestFor, options?: import("./generation.js").AwaitOptions) => import("effect/Effect").Effect<VideoResponse, import("./index.js").AIError, VideoClientService>;
12
+ readonly generate: (request: VideoRequestFor, options?: import("./generation.js").AwaitOptions) => import("effect/Effect").Effect<VideoResponse, import("./effect.js").AIError, VideoClientService>;
13
13
  readonly stream: (request: VideoRequestFor, options?: import("./generation.js").AwaitOptions) => import("effect/Stream").Stream<{
14
14
  readonly type: "video";
15
15
  readonly index: number;
@@ -51,11 +51,11 @@ export declare const VideoClient: {
51
51
  readonly [x: string]: unknown;
52
52
  };
53
53
  } | undefined;
54
- } | import("./generation.js").Observation, import("./index.js").AIError, VideoClientService>;
55
- readonly start: (request: VideoRequestFor) => import("effect/Effect").Effect<import("./generation.js").Generation<VideoResponse>, import("./index.js").AIError, VideoClientService>;
54
+ } | import("./generation.js").Observation, import("./effect.js").AIError, VideoClientService>;
55
+ readonly start: (request: VideoRequestFor) => import("effect/Effect").Effect<import("./generation.js").Generation<VideoResponse>, import("./effect.js").AIError, VideoClientService>;
56
56
  readonly resume: (model: import("./video.js").VideoModel<{
57
57
  readonly [x: string]: unknown;
58
- }>, token: unknown) => import("effect/Effect").Effect<import("./generation.js").Generation<VideoResponse>, import("./index.js").AIError, VideoClientService>;
58
+ }>, token: unknown) => import("effect/Effect").Effect<import("./generation.js").Generation<VideoResponse>, import("./effect.js").AIError, VideoClientService>;
59
59
  readonly Service: typeof VideoClientService;
60
60
  };
61
61
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
- "version": "0.0.0-dev-21801",
3
+ "version": "0.0.0-dev-21803",
4
4
  "name": "@opencode/ai",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -19,9 +19,9 @@
19
19
  "import": "./dist/index.js",
20
20
  "types": "./dist/index.d.ts"
21
21
  },
22
- "./promise": {
23
- "import": "./dist/promise.js",
24
- "types": "./dist/promise.d.ts"
22
+ "./effect": {
23
+ "import": "./dist/effect.js",
24
+ "types": "./dist/effect.d.ts"
25
25
  },
26
26
  "./testing": {
27
27
  "import": "./dist/testing.js",
@@ -35,7 +35,7 @@
35
35
  "devDependencies": {
36
36
  "@clack/prompts": "1.0.0-alpha.1",
37
37
  "@effect/platform-node": "4.0.1",
38
- "@opencode/http-recorder": "0.0.0-dev-21801",
38
+ "@opencode/http-recorder": "0.0.0-dev-21803",
39
39
  "@tsconfig/bun": "1.0.9",
40
40
  "@types/bun": "1.4.3",
41
41
  "@typescript/native": "npm:typescript@7.1.0-beta",
@@ -45,7 +45,7 @@
45
45
  "@aws-sdk/credential-providers": "3.1057.0",
46
46
  "@smithy/eventstream-codec": "4.2.14",
47
47
  "@smithy/util-utf8": "4.2.2",
48
- "@opencode/schema": "0.0.0-dev-21801",
48
+ "@opencode/schema": "0.0.0-dev-21803",
49
49
  "aws4fetch": "1.0.20",
50
50
  "effect": "4.0.1",
51
51
  "google-auth-library": "10.5.0"