77 lines
3.1 KiB
Go
77 lines
3.1 KiB
Go
package artifact
|
|
|
|
import (
|
|
"context"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"html"
|
|
"os"
|
|
"path/filepath"
|
|
"time"
|
|
)
|
|
|
|
// CreatePipelineTestCard exercises the local, post-generation RIFT pipeline
|
|
// without calling any image provider. The existing character anchor is used as
|
|
// the mock subject and the selected task style image as the background.
|
|
func (w *Worker) CreatePipelineTestCard(ctx context.Context, taskID string) (string, error) {
|
|
var x win
|
|
if err := w.db.QueryRowContext(ctx, `SELECT id,public_seed,display_name,range_bits,nft_prompt_instructions,nft_negative_prompt,nft_style_reference FROM tasks WHERE id=?`, taskID).
|
|
Scan(&x.ID, &x.Seed, &x.DisplayName, &x.RangeBits, &x.PromptInstructions, &x.NegativePrompt, &x.StyleReference); err != nil {
|
|
return "", err
|
|
}
|
|
anchor, err := os.ReadFile(w.characterAnchorPath())
|
|
if err != nil || len(anchor) <= 1024 {
|
|
return "", errors.New("character_anchor.png fehlt; bitte zuerst den RIFT-Anchor erzeugen")
|
|
}
|
|
style, err := w.loadStyleReference(x.StyleReference)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
x.Winner = "LOCAL-PIPELINE-TEST"
|
|
x.Signature = "NO-API-NO-SIGNATURE"
|
|
x.Guess = "LOCAL-TEST"
|
|
x.Completed = time.Now().UTC()
|
|
x.PublicJWK = json.RawMessage(`{}`)
|
|
traits := deriveCollectionTraits(x)
|
|
art := mockTestArtwork(anchor, style)
|
|
card := renderCardSVG(art, "svg", x, traits)
|
|
dir := filepath.Join(w.dir, "_test", taskID)
|
|
if err := os.MkdirAll(dir, 0o750); err != nil {
|
|
return "", err
|
|
}
|
|
if err := atomicWrite(filepath.Join(dir, "art.svg"), art, 0o640); err != nil {
|
|
return "", err
|
|
}
|
|
if err := atomicWrite(filepath.Join(dir, "image.svg"), card, 0o640); err != nil {
|
|
return "", err
|
|
}
|
|
manifest, _ := json.MarshalIndent(map[string]any{
|
|
"kind": "local_pipeline_test", "api_calls": 0, "estimated_cost_usd": 0,
|
|
"task_id": taskID, "style_reference": style.Name, "style_custom": style.Custom,
|
|
"collection_character": "RIFT", "collection_traits": traits,
|
|
"note": "Local test only. No OpenAI/API request was made and no task artifact status was changed.",
|
|
}, "", " ")
|
|
_ = atomicWrite(filepath.Join(dir, "manifest.json"), manifest, 0o640)
|
|
return filepath.Join(dir, "image.svg"), nil
|
|
}
|
|
|
|
func mockTestArtwork(anchor []byte, style TaskStyleReference) []byte {
|
|
a := base64.StdEncoding.EncodeToString(anchor)
|
|
s := base64.StdEncoding.EncodeToString(style.Bytes)
|
|
styleMime := html.EscapeString(style.ContentType)
|
|
if styleMime == "" {
|
|
styleMime = "image/jpeg"
|
|
}
|
|
return []byte(fmt.Sprintf(`<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1536" viewBox="0 0 1024 1536">
|
|
<defs>
|
|
<filter id="blur"><feGaussianBlur stdDeviation="18"/></filter>
|
|
<linearGradient id="shade" x1="0" y1="0" x2="0" y2="1"><stop stop-color="#07101d" stop-opacity=".18"/><stop offset="1" stop-color="#07101d" stop-opacity=".72"/></linearGradient>
|
|
</defs>
|
|
<image href="data:%s;base64,%s" x="-70" y="-50" width="1164" height="1636" preserveAspectRatio="xMidYMid slice" filter="url(#blur)" opacity=".62"/>
|
|
<rect width="1024" height="1536" fill="url(#shade)"/>
|
|
<image href="data:image/png;base64,%s" x="92" y="120" width="840" height="1180" preserveAspectRatio="xMidYMid meet"/>
|
|
</svg>`, styleMime, s, a))
|
|
}
|