Files
Agent-System-Backend/internal/runtime/llm_direct_e1_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

104 lines
4.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package runtime
import (
"strings"
"testing"
nruntime "git-ssh.u1680832.nyat.app/weichen/Agent-Native.git/runtime"
agentllm "git-ssh.u1680832.nyat.app/weichen/Agent-Native.git/runtime/llm"
core "git-ssh.u1680832.nyat.app/weichen/Agent-System-Core.git"
)
// ---- F1:delegate_task 回传子代理全文,不再只有 160 字摘要 ----
func TestDelegateToolReturnsFullText(t *testing.T) {
full := strings.Repeat("子代理结论细节。", 60) // 480 字,远超 160
f := &fakeRunCtx{spawnAgentFn: func(agentID, name, task, hint string) (core.AgentResultPayload, error) {
return core.AgentResultPayload{Status: core.RunCompleted, Text: full, Summary: "完成(160字内摘要)"}, nil
}}
st := &directRun{l: &LLMRunner{}, rc: f, eff: f.Settings(), inflight: map[string]*toolSlot{}}
res := st.runTool("delegate_task", `{"name":"调研","task":"查证X"}`)
if res.IsError {
t.Fatalf("delegate failed: %s", res.Content)
}
if !strings.Contains(res.Content, "子代理结论细节。子代理结论细节。子代理结论细节。") {
t.Fatalf("delegate result must carry the full child text, got: %.120s…", res.Content)
}
if strings.Contains(res.Content, "160字内摘要") {
t.Fatalf("summary must be fallback only, got: %.120s…", res.Content)
}
}
// ---- F5:并行执行下工具事件按 CallID 配对 ----
func TestToolEventsPairByCallID(t *testing.T) {
f := &fakeRunCtx{}
st := &directRun{l: &LLMRunner{}, rc: f, eff: f.Settings(), inflight: map[string]*toolSlot{}}
st.queued = []agentllm.ToolCall{
{ID: "c1", Name: "knowledge_search", Arguments: `{}`},
{ID: "c2", Name: "web_search", Arguments: `{}`},
}
// 乱序进出:c1 先开始,c2 先结束(并行下的真实时序)。
st.onEvent(nruntime.Event{Type: "tool_execution_start", Tool: "knowledge_search", CallID: "c1"})
st.onEvent(nruntime.Event{Type: "tool_execution_start", Tool: "web_search", CallID: "c2"})
st.onEvent(nruntime.Event{Type: "tool_execution_end", Tool: "web_search", CallID: "c2"})
st.onEvent(nruntime.Event{Type: "tool_execution_end", Tool: "knowledge_search", CallID: "c1"})
if len(st.inflight) != 0 {
t.Fatalf("inflight must be empty after both ends: %v", st.inflight)
}
if len(st.finalParts) != 4 {
t.Fatalf("finalParts = %d (want 4: call+result ×2)", len(st.finalParts))
}
// 结束顺序决定部件顺序:先 c2 后 c1。
if got := st.finalParts[0].ToolCall.ID; got != "c2" {
t.Fatalf("first tool part = %s (want c2)", got)
}
if got := st.finalParts[1].ToolResult.CallID; got != "c2" {
t.Fatalf("first result part = %s (want c2)", got)
}
if got := st.finalParts[2].ToolCall.ID; got != "c1" {
t.Fatalf("second tool part = %s (want c1)", got)
}
if got := st.finalParts[3].ToolResult.CallID; got != "c1" {
t.Fatalf("second result part = %s (want c1)", got)
}
}
// ---- F5:执行模式与串行集合 ----
func TestToolExecModeAndSerialSet(t *testing.T) {
if got := toolExecMode(core.EffectiveSettings{}); got != nruntime.ExecParallel {
t.Fatalf("default must be parallel, got %q", got)
}
off := core.EffectiveSettings{"runtime": map[string]any{"parallelTools": false}}
if got := toolExecMode(off); got != "" {
t.Fatalf("parallelTools=false must fall back to sequential, got %q", got)
}
// 顺序敏感工具必须串行;可并行的读检索与委派不得误入。
for _, name := range []string{"update_plan", "update_todos", "submit_card", "card_begin", "card_append", "card_commit", "run_workflow", "squad_orchestrate", "run_shell", "run_python", "register_artifact"} {
if !serialTools[name] {
t.Fatalf("serialTools missing %s", name)
}
}
for _, name := range []string{"delegate_task", "review_findings", "knowledge_search", "web_search", "fs_read_file", "write_artifact"} {
if serialTools[name] {
t.Fatalf("serialTools must not contain %s", name)
}
}
}
// ---- F2:MiniMax 前缀自动置位 reasoning_split ----
func TestOpenLLMClientReasoningSplitAuto(t *testing.T) {
if c := openLLMClient("http://x", "k", "MiniMax-M2.7-highspeed"); !c.ReasoningSplit {
t.Fatal("MiniMax model must set ReasoningSplit")
}
if c := openLLMClient("http://x", "k", "MiniMax-M3"); !c.ReasoningSplit {
t.Fatal("MiniMax M3 must set ReasoningSplit")
}
if c := openLLMClient("http://x", "k", "claude-sonnet-5"); c.ReasoningSplit {
t.Fatal("non-MiniMax model must not set ReasoningSplit")
}
}