refactor(ai): make Promise the root entry and move Effect to @opencode/ai/effect (#58847)

This commit is contained in:
Aiden Cline 2026-10-24 22:18:23 -05:00 • committed by GitHub
parent ddd9e103fe
commit dad29a8991
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
265 changed files with 678 additions and 670 deletions

View file

@ -12,7 +12,7 @@
- Use `HttpClient`, `Stream`, and Schema codecs instead of `fetch`, async generators, and `JSON.parse`/`JSON.stringify`.
- `return yield* new MyError(...)` instead of `Effect.fail(...)`; `Effect.void` instead of `Effect.succeed(undefined)`.
- Only `src/promise.ts` may use Promises.
- Only `src/index.ts` (the Promise entry) may use Promises.
## Tests

View file

@ -2,9 +2,32 @@
Schema-first APIs for text, images, video, speech, and transcription, built with Effect.
`@opencode/ai` is the Promise API: one managed runtime behind the LLM and media methods, plus asset helpers. `ai.file`
and `ai.write` load `node:fs/promises` on first use, so no Effect `FileSystem` is needed:
```ts
import { AI } from "@opencode/ai"
import { OpenAI } from "@opencode/ai/providers"
const openai = OpenAI.configure({ apiKey: process.env.OPENAI_API_KEY })
const ai = AI.make()
const input = { model: openai.responses("gpt-4o-mini"), prompt: "Say hello." }
const response = await ai.llm.generate(input)
console.log(response.text)
const generated = await ai.image.generate({ model: openai.image("gpt-image-2"), prompt: "A lighthouse" })
await ai.write(generated.image, "./lighthouse.png") // also ai.file(path), ai.bytes(asset), ai.base64(asset), ai.materialize(asset)
for await (const event of ai.llm.stream(ai.llm.request(input))) {
// LLMEvent
}
await ai.dispose()
```
The Effect API lives at `@opencode/ai/effect`. Its methods return `Effect` or `Stream`:
```ts
import { Effect } from "effect"
import { AIClient, LLM } from "@opencode/ai"
import { AIClient, LLM } from "@opencode/ai/effect"
import { OpenAI } from "@opencode/ai/providers"
const openai = OpenAI.configure({ apiKey: process.env.OPENAI_API_KEY })
@ -64,7 +87,7 @@ provider's image route from the model and returns `Media.Asset`s with lazily dec
```ts
import { NodeFileSystem } from "@effect/platform-node"
import { Image, Media } from "@opencode/ai"
import { Image, Media } from "@opencode/ai/effect"
const image = Effect.gen(function* () {
const response = yield* Image.generate({
@ -96,30 +119,13 @@ const logged = RequestExecutor.middleware((request, next) =>
const everything = AIClient.layerWith(logged) // or AI.make({ layer: logged })
```
Prefer promises? `@opencode/ai/promise` exposes the same LLM and media APIs over one managed runtime, plus asset
helpers; `ai.file` and `ai.write` load `node:fs/promises` on first use, so no Effect `FileSystem` is needed:
```ts
import { AI } from "@opencode/ai/promise"
const ai = AI.make()
const input = { model: openai.responses("gpt-4o-mini"), prompt: "Say hello." }
const text = await ai.llm.generate(input)
const generated = await ai.image.generate({ model: openai.image("gpt-image-2"), prompt: "A lighthouse" })
await ai.write(generated.image, "./lighthouse.png") // also ai.file(path), ai.bytes(asset), ai.base64(asset), ai.materialize(asset)
for await (const event of ai.llm.stream(ai.llm.request(input))) {
// LLMEvent
}
await ai.dispose()
```
## Venice AI
`Venice` provides native Chat Completions with streaming tools and reasoning. `model` and `chat`
select the same API; credentials default to `VENICE_API_KEY`.
```ts
import { LLM } from "@opencode/ai"
import { LLM } from "@opencode/ai/effect"
import { Venice } from "@opencode/ai/providers"
import { Effect } from "effect"
@ -155,7 +161,7 @@ values, or a `score` on ordered levels, each with the probability of every optio
```ts
import { Effect } from "effect"
import { Decision, DecisionClient, Media } from "@opencode/ai"
import { Decision, DecisionClient, Media } from "@opencode/ai/effect"
import { OpenAI } from "@opencode/ai/providers"
const program = Decision.generate({
@ -223,7 +229,7 @@ Chat Completions (`.model` or `.chat`), Anthropic-compatible Messages (`.message
Responses (`.responses`). These routes use HTTP/SSE.
```ts
import { LLM } from "@opencode/ai"
import { LLM } from "@opencode/ai/effect"
import { Alibaba } from "@opencode/ai/providers"
const alibaba = Alibaba.configure({
@ -302,7 +308,7 @@ pass through. Results are URLs that Model Studio keeps for 24 hours. A complete
the existing `.image(...)` selector provides image generation.
```ts
import { LLM } from "@opencode/ai"
import { LLM } from "@opencode/ai/effect"
import { ZAI, ZAICodingPlan } from "@opencode/ai/providers"
const zai = ZAI.configure({ apiKey: process.env.ZAI_API_KEY })
@ -368,7 +374,7 @@ Package entrypoints are `@opencode/ai/providers/zai`, `zai/chat`, `zai-coding-pl
Moonshot defaults to Chat Completions, with Messages and Responses selectors for Kimi K3:
```ts
import { LLM } from "@opencode/ai"
import { LLM } from "@opencode/ai/effect"
import { Moonshot } from "@opencode/ai/providers"
const moonshot = Moonshot.configure({ apiKey: process.env.MOONSHOT_API_KEY })
@ -430,7 +436,7 @@ MiniMax defaults to its Messages API and reads `MINIMAX_API_KEY` when `apiKey` i
```ts
import { Effect } from "effect"
import { AIClient, LLM } from "@opencode/ai"
import { AIClient, LLM } from "@opencode/ai/effect"
import { MiniMax } from "@opencode/ai/providers"
const minimax = MiniMax.configure({ apiKey: process.env.MINIMAX_API_KEY })
@ -507,7 +513,7 @@ citations or separate result blocks. Retain `response.message` for either API's
Use `Image.generate` for one-off generation or editing:
```ts
import { Image, Media } from "@opencode/ai"
import { Image, Media } from "@opencode/ai/effect"
const generation = Image.generate({
model: meta.image("muse-image-1.0"),
@ -543,7 +549,7 @@ selected model. `aspect` is a ratio such as `"16:9"`; `resolution` is a size tie
at that ratio, `"2K"` about 2048×2048). Exact sizes, seeds, masks, and output formats are provider options:
```ts
import { Image, Media } from "@opencode/ai"
import { Image, Media } from "@opencode/ai/effect"
import { OpenAI } from "@opencode/ai/providers"
const openai = OpenAI.configure({ apiKey: process.env.OPENAI_API_KEY })
@ -729,7 +735,7 @@ and emits `image-partial` events before each final `image`; `Image.generate` kee
```ts
import { Stream } from "effect"
import { ImageEvent } from "@opencode/ai"
import { ImageEvent } from "@opencode/ai/effect"
const previews = Image.stream({
model: openai.image("gpt-image-2"),
@ -813,7 +819,7 @@ Common fields (`frames`, `references`, `video`, `durationSeconds`, `aspect`, `re
under `providerOptions`, inferred from the selected model.
```ts
import { Video } from "@opencode/ai"
import { Video } from "@opencode/ai/effect"
import { Google, Runway } from "@opencode/ai/providers"
const google = Google.configure({ apiKey: process.env.GOOGLE_GENERATIVE_AI_API_KEY })
@ -885,7 +891,7 @@ aborting rejects the Promise or throws from the `for await` loop with `signal.re
unless `abort(reason)` passed one), while `break` stops a stream without throwing.
```ts
import { ai } from "@opencode/ai/promise"
import { ai } from "@opencode/ai"
const generation = await ai.video.start({ model, prompt })
for await (const event of generation.events({ poll: { interval: 10_000 } })) console.log(event.type)
@ -902,7 +908,7 @@ selectors on the `OpenAI`, `Google` (Gemini TTS), `ElevenLabs`, `Cartesia`, and
before any network call; provider-native controls live under `providerOptions`, inferred from the selected model.
```ts
import { Media, Speech, SpeechClient, SpeechEvent } from "@opencode/ai"
import { Media, Speech, SpeechClient, SpeechEvent } from "@opencode/ai/effect"
import { ElevenLabs, OpenAI } from "@opencode/ai/providers"
const openai = OpenAI.configure({ apiKey: process.env.OPENAI_API_KEY })
@ -967,7 +973,7 @@ Provider notes:
The promise client mirrors the Effect API; `ai.speech.stream` is an `AsyncIterable`.
```ts
import { ai } from "@opencode/ai/promise"
import { ai } from "@opencode/ai"
const response = await ai.speech.generate({ model, text: "Hello from OpenCode.", voice: "coral" })
await ai.write(response.audio, "hello.mp3")
@ -989,7 +995,7 @@ any network call; a route may return more than asked.
```ts
import { Console, Effect, Stream } from "effect"
import { Media, Transcription, TranscriptionEvent } from "@opencode/ai"
import { Media, Transcription, TranscriptionEvent } from "@opencode/ai/effect"
import { AssemblyAI, Deepgram, OpenAI } from "@opencode/ai/providers"
const openai = OpenAI.configure({ apiKey: process.env.OPENAI_API_KEY })
@ -1050,6 +1056,8 @@ const transcript = await generation.await({ poll: { interval: 3_000 } })
## Public API
Everything below is exported from `@opencode/ai/effect` unless noted.
- **`LLM.request({...})`** — build a provider-neutral `LLMRequest`. Accepts ergonomic inputs (`system: string`, `prompt: string`) that normalize into the canonical Schema classes.
- **`LLM.generate` / `LLM.stream`** — run direct input or an `LLMRequest` through `LLMClient` for one-import use.
- **`Message.user(...)` / `Message.assistant(...)` / `Message.tool(...)`** — message constructors from the canonical schema model.
@ -1063,7 +1071,7 @@ const transcript = await generation.await({ poll: { interval: 3_000 } })
- **`Speech.request` / `Speech.generate` / `Speech.stream`** — text-to-speech through a provider-neutral request; `SpeechClient` is its Effect service and layer.
- **`Transcription.request` / `generate` / `stream` / `start` / `resume`** — speech-to-text over inline, streaming, and queued routes; `TranscriptionClient` is its Effect service and layer.
- **`AIClient.layer` / `AIClient.layerWith(executor)`** — every modality client plus the request executor in one layer.
- **`@opencode/ai/promise`** — `AI.make({ layer? })` and a default `ai` client exposing `llm`, `image`, `video`, `speech`, and `transcription` as Promise / `AsyncIterable` APIs, plus `file`, `write`, `bytes`, `base64`, and `materialize` for assets.
- **`@opencode/ai`** (Promise root) — `AI.make({ layer? })` and a default `ai` client exposing `llm`, `image`, `video`, `speech`, and `transcription` as Promise / `AsyncIterable` APIs, plus `file`, `write`, `bytes`, `base64`, and `materialize` for assets.
## Testing
@ -1188,7 +1196,7 @@ Enable OpenAI compaction with typed provider options:
```ts
import { Effect } from "effect"
import { LLM, LLMClient, LLMRequest, Message } from "@opencode/ai"
import { LLM, LLMClient, LLMRequest, Message } from "@opencode/ai/effect"
import { OpenAI } from "@opencode/ai/providers"
const request = LLM.request({
@ -1211,7 +1219,7 @@ const continued = Effect.gen(function* () {
A compaction part has `provider` and exactly one representation: `encrypted` for Responses, or `text` for Anthropic. Responses also preserves the optional checkpoint `id`. These fields survive message serialization without becoming visible assistant text. Sending a checkpoint to another provider or an incompatible API fails rather than silently losing context.
```ts
import { CompactionPart, ProviderID } from "@opencode/ai"
import { CompactionPart, ProviderID } from "@opencode/ai/effect"
CompactionPart.make({ provider: ProviderID.make("openai"), id: "cmp_123", encrypted: "..." })
CompactionPart.make({ provider: ProviderID.make("anthropic"), text: "Summary of the conversation..." })
@ -1431,7 +1439,7 @@ Compose a route with `Route.make({ protocol, endpoint, auth, framing, ... })`. T
## Effect
This package is built on Effect. Public methods return `Effect` or `Stream`; provide `AIClient.layer` (or `AIClient.layerWith(executor)`) for every modality, then import the provider/protocol modules for the routes you use. The example at `example/tutorial.ts` is a runnable walkthrough.
This package is built on Effect. Public methods in `@opencode/ai/effect` return `Effect` or `Stream`; provide `AIClient.layer` (or `AIClient.layerWith(executor)`) for every modality, then import the provider/protocol modules for the routes you use. The example at `example/tutorial.ts` is a runnable walkthrough.
## See also

View file

@ -1,6 +1,6 @@
import { Config, Effect, Formatter, Schema, Stream } from "effect"
import { NodeFileSystem } from "@effect/platform-node"
import { AIClient, Image, LLM, LLMRequest, Media, Message, ProviderID, Tool, ToolRuntime } from "@opencode/ai"
import { AIClient, Image, LLM, LLMRequest, Media, Message, ProviderID, Tool, ToolRuntime } from "@opencode/ai/effect"
import { Route, Auth, Endpoint, Framing, Protocol } from "@opencode/ai/route"
import { OpenAI } from "@opencode/ai/providers"

View file

@ -16,7 +16,7 @@
],
"exports": {
".": "./src/index.ts",
"./promise": "./src/promise.ts",
"./effect": "./src/effect.ts",
"./testing": "./src/testing.ts",
"./*": "./src/*.ts"
},

153
packages/ai/src/effect.ts Normal file
View file

@ -0,0 +1,153 @@
export { AIClient } from "./ai-client.js"
export { LLMClient } from "./route/client.js"
export { ImageClient } from "./image-client.js"
export { Auth } from "./route/auth.js"
export { Provider } from "./provider.js"
export { ProviderPackage } from "./provider-package.js"
export { isContextOverflow, isContextOverflowFailure, isRetryable } from "./provider-error.js"
export type {
RouteLanguageModelInput,
RouteRoutedLanguageModelInput,
Interface as LLMClientShape,
LLMClientService,
} from "./route/client.js"
export * from "./schema/index.js"
export { ImageAspectRatio, ImageEvent, ImageModel, ImageModelSchema, ImageRequest, ImageResponse } from "./image.js"
export type {
ImageModelOptions,
ImageOptions,
ImageRequestFor,
ImageRequestInput,
ImageResolution,
ImageRoute,
} from "./image.js"
export { Image } from "./image.js"
export { VideoClient } from "./video-client.js"
export {
VideoAspectRatio,
VideoEvent,
VideoFrames,
VideoModel,
VideoModelSchema,
VideoRequest,
VideoResponse,
} from "./video.js"
export type {
VideoModelOptions,
VideoOptions,
VideoRequestFor,
VideoRequestInput,
VideoResolution,
VideoRoute,
} from "./video.js"
export { Video } from "./video.js"
export { SpeechClient } from "./speech-client.js"
export {
SpeechEvent,
SpeechModel,
SpeechModelSchema,
SpeechRequest,
SpeechResponse,
SpeechTimestamp,
SpeechVoice,
} from "./speech.js"
export type {
SpeechFormat,
SpeechModelOptions,
SpeechOptions,
SpeechRequestFor,
SpeechRequestInput,
SpeechRoute,
} from "./speech.js"
export { Speech } from "./speech.js"
export { TranscriptionClient } from "./transcription-client.js"
export {
TranscriptionEvent,
TranscriptionModel,
TranscriptionModelSchema,
TranscriptionRequest,
TranscriptionResponse,
TranscriptionSegment,
TranscriptionTimestamps,
TranscriptionWord,
} from "./transcription.js"
export type {
TranscriptionModelOptions,
TranscriptionOptions,
TranscriptionRequestFor,
TranscriptionRequestInput,
TranscriptionRoute,
} from "./transcription.js"
export { Transcription } from "./transcription.js"
export { DecisionClient } from "./decision-client.js"
export {
ChoiceAnswer,
ChoiceQuestion,
ChoiceValue,
DecisionAnswer,
DecisionJsonPart,
DecisionMediaPart,
DecisionModel,
DecisionModelCompatibility,
DecisionModelSchema,
DecisionPart,
DecisionQuestion,
DecisionRequest,
DecisionResponse,
DecisionTextPart,
ErrorAnswer,
PredicateAnswer,
PredicateQuestion,
ScoreAnswer,
ScoreQuestion,
} from "./decision.js"
export type {
AnswerFor,
AnswersFor,
DecisionInput,
DecisionModelOptions,
DecisionOptions,
DecisionQuestions,
DecisionRequestFor,
DecisionRequestInput,
DecisionResponseFor,
DecisionModelRoute,
} from "./decision.js"
export { Decision } from "./decision.js"
export { DecisionRoute } from "./route/decision.js"
export { Media } from "./media.js"
export { Generation } from "./generation.js"
export type {
AwaitOptions as GenerationAwaitOptions,
Event as GenerationEvent,
Poll,
Route as GenerationRoute,
Snapshot as GenerationSnapshot,
Status as GenerationStatus,
} from "./generation.js"
export { Tool, ToolFailure, toDefinitions } from "./tool.js"
export { ToolRuntime } from "./tool-runtime.js"
export type { DispatchResult as ToolDispatchResult, ToolSettlement } from "./tool-runtime.js"
export type {
AnyExecutableTool,
AnyTool,
ExecutableTool,
ExecutableTools,
Definition as ToolShape,
ToolExecute,
ToolExecuteContext,
ToolModelOutputInput,
Tools,
ToolSchema,
ToolToModelOutput,
} from "./tool.js"
export * as LLM from "./llm.js"
export type {
Definition as ProviderDefinition,
LanguageModelFactory as ProviderLanguageModelFactory,
LanguageModelOptions as ProviderLanguageModelOptions,
} from "./provider.js"
export type {
Definition as ProviderPackageDefinition,
Settings as ProviderPackageSettings,
} from "./provider-package.js"

View file

@ -1,153 +1,218 @@
export { AIClient } from "./ai-client.js"
export { LLMClient } from "./route/client.js"
export { ImageClient } from "./image-client.js"
export { Auth } from "./route/auth.js"
export { Provider } from "./provider.js"
export { ProviderPackage } from "./provider-package.js"
export { isContextOverflow, isContextOverflowFailure, isRetryable } from "./provider-error.js"
export type {
RouteLanguageModelInput,
RouteRoutedLanguageModelInput,
Interface as LLMClientShape,
LLMClientService,
} from "./route/client.js"
export * from "./schema/index.js"
export { ImageAspectRatio, ImageEvent, ImageModel, ImageModelSchema, ImageRequest, ImageResponse } from "./image.js"
export type {
ImageModelOptions,
ImageOptions,
ImageRequestFor,
ImageRequestInput,
ImageResolution,
ImageRoute,
} from "./image.js"
export { Image } from "./image.js"
export { VideoClient } from "./video-client.js"
export {
VideoAspectRatio,
VideoEvent,
VideoFrames,
VideoModel,
VideoModelSchema,
VideoRequest,
VideoResponse,
} from "./video.js"
export type {
VideoModelOptions,
VideoOptions,
VideoRequestFor,
VideoRequestInput,
VideoResolution,
VideoRoute,
} from "./video.js"
export { Video } from "./video.js"
export { SpeechClient } from "./speech-client.js"
export {
SpeechEvent,
SpeechModel,
SpeechModelSchema,
SpeechRequest,
SpeechResponse,
SpeechTimestamp,
SpeechVoice,
} from "./speech.js"
export type {
SpeechFormat,
SpeechModelOptions,
SpeechOptions,
SpeechRequestFor,
SpeechRequestInput,
SpeechRoute,
} from "./speech.js"
export { Speech } from "./speech.js"
export { TranscriptionClient } from "./transcription-client.js"
export {
TranscriptionEvent,
TranscriptionModel,
TranscriptionModelSchema,
TranscriptionRequest,
TranscriptionResponse,
TranscriptionSegment,
TranscriptionTimestamps,
TranscriptionWord,
import { Effect, Layer, ManagedRuntime, Stream } from "effect"
import { AIClient } from "./ai-client.js"
import { Decision, type DecisionModel, type DecisionQuestions, type DecisionRequestInput } from "./decision.js"
import type { AwaitOptions, Event, Generation, Snapshot } from "./generation.js"
import { Image, type ImageModel, type ImageRequest, type ImageRequestInput } from "./image.js"
import { LLM } from "./effect.js"
import { Media } from "./media.js"
import { RequestExecutor } from "./route/executor.js"
import { AIError, InvalidRequestError, LanguageModel, type LLMRequest } from "./schema/index.js"
import type { RequestInput } from "./llm.js"
import { Speech, type SpeechModel, type SpeechRequest, type SpeechRequestInput } from "./speech.js"
import {
Transcription,
type TranscriptionModel,
type TranscriptionRequest,
type TranscriptionRequestInput,
} from "./transcription.js"
export type {
TranscriptionModelOptions,
TranscriptionOptions,
TranscriptionRequestFor,
TranscriptionRequestInput,
TranscriptionRoute,
} from "./transcription.js"
export { Transcription } from "./transcription.js"
export { DecisionClient } from "./decision-client.js"
export {
ChoiceAnswer,
ChoiceQuestion,
ChoiceValue,
DecisionAnswer,
DecisionJsonPart,
DecisionMediaPart,
DecisionModel,
DecisionModelCompatibility,
DecisionModelSchema,
DecisionPart,
DecisionQuestion,
DecisionRequest,
DecisionResponse,
DecisionTextPart,
ErrorAnswer,
PredicateAnswer,
PredicateQuestion,
ScoreAnswer,
ScoreQuestion,
} from "./decision.js"
export type {
AnswerFor,
AnswersFor,
DecisionInput,
DecisionModelOptions,
DecisionOptions,
DecisionQuestions,
DecisionRequestFor,
DecisionRequestInput,
DecisionResponseFor,
DecisionModelRoute,
} from "./decision.js"
export { Decision } from "./decision.js"
export { DecisionRoute } from "./route/decision.js"
export { Media } from "./media.js"
export { Generation } from "./generation.js"
export type {
AwaitOptions as GenerationAwaitOptions,
Event as GenerationEvent,
Poll,
Route as GenerationRoute,
Snapshot as GenerationSnapshot,
Status as GenerationStatus,
} from "./generation.js"
export { Tool, ToolFailure, toDefinitions } from "./tool.js"
export { ToolRuntime } from "./tool-runtime.js"
export type { DispatchResult as ToolDispatchResult, ToolSettlement } from "./tool-runtime.js"
export type {
AnyExecutableTool,
AnyTool,
ExecutableTool,
ExecutableTools,
Definition as ToolShape,
ToolExecute,
ToolExecuteContext,
ToolModelOutputInput,
Tools,
ToolSchema,
ToolToModelOutput,
} from "./tool.js"
export * as LLM from "./llm.js"
export type {
Definition as ProviderDefinition,
LanguageModelFactory as ProviderLanguageModelFactory,
LanguageModelOptions as ProviderLanguageModelOptions,
} from "./provider.js"
export type {
Definition as ProviderPackageDefinition,
Settings as ProviderPackageSettings,
} from "./provider-package.js"
import { fileMediaType } from "./utils/media-type.js"
import { Video, type VideoModel, type VideoRequest, type VideoRequestInput } from "./video.js"
/**
* Promise-first entrypoint for scripts and non-Effect callers. One `ManagedRuntime` hosts the LLM, image, video, speech,
* transcription and decision clients over a request executor; every method runs the corresponding Effect API and rethrows
* `AIError` unchanged. `file` and `write` load `node:fs/promises` on first use, so importing this module does not.
*/
export interface Options {
/** Executor layer; defaults to `RequestExecutor.fetchLayer`. Inject a recorder or `RequestExecutor.middleware(fn)` here. */
readonly layer?: Layer.Layer<RequestExecutor.Service>
}
export interface RunOptions {
readonly signal?: AbortSignal
}
export type Services = AIClient.Services
/**
* Promise view of a `Generation`. Its fields are a snapshot taken when the handle was created; `refresh()` resolves to a
* new handle rather than updating this one.
*/
export type GenerationHandle<Response> = Snapshot & {
/** Serializable JSON; pass it back to `resume` from another process. */
readonly token: unknown
readonly await: (options?: AwaitOptions & RunOptions) => Promise<Response>
/** Status observations until the first terminal one, polling like `await`; abort throws `signal.reason`. */
readonly events: (options?: AwaitOptions & RunOptions) => AsyncIterable<Event>
/** The result without polling; fails when the generation has not completed. */
readonly result: (options?: RunOptions) => Promise<Response>
readonly refresh: (options?: RunOptions) => Promise<GenerationHandle<Response>>
readonly cancel: (options?: RunOptions) => Promise<void>
}
// Fails with `signal.reason` so aborted calls reject and aborted streams throw like `fetch`: an `AbortError` by default.
const abortEffect = (signal: AbortSignal | undefined) =>
signal === undefined
? Effect.never
: Effect.callback<never, unknown>((resume) => {
if (signal.aborted) {
resume(Effect.fail(signal.reason))
return
}
const onAbort = () => resume(Effect.fail(signal.reason))
signal.addEventListener("abort", onAbort, { once: true })
return Effect.sync(() => signal.removeEventListener("abort", onAbort))
})
export const make = (options: Options = {}) => {
const runtime = ManagedRuntime.make(AIClient.layerWith(options.layer ?? RequestExecutor.fetchLayer))
/** Run any package Effect (for example `LLMClient.compact(...)`) inside this runtime. */
const run = <A, E>(effect: Effect.Effect<A, E, Services>, options?: RunOptions) =>
runtime.runPromise(Effect.raceFirst(effect, abortEffect(options?.signal)))
const iterate = <A, E>(stream: Stream.Stream<A, E, Services>, options?: RunOptions): AsyncIterable<A> =>
Stream.toAsyncIterable(
Stream.unwrap(
runtime.contextEffect.pipe(
Effect.map(
(context): Stream.Stream<A, unknown> =>
stream.pipe(Stream.interruptWhen(abortEffect(options?.signal)), Stream.provideContext(context)),
),
),
),
)
const handle = <Response>(generation: Generation<Response>): GenerationHandle<Response> => ({
...generation.snapshot,
token: generation.token,
await: (options) => run(generation.await({ poll: options?.poll }), options),
events: (options) => iterate(generation.events({ poll: options?.poll }), options),
result: (options) => run(generation.result(), options),
refresh: (options) => run(generation.refresh(), options).then(handle),
cancel: (options) => run(generation.cancel(), options),
})
return {
run,
/** Decoded asset bytes, downloading `url` sources through the executor. */
bytes: (asset: Media.Asset, options?: RunOptions) => run(asset.bytes(), options),
base64: (asset: Media.Asset, options?: RunOptions) => run(asset.base64(), options),
/** Pull a `url` asset into owned bytes before the provider URL expires. */
materialize: (asset: Media.Asset, options?: RunOptions) => run(asset.materialize(), options),
/** Read a file into an asset like `Media.file`: sniffed media type, then the extension's. */
file: async (path: string, options?: Media.AssetOptions & RunOptions) => {
const { readFile } = await import("node:fs/promises")
return run(
Effect.tryPromise({
try: (signal) => readFile(path, { signal }),
catch: (cause) => fileError(`Failed to read media file ${path}`, cause),
}).pipe(
Effect.map((buffer) => {
const data = new Uint8Array(buffer)
return Media.bytes(data, fileMediaType(data, path), options)
}),
),
options,
)
},
/** Write an asset's bytes like `Media.write`, downloading `url` sources through the executor. */
write: async (asset: Media.Asset, path: string, options?: RunOptions) => {
const { writeFile } = await import("node:fs/promises")
return run(
asset.bytes().pipe(
Effect.flatMap((data) =>
Effect.tryPromise({
try: (signal) => writeFile(path, data, { signal }),
catch: (cause) => fileError(`Failed to write media file ${path}`, cause),
}),
),
),
options,
)
},
llm: {
request: LLM.request,
generate: <const Model extends LanguageModel>(input: RequestInput<Model> | LLMRequest, options?: RunOptions) =>
run(LLM.generate(input), options),
stream: <const Model extends LanguageModel>(input: RequestInput<Model> | LLMRequest, options?: RunOptions) =>
iterate(LLM.stream(input), options),
},
image: {
request: Image.request,
generate: <const Model extends ImageModel>(
input: ImageRequestInput<Model> | ImageRequest,
options?: AwaitOptions & RunOptions,
) => run(Image.generate(input, { poll: options?.poll }), options),
stream: <const Model extends ImageModel>(
input: ImageRequestInput<Model> | ImageRequest,
options?: AwaitOptions & RunOptions,
) => iterate(Image.stream(input, { poll: options?.poll }), options),
start: <const Model extends ImageModel>(input: ImageRequestInput<Model> | ImageRequest, options?: RunOptions) =>
run(Image.start(input), options).then(handle),
resume: (model: ImageModel, token: unknown, options?: RunOptions) =>
run(Image.resume(model, token), options).then(handle),
},
video: {
request: Video.request,
start: <const Model extends VideoModel>(input: VideoRequestInput<Model> | VideoRequest, options?: RunOptions) =>
run(Video.start(input), options).then(handle),
generate: <const Model extends VideoModel>(
input: VideoRequestInput<Model> | VideoRequest,
options?: AwaitOptions & RunOptions,
) => run(Video.generate(input, { poll: options?.poll }), options),
resume: (model: VideoModel, token: unknown, options?: RunOptions) =>
run(Video.resume(model, token), options).then(handle),
stream: <const Model extends VideoModel>(
input: VideoRequestInput<Model> | VideoRequest,
options?: AwaitOptions & RunOptions,
) => iterate(Video.stream(input, { poll: options?.poll }), options),
},
speech: {
request: Speech.request,
generate: <const Model extends SpeechModel>(
input: SpeechRequestInput<Model> | SpeechRequest,
options?: RunOptions,
) => run(Speech.generate(input), options),
stream: <const Model extends SpeechModel>(
input: SpeechRequestInput<Model> | SpeechRequest,
options?: RunOptions,
) => iterate(Speech.stream(input), options),
},
transcription: {
request: Transcription.request,
generate: <const Model extends TranscriptionModel>(
input: TranscriptionRequestInput<Model> | TranscriptionRequest,
options?: AwaitOptions & RunOptions,
) => run(Transcription.generate(input, { poll: options?.poll }), options),
stream: <const Model extends TranscriptionModel>(
input: TranscriptionRequestInput<Model> | TranscriptionRequest,
options?: AwaitOptions & RunOptions,
) => iterate(Transcription.stream(input, { poll: options?.poll }), options),
start: <const Model extends TranscriptionModel>(
input: TranscriptionRequestInput<Model> | TranscriptionRequest,
options?: RunOptions,
) => run(Transcription.start(input), options).then(handle),
resume: (model: TranscriptionModel, token: unknown, options?: RunOptions) =>
run(Transcription.resume(model, token), options).then(handle),
},
decision: {
request: Decision.request,
generate: <const Model extends DecisionModel, const Questions extends DecisionQuestions>(
input: DecisionRequestInput<Model, Questions>,
options?: RunOptions,
) => run(Decision.generate(input), options),
},
dispose: () => runtime.dispose(),
}
}
export type Client = ReturnType<typeof make>
const fileError = (message: string, cause: unknown) =>
new AIError({ reason: new InvalidRequestError({ message, cause }) })
/** Default client over `RequestExecutor.fetchLayer` for scripts; the runtime builds its layer on first use. */
export const ai = make()
export * as AI from "./index.js"

View file

@ -1,218 +0,0 @@
import { Effect, Layer, ManagedRuntime, Stream } from "effect"
import { AIClient } from "./ai-client.js"
import { Decision, type DecisionModel, type DecisionQuestions, type DecisionRequestInput } from "./decision.js"
import type { AwaitOptions, Event, Generation, Snapshot } from "./generation.js"
import { Image, type ImageModel, type ImageRequest, type ImageRequestInput } from "./image.js"
import { LLM } from "./index.js"
import { Media } from "./media.js"
import { RequestExecutor } from "./route/executor.js"
import { AIError, InvalidRequestError, LanguageModel, type LLMRequest } from "./schema/index.js"
import type { RequestInput } from "./llm.js"
import { Speech, type SpeechModel, type SpeechRequest, type SpeechRequestInput } from "./speech.js"
import {
Transcription,
type TranscriptionModel,
type TranscriptionRequest,
type TranscriptionRequestInput,
} from "./transcription.js"
import { fileMediaType } from "./utils/media-type.js"
import { Video, type VideoModel, type VideoRequest, type VideoRequestInput } from "./video.js"
/**
* Promise-first entrypoint for scripts and non-Effect callers. One `ManagedRuntime` hosts the LLM, image, video, speech,
* transcription and decision clients over a request executor; every method runs the corresponding Effect API and rethrows
* `AIError` unchanged. `file` and `write` load `node:fs/promises` on first use, so importing this module does not.
*/
export interface Options {
/** Executor layer; defaults to `RequestExecutor.fetchLayer`. Inject a recorder or `RequestExecutor.middleware(fn)` here. */
readonly layer?: Layer.Layer<RequestExecutor.Service>
}
export interface RunOptions {
readonly signal?: AbortSignal
}
export type Services = AIClient.Services
/**
* Promise view of a `Generation`. Its fields are a snapshot taken when the handle was created; `refresh()` resolves to a
* new handle rather than updating this one.
*/
export type GenerationHandle<Response> = Snapshot & {
/** Serializable JSON; pass it back to `resume` from another process. */
readonly token: unknown
readonly await: (options?: AwaitOptions & RunOptions) => Promise<Response>
/** Status observations until the first terminal one, polling like `await`; abort throws `signal.reason`. */
readonly events: (options?: AwaitOptions & RunOptions) => AsyncIterable<Event>
/** The result without polling; fails when the generation has not completed. */
readonly result: (options?: RunOptions) => Promise<Response>
readonly refresh: (options?: RunOptions) => Promise<GenerationHandle<Response>>
readonly cancel: (options?: RunOptions) => Promise<void>
}
// Fails with `signal.reason` so aborted calls reject and aborted streams throw like `fetch`: an `AbortError` by default.
const abortEffect = (signal: AbortSignal | undefined) =>
signal === undefined
? Effect.never
: Effect.callback<never, unknown>((resume) => {
if (signal.aborted) {
resume(Effect.fail(signal.reason))
return
}
const onAbort = () => resume(Effect.fail(signal.reason))
signal.addEventListener("abort", onAbort, { once: true })
return Effect.sync(() => signal.removeEventListener("abort", onAbort))
})
export const make = (options: Options = {}) => {
const runtime = ManagedRuntime.make(AIClient.layerWith(options.layer ?? RequestExecutor.fetchLayer))
/** Run any package Effect (for example `LLMClient.compact(...)`) inside this runtime. */
const run = <A, E>(effect: Effect.Effect<A, E, Services>, options?: RunOptions) =>
runtime.runPromise(Effect.raceFirst(effect, abortEffect(options?.signal)))
const iterate = <A, E>(stream: Stream.Stream<A, E, Services>, options?: RunOptions): AsyncIterable<A> =>
Stream.toAsyncIterable(
Stream.unwrap(
runtime.contextEffect.pipe(
Effect.map(
(context): Stream.Stream<A, unknown> =>
stream.pipe(Stream.interruptWhen(abortEffect(options?.signal)), Stream.provideContext(context)),
),
),
),
)
const handle = <Response>(generation: Generation<Response>): GenerationHandle<Response> => ({
...generation.snapshot,
token: generation.token,
await: (options) => run(generation.await({ poll: options?.poll }), options),
events: (options) => iterate(generation.events({ poll: options?.poll }), options),
result: (options) => run(generation.result(), options),
refresh: (options) => run(generation.refresh(), options).then(handle),
cancel: (options) => run(generation.cancel(), options),
})
return {
run,
/** Decoded asset bytes, downloading `url` sources through the executor. */
bytes: (asset: Media.Asset, options?: RunOptions) => run(asset.bytes(), options),
base64: (asset: Media.Asset, options?: RunOptions) => run(asset.base64(), options),
/** Pull a `url` asset into owned bytes before the provider URL expires. */
materialize: (asset: Media.Asset, options?: RunOptions) => run(asset.materialize(), options),
/** Read a file into an asset like `Media.file`: sniffed media type, then the extension's. */
file: async (path: string, options?: Media.AssetOptions & RunOptions) => {
const { readFile } = await import("node:fs/promises")
return run(
Effect.tryPromise({
try: (signal) => readFile(path, { signal }),
catch: (cause) => fileError(`Failed to read media file ${path}`, cause),
}).pipe(
Effect.map((buffer) => {
const data = new Uint8Array(buffer)
return Media.bytes(data, fileMediaType(data, path), options)
}),
),
options,
)
},
/** Write an asset's bytes like `Media.write`, downloading `url` sources through the executor. */
write: async (asset: Media.Asset, path: string, options?: RunOptions) => {
const { writeFile } = await import("node:fs/promises")
return run(
asset.bytes().pipe(
Effect.flatMap((data) =>
Effect.tryPromise({
try: (signal) => writeFile(path, data, { signal }),
catch: (cause) => fileError(`Failed to write media file ${path}`, cause),
}),
),
),
options,
)
},
llm: {
request: LLM.request,
generate: <const Model extends LanguageModel>(input: RequestInput<Model> | LLMRequest, options?: RunOptions) =>
run(LLM.generate(input), options),
stream: <const Model extends LanguageModel>(input: RequestInput<Model> | LLMRequest, options?: RunOptions) =>
iterate(LLM.stream(input), options),
},
image: {
request: Image.request,
generate: <const Model extends ImageModel>(
input: ImageRequestInput<Model> | ImageRequest,
options?: AwaitOptions & RunOptions,
) => run(Image.generate(input, { poll: options?.poll }), options),
stream: <const Model extends ImageModel>(
input: ImageRequestInput<Model> | ImageRequest,
options?: AwaitOptions & RunOptions,
) => iterate(Image.stream(input, { poll: options?.poll }), options),
start: <const Model extends ImageModel>(input: ImageRequestInput<Model> | ImageRequest, options?: RunOptions) =>
run(Image.start(input), options).then(handle),
resume: (model: ImageModel, token: unknown, options?: RunOptions) =>
run(Image.resume(model, token), options).then(handle),
},
video: {
request: Video.request,
start: <const Model extends VideoModel>(input: VideoRequestInput<Model> | VideoRequest, options?: RunOptions) =>
run(Video.start(input), options).then(handle),
generate: <const Model extends VideoModel>(
input: VideoRequestInput<Model> | VideoRequest,
options?: AwaitOptions & RunOptions,
) => run(Video.generate(input, { poll: options?.poll }), options),
resume: (model: VideoModel, token: unknown, options?: RunOptions) =>
run(Video.resume(model, token), options).then(handle),
stream: <const Model extends VideoModel>(
input: VideoRequestInput<Model> | VideoRequest,
options?: AwaitOptions & RunOptions,
) => iterate(Video.stream(input, { poll: options?.poll }), options),
},
speech: {
request: Speech.request,
generate: <const Model extends SpeechModel>(
input: SpeechRequestInput<Model> | SpeechRequest,
options?: RunOptions,
) => run(Speech.generate(input), options),
stream: <const Model extends SpeechModel>(
input: SpeechRequestInput<Model> | SpeechRequest,
options?: RunOptions,
) => iterate(Speech.stream(input), options),
},
transcription: {
request: Transcription.request,
generate: <const Model extends TranscriptionModel>(
input: TranscriptionRequestInput<Model> | TranscriptionRequest,
options?: AwaitOptions & RunOptions,
) => run(Transcription.generate(input, { poll: options?.poll }), options),
stream: <const Model extends TranscriptionModel>(
input: TranscriptionRequestInput<Model> | TranscriptionRequest,
options?: AwaitOptions & RunOptions,
) => iterate(Transcription.stream(input, { poll: options?.poll }), options),
start: <const Model extends TranscriptionModel>(
input: TranscriptionRequestInput<Model> | TranscriptionRequest,
options?: RunOptions,
) => run(Transcription.start(input), options).then(handle),
resume: (model: TranscriptionModel, token: unknown, options?: RunOptions) =>
run(Transcription.resume(model, token), options).then(handle),
},
decision: {
request: Decision.request,
generate: <const Model extends DecisionModel, const Questions extends DecisionQuestions>(
input: DecisionRequestInput<Model, Questions>,
options?: RunOptions,
) => run(Decision.generate(input), options),
},
dispose: () => runtime.dispose(),
}
}
export type Client = ReturnType<typeof make>
const fileError = (message: string, cause: unknown) =>
new AIError({ reason: new InvalidRequestError({ message, cause }) })
/** Default client over `RequestExecutor.fetchLayer` for scripts; the runtime builds its layer on first use. */
export const ai = make()
export * as AI from "./promise.js"

View file

@ -1,6 +1,6 @@
import { describe, expect, test } from "bun:test"
import { Effect, Schema, Stream } from "effect"
import { LLM, LLMRequest, LLMResponse } from "../src/index.js"
import { LLM, LLMRequest, LLMResponse } from "../src/effect.js"
import { Route, Endpoint, LLMClient, Protocol, type FramingDef } from "../src/route.js"
import { compileRequest } from "../src/route/client.js"
import { LanguageModel } from "../src/schema/index.js"

View file

@ -1,7 +1,7 @@
import { describe, expect } from "bun:test"
import { ConfigProvider, Effect } from "effect"
import { Headers } from "effect/http"
import { LLM } from "../src/index.js"
import { LLM } from "../src/effect.js"
import { Auth } from "../src/route/auth.js"
import * as OpenAIChat from "../src/protocols/openai-chat.js"
import { LanguageModel } from "../src/schema/index.js"

View file

@ -1,6 +1,6 @@
import { describe, expect, test } from "bun:test"
import { Effect } from "effect"
import { CacheHint, CompactionPart, LLM, Message, ProviderID, ToolCallPart } from "../src/index.js"
import { CacheHint, CompactionPart, LLM, Message, ProviderID, ToolCallPart } from "../src/effect.js"
import { Auth } from "../src/route.js"
import { compileRequest } from "../src/route/client.js"
import {

View file

@ -1,7 +1,7 @@
import { expect } from "bun:test"
import { Effect } from "effect"
import { FetchHttpClient } from "effect/http"
import { LLM, LLMRequest, Message } from "../src/index.js"
import { LLM, LLMRequest, Message } from "../src/effect.js"
import { LLMClient } from "../src/route/client.js"
import { OpenAI } from "../src/providers.js"
import { testEffect } from "./lib/effect.js"

View file

@ -1,7 +1,7 @@
import { expect, test } from "bun:test"
import { Effect, Schema } from "effect"
import { CompactionPart, CompactionResponse, LLMEvent, LLMResponse, Message, ProviderID } from "../src/schema/index.js"
import { LLM, LLMClient, LLMRequest, LanguageModel } from "../src/index.js"
import { LLM, LLMClient, LLMRequest, LanguageModel } from "../src/effect.js"
import { OpenAI, Anthropic } from "../src/providers.js"
import { testEffect } from "./lib/effect.js"
import { fixedResponse } from "./lib/http.js"

View file

@ -9,7 +9,7 @@ import {
ToolDefinition,
ToolNamespace,
mergeProviderOptions,
} from "../src/index.js"
} from "../src/effect.js"
import { AnthropicMessages, OpenAIChat, OpenAIResponses } from "../src/protocols.js"
import { Auth, LLMClient } from "../src/route.js"
import { compileRequest } from "../src/route/client.js"

View file

@ -7,7 +7,7 @@ import {
ToolResultPart,
type ContentPart,
type LanguageModel,
} from "../src/index.js"
} from "../src/effect.js"
export const basicContinuation = ["system", "user-text", "assistant-text", "user-follow-up"] as const
export const toolContinuation = ["tool-call", "tool-result"] as const

View file

@ -1,7 +1,7 @@
import { describe, expect } from "bun:test"
import { Effect, Layer } from "effect"
import { HttpClientRequest } from "effect/http"
import { Decision, DecisionClient, Media } from "../src/index.js"
import { Decision, DecisionClient, Media } from "../src/effect.js"
import {
Baseten,
CloudflareWorkersAI,

View file

@ -5,7 +5,7 @@ import {
DecisionModel,
type DecisionRequestInput,
type DecisionModelRoute,
} from "../src/index.js"
} from "../src/effect.js"
import type { Service } from "../src/decision-client.js"
import { Baseten, OpenAI, OpenCode, OpenRouter, TypeSafeAI } from "../src/providers.js"

View file

@ -1,6 +1,6 @@
import { describe, expect, test } from "bun:test"
import { Effect } from "effect"
import { LLM, Message, ToolCallPart } from "../src/index.js"
import { LLM, Message, ToolCallPart } from "../src/effect.js"
import { Auth, LLMClient } from "../src/route.js"
import { compileRequest } from "../src/route/client.js"
import { AnthropicMessages } from "../src/protocols/anthropic-messages.js"

View file

@ -1,5 +1,5 @@
import { describe, expect, test } from "bun:test"
import { LLM } from "../src/index.js"
import { LLM } from "../src/effect.js"
import * as OpenAIChat from "../src/protocols/openai-chat.js"
import { Endpoint } from "../src/route.js"
import { LanguageModel } from "../src/schema/index.js"

View file

@ -2,7 +2,7 @@ import { describe, expect } from "bun:test"
import { Deferred, Effect, Fiber, Layer, Ref, Stream } from "effect"
import { Headers, HttpClientError, HttpClientRequest, HttpClientResponse } from "effect/http"
import { Socket } from "effect/socket"
import { LLM, AIError, HttpContext, InvalidProviderOutputError, TransportError, isRetryable } from "../src/index.js"
import { LLM, AIError, HttpContext, InvalidProviderOutputError, TransportError, isRetryable } from "../src/effect.js"
import {
LLMClient,
RequestExecutor,

View file

@ -19,7 +19,7 @@ import {
TranscriptionClient,
Video,
VideoClient,
} from "@opencode/ai"
} from "@opencode/ai/effect"
import { Route, Protocol, RequestExecutor, WebSocketTransport } from "@opencode/ai/route"
import { Provider as ProviderSubpath } from "@opencode/ai/provider"
import {

View file

@ -1,6 +1,6 @@
import { describe, expect, test } from "bun:test"
import { Effect, Schema } from "effect"
import { LLM } from "../src/index.js"
import { LLM } from "../src/effect.js"
import * as OpenAIChat from "../src/protocols/openai-chat.js"
import { Auth } from "../src/route.js"
import { Tool, toDefinitions } from "../src/tool.js"

View file

@ -8,7 +8,7 @@ import {
type GenerationRoute,
type GenerationSnapshot,
type GenerationStatus,
} from "../src/index.js"
} from "../src/effect.js"
import { it } from "./lib/effect.js"
/** In-memory generation route whose status advances through `statuses` on every poll. */

View file

@ -1,7 +1,7 @@
import { describe, expect } from "bun:test"
import { Deferred, Effect, Fiber } from "effect"
import * as TestClock from "effect/testing/TestClock"
import { HttpOptions, LLM, mergeHttpOptions } from "../src/index.js"
import { HttpOptions, LLM, mergeHttpOptions } from "../src/effect.js"
import { LLMClient } from "../src/route.js"
import { configure } from "../src/providers/openai.js"
import { dynamicResponse } from "./lib/http.js"

View file

@ -1,7 +1,7 @@
import { describe, expect } from "bun:test"
import { Effect, Layer, Stream } from "effect"
import { HttpClientRequest } from "effect/http"
import { Image, ImageClient, Media } from "../src/index.js"
import { Image, ImageClient, Media } from "../src/effect.js"
import {
BlackForestLabs,
Fal,

View file

@ -7,7 +7,7 @@ import {
type ImageModelOptions,
type ImageOptions,
type ImageRequestFor,
} from "../src/index.js"
} from "../src/effect.js"
import type { Service } from "../src/image-client.js"
import { Anthropic, BlackForestLabs, Google, OpenAI, Stability, XAI, ZAI } from "../src/providers.js"

View file

@ -5,8 +5,8 @@ import {
type LanguageModel,
type LanguageModelProviderOptions,
type ProviderOptions,
} from "../src/index.js"
import { ai } from "../src/promise.js"
} from "../src/effect.js"
import { ai } from "../src/index.js"
import { OpenAIChat } from "../src/protocols.js"
interface ExampleOptions {

View file

@ -1,6 +1,6 @@
import { describe, expect, test } from "bun:test"
import { Effect, Schema, Stream } from "effect"
import { CacheHint, LLM, LLMEvent, LLMResponse, ToolEntry, ToolNamespace } from "../src/index.js"
import { CacheHint, LLM, LLMEvent, LLMResponse, ToolEntry, ToolNamespace } from "../src/effect.js"
import { OpenAI } from "../src/providers.js"
import * as OpenAIChat from "../src/protocols/openai-chat.js"
import * as OpenAIResponses from "../src/protocols/openai-responses.js"

View file

@ -3,7 +3,7 @@ import { NodeFileSystem } from "@effect/platform-node"
import { Effect, Ref, Schema } from "effect"
import { FileSystem } from "effect"
import { HttpClientRequest } from "effect/http"
import { AIError, LLM, Media, Message, ToolCallPart } from "../src/index.js"
import { AIError, LLM, Media, Message, ToolCallPart } from "../src/effect.js"
import { OpenAI } from "../src/providers.js"
import { compileRequest } from "../src/route/client.js"
import { it } from "./lib/effect.js"

View file

@ -4,9 +4,9 @@ import { tmpdir } from "node:os"
import { join } from "node:path"
import { Effect, Layer } from "effect"
import { HttpClientRequest } from "effect/http"
import { AIError, LLMEvent, Media, SpeechEvent, TranscriptionEvent } from "../src/index.js"
import { AIError, LLMEvent, Media, SpeechEvent, TranscriptionEvent } from "../src/effect.js"
import { RequestExecutor } from "../src/route.js"
import { AI } from "../src/promise.js"
import { AI } from "../src/index.js"
import { AssemblyAI, OpenAI, Replicate, Runway } from "../src/providers.js"
import { handlerLayer, json } from "./lib/http.js"
import { sseEvents } from "./lib/sse.js"

View file

@ -1,5 +1,5 @@
import { describe, expect, test } from "bun:test"
import { isContextOverflow, isContextOverflowFailure, isRetryable } from "../src/index.js"
import { isContextOverflow, isContextOverflowFailure, isRetryable } from "../src/effect.js"
import { AIError } from "../src/schema/index.js"
import { classifyProviderFailure, providerErrorMessage } from "../src/provider-error.js"

View file

@ -1,4 +1,4 @@
import { LLM } from "../../src/index.js"
import { LLM } from "../../src/effect.js"
import { Alibaba } from "../../src/providers.js"
const provider = Alibaba.configure({ region: "ap-southeast-1" })

View file

@ -1,4 +1,4 @@
import { LLM } from "../../src/index.js"
import { LLM } from "../../src/effect.js"
import { AnthropicCompatible } from "../../src/providers.js"
const model = AnthropicCompatible.configure({ baseURL: "https://example.com" }).model("claude")

View file

@ -1,4 +1,4 @@
import { LLM } from "../../src/index.js"
import { LLM } from "../../src/effect.js"
import { Anthropic } from "../../src/providers.js"
const model = Anthropic.provider.model("claude-sonnet-4-5")

View file

@ -1,4 +1,4 @@
import { LLM } from "../../src/index.js"
import { LLM } from "../../src/effect.js"
import { Azure } from "../../src/providers.js"
const model = Azure.configure({ resourceName: "example" }).responses("deployment")

View file

@ -6,7 +6,7 @@ import {
LLM,
LLMClient,
LLMRequest,
} from "../../src/index.js"
} from "../../src/effect.js"
import {
Anthropic,
Azure,

View file

@ -1,4 +1,4 @@
import { LLM } from "../../src/index.js"
import { LLM } from "../../src/effect.js"
import { CloudflareWorkersAI } from "../../src/providers.js"
const model = CloudflareWorkersAI.configure({ accountId: "account", apiKey: "test" }).model("model")

View file

@ -8,7 +8,7 @@ import {
LLMRequest,
Message,
ProviderID,
} from "../../src/index.js"
} from "../../src/effect.js"
import { OpenAI, Azure, XAI, Anthropic, OpenAICompatibleResponses } from "../../src/providers.js"
const openai = OpenAI.configure({

View file

@ -1,4 +1,4 @@
import { LLM } from "../../src/index.js"
import { LLM } from "../../src/effect.js"
import { GoogleVertexChat } from "../../src/providers.js"
const model = GoogleVertexChat.configure({ accessToken: "test", project: "project" }).model("gemini")

View file

@ -1,4 +1,4 @@
import { LLM } from "../../src/index.js"
import { LLM } from "../../src/effect.js"
import { GoogleVertexInteractions } from "../../src/providers.js"
import { Auth } from "../../src/route/auth.js"
import { model } from "../../src/providers/google-vertex/interactions.js"

View file

@ -1,4 +1,4 @@
import { LLM } from "../../src/index.js"
import { LLM } from "../../src/effect.js"
import { GoogleVertexMessages } from "../../src/providers.js"
const model = GoogleVertexMessages.configure({ accessToken: "test", project: "project" }).model("claude")

View file

@ -1,4 +1,4 @@
import { LLM } from "../../src/index.js"
import { LLM } from "../../src/effect.js"
import { GoogleVertexResponses } from "../../src/providers.js"
const model = GoogleVertexResponses.configure({ accessToken: "test", project: "project" }).model("gemini")

View file

@ -1,4 +1,4 @@
import { LLM } from "../../src/index.js"
import { LLM } from "../../src/effect.js"
import { GoogleVertex } from "../../src/providers.js"
const model = GoogleVertex.provider.configure({ apiKey: "test" }).model("gemini-2.5-pro")

View file

@ -1,4 +1,4 @@
import { LLM } from "../../src/index.js"
import { LLM } from "../../src/effect.js"
import { Google } from "../../src/providers.js"
const model = Google.provider.model("gemini-2.5-pro")

View file

@ -1,4 +1,4 @@
import { LLM } from "../../src/index.js"
import { LLM } from "../../src/effect.js"
import { MiniMax } from "../../src/providers.js"
const minimax = MiniMax.configure()

View file

@ -1,4 +1,4 @@
import { LLM } from "../../src/index.js"
import { LLM } from "../../src/effect.js"
import { Mistral } from "../../src/providers.js"
const selected = Mistral.provider.model("mistral-small-latest")

View file

@ -1,4 +1,4 @@
import { LLM } from "../../src/index.js"
import { LLM } from "../../src/effect.js"
import { Moonshot } from "../../src/providers.js"
const moonshot = Moonshot.configure()

View file

@ -1,4 +1,4 @@
import { LLM } from "../../src/index.js"
import { LLM } from "../../src/effect.js"
import { OpenAICompatibleResponses } from "../../src/providers.js"
const model = OpenAICompatibleResponses.configure({ baseURL: "https://example.com" }).model("model")

View file

@ -1,4 +1,4 @@
import { LLM } from "../../src/index.js"
import { LLM } from "../../src/effect.js"
import { OpenAICompatible } from "../../src/providers.js"
const model = OpenAICompatible.configure({ baseURL: "https://compatible.example/v1" }).model("test-model")

View file

@ -1,4 +1,4 @@
import { LLM } from "../../src/index.js"
import { LLM } from "../../src/effect.js"
import { OpenAI } from "../../src/providers.js"
const selected = OpenAI.responses("gpt-5")

View file

@ -1,4 +1,4 @@
import { LLM } from "../../src/index.js"
import { LLM } from "../../src/effect.js"
import { OpenRouter } from "../../src/providers.js"
const model = OpenRouter.provider.model("anthropic/claude-sonnet-4.5")

View file

@ -1,4 +1,4 @@
import { LLM } from "../../src/index.js"
import { LLM } from "../../src/effect.js"
import { Venice } from "../../src/providers.js"
LLM.request({

View file

@ -1,4 +1,4 @@
import { LLM } from "../../src/index.js"
import { LLM } from "../../src/effect.js"
import { VercelAIGateway } from "../../src/providers/vercel-ai-gateway.js"
const gateway = VercelAIGateway.configure({

View file

@ -1,4 +1,4 @@
import { LLM } from "../../src/index.js"
import { LLM } from "../../src/effect.js"
import { XAI } from "../../src/providers.js"
const model = XAI.provider.model("grok-4")

View file

@ -1,4 +1,4 @@
import { LLM } from "../../src/index.js"
import { LLM } from "../../src/effect.js"
import { ZAI, ZAICodingPlan } from "../../src/providers.js"
LLM.request({

View file

@ -1,6 +1,6 @@
import { describe, expect, test } from "bun:test"
import { model } from "@opencode/ai/providers/openai"
import { LLM } from "../src/index.js"
import { LLM } from "../src/effect.js"
import { Endpoint } from "../src/route/endpoint.js"
const configuration = (provider: string, message: string) =>

View file

@ -1,6 +1,6 @@
import { describe, expect } from "bun:test"
import { Effect } from "effect"
import { LLM, LLMEvent, LLMRequest, Message, ToolDefinition, Media } from "../../src/index.js"
import { LLM, LLMEvent, LLMRequest, Message, ToolDefinition, Media } from "../../src/effect.js"
import { Alibaba } from "../../src/providers.js"
import { LLMClient } from "../../src/route.js"
import { compileRequest } from "../../src/route/client.js"

View file

@ -1,6 +1,6 @@
import { describe, expect } from "bun:test"
import { Effect } from "effect"
import { Image, Media } from "../../src/index.js"
import { Image, Media } from "../../src/effect.js"
import { Alibaba } from "../../src/providers.js"
import { recordedTests } from "../recorded-test.js"

View file

@ -1,7 +1,7 @@
import { describe, expect } from "bun:test"
import { Effect, Layer } from "effect"
import { HttpClientRequest } from "effect/http"
import { Image, ImageClient, Media } from "../../src/index.js"
import { Image, ImageClient, Media } from "../../src/effect.js"
import { Alibaba } from "../../src/providers.js"
import { it } from "../lib/effect.js"
import { dynamicResponse, fixedResponse } from "../lib/http.js"

View file

@ -1,6 +1,6 @@
import { describe, expect } from "bun:test"
import { Effect } from "effect"
import { LLM, LLMEvent, LLMRequest, Message, ToolDefinition, type LLMResponse } from "../../src/index.js"
import { LLM, LLMEvent, LLMRequest, Message, ToolDefinition, type LLMResponse } from "../../src/effect.js"
import { Alibaba } from "../../src/providers.js"
import { LLMClient } from "../../src/route.js"
import { compileRequest } from "../../src/route/client.js"

View file

@ -1,7 +1,7 @@
import { expect, test } from "bun:test"
import { ConfigProvider, Effect } from "effect"
import { Headers } from "effect/http"
import { Auth, LLM, LLMClient, LLMRequest, Message, ReasoningPart, ToolDefinition } from "../../src/index.js"
import { Auth, LLM, LLMClient, LLMRequest, Message, ReasoningPart, ToolDefinition } from "../../src/effect.js"
import { Alibaba } from "../../src/providers.js"
import { compileRequest } from "../../src/route/client.js"
import { Endpoint } from "../../src/route/endpoint.js"

View file

@ -1,6 +1,6 @@
import { expect } from "bun:test"
import { Effect, Schema } from "effect"
import { LLM, LLMRequest, Message } from "../../src/index.js"
import { LLM, LLMRequest, Message } from "../../src/effect.js"
import { LLMClient } from "../../src/route/client.js"
import { Anthropic, GoogleVertexMessages } from "../../src/providers/index.js"
import { testEffect } from "../lib/effect.js"

View file

@ -1,6 +1,6 @@
import { describe, expect } from "bun:test"
import { Effect } from "effect"
import { CacheHint, LLM, LLMRequest, Message, ToolCallPart, ToolDefinition } from "../../src/index.js"
import { CacheHint, LLM, LLMRequest, Message, ToolCallPart, ToolDefinition } from "../../src/effect.js"
import { LLMClient } from "../../src/route.js"
import * as Anthropic from "../../src/providers/anthropic.js"
import { LARGE_CACHEABLE_SYSTEM } from "../recorded-scenarios.js"

View file

@ -1,6 +1,6 @@
import { describe, expect } from "bun:test"
import { Effect } from "effect"
import { LLM, AIError, Message, ToolCallPart } from "../../src/index.js"
import { LLM, AIError, Message, ToolCallPart } from "../../src/effect.js"
import { LLMClient } from "../../src/route.js"
import * as Anthropic from "../../src/providers/anthropic.js"
import { weatherToolName } from "../recorded-scenarios.js"

View file

@ -11,7 +11,7 @@ import {
ToolDefinition,
Usage,
Media,
} from "../../src/index.js"
} from "../../src/effect.js"
import { Auth, Endpoint, LLMClient, Route } from "../../src/route.js"
import { compileRequest } from "../../src/route/client.js"
import { AnthropicMessages } from "../../src/protocols/anthropic-messages.js"

View file

@ -1,6 +1,6 @@
import { expect } from "bun:test"
import { Effect } from "effect"
import { LLM } from "../../src/index.js"
import { LLM } from "../../src/effect.js"
import { AnthropicMessages } from "../../src/protocols/anthropic-messages.js"
import { compileRequest } from "../../src/route/client.js"
import { it } from "../lib/effect.js"

View file

@ -1,6 +1,6 @@
import { describe, expect } from "bun:test"
import { Effect } from "effect"
import { Transcription } from "../../src/index.js"
import { Transcription } from "../../src/effect.js"
import { AssemblyAI } from "../../src/providers.js"
import { recordedTests } from "../recorded-test.js"
import { TRANSCRIPT, audio, audioRecording } from "./transcription-recording.js"

View file

@ -1,6 +1,6 @@
import { describe, expect } from "bun:test"
import { Effect } from "effect"
import { LLM, Message, ToolDefinition, ToolCallPart } from "../../src/index.js"
import { LLM, Message, ToolDefinition, ToolCallPart } from "../../src/effect.js"
import { Azure } from "../../src/providers.js"
import { LLMClient } from "../../src/route.js"
import { recordedTests } from "../recorded-test.js"

View file

@ -1,6 +1,6 @@
import { describe, expect } from "bun:test"
import { Effect } from "effect"
import { CacheHint, LLM } from "../../src/index.js"
import { CacheHint, LLM } from "../../src/effect.js"
import { LLMClient } from "../../src/route.js"
import { AmazonBedrock } from "../../src/providers.js"
import { LARGE_CACHEABLE_SYSTEM } from "../recorded-scenarios.js"

View file

@ -1,6 +1,6 @@
import { describe, expect } from "bun:test"
import { Effect } from "effect"
import { CacheHint, LanguageModel, LLM, Message, ToolCallPart } from "../../src/index.js"
import { CacheHint, LanguageModel, LLM, Message, ToolCallPart } from "../../src/effect.js"
import { AmazonBedrock } from "../../src/providers.js"
import { compileRequest } from "../../src/route/client.js"
import { it } from "../lib/effect.js"

View file

@ -1,6 +1,6 @@
import { describe, expect } from "bun:test"
import { Effect } from "effect"
import { LLM, LLMRequest, Message } from "../../src/index.js"
import { LLM, LLMRequest, Message } from "../../src/effect.js"
import { LLMClient } from "../../src/route.js"
import { AmazonBedrock } from "../../src/providers.js"
import { recordedTests } from "../recorded-test.js"

View file

@ -1,6 +1,6 @@
import { describe, expect } from "bun:test"
import { Effect } from "effect"
import { GenerationOptions, LanguageModel, LLM } from "../../src/index.js"
import { GenerationOptions, LanguageModel, LLM } from "../../src/effect.js"
import { compileRequest } from "../../src/route/client.js"
import { AmazonBedrock } from "../../src/providers.js"
import { it } from "../lib/effect.js"

View file

@ -18,7 +18,7 @@ import {
ToolChoice,
ToolDefinition,
ToolRuntime,
} from "../../src/index.js"
} from "../../src/effect.js"
import { LLMClient } from "../../src/route.js"
import { compileRequest } from "../../src/route/client.js"
import { AmazonBedrock } from "../../src/providers.js"

View file

@ -1,7 +1,7 @@
import { describe, expect } from "bun:test"
import { Effect } from "effect"
import { HttpClientRequest } from "effect/http"
import { LLM, LLMEvent, LLMRequest, Message, ToolDefinition } from "../../src/index.js"
import { LLM, LLMEvent, LLMRequest, Message, ToolDefinition } from "../../src/effect.js"
import { AmazonBedrockMantle } from "../../src/providers.js"
import { OpenResponses } from "../../src/protocols/open-responses.js"
import { compileRequest, LLMClient } from "../../src/route/client.js"

View file

@ -1,6 +1,6 @@
import { expect } from "bun:test"
import { Effect, Schema } from "effect"
import { LLM, Message, ToolCallPart } from "../../src/index.js"
import { LLM, Message, ToolCallPart } from "../../src/effect.js"
import { AmazonBedrock } from "../../src/providers.js"
import { BedrockConverse } from "../../src/protocols/bedrock-converse.js"
import { compileRequest } from "../../src/route/client.js"

View file

@ -1,6 +1,6 @@
import { describe, expect } from "bun:test"
import { Effect } from "effect"
import { Image } from "../../src/index.js"
import { Image } from "../../src/effect.js"
import { BlackForestLabs } from "../../src/providers.js"
import { dimensions } from "../lib/image.js"
import { recordedTests } from "../recorded-test.js"

View file

@ -1,6 +1,6 @@
import { describe, expect } from "bun:test"
import { Effect } from "effect"
import { Speech, SpeechEvent } from "../../src/index.js"
import { Speech, SpeechEvent } from "../../src/effect.js"
import { Cartesia } from "../../src/providers.js"
import { recordedTests } from "../recorded-test.js"
import { TEXT, collectSpeech } from "./speech-recording.js"

View file

@ -1,6 +1,6 @@
import { expect } from "bun:test"
import { Effect, Stream } from "effect"
import { LLM, LLMClient, LLMRequest } from "../../src/index.js"
import { LLM, LLMClient, LLMRequest } from "../../src/effect.js"
import { Azure, OpenAI } from "../../src/providers.js"
import { testEffect } from "../lib/effect.js"
import { dynamicResponse, fixedResponse } from "../lib/http.js"

View file

@ -1,6 +1,6 @@
import { describe, expect } from "bun:test"
import { Effect } from "effect"
import { Image } from "../../src/index.js"
import { Image } from "../../src/effect.js"
import { CloudflareWorkersAI } from "../../src/providers.js"
import { dimensions } from "../lib/image.js"
import { recordedTests } from "../recorded-test.js"

View file

@ -1,7 +1,7 @@
import { describe, expect, test } from "bun:test"
import { Effect, Layer } from "effect"
import { HttpClientRequest } from "effect/http"
import { Image, ImageClient, Media } from "../../src/index.js"
import { Image, ImageClient, Media } from "../../src/effect.js"
import { CloudflareWorkersAI } from "../../src/providers.js"
import { ProviderConfigurationError } from "../../src/schema/index.js"
import { it } from "../lib/effect.js"

View file

@ -1,7 +1,7 @@
import { describe, expect } from "bun:test"
import { ConfigProvider, Effect, Schema } from "effect"
import { HttpClientRequest } from "effect/http"
import { LLM, LLMEvent } from "../../src/index.js"
import { LLM, LLMEvent } from "../../src/effect.js"
import { CloudflareAIGateway } from "../../src/providers/cloudflare-ai-gateway.js"
import { CloudflareWorkersAI } from "../../src/providers/cloudflare-workers-ai.js"
import { compileRequest } from "../../src/route/client.js"

View file

@ -1,6 +1,6 @@
import { expect } from "bun:test"
import { Effect } from "effect"
import { LLM, LLMEvent, LLMRequest, SystemPart } from "../../src/index.js"
import { LLM, LLMEvent, LLMRequest, SystemPart } from "../../src/effect.js"
import { Cohere } from "../../src/providers/cohere.js"
import { LLMClient } from "../../src/route.js"
import { expectWeatherToolLoop, goldenWeatherToolLoopRequest, runWeatherToolLoop } from "../recorded-scenarios.js"

View file

@ -1,6 +1,6 @@
import { expect, test } from "bun:test"
import { Effect } from "effect"
import { LLM, LLMClient, LLMEvent, Media, Message, SystemPart, isRetryable } from "../../src/index.js"
import { LLM, LLMClient, LLMEvent, Media, Message, SystemPart, isRetryable } from "../../src/effect.js"
import { Cohere } from "../../src/providers/cohere.js"
import { compileRequest } from "../../src/route/client.js"
import { it } from "../lib/effect.js"

View file

@ -1,6 +1,6 @@
import { expect } from "bun:test"
import { Effect, Stream } from "effect"
import { LLM, LLMRequest, Message } from "../../src/index.js"
import { LLM, LLMRequest, Message } from "../../src/effect.js"
import { LLMClient, WebSocketTransport } from "../../src/route.js"
import { OpenAI } from "../../src/providers.js"
import { testEffect } from "../lib/effect.js"

View file

@ -1,6 +1,6 @@
import { expect } from "bun:test"
import { Effect } from "effect"
import { LLM, LLMRequest, Message } from "../../src/index.js"
import { LLM, LLMRequest, Message } from "../../src/effect.js"
import { LLMClient } from "../../src/route/client.js"
import { OpenAI, XAI, Anthropic } from "../../src/providers.js"
import { recordedTests } from "../recorded-test.js"

View file

@ -1,6 +1,6 @@
import { expect } from "bun:test"
import { Effect, Schema } from "effect"
import { LLM, LLMRequest, Message } from "../../src/index.js"
import { LLM, LLMRequest, Message } from "../../src/effect.js"
import { LLMClient } from "../../src/route/client.js"
import { OpenAI, Azure, XAI } from "../../src/providers/index.js"
import { testEffect } from "../lib/effect.js"

View file

@ -1,6 +1,6 @@
import { expect } from "bun:test"
import { Effect } from "effect"
import { LLM, Message } from "../../src/index.js"
import { LLM, Message } from "../../src/effect.js"
import { OpenAI } from "../../src/providers.js"
import { OpenResponses } from "../../src/protocols/open-responses.js"
import { it } from "../lib/effect.js"

View file

@ -1,6 +1,6 @@
import { describe, expect } from "bun:test"
import { Effect } from "effect"
import { Decision, type DecisionModel, type DecisionOptions } from "../../src/index.js"
import { Decision, type DecisionModel, type DecisionOptions } from "../../src/effect.js"
import {
Baseten,
CloudflareAIGateway,

View file

@ -1,6 +1,6 @@
import { describe, expect } from "bun:test"
import { Effect } from "effect"
import { Speech } from "../../src/index.js"
import { Speech } from "../../src/effect.js"
import { Deepgram } from "../../src/providers.js"
import { recordedTests } from "../recorded-test.js"
import { TEXT, collectSpeech } from "./speech-recording.js"

View file

@ -1,6 +1,6 @@
import { describe, expect } from "bun:test"
import { Effect, Stream } from "effect"
import { Transcription } from "../../src/index.js"
import { Transcription } from "../../src/effect.js"
import { Deepgram } from "../../src/providers.js"
import { recordedTests } from "../recorded-test.js"
import { TRANSCRIPT, audio, audioRecording, dialog } from "./transcription-recording.js"

View file

@ -1,6 +1,6 @@
import { describe, expect } from "bun:test"
import { Effect } from "effect"
import { LLM, LLMRequest } from "../../src/index.js"
import { LLM, LLMRequest } from "../../src/effect.js"
import { DigitalOcean } from "../../src/providers/digitalocean.js"
import { LLMClient } from "../../src/route.js"
import {

View file

@ -1,6 +1,6 @@
import { describe, expect, test } from "bun:test"
import { Effect } from "effect"
import { CacheHint, LLM } from "../../src/index.js"
import { CacheHint, LLM } from "../../src/effect.js"
import { compileRequest } from "../../src/route/client.js"
import { DigitalOcean } from "../../src/providers/digitalocean.js"
import { it } from "../lib/effect.js"

View file

@ -1,6 +1,6 @@
import { describe, expect } from "bun:test"
import { Effect } from "effect"
import { Speech, SpeechEvent } from "../../src/index.js"
import { Speech, SpeechEvent } from "../../src/effect.js"
import { ElevenLabs } from "../../src/providers.js"
import { recordedTests } from "../recorded-test.js"
import { TEXT, collectSpeech } from "./speech-recording.js"

View file

@ -1,6 +1,6 @@
import { describe, expect } from "bun:test"
import { Effect, Stream } from "effect"
import { Transcription } from "../../src/index.js"
import { Transcription } from "../../src/effect.js"
import { ElevenLabs } from "../../src/providers.js"
import { recordedTests } from "../recorded-test.js"
import { TRANSCRIPT, audio, audioRecording, dialog } from "./transcription-recording.js"

View file

@ -1,6 +1,6 @@
import { expect } from "bun:test"
import { Effect } from "effect"
import { LLM, Message, ToolCallPart } from "../../src/index.js"
import { LLM, Message, ToolCallPart } from "../../src/effect.js"
import { OpenAIChat } from "../../src/protocols/openai-chat.js"
import { OpenAIResponses } from "../../src/protocols/openai-responses.js"
import { Auth } from "../../src/route/auth.js"

View file

@ -1,6 +1,6 @@
import { describe, expect } from "bun:test"
import { Effect } from "effect"
import { LLM } from "../../src/index.js"
import { LLM } from "../../src/effect.js"
import { Anthropic, Google, OpenAI } from "../../src/providers.js"
import { LLMClient } from "../../src/route.js"
import { it } from "../lib/effect.js"

View file

@ -1,6 +1,6 @@
import { expect } from "bun:test"
import { Effect, Schema } from "effect"
import { LLM, LLMRequest, Message, ToolDefinition } from "../../src/index.js"
import { LLM, LLMRequest, Message, ToolDefinition } from "../../src/effect.js"
import { LLMClient, Route } from "../../src/route/client.js"
import { Auth } from "../../src/route/auth.js"
import { Endpoint } from "../../src/route/endpoint.js"

View file

@ -1,6 +1,6 @@
import { describe, expect } from "bun:test"
import { Effect } from "effect"
import { Image } from "../../src/index.js"
import { Image } from "../../src/effect.js"
import { Fal } from "../../src/providers.js"
import { dimensions } from "../lib/image.js"
import { recordedTests } from "../recorded-test.js"

View file

@ -1,6 +1,6 @@
import { describe, expect } from "bun:test"
import { Effect } from "effect"
import { Video } from "../../src/index.js"
import { Video } from "../../src/effect.js"
import { Fal } from "../../src/providers.js"
import { recordedTests } from "../recorded-test.js"
import { queuedPoll } from "./queued-recording.js"

View file

@ -1,6 +1,6 @@
import { describe, expect } from "bun:test"
import { Effect } from "effect"
import { LLM } from "../../src/index.js"
import { LLM } from "../../src/effect.js"
import { LLMClient } from "../../src/route.js"
import * as Google from "../../src/providers/google.js"
import { LARGE_CACHEABLE_SYSTEM } from "../recorded-scenarios.js"

View file

@ -1,6 +1,6 @@
import { describe, expect } from "bun:test"
import { Effect } from "effect"
import { LLM, LLMEvent, LLMRequest, Message, ToolRuntime, toDefinitions } from "../../src/index.js"
import { LLM, LLMEvent, LLMRequest, Message, ToolRuntime, toDefinitions } from "../../src/effect.js"
import * as OpenAICompatible from "../../src/providers/openai-compatible.js"
import { LLMClient } from "../../src/route.js"
import { compileRequest } from "../../src/route/client.js"

Some files were not shown because too many files have changed in this diff Show more