| import json |
| import os |
| import uuid |
| from pathlib import Path |
|
|
| from flask import Flask, render_template, request, jsonify, send_from_directory |
| from openai import OpenAI |
| from faster_whisper import WhisperModel |
|
|
| from pipeline import run_pipeline |
|
|
| app = Flask(__name__, static_folder='static', template_folder='templates') |
|
|
| UPLOAD_FOLDER = Path('uploads') |
| UPLOAD_FOLDER.mkdir(exist_ok=True) |
|
|
| AMD_ENDPOINT = os.environ.get("AMD_ENDPOINT", "http://134.199.198.41:8000/v1") |
| MODEL_NAME = os.environ.get("MODEL_NAME", "Qwen/Qwen3-14B") |
|
|
| llm_client = OpenAI(base_url=AMD_ENDPOINT, api_key="not-required") |
| whisper_model = WhisperModel("base", device="cpu", compute_type="int8") |
|
|
|
|
| def call_llm(prompt, max_tokens=200): |
| response = llm_client.chat.completions.create( |
| model=MODEL_NAME, |
| messages=[{"role": "user", "content": prompt}], |
| max_tokens=max_tokens, |
| temperature=0.3, |
| extra_body={"chat_template_kwargs": {"enable_thinking": False}}, |
| ) |
| content = response.choices[0].message.content.strip() |
| if "</think>" in content: |
| content = content.split("</think>")[-1].strip() |
| if content.startswith("```"): |
| content = content.split("\n", 1)[1].rsplit("```", 1)[0].strip() |
| return content |
|
|
|
|
| @app.route('/') |
| def index(): |
| return render_template('index.html') |
|
|
|
|
| @app.route('/session') |
| def session(): |
| return render_template('session.html') |
|
|
|
|
| @app.route('/summarize', methods=['POST']) |
| def summarize(): |
| data = request.get_json() |
| text = data.get('text', '') |
| category = data.get('category', 'legal') |
|
|
| if not text.strip(): |
| return jsonify({'summary': '', 'keywords': []}) |
|
|
| try: |
| prompt = f"""You are Ken, an AI co-listener. A user is about to enter a {category} consultation. |
| |
| Their input: "{text}" |
| |
| Return JSON with: |
| 1. "summary": A 2-3 sentence summary of their situation (concise, factual) |
| 2. "keywords": An array of 3-5 key topics/concerns to listen for during the conversation |
| |
| Return ONLY valid JSON, no other text.""" |
|
|
| content = call_llm(prompt, max_tokens=200) |
| result = json.loads(content) |
| return jsonify(result) |
| except Exception as e: |
| print(f"Summarize error: {e}") |
| return jsonify({'summary': text[:200], 'keywords': []}) |
|
|
|
|
| @app.route('/transcribe', methods=['POST']) |
| def transcribe(): |
| if 'audio' not in request.files: |
| return jsonify({'error': 'No audio'}), 400 |
|
|
| audio_file = request.files['audio'] |
| audio_path = UPLOAD_FOLDER / f'{uuid.uuid4().hex}.webm' |
| audio_file.save(audio_path) |
|
|
| try: |
| segments, _ = whisper_model.transcribe(str(audio_path), beam_size=3, language="en") |
| text = " ".join(seg.text.strip() for seg in segments) |
| audio_path.unlink(missing_ok=True) |
| return jsonify({'text': text}) |
| except Exception as e: |
| return jsonify({'error': str(e)}), 500 |
|
|
|
|
| @app.route('/process', methods=['POST']) |
| def process(): |
| if 'video' not in request.files: |
| return jsonify({'error': 'No video file'}), 400 |
|
|
| video_file = request.files['video'] |
| profile_text = request.form.get('profile_text', '') |
| profile_cat = request.form.get('profile_cat', 'Legal') |
|
|
| video_path = UPLOAD_FOLDER / f'{uuid.uuid4().hex}_{video_file.filename}' |
| video_file.save(video_path) |
|
|
| |
| summary = "" |
| if profile_text.strip(): |
| try: |
| prompt = f"""Summarize this user's situation in 2-3 concise sentences for a {profile_cat} consultation. Focus on key facts and concerns.\n\nUser input: "{profile_text}"\n\nReturn ONLY the summary.""" |
| summary = call_llm(prompt, max_tokens=150) |
| except Exception as e: |
| print(f"Summarization error: {e}") |
| summary = profile_text[:200] |
|
|
| profile = { |
| "name": "User", |
| "situation": summary or f"{profile_cat}: {profile_text}", |
| "knowledge_level": "intermediate", |
| "concerns": [profile_text] if profile_text else [] |
| } |
|
|
| result = run_pipeline(str(video_path), profile) |
| result['summary'] = summary |
|
|
| video_path.unlink(missing_ok=True) |
| return jsonify(result) |
|
|
|
|
| @app.route('/uploads/<path:filename>') |
| def uploaded_file(filename): |
| return send_from_directory(UPLOAD_FOLDER, filename) |
|
|
|
|
| if __name__ == '__main__': |
| debug = os.environ.get('DEBUG', 'false').lower() == 'true' |
| app.run(host='0.0.0.0', port=7860, debug=debug) |
|
|