Files
ttstd-infinite-canvas/backend/internal/app/cloud_agent_context_test.go

125 lines
4.8 KiB
Go

package app
import (
"encoding/json"
"strings"
"testing"
)
func TestCloudAgentContextCompactionPreservesToolPairsAndWrites(t *testing.T) {
request := canonicalAgentRequest{SystemPrompt: "system", PromptCacheKey: "stable", Messages: []map[string]any{{"role": "user", "content": "original instructions"}}}
write := `{"taskId":"paid-task","nodeId":"node","taskSubmitted":true,"status":"running"}`
for i := 0; i < 16; i++ {
body := `{"path":"SKILL.md","content":"` + strings.Repeat("x", 16000) + `"}`
if i == 1 {
body = write
}
request.Messages = append(request.Messages,
map[string]any{"role": "assistant", "content": "", "tool_calls": []map[string]any{{"id": i}}},
map[string]any{"role": "tool", "tool_call_id": i, "content": body})
}
last, _ := json.Marshal(request.Messages[len(request.Messages)-2:])
if !compactCloudAgentContext(&request) {
t.Fatal("large read bodies not compacted")
}
if len(request.Messages) != 33 || request.Messages[0]["content"] != "original instructions" || request.Messages[4]["content"] != write {
t.Fatal("instructions or write receipt lost")
}
after, _ := json.Marshal(request.Messages[len(request.Messages)-2:])
if string(last) != string(after) || request.SystemPrompt != "system" || request.PromptCacheKey != "stable" {
t.Fatal("latest turn or cache prefix modified")
}
for i := 1; i < len(request.Messages); i += 2 {
if request.Messages[i]["role"] != "assistant" || request.Messages[i+1]["role"] != "tool" {
t.Fatal("tool pair broken")
}
}
if compactCloudAgentContext(&request) {
t.Fatal("compaction is not idempotent")
}
}
func TestCloudAgentCanonicalUsesAutomaticToolChoice(t *testing.T) {
req := agentTestRequest()
request := cloudAgentCanonical("system", nil, "读取画布", req)
if request.ToolChoice != "auto" {
t.Fatalf("cloud agent tool choice = %#v", request.ToolChoice)
}
}
func TestCloudAgentUnlimitedBudgetKeepsGenerationTools(t *testing.T) {
for _, limit := range []int{0, 25} {
req := agentTestRequest()
req.PermissionMode = "auto"
req.Budget.MaxGenerationTasks = limit
req.Budget.MaxVideoSeconds = limit * 100
for i := 0; i < 12; i++ {
req.SkillIDs = append(req.SkillIDs, strings.Repeat("s", i+1))
}
if err := validateCloudAgentRequest(&req); err != nil {
t.Fatal(err)
}
if !cloudAgentToolAllowed(req, "generate_media") || !cloudAgentToolAllowed(req, "model_list") {
t.Fatal("unlimited budget disabled generation")
}
}
state := cloudAgentRuntime{Request: agentTestRequest(), Generations: 100, VideoSeconds: 5000}
a := cloudAgentMediaArgs{Mode: "video", Duration: 180, Prompt: "p", Title: "title", NodeID: "new-node", SnapshotHash: strings.Repeat("a", 64), Size: "16:9"}
if err := validateCloudAgentMediaArgs(a, &state); err != nil {
t.Fatal(err)
}
state.Request.Budget.MaxVideoSeconds = 5100
if err := validateCloudAgentMediaArgs(a, &state); err == nil {
t.Fatal("positive video budget bypassed")
}
state.Request.Budget.MaxVideoSeconds = 0
state.Request.Budget.MaxGenerationTasks = 100
if err := validateCloudAgentMediaArgs(a, &state); err == nil {
t.Fatal("positive generation budget bypassed")
}
}
func TestCloudAgentSkillsLoadOnDemandAndPage(t *testing.T) {
s, _, _, _ := creationTestService(t)
var ids []string
for i := 0; i < 10; i++ {
skill, err := s.CreateSkill("user", SkillMutationRequest{SkillName: strings.Repeat("s", i+1), Description: "test", Instruction: "# Test\n\nDescription\n\n" + strings.Repeat("中文", 10000), Tag: "others", IsPrivate: true})
if err != nil {
t.Fatal(err)
}
ids = append(ids, skill.SkillID)
}
snapshots, err := s.cloudAgentSkills("user", ids)
if err != nil {
t.Fatal(err)
}
for _, skill := range snapshots {
if skill.Instruction != "" || skill.Files[cloudAgentSkillEntryPath] != "" {
t.Fatal("eager skill content")
}
}
state := cloudAgentRuntime{Skills: snapshots}
call := skillFeedbackCall(ids[0], cloudAgentSkillEntryPath)
result, err := cloudAgentReadTool(nil, "user", &state, call, s)
if err != nil {
t.Fatal(err)
}
page := result.(map[string]any)
if len([]rune(page["content"].(string))) != 12000 || page["hasMore"] != true {
t.Fatal("missing bounded page")
}
args, _ := json.Marshal(map[string]any{"skillId": ids[0], "path": cloudAgentSkillEntryPath, "offset": page["nextOffset"]})
call.Function.Arguments = string(args)
result, err = cloudAgentReadTool(nil, "user", &state, call, s)
if err != nil || result.(map[string]any)["hasMore"] != false {
t.Fatalf("continuation failed: %v", err)
}
if _, err := cloudAgentReadTool(nil, "other-user", &cloudAgentRuntime{Skills: snapshots}, skillFeedbackCall(ids[0], cloudAgentSkillEntryPath), s); err == nil {
t.Fatal("cross-user skill read")
}
snapshots[0].Hash = "changed"
if _, err := cloudAgentReadTool(nil, "user", &cloudAgentRuntime{Skills: snapshots}, skillFeedbackCall(ids[0], cloudAgentSkillEntryPath), s); err == nil {
t.Fatal("mixed skill version")
}
}