Files
Agent-System-Backend/internal/runtime/llm_direct_n3_test.go
T
weichen f1302ed5b3 Enable canonical versioned Go module consumption
Why: Allow consumers to download an immutable module revision without nested source checkouts or local path replacements.

Evidence: SELF package tests and the prior canonical-path rejection are recorded in the Dev dependency-workspace task card; remote consumption is replayed against the committed candidate.

Impact: Change the module identity and its Go imports while preserving API behavior. Existing historical submodule revisions remain buildable.

Refs: Versioned dependencies U1/U3; feature/dependency-workspace
2026-10-13 15:47:16 +00:00

171 lines
5.7 KiB
Go

package runtime
import (
"context"
"path/filepath"
"strings"
"testing"
agentllm "git-ssh.u1680832.nyat.app/weichen/Agent-Native.git/runtime/llm"
agentsession "git-ssh.u1680832.nyat.app/weichen/Agent-Native.git/runtime/session"
core "git-ssh.u1680832.nyat.app/weichen/Agent-System-Core.git"
)
// ---- N3 WP2:产品会话路径持久化(验收① 产品层机器证据)----
func textTurns(texts ...string) [][]agentllm.Event {
out := make([][]agentllm.Event, 0, len(texts))
for _, t := range texts {
out = append(out, []agentllm.Event{
{Kind: agentllm.KindTextDelta, Text: t},
{Kind: agentllm.KindMessageEnd, Message: agentllm.Message{Role: agentllm.RoleAssistant, Content: t, StopReason: agentllm.StopEnd}},
})
}
return out
}
// 杀窗口形态的种子转录:新 run 前配对修复成立,且 run 正常产出。
func TestDirectRunPairsAfterKillSeed(t *testing.T) {
sandbox := t.TempDir()
sess, err := agentsession.Open(filepath.Join(sandbox, ".agentx", "transcript.jsonl"))
if err != nil {
t.Fatal(err)
}
for _, m := range []agentllm.Message{
{Role: agentllm.RoleSystem, Content: "sys"},
{Role: agentllm.RoleUser, Content: "早前"},
{Role: agentllm.RoleAssistant, StopReason: agentllm.StopToolCalls, ToolCalls: []agentllm.ToolCall{{ID: "c1", Name: "run_python", Arguments: `{}`}}},
} {
if err := sess.Append(m); err != nil {
t.Fatal(err)
}
}
rc := &fakeRunCtx{
ctx: context.Background(), sandbox: sandbox,
prof: core.AgentProfile{Model: core.ModelRoute{Chat: "test-model"}},
history: []core.Message{
{ID: "m1", Role: core.RoleUser, Parts: []core.Part{{Type: core.PartText, Text: "还在吗"}}},
},
}
r := &LLMRunner{Stream: scriptStream(textTurns("还在"), nil)}
if err := r.Execute(rc); err != nil {
t.Fatalf("Execute: %v", err)
}
loaded, err := sess.Load()
if err != nil {
t.Fatal(err)
}
var paired bool
for _, m := range loaded {
if m.Role == agentllm.RoleTool && m.ToolCallID == "c1" {
paired = true
if !m.IsError || !strings.Contains(m.Content, "进程中断") {
t.Fatalf("兜底配对形态不对:%+v", m)
}
}
}
if !paired {
t.Fatalf("杀窗口调用未配对:%#v", loaded)
}
}
// 转录权威语义(质量 P2-I2):首次播种把组装历史持久进转录且恰好一次;
// 后续 run 由转录承载,不重复播种。
func TestDirectRunSeedsStoreHistoryOnce(t *testing.T) {
sandbox := t.TempDir()
rc := &fakeRunCtx{
ctx: context.Background(), sandbox: sandbox,
prof: core.AgentProfile{Model: core.ModelRoute{Chat: "test-model"}},
history: []core.Message{
{ID: "m1", Role: core.RoleUser, Parts: []core.Part{{Type: core.PartText, Text: "历史一"}}},
{ID: "m2", Role: core.RoleAssistant, Parts: []core.Part{{Type: core.PartText, Text: "回复一"}}},
{ID: "m3", Role: core.RoleUser, Parts: []core.Part{{Type: core.PartText, Text: "最新"}}},
},
}
var requests [][]agentllm.Message
base := scriptStream(textTurns("一答", "二答"), nil)
r := &LLMRunner{Stream: func(ctx context.Context, model string, msgs []agentllm.Message, tools []agentllm.ToolDecl, opts agentllm.StreamOptions) <-chan agentllm.Event {
requests = append(requests, append([]agentllm.Message{}, msgs...))
return base(ctx, model, msgs, tools, opts)
}}
if err := r.Execute(rc); err != nil {
t.Fatalf("first Execute: %v", err)
}
// 第二跑:同一 rc(新 user 消息由 history 尾部提供)。
rc.history = append(rc.history, core.Message{ID: "m4", Role: core.RoleUser, Parts: []core.Part{{Type: core.PartText, Text: "再来"}}})
if err := r.Execute(rc); err != nil {
t.Fatalf("second Execute: %v", err)
}
if len(requests) < 2 {
t.Fatalf("stream 调用数 = %d", len(requests))
}
for i, req := range requests {
count := 0
for _, m := range req {
if m.Content == "历史一" {
count++
}
}
if count != 1 {
t.Fatalf("第 %d 跑历史中「历史一」必须恰好一次,得到 %d", i+1, count)
}
}
sess, err := agentsession.Open(filepath.Join(sandbox, ".agentx", "transcript.jsonl"))
if err != nil {
t.Fatal(err)
}
loaded, err := sess.Load()
if err != nil {
t.Fatal(err)
}
count := 0
for _, m := range loaded {
if m.Content == "历史一" {
count++
}
}
if count != 1 {
t.Fatalf("转录应恰好一次包含种子历史「历史一」,得到 %d:%#v", count, loaded)
}
}
// ---- N3 WP5:记忆端口迁移对照(注入文案同一形态)----
// 端口注入的 [记忆] 块到达请求(迁移前经组装侧 InjectMemory 同文案形态)。
func TestDirectRunInjectsMemoryBlock(t *testing.T) {
var gotReq []agentllm.Message
base := scriptStream(textTurns("收到"), nil)
rc := &fakeRunCtx{
ctx: context.Background(),
prof: core.AgentProfile{Model: core.ModelRoute{Chat: "test-model"}},
memEntries: []memEntry{
{typ: "fact", content: "项目用 Go 1.27"},
{typ: "preference", content: "喜欢简洁回复"},
},
}
r := &LLMRunner{Stream: func(ctx context.Context, model string, msgs []agentllm.Message, tools []agentllm.ToolDecl, opts agentllm.StreamOptions) <-chan agentllm.Event {
gotReq = append([]agentllm.Message{}, msgs...)
return base(ctx, model, msgs, tools, opts)
}}
if err := r.Execute(rc); err != nil {
t.Fatalf("Execute: %v", err)
}
var block string
for _, m := range gotReq {
if strings.Contains(m.Content, "[记忆]") {
block = m.Content
}
}
if block == "" {
t.Fatalf("记忆块缺失:%#v", gotReq)
}
// 形态对照(与 ctxasm.InjectMemory 同文):[记忆]\n- (typ) content,确定性排序。
if !strings.Contains(block, "- (fact) 项目用 Go 1.27") ||
!strings.Contains(block, "- (preference) 喜欢简洁回复") {
t.Fatalf("记忆块形态不对:%q", block)
}
if strings.Index(block, "(fact)") > strings.Index(block, "(preference)") {
t.Fatalf("排序不确定:%q", block)
}
}