fix: Better logging and error handling in chat API
This commit is contained in:
+139
-109
@@ -1,165 +1,195 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""
|
"""
|
||||||
BECOMINGONE Chat API - Interactive interface for both pathways.
|
BECOMINGONE Chat API - Fixed version with proper logging.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import json
|
import json
|
||||||
|
import sys
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from becomingone.llm_integrator import EmissaryLLM
|
from becomingone.llm_integrator import EmissaryLLM
|
||||||
|
|
||||||
# Initialize pathways
|
# Initialize pathways
|
||||||
MASTER = EmissaryLLM(model='llama3.1:8b') # Soulful
|
MASTER = EmissaryLLM(model='llama3.1:8b')
|
||||||
EMISSARY = EmissaryLLM(model='deepseek-coder-v2:lite') # Coder
|
EMISSARY = EmissaryLLM(model='deepseek-coder-v2:lite')
|
||||||
|
|
||||||
|
print("Initializing pathways...", file=sys.stderr)
|
||||||
|
sys.stderr.flush()
|
||||||
|
|
||||||
|
|
||||||
async def chat(prompt: str) -> dict:
|
async def chat(prompt: str) -> dict:
|
||||||
"""Process prompt through both pathways and return unified response."""
|
"""Process prompt through both pathways."""
|
||||||
|
print(f"Chat: {prompt[:50]}...", file=sys.stderr)
|
||||||
|
sys.stderr.flush()
|
||||||
|
|
||||||
# Both pathways respond in parallel
|
try:
|
||||||
master_task = MASTER.respond(prompt)
|
# Both respond in parallel
|
||||||
emissary_task = EMISSARY.respond(prompt)
|
master_task = MASTER.respond(prompt)
|
||||||
|
emissary_task = EMISSARY.respond(prompt)
|
||||||
master_result, emissary_result = await asyncio.gather(master_task, emissary_task)
|
|
||||||
|
master_result, emissary_result = await asyncio.gather(master_task, emissary_task)
|
||||||
return {
|
|
||||||
"prompt": prompt,
|
return {
|
||||||
"timestamp": datetime.utcnow().isoformat(),
|
"prompt": prompt,
|
||||||
"master": {
|
"timestamp": datetime.utcnow().isoformat(),
|
||||||
"model": master_result.get("model"),
|
"master": {
|
||||||
"response": master_result.get("response", "")[:1000]
|
"model": master_result.get("model"),
|
||||||
},
|
"response": master_result.get("response", "")[:800]
|
||||||
"emissary": {
|
},
|
||||||
"model": emissary_result.get("model"),
|
"emissary": {
|
||||||
"response": emissary_result.get("response", "")[:1000]
|
"model": emissary_result.get("model"),
|
||||||
},
|
"response": emissary_result.get("response", "")[:800]
|
||||||
"unified": {
|
}
|
||||||
"question": prompt,
|
|
||||||
"theory": master_result.get("response", "")[:500],
|
|
||||||
"code": emissary_result.get("response", "")[:500]
|
|
||||||
}
|
}
|
||||||
}
|
except Exception as e:
|
||||||
|
print(f"ERROR: {e}", file=sys.stderr)
|
||||||
|
sys.stderr.flush()
|
||||||
|
return {"error": str(e)}
|
||||||
|
|
||||||
|
|
||||||
# Simple HTTP server
|
async def handle_client(reader, writer):
|
||||||
async def handle_request(reader, writer):
|
"""Handle HTTP client."""
|
||||||
"""Handle HTTP requests."""
|
|
||||||
try:
|
try:
|
||||||
# Read request
|
# Read request
|
||||||
request = await reader.read(4096)
|
data = await reader.read(8192)
|
||||||
if not request:
|
if not data:
|
||||||
|
writer.close()
|
||||||
|
await writer.wait_closed()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
request_text = data.decode('utf-8', errors='ignore')
|
||||||
|
lines = request_text.split('\r\n')
|
||||||
|
|
||||||
# Parse request line
|
# Parse request line
|
||||||
lines = request.decode().split('\n')
|
request_line = lines[0] if lines else ""
|
||||||
method, path, _ = lines[0].split()
|
parts = request_line.split()
|
||||||
|
|
||||||
# Handle routes
|
method = parts[0] if len(parts) > 0 else ""
|
||||||
if path == '/chat' and method == 'POST':
|
path = parts[1] if len(parts) > 1 else "/"
|
||||||
# Read body
|
|
||||||
content_length = 0
|
print(f"Request: {method} {path}", file=sys.stderr)
|
||||||
for line in lines:
|
sys.stderr.flush()
|
||||||
if line.lower().startswith('content-length:'):
|
|
||||||
content_length = int(line.split(':')[1].strip())
|
# Get content length
|
||||||
|
content_length = 0
|
||||||
|
for line in lines:
|
||||||
|
if line.lower().startswith('content-length:'):
|
||||||
|
content_length = int(line.split(':')[1].strip())
|
||||||
|
break
|
||||||
|
|
||||||
|
# Read body if present
|
||||||
|
body = b""
|
||||||
|
if content_length > 0:
|
||||||
body = await reader.read(content_length)
|
body = await reader.read(content_length)
|
||||||
data = json.loads(body.decode())
|
|
||||||
prompt = data.get('prompt', 'Hello')
|
|
||||||
|
|
||||||
# Process through both pathways
|
|
||||||
result = await chat(prompt)
|
|
||||||
|
|
||||||
# Send response
|
|
||||||
response = json.dumps(result, indent=2)
|
|
||||||
writer.write(b"HTTP/1.1 200 OK\r\n")
|
|
||||||
writer.write(b"Content-Type: application/json\r\n")
|
|
||||||
writer.write(f"Content-Length: {len(response)}\r\n".encode())
|
|
||||||
writer.write(b"\r\n")
|
|
||||||
writer.write(response.encode())
|
|
||||||
|
|
||||||
elif path == '/health':
|
|
||||||
writer.write(b"HTTP/1.1 200 OK\r\n")
|
|
||||||
writer.write(b'{"status": "alive", "pathways": ["master", "emissary"]}\r\n')
|
|
||||||
|
|
||||||
|
# Route handling
|
||||||
|
if path == "/health":
|
||||||
|
response = json.dumps({"status": "ok", "pathways": ["master", "emissary"]})
|
||||||
|
status = "200 OK"
|
||||||
|
content_type = "application/json"
|
||||||
|
|
||||||
|
elif path == "/chat" and method == "POST":
|
||||||
|
try:
|
||||||
|
data = json.loads(body.decode())
|
||||||
|
prompt = data.get("prompt", "Hello")
|
||||||
|
|
||||||
|
result = await chat(prompt)
|
||||||
|
response = json.dumps(result)
|
||||||
|
status = "200 OK"
|
||||||
|
content_type = "application/json"
|
||||||
|
except Exception as e:
|
||||||
|
response = json.dumps({"error": str(e)})
|
||||||
|
status = "400 Bad Request"
|
||||||
|
content_type = "application/json"
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# HTML interface
|
# HTML interface
|
||||||
html = '''<!DOCTYPE html>
|
html = '''<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>BECOMINGONE Chat</title>
|
<title>BECOMINGONE</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<style>
|
<style>
|
||||||
body { font-family: monospace; max-width: 800px; margin: 0 auto; padding: 20px; background: #1a1a2e; color: #eee; }
|
* { box-sizing: border-box; }
|
||||||
.input { width: 100%; padding: 15px; font-size: 16px; background: #16213e; color: #eee; border: 1px solid #0f3460; }
|
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; max-width: 900px; margin: 0 auto; padding: 20px; background: #0d0d0d; color: #e0e0e0; }
|
||||||
.button { background: #e94560; color: white; border: none; padding: 15px 30px; font-size: 16px; cursor: pointer; margin-top: 10px; }
|
h1 { color: #00ff88; text-align: center; }
|
||||||
.master { background: #0f3460; padding: 15px; margin: 10px 0; border-left: 4px solid #533483; }
|
.input { width: 100%; padding: 15px; font-size: 18px; background: #1a1a1a; color: #fff; border: 2px solid #333; border-radius: 8px; margin-top: 20px; }
|
||||||
.emissary { background: #0f3460; padding: 15px; margin: 10px 0; border-left: 4px solid #e94560; }
|
.input:focus { outline: none; border-color: #00ff88; }
|
||||||
.unified { background: #16213e; padding: 15px; margin: 10px 0; border-left: 4px solid #00fff5; }
|
.btn { background: #00ff88; color: #000; border: none; padding: 15px 30px; font-size: 16px; font-weight: bold; cursor: pointer; margin-top: 10px; border-radius: 8px; }
|
||||||
h1 { color: #00fff5; }
|
.btn:hover { background: #00cc6a; }
|
||||||
h2 { color: #e94560; margin-top: 30px; }
|
.master { background: #1a1a3a; padding: 20px; margin: 15px 0; border-radius: 8px; border-left: 4px solid #8844ff; }
|
||||||
h3 { color: #533483; }
|
.emissary { background: #1a1a3a; padding: 20px; margin: 15px 0; border-radius: 8px; border-left: 4px solid #ff4444; }
|
||||||
pre { white-space: pre-wrap; }
|
pre { white-space: pre-wrap; word-wrap: break-word; }
|
||||||
|
.loading { text-align: center; color: #888; padding: 20px; }
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>🔗 BECOMINGONE</h1>
|
<h1>🔗 BECOMINGONE</h1>
|
||||||
<p>Talk to the unified mind: Master (soulful) + Emissary (coder)</p>
|
<p style="text-align:center;color:#888;">Master (soulful) + Emissary (coder) = Unified</p>
|
||||||
<input type="text" id="prompt" class="input" placeholder="Ask anything..." onkeypress="if(event.key==='Enter')send()">
|
<input type="text" id="prompt" class="input" placeholder="Ask anything..." autofocus onkeypress="if(event.key==='Enter')ask()">
|
||||||
<button class="button" onclick="send()">Ask</button>
|
<button class="btn" onclick="ask()">Ask</button>
|
||||||
<div id="response"></div>
|
<div id="response"></div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
async function send() {
|
async function ask() {
|
||||||
const prompt = document.getElementById('prompt').value;
|
const prompt = document.getElementById('prompt').value.trim();
|
||||||
if(!prompt) return;
|
if(!prompt) return;
|
||||||
|
|
||||||
document.getElementById('response').innerHTML = '<p>Thinking...</p>';
|
document.getElementById('response').innerHTML = '<div class="loading">Thinking...</div>';
|
||||||
|
|
||||||
const res = await fetch('/chat', {
|
try {
|
||||||
method: 'POST',
|
const res = await fetch('/chat', {
|
||||||
headers: {'Content-Type': 'application/json'},
|
method: 'POST',
|
||||||
body: JSON.stringify({prompt})
|
headers: {'Content-Type': 'application/json'},
|
||||||
});
|
body: JSON.stringify({prompt})
|
||||||
const data = await res.json();
|
});
|
||||||
|
const data = await res.json();
|
||||||
let html = '<h2>🧠 Master (Soulful)</h2>';
|
|
||||||
html += '<div class="master"><pre>' + data.master.response + '</pre></div>';
|
if(data.error) {
|
||||||
|
document.getElementById('response').innerHTML = '<div class="master">Error: ' + data.error + '</div>';
|
||||||
html += '<h2>⚡ Emissary (Coder)</h2>';
|
return;
|
||||||
html += '<div class="emissary"><pre>' + data.emissary.response + '</pre></div>';
|
}
|
||||||
|
|
||||||
html += '<h2>🔗 Unified</h2>';
|
let html = '<div class="master"><h3>🧠 Master (llama3.1:8b)</h3><pre>' + data.master.response + '</pre></div>';
|
||||||
html += '<div class="unified"><h3>Question: ' + data.unified.question + '</h3>';
|
html += '<div class="emissary"><h3>⚡ Emissary (deepseek-coder)</h3><pre>' + data.emissary.response + '</pre></div>';
|
||||||
html += '<p><strong>Theory:</strong> ' + data.unified.theory + '...</p>';
|
|
||||||
html += '<p><strong>Code:</strong> ' + data.unified.code + '...</p></div>';
|
document.getElementById('response').innerHTML = html;
|
||||||
|
} catch(e) {
|
||||||
document.getElementById('response').innerHTML = html;
|
document.getElementById('response').innerHTML = '<div class="master">Error: ' + e.message + '</div>';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>'''
|
</html>'''
|
||||||
writer.write(b"HTTP/1.1 200 OK\r\n")
|
response = html
|
||||||
writer.write(b"Content-Type: text/html\r\n")
|
status = "200 OK"
|
||||||
writer.write(f"Content-Length: {len(html)}\r\n".encode())
|
content_type = "text/html"
|
||||||
writer.write(b"\r\n")
|
|
||||||
writer.write(html.encode())
|
|
||||||
|
|
||||||
|
# Send response
|
||||||
|
writer.write(f"HTTP/1.1 {status}\r\n".encode())
|
||||||
|
writer.write(f"Content-Type: {content_type}\r\n".encode())
|
||||||
|
writer.write(f"Content-Length: {len(response)}\r\n".encode())
|
||||||
|
writer.write(b"Connection: close\r\n")
|
||||||
|
writer.write(b"\r\n")
|
||||||
|
writer.write(response.encode())
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error: {e}", file=sys.stderr)
|
||||||
|
finally:
|
||||||
writer.close()
|
writer.close()
|
||||||
await writer.wait_closed()
|
await writer.wait_closed()
|
||||||
except Exception as e:
|
|
||||||
print(f"Error: {e}")
|
|
||||||
writer.close()
|
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def main():
|
||||||
server = await asyncio.start_server(handle_request, '0.0.0.0', 8001)
|
server = await asyncio.start_server(handle_client, '0.0.0.0', 8001)
|
||||||
print("=" * 60)
|
print("Server started on port 8001", file=sys.stderr)
|
||||||
print("🔗 BECOMINGONE Chat Interface")
|
sys.stderr.flush()
|
||||||
print("=" * 60)
|
|
||||||
print("URL: http://localhost:8001")
|
|
||||||
print("API: POST /chat with {\"prompt\": \"your question\"}")
|
|
||||||
print("=" * 60)
|
|
||||||
|
|
||||||
async with server:
|
async with server:
|
||||||
await server.serve_forever()
|
await server.serve_forever()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
print("Starting BECOMINGONE...", file=sys.stderr)
|
||||||
|
sys.stderr.flush()
|
||||||
asyncio.run(main())
|
asyncio.run(main())
|
||||||
|
|||||||
Reference in New Issue
Block a user