Spaces:
Runtime error
Runtime error
| import os | |
| import tempfile | |
| import requests | |
| from typing import Dict, Any, Annotated | |
| from typing_extensions import TypedDict | |
| import gradio as gr | |
| import pandas as pd | |
| # Your constants + imports stay | |
| # New imports for the stack | |
| from smolagents import CodeAgent, HfApiModel # Smolagents for code/web agents | |
| from smolagents.tools import DuckDuckGoSearchResults # Built-in web tool | |
| from langgraph.graph import StateGraph, END | |
| from langgraph.prebuilt import ToolNode, tools_condition | |
| from langchain_core.tools import tool | |
| from langchain_core.messages import HumanMessage, AIMessage | |
| from transformers import pipeline # For lightweight LLM routing | |
| # --- Enhanced Agent with LangGraph + Smolagents --- | |
| class CrmAgent: | |
| def __init__(self): | |
| print("CrmAgent initialized with LangGraph + Smolagents.") | |
| # Lightweight router LLM (free HF inference) | |
| self.router = pipeline("text-generation", model="gpt2", device=-1) # CPU for hack | |
| # Smolagents CodeAgent with web tool | |
| self.llm = HfApiModel(model_id="microsoft/DialoGPT-medium") # Free HF model | |
| search_tool = DuckDuckGoSearchResults(num_results=3) # Quick web hits | |
| self.code_agent = CodeAgent(llm=self.llm, tools=[search_tool]) | |
| # Temp dir for files | |
| self.temp_dir = tempfile.mkdtemp() | |
| # Tool: Download file if needed (GAIA questions may have attachments) | |
| def download_file(self, task_id: str) -> str: | |
| """Downloads file for task_id if exists, returns path.""" | |
| url = f"{DEFAULT_API_URL}/files/{task_id}" | |
| try: | |
| resp = requests.get(url, timeout=10) | |
| if resp.status_code == 200: | |
| file_path = os.path.join(self.temp_dir, f"{task_id}_file") | |
| with open(file_path, "wb") as f: | |
| f.write(resp.content) | |
| return f"File downloaded: {file_path}" | |
| return "No file found." | |
| except Exception as e: | |
| return f"Download error: {e}" | |
| # Router Node: Decide path with LLM | |
| def router_node(self, state: Dict[str, Any]) -> Dict[str, str]: | |
| question = state["question"] | |
| prompt = f"Given question: '{question[:100]}...'. Respond with route: 'search' if needs web info, 'code' if math/file/code, 'both' if both, 'direct' if obvious." | |
| response = self.router(prompt, max_length=20, num_return_sequences=1)[0]["generated_text"] | |
| route = response.strip().lower().split()[-1] # Crude parse, tweak as needed | |
| state["route"] = route | |
| print(f"Routed to: {route}") | |
| return state | |
| # Search Node: Use smolagents web | |
| def search_node(self, state: Dict[str, Any]) -> Dict[str, Any]: | |
| question = state["question"] | |
| try: | |
| # Smolagents call (it handles tool selection internally) | |
| result = self.code_agent.run(question) # Runs code/web as needed | |
| state["search_results"] = result | |
| print(f"Search/code output: {result[:100]}...") | |
| except Exception as e: | |
| state["search_results"] = f"Error: {e}" | |
| return state | |
| # Direct Node: Simple guess or pass | |
| def direct_node(self, state: Dict[str, Any]) -> Dict[str, Any]: | |
| # Fallback: Basic heuristic or empty | |
| state["final_answer"] = "Direct answer needed—implement heuristic here." | |
| return state | |
| # Conditional Edge: Based on route | |
| def conditional_route(self, state: Dict[str, Any]) -> str: | |
| route = state.get("route", "direct") | |
| if route in ["search", "both"]: | |
| return "search" | |
| elif route == "code": | |
| return "search" # Smolagents handles code too | |
| return "direct" | |
| # Build the Graph | |
| def build_graph(self): | |
| # State | |
| class AgentState(TypedDict): | |
| question: str | |
| route: str | |
| search_results: str | |
| final_answer: str | |
| # Graph | |
| workflow = StateGraph(AgentState) | |
| workflow.add_node("router", self.router_node) | |
| workflow.add_node("search", self.search_node) | |
| workflow.add_node("direct", self.direct_node) | |
| # Edges | |
| workflow.set_entry_point("router") | |
| workflow.add_conditional_edges("router", self.conditional_route, {"search": "search", "direct": "direct"}) | |
| workflow.add_edge("search", END) | |
| workflow.add_edge("direct", END) | |
| # Compile | |
| self.graph = workflow.compile() | |
| def __call__(self, question: str, task_id: str = None) -> str: | |
| if not hasattr(self, "graph"): | |
| self.build_graph() | |
| # Download file if task_id | |
| if task_id: | |
| file_info = self.download_file.invoke({"task_id": task_id}) | |
| question += f" [File info: {file_info}]" # Append to prompt | |
| # Run graph | |
| initial_state = {"question": question, "route": "", "search_results": "", "final_answer": ""} | |
| final_state = self.graph.invoke(initial_state) | |
| # Extract clean answer (smolagents outputs code-thought → result) | |
| answer = final_state.get("search_results", final_state.get("final_answer", "No answer generated.")) | |
| # Strip to exact (no extras) | |
| if "final answer" in answer.lower(): | |
| answer = answer.split("final answer")[-1].strip().split()[0] if answer.split("final answer")[-1].strip() else answer | |
| print(f"Agent final: {answer}") | |
| return answer | |
| # --- Update run_and_submit_all (minor tweak for task_id) --- | |
| def run_and_submit_all(profile: gr.OAuthProfile | None): | |
| # ... (keep all your existing code up to agent init) | |
| # 1. Instantiate Agent | |
| try: | |
| agent = CrmAgent() # Our new beast | |
| except Exception as e: | |
| # ... | |
| # 3. Run your Agent (pass task_id) | |
| results_log = [] | |
| answers_payload = [] | |
| print(f"Running agent on {len(questions_data)} questions...") | |
| for item in questions_data: | |
| task_id = item.get("task_id") | |
| question_text = item.get("question") | |
| if not task_id or question_text is None: | |
| # ... | |
| try: | |
| submitted_answer = agent(question_text, task_id) # Pass task_id for files | |
| answers_payload.append({"task_id": task_id, "submitted_answer": submitted_answer}) | |
| results_log.append({"Task ID": task_id, "Question": question_text[:50] + "...", "Submitted Answer": submitted_answer}) | |
| except Exception as e: | |
| # ... | |
| # ... (rest unchanged—submit as before) |