github HKUDS/DeepTutor v1.2.2
DeepTutor-v1.2.2

12 hours ago

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.

  • BackendSkillService (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/skills with GET /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.ts provides a cached client-side API layer. UnifiedChatContext carries skills through the WebSocket payload.

Chat Input Performance Overhaul (#351, #360, #362)

Eliminated input lag in long conversations through deep state colocation:

  • ComposerInput — extracted from ChatComposer into its own memo'd component so frequent keystroke re-renders are isolated from the rest of the composer (capability panels, reference chips, tool menus). Exposes an imperative ComposerInputHandle (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.memo on config panelsQuizConfigPanel, MathAnimatorConfigPanel, ResearchConfigPanel, and VisualizeConfigPanel are now wrapped in React.memo; parent callbacks in ChatPage stabilized with useCallback.
  • @-mention helpers exportedshouldOpenAtPopup and stripTrailingAtMention moved to ComposerInput.tsx as 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_format heuristic detects the rejection in _openai_complete and _openai_stream; on match, the request is retried without response_format and the (binding, model) pair is cached.
  • OpenAI SDK path (executors.py) — _create_with_format_fallback wraps client.chat.completions.create, catching BadRequestError and applying the same retry + cache logic.
  • Runtime cache (capabilities.py) — disable_response_format_at_runtime / is_response_format_disabled_at_runtime record discovered incompatibilities in a module-level set so subsequent calls skip response_format upfront without paying the retry cost.
  • _answer_now.pystream_synthesis now checks supports_response_format before attaching response_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/version route — a Next.js ISR endpoint that queries api.github.com/repos/HKUDS/DeepTutor/releases/latest (hourly revalidation, optional GITHUB_TOKEN for rate-limit headroom) and returns the latest tag, name, URL, and publish date.
  • VersionBadge — compares NEXT_PUBLIC_APP_VERSION (injected at build time via next.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 — new APP_VERSION build 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_send wrapper — all ws.send_json calls go through a helper that catches WebSocketDisconnect / RuntimeError, preventing unhandled exceptions when the client drops mid-stream.
  • Graceful disconnect_handle_user_messages catches WebSocketDisconnect on receive_text and 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 ConnectError not retried (#353) — added httpx.ConnectError to the retry exception tuple in OpenAICompatibleEmbeddingAdapter, so transient connection failures during embedding are retried with exponential backoff instead of raising immediately.
  • RAG None-embedding hardeningCustomEmbedding._aget_query_embedding and _aget_text_embedding now raise a clear ValueError when the provider returns None instead of crashing later in similarity computation. The batch method _aget_text_embeddings now determines the fallback zero-vector dimension from sibling vectors or the configured dim, and raises if neither is available (preventing silent persistence of zero-length vectors).
  • KB delete crash on missing directoryKnowledgeBaseManager.delete_knowledge_base now resolves the path directly via self.base_dir / name and handles the case where the on-disk folder was already removed, cleaning up the orphaned kb_config.json entry instead of raising FileNotFoundError.
  • CLI parse_json_object whitespace — the function now strips leading/trailing whitespace before parsing, so trailing newlines from shell piping no longer cause json.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_object whitespace handling and invalid JSON (tests/cli/test_common.py).
  • Route params — 3 tests for the new firstParam utility (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

New Contributors

Full Changelog: v1.2.1...v1.2.2

Don't miss a new DeepTutor release

NewReleases is sending notifications on new releases.