feat: unwrap provider namespaces to flat exports + barrel (#22760)
This commit is contained in:
@@ -6,7 +6,7 @@ import { generateObject, streamObject, type ModelMessage } from "ai"
|
|||||||
import { Instance } from "../project/instance"
|
import { Instance } from "../project/instance"
|
||||||
import { Truncate } from "../tool"
|
import { Truncate } from "../tool"
|
||||||
import { Auth } from "../auth"
|
import { Auth } from "../auth"
|
||||||
import { ProviderTransform } from "../provider/transform"
|
import { ProviderTransform } from "../provider"
|
||||||
|
|
||||||
import PROMPT_GENERATE from "./generate.txt"
|
import PROMPT_GENERATE from "./generate.txt"
|
||||||
import PROMPT_COMPACTION from "./prompt/compaction.txt"
|
import PROMPT_COMPACTION from "./prompt/compaction.txt"
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import type {
|
|||||||
} from "@octokit/webhooks-types"
|
} from "@octokit/webhooks-types"
|
||||||
import { UI } from "../ui"
|
import { UI } from "../ui"
|
||||||
import { cmd } from "./cmd"
|
import { cmd } from "./cmd"
|
||||||
import { ModelsDev } from "../../provider/models"
|
import { ModelsDev } from "../../provider"
|
||||||
import { Instance } from "@/project/instance"
|
import { Instance } from "@/project/instance"
|
||||||
import { bootstrap } from "../bootstrap"
|
import { bootstrap } from "../bootstrap"
|
||||||
import { SessionShare } from "@/share"
|
import { SessionShare } from "@/share"
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import type { Argv } from "yargs"
|
|||||||
import { Instance } from "../../project/instance"
|
import { Instance } from "../../project/instance"
|
||||||
import { Provider } from "../../provider"
|
import { Provider } from "../../provider"
|
||||||
import { ProviderID } from "../../provider/schema"
|
import { ProviderID } from "../../provider/schema"
|
||||||
import { ModelsDev } from "../../provider/models"
|
import { ModelsDev } from "../../provider"
|
||||||
import { cmd } from "./cmd"
|
import { cmd } from "./cmd"
|
||||||
import { UI } from "../ui"
|
import { UI } from "../ui"
|
||||||
import { EOL } from "os"
|
import { EOL } from "os"
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { AppRuntime } from "../../effect/app-runtime"
|
|||||||
import { cmd } from "./cmd"
|
import { cmd } from "./cmd"
|
||||||
import * as prompts from "@clack/prompts"
|
import * as prompts from "@clack/prompts"
|
||||||
import { UI } from "../ui"
|
import { UI } from "../ui"
|
||||||
import { ModelsDev } from "../../provider/models"
|
import { ModelsDev } from "../../provider"
|
||||||
import { map, pipe, sortBy, values } from "remeda"
|
import { map, pipe, sortBy, values } from "remeda"
|
||||||
import path from "path"
|
import path from "path"
|
||||||
import os from "os"
|
import os from "os"
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import { Storage } from "@/storage"
|
|||||||
import { Snapshot } from "@/snapshot"
|
import { Snapshot } from "@/snapshot"
|
||||||
import { Plugin } from "@/plugin"
|
import { Plugin } from "@/plugin"
|
||||||
import { Provider } from "@/provider"
|
import { Provider } from "@/provider"
|
||||||
import { ProviderAuth } from "@/provider/auth"
|
import { ProviderAuth } from "@/provider"
|
||||||
import { Agent } from "@/agent/agent"
|
import { Agent } from "@/agent/agent"
|
||||||
import { Skill } from "@/skill"
|
import { Skill } from "@/skill"
|
||||||
import { Discovery } from "@/skill/discovery"
|
import { Discovery } from "@/skill/discovery"
|
||||||
|
|||||||
@@ -9,83 +9,82 @@ import { ProviderID } from "./schema"
|
|||||||
import { Array as Arr, Effect, Layer, Record, Result, Context, Schema } from "effect"
|
import { Array as Arr, Effect, Layer, Record, Result, Context, Schema } from "effect"
|
||||||
import z from "zod"
|
import z from "zod"
|
||||||
|
|
||||||
export namespace ProviderAuth {
|
const When = Schema.Struct({
|
||||||
const When = Schema.Struct({
|
|
||||||
key: Schema.String,
|
key: Schema.String,
|
||||||
op: Schema.Literals(["eq", "neq"]),
|
op: Schema.Literals(["eq", "neq"]),
|
||||||
value: Schema.String,
|
value: Schema.String,
|
||||||
})
|
})
|
||||||
|
|
||||||
const TextPrompt = Schema.Struct({
|
const TextPrompt = Schema.Struct({
|
||||||
type: Schema.Literal("text"),
|
type: Schema.Literal("text"),
|
||||||
key: Schema.String,
|
key: Schema.String,
|
||||||
message: Schema.String,
|
message: Schema.String,
|
||||||
placeholder: Schema.optional(Schema.String),
|
placeholder: Schema.optional(Schema.String),
|
||||||
when: Schema.optional(When),
|
when: Schema.optional(When),
|
||||||
})
|
})
|
||||||
|
|
||||||
const SelectOption = Schema.Struct({
|
const SelectOption = Schema.Struct({
|
||||||
label: Schema.String,
|
label: Schema.String,
|
||||||
value: Schema.String,
|
value: Schema.String,
|
||||||
hint: Schema.optional(Schema.String),
|
hint: Schema.optional(Schema.String),
|
||||||
})
|
})
|
||||||
|
|
||||||
const SelectPrompt = Schema.Struct({
|
const SelectPrompt = Schema.Struct({
|
||||||
type: Schema.Literal("select"),
|
type: Schema.Literal("select"),
|
||||||
key: Schema.String,
|
key: Schema.String,
|
||||||
message: Schema.String,
|
message: Schema.String,
|
||||||
options: Schema.Array(SelectOption),
|
options: Schema.Array(SelectOption),
|
||||||
when: Schema.optional(When),
|
when: Schema.optional(When),
|
||||||
})
|
})
|
||||||
|
|
||||||
const Prompt = Schema.Union([TextPrompt, SelectPrompt])
|
const Prompt = Schema.Union([TextPrompt, SelectPrompt])
|
||||||
|
|
||||||
export class Method extends Schema.Class<Method>("ProviderAuthMethod")({
|
export class Method extends Schema.Class<Method>("ProviderAuthMethod")({
|
||||||
type: Schema.Literals(["oauth", "api"]),
|
type: Schema.Literals(["oauth", "api"]),
|
||||||
label: Schema.String,
|
label: Schema.String,
|
||||||
prompts: Schema.optional(Schema.Array(Prompt)),
|
prompts: Schema.optional(Schema.Array(Prompt)),
|
||||||
}) {
|
}) {
|
||||||
static readonly zod = zod(this)
|
static readonly zod = zod(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Methods = Schema.Record(Schema.String, Schema.Array(Method)).pipe(withStatics((s) => ({ zod: zod(s) })))
|
export const Methods = Schema.Record(Schema.String, Schema.Array(Method)).pipe(withStatics((s) => ({ zod: zod(s) })))
|
||||||
export type Methods = typeof Methods.Type
|
export type Methods = typeof Methods.Type
|
||||||
|
|
||||||
export class Authorization extends Schema.Class<Authorization>("ProviderAuthAuthorization")({
|
export class Authorization extends Schema.Class<Authorization>("ProviderAuthAuthorization")({
|
||||||
url: Schema.String,
|
url: Schema.String,
|
||||||
method: Schema.Literals(["auto", "code"]),
|
method: Schema.Literals(["auto", "code"]),
|
||||||
instructions: Schema.String,
|
instructions: Schema.String,
|
||||||
}) {
|
}) {
|
||||||
static readonly zod = zod(this)
|
static readonly zod = zod(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const OauthMissing = NamedError.create("ProviderAuthOauthMissing", z.object({ providerID: ProviderID.zod }))
|
export const OauthMissing = NamedError.create("ProviderAuthOauthMissing", z.object({ providerID: ProviderID.zod }))
|
||||||
|
|
||||||
export const OauthCodeMissing = NamedError.create(
|
export const OauthCodeMissing = NamedError.create(
|
||||||
"ProviderAuthOauthCodeMissing",
|
"ProviderAuthOauthCodeMissing",
|
||||||
z.object({ providerID: ProviderID.zod }),
|
z.object({ providerID: ProviderID.zod }),
|
||||||
)
|
)
|
||||||
|
|
||||||
export const OauthCallbackFailed = NamedError.create("ProviderAuthOauthCallbackFailed", z.object({}))
|
export const OauthCallbackFailed = NamedError.create("ProviderAuthOauthCallbackFailed", z.object({}))
|
||||||
|
|
||||||
export const ValidationFailed = NamedError.create(
|
export const ValidationFailed = NamedError.create(
|
||||||
"ProviderAuthValidationFailed",
|
"ProviderAuthValidationFailed",
|
||||||
z.object({
|
z.object({
|
||||||
field: z.string(),
|
field: z.string(),
|
||||||
message: z.string(),
|
message: z.string(),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
export type Error =
|
export type Error =
|
||||||
| Auth.AuthError
|
| Auth.AuthError
|
||||||
| InstanceType<typeof OauthMissing>
|
| InstanceType<typeof OauthMissing>
|
||||||
| InstanceType<typeof OauthCodeMissing>
|
| InstanceType<typeof OauthCodeMissing>
|
||||||
| InstanceType<typeof OauthCallbackFailed>
|
| InstanceType<typeof OauthCallbackFailed>
|
||||||
| InstanceType<typeof ValidationFailed>
|
| InstanceType<typeof ValidationFailed>
|
||||||
|
|
||||||
type Hook = NonNullable<Hooks["auth"]>
|
type Hook = NonNullable<Hooks["auth"]>
|
||||||
|
|
||||||
export interface Interface {
|
export interface Interface {
|
||||||
readonly methods: () => Effect.Effect<Methods>
|
readonly methods: () => Effect.Effect<Methods>
|
||||||
readonly authorize: (input: {
|
readonly authorize: (input: {
|
||||||
providerID: ProviderID
|
providerID: ProviderID
|
||||||
@@ -93,16 +92,16 @@ export namespace ProviderAuth {
|
|||||||
inputs?: Record<string, string>
|
inputs?: Record<string, string>
|
||||||
}) => Effect.Effect<Authorization | undefined, Error>
|
}) => Effect.Effect<Authorization | undefined, Error>
|
||||||
readonly callback: (input: { providerID: ProviderID; method: number; code?: string }) => Effect.Effect<void, Error>
|
readonly callback: (input: { providerID: ProviderID; method: number; code?: string }) => Effect.Effect<void, Error>
|
||||||
}
|
}
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
hooks: Record<ProviderID, Hook>
|
hooks: Record<ProviderID, Hook>
|
||||||
pending: Map<ProviderID, AuthOAuthResult>
|
pending: Map<ProviderID, AuthOAuthResult>
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Service extends Context.Service<Service, Interface>()("@opencode/ProviderAuth") {}
|
export class Service extends Context.Service<Service, Interface>()("@opencode/ProviderAuth") {}
|
||||||
|
|
||||||
export const layer: Layer.Layer<Service, never, Auth.Service | Plugin.Service> = Layer.effect(
|
export const layer: Layer.Layer<Service, never, Auth.Service | Plugin.Service> = Layer.effect(
|
||||||
Service,
|
Service,
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const auth = yield* Auth.Service
|
const auth = yield* Auth.Service
|
||||||
@@ -219,9 +218,8 @@ export namespace ProviderAuth {
|
|||||||
|
|
||||||
return Service.of({ methods, authorize, callback })
|
return Service.of({ methods, authorize, callback })
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
export const defaultLayer = Layer.suspend(() =>
|
export const defaultLayer = Layer.suspend(() =>
|
||||||
layer.pipe(Layer.provide(Auth.defaultLayer), Layer.provide(Plugin.defaultLayer)),
|
layer.pipe(Layer.provide(Auth.defaultLayer), Layer.provide(Plugin.defaultLayer)),
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|||||||
@@ -3,10 +3,9 @@ import { STATUS_CODES } from "http"
|
|||||||
import { iife } from "@/util/iife"
|
import { iife } from "@/util/iife"
|
||||||
import type { ProviderID } from "./schema"
|
import type { ProviderID } from "./schema"
|
||||||
|
|
||||||
export namespace ProviderError {
|
// Adapted from overflow detection patterns in:
|
||||||
// Adapted from overflow detection patterns in:
|
// https://github.com/badlogic/pi-mono/blob/main/packages/ai/src/utils/overflow.ts
|
||||||
// https://github.com/badlogic/pi-mono/blob/main/packages/ai/src/utils/overflow.ts
|
const OVERFLOW_PATTERNS = [
|
||||||
const OVERFLOW_PATTERNS = [
|
|
||||||
/prompt is too long/i, // Anthropic
|
/prompt is too long/i, // Anthropic
|
||||||
/input is too long for requested model/i, // Amazon Bedrock
|
/input is too long for requested model/i, // Amazon Bedrock
|
||||||
/exceeds the context window/i, // OpenAI (Completions + Responses API message text)
|
/exceeds the context window/i, // OpenAI (Completions + Responses API message text)
|
||||||
@@ -26,27 +25,27 @@ export namespace ProviderError {
|
|||||||
/prompt too long; exceeded (?:max )?context length/i, // Ollama explicit overflow error
|
/prompt too long; exceeded (?:max )?context length/i, // Ollama explicit overflow error
|
||||||
/too large for model with \d+ maximum context length/i, // Mistral
|
/too large for model with \d+ maximum context length/i, // Mistral
|
||||||
/model_context_window_exceeded/i, // z.ai non-standard finish_reason surfaced as error text
|
/model_context_window_exceeded/i, // z.ai non-standard finish_reason surfaced as error text
|
||||||
]
|
]
|
||||||
|
|
||||||
function isOpenAiErrorRetryable(e: APICallError) {
|
function isOpenAiErrorRetryable(e: APICallError) {
|
||||||
const status = e.statusCode
|
const status = e.statusCode
|
||||||
if (!status) return e.isRetryable
|
if (!status) return e.isRetryable
|
||||||
// openai sometimes returns 404 for models that are actually available
|
// openai sometimes returns 404 for models that are actually available
|
||||||
return status === 404 || e.isRetryable
|
return status === 404 || e.isRetryable
|
||||||
}
|
}
|
||||||
|
|
||||||
// Providers not reliably handled in this function:
|
// Providers not reliably handled in this function:
|
||||||
// - z.ai: can accept overflow silently (needs token-count/context-window checks)
|
// - z.ai: can accept overflow silently (needs token-count/context-window checks)
|
||||||
function isOverflow(message: string) {
|
function isOverflow(message: string) {
|
||||||
if (OVERFLOW_PATTERNS.some((p) => p.test(message))) return true
|
if (OVERFLOW_PATTERNS.some((p) => p.test(message))) return true
|
||||||
|
|
||||||
// Providers/status patterns handled outside of regex list:
|
// Providers/status patterns handled outside of regex list:
|
||||||
// - Cerebras: often returns "400 (no body)" / "413 (no body)"
|
// - Cerebras: often returns "400 (no body)" / "413 (no body)"
|
||||||
// - Mistral: often returns "400 (no body)" / "413 (no body)"
|
// - Mistral: often returns "400 (no body)" / "413 (no body)"
|
||||||
return /^4(00|13)\s*(status code)?\s*\(no body\)/i.test(message)
|
return /^4(00|13)\s*(status code)?\s*\(no body\)/i.test(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
function message(providerID: ProviderID, e: APICallError) {
|
function message(providerID: ProviderID, e: APICallError) {
|
||||||
return iife(() => {
|
return iife(() => {
|
||||||
const msg = e.message
|
const msg = e.message
|
||||||
if (msg === "") {
|
if (msg === "") {
|
||||||
@@ -85,9 +84,9 @@ export namespace ProviderError {
|
|||||||
|
|
||||||
return `${msg}: ${e.responseBody}`
|
return `${msg}: ${e.responseBody}`
|
||||||
}).trim()
|
}).trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
function json(input: unknown) {
|
function json(input: unknown) {
|
||||||
if (typeof input === "string") {
|
if (typeof input === "string") {
|
||||||
try {
|
try {
|
||||||
const result = JSON.parse(input)
|
const result = JSON.parse(input)
|
||||||
@@ -101,9 +100,9 @@ export namespace ProviderError {
|
|||||||
return input
|
return input
|
||||||
}
|
}
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ParsedStreamError =
|
export type ParsedStreamError =
|
||||||
| {
|
| {
|
||||||
type: "context_overflow"
|
type: "context_overflow"
|
||||||
message: string
|
message: string
|
||||||
@@ -116,7 +115,7 @@ export namespace ProviderError {
|
|||||||
responseBody: string
|
responseBody: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export function parseStreamError(input: unknown): ParsedStreamError | undefined {
|
export function parseStreamError(input: unknown): ParsedStreamError | undefined {
|
||||||
const body = json(input)
|
const body = json(input)
|
||||||
if (!body) return
|
if (!body) return
|
||||||
|
|
||||||
@@ -152,9 +151,9 @@ export namespace ProviderError {
|
|||||||
responseBody,
|
responseBody,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ParsedAPICallError =
|
export type ParsedAPICallError =
|
||||||
| {
|
| {
|
||||||
type: "context_overflow"
|
type: "context_overflow"
|
||||||
message: string
|
message: string
|
||||||
@@ -170,7 +169,7 @@ export namespace ProviderError {
|
|||||||
metadata?: Record<string, string>
|
metadata?: Record<string, string>
|
||||||
}
|
}
|
||||||
|
|
||||||
export function parseAPICallError(input: { providerID: ProviderID; error: APICallError }): ParsedAPICallError {
|
export function parseAPICallError(input: { providerID: ProviderID; error: APICallError }): ParsedAPICallError {
|
||||||
const m = message(input.providerID, input.error)
|
const m = message(input.providerID, input.error)
|
||||||
const body = json(input.error.responseBody)
|
const body = json(input.error.responseBody)
|
||||||
if (isOverflow(m) || input.error.statusCode === 413 || body?.error?.code === "context_length_exceeded") {
|
if (isOverflow(m) || input.error.statusCode === 413 || body?.error?.code === "context_length_exceeded") {
|
||||||
@@ -193,5 +192,4 @@ export namespace ProviderError {
|
|||||||
responseBody: input.error.responseBody,
|
responseBody: input.error.responseBody,
|
||||||
metadata,
|
metadata,
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1,5 @@
|
|||||||
export * as Provider from "./provider"
|
export * as Provider from "./provider"
|
||||||
|
export * as ProviderAuth from "./auth"
|
||||||
|
export * as ProviderError from "./error"
|
||||||
|
export * as ModelsDev from "./models"
|
||||||
|
export * as ProviderTransform from "./transform"
|
||||||
|
|||||||
@@ -13,22 +13,21 @@ import { Hash } from "@opencode-ai/shared/util/hash"
|
|||||||
// Falls back to undefined in dev mode when snapshot doesn't exist
|
// Falls back to undefined in dev mode when snapshot doesn't exist
|
||||||
/* @ts-ignore */
|
/* @ts-ignore */
|
||||||
|
|
||||||
export namespace ModelsDev {
|
const log = Log.create({ service: "models.dev" })
|
||||||
const log = Log.create({ service: "models.dev" })
|
const source = url()
|
||||||
const source = url()
|
const filepath = path.join(
|
||||||
const filepath = path.join(
|
|
||||||
Global.Path.cache,
|
Global.Path.cache,
|
||||||
source === "https://models.dev" ? "models.json" : `models-${Hash.fast(source)}.json`,
|
source === "https://models.dev" ? "models.json" : `models-${Hash.fast(source)}.json`,
|
||||||
)
|
)
|
||||||
const ttl = 5 * 60 * 1000
|
const ttl = 5 * 60 * 1000
|
||||||
|
|
||||||
type JsonValue = string | number | boolean | null | { [key: string]: JsonValue } | JsonValue[]
|
type JsonValue = string | number | boolean | null | { [key: string]: JsonValue } | JsonValue[]
|
||||||
|
|
||||||
const JsonValue: z.ZodType<JsonValue> = z.lazy(() =>
|
const JsonValue: z.ZodType<JsonValue> = z.lazy(() =>
|
||||||
z.union([z.string(), z.number(), z.boolean(), z.null(), z.array(JsonValue), z.record(z.string(), JsonValue)]),
|
z.union([z.string(), z.number(), z.boolean(), z.null(), z.array(JsonValue), z.record(z.string(), JsonValue)]),
|
||||||
)
|
)
|
||||||
|
|
||||||
const Cost = z.object({
|
const Cost = z.object({
|
||||||
input: z.number(),
|
input: z.number(),
|
||||||
output: z.number(),
|
output: z.number(),
|
||||||
cache_read: z.number().optional(),
|
cache_read: z.number().optional(),
|
||||||
@@ -41,9 +40,9 @@ export namespace ModelsDev {
|
|||||||
cache_write: z.number().optional(),
|
cache_write: z.number().optional(),
|
||||||
})
|
})
|
||||||
.optional(),
|
.optional(),
|
||||||
})
|
})
|
||||||
|
|
||||||
export const Model = z.object({
|
export const Model = z.object({
|
||||||
id: z.string(),
|
id: z.string(),
|
||||||
name: z.string(),
|
name: z.string(),
|
||||||
family: z.string().optional(),
|
family: z.string().optional(),
|
||||||
@@ -94,41 +93,41 @@ export namespace ModelsDev {
|
|||||||
.optional(),
|
.optional(),
|
||||||
status: z.enum(["alpha", "beta", "deprecated"]).optional(),
|
status: z.enum(["alpha", "beta", "deprecated"]).optional(),
|
||||||
provider: z.object({ npm: z.string().optional(), api: z.string().optional() }).optional(),
|
provider: z.object({ npm: z.string().optional(), api: z.string().optional() }).optional(),
|
||||||
})
|
})
|
||||||
export type Model = z.infer<typeof Model>
|
export type Model = z.infer<typeof Model>
|
||||||
|
|
||||||
export const Provider = z.object({
|
export const Provider = z.object({
|
||||||
api: z.string().optional(),
|
api: z.string().optional(),
|
||||||
name: z.string(),
|
name: z.string(),
|
||||||
env: z.array(z.string()),
|
env: z.array(z.string()),
|
||||||
id: z.string(),
|
id: z.string(),
|
||||||
npm: z.string().optional(),
|
npm: z.string().optional(),
|
||||||
models: z.record(z.string(), Model),
|
models: z.record(z.string(), Model),
|
||||||
})
|
})
|
||||||
|
|
||||||
export type Provider = z.infer<typeof Provider>
|
export type Provider = z.infer<typeof Provider>
|
||||||
|
|
||||||
function url() {
|
function url() {
|
||||||
return Flag.OPENCODE_MODELS_URL || "https://models.dev"
|
return Flag.OPENCODE_MODELS_URL || "https://models.dev"
|
||||||
}
|
}
|
||||||
|
|
||||||
function fresh() {
|
function fresh() {
|
||||||
return Date.now() - Number(Filesystem.stat(filepath)?.mtimeMs ?? 0) < ttl
|
return Date.now() - Number(Filesystem.stat(filepath)?.mtimeMs ?? 0) < ttl
|
||||||
}
|
}
|
||||||
|
|
||||||
function skip(force: boolean) {
|
function skip(force: boolean) {
|
||||||
return !force && fresh()
|
return !force && fresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchApi = async () => {
|
const fetchApi = async () => {
|
||||||
const result = await fetch(`${url()}/api.json`, {
|
const result = await fetch(`${url()}/api.json`, {
|
||||||
headers: { "User-Agent": Installation.USER_AGENT },
|
headers: { "User-Agent": Installation.USER_AGENT },
|
||||||
signal: AbortSignal.timeout(10000),
|
signal: AbortSignal.timeout(10000),
|
||||||
})
|
})
|
||||||
return { ok: result.ok, text: await result.text() }
|
return { ok: result.ok, text: await result.text() }
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Data = lazy(async () => {
|
export const Data = lazy(async () => {
|
||||||
const result = await Filesystem.readJson(Flag.OPENCODE_MODELS_PATH ?? filepath).catch(() => {})
|
const result = await Filesystem.readJson(Flag.OPENCODE_MODELS_PATH ?? filepath).catch(() => {})
|
||||||
if (result) return result
|
if (result) return result
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@@ -148,34 +147,33 @@ export namespace ModelsDev {
|
|||||||
}
|
}
|
||||||
return JSON.parse(result2.text)
|
return JSON.parse(result2.text)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
export async function get() {
|
export async function get() {
|
||||||
const result = await Data()
|
const result = await Data()
|
||||||
return result as Record<string, Provider>
|
return result as Record<string, Provider>
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function refresh(force = false) {
|
export async function refresh(force = false) {
|
||||||
if (skip(force)) return ModelsDev.Data.reset()
|
if (skip(force)) return Data.reset()
|
||||||
await Flock.withLock(`models-dev:${filepath}`, async () => {
|
await Flock.withLock(`models-dev:${filepath}`, async () => {
|
||||||
if (skip(force)) return ModelsDev.Data.reset()
|
if (skip(force)) return Data.reset()
|
||||||
const result = await fetchApi()
|
const result = await fetchApi()
|
||||||
if (!result.ok) return
|
if (!result.ok) return
|
||||||
await Filesystem.write(filepath, result.text)
|
await Filesystem.write(filepath, result.text)
|
||||||
ModelsDev.Data.reset()
|
Data.reset()
|
||||||
}).catch((e) => {
|
}).catch((e) => {
|
||||||
log.error("Failed to fetch models.dev", {
|
log.error("Failed to fetch models.dev", {
|
||||||
error: e,
|
error: e,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Flag.OPENCODE_DISABLE_MODELS_FETCH && !process.argv.includes("--get-yargs-completions")) {
|
if (!Flag.OPENCODE_DISABLE_MODELS_FETCH && !process.argv.includes("--get-yargs-completions")) {
|
||||||
void ModelsDev.refresh()
|
void refresh()
|
||||||
setInterval(
|
setInterval(
|
||||||
async () => {
|
async () => {
|
||||||
await ModelsDev.refresh()
|
await refresh()
|
||||||
},
|
},
|
||||||
60 * 1000 * 60,
|
60 * 1000 * 60,
|
||||||
).unref()
|
).unref()
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { Hash } from "@opencode-ai/shared/util/hash"
|
|||||||
import { Plugin } from "../plugin"
|
import { Plugin } from "../plugin"
|
||||||
import { NamedError } from "@opencode-ai/shared/util/error"
|
import { NamedError } from "@opencode-ai/shared/util/error"
|
||||||
import { type LanguageModelV3 } from "@ai-sdk/provider"
|
import { type LanguageModelV3 } from "@ai-sdk/provider"
|
||||||
import { ModelsDev } from "./models"
|
import * as ModelsDev from "./models"
|
||||||
import { Auth } from "../auth"
|
import { Auth } from "../auth"
|
||||||
import { Env } from "../env"
|
import { Env } from "../env"
|
||||||
import { Instance } from "../project/instance"
|
import { Instance } from "../project/instance"
|
||||||
@@ -55,7 +55,7 @@ import {
|
|||||||
} from "gitlab-ai-provider"
|
} from "gitlab-ai-provider"
|
||||||
import { fromNodeProviderChain } from "@aws-sdk/credential-providers"
|
import { fromNodeProviderChain } from "@aws-sdk/credential-providers"
|
||||||
import { GoogleAuth } from "google-auth-library"
|
import { GoogleAuth } from "google-auth-library"
|
||||||
import { ProviderTransform } from "./transform"
|
import * as ProviderTransform from "./transform"
|
||||||
import { Installation } from "../installation"
|
import { Installation } from "../installation"
|
||||||
import { ModelID, ProviderID } from "./schema"
|
import { ModelID, ProviderID } from "./schema"
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { mergeDeep, unique } from "remeda"
|
|||||||
import type { JSONSchema7 } from "@ai-sdk/provider"
|
import type { JSONSchema7 } from "@ai-sdk/provider"
|
||||||
import type { JSONSchema } from "zod/v4/core"
|
import type { JSONSchema } from "zod/v4/core"
|
||||||
import type * as Provider from "./provider"
|
import type * as Provider from "./provider"
|
||||||
import type { ModelsDev } from "./models"
|
import type * as ModelsDev from "./models"
|
||||||
import { iife } from "@/util/iife"
|
import { iife } from "@/util/iife"
|
||||||
import { Flag } from "@/flag/flag"
|
import { Flag } from "@/flag/flag"
|
||||||
|
|
||||||
@@ -17,11 +17,10 @@ function mimeToModality(mime: string): Modality | undefined {
|
|||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
export namespace ProviderTransform {
|
export const OUTPUT_TOKEN_MAX = Flag.OPENCODE_EXPERIMENTAL_OUTPUT_TOKEN_MAX || 32_000
|
||||||
export const OUTPUT_TOKEN_MAX = Flag.OPENCODE_EXPERIMENTAL_OUTPUT_TOKEN_MAX || 32_000
|
|
||||||
|
|
||||||
// Maps npm package to the key the AI SDK expects for providerOptions
|
// Maps npm package to the key the AI SDK expects for providerOptions
|
||||||
function sdkKey(npm: string): string | undefined {
|
function sdkKey(npm: string): string | undefined {
|
||||||
switch (npm) {
|
switch (npm) {
|
||||||
case "@ai-sdk/github-copilot":
|
case "@ai-sdk/github-copilot":
|
||||||
return "copilot"
|
return "copilot"
|
||||||
@@ -44,13 +43,13 @@ export namespace ProviderTransform {
|
|||||||
return "openrouter"
|
return "openrouter"
|
||||||
}
|
}
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeMessages(
|
function normalizeMessages(
|
||||||
msgs: ModelMessage[],
|
msgs: ModelMessage[],
|
||||||
model: Provider.Model,
|
model: Provider.Model,
|
||||||
_options: Record<string, unknown>,
|
_options: Record<string, unknown>,
|
||||||
): ModelMessage[] {
|
): ModelMessage[] {
|
||||||
// Anthropic rejects messages with empty content - filter out empty string messages
|
// Anthropic rejects messages with empty content - filter out empty string messages
|
||||||
// and remove empty text/reasoning parts from array content
|
// and remove empty text/reasoning parts from array content
|
||||||
if (model.api.npm === "@ai-sdk/anthropic" || model.api.npm === "@ai-sdk/amazon-bedrock") {
|
if (model.api.npm === "@ai-sdk/anthropic" || model.api.npm === "@ai-sdk/amazon-bedrock") {
|
||||||
@@ -212,9 +211,9 @@ export namespace ProviderTransform {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return msgs
|
return msgs
|
||||||
}
|
}
|
||||||
|
|
||||||
function applyCaching(msgs: ModelMessage[], model: Provider.Model): ModelMessage[] {
|
function applyCaching(msgs: ModelMessage[], model: Provider.Model): ModelMessage[] {
|
||||||
const system = msgs.filter((msg) => msg.role === "system").slice(0, 2)
|
const system = msgs.filter((msg) => msg.role === "system").slice(0, 2)
|
||||||
const final = msgs.filter((msg) => msg.role !== "system").slice(-2)
|
const final = msgs.filter((msg) => msg.role !== "system").slice(-2)
|
||||||
|
|
||||||
@@ -263,9 +262,9 @@ export namespace ProviderTransform {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return msgs
|
return msgs
|
||||||
}
|
}
|
||||||
|
|
||||||
function unsupportedParts(msgs: ModelMessage[], model: Provider.Model): ModelMessage[] {
|
function unsupportedParts(msgs: ModelMessage[], model: Provider.Model): ModelMessage[] {
|
||||||
return msgs.map((msg) => {
|
return msgs.map((msg) => {
|
||||||
if (msg.role !== "user" || !Array.isArray(msg.content)) return msg
|
if (msg.role !== "user" || !Array.isArray(msg.content)) return msg
|
||||||
|
|
||||||
@@ -301,9 +300,9 @@ export namespace ProviderTransform {
|
|||||||
|
|
||||||
return { ...msg, content: filtered }
|
return { ...msg, content: filtered }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function message(msgs: ModelMessage[], model: Provider.Model, options: Record<string, unknown>) {
|
export function message(msgs: ModelMessage[], model: Provider.Model, options: Record<string, unknown>) {
|
||||||
msgs = unsupportedParts(msgs, model)
|
msgs = unsupportedParts(msgs, model)
|
||||||
msgs = normalizeMessages(msgs, model, options)
|
msgs = normalizeMessages(msgs, model, options)
|
||||||
if (
|
if (
|
||||||
@@ -348,9 +347,9 @@ export namespace ProviderTransform {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return msgs
|
return msgs
|
||||||
}
|
}
|
||||||
|
|
||||||
export function temperature(model: Provider.Model) {
|
export function temperature(model: Provider.Model) {
|
||||||
const id = model.id.toLowerCase()
|
const id = model.id.toLowerCase()
|
||||||
if (id.includes("qwen")) return 0.55
|
if (id.includes("qwen")) return 0.55
|
||||||
if (id.includes("claude")) return undefined
|
if (id.includes("claude")) return undefined
|
||||||
@@ -366,18 +365,18 @@ export namespace ProviderTransform {
|
|||||||
return 0.6
|
return 0.6
|
||||||
}
|
}
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
export function topP(model: Provider.Model) {
|
export function topP(model: Provider.Model) {
|
||||||
const id = model.id.toLowerCase()
|
const id = model.id.toLowerCase()
|
||||||
if (id.includes("qwen")) return 1
|
if (id.includes("qwen")) return 1
|
||||||
if (["minimax-m2", "gemini", "kimi-k2.5", "kimi-k2p5", "kimi-k2-5"].some((s) => id.includes(s))) {
|
if (["minimax-m2", "gemini", "kimi-k2.5", "kimi-k2p5", "kimi-k2-5"].some((s) => id.includes(s))) {
|
||||||
return 0.95
|
return 0.95
|
||||||
}
|
}
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
export function topK(model: Provider.Model) {
|
export function topK(model: Provider.Model) {
|
||||||
const id = model.id.toLowerCase()
|
const id = model.id.toLowerCase()
|
||||||
if (id.includes("minimax-m2")) {
|
if (id.includes("minimax-m2")) {
|
||||||
if (["m2.", "m25", "m21"].some((s) => id.includes(s))) return 40
|
if (["m2.", "m25", "m21"].some((s) => id.includes(s))) return 40
|
||||||
@@ -385,12 +384,12 @@ export namespace ProviderTransform {
|
|||||||
}
|
}
|
||||||
if (id.includes("gemini")) return 64
|
if (id.includes("gemini")) return 64
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
const WIDELY_SUPPORTED_EFFORTS = ["low", "medium", "high"]
|
const WIDELY_SUPPORTED_EFFORTS = ["low", "medium", "high"]
|
||||||
const OPENAI_EFFORTS = ["none", "minimal", ...WIDELY_SUPPORTED_EFFORTS, "xhigh"]
|
const OPENAI_EFFORTS = ["none", "minimal", ...WIDELY_SUPPORTED_EFFORTS, "xhigh"]
|
||||||
|
|
||||||
export function variants(model: Provider.Model): Record<string, Record<string, any>> {
|
export function variants(model: Provider.Model): Record<string, Record<string, any>> {
|
||||||
if (!model.capabilities.reasoning) return {}
|
if (!model.capabilities.reasoning) return {}
|
||||||
|
|
||||||
const id = model.id.toLowerCase()
|
const id = model.id.toLowerCase()
|
||||||
@@ -769,13 +768,13 @@ export namespace ProviderTransform {
|
|||||||
return {}
|
return {}
|
||||||
}
|
}
|
||||||
return {}
|
return {}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function options(input: {
|
export function options(input: {
|
||||||
model: Provider.Model
|
model: Provider.Model
|
||||||
sessionID: string
|
sessionID: string
|
||||||
providerOptions?: Record<string, any>
|
providerOptions?: Record<string, any>
|
||||||
}): Record<string, any> {
|
}): Record<string, any> {
|
||||||
const result: Record<string, any> = {}
|
const result: Record<string, any> = {}
|
||||||
|
|
||||||
// openai and providers using openai package should set store to false by default.
|
// openai and providers using openai package should set store to false by default.
|
||||||
@@ -901,9 +900,9 @@ export namespace ProviderTransform {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
export function smallOptions(model: Provider.Model) {
|
export function smallOptions(model: Provider.Model) {
|
||||||
if (
|
if (
|
||||||
model.providerID === "openai" ||
|
model.providerID === "openai" ||
|
||||||
model.api.npm === "@ai-sdk/openai" ||
|
model.api.npm === "@ai-sdk/openai" ||
|
||||||
@@ -936,15 +935,15 @@ export namespace ProviderTransform {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {}
|
return {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Maps model ID prefix to provider slug used in providerOptions.
|
// Maps model ID prefix to provider slug used in providerOptions.
|
||||||
// Example: "amazon/nova-2-lite" → "bedrock"
|
// Example: "amazon/nova-2-lite" → "bedrock"
|
||||||
const SLUG_OVERRIDES: Record<string, string> = {
|
const SLUG_OVERRIDES: Record<string, string> = {
|
||||||
amazon: "bedrock",
|
amazon: "bedrock",
|
||||||
}
|
}
|
||||||
|
|
||||||
export function providerOptions(model: Provider.Model, options: { [x: string]: any }) {
|
export function providerOptions(model: Provider.Model, options: { [x: string]: any }) {
|
||||||
if (model.api.npm === "@ai-sdk/gateway") {
|
if (model.api.npm === "@ai-sdk/gateway") {
|
||||||
// Gateway providerOptions are split across two namespaces:
|
// Gateway providerOptions are split across two namespaces:
|
||||||
// - `gateway`: gateway-native routing/caching controls (order, only, byok, etc.)
|
// - `gateway`: gateway-native routing/caching controls (order, only, byok, etc.)
|
||||||
@@ -983,13 +982,13 @@ export namespace ProviderTransform {
|
|||||||
return { openai: options, azure: options }
|
return { openai: options, azure: options }
|
||||||
}
|
}
|
||||||
return { [key]: options }
|
return { [key]: options }
|
||||||
}
|
}
|
||||||
|
|
||||||
export function maxOutputTokens(model: Provider.Model): number {
|
export function maxOutputTokens(model: Provider.Model): number {
|
||||||
return Math.min(model.limit.output, OUTPUT_TOKEN_MAX) || OUTPUT_TOKEN_MAX
|
return Math.min(model.limit.output, OUTPUT_TOKEN_MAX) || OUTPUT_TOKEN_MAX
|
||||||
}
|
}
|
||||||
|
|
||||||
export function schema(model: Provider.Model, schema: JSONSchema.BaseSchema | JSONSchema7): JSONSchema7 {
|
export function schema(model: Provider.Model, schema: JSONSchema.BaseSchema | JSONSchema7): JSONSchema7 {
|
||||||
/*
|
/*
|
||||||
if (["openai", "azure"].includes(providerID)) {
|
if (["openai", "azure"].includes(providerID)) {
|
||||||
if (schema.type === "object" && schema.properties) {
|
if (schema.type === "object" && schema.properties) {
|
||||||
@@ -1088,5 +1087,4 @@ export namespace ProviderTransform {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return schema as JSONSchema7
|
return schema as JSONSchema7
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { ProviderAuth } from "@/provider/auth"
|
import { ProviderAuth } from "@/provider"
|
||||||
import { Effect, Layer } from "effect"
|
import { Effect, Layer } from "effect"
|
||||||
import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, OpenApi } from "effect/unstable/httpapi"
|
import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, OpenApi } from "effect/unstable/httpapi"
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import { describeRoute, validator, resolver } from "hono-openapi"
|
|||||||
import z from "zod"
|
import z from "zod"
|
||||||
import { Config } from "../../config"
|
import { Config } from "../../config"
|
||||||
import { Provider } from "../../provider"
|
import { Provider } from "../../provider"
|
||||||
import { ModelsDev } from "../../provider/models"
|
import { ModelsDev } from "../../provider"
|
||||||
import { ProviderAuth } from "../../provider/auth"
|
import { ProviderAuth } from "../../provider"
|
||||||
import { ProviderID } from "../../provider/schema"
|
import { ProviderID } from "../../provider/schema"
|
||||||
import { AppRuntime } from "../../effect/app-runtime"
|
import { AppRuntime } from "../../effect/app-runtime"
|
||||||
import { mapValues } from "remeda"
|
import { mapValues } from "remeda"
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import * as Stream from "effect/Stream"
|
|||||||
import { streamText, wrapLanguageModel, type ModelMessage, type Tool, tool, jsonSchema } from "ai"
|
import { streamText, wrapLanguageModel, type ModelMessage, type Tool, tool, jsonSchema } from "ai"
|
||||||
import { mergeDeep, pipe } from "remeda"
|
import { mergeDeep, pipe } from "remeda"
|
||||||
import { GitLabWorkflowLanguageModel } from "gitlab-ai-provider"
|
import { GitLabWorkflowLanguageModel } from "gitlab-ai-provider"
|
||||||
import { ProviderTransform } from "@/provider/transform"
|
import { ProviderTransform } from "@/provider"
|
||||||
import { Config } from "@/config"
|
import { Config } from "@/config"
|
||||||
import { Instance } from "@/project/instance"
|
import { Instance } from "@/project/instance"
|
||||||
import type { Agent } from "@/agent/agent"
|
import type { Agent } from "@/agent/agent"
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { Snapshot } from "@/snapshot"
|
|||||||
import { SyncEvent } from "../sync"
|
import { SyncEvent } from "../sync"
|
||||||
import { Database, NotFoundError, and, desc, eq, inArray, lt, or } from "@/storage"
|
import { Database, NotFoundError, and, desc, eq, inArray, lt, or } from "@/storage"
|
||||||
import { MessageTable, PartTable, SessionTable } from "./session.sql"
|
import { MessageTable, PartTable, SessionTable } from "./session.sql"
|
||||||
import { ProviderError } from "@/provider/error"
|
import { ProviderError } from "@/provider"
|
||||||
import { iife } from "@/util/iife"
|
import { iife } from "@/util/iife"
|
||||||
import { errorMessage } from "@/util/error"
|
import { errorMessage } from "@/util/error"
|
||||||
import type { SystemError } from "bun"
|
import type { SystemError } from "bun"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { Config } from "@/config"
|
import type { Config } from "@/config"
|
||||||
import type { Provider } from "@/provider"
|
import type { Provider } from "@/provider"
|
||||||
import { ProviderTransform } from "@/provider/transform"
|
import { ProviderTransform } from "@/provider"
|
||||||
import type { MessageV2 } from "./message-v2"
|
import type { MessageV2 } from "./message-v2"
|
||||||
|
|
||||||
const COMPACTION_BUFFER = 20_000
|
const COMPACTION_BUFFER = 20_000
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import { ModelID, ProviderID } from "../provider/schema"
|
|||||||
import { type Tool as AITool, tool, jsonSchema, type ToolExecutionOptions, asSchema } from "ai"
|
import { type Tool as AITool, tool, jsonSchema, type ToolExecutionOptions, asSchema } from "ai"
|
||||||
import { SessionCompaction } from "./compaction"
|
import { SessionCompaction } from "./compaction"
|
||||||
import { Bus } from "../bus"
|
import { Bus } from "../bus"
|
||||||
import { ProviderTransform } from "../provider/transform"
|
import { ProviderTransform } from "../provider"
|
||||||
import { SystemPrompt } from "./system"
|
import { SystemPrompt } from "./system"
|
||||||
import { Instruction } from "./instruction"
|
import { Instruction } from "./instruction"
|
||||||
import { Plugin } from "../plugin"
|
import { Plugin } from "../plugin"
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import fs from "fs/promises"
|
|||||||
import { Effect } from "effect"
|
import { Effect } from "effect"
|
||||||
import { tmpdir } from "../fixture/fixture"
|
import { tmpdir } from "../fixture/fixture"
|
||||||
import { Instance } from "../../src/project/instance"
|
import { Instance } from "../../src/project/instance"
|
||||||
import { ProviderAuth } from "../../src/provider/auth"
|
import { ProviderAuth } from "../../src/provider"
|
||||||
import { ProviderID } from "../../src/provider/schema"
|
import { ProviderID } from "../../src/provider/schema"
|
||||||
|
|
||||||
describe("plugin.auth-override", () => {
|
describe("plugin.auth-override", () => {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { tmpdir } from "../fixture/fixture"
|
|||||||
import { Global } from "../../src/global"
|
import { Global } from "../../src/global"
|
||||||
import { Instance } from "../../src/project/instance"
|
import { Instance } from "../../src/project/instance"
|
||||||
import { Plugin } from "../../src/plugin/index"
|
import { Plugin } from "../../src/plugin/index"
|
||||||
import { ModelsDev } from "../../src/provider/models"
|
import { ModelsDev } from "../../src/provider"
|
||||||
import { Provider } from "../../src/provider"
|
import { Provider } from "../../src/provider"
|
||||||
import { ProviderID, ModelID } from "../../src/provider/schema"
|
import { ProviderID, ModelID } from "../../src/provider/schema"
|
||||||
import { Filesystem } from "../../src/util"
|
import { Filesystem } from "../../src/util"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { describe, expect, test } from "bun:test"
|
import { describe, expect, test } from "bun:test"
|
||||||
import { ProviderTransform } from "../../src/provider/transform"
|
import { ProviderTransform } from "../../src/provider"
|
||||||
import { ModelID, ProviderID } from "../../src/provider/schema"
|
import { ModelID, ProviderID } from "../../src/provider/schema"
|
||||||
|
|
||||||
describe("ProviderTransform.options - setCacheKey", () => {
|
describe("ProviderTransform.options - setCacheKey", () => {
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ import { makeRuntime } from "../../src/effect/run-service"
|
|||||||
import { LLM } from "../../src/session/llm"
|
import { LLM } from "../../src/session/llm"
|
||||||
import { Instance } from "../../src/project/instance"
|
import { Instance } from "../../src/project/instance"
|
||||||
import { Provider } from "../../src/provider"
|
import { Provider } from "../../src/provider"
|
||||||
import { ProviderTransform } from "../../src/provider/transform"
|
import { ProviderTransform } from "../../src/provider"
|
||||||
import { ModelsDev } from "../../src/provider/models"
|
import { ModelsDev } from "../../src/provider"
|
||||||
import { ProviderID, ModelID } from "../../src/provider/schema"
|
import { ProviderID, ModelID } from "../../src/provider/schema"
|
||||||
import { Filesystem } from "../../src/util"
|
import { Filesystem } from "../../src/util"
|
||||||
import { tmpdir } from "../fixture/fixture"
|
import { tmpdir } from "../fixture/fixture"
|
||||||
|
|||||||
Reference in New Issue
Block a user