feat(image): integrate PoloX image editing workflows
Add WaveSpeed image editing and image tools plugins, canvas image editing workflows, layer selection, and Cloud Agent image tools with approval-safe transient references.
This commit is contained in:
1 parent
c7c37f2d4f
commit
6abaa7b45a
34 files changed
+2537
-75
No files matched your search
@@ -140,6 +140,80 @@ func TestCloudAgentToolsFollowCanvasCapabilityRegistry(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCloudAgentImageEditingToolsRespectPermissionBoundaries(t *testing.T) {
|
||||
readOnly := agentTestRequest()
|
||||
readOnly.PermissionMode = "read_only"
|
||||
readOnly.ContextScope = []string{"canvas"}
|
||||
readNames := map[string]bool{}
|
||||
for _, tool := range cloudAgentTools(readOnly) {
|
||||
readNames[tool["function"].(map[string]any)["name"].(string)] = true
|
||||
}
|
||||
for _, name := range []string{"image_text_detect", "image_annotation_render"} {
|
||||
if !readNames[name] {
|
||||
t.Fatalf("read-only tool %s missing", name)
|
||||
}
|
||||
}
|
||||
if readNames["image_layer_split"] {
|
||||
t.Fatal("image_layer_split must not be exposed in read-only mode")
|
||||
}
|
||||
|
||||
write := readOnly
|
||||
write.PermissionMode = "auto"
|
||||
write.Budget.MaxGenerationTasks = 1
|
||||
writeNames := map[string]bool{}
|
||||
for _, tool := range cloudAgentTools(write) {
|
||||
writeNames[tool["function"].(map[string]any)["name"].(string)] = true
|
||||
}
|
||||
if !writeNames["image_layer_split"] || !cloudAgentWrite("image_layer_split") {
|
||||
t.Fatal("image_layer_split must be an approved write tool")
|
||||
}
|
||||
if got := cloudAgentMediaCall(cloudAgentCall{Function: struct {
|
||||
Name string `json:"name"`
|
||||
Arguments string `json:"arguments"`
|
||||
}{Name: "image_layer_split", Arguments: `{"prompt":"split"}`}}); got.Function.Name != "image_layer_split" || !strings.Contains(got.Function.Arguments, `"mode":"image"`) {
|
||||
t.Fatalf("image layer split was not normalized to image media: %+v", got.Function)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCloudAgentAnnotationRenderFeedsControlledTransientReference(t *testing.T) {
|
||||
s, db, _, _ := creationTestService(t)
|
||||
canvas := &model.CanvasProject{ID: "annotation-canvas", UserID: "user", PayloadJSON: `{"nodes":[{"id":"image-1","type":"image","title":"原图","width":640,"height":480,"metadata":{"content":"saved"}}],"connections":[]}`}
|
||||
if err := db.Create(canvas).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
state := &cloudAgentRuntime{Request: CloudAgentRequest{CanvasID: canvas.ID}, TransientReferences: map[string]cloudAgentTransientReference{}}
|
||||
call := cloudAgentCall{ID: "call-annotation"}
|
||||
call.Function.Name = "image_annotation_render"
|
||||
call.Function.Arguments = `{"nodeId":"image-1","annotations":[{"label":"主体","x":0.5,"y":0.5}]}`
|
||||
result, err := cloudAgentReadTool(s.repo, "user", state, call)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
view := result.(map[string]any)
|
||||
refID := stringValue(view["referenceTransientId"])
|
||||
ref, ok := state.TransientReferences[refID]
|
||||
if !ok || !strings.HasPrefix(ref.DataURL, "data:image/png;base64,") {
|
||||
t.Fatalf("annotation transient reference missing or unsafe: %#v", state.TransientReferences)
|
||||
}
|
||||
resultJSON, _ := json.Marshal(result)
|
||||
if !strings.Contains(string(resultJSON), "image/png") {
|
||||
t.Fatalf("annotation result must advertise PNG reference: %s", resultJSON)
|
||||
}
|
||||
doc, err := creationDocument(canvas.PayloadJSON)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
args := cloudAgentMediaArgs{Mode: "image", Prompt: "按标注编辑", SnapshotHash: cloudAgentMediaContentHash(doc), NodeID: "result-image", Title: "编辑结果", Size: "1:1", ReferenceTransientIDs: []string{refID}}
|
||||
_, _, refs, err := cloudAgentMediaDocument(s.repo, "user", canvas.ID, args, state.TransientReferences)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
images, ok := refs["referenceImages"].([]any)
|
||||
if !ok || len(images) != 1 || stringValue(images[0].(map[string]any)["id"]) != refID {
|
||||
t.Fatalf("controlled transient reference was not forwarded: %#v", refs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCloudAgentPolicyPublishesSkillManifestWithoutInliningSkillBody(t *testing.T) {
|
||||
skill := cloudAgentSkill{ID: "skill-1", Name: "任务技能", Version: "v1", Hash: agentProfileHash("skill"), Instruction: "PRIVATE_SKILL_BODY", Files: map[string]string{"references/a.md": "A"}}
|
||||
text, _, err := compileCloudAgentPolicies(agentTestRequest(), []cloudAgentSkill{skill}, "", cloudAgentProfileSnapshot{Revision: agentProfileRevision(nil), Hash: agentProfileHash("")})
|
||||
|
||||
Reference in new issue
Block a user