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

101 lines
3.7 KiB
Go

package app
import (
"strings"
"yingce/backend/internal/model"
"yingce/backend/internal/repository"
)
// The anchor records the current request and observable reference candidates.
// It does not infer creative permissions or carry an old goal into a new turn.
type cloudAgentCreativeAnchor struct {
Version int `json:"version"`
UserPrompt string `json:"userPrompt"`
ReferenceNodeIDs []string `json:"referenceNodeIds,omitempty"`
ReferenceAssets []cloudAgentReferenceAnchor `json:"referenceAssets,omitempty"`
}
type cloudAgentReferenceAnchor struct {
NodeID string `json:"nodeId"`
Type string `json:"type"`
Title string `json:"title,omitempty"`
Prompt string `json:"prompt,omitempty"`
AssetTags []string `json:"assetTags,omitempty"`
ReferenceReady bool `json:"referenceReady"`
VisualIdentity string `json:"visualIdentity"`
RequiresVisualInspection bool `json:"requiresVisualInspection"`
// 旧检查点可能含有未经确认的正文摘录,不再自动写入或跨轮继承。
VisualNote string `json:"visualNote,omitempty"`
Width any `json:"width,omitempty"`
Height any `json:"height,omitempty"`
}
// cloudAgentCreativeAnchorForCanvas 按当前画布重建候选素材锚点。
// 不继承旧视觉标记:工具成功和下一段正文都不是识别成功的可靠证据,节点也可能换图。
func cloudAgentCreativeAnchorForCanvas(repo *repository.Repository, userID string, canvas *model.CanvasProject, prompt string, _ *cloudAgentCreativeAnchor) (cloudAgentCreativeAnchor, error) {
anchor := cloudAgentCreativeAnchor{Version: 2, UserPrompt: prompt}
doc, err := creationDocument(canvas.PayloadJSON)
if err != nil {
return cloudAgentCreativeAnchor{}, BadAuthRequest("服务端画布内容无法解析,请先重新同步")
}
nodes := creationMaps(doc["nodes"])
byID := make(map[string]map[string]any, len(nodes))
for _, node := range nodes {
if id := stringValue(node["id"]); id != "" {
byID[id] = node
}
}
ids := make([]string, 0, 16)
for _, node := range nodes {
descriptor, known := cloudAgentNodeCapabilityForNode(node)
if known && descriptor.Connection.CanReference {
ids = append(ids, stringValue(node["id"]))
}
if len(ids) == 16 {
break
}
}
for _, id := range ids {
if id == "" || byID[id] == nil {
continue
}
node := byID[id]
descriptor, known := cloudAgentNodeCapabilityForNode(node)
if !known || !descriptor.Connection.CanReference {
continue
}
meta, _ := node["metadata"].(map[string]any)
prompt, _ := cloudAgentMediaPrompt(meta)
item := cloudAgentReferenceAnchor{
NodeID: stringValue(node["id"]), Type: descriptor.Type,
Title: truncateRunes(stringValue(node["title"]), 300),
Prompt: truncateRunes(prompt, 1000),
VisualIdentity: "unknown", RequiresVisualInspection: true,
}
if tags, ok := meta["assetTags"].([]any); ok {
for _, tag := range tags {
if text := strings.TrimSpace(stringValue(tag)); text != "" {
item.AssetTags = append(item.AssetTags, truncateRunes(text, 120))
}
}
}
if repo != nil {
// 角色卡按三视图判定是否可作参考;普通媒体按节点资源判定。
ref, _, refErr := cloudAgentMediaReference(repo, userID, "", node)
if refErr == nil {
item.ReferenceReady = true
item.Width, item.Height = ref["width"], ref["height"]
}
}
anchor.ReferenceNodeIDs = append(anchor.ReferenceNodeIDs, item.NodeID)
anchor.ReferenceAssets = append(anchor.ReferenceAssets, item)
if len(anchor.ReferenceAssets) == 16 {
break
}
}
return anchor, nil
}