340 lines
12 KiB
Go
340 lines
12 KiB
Go
package openai
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
)
|
|
|
|
type hotPathEndpointError struct {
|
|
Status int
|
|
Type string
|
|
Message string
|
|
}
|
|
|
|
type hotPathTerminalIntent struct {
|
|
Output normalizedStageOutput
|
|
Error *hotPathEndpointError
|
|
}
|
|
|
|
type hotPathCleanupTurn struct {
|
|
RequestID string
|
|
Output normalizedStageOutput
|
|
}
|
|
|
|
func (i hotPathTerminalIntent) clone() hotPathTerminalIntent {
|
|
out := hotPathTerminalIntent{Output: cloneNormalizedStageOutput(i.Output)}
|
|
if i.Error != nil {
|
|
endpointErr := *i.Error
|
|
out.Error = &endpointErr
|
|
}
|
|
return out
|
|
}
|
|
|
|
func (i hotPathTerminalIntent) terminalClass() string {
|
|
if i.Error != nil {
|
|
return "primary_error"
|
|
}
|
|
return "success"
|
|
}
|
|
|
|
func (s *hotPathLightStore) beginCleanup(
|
|
ctx context.Context,
|
|
requestID, ownerEdgeID string,
|
|
intent hotPathTerminalIntent,
|
|
coordinator *logicalRequestCoordinator,
|
|
) (normalizedStageOutput, error) {
|
|
if s == nil || coordinator == nil {
|
|
return normalizedStageOutput{}, fmt.Errorf("light cleanup is unavailable")
|
|
}
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
record := s.records[requestID]
|
|
if record == nil || record.ownerEdgeID != ownerEdgeID || !record.running || record.pending != nil {
|
|
return normalizedStageOutput{}, fmt.Errorf("review completion cannot enter cleanup")
|
|
}
|
|
if record.phase != hotPathPhaseReviewResolution && record.phase != hotPathPhaseReviewRepair {
|
|
return normalizedStageOutput{}, fmt.Errorf("review completion is not resolution or repair")
|
|
}
|
|
return s.beginCleanupLocked(ctx, record, record.reviewStageID, intent, coordinator)
|
|
}
|
|
|
|
func (s *hotPathLightStore) beginPrimaryErrorCleanup(
|
|
ctx context.Context,
|
|
requestID, ownerEdgeID string,
|
|
primary hotPathEndpointError,
|
|
coordinator *logicalRequestCoordinator,
|
|
) (normalizedStageOutput, error) {
|
|
if s == nil || coordinator == nil {
|
|
return normalizedStageOutput{}, fmt.Errorf("light cleanup is unavailable")
|
|
}
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
record := s.records[requestID]
|
|
if record == nil || record.ownerEdgeID != ownerEdgeID || record.cleanupTransitions != 0 || record.terminalIntent != nil {
|
|
return normalizedStageOutput{}, fmt.Errorf("primary-error cleanup is unavailable")
|
|
}
|
|
fromStageID, err := record.primaryErrorCleanupSource()
|
|
if err != nil {
|
|
return normalizedStageOutput{}, err
|
|
}
|
|
intent := hotPathTerminalIntent{Error: &primary}
|
|
return s.beginCleanupLocked(ctx, record, fromStageID, intent, coordinator)
|
|
}
|
|
|
|
func (r *hotPathLightRecord) primaryErrorCleanupSource() (string, error) {
|
|
if r == nil || r.running || r.pending != nil || r.cleanupTransitions != 0 || r.terminalIntent != nil {
|
|
return "", fmt.Errorf("primary-error cleanup source is unavailable")
|
|
}
|
|
if r.selectorCommit.StageID != r.selectorStageID || strings.TrimSpace(r.selectorCommit.ResponseID) == "" {
|
|
return "", fmt.Errorf("primary-error cleanup selector correlation is unavailable")
|
|
}
|
|
|
|
switch r.phase {
|
|
case hotPathPhaseAwaitArtifacts:
|
|
if r.localStageID != "" || r.reviewStageID != "" {
|
|
return "", fmt.Errorf("primary-error cleanup artifact source is mismatched")
|
|
}
|
|
return "", nil
|
|
case hotPathPhaseLocalActive:
|
|
if !r.artifactReady || !validLogicalRequestID(r.localStageID) || r.reviewStageID != "" {
|
|
return "", fmt.Errorf("primary-error cleanup local source is mismatched")
|
|
}
|
|
return r.localStageID, nil
|
|
case hotPathPhaseReviewActive, hotPathPhaseReviewAwaitRead, hotPathPhaseReviewResolution, hotPathPhaseReviewRepair:
|
|
if !r.artifactReady || !validLogicalRequestID(r.localStageID) || !validLogicalRequestID(r.reviewStageID) ||
|
|
r.localCommit.StageID != r.localStageID || strings.TrimSpace(r.localCommit.ResponseID) == "" {
|
|
return "", fmt.Errorf("primary-error cleanup review source is mismatched")
|
|
}
|
|
return r.reviewStageID, nil
|
|
default:
|
|
return "", fmt.Errorf("phase %q cannot enter primary-error cleanup", r.phase)
|
|
}
|
|
}
|
|
|
|
func (s *hotPathLightStore) beginCleanupLocked(
|
|
ctx context.Context,
|
|
record *hotPathLightRecord,
|
|
fromStageID string,
|
|
intent hotPathTerminalIntent,
|
|
coordinator *logicalRequestCoordinator,
|
|
) (normalizedStageOutput, error) {
|
|
if err := ctx.Err(); err != nil {
|
|
record.running = false
|
|
_ = coordinator.disconnect(record.requestID, record.ownerEdgeID, "cancelled")
|
|
return normalizedStageOutput{}, err
|
|
}
|
|
if record.cleanupTransitions != 0 || record.terminalIntent != nil {
|
|
return normalizedStageOutput{}, fmt.Errorf("cleanup pending was already committed")
|
|
}
|
|
|
|
cleanupStageID, err := coordinator.newStageID()
|
|
if err != nil {
|
|
return normalizedStageOutput{}, err
|
|
}
|
|
providerCallID, err := coordinator.newCallID()
|
|
if err != nil {
|
|
return normalizedStageOutput{}, err
|
|
}
|
|
paths := newReservedPaths(record.requestID)
|
|
deleteBinding := record.binding.operation(opKindDelete)
|
|
if deleteBinding == nil {
|
|
return normalizedStageOutput{}, fmt.Errorf("cleanup delete binding is unavailable")
|
|
}
|
|
deleteArgs := make(map[string]any)
|
|
setMappedArgument(deleteArgs, deleteBinding.pathField, paths.JobDir)
|
|
providerCall := normalizedToolCall{
|
|
ID: providerCallID, ProviderCallID: providerCallID, Name: deleteBinding.toolName,
|
|
Arguments: deleteArgs, Path: paths.JobDir,
|
|
}
|
|
mapped, payload, err := mapArtifactCall(record.binding, providerCall, opKindDelete, paths.JobDir, coordinator)
|
|
if err != nil {
|
|
return normalizedStageOutput{}, fmt.Errorf("map cleanup delete: %w", err)
|
|
}
|
|
responseID := strings.TrimSpace(intent.Output.ResponseID)
|
|
if responseID == "" {
|
|
responseID = strings.TrimSpace(record.selectorCommit.ResponseID)
|
|
}
|
|
if responseID == "" {
|
|
return normalizedStageOutput{}, fmt.Errorf("cleanup response identity is unavailable")
|
|
}
|
|
cleanupOutput := normalizedStageOutput{
|
|
ResponseID: responseID, Created: intent.Output.Created,
|
|
ToolCalls: []normalizedToolCall{mapped}, TerminalReason: "tool_calls",
|
|
}
|
|
if record.protocol == "anthropic" {
|
|
cleanupOutput.TerminalReason = "tool_use"
|
|
}
|
|
issuedHash, err := directIssuedCallHash(record.protocol, cleanupOutput)
|
|
if err != nil {
|
|
return normalizedStageOutput{}, fmt.Errorf("fingerprint cleanup call: %w", err)
|
|
}
|
|
if _, err := coordinator.startCleanup(record.requestID, record.ownerEdgeID, fromStageID, cleanupStageID, intent.terminalClass()); err != nil {
|
|
return normalizedStageOutput{}, err
|
|
}
|
|
if _, err := coordinator.awaitToolResults(record.requestID, record.ownerEdgeID, cleanupStageID, []logicalRequestExpectedTool{{
|
|
PublicCallID: mapped.ID, ProviderCallID: mapped.ProviderCallID,
|
|
}}, issuedHash); err != nil {
|
|
return normalizedStageOutput{}, err
|
|
}
|
|
|
|
stored := intent.clone()
|
|
record.terminalIntent = &stored
|
|
record.pendingKind = hotPathPendingCleanup
|
|
record.pending = map[string]hotPathPendingCall{
|
|
mapped.ID: {publicCallID: mapped.ID, providerCallID: mapped.ProviderCallID, payload: payload},
|
|
}
|
|
record.pendingHash = issuedHash
|
|
record.pendingOutput = cloneNormalizedStageOutput(cleanupOutput)
|
|
record.phase = hotPathPhaseCleanupPending
|
|
record.cleanupTransitions++
|
|
record.running = false
|
|
return cleanupOutput, nil
|
|
}
|
|
|
|
func (s *hotPathLightStore) consumeCleanupLocked(
|
|
record *hotPathLightRecord,
|
|
lineage logicalRequestContinuationLineage,
|
|
results []workspaceResult,
|
|
coordinator *logicalRequestCoordinator,
|
|
) (logicalRequestSnapshot, hotPathLightDisposition, bool, error) {
|
|
if record.terminalIntent == nil || len(record.pending) != 1 || len(results) != 1 {
|
|
return logicalRequestSnapshot{}, hotPathLightDisposition{}, true, fmt.Errorf("cleanup result set mismatch")
|
|
}
|
|
result := results[0]
|
|
pending, ok := record.pending[result.callID]
|
|
if !ok || pending.payload == nil {
|
|
return logicalRequestSnapshot{}, hotPathLightDisposition{}, true, fmt.Errorf("cleanup result id is not pending")
|
|
}
|
|
if reason := matchResultCorrelation(record.binding, pending.payload, result); reason != "" {
|
|
return logicalRequestSnapshot{}, hotPathLightDisposition{}, true, fmt.Errorf("cleanup receipt rejected: %s", reason)
|
|
}
|
|
|
|
intent := record.terminalIntent.clone()
|
|
receipt := matchResultReceipt(record.binding, pending.payload, result)
|
|
if !receipt.matched && intent.Error == nil {
|
|
intent.Error = standardCleanupEndpointError(record.protocol)
|
|
intent.Output = normalizedStageOutput{}
|
|
}
|
|
snap, err := coordinator.commitCleanupByLineage(record.ownerEdgeID, record.principalRef, lineage)
|
|
if err != nil {
|
|
return logicalRequestSnapshot{}, hotPathLightDisposition{}, true, err
|
|
}
|
|
requestID := record.requestID
|
|
stageID := snap.ActiveStageID
|
|
delete(s.records, requestID)
|
|
return snap, hotPathLightDisposition{
|
|
RequestID: requestID, StageID: stageID, Phase: hotPathPhaseCleanupPending, Terminal: &intent,
|
|
}, true, nil
|
|
}
|
|
|
|
func standardCleanupEndpointError(protocol string) *hotPathEndpointError {
|
|
if protocol == "anthropic" {
|
|
return &hotPathEndpointError{Status: http.StatusBadGateway, Type: "api_error", Message: "workspace cleanup failed"}
|
|
}
|
|
return &hotPathEndpointError{Status: http.StatusBadGateway, Type: "run_error", Message: "workspace cleanup failed"}
|
|
}
|
|
|
|
// commitCleanupByLineage admits the exact cleanup continuation and removes the
|
|
// coordinator record in the same critical section. This is the terminal owner
|
|
// shared by cleanup-result and TTL races.
|
|
func (c *logicalRequestCoordinator) commitCleanupByLineage(
|
|
ownerEdgeID, principalRef string,
|
|
lineage logicalRequestContinuationLineage,
|
|
) (logicalRequestSnapshot, error) {
|
|
c.mu.Lock()
|
|
defer c.mu.Unlock()
|
|
var target *logicalRequestRecord
|
|
for _, record := range c.requests {
|
|
if record.state == logicalRequestStateCleanup && record.cleanup && record.ownerEdgeID == ownerEdgeID && record.principalRef == principalRef &&
|
|
record.lineage == lineage.Prefix && sameLogicalRequestResultIDs(record.expected, lineage.ResultIDs) {
|
|
target = record
|
|
break
|
|
}
|
|
}
|
|
if target == nil {
|
|
return logicalRequestSnapshot{}, errLogicalRequestNotFound
|
|
}
|
|
if err := validateLogicalRequestContinuationLineage(target.lineage, target.expectedIssuedCallHash, target.expected, lineage); err != nil {
|
|
return logicalRequestSnapshot{}, err
|
|
}
|
|
snapshot := target.snapshot()
|
|
delete(c.requests, target.id)
|
|
return snapshot, nil
|
|
}
|
|
|
|
func (s *Server) writeHotPathTerminal(
|
|
w http.ResponseWriter,
|
|
r *http.Request,
|
|
dispatch routeDispatch,
|
|
protocol string,
|
|
stream bool,
|
|
requestID string,
|
|
intent hotPathTerminalIntent,
|
|
) error {
|
|
if intent.Error != nil {
|
|
if protocol == "anthropic" {
|
|
writeAnthropicError(w, intent.Error.Status, intent.Error.Type, intent.Error.Message)
|
|
} else {
|
|
writeError(w, intent.Error.Status, intent.Error.Type, intent.Error.Message)
|
|
}
|
|
return fmt.Errorf("%s", intent.Error.Message)
|
|
}
|
|
return s.writeHotPathStageResponse(w, r, dispatch, protocol, stream, requestID, intent.Output)
|
|
}
|
|
|
|
func hotPathLightEndpointError(protocol string, status int, message string) hotPathEndpointError {
|
|
errorType := "run_error"
|
|
if protocol == "anthropic" {
|
|
errorType = "api_error"
|
|
}
|
|
return hotPathEndpointError{Status: status, Type: errorType, Message: message}
|
|
}
|
|
|
|
func (s *Server) retainHotPathPrimaryErrorForTTL(requestID string, primary hotPathEndpointError) *hotPathTerminalIntent {
|
|
ownerEdgeID := s.edgeIDValue()
|
|
if s.lightFlows != nil {
|
|
s.lightFlows.abortDispatch(requestID, ownerEdgeID)
|
|
}
|
|
_ = s.requestCoordinator.disconnect(requestID, ownerEdgeID, "primary_error")
|
|
return &hotPathTerminalIntent{Error: &primary}
|
|
}
|
|
|
|
func (s *Server) writeHotPathPrimaryError(
|
|
w http.ResponseWriter,
|
|
r *http.Request,
|
|
dispatch routeDispatch,
|
|
protocol string,
|
|
stream bool,
|
|
requestID string,
|
|
primary hotPathEndpointError,
|
|
) error {
|
|
ownerEdgeID := s.edgeIDValue()
|
|
s.lightFlows.abortDispatch(requestID, ownerEdgeID)
|
|
if err := r.Context().Err(); err != nil {
|
|
s.disconnectHotPathRequest(requestID, ownerEdgeID)
|
|
return err
|
|
}
|
|
|
|
cleanup, err := s.lightFlows.beginPrimaryErrorCleanup(r.Context(), requestID, ownerEdgeID, primary, s.requestCoordinator)
|
|
if err == nil {
|
|
return s.writeHotPathStageResponse(w, r, dispatch, protocol, stream, requestID, cleanup)
|
|
}
|
|
if contextErr := r.Context().Err(); contextErr != nil {
|
|
s.disconnectHotPathRequest(requestID, ownerEdgeID)
|
|
return contextErr
|
|
}
|
|
intent := s.retainHotPathPrimaryErrorForTTL(requestID, primary)
|
|
return s.writeHotPathTerminal(w, r, dispatch, protocol, stream, requestID, *intent)
|
|
}
|
|
|
|
func (s *Server) disconnectHotPathRequest(requestID, ownerEdgeID string) {
|
|
if requestID == "" {
|
|
return
|
|
}
|
|
if s.lightFlows != nil {
|
|
s.lightFlows.abortDispatch(requestID, ownerEdgeID)
|
|
}
|
|
_ = s.requestCoordinator.disconnect(requestID, ownerEdgeID, "cancelled")
|
|
}
|