Trae AI
Enhance: Rich AI analysis visualization and fix Toast rendering
4949928
import os
import json
import sqlite3
import requests
import random
import time
from datetime import datetime
from flask import Flask, render_template, request, jsonify, g
from werkzeug.exceptions import HTTPException
app = Flask(__name__, template_folder='templates')
app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', 'dev_key_bio_synthesis')
app.config['MAX_CONTENT_LENGTH'] = 100 * 1024 * 1024 # 100MB
app.config['UPLOAD_FOLDER'] = os.path.join(app.instance_path, 'uploads')
os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
# Database Setup
def get_db():
if 'db' not in g:
db_path = os.path.join(app.instance_path, 'bio_synthesis.db')
os.makedirs(app.instance_path, exist_ok=True)
g.db = sqlite3.connect(db_path)
g.db.row_factory = sqlite3.Row
return g.db
@app.teardown_appcontext
def close_db(error):
if hasattr(g, 'db'):
g.db.close()
def init_db():
with app.app_context():
db = get_db()
# Experiments Table: Stores the full lifecycle of a bio-experiment
db.execute('''
CREATE TABLE IF NOT EXISTS experiments (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
status TEXT DEFAULT 'planned', -- planned, running, completed, failed
target_molecule TEXT,
protocol_json TEXT, -- JSON: Steps, Reagents, Conditions
sensor_log_json TEXT, -- JSON: Time-series data (Temp, pH, OD600)
ai_analysis_json TEXT, -- JSON: AI optimization suggestions
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')
# Knowledge Base: Stored protocols/assets
db.execute('''
CREATE TABLE IF NOT EXISTS assets (
id INTEGER PRIMARY KEY AUTOINCREMENT,
category TEXT, -- plasmid, strain, reagent
name TEXT NOT NULL,
properties_json TEXT,
stock_level INTEGER DEFAULT 0
)
''')
db.commit()
# SiliconFlow API Configuration
SILICONFLOW_API_KEY = "sk-vimuseiptfbomzegyuvmebjzooncsqbyjtlddrfodzcdskgi"
SILICONFLOW_API_URL = "https://api.siliconflow.cn/v1/chat/completions"
MODEL_NAME = "Qwen/Qwen2.5-7B-Instruct"
def call_ai_agent(system_prompt, user_message, json_mode=False):
"""
Calls SiliconFlow API with fallback to Mock Mode.
"""
headers = {
"Authorization": f"Bearer {SILICONFLOW_API_KEY}",
"Content-Type": "application/json"
}
messages = [
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_message}
]
payload = {
"model": MODEL_NAME,
"messages": messages,
"temperature": 0.7,
"max_tokens": 1024
}
if json_mode:
payload["response_format"] = {"type": "json_object"}
try:
# Short timeout for responsive UI
response = requests.post(SILICONFLOW_API_URL, json=payload, headers=headers, timeout=8)
response.raise_for_status()
data = response.json()
content = data['choices'][0]['message']['content']
return content
except Exception as e:
print(f"AI API Error (Using Mock): {e}")
return mock_ai_response(user_message, json_mode)
def mock_ai_response(query, json_mode):
"""
Provides context-aware mock responses for demonstration/offline mode.
"""
query_lower = query.lower()
if json_mode:
if "protocol" in query_lower or "设计" in query_lower:
return json.dumps({
"title": "GFP 绿色荧光蛋白优化表达方案",
"steps": [
{"step": 1, "action": "接种 (Inoculation)", "params": "37°C, 200rpm, 16小时"},
{"step": 2, "action": "诱导 (Induction)", "params": "OD600=0.6 时添加 0.5mM IPTG"},
{"step": 3, "action": "收获 (Harvest)", "params": "5000g 离心, 10分钟"}
],
"risk_assessment": "诱导期间如果温度超过 30°C,极易形成包涵体。"
}, ensure_ascii=False)
elif "analyze" in query_lower or "分析" in query_lower:
return json.dumps({
"diagnosis": "检测到发酵液溶氧 (DO) 水平异常波动。",
"anomaly_detected": True,
"yield_prediction": "1.2 g/L (预计低于目标 1.5 g/L)",
"metabolic_bottleneck": "氧传递速率 (OTR) 受限,导致菌体代谢转向副产物生成。",
"recommendation": "建议立即将搅拌转速提高至 250rpm,并补充纯氧通气。同时检查消泡剂添加量。"
}, ensure_ascii=False)
elif "anomaly" in query_lower or "异常" in query_lower:
return json.dumps({
"anomaly_detected": True,
"diagnosis": "pH 值在过去 1 小时内意外下降。",
"recommendation": "建议添加缓冲液或检查补料速率。"
}, ensure_ascii=False)
return "系统正在模拟模式下运行。AI建议:当前发酵参数偏离最优值,建议将温度降低至30°C以提高蛋白溶解度。请检查DO(溶解氧)探头读数。"
# Routes
@app.route('/')
def index():
return render_template('index.html')
@app.route('/api/experiments', methods=['GET', 'POST'])
def handle_experiments():
db = get_db()
if request.method == 'POST':
data = request.json
name = data.get('name', 'New Experiment')
target = data.get('target', 'Unknown')
# Initial Protocol Generation (AI)
prompt = f"Design a bio-synthesis protocol for {target}. Return valid JSON with 'steps' array."
protocol = call_ai_agent(
"You are a Senior Bio-Process Engineer. Output JSON only.",
prompt,
json_mode=True
)
db.execute(
'INSERT INTO experiments (name, target_molecule, protocol_json, status) VALUES (?, ?, ?, ?)',
(name, target, protocol, 'planned')
)
db.commit()
return jsonify({"status": "created", "protocol": protocol})
else:
rows = db.execute('SELECT * FROM experiments ORDER BY created_at DESC').fetchall()
return jsonify([dict(row) for row in rows])
@app.route('/api/bioreactor/stream')
def bioreactor_stream():
"""
Simulates real-time sensor data from a bioreactor.
Returns simulated values for: Temp, pH, DO (Dissolved Oxygen), OD600 (Cell Density).
"""
# Simulate a growth curve based on time of day or random walk
now = time.time()
# Simple sine wave + noise for demo
temp = 37.0 + (random.random() - 0.5) * 0.5
ph = 7.0 + (random.random() - 0.5) * 0.2
do = 40.0 + (random.random() - 0.5) * 5.0 # % saturation
# Sigmoidal growth simulation for OD600
# Just random walk for now to show activity
od600 = 0.5 + (random.random() * 0.1)
return jsonify({
"timestamp": datetime.now().isoformat(),
"temperature": round(temp, 2),
"ph": round(ph, 2),
"dissolved_oxygen": round(do, 1),
"od600": round(od600, 3),
"status": "active"
})
@app.route('/api/analyze_run', methods=['POST'])
def analyze_run():
"""
Analyzes current experiment data using AI.
"""
data = request.json
sensor_data = data.get('sensor_data', {})
prompt = f"""
Analyze the following bioreactor data:
Temperature: {sensor_data.get('temperature')} C
pH: {sensor_data.get('ph')}
DO: {sensor_data.get('dissolved_oxygen')}%
Identify any anomalies and suggest process control adjustments. Return JSON.
"""
analysis = call_ai_agent(
"You are a Bioreactor Data Analyst. Return JSON with keys: anomaly_detected (bool), diagnosis, recommendation.",
prompt,
json_mode=True
)
return jsonify({"analysis": analysis})
@app.errorhandler(413)
def request_entity_too_large(error):
return jsonify({"error": "文件过大,请上传小于 100MB 的文件"}), 413
@app.route('/api/upload', methods=['POST'])
def upload_file():
if 'file' not in request.files:
return jsonify({"error": "没有文件部分"}), 400
file = request.files['file']
if file.filename == '':
return jsonify({"error": "未选择文件"}), 400
if file:
from werkzeug.utils import secure_filename
filename = secure_filename(file.filename)
# Handle non-ASCII filenames preservation if needed, or just use secure_filename
# For simplicity and safety in demo:
save_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
file.save(save_path)
return jsonify({"status": "success", "filename": filename, "path": save_path})
@app.errorhandler(404)
def not_found(e):
return jsonify({"error": "Resource not found"}), 404
@app.errorhandler(500)
def server_error(e):
return jsonify({"error": "Internal server error"}), 500
# Initialize DB on import
init_db()
if __name__ == '__main__':
app.run(host='0.0.0.0', port=7860, debug=True)