From b1560946912747592af2691f36569eb38075109d Mon Sep 17 00:00:00 2001 From: Paul Walko Date: Thu, 25 Dec 2025 02:51:49 +0100 Subject: [PATCH] sources only --- web/agent/src/agent.py | 11 ++- web/agent/src/main.py | 9 ++- web/src/app/api/copilotkit/route.ts | 10 ++- web/src/app/layout.tsx | 5 +- web/src/app/page.tsx | 118 +++++++++++++++++----------- 5 files changed, 98 insertions(+), 55 deletions(-) diff --git a/web/agent/src/agent.py b/web/agent/src/agent.py index afe3471..2f56376 100644 --- a/web/agent/src/agent.py +++ b/web/agent/src/agent.py @@ -87,6 +87,8 @@ Rules: 7. Use tools sparingly—one search usually suffices. 8. If you hit the search limit, end your reply with an italicized note: *Your question may be too broad. Try asking something more specific.* Do NOT mention "tools" or "tool limits"—the user doesn't know what those are.""" +SOURCES_ONLY_INSTRUCTIONS = """SOURCES ONLY MODE: Give a 1-2 sentence summary maximum. Focus on listing sources in a bulleted list. No detailed explanations.""" + def create_tool_call_limiter(max_calls: int = 3): """Create a process_tool_call callback that limits tool calls.""" @@ -110,7 +112,7 @@ def create_tool_call_limiter(max_calls: int = 3): return process_tool_call -def create_agent(user_roles: list[str] | None = None): +def create_agent(user_roles: list[str] | None = None, sources_only: bool = False): """Create an agent with MCP tools configured for the given user roles.""" toolsets = [] @@ -140,10 +142,15 @@ def create_agent(user_roles: list[str] | None = None): else: logger.info("MCP server unavailable - running without MCP tools") + # Build instructions based on mode + instructions = AGENT_INSTRUCTIONS + if sources_only: + instructions = f"{SOURCES_ONLY_INSTRUCTIONS}\n\n{AGENT_INSTRUCTIONS}" + return Agent( model="anthropic:claude-sonnet-4-5", toolsets=toolsets if toolsets else None, - instructions=AGENT_INSTRUCTIONS, + instructions=instructions, history_processors=[limit_history], model_settings=ModelSettings(max_tokens=4096), ) diff --git a/web/agent/src/main.py b/web/agent/src/main.py index e7e3d3b..c2b3dba 100644 --- a/web/agent/src/main.py +++ b/web/agent/src/main.py @@ -67,8 +67,13 @@ async def handle_agent_request(request: Request) -> Response: except json.JSONDecodeError as e: logger.warning(f"Failed to parse x-user-roles header: {e}") - # Create agent with the user's roles - agent = create_agent(user_roles) + # Extract sources-only mode from header + sources_only = request.headers.get("x-sources-only", "false") == "true" + if sources_only: + logger.info("Sources-only mode enabled") + + # Create agent with the user's roles and mode + agent = create_agent(user_roles, sources_only=sources_only) # Dispatch the request - tool limits handled by ToolCallLimiter in agent.py return await AGUIAdapter.dispatch_request( diff --git a/web/src/app/api/copilotkit/route.ts b/web/src/app/api/copilotkit/route.ts index 8c17e82..b9f6942 100644 --- a/web/src/app/api/copilotkit/route.ts +++ b/web/src/app/api/copilotkit/route.ts @@ -15,13 +15,19 @@ export const POST = async (req: NextRequest) => { const session = await auth0.getSession(); const userRoles = (session?.user?.roles as string[]) || []; - console.log("DEBUG: User roles from session:", userRoles); + // Get sources-only mode from query param + const url = new URL(req.url); + const sourcesOnly = url.searchParams.get("sourcesOnly") === "true"; - // Create HttpAgent with user roles header + console.log("DEBUG: User roles from session:", userRoles); + console.log("DEBUG: Sources only mode:", sourcesOnly); + + // Create HttpAgent with user roles and sources-only headers const agent = new HttpAgent({ url: process.env.AGENT_URL || "http://localhost:8000/", headers: { "x-user-roles": JSON.stringify(userRoles), + "x-sources-only": sourcesOnly ? "true" : "false", }, }); diff --git a/web/src/app/layout.tsx b/web/src/app/layout.tsx index a05ecb9..208f9ba 100644 --- a/web/src/app/layout.tsx +++ b/web/src/app/layout.tsx @@ -1,6 +1,5 @@ import type { Metadata } from "next"; -import { CopilotKit } from "@copilotkit/react-core"; import { Auth0Provider } from "@auth0/nextjs-auth0/client"; import "./globals.css"; import "@copilotkit/react-ui/styles.css"; @@ -19,9 +18,7 @@ export default function RootLayout({ - - {children} - + {children} diff --git a/web/src/app/page.tsx b/web/src/app/page.tsx index 07e252f..1e146eb 100644 --- a/web/src/app/page.tsx +++ b/web/src/app/page.tsx @@ -1,6 +1,6 @@ "use client"; -import { useCopilotAction, useCopilotChat } from "@copilotkit/react-core"; +import { CopilotKit, useCopilotAction, useCopilotChat } from "@copilotkit/react-core"; import { CopilotKitCSSProperties, CopilotChat } from "@copilotkit/react-ui"; import { useState } from "react"; import { useUser } from "@auth0/nextjs-auth0/client"; @@ -36,10 +36,8 @@ function LoadingOverlay() { } } -export default function CopilotKitPage() { - const [themeColor, setThemeColor] = useState("#6366f1"); - const { user, isLoading: authLoading } = useUser(); - +// Chat content with CopilotKit hooks - must be inside CopilotKit provider +function ChatContent({ themeColor, setThemeColor }: { themeColor: string; setThemeColor: (color: string) => void }) { useCopilotAction({ name: "setThemeColor", parameters: [{ @@ -52,6 +50,35 @@ export default function CopilotKitPage() { }, }); + return ( +
+
+ +
+ +
+ ); +} + +export default function CopilotKitPage() { + const [themeColor, setThemeColor] = useState("#6366f1"); + const [sourcesOnlyMode, setSourcesOnlyMode] = useState(false); + const { user, isLoading: authLoading } = useUser(); + + // Dynamic runtime URL based on sources-only mode + const runtimeUrl = sourcesOnlyMode + ? "/api/copilotkit?sourcesOnly=true" + : "/api/copilotkit"; + // Show loading state while checking authentication if (authLoading) { return ( @@ -88,50 +115,51 @@ export default function CopilotKitPage() { // If authenticated, show the CopilotKit chat with user profile return ( -
- {/* Header with user profile and logout */} -
-
-
-

Cavepedia

-
-
- {user.picture && ( - {user.name - )} -
- {user.name} - {(user as any).roles && (user as any).roles.length > 0 && ( - - {(user as any).roles.join(', ')} - - )} +
+ {/* Header with user profile and logout */} +
+
+
+

Cavepedia

+ +
+
+ {user.picture && ( + {user.name + )} +
+ {user.name} + {(user as any).roles && (user as any).roles.length > 0 && ( + + {(user as any).roles.join(', ')} + + )} +
+
-
-
- {/* CopilotKit Chat */} -
-
- -
- -
-
+ {/* CopilotKit Chat */} + + + ); }