@gonrocca/nodd 0.4.0 → 0.4.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.
|
@@ -279,3 +279,39 @@ test("nothing in this extension names forge's config file", () => {
|
|
|
279
279
|
assert.ok(!source.includes(`zero${"."}json`), "NODD never opens forge's config");
|
|
280
280
|
assert.ok(source.includes("nodd.json") || source.includes("noddConfigPath"), "it uses its own");
|
|
281
281
|
});
|
|
282
|
+
|
|
283
|
+
test("the picker offers the providers you can actually use, not the whole catalogue", () => {
|
|
284
|
+
// pi's registry carries every provider it knows how to speak to — bedrock,
|
|
285
|
+
// baseten, huggingface, cloudflare — whether or not you have credentials for
|
|
286
|
+
// any of them. Browsing all of it meant scrolling past dozens of providers
|
|
287
|
+
// that cannot answer. `getAvailable()` is pi's own answer to "which of these
|
|
288
|
+
// has configured auth", so that is what the picker lists.
|
|
289
|
+
const registry = {
|
|
290
|
+
getAll: () => [
|
|
291
|
+
{ provider: "cliproxy", id: "ds/deepseek-flash" },
|
|
292
|
+
{ provider: "amazon-bedrock", id: "claude-3" },
|
|
293
|
+
{ provider: "huggingface", id: "meta-llama/x" },
|
|
294
|
+
],
|
|
295
|
+
getAvailable: () => [{ provider: "cliproxy", id: "ds/deepseek-flash" }],
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
const groups = register.groupsFrom(registry);
|
|
299
|
+
assert.deepEqual([...groups.keys()], ["cliproxy/ds"], "only the configured provider is browsable");
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
test("with no availability information the full registry is still offered", () => {
|
|
303
|
+
// An older host, or one that cannot answer, must not leave the picker empty:
|
|
304
|
+
// showing everything beats showing nothing.
|
|
305
|
+
const groups = register.groupsFrom({ getAll: () => [{ provider: "anthropic", id: "claude-opus-4-1" }] });
|
|
306
|
+
assert.deepEqual([...groups.keys()], ["anthropic"]);
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
test("an empty availability list falls back rather than offering nothing", () => {
|
|
310
|
+
// Availability is refreshed asynchronously, so early in a session it can be
|
|
311
|
+
// legitimately empty. Trusting it blindly would show an empty provider list.
|
|
312
|
+
const groups = register.groupsFrom({
|
|
313
|
+
getAll: () => [{ provider: "anthropic", id: "claude-opus-4-1" }],
|
|
314
|
+
getAvailable: () => [],
|
|
315
|
+
});
|
|
316
|
+
assert.deepEqual([...groups.keys()], ["anthropic"], "an empty answer is not an answer");
|
|
317
|
+
});
|
|
@@ -48,13 +48,31 @@ export function fileConfigIo(path: string = noddConfigPath()): ConfigIo {
|
|
|
48
48
|
};
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
type ModelRegistry = {
|
|
51
|
+
type ModelRegistry = {
|
|
52
|
+
getAll(): RegistryModel[];
|
|
53
|
+
/** pi's own answer to "which providers have configured auth". */
|
|
54
|
+
getAvailable?(): RegistryModel[];
|
|
55
|
+
};
|
|
52
56
|
|
|
53
57
|
const USAGE =
|
|
54
58
|
"uso: /nodd-models · /nodd-models <slot>=<provider>/<modelo> · /nodd-models profile [list|new <n>|save [n]|use <n>|delete <n>]";
|
|
55
59
|
|
|
60
|
+
/**
|
|
61
|
+
* The models worth browsing.
|
|
62
|
+
*
|
|
63
|
+
* pi's registry lists every provider it can speak to — bedrock, baseten,
|
|
64
|
+
* huggingface — whether or not credentials exist for any of them, which buried
|
|
65
|
+
* the few usable ones under dozens that cannot answer. `getAvailable()` narrows
|
|
66
|
+
* that to the configured providers.
|
|
67
|
+
*
|
|
68
|
+
* An empty or missing answer falls back to the full registry: availability is
|
|
69
|
+
* refreshed asynchronously, so early in a session it can be legitimately empty,
|
|
70
|
+
* and an empty picker is worse than a long one.
|
|
71
|
+
*/
|
|
56
72
|
function groupsFrom(registry: ModelRegistry | undefined): Map<string, string[]> {
|
|
57
73
|
try {
|
|
74
|
+
const available = registry?.getAvailable?.();
|
|
75
|
+
if (available && available.length > 0) return groupByProvider(available);
|
|
58
76
|
const all = registry?.getAll?.();
|
|
59
77
|
return all && all.length > 0 ? groupByProvider(all) : new Map();
|
|
60
78
|
} catch {
|
|
@@ -362,5 +380,6 @@ function register(pi?: PiApi): void {
|
|
|
362
380
|
register.runPicker = runPicker;
|
|
363
381
|
register.createComponent = createComponent;
|
|
364
382
|
register.pickerInput = pickerInput;
|
|
383
|
+
register.groupsFrom = groupsFrom;
|
|
365
384
|
|
|
366
385
|
export default register;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gonrocca/nodd",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "Non-negotiable Organic Driven Development — the ODD protocol as runtime mechanism for pi: blocking gates, observed evidence, and promotion to /forge.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
|
@@ -572,3 +572,16 @@ test("with no profile at all there is nothing to preview", () => {
|
|
|
572
572
|
const state = createPickerState({ models: {}, thinking: {}, groups: new Map(), profiles: {}, activeProfile: null });
|
|
573
573
|
assert.deepEqual(previewRows(state), [], "an empty preview is what suppresses the second panel");
|
|
574
574
|
});
|
|
575
|
+
|
|
576
|
+
test("choosing through a prefixed group does not double the prefix", () => {
|
|
577
|
+
// The group key is `cliproxy/ds` and the id inside it is `ds/deepseek-v4-pro`:
|
|
578
|
+
// joining them naively produced `cliproxy/ds/ds/deepseek-v4-pro`, a model id
|
|
579
|
+
// that resolves to nothing. The key's prefix belongs to the id, not to the
|
|
580
|
+
// provider, so only the provider part may be prepended.
|
|
581
|
+
let state = open({ groups: new Map([["cliproxy/ds", ["ds/deepseek-v4-pro"]]]) });
|
|
582
|
+
state = press(state, "edit-loose");
|
|
583
|
+
state = assign(state, "implement", "cliproxy/ds", "ds/deepseek-v4-pro", "high");
|
|
584
|
+
|
|
585
|
+
assert.equal(state.edits.models.implement, "cliproxy/ds/deepseek-v4-pro");
|
|
586
|
+
assert.equal(state.edits.thinking.implement, "high");
|
|
587
|
+
});
|
package/src/models/picker.ts
CHANGED
|
@@ -528,9 +528,15 @@ function commitDrill(state: PickerState, level: ThinkingLevel): PickerState {
|
|
|
528
528
|
const { drillSlot, drillProvider, drillModel } = state;
|
|
529
529
|
if (drillSlot === null || drillModel === null) return state;
|
|
530
530
|
|
|
531
|
-
//
|
|
531
|
+
// `drillProvider` is a group key, and a pooled provider's key carries the
|
|
532
|
+
// pool prefix (`cliproxy/ds`) which the id already repeats
|
|
533
|
+
// (`ds/deepseek-v4-pro`). Only the provider part is prepended, or the commit
|
|
534
|
+
// doubles the prefix into an id that resolves to nothing.
|
|
535
|
+
//
|
|
536
|
+
// No provider at all is a bare model id — what the empty-registry path
|
|
532
537
|
// produces, and what the command's validation accepts.
|
|
533
|
-
const
|
|
538
|
+
const provider = drillProvider?.split("/")[0] ?? null;
|
|
539
|
+
const qualified = provider ? `${provider}/${drillModel}` : drillModel;
|
|
534
540
|
return {
|
|
535
541
|
...state,
|
|
536
542
|
edits: {
|