DeepTutor v1.2.2 Release Notes
Release Date: 2026.04.22
Highlights
User-Authored Skills System
Introduced a full Skills subsystem that lets users create, edit, and activate custom SKILL.md files from the web UI. Each skill lives under data/user/workspace/skills/<name>/SKILL.md with YAML frontmatter (name, description, optional triggers) and a Markdown body that is injected verbatim into the chat system prompt when active.
- Backend —
SkillService(deeptutor/services/skill/service.py) provides CRUD + listing + selection with strict name validation (^[a-z0-9][a-z0-9-]{0,63}$), frontmatter parsing, and directory-level isolation. REST API mounted at/api/v1/skillswithGET /list,GET /{name},POST /create,PUT /{name},DELETE /{name}. - Frontend — a new Skills tab in the Knowledge management page (
web/app/(utility)/knowledge/page.tsx) with inline SKILL.md editor, and a skill picker menu in the chat composer. Skills can be toggled individually or set to auto mode (sends["auto"]to the backend).web/lib/skills-api.tsprovides a cached client-side API layer.UnifiedChatContextcarriesskillsthrough the WebSocket payload.
Chat Input Performance Overhaul (#351, #360, #362)
Eliminated input lag in long conversations through deep state colocation:
ComposerInput— extracted fromChatComposerinto its ownmemo'd component so frequent keystroke re-renders are isolated from the rest of the composer (capability panels, reference chips, tool menus). Exposes an imperativeComposerInputHandle(clear(),getValue()) to avoid lifting input state.SimpleComposerInput— a lightweight variant for the TutorBot chat page that strips @-mention and capability overhead entirely, fixing residual lag reported after the initial refactor.React.memoon config panels —QuizConfigPanel,MathAnimatorConfigPanel,ResearchConfigPanel, andVisualizeConfigPanelare now wrapped inReact.memo; parent callbacks inChatPagestabilized withuseCallback.- @-mention helpers exported —
shouldOpenAtPopupandstripTrailingAtMentionmoved toComposerInput.tsxas named exports for cross-component reuse.
Auto-Fallback for response_format Rejection
Extended the v1.2.1 static supports_response_format guard with a runtime auto-recovery path. When a provider unexpectedly returns HTTP 400 for response_format={"type":"json_object"} (common with LM Studio + Gemma/Qwen-style models), both LLM execution paths now detect the error, drop the field, and retry once:
- aiohttp path (
cloud_provider.py) —_looks_like_unsupported_response_formatheuristic detects the rejection in_openai_completeand_openai_stream; on match, the request is retried withoutresponse_formatand the (binding, model) pair is cached. - OpenAI SDK path (
executors.py) —_create_with_format_fallbackwrapsclient.chat.completions.create, catchingBadRequestErrorand applying the same retry + cache logic. - Runtime cache (
capabilities.py) —disable_response_format_at_runtime/is_response_format_disabled_at_runtimerecord discovered incompatibilities in a module-levelsetso subsequent calls skipresponse_formatupfront without paying the retry cost. _answer_now.py—stream_synthesisnow checkssupports_response_formatbefore attachingresponse_format, preventing the 400 in the first place for fast-path answer flows.
LAN / Remote Access Fix (#340)
When another machine on the local network opens the web UI, the build-time NEXT_PUBLIC_API_BASE (typically http://localhost:8001) would resolve to the remote browser's own loopback instead of the server. A new resolveBase() helper in web/lib/api.ts detects this mismatch and swaps the hostname for window.location.hostname at runtime, so apiUrl() and wsUrl() reach the correct backend regardless of which machine opened the page.
Sidebar Version Badge & GitHub Link
The sidebar now displays the current build version alongside a status indicator:
/api/versionroute — a Next.js ISR endpoint that queriesapi.github.com/repos/HKUDS/DeepTutor/releases/latest(hourly revalidation, optionalGITHUB_TOKENfor rate-limit headroom) and returns the latest tag, name, URL, and publish date.VersionBadge— comparesNEXT_PUBLIC_APP_VERSION(injected at build time vianext.config.js/git describe --tags) against the fetched latest release. Shows a green dot when up-to-date, amber when outdated, and neutral when unknown. Clicking navigates to the release page.Dockerfile— newAPP_VERSIONbuild arg piped into the Next.js env so Docker-based deployments also get accurate version display.- GitHub icon — a direct link to the repository added to both collapsed and expanded sidebar states.
Deep Solve Image Attachment Support
DeepSolveCapability now extracts the first image attachment (data-URI or URL) from context.attachments via a new _first_image_url helper and passes it to the planner and solver agents. Internally, PlannerAgent and SolverAgent were refactored to use the Attachment dataclass through BaseAgent's unified multimodal pipeline instead of manually constructing multimodal message arrays — removing two identical _build_multimodal_messages helper functions. BaseAgent.stream_llm also gained logic to construct messages from system_prompt + user_prompt when attachments are provided without explicit messages, and logs when images are stripped for non-vision models.
Agentic Chat Pipeline Attachment Passthrough
The _stage_responding and _stage_acting methods in AgenticChatPipeline now call _prepare_messages_with_attachments after building messages, ensuring that user-uploaded images are forwarded to the LLM in every chat stage — not just the initial thinking stage.
TutorBot WebSocket Resilience (#354)
Hardened the /api/v1/tutorbot/{bot_id}/ws endpoint:
- Auto-start — if the bot is configured but not running when a WebSocket connects, the endpoint now calls
mgr.start_bot()automatically instead of immediately closing with 4004. Unknown bot IDs still receive a JSON error payload before close. _safe_sendwrapper — allws.send_jsoncalls go through a helper that catchesWebSocketDisconnect/RuntimeError, preventing unhandled exceptions when the client drops mid-stream.- Graceful disconnect —
_handle_user_messagescatchesWebSocketDisconnectonreceive_textand breaks cleanly.
Settings Page: API Key Masking (#355)
The API Key field on the Settings page is now rendered as type="password" by default with an Eye / EyeOff toggle button. The visibility state resets when switching between services or profiles.
Book Library UI Overhaul
Replaced the minimal "Select a book" placeholder with a full BookLibrary component (web/app/(workspace)/book/components/BookLibrary.tsx) featuring search, status-filtered cards with chapter/page counts, creation and deletion actions, and status badges (Draft / Outline / Compiling / Ready). BookSidebar was simplified to a single-book reader view with a "← All books" back button, and the book page now conditionally shows either the library or the sidebar based on the current view.
Visualization Fullscreen Mode
SVG, Mermaid, and ChartJS visualizations now have a Maximize button (top-right) that opens the graphic in a fullscreen overlay with Escape-to-close. HTML iframe visualizations are excluded since they already provide their own "Open in new tab" affordance.
Bug Fixes
- Embedding adapter
ConnectErrornot retried (#353) — addedhttpx.ConnectErrorto the retry exception tuple inOpenAICompatibleEmbeddingAdapter, so transient connection failures during embedding are retried with exponential backoff instead of raising immediately. - RAG
None-embedding hardening —CustomEmbedding._aget_query_embeddingand_aget_text_embeddingnow raise a clearValueErrorwhen the provider returnsNoneinstead of crashing later in similarity computation. The batch method_aget_text_embeddingsnow determines the fallback zero-vector dimension from sibling vectors or the configureddim, and raises if neither is available (preventing silent persistence of zero-length vectors). - KB delete crash on missing directory —
KnowledgeBaseManager.delete_knowledge_basenow resolves the path directly viaself.base_dir / nameand handles the case where the on-disk folder was already removed, cleaning up the orphanedkb_config.jsonentry instead of raisingFileNotFoundError. - CLI
parse_json_objectwhitespace — the function now strips leading/trailing whitespace before parsing, so trailing newlines from shell piping no longer causejson.JSONDecodeError.
Test Suite Expansion
- TutorBot WebSocket — 2 new tests covering auto-start of stopped-but-configured bots and JSON error payload for unknown bot IDs (
tests/api/test_tutorbot_router.py). - CLI helpers — 2 tests for
parse_json_objectwhitespace handling and invalid JSON (tests/cli/test_common.py). - Route params — 3 tests for the new
firstParamutility (web/tests/route-params.test.ts). - API resolve base — tests for the LAN hostname swap logic (
web/tests/api-resolve-base.test.ts).
What's Changed
- fix: add ConnectError to embedding retry exceptions by @S-A-D-4 in #353
- feat: Hide the API key on settings page by @srinivasrk in #355
- fix : Tutorbot websocket resilience and CLI config parsing edge case by @srinivasrk in #354
- Fix #296: [Bug]:LLM 0, EMBEDDING 0, SEARCH 0 by @JiwaniZakir in #340
- perf(web): decouple chat input state to resolve lag in long conversat… by @jiakeboge in #360
- Perf/optimize chat input by @jiakeboge in #362
New Contributors
- @S-A-D-4 made their first contribution in #353
- @JiwaniZakir made their first contribution in #340
Full Changelog: v1.2.1...v1.2.2