From 299e638c6a131cea486604445999279fda58d145 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Fri, 30 Jan 2026 16:59:06 -0300 Subject: [PATCH] ai: persist conversation id --- backend/atlas_portal/routes/ai.py | 10 +++++++--- frontend/src/views/AiView.vue | 18 +++++++++++++++++- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/backend/atlas_portal/routes/ai.py b/backend/atlas_portal/routes/ai.py index 3d5e705..b0ffef2 100644 --- a/backend/atlas_portal/routes/ai.py +++ b/backend/atlas_portal/routes/ai.py @@ -20,6 +20,7 @@ def register(app) -> None: user_message = (payload.get("message") or "").strip() history = payload.get("history") or [] profile = (payload.get("profile") or payload.get("mode") or "atlas-quick").strip().lower() + conversation_id = payload.get("conversation_id") if isinstance(payload.get("conversation_id"), str) else "" if not user_message: return jsonify({"error": "message required"}), 400 @@ -30,7 +31,7 @@ def register(app) -> None: source = "stock" else: mode = "smart" if profile in {"atlas-smart", "smart"} else "quick" - reply = _atlasbot_answer(user_message, mode) + reply = _atlasbot_answer(user_message, mode, conversation_id) source = f"atlas-{mode}" if reply: elapsed_ms = int((time.time() - started) * 1000) @@ -54,7 +55,7 @@ def register(app) -> None: _start_keep_warm() -def _atlasbot_answer(message: str, mode: str) -> str: +def _atlasbot_answer(message: str, mode: str, conversation_id: str) -> str: endpoint = settings.AI_ATLASBOT_ENDPOINT if not endpoint: return "" @@ -62,8 +63,11 @@ def _atlasbot_answer(message: str, mode: str) -> str: if settings.AI_ATLASBOT_TOKEN: headers["X-Internal-Token"] = settings.AI_ATLASBOT_TOKEN try: + payload = {"prompt": message, "mode": mode} + if conversation_id: + payload["conversation_id"] = conversation_id with httpx.Client(timeout=settings.AI_ATLASBOT_TIMEOUT_SEC) as client: - resp = client.post(endpoint, json={"prompt": message, "mode": mode}, headers=headers) + resp = client.post(endpoint, json=payload, headers=headers) if resp.status_code != 200: return "" data = resp.json() diff --git a/frontend/src/views/AiView.vue b/frontend/src/views/AiView.vue index 9bfa28a..75421b9 100644 --- a/frontend/src/views/AiView.vue +++ b/frontend/src/views/AiView.vue @@ -117,6 +117,21 @@ const draft = ref(""); const sending = ref(false); const chatWindow = ref(null); const copied = ref(false); +const conversationIds = reactive({}); + +function ensureConversationId(profile) { + if (conversationIds[profile]) return conversationIds[profile]; + const key = `atlas-ai-conversation:${profile}`; + let value = localStorage.getItem(key); + if (!value) { + const suffix = + typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : `${Math.random()}`.slice(2); + value = `${profile}-${Date.now()}-${suffix}`; + localStorage.setItem(key, value); + } + conversationIds[profile] = value; + return value; +} onMounted(() => fetchMeta(activeProfile.value)); watch(activeProfile, (profile) => fetchMeta(profile)); @@ -157,11 +172,12 @@ async function sendMessage() { try { const history = state.messages.filter((m) => !m.streaming).map((m) => ({ role: m.role, content: m.content })); + const conversation_id = ensureConversationId(activeProfile.value); const start = performance.now(); const resp = await fetch(API_URL, { method: "POST", headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ message: text, history, profile: activeProfile.value }), + body: JSON.stringify({ message: text, history, profile: activeProfile.value, conversation_id }), }); const contentType = resp.headers.get("content-type") || "";