|
|
|
import http.server |
|
import json |
|
import os |
|
import sys |
|
import datetime |
|
import traceback |
|
from pathlib import Path |
|
|
|
|
|
AGENT_DIR = os.environ.get("TEN_AGENT_DIR", "/tmp/ten_user/agents") |
|
|
|
class TENAgentHandler(http.server.BaseHTTPRequestHandler): |
|
def _set_headers(self, content_type="application/json"): |
|
self.send_response(200) |
|
self.send_header('Content-type', content_type) |
|
self.send_header('Access-Control-Allow-Origin', '*') |
|
self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS') |
|
self.send_header('Access-Control-Allow-Headers', 'Content-Type, X-Requested-With') |
|
self.end_headers() |
|
|
|
def do_OPTIONS(self): |
|
self._set_headers() |
|
|
|
def log_request(self, code='-', size='-'): |
|
|
|
|
|
if self.path != '/health': |
|
super().log_request(code, size) |
|
|
|
def do_GET(self): |
|
try: |
|
print(f"GET request: {self.path}") |
|
|
|
|
|
if self.path in ["/graphs", "/api/graphs"]: |
|
|
|
try: |
|
property_file = Path(AGENT_DIR) / "property.json" |
|
if not property_file.exists(): |
|
print("⚠️ Property file not found at", property_file) |
|
|
|
create_basic_property_file(property_file) |
|
|
|
with open(property_file, "r") as f: |
|
property_data = json.load(f) |
|
|
|
graphs = property_data.get("graphs", []) |
|
print(f"✅ Returning {len(graphs)} graphs from property.json") |
|
print(f"Graphs: {json.dumps(graphs, indent=2)}") |
|
|
|
self._set_headers() |
|
self.wfile.write(json.dumps(graphs).encode()) |
|
except Exception as e: |
|
print(f"Error reading property.json: {e}") |
|
traceback.print_exc() |
|
|
|
|
|
fallback_graphs = [ |
|
{ |
|
"name": "Voice Agent (Fallback)", |
|
"description": "Basic voice agent with OpenAI", |
|
"file": "voice_agent.json" |
|
} |
|
] |
|
self._set_headers() |
|
self.wfile.write(json.dumps(fallback_graphs).encode()) |
|
|
|
elif self.path in ["/health", "/"]: |
|
|
|
self._set_headers() |
|
self.wfile.write(json.dumps({ |
|
"status": "ok", |
|
"time": str(datetime.datetime.now()), |
|
"message": "TEN Agent API wrapper is running" |
|
}).encode()) |
|
|
|
elif self.path == "/list": |
|
|
|
self._set_headers() |
|
self.wfile.write(json.dumps([]).encode()) |
|
|
|
elif self.path.startswith("/dev-tmp/"): |
|
|
|
self._set_headers() |
|
self.wfile.write(json.dumps({}).encode()) |
|
|
|
elif self.path == "/vector/document/preset/list": |
|
|
|
self._set_headers() |
|
self.wfile.write(json.dumps([]).encode()) |
|
|
|
|
|
elif self.path.startswith("/api/designer/") or self.path.startswith("/api/dev/"): |
|
if "/packages/reload" in self.path: |
|
|
|
property_file = Path(AGENT_DIR) / "property.json" |
|
if property_file.exists(): |
|
with open(property_file, "r") as f: |
|
property_data = json.load(f) |
|
|
|
graphs = property_data.get("graphs", []) |
|
response_data = { |
|
"data": graphs, |
|
"status": 200, |
|
"message": "Success" |
|
} |
|
else: |
|
response_data = { |
|
"data": [], |
|
"status": 200, |
|
"message": "Success" |
|
} |
|
|
|
self._set_headers() |
|
self.wfile.write(json.dumps(response_data).encode()) |
|
else: |
|
self._set_headers() |
|
self.wfile.write(json.dumps({"data": [], "status": 200, "message": "Success"}).encode()) |
|
|
|
else: |
|
|
|
self.send_error(404, "Not found") |
|
except Exception as e: |
|
print(f"Error handling GET request: {e}") |
|
traceback.print_exc() |
|
self.send_error(500, f"Internal server error: {e}") |
|
|
|
def do_POST(self): |
|
try: |
|
print(f"POST request: {self.path}") |
|
|
|
|
|
content_length = int(self.headers['Content-Length']) if 'Content-Length' in self.headers else 0 |
|
post_data = self.rfile.read(content_length) |
|
|
|
try: |
|
request_data = json.loads(post_data) if content_length > 0 else {} |
|
except json.JSONDecodeError: |
|
request_data = {} |
|
|
|
print(f"Request data: {request_data}") |
|
|
|
if self.path == "/ping": |
|
|
|
self._set_headers() |
|
self.wfile.write(json.dumps({"status": "ok"}).encode()) |
|
|
|
elif self.path == "/token/generate": |
|
|
|
|
|
self._set_headers() |
|
response = { |
|
"token": "dummy_token_for_agora", |
|
"request_id": request_data.get("RequestId", ""), |
|
"channel_name": request_data.get("ChannelName", ""), |
|
"uid": request_data.get("Uid", 0) |
|
} |
|
self.wfile.write(json.dumps(response).encode()) |
|
|
|
elif self.path == "/start": |
|
|
|
self._set_headers() |
|
|
|
session_id = "fallback-session-" + datetime.datetime.now().strftime("%Y%m%d%H%M%S") |
|
self.wfile.write(json.dumps({ |
|
"status": "ok", |
|
"session_id": session_id |
|
}).encode()) |
|
|
|
elif self.path == "/stop": |
|
|
|
self._set_headers() |
|
self.wfile.write(json.dumps({"status": "ok"}).encode()) |
|
|
|
elif self.path.startswith("/vector/document/"): |
|
|
|
self._set_headers() |
|
self.wfile.write(json.dumps({"status": "ok", "message": "Operation completed"}).encode()) |
|
|
|
|
|
elif self.path.startswith("/api/designer/") or self.path.startswith("/api/dev/"): |
|
self._set_headers() |
|
self.wfile.write(json.dumps({"data": {}, "status": 200, "message": "Success"}).encode()) |
|
|
|
else: |
|
|
|
self.send_error(404, "Not found") |
|
except Exception as e: |
|
print(f"Error handling POST request: {e}") |
|
traceback.print_exc() |
|
self.send_error(500, f"Internal server error: {e}") |
|
|
|
def create_basic_property_file(filepath): |
|
"""Создает базовый property.json файл""" |
|
print(f"Creating basic property.json file at {filepath}") |
|
|
|
|
|
property_data = { |
|
"name": "TEN Agent Example", |
|
"version": "0.0.1", |
|
"extensions": ["openai_chatgpt"], |
|
"description": "A basic voice agent with OpenAI", |
|
"graphs": [ |
|
{ |
|
"name": "Voice Agent", |
|
"description": "Basic voice agent with OpenAI", |
|
"file": "voice_agent.json" |
|
}, |
|
{ |
|
"name": "Chat Agent", |
|
"description": "Simple chat agent", |
|
"file": "chat_agent.json" |
|
} |
|
] |
|
} |
|
|
|
try: |
|
|
|
filepath.parent.mkdir(exist_ok=True, parents=True) |
|
|
|
with open(filepath, "w") as f: |
|
json.dump(property_data, f, indent=2) |
|
print(f"✅ Created property.json at {filepath}") |
|
|
|
|
|
create_basic_graph_files(filepath.parent) |
|
|
|
return True |
|
except Exception as e: |
|
print(f"❌ Error creating property.json: {e}") |
|
traceback.print_exc() |
|
return False |
|
|
|
def create_basic_graph_files(dir_path): |
|
"""Создает базовые файлы графов""" |
|
print(f"Creating basic graph files in {dir_path}") |
|
|
|
|
|
voice_agent = { |
|
"_ten": {"version": "0.0.1"}, |
|
"nodes": [ |
|
{ |
|
"id": "start", |
|
"type": "start", |
|
"data": {"x": 100, "y": 100} |
|
}, |
|
{ |
|
"id": "openai_chatgpt", |
|
"type": "openai_chatgpt", |
|
"data": { |
|
"x": 300, |
|
"y": 200, |
|
"properties": { |
|
"model": "gpt-3.5-turbo", |
|
"temperature": 0.7, |
|
"system_prompt": "You are a helpful assistant." |
|
} |
|
} |
|
}, |
|
{ |
|
"id": "end", |
|
"type": "end", |
|
"data": {"x": 500, "y": 100} |
|
} |
|
], |
|
"edges": [ |
|
{ |
|
"id": "start_to_chatgpt", |
|
"source": "start", |
|
"target": "openai_chatgpt" |
|
}, |
|
{ |
|
"id": "chatgpt_to_end", |
|
"source": "openai_chatgpt", |
|
"target": "end" |
|
} |
|
], |
|
"groups": [], |
|
"templates": [], |
|
"root": "start" |
|
} |
|
|
|
try: |
|
with open(dir_path / "voice_agent.json", "w") as f: |
|
json.dump(voice_agent, f, indent=2) |
|
print(f"✅ Created voice_agent.json") |
|
|
|
|
|
chat_agent = dict(voice_agent) |
|
chat_agent["nodes"][1]["data"]["properties"]["system_prompt"] = "You are a helpful chat assistant." |
|
|
|
with open(dir_path / "chat_agent.json", "w") as f: |
|
json.dump(chat_agent, f, indent=2) |
|
print(f"✅ Created chat_agent.json") |
|
|
|
|
|
manifest = { |
|
"_ten": {"version": "0.0.1"}, |
|
"name": "default", |
|
"agents": [ |
|
{ |
|
"name": "voice_agent", |
|
"description": "A simple voice agent", |
|
"type": "voice" |
|
}, |
|
{ |
|
"name": "chat_agent", |
|
"description": "A text chat agent", |
|
"type": "chat" |
|
} |
|
] |
|
} |
|
|
|
with open(dir_path / "manifest.json", "w") as f: |
|
json.dump(manifest, f, indent=2) |
|
print(f"✅ Created manifest.json") |
|
|
|
return True |
|
except Exception as e: |
|
print(f"❌ Error creating graph files: {e}") |
|
traceback.print_exc() |
|
return False |
|
|
|
def run(server_class=http.server.HTTPServer, handler_class=TENAgentHandler, port=8080): |
|
server_address = ('', port) |
|
httpd = server_class(server_address, handler_class) |
|
print(f"Starting API server on port {port}...") |
|
print(f"Using agent directory: {AGENT_DIR}") |
|
|
|
|
|
agent_dir_path = Path(AGENT_DIR) |
|
if not agent_dir_path.exists(): |
|
print(f"WARNING: Agent directory {AGENT_DIR} does not exist, creating it...") |
|
agent_dir_path.mkdir(exist_ok=True, parents=True) |
|
|
|
|
|
property_file = agent_dir_path / "property.json" |
|
if not property_file.exists(): |
|
print(f"WARNING: property.json not found in {AGENT_DIR}") |
|
create_basic_property_file(property_file) |
|
else: |
|
print(f"✅ Using existing property.json at {property_file}") |
|
|
|
try: |
|
with open(property_file, "r") as f: |
|
property_data = json.load(f) |
|
print(f"Property.json content: {json.dumps(property_data, indent=2)}") |
|
except Exception as e: |
|
print(f"Error reading property.json: {e}") |
|
|
|
try: |
|
print(f"✅ API server is ready to receive requests!") |
|
httpd.serve_forever() |
|
except KeyboardInterrupt: |
|
print("Shutting down API server...") |
|
except Exception as e: |
|
print(f"Error in API server: {e}") |
|
traceback.print_exc() |
|
|
|
if __name__ == "__main__": |
|
port = int(os.environ.get("API_PORT", 8080)) |
|
run(port=port) |