Add dialoguectx packaging, conflictresolve adjudication with HITL routing, workspace context/dialogue/ injection (default on), and goose 00071.
92 lines
2.5 KiB
Go
92 lines
2.5 KiB
Go
package conflictresolve_test
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
|
|
"chat-agent-system/internal/conflictresolve"
|
|
"chat-agent-system/internal/dialoguectx"
|
|
)
|
|
|
|
func TestAdjudicateBlackboardBeatsDialogue(t *testing.T) {
|
|
now := time.Now()
|
|
engine := &conflictresolve.Engine{}
|
|
verdicts := engine.Adjudicate(context.Background(),
|
|
[]dialoguectx.DialogueFact{{
|
|
Key: "device_type", Value: "超声", SpeakerName: "张工",
|
|
StatedAt: now, Authority: dialoguectx.AuthorityCollabDialogue,
|
|
}},
|
|
[]conflictresolve.FactClaim{{
|
|
Key: "device_type", Value: "眼科OCT", AuthorityRank: conflictresolve.RankBlackboard,
|
|
SourceLabel: "blackboard", StatedAt: now.Add(-time.Hour),
|
|
}},
|
|
)
|
|
if len(verdicts) != 1 {
|
|
t.Fatalf("want 1 verdict, got %d", len(verdicts))
|
|
}
|
|
v := verdicts[0]
|
|
if v.Winning.Value != "眼科OCT" {
|
|
t.Fatalf("blackboard should win, got %s", v.Winning.Value)
|
|
}
|
|
if v.NeedsHITL {
|
|
t.Fatal("clear authority gap should not need HITL")
|
|
}
|
|
}
|
|
|
|
func TestAdjudicateAmbiguousNeedsHITL(t *testing.T) {
|
|
now := time.Now()
|
|
engine := &conflictresolve.Engine{}
|
|
verdicts := engine.Adjudicate(context.Background(),
|
|
[]dialoguectx.DialogueFact{
|
|
{Key: "budget", Value: "一千万", SpeakerName: "张工", StatedAt: now},
|
|
{Key: "budget", Value: "两千万", SpeakerName: "李工", StatedAt: now.Add(5 * time.Minute)},
|
|
},
|
|
nil,
|
|
)
|
|
if len(verdicts) != 1 {
|
|
t.Fatalf("want 1 verdict, got %d", len(verdicts))
|
|
}
|
|
if !verdicts[0].NeedsHITL {
|
|
t.Fatalf("expected HITL for ambiguous same-rank, got %#v", verdicts[0])
|
|
}
|
|
if verdicts[0].HITLRoute != conflictresolve.RouteTriggerUser {
|
|
t.Fatalf("expected trigger_user route, got %s", verdicts[0].HITLRoute)
|
|
}
|
|
}
|
|
|
|
func TestCardLimitAutoAdjudicates(t *testing.T) {
|
|
verdicts := make([]conflictresolve.Verdict, 0, 5)
|
|
for i := 0; i < 5; i++ {
|
|
verdicts = append(verdicts, conflictresolve.Verdict{
|
|
FactKey: "k", NeedsHITL: true, CardEligible: true,
|
|
Resolution: conflictresolve.ResolutionNeedsConfirm,
|
|
Winning: conflictresolve.FactClaim{Value: "v"},
|
|
})
|
|
}
|
|
out := conflictresolve.ApplyCardLimit(verdicts)
|
|
cards := 0
|
|
autos := 0
|
|
for _, v := range out {
|
|
if v.NeedsHITL {
|
|
cards++
|
|
}
|
|
if v.Resolution == conflictresolve.ResolutionCardLimitAuto {
|
|
autos++
|
|
}
|
|
}
|
|
if cards != conflictresolve.MaxCardsPerTask {
|
|
t.Fatalf("want %d cards, got %d", conflictresolve.MaxCardsPerTask, cards)
|
|
}
|
|
if autos != 2 {
|
|
t.Fatalf("want 2 auto, got %d", autos)
|
|
}
|
|
}
|
|
|
|
func TestRenderConfirmationsMD(t *testing.T) {
|
|
md := conflictresolve.RenderConfirmationsMD(nil, time.Now())
|
|
if md == "" {
|
|
t.Fatal("empty md")
|
|
}
|
|
}
|