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
69 lines
2.4 KiB
Go
69 lines
2.4 KiB
Go
// 最终输出契约测试:内联 <think> 折离 + 终轮正文干净(llm.go splitThinkBlock)。
|
|
package runtime
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"testing"
|
|
|
|
agentllm "git-ssh.u1680832.nyat.app/weichen/Agent-Native.git/runtime/llm"
|
|
core "git-ssh.u1680832.nyat.app/weichen/Agent-System-Core.git"
|
|
)
|
|
|
|
// TestSplitThinkBlock 单测:首部 <think>…</think> 剥离;未闭合/非首部/空块按原文。
|
|
func TestSplitThinkBlock(t *testing.T) {
|
|
cases := []struct {
|
|
in string
|
|
body, think string
|
|
}{
|
|
{"<think>推理</think>答案", "答案", "推理"},
|
|
{" <think>推理</think>\n答案", "答案", "推理"},
|
|
{"<think>推理</think>", "", "推理"},
|
|
{"<think>未闭合", "<think>未闭合", ""},
|
|
{"正文 <think>混入</think>", "正文 <think>混入</think>", ""},
|
|
{"没有标签的正文", "没有标签的正文", ""},
|
|
{"<think></think>答案", "答案", ""},
|
|
}
|
|
for _, c := range cases {
|
|
b, th := splitThinkBlock(c.in)
|
|
if b != c.body || th != c.think {
|
|
t.Errorf("splitThinkBlock(%q) = (%q, %q), want (%q, %q)", c.in, b, th, c.body, c.think)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestLLMRunnerThinkStrippedFromReply 契约:模型把推理以 <think> 内联进最终回复时,
|
|
// 落库拆为「思考(过程)+ 干净正文」——最终输出即对用户的回复。
|
|
func TestLLMRunnerThinkStrippedFromReply(t *testing.T) {
|
|
turns := [][]agentllm.Event{
|
|
{
|
|
agentllm.Event{Kind: agentllm.KindTextDelta, Text: "<think>用户要纪要,我分三步……</think>\n已生成会议纪要,要点三项。"},
|
|
agentllm.Event{Kind: agentllm.KindMessageEnd},
|
|
},
|
|
}
|
|
r := &LLMRunner{Stream: scriptStream(turns, nil)}
|
|
rc := &fakeRunCtx{ctx: context.Background(), prof: core.AgentProfile{Model: core.ModelRoute{Chat: "m"}}}
|
|
if err := r.Execute(rc); err != nil {
|
|
t.Fatalf("Execute: %v", err)
|
|
}
|
|
var think, text *core.Part
|
|
for i := range rc.assistant {
|
|
p := rc.assistant[i]
|
|
if p.Type == core.PartThinking {
|
|
think = &rc.assistant[i]
|
|
}
|
|
if p.Type == core.PartText {
|
|
text = &rc.assistant[i]
|
|
}
|
|
}
|
|
if think == nil || !strings.Contains(think.Text, "分三步") || !think.Process {
|
|
t.Errorf("内联推理未折离为思考部件: %+v", rc.assistant)
|
|
}
|
|
if text == nil || strings.Contains(text.Text, "<think>") || !strings.Contains(text.Text, "要点三项") {
|
|
t.Errorf("最终正文不干净: %+v", text)
|
|
}
|
|
if text.Process {
|
|
t.Errorf("终轮正文不得标记 Process: %+v", text)
|
|
}
|
|
}
|