ci / verify (push) Canceled after 0s
Add customer and operator login, BLE provisioning, device fleet controls, call memory, memoir generation, and refreshed family and maintenance portals. Include API contracts, migrations, firmware sources, tests, assets, and deployment documentation.
63 lines
2.0 KiB
Go
63 lines
2.0 KiB
Go
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
|
|
}
|