show loading until text appears

This commit is contained in:
2025-12-09 05:30:31 +01:00
parent a30e5a6524
commit a515b6dc10
4 changed files with 23 additions and 14 deletions

View File

@@ -15,7 +15,6 @@ from langgraph.prebuilt import ToolNode, tools_condition
from langgraph.types import Command from langgraph.types import Command
from langchain_mcp_adapters.client import MultiServerMCPClient from langchain_mcp_adapters.client import MultiServerMCPClient
from langchain_mcp_adapters.interceptors import MCPToolCallRequest, MCPToolCallResult from langchain_mcp_adapters.interceptors import MCPToolCallRequest, MCPToolCallResult
from copilotkit.langgraph import copilotkit_customize_config
class AgentState(MessagesState): class AgentState(MessagesState):
@@ -27,7 +26,6 @@ class AgentState(MessagesState):
""" """
tools: List[Any] tools: List[Any]
# your_custom_agent_state: str = ""
# @tool # @tool
@@ -130,19 +128,13 @@ async def chat_node(state: AgentState, config: RunnableConfig) -> dict:
content=f"You are a helpful assistant with access to cave-related information through the Cavepedia MCP server. You can help users find information about caves, caving techniques, and related topics. User roles: {', '.join(user_roles) if user_roles else 'none'}" content=f"You are a helpful assistant with access to cave-related information through the Cavepedia MCP server. You can help users find information about caves, caving techniques, and related topics. User roles: {', '.join(user_roles) if user_roles else 'none'}"
) )
# 3.5 Customize config for CopilotKit to properly handle message streaming
modified_config = copilotkit_customize_config(
config,
emit_messages=True,
)
# 4. Run the model to generate a response # 4. Run the model to generate a response
response = await model_with_tools.ainvoke( response = await model_with_tools.ainvoke(
[ [
system_message, system_message,
*state["messages"], *state["messages"],
], ],
modified_config, config,
) )
# 5. Return the response in the messages # 5. Return the response in the messages

View File

@@ -249,6 +249,11 @@ html {
to { opacity: 1; transform: scale(1); } to { opacity: 1; transform: scale(1); }
} }
/* Hide CopilotKit's built-in loading indicator */
.copilotKitActivityDot {
display: none !important;
}
@media (max-width: 600px) { @media (max-width: 600px) {
.main-card-wrapper { .main-card-wrapper {
padding: 2rem; padding: 2rem;

View File

@@ -19,7 +19,7 @@ export default function RootLayout({
<html lang="en"> <html lang="en">
<body className={"antialiased"}> <body className={"antialiased"}>
<Auth0Provider> <Auth0Provider>
<CopilotKit runtimeUrl="/api/copilotkit" agent="sample_agent" properties={{ streamMode: ["events"] }}> <CopilotKit runtimeUrl="/api/copilotkit" agent="sample_agent">
{children} {children}
</CopilotKit> </CopilotKit>
</Auth0Provider> </Auth0Provider>

View File

@@ -1,6 +1,6 @@
"use client"; "use client";
import { useCopilotAction, useUser as useCopilotUser } from "@copilotkit/react-core"; import { useCopilotAction, useCopilotChat } from "@copilotkit/react-core";
import { CopilotKitCSSProperties, CopilotChat } from "@copilotkit/react-ui"; import { CopilotKitCSSProperties, CopilotChat } from "@copilotkit/react-ui";
import { useState } from "react"; import { useState } from "react";
import { useUser } from "@auth0/nextjs-auth0/client"; import { useUser } from "@auth0/nextjs-auth0/client";
@@ -10,7 +10,8 @@ import Profile from "@/components/Profile";
export default function CopilotKitPage() { export default function CopilotKitPage() {
const [themeColor, setThemeColor] = useState("#6366f1"); const [themeColor, setThemeColor] = useState("#6366f1");
const { user, isLoading } = useUser(); const { user, isLoading: authLoading } = useUser();
const { isLoading: chatLoading } = useCopilotChat();
useCopilotAction({ useCopilotAction({
name: "setThemeColor", name: "setThemeColor",
@@ -25,7 +26,7 @@ export default function CopilotKitPage() {
}); });
// Show loading state while checking authentication // Show loading state while checking authentication
if (isLoading) { if (authLoading) {
return ( return (
<div className="app-container"> <div className="app-container">
<div className="loading-state"> <div className="loading-state">
@@ -92,7 +93,7 @@ export default function CopilotKitPage() {
</div> </div>
{/* CopilotKit Chat */} {/* CopilotKit Chat */}
<div className="flex-1 flex justify-center py-8 px-2 overflow-hidden"> <div className="flex-1 flex justify-center py-8 px-2 overflow-hidden relative">
<div className="h-full w-full max-w-5xl flex flex-col"> <div className="h-full w-full max-w-5xl flex flex-col">
<CopilotChat <CopilotChat
instructions={"You are a knowledgeable caving assistant. Help users with all aspects of caving including cave exploration, safety, surveying techniques, cave locations, geology, equipment, history, conservation, and any other caving-related topics. Provide accurate, helpful, and safety-conscious information. CRITICAL: Always cite sources at the end of each response."} instructions={"You are a knowledgeable caving assistant. Help users with all aspects of caving including cave exploration, safety, surveying techniques, cave locations, geology, equipment, history, conservation, and any other caving-related topics. Provide accurate, helpful, and safety-conscious information. CRITICAL: Always cite sources at the end of each response."}
@@ -103,6 +104,17 @@ export default function CopilotKitPage() {
className="h-full w-full" className="h-full w-full"
/> />
</div> </div>
{/* Loading overlay */}
{chatLoading && (
<div className="absolute bottom-24 left-1/2 transform -translate-x-1/2 bg-white shadow-lg rounded-full px-4 py-2 flex items-center gap-2 z-50">
<div className="flex gap-1">
<span className="w-2 h-2 bg-indigo-500 rounded-full animate-bounce" style={{ animationDelay: "0ms" }}></span>
<span className="w-2 h-2 bg-indigo-500 rounded-full animate-bounce" style={{ animationDelay: "150ms" }}></span>
<span className="w-2 h-2 bg-indigo-500 rounded-full animate-bounce" style={{ animationDelay: "300ms" }}></span>
</div>
<span className="text-sm text-gray-600">Thinking...</span>
</div>
)}
</div> </div>
</main> </main>
); );