smallm 0.5.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.
Files changed (64) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +244 -0
  3. package/dist/benchmark.d.ts +10 -0
  4. package/dist/benchmark.d.ts.map +1 -0
  5. package/dist/benchmark.js +35 -0
  6. package/dist/benchmark.js.map +1 -0
  7. package/dist/cache-file.d.ts +40 -0
  8. package/dist/cache-file.d.ts.map +1 -0
  9. package/dist/cache-file.js +163 -0
  10. package/dist/cache-file.js.map +1 -0
  11. package/dist/cache.d.ts +23 -0
  12. package/dist/cache.d.ts.map +1 -0
  13. package/dist/cache.js +58 -0
  14. package/dist/cache.js.map +1 -0
  15. package/dist/cli.d.ts +32 -0
  16. package/dist/cli.d.ts.map +1 -0
  17. package/dist/cli.js +256 -0
  18. package/dist/cli.js.map +1 -0
  19. package/dist/data/benchmarks.json +61 -0
  20. package/dist/embeddings.d.ts +50 -0
  21. package/dist/embeddings.d.ts.map +1 -0
  22. package/dist/embeddings.js +118 -0
  23. package/dist/embeddings.js.map +1 -0
  24. package/dist/errors.d.ts +18 -0
  25. package/dist/errors.d.ts.map +1 -0
  26. package/dist/errors.js +42 -0
  27. package/dist/errors.js.map +1 -0
  28. package/dist/filters.d.ts +11 -0
  29. package/dist/filters.d.ts.map +1 -0
  30. package/dist/filters.js +66 -0
  31. package/dist/filters.js.map +1 -0
  32. package/dist/index.d.ts +52 -0
  33. package/dist/index.d.ts.map +1 -0
  34. package/dist/index.js +110 -0
  35. package/dist/index.js.map +1 -0
  36. package/dist/params.d.ts +21 -0
  37. package/dist/params.d.ts.map +1 -0
  38. package/dist/params.js +50 -0
  39. package/dist/params.js.map +1 -0
  40. package/dist/query.d.ts +23 -0
  41. package/dist/query.d.ts.map +1 -0
  42. package/dist/query.js +81 -0
  43. package/dist/query.js.map +1 -0
  44. package/dist/registry/huggingface.d.ts +24 -0
  45. package/dist/registry/huggingface.d.ts.map +1 -0
  46. package/dist/registry/huggingface.js +97 -0
  47. package/dist/registry/huggingface.js.map +1 -0
  48. package/dist/registry/ollama.d.ts +3 -0
  49. package/dist/registry/ollama.d.ts.map +1 -0
  50. package/dist/registry/ollama.js +78 -0
  51. package/dist/registry/ollama.js.map +1 -0
  52. package/dist/registry/provider.d.ts +12 -0
  53. package/dist/registry/provider.d.ts.map +1 -0
  54. package/dist/registry/provider.js +3 -0
  55. package/dist/registry/provider.js.map +1 -0
  56. package/dist/scorer.d.ts +45 -0
  57. package/dist/scorer.d.ts.map +1 -0
  58. package/dist/scorer.js +161 -0
  59. package/dist/scorer.js.map +1 -0
  60. package/dist/types.d.ts +147 -0
  61. package/dist/types.d.ts.map +1 -0
  62. package/dist/types.js +8 -0
  63. package/dist/types.js.map +1 -0
  64. package/package.json +45 -0
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.applyHardFilters = applyHardFilters;
4
+ const huggingface_1 = require("./registry/huggingface");
5
+ const benchmark_1 = require("./benchmark");
6
+ const query_1 = require("./query");
7
+ /**
8
+ * (v0.4) Rough VRAM-per-billion-parameters estimate for fp16 inference,
9
+ * used only to translate paramsB into an estimated footprint for the
10
+ * HardwareSpec.vramGB filter below. The doc specifies "use vramGB when
11
+ * present" but no exact formula — this is a commonly-cited heuristic
12
+ * (~2GB VRAM per 1B params at fp16), not a measured or authoritative
13
+ * figure. Flagged here and in the README as an approximation.
14
+ */
15
+ const ESTIMATED_GB_PER_BILLION_PARAMS = 2;
16
+ /**
17
+ * Applies hard filters before any scoring happens. A model failing ANY
18
+ * filter is excluded outright — it never reaches scorer.ts. Unknown values
19
+ * never cause exclusion (MVP Section 4.4's rule, carried forward per the
20
+ * v0.2/v0.3/v0.4 guides' "unknown data is never punished" cross-phase
21
+ * principle). Provider-agnostic throughout — never branches on
22
+ * model.provider (v0.4 Do's/Don'ts).
23
+ */
24
+ function applyHardFilters(models, query) {
25
+ const expectedPipelineTag = (0, huggingface_1.mapTaskToPipelineTag)(query.task);
26
+ return models.filter((model) => {
27
+ // 1. maxParamsB — known + over budget => exclude. Unknown => let through.
28
+ if (query.maxParamsB !== undefined &&
29
+ model.paramsB != null &&
30
+ model.paramsB > query.maxParamsB) {
31
+ return false;
32
+ }
33
+ // 2. contextLength — known + too small => exclude. Unknown => let through.
34
+ if (model.contextWindow != null && model.contextWindow < query.contextLength) {
35
+ return false;
36
+ }
37
+ // 3. task compatibility — pipeline_tag present but doesn't map => exclude.
38
+ // Missing pipeline_tag entirely is treated as "can't determine", not excluded.
39
+ // (Ollama candidates never set pipeline_tag, so they're never excluded here —
40
+ // same "unknown, let through" rule, not a provider-specific carve-out.)
41
+ if (model.pipeline_tag && model.pipeline_tag !== expectedPipelineTag) {
42
+ return false;
43
+ }
44
+ // 4. (v0.2) maxLatencyMs — only enforced when benchmark data exists AND
45
+ // hardware is the original string-enum form (the benchmark dataset is
46
+ // keyed by that enum, not by arbitrary HardwareSpec objects). No
47
+ // benchmark entry => let through, same unknown-value rule.
48
+ if (query.maxLatencyMs !== undefined && (0, query_1.isHardwareEnumString)(query.hardware)) {
49
+ const measuredLatency = (0, benchmark_1.lookupLatencyMs)(model.id, query.hardware);
50
+ if (measuredLatency !== null && measuredLatency > query.maxLatencyMs) {
51
+ return false;
52
+ }
53
+ }
54
+ // 5. (v0.4) HardwareSpec.vramGB — known paramsB + known vramGB budget +
55
+ // estimated footprint exceeds it => exclude. Unknown paramsB, or no
56
+ // vramGB given, => let through (unknown-value rule again).
57
+ if ((0, query_1.isHardwareSpec)(query.hardware) && query.hardware.vramGB !== undefined && model.paramsB != null) {
58
+ const estimatedVramGB = model.paramsB * ESTIMATED_GB_PER_BILLION_PARAMS;
59
+ if (estimatedVramGB > query.hardware.vramGB) {
60
+ return false;
61
+ }
62
+ }
63
+ return true;
64
+ });
65
+ }
66
+ //# sourceMappingURL=filters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filters.js","sourceRoot":"","sources":["../src/filters.ts"],"names":[],"mappings":";;;AACA,wDAA8D;AAC9D,2CAA8C;AAC9C,mCAA+D;AAE/D;;;;;;;GAOG;AACH,MAAM,+BAA+B,GAAG,CAAC,CAAC;AAE1C;;;;;;;GAOG;AACH,0BAAiC,MAAiB,EAAE,KAAiB;IACnE,MAAM,mBAAmB,GAAG,IAAA,kCAAoB,EAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAE7D,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;QAC7B,0EAA0E;QAC1E,IACE,KAAK,CAAC,UAAU,KAAK,SAAS;YAC9B,KAAK,CAAC,OAAO,IAAI,IAAI;YACrB,KAAK,CAAC,OAAO,GAAG,KAAK,CAAC,UAAU,EAChC,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC;QAED,2EAA2E;QAC3E,IAAI,KAAK,CAAC,aAAa,IAAI,IAAI,IAAI,KAAK,CAAC,aAAa,GAAG,KAAK,CAAC,aAAa,EAAE,CAAC;YAC7E,OAAO,KAAK,CAAC;QACf,CAAC;QAED,2EAA2E;QAC3E,kFAAkF;QAClF,iFAAiF;QACjF,2EAA2E;QAC3E,IAAI,KAAK,CAAC,YAAY,IAAI,KAAK,CAAC,YAAY,KAAK,mBAAmB,EAAE,CAAC;YACrE,OAAO,KAAK,CAAC;QACf,CAAC;QAED,wEAAwE;QACxE,yEAAyE;QACzE,oEAAoE;QACpE,8DAA8D;QAC9D,IAAI,KAAK,CAAC,YAAY,KAAK,SAAS,IAAI,IAAA,4BAAoB,EAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC7E,MAAM,eAAe,GAAG,IAAA,2BAAe,EAAC,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;YAClE,IAAI,eAAe,KAAK,IAAI,IAAI,eAAe,GAAG,KAAK,CAAC,YAAY,EAAE,CAAC;gBACrE,OAAO,KAAK,CAAC;YACf,CAAC;QACH,CAAC;QAED,wEAAwE;QACxE,uEAAuE;QACvE,8DAA8D;QAC9D,IAAI,IAAA,sBAAc,EAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,KAAK,SAAS,IAAI,KAAK,CAAC,OAAO,IAAI,IAAI,EAAE,CAAC;YACnG,MAAM,eAAe,GAAG,KAAK,CAAC,OAAO,GAAG,+BAA+B,CAAC;YACxE,IAAI,eAAe,GAAG,KAAK,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;gBAC5C,OAAO,KAAK,CAAC;YACf,CAAC;QACH,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,52 @@
1
+ import { ModelQuery, ModelMatch } from "./types";
2
+ /**
3
+ * Finds and ranks models matching the given query, across one or more
4
+ * providers (HuggingFace by default; optionally Ollama as of v0.4).
5
+ *
6
+ * Pipeline (locked order, per MVP guide Section 5, Step 8 — unchanged since MVP):
7
+ * validate -> fetch -> hard-filter -> score -> tie-break -> sort -> limit -> return
8
+ *
9
+ * v0.4 changes (see post-MVP guide):
10
+ * - New optional `providers` field: which registries to query. Defaults
11
+ * to ["huggingface"] — identical behavior to v0.1-v0.3 when omitted.
12
+ * - The FETCH step now fans out to every requested provider in parallel
13
+ * and merges their RawModel[] results before hard-filtering — filters.ts
14
+ * and scorer.ts never see provider-specific shapes or branch on
15
+ * provider name (v0.4 Do's/Don'ts).
16
+ * - `hardware` is widened to optionally accept a structured HardwareSpec
17
+ * ({ type, vramGB? }) alongside the original string enum. When vramGB
18
+ * is given, filters.ts applies an estimated-footprint hard filter
19
+ * (see that file for the heuristic and its caveats).
20
+ * - Ollama being unreachable never fails the whole call — see
21
+ * registry/ollama.ts for the graceful-degradation behavior.
22
+ *
23
+ * v0.3 changes (see post-MVP guide):
24
+ * - New optional `scoringMode`: "rule" (default, unchanged v0.1/v0.2
25
+ * behavior) | "embedding" | "hybrid". Only the SCORE step changes
26
+ * between modes — hard filters (step 3) run identically regardless of
27
+ * scoringMode, per the "filters stay mode-agnostic" cross-phase
28
+ * principle. See scorer.ts's computeFinalScore for the per-mode logic.
29
+ * - "embedding" mode uses a local, dependency-free text-similarity
30
+ * scorer (embeddings.ts) instead of a downloaded neural model — see
31
+ * that file's docblock for why, and the trade-off involved.
32
+ *
33
+ * v0.2 changes (see post-MVP guide):
34
+ * - fetch is now backed by a file-based cache (cache-file.ts) instead of
35
+ * MVP's in-memory Map, so repeated queries survive process restarts.
36
+ * - maxLatencyMs is now a real hard filter when both it and the original
37
+ * string-form `hardware` are provided AND a benchmark entry exists for
38
+ * that (model, hardware) pair (see filters.ts, benchmark.ts). Still
39
+ * unenforced otherwise — unknown values are never punished.
40
+ * - HuggingFace fetch failures now retry transient errors (429/5xx) with
41
+ * backoff before throwing a typed SmallmError subclass (errors.ts).
42
+ *
43
+ * Known MVP-era limitation, still present through v0.4: candidate models
44
+ * from HuggingFace come only from its list endpoint, which does not return
45
+ * context-window data, so `contextWindow` stays null/unknown for those
46
+ * models and the context-fit score component stays neutral (50). Not part
47
+ * of any shipped phase's scope yet — tracked as a future addition.
48
+ */
49
+ export declare function findModels(query: ModelQuery): Promise<ModelMatch[]>;
50
+ export type { ModelQuery, ModelMatch } from "./types";
51
+ export { SmallmError, HFApiError, RateLimitError, ValidationError } from "./errors";
52
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,UAAU,EAA0B,MAAM,SAAS,CAAC;AAgBzE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,wBAAsB,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAmDzE;AAKD,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAKtD,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,110 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ValidationError = exports.RateLimitError = exports.HFApiError = exports.SmallmError = void 0;
4
+ exports.findModels = findModels;
5
+ const query_1 = require("./query");
6
+ const cache_file_1 = require("./cache-file");
7
+ const params_1 = require("./params");
8
+ const filters_1 = require("./filters");
9
+ const scorer_1 = require("./scorer");
10
+ const huggingface_1 = require("./registry/huggingface");
11
+ const ollama_1 = require("./registry/ollama");
12
+ /** (v0.4) Registry of known providers, keyed by name, for the fan-out step below. */
13
+ const PROVIDERS = {
14
+ huggingface: huggingface_1.huggingfaceProvider,
15
+ ollama: ollama_1.ollamaProvider,
16
+ };
17
+ /**
18
+ * Finds and ranks models matching the given query, across one or more
19
+ * providers (HuggingFace by default; optionally Ollama as of v0.4).
20
+ *
21
+ * Pipeline (locked order, per MVP guide Section 5, Step 8 — unchanged since MVP):
22
+ * validate -> fetch -> hard-filter -> score -> tie-break -> sort -> limit -> return
23
+ *
24
+ * v0.4 changes (see post-MVP guide):
25
+ * - New optional `providers` field: which registries to query. Defaults
26
+ * to ["huggingface"] — identical behavior to v0.1-v0.3 when omitted.
27
+ * - The FETCH step now fans out to every requested provider in parallel
28
+ * and merges their RawModel[] results before hard-filtering — filters.ts
29
+ * and scorer.ts never see provider-specific shapes or branch on
30
+ * provider name (v0.4 Do's/Don'ts).
31
+ * - `hardware` is widened to optionally accept a structured HardwareSpec
32
+ * ({ type, vramGB? }) alongside the original string enum. When vramGB
33
+ * is given, filters.ts applies an estimated-footprint hard filter
34
+ * (see that file for the heuristic and its caveats).
35
+ * - Ollama being unreachable never fails the whole call — see
36
+ * registry/ollama.ts for the graceful-degradation behavior.
37
+ *
38
+ * v0.3 changes (see post-MVP guide):
39
+ * - New optional `scoringMode`: "rule" (default, unchanged v0.1/v0.2
40
+ * behavior) | "embedding" | "hybrid". Only the SCORE step changes
41
+ * between modes — hard filters (step 3) run identically regardless of
42
+ * scoringMode, per the "filters stay mode-agnostic" cross-phase
43
+ * principle. See scorer.ts's computeFinalScore for the per-mode logic.
44
+ * - "embedding" mode uses a local, dependency-free text-similarity
45
+ * scorer (embeddings.ts) instead of a downloaded neural model — see
46
+ * that file's docblock for why, and the trade-off involved.
47
+ *
48
+ * v0.2 changes (see post-MVP guide):
49
+ * - fetch is now backed by a file-based cache (cache-file.ts) instead of
50
+ * MVP's in-memory Map, so repeated queries survive process restarts.
51
+ * - maxLatencyMs is now a real hard filter when both it and the original
52
+ * string-form `hardware` are provided AND a benchmark entry exists for
53
+ * that (model, hardware) pair (see filters.ts, benchmark.ts). Still
54
+ * unenforced otherwise — unknown values are never punished.
55
+ * - HuggingFace fetch failures now retry transient errors (429/5xx) with
56
+ * backoff before throwing a typed SmallmError subclass (errors.ts).
57
+ *
58
+ * Known MVP-era limitation, still present through v0.4: candidate models
59
+ * from HuggingFace come only from its list endpoint, which does not return
60
+ * context-window data, so `contextWindow` stays null/unknown for those
61
+ * models and the context-fit score component stays neutral (50). Not part
62
+ * of any shipped phase's scope yet — tracked as a future addition.
63
+ */
64
+ async function findModels(query) {
65
+ // 1. validate
66
+ const validated = (0, query_1.validateQuery)(query);
67
+ // (v0.2) apply per-call cache options, if provided, before the fetch step.
68
+ if (validated.cacheOptions) {
69
+ (0, cache_file_1.configureCandidateModelsCache)(validated.cacheOptions);
70
+ }
71
+ // 2. fetch — (v0.4) fan out to every requested provider, cached per
72
+ // (provider, task) pair, then merge before anything downstream sees them.
73
+ const providerLists = await Promise.all(validated.providers.map((providerName) => {
74
+ const provider = PROVIDERS[providerName];
75
+ return (0, cache_file_1.getOrFetchCached)(`${provider.name}:${validated.task}`, () => provider.listCandidates(validated));
76
+ }));
77
+ const rawCandidates = providerLists.flat();
78
+ const enriched = (0, params_1.enrichWithParams)(rawCandidates);
79
+ // 3. hard-filter — mode-agnostic and provider-agnostic, runs identically regardless of scoringMode/providers
80
+ const filtered = (0, filters_1.applyHardFilters)(enriched, validated);
81
+ // 4. score — mode-aware as of v0.3 (rule / embedding / hybrid)
82
+ const expectedPipelineTag = (0, huggingface_1.mapTaskToPipelineTag)(validated.task);
83
+ const scored = filtered.map((model) => ({
84
+ model,
85
+ result: (0, scorer_1.computeFinalScore)(model, validated, expectedPipelineTag),
86
+ }));
87
+ // 5. tie-break + 6. sort (single stable sort using the tie-break-aware comparator)
88
+ scored.sort((a, b) => (0, scorer_1.compareModels)({ score: a.result.finalScore, downloads: a.model.downloads ?? 0 }, { score: b.result.finalScore, downloads: b.model.downloads ?? 0 }));
89
+ // 7. limit
90
+ const top = scored.slice(0, validated.limit);
91
+ // 8. return — map to the public ModelMatch shape
92
+ return top.map(({ model, result }) => ({
93
+ name: model.id,
94
+ provider: model.provider ?? "huggingface",
95
+ paramsB: model.paramsB ?? null,
96
+ contextWindow: model.contextWindow ?? null,
97
+ reasonWhy: (0, scorer_1.generateReasonWhyForResult)(result),
98
+ score: result.finalScore,
99
+ scoringMode: result.scoringMode,
100
+ }));
101
+ }
102
+ // (v0.2) Public error exports — additive. Lets consumers do
103
+ // `catch (e) { if (e instanceof ValidationError) ... }` without reaching
104
+ // into internal modules.
105
+ var errors_1 = require("./errors");
106
+ Object.defineProperty(exports, "SmallmError", { enumerable: true, get: function () { return errors_1.SmallmError; } });
107
+ Object.defineProperty(exports, "HFApiError", { enumerable: true, get: function () { return errors_1.HFApiError; } });
108
+ Object.defineProperty(exports, "RateLimitError", { enumerable: true, get: function () { return errors_1.RateLimitError; } });
109
+ Object.defineProperty(exports, "ValidationError", { enumerable: true, get: function () { return errors_1.ValidationError; } });
110
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import { HFModel, ParamsSource } from "./types";
2
+ export interface ParamsExtractionResult {
3
+ paramsB: number | null;
4
+ paramsSource: ParamsSource;
5
+ }
6
+ /**
7
+ * Extracts a billions-of-params estimate for a model, trying id first,
8
+ * then tags. Config-based extraction ("config" source) would require
9
+ * fetching per-model detail, which the MVP pipeline does not do
10
+ * (see index.ts notes) — so "config" is defined in ParamsSource for
11
+ * forward-compat but never produced in MVP.
12
+ *
13
+ * Known limitation: sub-billion models (e.g. "opt-125m") are not detected
14
+ * since we only match the B suffix. They correctly fall through to
15
+ * "unknown" rather than being misread, which is the safe failure mode
16
+ * per the Unknown-Value Rule.
17
+ */
18
+ export declare function extractParamsB(model: HFModel): ParamsExtractionResult;
19
+ /** Applies extraction to a full list, returning enriched copies (does not mutate input). */
20
+ export declare function enrichWithParams(models: HFModel[]): HFModel[];
21
+ //# sourceMappingURL=params.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"params.d.ts","sourceRoot":"","sources":["../src/params.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAShD,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,YAAY,EAAE,YAAY,CAAC;CAC5B;AASD;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,sBAAsB,CAcrE;AAED,4FAA4F;AAC5F,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,EAAE,CAK7D"}
package/dist/params.js ADDED
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.extractParamsB = extractParamsB;
4
+ exports.enrichWithParams = enrichWithParams;
5
+ /**
6
+ * Matches patterns like "7B", "13b", "1.1B", "70B-Instruct".
7
+ * Requires a digit immediately before the B/b, and no letter/digit right after,
8
+ * so we don't accidentally match things like "Bert" or "B16".
9
+ */
10
+ const PARAM_REGEX = /(\d+(?:\.\d+)?)[\-_]?[bB](?![a-zA-Z0-9])/;
11
+ function extractFromString(value, regex) {
12
+ const match = value.match(regex);
13
+ if (!match)
14
+ return null;
15
+ const num = parseFloat(match[1]);
16
+ return Number.isFinite(num) ? num : null;
17
+ }
18
+ /**
19
+ * Extracts a billions-of-params estimate for a model, trying id first,
20
+ * then tags. Config-based extraction ("config" source) would require
21
+ * fetching per-model detail, which the MVP pipeline does not do
22
+ * (see index.ts notes) — so "config" is defined in ParamsSource for
23
+ * forward-compat but never produced in MVP.
24
+ *
25
+ * Known limitation: sub-billion models (e.g. "opt-125m") are not detected
26
+ * since we only match the B suffix. They correctly fall through to
27
+ * "unknown" rather than being misread, which is the safe failure mode
28
+ * per the Unknown-Value Rule.
29
+ */
30
+ function extractParamsB(model) {
31
+ const fromId = extractFromString(model.id, PARAM_REGEX);
32
+ if (fromId !== null) {
33
+ return { paramsB: fromId, paramsSource: "id" };
34
+ }
35
+ for (const tag of model.tags ?? []) {
36
+ const fromTag = extractFromString(tag, PARAM_REGEX);
37
+ if (fromTag !== null) {
38
+ return { paramsB: fromTag, paramsSource: "tag" };
39
+ }
40
+ }
41
+ return { paramsB: null, paramsSource: "unknown" };
42
+ }
43
+ /** Applies extraction to a full list, returning enriched copies (does not mutate input). */
44
+ function enrichWithParams(models) {
45
+ return models.map((model) => {
46
+ const { paramsB, paramsSource } = extractParamsB(model);
47
+ return { ...model, paramsB, paramsSource };
48
+ });
49
+ }
50
+ //# sourceMappingURL=params.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"params.js","sourceRoot":"","sources":["../src/params.ts"],"names":[],"mappings":";;;;AAEA;;;;GAIG;AACH,MAAM,WAAW,GAAG,0CAA0C,CAAC;AAO/D,SAAS,iBAAiB,CAAC,KAAa,EAAE,KAAa;IACrD,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACjC,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,MAAM,GAAG,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACjC,OAAO,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;AAC3C,CAAC;AAED;;;;;;;;;;;GAWG;AACH,wBAA+B,KAAc;IAC3C,MAAM,MAAM,GAAG,iBAAiB,CAAC,KAAK,CAAC,EAAE,EAAE,WAAW,CAAC,CAAC;IACxD,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QACpB,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC;IACjD,CAAC;IAED,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC;QACnC,MAAM,OAAO,GAAG,iBAAiB,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;QACpD,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;YACrB,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC;QACnD,CAAC;IACH,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,SAAS,EAAE,CAAC;AACpD,CAAC;AAED,4FAA4F;AAC5F,0BAAiC,MAAiB;IAChD,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QAC1B,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;QACxD,OAAO,EAAE,GAAG,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC;IAC7C,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,23 @@
1
+ import { ModelQuery, ScoringMode, ProviderName, HardwareSpec } from "./types";
2
+ import { ValidationError } from "./errors";
3
+ /** True for the original v0.1-v0.3 hardware string enum. */
4
+ export declare function isHardwareEnumString(hardware: unknown): hardware is "cpu" | "gpu-low" | "gpu-high";
5
+ /** True for the v0.4 structured HardwareSpec form. */
6
+ export declare function isHardwareSpec(hardware: unknown): hardware is HardwareSpec;
7
+ /**
8
+ * Backward-compatible alias. As of v0.2, query validation throws the typed
9
+ * ValidationError (a SmallmError subclass, see errors.ts) — this name is
10
+ * kept so any existing `catch (e) { if (e instanceof InvalidQueryError) }`
11
+ * code from v0.1 keeps working unchanged (it's the same class, not a copy).
12
+ */
13
+ export { ValidationError as InvalidQueryError };
14
+ /**
15
+ * Validates a raw ModelQuery and returns a normalized copy
16
+ * (limit defaulted to 5 if omitted). Throws InvalidQueryError on bad input.
17
+ */
18
+ export declare function validateQuery(query: ModelQuery): ModelQuery & {
19
+ limit: number;
20
+ scoringMode: ScoringMode;
21
+ providers: ProviderName[];
22
+ };
23
+ //# sourceMappingURL=query.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC9E,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAU3C,4DAA4D;AAC5D,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,OAAO,GAChB,QAAQ,IAAI,KAAK,GAAG,SAAS,GAAG,UAAU,CAE5C;AAED,sDAAsD;AACtD,wBAAgB,cAAc,CAAC,QAAQ,EAAE,OAAO,GAAG,QAAQ,IAAI,YAAY,CAE1E;AAED;;;;;GAKG;AACH,OAAO,EAAE,eAAe,IAAI,iBAAiB,EAAE,CAAC;AAEhD;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,UAAU,GAChB,UAAU,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,WAAW,CAAC;IAAC,SAAS,EAAE,YAAY,EAAE,CAAA;CAAE,CAgErF"}
package/dist/query.js ADDED
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.InvalidQueryError = void 0;
4
+ exports.isHardwareEnumString = isHardwareEnumString;
5
+ exports.isHardwareSpec = isHardwareSpec;
6
+ exports.validateQuery = validateQuery;
7
+ const errors_1 = require("./errors");
8
+ Object.defineProperty(exports, "InvalidQueryError", { enumerable: true, get: function () { return errors_1.ValidationError; } });
9
+ const VALID_HARDWARE = new Set(["cpu", "gpu-low", "gpu-high"]);
10
+ const VALID_SCORING_MODES = new Set(["rule", "embedding", "hybrid"]);
11
+ const VALID_PROVIDERS = new Set(["huggingface", "ollama"]);
12
+ const VALID_HARDWARE_SPEC_TYPES = new Set(["cpu", "gpu"]);
13
+ const DEFAULT_LIMIT = 5;
14
+ const DEFAULT_SCORING_MODE = "rule";
15
+ const DEFAULT_PROVIDERS = ["huggingface"];
16
+ /** True for the original v0.1-v0.3 hardware string enum. */
17
+ function isHardwareEnumString(hardware) {
18
+ return typeof hardware === "string" && VALID_HARDWARE.has(hardware);
19
+ }
20
+ /** True for the v0.4 structured HardwareSpec form. */
21
+ function isHardwareSpec(hardware) {
22
+ return typeof hardware === "object" && hardware !== null && "type" in hardware;
23
+ }
24
+ /**
25
+ * Validates a raw ModelQuery and returns a normalized copy
26
+ * (limit defaulted to 5 if omitted). Throws InvalidQueryError on bad input.
27
+ */
28
+ function validateQuery(query) {
29
+ if (!query || typeof query !== "object") {
30
+ throw new errors_1.ValidationError("query must be an object");
31
+ }
32
+ if (typeof query.task !== "string" || query.task.trim() === "") {
33
+ throw new errors_1.ValidationError("query.task is required and must be a non-empty string");
34
+ }
35
+ if (typeof query.contextLength !== "number" || !Number.isFinite(query.contextLength) || query.contextLength <= 0) {
36
+ throw new errors_1.ValidationError("query.contextLength is required and must be a positive number");
37
+ }
38
+ if (query.hardware !== undefined && !isHardwareEnumString(query.hardware) && !isHardwareSpec(query.hardware)) {
39
+ throw new errors_1.ValidationError(`query.hardware must be one of: ${[...VALID_HARDWARE].join(", ")}, or a { type, vramGB? } object`);
40
+ }
41
+ if (isHardwareSpec(query.hardware)) {
42
+ if (!VALID_HARDWARE_SPEC_TYPES.has(query.hardware.type)) {
43
+ throw new errors_1.ValidationError(`query.hardware.type must be one of: ${[...VALID_HARDWARE_SPEC_TYPES].join(", ")}`);
44
+ }
45
+ if (query.hardware.vramGB !== undefined && (typeof query.hardware.vramGB !== "number" || query.hardware.vramGB <= 0)) {
46
+ throw new errors_1.ValidationError("query.hardware.vramGB must be a positive number when provided");
47
+ }
48
+ }
49
+ if (query.domain !== undefined && (typeof query.domain !== "string" || query.domain.trim() === "")) {
50
+ throw new errors_1.ValidationError("query.domain must be a non-empty string when provided");
51
+ }
52
+ if (query.maxParamsB !== undefined && (typeof query.maxParamsB !== "number" || query.maxParamsB <= 0)) {
53
+ throw new errors_1.ValidationError("query.maxParamsB must be a positive number when provided");
54
+ }
55
+ if (query.maxLatencyMs !== undefined && (typeof query.maxLatencyMs !== "number" || query.maxLatencyMs <= 0)) {
56
+ throw new errors_1.ValidationError("query.maxLatencyMs must be a positive number when provided");
57
+ }
58
+ if (query.limit !== undefined && (typeof query.limit !== "number" || !Number.isInteger(query.limit) || query.limit <= 0)) {
59
+ throw new errors_1.ValidationError("query.limit must be a positive integer when provided");
60
+ }
61
+ if (query.scoringMode !== undefined && !VALID_SCORING_MODES.has(query.scoringMode)) {
62
+ throw new errors_1.ValidationError(`query.scoringMode must be one of: ${[...VALID_SCORING_MODES].join(", ")}`);
63
+ }
64
+ if (query.providers !== undefined) {
65
+ if (!Array.isArray(query.providers) || query.providers.length === 0) {
66
+ throw new errors_1.ValidationError("query.providers must be a non-empty array when provided");
67
+ }
68
+ for (const p of query.providers) {
69
+ if (!VALID_PROVIDERS.has(p)) {
70
+ throw new errors_1.ValidationError(`query.providers entries must be one of: ${[...VALID_PROVIDERS].join(", ")}`);
71
+ }
72
+ }
73
+ }
74
+ return {
75
+ ...query,
76
+ limit: query.limit ?? DEFAULT_LIMIT,
77
+ scoringMode: query.scoringMode ?? DEFAULT_SCORING_MODE,
78
+ providers: query.providers ?? DEFAULT_PROVIDERS,
79
+ };
80
+ }
81
+ //# sourceMappingURL=query.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query.js","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import { HFModel } from "../types";
2
+ import { HFApiError } from "../errors";
3
+ import { Provider } from "./provider";
4
+ /** Backward-compatible alias — v0.1 threw this name; v0.2 throws the same class under a new name. */
5
+ export { HFApiError as HuggingFaceApiError };
6
+ export declare function mapTaskToPipelineTag(task: string): string;
7
+ /**
8
+ * Fetches candidate models from HuggingFace for a given task.
9
+ * No API key required (public search endpoint, per docx 6.3).
10
+ *
11
+ * (v0.2) Retries transient failures (429 / 5xx) up to MAX_RETRIES times with
12
+ * exponential backoff (500ms, 1000ms, 2000ms). Non-transient failures
13
+ * (4xx other than 429) are thrown immediately — retrying them can't help.
14
+ * All thrown errors are typed SmallmError subclasses (see errors.ts).
15
+ */
16
+ export declare function fetchCandidateModels(task: string): Promise<HFModel[]>;
17
+ /**
18
+ * (v0.4) Provider implementation wrapping fetchCandidateModels — no behavior
19
+ * change to the underlying fetch/retry/error logic above, this just adapts
20
+ * the existing function to the shared Provider interface so index.ts can
21
+ * treat HuggingFace the same way it treats every other provider.
22
+ */
23
+ export declare const huggingfaceProvider: Provider;
24
+ //# sourceMappingURL=huggingface.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"huggingface.d.ts","sourceRoot":"","sources":["../../src/registry/huggingface.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACnC,OAAO,EAAE,UAAU,EAAkB,MAAM,WAAW,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAOtC,qGAAqG;AACrG,OAAO,EAAE,UAAU,IAAI,mBAAmB,EAAE,CAAC;AA6B7C,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEzD;AAsCD;;;;;;;;GAQG;AACH,wBAAsB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAyB3E;AAED;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,EAAE,QAGjC,CAAC"}
@@ -0,0 +1,97 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.huggingfaceProvider = exports.HuggingFaceApiError = void 0;
4
+ exports.mapTaskToPipelineTag = mapTaskToPipelineTag;
5
+ exports.fetchCandidateModels = fetchCandidateModels;
6
+ const errors_1 = require("../errors");
7
+ Object.defineProperty(exports, "HuggingFaceApiError", { enumerable: true, get: function () { return errors_1.HFApiError; } });
8
+ const HF_API_BASE = "https://huggingface.co/api/models";
9
+ /** Number of candidates to pull per query, per docx 6.3: fixed fetch limit, no pagination in MVP. */
10
+ const FETCH_LIMIT = 50;
11
+ /** (v0.2) Retry policy: cap at 3 attempts, exponential backoff, only for transient failures. */
12
+ const MAX_RETRIES = 3;
13
+ const BACKOFF_SCHEDULE_MS = [500, 1000, 2000];
14
+ function sleep(ms) {
15
+ return new Promise((resolve) => setTimeout(resolve, ms));
16
+ }
17
+ /** True for 429 (rate limit) and any 5xx — the only cases worth retrying. */
18
+ function isTransient(status) {
19
+ return status === 429 || (status >= 500 && status < 600);
20
+ }
21
+ /**
22
+ * Maps our public `task` values to HuggingFace's pipeline_tag vocabulary.
23
+ * Unknown/custom task strings pass through unchanged so query.task can
24
+ * still be a free string per the contract.
25
+ */
26
+ const TASK_TO_PIPELINE_TAG = {
27
+ summarize: "summarization",
28
+ classify: "text-classification",
29
+ extract: "token-classification",
30
+ chat: "text-generation",
31
+ code: "text-generation",
32
+ translate: "translation",
33
+ };
34
+ function mapTaskToPipelineTag(task) {
35
+ return TASK_TO_PIPELINE_TAG[task] ?? task;
36
+ }
37
+ /** A single fetch attempt — no retry logic here, that lives in fetchCandidateModels. */
38
+ async function fetchOnce(url) {
39
+ const response = await fetch(url);
40
+ if (!response.ok) {
41
+ if (response.status === 429) {
42
+ throw new errors_1.RateLimitError(`HuggingFace API rate-limited the request (429)`);
43
+ }
44
+ throw new errors_1.HFApiError(`HuggingFace API request failed with status ${response.status}`, response.status);
45
+ }
46
+ const data = (await response.json());
47
+ return data.map((item) => ({
48
+ id: item.id,
49
+ provider: "huggingface",
50
+ pipeline_tag: item.pipeline_tag,
51
+ tags: item.tags ?? [],
52
+ downloads: item.downloads ?? 0,
53
+ }));
54
+ }
55
+ /**
56
+ * Fetches candidate models from HuggingFace for a given task.
57
+ * No API key required (public search endpoint, per docx 6.3).
58
+ *
59
+ * (v0.2) Retries transient failures (429 / 5xx) up to MAX_RETRIES times with
60
+ * exponential backoff (500ms, 1000ms, 2000ms). Non-transient failures
61
+ * (4xx other than 429) are thrown immediately — retrying them can't help.
62
+ * All thrown errors are typed SmallmError subclasses (see errors.ts).
63
+ */
64
+ async function fetchCandidateModels(task) {
65
+ const pipelineTag = mapTaskToPipelineTag(task);
66
+ const url = `${HF_API_BASE}?pipeline_tag=${encodeURIComponent(pipelineTag)}&limit=${FETCH_LIMIT}&sort=downloads&direction=-1`;
67
+ let lastError;
68
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
69
+ try {
70
+ return await fetchOnce(url);
71
+ }
72
+ catch (err) {
73
+ if (!(err instanceof errors_1.HFApiError))
74
+ throw err; // unexpected error shape — don't swallow it
75
+ const status = err.status ?? 0;
76
+ const canRetry = isTransient(status) && attempt < MAX_RETRIES;
77
+ lastError = err;
78
+ if (!canRetry)
79
+ throw err;
80
+ await sleep(BACKOFF_SCHEDULE_MS[attempt]);
81
+ }
82
+ }
83
+ // Unreachable in practice (the loop always returns or throws), but keeps TS happy
84
+ // and gives a sane fallback if MAX_RETRIES were ever set to a negative number.
85
+ throw lastError ?? new errors_1.HFApiError("HuggingFace API request failed with no response");
86
+ }
87
+ /**
88
+ * (v0.4) Provider implementation wrapping fetchCandidateModels — no behavior
89
+ * change to the underlying fetch/retry/error logic above, this just adapts
90
+ * the existing function to the shared Provider interface so index.ts can
91
+ * treat HuggingFace the same way it treats every other provider.
92
+ */
93
+ exports.huggingfaceProvider = {
94
+ name: "huggingface",
95
+ listCandidates: (query) => fetchCandidateModels(query.task),
96
+ };
97
+ //# sourceMappingURL=huggingface.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"huggingface.js","sourceRoot":"","sources":["../../src/registry/huggingface.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { Provider } from "./provider";
2
+ export declare const ollamaProvider: Provider;
3
+ //# sourceMappingURL=ollama.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ollama.d.ts","sourceRoot":"","sources":["../../src/registry/ollama.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAiGtC,eAAO,MAAM,cAAc,EAAE,QAG5B,CAAC"}