openzoo 0.22.2 → 0.23.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/cursorcfg.js CHANGED
@@ -87,6 +87,46 @@ export async function quitEditor(which) {
87
87
  * should offer. Returns what changed, or null when the store is unavailable
88
88
  * (a fresh install with no globalStorage yet).
89
89
  */
90
+ /**
91
+ * PRETTY NAMES IN THE PICKER, WITHOUT LOSING VALIDATION.
92
+ *
93
+ * The editor validates `name` against its OWN catalog and refuses anything else
94
+ * ("Model name is not valid" / "Max Mode Required"), so `name` MUST stay a bland
95
+ * id it already knows. But the picker renders `inputboxShortModelName`, which it
96
+ * does not validate — so the row can read "Opus 5" while the id stays gpt-4o.
97
+ *
98
+ * The zoo model that actually answers is chosen by the proxy, so a slot is a
99
+ * (bland id -> real model -> label) triple. Override with OPENZOO_EDITOR_MAP:
100
+ * OPENZOO_EDITOR_MAP="gpt-4o=x-ai/grok-4.6:Grok 4.6,gpt-4.1=anthropic/claude-sonnet-5:Sonnet 5"
101
+ */
102
+ const DEFAULT_SLOTS = {
103
+ 'gpt-4o': { served: 'anthropic/claude-opus-5', label: 'Opus 5' },
104
+ 'gpt-4.1': { served: 'anthropic/claude-sonnet-5', label: 'Sonnet 5' },
105
+ 'gpt-4-turbo': { served: 'x-ai/grok-4.6', label: 'Grok 4.6' },
106
+ 'gpt-4o-mini': { served: 'google/gemini-3.7-flash', label: 'Gemini 3.7 Flash' },
107
+ 'gpt-4.1-mini': { served: 'deepseek/deepseek-v4-pro-0813', label: 'DeepSeek V4 Pro' },
108
+ 'gpt-3.5-turbo': { served: 'qwen/qwen3.8-2.4t-a95b', label: 'Qwen3.8 2.4T' },
109
+ };
110
+
111
+ export function slotFor(id) {
112
+ // KILL SWITCH: if a custom label ever makes the editor treat a row as
113
+ // unavailable, OPENZOO_NO_LABELS=1 puts the bland id back as the label. The
114
+ // mapping (which zoo model answers) is unaffected — only the text changes.
115
+ if (process.env.OPENZOO_NO_LABELS === '1') {
116
+ return { served: (DEFAULT_SLOTS[id] || {}).served || id, label: id };
117
+ }
118
+ const env = process.env.OPENZOO_EDITOR_MAP;
119
+ if (env) {
120
+ for (const part of env.split(',')) {
121
+ const [lhs, rhs] = part.split('=');
122
+ if ((lhs || '').trim() !== id || !rhs) continue;
123
+ const [served, ...lbl] = rhs.split(':');
124
+ return { served: served.trim(), label: (lbl.join(':') || served).trim() };
125
+ }
126
+ }
127
+ return DEFAULT_SLOTS[id] || { served: id, label: id };
128
+ }
129
+
90
130
  export function writeEditorProviderConfig(which, { baseUrl, models }) {
91
131
  const db = storagePath(which);
92
132
  if (!fs.existsSync(db)) return null;
@@ -132,11 +172,11 @@ export function writeEditorProviderConfig(which, { baseUrl, models }) {
132
172
  supportsMaxMode: true,
133
173
  supportsNonMaxMode: true,
134
174
  serverModelName: m,
175
+ inputboxShortModelName: slotFor(m).label,
135
176
  isRecommendedForBackgroundComposer: false,
136
177
  supportsPlanMode: true,
137
178
  supportsSandboxing: true,
138
179
  isUserAdded: true,
139
- inputboxShortModelName: m,
140
180
  parameterDefinitions: [],
141
181
  variants: [],
142
182
  legacySlugs: [],
@@ -214,7 +254,7 @@ export function pinEditorProviderConfig(which, { baseUrl, models }) {
214
254
  supportsThinking: true, supportsImages: true, supportsMaxMode: true,
215
255
  supportsNonMaxMode: true, serverModelName: m,
216
256
  isRecommendedForBackgroundComposer: false, supportsPlanMode: true,
217
- supportsSandboxing: true, isUserAdded: true, inputboxShortModelName: m,
257
+ supportsSandboxing: true, isUserAdded: true, inputboxShortModelName: slotFor(m).label,
218
258
  parameterDefinitions: [], variants: [], legacySlugs: [], idAliases: [],
219
259
  namedModelSectionIndex: 1, cloudAgentEffortModes: [], modelPickerBadges: [],
220
260
  }))));
package/lib/models.js CHANGED
@@ -19,6 +19,31 @@ import { config } from './config.js';
19
19
  * cached briefly, so new zoo models resolve without shipping this package.
20
20
  */
21
21
 
22
+ /**
23
+ * The editor-slot map: bland id the editor accepts -> the zoo model that should
24
+ * actually answer it. Mirrors DEFAULT_SLOTS in cursorcfg.js; OPENZOO_EDITOR_MAP
25
+ * overrides both ("gpt-4o=x-ai/grok-4.6:Grok 4.6,...").
26
+ */
27
+ const DEFAULT_SLOT_MODELS = {
28
+ 'gpt-4o': 'anthropic/claude-opus-5',
29
+ 'gpt-4.1': 'anthropic/claude-sonnet-5',
30
+ 'gpt-4-turbo': 'x-ai/grok-4.6',
31
+ 'gpt-4o-mini': 'google/gemini-3.7-flash',
32
+ 'gpt-4.1-mini': 'deepseek/deepseek-v4-pro-0813',
33
+ 'gpt-3.5-turbo': 'qwen/qwen3.8-2.4t-a95b',
34
+ };
35
+
36
+ export function editorSlot(id) {
37
+ const env = process.env.OPENZOO_EDITOR_MAP;
38
+ if (env) {
39
+ for (const part of env.split(',')) {
40
+ const [lhs, rhs] = part.split('=');
41
+ if ((lhs || '').trim() === id && rhs) return rhs.split(':')[0].trim();
42
+ }
43
+ }
44
+ return DEFAULT_SLOT_MODELS[id] || null;
45
+ }
46
+
22
47
  const CATALOG_TTL_MS = 5 * 60 * 1000;
23
48
  let cache = { at: 0, ids: null };
24
49
 
@@ -114,9 +139,19 @@ export function resolveModel(requested, ids) {
114
139
  if (exact) return exact;
115
140
  requested = bare;
116
141
  }
142
+ // An explicit OPENZOO_DEFAULT_MODEL is a deliberate act and outranks
143
+ // everything below it, including the editor slots.
117
144
  const env = process.env.OPENZOO_DEFAULT_MODEL;
118
145
  if (env && ids.includes(env)) return env;
119
146
 
147
+ // SLOT MAP. The editor only accepts a handful of bland ids it already knows,
148
+ // so the shim writes those as slots and shows a real label for each (slotFor
149
+ // in cursorcfg.js). That label would be a LIE if the id then fell through to
150
+ // the similarity scorer, so the same mapping is honoured here: gpt-4o really
151
+ // is served by claude-opus-5. One source of truth, OPENZOO_EDITOR_MAP.
152
+ const slot = editorSlot(requested);
153
+ if (slot && ids.includes(slot)) return slot;
154
+
120
155
  const reqTier = tierOf(requested);
121
156
  const reqCode = CODE_RE.test(requested);
122
157
  const reqToks = new Set(tokensOf(requested));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openzoo",
3
- "version": "0.22.2",
3
+ "version": "0.23.1",
4
4
  "description": "Local x402-paying proxy + MCP server for openzoo.fun — point any OpenAI-compatible harness (Cursor, Claude Code, aider, SDKs) at localhost and it pays per call from a local burner wallet. Solana and Base rails live; Robinhood experimental.",
5
5
  "license": "MIT",
6
6
  "type": "module",