feat(hub): add AI coach page with daily summary and chat

Aggregate four-account trades via hub_ai module and /api/hub/trades/today; store sessions in JSON; default OpenAI config matches instances.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-06-06 23:51:36 +08:00
parent 4fad5696df
commit cee641ba5d
23 changed files with 1557 additions and 14 deletions
+88
View File
@@ -0,0 +1,88 @@
"""中控 AI:单会话聊天(直到用户点击新开)。"""
from __future__ import annotations
from typing import Any
from hub_ai.client import generate_text, model_label
from hub_ai.config import CHAT_MAX_HISTORY_TURNS, CHAT_TEMPERATURE
from hub_ai.context import build_daily_context, format_chat_context_brief
from hub_ai.prompts import CHAT_SYSTEM, build_chat_user_prompt
from hub_ai.store import (
append_chat_message,
create_new_session,
ensure_active_session,
get_active_session,
load_chat_store,
summary_excerpt_for_chat,
)
def _history_lines(messages: list[dict], max_turns: int = CHAT_MAX_HISTORY_TURNS) -> str:
rows = [m for m in (messages or []) if m.get("role") in ("user", "assistant")]
rows = rows[-max_turns * 2 :]
lines = []
for m in rows:
role = "用户" if m.get("role") == "user" else "搭档"
lines.append(f"{role}{m.get('content') or ''}")
return "\n".join(lines)
def get_chat_state() -> dict[str, Any]:
store = load_chat_store()
session = get_active_session()
return {
"active_session_id": store.get("active_session_id"),
"session": session,
"model": model_label(),
}
def start_new_chat(*, trading_day: str) -> dict:
session = create_new_session(trading_day=trading_day)
return {"ok": True, "session": session, "model": model_label()}
def send_chat_message(
exchanges: list[dict],
message: str,
*,
trading_day: str | None = None,
) -> dict[str, Any]:
text = (message or "").strip()
if not text:
return {"ok": False, "msg": "消息不能为空"}
ctx = build_daily_context(exchanges, trading_day=trading_day)
day = ctx["trading_day"]
session = ensure_active_session(trading_day=day)
sid = session["id"]
history = _history_lines(session.get("messages") or [])
append_chat_message(sid, "user", text)
brief_ctx = format_chat_context_brief(ctx)
excerpt = summary_excerpt_for_chat(day)
user_prompt = build_chat_user_prompt(
context_text=brief_ctx,
trading_day=day,
summary_excerpt=excerpt,
history_lines=history,
user_message=text,
)
reply = generate_text(
system=CHAT_SYSTEM,
user=user_prompt,
temperature=CHAT_TEMPERATURE,
)
if reply.startswith("AI 调用失败"):
return {"ok": False, "msg": reply, "session_id": sid}
session = append_chat_message(sid, "assistant", reply)
return {
"ok": True,
"trading_day": day,
"session": session,
"reply": reply,
"model": model_label(),
}