fix: send provider model ids
This commit is contained in:
1 parent
6bd331bc73
commit
af75bcd741
9 files changed
+235
-33
No files matched your search
@@ -18,7 +18,12 @@ import {
|
||||
integrationUserPermissions,
|
||||
items,
|
||||
} from "@homarr/db/schema";
|
||||
import { assistantProviderIds, assistantProviderPresets, assistantProviderRequiresApiKey } from "@homarr/definitions";
|
||||
import {
|
||||
assistantProviderIds,
|
||||
assistantProviderPresets,
|
||||
assistantProviderRequiresApiKey,
|
||||
resolveAssistantModelId,
|
||||
} from "@homarr/definitions";
|
||||
|
||||
import { createTRPCRouter, permissionRequiredProcedure, protectedProcedure } from "../trpc";
|
||||
import type { createTRPCContext } from "../trpc";
|
||||
@@ -256,7 +261,9 @@ export const getSelectedModelDetailsAsync = async (configuration: AssistantConfi
|
||||
const cached = selectedModelCache.get(cacheKey);
|
||||
if (cached && cached.expiresAt > Date.now()) return cached.value;
|
||||
|
||||
const value = (await fetchModelsAsync(configuration)).find((model) => model.id === configuration.modelId) ?? null;
|
||||
const models = await fetchModelsAsync(configuration);
|
||||
const resolvedModelId = resolveAssistantModelId(models, configuration.modelId);
|
||||
const value = models.find((model) => model.id === resolvedModelId) ?? null;
|
||||
selectedModelCache.clear();
|
||||
selectedModelCache.set(cacheKey, { expiresAt: Date.now() + 10 * 60_000, value });
|
||||
return value;
|
||||
@@ -547,9 +554,15 @@ export const assistantRouter = createTRPCRouter({
|
||||
if (input.enabled && requiresApiKey && !configuration.encryptedApiKey) {
|
||||
throw new TRPCError({ code: "PRECONDITION_FAILED", message: "This provider requires an API key." });
|
||||
}
|
||||
const discoveredModels = configuration.modelDiscoveryPath
|
||||
? await fetchModelsAsync(configuration).catch(() => null)
|
||||
: null;
|
||||
const modelId = discoveredModels
|
||||
? (resolveAssistantModelId(discoveredModels, input.modelId) ?? input.modelId)
|
||||
: input.modelId;
|
||||
await ctx.db
|
||||
.update(assistantConfigurations)
|
||||
.set({ enabled: input.enabled, modelId: input.modelId, updatedAt: new Date() })
|
||||
.set({ enabled: input.enabled, modelId, updatedAt: new Date() })
|
||||
.where(eq(assistantConfigurations.id, configurationId));
|
||||
}),
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import { parse } from "superjson";
|
||||
|
||||
import type { Session } from "@homarr/auth";
|
||||
import { createId } from "@homarr/common";
|
||||
import { eq } from "@homarr/db";
|
||||
import {
|
||||
apps,
|
||||
assistantConfigurations,
|
||||
@@ -101,6 +102,44 @@ describe("assistantRouter.updateConnection", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("assistantRouter.updateConfiguration", () => {
|
||||
test("normalizes legacy model labels to the discovered provider ID", async () => {
|
||||
const db = await createConfiguredAssistantAsync();
|
||||
await db
|
||||
.update(assistantConfigurations)
|
||||
.set({ encryptedHeaders: null })
|
||||
.where(eq(assistantConfigurations.id, "default"));
|
||||
const caller = assistantRouter.createCaller({ db, deviceType: undefined, session: adminSession });
|
||||
vi.stubGlobal(
|
||||
"fetch",
|
||||
vi.fn().mockResolvedValue(
|
||||
Response.json({
|
||||
data: [
|
||||
{
|
||||
id: "deepseek/deepseek-v4-pro",
|
||||
name: "DeepSeek: DeepSeek V4 Pro",
|
||||
supported_parameters: ["tools"],
|
||||
architecture: { output_modalities: ["text"] },
|
||||
},
|
||||
],
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
try {
|
||||
await caller.updateConfiguration({
|
||||
enabled: true,
|
||||
modelId: "DeepSeek: DeepSeek V4 Pro (deepseek/deepseek-v4-pro)",
|
||||
});
|
||||
} finally {
|
||||
vi.unstubAllGlobals();
|
||||
}
|
||||
|
||||
const [configuration] = await db.select().from(assistantConfigurations);
|
||||
expect(configuration?.modelId).toBe("deepseek/deepseek-v4-pro");
|
||||
});
|
||||
});
|
||||
|
||||
describe("assistant conversation features", () => {
|
||||
test("returns permission-checked entities for composer mentions", async () => {
|
||||
const db = await createConfiguredAssistantAsync();
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import { describe, expect, test } from "vitest";
|
||||
|
||||
import { assistantProviderIds, assistantProviderPresets, assistantProviderRequiresApiKey } from "@homarr/definitions";
|
||||
import {
|
||||
assistantProviderIds,
|
||||
assistantProviderPresets,
|
||||
assistantProviderRequiresApiKey,
|
||||
getAssistantModelOptionLabel,
|
||||
resolveAssistantModelId,
|
||||
} from "@homarr/definitions";
|
||||
|
||||
describe("assistant provider presets", () => {
|
||||
test("defines one complete preset for every provider", () => {
|
||||
@@ -30,4 +36,15 @@ describe("assistant provider presets", () => {
|
||||
expect(() => new URL(iconUrl ?? "")).not.toThrow();
|
||||
},
|
||||
);
|
||||
|
||||
test("keeps the display label separate from the provider model ID", () => {
|
||||
const model = {
|
||||
id: "deepseek/deepseek-v4-pro",
|
||||
name: "DeepSeek: DeepSeek V4 Pro",
|
||||
};
|
||||
|
||||
expect(getAssistantModelOptionLabel(model)).toBe("DeepSeek: DeepSeek V4 Pro (deepseek/deepseek-v4-pro)");
|
||||
expect(resolveAssistantModelId([model], model.id)).toBe(model.id);
|
||||
expect(resolveAssistantModelId([model], getAssistantModelOptionLabel(model))).toBe(model.id);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user