sonthaiha commited on
Commit
fbc4549
·
verified ·
1 Parent(s): 2e91995

Fix: Add Root UI and Agentic Routes

Browse files
Files changed (1) hide show
  1. src/server.py +85 -2
src/server.py CHANGED
@@ -8,6 +8,7 @@ import uuid
8
  import shutil
9
  import re
10
  from fastapi import FastAPI, UploadFile, File
 
11
  from pydantic import BaseModel
12
  from src.core.engine import ModelEngine
13
  from src.core.memory import MemoryManager
@@ -17,6 +18,7 @@ from src.agents.manager import ManagerAgent
17
  from src.agents.coder import CoderAgent
18
  from src.agents.vision import VisionAgent
19
 
 
20
  try:
21
  if 'engine' not in globals(): engine = ModelEngine()
22
  except: engine = None
@@ -39,6 +41,89 @@ def clean_output(text):
39
  text = re.sub(r"<think>.*?</think>", "", text, flags=re.DOTALL)
40
  return text.replace("</think>", "").replace("<think>", "").strip()
41
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  @app.post("/upload")
43
  async def upload_file(file: UploadFile = File(...)):
44
  file_ext = file.filename.split(".")[-1].lower()
@@ -50,7 +135,6 @@ async def upload_file(file: UploadFile = File(...)):
50
  analysis = f"File {file.filename}"
51
  if file_ext in ['jpg', 'png']: analysis = vision.analyze_media(save_path)
52
 
53
- # Save to Memory (Hardcoded User 1 for now, should come from Form)
54
  memory.save_attachment(1, 1, file.filename, file_ext, analysis)
55
  return {"status": "success", "vision_analysis": analysis}
56
 
@@ -62,7 +146,6 @@ async def chat_endpoint(req: ChatRequest):
62
  decision = manager.analyze_task(req.message, history)
63
  cat = decision.get("category", "GENERAL")
64
 
65
- # Vision Override
66
  if "ảnh" in req.message.lower(): cat = "GENERAL"
67
 
68
  resp = ""
 
8
  import shutil
9
  import re
10
  from fastapi import FastAPI, UploadFile, File
11
+ from fastapi.responses import HTMLResponse
12
  from pydantic import BaseModel
13
  from src.core.engine import ModelEngine
14
  from src.core.memory import MemoryManager
 
18
  from src.agents.coder import CoderAgent
19
  from src.agents.vision import VisionAgent
20
 
21
+ # Initialize Components
22
  try:
23
  if 'engine' not in globals(): engine = ModelEngine()
24
  except: engine = None
 
41
  text = re.sub(r"<think>.*?</think>", "", text, flags=re.DOTALL)
42
  return text.replace("</think>", "").replace("<think>", "").strip()
43
 
44
+ # --- ROOT ROUTE (THE FIX) ---
45
+ @app.get("/", response_class=HTMLResponse)
46
+ async def root():
47
+ return """
48
+ <!DOCTYPE html>
49
+ <html lang="vi">
50
+ <head>
51
+ <meta charset="UTF-8">
52
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
53
+ <title>Project A - Agentic Console</title>
54
+ <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
55
+ <style>
56
+ :root { --primary: #0084ff; --bg: #f0f2f5; --chat-bg: #ffffff; --user-msg: #0084ff; --ai-msg: #f0f0f0; }
57
+ body { font-family: sans-serif; background: var(--bg); display: flex; justify-content: center; height: 100vh; margin: 0; }
58
+ .chat-widget { width: 100%; max-width: 600px; height: 100vh; background: var(--chat-bg); display: flex; flex-direction: column; }
59
+ .header { background: white; padding: 15px; border-bottom: 1px solid #eee; font-weight: bold; display: flex; justify-content: space-between; }
60
+ .messages { flex: 1; padding: 20px; overflow-y: auto; display: flex; flex-direction: column; gap: 15px; }
61
+ .msg { padding: 10px 15px; border-radius: 15px; max-width: 80%; line-height: 1.5; word-wrap: break-word; }
62
+ .msg.user { background: var(--user-msg); color: white; align-self: flex-end; }
63
+ .msg.ai { background: var(--ai-msg); color: black; align-self: flex-start; }
64
+ .input-area { padding: 15px; border-top: 1px solid #eee; display: flex; gap: 10px; background: white; }
65
+ input { flex: 1; padding: 10px; border: 1px solid #ddd; border-radius: 20px; outline: none; }
66
+ button { padding: 10px 20px; background: var(--primary); color: white; border: none; border-radius: 20px; cursor: pointer; }
67
+ #typing { display: none; margin-left: 20px; color: #888; font-size: 12px; }
68
+ </style>
69
+ </head>
70
+ <body>
71
+ <div class="chat-widget">
72
+ <div class="header">
73
+ Project A (Agentic Mode)
74
+ <select id="user-select"><option value="1">User A</option></select>
75
+ </div>
76
+ <div class="messages" id="messages">
77
+ <div class="msg ai">System Online. Connected to Cloud DB.<br>I can see your tables: sales, products, workflows.<br>Try: "Create a workflow to email vip customers."</div>
78
+ </div>
79
+ <div id="typing">Thinking...</div>
80
+ <div class="input-area">
81
+ <input type="text" id="input" placeholder="Type a command..." onkeypress="handleEnter(event)">
82
+ <button onclick="sendMessage()">Send</button>
83
+ </div>
84
+ </div>
85
+ <script>
86
+ // Empty string means "Use current domain" (Relative path)
87
+ // This makes it work automatically on Hugging Face Spaces
88
+ const API_URL = "";
89
+
90
+ async function sendMessage() {
91
+ const input = document.getElementById("input");
92
+ const text = input.value.trim();
93
+ if (!text) return;
94
+
95
+ addMessage(text, "user");
96
+ input.value = "";
97
+ document.getElementById("typing").style.display = "block";
98
+
99
+ try {
100
+ const res = await fetch(`${API_URL}/chat`, {
101
+ method: "POST",
102
+ headers: { "Content-Type": "application/json" },
103
+ body: JSON.stringify({ user_id: 1, store_id: 1, message: text })
104
+ });
105
+ const data = await res.json();
106
+ document.getElementById("typing").style.display = "none";
107
+ addMessage(data.response, "ai");
108
+ } catch (e) {
109
+ document.getElementById("typing").style.display = "none";
110
+ addMessage("Error: " + e.message, "ai");
111
+ }
112
+ }
113
+
114
+ function addMessage(text, role) {
115
+ const div = document.createElement("div");
116
+ div.className = `msg ${role}`;
117
+ div.innerHTML = marked.parse(text);
118
+ document.getElementById("messages").appendChild(div);
119
+ document.getElementById("messages").scrollTop = document.getElementById("messages").scrollHeight;
120
+ }
121
+ function handleEnter(e) { if (e.key === "Enter") sendMessage(); }
122
+ </script>
123
+ </body>
124
+ </html>
125
+ """
126
+
127
  @app.post("/upload")
128
  async def upload_file(file: UploadFile = File(...)):
129
  file_ext = file.filename.split(".")[-1].lower()
 
135
  analysis = f"File {file.filename}"
136
  if file_ext in ['jpg', 'png']: analysis = vision.analyze_media(save_path)
137
 
 
138
  memory.save_attachment(1, 1, file.filename, file_ext, analysis)
139
  return {"status": "success", "vision_analysis": analysis}
140
 
 
146
  decision = manager.analyze_task(req.message, history)
147
  cat = decision.get("category", "GENERAL")
148
 
 
149
  if "ảnh" in req.message.lower(): cat = "GENERAL"
150
 
151
  resp = ""