The /api/v1/general-agent/settings endpoint returned HTTP 500 because
config/general_agent.json stores skills as a list while the response
model expected Optional[str]. Add _normalize_skills_list and apply it in
both get_general_agent_settings_api and get_bot_settings so list and
legacy comma-separated string inputs both yield a list output.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Allow the frontend to inject extra skills (merged with the bot's default
skills) for a single request. Used by the home/chat pages to enable
ragflow-loader when the user selects a knowledge base.
_extract_skills_to_robot used shutil.copytree(dirs_exist_ok=True), which
only overwrites same-named files. Renamed/removed files and __pycache__
in the robot project's skill copy were left behind, so after a skill
refactor (e.g. rag-retrieve adding create_error_response and dropping
call_rag_retrieve) stale copies ended up with mismatched imports and
failed to load.
Switch to rmtree + copytree so each managed skill directory is fully
replaced from source on every sync. Also ignore __pycache__/*.pyc so the
source's compiled artifacts are not propagated.
Refs: #59
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add ToolErrorRecoveryMiddleware as the outermost agent middleware so any
tool-call exception (notably MCP ToolException) is converted into a
ToolMessage with status="error" carrying the raw error text. The agent
can then loop once more and reply to the user in natural language about
what failed, instead of bubbling the exception up through agent.astream
and breaking the SSE response in routes/chat.py.
The recovery layer extracts the inner `text="..."` payload out of the MCP
TextContent repr when present, falling back to str(error) otherwise. It
deliberately re-raises asyncio.CancelledError so task cancellation still
propagates, and sits *outside* ToolMetricsMiddleware so the existing
status=error metric is still emitted before recovery kicks in.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Normalize OpenAI-style and LangChain standard image blocks into LangChain
standard content blocks so provider block_translators auto-convert for
either OpenAI or Anthropic. Flatten multimodal content to plain text when
persisting history and computing term embeddings.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The unused 'from sympy.printing.cxx import none' was accidentally added
by IDE autocomplete. sympy is not installed in the image, so importing
agent/deep_assistant.py raised ModuleNotFoundError and the API server
crash-looped on startup.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>