feat: mock Homarr Assistant in demo mode and fix board page crash (#6524)

This commit is contained in:
Thomas authored and GitHub committed 2026-08-06 21:33:03 +02:00
1 parent fce950d406
commit eca8b4286d
7 files changed
+130 -48

No files matched your search

+24 -25
View File
@@ -30,7 +30,7 @@ import {
import type { createTRPCContext } from "../trpc";
import { fetchOpenRouterGenerationTelemetryAsync } from "../assistant-generation-telemetry";
import { verifyAssistantGenerationAccessToken } from "../assistant-generation-access";
import { createTRPCRouter, permissionRequiredProcedure, protectedProcedure } from "../trpc";
import { createTRPCRouter, isDemoMode, permissionRequiredProcedure, protectedProcedure } from "../trpc";
import { boardRouter } from "./board";
const adminProcedure = permissionRequiredProcedure.requiresPermission("admin");
@@ -440,14 +440,10 @@ const addFeedbackToMessageContent = (serializedContent: string, type: "positive"
};
export const assistantRouter = createTRPCRouter({
getAvailability: protectedProcedure
.meta({
mcp: {
enabled: true,
description: "Check whether Homarr Assistant is enabled and configured for the current user",
},
})
.query(async ({ ctx }) => {
getAvailability: protectedProcedure.query(async ({ ctx }) => {
if (isDemoMode) {
return { enabled: true };
}
const configuration = await getConfigurationAsync(ctx.db);
const requiresApiKey = configuration ? assistantProviderRequiresApiKey(configuration.provider) : false;
return {
@@ -457,26 +453,11 @@ export const assistantRouter = createTRPCRouter({
};
}),
getContextEntities: protectedProcedure
.meta({
mcp: {
enabled: true,
description:
"Lists the apps, integrations, boards, and widgets the signed-in user may reference. Integration entries are permission-filtered. Use returned IDs as inputs for other tools.",
},
})
.query(async ({ ctx }) => {
getContextEntities: protectedProcedure.query(async ({ ctx }) => {
return await getAssistantContextEntitiesAsync(ctx);
}),
getModelCapabilities: protectedProcedure
.meta({
mcp: {
enabled: true,
description:
"Returns the configured assistant model's discovered input modalities and whether image input is supported, unsupported, or unknown.",
},
})
.input(z.object({ modelId: z.string().trim().min(1).max(256).optional() }).optional())
.query(async ({ ctx, input }) => {
const configuration = await getConfigurationAsync(ctx.db);
@@ -495,6 +476,24 @@ export const assistantRouter = createTRPCRouter({
getRuntimeOptions: protectedProcedure.query(async ({ ctx }) => {
const configuration = await getConfigurationAsync(ctx.db);
if (isDemoMode) {
return {
provider: "homarr",
defaultModelId: "homarr-assistant",
models: [
{
id: "homarr-assistant",
name: "Homarr Assistant (Demo)",
description: null,
contextLength: null,
promptPrice: null,
completionPrice: null,
inputModalities: [],
toolSupport: "unknown" as const,
},
],
};
}
if (!configuration?.enabled || !configuration.modelId) {
throw new TRPCError({ code: "PRECONDITION_FAILED", message: "Homarr Assistant is not configured." });
}
-5
View File
@@ -3,7 +3,6 @@
import { expect, test, vi } from "vitest";
import { extractToolsFromProcedures } from "trpc-to-mcp";
import { assistantRouter } from "../router/assistant";
import { appRouter } from "../router/app";
import { boardRouter } from "../router/board";
import { customWidgetRouter } from "../router/custom-widget/custom-widget-router";
@@ -28,7 +27,6 @@ vi.mock("@homarr/core/infrastructure/db/env", () => ({
}));
const mcpTestRouter = createTRPCRouter({
assistant: assistantRouter,
app: appRouter,
board: boardRouter,
customWidget: customWidgetRouter,
@@ -49,9 +47,6 @@ test("MCP tools should contain expected procedures", () => {
const toolNames = tools.map((tool) => tool.name);
expect(tools.length).toBeGreaterThan(0);
expect(toolNames).toContain("assistant_getAvailability");
expect(toolNames).toContain("assistant_getContextEntities");
expect(toolNames).toContain("assistant_getModelCapabilities");
expect(toolNames).toContain("app_all");
expect(toolNames).toContain("app_byId");
expect(toolNames).toContain("app_create");