Enhance checkpoint handling and error management in forwarder

- Added flushing of assistant text during provider completion to ensure no output is lost on transport failure.
- Updated checkpoint blob synchronization tests to validate behavior under various conditions, including terminal and non-terminal states.
- Introduced new functions for managing checkpoint terminal actions, improving clarity and maintainability of the code.
- Implemented additional tests for imported blob handling and conversation state restoration, ensuring robustness in data integrity across operations.
This commit is contained in:
leookun
2026-08-10 22:25:37 +08:00
parent f1992b0cfe
commit 9373e57ebf
15 changed files with 884 additions and 190 deletions
+28 -11
View File
@@ -559,6 +559,7 @@ func (service *Service) decodeInboundIntent(requestID string, message *agentv1.A
}
intent.ConversationID = conversationID
intent.ConversationState = runRequest.GetConversationState()
intent.PreFetchedBlobs = runRequest.GetPreFetchedBlobs()
intent.UserMessage = extractUserMessage(message)
intent.RequestContext = extractRequestContext(message)
if service.shouldIgnoreEmptyResumeRunRequest(requestID, runRequest, intent.UserMessage, intent.RequestContext) {
@@ -606,6 +607,7 @@ func (service *Service) decodeInboundIntent(requestID string, message *agentv1.A
intent.ConversationID = conversationID
intent.SubagentTypeName = strings.TrimSpace(prewarmRequest.GetSubagentTypeName())
intent.ConversationState = prewarmRequest.GetConversationState()
intent.PreFetchedBlobs = prewarmRequest.GetPreFetchedBlobs()
intent.Mode, intent.ModeSource, intent.HasExplicitMode, err = extractPrewarmMode(prewarmRequest)
if err != nil {
return InboundIntent{}, err
@@ -2246,6 +2248,15 @@ func (service *Service) finishSuccessfulTurnAfterCheckpoint(stream *ActiveStream
return nil
}
func (service *Service) finishFailedTurnAfterCheckpoint(stream *ActiveStream, terminalCode string, terminalMessage string) error {
if stream == nil {
return nil
}
err := service.broker.Fail(stream.RequestID, terminalCode, terminalMessage)
service.setTurnPhase(stream, TurnPhaseFailed)
return err
}
func (service *Service) failStreamIfNonTerminal(stream *ActiveStream, terminalCode string, cause error) error {
if stream == nil || cause == nil {
return nil
@@ -2265,6 +2276,10 @@ func (service *Service) publishCheckpoint(requestID string, conversationID strin
}
func (service *Service) publishCheckpointWithCompletion(requestID string, _ string, completion *pendingTurnCompletion) error {
return service.publishCheckpointWithTerminalAction(requestID, successfulCheckpointTerminalAction(completion))
}
func (service *Service) publishCheckpointWithTerminalAction(requestID string, terminal checkpointTerminalAction) error {
stream, ok := service.broker.Get(requestID)
if !ok || stream == nil {
return fmt.Errorf("request is not active: %s", requestID)
@@ -2282,7 +2297,7 @@ func (service *Service) publishCheckpointWithCompletion(requestID string, _ stri
}
projection.State.PendingToolCalls = buildPendingToolCalls(pendingExecs, pendingInteractions)
service.rewriteCheckpointTokenDetailsForClient(stream, conversation, projection.State)
return service.queueCheckpointProjection(stream, projection, completion)
return service.queueCheckpointProjectionWithTerminal(stream, projection, terminal)
}
func (service *Service) rewriteCheckpointTokenDetailsForClient(stream *ActiveStream, conversation *ConversationFile, state *agentv1.ConversationStateStructure) {
@@ -2422,18 +2437,20 @@ func (service *Service) failActiveStream(stream *ActiveStream, conversationID st
if cancel != nil {
cancel()
}
service.setTurnPhase(stream, TurnPhaseFailed)
var firstErr error
if err := service.syncSummaryCarryForward(conversationID, requestID, modelCallID); err != nil && firstErr == nil {
firstErr = err
if err := service.syncSummaryCarryForward(conversationID, requestID, modelCallID); err != nil {
log.Printf(
"forwarder summary sync before failed terminal skipped request_id=%s model_call_id=%s err=%v",
strings.TrimSpace(requestID),
strings.TrimSpace(modelCallID),
err,
)
}
if err := service.publishCheckpoint(requestID, conversationID); err != nil && firstErr == nil {
firstErr = err
terminal := failedCheckpointTerminalAction(terminalCode, terminalMessage)
if err := service.publishCheckpointWithTerminalAction(requestID, terminal); err != nil {
log.Printf("forwarder checkpoint queue before failed terminal skipped request_id=%s err=%v", strings.TrimSpace(requestID), err)
return service.finishFailedTurnAfterCheckpoint(stream, terminalCode, terminalMessage)
}
if err := service.broker.Fail(requestID, terminalCode, terminalMessage); err != nil && firstErr == nil {
firstErr = err
}
return firstErr
return nil
}
// buildRunEntries 构造一次 run intent 需要写入 history 的首批 entry。