iop/apps/edge/internal/openai/hot_path_light.go
toki 495996fee4 feat(openai): 핫패스 에이전트 실행 경로를 확장한다
Anthropic·Chat 게이트와 관찰·종료 제어를 통합하고 관련 계약·검증 산출물을 반영한다.
2026-08-06 00:09:24 +09:00

1088 lines
42 KiB
Go

package openai
import (
"context"
"encoding/json"
"fmt"
"net/http"
"strconv"
"strings"
"sync"
"time"
edgeservice "iop/apps/edge/internal/service"
"iop/packages/go/config"
)
const defaultHotPathLightCapacity = 1024
const hotPathOutputCapMetadata = "iop_hot_path_output_token_cap"
func hotPathOutputTokenCap(metadata map[string]string) int {
if metadata == nil {
return 0
}
cap, err := strconv.Atoi(strings.TrimSpace(metadata[hotPathOutputCapMetadata]))
if err != nil || cap < 1 {
return 0
}
return cap
}
// applyHotPathOutputTokenCap replaces any caller metadata value with the
// validated endpoint field. A missing field removes the internal key so
// metadata cannot manufacture a trusted output budget.
func applyHotPathOutputTokenCap(metadata map[string]string, candidates ...*int) {
if metadata == nil {
return
}
delete(metadata, hotPathOutputCapMetadata)
for _, candidate := range candidates {
if candidate != nil && *candidate > 0 {
metadata[hotPathOutputCapMetadata] = strconv.Itoa(*candidate)
return
}
}
}
type hotPathLightPhase string
const (
hotPathPhaseAwaitArtifacts hotPathLightPhase = "await_artifacts"
hotPathPhaseLocalActive hotPathLightPhase = "local_active"
hotPathPhaseReviewActive hotPathLightPhase = "review_active"
hotPathPhaseReviewAwaitRead hotPathLightPhase = "review_write_wait"
hotPathPhaseReviewResolution hotPathLightPhase = "review_resolution_active"
hotPathPhaseReviewRepair hotPathLightPhase = "review_repair_active"
hotPathPhaseCleanupPending hotPathLightPhase = "cleanup_pending"
)
type hotPathPendingKind string
const (
hotPathPendingLocalTools hotPathPendingKind = "local_tools"
hotPathPendingReviewInspection hotPathPendingKind = "review_inspection"
hotPathPendingReviewWrite hotPathPendingKind = "review_write"
hotPathPendingReviewRead hotPathPendingKind = "review_read"
hotPathPendingReviewRepair hotPathPendingKind = "review_repair"
hotPathPendingCleanup hotPathPendingKind = "cleanup"
)
type hotPathStageToolResult struct {
ProviderCallID string
Body string
IsError bool
}
type hotPathStageExchange struct {
Output normalizedStageOutput
Results []hotPathStageToolResult
}
type hotPathPendingCall struct {
publicCallID string
providerCallID string
payload *workspaceEncodedPayload
}
type hotPathLightRecord struct {
requestID string
ownerEdgeID string
principalRef string
protocol string
lineage logicalRequestLineage
immutableTask string
tools []any
binding *workspaceBinding
preset config.ExecutionPreset
dispatch routeDispatch
selectorStageID string
selectorCommit hotPathStageCorrelation
localStageID string
localCommit hotPathStageCorrelation
reviewStageID string
cleanupStageID string
phase hotPathLightPhase
artifactReady bool
running bool
pendingKind hotPathPendingKind
pending map[string]hotPathPendingCall
pendingHash string
pendingOutput normalizedStageOutput
consumedHashes map[string]struct{}
consumedIDs map[string]struct{}
localTranscript []hotPathStageExchange
reviewTranscript []hotPathStageExchange
cleanupTransitions int
terminalIntent *hotPathTerminalIntent
terminalDisposition *hotPathTerminalDisposition
}
type hotPathLightStore struct {
mu sync.Mutex
capacity int
records map[string]*hotPathLightRecord
}
type hotPathDispatchSnapshot struct {
RequestID string
OwnerEdgeID string
PrincipalRef string
Protocol string
Phase hotPathLightPhase
StageID string
Stage config.ExecutionRouteStage
Route routeDispatch
PresetRoute routeDispatch
Input hotPathStageInput
Tools []any
Transcript []hotPathStageExchange
Stream bool
// OutputBudget is recalculated from the request-local outer accumulator
// before every stage. Limited, remaining, and exhausted are distinct so an
// exhausted turn cannot be encoded as a one-token provider request.
OutputBudget hotPathOutputBudget
}
type hotPathLightDisposition struct {
RequestID string
StageID string
Phase hotPathLightPhase
TransitionFrom hotPathLightPhase
Terminal *hotPathTerminalIntent
}
func newHotPathLightStore(capacity int) *hotPathLightStore {
if capacity <= 0 {
capacity = defaultHotPathLightCapacity
}
return &hotPathLightStore{capacity: capacity, records: make(map[string]*hotPathLightRecord)}
}
func (s *hotPathLightStore) pin(
requestID, ownerEdgeID, principalRef, protocol, selectorStageID string,
lineage logicalRequestLineage,
task string,
tools any,
binding *workspaceBinding,
preset config.ExecutionPreset,
dispatch routeDispatch,
) error {
if s == nil || binding == nil {
return fmt.Errorf("light flow binding is unavailable")
}
if !validLogicalRequestID(requestID) || !validLogicalRequestID(selectorStageID) {
return fmt.Errorf("light flow identity is invalid")
}
immutableTools, err := cloneHotPathTools(tools)
if err != nil {
return err
}
if strings.TrimSpace(task) == "" {
return fmt.Errorf("light flow immutable task is empty")
}
s.mu.Lock()
defer s.mu.Unlock()
if _, exists := s.records[requestID]; exists {
return fmt.Errorf("light flow already exists")
}
if len(s.records) >= s.capacity {
return fmt.Errorf("light flow capacity reached")
}
s.records[requestID] = &hotPathLightRecord{
requestID: requestID, ownerEdgeID: ownerEdgeID, principalRef: principalRef,
protocol: protocol, lineage: lineage, immutableTask: strings.TrimSpace(task),
tools: immutableTools, binding: binding, preset: preset.Clone(), dispatch: cloneHotPathDispatch(dispatch),
selectorStageID: selectorStageID, phase: hotPathPhaseAwaitArtifacts,
consumedHashes: make(map[string]struct{}), consumedIDs: make(map[string]struct{}),
}
return nil
}
func cloneHotPathTools(tools any) ([]any, error) {
raw, err := json.Marshal(tools)
if err != nil {
return nil, fmt.Errorf("clone light flow tools: %w", err)
}
var out []any
decoder := json.NewDecoder(strings.NewReader(string(raw)))
decoder.UseNumber()
if err := decoder.Decode(&out); err != nil {
return nil, fmt.Errorf("clone light flow tools: %w", err)
}
return out, nil
}
func cloneHotPathDispatch(dispatch routeDispatch) routeDispatch {
out := dispatch
out.Preset = dispatch.Preset.Clone()
if dispatch.PresetResolvedBindings != nil {
out.PresetResolvedBindings = make(map[string]routeDispatch, len(dispatch.PresetResolvedBindings))
for key, binding := range dispatch.PresetResolvedBindings {
binding.Preset = binding.Preset.Clone()
binding.PresetResolvedBindings = nil
out.PresetResolvedBindings[key] = binding
}
}
return out
}
func (s *hotPathLightStore) remove(requestID, ownerEdgeID string) {
if s == nil || requestID == "" {
return
}
s.mu.Lock()
defer s.mu.Unlock()
if record := s.records[requestID]; record != nil && record.ownerEdgeID == ownerEdgeID {
delete(s.records, requestID)
}
}
func (s *hotPathLightStore) has(requestID, ownerEdgeID string) bool {
if s == nil || requestID == "" {
return false
}
s.mu.Lock()
defer s.mu.Unlock()
record := s.records[requestID]
return record != nil && record.ownerEdgeID == ownerEdgeID
}
func (s *hotPathLightStore) updateArtifactLineage(requestID, ownerEdgeID string, lineage logicalRequestLineage, localEligible bool) error {
if s == nil {
return fmt.Errorf("light flow is unavailable")
}
s.mu.Lock()
defer s.mu.Unlock()
record := s.records[requestID]
if record == nil || record.ownerEdgeID != ownerEdgeID {
return fmt.Errorf("light flow state is unavailable")
}
record.lineage = lineage
if localEligible {
record.artifactReady = true
}
return nil
}
func (s *hotPathLightStore) commitSelector(requestID, ownerEdgeID string, output normalizedStageOutput, gate hotPathSelectorGate) error {
if s == nil {
return fmt.Errorf("light flow is unavailable")
}
s.mu.Lock()
defer s.mu.Unlock()
record := s.records[requestID]
if record == nil || record.ownerEdgeID != ownerEdgeID || record.phase != hotPathPhaseAwaitArtifacts {
return fmt.Errorf("light flow selector commit is unavailable")
}
if strings.TrimSpace(output.ResponseID) == "" || strings.TrimSpace(gate.RunID) == "" {
return fmt.Errorf("light flow selector correlation is incomplete")
}
record.selectorCommit = hotPathStageCorrelation{
StageID: record.selectorStageID, ResponseID: output.ResponseID, RunID: gate.RunID,
ProviderID: gate.ProviderID, Terminal: output.TerminalReason,
}
return nil
}
func (s *hotPathLightStore) startLocal(requestID, ownerEdgeID string, coordinator *logicalRequestCoordinator) (hotPathLightDisposition, error) {
if s == nil || coordinator == nil {
return hotPathLightDisposition{}, fmt.Errorf("light flow is unavailable")
}
s.mu.Lock()
defer s.mu.Unlock()
record := s.records[requestID]
if record == nil || record.ownerEdgeID != ownerEdgeID {
return hotPathLightDisposition{}, fmt.Errorf("light flow state is unavailable")
}
if record.phase != hotPathPhaseAwaitArtifacts || !record.artifactReady || strings.TrimSpace(record.selectorCommit.ResponseID) == "" {
return hotPathLightDisposition{}, fmt.Errorf("light flow is not eligible for local execution")
}
stageID, err := coordinator.newStageID()
if err != nil {
return hotPathLightDisposition{}, err
}
if _, err := coordinator.activateStage(requestID, ownerEdgeID, stageID); err != nil {
return hotPathLightDisposition{}, err
}
record.localStageID = stageID
record.phase = hotPathPhaseLocalActive
return hotPathLightDisposition{RequestID: requestID, StageID: stageID, Phase: record.phase}, nil
}
func (s *hotPathLightStore) beginDispatch(requestID, ownerEdgeID string, stream bool) (hotPathDispatchSnapshot, error) {
if s == nil {
return hotPathDispatchSnapshot{}, fmt.Errorf("light flow is unavailable")
}
s.mu.Lock()
defer s.mu.Unlock()
record := s.records[requestID]
if record == nil || record.ownerEdgeID != ownerEdgeID {
return hotPathDispatchSnapshot{}, fmt.Errorf("light flow state is unavailable")
}
if record.running || record.pending != nil || record.phase == hotPathPhaseCleanupPending || record.phase == hotPathPhaseAwaitArtifacts {
return hotPathDispatchSnapshot{}, fmt.Errorf("light flow stage is not dispatchable")
}
stage, route, stageID, input, transcript, err := record.dispatchValues()
if err != nil {
return hotPathDispatchSnapshot{}, err
}
record.running = true
return hotPathDispatchSnapshot{
RequestID: requestID, OwnerEdgeID: ownerEdgeID, PrincipalRef: record.principalRef,
Protocol: record.protocol, Phase: record.phase, StageID: stageID, Stage: stage,
Route: route, PresetRoute: cloneHotPathDispatch(record.dispatch), Input: input,
Tools: cloneAnySlice(record.tools), Transcript: cloneStageTranscript(transcript), Stream: stream,
}, nil
}
func (r *hotPathLightRecord) dispatchValues() (config.ExecutionRouteStage, routeDispatch, string, hotPathStageInput, []hotPathStageExchange, error) {
route, ok := r.preset.Routes[config.ModeLight]
if !ok || len(route.Stages) != 2 {
return config.ExecutionRouteStage{}, routeDispatch{}, "", hotPathStageInput{}, nil, fmt.Errorf("light route requires local and review stages")
}
paths := newReservedPaths(r.requestID)
switch r.phase {
case hotPathPhaseLocalActive:
stage := route.Stages[0].Clone()
binding, ok := r.dispatch.PresetResolvedBindings[stage.Model]
if !ok {
return config.ExecutionRouteStage{}, routeDispatch{}, "", hotPathStageInput{}, nil, fmt.Errorf("local stage binding is unavailable")
}
return stage, binding, r.localStageID, buildLocalStageInput(r.immutableTask, paths, r.selectorCommit), r.localTranscript, nil
case hotPathPhaseReviewActive, hotPathPhaseReviewAwaitRead, hotPathPhaseReviewResolution, hotPathPhaseReviewRepair:
stage := route.Stages[1].Clone()
binding, ok := r.dispatch.PresetResolvedBindings[stage.Model]
if !ok {
return config.ExecutionRouteStage{}, routeDispatch{}, "", hotPathStageInput{}, nil, fmt.Errorf("review stage binding is unavailable")
}
return stage, binding, r.reviewStageID, buildReviewStageInput(r.immutableTask, paths, r.selectorCommit, r.localCommit), r.reviewTranscript, nil
default:
return config.ExecutionRouteStage{}, routeDispatch{}, "", hotPathStageInput{}, nil, fmt.Errorf("phase %q is not dispatchable", r.phase)
}
}
func cloneAnySlice(values []any) []any {
if values == nil {
return nil
}
out := make([]any, len(values))
for i, value := range values {
out[i] = cloneAnyValue(value)
}
return out
}
func cloneStageTranscript(values []hotPathStageExchange) []hotPathStageExchange {
out := make([]hotPathStageExchange, len(values))
for i, value := range values {
out[i].Output = cloneNormalizedStageOutput(value.Output)
out[i].Results = append([]hotPathStageToolResult(nil), value.Results...)
}
return out
}
func cloneNormalizedStageOutput(value normalizedStageOutput) normalizedStageOutput {
out := value
out.Deltas = append([]normalizedStageDelta(nil), value.Deltas...)
out.ToolCalls = make([]normalizedToolCall, len(value.ToolCalls))
for i, call := range value.ToolCalls {
out.ToolCalls[i] = call
out.ToolCalls[i].Arguments = cloneAnyMap(call.Arguments)
}
out.Usage = cloneRawJSON(value.Usage)
if value.OpenAIUsage != nil {
usage := *value.OpenAIUsage
out.OpenAIUsage = &usage
}
return out
}
func (s *hotPathLightStore) abortDispatch(requestID, ownerEdgeID string) {
if s == nil {
return
}
s.mu.Lock()
defer s.mu.Unlock()
if record := s.records[requestID]; record != nil && record.ownerEdgeID == ownerEdgeID {
record.running = false
}
}
func (s *hotPathLightStore) abortWithDisposition(requestID, ownerEdgeID string, disposition hotPathTerminalDisposition) {
if s == nil || !disposition.valid() {
return
}
s.mu.Lock()
defer s.mu.Unlock()
if record := s.records[requestID]; record != nil && record.ownerEdgeID == ownerEdgeID {
record.running = false
selected := disposition
record.terminalDisposition = &selected
}
}
func (s *hotPathLightStore) issueTools(
ctx context.Context,
requestID, ownerEdgeID string,
output normalizedStageOutput,
visible normalizedStageOutput,
kind hotPathPendingKind,
outer *hotPathOuterTurn,
coordinator *logicalRequestCoordinator,
) (normalizedStageOutput, error) {
if s == nil || coordinator == nil {
return normalizedStageOutput{}, fmt.Errorf("light flow 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("light flow tool frontier is unavailable")
}
preallocated := make(map[string]string)
if outer != nil && output.ProgressivelyReleased {
for _, call := range outer.accumulator().ToolCalls {
preallocated[call.ProviderCallID] = call.ID
}
}
mapped, pending, err := mapHotPathStageCalls(record, output, kind, coordinator, preallocated)
if err != nil {
return normalizedStageOutput{}, err
}
stageID := record.localStageID
if kind != hotPathPendingLocalTools {
stageID = record.reviewStageID
}
if outer != nil {
if !output.ProgressivelyReleased {
if err := runHotPathCollectedStage(ctx, outer, stageID, mapped); err != nil {
return normalizedStageOutput{}, fmt.Errorf("collect light tool outer turn: %w", err)
}
}
current := hotPathCompatibilityOutput(outer, mapped, record.protocol)
if len(current.ToolCalls) == 0 && outer.outputBudget().Exhausted {
outer.commitLengthTerminal()
return hotPathCompatibilityOutput(outer, mapped.StageResponseOverlay(visible), record.protocol), nil
}
if err := outer.projectToolIdentities(mapped.ToolCalls); err != nil {
return normalizedStageOutput{}, err
}
}
mapped = mapped.StageResponseOverlay(visible)
if outer != nil {
mapped = hotPathCompatibilityOutput(outer, mapped, record.protocol)
}
issuedHash, err := directIssuedCallHash(record.protocol, mapped)
if err != nil {
return normalizedStageOutput{}, err
}
expected := make([]logicalRequestExpectedTool, 0, len(mapped.ToolCalls))
for _, call := range mapped.ToolCalls {
expected = append(expected, logicalRequestExpectedTool{PublicCallID: call.ID, ProviderCallID: call.ProviderCallID})
}
if _, err := coordinator.awaitToolResults(requestID, ownerEdgeID, stageID, expected, issuedHash); err != nil {
return normalizedStageOutput{}, err
}
record.pendingKind = kind
record.pending = pending
record.pendingHash = issuedHash
record.pendingOutput = cloneNormalizedStageOutput(output)
record.running = false
return mapped, nil
}
func mapHotPathStageCalls(record *hotPathLightRecord, output normalizedStageOutput, kind hotPathPendingKind, coordinator *logicalRequestCoordinator, preallocated map[string]string) (normalizedStageOutput, map[string]hotPathPendingCall, error) {
if len(output.ToolCalls) == 0 {
return normalizedStageOutput{}, nil, fmt.Errorf("light flow tool output is empty")
}
mappedCalls := make([]normalizedToolCall, 0, len(output.ToolCalls))
pending := make(map[string]hotPathPendingCall, len(output.ToolCalls))
paths := newReservedPaths(record.requestID)
for _, call := range output.ToolCalls {
providerID := strings.TrimSpace(call.ProviderCallID)
if providerID == "" {
providerID = strings.TrimSpace(call.ID)
}
if !validLogicalRequestID(providerID) {
return normalizedStageOutput{}, nil, fmt.Errorf("stage provider tool id is invalid")
}
publicID := strings.TrimSpace(preallocated[providerID])
if publicID != "" && !validLogicalRequestID(publicID) {
return normalizedStageOutput{}, nil, fmt.Errorf("stage public tool id is invalid")
}
operation, requiredPath, reserved, err := hotPathWorkspaceCall(record.phase, kind, paths, call)
if err != nil {
return normalizedStageOutput{}, nil, err
}
var mapped normalizedToolCall
var payload *workspaceEncodedPayload
if reserved {
mapped, payload, err = mapArtifactCall(record.binding, call, operation, requiredPath, coordinator)
if err != nil {
return normalizedStageOutput{}, nil, err
}
if publicID != "" {
mapped.ID = publicID
payload.publicCallID = publicID
payload.correlationDigest = computePayloadCorrelationDigest(payload)
}
} else {
if !hotPathToolAllowed(record.tools, call.Name) {
return normalizedStageOutput{}, nil, fmt.Errorf("stage tool %q is not in the immutable caller tool set", call.Name)
}
if publicID == "" {
var allocErr error
publicID, allocErr = coordinator.newCallID()
if allocErr != nil {
return normalizedStageOutput{}, nil, allocErr
}
}
mapped = call
mapped.ID = publicID
mapped.ProviderCallID = providerID
mapped.Arguments = cloneAnyMap(call.Arguments)
}
mappedCalls = append(mappedCalls, mapped)
pending[mapped.ID] = hotPathPendingCall{publicCallID: mapped.ID, providerCallID: providerID, payload: payload}
}
mapped := cloneNormalizedStageOutput(output)
mapped.ToolCalls = mappedCalls
if record.protocol == "anthropic" {
mapped.TerminalReason = "tool_use"
} else {
mapped.TerminalReason = "tool_calls"
}
return mapped, pending, nil
}
func hotPathToolAllowed(tools []any, name string) bool {
schemas, err := normalizeToolSchemas(tools)
if err != nil {
return false
}
_, ok := schemas[strings.TrimSpace(name)]
return ok
}
func hotPathWorkspaceCall(phase hotPathLightPhase, kind hotPathPendingKind, paths reservedPaths, call normalizedToolCall) (workspaceOperationKind, string, bool, error) {
reserved := reservedPathsFromToolCall(call)
if len(reserved) == 0 {
if kind == hotPathPendingReviewWrite || kind == hotPathPendingReviewRead {
return "", "", false, fmt.Errorf("review control turn must use the exact review path")
}
return "", "", false, nil
}
if len(reserved) != 1 {
return "", "", false, fmt.Errorf("stage tool call contains ambiguous reserved paths")
}
observed := cleanRelativePath(reserved[0])
switch kind {
case hotPathPendingLocalTools, hotPathPendingReviewInspection:
if observed != cleanRelativePath(paths.PlanPath) && observed != cleanRelativePath(paths.ReviewPath) {
return "", "", false, fmt.Errorf("stage read targets an unissued reserved path")
}
return opKindRead, observed, true, nil
case hotPathPendingReviewWrite:
if observed != cleanRelativePath(paths.ReviewPath) {
return "", "", false, fmt.Errorf("review write targets a non-review path")
}
return opKindWrite, paths.ReviewPath, true, nil
case hotPathPendingReviewRead:
if observed != cleanRelativePath(paths.ReviewPath) {
return "", "", false, fmt.Errorf("review resolution read targets a non-review path")
}
return opKindRead, paths.ReviewPath, true, nil
case hotPathPendingReviewRepair:
return "", "", false, fmt.Errorf("repair cannot start a second reserved review cycle")
default:
return "", "", false, fmt.Errorf("unknown light tool frontier %q in phase %q", kind, phase)
}
}
func (s *hotPathLightStore) consumeChat(ownerEdgeID, principalRef string, rawBody []byte, lineage logicalRequestContinuationLineage, coordinator *logicalRequestCoordinator) (logicalRequestSnapshot, hotPathLightDisposition, bool, error) {
results, err := decodeChatWorkspaceResults(rawBody)
if err != nil {
return logicalRequestSnapshot{}, hotPathLightDisposition{}, true, err
}
return s.consume(ownerEdgeID, principalRef, "openai", lineage, results, coordinator)
}
func (s *hotPathLightStore) consumeAnthropic(ownerEdgeID, principalRef string, rawBody []byte, lineage logicalRequestContinuationLineage, coordinator *logicalRequestCoordinator) (logicalRequestSnapshot, hotPathLightDisposition, bool, error) {
results, err := decodeAnthropicWorkspaceResults(rawBody)
if err != nil {
return logicalRequestSnapshot{}, hotPathLightDisposition{}, true, err
}
return s.consume(ownerEdgeID, principalRef, "anthropic", lineage, results, coordinator)
}
func (s *hotPathLightStore) consume(ownerEdgeID, principalRef, protocol string, lineage logicalRequestContinuationLineage, results []workspaceResult, coordinator *logicalRequestCoordinator) (logicalRequestSnapshot, hotPathLightDisposition, bool, error) {
if s == nil || coordinator == nil {
return logicalRequestSnapshot{}, hotPathLightDisposition{}, false, nil
}
s.mu.Lock()
defer s.mu.Unlock()
record, matched, err := s.matchRecordLocked(ownerEdgeID, principalRef, protocol, lineage)
if !matched || err != nil {
return logicalRequestSnapshot{}, hotPathLightDisposition{}, matched, err
}
if record.phase == hotPathPhaseCleanupPending && record.pendingKind == hotPathPendingCleanup {
return s.consumeCleanupLocked(record, lineage, results, coordinator)
}
if record.pending == nil || record.pendingHash == "" || len(results) != len(record.pending) {
return logicalRequestSnapshot{}, hotPathLightDisposition{}, true, fmt.Errorf("light tool result set mismatch")
}
byPublic := make(map[string]workspaceResult, len(results))
for _, result := range results {
pending, ok := record.pending[result.callID]
if !ok {
return logicalRequestSnapshot{}, hotPathLightDisposition{}, true, fmt.Errorf("light tool result id is not pending")
}
if _, duplicate := byPublic[result.callID]; duplicate {
return logicalRequestSnapshot{}, hotPathLightDisposition{}, true, fmt.Errorf("light tool result id is duplicated")
}
if pending.payload != nil {
receipt := matchResultReceipt(record.binding, pending.payload, result)
if !receipt.matched {
return logicalRequestSnapshot{}, hotPathLightDisposition{}, true, fmt.Errorf("light workspace receipt rejected: %s", receipt.mismatchReason)
}
}
byPublic[result.callID] = result
}
snap, err := coordinator.consumeContinuationByLineage(ownerEdgeID, principalRef, lineage)
if err != nil {
return logicalRequestSnapshot{}, hotPathLightDisposition{}, true, err
}
stageResults := make([]hotPathStageToolResult, 0, len(record.pendingOutput.ToolCalls))
for _, providerCall := range record.pendingOutput.ToolCalls {
providerID := strings.TrimSpace(providerCall.ProviderCallID)
if providerID == "" {
providerID = providerCall.ID
}
var pending hotPathPendingCall
var result workspaceResult
for publicID, item := range record.pending {
if item.providerCallID == providerID {
pending = item
result = byPublic[publicID]
break
}
}
if pending.providerCallID == "" {
return logicalRequestSnapshot{}, hotPathLightDisposition{}, true, fmt.Errorf("light provider result correlation is unavailable")
}
stageResults = append(stageResults, hotPathStageToolResult{ProviderCallID: providerID, Body: string(result.body), IsError: result.status == "error"})
}
exchange := hotPathStageExchange{Output: cloneNormalizedStageOutput(record.pendingOutput), Results: stageResults}
if record.pendingKind == hotPathPendingLocalTools {
record.localTranscript = append(record.localTranscript, exchange)
} else {
record.reviewTranscript = append(record.reviewTranscript, exchange)
}
for id := range record.pending {
record.consumedIDs[id] = struct{}{}
}
record.consumedHashes[record.pendingHash] = struct{}{}
record.lineage = lineage.Committed
record.pending = nil
record.pendingHash = ""
record.pendingOutput = normalizedStageOutput{}
previousPhase := record.phase
record.phase = phaseAfterHotPathResult(record.pendingKind)
record.pendingKind = ""
stageID := record.localStageID
if record.phase != hotPathPhaseLocalActive {
stageID = record.reviewStageID
}
if _, err := coordinator.activateStage(record.requestID, record.ownerEdgeID, stageID); err != nil {
return logicalRequestSnapshot{}, hotPathLightDisposition{}, true, err
}
return snap, hotPathLightDisposition{
RequestID: record.requestID, StageID: stageID, Phase: record.phase, TransitionFrom: previousPhase,
}, true, nil
}
func (s *hotPathLightStore) cleanupStage(requestID, ownerEdgeID string) string {
if s == nil {
return ""
}
s.mu.Lock()
defer s.mu.Unlock()
record := s.records[requestID]
if record == nil || record.ownerEdgeID != ownerEdgeID || record.phase != hotPathPhaseCleanupPending {
return ""
}
return record.cleanupStageID
}
func phaseAfterHotPathResult(kind hotPathPendingKind) hotPathLightPhase {
switch kind {
case hotPathPendingLocalTools:
return hotPathPhaseLocalActive
case hotPathPendingReviewInspection:
return hotPathPhaseReviewActive
case hotPathPendingReviewWrite:
return hotPathPhaseReviewAwaitRead
case hotPathPendingReviewRead:
return hotPathPhaseReviewResolution
case hotPathPendingReviewRepair:
return hotPathPhaseReviewRepair
default:
return ""
}
}
func (s *hotPathLightStore) matchRecordLocked(ownerEdgeID, principalRef, protocol string, lineage logicalRequestContinuationLineage) (*hotPathLightRecord, bool, error) {
var candidates []*hotPathLightRecord
for _, record := range s.records {
pendingRelated := record.pending != nil && (record.pendingHash == lineage.IssuedCallHash || hotPathPendingIDsIntersect(record, lineage.ResultIDs) || record.lineage == lineage.Prefix)
_, consumedHash := record.consumedHashes[lineage.IssuedCallHash]
if pendingRelated || consumedHash || hotPathConsumedIDsIntersect(record, lineage.ResultIDs) {
candidates = append(candidates, record)
}
}
if len(candidates) == 0 {
return nil, false, nil
}
for _, record := range candidates {
if _, replay := record.consumedHashes[lineage.IssuedCallHash]; replay {
return nil, true, fmt.Errorf("light tool frontier replay rejected")
}
}
for _, record := range candidates {
if record.pendingHash != lineage.IssuedCallHash {
continue
}
if record.ownerEdgeID != ownerEdgeID {
return nil, true, errLogicalRequestOwnerMismatch
}
if record.principalRef != principalRef {
return nil, true, errLogicalRequestPrincipal
}
if record.protocol != protocol || record.lineage != lineage.Prefix {
return nil, true, errLogicalRequestLineage
}
return record, true, nil
}
return nil, true, errLogicalRequestLineage
}
func hotPathPendingIDsIntersect(record *hotPathLightRecord, ids []string) bool {
for _, id := range ids {
if _, ok := record.pending[id]; ok {
return true
}
}
return false
}
func hotPathConsumedIDsIntersect(record *hotPathLightRecord, ids []string) bool {
for _, id := range ids {
if _, ok := record.consumedIDs[id]; ok {
return true
}
}
return false
}
func (s *hotPathLightStore) commitLocal(requestID, ownerEdgeID string, output normalizedStageOutput, correlation hotPathStageCorrelation, coordinator *logicalRequestCoordinator) (hotPathLightDisposition, error) {
if s == nil || coordinator == nil {
return hotPathLightDisposition{}, fmt.Errorf("light flow is unavailable")
}
s.mu.Lock()
defer s.mu.Unlock()
record := s.records[requestID]
if record == nil || record.ownerEdgeID != ownerEdgeID || record.phase != hotPathPhaseLocalActive || !record.running || len(output.ToolCalls) != 0 {
return hotPathLightDisposition{}, fmt.Errorf("local completion cannot transition to review")
}
reviewStageID, err := coordinator.newStageID()
if err != nil {
return hotPathLightDisposition{}, err
}
if _, err := coordinator.transitionStage(requestID, ownerEdgeID, record.localStageID, reviewStageID); err != nil {
return hotPathLightDisposition{}, err
}
correlation.StageID = record.localStageID
correlation.ResponseID = output.ResponseID
correlation.Terminal = output.TerminalReason
record.localCommit = correlation
record.reviewStageID = reviewStageID
record.phase = hotPathPhaseReviewActive
record.running = false
return hotPathLightDisposition{RequestID: requestID, StageID: reviewStageID, Phase: record.phase}, nil
}
func (s *Server) runHotPathLocalEligible(w http.ResponseWriter, r *http.Request, dispatch routeDispatch, protocol string, stream bool, metadata map[string]string) error {
requestID := strings.TrimSpace(metadata["iop_logical_request_id"])
if requestID == "" {
return s.writeHotPathLightError(w, protocol, http.StatusBadRequest, "light flow request identity is unavailable")
}
if _, err := s.lightFlows.startLocal(requestID, s.edgeIDValue(), s.requestCoordinator); err != nil {
return s.writeHotPathPrimaryError(w, r, dispatch, protocol, stream, requestID,
hotPathLightEndpointError(protocol, http.StatusBadRequest, err.Error()))
}
return s.runHotPathLightStage(w, r, dispatch, protocol, stream, requestID, hotPathOutputTokenCap(metadata))
}
func (s *Server) runHotPathLightContinuation(w http.ResponseWriter, r *http.Request, dispatch routeDispatch, protocol string, stream bool, metadata map[string]string) error {
requestID := strings.TrimSpace(metadata["iop_logical_request_id"])
if requestID == "" {
return s.writeHotPathLightError(w, protocol, http.StatusBadRequest, "light flow request identity is unavailable")
}
return s.runHotPathLightStage(w, r, dispatch, protocol, stream, requestID, hotPathOutputTokenCap(metadata))
}
func (s *Server) runHotPathLightStage(w http.ResponseWriter, r *http.Request, dispatch routeDispatch, protocol string, stream bool, requestID string, outputTokenCap int) error {
// This object is deliberately request-local. It is never stored in the
// logical-request record: a caller tool result starts a new HTTP turn and
// therefore must not retain the previous response writer or terminal.
outer := hotPathCallerOuterTurn(r, protocol, "", outputTokenCap)
if protocol == "openai" && stream {
if err := outer.setToolIDAllocator(s.requestCoordinator.newCallID); err != nil {
return err
}
if codec := hotPathChatOuterCodecFromRequest(r); codec != nil {
if err := codec.prepareProgressiveWriter(w, outer); err != nil {
return err
}
}
}
if protocol == "anthropic" && stream {
if codec := hotPathAnthropicCodecFromRequest(r); codec != nil {
if err := codec.prepareProgressiveWriter(w, outer, true); err != nil {
return err
}
}
}
var visible normalizedStageOutput
for transitions := 0; transitions < 2; transitions++ {
budget := outer.outputBudget()
if budget.Exhausted {
return s.writeHotPathLightLengthTerminal(w, r, dispatch, protocol, stream, requestID, visible)
}
if budget.MissingUsage {
return s.writeHotPathPrimaryError(w, r, dispatch, protocol, stream, requestID,
hotPathLightEndpointError(protocol, http.StatusBadGateway,
"provider output usage is required before a later Hot Path stage"))
}
snapshot, err := s.lightFlows.beginDispatch(requestID, s.edgeIDValue(), stream)
if err != nil {
// A failed dispatch acquisition does not own the record's running
// stage, so it must not abort or transfer another caller's work.
return s.writeHotPathLightError(w, protocol, http.StatusBadRequest, err.Error())
}
snapshot.OutputBudget = budget
stageStart := time.Now()
output, correlation, err := s.dispatchHotPathStage(r.Context(), r, snapshot, outer)
stageDuration := time.Since(stageStart).Seconds()
attemptDisposition := hotPathDispositionForSuccess(output.TerminalReason, len(output.ToolCalls) > 0)
if err != nil {
attemptDisposition = hotPathDispositionForError(err)
if disposition, ok := hotPathDispositionFromError(err); ok {
attemptDisposition = disposition.Kind
}
}
// Every acquired provider attempt owns exactly one stage projection,
// including provider errors, timeouts, and caller cancellation.
s.observeHotPathStage(r.Context(), hotPathModeLight, hotPathStageKindForPhase(snapshot.Phase),
hotPathAttemptBucketForTranscript(snapshot.Transcript),
hotPathTerminalDispositionFromKind(attemptDisposition), snapshot.RequestID, snapshot.StageID,
dispatch.Preset.ID, stageDuration)
if err != nil {
return s.writeHotPathPrimaryError(w, r, dispatch, protocol, stream, requestID,
hotPathLightEndpointErrorForCause(protocol, http.StatusBadGateway, snapshot.StageID, err))
}
visible = mergeVisibleStageOutput(visible, output)
// The collector compatibility path remains the endpoint renderer until
// endpoint codecs consume released deltas directly. Feed the same
// output into the sequencer now so its usage and terminal boundary span
// local→review transitions in this HTTP turn.
if len(output.ToolCalls) == 0 && !output.ProgressivelyReleased {
if err := runHotPathCollectedStage(r.Context(), outer, snapshot.StageID, output); err != nil {
return s.writeHotPathPrimaryError(w, r, dispatch, protocol, stream, requestID,
hotPathLightEndpointError(protocol, http.StatusBadGateway, err.Error()))
}
}
if len(output.ToolCalls) == 0 && hotPathIsProviderLengthTerminal(output.TerminalReason) {
return s.writeHotPathLightLengthTerminal(w, r, dispatch, protocol, stream, requestID, output)
}
switch snapshot.Phase {
case hotPathPhaseLocalActive:
if len(output.ToolCalls) > 0 {
mapped, err := s.lightFlows.issueTools(r.Context(), requestID, s.edgeIDValue(), output, visible, hotPathPendingLocalTools, outer, s.requestCoordinator)
if err != nil {
return s.writeHotPathPrimaryError(w, r, dispatch, protocol, stream, requestID,
hotPathLightEndpointError(protocol, http.StatusBadRequest, err.Error()))
}
if len(mapped.ToolCalls) == 0 && outer.outputBudget().Exhausted {
return s.writeHotPathLightLengthTerminal(w, r, dispatch, protocol, stream, requestID, mapped)
}
outer.commitTerminalSuccess(mapped.TerminalReason)
return s.writeHotPathStageResponse(w, r, dispatch, protocol, stream, requestID,
hotPathCompatibilityOutput(outer, mapped, protocol))
}
if outer.outputBudget().Exhausted {
return s.writeHotPathLightLengthTerminal(w, r, dispatch, protocol, stream, requestID, output)
}
if outer.outputBudget().MissingUsage {
return s.writeHotPathPrimaryError(w, r, dispatch, protocol, stream, requestID,
hotPathLightEndpointError(protocol, http.StatusBadGateway,
"provider output usage is required before a later Hot Path stage"))
}
if disposition, err := s.lightFlows.commitLocal(requestID, s.edgeIDValue(), output, correlation, s.requestCoordinator); err != nil {
return s.writeHotPathPrimaryError(w, r, dispatch, protocol, stream, requestID,
hotPathLightEndpointError(protocol, http.StatusBadRequest, err.Error()))
} else {
// Emit the local→review transition observation exactly once. The
// review stage id and bounded mode/stage-kind join the lifecycle.
s.observeHotPathLightTransition(r.Context(), hotPathStageKindReview, hotPathAttemptFirst,
disposition.RequestID, disposition.StageID, dispatch.Preset.ID)
}
continue
default:
final, done, err := s.advanceHotPathReview(r.Context(), requestID, snapshot.Phase, output, visible, outer, protocol)
if err != nil {
return s.writeHotPathPrimaryError(w, r, dispatch, protocol, stream, requestID,
hotPathLightEndpointError(protocol, http.StatusBadRequest, err.Error()))
}
if s.lightFlows.cleanupStage(requestID, s.edgeIDValue()) != "" {
s.observeHotPathCleanupTransition(r.Context(), requestID, dispatch.Preset.ID)
}
if done {
if len(final.ToolCalls) == 0 && outer.outputBudget().Exhausted {
return s.writeHotPathLightLengthTerminal(w, r, dispatch, protocol, stream, requestID, final)
}
outer.commitTerminalSuccess(final.TerminalReason)
return s.writeHotPathStageResponse(w, r, dispatch, protocol, stream, requestID,
hotPathCompatibilityOutput(outer, final, protocol))
}
}
}
message := "light flow exceeded the fixed internal transition bound"
return s.writeHotPathPrimaryError(w, r, dispatch, protocol, stream, requestID,
hotPathLightEndpointError(protocol, http.StatusInternalServerError, message))
}
// writeHotPathLightLengthTerminal writes the endpoint response for a light-mode
// request that terminates by provider length or output-budget exhaustion without
// entering the cleanup phase, then emits its exactly-once outer terminal
// observation with the winning disposition. It is the non-cleanup peer of
// writeHotPathTerminal's cleanup-ending terminal owner: the two light sub-paths
// are disjoint (cleanup-ending vs length/budget), so a light request still emits
// exactly one terminal. Following the cleanup post-write ownership rule, the
// intended length terminal is resolved against the endpoint write result through
// resolveHotPathObservedDisposition, so a caller-canceled or timed-out response
// write wins over length instead of publishing length before the caller
// disposition can be selected. Preset state is closed before the write and the
// response write error is preserved as the return value. The resolved
// disposition is a closed enum, so raw error text never reaches logs or metric
// labels (SDD S15).
func (s *Server) writeHotPathLightLengthTerminal(w http.ResponseWriter, r *http.Request, dispatch routeDispatch, protocol string, stream bool, requestID string, output normalizedStageOutput) error {
outer := hotPathCurrentCallerOuterTurn(r, protocol)
outer.commitLengthTerminal()
s.terminalPresetRequest(requestID, s.edgeIDValue())
endpointWriteErr := s.writeHotPathStageResponse(w, r, dispatch, protocol, stream, requestID,
hotPathCompatibilityOutput(outer, output, protocol))
winning := resolveHotPathObservedDisposition(outer, hotPathTerminalDisposition{
Kind: hotPathDispositionLength, Source: "light_length",
}, endpointWriteErr)
s.observeHotPathTerminal(r.Context(), hotPathModeLight,
hotPathTerminalDispositionFromKind(winning.Kind), requestID, winning.StageID, dispatch.Preset.ID)
return endpointWriteErr
}
func hotPathIsProviderLengthTerminal(reason string) bool {
switch strings.TrimSpace(reason) {
case "length", "max_tokens":
return true
default:
return false
}
}
func (output normalizedStageOutput) StageResponseOverlay(visible normalizedStageOutput) normalizedStageOutput {
visible.ResponseID = output.ResponseID
visible.Created = output.Created
visible.ToolCalls = cloneNormalizedStageOutput(output).ToolCalls
visible.TerminalReason = output.TerminalReason
visible.Usage = cloneRawJSON(output.Usage)
visible.OpenAIUsage = output.OpenAIUsage
return visible
}
func mergeVisibleStageOutput(left, right normalizedStageOutput) normalizedStageOutput {
if strings.TrimSpace(left.ResponseID) == "" {
return cloneNormalizedStageOutput(right)
}
out := cloneNormalizedStageOutput(right)
out.Content = joinVisibleText(left.Content, right.Content)
out.Reasoning = joinVisibleText(left.Reasoning, right.Reasoning)
return out
}
func joinVisibleText(left, right string) string {
if left == "" {
return right
}
if right == "" {
return left
}
return left + "\n" + right
}
func (s *Server) writeHotPathStageResponse(w http.ResponseWriter, r *http.Request, dispatch routeDispatch, protocol string, stream bool, requestID string, output normalizedStageOutput) error {
turn := &hotPathTurn{
RequestID: requestID, OwnerEdgeID: s.edgeIDValue(), Dispatch: dispatch,
Protocol: protocol, Stream: stream, PublicModelID: dispatch.ExternalModelID,
Writer: w, Request: r,
}
turn.OuterTurn = hotPathCurrentCallerOuterTurn(r, protocol)
return s.writeDirectResponse(turn, output)
}
func hotPathCurrentCallerOuterTurn(r *http.Request, protocol string) *hotPathOuterTurn {
switch protocol {
case "openai":
if codec := hotPathChatOuterCodecFromRequest(r); codec != nil {
return codec.currentOuterTurn()
}
case "anthropic":
if codec := hotPathAnthropicCodecFromRequest(r); codec != nil {
return codec.currentOuterTurn()
}
}
return nil
}
func (s *Server) writeHotPathLightError(w http.ResponseWriter, protocol string, status int, message string) error {
disposition := hotPathTerminalDisposition{
Kind: hotPathDispositionProviderError, Cause: message, Source: "light_flow",
}
if status >= http.StatusBadRequest && status < http.StatusInternalServerError {
disposition.Kind = hotPathDispositionValidationError
}
if protocol == "anthropic" {
policy := anthropicHotPathPolicy(disposition)
writeAnthropicError(w, policy.status, policy.errorType, message)
} else {
policy := chatHotPathPolicy(disposition)
writeError(w, policy.status, policy.errorType, message)
}
return fmt.Errorf("%s", message)
}
func (s *Server) dispatchHotPathStage(ctx context.Context, r *http.Request, snapshot hotPathDispatchSnapshot, outer *hotPathOuterTurn) (normalizedStageOutput, hotPathStageCorrelation, error) {
return s.submitHotPathStage(ctx, r, snapshot, outer)
}
// Compile-time assertion that the stage dispatcher still uses the same
// surface-neutral service request type as selector dispatch.
var _ = edgeservice.ProviderPoolDispatchRequest{}