fix(agent): 审批暂停与图片参数异常恢复

This commit is contained in:
ddcat666
2026-10-13 21:55:08 +08:00
parent c68645931c
commit 1f2f4da42b
4 changed files with 49 additions and 12 deletions

View File

@@ -356,18 +356,21 @@ func (s *Service) runCloudAgentPiSession(ctx context.Context, userID, runID stri
},
})
if err != nil {
// 已经因审批暂停的会话,收尾阶段再报错不影响运行:状态停在等审批,
// 由审批结论接管。把它当失败处理会让用户还没来得及审批就整轮中断。
if pausedForApproval.Load() && !errors.Is(err, context.Canceled) {
log.Printf("[Agent] session error after approval pause ignored run=%s: %v", runID, err)
// The runtime can abort a prompt before the in-process pause flag is set
// (for example when the SDK races into a second model request). The durable
// run state is authoritative: an approval/user wait owns the next action
// and must never be converted into a terminal runtime failure.
paused := pausedForApproval.Load() || s.cloudAgentRunPausedForInput(userID, runID)
if paused {
log.Printf("[Agent] session error after durable pause ignored run=%s: %v", runID, err)
return nil
}
return err
}
// 本轮因审批暂停:运行由审批决定接管,不能在这里完成。用户可能在会话退出前
// 就已批准,此时状态已回到 running,若继续会把未恢复的运行误标为完成。
if pausedForApproval.Load() {
// The same durable check covers a clean runtime exit after an asynchronous
// abort, when the local atomic flag was not observed by this coordinator.
if pausedForApproval.Load() || s.cloudAgentRunPausedForInput(userID, runID) {
return nil
}
@@ -382,6 +385,17 @@ func (s *Service) runCloudAgentPiSession(ctx context.Context, userID, runID stri
return s.completeCloudAgentPiRun(userID, runID)
}
func (s *Service) cloudAgentRunPausedForInput(userID, runID string) bool {
if s == nil || s.repo == nil || userID == "" || runID == "" {
return false
}
run, err := s.repo.CloudAgent(userID, runID)
if err != nil {
return false
}
return run.Status == "waiting_approval" || run.Status == "waiting_user"
}
// cloudAgentPiTurnSettled 判断本轮是否已在会话里完整结束:本轮已有成功的助手回复,
// 没有挂着的任务或审批,且会话最后一条消息是不带工具调用的正常结束回复。
// 只能用于本轮自己落库的快照:运行时的事件按顺序提交,助手回复计数增长之前,

View File

@@ -262,6 +262,14 @@ func imageParameterValue(p protocol.ImageParameter, value any) any {
return value
}
func imageParameterAdmissionError(field, issue, message string) error {
return NewModelError(ErrCodeModelCapabilityNotSupported, message).WithDetails(map[string]any{
"field": field,
"issue": issue,
"message": message,
})
}
func resolveImageParameterValues(parameters []protocol.ImageParameter, values map[string]any) (map[string]any, error) {
allowed := map[string]protocol.ImageParameter{}
result := imageParameterDefaults(parameters)
@@ -273,11 +281,11 @@ func resolveImageParameterValues(parameters []protocol.ImageParameter, values ma
for name, value := range values {
p, ok := allowed[name]
if !ok {
return nil, ModelCapabilityNotSupported("当前图片模型未启用参数:" + name)
return nil, imageParameterAdmissionError(name, "unsupported_value", "当前图片模型未启用参数:"+name)
}
value = imageParameterValue(p, value)
if !p.Accepts(value) {
return nil, ModelCapabilityNotSupported("图片参数超出支持范围:" + p.Label)
return nil, imageParameterAdmissionError(p.Name, "unsupported_value", "图片参数超出支持范围:"+p.Label)
}
if value != nil && value != "" {
result[name] = value
@@ -285,7 +293,7 @@ func resolveImageParameterValues(parameters []protocol.ImageParameter, values ma
}
for _, p := range parameters {
if p.IsEnabled() && p.Required && (result[p.Name] == nil || result[p.Name] == "") {
return nil, ModelCapabilityNotSupported("请填写图片参数:" + p.Label)
return nil, imageParameterAdmissionError(p.Name, "required", "请填写图片参数:"+p.Label)
}
}
return result, nil

View File

@@ -28,7 +28,7 @@ func newModelCapabilityArgumentError(field, issue string, cause error) error {
func modelCapabilityMismatchError(match CapabilityMatch) error {
details := map[string]any{"capabilityIssues": match.Issues}
for _, issue := range match.Issues {
if issue.Kind != "option" {
if issue.Kind != "option" && issue.Kind != "image_parameter" {
continue
}
field := issue.Name

View File

@@ -8,6 +8,7 @@ package app
import (
"encoding/json"
"errors"
"fmt"
"math"
"strconv"
@@ -325,7 +326,21 @@ func MatchCapability(spec CapabilitySpec, intent ModelRequestIntent) CapabilityM
}
}
if _, err := resolveImageParameterValues(parameters, values); err != nil {
addIssue("image_parameter", "imageParameters", err.Error())
var modelErr *ModelError
if errors.As(err, &modelErr) && modelErr != nil {
field, _ := modelErr.Details["field"].(string)
message, _ := modelErr.Details["message"].(string)
if field != "" {
if message == "" {
message = modelErr.Error()
}
addIssue("image_parameter", field, message)
} else {
addIssue("image_parameter", "imageParameters", err.Error())
}
} else {
addIssue("image_parameter", "imageParameters", err.Error())
}
}
}
if normalizeCapability(intent.Capability) != normalizeCapability(spec.Capability) {