Files

63 lines
2.0 KiB
Go
Raw Permalink Normal View History

package memoir
import (
"bytes"
"context"
"data-terminal-cloud/internal/extraction"
"encoding/base64"
"encoding/json"
"errors"
"io"
"net/http"
"strings"
"time"
)
type ArkIllustrator struct {
BaseURL, APIKey, Model string
Client *http.Client
}
func NewIllustrator(base, key, model string) *ArkIllustrator {
return &ArkIllustrator{strings.TrimRight(base, "/"), key, model, &http.Client{Timeout: 120 * time.Second}}
}
func (a *ArkIllustrator) Generate(ctx context.Context, prompt string) ([]byte, error) {
body, _ := json.Marshal(map[string]any{"model": a.Model, "prompt": prompt + "。温暖的水彩情景插画,米色纸张肌理,禁止文字和水印以外的标记。这是基于叙述的想象性插画,不是真实历史照片。", "size": "2K", "output_format": "jpeg", "response_format": "b64_json", "watermark": true})
req, e := http.NewRequestWithContext(ctx, "POST", a.BaseURL+"/images/generations", bytes.NewReader(body))
if e != nil {
return nil, e
}
req.Header.Set("Authorization", "Bearer "+a.APIKey)
req.Header.Set("Content-Type", "application/json")
resp, e := a.Client.Do(req)
if e != nil {
return nil, errors.New("MEMOIR_IMAGE_UNAVAILABLE")
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
var failure struct {
Error struct {
Code string `json:"code"`
} `json:"error"`
}
_ = json.NewDecoder(io.LimitReader(resp.Body, 64<<10)).Decode(&failure)
if failure.Error.Code == "AccountOverdueError" {
return nil, extraction.ErrModelAccountOverdue
}
return nil, errors.New("MEMOIR_IMAGE_REJECTED")
}
var out struct {
Data []struct {
Base64 string `json:"b64_json"`
} `json:"data"`
}
if json.NewDecoder(io.LimitReader(resp.Body, 16<<20)).Decode(&out) != nil || len(out.Data) != 1 {
return nil, errors.New("MEMOIR_IMAGE_INVALID")
}
b, e := base64.StdEncoding.DecodeString(out.Data[0].Base64)
if e != nil || len(b) > 10<<20 || http.DetectContentType(b) != "image/jpeg" {
return nil, errors.New("MEMOIR_IMAGE_INVALID")
}
return b, nil
}