mirror of
https://github.com/Twigpine/zero.git
synced 2026-11-11 05:01:36 -10:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1d03609eaa |
No files matched your search
@@ -1378,14 +1378,43 @@ func TestResolveAppliesOpenGatewayCatalogDefaults(t *testing.T) {
|
||||
if resolved.Provider.BaseURL != "https://api.twigpine.com/v1" {
|
||||
t.Fatalf("BaseURL = %q, want https://api.twigpine.com/v1", resolved.Provider.BaseURL)
|
||||
}
|
||||
if resolved.Provider.Model != "xiaomi/mimo-v2.6-pro" {
|
||||
t.Fatalf("Model = %q, want xiaomi/mimo-v2.6-pro", resolved.Provider.Model)
|
||||
if resolved.Provider.Model != "auto" {
|
||||
t.Fatalf("Model = %q, want auto", resolved.Provider.Model)
|
||||
}
|
||||
if resolved.Provider.APIKeyEnv != "GITLAWB_OPENGATEWAY_API_KEY" || resolved.Provider.APIKey != "ogw_live_test" {
|
||||
t.Fatalf("credential = (%q, %q), want GITLAWB_OPENGATEWAY_API_KEY env-resolved key", resolved.Provider.APIKeyEnv, resolved.Provider.APIKey)
|
||||
}
|
||||
}
|
||||
|
||||
// The catalog default only fills a profile that names no model. A profile saved
|
||||
// while MiMo V2.6 Pro was the default persisted that id, and must keep it now
|
||||
// that the default is the smart router.
|
||||
func TestResolveKeepsSavedOpenGatewayModel(t *testing.T) {
|
||||
path := writeConfig(t, `{
|
||||
"activeProvider": "gitlawb-opengateway",
|
||||
"providers": [{
|
||||
"name": "gitlawb-opengateway",
|
||||
"catalogID": "gitlawb-opengateway",
|
||||
"providerKind": "openai-compatible",
|
||||
"baseURL": "https://api.twigpine.com/v1",
|
||||
"model": "xiaomi/mimo-v2.6-pro"
|
||||
}]
|
||||
}`)
|
||||
|
||||
resolved, err := Resolve(ResolveOptions{
|
||||
UserConfigPath: path,
|
||||
Env: map[string]string{
|
||||
"GITLAWB_OPENGATEWAY_API_KEY": "ogw_live_test",
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Resolve() error = %v", err)
|
||||
}
|
||||
if resolved.Provider.Model != "xiaomi/mimo-v2.6-pro" {
|
||||
t.Fatalf("Model = %q, want the saved model kept", resolved.Provider.Model)
|
||||
}
|
||||
}
|
||||
|
||||
// Profiles saved before the Twigpine rename persist the catalog's old base URL
|
||||
// (https://opengateway.gitlawb.com/v1) explicitly. That host still serves the
|
||||
// gateway, so those configs must keep resolving exactly as before: same host,
|
||||
|
||||
@@ -111,11 +111,14 @@ var descriptors = []Descriptor{
|
||||
// Bearer ogw_live_… key; listed first and badged in every picker. The public
|
||||
// GET /v1/models lists only active models and is what pickers show when
|
||||
// reachable. DefaultModel must be an exact id from that list (not an alias)
|
||||
// so onboarding keeps it selected after live discovery. The id and env var
|
||||
// so onboarding keeps it selected after live discovery. It is the gateway's
|
||||
// smart router, "auto": an account with no credits is routed to a free
|
||||
// model instead of failing with 402 on a paid one. Only new setups and
|
||||
// profiles that name no model get it; a saved model is kept. The id and env var
|
||||
// keep their gitlawb names so saved configs keep resolving; the old host
|
||||
// (opengateway.gitlawb.com) still serves clients and stays a legacy catalog
|
||||
// endpoint so profiles saved against it resolve unchanged.
|
||||
recommended(withLegacyBaseURLs(openAICompat("gitlawb-opengateway", "Twigpine OpenGateway", "https://api.twigpine.com/v1", "xiaomi/mimo-v2.6-pro", []string{"GITLAWB_OPENGATEWAY_API_KEY"}, "gitlawb opengateway", "opengateway", "twigpine opengateway", "twigpine"), "https://opengateway.gitlawb.com/v1")),
|
||||
recommended(withLegacyBaseURLs(openAICompat("gitlawb-opengateway", "Twigpine OpenGateway", "https://api.twigpine.com/v1", "auto", []string{"GITLAWB_OPENGATEWAY_API_KEY"}, "gitlawb opengateway", "opengateway", "twigpine opengateway", "twigpine"), "https://opengateway.gitlawb.com/v1")),
|
||||
// aimlapi.com — OpenAI-compatible aggregating gateway, also badged as
|
||||
// recommended (second, right after the OpenGateway default). The built-in TUI
|
||||
// onboarding can register/top up an account and save the issued key; the partner
|
||||
|
||||
@@ -115,10 +115,11 @@ func TestRecommendedProviderEndpoint(t *testing.T) {
|
||||
t.Fatalf("OpenGateway base URL = %q, want %q", descriptor.DefaultBaseURL, "https://api.twigpine.com/v1")
|
||||
}
|
||||
// The default must be an exact id from the live /v1/models list (not a
|
||||
// gateway alias such as the bare "mimo-v2.6-pro"): onboarding keeps the
|
||||
// default selected after live discovery only on an exact id match.
|
||||
if descriptor.DefaultModel != "xiaomi/mimo-v2.6-pro" {
|
||||
t.Fatalf("OpenGateway default model = %q, want %q", descriptor.DefaultModel, "xiaomi/mimo-v2.6-pro")
|
||||
// gateway alias such as "twigpine/auto"): onboarding keeps the default
|
||||
// selected after live discovery only on an exact id match. It is the smart
|
||||
// router so a new account with no credits is routed to a free model.
|
||||
if descriptor.DefaultModel != "auto" {
|
||||
t.Fatalf("OpenGateway default model = %q, want %q", descriptor.DefaultModel, "auto")
|
||||
}
|
||||
if descriptor.Transport != TransportOpenAICompatible {
|
||||
t.Fatalf("OpenGateway transport = %q, want %q", descriptor.Transport, TransportOpenAICompatible)
|
||||
|
||||
@@ -229,12 +229,15 @@ var curatedModels = map[string][]Model{
|
||||
// GET /v1/models; pickers and onboarding show that list whenever it is
|
||||
// reachable (see providermodeldiscovery.DiscoverCatalog). This is only the
|
||||
// offline fallback, so keep it to ids the gateway is expected to keep
|
||||
// serving: the smart router, the catalog default, and its fast sibling. Do
|
||||
// not add promo or time-limited models here. Users can still type any id
|
||||
// the gateway accepts.
|
||||
// serving: the smart router (the catalog default), MiMo V2.6 Pro, and its
|
||||
// fast sibling. Do not add promo or time-limited models here. Users can
|
||||
// still type any id the gateway accepts. "auto" carries no context window on
|
||||
// purpose: the gateway reports none (the serving model varies per request),
|
||||
// so compaction sizes itself from modelregistry.FallbackContextWindow and
|
||||
// the context gauges show no guessed denominator.
|
||||
"gitlawb-opengateway": {
|
||||
{ID: "xiaomi/mimo-v2.6-pro", Description: "catalog default", ContextWindow: 262_144},
|
||||
{ID: "auto", Description: "smart routing (picks a capable model per request)"},
|
||||
{ID: "auto", Description: "catalog default"},
|
||||
{ID: "xiaomi/mimo-v2.6-pro", Description: "general model (Xiaomi MiMo V2.6 Pro)", ContextWindow: 262_144},
|
||||
{ID: "xiaomi/mimo-v2.6-flash", Description: "fast model (Xiaomi MiMo V2.6 Flash)", ContextWindow: 262_144},
|
||||
},
|
||||
"atomic-chat": {
|
||||
|
||||
@@ -76,7 +76,7 @@ func TestModelsAreProviderScoped(t *testing.T) {
|
||||
{
|
||||
// Offline fallback only; the live /v1/models list is shown when reachable.
|
||||
provider: "gitlawb-opengateway",
|
||||
want: []string{"xiaomi/mimo-v2.6-pro", "auto", "xiaomi/mimo-v2.6-flash"},
|
||||
want: []string{"auto", "xiaomi/mimo-v2.6-pro", "xiaomi/mimo-v2.6-flash"},
|
||||
notWant: []string{
|
||||
"openai/gpt-4.1", "claude-sonnet-4.5",
|
||||
"mimo-v2.5-pro", "xiaomi/mimo-v2.5-pro", "qwen-plus", "glm-4.6", "gemini-2.5-pro",
|
||||
@@ -116,6 +116,24 @@ func TestModelsAreProviderScoped(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// The offline OpenGateway list leads with the smart router, the catalog
|
||||
// default. The gateway reports no context window for it (the serving model
|
||||
// varies per request), so the curated entry must not invent one: an unknown
|
||||
// window sizes compaction from modelregistry.FallbackContextWindow.
|
||||
func TestOpenGatewayModelsLeadWithAutoDefault(t *testing.T) {
|
||||
descriptor, ok := providercatalog.Get("gitlawb-opengateway")
|
||||
if !ok {
|
||||
t.Fatal("provider gitlawb-opengateway missing from catalog")
|
||||
}
|
||||
models := Models(descriptor)
|
||||
if len(models) == 0 || models[0].ID != "auto" || models[0].ID != descriptor.DefaultModel {
|
||||
t.Fatalf("OpenGateway models = %#v, want the auto default first", modelIDs(models))
|
||||
}
|
||||
if models[0].ContextWindow != 0 {
|
||||
t.Fatalf("auto context window = %d, want unknown (0)", models[0].ContextWindow)
|
||||
}
|
||||
}
|
||||
|
||||
func TestModelsDoNotAliasMutableCatalogState(t *testing.T) {
|
||||
descriptor, ok := providercatalog.Get("minimax")
|
||||
if !ok {
|
||||
|
||||
@@ -69,10 +69,23 @@ func TestParseModelsDevProviderScopesAndMapsMetadata(t *testing.T) {
|
||||
|
||||
func TestParseOpenGatewayCatalogSkipsRetiredModels(t *testing.T) {
|
||||
// Mirrors GET https://api.twigpine.com/v1/models?status=all once the gateway
|
||||
// retires models: an active entry plus a retired one carrying a successor.
|
||||
// retires models: the smart router (listed with null metadata, since the
|
||||
// serving model varies per request), an active entry, plus a retired one
|
||||
// carrying a successor.
|
||||
body := []byte(`{
|
||||
"object": "list",
|
||||
"data": [
|
||||
{
|
||||
"id": "auto",
|
||||
"object": "model",
|
||||
"name": "Auto (smart routing)",
|
||||
"description": "picks the cheapest capable model",
|
||||
"aliases": ["twigpine/auto", "gitlawb/auto"],
|
||||
"context_window": null,
|
||||
"pricing": null,
|
||||
"supports_tools": null,
|
||||
"status": "active"
|
||||
},
|
||||
{
|
||||
"id": "xiaomi/mimo-v2.6-pro",
|
||||
"object": "model",
|
||||
@@ -100,8 +113,14 @@ func TestParseOpenGatewayCatalogSkipsRetiredModels(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("ParseOpenGatewayCatalog returned error: %v", err)
|
||||
}
|
||||
if got := strings.Join(modelIDs(models), ","); got != "xiaomi/mimo-v2.6-pro" {
|
||||
t.Fatalf("models = %q, want only the active entry", got)
|
||||
// Sorted by description label (flagship…, picks…).
|
||||
if got := strings.Join(modelIDs(models), ","); got != "xiaomi/mimo-v2.6-pro,auto" {
|
||||
t.Fatalf("models = %q, want only the active entries", got)
|
||||
}
|
||||
// The router's null context window and pricing parse as unknown (0), not
|
||||
// as an error or a guessed value.
|
||||
if auto := models[1]; auto.ContextWindow != 0 || auto.InputCost != 0 || auto.OutputCost != 0 {
|
||||
t.Fatalf("auto metadata = %#v, want unknown context window and pricing", auto)
|
||||
}
|
||||
descriptor, ok := providercatalog.Get("gitlawb-opengateway")
|
||||
if !ok {
|
||||
|
||||
@@ -347,11 +347,13 @@ func TestDiscoverCatalogOpenGatewayUsesLiveListWithoutKey(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDiscoverCatalogOpenGatewayDropsRetiredAndKeepsDefault(t *testing.T) {
|
||||
// Both the catalog fetch and the live /v1/models probe return one active and
|
||||
// one retired entry. Users must only ever see the active one, and the
|
||||
// catalog's default model must be among the ids discovery returns (onboarding
|
||||
// keeps the default selected only on an exact id match).
|
||||
// Both the catalog fetch and the live /v1/models probe return the smart
|
||||
// router, one active and one retired entry. Users must only ever see the
|
||||
// active ones, and the catalog's default model must be among the ids
|
||||
// discovery returns (onboarding keeps the default selected only on an exact
|
||||
// id match).
|
||||
const body = `{"object":"list","data":[
|
||||
{"id":"auto","name":"Auto (smart routing)","context_window":null,"pricing":null,"supports_tools":null,"status":"active"},
|
||||
{"id":"xiaomi/mimo-v2.6-pro","name":"MiMo V2.6-Pro","context_window":262144,"supports_tools":true,"status":"active"},
|
||||
{"id":"qwen/qwen-plus","name":"Qwen Plus","context_window":131072,"status":"retired","successor":"qwen/qwen3.7-max"}
|
||||
]}`
|
||||
@@ -385,8 +387,8 @@ func TestDiscoverCatalogOpenGatewayDropsRetiredAndKeepsDefault(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("DiscoverCatalog: %v", err)
|
||||
}
|
||||
if got := strings.Join(modelIDs(models), ","); got != "xiaomi/mimo-v2.6-pro" {
|
||||
t.Fatalf("models = %q, want only the active entry", got)
|
||||
if got := strings.Join(modelIDs(models), ","); got != "auto,xiaomi/mimo-v2.6-pro" {
|
||||
t.Fatalf("models = %q, want only the active entries", got)
|
||||
}
|
||||
if models[0].ID != descriptor.DefaultModel {
|
||||
t.Fatalf("default model %q missing from discovered %v", descriptor.DefaultModel, modelIDs(models))
|
||||
|
||||
Reference in new issue
Block a user