05/31 Phase 5
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
from flask import Blueprint, render_template, request, Response, stream_with_context, jsonify
|
||||
from flask_login import login_required
|
||||
from app.models.ai_insight import AiInsight
|
||||
from app.services.ai_service import stream_chat, get_latest_daily_insight, generate_daily_insight
|
||||
|
||||
ai_bp = Blueprint('ai', __name__, url_prefix='/ai')
|
||||
|
||||
|
||||
@ai_bp.route('/')
|
||||
@login_required
|
||||
def index():
|
||||
latest_insight = get_latest_daily_insight()
|
||||
recent_chats = AiInsight.query\
|
||||
.filter_by(insight_type='chat_response')\
|
||||
.order_by(AiInsight.created_at.desc())\
|
||||
.limit(20).all()
|
||||
return render_template('ai/index.html',
|
||||
latest_insight=latest_insight,
|
||||
recent_chats=recent_chats)
|
||||
|
||||
|
||||
@ai_bp.route('/stream')
|
||||
@login_required
|
||||
def stream():
|
||||
"""SSE endpoint — streams Groq response chunk by chunk."""
|
||||
message = request.args.get('message', '').strip()
|
||||
if not message:
|
||||
def empty():
|
||||
yield 'data: Please enter a message.\n\n'
|
||||
yield 'data: [DONE]\n\n'
|
||||
return Response(stream_with_context(empty()),
|
||||
mimetype='text/event-stream',
|
||||
headers={
|
||||
'Cache-Control': 'no-cache',
|
||||
'X-Accel-Buffering': 'no',
|
||||
})
|
||||
|
||||
return Response(
|
||||
stream_with_context(stream_chat(message)),
|
||||
mimetype='text/event-stream',
|
||||
headers={
|
||||
'Cache-Control': 'no-cache',
|
||||
'X-Accel-Buffering': 'no',
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@ai_bp.route('/generate-insight', methods=['POST'])
|
||||
@login_required
|
||||
def trigger_insight():
|
||||
"""Manually trigger a daily insight generation."""
|
||||
content = generate_daily_insight()
|
||||
if content:
|
||||
return jsonify({'status': 'ok', 'content': content})
|
||||
return jsonify({'status': 'error', 'message': 'Failed to generate insight'}), 500
|
||||
|
||||
|
||||
@ai_bp.route('/history')
|
||||
@login_required
|
||||
def history():
|
||||
page = request.args.get('page', 1, type=int)
|
||||
insights = AiInsight.query\
|
||||
.order_by(AiInsight.created_at.desc())\
|
||||
.paginate(page=page, per_page=20, error_out=False)
|
||||
return render_template('ai/history.html', insights=insights)
|
||||
@@ -6,6 +6,7 @@ from app.models.account import Account
|
||||
from app.models.transaction import Transaction
|
||||
from app.models.category import Category
|
||||
from app.services.fx_service import get_today_rate, get_rate_history
|
||||
from app.services.ai_service import get_latest_daily_insight
|
||||
from app.services.account_service import get_total_assets, get_total_liabilities
|
||||
from datetime import date, datetime, timedelta
|
||||
import calendar
|
||||
@@ -127,6 +128,9 @@ def index():
|
||||
'rates': [float(r.usd_to_vnd) for r in fx_history],
|
||||
}
|
||||
|
||||
# ── AI insight ──────────────────────────────────────
|
||||
ai_insight = get_latest_daily_insight()
|
||||
|
||||
return render_template('dashboard/index.html',
|
||||
period=period,
|
||||
period_label=period_label,
|
||||
@@ -145,7 +149,8 @@ def index():
|
||||
chart_expense=chart_expense,
|
||||
recent_txns=recent_txns,
|
||||
fx=fx,
|
||||
fx_history_data=fx_history_data)
|
||||
fx_history_data=fx_history_data,
|
||||
ai_insight=ai_insight)
|
||||
|
||||
|
||||
@dashboard_bp.route('/api/fx-history')
|
||||
|
||||
Reference in New Issue
Block a user