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 @app.route('/') def index(): return render_template('index.html') @app.route('/api/workflows', methods=['GET']) 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]) @app.route('/api/workflows', methods=['POST']) 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"}) @app.route('/api/workflows/', methods=['PUT']) 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"}) @app.route('/api/workflows/', methods=['DELETE']) def delete_workflow(id): conn = get_db_connection() conn.execute('DELETE FROM workflows WHERE id = ?', (id,)) conn.commit() conn.close() return jsonify({"status": "deleted"}) @app.route('/api/run_workflow', methods=['POST']) 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 }) @app.route('/api/chat', methods=['POST']) 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)