Skip to content

Windows fixes #127

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 26 additions & 7 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,20 +1,39 @@
.PHONY: help dev-frontend dev-backend dev
.PHONY: help install install-backend install-frontend dev-frontend dev-backend dev

help:
@echo "Available commands:"
@echo " make dev-frontend - Starts the frontend development server (Vite)"
@echo " make dev-backend - Starts the backend development server (Uvicorn with reload)"
@echo " make dev - Starts both frontend and backend development servers"
@echo " make install - Installs all dependencies for Windows"
@echo " make dev - Starts both frontend and backend servers for Windows"
@echo " ---"
@echo " make install-backend - Installs backend dependencies"
@echo " make install-frontend - Installs frontend dependencies"
@echo " make dev-frontend - Starts the frontend development server (Vite)"
@echo " make dev-backend - Starts the backend development server (Langgraph)"


install: install-backend install-frontend

install-backend:
@echo "Checking for backend virtual environment..."
@if not exist backend\venv (py -m venv backend\venv)
@echo "Installing backend dependencies..."
@call backend\venv\Scripts\activate.bat && pip install -e backend

install-frontend:
@echo "Installing frontend dependencies..."
@cd frontend && npm install

dev-frontend:
@echo "Starting frontend development server..."
@cd frontend && npm run dev

dev-backend:
@echo "Starting backend development server..."
@cd backend && langgraph dev
@echo "Activating backend environment and starting server..."
@cd backend && call .\venv\Scripts\activate.bat && langgraph dev

# Run frontend and backend concurrently
dev:
@echo "Starting both frontend and backend development servers..."
@make dev-frontend & make dev-backend
@echo "Starting both frontend and backend development servers in new windows..."
@start "Frontend" make dev-frontend
@start "Backend" make dev-backend
48 changes: 47 additions & 1 deletion backend/src/agent/app.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,31 @@
# mypy: disable - error - code = "no-untyped-def,misc"
import pathlib
from fastapi import FastAPI, Response
from typing import List, Any
from fastapi import FastAPI, Response, Body
from fastapi.staticfiles import StaticFiles
from pydantic import BaseModel

# Define the FastAPI app
app = FastAPI()

# In-memory store for sessions
sessions_db: List[Any] = []


class SessionItem(BaseModel):
session_id: str
messages: List[Any] # Replace Any with your Message model if available
historicalActivities: Any # Replace Any with your Activity model if available

@app.post("/sessions")
async def save_session(session_item: SessionItem = Body(...)):
sessions_db.append(session_item.dict())
return {"status": "ok", "session_id": session_item.session_id}

@app.get("/sessions")
async def get_sessions() -> List[Any]:
return sessions_db


def create_frontend_router(build_dir="../frontend/dist"):
"""Creates a router to serve the React frontend.
Expand All @@ -18,6 +38,32 @@ def create_frontend_router(build_dir="../frontend/dist"):
"""
build_path = pathlib.Path(__file__).parent.parent.parent / build_dir

print(f"DEBUG: __file__ is {__file__}")
print(f"DEBUG: build_dir is {build_dir}")
print(f"DEBUG: Calculated build_path is {build_path}")
try:
abs_build_path = build_path.resolve()
print(f"DEBUG: Absolute build_path is {abs_build_path}")
print(f"DEBUG: build_path exists: {abs_build_path.exists()}")
print(f"DEBUG: build_path is_dir: {abs_build_path.is_dir()}")
index_html_path = abs_build_path / "index.html"
print(f"DEBUG: index_html_path is {index_html_path}")
print(f"DEBUG: index_html_path exists: {index_html_path.exists()}")
print(f"DEBUG: index_html_path is_file: {index_html_path.is_file()}")

# Try to list directories - this might fail if paths are incorrect or due to permissions
try:
frontend_dir_to_list = pathlib.Path("C:/AI-Programmi/new/gemini-fullstack-langgraph-quickstart/frontend")
print(f"DEBUG: Listing {frontend_dir_to_list}: {list(frontend_dir_to_list.iterdir()) if frontend_dir_to_list.exists() else 'DOES NOT EXIST'}")
frontend_dist_dir_to_list = pathlib.Path("C:/AI-Programmi/new/gemini-fullstack-langgraph-quickstart/frontend/dist")
print(f"DEBUG: Listing {frontend_dist_dir_to_list}: {list(frontend_dist_dir_to_list.iterdir()) if frontend_dist_dir_to_list.exists() else 'DOES NOT EXIST'}")
except Exception as e:
print(f"DEBUG: Error listing directories: {e}")

except Exception as e:
print(f"DEBUG: Error resolving or checking build_path: {e}")


if not build_path.is_dir() or not (build_path / "index.html").is_file():
print(
f"WARN: Frontend build directory not found or incomplete at {build_path}. Serving frontend will likely fail."
Expand Down
232 changes: 232 additions & 0 deletions backup/App.tsx.bak
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
import { useStream } from "@langchain/langgraph-sdk/react";
import type { Message } from "@langchain/langgraph-sdk";
import { useState, useEffect, useRef, useCallback } from "react";
import { ProcessedEvent } from "@/components/ActivityTimeline";
import { WelcomeScreen } from "@/components/WelcomeScreen";
import { ChatMessagesView } from "@/components/ChatMessagesView";
import { HistoryPanel } from "@/components/HistoryPanel";

const API_URL = import.meta.env.DEV
? "http://localhost:2024"
: "http://localhost:8123";

export default function App() {
const [processedEventsTimeline, setProcessedEventsTimeline] = useState<
ProcessedEvent[]
>([]);
const [historicalActivities, setHistoricalActivities] = useState<
Record<string, ProcessedEvent[]>
>({});
const scrollAreaRef = useRef<HTMLDivElement>(null);
const hasFinalizeEventOccurredRef = useRef(false);
const [history, setHistory] = useState<any[]>([]);
const [threadId, setThreadId] = useState<string | null>(null);
const [messages, setMessages] = useState<Message[]>([]);
const thread = useStream<{
messages: Message[];
initial_search_query_count: number;
max_research_loops: number;
reasoning_model: string;
}>({
apiUrl: API_URL,
assistantId: "agent",
threadId,
onThreadId: setThreadId,
messagesKey: "messages",
onUpdateEvent: (event: any) => {
let processedEvent: ProcessedEvent | null = null;
if (event.generate_query) {
processedEvent = {
title: "Generating Search Queries",
data: event.generate_query?.search_query?.join(", ") || "",
};
} else if (event.web_research) {
const sources = event.web_research.sources_gathered || [];
const numSources = sources.length;
const uniqueLabels = [
...new Set(sources.map((s: any) => s.label).filter(Boolean)),
];
const exampleLabels = uniqueLabels.slice(0, 3).join(", ");
processedEvent = {
title: "Web Research",
data: `Gathered ${numSources} sources. Related to: ${
exampleLabels || "N/A"
}.`,
};
} else if (event.reflection) {
processedEvent = {
title: "Reflection",
data: "Analysing Web Research Results",
};
} else if (event.finalize_answer) {
processedEvent = {
title: "Finalizing Answer",
data: "Composing and presenting the final answer.",
};
hasFinalizeEventOccurredRef.current = true;
}
if (processedEvent) {
setProcessedEventsTimeline((prevEvents) => [
...prevEvents,
processedEvent!,
]);
}
},
onFinish: async (threadState) => {
if (threadId) {
const lastMessage =
threadState.values.messages[
threadState.values.messages.length - 1
];
let updatedHistoricalActivities = { ...historicalActivities };
if (lastMessage && lastMessage.type === "ai" && lastMessage.id) {
updatedHistoricalActivities = {
...historicalActivities,
[lastMessage.id]: [...processedEventsTimeline],
};
setHistoricalActivities(updatedHistoricalActivities);
}

const newHistoryItem = {
session_id: threadId,
messages: threadState.values.messages,
historicalActivities: updatedHistoricalActivities,
};

try {
await fetch(`${API_URL}/sessions`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(newHistoryItem),
});
setHistory((prevHistory) => [newHistoryItem, ...prevHistory]);
} catch (error) {
console.error("Failed to save history:", error);
}
}
},
onError: (error: any) => {
console.error(error);
},
});

useEffect(() => {
async function fetchHistory() {
try {
const response = await fetch(`${API_URL}/sessions`);
const data = await response.json();
setHistory(data);
} catch (error) {
console.error("Failed to fetch history:", error);
}
}
fetchHistory();
}, []);

useEffect(() => {
if (scrollAreaRef.current) {
const scrollViewport = scrollAreaRef.current.querySelector(
"[data-radix-scroll-area-viewport]"
);
if (scrollViewport) {
scrollViewport.scrollTop = scrollViewport.scrollHeight;
}
}
}, [messages]);

useEffect(() => {
if (thread.messages.length > messages.length) {
setMessages(thread.messages);
}
}, [thread.messages, messages]);

const handleSelectHistory = (historyItem: any) => {
setThreadId(historyItem.session_id);
setMessages(historyItem.messages);
setHistoricalActivities(historyItem.historicalActivities || {});
setProcessedEventsTimeline([]);
};

const handleSubmit = useCallback(
async (submittedInputValue: string, effort: string, model: string) => {
if (!submittedInputValue.trim() || thread.isLoading) return;
setProcessedEventsTimeline([]);
hasFinalizeEventOccurredRef.current = false;
if (!threadId) {
setThreadId(new Date().toISOString());
}

// convert effort to, initial_search_query_count and max_research_loops
// low means max 1 loop and 1 query
// medium means max 3 loops and 3 queries
// high means max 10 loops and 5 queries
let initial_search_query_count = 0;
let max_research_loops = 0;
switch (effort) {
case "low":
initial_search_query_count = 1;
max_research_loops = 1;
break;
case "medium":
initial_search_query_count = 3;
max_research_loops = 3;
break;
case "high":
initial_search_query_count = 5;
max_research_loops = 10;
break;
}

const newMessages: Message[] = [
...messages,
{
type: "human",
content: submittedInputValue,
id: Date.now().toString(),
},
];
setMessages(newMessages);
thread.submit({
messages: newMessages,
initial_search_query_count: initial_search_query_count,
max_research_loops: max_research_loops,
reasoning_model: model,
});
},
[thread, threadId, messages]
);

const handleCancel = useCallback(() => {
thread.stop();
}, [thread]);

return (
<div className="flex h-screen bg-neutral-800 text-neutral-100 font-sans antialiased">
<HistoryPanel
history={history}
onSelectHistory={handleSelectHistory}
/>
<main className="h-full w-full max-w-4xl mx-auto">
{messages.length === 0 ? (
<WelcomeScreen
handleSubmit={handleSubmit}
isLoading={thread.isLoading}
onCancel={handleCancel}
/>
) : (
<ChatMessagesView
messages={messages}
isLoading={thread.isLoading}
scrollAreaRef={scrollAreaRef}
onSubmit={handleSubmit}
onCancel={handleCancel}
liveActivityEvents={processedEventsTimeline}
historicalActivities={historicalActivities}
/>
)}
</main>
</div>
);
}
Loading