feat(router): add aiChat tRPC sub-router

CRUD for chat sessions and messages, used by both home and contextual
channels. No UI consumer yet — added ahead of refactor.
This commit is contained in:
Roberto
2026-05-14 18:53:03 +02:00
parent b879760013
commit 425025ad68
2 changed files with 107 additions and 0 deletions

105
src/main/router/ai-chat.ts Normal file
View File

@@ -0,0 +1,105 @@
// adiuvAI/src/main/router/ai-chat.ts
import { initTRPC } from '@trpc/server';
import { z } from 'zod';
import { eq, desc, asc } from 'drizzle-orm';
import { getDb } from '../db';
import { aiChatSessions, aiChatMessages } from '../db/schema';
import type { TRPCContext } from '../ipc';
const t = initTRPC.context<TRPCContext>().create();
const router = t.router;
const publicProcedure = t.procedure;
const ChannelSchema = z.enum(['home', 'contextual']);
const RoleSchema = z.enum(['user', 'assistant', 'system']);
export const aiChatRouter = router({
listSessions: publicProcedure
.input(z.object({ channel: ChannelSchema }))
.query(({ input }) => {
return getDb()
.select()
.from(aiChatSessions)
.where(eq(aiChatSessions.channel, input.channel))
.orderBy(desc(aiChatSessions.updatedAt))
.all();
}),
getSession: publicProcedure
.input(z.object({ id: z.string() }))
.query(({ input }) => {
const db = getDb();
const session = db
.select()
.from(aiChatSessions)
.where(eq(aiChatSessions.id, input.id))
.get();
if (!session) return null;
const messages = db
.select()
.from(aiChatMessages)
.where(eq(aiChatMessages.sessionId, input.id))
.orderBy(asc(aiChatMessages.createdAt))
.all();
return { session, messages };
}),
createSession: publicProcedure
.input(z.object({
channel: ChannelSchema,
initialScope: z.string().optional(),
}))
.mutation(({ input }) => {
const db = getDb();
const id = crypto.randomUUID();
const now = Date.now();
db.insert(aiChatSessions).values({
id,
channel: input.channel,
title: null,
createdAt: now,
updatedAt: now,
lastScope: input.initialScope ?? null,
}).run();
return { id };
}),
appendMessage: publicProcedure
.input(z.object({
sessionId: z.string(),
role: RoleSchema,
content: z.string(),
toolCalls: z.string().optional(),
toolResults: z.string().optional(),
scope: z.string().optional(),
}))
.mutation(({ input }) => {
const db = getDb();
const id = crypto.randomUUID();
const now = Date.now();
db.insert(aiChatMessages).values({
id,
sessionId: input.sessionId,
role: input.role,
content: input.content,
toolCalls: input.toolCalls ?? null,
toolResults: input.toolResults ?? null,
scope: input.scope ?? null,
createdAt: now,
}).run();
db.update(aiChatSessions)
.set({ updatedAt: now, lastScope: input.scope ?? null })
.where(eq(aiChatSessions.id, input.sessionId))
.run();
return { id };
}),
deleteSession: publicProcedure
.input(z.object({ id: z.string() }))
.mutation(({ input }) => {
const db = getDb();
db.delete(aiChatMessages).where(eq(aiChatMessages.sessionId, input.id)).run();
db.delete(aiChatSessions).where(eq(aiChatSessions.id, input.id)).run();
return { ok: true };
}),
});

View File

@@ -18,6 +18,7 @@ import { getAuthManager, AuthError } from '../auth/auth-manager';
import { detectFormatPrefs, detectLanguage } from '../auth/locale-defaults';
import type { TRPCContext } from '../ipc';
import { projectFoldersRouter } from './projectFolders';
import { aiChatRouter } from './ai-chat';
const t = initTRPC.context<TRPCContext>().create();
@@ -1856,6 +1857,7 @@ export const appRouter = router({
agent: agentRouter,
memory: memoryRouter,
projectFolders: projectFoldersRouter,
aiChat: aiChatRouter,
});
export type AppRouter = typeof appRouter;