Hongtyyuqu commited on
Commit
ee4c89e
·
verified ·
1 Parent(s): d55f45a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +79 -71
app.py CHANGED
@@ -1,87 +1,95 @@
1
  import os
2
  import json
3
- import time
4
- import requests
5
  from fastapi import FastAPI, HTTPException
6
  from fastapi.responses import StreamingResponse
7
  from pydantic import BaseModel
8
- from tavily import TavilyClient
 
9
 
10
  app = FastAPI()
11
 
12
- class SearchRequest(BaseModel):
13
  query: str
14
 
15
- @app.post("/search")
16
- async def search_endpoint(request: SearchRequest):
17
- tavily_key = os.environ.get("TAVILY_API_KEY")
18
- openrouter_key = os.environ.get("OPENROUTER_API_KEY")
 
 
 
 
 
 
 
 
 
 
 
19
 
20
- if not tavily_key or not openrouter_key:
21
- raise HTTPException(status_code=500, detail="Missing API keys configuration.")
 
 
 
 
 
 
 
 
 
 
 
 
 
22
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
  try:
24
- tavily_client = TavilyClient(api_key=tavily_key)
25
- research_res = tavily_client.research(request.query)
26
- request_id = research_res.get("request_id")
27
-
28
- context = ""
29
- sources = []
30
-
31
- while True:
32
- result = tavily_client.get_research_result(request_id)
33
- if result.get("status") == "completed":
34
- context = result.get("content", "")
35
- sources = result.get("sources", [])
36
- break
37
- elif result.get("status") == "failed":
38
- break
39
- time.sleep(1)
40
- except Exception:
41
- context = ""
42
- sources = []
 
 
 
43
 
44
- def event_generator():
45
- url = "https://openrouter.ai/api/v1/chat/completions"
46
- headers = {
47
- "Authorization": f"Bearer {openrouter_key}",
48
- "Content-Type": "application/json"
49
- }
50
- payload = {
51
- "model": "nvidia/nemotron-3-ultra-550b-a55b:free",
52
- "stream": True,
53
- "messages": [
54
- {"role": "system", "content": f"Answer the user query based on this context:\n{context}"},
55
- {"role": "user", "content": request.query}
56
- ]
57
- }
58
-
59
- try:
60
- response = requests.post(url, headers=headers, json=payload, stream=True)
61
- for line in response.iter_lines():
62
- if line:
63
- decoded_line = line.decode('utf-8')
64
- if decoded_line.startswith("data: "):
65
- data_str = decoded_line[6:]
66
- if data_str.strip() == "[DONE]":
67
- break
68
- try:
69
- data_json = json.loads(data_str)
70
- choices = data_json.get("choices", [])
71
- if choices:
72
- delta = choices[0].get("delta", {})
73
- content = delta.get("content", "")
74
- if content:
75
- yield f"data: {json.dumps({'type': 'content', 'value': content})}\n\n"
76
- except Exception:
77
- pass
78
- except Exception as e:
79
- yield f"data: {json.dumps({'type': 'error', 'value': str(e)})}\n\n"
80
-
81
- yield f"data: {json.dumps({'type': 'sources', 'value': sources})}\n\n"
82
 
83
- return StreamingResponse(event_generator(), media_type="text/event-stream")
84
 
85
- if __name__ == "__main__":
86
- import uvicorn
87
- uvicorn.run(app, host="0.0.0.0", port=7860)
 
1
  import os
2
  import json
3
+ import asyncio
 
4
  from fastapi import FastAPI, HTTPException
5
  from fastapi.responses import StreamingResponse
6
  from pydantic import BaseModel
7
+ from duckduckgo_search import DDGS
8
+ import requests
9
 
10
  app = FastAPI()
11
 
12
+ class QueryRequest(BaseModel):
13
  query: str
14
 
15
+ def search_duckduckgo(query: str, max_results: int = 5):
16
+ try:
17
+ with DDGS() as ddgs:
18
+ results = [r for r in ddgs.text(query, max_results=max_results)]
19
+ return results
20
+ except Exception:
21
+ return []
22
+
23
+ async def stream_search_and_llm(query: str):
24
+ OPENROUTER_API_KEY = os.getenv("OPENROUTER_API_KEY")
25
+ if not OPENROUTER_API_KEY:
26
+ yield f"data: {json.dumps({'error': 'API key missing'})}\n\n"
27
+ return
28
+
29
+ search_results = search_duckduckgo(query)
30
 
31
+ context = ""
32
+ sources = []
33
+ for idx, result in enumerate(search_results, 1):
34
+ title = result.get('title', '')
35
+ href = result.get('href', '')
36
+ body = result.get('body', '')
37
+ context += f"[{idx}] Source: {title}\nURL: {href}\nContent: {body}\n\n"
38
+ sources.append({"id": idx, "title": title, "url": href})
39
+
40
+ system_prompt = (
41
+ "You are a helpful AI assistant with access to real-time web search results. "
42
+ "Answer the user's question accurately based on the provided search results. "
43
+ "Cite sources using [1], [2], etc., matching the given source numbers. "
44
+ "If the search results do not contain the answer, use your knowledge but state so."
45
+ )
46
 
47
+ user_content = f"Search Results:\n{context}\n\nUser Query: {query}"
48
+
49
+ url = "https://openrouter.ai/api/v1/chat/completions"
50
+ headers = {
51
+ "Authorization": f"Bearer {OPENROUTER_API_KEY}",
52
+ "Content-Type": "application/json",
53
+ }
54
+ payload = {
55
+ "model": "nvidia/nemotron-3-ultra-550b-a55b:free",
56
+ "stream": True,
57
+ "messages": [
58
+ {"role": "system", "content": system_prompt},
59
+ {"role": "user", "content": user_content}
60
+ ]
61
+ }
62
+
63
  try:
64
+ response = requests.post(url, headers=headers, json=payload, stream=True)
65
+ if response.status_code != 200:
66
+ yield f"data: {json.dumps({'error': 'LLM request failed'})}\n\n"
67
+ return
68
+
69
+ for line in response.iter_lines():
70
+ if line:
71
+ line_str = line.decode('utf-8').strip()
72
+ if line_str.startswith("data:"):
73
+ data_body = line_str[5:].strip()
74
+ if data_body == "[DONE]":
75
+ break
76
+ try:
77
+ data_json = json.loads(data_body)
78
+ choices = data_json.get("choices", [])
79
+ if choices:
80
+ delta = choices[0].get("delta", {})
81
+ content = delta.get("content", "")
82
+ if content:
83
+ yield f"data: {json.dumps({'type': 'answer', 'content': content})}\n\n"
84
+ except Exception:
85
+ continue
86
 
87
+ except Exception:
88
+ yield f"data: {json.dumps({'error': 'Streaming failed'})}\n\n"
89
+ return
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
 
91
+ yield f"data: {json.dumps({'type': 'sources', 'content': sources})}\n\n"
92
 
93
+ @app.post("/api/search")
94
+ async def search_endpoint(request: QueryRequest):
95
+ return StreamingResponse(stream_search_and_llm(request.query), media_type="text/event-stream")