Split ingress keys into text/image protocols with Replicate model listing.
CI / docker (push) Successful in 3m38s

Add model input defaults (including prompt), workflow model editing, ingress API docs, and fix img2img image upload filenames for signed URLs.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
renjue
2026-06-29 21:41:06 +08:00
parent 2665ac1dee
commit 321683f863
35 changed files with 1646 additions and 167 deletions
+7 -5
View File
@@ -82,17 +82,19 @@ type ModelEntry struct {
WorkflowType WorkflowType `gorm:"default:txt2img" json:"workflow_type"`
WorkflowJSON string `gorm:"type:text" json:"workflow_json"`
InputBindingJSON string `gorm:"type:text" json:"input_binding_json"`
InputDefaultsJSON string `gorm:"type:text;default:'{}'" json:"input_defaults_json"`
VersionHash string `gorm:"index" json:"version_hash"`
Enabled bool `gorm:"default:true" json:"enabled"`
CreatedAt time.Time `json:"created_at"`
}
type IngressKey struct {
ID uint `gorm:"primaryKey" json:"id"`
Name string `gorm:"not null" json:"name"`
KeyHash string `gorm:"uniqueIndex;not null" json:"-"`
Prefix string `gorm:"not null" json:"prefix"`
Enabled bool `gorm:"default:true" json:"enabled"`
ID uint `gorm:"primaryKey" json:"id"`
Name string `gorm:"not null" json:"name"`
KeyHash string `gorm:"uniqueIndex;not null" json:"-"`
Prefix string `gorm:"not null" json:"prefix"`
Protocol IngressKeyProtocol `gorm:"not null;default:text" json:"protocol"`
Enabled bool `gorm:"default:true" json:"enabled"`
BudgetLimit float64 `gorm:"default:0" json:"budget_limit"`
BudgetUsed float64 `gorm:"default:0" json:"budget_used"`
RateLimitRPM int `gorm:"default:0" json:"rate_limit_rpm"`
+21
View File
@@ -0,0 +1,21 @@
package model
// IngressKeyProtocol distinguishes OpenAI-compatible text APIs from Replicate-compatible image APIs.
type IngressKeyProtocol string
const (
IngressProtocolText IngressKeyProtocol = "text"
IngressProtocolImage IngressKeyProtocol = "image"
)
func (p IngressKeyProtocol) Valid() bool {
return p == IngressProtocolText || p == IngressProtocolImage
}
// EffectiveProtocol returns text for legacy rows without an explicit protocol.
func (k IngressKey) EffectiveProtocol() IngressKeyProtocol {
if k.Protocol == "" {
return IngressProtocolText
}
return k.Protocol
}
+22
View File
@@ -0,0 +1,22 @@
package model
import "testing"
func TestIngressKeyEffectiveProtocol(t *testing.T) {
if (IngressKey{}).EffectiveProtocol() != IngressProtocolText {
t.Fatal("empty protocol should default to text")
}
k := IngressKey{Protocol: IngressProtocolImage}
if k.EffectiveProtocol() != IngressProtocolImage {
t.Fatal("expected image protocol")
}
}
func TestIngressKeyProtocolValid(t *testing.T) {
if !IngressProtocolText.Valid() || !IngressProtocolImage.Valid() {
t.Fatal("text and image should be valid")
}
if IngressKeyProtocol("other").Valid() {
t.Fatal("unexpected valid protocol")
}
}
+38
View File
@@ -1,5 +1,7 @@
package model
import "strings"
// IngressID returns the model name exposed to ingress API clients.
func (m ModelEntry) IngressID() string {
if m.Alias != "" {
@@ -8,6 +10,27 @@ func (m ModelEntry) IngressID() string {
return m.ModelID
}
// SplitIngressOwnerName splits an ingress model identifier into Replicate owner/name parts.
func SplitIngressOwnerName(ingressID, providerName string) (owner, name string) {
if i := strings.Index(ingressID, "/"); i > 0 {
return ingressID[:i], ingressID[i+1:]
}
owner = providerName
if owner == "" {
owner = "luminary"
}
name = ingressID
if name == "" {
name = "model"
}
return owner, name
}
// OwnerName returns the Replicate-style owner/name for an ingress model entry.
func (m ModelEntry) OwnerName(providerName string) (string, string) {
return SplitIngressOwnerName(m.IngressID(), providerName)
}
// MatchesIngressName reports whether the entry matches an ingress model identifier.
func (m ModelEntry) MatchesIngressName(name string) bool {
if name == "" || !m.Enabled {
@@ -18,3 +41,18 @@ func (m ModelEntry) MatchesIngressName(name string) bool {
}
return m.Alias != "" && m.Alias == name
}
// MatchesIngressRef reports whether ref identifies this model (ingress ID, owner/name, or short name).
func (m ModelEntry) MatchesIngressRef(ref, providerName string) bool {
if !m.Enabled || ref == "" {
return false
}
if m.MatchesIngressName(ref) {
return true
}
owner, name := m.OwnerName(providerName)
if ref == name || ref == owner+"/"+name {
return true
}
return false
}
+32
View File
@@ -0,0 +1,32 @@
package model
import "testing"
func TestSplitIngressOwnerName(t *testing.T) {
owner, name := SplitIngressOwnerName("luminary/flux-sdxl", "comfy-local")
if owner != "luminary" || name != "flux-sdxl" {
t.Fatalf("got %q/%q", owner, name)
}
owner, name = SplitIngressOwnerName("plain-model", "comfy-local")
if owner != "comfy-local" || name != "plain-model" {
t.Fatalf("got %q/%q", owner, name)
}
}
func TestMatchesIngressRef(t *testing.T) {
entry := ModelEntry{
Enabled: true,
Alias: "luminary/flux-txt2img",
ModelID: "internal-flux",
}
provider := "comfy-ui"
if !entry.MatchesIngressRef("luminary/flux-txt2img", provider) {
t.Fatal("expected ingress id / owner/name match")
}
if !entry.MatchesIngressRef("flux-txt2img", provider) {
t.Fatal("expected short name match")
}
if entry.MatchesIngressRef("other/flux-txt2img", provider) {
t.Fatal("unexpected owner mismatch match")
}
}