Spaces:
Sleeping
Sleeping
| import os | |
| import json | |
| import sqlite3 | |
| import requests | |
| import datetime | |
| import uuid | |
| from flask import Flask, render_template, request, jsonify, send_from_directory | |
| from flask_cors import CORS | |
| app = Flask(__name__, static_folder='static', template_folder='templates') | |
| CORS(app) | |
| # Configuration | |
| SILICONFLOW_API_KEY = os.environ.get("SILICONFLOW_API_KEY", "sk-vimuseiptfbomzegyuvmebjzooncsqbyjtlddrfodzcdskgi") | |
| SILICONFLOW_API_URL = "https://api.siliconflow.cn/v1/chat/completions" | |
| DB_PATH = os.path.join(app.instance_path, "logic_stream.db") | |
| # Ensure instance folder exists | |
| os.makedirs(app.instance_path, exist_ok=True) | |
| # Database Initialization | |
| def init_db(): | |
| conn = sqlite3.connect(DB_PATH) | |
| c = conn.cursor() | |
| c.execute('''CREATE TABLE IF NOT EXISTS workflows | |
| (id TEXT PRIMARY KEY, name TEXT, description TEXT, steps TEXT, created_at TEXT, updated_at TEXT)''') | |
| c.execute('''CREATE TABLE IF NOT EXISTS runs | |
| (id TEXT PRIMARY KEY, workflow_id TEXT, status TEXT, result TEXT, logs TEXT, created_at TEXT)''') | |
| # Check if empty, add default data | |
| c.execute("SELECT count(*) FROM workflows") | |
| if c.fetchone()[0] == 0: | |
| default_workflow = { | |
| "id": str(uuid.uuid4()), | |
| "name": "市场趋势分析 (Market Trend Analysis)", | |
| "description": "自动分析市场新闻并生成简报 (Auto-analyze market news and generate brief)", | |
| "steps": json.dumps([ | |
| {"id": "step_1", "type": "input", "name": "输入主题", "content": "AI Agent 2025年发展趋势"}, | |
| {"id": "step_2", "type": "llm", "name": "深度分析", "prompt": "请分析以下主题的市场趋势和商业机会:{{step_1.output}}。输出 JSON 格式。"}, | |
| {"id": "step_3", "type": "llm", "name": "生成简报", "prompt": "根据以下分析生成一份简洁的投资简报:{{step_2.output}}"} | |
| ]), | |
| "created_at": datetime.datetime.now().isoformat(), | |
| "updated_at": datetime.datetime.now().isoformat() | |
| } | |
| c.execute("INSERT INTO workflows VALUES (?, ?, ?, ?, ?, ?)", | |
| (default_workflow["id"], default_workflow["name"], default_workflow["description"], | |
| default_workflow["steps"], default_workflow["created_at"], default_workflow["updated_at"])) | |
| conn.commit() | |
| print("Default data initialized.") | |
| conn.commit() | |
| conn.close() | |
| init_db() | |
| def get_db_connection(): | |
| conn = sqlite3.connect(DB_PATH) | |
| conn.row_factory = sqlite3.Row | |
| return conn | |
| # Routes | |
| def index(): | |
| return render_template('index.html') | |
| def get_workflows(): | |
| conn = get_db_connection() | |
| workflows = conn.execute('SELECT * FROM workflows ORDER BY updated_at DESC').fetchall() | |
| conn.close() | |
| return jsonify([dict(w) for w in workflows]) | |
| def create_workflow(): | |
| data = request.json | |
| workflow_id = str(uuid.uuid4()) | |
| now = datetime.datetime.now().isoformat() | |
| conn = get_db_connection() | |
| conn.execute('INSERT INTO workflows (id, name, description, steps, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)', | |
| (workflow_id, data['name'], data.get('description', ''), json.dumps(data['steps']), now, now)) | |
| conn.commit() | |
| conn.close() | |
| return jsonify({"id": workflow_id, "status": "created"}) | |
| def update_workflow(id): | |
| data = request.json | |
| now = datetime.datetime.now().isoformat() | |
| conn = get_db_connection() | |
| conn.execute('UPDATE workflows SET name = ?, description = ?, steps = ?, updated_at = ? WHERE id = ?', | |
| (data['name'], data.get('description', ''), json.dumps(data['steps']), now, id)) | |
| conn.commit() | |
| conn.close() | |
| return jsonify({"status": "updated"}) | |
| def delete_workflow(id): | |
| conn = get_db_connection() | |
| conn.execute('DELETE FROM workflows WHERE id = ?', (id,)) | |
| conn.commit() | |
| conn.close() | |
| return jsonify({"status": "deleted"}) | |
| def run_workflow(): | |
| data = request.json | |
| workflow_id = data.get('workflow_id') | |
| steps = data.get('steps', []) | |
| # If workflow_id is provided, fetch steps from DB (optional, but here we assume frontend sends current steps) | |
| run_id = str(uuid.uuid4()) | |
| logs = [] | |
| context = {} | |
| try: | |
| for step in steps: | |
| step_id = step['id'] | |
| step_type = step['type'] | |
| step_name = step['name'] | |
| logs.append({"timestamp": datetime.datetime.now().isoformat(), "level": "INFO", "message": f"Starting step: {step_name} ({step_type})"}) | |
| output = "" | |
| if step_type == 'input': | |
| output = step.get('content', '') | |
| logs.append({"timestamp": datetime.datetime.now().isoformat(), "level": "SUCCESS", "message": f"Input received: {output[:50]}..."}) | |
| elif step_type == 'llm': | |
| prompt_template = step.get('prompt', '') | |
| # Simple variable substitution {{step_id.output}} | |
| prompt = prompt_template | |
| for prev_step_id, prev_output in context.items(): | |
| prompt = prompt.replace(f"{{{{{prev_step_id}.output}}}}", str(prev_output)) | |
| logs.append({"timestamp": datetime.datetime.now().isoformat(), "level": "INFO", "message": f"Calling LLM with prompt: {prompt[:50]}..."}) | |
| # Call SiliconFlow API | |
| try: | |
| headers = { | |
| "Authorization": f"Bearer {SILICONFLOW_API_KEY}", | |
| "Content-Type": "application/json" | |
| } | |
| payload = { | |
| "model": "Qwen/Qwen2.5-7B-Instruct", # Reliable model | |
| "messages": [ | |
| {"role": "system", "content": "You are a helpful business logic assistant. Output clean, structured responses."}, | |
| {"role": "user", "content": prompt} | |
| ], | |
| "stream": False, | |
| "temperature": 0.7 | |
| } | |
| response = requests.post(SILICONFLOW_API_URL, headers=headers, json=payload, timeout=30) | |
| response.raise_for_status() | |
| result = response.json() | |
| output = result['choices'][0]['message']['content'] | |
| logs.append({"timestamp": datetime.datetime.now().isoformat(), "level": "SUCCESS", "message": "LLM response received."}) | |
| except Exception as e: | |
| logs.append({"timestamp": datetime.datetime.now().isoformat(), "level": "ERROR", "message": f"LLM Error: {str(e)}"}) | |
| # Mock fallback for reliability | |
| output = f"[Mock Output] Failed to call API. Mock result for prompt: {prompt[:20]}..." | |
| # Store output in context | |
| context[step_id] = output | |
| # Also update the step object to return to frontend | |
| step['output'] = output | |
| final_status = "success" | |
| except Exception as e: | |
| final_status = "error" | |
| logs.append({"timestamp": datetime.datetime.now().isoformat(), "level": "CRITICAL", "message": f"Workflow failed: {str(e)}"}) | |
| # Save run | |
| conn = get_db_connection() | |
| conn.execute('INSERT INTO runs (id, workflow_id, status, result, logs, created_at) VALUES (?, ?, ?, ?, ?, ?)', | |
| (run_id, workflow_id, final_status, json.dumps(context), json.dumps(logs), datetime.datetime.now().isoformat())) | |
| conn.commit() | |
| conn.close() | |
| return jsonify({ | |
| "run_id": run_id, | |
| "status": final_status, | |
| "logs": logs, | |
| "results": context | |
| }) | |
| def chat(): | |
| # Direct chat endpoint for "Assistant" feature | |
| data = request.json | |
| message = data.get('message', '') | |
| try: | |
| headers = { | |
| "Authorization": f"Bearer {SILICONFLOW_API_KEY}", | |
| "Content-Type": "application/json" | |
| } | |
| payload = { | |
| "model": "Qwen/Qwen2.5-7B-Instruct", | |
| "messages": [ | |
| {"role": "system", "content": "You are LogicStream AI, an intelligent assistant for building business workflows."}, | |
| {"role": "user", "content": message} | |
| ], | |
| "stream": False | |
| } | |
| response = requests.post(SILICONFLOW_API_URL, headers=headers, json=payload, timeout=30) | |
| return jsonify(response.json()) | |
| except Exception as e: | |
| return jsonify({"error": str(e)}), 500 | |
| if __name__ == '__main__': | |
| app.run(host='0.0.0.0', port=7860, debug=True) | |