bk939448 commited on
Commit
9dcdc67
·
verified ·
1 Parent(s): 301ad7f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +15 -124
app.py CHANGED
@@ -1,142 +1,33 @@
1
- import os
2
  import asyncio
3
- import re
4
- import json
5
- from typing import Optional
6
- from datetime import datetime, timezone
7
  from fastapi import FastAPI
8
- from pydantic import BaseModel
9
  from fastapi.responses import StreamingResponse
10
  from fastapi.middleware.cors import CORSMiddleware
11
- import httpx
12
- import trafilatura
13
- import google.generativeai as genai
14
 
15
  # --- FastAPI App with CORS ---
16
- app = FastAPI(title="AI Research Agent API")
17
  app.add_middleware(
18
  CORSMiddleware,
19
  allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"],
20
  )
21
 
22
- # --- Prompts (No changes) ---
23
- PROMPT_ULTRADEEP_PLANNER = """You are an expert research planner... Your output MUST be a valid JSON object: {"queries": ["query 1", ...]}..."""
24
- PROMPT_ULTRADEEP_SYNTHESIZER = """You are a master research analyst... If info is incomplete, output JSON: `{"status": "INCOMPLETE", "missing_queries": ["..."]}`. If complete, output the final markdown report..."""
25
-
26
- # --- Core Logic (No changes) ---
27
- async def search_web_logic(query: str, serper_api_key: str) -> str:
28
- # ... (code from previous version, no changes)
29
- if not serper_api_key: return "Error: Serper API Key is missing."
30
- try:
31
- headers = {"X-API-KEY": serper_api_key, "Content-Type": "application/json"}
32
- async with httpx.AsyncClient(timeout=15) as client:
33
- resp = await client.post("https://google.serper.dev/search", headers=headers, json={"q": query, "num": 5})
34
- if resp.status_code == 401: return "Error: Invalid Serper API Key."
35
- if resp.status_code != 200: return f"Error: Serper API returned status {resp.status_code}."
36
- results = resp.json().get("organic", []);
37
- if not results: return f"Error: No web results found for query '{query}'."
38
- urls = [r["link"] for r in results]
39
- async with httpx.AsyncClient(timeout=20, follow_redirects=True) as client:
40
- tasks = [client.get(u) for u in urls]; responses = await asyncio.gather(*tasks, return_exceptions=True)
41
- texts = [f"Source URL: {meta['link']}\nContent: {body.strip()}\n" for meta, response in zip(results, responses) if not isinstance(response, Exception) and (body := trafilatura.extract(response.text))]
42
- if not texts: return "Error: Found web results, but could not extract content."
43
- return "\n---\n".join(texts)
44
- except Exception as e: return f"Error during web search: {str(e)}"
45
-
46
- async def call_gemini(prompt: str, gemini_key: str, model_name: str, json_mode: bool = False) -> str:
47
- # ... (code from previous version, no changes)
48
- if not gemini_key: return json.dumps({"error": "Gemini API Key is missing."})
49
- try:
50
- genai.configure(api_key=gemini_key)
51
- model = genai.GenerativeModel(model_name)
52
- generation_config = {"response_mime_type": "application/json"} if json_mode else None
53
- response = await model.generate_content_async(prompt, generation_config=generation_config)
54
- return response.text
55
- except Exception as e: return json.dumps({"error": f"Error calling Gemini: {str(e)}"})
56
-
57
- # --- AI Agent with Heartbeat Logic ---
58
- async def ultradeep_research_agent_streamer(query: str, serper_api_key: str, gemini_key: str, model_name: str):
59
- yield "STATUS: Initiating UltraDeep Research Agent... 🤖\n"
60
-
61
- # Step 1: Plan with Heartbeat
62
- yield "STATUS: Creating a research plan with AI... 🧠"
63
- planner_prompt = PROMPT_ULTRADEEP_PLANNER.format(query=query)
64
 
65
- # <<< HEARTBEAT LOGIC START >>>
66
- gemini_task = asyncio.create_task(call_gemini(planner_prompt, gemini_key, model_name, json_mode=True))
67
- while not gemini_task.done():
68
- yield "." # Send a heartbeat every 2 seconds
69
- await asyncio.sleep(2)
70
- plan_str = await gemini_task
71
- yield "\n" # Newline after heartbeats
72
- # <<< HEARTBEAT LOGIC END >>>
73
-
74
- try:
75
- match = re.search(r'\{.*\}', plan_str, re.DOTALL)
76
- if not match: raise ValueError("No JSON object found in Gemini's planner response.")
77
- plan_data = json.loads(match.group(0))
78
- if "error" in plan_data:
79
- yield f"FINAL: Error during planning phase: {plan_data['error']}"
80
- return
81
- search_queries = plan_data["queries"]
82
- plan_display = "\n".join([f" - {q}" for q in search_queries])
83
- yield f"PLAN:\n{plan_display}\n\n"
84
- except Exception as e:
85
- yield f"FINAL:Error: Could not create a valid research plan. Details: {str(e)}\nRaw Response: {plan_str}"
86
- return
87
-
88
- # Step 2: Execute
89
- all_scraped_text = ""
90
- for i, sub_query in enumerate(search_queries):
91
- yield f"STATUS: Searching for '{sub_query}' ({i+1}/{len(search_queries)})... 🕵️‍♂️\n"
92
- # Search can also be slow, so we can add a heartbeat here too if needed, but let's skip for now
93
- scraped_text = await search_web_logic(sub_query, serper_api_key)
94
- if scraped_text.startswith("Error:"):
95
- yield f"WARNING: Skipping search for '{sub_query}': {scraped_text}\n"
96
- else:
97
- all_scraped_text += scraped_text
98
  await asyncio.sleep(1)
 
 
99
 
100
- if not all_scraped_text:
101
- yield "FINAL: Error: Could not retrieve any web content. Please check Serper key and try again."
102
- return
103
-
104
- # Step 3: Synthesize with Heartbeat
105
- yield "STATUS: All searches complete. Synthesizing the final report... ✍️"
106
- current_date = datetime.now(timezone.utc).strftime("%Y-%m-%d")
107
- synthesizer_prompt = PROMPT_ULTRADEEP_SYNTHESIZER.format(query=query, current_date=current_date, context_text=all_scraped_text)
108
-
109
- # <<< HEARTBEAT LOGIC START >>>
110
- synthesis_task = asyncio.create_task(call_gemini(synthesizer_prompt, gemini_key, model_name))
111
- while not synthesis_task.done():
112
- yield "."
113
- await asyncio.sleep(2)
114
- final_report = await synthesis_task
115
- yield "\n"
116
- # <<< HEARTBEAT LOGIC END >>>
117
-
118
- yield f"FINAL:{final_report}"
119
-
120
- # --- FastAPI Endpoints and Server Startup (No Changes) ---
121
- class ResearchRequest(BaseModel):
122
- query: str
123
- serper_api_key: str
124
- gemini_api_key: str
125
- research_mode: str = "ultradeep"
126
- gemini_model: str = "gemini-1.5-flash-latest"
127
-
128
- @app.post("/api/research-stream")
129
- async def api_research_stream(request: ResearchRequest):
130
- # This endpoint is now only for ultradeep
131
- return StreamingResponse(
132
- ultradeep_research_agent_streamer(
133
- request.query, request.serper_api_key, request.gemini_api_key, request.gemini_model
134
- ),
135
- media_type="text/event-stream"
136
- )
137
-
138
- # ... (We can add back the simple endpoint and Gradio UI later if needed)
139
 
 
140
  if __name__ == "__main__":
141
  import uvicorn
142
  uvicorn.run(app, host="0.0.0.0", port=7860)
 
 
1
  import asyncio
 
 
 
 
2
  from fastapi import FastAPI
 
3
  from fastapi.responses import StreamingResponse
4
  from fastapi.middleware.cors import CORSMiddleware
5
+ from pydantic import BaseModel
 
 
6
 
7
  # --- FastAPI App with CORS ---
8
+ app = FastAPI(title="Streaming Test App")
9
  app.add_middleware(
10
  CORSMiddleware,
11
  allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"],
12
  )
13
 
14
+ # --- Dummy Streaming Function ---
15
+ async def dummy_streamer():
16
+ yield "STATUS: Connection established! Starting test...\n"
17
+ await asyncio.sleep(2)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
 
19
+ for i in range(1, 6):
20
+ yield f"MESSAGE: Ping #{i} from server.\n"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  await asyncio.sleep(1)
22
+
23
+ yield "FINAL: Test complete. Connection is working!"
24
 
25
+ # --- Test Endpoint ---
26
+ @app.post("/api/test-stream")
27
+ async def api_test_stream():
28
+ return StreamingResponse(dummy_streamer(), media_type="text/event-stream")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
 
30
+ # --- Server Startup ---
31
  if __name__ == "__main__":
32
  import uvicorn
33
  uvicorn.run(app, host="0.0.0.0", port=7860)