josondev commited on
Commit
d4557ee
·
verified ·
1 Parent(s): c5a2611

Update veryfinal.py

Browse files
Files changed (1) hide show
  1. veryfinal.py +144 -339
veryfinal.py CHANGED
@@ -1,13 +1,14 @@
1
  """Enhanced LangGraph + Agno Hybrid Agent System"""
2
- import os, time, random, asyncio
 
 
3
  from dotenv import load_dotenv
4
  from typing import List, Dict, Any, TypedDict, Annotated
5
  import operator
6
 
7
  # LangGraph imports
8
  from langgraph.graph import START, StateGraph, MessagesState
9
- from langgraph.prebuilt import tools_condition
10
- from langgraph.prebuilt import ToolNode
11
  from langgraph.checkpoint.memory import MemorySaver
12
 
13
  # LangChain imports
@@ -15,82 +16,73 @@ from langchain_core.messages import SystemMessage, HumanMessage, AIMessage
15
  from langchain_core.tools import tool
16
  from langchain_groq import ChatGroq
17
  from langchain_google_genai import ChatGoogleGenerativeAI
 
18
  from langchain_community.tools.tavily_search import TavilySearchResults
19
- from langchain_community.document_loaders import WikipediaLoader
20
  from langchain_community.vectorstores import FAISS
21
- from langchain_nvidia_ai_endpoints import NVIDIAEmbeddings
22
  from langchain.tools.retriever import create_retriever_tool
23
  from langchain_text_splitters import RecursiveCharacterTextSplitter
24
- from langchain_community.document_loaders import JSONLoader
25
 
26
  # Agno imports
27
  from agno.agent import Agent
28
- from agno.models.groq import Groq
29
- from agno.models.google import Gemini
30
  from agno.tools.duckduckgo import DuckDuckGoTools
31
  from agno.memory.agent import AgentMemory
32
- from agno.storage.sqlite import SqliteStorage
33
 
34
  load_dotenv()
35
 
36
- # Enhanced Rate Limiter with Performance Optimization
37
  class PerformanceRateLimiter:
38
- def __init__(self, requests_per_minute: int, provider_name: str):
39
- self.requests_per_minute = requests_per_minute
40
- self.provider_name = provider_name
41
- self.request_times = []
42
- self.consecutive_failures = 0
43
- self.performance_cache = {} # Cache for repeated queries
44
-
45
  def wait_if_needed(self):
46
- current_time = time.time()
47
- self.request_times = [t for t in self.request_times if current_time - t < 60]
48
-
49
- if len(self.request_times) >= self.requests_per_minute:
50
- wait_time = 60 - (current_time - self.request_times[0]) + random.uniform(1, 3)
51
- time.sleep(wait_time)
52
-
53
- if self.consecutive_failures > 0:
54
- backoff_time = min(2 ** self.consecutive_failures, 30) + random.uniform(0.5, 1.5)
55
- time.sleep(backoff_time)
56
-
57
- self.request_times.append(current_time)
58
-
59
  def record_success(self):
60
- self.consecutive_failures = 0
61
-
62
  def record_failure(self):
63
- self.consecutive_failures += 1
64
 
65
- # Initialize optimized rate limiters
66
- gemini_limiter = PerformanceRateLimiter(requests_per_minute=28, provider_name="Gemini")
67
- groq_limiter = PerformanceRateLimiter(requests_per_minute=28, provider_name="Groq")
68
- nvidia_limiter = PerformanceRateLimiter(requests_per_minute=4, provider_name="NVIDIA")
69
 
70
- # Agno Agent Setup with Performance Optimization
71
  def create_agno_agents():
72
- """Create high-performance Agno agents"""
73
-
74
- # Storage for persistent memory
75
  storage = SqliteStorage(
76
  table_name="agent_sessions",
77
- db_file="tmp/agent_storage.db"
 
78
  )
79
-
80
- # Math specialist using Groq (fastest)
81
  math_agent = Agent(
82
  name="MathSpecialist",
83
- model=Groq(
84
  model="llama-3.3-70b-versatile",
85
  api_key=os.getenv("GROQ_API_KEY"),
86
  temperature=0
87
  ),
88
  description="Expert mathematical problem solver",
89
  instructions=[
90
- "Solve mathematical problems with precision",
91
  "Show step-by-step calculations",
92
- "Use tools for complex computations",
93
- "Always provide numerical answers"
94
  ],
95
  memory=AgentMemory(
96
  db=storage,
@@ -100,21 +92,18 @@ def create_agno_agents():
100
  show_tool_calls=False,
101
  markdown=False
102
  )
103
-
104
- # Research specialist using Gemini (most capable)
105
  research_agent = Agent(
106
  name="ResearchSpecialist",
107
- model=Gemini(
108
  model="gemini-2.0-flash-lite",
109
  api_key=os.getenv("GOOGLE_API_KEY"),
110
  temperature=0
111
  ),
112
- description="Expert research and information gathering specialist",
113
  instructions=[
114
- "Conduct thorough research using available tools",
115
- "Synthesize information from multiple sources",
116
- "Provide comprehensive, well-cited answers",
117
- "Focus on accuracy and relevance"
118
  ],
119
  tools=[DuckDuckGoTools()],
120
  memory=AgentMemory(
@@ -125,337 +114,153 @@ def create_agno_agents():
125
  show_tool_calls=False,
126
  markdown=False
127
  )
128
-
129
- return {
130
- "math": math_agent,
131
- "research": research_agent
132
- }
133
 
134
- # LangGraph Tools (optimized)
135
  @tool
136
  def multiply(a: int, b: int) -> int:
137
- """Multiply two numbers."""
138
  return a * b
139
 
140
  @tool
141
  def add(a: int, b: int) -> int:
142
- """Add two numbers."""
143
  return a + b
144
 
145
  @tool
146
  def subtract(a: int, b: int) -> int:
147
- """Subtract two numbers."""
148
  return a - b
149
 
150
  @tool
151
  def divide(a: int, b: int) -> float:
152
- """Divide two numbers."""
153
  if b == 0:
154
  raise ValueError("Cannot divide by zero.")
155
  return a / b
156
 
157
  @tool
158
  def modulus(a: int, b: int) -> int:
159
- """Get the modulus of two numbers."""
160
  return a % b
161
 
162
  @tool
163
  def optimized_web_search(query: str) -> str:
164
- """Optimized web search with caching."""
165
  try:
166
- time.sleep(random.uniform(1, 2)) # Reduced wait time
167
- search_docs = TavilySearchResults(max_results=2).invoke(query=query) # Reduced results for speed
168
- formatted_search_docs = "\n\n---\n\n".join([
169
- f'<Document source="{doc.get("url", "")}" />\n{doc.get("content", "")[:500]}\n</Document>' # Truncated for speed
170
- for doc in search_docs
171
- ])
172
- return formatted_search_docs
173
  except Exception as e:
174
- return f"Web search failed: {str(e)}"
175
 
176
  @tool
177
  def optimized_wiki_search(query: str) -> str:
178
- """Optimized Wikipedia search."""
179
  try:
180
- time.sleep(random.uniform(0.5, 1)) # Reduced wait time
181
- search_docs = WikipediaLoader(query=query, load_max_docs=1).load()
182
- formatted_search_docs = "\n\n---\n\n".join([
183
- f'<Document source="{doc.metadata["source"]}" />\n{doc.page_content[:800]}\n</Document>' # Truncated for speed
184
- for doc in search_docs
185
- ])
186
- return formatted_search_docs
187
  except Exception as e:
188
- return f"Wikipedia search failed: {str(e)}"
189
 
190
- # Optimized FAISS setup
191
- def setup_optimized_faiss():
192
- """Setup optimized FAISS vector store"""
193
  try:
194
- jq_schema = """
195
- {
196
- page_content: .Question,
197
- metadata: {
198
- task_id: .task_id,
199
- Final_answer: ."Final answer"
200
- }
201
- }
202
  """
203
-
204
- json_loader = JSONLoader(file_path="metadata.jsonl", jq_schema=jq_schema, json_lines=True, text_content=False)
205
- json_docs = json_loader.load()
206
-
207
- # Smaller chunks for faster processing
208
- text_splitter = RecursiveCharacterTextSplitter(chunk_size=256, chunk_overlap=50)
209
- json_chunks = text_splitter.split_documents(json_docs)
210
-
211
- embeddings = NVIDIAEmbeddings(
212
- model="nvidia/nv-embedqa-e5-v5",
213
- api_key=os.getenv("NVIDIA_API_KEY")
214
- )
215
- vector_store = FAISS.from_documents(json_chunks, embeddings)
216
-
217
- return vector_store
218
  except Exception as e:
219
  print(f"FAISS setup failed: {e}")
220
  return None
221
 
222
- # Enhanced State with Performance Tracking
223
- class EnhancedAgentState(TypedDict):
224
- messages: Annotated[List[HumanMessage | AIMessage], operator.add]
225
  query: str
226
  agent_type: str
227
  final_answer: str
228
- performance_metrics: Dict[str, Any]
229
- agno_response: str
230
 
231
- # Hybrid LangGraph + Agno System
232
- class HybridLangGraphAgnoSystem:
233
  def __init__(self):
234
- self.agno_agents = create_agno_agents()
235
- self.vector_store = setup_optimized_faiss()
236
- self.langgraph_tools = [multiply, add, subtract, divide, modulus, optimized_web_search, optimized_wiki_search]
237
-
238
- if self.vector_store:
239
- retriever = self.vector_store.as_retriever(search_type="similarity", search_kwargs={"k": 2})
240
- retriever_tool = create_retriever_tool(
241
- retriever=retriever,
242
- name="Question_Search",
243
- description="Retrieve similar questions from knowledge base."
244
- )
245
- self.langgraph_tools.append(retriever_tool)
246
-
247
- self.graph = self._build_hybrid_graph()
248
-
249
- def _build_hybrid_graph(self):
250
- """Build hybrid LangGraph with Agno integration"""
251
-
252
- # LangGraph LLMs
253
- groq_llm = ChatGroq(model="llama-3.3-70b-versatile", temperature=0)
254
- gemini_llm = ChatGoogleGenerativeAI(model="gemini-2.0-flash-lite", temperature=0)
255
-
256
- def router_node(state: EnhancedAgentState) -> EnhancedAgentState:
257
- """Smart routing between LangGraph and Agno"""
258
- query = state["query"].lower()
259
-
260
- # Route math to LangGraph (faster for calculations)
261
- if any(word in query for word in ['calculate', 'math', 'multiply', 'add', 'subtract', 'divide']):
262
- agent_type = "langgraph_math"
263
- # Route complex research to Agno (better reasoning)
264
- elif any(word in query for word in ['research', 'analyze', 'explain', 'compare']):
265
- agent_type = "agno_research"
266
- # Route factual queries to LangGraph (faster retrieval)
267
- elif any(word in query for word in ['what is', 'who is', 'when', 'where']):
268
- agent_type = "langgraph_retrieval"
269
- else:
270
- agent_type = "agno_general"
271
-
272
- return {**state, "agent_type": agent_type}
273
-
274
- def langgraph_math_node(state: EnhancedAgentState) -> EnhancedAgentState:
275
- """LangGraph math processing (optimized for speed)"""
276
  groq_limiter.wait_if_needed()
277
-
278
- start_time = time.time()
279
- llm_with_tools = groq_llm.bind_tools([multiply, add, subtract, divide, modulus])
280
-
281
- system_msg = SystemMessage(content="You are a fast mathematical calculator. Use tools for calculations. Provide precise numerical answers. Format: FINAL ANSWER: [result]")
282
- messages = [system_msg, HumanMessage(content=state["query"])]
283
-
284
- try:
285
- response = llm_with_tools.invoke(messages)
286
- processing_time = time.time() - start_time
287
-
288
- return {
289
- **state,
290
- "messages": state["messages"] + [response],
291
- "final_answer": response.content,
292
- "performance_metrics": {"processing_time": processing_time, "provider": "LangGraph-Groq"}
293
- }
294
- except Exception as e:
295
- return {**state, "final_answer": f"Math processing error: {str(e)}"}
296
-
297
- def agno_research_node(state: EnhancedAgentState) -> EnhancedAgentState:
298
- """Agno research processing (optimized for quality)"""
299
  gemini_limiter.wait_if_needed()
300
-
301
- start_time = time.time()
302
- try:
303
- # Use Agno's research agent for complex reasoning
304
- response = self.agno_agents["research"].run(state["query"], stream=False)
305
- processing_time = time.time() - start_time
306
-
307
- return {
308
- **state,
309
- "agno_response": response,
310
- "final_answer": response,
311
- "performance_metrics": {"processing_time": processing_time, "provider": "Agno-Gemini"}
312
- }
313
- except Exception as e:
314
- return {**state, "final_answer": f"Research processing error: {str(e)}"}
315
-
316
- def langgraph_retrieval_node(state: EnhancedAgentState) -> EnhancedAgentState:
317
- """LangGraph retrieval processing (optimized for speed)"""
318
  groq_limiter.wait_if_needed()
319
-
320
- start_time = time.time()
321
- llm_with_tools = groq_llm.bind_tools(self.langgraph_tools)
322
-
323
- system_msg = SystemMessage(content="You are a fast information retrieval assistant. Use search tools efficiently. Provide concise, accurate answers. Format: FINAL ANSWER: [answer]")
324
- messages = [system_msg, HumanMessage(content=state["query"])]
325
-
326
- try:
327
- response = llm_with_tools.invoke(messages)
328
- processing_time = time.time() - start_time
329
-
330
- return {
331
- **state,
332
- "messages": state["messages"] + [response],
333
- "final_answer": response.content,
334
- "performance_metrics": {"processing_time": processing_time, "provider": "LangGraph-Retrieval"}
335
- }
336
- except Exception as e:
337
- return {**state, "final_answer": f"Retrieval processing error: {str(e)}"}
338
-
339
- def agno_general_node(state: EnhancedAgentState) -> EnhancedAgentState:
340
- """Agno general processing"""
341
- gemini_limiter.wait_if_needed()
342
-
343
- start_time = time.time()
344
- try:
345
- # Route to appropriate Agno agent based on query complexity
346
- if any(word in state["query"].lower() for word in ['calculate', 'compute']):
347
- response = self.agno_agents["math"].run(state["query"], stream=False)
348
- else:
349
- response = self.agno_agents["research"].run(state["query"], stream=False)
350
-
351
- processing_time = time.time() - start_time
352
-
353
- return {
354
- **state,
355
- "agno_response": response,
356
- "final_answer": response,
357
- "performance_metrics": {"processing_time": processing_time, "provider": "Agno-General"}
358
- }
359
- except Exception as e:
360
- return {**state, "final_answer": f"General processing error: {str(e)}"}
361
-
362
- def route_agent(state: EnhancedAgentState) -> str:
363
- """Route to appropriate processing node"""
364
- agent_type = state.get("agent_type", "agno_general")
365
- return agent_type
366
-
367
- # Build the graph
368
- builder = StateGraph(EnhancedAgentState)
369
- builder.add_node("router", router_node)
370
- builder.add_node("langgraph_math", langgraph_math_node)
371
- builder.add_node("agno_research", agno_research_node)
372
- builder.add_node("langgraph_retrieval", langgraph_retrieval_node)
373
- builder.add_node("agno_general", agno_general_node)
374
-
375
- builder.set_entry_point("router")
376
- builder.add_conditional_edges(
377
- "router",
378
- route_agent,
379
- {
380
- "langgraph_math": "langgraph_math",
381
- "agno_research": "agno_research",
382
- "langgraph_retrieval": "langgraph_retrieval",
383
- "agno_general": "agno_general"
384
- }
385
- )
386
-
387
- # All nodes end the workflow
388
- for node in ["langgraph_math", "agno_research", "langgraph_retrieval", "agno_general"]:
389
- builder.add_edge(node, "END")
390
-
391
- memory = MemorySaver()
392
- return builder.compile(checkpointer=memory)
393
-
394
- def process_query(self, query: str) -> Dict[str, Any]:
395
- """Process query with performance optimization"""
396
- start_time = time.time()
397
-
398
- initial_state = {
399
- "messages": [HumanMessage(content=query)],
400
- "query": query,
401
- "agent_type": "",
402
- "final_answer": "",
403
- "performance_metrics": {},
404
- "agno_response": ""
405
- }
406
-
407
- config = {"configurable": {"thread_id": f"hybrid_{hash(query)}"}}
408
-
409
  try:
410
- result = self.graph.invoke(initial_state, config)
411
- total_time = time.time() - start_time
412
-
413
- return {
414
- "answer": result.get("final_answer", "No response generated"),
415
- "performance_metrics": {
416
- **result.get("performance_metrics", {}),
417
- "total_time": total_time
418
- },
419
- "provider_used": result.get("performance_metrics", {}).get("provider", "Unknown")
420
- }
421
  except Exception as e:
422
- return {
423
- "answer": f"Error: {str(e)}",
424
- "performance_metrics": {"total_time": time.time() - start_time, "error": True},
425
- "provider_used": "Error"
426
- }
427
-
428
- # Build graph function for compatibility
429
- def build_graph(provider: str = "hybrid"):
430
- """Build the hybrid graph system"""
431
- if provider == "hybrid":
432
- system = HybridLangGraphAgnoSystem()
433
- return system.graph
434
- else:
435
- # Fallback to original implementation
436
- return build_original_graph(provider)
437
 
438
- def build_original_graph(provider: str):
439
- """Original graph implementation for fallback"""
440
- # Implementation of original graph...
441
- pass
442
 
443
- # Main execution
444
- if __name__ == "__main__":
445
- # Test the hybrid system
446
- hybrid_system = HybridLangGraphAgnoSystem()
447
-
448
- test_queries = [
449
- "What is 25 * 4 + 10?", # Should route to LangGraph math
450
- "Explain the economic impacts of AI automation", # Should route to Agno research
451
- "What are the names of US presidents who were assassinated?", # Should route to LangGraph retrieval
452
- "Compare quantum computing with classical computing" # Should route to Agno general
453
- ]
454
-
455
- for query in test_queries:
456
- print(f"\nQuery: {query}")
457
- result = hybrid_system.process_query(query)
458
- print(f"Answer: {result['answer']}")
459
- print(f"Provider: {result['provider_used']}")
460
- print(f"Processing Time: {result['performance_metrics'].get('total_time', 0):.2f}s")
461
- print("-" * 80)
 
1
  """Enhanced LangGraph + Agno Hybrid Agent System"""
2
+ import os
3
+ import time
4
+ import random
5
  from dotenv import load_dotenv
6
  from typing import List, Dict, Any, TypedDict, Annotated
7
  import operator
8
 
9
  # LangGraph imports
10
  from langgraph.graph import START, StateGraph, MessagesState
11
+ from langgraph.prebuilt import tools_condition, ToolNode
 
12
  from langgraph.checkpoint.memory import MemorySaver
13
 
14
  # LangChain imports
 
16
  from langchain_core.tools import tool
17
  from langchain_groq import ChatGroq
18
  from langchain_google_genai import ChatGoogleGenerativeAI
19
+ from langchain_nvidia_ai_endpoints import ChatNVIDIA, NVIDIAEmbeddings
20
  from langchain_community.tools.tavily_search import TavilySearchResults
21
+ from langchain_community.document_loaders import WikipediaLoader, ArxivLoader, JSONLoader
22
  from langchain_community.vectorstores import FAISS
 
23
  from langchain.tools.retriever import create_retriever_tool
24
  from langchain_text_splitters import RecursiveCharacterTextSplitter
 
25
 
26
  # Agno imports
27
  from agno.agent import Agent
28
+ from agno.models.groq import GroqChat
29
+ from agno.models.google import GeminiChat
30
  from agno.tools.duckduckgo import DuckDuckGoTools
31
  from agno.memory.agent import AgentMemory
32
+ from agno.storage.sqlite import SqliteStorage # updated per docs
33
 
34
  load_dotenv()
35
 
36
+ # Rate limiter with exponential backoff
37
  class PerformanceRateLimiter:
38
+ def __init__(self, rpm: int, name: str):
39
+ self.rpm = rpm
40
+ self.name = name
41
+ self.times = []
42
+ self.failures = 0
43
+
 
44
  def wait_if_needed(self):
45
+ now = time.time()
46
+ self.times = [t for t in self.times if now - t < 60]
47
+ if len(self.times) >= self.rpm:
48
+ wait = 60 - (now - self.times[0]) + random.uniform(1, 3)
49
+ time.sleep(wait)
50
+ if self.failures:
51
+ backoff = min(2 ** self.failures, 30) + random.uniform(0.5, 1.5)
52
+ time.sleep(backoff)
53
+ self.times.append(now)
54
+
 
 
 
55
  def record_success(self):
56
+ self.failures = 0
57
+
58
  def record_failure(self):
59
+ self.failures += 1
60
 
61
+ # initialize limiters
62
+ gemini_limiter = PerformanceRateLimiter(28, "Gemini")
63
+ groq_limiter = PerformanceRateLimiter(28, "Groq")
64
+ nvidia_limiter = PerformanceRateLimiter(4, "NVIDIA")
65
 
66
+ # create Agno agents with SQLite storage
67
  def create_agno_agents():
 
 
 
68
  storage = SqliteStorage(
69
  table_name="agent_sessions",
70
+ db_file="tmp/agent_sessions.db",
71
+ auto_upgrade_schema=True
72
  )
 
 
73
  math_agent = Agent(
74
  name="MathSpecialist",
75
+ model=GroqChat(
76
  model="llama-3.3-70b-versatile",
77
  api_key=os.getenv("GROQ_API_KEY"),
78
  temperature=0
79
  ),
80
  description="Expert mathematical problem solver",
81
  instructions=[
82
+ "Solve math problems with precision",
83
  "Show step-by-step calculations",
84
+ "Use calculation tools as needed",
85
+ "Finish with: FINAL ANSWER: [result]"
86
  ],
87
  memory=AgentMemory(
88
  db=storage,
 
92
  show_tool_calls=False,
93
  markdown=False
94
  )
 
 
95
  research_agent = Agent(
96
  name="ResearchSpecialist",
97
+ model=GeminiChat(
98
  model="gemini-2.0-flash-lite",
99
  api_key=os.getenv("GOOGLE_API_KEY"),
100
  temperature=0
101
  ),
102
+ description="Expert research and information specialist",
103
  instructions=[
104
+ "Use web and wiki tools to gather data",
105
+ "Synthesize information with clarity",
106
+ "Cite sources and finish with: FINAL ANSWER: [answer]"
 
107
  ],
108
  tools=[DuckDuckGoTools()],
109
  memory=AgentMemory(
 
114
  show_tool_calls=False,
115
  markdown=False
116
  )
117
+ return {"math": math_agent, "research": research_agent}
 
 
 
 
118
 
119
+ # LangGraph tools
120
  @tool
121
  def multiply(a: int, b: int) -> int:
 
122
  return a * b
123
 
124
  @tool
125
  def add(a: int, b: int) -> int:
 
126
  return a + b
127
 
128
  @tool
129
  def subtract(a: int, b: int) -> int:
 
130
  return a - b
131
 
132
  @tool
133
  def divide(a: int, b: int) -> float:
 
134
  if b == 0:
135
  raise ValueError("Cannot divide by zero.")
136
  return a / b
137
 
138
  @tool
139
  def modulus(a: int, b: int) -> int:
 
140
  return a % b
141
 
142
  @tool
143
  def optimized_web_search(query: str) -> str:
 
144
  try:
145
+ time.sleep(random.uniform(1, 2))
146
+ docs = TavilySearchResults(max_results=2).invoke(query=query)
147
+ return "\n\n---\n\n".join(f"<Doc url='{d.get('url','')}'>{d.get('content','')[:500]}</Doc>" for d in docs)
 
 
 
 
148
  except Exception as e:
149
+ return f"Web search failed: {e}"
150
 
151
  @tool
152
  def optimized_wiki_search(query: str) -> str:
 
153
  try:
154
+ time.sleep(random.uniform(0.5,1))
155
+ docs = WikipediaLoader(query=query, load_max_docs=1).load()
156
+ return "\n\n---\n\n".join(f"<Doc src='{d.metadata['source']}'>{d.page_content[:800]}</Doc>" for d in docs)
 
 
 
 
157
  except Exception as e:
158
+ return f"Wikipedia search failed: {e}"
159
 
160
+ # FAISS setup
161
+ def setup_faiss():
 
162
  try:
163
+ schema = """
164
+ { page_content: .Question, metadata: { task_id: .task_id, Final_answer: ."Final answer" } }
 
 
 
 
 
 
165
  """
166
+ loader = JSONLoader("metadata.jsonl", jq_schema=schema, json_lines=True, text_content=False)
167
+ docs = loader.load()
168
+ split = RecursiveCharacterTextSplitter(chunk_size=256, chunk_overlap=50)
169
+ chunks = split.split_documents(docs)
170
+ embeds = NVIDIAEmbeddings(model="nvidia/nv-embedqa-e5-v5", api_key=os.getenv("NVIDIA_API_KEY"))
171
+ return FAISS.from_documents(chunks, embeds)
 
 
 
 
 
 
 
 
 
172
  except Exception as e:
173
  print(f"FAISS setup failed: {e}")
174
  return None
175
 
176
+ # state type
177
+ class State(TypedDict):
178
+ messages: Annotated[List[HumanMessage|AIMessage], operator.add]
179
  query: str
180
  agent_type: str
181
  final_answer: str
182
+ perf: Dict[str,Any]
183
+ agno_resp: str
184
 
185
+ class HybridSystem:
 
186
  def __init__(self):
187
+ self.agno = create_agno_agents()
188
+ self.store = setup_faiss()
189
+ self.tools = [multiply, add, subtract, divide, modulus, optimized_web_search, optimized_wiki_search]
190
+ if self.store:
191
+ retr = self.store.as_retriever(search_type="similarity", search_kwargs={"k":2})
192
+ self.tools.append(create_retriever_tool(retr, "Question_Search","retrieve similar Qs"))
193
+ self.graph = self._build_graph()
194
+ def _build_graph(self):
195
+ groq = ChatGroq(model="llama-3.3-70b-versatile",temperature=0)
196
+ gem = ChatGoogleGenerativeAI(model="gemini-2.0-flash-lite",temperature=0)
197
+ nvd = ChatNVIDIA(model="meta/llama-3.1-70b-instruct",temperature=0)
198
+ def route(st:State)->State:
199
+ q=st["query"].lower()
200
+ if any(w in q for w in ["calculate","math"]): t="lg_math"
201
+ elif any(w in q for w in ["research","analyze"]): t="agno_research"
202
+ elif any(w in q for w in ["what is","who is"]): t="lg_retrieval"
203
+ else: t="agno_general"
204
+ return {**st,"agent_type":t}
205
+ def lg_math(st:State)->State:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
206
  groq_limiter.wait_if_needed()
207
+ t0=time.time()
208
+ llmt=groq.bind_tools([multiply,add,subtract,divide,modulus])
209
+ sys=SystemMessage(content="Calc fast. FINAL ANSWER: [result]")
210
+ res=llmt.invoke([sys,HumanMessage(content=st["query"])])
211
+ return {**st,"final_answer":res.content,"perf":{"time":time.time()-t0,"prov":"LG-Groq"}}
212
+ def agno_research(st:State)->State:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
213
  gemini_limiter.wait_if_needed()
214
+ t0=time.time()
215
+ resp=self.agno["research"].run(st["query"],stream=False)
216
+ return {**st,"final_answer":resp,"perf":{"time":time.time()-t0,"prov":"Agno-Gemini"}}
217
+ def lg_retrieval(st:State)->State:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
218
  groq_limiter.wait_if_needed()
219
+ t0=time.time()
220
+ llmt=groq.bind_tools(self.tools)
221
+ sys=SystemMessage(content="Retrieve fast. FINAL ANSWER: [ans]")
222
+ res=llmt.invoke([sys,HumanMessage(content=st["query"])])
223
+ return {**st,"final_answer":res.content,"perf":{"time":time.time()-t0,"prov":"LG-Retrieval"}}
224
+ def agno_general(st:State)->State:
225
+ nvidia_limiter.wait_if_needed()
226
+ t0=time.time()
227
+ if any(w in st["query"].lower() for w in ["calculate","compute"]):
228
+ resp=self.agno["math"].run(st["query"],stream=False)
229
+ else:
230
+ resp=self.agno["research"].run(st["query"],stream=False)
231
+ return {**st,"final_answer":resp,"perf":{"time":time.time()-t0,"prov":"Agno-Gen"}}
232
+ def pick(st:State)->str: return st["agent_type"]
233
+ g=StateGraph(State)
234
+ g.add_node("router",route)
235
+ g.add_node("lg_math",lg_math)
236
+ g.add_node("agno_research",agno_research)
237
+ g.add_node("lg_retrieval",lg_retrieval)
238
+ g.add_node("agno_general",agno_general)
239
+ g.set_entry_point("router")
240
+ g.add_conditional_edges("router",pick,{
241
+ "lg_math":"lg_math","agno_research":"agno_research","lg_retrieval":"lg_retrieval","agno_general":"agno_general"
242
+ })
243
+ for n in ["lg_math","agno_research","lg_retrieval","agno_general"]:
244
+ g.add_edge(n,"END")
245
+ return g.compile(checkpointer=MemorySaver())
246
+ def process(self,q:str)->Dict[str,Any]:
247
+ st={"messages":[HumanMessage(content=q)],"query":q,"agent_type":"","final_answer":"","perf":{}, "agno_resp":""}
248
+ cfg={"configurable":{"thread_id":f"hybrid_{hash(q)}"}}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
249
  try:
250
+ out=self.graph.invoke(st,cfg)
251
+ return {"answer":out["final_answer"],"perf":out["perf"],"prov":out["perf"].get("prov")}
 
 
 
 
 
 
 
 
 
252
  except Exception as e:
253
+ return {"answer":f"Error: {e}","perf":{},"prov":"Error"}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
254
 
255
+ def build_graph(provider:str="hybrid"):
256
+ if provider=="hybrid":
257
+ return HybridSystem().graph
258
+ raise ValueError("Only 'hybrid' supported")
259
 
260
+ # Test
261
+ if __name__=="__main__":
262
+ graph=build_graph()
263
+ msgs=[HumanMessage(content="What are the names of the US presidents who were assassinated?")]
264
+ res=graph.invoke({"messages":msgs},{"configurable":{"thread_id":"test"}})
265
+ for m in res["messages"]:
266
+ m.pretty_print()