Download packages/core/src/aisdk.ts from SaylorTwift/opencode: direct link, hf CLI and curl.
- Browser
- Download file 7.99 kB
-
https://huggingface.co/SaylorTwift/opencode/resolve/main/packages/core/src/aisdk.ts
- Command line
-
hf download hf://SaylorTwift/opencode/packages/core/src/aisdk.ts
-
curl -L -o aisdk.ts https://huggingface.co/SaylorTwift/opencode/resolve/main/packages/core/src/aisdk.ts
7.99 kB
| export * as AISDK from "./aisdk" | |
| import { makeLocationNode } from "./effect/app-node" | |
| import type { LanguageModelV3 } from "@ai-sdk/provider" | |
| import { Cause, Context, Effect, Layer, Schema, Scope } from "effect" | |
| import { ModelV2 } from "./model" | |
| import { ProviderV2 } from "./provider" | |
| import { State } from "./state" | |
| type SDK = any | |
| export interface SDKEvent { | |
| readonly model: ModelV2.Info | |
| readonly package: string | |
| readonly options: Record<string, any> | |
| sdk?: SDK | |
| } | |
| export interface LanguageEvent { | |
| readonly model: ModelV2.Info | |
| readonly sdk: SDK | |
| readonly options: Record<string, any> | |
| language?: LanguageModelV3 | |
| } | |
| 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) | |
| reader.cancel(err).catch(() => {}) | |
| 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 hook: { | |
| readonly sdk: ( | |
| callback: (event: SDKEvent) => Effect.Effect<void> | void, | |
| ) => Effect.Effect<State.Registration, never, Scope.Scope> | |
| readonly language: ( | |
| callback: (event: LanguageEvent) => Effect.Effect<void> | void, | |
| ) => Effect.Effect<State.Registration, never, Scope.Scope> | |
| } | |
| readonly runSDK: (event: SDKEvent) => Effect.Effect<SDKEvent> | |
| readonly runLanguage: (event: LanguageEvent) => Effect.Effect<LanguageEvent> | |
| readonly language: (model: ModelV2.Info) => Effect.Effect<LanguageModelV3, InitError> | |
| } | |
| export class Service extends Context.Service<Service, Interface>()("@opencode/v2/AISDK") {} | |
| export const locationLayer = Layer.effect( | |
| Service, | |
| Effect.gen(function* () { | |
| let sdkHooks: ((event: SDKEvent) => Effect.Effect<void> | void)[] = [] | |
| let languageHooks: ((event: LanguageEvent) => Effect.Effect<void> | void)[] = [] | |
| const languages = new Map<string, LanguageModelV3>() | |
| const sdks = new Map<string, SDK>() | |
| const register = <Event>( | |
| hooks: () => ((event: Event) => Effect.Effect<void> | void)[], | |
| update: (hooks: ((event: Event) => Effect.Effect<void> | void)[]) => void, | |
| ) => | |
| Effect.fn("AISDK.hook")(function* (callback: (event: Event) => Effect.Effect<void> | void) { | |
| const scope = yield* Scope.Scope | |
| let active = true | |
| update([...hooks(), callback]) | |
| const dispose = Effect.sync(() => { | |
| if (!active) return | |
| active = false | |
| update(hooks().filter((item) => item !== callback)) | |
| }) | |
| yield* Scope.addFinalizer(scope, dispose) | |
| return { dispose } | |
| }) | |
| const run = Effect.fnUntraced(function* <Event>( | |
| hooks: readonly ((event: Event) => Effect.Effect<void> | void)[], | |
| event: Event, | |
| ) { | |
| for (const hook of hooks) { | |
| const result = hook(event) | |
| if (Effect.isEffect(result)) yield* result | |
| } | |
| return event | |
| }) | |
| const service = Service.of({ | |
| hook: { | |
| sdk: register( | |
| () => sdkHooks, | |
| (next) => (sdkHooks = next), | |
| ), | |
| language: register( | |
| () => languageHooks, | |
| (next) => (languageHooks = next), | |
| ), | |
| }, | |
| runSDK: (event) => run(sdkHooks, event), | |
| runLanguage: (event) => run(languageHooks, event), | |
| 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* service.runSDK({ 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* service.runLanguage({ 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 | |
| }), | |
| }) | |
| return service | |
| }), | |
| ) | |
| export const node = makeLocationNode({ service: Service, layer: locationLayer, deps: [] }) | |