837 lines
31 KiB
Go
837 lines
31 KiB
Go
package openai
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
"sync"
|
|
|
|
edgeservice "iop/apps/edge/internal/service"
|
|
"iop/packages/go/config"
|
|
)
|
|
|
|
const defaultHotPathLightCapacity = 1024
|
|
|
|
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
|
|
|
|
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
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
type hotPathLightDisposition struct {
|
|
RequestID string
|
|
StageID string
|
|
Phase 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.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) issueTools(
|
|
requestID, ownerEdgeID string,
|
|
output normalizedStageOutput,
|
|
visible normalizedStageOutput,
|
|
kind hotPathPendingKind,
|
|
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")
|
|
}
|
|
mapped, pending, err := mapHotPathStageCalls(record, output, kind, coordinator)
|
|
if err != nil {
|
|
return normalizedStageOutput{}, err
|
|
}
|
|
mapped = mapped.StageResponseOverlay(visible)
|
|
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})
|
|
}
|
|
stageID := record.localStageID
|
|
if kind != hotPathPendingLocalTools {
|
|
stageID = record.reviewStageID
|
|
}
|
|
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) (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")
|
|
}
|
|
|
|
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
|
|
}
|
|
} 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)
|
|
}
|
|
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{}
|
|
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}, true, nil
|
|
}
|
|
|
|
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)
|
|
}
|
|
|
|
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)
|
|
}
|
|
|
|
func (s *Server) runHotPathLightStage(w http.ResponseWriter, r *http.Request, dispatch routeDispatch, protocol string, stream bool, requestID string) error {
|
|
var visible normalizedStageOutput
|
|
for transitions := 0; transitions < 2; transitions++ {
|
|
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())
|
|
}
|
|
output, correlation, err := s.dispatchHotPathStage(r.Context(), r, snapshot)
|
|
if err != nil {
|
|
return s.writeHotPathPrimaryError(w, r, dispatch, protocol, stream, requestID,
|
|
hotPathLightEndpointError(protocol, http.StatusBadGateway, err.Error()))
|
|
}
|
|
visible = mergeVisibleStageOutput(visible, output)
|
|
|
|
switch snapshot.Phase {
|
|
case hotPathPhaseLocalActive:
|
|
if len(output.ToolCalls) > 0 {
|
|
mapped, err := s.lightFlows.issueTools(requestID, s.edgeIDValue(), output, visible, hotPathPendingLocalTools, s.requestCoordinator)
|
|
if err != nil {
|
|
return s.writeHotPathPrimaryError(w, r, dispatch, protocol, stream, requestID,
|
|
hotPathLightEndpointError(protocol, http.StatusBadRequest, err.Error()))
|
|
}
|
|
return s.writeHotPathStageResponse(w, r, dispatch, protocol, stream, requestID, mapped)
|
|
}
|
|
if _, 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()))
|
|
}
|
|
continue
|
|
default:
|
|
final, done, err := s.advanceHotPathReview(r.Context(), requestID, snapshot.Phase, output, visible)
|
|
if err != nil {
|
|
return s.writeHotPathPrimaryError(w, r, dispatch, protocol, stream, requestID,
|
|
hotPathLightEndpointError(protocol, http.StatusBadRequest, err.Error()))
|
|
}
|
|
if done {
|
|
return s.writeHotPathStageResponse(w, r, dispatch, protocol, stream, requestID, final)
|
|
}
|
|
}
|
|
}
|
|
message := "light flow exceeded the fixed internal transition bound"
|
|
return s.writeHotPathPrimaryError(w, r, dispatch, protocol, stream, requestID,
|
|
hotPathLightEndpointError(protocol, http.StatusInternalServerError, message))
|
|
}
|
|
|
|
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,
|
|
}
|
|
return s.writeDirectResponse(turn, output)
|
|
}
|
|
|
|
func (s *Server) writeHotPathLightError(w http.ResponseWriter, protocol string, status int, message string) error {
|
|
if protocol == "anthropic" {
|
|
writeAnthropicError(w, status, "api_error", message)
|
|
} else {
|
|
writeError(w, status, "run_error", message)
|
|
}
|
|
return fmt.Errorf("%s", message)
|
|
}
|
|
|
|
func (s *Server) dispatchHotPathStage(ctx context.Context, r *http.Request, snapshot hotPathDispatchSnapshot) (normalizedStageOutput, hotPathStageCorrelation, error) {
|
|
return s.submitHotPathStage(ctx, r, snapshot)
|
|
}
|
|
|
|
// Compile-time assertion that the stage dispatcher still uses the same
|
|
// surface-neutral service request type as selector dispatch.
|
|
var _ = edgeservice.ProviderPoolDispatchRequest{}
|