848 lines
39 KiB
Go
848 lines
39 KiB
Go
//go:build live
|
|
|
|
package server
|
|
|
|
import (
|
|
bruntime "agent-system-backend/internal/runtime"
|
|
"agent-system-backend/internal/store"
|
|
core "agent-system-core"
|
|
"context"
|
|
"database/sql"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"sync/atomic"
|
|
"testing"
|
|
"time"
|
|
|
|
agentllm "agent-native/runtime/llm"
|
|
)
|
|
|
|
func TestEvaluationRecordsContentFreeTerminal(t *testing.T) {
|
|
dir, _ := newEvaluationEventStore(t)
|
|
args := `{"secret":"PRIVATE_ARGUMENT_R05"}`
|
|
provider := func(context.Context, string, []agentllm.Message, []agentllm.ToolDecl, agentllm.StreamOptions) <-chan agentllm.Event {
|
|
out := make(chan agentllm.Event, 2)
|
|
out <- agentllm.Event{Kind: agentllm.KindToolCallDelta, ToolIndex: 0, ToolID: "call-1", ToolName: "PRIVATE_TOOL_R05", ArgumentsDelta: args}
|
|
out <- agentllm.Event{Kind: agentllm.KindMessageEnd,
|
|
Message: agentllm.Message{Role: agentllm.RoleAssistant, Content: "PRIVATE_BODY_R05", StopReason: agentllm.StopToolCalls,
|
|
ToolCalls: []agentllm.ToolCall{{ID: "call-1", Name: "PRIVATE_TOOL_R05", Arguments: args}}},
|
|
Usage: agentllm.Usage{PromptTokens: 12, CompletionTokens: 5, TotalTokens: 17, CacheReadTokens: 3},
|
|
FinishReason: "tool_calls", StreamComplete: true, HTTPStatus: http.StatusOK,
|
|
TerminalObservation: &agentllm.TerminalObservation{ReadOutcome: "done_marker", LastFrameJSONComplete: true,
|
|
UsagePresent: true, ToolFragments: 1, ToolArgumentBytes: len(args), ToolArgumentsValid: true},
|
|
}
|
|
close(out)
|
|
return out
|
|
}
|
|
stream := evalProviderStreamWithProvider(provider, dir)
|
|
for range stream(evalTestContext(t), "fixture", []agentllm.Message{{Role: agentllm.RoleUser, Content: "PRIVATE_PROMPT_R05"}},
|
|
[]agentllm.ToolDecl{{Name: "PRIVATE_TOOL_R05", Description: "PRIVATE_SCHEMA_R05"}}, agentllm.StreamOptions{MaxTokens: 4000}) {
|
|
}
|
|
path := filepath.Join(dir, "provider-records.json")
|
|
raw, err := os.ReadFile(path)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, forbidden := range []string{"PRIVATE_PROMPT_R05", "PRIVATE_ARGUMENT_R05", "PRIVATE_BODY_R05", "PRIVATE_TOOL_R05", "PRIVATE_SCHEMA_R05",
|
|
`"messages"`, `"toolDeclarations"`, `"terminal"`} {
|
|
if strings.Contains(string(raw), forbidden) {
|
|
t.Fatalf("provider record retained %q", forbidden)
|
|
}
|
|
}
|
|
var record struct {
|
|
Calls []struct {
|
|
RunID string `json:"runId"`
|
|
ReportedTokens int `json:"reportedTokens"`
|
|
Usage agentllm.Usage `json:"usage"`
|
|
Observation *agentllm.TerminalObservation `json:"terminalObservation"`
|
|
} `json:"calls"`
|
|
}
|
|
if err := json.Unmarshal(raw, &record); err != nil || len(record.Calls) != 1 || record.Calls[0].RunID == "" ||
|
|
record.Calls[0].ReportedTokens != 17 || record.Calls[0].Usage.CacheReadTokens != 3 ||
|
|
record.Calls[0].Observation == nil || record.Calls[0].Observation.ReadOutcome != "done_marker" {
|
|
t.Fatalf("content-free record lost accounting: %+v err=%v", record, err)
|
|
}
|
|
info, err := os.Stat(path)
|
|
if err != nil || info.Mode().Perm() != 0600 {
|
|
t.Fatalf("provider record permission = %v err=%v", info, err)
|
|
}
|
|
}
|
|
|
|
func TestEvaluationRedactsProviderControlledTerminalLabels(t *testing.T) {
|
|
dir, _ := newEvaluationEventStore(t)
|
|
provider := func(context.Context, string, []agentllm.Message, []agentllm.ToolDecl, agentllm.StreamOptions) <-chan agentllm.Event {
|
|
out := make(chan agentllm.Event, 1)
|
|
out <- agentllm.Event{Kind: agentllm.KindMessageEnd,
|
|
Message: agentllm.Message{Role: agentllm.RoleAssistant, Content: "safe", StopReason: agentllm.StopEnd},
|
|
Usage: agentllm.Usage{PromptTokens: 12, CompletionTokens: 5, TotalTokens: 17},
|
|
FinishReason: "PRIVATE_REASON_R05", StreamComplete: true,
|
|
TerminalObservation: &agentllm.TerminalObservation{ReadOutcome: "PRIVATE_OUTCOME_R05", UsagePresent: true},
|
|
}
|
|
close(out)
|
|
return out
|
|
}
|
|
for range evalProviderStreamWithProvider(provider, dir)(evalTestContext(t), "fixture", nil, nil, agentllm.StreamOptions{MaxTokens: 4000}) {
|
|
}
|
|
raw, err := os.ReadFile(filepath.Join(dir, "provider-records.json"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if strings.Contains(string(raw), "PRIVATE_REASON_R05") || strings.Contains(string(raw), "PRIVATE_OUTCOME_R05") {
|
|
t.Fatal("provider-controlled terminal label escaped into a content-free record")
|
|
}
|
|
var record struct {
|
|
Calls []struct {
|
|
FinishReason string `json:"finishReason"`
|
|
Observation *agentllm.TerminalObservation `json:"terminalObservation"`
|
|
} `json:"calls"`
|
|
}
|
|
if err := json.Unmarshal(raw, &record); err != nil || len(record.Calls) != 1 ||
|
|
record.Calls[0].FinishReason != "unknown" || record.Calls[0].Observation == nil ||
|
|
record.Calls[0].Observation.ReadOutcome != "unknown" {
|
|
t.Fatalf("unexpected sanitized terminal labels: %+v err=%v", record, err)
|
|
}
|
|
}
|
|
|
|
func TestEvaluationRedactsErrorTerminalAndBlocksTool(t *testing.T) {
|
|
dir, _ := newEvaluationEventStore(t)
|
|
var calls atomic.Int32
|
|
provider := func(context.Context, string, []agentllm.Message, []agentllm.ToolDecl, agentllm.StreamOptions) <-chan agentllm.Event {
|
|
calls.Add(1)
|
|
out := make(chan agentllm.Event, 1)
|
|
out <- agentllm.Event{Kind: agentllm.KindMessageEnd,
|
|
Message: agentllm.Message{Role: agentllm.RoleAssistant, StopReason: agentllm.StopError,
|
|
ToolCalls: []agentllm.ToolCall{{ID: "call-1", Name: "PRIVATE_TOOL_R05", Arguments: `{"secret":"PRIVATE_ARGUMENT_R05"}`}}},
|
|
Usage: agentllm.Usage{PromptTokens: 12, CompletionTokens: 5, TotalTokens: 17},
|
|
Err: errors.New("PRIVATE_ERROR_R05"), ErrorMessage: "PRIVATE_ERROR_R05",
|
|
FinishReason: "tool_calls", StreamComplete: false,
|
|
TerminalObservation: &agentllm.TerminalObservation{ReadOutcome: "read_error", UsagePresent: true,
|
|
ToolFragments: 1, ToolArgumentBytes: 33, ToolArgumentsValid: true},
|
|
}
|
|
close(out)
|
|
return out
|
|
}
|
|
stream := evalProviderStreamWithProvider(provider, dir)
|
|
var terminal agentllm.Event
|
|
for event := range stream(evalTestContext(t), "fixture", nil, nil, agentllm.StreamOptions{MaxTokens: 4000}) {
|
|
if event.Kind == agentllm.KindMessageEnd {
|
|
terminal = event
|
|
}
|
|
}
|
|
if terminal.Err == nil || terminal.Usage.TotalTokens != 17 || len(terminal.Message.ToolCalls) != 0 {
|
|
t.Fatalf("error-bearing tool call escaped the controlled terminal: %#v", terminal)
|
|
}
|
|
blocked := false
|
|
for event := range stream(bruntime.WithProviderRunID(context.Background(), t.Name()+"-next"), "fixture", nil, nil, agentllm.StreamOptions{MaxTokens: 4000}) {
|
|
blocked = blocked || event.Kind == agentllm.KindError
|
|
}
|
|
if !blocked || calls.Load() != 1 {
|
|
t.Fatalf("failed terminal allowed another provider call: blocked=%v calls=%d", blocked, calls.Load())
|
|
}
|
|
raw, err := os.ReadFile(filepath.Join(dir, "provider-records.json"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if strings.Contains(string(raw), "PRIVATE_ERROR_R05") || strings.Contains(string(raw), "PRIVATE_ARGUMENT_R05") ||
|
|
strings.Contains(string(raw), `"terminal"`) {
|
|
t.Fatal("error record retained raw provider content")
|
|
}
|
|
var record struct {
|
|
ReportedTokens int `json:"reportedTokens"`
|
|
Calls []struct {
|
|
FailureClass string `json:"failureClass"`
|
|
} `json:"calls"`
|
|
}
|
|
if err := json.Unmarshal(raw, &record); err != nil || record.ReportedTokens != 17 || len(record.Calls) != 1 || record.Calls[0].FailureClass != "terminal_error" {
|
|
t.Fatalf("failed terminal accounting/classification missing: %+v err=%v", record, err)
|
|
}
|
|
}
|
|
|
|
func TestEvaluationStopsAfterMalformedToolTerminal(t *testing.T) {
|
|
t.Setenv("WORK_CHECKPOINT_REMAINING_TOKENS", "60000")
|
|
for _, tc := range []struct {
|
|
name, arguments, trailer string
|
|
complete bool
|
|
argumentsValid bool
|
|
}{
|
|
{"invalid-json", `{"findings":`, "data: [DONE]\n\n", true, false},
|
|
{"missing-done", `{}`, "", false, true},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
var providerCalls atomic.Int32
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
providerCalls.Add(1)
|
|
w.Header().Set("Content-Type", "text/event-stream")
|
|
args, _ := json.Marshal(tc.arguments)
|
|
fmt.Fprintf(w, "data: {\"choices\":[{\"delta\":{\"tool_calls\":[{\"index\":0,\"id\":\"call_1\",\"function\":{\"name\":\"publish_work_checkpoint\",\"arguments\":%s}}]}}]}\n\n", args)
|
|
fmt.Fprint(w, "data: {\"choices\":[{\"delta\":{},\"finish_reason\":\"tool_calls\"}],\"usage\":{\"prompt_tokens\":12,\"completion_tokens\":5,\"total_tokens\":17}}\n\n")
|
|
fmt.Fprint(w, tc.trailer)
|
|
}))
|
|
defer srv.Close()
|
|
dir, _ := newEvaluationEventStore(t)
|
|
stream := evalProviderStream(map[string]any{"baseUrl": srv.URL, "apiKey": "fixture", "model": "fixture"}, dir)
|
|
var terminal agentllm.Event
|
|
for event := range stream(evalTestContext(t), "fixture", nil, nil, agentllm.StreamOptions{MaxTokens: 4000}) {
|
|
if event.Kind == agentllm.KindMessageEnd {
|
|
terminal = event
|
|
}
|
|
}
|
|
if terminal.Kind != agentllm.KindMessageEnd || terminal.Err == nil || terminal.Usage.TotalTokens != 17 ||
|
|
terminal.StreamComplete != tc.complete || len(terminal.Message.ToolCalls) != 0 {
|
|
t.Fatalf("unsafe provider terminal: %#v", terminal)
|
|
}
|
|
blocked := false
|
|
otherRun := bruntime.WithProviderRunID(context.Background(), t.Name()+"-other-run")
|
|
for event := range stream(otherRun, "fixture", nil, nil, agentllm.StreamOptions{MaxTokens: 4000}) {
|
|
blocked = blocked || event.Kind == agentllm.KindError
|
|
}
|
|
if !blocked || providerCalls.Load() != 1 {
|
|
t.Fatalf("terminal failure allowed another provider call: blocked=%v calls=%d", blocked, providerCalls.Load())
|
|
}
|
|
raw, err := os.ReadFile(filepath.Join(dir, "provider-records.json"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var record struct {
|
|
ReportedTokens int `json:"reportedTokens"`
|
|
UsageUnknown bool `json:"usageUnknown"`
|
|
TerminalFault bool `json:"terminalFault"`
|
|
Calls []struct {
|
|
ReportedTokens int `json:"reportedTokens"`
|
|
OutputCap int `json:"outputCap"`
|
|
FinishReason string `json:"finishReason"`
|
|
StreamComplete bool `json:"streamComplete"`
|
|
ToolArgumentBytes int `json:"toolArgumentBytes"`
|
|
ArgumentsValid bool `json:"toolArgumentsValid"`
|
|
UsagePresent bool `json:"usagePresent"`
|
|
Usage agentllm.Usage `json:"usage"`
|
|
HTTPStatus int `json:"httpStatus"`
|
|
Observation *agentllm.TerminalObservation `json:"terminalObservation"`
|
|
FailureClass string `json:"failureClass"`
|
|
} `json:"calls"`
|
|
}
|
|
if err = json.Unmarshal(raw, &record); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
wantOutcome := "done_marker"
|
|
if !tc.complete {
|
|
wantOutcome = "clean_eof"
|
|
}
|
|
if record.ReportedTokens != 17 || record.UsageUnknown || !record.TerminalFault || len(record.Calls) != 1 ||
|
|
record.Calls[0].ReportedTokens != 17 || record.Calls[0].OutputCap != 4000 ||
|
|
record.Calls[0].FinishReason != "tool_calls" || record.Calls[0].StreamComplete != tc.complete ||
|
|
record.Calls[0].ToolArgumentBytes == 0 || record.Calls[0].ArgumentsValid != tc.argumentsValid ||
|
|
!record.Calls[0].UsagePresent || record.Calls[0].Usage.TotalTokens != 17 ||
|
|
record.Calls[0].HTTPStatus != http.StatusOK || record.Calls[0].FailureClass != "terminal_error" ||
|
|
record.Calls[0].Observation == nil || record.Calls[0].Observation.ReadOutcome != wantOutcome ||
|
|
record.Calls[0].Observation.ToolArgumentsValid != tc.argumentsValid {
|
|
t.Fatalf("incomplete diagnostic or lost Usage: %+v", record)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestEvaluationTransportObservationSanitized(t *testing.T) {
|
|
const maxTail, maxBytes = 10000, int64(32*1024*1024 + 1)
|
|
const maxFragments, maxArgumentBytes = 10000, 4*1024*1024 + 1
|
|
for _, tc := range []struct {
|
|
name string
|
|
input agentllm.TerminalObservation
|
|
wantTail int
|
|
wantBytes int64
|
|
wantFragments int
|
|
wantArgBytes int
|
|
wantKind string
|
|
wantFields [3]string
|
|
}{
|
|
{"negative-tail-oversized-body", agentllm.TerminalObservation{ReadOutcome: "done_marker",
|
|
TailEventKind: "PRIVATE_TAIL_R06", TailEventCount: -5, ToolFragments: -3, ToolArgumentBytes: 1 << 30,
|
|
BodyBoundary: &agentllm.BodyBoundaryObservation{ProtocolClass: "PRIVATE_HEADER_R06", FramingClass: "PRIVATE_FRAMING_R06",
|
|
LengthRelation: "PRIVATE_LENGTH_R06", BytesRead: 1 << 40, DeclaredLengthKnown: true, BytesSaturated: true, EOFSeen: true, Uncompressed: true}},
|
|
0, maxBytes, 0, maxArgumentBytes, "unknown", [3]string{"other", "unknown", "unknown"}},
|
|
{"oversized-tail-negative-body", agentllm.TerminalObservation{ReadOutcome: "done_marker",
|
|
TailEventKind: "PRIVATE_TAIL_R06", TailEventCount: 1 << 20, ToolFragments: 1 << 20, ToolArgumentBytes: -9,
|
|
BodyBoundary: &agentllm.BodyBoundaryObservation{ProtocolClass: "PRIVATE_HEADER_R06", FramingClass: "PRIVATE_FRAMING_R06",
|
|
LengthRelation: "PRIVATE_LENGTH_R06", BytesRead: -7, DeclaredLengthKnown: true, BytesSaturated: true, EOFSeen: true, Uncompressed: true}},
|
|
maxTail, 0, maxFragments, 0, "unknown", [3]string{"other", "unknown", "unknown"}},
|
|
{"known-values-kept", agentllm.TerminalObservation{ReadOutcome: "done_marker",
|
|
TailEventKind: "comment", TailEventCount: 3, ToolFragments: 2, ToolArgumentBytes: 64,
|
|
BodyBoundary: &agentllm.BodyBoundaryObservation{ProtocolClass: "http2", FramingClass: "chunked",
|
|
LengthRelation: "match", BytesRead: 128, DeclaredLengthKnown: true, BytesSaturated: true, EOFSeen: true, Uncompressed: true}},
|
|
3, 128, 2, 64, "comment", [3]string{"http2", "chunked", "match"}},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
input := tc.input
|
|
inputBody := *input.BodyBoundary
|
|
input.BodyBoundary = &inputBody
|
|
clean := evalTerminalObservation(&input)
|
|
if clean == nil || clean.BodyBoundary == nil || clean.BodyBoundary == input.BodyBoundary {
|
|
t.Fatalf("observation was not deep-copied: %+v", clean)
|
|
}
|
|
body := clean.BodyBoundary
|
|
if clean.ReadOutcome != "done_marker" || clean.TailEventKind != tc.wantKind || clean.TailEventCount != tc.wantTail ||
|
|
clean.ToolFragments != tc.wantFragments || clean.ToolArgumentBytes != tc.wantArgBytes ||
|
|
body.ProtocolClass != tc.wantFields[0] || body.FramingClass != tc.wantFields[1] || body.LengthRelation != tc.wantFields[2] ||
|
|
body.BytesRead != tc.wantBytes || !body.DeclaredLengthKnown || !body.BytesSaturated || !body.EOFSeen || !body.Uncompressed {
|
|
t.Fatalf("unbounded observation: %+v body=%+v", clean, body)
|
|
}
|
|
input.BodyBoundary.ProtocolClass = "PRIVATE_LATE_R06"
|
|
if body.ProtocolClass == "PRIVATE_LATE_R06" {
|
|
t.Fatal("sanitized observation shares the provider body boundary")
|
|
}
|
|
|
|
dir, _ := newEvaluationEventStore(t)
|
|
observed := tc.input
|
|
observedBody := *observed.BodyBoundary
|
|
observed.BodyBoundary = &observedBody
|
|
provider := func(context.Context, string, []agentllm.Message, []agentllm.ToolDecl, agentllm.StreamOptions) <-chan agentllm.Event {
|
|
out := make(chan agentllm.Event, 1)
|
|
out <- agentllm.Event{Kind: agentllm.KindMessageEnd,
|
|
Message: agentllm.Message{Role: agentllm.RoleAssistant, Content: "PRIVATE_BODY_R06", StopReason: agentllm.StopEnd},
|
|
Usage: agentllm.Usage{PromptTokens: 12, CompletionTokens: 5, TotalTokens: 17},
|
|
FinishReason: "stop", StreamComplete: true, HTTPStatus: http.StatusOK, TerminalObservation: &observed,
|
|
}
|
|
close(out)
|
|
return out
|
|
}
|
|
for range evalProviderStreamWithProvider(provider, dir)(evalTestContext(t), "fixture",
|
|
[]agentllm.Message{{Role: agentllm.RoleUser, Content: "PRIVATE_PROMPT_R06"}}, nil, agentllm.StreamOptions{MaxTokens: 4000}) {
|
|
}
|
|
raw, err := os.ReadFile(filepath.Join(dir, "provider-records.json"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, forbidden := range []string{"PRIVATE_TAIL_R06", "PRIVATE_HEADER_R06", "PRIVATE_FRAMING_R06", "PRIVATE_LENGTH_R06",
|
|
"PRIVATE_BODY_R06", "PRIVATE_PROMPT_R06", `"messages"`, `"terminal"`} {
|
|
if strings.Contains(string(raw), forbidden) {
|
|
t.Fatalf("provider record retained %q", forbidden)
|
|
}
|
|
}
|
|
var record struct {
|
|
Calls []struct {
|
|
RunID string `json:"runId"`
|
|
Usage agentllm.Usage `json:"usage"`
|
|
Observation *agentllm.TerminalObservation `json:"terminalObservation"`
|
|
} `json:"calls"`
|
|
}
|
|
if err := json.Unmarshal(raw, &record); err != nil || len(record.Calls) != 1 || record.Calls[0].RunID != t.Name() ||
|
|
record.Calls[0].Usage.TotalTokens != 17 || record.Calls[0].Observation == nil || record.Calls[0].Observation.BodyBoundary == nil {
|
|
t.Fatalf("sanitized record lost accounting: %s err=%v", raw, err)
|
|
}
|
|
got := record.Calls[0].Observation
|
|
if got.TailEventKind != tc.wantKind || got.TailEventCount != tc.wantTail || got.ToolFragments != tc.wantFragments ||
|
|
got.ToolArgumentBytes != tc.wantArgBytes || got.BodyBoundary.ProtocolClass != tc.wantFields[0] ||
|
|
got.BodyBoundary.FramingClass != tc.wantFields[1] || got.BodyBoundary.LengthRelation != tc.wantFields[2] ||
|
|
got.BodyBoundary.BytesRead != tc.wantBytes {
|
|
t.Fatalf("unbounded persisted observation: %s", raw)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestEvaluationTransportEvidenceStopsAfterUnmarkedToolEOF(t *testing.T) {
|
|
t.Setenv("WORK_CHECKPOINT_REMAINING_TOKENS", "60000")
|
|
dir, _ := newEvaluationEventStore(t)
|
|
var providerCalls atomic.Int32
|
|
provider := func(context.Context, string, []agentllm.Message, []agentllm.ToolDecl, agentllm.StreamOptions) <-chan agentllm.Event {
|
|
providerCalls.Add(1)
|
|
out := make(chan agentllm.Event, 2)
|
|
out <- agentllm.Event{Kind: agentllm.KindToolCallDelta, ToolIndex: 0, ToolID: "call_1", ToolName: "publish_work_checkpoint", ArgumentsDelta: `{}`}
|
|
out <- agentllm.Event{Kind: agentllm.KindMessageEnd,
|
|
Message: agentllm.Message{Role: agentllm.RoleAssistant, StopReason: agentllm.StopError},
|
|
Usage: agentllm.Usage{PromptTokens: 12, CompletionTokens: 5, TotalTokens: 17},
|
|
Err: errors.New("PRIVATE_ERROR_R06"), ErrorMessage: "PRIVATE_ERROR_R06",
|
|
FinishReason: "tool_calls", StreamComplete: false, HTTPStatus: http.StatusOK,
|
|
TerminalObservation: &agentllm.TerminalObservation{ReadOutcome: "clean_eof", LastFrameJSONComplete: true, UsagePresent: true,
|
|
ToolFragments: 1, ToolArgumentBytes: 2, ToolArgumentsValid: true, TailEventKind: "json_frame", TailEventCount: 2,
|
|
BodyBoundary: &agentllm.BodyBoundaryObservation{ProtocolClass: "http1", FramingClass: "chunked", BytesRead: 256,
|
|
EOFSeen: true, LengthRelation: "unknown"}},
|
|
}
|
|
close(out)
|
|
return out
|
|
}
|
|
stream := evalProviderStreamWithProvider(provider, dir)
|
|
var terminal agentllm.Event
|
|
for event := range stream(evalTestContext(t), "fixture", nil, nil, agentllm.StreamOptions{MaxTokens: 4000}) {
|
|
if event.Kind == agentllm.KindMessageEnd {
|
|
terminal = event
|
|
}
|
|
}
|
|
if terminal.Kind != agentllm.KindMessageEnd || terminal.Err == nil || terminal.StreamComplete ||
|
|
terminal.Usage.TotalTokens != 17 || len(terminal.Message.ToolCalls) != 0 {
|
|
t.Fatalf("unmarked tool EOF escaped the controlled terminal: %#v", terminal)
|
|
}
|
|
blocked := false
|
|
for event := range stream(bruntime.WithProviderRunID(context.Background(), t.Name()+"-next"), "fixture", nil, nil, agentllm.StreamOptions{MaxTokens: 4000}) {
|
|
blocked = blocked || event.Kind == agentllm.KindError
|
|
}
|
|
if !blocked || providerCalls.Load() != 1 {
|
|
t.Fatalf("transport EOF allowed another provider call: blocked=%v calls=%d", blocked, providerCalls.Load())
|
|
}
|
|
raw, err := os.ReadFile(filepath.Join(dir, "provider-records.json"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if strings.Contains(string(raw), "PRIVATE_ERROR_R06") || strings.Contains(string(raw), `"terminal"`) {
|
|
t.Fatal("transport evidence record retained raw provider content")
|
|
}
|
|
var record struct {
|
|
ReportedTokens int `json:"reportedTokens"`
|
|
TerminalFault bool `json:"terminalFault"`
|
|
Calls []struct {
|
|
Usage agentllm.Usage `json:"usage"`
|
|
StreamComplete bool `json:"streamComplete"`
|
|
FailureClass string `json:"failureClass"`
|
|
Observation *agentllm.TerminalObservation `json:"terminalObservation"`
|
|
} `json:"calls"`
|
|
}
|
|
if err := json.Unmarshal(raw, &record); err != nil || record.ReportedTokens != 17 || !record.TerminalFault || len(record.Calls) != 1 ||
|
|
record.Calls[0].Usage.TotalTokens != 17 || record.Calls[0].StreamComplete || record.Calls[0].FailureClass != "terminal_error" {
|
|
t.Fatalf("transport EOF accounting missing: %s err=%v", raw, err)
|
|
}
|
|
got := record.Calls[0].Observation
|
|
if got == nil || got.ReadOutcome != "clean_eof" || got.TailEventKind != "json_frame" || got.TailEventCount != 2 ||
|
|
got.BodyBoundary == nil || !got.BodyBoundary.EOFSeen || got.BodyBoundary.FramingClass != "chunked" {
|
|
t.Fatalf("transport evidence missing from failed terminal record: %s", raw)
|
|
}
|
|
}
|
|
|
|
func TestEvaluationKeepsUsageOnTerminalFailure(t *testing.T) {
|
|
t.Setenv("WORK_CHECKPOINT_REMAINING_TOKENS", "60000")
|
|
var providerCalls atomic.Int32
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
providerCalls.Add(1)
|
|
w.Header().Set("Content-Type", "text/event-stream")
|
|
fmt.Fprint(w, "data: {\"choices\":[{\"delta\":{\"tool_calls\":[{\"index\":0,\"id\":\"call_1\",\"function\":{\"name\":\"publish_work_checkpoint\",\"arguments\":\"{}\"}}]}}],\"usage\":{\"prompt_tokens\":12,\"completion_tokens\":5,\"total_tokens\":17}}\n\n")
|
|
fmt.Fprint(w, "data: {not-json}\n\n")
|
|
}))
|
|
defer srv.Close()
|
|
dir, _ := newEvaluationEventStore(t)
|
|
stream := evalProviderStream(map[string]any{"baseUrl": srv.URL, "apiKey": "fixture", "model": "fixture"}, dir)
|
|
var terminal agentllm.Event
|
|
for ev := range stream(evalTestContext(t), "fixture", nil, nil, agentllm.StreamOptions{MaxTokens: 4000}) {
|
|
if ev.Kind == agentllm.KindMessageEnd {
|
|
terminal = ev
|
|
}
|
|
}
|
|
if terminal.Err == nil || terminal.Usage.TotalTokens != 17 {
|
|
t.Fatalf("late stream failure lost returned Usage: %#v", terminal)
|
|
}
|
|
blocked := false
|
|
otherRun := bruntime.WithProviderRunID(context.Background(), t.Name()+"-other-run")
|
|
for ev := range stream(otherRun, "fixture", nil, nil, agentllm.StreamOptions{MaxTokens: 4000}) {
|
|
blocked = blocked || ev.Kind == agentllm.KindError
|
|
}
|
|
raw, err := os.ReadFile(filepath.Join(dir, "provider-records.json"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var record struct {
|
|
ReportedTokens int `json:"reportedTokens"`
|
|
UsageUnknown bool `json:"usageUnknown"`
|
|
TerminalFault bool `json:"terminalFault"`
|
|
}
|
|
if err = json.Unmarshal(raw, &record); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !blocked || providerCalls.Load() != 1 || record.ReportedTokens != 17 || record.UsageUnknown || !record.TerminalFault {
|
|
t.Fatalf("late terminal was retried or Usage lost: blocked=%v calls=%d record=%+v", blocked, providerCalls.Load(), record)
|
|
}
|
|
}
|
|
|
|
func TestEvaluationStopsWhenUsageMissing(t *testing.T) {
|
|
t.Setenv("WORK_CHECKPOINT_REMAINING_TOKENS", "60000")
|
|
var providerCalls atomic.Int32
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
providerCalls.Add(1)
|
|
w.Header().Set("Content-Type", "text/event-stream")
|
|
fmt.Fprint(w, "data: {\"choices\":[{\"delta\":{\"content\":\"done\"},\"finish_reason\":\"stop\"}]}\n\ndata: [DONE]\n\n")
|
|
}))
|
|
defer srv.Close()
|
|
dir, _ := newEvaluationEventStore(t)
|
|
stream := evalProviderStream(map[string]any{"baseUrl": srv.URL, "apiKey": "fixture", "model": "fixture"}, dir)
|
|
for range stream(evalTestContext(t), "fixture", nil, nil, agentllm.StreamOptions{MaxTokens: 4000}) {
|
|
}
|
|
blocked := false
|
|
for ev := range stream(evalTestContext(t), "fixture", nil, nil, agentllm.StreamOptions{MaxTokens: 4000}) {
|
|
blocked = blocked || ev.Kind == agentllm.KindError
|
|
}
|
|
if !blocked || providerCalls.Load() != 1 {
|
|
t.Fatalf("missing Usage allowed a second paid request: blocked=%v calls=%d", blocked, providerCalls.Load())
|
|
}
|
|
raw, err := os.ReadFile(filepath.Join(dir, "provider-records.json"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var record struct {
|
|
UsageUnknown bool `json:"usageUnknown"`
|
|
}
|
|
if err = json.Unmarshal(raw, &record); err != nil || !record.UsageUnknown {
|
|
t.Fatalf("missing Usage was not recorded: %v %+v", err, record)
|
|
}
|
|
}
|
|
|
|
func TestEvaluationDoesNotForwardToolTerminalBeforeUsageRecorded(t *testing.T) {
|
|
t.Setenv("WORK_CHECKPOINT_REMAINING_TOKENS", "60000")
|
|
dir, _ := newEvaluationEventStore(t)
|
|
var calls atomic.Int32
|
|
release := make(chan struct{})
|
|
provider := func(context.Context, string, []agentllm.Message, []agentllm.ToolDecl, agentllm.StreamOptions) <-chan agentllm.Event {
|
|
calls.Add(1)
|
|
out := make(chan agentllm.Event)
|
|
go func() {
|
|
defer close(out)
|
|
out <- agentllm.Event{Kind: agentllm.KindMessageEnd, Message: agentllm.Message{Role: agentllm.RoleAssistant,
|
|
StopReason: agentllm.StopToolCalls, ToolCalls: []agentllm.ToolCall{{ID: "call-1", Name: "publish_work_checkpoint", Arguments: `{}`}}},
|
|
FinishReason: "tool_calls", StreamComplete: true}
|
|
<-release // keep the provider stream open after its terminal
|
|
}()
|
|
return out
|
|
}
|
|
stream := evalProviderStreamWithProvider(provider, dir)
|
|
time.AfterFunc(100*time.Millisecond, func() { close(release) })
|
|
var terminal agentllm.Event
|
|
first := stream(evalTestContext(t), "fixture", nil, nil, agentllm.StreamOptions{MaxTokens: 4000})
|
|
for ev := range first {
|
|
if ev.Kind == agentllm.KindMessageEnd {
|
|
terminal = ev
|
|
break
|
|
}
|
|
}
|
|
blocked := false
|
|
for ev := range stream(bruntime.WithProviderRunID(context.Background(), t.Name()+"-next"), "fixture", nil, nil, agentllm.StreamOptions{MaxTokens: 4000}) {
|
|
blocked = blocked || ev.Kind == agentllm.KindError
|
|
}
|
|
for range first {
|
|
}
|
|
if terminal.Err == nil || len(terminal.Message.ToolCalls) != 0 || !blocked || calls.Load() != 1 {
|
|
t.Fatalf("missing Usage tool terminal escaped stop gate: terminal=%#v blocked=%v providerCalls=%d", terminal, blocked, calls.Load())
|
|
}
|
|
}
|
|
|
|
func TestEvaluationStopsWhenProviderRecordUnavailable(t *testing.T) {
|
|
t.Setenv("WORK_CHECKPOINT_REMAINING_TOKENS", "60000")
|
|
var providerCalls atomic.Int32
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
providerCalls.Add(1)
|
|
w.Header().Set("Content-Type", "text/event-stream")
|
|
fmt.Fprint(w, "data: {\"choices\":[{\"delta\":{\"content\":\"done\"},\"finish_reason\":\"stop\"}],\"usage\":{\"prompt_tokens\":1,\"completion_tokens\":1,\"total_tokens\":2}}\n\ndata: [DONE]\n\n")
|
|
}))
|
|
defer srv.Close()
|
|
dir, _ := newEvaluationEventStore(t)
|
|
if err := os.Mkdir(filepath.Join(dir, "provider-records.json"), 0700); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
stream := evalProviderStream(map[string]any{"baseUrl": srv.URL, "apiKey": "fixture", "model": "fixture"}, dir)
|
|
var terminal agentllm.Event
|
|
for ev := range stream(evalTestContext(t), "fixture", nil, nil, agentllm.StreamOptions{MaxTokens: 4000}) {
|
|
if ev.Kind == agentllm.KindMessageEnd {
|
|
terminal = ev
|
|
}
|
|
}
|
|
if terminal.Usage.TotalTokens != 2 {
|
|
t.Fatalf("control provider Usage missing: %#v", terminal)
|
|
}
|
|
blocked := false
|
|
for ev := range stream(evalTestContext(t), "fixture", nil, nil, agentllm.StreamOptions{MaxTokens: 4000}) {
|
|
blocked = blocked || ev.Kind == agentllm.KindError
|
|
}
|
|
if !blocked || providerCalls.Load() != 1 {
|
|
t.Fatalf("missing durable record allowed another request: blocked=%v calls=%d", blocked, providerCalls.Load())
|
|
}
|
|
}
|
|
|
|
func TestEvaluationGroupsChangingPromptsByActualRunID(t *testing.T) {
|
|
t.Setenv("WORK_CHECKPOINT_REMAINING_TOKENS", "60000")
|
|
providerCalls := 0
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
providerCalls++
|
|
w.Header().Set("Content-Type", "text/event-stream")
|
|
fmt.Fprint(w, "data: {\"choices\":[{\"delta\":{\"content\":\"done\"},\"finish_reason\":\"stop\"}],\"usage\":{\"prompt_tokens\":1,\"completion_tokens\":1,\"total_tokens\":2}}\n\ndata: [DONE]\n\n")
|
|
}))
|
|
defer srv.Close()
|
|
dir, _ := newEvaluationEventStore(t)
|
|
stream := evalProviderStream(map[string]any{"baseUrl": srv.URL, "apiKey": "fixture", "model": "fixture"}, dir)
|
|
ctx := bruntime.WithProviderRunID(context.Background(), "same-real-run")
|
|
for _, user := range []string{"first prompt", "later prompt changed by runtime"} {
|
|
for range stream(ctx, "fixture", []agentllm.Message{{Role: agentllm.RoleSystem, Content: "stable"}, {Role: agentllm.RoleUser, Content: user}}, nil, agentllm.StreamOptions{MaxTokens: 4000}) {
|
|
}
|
|
}
|
|
raw, err := os.ReadFile(filepath.Join(dir, "provider-records.json"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var record struct {
|
|
ModelRuns int `json:"modelRuns"`
|
|
Calls []struct {
|
|
RunID string `json:"runId"`
|
|
RunKey string `json:"runKey"`
|
|
Call int `json:"call"`
|
|
} `json:"calls"`
|
|
}
|
|
if err := json.Unmarshal(raw, &record); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if providerCalls != 2 || record.ModelRuns != 1 || len(record.Calls) != 2 || record.Calls[0].RunID != "same-real-run" || record.Calls[1].RunID != "same-real-run" || record.Calls[0].RunKey != record.Calls[1].RunKey || record.Calls[0].Call != 1 || record.Calls[1].Call != 2 {
|
|
t.Fatalf("one real run split into evaluator groups: provider=%d record=%+v", providerCalls, record)
|
|
}
|
|
}
|
|
|
|
func TestEvaluationRefusesRequestWithoutActualRunID(t *testing.T) {
|
|
providerCalls := 0
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { providerCalls++ }))
|
|
defer srv.Close()
|
|
dir, _ := newEvaluationEventStore(t)
|
|
stream := evalProviderStream(map[string]any{"baseUrl": srv.URL, "apiKey": "fixture", "model": "fixture"}, dir)
|
|
rejected := false
|
|
for event := range stream(context.Background(), "fixture", []agentllm.Message{{Role: agentllm.RoleUser, Content: "no run identity"}}, nil, agentllm.StreamOptions{MaxTokens: 4000}) {
|
|
rejected = rejected || event.Kind == agentllm.KindError
|
|
}
|
|
if !rejected || providerCalls != 0 {
|
|
t.Fatalf("missing run ID reached provider: rejected=%v calls=%d", rejected, providerCalls)
|
|
}
|
|
}
|
|
|
|
func evalTestContext(t *testing.T) context.Context {
|
|
t.Helper()
|
|
return bruntime.WithProviderRunID(context.Background(), t.Name())
|
|
}
|
|
|
|
func TestMechanismSourceExtractionAfterSourceOnce(t *testing.T) {
|
|
id := "src_" + strings.Repeat("a", 24)
|
|
got := mechanismCheckpointSources("sourceId=" + id + " blockIndex=0\n上线六月。验收条件需要补齐")
|
|
if len(got) != 1 || got[0][1] != id || got[0][2] != "0" || !strings.Contains(got[0][3], "上线六月") {
|
|
t.Fatalf("inline source unavailable to mechanism fixture: %v", got)
|
|
}
|
|
}
|
|
|
|
func TestEvaluationFixtureUsesEffectiveNumericOutputCap(t *testing.T) {
|
|
settings := core.EffectiveSettings(evalFixtureSettings())
|
|
if got := settings.GetNumber("models.chat.maxOutputTokens", 0); got != 4000 {
|
|
t.Fatalf("mechanism output cap is not effective: got %v, want 4000", got)
|
|
}
|
|
if got := settings.GetNumber("runtime.llmRetries", 2); got != 0 {
|
|
t.Fatalf("fixture disabled-retry setting is not effective: got %v, want 0", got)
|
|
}
|
|
}
|
|
|
|
func newEvaluationEventStore(t *testing.T) (string, *sql.DB) {
|
|
t.Helper()
|
|
dir := t.TempDir()
|
|
db, err := sql.Open("sqlite", filepath.Join(dir, "agent-system.db"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err = db.Exec(`CREATE TABLE events (conversation_id TEXT, run_id TEXT, seq INTEGER, type TEXT, payload TEXT)`); err != nil {
|
|
db.Close()
|
|
t.Fatal(err)
|
|
}
|
|
t.Cleanup(func() { db.Close() })
|
|
return dir, db
|
|
}
|
|
|
|
func TestExtraA01LimitsActualRunIDs(t *testing.T) {
|
|
_, st := newTestServer(t)
|
|
p, err := st.CreateProject(store.OwnerDefault, "allowance")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
c, err := st.CreateConversation(store.OwnerDefault, p.ID, "allowance", "")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, kind := range []core.RunKind{core.RunConversation, core.RunSubagent} {
|
|
r := core.Run{Owner: c.Owner, ProjectID: p.ID, ConversationID: c.ID, Kind: kind, Status: core.RunRunning}
|
|
if err = st.CreateRun(&r); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
calls := 0
|
|
guard := limitExtraA01Runs(st, func(context.Context, string, []agentllm.Message, []agentllm.ToolDecl, agentllm.StreamOptions) <-chan agentllm.Event {
|
|
calls++
|
|
out := make(chan agentllm.Event)
|
|
close(out)
|
|
return out
|
|
})
|
|
for range guard(context.Background(), "fixture", nil, nil, agentllm.StreamOptions{}) {
|
|
}
|
|
if calls != 1 {
|
|
t.Fatal("two actual runs were blocked")
|
|
}
|
|
extra := core.Run{Owner: c.Owner, ProjectID: p.ID, ConversationID: c.ID, Kind: core.RunSubagent, Status: core.RunRunning}
|
|
if err = st.CreateRun(&extra); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for event := range guard(context.Background(), "fixture", nil, nil, agentllm.StreamOptions{}) {
|
|
if event.Err == nil {
|
|
t.Fatal("missing allowance error")
|
|
}
|
|
}
|
|
if calls != 1 {
|
|
t.Fatal("a third actual run made a paid request")
|
|
}
|
|
}
|
|
|
|
func TestFollowupA01UsesNewBoundedAuthorization(t *testing.T) {
|
|
for _, tc := range []struct {
|
|
label string
|
|
tokens int
|
|
guard bool
|
|
}{
|
|
{"", 20000, false},
|
|
{"approved-20261013", 20000, true},
|
|
{"approved-followup-20261013", 30000, true},
|
|
{"approved-r02-20261013", 30000, true},
|
|
{"approved-r03-20261013", 30000, true},
|
|
{"approved-r04-20261014", 30000, true},
|
|
{"unapproved", 20000, false},
|
|
} {
|
|
tokens, guard := evalA01Budget(tc.label)
|
|
if tokens != tc.tokens || guard != tc.guard {
|
|
t.Fatalf("label %q: got budget=%d guard=%v; want budget=%d guard=%v", tc.label, tokens, guard, tc.tokens, tc.guard)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestEvaluationPreservesProductionOutputCap(t *testing.T) {
|
|
for _, tc := range []struct{ supplied, want int }{{780, 780}, {5000, 4000}, {0, 4000}} {
|
|
t.Run(fmt.Sprint(tc.supplied), func(t *testing.T) {
|
|
got := make(chan int, 1)
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
var body struct {
|
|
MaxTokens int `json:"max_tokens"`
|
|
}
|
|
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
|
|
t.Error(err)
|
|
}
|
|
got <- body.MaxTokens
|
|
w.Header().Set("Content-Type", "text/event-stream")
|
|
fmt.Fprint(w, "data: {\"choices\":[{\"delta\":{\"content\":\"done\"},\"finish_reason\":\"stop\"}],\"usage\":{\"prompt_tokens\":1,\"completion_tokens\":1,\"total_tokens\":2}}\n\ndata: [DONE]\n\n")
|
|
}))
|
|
defer srv.Close()
|
|
dir, _ := newEvaluationEventStore(t)
|
|
stream := evalProviderStream(map[string]any{"baseUrl": srv.URL, "apiKey": "fixture", "model": "fixture"}, dir)
|
|
for range stream(evalTestContext(t), "fixture", []agentllm.Message{{Role: agentllm.RoleUser, Content: "check cap"}}, nil, agentllm.StreamOptions{MaxTokens: tc.supplied}) {
|
|
}
|
|
if actual := <-got; actual != tc.want {
|
|
t.Fatalf("evaluation overwrote production cap: got %d want %d", actual, tc.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestEvaluationStopsPaidRequestsAfterRejectedCheckpoint(t *testing.T) {
|
|
t.Setenv("WORK_CHECKPOINT_REMAINING_TOKENS", "60000")
|
|
providerCalls := 0
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
providerCalls++
|
|
w.Header().Set("Content-Type", "text/event-stream")
|
|
fmt.Fprint(w, "data: {\"choices\":[{\"delta\":{\"content\":\"done\"},\"finish_reason\":\"stop\"}],\"usage\":{\"prompt_tokens\":1,\"completion_tokens\":1,\"total_tokens\":2}\n\ndata: [DONE]\n\n")
|
|
}))
|
|
defer srv.Close()
|
|
dir, _ := newEvaluationEventStore(t)
|
|
stream := evalProviderStream(map[string]any{"baseUrl": srv.URL, "apiKey": "fixture", "model": "fixture"}, dir)
|
|
for _, messages := range [][]agentllm.Message{
|
|
{{Role: agentllm.RoleTool, Name: "publish_work_checkpoint", IsError: true, Content: "阶段发现未发布:checkpoint.source: quote not in provided block"}},
|
|
{{Role: agentllm.RoleUser, Content: "another request after rejection"}},
|
|
} {
|
|
rejected := false
|
|
for event := range stream(evalTestContext(t), "fixture", messages, nil, agentllm.StreamOptions{MaxTokens: 4000}) {
|
|
if event.Kind == agentllm.KindError {
|
|
rejected = true
|
|
}
|
|
}
|
|
if !rejected {
|
|
t.Fatal("rejected checkpoint did not stop the controlled sample")
|
|
}
|
|
}
|
|
if providerCalls != 0 {
|
|
t.Fatalf("%d paid provider calls started after checkpoint rejection", providerCalls)
|
|
}
|
|
}
|
|
|
|
func TestEvaluationStopsAcrossRunsAfterPersistedCheckpointRejection(t *testing.T) {
|
|
t.Setenv("WORK_CHECKPOINT_REMAINING_TOKENS", "60000")
|
|
dir, db := newEvaluationEventStore(t)
|
|
_, err := db.Exec(`INSERT INTO events(conversation_id,run_id,seq,type,payload) VALUES(?,?,?,?,?)`, "background", "child", 1, "tool.call", `{"callId":"rejected-call","name":"publish_work_checkpoint"}`)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
_, err = db.Exec(`INSERT INTO events(conversation_id,run_id,seq,type,payload) VALUES(?,?,?,?,?)`, "background", "child", 2, "tool.result", `{"callId":"rejected-call","status":"error","summary":"json: unknown field \"$text\""}`)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
providerCalls := 0
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
providerCalls++
|
|
w.Header().Set("Content-Type", "text/event-stream")
|
|
fmt.Fprint(w, "data: {\"choices\":[{\"delta\":{\"content\":\"done\"},\"finish_reason\":\"stop\"}],\"usage\":{\"prompt_tokens\":1,\"completion_tokens\":1,\"total_tokens\":2}\n\ndata: [DONE]\n\n")
|
|
}))
|
|
defer srv.Close()
|
|
stream := evalProviderStream(map[string]any{"baseUrl": srv.URL, "apiKey": "fixture", "model": "fixture"}, dir)
|
|
rejected := false
|
|
for event := range stream(evalTestContext(t), "fixture", []agentllm.Message{{Role: agentllm.RoleUser, Content: "unrelated foreground history"}}, nil, agentllm.StreamOptions{MaxTokens: 4000}) {
|
|
if event.Kind == agentllm.KindError {
|
|
rejected = true
|
|
}
|
|
}
|
|
if !rejected || providerCalls != 0 {
|
|
t.Fatalf("persisted background rejection allowed foreground provider request: error=%v calls=%d", rejected, providerCalls)
|
|
}
|
|
}
|
|
|
|
func TestEvaluationFailsClosedWhenCheckpointEventsUnavailable(t *testing.T) {
|
|
providerCalls := 0
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
providerCalls++
|
|
}))
|
|
defer srv.Close()
|
|
stream := evalProviderStream(map[string]any{"baseUrl": srv.URL, "apiKey": "fixture", "model": "fixture"}, t.TempDir())
|
|
rejected := false
|
|
for event := range stream(evalTestContext(t), "fixture", []agentllm.Message{{Role: agentllm.RoleUser, Content: "request without the event store"}}, nil, agentllm.StreamOptions{MaxTokens: 4000}) {
|
|
if event.Kind == agentllm.KindError {
|
|
rejected = true
|
|
}
|
|
}
|
|
if !rejected || providerCalls != 0 {
|
|
t.Fatalf("missing checkpoint event store allowed provider request: error=%v calls=%d", rejected, providerCalls)
|
|
}
|
|
}
|
|
|
|
func TestEvaluationMatchesCheckpointRejectionWithinItsRun(t *testing.T) {
|
|
for _, tc := range []struct {
|
|
name, tool, resultRun string
|
|
}{
|
|
{"different tool", "read_file", "child"},
|
|
{"different run", "publish_work_checkpoint", "other-run"},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
dir, db := newEvaluationEventStore(t)
|
|
if _, err := db.Exec(`INSERT INTO events(conversation_id,run_id,seq,type,payload) VALUES(?,?,?,?,?)`, "background", "child", 1, "tool.call", fmt.Sprintf(`{"callId":"shared","name":%q}`, tc.tool)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := db.Exec(`INSERT INTO events(conversation_id,run_id,seq,type,payload) VALUES(?,?,?,?,?)`, "background", tc.resultRun, 2, "tool.result", `{"callId":"shared","status":"error"}`); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
providerCalls := 0
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
providerCalls++
|
|
w.Header().Set("Content-Type", "text/event-stream")
|
|
fmt.Fprint(w, "data: {\"choices\":[{\"delta\":{\"content\":\"done\"},\"finish_reason\":\"stop\"}],\"usage\":{\"prompt_tokens\":1,\"completion_tokens\":1,\"total_tokens\":2}\n\ndata: [DONE]\n\n")
|
|
}))
|
|
defer srv.Close()
|
|
stream := evalProviderStream(map[string]any{"baseUrl": srv.URL, "apiKey": "fixture", "model": "fixture"}, dir)
|
|
for range stream(evalTestContext(t), "fixture", []agentllm.Message{{Role: agentllm.RoleUser, Content: "unrelated request"}}, nil, agentllm.StreamOptions{MaxTokens: 4000}) {
|
|
}
|
|
if providerCalls != 1 {
|
|
t.Fatalf("unrelated tool error blocked the provider: calls=%d", providerCalls)
|
|
}
|
|
})
|
|
}
|
|
}
|