From 1ebe7b1b38febfff916ea7a770d599a40fbdddf8 Mon Sep 17 00:00:00 2001 From: wiiiii123 Date: Mon, 25 May 2026 01:07:47 +0700 Subject: [PATCH] fix: route uploaded lessons through LMS preview --- .../direct_document_preview_payloads.py | 21 +--- .../direct_node_uploaded_context.py | 47 +------ .../multi_agent/document_preview_contract.py | 56 +++++++++ .../app/engine/multi_agent/tool_collection.py | 119 +++--------------- .../tests/unit/test_document_context_api.py | 27 ++++ .../unit/test_document_preview_contract.py | 12 ++ wiii-desktop/src/EmbedApp.tsx | 10 +- 7 files changed, 130 insertions(+), 162 deletions(-) diff --git a/maritime-ai-service/app/engine/multi_agent/direct_document_preview_payloads.py b/maritime-ai-service/app/engine/multi_agent/direct_document_preview_payloads.py index ee5ae2dc..7d193a2d 100644 --- a/maritime-ai-service/app/engine/multi_agent/direct_document_preview_payloads.py +++ b/maritime-ai-service/app/engine/multi_agent/direct_document_preview_payloads.py @@ -9,6 +9,7 @@ DOC_PREVIEW_HOST_ACTION_TOOL as _DOC_PREVIEW_HOST_ACTION_TOOL, find_document_host_action_tool, looks_uploaded_document_course_request as _looks_uploaded_doc_course_request, + looks_uploaded_document_lesson_preview_request as _looks_uploaded_doc_lesson_preview_request, normalize_document_contract_text as _normalize_doc_preview_text, uploaded_document_attachments_from_state as _uploaded_document_attachments_from_state, ) @@ -144,25 +145,7 @@ def _should_request_uploaded_doc_preview( return False if not _uploaded_document_attachments_from_state(state): return False - normalized = _normalize_doc_preview_text(query) - return any( - marker in normalized - for marker in ( - "preview", - "xem truoc", - "ban xem truoc", - "ban nhap", - "draft", - "cap nhat bai hoc", - "tao ban xem truoc", - "lesson patch", - "preview_lesson_patch", - "source_references", - "citation", - "trich dan", - "nguon", - ) - ) + return _looks_uploaded_doc_lesson_preview_request(query) def _resolve_doc_preview_lesson_id(state: AgentState | None) -> str: diff --git a/maritime-ai-service/app/engine/multi_agent/direct_node_uploaded_context.py b/maritime-ai-service/app/engine/multi_agent/direct_node_uploaded_context.py index 916e7440..274f792d 100644 --- a/maritime-ai-service/app/engine/multi_agent/direct_node_uploaded_context.py +++ b/maritime-ai-service/app/engine/multi_agent/direct_node_uploaded_context.py @@ -8,6 +8,8 @@ from app.engine.multi_agent.document_preview_contract import ( has_uploaded_document_context as _has_uploaded_document_context, + looks_uploaded_document_course_request as _looks_uploaded_document_course_request, + looks_uploaded_document_lesson_preview_request as _looks_uploaded_document_lesson_preview_request, uploaded_document_attachments_from_context as _uploaded_document_attachments, ) from app.engine.multi_agent.direct_session_memory_runtime import ( @@ -121,48 +123,11 @@ def _looks_uploaded_document_preview_request(query: str) -> bool: folded = _fold_direct_text(query) if not folded: return False - preview_markers = ( - "approval_token", - "ban nhap", - "ban xem truoc", - "citation", - "diff", - "lesson patch", - "preview", - "preview_lesson_patch", - "source references", - "source_references", - "lap bai giang", - "soan bai giang", - "soan giao an", - "tao bai giang", - "tao giao an", - "tao hoc lieu", - "tao ban xem truoc", - "tao khoa hoc", - "thiet ke bai giang", - "thiet ke khoa hoc", - "xay dung bai giang", - "cau truc khoa hoc", - "toan bo khoa", - "cay khoa", - "chia khoa", - "course architect", - "course outline", - "course syllabus", - "curriculum", - "de cuong khoa", - "de cuong mon", - "giao trinh", - "ke hoach giang day", - "learning path", - "lo trinh hoc", - "syllabus", - "generate_course_from_document", - "trich dan", - "xem truoc", + return _looks_uploaded_document_course_request( + folded + ) or _looks_uploaded_document_lesson_preview_request( + folded ) - return any(marker in folded for marker in preview_markers) def _looks_uploaded_context_fact_query(query: str, ctx: dict[str, Any]) -> bool: diff --git a/maritime-ai-service/app/engine/multi_agent/document_preview_contract.py b/maritime-ai-service/app/engine/multi_agent/document_preview_contract.py index bba406fb..3acfd3b7 100644 --- a/maritime-ai-service/app/engine/multi_agent/document_preview_contract.py +++ b/maritime-ai-service/app/engine/multi_agent/document_preview_contract.py @@ -66,6 +66,45 @@ "outline", ) +_LESSON_AUTHORING_EXCLUSION_MARKERS = ( + "bai tap", + "bai kiem tra", + "cau hoi", + "kiem tra", + "quiz", +) + +_LESSON_AUTHORING_VERBS = ( + "build", + "create", + "lam", + "lap", + "soan", + "tao", + "thiet ke", + "viet", + "write", + "xay dung", +) + +_LESSON_PREVIEW_REQUEST_MARKERS = ( + "approval_token", + "ban nhap", + "ban xem truoc", + "cap nhat bai hoc", + "citation", + "diff", + "draft", + "lesson patch", + "preview", + "preview_lesson_patch", + "source references", + "source_references", + "tao ban xem truoc", + "trich dan", + "xem truoc", +) + def normalize_document_contract_text(value: Any) -> str: text = str(value or "").replace("\\_", "_") @@ -127,6 +166,23 @@ def looks_uploaded_document_course_request(query: str) -> bool: return any(marker in normalized for marker in _COURSE_REQUEST_MARKERS) +def _looks_singular_lesson_authoring_request(normalized: str) -> bool: + if not ("bai hoc" in normalized or "lesson" in normalized): + return False + if any(marker in normalized for marker in _LESSON_AUTHORING_EXCLUSION_MARKERS): + return False + return any(marker in normalized for marker in _LESSON_AUTHORING_VERBS) + + +def looks_uploaded_document_lesson_preview_request(query: str) -> bool: + normalized = normalize_document_contract_text(query) + if not normalized: + return False + return any(marker in normalized for marker in _LESSON_PREVIEW_REQUEST_MARKERS) or ( + _looks_singular_lesson_authoring_request(normalized) + ) + + def _runtime_tool_name( tool: Any, *, diff --git a/maritime-ai-service/app/engine/multi_agent/tool_collection.py b/maritime-ai-service/app/engine/multi_agent/tool_collection.py index 80a810fa..e7fbba75 100644 --- a/maritime-ai-service/app/engine/multi_agent/tool_collection.py +++ b/maritime-ai-service/app/engine/multi_agent/tool_collection.py @@ -11,7 +11,12 @@ from typing import Any, Optional from app.core.config import settings +from app.engine.multi_agent.document_preview_contract import ( + looks_uploaded_document_course_request as _contract_looks_uploaded_document_course_request, + looks_uploaded_document_lesson_preview_request as _contract_looks_uploaded_document_lesson_preview_request, +) from app.engine.multi_agent.state import AgentState + logger = logging.getLogger(__name__) @@ -24,6 +29,14 @@ def _normalize_for_intent(query: str) -> str: return _load_attr("app.engine.multi_agent.direct_intent", "_normalize_for_intent")(query) +def _looks_uploaded_document_course_request(query: str) -> bool: + return _contract_looks_uploaded_document_course_request(query) + + +def _looks_uploaded_document_lesson_preview_request(query: str) -> bool: + return _contract_looks_uploaded_document_lesson_preview_request(query) + + def _needs_web_search(query: str) -> bool: return _load_attr("app.engine.multi_agent.direct_intent", "_needs_web_search")(query) @@ -277,113 +290,17 @@ def _has_uploaded_document_context_state(state: Optional[AgentState]) -> bool: def _looks_like_document_preview_request(query: str, state: Optional[AgentState]) -> bool: if not _has_uploaded_document_context_state(state): return False - normalized = _normalize_for_intent(query) - return any( - marker in normalized - for marker in ( - "preview", - "xem truoc", - "ban xem truoc", - "ban nhap", - "draft", - "cap nhat bai hoc", - "lap bai giang", - "soan bai giang", - "soan giao an", - "tao bai giang", - "tao giao an", - "tao hoc lieu", - "tao bai hoc", - "tao khoa hoc", - "thiet ke bai giang", - "thiet ke khoa hoc", - "xay dung bai giang", - "cau truc khoa hoc", - "toan bo khoa", - "cay khoa", - "chia khoa", - "course architect", - "course outline", - "course syllabus", - "curriculum", - "de cuong khoa", - "de cuong mon", - "giao trinh", - "ke hoach giang day", - "learning path", - "lo trinh hoc", - "syllabus", - "generate_course_from_document", - "lesson patch", - "preview_lesson_patch", - "source_references", - "citation", - "trich dan", - "nguon", - ) + return _looks_uploaded_document_course_request( + query + ) or _looks_uploaded_document_lesson_preview_request( + query ) def _looks_like_document_course_preview_request(query: str, state: Optional[AgentState]) -> bool: if not _has_uploaded_document_context_state(state): return False - normalized = _normalize_for_intent(query) - if any( - marker in normalized - for marker in ( - "preview_lesson_patch", - "lesson patch", - "bai hoc hien tai", - "cap nhat bai hoc", - ) - ): - return False - return any( - marker in normalized - for marker in ( - "generate_course_from_document", - "lap bai giang", - "soan bai giang", - "soan giao an", - "tao bai giang", - "tao giao an", - "tao hoc lieu", - "course architect", - "course outline", - "course syllabus", - "curriculum", - "full course", - "toan bo khoa", - "cay khoa", - "chia khoa", - "chia thanh bai", - "chia thanh chuong", - "chuong trinh dao tao", - "de cuong khoa", - "de cuong mon", - "giao trinh", - "ke hoach giang day", - "khoa dao tao", - "khoa day du", - "khoa hoan chinh", - "learning path", - "lo trinh hoc", - "lo trinh khoa", - "nhieu bai hoc", - "nhieu chuong", - "phan chia bai hoc", - "syllabus", - "tao khoa hoc", - "thiet ke bai giang", - "thiet ke khoa hoc", - "xay dung bai giang", - "cau truc khoa hoc", - "chuong/bai", - "chuong bai", - "module", - "outline", - ) - ) + return _looks_uploaded_document_course_request(query) def _document_preview_host_action_tools(tools: list[Any]) -> list[Any]: diff --git a/maritime-ai-service/tests/unit/test_document_context_api.py b/maritime-ai-service/tests/unit/test_document_context_api.py index 60fdf29a..8ef47f49 100644 --- a/maritime-ai-service/tests/unit/test_document_context_api.py +++ b/maritime-ai-service/tests/unit/test_document_context_api.py @@ -507,6 +507,33 @@ def test_uploaded_document_preview_request_bypasses_fact_fast_path(): assert not _looks_uploaded_context_fact_query(query, ctx) +def test_uploaded_document_lesson_creation_request_bypasses_fact_fast_path(): + from app.engine.multi_agent.direct_node_uploaded_context import ( + _looks_uploaded_context_fact_query, + _looks_uploaded_document_preview_request, + ) + + ctx = { + "document_context": { + "attachments": [ + { + "file_name": "lesson.docx", + "media_kind": "document", + "parser": "markitdown", + "markdown": ( + "Ke hoach bai hoc thu nghiem Wiii\n" + "Chu de: An toan hang hai va approval_token.\n" + ), + } + ] + } + } + query = "tao cho minh bai hoc" + + assert _looks_uploaded_document_preview_request(query) + assert not _looks_uploaded_context_fact_query(query, ctx) + + def test_uploaded_document_visual_guard_does_not_describe_frames_without_vision(): from app.engine.multi_agent.direct_node_uploaded_context import ( _build_uploaded_document_visual_guard_answer, diff --git a/maritime-ai-service/tests/unit/test_document_preview_contract.py b/maritime-ai-service/tests/unit/test_document_preview_contract.py index e3785f77..b46498bd 100644 --- a/maritime-ai-service/tests/unit/test_document_preview_contract.py +++ b/maritime-ai-service/tests/unit/test_document_preview_contract.py @@ -8,6 +8,7 @@ has_document_preview_host_action_tool, has_uploaded_document_context, looks_uploaded_document_course_request, + looks_uploaded_document_lesson_preview_request, uploaded_document_attachments_from_context, uploaded_document_attachments_from_state, ) @@ -44,6 +45,13 @@ def test_uploaded_document_course_intent_is_shared_by_preview_and_tool_rounds(): assert not looks_uploaded_document_course_request("cap nhat bai hoc hien tai") +def test_uploaded_document_lesson_preview_intent_is_shared_by_preview_and_tool_rounds(): + assert looks_uploaded_document_lesson_preview_request("tao cho minh bai hoc") + assert looks_uploaded_document_lesson_preview_request("cap nhat bai hoc hien tai") + assert looks_uploaded_document_lesson_preview_request("create a lesson from this file") + assert not looks_uploaded_document_lesson_preview_request("tao cau hoi cho bai hoc") + + def test_document_preview_forced_tool_choice_prefers_course_when_available(): tools = [ SimpleNamespace(name=DOC_PREVIEW_HOST_ACTION_TOOL), @@ -59,6 +67,10 @@ def test_document_preview_forced_tool_choice_prefers_course_when_available(): document_preview_forced_tool_choice("cap nhat bai hoc hien tai", tools) == DOC_PREVIEW_HOST_ACTION_TOOL ) + assert ( + document_preview_forced_tool_choice("tao cho minh bai hoc", tools) + == DOC_PREVIEW_HOST_ACTION_TOOL + ) def test_extract_document_preview_capabilities_from_state_and_context(): diff --git a/wiii-desktop/src/EmbedApp.tsx b/wiii-desktop/src/EmbedApp.tsx index f04a9c27..f5c0a6c5 100644 --- a/wiii-desktop/src/EmbedApp.tsx +++ b/wiii-desktop/src/EmbedApp.tsx @@ -17,7 +17,7 @@ * 5. Render directly (no shell) * 6. sendReadySignal() to parent */ -import { useEffect, useState } from "react"; +import { lazy, Suspense, useEffect, useState } from "react"; import { ChatView } from "@/components/chat/ChatView"; import { ErrorBoundary } from "@/components/common/ErrorBoundary"; import { ToastContainer } from "@/components/common/Toast"; @@ -35,6 +35,11 @@ import { sendReadySignal, sendError, setParentOrigin } from "@/lib/embed-bridge" import { buildAuthUserFromJwt, toCompatibilitySettingsRole } from "@/lib/auth-user"; import type { EmbedConfig } from "@/lib/embed-auth"; +const PreviewPanel = lazy(async () => { + const mod = await import("@/components/layout/PreviewPanel"); + return { default: mod.PreviewPanel }; +}); + export default function EmbedApp() { const [embedConfig, setEmbedConfig] = useState(null); const [initError, setInitError] = useState(null); @@ -312,6 +317,9 @@ export default function EmbedApp() {
+ + +