feat(piagent): bypass planner for direct image generation modes
For image / pure-image / image-generator / image-action modes, skip the task-planning round trip and hand the prompt straight to the image model, deriving the requested image count from the composer settings. Also normalize completed image-generator nodes back into regular image nodes (clearing the image-generator layer role and defaulting Tone to "visual") when merging generated results and when restoring finished nodes, so a finished generator node is not re-appended as a duplicate. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -46,6 +46,10 @@ func NewDesignAgent(base design.AgentRunner, imageAgent ImageGenerator, textAgen
|
||||
}
|
||||
|
||||
func (a *DesignAgent) Plan(ctx context.Context, req design.AgentRequest) (design.AgentPlan, error) {
|
||||
if shouldUseDirectImageGeneration(req) {
|
||||
return a.planDirectImageGeneration(ctx, req)
|
||||
}
|
||||
|
||||
plan, err := a.base.Plan(ctx, req)
|
||||
if err != nil {
|
||||
return design.AgentPlan{}, err
|
||||
@@ -76,6 +80,10 @@ func (a *DesignAgent) Plan(ctx context.Context, req design.AgentRequest) (design
|
||||
}
|
||||
|
||||
func (a *DesignAgent) PlanIncremental(ctx context.Context, req design.AgentRequest, onResult func(design.AgentImageGenerationResult) error) (design.AgentPlan, error) {
|
||||
if shouldUseDirectImageGeneration(req) {
|
||||
return a.planDirectImageGenerationIncremental(ctx, req, onResult)
|
||||
}
|
||||
|
||||
plan, err := a.base.Plan(ctx, req)
|
||||
if err != nil {
|
||||
return design.AgentPlan{}, err
|
||||
@@ -115,6 +123,92 @@ func (a *DesignAgent) PlanIncremental(ctx context.Context, req design.AgentReque
|
||||
return plan, nil
|
||||
}
|
||||
|
||||
func (a *DesignAgent) planDirectImageGeneration(ctx context.Context, req design.AgentRequest) (design.AgentPlan, error) {
|
||||
if a.imageAgent == nil {
|
||||
return design.AgentPlan{}, fmt.Errorf("gpt image 2 is not configured")
|
||||
}
|
||||
generationReq := req
|
||||
generationReq.ImageSize = resolveImageSizeForGeneration(req.ImageSize, req.Prompt)
|
||||
prompts := directImagePrompts(generationReq)
|
||||
imageURLs, err := a.generatePlannedImages(ctx, prompts, generationReq)
|
||||
if err != nil {
|
||||
return design.AgentPlan{}, fmt.Errorf("gpt image 2 generation failed: %w", err)
|
||||
}
|
||||
plan := directImagePlan(generationReq)
|
||||
applyPlannedGeneratedImages(&plan, generationReq, imageURLs, prompts)
|
||||
title := ""
|
||||
if len(prompts) > 0 {
|
||||
title = prompts[0].Title
|
||||
}
|
||||
plan.Message.Title = generatedMessageTitle(title, req.Prompt)
|
||||
return plan, nil
|
||||
}
|
||||
|
||||
func (a *DesignAgent) planDirectImageGenerationIncremental(ctx context.Context, req design.AgentRequest, onResult func(design.AgentImageGenerationResult) error) (design.AgentPlan, error) {
|
||||
if a.imageAgent == nil {
|
||||
return design.AgentPlan{}, fmt.Errorf("gpt image 2 is not configured")
|
||||
}
|
||||
generationReq := req
|
||||
generationReq.ImageSize = resolveImageSizeForGeneration(req.ImageSize, req.Prompt)
|
||||
prompts := directImagePrompts(generationReq)
|
||||
plan := directImagePlan(generationReq)
|
||||
nodes, err := a.generatePlannedImagesIncremental(ctx, prompts, generationReq, plan.GeneratedNodes, onResult)
|
||||
if err != nil {
|
||||
if len(nodes) > 0 {
|
||||
plan.GeneratedNodes = nodes
|
||||
plan.Connections = nil
|
||||
}
|
||||
return plan, fmt.Errorf("gpt image 2 generation failed: %w", err)
|
||||
}
|
||||
plan.GeneratedNodes = nodes
|
||||
plan.Connections = nil
|
||||
title := ""
|
||||
if len(prompts) > 0 {
|
||||
title = prompts[0].Title
|
||||
}
|
||||
plan.Message.Title = generatedMessageTitle(title, req.Prompt)
|
||||
return plan, nil
|
||||
}
|
||||
|
||||
func directImagePlan(req design.AgentRequest) design.AgentPlan {
|
||||
return design.AgentPlan{
|
||||
Message: design.Message{
|
||||
ID: uuid.NewString(),
|
||||
Role: "assistant",
|
||||
Type: "assistant",
|
||||
Title: generatedMessageTitle("", req.Prompt),
|
||||
Content: "",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func directImagePrompts(req design.AgentRequest) []design.AgentImagePrompt {
|
||||
count := plannedGenerationCount(req)
|
||||
prompt := strings.TrimSpace(req.Prompt)
|
||||
if prompt == "" {
|
||||
prompt = fallbackDirectImagePrompt(req)
|
||||
}
|
||||
prompts := make([]design.AgentImagePrompt, 0, count)
|
||||
for index := 0; index < count; index++ {
|
||||
title := normalizeGeneratedTitle("", req.Prompt)
|
||||
if index < len(req.TaskPlan.ImageTasks) {
|
||||
if taskTitle := strings.TrimSpace(req.TaskPlan.ImageTasks[index].Title); taskTitle != "" {
|
||||
title = normalizeGeneratedTitle(taskTitle, req.Prompt)
|
||||
}
|
||||
}
|
||||
prompts = append(prompts, design.AgentImagePrompt{
|
||||
Title: title,
|
||||
Prompt: prepareGPTImagePrompt(variantImagePrompt(prompt, index+1, count), imageGenerationPromptMaxRunes),
|
||||
})
|
||||
}
|
||||
return prompts
|
||||
}
|
||||
|
||||
func shouldUseDirectImageGeneration(req design.AgentRequest) bool {
|
||||
mode := strings.ToLower(strings.TrimSpace(req.Mode))
|
||||
return mode == "image" || mode == "pure-image" || mode == "image-generator" || strings.Contains(mode, "image-action")
|
||||
}
|
||||
|
||||
func (a *DesignAgent) buildImagePrompts(ctx context.Context, req design.AgentRequest) []design.AgentImagePrompt {
|
||||
count := plannedGenerationCount(req)
|
||||
prompts := make([]design.AgentImagePrompt, 0, count)
|
||||
@@ -667,28 +761,7 @@ func applyGeneratedImages(plan *design.AgentPlan, req design.AgentRequest, image
|
||||
slots := generatedImageSlots(req, plan.GeneratedNodes, requestedCount)
|
||||
nodes := make([]design.Node, 0, requestedCount)
|
||||
for index := 0; index < requestedCount; index++ {
|
||||
node := slots[index]
|
||||
node.Type = design.NodeTypeImage
|
||||
node.Title = generatedNodeTitle(title, req.Prompt, index, requestedCount)
|
||||
node.Content = imageURLs[index]
|
||||
node.Status = "success"
|
||||
if node.ID == "" {
|
||||
node.ID = uuid.NewString()
|
||||
}
|
||||
if width, height, ok := imageSizeDimensions(req.ImageSize); ok {
|
||||
node.Width = width
|
||||
node.Height = height
|
||||
}
|
||||
if node.Width <= 0 {
|
||||
node.Width = design.DefaultGeneratedImageWidth
|
||||
}
|
||||
if node.Height <= 0 {
|
||||
node.Height = design.DefaultGeneratedImageHeight
|
||||
}
|
||||
if node.Tone == "" {
|
||||
node.Tone = "visual"
|
||||
}
|
||||
nodes = append(nodes, node)
|
||||
nodes = append(nodes, generatedImageNodeFromSlot(slots[index], req, imageURLs[index], title, index, requestedCount))
|
||||
}
|
||||
plan.GeneratedNodes = nodes
|
||||
plan.Connections = nil
|
||||
@@ -731,7 +804,10 @@ func generatedImageNodeFromSlot(slot design.Node, req design.AgentRequest, image
|
||||
if node.Height <= 0 {
|
||||
node.Height = design.DefaultGeneratedImageHeight
|
||||
}
|
||||
if node.Tone == "" {
|
||||
if node.LayerRole == "image-generator" {
|
||||
node.LayerRole = ""
|
||||
}
|
||||
if node.Tone == "" || node.Tone == "image-generator" {
|
||||
node.Tone = "visual"
|
||||
}
|
||||
return node
|
||||
|
||||
Reference in New Issue
Block a user