From dff659605184b9420bd04e0a100863ae7fbf3dea Mon Sep 17 00:00:00 2001 From: Nandha Reddy Date: Fri, 6 Jun 2025 00:36:34 +1000 Subject: [PATCH 1/6] Update Python version requirement to match pyproject.toml The README incorrectly stated Python 3.8+ was sufficient, but backend/pyproject.toml requires Python >=3.11. This caused installation errors for users with Python 3.8-3.10. Fixes #27 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 275a8d8..e004742 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ Follow these steps to get the application running locally for development and te **1. Prerequisites:** - Node.js and npm (or yarn/pnpm) -- Python 3.8+ +- Python 3.11+ - **`GEMINI_API_KEY`**: The backend agent requires a Google Gemini API key. 1. Navigate to the `backend/` directory. 2. Create a file named `.env` by copying the `backend/.env.example` file. From 128e36536ac0fc4c30dcb2d84cfc33f20d1fa7a9 Mon Sep 17 00:00:00 2001 From: philschmid Date: Tue, 10 Jun 2025 07:51:35 +0000 Subject: [PATCH 2/6] update react serving part --- backend/src/agent/app.py | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/backend/src/agent/app.py b/backend/src/agent/app.py index 090e2ed..f20f6ed 100644 --- a/backend/src/agent/app.py +++ b/backend/src/agent/app.py @@ -1,8 +1,7 @@ # mypy: disable - error - code = "no-untyped-def,misc" import pathlib -from fastapi import FastAPI, Request, Response +from fastapi import FastAPI, Response from fastapi.staticfiles import StaticFiles -import fastapi.exceptions # Define the FastAPI app app = FastAPI() @@ -18,7 +17,6 @@ def create_frontend_router(build_dir="../frontend/dist"): A Starlette application serving the frontend. """ build_path = pathlib.Path(__file__).parent.parent.parent / build_dir - static_files_path = build_path / "assets" # Vite uses 'assets' subdir if not build_path.is_dir() or not (build_path / "index.html").is_file(): print( @@ -36,21 +34,7 @@ def create_frontend_router(build_dir="../frontend/dist"): return Route("/{path:path}", endpoint=dummy_frontend) - build_dir = pathlib.Path(build_dir) - - react = FastAPI(openapi_url="") - react.mount( - "/assets", StaticFiles(directory=static_files_path), name="static_assets" - ) - - @react.get("/{path:path}") - async def handle_catch_all(request: Request, path: str): - fp = build_path / path - if not fp.exists() or not fp.is_file(): - fp = build_path / "index.html" - return fastapi.responses.FileResponse(fp) - - return react + return StaticFiles(directory=build_path, html=True) # Mount the frontend under /app to not conflict with the LangGraph API routes From a59a1981bd116da1b88412bb6462ed545ca6a25a Mon Sep 17 00:00:00 2001 From: Yishen Tu Date: Fri, 13 Jun 2025 23:28:31 +0800 Subject: [PATCH 3/6] fix: handle undefined follow_up_queries in reflection event Add optional chaining and fallback value to prevent TypeError when event.reflection.follow_up_queries is undefined in onUpdateEvent handler. --- frontend/src/App.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 6e68e50..9c8d1a3 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -54,9 +54,9 @@ export default function App() { title: "Reflection", data: event.reflection.is_sufficient ? "Search successful, generating final answer." - : `Need more information, searching for ${event.reflection.follow_up_queries.join( + : `Need more information, searching for ${event.reflection.follow_up_queries?.join( ", " - )}`, + ) || "additional information"}`, }; } else if (event.finalize_answer) { processedEvent = { From 5c85818994f58d1dd8ccc03406a2a90f66e94873 Mon Sep 17 00:00:00 2001 From: Yishen Tu Date: Fri, 13 Jun 2025 23:46:43 +0800 Subject: [PATCH 4/6] feat: improve UI/UX with scroll behavior and styling enhancements - Remove white space at bottom of chat interface - Prevent scroll bounce/overscroll behavior on all scroll areas - Make scroll bar thinner and more subtle with low contrast - Add hover effects for scroll bar visibility - Clean up Docker container names for better display - Improve overall scrolling experience and visual polish --- docker-compose.yml | 3 ++ frontend/src/components/ChatMessagesView.tsx | 6 ++-- frontend/src/components/InputForm.tsx | 2 +- frontend/src/components/ui/scroll-area.tsx | 7 +++-- frontend/src/global.css | 33 ++++++++++++++++++++ 5 files changed, 44 insertions(+), 7 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index f348347..6cec5c0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,6 +4,7 @@ volumes: services: langgraph-redis: image: docker.io/redis:6 + container_name: langgraph-redis healthcheck: test: redis-cli ping interval: 5s @@ -11,6 +12,7 @@ services: retries: 5 langgraph-postgres: image: docker.io/postgres:16 + container_name: langgraph-postgres ports: - "5433:5432" environment: @@ -27,6 +29,7 @@ services: interval: 5s langgraph-api: image: gemini-fullstack-langgraph + container_name: langgraph-api ports: - "8123:8000" depends_on: diff --git a/frontend/src/components/ChatMessagesView.tsx b/frontend/src/components/ChatMessagesView.tsx index 1792e6f..f5eb849 100644 --- a/frontend/src/components/ChatMessagesView.tsx +++ b/frontend/src/components/ChatMessagesView.tsx @@ -252,9 +252,9 @@ export function ChatMessagesView({ }; return ( -
- -
+
+ +
{messages.map((message, index) => { const isLast = index === messages.length - 1; return ( diff --git a/frontend/src/components/InputForm.tsx b/frontend/src/components/InputForm.tsx index 6f3127c..b6ac1ea 100644 --- a/frontend/src/components/InputForm.tsx +++ b/frontend/src/components/InputForm.tsx @@ -49,7 +49,7 @@ export const InputForm: React.FC = ({ return (
{children} @@ -38,16 +39,16 @@ function ScrollBar({ className={cn( "flex touch-none p-px transition-colors select-none", orientation === "vertical" && - "h-full w-2.5 border-l border-l-transparent", + "h-full w-1.5 border-l border-l-transparent", orientation === "horizontal" && - "h-2.5 flex-col border-t border-t-transparent", + "h-1.5 flex-col border-t border-t-transparent", className )} {...props} > ) diff --git a/frontend/src/global.css b/frontend/src/global.css index b8b98d4..7e19242 100644 --- a/frontend/src/global.css +++ b/frontend/src/global.css @@ -116,6 +116,13 @@ } body { @apply bg-background text-foreground; + /* Prevent scroll bounce/overscroll on mobile */ + overscroll-behavior: none; + -webkit-overflow-scrolling: touch; + } + html { + /* Prevent scroll bounce on the entire page */ + overscroll-behavior: none; } } @@ -150,5 +157,31 @@ animation: fadeInUpSmooth 0.3s ease-out forwards; } +/* Prevent scroll bounce on scroll areas */ +[data-radix-scroll-area-viewport] { + overscroll-behavior: none !important; + -webkit-overflow-scrolling: touch; +} + +/* Hide any white space that might appear during scroll bounce */ +[data-radix-scroll-area-viewport]::-webkit-scrollbar { + width: 0px; + background: transparent; +} + +/* Subtle scroll bar styling */ +[data-slot="scroll-area-scrollbar"] { + opacity: 0.3; + transition: opacity 0.2s ease; +} + +[data-slot="scroll-area"]:hover [data-slot="scroll-area-scrollbar"] { + opacity: 0.6; +} + +[data-slot="scroll-area-thumb"] { + background-color: rgb(115 115 115 / 0.2) !important; +} + /* Ensure your body or html has a dark background if not already set, e.g.: */ /* body { background-color: #0c0c0d; } */ /* This is similar to neutral-950 */ From 6dab27bf192e3a73fba69ad58198e97704594bc6 Mon Sep 17 00:00:00 2001 From: philschmid Date: Wed, 18 Jun 2025 12:17:50 +0000 Subject: [PATCH 5/6] error display --- backend/src/agent/configuration.py | 4 +-- backend/src/agent/graph.py | 4 +-- frontend/src/App.tsx | 28 +++++++++++++++++--- frontend/src/components/ChatMessagesView.tsx | 6 ++++- 4 files changed, 33 insertions(+), 9 deletions(-) diff --git a/backend/src/agent/configuration.py b/backend/src/agent/configuration.py index 6256dee..e57122d 100644 --- a/backend/src/agent/configuration.py +++ b/backend/src/agent/configuration.py @@ -16,14 +16,14 @@ class Configuration(BaseModel): ) reflection_model: str = Field( - default="gemini-2.5-flash-preview-04-17", + default="gemini-2.5-flash", metadata={ "description": "The name of the language model to use for the agent's reflection." }, ) answer_model: str = Field( - default="gemini-2.5-pro-preview-05-06", + default="gemini-2.5-pro", metadata={ "description": "The name of the language model to use for the agent's answer." }, diff --git a/backend/src/agent/graph.py b/backend/src/agent/graph.py index dae64b7..0340d72 100644 --- a/backend/src/agent/graph.py +++ b/backend/src/agent/graph.py @@ -153,7 +153,7 @@ def reflection(state: OverallState, config: RunnableConfig) -> ReflectionState: configurable = Configuration.from_runnable_config(config) # Increment the research loop count and get the reasoning model state["research_loop_count"] = state.get("research_loop_count", 0) + 1 - reasoning_model = state.get("reasoning_model") or configurable.reasoning_model + reasoning_model = state.get("reasoning_model", configurable.reflection_model) # Format the prompt current_date = get_current_date() @@ -231,7 +231,7 @@ def finalize_answer(state: OverallState, config: RunnableConfig): Dictionary with state update, including running_summary key containing the formatted final summary with sources """ configurable = Configuration.from_runnable_config(config) - reasoning_model = state.get("reasoning_model") or configurable.reasoning_model + reasoning_model = state.get("reasoning_model") or configurable.answer_model # Format the prompt current_date = get_current_date() diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 9c8d1a3..5e186f4 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -4,6 +4,7 @@ import { useState, useEffect, useRef, useCallback } from "react"; import { ProcessedEvent } from "@/components/ActivityTimeline"; import { WelcomeScreen } from "@/components/WelcomeScreen"; import { ChatMessagesView } from "@/components/ChatMessagesView"; +import { Button } from "@/components/ui/button"; export default function App() { const [processedEventsTimeline, setProcessedEventsTimeline] = useState< @@ -14,7 +15,8 @@ export default function App() { >({}); const scrollAreaRef = useRef(null); const hasFinalizeEventOccurredRef = useRef(false); - + const [error, setError] = useState(null); + console.log(import.meta.env.DEV); const thread = useStream<{ messages: Message[]; initial_search_query_count: number; @@ -54,9 +56,10 @@ export default function App() { title: "Reflection", data: event.reflection.is_sufficient ? "Search successful, generating final answer." - : `Need more information, searching for ${event.reflection.follow_up_queries?.join( - ", " - ) || "additional information"}`, + : `Need more information, searching for ${ + event.reflection.follow_up_queries?.join(", ") || + "additional information" + }`, }; } else if (event.finalize_answer) { processedEvent = { @@ -72,6 +75,9 @@ export default function App() { ]); } }, + onError: (error: any) => { + setError(error.message); + }, }); useEffect(() => { @@ -166,6 +172,20 @@ export default function App() { isLoading={thread.isLoading} onCancel={handleCancel} /> + ) : error ? ( +
+
+

Error

+

{JSON.stringify(error)}

+ + +
+
) : ( = ({