Skip to content

fix: 修复上下文持久化与 Dashboard 状态问题#9279

Open
liuwanwan1 wants to merge 1 commit into
AstrBotDevs:masterfrom
liuwanwan1:codex/fix-priority-reliability
Open

fix: 修复上下文持久化与 Dashboard 状态问题#9279
liuwanwan1 wants to merge 1 commit into
AstrBotDevs:masterfrom
liuwanwan1:codex/fix-priority-reliability

Conversation

@liuwanwan1

@liuwanwan1 liuwanwan1 commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Closes #9278

变更说明

本 PR 优先处理一组共享可靠性问题:

  • 将 Agent 的模型请求工作上下文与持久化历史分离,避免上下文裁剪后覆盖完整对话历史。
  • OpenAI-compatible Provider 上下文超限时按完整会话轮次删除,避免破坏 assistant(tool_calls)tool 消息配对。
  • 修复带版本号或 . 的 Skill 名称生成 ZIP 时文件名被截断的问题。
  • 为 Dashboard API 增加未处理异常的统一 JSON 响应;服务端保留完整堆栈,客户端不暴露内部异常详情。
  • 删除未保存的 Provider 草稿时仅清理前端状态,不再错误调用后端。
  • 调整上下文上限优先级:用户显式配置优先于外部模型元数据。
  • 修正 Windows file:// URI 测试,使断言符合本地文件系统路径语义。

关联问题

验证结果

  • uv run pytest tests/test_openai_source.py tests/agent/test_tool_loop_runner_history.py tests/unit/test_skills_service.py tests/test_fastapi_v1_dashboard.py -q
    • 138 passed
  • Dashboard Node tests
    • 36 passed
  • pnpm typecheck
    • passed
  • uv run ruff format .
    • passed
  • uv run ruff check .
    • passed

完整测试说明

Windows 本地执行完整 pytest 时,在测试收集阶段触发原生 access violation,堆栈涉及 APScheduler 后台线程,尚未进入本 PR 修改的测试。上述定向测试均已通过,完整 Linux CI 结果以 GitHub Actions 为准。

Summary by Sourcery

Separate agent request context trimming from persistent conversation history, improve OpenAI-compatible context trimming behavior, and harden dashboard and provider management reliability.

Bug Fixes:

  • Ensure OpenAI-compatible context trimming removes the oldest complete conversation turn, preserving assistant tool_calls and tool message pairing.
  • Prevent agent persistent conversation history from being overwritten by trimmed request contexts so full histories are stored correctly.
  • Fix skill archive creation so versioned or dotted skill names produce ZIP filenames without truncation.
  • Avoid backend delete calls when removing unsaved provider source drafts, and properly clear related frontend state only.
  • Adjust provider context limit resolution so explicit user configuration takes precedence over external model metadata.
  • Normalize Windows file:// URI handling to align with local path semantics in tests.
  • Return a consistent JSON error payload for unhandled dashboard API exceptions while logging full server-side details.

Enhancements:

  • Introduce persistent message tracking in the tool loop agent runner and use it when emitting final agent events for storage.
  • Record whether provider sources originate from persisted IDs to drive delete behavior and configuration reloads more accurately.

Tests:

  • Add tests covering OpenAI provider context trimming around tool call turns.
  • Add tests ensuring persistent agent history is decoupled from trimmed request contexts.
  • Add tests verifying skill archive filenames preserve versioned skill names.
  • Add tests validating unhandled dashboard exceptions yield safe JSON responses.
  • Update Windows file URI tests to assert normalized path behavior.

@dosubot dosubot Bot added size:L This PR changes 100-499 lines, ignoring generated files. area:core The bug / feature is about astrbot's core, backend area:provider The bug / feature is about AI Provider, Models, LLM Agent, LLM Agent Runner. area:webui The bug / feature is about webui(dashboard) of astrbot. labels Jul 14, 2026

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've reviewed your changes and they look great!


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces several enhancements and bug fixes across the agent runner, provider, and dashboard modules. Key updates include preserving complete conversation history for persistence in ToolLoopAgentRunner, refactoring pop_record in Provider to correctly remove the oldest complete conversation turn, adding a global exception handler to the dashboard API, and fixing a bug in skill archive naming when version numbers contain dots. The review feedback correctly identifies a potential AttributeError in pop_record if the context contains Message objects instead of dictionaries, suggesting a robust helper function to handle both types.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +180 to +206
first_message_index = next(
(
index
for index, record in enumerate(context)
if record.get("role") != "system"
),
None,
)
if first_message_index is None:
return

next_user_index = next(
(
index
for index in range(first_message_index + 1, len(context))
if context[index].get("role") == "user"
),
len(context),
)
context[:] = [
record
for index, record in enumerate(context)
if not (
first_message_index <= index < next_user_index
and record.get("role") != "system"
)
]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

pop_record 中,如果 context 列表中包含的是 Message 对象(例如 Pydantic 模型)而不是原始的 dict,直接调用 record.get("role") 会抛出 AttributeError。建议实现一个辅助函数 _get_role,兼容处理 dict 和具有 role 属性的对象,以提高代码的健壮性。

        def _get_role(msg):
            if isinstance(msg, dict):
                return msg.get("role")
            return getattr(msg, "role", None)

        first_message_index = next(
            (
                index
                for index, record in enumerate(context)
                if _get_role(record) != "system"
            ),
            None,
        )
        if first_message_index is None:
            return

        next_user_index = next(
            (
                index
                for index in range(first_message_index + 1, len(context))
                if _get_role(context[index]) == "user"
            ),
            len(context),
        )
        context[:] = [
            record
            for index, record in enumerate(context)
            if not (
                first_message_index < index < next_user_index
                and _get_role(record) != "system"
            )
        ]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:core The bug / feature is about astrbot's core, backend area:provider The bug / feature is about AI Provider, Models, LLM Agent, LLM Agent Runner. area:webui The bug / feature is about webui(dashboard) of astrbot. size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] 修复上下文持久化与 Dashboard 状态的共享可靠性问题

1 participant