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
@app.route('/')
def index():
return render_template('index.html')
@app.route('/api/analyze', methods=['POST'])
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}
}})
@app.route('/api/deals', methods=['GET', 'POST'])
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"})
@app.route('/api/memo', methods=['POST'])
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})
@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)
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
@app.errorhandler(413)
def request_entity_too_large(error):
return jsonify({"error": "File too large (Max 16MB)"}), 413
@app.errorhandler(500)
def internal_server_error(error):
return jsonify({"error": "Internal Server Error"}), 500
@app.errorhandler(404)
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)