Spaces:
Sleeping
Sleeping
duqing2026
feat: enhance venture scope agent with file upload, better UI, and deployment config
9a5c573 | import os | |
| import json | |
| import sqlite3 | |
| import requests | |
| from flask import Flask, render_template, request, jsonify, send_from_directory | |
| from werkzeug.utils import secure_filename | |
| import PyPDF2 | |
| app = Flask(__name__) | |
| app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 16MB limit | |
| app.config['UPLOAD_FOLDER'] = os.path.join(app.instance_path, 'uploads') | |
| # Configuration | |
| API_KEY = "sk-vimuseiptfbomzegyuvmebjzooncsqbyjtlddrfodzcdskgi" | |
| DB_PATH = os.path.join(app.instance_path, 'venture.db') | |
| # Ensure upload directory exists | |
| os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True) | |
| def get_db(): | |
| conn = sqlite3.connect(DB_PATH) | |
| conn.row_factory = sqlite3.Row | |
| return conn | |
| def init_db(): | |
| os.makedirs(app.instance_path, exist_ok=True) | |
| conn = get_db() | |
| conn.execute(''' | |
| CREATE TABLE IF NOT EXISTS deals ( | |
| id INTEGER PRIMARY KEY AUTOINCREMENT, | |
| name TEXT NOT NULL, | |
| stage TEXT DEFAULT 'New', | |
| sector TEXT, | |
| description TEXT, | |
| score INTEGER, | |
| analysis TEXT, | |
| created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP | |
| ) | |
| ''') | |
| conn.commit() | |
| conn.close() | |
| # Initialize DB on startup | |
| with app.app_context(): | |
| init_db() | |
| def call_llm(prompt, system_prompt="You are a Venture Capital analyst."): | |
| headers = { | |
| "Authorization": f"Bearer {API_KEY}", | |
| "Content-Type": "application/json" | |
| } | |
| data = { | |
| "model": "Qwen/Qwen2.5-7B-Instruct", | |
| "messages": [ | |
| {"role": "system", "content": system_prompt}, | |
| {"role": "user", "content": prompt} | |
| ], | |
| "temperature": 0.7 | |
| } | |
| try: | |
| response = requests.post("https://api.siliconflow.cn/v1/chat/completions", json=data, headers=headers, timeout=30) | |
| response.raise_for_status() | |
| return response.json()['choices'][0]['message']['content'] | |
| except Exception as e: | |
| print(f"LLM Error: {e}") | |
| return None | |
| def index(): | |
| return render_template('index.html') | |
| def analyze_startup(): | |
| data = request.json | |
| description = data.get('description', '') | |
| system_prompt = """ | |
| You are a Senior VC Analyst. Analyze the startup description. | |
| Output ONLY valid JSON format with these keys: | |
| { | |
| "score": (0-100 integer), | |
| "summary": "One sentence summary", | |
| "strengths": ["point 1", "point 2", "point 3"], | |
| "weaknesses": ["point 1", "point 2"], | |
| "sector": "Industry Sector", | |
| "radar": {"team": 0-10, "product": 0-10, "market": 0-10, "traction": 0-10, "moat": 0-10} | |
| } | |
| Do not add markdown formatting like ```json. Just raw JSON. | |
| """ | |
| prompt = f"Analyze this startup:\n{description}" | |
| try: | |
| content = call_llm(prompt, system_prompt) | |
| # Clean potential markdown | |
| if content.startswith('```json'): | |
| content = content[7:] | |
| if content.endswith('```'): | |
| content = content[:-3] | |
| result = json.loads(content.strip()) | |
| return jsonify(result) | |
| except Exception as e: | |
| return jsonify({"error": str(e), "mock": True, "result": { | |
| "score": 75, | |
| "summary": "AI-driven analytics platform (Mock Analysis)", | |
| "strengths": ["Strong tech", "Growing market"], | |
| "weaknesses": ["Competitive landscape"], | |
| "sector": "SaaS", | |
| "radar": {"team": 7, "product": 8, "market": 8, "traction": 6, "moat": 5} | |
| }}) | |
| def handle_deals(): | |
| conn = get_db() | |
| if request.method == 'GET': | |
| deals = conn.execute('SELECT * FROM deals ORDER BY created_at DESC').fetchall() | |
| conn.close() | |
| return jsonify([dict(row) for row in deals]) | |
| if request.method == 'POST': | |
| data = request.json | |
| conn.execute('INSERT INTO deals (name, stage, sector, description, score, analysis) VALUES (?, ?, ?, ?, ?, ?)', | |
| (data['name'], data['stage'], data['sector'], data['description'], data['score'], json.dumps(data['analysis']))) | |
| conn.commit() | |
| deal_id = conn.execute('SELECT last_insert_rowid()').fetchone()[0] | |
| conn.close() | |
| return jsonify({"id": deal_id, "status": "success"}) | |
| def generate_memo(): | |
| data = request.json | |
| deal_name = data.get('name') | |
| deal_desc = data.get('description') | |
| prompt = f""" | |
| Write a formal Investment Memo for '{deal_name}'. | |
| Description: {deal_desc} | |
| Structure: | |
| 1. Executive Summary | |
| 2. Market Opportunity | |
| 3. Product/Technology | |
| 4. Business Model | |
| 5. Risks & Mitigations | |
| 6. Recommendation | |
| Use Markdown formatting. | |
| """ | |
| content = call_llm(prompt) | |
| if not content: | |
| content = f"# Investment Memo: {deal_name}\n\n**Note:** AI generation failed, showing placeholder.\n\n## Executive Summary\n{deal_desc}..." | |
| return jsonify({"content": content}) | |
| 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) | |
| text_content = "" | |
| try: | |
| if filename.lower().endswith('.pdf'): | |
| with open(filepath, 'rb') as f: | |
| reader = PyPDF2.PdfReader(f) | |
| for page in reader.pages: | |
| text_content += page.extract_text() + "\n" | |
| elif filename.lower().endswith('.txt') or filename.lower().endswith('.md'): | |
| with open(filepath, 'r', encoding='utf-8') as f: | |
| text_content = f.read() | |
| else: | |
| return jsonify({"error": "Unsupported file type. Please upload PDF or TXT."}), 400 | |
| # Truncate if too long for LLM | |
| if len(text_content) > 10000: | |
| text_content = text_content[:10000] + "...(truncated)" | |
| return jsonify({"text": text_content, "filename": filename}) | |
| except Exception as e: | |
| return jsonify({"error": f"Failed to process file: {str(e)}"}), 500 | |
| def request_entity_too_large(error): | |
| return jsonify({"error": "File too large (Max 16MB)"}), 413 | |
| def internal_server_error(error): | |
| return jsonify({"error": "Internal Server Error"}), 500 | |
| def not_found_error(error): | |
| return jsonify({"error": "Resource Not Found"}), 404 | |
| if __name__ == '__main__': | |
| app.run(host='0.0.0.0', port=7860, debug=True) | |