更新runway

This commit is contained in:
2026-07-14 22:44:18 +08:00
parent 4a1b91b759
commit 7a781fecfc
3 changed files with 215 additions and 37 deletions
+30 -14
View File
@@ -10,7 +10,6 @@ import (
"time"
tlsclient "github.com/bogdanfinn/tls-client"
"github.com/google/uuid"
)
// GenerateImage runs a Runway gemini image text/image-to-image pipeline:
@@ -48,6 +47,7 @@ func (c *Client) GenerateImage(ctx context.Context, token, teamID, modelID, prom
}
var refImages []map[string]any
var refAssetIDs []string
for i, raw := range refs {
if len(raw) == 0 {
continue
@@ -62,11 +62,23 @@ func (c *Client) GenerateImage(ctx context.Context, token, teamID, modelID, prom
"assetId": assetID,
"url": url,
})
refAssetIDs = append(refAssetIDs, assetID)
}
assetGroupID, _ := c.assetGroupID(ctx, directClient, token, teamID) // best-effort
// Browser order: create the real session FIRST, attach the references and its
// own asset group, THEN submit the task into it (see createSession). The task
// carries this session's assetGroupId, NOT the account-wide "Generations"
// group.
sessionID, err := c.createSession(ctx, submitClient, token, teamID)
if err != nil {
return nil, nil, err
}
for _, aid := range refAssetIDs {
c.attachReference(ctx, submitClient, token, teamID, sessionID, aid)
}
assetGroupID, _ := c.sessionAssetGroup(ctx, submitClient, token, teamID, sessionID) // best-effort
taskID, err := c.createImageTask(ctx, submitClient, token, teamID, modelID, prompt, aspectRatio, imageSize, assetGroupID, refImages)
taskID, err := c.createImageTask(ctx, submitClient, token, teamID, modelID, prompt, aspectRatio, imageSize, sessionID, assetGroupID, refImages)
if err != nil {
return nil, nil, err
}
@@ -111,13 +123,19 @@ func (c *Client) uploadReference(ctx context.Context, client tlsclient.HttpClien
return assetID, refURL, nil
}
// createImageTask creates a gemini image task (workflow_gemini_image for Pro,
// gemini_3_1_flash_image for Nano Banana 2) and returns its id.
func (c *Client) createImageTask(ctx context.Context, client tlsclient.HttpClient, token, teamID, modelID, prompt, aspectRatio, imageSize, assetGroupID string, refImages []map[string]any) (string, error) {
// createImageTask submits a gemini image task (workflow_gemini_image for Pro,
// gemini_3_1_flash_image for Nano Banana 2) INTO the pre-created session and
// returns its id. sessionID must be a real server-side session (see
// createSession) and assetGroupID must be that session's own group.
func (c *Client) createImageTask(ctx context.Context, client tlsclient.HttpClient, token, teamID, modelID, prompt, aspectRatio, imageSize, sessionID, assetGroupID string, refImages []map[string]any) (string, error) {
if strings.TrimSpace(aspectRatio) == "" {
aspectRatio = "16:9"
}
taskType := "workflow_gemini_image"
opts := map[string]any{
"name": "Nano Banana Pro - " + prompt,
"text_prompt": prompt,
"aspect_ratio": aspectRatio,
"num_images": 1,
"image_size": imageSize,
"model": "gemini-3-pro-image-preview",
@@ -128,25 +146,21 @@ func (c *Client) createImageTask(ctx context.Context, client tlsclient.HttpClien
taskType = "gemini_3_1_flash_image"
opts["name"] = "Nano Banana 2 - " + prompt
opts["model"] = "gemini-3.1-flash-image-preview"
if strings.TrimSpace(aspectRatio) == "" {
aspectRatio = "16:9"
}
opts["aspect_ratio"] = aspectRatio
}
// assetGroupId is present on every real browser task submit; omitting it is a
// bot tell. Best-effort — only attach when we resolved the "Generations"
// group for this workspace.
if assetGroupID != "" {
opts["assetGroupId"] = assetGroupID
}
if len(refImages) > 0 {
opts["reference_images"] = refImages
}
// Pre-flight cost estimate, exactly as the web app does before every spend.
c.estimateCost(ctx, client, token, teamID, "gemini_image", opts)
res, err := c.submitTask(ctx, client, token, teamID, map[string]any{
"taskType": taskType,
"options": opts,
"asTeamId": jsonNumberOrString(teamID),
"sessionId": uuid.NewString(),
"sessionId": sessionID,
})
if err != nil {
return "", err
@@ -156,5 +170,7 @@ func (c *Client) createImageTask(ctx context.Context, client tlsclient.HttpClien
if id == "" {
return "", fmt.Errorf("%w: image task missing id", ErrTemporaryUpstream)
}
// Post-submit: generation record + session play (session already exists).
c.recordGeneration(ctx, client, token, teamID, id, sessionID, prompt, opts)
return id, nil
}