package openai import ( "context" "encoding/json" "fmt" "net/http" "net/http/httptest" "strings" "sync" "testing" edgeservice "iop/apps/edge/internal/service" "iop/packages/go/config" ) func TestHotPathLightLocalTransition(t *testing.T) { for _, endpoint := range []string{"openai", "anthropic"} { endpoint := endpoint t.Run(endpoint, func(t *testing.T) { fixture := newScriptedLightFixture(t, endpoint, false) final := fixture.run() if final.Code != http.StatusOK || !strings.Contains(final.Body.String(), "review-resolution-visible") { t.Fatalf("final response: status=%d body=%s", final.Code, final.Body.String()) } history, _ := json.Marshal(fixture.history) if !strings.Contains(string(history), "local-complete-visible") { t.Fatalf("local completion was not visible before review: history=%s", history) } fixture.assertCleanupCommitted(7) }) } } func TestHotPathStageInputIsolation(t *testing.T) { paths := newReservedPaths("req_stage_isolation") selector := hotPathStageCorrelation{StageID: "stg_selector", ResponseID: "provider:selector.actual/1", RunID: "run-selector", ProviderID: "provider.actual", Terminal: "stop,done\"quoted\""} local := hotPathStageCorrelation{StageID: "stg_local", ResponseID: "provider:local.actual/2", RunID: "run-local", ProviderID: "provider.actual", Terminal: "tool_calls,stop"} localInput := buildLocalStageInput("immutable user task", paths, selector) reviewInput := buildReviewStageInput("immutable user task", paths, selector, local) for _, input := range []hotPathStageInput{localInput, reviewInput} { phase := hotPathPhaseLocalActive if input.Role == "review" { phase = hotPathPhaseReviewActive } prompt, err := input.prompt(phase) if err != nil { t.Fatal(err) } for _, forbidden := range []string{"PLAN_FILE_SECRET", "credential-secret", "previous internal prompt", "provider-target.internal"} { if strings.Contains(prompt, forbidden) { t.Fatalf("stage prompt leaked %q: %s", forbidden, prompt) } } if !strings.Contains(prompt, "immutable user task") || !strings.Contains(prompt, paths.PlanPath) || !strings.Contains(prompt, paths.ReviewPath) { t.Fatalf("stage prompt omitted immutable input: %s", prompt) } // Exact committed selector correlation must be present for both roles. if !strings.Contains(prompt, "Committed selector stage success:") { t.Fatalf("prompt missing committed selector correlation: %s", prompt) } if !strings.Contains(prompt, selector.StageID) || !strings.Contains(prompt, selector.RunID) { t.Fatalf("prompt omitted exact selector correlation fields: %s", prompt) } // Verify serialized JSON block decoding and single-line format selHeaderIdx := strings.Index(prompt, "Committed selector stage success:\n") if selHeaderIdx == -1 { t.Fatalf("prompt missing selector header format") } selJSONLine := prompt[selHeaderIdx+len("Committed selector stage success:\n"):] if newlineIdx := strings.IndexByte(selJSONLine, '\n'); newlineIdx != -1 { selJSONLine = selJSONLine[:newlineIdx] } var selDecoded correlationPromptValue if err := json.Unmarshal([]byte(selJSONLine), &selDecoded); err != nil { t.Fatalf("failed to decode selector correlation JSON line %q: %v", selJSONLine, err) } if selDecoded.StageID != selector.StageID || selDecoded.ResponseID != selector.ResponseID || selDecoded.RunID != selector.RunID || selDecoded.ProviderID != selector.ProviderID || selDecoded.Terminal != selector.Terminal { t.Fatalf("decoded selector correlation mismatch: got %#v want %#v", selDecoded, selector) } // Local stage must NOT carry a local correlation. if input.Role == "local" { if strings.Contains(prompt, "Committed local stage success:") { t.Fatalf("local prompt leaked local correlation: %s", prompt) } if strings.Contains(prompt, local.StageID) { t.Fatalf("local prompt contained local correlation fields: %s", prompt) } } // Review stage must carry both selector and local correlations. if input.Role == "review" { if !strings.Contains(prompt, "Committed local stage success:") { t.Fatalf("review prompt missing committed local correlation: %s", prompt) } if !strings.Contains(prompt, local.StageID) || !strings.Contains(prompt, local.RunID) { t.Fatalf("review prompt omitted exact local correlation fields: %s", prompt) } locHeaderIdx := strings.Index(prompt, "Committed local stage success:\n") if locHeaderIdx == -1 { t.Fatalf("prompt missing local header format") } locJSONLine := prompt[locHeaderIdx+len("Committed local stage success:\n"):] if newlineIdx := strings.IndexByte(locJSONLine, '\n'); newlineIdx != -1 { locJSONLine = locJSONLine[:newlineIdx] } var locDecoded correlationPromptValue if err := json.Unmarshal([]byte(locJSONLine), &locDecoded); err != nil { t.Fatalf("failed to decode local correlation JSON line %q: %v", locJSONLine, err) } if locDecoded.StageID != local.StageID || locDecoded.ResponseID != local.ResponseID || locDecoded.RunID != local.RunID || locDecoded.ProviderID != local.ProviderID || locDecoded.Terminal != local.Terminal { t.Fatalf("decoded local correlation mismatch: got %#v want %#v", locDecoded, local) } } } // Test invalid correlation field values fail closed for opaque fields. invalidOpaqueValues := []string{ "", "invalid\nvalue", "invalid\rvalue", "invalid\tvalue", strings.Repeat("a", 257), } for _, invalid := range invalidOpaqueValues { // Mutate Selector ResponseID selBadResponse := selector selBadResponse.ResponseID = invalid inputBadSelResponse := buildLocalStageInput("immutable user task", paths, selBadResponse) if p, err := inputBadSelResponse.prompt(hotPathPhaseLocalActive); err == nil || p != "" { t.Fatalf("selector ResponseID %q accepted: prompt=%q, err=%v", invalid, p, err) } // Mutate Selector ProviderID selBadProvider := selector selBadProvider.ProviderID = invalid inputBadSelProvider := buildLocalStageInput("immutable user task", paths, selBadProvider) if p, err := inputBadSelProvider.prompt(hotPathPhaseLocalActive); err == nil || p != "" { t.Fatalf("selector ProviderID %q accepted: prompt=%q, err=%v", invalid, p, err) } // Mutate Selector Terminal selBadTerminal := selector selBadTerminal.Terminal = invalid inputBadSelTerminal := buildLocalStageInput("immutable user task", paths, selBadTerminal) if p, err := inputBadSelTerminal.prompt(hotPathPhaseLocalActive); err == nil || p != "" { t.Fatalf("selector Terminal %q accepted: prompt=%q, err=%v", invalid, p, err) } // Mutate Local ResponseID in review stage localBadResponse := local localBadResponse.ResponseID = invalid inputBadLocalResponse := buildReviewStageInput("immutable user task", paths, selector, localBadResponse) if p, err := inputBadLocalResponse.prompt(hotPathPhaseReviewActive); err == nil || p != "" { t.Fatalf("local ResponseID %q accepted in review stage: prompt=%q, err=%v", invalid, p, err) } // Mutate Local ProviderID in review stage localBadProvider := local localBadProvider.ProviderID = invalid inputBadLocalProvider := buildReviewStageInput("immutable user task", paths, selector, localBadProvider) if p, err := inputBadLocalProvider.prompt(hotPathPhaseReviewActive); err == nil || p != "" { t.Fatalf("local ProviderID %q accepted in review stage: prompt=%q, err=%v", invalid, p, err) } // Mutate Local Terminal in review stage localBadTerminal := local localBadTerminal.Terminal = invalid inputBadLocalTerminal := buildReviewStageInput("immutable user task", paths, selector, localBadTerminal) if p, err := inputBadLocalTerminal.prompt(hotPathPhaseReviewActive); err == nil || p != "" { t.Fatalf("local Terminal %q accepted in review stage: prompt=%q, err=%v", invalid, p, err) } } // Test invalid IOP-owned ID field values fail closed. invalidLogicalIDs := []string{ "", "invalid:value", "invalid,value", "invalid.value", "invalid\nvalue", strings.Repeat("a", 257), } for _, invalid := range invalidLogicalIDs { selBadStage := selector selBadStage.StageID = invalid if p, err := buildLocalStageInput("immutable user task", paths, selBadStage).prompt(hotPathPhaseLocalActive); err == nil || p != "" { t.Fatalf("selector StageID %q accepted: prompt=%q, err=%v", invalid, p, err) } selBadRun := selector selBadRun.RunID = invalid if p, err := buildLocalStageInput("immutable user task", paths, selBadRun).prompt(hotPathPhaseLocalActive); err == nil || p != "" { t.Fatalf("selector RunID %q accepted: prompt=%q, err=%v", invalid, p, err) } } pinned := routeDispatch{ Managed: true, PrincipalRef: "principal", ModelGroupKey: "local-model", RouteID: "route-local", CredentialSlotRef: "slot-local", ProfileID: "profile", UpstreamModel: "served-local", ResourceSelector: "resource", RouteRevision: 7, CredentialRevision: 11, ProjectionGeneration: 13, } changed := pinned changed.CredentialRevision++ if samePinnedHotPathRoute(pinned, changed) { t.Fatal("credential revision drift was accepted") } changed = pinned changed.RouteRevision++ if samePinnedHotPathRoute(pinned, changed) { t.Fatal("route revision drift was accepted") } } type scriptedLightPoolService struct { providerFakeRunService mu sync.Mutex endpoint string candidate edgeservice.ProviderPoolCandidate responses []func(string) string requests []edgeservice.ProviderPoolDispatchRequest } func (s *scriptedLightPoolService) SubmitProviderPool(_ context.Context, req edgeservice.ProviderPoolDispatchRequest) (*edgeservice.ProviderPoolDispatchResult, error) { s.mu.Lock() index := len(s.requests) s.requests = append(s.requests, req) if index >= len(s.responses) { s.mu.Unlock() return nil, fmt.Errorf("unexpected light stage dispatch %d", index+1) } response := s.responses[index] candidate := s.candidate endpoint := s.endpoint s.mu.Unlock() requestID := req.Run.Metadata["iop_logical_request_id"] body := response(requestID) dispatch := edgeservice.RunDispatch{ RunID: fmt.Sprintf("run-light-%d", index+1), NodeID: "node-light", ModelGroupKey: req.Run.ModelGroupKey, ProviderID: candidate.ProviderID, ExecutionPath: string(edgeservice.ProviderPoolPathTunnel), ProfileID: candidate.ProfileID, ProfileDriver: candidate.ProfileDriver, ProfileCapabilities: append([]string(nil), candidate.ProfileCapabilities...), } frames := staticProviderTunnelFrames(body) if endpoint == "anthropic" { frames = anthropicTunnelFrames(http.StatusOK, "application/json", []byte(body)) } return &edgeservice.ProviderPoolDispatchResult{ Path: edgeservice.ProviderPoolPathTunnel, Tunnel: &fakeTunnelHandle{dispatch: dispatch, frames: frames}, DispatchInfo: dispatch, }, nil } func (s *scriptedLightPoolService) snapshots() []edgeservice.ProviderPoolDispatchRequest { s.mu.Lock() defer s.mu.Unlock() return append([]edgeservice.ProviderPoolDispatchRequest(nil), s.requests...) } type scriptedLightFixture struct { t *testing.T endpoint string server *Server service *scriptedLightPoolService tools []any history []any repair bool } func newScriptedLightFixture(t *testing.T, endpoint string, repair bool) *scriptedLightFixture { t.Helper() candidate := anthropicTestCandidate(t, map[string]string{"openai": "openai", "anthropic": "anthropic"}[endpoint]) service := &scriptedLightPoolService{endpoint: endpoint, candidate: candidate} service.responses = []func(string) string{ func(requestID string) string { return scriptedArtifactPrepare(endpoint, requestID) }, func(requestID string) string { return scriptedArtifactPair(endpoint, requestID) }, func(requestID string) string { return scriptedArtifactLocalRead(endpoint, requestID) }, func(string) string { return scriptedLightCompletion(endpoint, "local-complete-visible") }, func(requestID string) string { return scriptedReviewWrite(endpoint, requestID) }, func(requestID string) string { return scriptedReviewRead(endpoint, requestID) }, } if repair { service.responses = append(service.responses, func(string) string { return scriptedRepairTool(endpoint) }, func(string) string { return scriptedLightCompletion(endpoint, "repair-complete-visible") }, ) } else { service.responses = append(service.responses, func(string) string { return scriptedLightCompletion(endpoint, "review-resolution-visible PASS and DEFECT prose") }) } preset := hotPathSelectorPreset([]string{config.ModeDirect, config.ModeLight}) preset.WorkspaceTools = []config.ExecutionWorkspaceToolAlternative{scriptedLightWorkspaceAlternative()} server := NewServer(config.EdgeOpenAIConf{}, service, nil) server.SetEdgeID("edge-scripted-light") server.SetExecutionPresets([]config.ExecutionPreset{preset}) server.SetModelCatalog([]config.ModelCatalogEntry{ {ID: "virtual-model", ExecutionPreset: preset.ID}, {ID: "selector-model", Providers: map[string]string{candidate.ProviderID: "served-selector"}}, {ID: "local-model", Providers: map[string]string{candidate.ProviderID: "served-local"}}, {ID: "review-model", Providers: map[string]string{candidate.ProviderID: "served-review"}}, }) tools := scriptedLightTools(endpoint) return &scriptedLightFixture{ t: t, endpoint: endpoint, server: server, service: service, tools: tools, history: []any{map[string]any{"role": "user", "content": "immutable user task"}}, repair: repair, } } func scriptedLightWorkspaceAlternative() config.ExecutionWorkspaceToolAlternative { matcher := successMatcher() return config.ExecutionWorkspaceToolAlternative{ Name: "scripted-light-tools", Operations: map[string]config.ExecutionWorkspaceOperation{ "prepare": {ToolName: "mkdir_p", SchemaMatcher: map[string]any{"type": "object"}, ArgumentMap: map[string]any{"path": "path"}, ResultMatcher: matcher, CreatesParents: true}, "read": {ToolName: "read_file", SchemaMatcher: map[string]any{"type": "object"}, ArgumentMap: map[string]any{"path": "path"}, ResultMatcher: matcher}, "write": {ToolName: "write_file", SchemaMatcher: map[string]any{"type": "object"}, ArgumentMap: map[string]any{"path": "path", "content": "content"}, ResultMatcher: matcher, CreatesParents: false}, "delete": {ToolName: "delete_file", SchemaMatcher: map[string]any{"type": "object"}, ArgumentMap: map[string]any{"path": "path"}, ResultMatcher: matcher}, }, } } func scriptedLightTools(endpoint string) []any { tools := scriptedArtifactTools(endpoint) schema := map[string]any{"type": "object", "properties": map[string]any{"command": map[string]any{"type": "string"}}, "required": []any{"command"}} if endpoint == "anthropic" { return append(tools, anthropicWorkspaceTool("run_command", schema)) } return append(tools, openAIChatTool("run_command", schema)) } func (f *scriptedLightFixture) run() *httptest.ResponseRecorder { f.t.Helper() cleanup := f.runToCleanup() f.consumeToolResponse(cleanup, []string{`{"written":true}`}) return f.request() } func (f *scriptedLightFixture) runToCleanup() *httptest.ResponseRecorder { f.t.Helper() prepare := f.request() f.consumeToolResponse(prepare, []string{`{"written":true}`}) pair := f.request() f.consumeToolResponse(pair, []string{`{"written":true}`, `{"written":true}`}) localRead := f.request() f.consumeToolResponse(localRead, []string{`{"written":true}`}) reviewWrite := f.request() f.consumeToolResponse(reviewWrite, []string{`{"written":true}`}) reviewRead := f.request() f.consumeToolResponse(reviewRead, []string{`{"written":true}`}) resolution := f.request() if !f.repair { return resolution } f.consumeToolResponse(resolution, []string{`{"ok":true}`}) return f.request() } func (f *scriptedLightFixture) request() *httptest.ResponseRecorder { f.t.Helper() body := scriptedArtifactRequestBody(f.t, f.endpoint, f.tools, f.history) return serveScriptedArtifactRequest(f.t, f.server, f.endpoint, body) } func (f *scriptedLightFixture) consumeToolResponse(response *httptest.ResponseRecorder, results []string) { f.t.Helper() if response.Code != http.StatusOK { f.t.Fatalf("tool response status=%d body=%s", response.Code, response.Body.String()) } assistant, ids, err := artifactAssistantFromResponse(f.endpoint, response.Body.Bytes()) if err != nil || len(ids) != len(results) { f.t.Fatalf("decode tool response: ids=%v results=%v err=%v body=%s", ids, results, err, response.Body.String()) } f.history = append(f.history, assistant) f.history = scriptedArtifactAppendResults(f.endpoint, f.history, ids, results) } func (f *scriptedLightFixture) assertCleanupCommitted(wantCalls int) { f.t.Helper() requests := f.service.snapshots() if len(requests) != wantCalls { f.t.Fatalf("provider calls=%d, want %d", len(requests), wantCalls) } if requests[0].Run.ModelGroupKey != "selector-model" || requests[1].Run.ModelGroupKey != "selector-model" { f.t.Fatalf("selector model groups changed: %q %q", requests[0].Run.ModelGroupKey, requests[0].Run.ModelGroupKey) } if requests[2].Run.ModelGroupKey != "local-model" || requests[3].Run.ModelGroupKey != "local-model" { f.t.Fatalf("local model group changed: %q %q", requests[2].Run.ModelGroupKey, requests[3].Run.ModelGroupKey) } localStage := requests[2].Run.Metadata["iop_stage_id"] if localStage == "" || requests[3].Run.Metadata["iop_stage_id"] != localStage { f.t.Fatalf("local stage was not resumed: %#v %#v", requests[2].Run.Metadata, requests[3].Run.Metadata) } reviewStage := requests[4].Run.Metadata["iop_stage_id"] if reviewStage == "" || reviewStage == localStage { f.t.Fatalf("review stage identity is not fixed and distinct: local=%q review=%q", localStage, reviewStage) } for index := 4; index < len(requests); index++ { if requests[index].Run.ModelGroupKey != "review-model" || requests[index].Run.Metadata["iop_stage_id"] != reviewStage { f.t.Fatalf("review dispatch %d changed binding: group=%q metadata=%#v", index, requests[index].Run.ModelGroupKey, requests[index].Run.Metadata) } } selectorStage := requests[1].Run.Metadata["iop_stage_id"] selectorResponse := "chatcmpl-scripted-pair" if f.endpoint == "anthropic" { selectorResponse = "msg-scripted-pair" } localResponse := "chatcmpl-light-complete" if f.endpoint == "anthropic" { localResponse = "msg-light-complete" } // Regression: local stage must carry selector correlation and must NOT // carry local correlation in both normalized Run.Input and tunnel body. assertLocalCorrelationRegression(f.t, requests[2], f.service.candidate, selectorStage, selectorResponse) assertLocalCorrelationRegression(f.t, requests[3], f.service.candidate, selectorStage, selectorResponse) // Regression: review stage must carry both selector and local correlations // in both normalized Run.Input and tunnel body. assertReviewCorrelationRegression(f.t, requests[4], f.service.candidate, selectorStage, selectorResponse, localStage, localResponse) assertReviewCorrelationRegression(f.t, requests[5], f.service.candidate, selectorStage, selectorResponse, localStage, localResponse) // Regression: forbidden data must not appear in any provider-visible payload. for index, req := range requests { for _, forbidden := range []string{"PLAN_FILE_SECRET", "credential-secret", "previous internal prompt", "provider-target.internal"} { if strings.Contains(req.Run.Prompt, forbidden) { f.t.Fatalf("request %d Run.Prompt leaked %q", index, forbidden) } if body, ok := req.Run.Input["prompt"]; ok { if strings.Contains(fmt.Sprint(body), forbidden) { f.t.Fatalf("request %d Run.Input[\"prompt\"] leaked %q", index, forbidden) } } } } f.assertCleanupStoresRemoved() } func (f *scriptedLightFixture) assertCleanupStoresRemoved() { f.t.Helper() f.server.lightFlows.mu.Lock() lightCount := len(f.server.lightFlows.records) f.server.lightFlows.mu.Unlock() if lightCount != 0 { f.t.Fatalf("light records=%d, want 0 after cleanup commit", lightCount) } f.server.artifactFrontiers.mu.Lock() artifactCount := len(f.server.artifactFrontiers.records) f.server.artifactFrontiers.mu.Unlock() if artifactCount != 0 { f.t.Fatalf("artifact records=%d, want 0 after cleanup commit", artifactCount) } f.server.requestCoordinator.mu.Lock() coordinatorCount := len(f.server.requestCoordinator.requests) f.server.requestCoordinator.mu.Unlock() if coordinatorCount != 0 { f.t.Fatalf("coordinator records=%d, want 0 after cleanup commit", coordinatorCount) } } // assertLocalCorrelationRegression verifies that a captured local-stage request // carries the committed selector correlation in Run.Prompt, Run.Input["prompt"], // and the decoded tunnel body, while omitting any local-stage correlation. func assertLocalCorrelationRegression(t *testing.T, req edgeservice.ProviderPoolDispatchRequest, selected edgeservice.ProviderPoolCandidate, selectorStage, selectorResponse string) { t.Helper() prompt := req.Run.Prompt if prompt == "" { t.Fatalf("local request prompt is empty") } input, ok := req.Run.Input["prompt"] if !ok || input == nil { t.Fatalf("local Run.Input[\"prompt\"] is missing") } inputStr := fmt.Sprint(input) if !strings.Contains(prompt, "Committed selector stage success:") { t.Fatalf("local Run.Prompt missing selector correlation: %s", prompt) } if !strings.Contains(prompt, selectorStage) || !strings.Contains(prompt, selectorResponse) { t.Fatalf("local Run.Prompt missing exact selector stage/response %q/%q: %s", selectorStage, selectorResponse, prompt) } if !strings.Contains(inputStr, "Committed selector stage success:") { t.Fatalf("local Run.Input[\"prompt\"] missing selector correlation: %v", input) } if !strings.Contains(inputStr, selectorStage) || !strings.Contains(inputStr, selectorResponse) { t.Fatalf("local Run.Input[\"prompt\"] missing exact selector stage/response %q/%q: %v", selectorStage, selectorResponse, input) } if strings.Contains(prompt, "Committed local stage success:") { t.Fatalf("local Run.Prompt leaked local correlation: %s", prompt) } if strings.Contains(inputStr, "Committed local stage success:") { t.Fatalf("local Run.Input[\"prompt\"] leaked local correlation: %v", input) } // Mandatory: decode and verify selected protocol tunnel prompt. _, tunnelPrompt, err := decodeSelectedTunnelPrompt(req, selected) if err != nil { t.Fatalf("local tunnel decode error: %v", err) } if tunnelPrompt != prompt { t.Fatalf("local decoded tunnel prompt mismatch: got %q want %q", tunnelPrompt, prompt) } if !strings.Contains(tunnelPrompt, "Committed selector stage success:") { t.Fatalf("local tunnel body missing selector correlation: %s", tunnelPrompt) } if !strings.Contains(tunnelPrompt, selectorStage) || !strings.Contains(tunnelPrompt, selectorResponse) { t.Fatalf("local tunnel body missing exact selector stage/response %q/%q: %s", selectorStage, selectorResponse, tunnelPrompt) } if strings.Contains(tunnelPrompt, "Committed local stage success:") { t.Fatalf("local tunnel body leaked local correlation: %s", tunnelPrompt) } } // assertReviewCorrelationRegression verifies that a captured review-stage request // carries both committed selector and local correlations in Run.Prompt, // Run.Input["prompt"], and the decoded tunnel body. func assertReviewCorrelationRegression(t *testing.T, req edgeservice.ProviderPoolDispatchRequest, selected edgeservice.ProviderPoolCandidate, selectorStage, selectorResponse, localStage, localResponse string) { t.Helper() prompt := req.Run.Prompt if prompt == "" { t.Fatalf("review request prompt is empty") } input, ok := req.Run.Input["prompt"] if !ok || input == nil { t.Fatalf("review Run.Input[\"prompt\"] is missing") } inputStr := fmt.Sprint(input) if !strings.Contains(prompt, "Committed selector stage success:") { t.Fatalf("review Run.Prompt missing selector correlation: %s", prompt) } if !strings.Contains(prompt, "Committed local stage success:") { t.Fatalf("review Run.Prompt missing local correlation: %s", prompt) } if !strings.Contains(prompt, selectorStage) || !strings.Contains(prompt, selectorResponse) { t.Fatalf("review Run.Prompt missing exact selector stage/response %q/%q: %s", selectorStage, selectorResponse, prompt) } if !strings.Contains(prompt, localStage) || !strings.Contains(prompt, localResponse) { t.Fatalf("review Run.Prompt missing exact local stage/response %q/%q: %s", localStage, localResponse, prompt) } if !strings.Contains(inputStr, "Committed selector stage success:") { t.Fatalf("review Run.Input[\"prompt\"] missing selector correlation: %v", input) } if !strings.Contains(inputStr, "Committed local stage success:") { t.Fatalf("review Run.Input[\"prompt\"] missing local correlation: %v", input) } if !strings.Contains(inputStr, selectorStage) || !strings.Contains(inputStr, selectorResponse) { t.Fatalf("review Run.Input[\"prompt\"] missing exact selector stage/response %q/%q: %v", selectorStage, selectorResponse, input) } if !strings.Contains(inputStr, localStage) || !strings.Contains(inputStr, localResponse) { t.Fatalf("review Run.Input[\"prompt\"] missing exact local stage/response %q/%q: %v", localStage, localResponse, input) } // Mandatory: decode and verify selected protocol tunnel prompt. _, tunnelPrompt, err := decodeSelectedTunnelPrompt(req, selected) if err != nil { t.Fatalf("review tunnel decode error: %v", err) } if tunnelPrompt != prompt { t.Fatalf("review decoded tunnel prompt mismatch: got %q want %q", tunnelPrompt, prompt) } if !strings.Contains(tunnelPrompt, "Committed selector stage success:") { t.Fatalf("review tunnel body missing selector correlation: %s", tunnelPrompt) } if !strings.Contains(tunnelPrompt, "Committed local stage success:") { t.Fatalf("review tunnel body missing local correlation: %s", tunnelPrompt) } if !strings.Contains(tunnelPrompt, selectorStage) || !strings.Contains(tunnelPrompt, selectorResponse) { t.Fatalf("review tunnel body missing exact selector stage/response %q/%q: %s", selectorStage, selectorResponse, tunnelPrompt) } if !strings.Contains(tunnelPrompt, localStage) || !strings.Contains(tunnelPrompt, localResponse) { t.Fatalf("review tunnel body missing exact local stage/response %q/%q: %s", localStage, localResponse, tunnelPrompt) } } // decodeSelectedTunnelPrompt invokes PrepareProtocolTunnel unconditionally, builds the protocol // body, checks expected path/op for OpenAI vs Anthropic, and extracts the first user message content string. func decodeSelectedTunnelPrompt(req edgeservice.ProviderPoolDispatchRequest, selected edgeservice.ProviderPoolCandidate) (edgeservice.SubmitProviderTunnelRequest, string, error) { if req.PrepareProtocolTunnel == nil { return edgeservice.SubmitProviderTunnelRequest{}, "", fmt.Errorf("PrepareProtocolTunnel is not set") } prepared, err := req.PrepareProtocolTunnel(req.Tunnel, selected) if err != nil { return prepared, "", fmt.Errorf("PrepareProtocolTunnel error: %w", err) } if prepared.BuildBody == nil { return prepared, "", fmt.Errorf("BuildBody is not set after PrepareProtocolTunnel") } bodyBytes, err := prepared.BuildBody("target-model") if err != nil { return prepared, "", fmt.Errorf("BuildBody error: %w", err) } if selected.ProfileDriver == string(config.ProtocolDriverAnthropicMessages) { if prepared.Path != "/v1/messages" || prepared.Operation != string(config.OperationMessages) { return prepared, "", fmt.Errorf("anthropic tunnel path/op mismatch: path=%q op=%q", prepared.Path, prepared.Operation) } var payload struct { Messages []struct { Role string `json:"role"` Content any `json:"content"` } `json:"messages"` } if err := json.Unmarshal(bodyBytes, &payload); err != nil { return prepared, "", fmt.Errorf("unmarshal anthropic payload: %w (body=%s)", err, string(bodyBytes)) } if len(payload.Messages) == 0 || payload.Messages[0].Role != "user" { return prepared, "", fmt.Errorf("anthropic body missing first user message: %s", string(bodyBytes)) } return prepared, extractMessageContentString(payload.Messages[0].Content), nil } else { if prepared.Path != "/v1/chat/completions" || prepared.Operation != string(config.OperationChatCompletions) { return prepared, "", fmt.Errorf("openai tunnel path/op mismatch: path=%q op=%q", prepared.Path, prepared.Operation) } var payload struct { Messages []struct { Role string `json:"role"` Content any `json:"content"` } `json:"messages"` } if err := json.Unmarshal(bodyBytes, &payload); err != nil { return prepared, "", fmt.Errorf("unmarshal openai payload: %w (body=%s)", err, string(bodyBytes)) } if len(payload.Messages) == 0 || payload.Messages[0].Role != "user" { return prepared, "", fmt.Errorf("openai body missing first user message: %s", string(bodyBytes)) } return prepared, extractMessageContentString(payload.Messages[0].Content), nil } } func extractMessageContentString(content any) string { switch v := content.(type) { case string: return v case []any: var parts []string for _, item := range v { if m, ok := item.(map[string]any); ok { if text, ok := m["text"].(string); ok { parts = append(parts, text) } } } return strings.Join(parts, "") default: return fmt.Sprint(content) } } func scriptedLightCompletion(endpoint, content string) string { if endpoint == "anthropic" { return fmt.Sprintf(`{"id":"msg-light-complete","type":"message","role":"assistant","content":[{"type":"text","text":%q}],"stop_reason":"end_turn"}`, content) } raw, _ := json.Marshal(content) return fmt.Sprintf(`{"id":"chatcmpl-light-complete","created":9,"choices":[{"message":{"role":"assistant","content":%s},"finish_reason":"stop"}]}`, raw) } func scriptedReviewWrite(endpoint, requestID string) string { path := newReservedPaths(requestID).ReviewPath if endpoint == "anthropic" { return fmt.Sprintf(`{"id":"msg-review-write","type":"message","role":"assistant","content":[{"type":"text","text":"review-write-visible"},{"type":"tool_use","id":"provider-review-write","name":"write_file","input":{"path":%q,"content":"review body"}}],"stop_reason":"tool_use"}`, path) } args, _ := json.Marshal(map[string]string{"path": path, "content": "review body"}) return fmt.Sprintf(`{"id":"chatcmpl-review-write","created":5,"choices":[{"message":{"role":"assistant","content":"review-write-visible","tool_calls":[{"id":"provider-review-write","type":"function","function":{"name":"write_file","arguments":%q}}]},"finish_reason":"tool_calls"}]}`, string(args)) } func scriptedReviewRead(endpoint, requestID string) string { path := newReservedPaths(requestID).ReviewPath if endpoint == "anthropic" { return fmt.Sprintf(`{"id":"msg-review-read","type":"message","role":"assistant","content":[{"type":"text","text":"review-read-visible"},{"type":"tool_use","id":"provider-review-read","name":"read_file","input":{"path":%q}}],"stop_reason":"tool_use"}`, path) } args, _ := json.Marshal(map[string]string{"path": path}) return fmt.Sprintf(`{"id":"chatcmpl-review-read","created":6,"choices":[{"message":{"role":"assistant","content":"review-read-visible","tool_calls":[{"id":"provider-review-read","type":"function","function":{"name":"read_file","arguments":%q}}]},"finish_reason":"tool_calls"}]}`, string(args)) } func scriptedRepairTool(endpoint string) string { if endpoint == "anthropic" { return `{"id":"msg-repair","type":"message","role":"assistant","content":[{"type":"text","text":"PASS prose but repair tool decides"},{"type":"tool_use","id":"provider-repair","name":"run_command","input":{"command":"go test ./..."}}],"stop_reason":"tool_use"}` } return `{"id":"chatcmpl-repair","created":7,"choices":[{"message":{"role":"assistant","content":"PASS prose but repair tool decides","tool_calls":[{"id":"provider-repair","type":"function","function":{"name":"run_command","arguments":"{\"command\":\"go test ./...\"}"}}]},"finish_reason":"tool_calls"}]}` }