provider-cleanup
Dax Raad 2025-12-03 19:00:22 -05:00
parent c57216f22d
commit bbbffbf928
3 changed files with 32 additions and 34 deletions

View File

@ -474,15 +474,7 @@ export namespace Config {
.extend({
whitelist: z.array(z.string()).optional(),
blacklist: z.array(z.string()).optional(),
models: z
.record(
z.string(),
ModelsDev.Model.partial().refine(
(input) => input.id === undefined,
"The model.id field can no longer be specified. Use model.target to specify an alternate model id to use when calling the provider.",
),
)
.optional(),
models: z.record(z.string(), ModelsDev.Model.partial()).optional(),
options: z
.object({
apiKey: z.string().optional(),

View File

@ -481,8 +481,14 @@ export namespace Provider {
function mergeProvider(providerID: string, provider: Partial<Info>) {
const match = database[providerID]
if (!match) return
// @ts-expect-error
providers[providerID] = mergeDeep(match, provider)
const existing = providers[providerID]
if (existing) {
// @ts-expect-error
providers[providerID] = mergeDeep(existing, provider)
} else {
// @ts-expect-error
providers[providerID] = mergeDeep(match, provider)
}
}
// extend database from config
@ -494,7 +500,7 @@ export namespace Provider {
env: provider.env ?? existing?.env ?? [],
options: mergeDeep(existing?.options ?? {}, provider.options ?? {}),
source: "config",
models: {},
models: existing?.models ?? {},
}
for (const [modelID, model] of Object.entries(provider.models ?? {})) {
@ -520,18 +526,18 @@ export namespace Provider {
attachment: model.attachment ?? existing?.capabilities.attachment ?? false,
toolcall: model.tool_call ?? existing?.capabilities.toolcall ?? true,
input: {
text: model.modalities?.input?.includes("text") ?? false,
audio: model.modalities?.input?.includes("audio") ?? false,
image: model.modalities?.input?.includes("image") ?? false,
video: model.modalities?.input?.includes("video") ?? false,
pdf: model.modalities?.input?.includes("pdf") ?? false,
text: model.modalities?.input?.includes("text") ?? existing?.capabilities.input.text ?? true,
audio: model.modalities?.input?.includes("audio") ?? existing?.capabilities.input.audio ?? false,
image: model.modalities?.input?.includes("image") ?? existing?.capabilities.input.image ?? false,
video: model.modalities?.input?.includes("video") ?? existing?.capabilities.input.video ?? false,
pdf: model.modalities?.input?.includes("pdf") ?? existing?.capabilities.input.pdf ?? false,
},
output: {
text: model.modalities?.output?.includes("text") ?? false,
audio: model.modalities?.output?.includes("audio") ?? false,
image: model.modalities?.output?.includes("image") ?? false,
video: model.modalities?.output?.includes("video") ?? false,
pdf: model.modalities?.output?.includes("pdf") ?? false,
text: model.modalities?.output?.includes("text") ?? existing?.capabilities.output.text ?? true,
audio: model.modalities?.output?.includes("audio") ?? existing?.capabilities.output.audio ?? false,
image: model.modalities?.output?.includes("image") ?? existing?.capabilities.output.image ?? false,
video: model.modalities?.output?.includes("video") ?? existing?.capabilities.output.video ?? false,
pdf: model.modalities?.output?.includes("pdf") ?? existing?.capabilities.output.pdf ?? false,
},
},
cost: {
@ -638,12 +644,11 @@ export namespace Provider {
// load config
for (const [providerID, provider] of configProviders) {
mergeProvider(providerID, {
source: "config",
env: provider.env,
name: provider.name,
options: provider.options,
})
const partial: Partial<Info> = { source: "config" }
if (provider.env) partial.env = provider.env
if (provider.name) partial.name = provider.name
if (provider.options) partial.options = provider.options
mergeProvider(providerID, partial)
}
for (const [providerID, provider] of Object.entries(providers)) {

View File

@ -634,7 +634,7 @@ test("getModel uses realIdByKey for aliased models", async () => {
})
})
test("provider api field sets default baseURL", async () => {
test("provider api field sets model api.url", async () => {
await using tmp = await tmpdir({
init: async (dir) => {
await Bun.write(
@ -667,7 +667,8 @@ test("provider api field sets default baseURL", async () => {
directory: tmp.path,
fn: async () => {
const providers = await Provider.list()
expect(providers["custom-api"].options.baseURL).toBe("https://api.example.com/v1")
// api field is stored on model.api.url, used by getSDK to set baseURL
expect(providers["custom-api"].models["model-1"].api.url).toBe("https://api.example.com/v1")
},
})
})
@ -1122,8 +1123,8 @@ test("provider with multiple env var options only includes apiKey when single en
fn: async () => {
const providers = await Provider.list()
expect(providers["multi-env"]).toBeDefined()
// When multiple env options exist, apiKey should NOT be auto-set
expect(providers["multi-env"].options.apiKey).toBeUndefined()
// When multiple env options exist, key should NOT be auto-set
expect(providers["multi-env"].key).toBeUndefined()
},
})
})
@ -1164,8 +1165,8 @@ test("provider with single env var includes apiKey automatically", async () => {
fn: async () => {
const providers = await Provider.list()
expect(providers["single-env"]).toBeDefined()
// Single env option should auto-set apiKey
expect(providers["single-env"].options.apiKey).toBe("my-api-key")
// Single env option should auto-set key
expect(providers["single-env"].key).toBe("my-api-key")
},
})
})