Initial commit: img-infinite-canvas AI design workbench MVP
Moteva-style AI design workbench replica. Home prompt creates a project; the project page provides an infinite canvas with node drag, zoom/pan, a design chat panel, history replay, image asset upload, and project save/regenerate. - Backend: go-zero, DDD layering, sqlc/pgx, optional PostgreSQL; memory or Redis cache; asynq job queue; MinIO/S3/R2/OSS object storage; sky-valley/pi agent runtime adapter. - Frontend: Next.js App Router + Vite artifact build, TypeScript, i18n, shadcn/ui components, auth (OTP/Turnstile/Google/WeChat). - Deploy: Docker Compose and k3s manifests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
"img_infinite_canvas/internal/domain/design"
|
||||
)
|
||||
|
||||
func TestAgentShortMemoryUsesSlidingWindow(t *testing.T) {
|
||||
now := time.Now()
|
||||
project := design.Project{Title: "Memory project"}
|
||||
for index := 0; index < 24; index++ {
|
||||
project.Messages = append(project.Messages, design.Message{
|
||||
ID: fmt.Sprintf("m-%d", index),
|
||||
Role: "user",
|
||||
Type: "user",
|
||||
Content: fmt.Sprintf("历史消息 %02d 需要保留高级品牌风格", index),
|
||||
CreatedAt: now.Add(time.Duration(index) * time.Second),
|
||||
})
|
||||
}
|
||||
|
||||
memory := buildAgentMemory(project, "继续优化当前画面", "design", nil)
|
||||
if !strings.Contains(memory.ShortTerm, "历史消息 23") {
|
||||
t.Fatalf("expected latest message in short memory, got %s", memory.ShortTerm)
|
||||
}
|
||||
if strings.Contains(memory.ShortTerm, "历史消息 00") {
|
||||
t.Fatalf("expected oldest message to be outside sliding window, got %s", memory.ShortTerm)
|
||||
}
|
||||
if utf8.RuneCountInString(memory.ShortTerm) > shortMemoryMaxRunes+80 {
|
||||
t.Fatalf("short memory exceeds budget: %d", utf8.RuneCountInString(memory.ShortTerm))
|
||||
}
|
||||
}
|
||||
|
||||
func TestAgentMemoryDoesNotLeakRawAssetURLs(t *testing.T) {
|
||||
project := design.Project{
|
||||
Title: "Asset memory",
|
||||
Nodes: []design.Node{
|
||||
{ID: "image-1", Type: design.NodeTypeImage, Title: "参考图", Content: "http://localhost:19000/canvas-assets/raw.png", Status: "success"},
|
||||
},
|
||||
}
|
||||
memory := buildAgentMemory(project, "参考这张图继续", "image", []design.AgentChatMessage{
|
||||
{
|
||||
Contents: []design.AgentContent{
|
||||
{Type: "image", ImageURL: "https://example.com/private/upload.png", ImageWidth: 1024, ImageHeight: 1024},
|
||||
},
|
||||
},
|
||||
})
|
||||
joined := memory.ShortTerm + "\n" + memory.LongTerm
|
||||
if strings.Contains(joined, "localhost:19000") || strings.Contains(joined, "example.com/private") {
|
||||
t.Fatalf("expected raw asset urls to be omitted from memory, got %s", joined)
|
||||
}
|
||||
if !strings.Contains(joined, "reference image") || !strings.Contains(joined, "asset reference") {
|
||||
t.Fatalf("expected compact asset references, got %s", joined)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAgentLongMemoryKeepsRecentDurableDecisions(t *testing.T) {
|
||||
project := design.Project{Title: "Long memory"}
|
||||
for index := 0; index < 20; index++ {
|
||||
project.Messages = append(project.Messages, design.Message{
|
||||
ID: fmt.Sprintf("d-%d", index),
|
||||
Role: "user",
|
||||
Type: "user",
|
||||
Content: fmt.Sprintf("必须保留品牌风格决定 %02d", index),
|
||||
})
|
||||
}
|
||||
|
||||
memory := buildAgentMemory(project, "继续", "design", nil)
|
||||
if !strings.Contains(memory.LongTerm, "品牌风格决定 19") {
|
||||
t.Fatalf("expected recent durable decision in long memory, got %s", memory.LongTerm)
|
||||
}
|
||||
if strings.Contains(memory.LongTerm, "品牌风格决定 00") {
|
||||
t.Fatalf("expected oldest durable decision to be omitted, got %s", memory.LongTerm)
|
||||
}
|
||||
if utf8.RuneCountInString(memory.LongTerm) > longMemoryMaxRunes+80 {
|
||||
t.Fatalf("long memory exceeds budget: %d", utf8.RuneCountInString(memory.LongTerm))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user