feat(TUI):收敛多模态输入链路:runtime 单入口提交 + session 归一化落地,清理 TUI 过渡职责并修复 schema/目录行为#337
Conversation
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
|
/code 修复review提到的问题,之后提高测试覆盖率,完成后应该在Fork仓库内开新PR而不是直接提交到目前的PR |
完成情况已完成你在触发评论里要求的两件事:修复 review 指出的问题,并补充测试覆盖;同时未直接提交到当前 PR 分支 关键修复
测试补充
验证
提交与 PR
|
…overage - require explicit @image: token and preserve whitespace when absorbing inline image refs - dedupe user-message rendering by run id instead of message content - rollback newly created session when asset save fails - make custom provider directory load read-only friendly when directory is missing - add/adjust tests for all updated behaviors Generated with [codeagent](https://github.com/qbox/codeagent) Co-authored-by: phantom5099 <245659304+phantom5099@users.noreply.github.com>
fix(tui,session,config): 修复 review 问题并补测试覆盖
|
@xgopilot 先分析当前PR中可能存在哪些风险点,然后给我一个让AI能够进行review的提示词 |
|
@xgopilot 请你作为严格的 Go 代码审查者,审查 PR #337(对比基线 origin/main...HEAD),仅输出“问题与风险”,不要输出表扬或泛泛总结。 审查目标:
请重点检查这些文件:
必须覆盖的风险维度:
输出格式要求:
|
@xgopilot 第一点提到附件路径未做工作目录边界约束,那如果后面想上传其他文件夹的文件该怎么办 |
@xgopilot 如果我想让@文件时只能选工作区内文件,使用+添加文件(你就当作是和@不一样的方式)就能任意添加其他路径上的文件,那么如果我目前直接改成非工作区路径就不允许上传,会影响以后任意添加其他路径上的文件功能的完成吗 |
@xgopilot 修复这些问题,完成后再提高测试覆盖率,然后提PR到Fork仓库(而非直接提交到当前PR) |
完成情况已按触发评论要求完成修复、补测并推送到 fork 分支,且已在 fork 仓库创建新 PR:
关键修复
测试与验证
未完成项
|
…olation Generated with [codeagent](https://github.com/qbox/codeagent) Co-authored-by: phantom5099 <245659304+phantom5099@users.noreply.github.com>
fix: harden multimodal input safety and event isolation
|
/code 提高测试覆盖率 |
Generated with [codeagent](https://github.com/qbox/codeagent) Co-authored-by: phantom5099 <245659304+phantom5099@users.noreply.github.com>
|
/review |
| mimeType := strings.TrimSpace(image.MimeType) | ||
|
|
||
| meta, err := p.saveImageAsset(ctx, session.ID, session.Workdir, path, mimeType) | ||
| if err != nil { |
There was a problem hiding this comment.
When a later image fails to save, previously saved images in the same Prepare call are not cleaned up for existing sessions. rollbackCreatedSession only removes newly created sessions, so this leaves orphan asset files/metadata that are never referenced by any message. Consider making the save loop atomic for existing sessions as well (e.g., track saved asset IDs and delete on failure, or validate all inputs before persisting).
| typeName, _ := normalized["type"].(string) | ||
| if strings.TrimSpace(strings.ToLower(typeName)) != "object" { | ||
| normalized["type"] = "object" | ||
| normalized["x-neocode-schema-downgraded"] = true |
There was a problem hiding this comment.
Setting x-neocode-schema-downgraded inside the tool schema can itself trigger request rejection on strict schema validators (some providers only allow known JSON-Schema keywords in function parameters). Consider tracking this downgrade flag outside the outbound schema payload (e.g., internal log/metric) to avoid introducing a non-standard key into parameters.
| if !a.shouldHandleRuntimeEvent(event) { | ||
| return false | ||
| } | ||
| if a.state.ActiveSessionID == "" { |
There was a problem hiding this comment.
ActiveSessionID is now initialized from any first runtime event. This can bind UI state to an invalid session ID in failure paths. Example: draft run with image input -> Prepare creates a new session -> image save fails -> session is rolled back/deleted -> asset_save_failed still carries that transient session ID -> this line sets ActiveSessionID to a deleted session. Subsequent UI flows operate on a non-existent session. Consider only adopting session ID from stable events (e.g. user_message/run-context after successful run start) or verifying existence before assignment.
| typeName, _ := normalized["type"].(string) | ||
| if strings.TrimSpace(strings.ToLower(typeName)) != "object" { | ||
| normalized["type"] = "object" | ||
| normalized["x-neocode-schema-downgraded"] = true |
There was a problem hiding this comment.
This injects x-neocode-schema-downgraded into the schema sent to upstream providers. That marker is internal-only metadata but is now part of the external tool schema payload, which can trip strict schema validators and reintroduce 400s in compatibility-sensitive providers. Suggest keeping downgrade telemetry out-of-band (logs/metrics) and not mutating outgoing schema with custom keys.
| return session, false, nil | ||
| } | ||
|
|
||
| session.Workdir = resolved |
There was a problem hiding this comment.
loadOrCreateSession 在已有会话上会先持久化 session.Workdir,后续若图片落盘失败(Prepare 返回 AssetSaveError)则不会回滚该变更。这样一次失败提交会产生“会话工作目录已变更但消息未提交”的部分提交状态。建议把 workdir 更新延后到附件全部保存成功之后,或在失败路径恢复原 workdir。
| mimeType := strings.TrimSpace(image.MimeType) | ||
|
|
||
| meta, err := p.saveImageAsset(ctx, session.ID, session.Workdir, path, mimeType) | ||
| if err != nil { |
There was a problem hiding this comment.
When Prepare processes multiple images for an existing session, a failure on a later image returns early but does not clean up already-saved assets from earlier iterations. This can leave orphaned asset files/metadata that are never referenced by any message. Consider tracking newly saved asset IDs and deleting them on error for non-new sessions too.
| return session, false, nil | ||
| } | ||
|
|
||
| session.Workdir = resolved |
There was a problem hiding this comment.
loadOrCreateSession persists session.Workdir before image normalization/saving completes. If Prepare later fails (e.g., invalid image path/MIME), input submission fails but session workdir still changes, creating a partial commit. Consider deferring this save until the full prepare succeeds, or restoring the previous workdir on failure.
| if !a.shouldHandleRuntimeEvent(event) { | ||
| return false | ||
| } | ||
| if a.state.ActiveSessionID == "" { |
There was a problem hiding this comment.
ActiveSessionID is set from the first accepted runtime event when currently empty. In failure paths where a temporary newly-created session is rolled back (e.g., asset save failure), UI can bind to a session ID that no longer exists. Consider only binding ActiveSessionID from events that guarantee persisted session state (or validate existence before assignment).
| typeName, _ := normalized["type"].(string) | ||
| if strings.TrimSpace(strings.ToLower(typeName)) != "object" { | ||
| normalized["type"] = "object" | ||
| normalized["x-neocode-schema-downgraded"] = true |
There was a problem hiding this comment.
Injecting x-neocode-schema-downgraded into tool parameters modifies the schema sent to upstream provider. Some strict validators reject unknown/custom keywords at schema root, so this may reintroduce request rejection risks. Safer option: keep compatibility markers in logs/telemetry instead of request payload schema.
|
/simplify |
处理结果已检查评论日志中的未解决 review 意见,并完成值得修复的问题落地。 已修复项
测试更新
验证
code-simplifier 执行情况按要求调用了 code-simplifier 子代理 2 次,但均因上游 提交与推送
|
Generated with [codeagent](https://github.com/qbox/codeagent) Co-authored-by: phantom5099 <245659304+phantom5099@users.noreply.github.com>
|
@xgopilot 仔细审查代码变更。你的核心任务是寻找内存状态污染、并发阻塞、Context 泄漏以及未处理的文件 I/O 异常。不要在简单的拼写或常规语法上浪费精力,直击架构重构带来的副作用。 Review Checkpoints (重点审查清单)
Output Format请以 Markdown 格式输出你的审查报告。结构如下:
如果你在某一项中未发现问题,请明确回复“针对 [第 X 项],代码已安全处理”。 |
🛑 致命隐患 (Blockers)
|
@xgopilot 修复这些问题 |
已完成已按触发评论要求修复 3 个问题并推送到当前工作分支:
测试已补充并更新测试:
已执行: 推送与 PR
未做事项
|
… cancel-safe asset flow Generated with [codeagent](https://github.com/qbox/codeagent) Co-authored-by: phantom5099 <245659304+phantom5099@users.noreply.github.com>
…99/neo-code into fork-pr-337-1776410957
fix(runtime/session/config): resolve submit-event blocking and cancellation gaps
问题背景
当前代码在“图片输入 -> 归一化 -> 发送”链路上存在职责分散和过渡残留:
TUI 承担了过多输入校验与能力判断,runtime 缺少统一提交入口,session 侧没有独立归一化组件,导致边界不够清晰且行为不一致。
同时,provider/tool schema 还存在会触发 400 的兼容性细节问题,providers 目录加载语义也与预期不一致。
本次修改(做了什么 & 为什么)
Submitinternal/runtime/runtime.go、internal/runtime/input_prepare.go、internal/tui/services/runtime_service.go、internal/tui/core/app/update.goSubmit(ctx, PrepareInput),由 runtime 统一串联PrepareUserInput + Run;TUI 改为调用RunSubmitCmd。internal/session/input_preparer.go,并在internal/app/bootstrap.go注入SetUserInputPreparer(...)AssetSaveError)。internal/runtime/events.go、internal/runtime/input_prepare.goinput_normalized、asset_saved、asset_save_failed及 payload;失败场景统一事件化。internal/tui/core/app/input_features.go、internal/tui/core/app/app.go、internal/tui/core/app/update.gointernal/provider/openaicompat/chatcompletions/request.go、internal/provider/openaicompat/chatcompletions/provider_test.gointernal/tools/todo/write.go、internal/tools/todo/write_test.goartifacts数组补齐items。internal/config/provider_loader.go、internal/config/loader_test.goloadCustomProviders改为先MkdirAll(providersDir)再ReadDir;同步修正测试期望。行为变化
测试
internal/session/input_preparer_test.gointernal/runtime/input_prepare_test.gointernal/provider/openaicompat/chatcompletions/provider_test.gointernal/tools/todo/write_test.gointernal/config/loader_test.goSubmit新接口go test ./...(通过)