nice-bill commited on
Commit
54ab7f7
·
verified ·
1 Parent(s): 8c9c824

deploy from github

Browse files
Files changed (2) hide show
  1. api/search_client.py +124 -0
  2. web/app.py +20 -1
api/search_client.py ADDED
@@ -0,0 +1,124 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Search tool for MiniMax M2.1 - uses Brave Search (free tier available)."""
2
+
3
+ import json
4
+ import os
5
+ import urllib.request
6
+ import urllib.error
7
+ from urllib.parse import quote
8
+ from typing import List, Dict
9
+ from pathlib import Path
10
+ from dotenv import load_dotenv
11
+ import sys
12
+
13
+ # Load .env from project root
14
+ PROJECT_ROOT = Path(__file__).parent.parent
15
+ load_dotenv(PROJECT_ROOT / ".env")
16
+
17
+ from config import MINIMAX_API_KEY
18
+
19
+
20
+ def web_search(query: str, api_key: str | None = None) -> str:
21
+ """Search using Brave Search API (free tier: 2,000 queries/month)."""
22
+ brave_key = api_key or os.environ.get("BRAVE_API_KEY", "")
23
+
24
+ if brave_key:
25
+ url = f"https://api.search.brave.com/res/v1/web/search?q={quote(query)}"
26
+ req = urllib.request.Request(
27
+ url,
28
+ headers={"X-Subscription-Token": brave_key, "Accept": "application/json"},
29
+ )
30
+ try:
31
+ with urllib.request.urlopen(req, timeout=10) as response:
32
+ data = json.loads(response.read().decode("utf-8"))
33
+ results = []
34
+ for item in data.get("web", {}).get("results", [])[:5]:
35
+ results.append(
36
+ {
37
+ "title": item.get("title", ""),
38
+ "url": item.get("url", ""),
39
+ "snippet": item.get("description", ""),
40
+ }
41
+ )
42
+ return json.dumps(results)
43
+ except Exception as e:
44
+ return json.dumps([{"error": str(e)}])
45
+
46
+ # Fallback: DuckDuckGo instant answer API (free, no key)
47
+ url = f"https://api.duckduckgo.com/?q={quote(query)}&format=json"
48
+ req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"})
49
+ try:
50
+ with urllib.request.urlopen(req, timeout=10) as response:
51
+ data = json.loads(response.read().decode("utf-8"))
52
+ results = []
53
+ for item in data.get("RelatedTopics", [])[:5]:
54
+ if isinstance(item, dict) and "Title" in item:
55
+ results.append(
56
+ {
57
+ "title": item.get("Title", ""),
58
+ "url": item.get("URL", ""),
59
+ "snippet": item.get("Text", ""),
60
+ }
61
+ )
62
+ return json.dumps(results)
63
+ except Exception as e:
64
+ return json.dumps([{"error": str(e)}])
65
+
66
+
67
+ def search_with_minimax(query: str, max_tokens: int = 2048) -> str:
68
+ """Search the web and use MiniMax to summarize."""
69
+ api_key = os.environ.get("MINIMAX_API_KEY", "") or MINIMAX_API_KEY
70
+
71
+ if not api_key:
72
+ return "Error: MINIMAX_API_KEY not set"
73
+
74
+ # Step 1: Search the web
75
+ search_results = web_search(query)
76
+
77
+ # Step 2: Ask MiniMax to summarize
78
+ payload = json.dumps(
79
+ {
80
+ "model": "MiniMax-M2.1",
81
+ "messages": [
82
+ {
83
+ "role": "system",
84
+ "content": "You are a helpful coding assistant. Summarize the search results with relevant URLs and code examples.",
85
+ },
86
+ {
87
+ "role": "user",
88
+ "content": f"Search query: {query}\n\nResults:\n{search_results}\n\nProvide a concise summary with relevant links.",
89
+ },
90
+ ],
91
+ "max_tokens": max_tokens,
92
+ "temperature": 0.7,
93
+ }
94
+ ).encode("utf-8")
95
+
96
+ try:
97
+ req = urllib.request.Request(
98
+ "https://api.minimax.io/v1/chat/completions",
99
+ data=payload,
100
+ headers={
101
+ "Authorization": f"Bearer {api_key}",
102
+ "Content-Type": "application/json",
103
+ },
104
+ method="POST",
105
+ )
106
+
107
+ with urllib.request.urlopen(req, timeout=30) as response:
108
+ data = json.loads(response.read().decode("utf-8"))
109
+ return data["choices"][0]["message"]["content"]
110
+
111
+ except urllib.error.HTTPError as e:
112
+ return f"HTTP Error: {e.code}"
113
+ except Exception as e:
114
+ return f"Error: {e}"
115
+
116
+
117
+ if __name__ == "__main__":
118
+ if len(sys.argv) > 1:
119
+ query = " ".join(sys.argv[1:])
120
+ result = search_with_minimax(query)
121
+ print(result)
122
+ else:
123
+ print("Usage: python api/search_client.py <query>")
124
+ print("\nOptional: Set BRAVE_API_KEY in .env for better results")
web/app.py CHANGED
@@ -1,10 +1,12 @@
1
  """FastAPI backend for DeFi Agents simulation dashboard."""
2
 
3
- from fastapi import FastAPI, HTTPException
4
  from fastapi.middleware.cors import CORSMiddleware
 
5
  from pydantic import BaseModel
6
  from typing import List, Optional, Dict, Any
7
  import json
 
8
 
9
  from api.supabase_client import SupabaseClient
10
  from core.simulation import Simulation
@@ -17,6 +19,23 @@ app = FastAPI(
17
  version="0.1.0"
18
  )
19
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  # CORS
21
  app.add_middleware(
22
  CORSMiddleware,
 
1
  """FastAPI backend for DeFi Agents simulation dashboard."""
2
 
3
+ from fastapi import FastAPI, HTTPException, Request
4
  from fastapi.middleware.cors import CORSMiddleware
5
+ from fastapi.responses import JSONResponse
6
  from pydantic import BaseModel
7
  from typing import List, Optional, Dict, Any
8
  import json
9
+ import traceback
10
 
11
  from api.supabase_client import SupabaseClient
12
  from core.simulation import Simulation
 
19
  version="0.1.0"
20
  )
21
 
22
+
23
+ # Global exception handler for better error messages
24
+ @app.exception_handler(Exception)
25
+ async def global_exception_handler(request: Request, exc: Exception):
26
+ error_msg = str(exc)
27
+ tb_str = traceback.format_exception(type(exc), exc, exc.__traceback__)
28
+ print(f"[GLOBAL ERROR] {error_msg}")
29
+ print("".join(tb_str))
30
+ return JSONResponse(
31
+ status_code=500,
32
+ content={
33
+ "error": "Internal Server Error",
34
+ "detail": error_msg,
35
+ "type": type(exc).__name__
36
+ }
37
+ )
38
+
39
  # CORS
40
  app.add_middleware(
41
  CORSMiddleware,