Split ingress keys into text/image protocols with Replicate model listing.
CI / docker (push) Successful in 3m38s
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:
@@ -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"`
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user