* fix(qqofficial): fix streaming message delivery for C2C
* fix(qqofficial): rewrite send_streaming for C2C vs non-C2C split
* fix(qqofficial): add _extract_response_message_id for safe id extraction
* fix(qqofficial): flush stream segment on tool-call break signal
* fix(qqofficial): downgrade rich-media to non-stream send in C2C
* fix(qqofficial): auto-append \n to final stream chunk (state=10)
* fix(qqofficial): propagate stream param to all _send_with_markdown_fallback call sites
* fix(qqofficial): retry on STREAM_MARKDOWN_NEWLINE_ERROR with newline fix
* fix(qqofficial): handle None/non-dict response in post_c2c_message gracefully
* fix(qqofficial): remove msg_id from video/file media payloads in send_by_session
QQ API rejects msg_id on proactive media (video/file, msg_type=7) messages
sent via the tool-call path, returning "请求参数msg_id无效或越权". The
msg_id passive-reply credential is consumed by the first send and cannot be
reused for subsequent media uploads in the same session.
Remove msg_id from the payload after setting msg_type=7 for video and file
sends, for both FRIEND_MESSAGE (C2C) and GROUP_MESSAGE paths.
* fix(qqofficial): replace deprecated get_event_loop() with get_running_loop()
asyncio.get_event_loop() is deprecated since Python 3.10 and raises a
DeprecationWarning (or errors) when called from inside a running coroutine
without a current event loop set on the thread. Replace both call-sites
in the streaming throttle logic with asyncio.get_running_loop(), which is
the correct API to use inside an already-running async context.
Co-Authored-By: Claude Sonnet <noreply@anthropic.com>
---------
Co-authored-by: 2ndelement <2ndelement@users.noreply.github.com>
Co-authored-by: Claude Sonnet <noreply@anthropic.com>
* feat: add video message support and enhance message type descriptions in SendMessageToUserTool
* feat: add error handling for disabled sandbox runtime in get_booter function
- Add audio data validation in MiniMax TTS get_audio() method to detect empty responses
- Validate generated audio file size in TTSProvider.test() to ensure valid output
- Provide detailed error messages guiding users to check group_id configuration
- Auto-cleanup test audio files after validation
- Fixes issue where 0KB audio files would pass TTS detection when group_id is not configured
* feat(extension): add PluginSortControl reusable component for sorting
* i18n: add i18n keys for plugin sorting and filtering features
* feat(extension): add sorting and status filtering for installed plugins
Backend changes (plugin.py):
- Add _resolve_plugin_dir method to resolve plugin directory path
- Add _get_plugin_installed_at method to get installation time from file mtime
- Add installed_at field to plugin API response
Frontend changes (InstalledPluginsTab.vue):
- Import PluginSortControl component
- Add status filter toggle (all/enabled/disabled) using v-btn-toggle
- Integrate PluginSortControl for sorting options
- Add toolbar layout with actions and controls sections
Frontend changes (MarketPluginsTab.vue):
- Import PluginSortControl component
- Replace v-select + v-btn combination with unified PluginSortControl
Frontend changes (useExtensionPage.js):
- Add installedStatusFilter, installedSortBy, installedSortOrder refs
- Add installedSortItems and installedSortUsesOrder computed properties
- Add sortInstalledPlugins function with multi-criteria support
- Support sorting by install time, name, author, and update status
- Add status filtering in filteredPlugins computed property
- Disable default table sorting by setting sortable: false
* test: add tests for installed_at field in plugin API
- Assert all plugins have installed_at field in get_plugins response
- Assert installed_at is not null after plugin installation
* fix(extension): add explicit fallbacks for installed plugin sort comparisons
* i18n(extension): rename install time label to last modified
* fix(extension): cache installed_at parsing and validate timestamp format in tests
* test(dashboard): strengthen installed_at coverage for plugin API
* fix(provider): handle MiniMax ThinkingBlock when max_tokens reached
Fixes#5912
Problem: MiniMax API returns ThinkingBlock when stop_reason='max_tokens',
but AstrBot throws 'completion 无法解析' exception because both
completion_text and tools_call_args are empty.
Root cause: The validation logic didn't consider ThinkingBlock
(reasoning_content) as valid content.
Fix: When completion_text and tools_call_args are empty but
reasoning_content is present, treat it as valid instead of throwing
exception. This happens when the model thinks but runs out of tokens
before generating the actual response.
Impact: MiniMax models now work correctly when responses are truncated
due to max_tokens limit.
* refactor: address review feedback
1. Use getattr for safe stop_reason access (prevent AttributeError)
2. Use ValueError instead of generic Exception for better error handling
Thanks @gemini-code-assist and @sourcery-ai for the review!
* refactor: flatten nested if/else with guard clause
Address Gemini Code Assist feedback:
- Use guard clause for early return
- Flattened nested conditional for better readability
Logic unchanged, just cleaner code structure.
* fix(provider): improve logging for ThinkingBlock completions in ProviderAnthropic
---------
Co-authored-by: Soulter <905617992@qq.com>
* fix: prevent crash on malformed MCP server config (#5666)
* fix: prevent crash on malformed MCP server config (#5666)
* fix: validate MCP connection before persisting server config
* fix: guard mcpServers type before iterating server list
* refactor: use typed empty-config error and extract MCP rollback helper
* fix: translate error messages and comments to English for consistency
---------
Co-authored-by: Soulter <905617992@qq.com>
* fix: apply reply_with_quote and reply_with_mention to image-only responses
* fix: restrict reply_with_quote and reply_with_mention to plain-text/image chains
* feat(skills): add batch upload functionality for multiple skill ZIP files
- Implemented a new endpoint for batch uploading skills.
- Enhanced the SkillsSection component to support multiple file selection and drag-and-drop functionality.
- Updated localization files for new upload features and messages.
- Added tests to validate batch upload behavior and error handling.
* feat(skills): improve batch upload handling and enhance accessibility for dropzone
* feat(skills): enhance batch upload process and improve UI for better user experience
* feat(skills): enhance skills upload dialog layout and styling for improved usability
* feat(skills): update upload dialog description styling for better visibility and usability
* feat(skills): improve upload dialog button styling and layout for enhanced usability
* feat(skills): refine upload dialog text for clarity and consistency
* feat(skills): enhance batch upload functionality by ignoring __MACOSX entries and improving upload dialog styling
* feat(skills): refactor upload dialog and button styles for improved consistency and usability
---------
Co-authored-by: whatevertogo <whatevertogo@users.noreply.github.com>
* refactor: bundled webui static files into wheel and replace astrbot cli log with English
- Translated and standardized log messages in cmd_conf.py for better clarity.
- Updated initialization logic in cmd_init.py to provide clearer user prompts and error handling.
- Improved plugin management commands in cmd_plug.py with consistent language and error messages.
- Enhanced run command in cmd_run.py with clearer status messages and error handling.
- Updated utility functions in basic.py and plugin.py to improve readability and maintainability.
- Added version comparison logic in version_comparator.py with clearer comments.
- Enhanced logging configuration in log.py to suppress noisy loggers.
- Updated the updater logic in updator.py to provide clearer error messages for users.
- Improved IO utility functions in io.py to handle dashboard versioning more effectively.
- Enhanced dashboard server logic in server.py to prioritize bundled assets and improve user feedback.
- Updated pyproject.toml to include bundled dashboard assets and custom build hooks.
- Added a custom build script (hatch_build.py) to automate dashboard builds during package creation.
* refactor: improve exception messages and formatting in CLI command validation
* perf: change npm install to npm ci for consistent dependency installation
* fix
* fix: resolve MCP tools race condition causing 'completion 无法解析' error
- Wait for MCP client initialization to complete before accepting requests
- Add Future-based synchronization in init_mcp_clients()
- Prevent tool_calls from being rejected due to empty func_list
- Improve error logging for MCP initialization failures
Fixes race condition where AI attempts to call MCP tools before they are
registered, resulting in 'API 返回的 completion 无法解析' exceptions.
The issue occurred because:
1. MCP clients were initialized asynchronously without waiting
2. System accepted user requests immediately after startup
3. AI received empty tool list and attempted to call non-existent tools
4. Tool matching failed, causing parsing errors
This fix ensures all MCP tools are loaded before the system processes
any requests that might use them.
* perf: add timeout and better error handling for MCP initialization
- Add 20-second total timeout to prevent slow MCP servers from blocking startup
- Show detailed configuration info when MCP initialization fails
- List all failed services in a summary warning
- Gracefully handle timeout by using already-completed services
This ensures that even if some MCP servers are slow or unreachable,
the system will start within a reasonable time and provide clear
feedback about which services failed and why.
* refactor: simplify MCP init orchestration and improve log security
- Replace Future-based sync with asyncio.wait + name→task mapping
- Explicitly cancel timed-out tasks after 20s timeout
- Downgrade sensitive config details (command/args/URL) to debug level
- Move urllib.parse import to top-level
* fix: prevent initialized MCP clients from being cleaned up on timeout
- Do not cancel pending tasks on timeout; let them continue running
in the background waiting for the termination signal (event.set()),
so successfully initialized services remain available
- Track initialization state with a flag to distinguish init failures
from post-init cancellations in _init_mcp_client_task_wrapper
* fix: restore task cancellation on timeout per review feedback
Pending tasks in asyncio.wait are tasks that have NOT completed
initialization within 20s, so cancelling them is safe and correct.
* fix: separate init signal from client lifetime in MCP task wrapper
The previous design awaited task completion, but tasks only finish
on shutdown (after event.wait()), causing asyncio.wait to always
hit the 20s timeout and cancel all clients.
Fix: introduce a dedicated ready_event that is set immediately after
_init_mcp_client completes. init_mcp_clients now waits only for
ready_event (with 20s timeout), while the long-lived client task
continues running in the background until shutdown_event is set.
This ensures startup returns promptly once clients are ready.
* security: redact sensitive MCP config from debug logs
Only log executable name and argument count instead of full
command/args to avoid leaking tokens or credentials even at
debug level.
* refactor: use McpClientInfo dataclass and MCP_INIT_TIMEOUT constant
- Extract MCP_INIT_TIMEOUT = 20.0 as a named module-level constant
- Replace tuple-based client_info with _McpClientInfo dataclass to
eliminate index-based access and improve readability
- Remove _wait_ready helper; use asyncio.create_task(event.wait()) directly
- Await cancelled tasks after timeout to prevent lingering background
tasks and unobserved exceptions
* fix: handle CancelledError and clean up wait_tasks on timeout
- Catch asyncio.CancelledError separately in _init_mcp_client_task_wrapper
so ready_event.set() is always called (Python 3.8+ CancelledError
inherits BaseException, not Exception)
- Cancel and await lingering wait_tasks after timeout to prevent
them from hanging indefinitely when ready_event is never set
* fix: align enable_mcp_server with new wrapper API and fix security/config issues
- Fix enable_mcp_server to pass shutdown_event + ready_event instead of
ready_future, matching _init_mcp_client_task_wrapper's current signature
- Cancel and await init_task on timeout; clean up mcp_client_event on failure
- Read MCP_INIT_TIMEOUT from env var ASTRBOT_MCP_INIT_TIMEOUT (default 20s)
so operators can tune it without code changes
- Strip userinfo from URL in debug log (use hostname+port only, not netloc)
to avoid leaking credentials embedded in URLs
* refactor: register mcp_client_event only after successful init in enable_mcp_server
Move self.mcp_client_event[name] assignment to after initialization
succeeds, so callers never observe a stale event for a failed client.
* fix: harden MCP init state handling and timeout parsing
* fix: improve MCP timeout and post-init error observability
* refactor: simplify MCP init lifecycle orchestration
* refactor: simplify MCP init flow and cap timeout values
* fix: refine mcp timeout handling and lifecycle task tracking
* fix: harden mcp shutdown and timeout source logging
* refactor: simplify mcp runtime registry and timeout flow
* fix: keep mcp init summary return contract
* refactor: streamline mcp lifecycle and init errors
* refactor: unify mcp lifecycle wait handling
* refactor: simplify mcp runtime ownership and timeout resolution
* fix: harden mcp shutdown waiting and startup signaling
* refactor: streamline mcp lifecycle and shutdown errors
* refactor: harden mcp runtime access and shutdown
* fix: ensure mcp client cleanup and clarify views
* refactor: cache mcp client view and guard startup
* refactor: simplify mcp init cleanup and runtime lock
* refactor: reduce mcp runtime duplication
* refactor: reuse mcp cleanup and client view
---------
Co-authored-by: idiotsj <idiotsj@users.noreply.github.com>
Co-authored-by: 邹永赫 <1259085392@qq.com>