fix(agent): 审批暂停与图片参数异常恢复
This commit is contained in:
@@ -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 判断本轮是否已在会话里完整结束:本轮已有成功的助手回复,
|
||||
// 没有挂着的任务或审批,且会话最后一条消息是不带工具调用的正常结束回复。
|
||||
// 只能用于本轮自己落库的快照:运行时的事件按顺序提交,助手回复计数增长之前,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user