openzoo 0.22.2 → 0.23.0

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,40 @@ 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
+ const env = process.env.OPENZOO_EDITOR_MAP;
113
+ if (env) {
114
+ for (const part of env.split(',')) {
115
+ const [lhs, rhs] = part.split('=');
116
+ if ((lhs || '').trim() !== id || !rhs) continue;
117
+ const [served, ...lbl] = rhs.split(':');
118
+ return { served: served.trim(), label: (lbl.join(':') || served).trim() };
119
+ }
120
+ }
121
+ return DEFAULT_SLOTS[id] || { served: id, label: id };
122
+ }
123
+
90
124
  export function writeEditorProviderConfig(which, { baseUrl, models }) {
91
125
  const db = storagePath(which);
92
126
  if (!fs.existsSync(db)) return null;
@@ -132,11 +166,11 @@ export function writeEditorProviderConfig(which, { baseUrl, models }) {
132
166
  supportsMaxMode: true,
133
167
  supportsNonMaxMode: true,
134
168
  serverModelName: m,
169
+ inputboxShortModelName: slotFor(m).label,
135
170
  isRecommendedForBackgroundComposer: false,
136
171
  supportsPlanMode: true,
137
172
  supportsSandboxing: true,
138
173
  isUserAdded: true,
139
- inputboxShortModelName: m,
140
174
  parameterDefinitions: [],
141
175
  variants: [],
142
176
  legacySlugs: [],
@@ -214,7 +248,7 @@ export function pinEditorProviderConfig(which, { baseUrl, models }) {
214
248
  supportsThinking: true, supportsImages: true, supportsMaxMode: true,
215
249
  supportsNonMaxMode: true, serverModelName: m,
216
250
  isRecommendedForBackgroundComposer: false, supportsPlanMode: true,
217
- supportsSandboxing: true, isUserAdded: true, inputboxShortModelName: m,
251
+ supportsSandboxing: true, isUserAdded: true, inputboxShortModelName: slotFor(m).label,
218
252
  parameterDefinitions: [], variants: [], legacySlugs: [], idAliases: [],
219
253
  namedModelSectionIndex: 1, cloudAgentEffortModes: [], modelPickerBadges: [],
220
254
  }))));
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.0",
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",