feat: implement full context-scoped AI chat UI in AIChatPanel
- Added AIChatPanel component with context header, user and AI message handling. - Integrated streaming responses via IPC and error handling for chat mutations. - Enhanced user experience with input handling and auto-scrolling features. - Updated AppShell to derive AI chat context from the current route. - Introduced ScrollArea component for better scrolling behavior in various dialogs. - Added support for Tailwind typography and improved global styles. - Updated project and task dialogs to utilize ScrollArea for better UX.
This commit is contained in:
@@ -6,7 +6,6 @@
|
||||
*/
|
||||
import { Annotation, StateGraph, START, END } from '@langchain/langgraph';
|
||||
import { SystemMessage, HumanMessage, type BaseMessage } from '@langchain/core/messages';
|
||||
import { z } from 'zod';
|
||||
import { eq, asc } from 'drizzle-orm';
|
||||
import { getDb } from '../db';
|
||||
import { projects, tasks, checkpoints, notes, clients } from '../db/schema';
|
||||
@@ -178,12 +177,6 @@ If the user asks about specific note contents that aren't included here, let the
|
||||
// LangGraph State
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const RouteSchema = z.object({
|
||||
route: z.enum(['project', 'knowledge', 'general']).describe(
|
||||
'Which specialist agent should handle this request',
|
||||
),
|
||||
});
|
||||
|
||||
const OrchestratorState = Annotation.Root({
|
||||
/** The user's original message */
|
||||
userMessage: Annotation<string>(),
|
||||
@@ -207,25 +200,29 @@ type State = typeof OrchestratorState.State;
|
||||
// Graph nodes
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/** Node 1: Classify intent using structured output */
|
||||
/** Node 1: Classify intent using plain-text extraction (works with all providers) */
|
||||
async function classifyIntent(state: State): Promise<Partial<State>> {
|
||||
const llm = await getLLM();
|
||||
if (!llm) throw new Error('AI provider not configured. Please add your token in Settings.');
|
||||
|
||||
const routerLLM = llm.withStructuredOutput(RouteSchema);
|
||||
|
||||
const result = await routerLLM.invoke([
|
||||
const response = await llm.invoke([
|
||||
new SystemMessage(
|
||||
`You are a routing classifier for Adiuva, a project management workspace.
|
||||
Classify the user's message into one of these categories:
|
||||
- "project": Question about a specific project (tasks, notes, checkpoints, progress, summaries)
|
||||
- "knowledge": Cross-project or historical question (e.g., "what did we decide about X?", "find notes about Y")
|
||||
- "general": Everything else (general help, scheduling, task overviews, workspace summaries)`,
|
||||
Classify the user's message into exactly one category. Reply with ONLY the category name, nothing else.
|
||||
|
||||
Categories:
|
||||
- project: Question about a specific project (tasks, notes, checkpoints, progress, summaries)
|
||||
- knowledge: Cross-project or historical question (e.g., "what did we decide about X?", "find notes about Y")
|
||||
- general: Everything else (general help, scheduling, task overviews, workspace summaries)`,
|
||||
),
|
||||
new HumanMessage(state.userMessage),
|
||||
]);
|
||||
|
||||
return { route: result.route };
|
||||
const text = (typeof response.content === 'string' ? response.content : '').trim().toLowerCase();
|
||||
const validRoutes = ['project', 'knowledge', 'general'] as const;
|
||||
const route = validRoutes.find((r) => text.includes(r)) ?? 'general';
|
||||
|
||||
return { route };
|
||||
}
|
||||
|
||||
/** Node 2a: Project agent — answer project-scoped questions */
|
||||
|
||||
Reference in New Issue
Block a user