orbit-ops-agent / app.py
Trae Assistant
Fix AI chat timeout: Reduce timeout to 5s and add robust mock fallback
33421ca
import os
import sqlite3
import json
import requests
import random
import time
from flask import Flask, render_template, request, jsonify, g
from flask_cors import CORS
from werkzeug.utils import secure_filename
app = Flask(__name__)
CORS(app)
# Configuration
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 16MB
app.config['UPLOAD_FOLDER'] = os.path.join(app.instance_path, 'uploads')
SILICONFLOW_API_KEY = "sk-vimuseiptfbomzegyuvmebjzooncsqbyjtlddrfodzcdskgi"
SILICONFLOW_API_URL = "https://api.siliconflow.cn/v1/chat/completions"
# Ensure upload directory exists
os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
# Error Handlers
@app.errorhandler(413)
def request_entity_too_large(error):
return jsonify({'error': 'File too large. Maximum size is 16MB.'}), 413
@app.errorhandler(404)
def not_found_error(error):
return render_template('index.html'), 200 # SPA fallback or just ignore
@app.errorhandler(500)
def internal_error(error):
return jsonify({'error': 'Internal Server Error'}), 500
# Database Setup
def get_db():
db = getattr(g, '_database', None)
if db is None:
db_path = os.path.join(app.instance_path, 'orbit_ops.db')
if not os.path.exists(app.instance_path):
os.makedirs(app.instance_path)
db = g._database = sqlite3.connect(db_path)
db.row_factory = sqlite3.Row
return db
@app.teardown_appcontext
def close_connection(exception):
db = getattr(g, '_database', None)
if db is not None:
db.close()
def init_db():
with app.app_context():
db = get_db()
# Satellites Table
db.execute('''
CREATE TABLE IF NOT EXISTS satellites (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
type TEXT NOT NULL,
status TEXT NOT NULL,
orbit_altitude INTEGER,
inclination REAL,
launch_date TEXT
)
''')
# Missions Table
db.execute('''
CREATE TABLE IF NOT EXISTS missions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
description TEXT,
status TEXT NOT NULL,
target_date TEXT
)
''')
# Seed Data if empty
cur = db.execute('SELECT count(*) FROM satellites')
if cur.fetchone()[0] == 0:
satellites = [
('StarLink-X1', 'Comm', 'Active', 550, 53.0, '2024-01-15'),
('Sentinel-Prime', 'EarthObs', 'Active', 700, 98.2, '2023-11-20'),
('Quantum-Relay', 'Comm', 'Testing', 1200, 45.0, '2025-02-01'),
('Debris-Hunter', 'Cleanup', 'Planned', 800, 85.0, '2026-06-10')
]
db.executemany('INSERT INTO satellites (name, type, status, orbit_altitude, inclination, launch_date) VALUES (?, ?, ?, ?, ?, ?)', satellites)
missions = [
('Polar Orbit Insertion', 'Deploy Sentinel-Prime to polar orbit for ice monitoring.', 'Completed', '2023-11-20'),
('Constellation Expansion', 'Launch batch of 60 StarLink-X satellites.', 'Pending', '2026-03-15'),
('Debris Removal Demo', 'Test capture mechanism on defunct satellite.', 'Planning', '2026-08-01')
]
db.executemany('INSERT INTO missions (name, description, status, target_date) VALUES (?, ?, ?, ?)', missions)
db.commit()
# Routes
@app.route('/')
def index():
return render_template('index.html')
@app.route('/api/satellites', methods=['GET', 'POST'])
def handle_satellites():
db = get_db()
if request.method == 'POST':
data = request.json
db.execute('INSERT INTO satellites (name, type, status, orbit_altitude, inclination, launch_date) VALUES (?, ?, ?, ?, ?, ?)',
(data['name'], data['type'], data['status'], data['orbit_altitude'], data['inclination'], data['launch_date']))
db.commit()
return jsonify({'status': 'success'})
else:
cur = db.execute('SELECT * FROM satellites')
return jsonify([dict(row) for row in cur.fetchall()])
@app.route('/api/missions', methods=['GET', 'POST'])
def handle_missions():
db = get_db()
if request.method == 'POST':
data = request.json
db.execute('INSERT INTO missions (name, description, status, target_date) VALUES (?, ?, ?, ?)',
(data['name'], data['description'], data['status'], data['target_date']))
db.commit()
return jsonify({'status': 'success'})
else:
cur = db.execute('SELECT * FROM missions')
return jsonify([dict(row) for row in cur.fetchall()])
@app.route('/api/upload', methods=['POST'])
def upload_file():
if 'file' not in request.files:
return jsonify({'error': 'No file part'}), 400
file = request.files['file']
if file.filename == '':
return jsonify({'error': 'No selected file'}), 400
if file:
filename = secure_filename(file.filename)
filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
file.save(filepath)
# If it's a JSON file, try to import satellites or missions
if filename.endswith('.json'):
try:
with open(filepath, 'r', encoding='utf-8') as f:
data = json.load(f)
db = get_db()
if 'satellites' in data:
for sat in data['satellites']:
db.execute('INSERT INTO satellites (name, type, status, orbit_altitude, inclination, launch_date) VALUES (?, ?, ?, ?, ?, ?)',
(sat.get('name'), sat.get('type'), sat.get('status'), sat.get('orbit_altitude'), sat.get('inclination'), sat.get('launch_date')))
if 'missions' in data:
for mission in data['missions']:
db.execute('INSERT INTO missions (name, description, status, target_date) VALUES (?, ?, ?, ?)',
(mission.get('name'), mission.get('description'), mission.get('status'), mission.get('target_date')))
db.commit()
return jsonify({'status': 'success', 'message': 'File uploaded and data imported successfully'})
except Exception as e:
return jsonify({'status': 'warning', 'message': f'File uploaded but import failed: {str(e)}'})
return jsonify({'status': 'success', 'message': 'File uploaded successfully'})
@app.route('/api/telemetry')
def get_telemetry():
# Mock real-time telemetry
return jsonify({
'timestamp': time.time(),
'signal_strength': random.uniform(80, 100),
'battery_level': random.uniform(90, 100),
'cpu_load': random.uniform(10, 40),
'temperature': random.uniform(20, 35)
})
# Mock Response Generator
def generate_mock_response(user_message):
msg = user_message.lower()
if "变轨" in msg or "maneuver" in msg or "规划" in msg:
return """### 任务规划建议 (Mock Mode)
根据当前轨道参数,建议执行霍曼转移:
1. **Delta-V**: 12.5 m/s
2. **点火时间**: T+2h 30m
3. **目标轨道**: 550km -> 560km
请在任务规划页面确认执行。"""
elif "状态" in msg or "status" in msg or "遥测" in msg:
return """### 卫星状态报告 (Mock Mode)
- **StarLink-X1**: 正常 (信号 98%)
- **Sentinel-Prime**: 正常 (电池 95%)
- **Quantum-Relay**: 警告 (温度偏高 45°C)
建议检查散热系统。"""
elif "发射" in msg or "launch" in msg:
return """### 发射窗口分析 (Mock Mode)
下一次最佳发射窗口:
- **日期**: 2026-03-15
- **时间**: 14:30 UTC
- **倾角**: 53.0°
天气状况良好,满足发射条件。"""
else:
return f"""### AI 助手 (Mock Mode)
我收到了你的指令:"{user_message}"。
由于连接外部大模型超时,我正在使用本地应急模式。
我可以帮你:
- 查询卫星状态
- 规划变轨任务
- 分析发射窗口
请尝试询问具体的技术参数。"""
@app.route('/api/chat', methods=['POST'])
def chat():
data = request.json
user_message = data.get('message', '')
headers = {
"Authorization": f"Bearer {SILICONFLOW_API_KEY}",
"Content-Type": "application/json"
}
payload = {
"model": "Qwen/Qwen2.5-7B-Instruct",
"messages": [
{"role": "system", "content": "你是 Orbit Ops 的 AI 任务规划助手。你是一个专业的航天工程师,负责协助用户进行卫星星座管理、轨道计算、发射任务规划和故障排查。请用中文回答,回答要专业、严谨,并在适当时提供 Markdown 格式的表格或列表。如果涉及数据分析,可以建议用户查看仪表盘。"},
{"role": "user", "content": user_message}
],
"stream": False
}
try:
# Reduced timeout to 5 seconds for better UX
response = requests.post(SILICONFLOW_API_URL, json=payload, headers=headers, timeout=5)
response.raise_for_status()
result = response.json()
content = result['choices'][0]['message']['content']
return jsonify({'response': content})
except Exception as e:
print(f"API Error (using fallback): {e}")
# Robust Mock Fallback
mock_content = generate_mock_response(user_message)
return jsonify({'response': mock_content})
if __name__ == '__main__':
init_db()
app.run(host='0.0.0.0', port=7866, debug=True)