a5a35648d7
Treat a generator task as completed when it produces artifacts, when a task message reaches a terminal state, or when the project is ready — even if the project is still exploring another concurrent thread. Adds generatorTaskMessageCompleted to classify terminal task messages and covers ready-without-messages and concurrent-thread cases with tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
191 lines
6.3 KiB
Go
191 lines
6.3 KiB
Go
package application
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"img_infinite_canvas/internal/domain/design"
|
|
)
|
|
|
|
func TestGeneratorTaskStatusWaitsForActiveCanvasWork(t *testing.T) {
|
|
threadID := "thread-image"
|
|
project := design.Project{
|
|
ID: "project-image",
|
|
LastThreadID: threadID,
|
|
Status: design.StatusExploring,
|
|
UpdatedAt: time.Now(),
|
|
Nodes: []design.Node{
|
|
{
|
|
ID: "image-1",
|
|
Type: design.NodeTypeImage,
|
|
Title: "Done image",
|
|
Content: "http://localhost:19000/canvas-assets/done.png",
|
|
Status: "success",
|
|
},
|
|
{
|
|
ID: "image-2",
|
|
Type: design.NodeTypeImage,
|
|
Title: "Pending image",
|
|
Content: "pending prompt",
|
|
Status: "generating",
|
|
},
|
|
},
|
|
Messages: []design.Message{
|
|
{ID: "user", Role: "user", Type: "user", Content: "make two images", ThreadID: threadID},
|
|
{ID: "planner", Role: "assistant", Type: "assistant", Status: "success", ThreadID: threadID, Name: "agent_planner"},
|
|
{ID: "tool", Role: "tool", Type: "tool", Title: "GPT Image 2", Status: "running", ThreadID: threadID},
|
|
{
|
|
ID: "artifact-1",
|
|
Role: "tool",
|
|
Type: "tool_use",
|
|
Title: "generate_media",
|
|
Name: "generate_media",
|
|
ToolHint: "generate_image_gpt_image_2",
|
|
Status: "success",
|
|
ThreadID: threadID,
|
|
CreatedAt: time.Now(),
|
|
Content: `[{"event_data":{"artifact":[{"artifact_id":"image-1","artifact_type":"image_info","format":"png","height":1024,"image_name":"Done image","image_url":"http://localhost:19000/canvas-assets/done.png","prompt_text":"make two images","width":1024}]}}]`,
|
|
},
|
|
},
|
|
}
|
|
|
|
task := generatorTaskFromProject(project, threadID)
|
|
if task.Status != "submitted" {
|
|
t.Fatalf("expected active canvas work to keep task submitted, got %s", task.Status)
|
|
}
|
|
if len(task.Artifacts) != 1 {
|
|
t.Fatalf("expected partial artifact to remain visible, got %#v", task.Artifacts)
|
|
}
|
|
}
|
|
|
|
func TestGeneratorTaskStatusDoesNotCompleteExploringPlannerOnlyThread(t *testing.T) {
|
|
threadID := "thread-planning"
|
|
project := design.Project{
|
|
ID: "project-planning",
|
|
LastThreadID: threadID,
|
|
Status: design.StatusExploring,
|
|
Messages: []design.Message{
|
|
{ID: "user", Role: "user", Type: "user", Content: "make images", ThreadID: threadID},
|
|
{ID: "planner", Role: "assistant", Type: "assistant", Status: "success", ThreadID: threadID, Name: "agent_planner"},
|
|
},
|
|
}
|
|
|
|
task := generatorTaskFromProject(project, threadID)
|
|
if task.Status != "submitted" {
|
|
t.Fatalf("expected exploring planner-only thread to stay submitted, got %s", task.Status)
|
|
}
|
|
}
|
|
|
|
func TestGeneratorTaskStatusCompletesWhenCanvasSettles(t *testing.T) {
|
|
threadID := "thread-image"
|
|
project := design.Project{
|
|
ID: "project-image",
|
|
LastThreadID: threadID,
|
|
Status: design.StatusReady,
|
|
UpdatedAt: time.Now(),
|
|
Nodes: []design.Node{
|
|
{
|
|
ID: "image-1",
|
|
Type: design.NodeTypeImage,
|
|
Title: "Done image",
|
|
Content: "http://localhost:19000/canvas-assets/done.png",
|
|
Status: "success",
|
|
},
|
|
},
|
|
Messages: []design.Message{
|
|
{ID: "user", Role: "user", Type: "user", Content: "make one image", ThreadID: threadID},
|
|
{ID: "tool", Role: "tool", Type: "tool", Title: "GPT Image 2", Status: "success", ThreadID: threadID},
|
|
{
|
|
ID: "artifact-1",
|
|
Role: "tool",
|
|
Type: "tool_use",
|
|
Title: "generate_media",
|
|
Name: "generate_media",
|
|
Status: "success",
|
|
ThreadID: threadID,
|
|
Content: `[{"event_data":{"artifact":[{"artifact_id":"image-1","artifact_type":"image_info","format":"png","height":1024,"image_name":"Done image","image_url":"http://localhost:19000/canvas-assets/done.png","prompt_text":"make one image","width":1024}]}}]`,
|
|
},
|
|
{ID: "assistant", Role: "assistant", Type: "assistant", Status: "success", ThreadID: threadID},
|
|
},
|
|
}
|
|
|
|
task := generatorTaskFromProject(project, threadID)
|
|
if task.Status != "completed" {
|
|
t.Fatalf("expected settled canvas task to complete, got %s", task.Status)
|
|
}
|
|
}
|
|
|
|
func TestGeneratorTaskStatusCompletesReadyProjectWithoutTaskMessages(t *testing.T) {
|
|
threadID := "thread-without-messages"
|
|
project := design.Project{
|
|
ID: "project-ready-without-messages",
|
|
LastThreadID: threadID,
|
|
Status: design.StatusReady,
|
|
UpdatedAt: time.Now(),
|
|
Nodes: []design.Node{
|
|
{
|
|
ID: "image-1",
|
|
Type: design.NodeTypeImage,
|
|
Title: "Generated image",
|
|
Content: "http://localhost:19000/canvas-assets/done.png",
|
|
Status: "success",
|
|
},
|
|
},
|
|
}
|
|
|
|
task := generatorTaskFromProject(project, threadID)
|
|
if task.Status != "completed" {
|
|
t.Fatalf("expected ready project without task messages to complete, got %s", task.Status)
|
|
}
|
|
}
|
|
|
|
func TestGeneratorTaskStatusCompletesFinishedThreadWhileProjectStillExploring(t *testing.T) {
|
|
threadID := "thread-done"
|
|
activeThreadID := "thread-active"
|
|
now := time.Now()
|
|
project := design.Project{
|
|
ID: "project-concurrent",
|
|
LastThreadID: activeThreadID,
|
|
Status: design.StatusExploring,
|
|
UpdatedAt: now,
|
|
Nodes: []design.Node{
|
|
{
|
|
ID: "done-image",
|
|
Type: design.NodeTypeImage,
|
|
Title: "Done image",
|
|
Content: "http://localhost:19000/canvas-assets/done.png",
|
|
Status: "success",
|
|
},
|
|
{
|
|
ID: "active-image",
|
|
Type: design.NodeTypeImage,
|
|
Title: "Active image",
|
|
Content: "pending prompt",
|
|
Status: "generating",
|
|
},
|
|
},
|
|
Messages: []design.Message{
|
|
generatorTaskMetadataMessage(threadID, removeBackgroundGeneratorName, design.GeneratorTaskInputArgs{}, 4, now.Add(-2*time.Second)),
|
|
{ID: "user", Role: "user", Type: "user", Name: "canvas_action", Status: "running", ThreadID: threadID},
|
|
toolArtifactsMessage(threadID, "remove background", []design.Node{
|
|
{
|
|
ID: "done-image",
|
|
Type: design.NodeTypeImage,
|
|
Title: "Done image",
|
|
Content: "http://localhost:19000/canvas-assets/done.png",
|
|
Status: "success",
|
|
Width: 1024,
|
|
Height: 1024,
|
|
},
|
|
}, now.Add(-time.Second)),
|
|
{ID: "assistant", Role: "assistant", Type: "assistant", Name: "canvas_action", Status: "success", ThreadID: threadID},
|
|
{ID: "active-tool", Role: "tool", Type: "tool", Title: "GPT Image 2", Status: "running", ThreadID: activeThreadID},
|
|
},
|
|
}
|
|
|
|
task := generatorTaskFromProject(project, threadID)
|
|
if task.Status != "completed" {
|
|
t.Fatalf("expected finished thread to complete while another task runs, got %s", task.Status)
|
|
}
|
|
}
|