Buckets:
| export * as AISDK from "./aisdk" | |
| import type { LanguageModelV3 } from "@ai-sdk/provider" | |
| import { Cause, Context, Effect, Layer, Schema } from "effect" | |
| import { ModelV2 } from "./model" | |
| import { EventV2 } from "./event" | |
| import { PluginV2 } from "./plugin" | |
| import { ProviderV2 } from "./provider" | |
| type SDK = any | |
| function wrapSSE(res: Response, ms: number, ctl: AbortController) { | |
| if (typeof ms !== "number" || ms <= 0) return res | |
| if (!res.body) return res | |
| if (!res.headers.get("content-type")?.includes("text/event-stream")) return res | |
| const reader = res.body.getReader() | |
| const body = new ReadableStream<Uint8Array>({ | |
| async pull(ctrl) { | |
| const part = await new Promise<Awaited<ReturnType<typeof reader.read>>>((resolve, reject) => { | |
| const id = setTimeout(() => { | |
| const err = new Error("SSE read timed out") | |
| ctl.abort(err) | |
| void reader.cancel(err) | |
| reject(err) | |
| }, ms) | |
| reader.read().then( | |
| (part) => { | |
| clearTimeout(id) | |
| resolve(part) | |
| }, | |
| (err) => { | |
| clearTimeout(id) | |
| reject(err) | |
| }, | |
| ) | |
| }) | |
| if (part.done) { | |
| ctrl.close() | |
| return | |
| } | |
| ctrl.enqueue(part.value) | |
| }, | |
| async cancel(reason) { | |
| ctl.abort(reason) | |
| await reader.cancel(reason) | |
| }, | |
| }) | |
| return new Response(body, { | |
| headers: new Headers(res.headers), | |
| status: res.status, | |
| statusText: res.statusText, | |
| }) | |
| } | |
| function prepareOptions(model: ModelV2.Info, pkg: string) { | |
| const options: Record<string, any> = { | |
| name: model.providerID, | |
| ...(model.api.type === "aisdk" ? (model.api.settings ?? {}) : {}), | |
| ...model.request.body, | |
| } | |
| if (model.api.type === "aisdk" && model.api.url) options.baseURL = model.api.url | |
| const customFetch = options.fetch | |
| const chunkTimeout = options.chunkTimeout | |
| delete options.chunkTimeout | |
| options.fetch = async (input: Parameters<typeof fetch>[0], init?: RequestInit) => { | |
| const opts = { ...(init ?? {}) } | |
| const signals = [ | |
| opts.signal, | |
| typeof chunkTimeout === "number" && chunkTimeout > 0 ? new AbortController() : undefined, | |
| options.timeout !== undefined && options.timeout !== null && options.timeout !== false | |
| ? AbortSignal.timeout(options.timeout) | |
| : undefined, | |
| ].filter((item): item is AbortSignal | AbortController => Boolean(item)) | |
| const chunkAbortCtl = signals.find((item): item is AbortController => item instanceof AbortController) | |
| const abortSignals = signals.map((item) => (item instanceof AbortController ? item.signal : item)) | |
| if (abortSignals.length === 1) opts.signal = abortSignals[0] | |
| if (abortSignals.length > 1) opts.signal = AbortSignal.any(abortSignals) | |
| if ( | |
| (pkg === "@ai-sdk/openai" || pkg === "@ai-sdk/azure" || pkg === "@ai-sdk/amazon-bedrock/mantle") && | |
| opts.body && | |
| opts.method === "POST" | |
| ) { | |
| const body = JSON.parse(opts.body as string) | |
| if (body.store !== true && Array.isArray(body.input)) { | |
| for (const item of body.input) { | |
| if ("id" in item) delete item.id | |
| } | |
| opts.body = JSON.stringify(body) | |
| } | |
| } | |
| const res = await (typeof customFetch === "function" ? customFetch : fetch)(input, { | |
| ...opts, | |
| timeout: false, | |
| }) | |
| if (!chunkAbortCtl || typeof chunkTimeout !== "number") return res | |
| return wrapSSE(res, chunkTimeout, chunkAbortCtl) | |
| } | |
| return options | |
| } | |
| export class InitError extends Schema.TaggedErrorClass<InitError>()("AISDK.InitError", { | |
| providerID: ProviderV2.ID, | |
| cause: Schema.Defect, | |
| }) {} | |
| function initError(providerID: ProviderV2.ID) { | |
| return Effect.catchCause((cause) => Effect.fail(new InitError({ providerID, cause: Cause.squash(cause) }))) | |
| } | |
| export interface Interface { | |
| readonly language: (model: ModelV2.Info) => Effect.Effect<LanguageModelV3, InitError> | |
| } | |
| export class Service extends Context.Service<Service, Interface>()("@opencode/v2/AISDK") {} | |
| export const layer = Layer.effect( | |
| Service, | |
| Effect.gen(function* () { | |
| const plugin = yield* PluginV2.Service | |
| const languages = new Map<string, LanguageModelV3>() | |
| const sdks = new Map<string, SDK>() | |
| return Service.of({ | |
| language: Effect.fn("AISDK.language")(function* (model) { | |
| const key = `${model.providerID}/${model.id}/${model.request.variant ?? "default"}` | |
| const existing = languages.get(key) | |
| if (existing) return existing | |
| if (model.api.type !== "aisdk") | |
| return yield* new InitError({ | |
| providerID: model.providerID, | |
| cause: new Error(`Unsupported api ${model.api.type}`), | |
| }) | |
| const options = prepareOptions(model, model.api.package) | |
| const sdkKey = JSON.stringify({ | |
| providerID: model.providerID, | |
| api: model.api, | |
| options, | |
| }) | |
| const sdk = | |
| sdks.get(sdkKey) ?? | |
| (yield* plugin | |
| .trigger("aisdk.sdk", { model, package: model.api.package, options }, {}) | |
| .pipe(initError(model.providerID))).sdk | |
| if (!sdk) | |
| return yield* new InitError({ | |
| providerID: model.providerID, | |
| cause: new Error("No AISDK provider plugin returned an SDK"), | |
| }) | |
| sdks.set(sdkKey, sdk) | |
| const result = yield* plugin | |
| .trigger( | |
| "aisdk.language", | |
| { | |
| model, | |
| sdk, | |
| options, | |
| }, | |
| {}, | |
| ) | |
| .pipe(initError(model.providerID)) | |
| const language = yield* Effect.sync(() => result.language ?? sdk.languageModel(model.api.id)).pipe( | |
| initError(model.providerID), | |
| ) | |
| languages.set(key, language) | |
| return language | |
| }), | |
| }) | |
| }), | |
| ) | |
| export const defaultLayer = layer.pipe(Layer.provide(PluginV2.locationLayer.pipe(Layer.provide(EventV2.defaultLayer)))) | |
Xet Storage Details
- Size:
- 5.95 kB
- Xet hash:
- 036809c88024c5282fe3bf46a121236f4363cc99521df739a8dbd6a34cef9291
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.