@auxiora/model-registry 1.10.7 → 1.10.8
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/dist/db.d.ts +3 -0
- package/dist/db.d.ts.map +1 -0
- package/{src/db.ts → dist/db.js} +7 -8
- package/dist/db.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/registry.d.ts +18 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +135 -0
- package/dist/registry.js.map +1 -0
- package/dist/types.d.ts +57 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +22 -3
- package/src/index.ts +0 -6
- package/src/registry.ts +0 -181
- package/src/types.ts +0 -58
- package/tests/registry.test.ts +0 -239
package/dist/db.d.ts
ADDED
package/dist/db.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAgC3C,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,CAMzD"}
|
package/{src/db.ts → dist/db.js}
RENAMED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { DatabaseSync } from 'node:sqlite';
|
|
2
|
-
|
|
3
2
|
const SCHEMA_DDL = `
|
|
4
3
|
CREATE TABLE IF NOT EXISTS discovered_models (
|
|
5
4
|
id TEXT PRIMARY KEY,
|
|
@@ -29,11 +28,11 @@ const SCHEMA_DDL = `
|
|
|
29
28
|
CREATE INDEX IF NOT EXISTS idx_models_source ON discovered_models(provider_source);
|
|
30
29
|
CREATE INDEX IF NOT EXISTS idx_models_enabled ON discovered_models(enabled);
|
|
31
30
|
`;
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
return db;
|
|
31
|
+
export function openDatabase(dbPath) {
|
|
32
|
+
const db = new DatabaseSync(dbPath);
|
|
33
|
+
db.exec('PRAGMA journal_mode=WAL');
|
|
34
|
+
db.exec('PRAGMA foreign_keys=ON');
|
|
35
|
+
db.exec(SCHEMA_DDL);
|
|
36
|
+
return db;
|
|
39
37
|
}
|
|
38
|
+
//# sourceMappingURL=db.js.map
|
package/dist/db.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"db.js","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,UAAU,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4BlB,CAAC;AAEF,MAAM,UAAU,YAAY,CAAC,MAAc;IACzC,MAAM,EAAE,GAAG,IAAI,YAAY,CAAC,MAAM,CAAC,CAAC;IACpC,EAAE,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;IACnC,EAAE,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;IAClC,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACpB,OAAO,EAAE,CAAC;AACZ,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,YAAY,EACV,eAAe,EACf,kBAAkB,EAClB,qBAAqB,GACtB,MAAM,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { DiscoveredModel, ModelCapabilitiesLike, ModelSearchOptions } from './types.js';
|
|
2
|
+
export declare class ModelRegistry {
|
|
3
|
+
private db;
|
|
4
|
+
constructor(dbPath: string);
|
|
5
|
+
upsertModels(models: DiscoveredModel[]): void;
|
|
6
|
+
getModels(options?: ModelSearchOptions): DiscoveredModel[];
|
|
7
|
+
getModel(id: string): DiscoveredModel | undefined;
|
|
8
|
+
/**
|
|
9
|
+
* Convert discovered models to the format ModelRouter expects.
|
|
10
|
+
* Returns Record<modelId, ModelCapabilities>.
|
|
11
|
+
*/
|
|
12
|
+
toModelCapabilities(source: string): Record<string, ModelCapabilitiesLike>;
|
|
13
|
+
search(query: string): DiscoveredModel[];
|
|
14
|
+
getTrending(limit?: number): DiscoveredModel[];
|
|
15
|
+
pruneStale(maxAgeMs: number): number;
|
|
16
|
+
setEnabled(id: string, enabled: boolean): void;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAoC7F,qBAAa,aAAa;IACxB,OAAO,CAAC,EAAE,CAAe;gBAEb,MAAM,EAAE,MAAM;IAI1B,YAAY,CAAC,MAAM,EAAE,eAAe,EAAE,GAAG,IAAI;IAqC7C,SAAS,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,eAAe,EAAE;IAqC1D,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS;IAQjD;;;OAGG;IACH,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,qBAAqB,CAAC;IAqB1E,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,EAAE;IAIxC,WAAW,CAAC,KAAK,SAAK,GAAG,eAAe,EAAE;IAW1C,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM;IAQpC,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI;CAK/C"}
|
package/dist/registry.js
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { openDatabase } from './db.js';
|
|
2
|
+
function rowToModel(row) {
|
|
3
|
+
return {
|
|
4
|
+
id: row.id,
|
|
5
|
+
providerSource: row.provider_source,
|
|
6
|
+
modelId: row.model_id,
|
|
7
|
+
displayName: row.display_name,
|
|
8
|
+
contextLength: row.context_length,
|
|
9
|
+
supportsVision: row.supports_vision === 1,
|
|
10
|
+
supportsTools: row.supports_tools === 1,
|
|
11
|
+
supportsStreaming: row.supports_streaming === 1,
|
|
12
|
+
supportsImageGen: row.supports_image_gen === 1,
|
|
13
|
+
costPer1kInput: row.cost_per_1k_input,
|
|
14
|
+
costPer1kOutput: row.cost_per_1k_output,
|
|
15
|
+
strengths: JSON.parse(row.strengths || '[]'),
|
|
16
|
+
rawMetadata: row.raw_metadata,
|
|
17
|
+
hfModelCard: row.hf_model_card,
|
|
18
|
+
hfDownloads: row.hf_downloads,
|
|
19
|
+
hfLikes: row.hf_likes,
|
|
20
|
+
hfTrendingScore: row.hf_trending_score,
|
|
21
|
+
hfTags: row.hf_tags ? JSON.parse(row.hf_tags) : undefined,
|
|
22
|
+
hfBenchmarkScores: row.hf_benchmark_scores
|
|
23
|
+
? JSON.parse(row.hf_benchmark_scores)
|
|
24
|
+
: undefined,
|
|
25
|
+
hfInferenceProviders: row.hf_inference_providers
|
|
26
|
+
? JSON.parse(row.hf_inference_providers)
|
|
27
|
+
: undefined,
|
|
28
|
+
lastRefreshedAt: row.last_refreshed_at,
|
|
29
|
+
createdAt: row.created_at,
|
|
30
|
+
enabled: row.enabled === 1,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export class ModelRegistry {
|
|
34
|
+
db;
|
|
35
|
+
constructor(dbPath) {
|
|
36
|
+
this.db = openDatabase(dbPath);
|
|
37
|
+
}
|
|
38
|
+
upsertModels(models) {
|
|
39
|
+
const stmt = this.db.prepare(`
|
|
40
|
+
INSERT OR REPLACE INTO discovered_models (
|
|
41
|
+
id, provider_source, model_id, display_name, context_length,
|
|
42
|
+
supports_vision, supports_tools, supports_streaming, supports_image_gen,
|
|
43
|
+
cost_per_1k_input, cost_per_1k_output, strengths, raw_metadata,
|
|
44
|
+
hf_model_card, hf_downloads, hf_likes, hf_trending_score,
|
|
45
|
+
hf_tags, hf_benchmark_scores, hf_inference_providers,
|
|
46
|
+
last_refreshed_at, created_at, enabled
|
|
47
|
+
) VALUES (
|
|
48
|
+
?, ?, ?, ?, ?,
|
|
49
|
+
?, ?, ?, ?,
|
|
50
|
+
?, ?, ?, ?,
|
|
51
|
+
?, ?, ?, ?,
|
|
52
|
+
?, ?, ?,
|
|
53
|
+
?, ?, ?
|
|
54
|
+
)
|
|
55
|
+
`);
|
|
56
|
+
for (const m of models) {
|
|
57
|
+
stmt.run(m.id, m.providerSource, m.modelId, m.displayName, m.contextLength, m.supportsVision ? 1 : 0, m.supportsTools ? 1 : 0, m.supportsStreaming ? 1 : 0, m.supportsImageGen ? 1 : 0, m.costPer1kInput, m.costPer1kOutput, JSON.stringify(m.strengths), m.rawMetadata ?? null, m.hfModelCard ?? null, m.hfDownloads ?? null, m.hfLikes ?? null, m.hfTrendingScore ?? null, m.hfTags ? JSON.stringify(m.hfTags) : null, m.hfBenchmarkScores ? JSON.stringify(m.hfBenchmarkScores) : null, m.hfInferenceProviders ? JSON.stringify(m.hfInferenceProviders) : null, m.lastRefreshedAt, m.createdAt, m.enabled ? 1 : 0);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
getModels(options) {
|
|
61
|
+
const conditions = [];
|
|
62
|
+
const params = [];
|
|
63
|
+
if (options?.source) {
|
|
64
|
+
conditions.push('provider_source = ?');
|
|
65
|
+
params.push(options.source);
|
|
66
|
+
}
|
|
67
|
+
if (options?.query) {
|
|
68
|
+
conditions.push('(display_name LIKE ? OR model_id LIKE ?)');
|
|
69
|
+
const q = `%${options.query}%`;
|
|
70
|
+
params.push(q, q);
|
|
71
|
+
}
|
|
72
|
+
if (options?.supportsVision !== undefined) {
|
|
73
|
+
conditions.push('supports_vision = ?');
|
|
74
|
+
params.push(options.supportsVision ? 1 : 0);
|
|
75
|
+
}
|
|
76
|
+
if (options?.supportsTools !== undefined) {
|
|
77
|
+
conditions.push('supports_tools = ?');
|
|
78
|
+
params.push(options.supportsTools ? 1 : 0);
|
|
79
|
+
}
|
|
80
|
+
if (options?.enabled !== undefined) {
|
|
81
|
+
conditions.push('enabled = ?');
|
|
82
|
+
params.push(options.enabled ? 1 : 0);
|
|
83
|
+
}
|
|
84
|
+
const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
|
|
85
|
+
const limit = options?.limit ?? 200;
|
|
86
|
+
const offset = options?.offset ?? 0;
|
|
87
|
+
const rows = this.db.prepare(`SELECT * FROM discovered_models ${where} ORDER BY display_name ASC LIMIT ? OFFSET ?`).all(...params, limit, offset);
|
|
88
|
+
return rows.map(rowToModel);
|
|
89
|
+
}
|
|
90
|
+
getModel(id) {
|
|
91
|
+
const row = this.db.prepare('SELECT * FROM discovered_models WHERE id = ?').get(id);
|
|
92
|
+
return row ? rowToModel(row) : undefined;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Convert discovered models to the format ModelRouter expects.
|
|
96
|
+
* Returns Record<modelId, ModelCapabilities>.
|
|
97
|
+
*/
|
|
98
|
+
toModelCapabilities(source) {
|
|
99
|
+
const models = this.getModels({ source, enabled: true });
|
|
100
|
+
const result = {};
|
|
101
|
+
for (const m of models) {
|
|
102
|
+
result[m.modelId] = {
|
|
103
|
+
maxContextTokens: m.contextLength,
|
|
104
|
+
supportsVision: m.supportsVision,
|
|
105
|
+
supportsTools: m.supportsTools,
|
|
106
|
+
supportsStreaming: m.supportsStreaming,
|
|
107
|
+
supportsImageGen: m.supportsImageGen,
|
|
108
|
+
costPer1kInput: m.costPer1kInput,
|
|
109
|
+
costPer1kOutput: m.costPer1kOutput,
|
|
110
|
+
strengths: m.strengths,
|
|
111
|
+
isLocal: false,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
return result;
|
|
115
|
+
}
|
|
116
|
+
search(query) {
|
|
117
|
+
return this.getModels({ query, enabled: true });
|
|
118
|
+
}
|
|
119
|
+
getTrending(limit = 20) {
|
|
120
|
+
const rows = this.db.prepare(`SELECT * FROM discovered_models
|
|
121
|
+
WHERE hf_trending_score IS NOT NULL AND enabled = 1
|
|
122
|
+
ORDER BY hf_trending_score DESC
|
|
123
|
+
LIMIT ?`).all(limit);
|
|
124
|
+
return rows.map(rowToModel);
|
|
125
|
+
}
|
|
126
|
+
pruneStale(maxAgeMs) {
|
|
127
|
+
const cutoff = Date.now() - maxAgeMs;
|
|
128
|
+
const result = this.db.prepare('DELETE FROM discovered_models WHERE last_refreshed_at < ?').run(cutoff);
|
|
129
|
+
return Number(result.changes);
|
|
130
|
+
}
|
|
131
|
+
setEnabled(id, enabled) {
|
|
132
|
+
this.db.prepare('UPDATE discovered_models SET enabled = ? WHERE id = ?').run(enabled ? 1 : 0, id);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A model discovered from an external provider API (OpenRouter, HuggingFace, etc.).
|
|
3
|
+
*/
|
|
4
|
+
export interface DiscoveredModel {
|
|
5
|
+
/** Composite key: `${providerSource}:${modelId}` */
|
|
6
|
+
id: string;
|
|
7
|
+
/** Source provider that discovered this model */
|
|
8
|
+
providerSource: string;
|
|
9
|
+
/** Original model ID from the external API */
|
|
10
|
+
modelId: string;
|
|
11
|
+
displayName: string;
|
|
12
|
+
contextLength: number;
|
|
13
|
+
supportsVision: boolean;
|
|
14
|
+
supportsTools: boolean;
|
|
15
|
+
supportsStreaming: boolean;
|
|
16
|
+
supportsImageGen: boolean;
|
|
17
|
+
costPer1kInput: number;
|
|
18
|
+
costPer1kOutput: number;
|
|
19
|
+
strengths: string[];
|
|
20
|
+
rawMetadata?: string;
|
|
21
|
+
/** HuggingFace-specific fields */
|
|
22
|
+
hfModelCard?: string;
|
|
23
|
+
hfDownloads?: number;
|
|
24
|
+
hfLikes?: number;
|
|
25
|
+
hfTrendingScore?: number;
|
|
26
|
+
hfTags?: string[];
|
|
27
|
+
hfBenchmarkScores?: Record<string, number>;
|
|
28
|
+
hfInferenceProviders?: string[];
|
|
29
|
+
lastRefreshedAt: number;
|
|
30
|
+
createdAt: number;
|
|
31
|
+
enabled: boolean;
|
|
32
|
+
}
|
|
33
|
+
export interface ModelSearchOptions {
|
|
34
|
+
source?: string;
|
|
35
|
+
query?: string;
|
|
36
|
+
supportsVision?: boolean;
|
|
37
|
+
supportsTools?: boolean;
|
|
38
|
+
enabled?: boolean;
|
|
39
|
+
limit?: number;
|
|
40
|
+
offset?: number;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Structural type matching ModelCapabilities from @auxiora/providers
|
|
44
|
+
* to avoid a hard dependency on that package.
|
|
45
|
+
*/
|
|
46
|
+
export interface ModelCapabilitiesLike {
|
|
47
|
+
maxContextTokens: number;
|
|
48
|
+
supportsVision: boolean;
|
|
49
|
+
supportsTools: boolean;
|
|
50
|
+
supportsStreaming: boolean;
|
|
51
|
+
supportsImageGen: boolean;
|
|
52
|
+
costPer1kInput: number;
|
|
53
|
+
costPer1kOutput: number;
|
|
54
|
+
strengths: string[];
|
|
55
|
+
isLocal: boolean;
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,oDAAoD;IACpD,EAAE,EAAE,MAAM,CAAC;IACX,iDAAiD;IACjD,cAAc,EAAE,MAAM,CAAC;IACvB,8CAA8C;IAC9C,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,OAAO,CAAC;IACxB,aAAa,EAAE,OAAO,CAAC;IACvB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,gBAAgB,EAAE,OAAO,CAAC;IAC1B,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kCAAkC;IAClC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC3C,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;IAChC,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,OAAO,CAAC;IACxB,aAAa,EAAE,OAAO,CAAC;IACvB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,gBAAgB,EAAE,OAAO,CAAC;IAC1B,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;CAClB"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
CHANGED
|
@@ -1,10 +1,29 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@auxiora/model-registry",
|
|
3
|
-
"version": "1.10.
|
|
3
|
+
"version": "1.10.8",
|
|
4
4
|
"type": "module",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
5
7
|
"exports": {
|
|
6
|
-
".":
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"import": "./dist/index.js"
|
|
11
|
+
}
|
|
7
12
|
},
|
|
13
|
+
"engines": {
|
|
14
|
+
"node": ">=22.0.0"
|
|
15
|
+
},
|
|
16
|
+
"publishConfig": {
|
|
17
|
+
"access": "public"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist/"
|
|
21
|
+
],
|
|
8
22
|
"dependencies": {},
|
|
9
|
-
"devDependencies": {}
|
|
23
|
+
"devDependencies": {},
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "tsc",
|
|
26
|
+
"clean": "rm -rf dist",
|
|
27
|
+
"typecheck": "tsc --noEmit"
|
|
28
|
+
}
|
|
10
29
|
}
|
package/src/index.ts
DELETED
package/src/registry.ts
DELETED
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
import type { DatabaseSync } from 'node:sqlite';
|
|
2
|
-
import { openDatabase } from './db.js';
|
|
3
|
-
import type { DiscoveredModel, ModelCapabilitiesLike, ModelSearchOptions } from './types.js';
|
|
4
|
-
|
|
5
|
-
type Row = Record<string, unknown>;
|
|
6
|
-
|
|
7
|
-
function rowToModel(row: Row): DiscoveredModel {
|
|
8
|
-
return {
|
|
9
|
-
id: row.id as string,
|
|
10
|
-
providerSource: row.provider_source as string,
|
|
11
|
-
modelId: row.model_id as string,
|
|
12
|
-
displayName: row.display_name as string,
|
|
13
|
-
contextLength: row.context_length as number,
|
|
14
|
-
supportsVision: (row.supports_vision as number) === 1,
|
|
15
|
-
supportsTools: (row.supports_tools as number) === 1,
|
|
16
|
-
supportsStreaming: (row.supports_streaming as number) === 1,
|
|
17
|
-
supportsImageGen: (row.supports_image_gen as number) === 1,
|
|
18
|
-
costPer1kInput: row.cost_per_1k_input as number,
|
|
19
|
-
costPer1kOutput: row.cost_per_1k_output as number,
|
|
20
|
-
strengths: JSON.parse((row.strengths as string) || '[]') as string[],
|
|
21
|
-
rawMetadata: row.raw_metadata as string | undefined,
|
|
22
|
-
hfModelCard: row.hf_model_card as string | undefined,
|
|
23
|
-
hfDownloads: row.hf_downloads as number | undefined,
|
|
24
|
-
hfLikes: row.hf_likes as number | undefined,
|
|
25
|
-
hfTrendingScore: row.hf_trending_score as number | undefined,
|
|
26
|
-
hfTags: row.hf_tags ? JSON.parse(row.hf_tags as string) as string[] : undefined,
|
|
27
|
-
hfBenchmarkScores: row.hf_benchmark_scores
|
|
28
|
-
? JSON.parse(row.hf_benchmark_scores as string) as Record<string, number>
|
|
29
|
-
: undefined,
|
|
30
|
-
hfInferenceProviders: row.hf_inference_providers
|
|
31
|
-
? JSON.parse(row.hf_inference_providers as string) as string[]
|
|
32
|
-
: undefined,
|
|
33
|
-
lastRefreshedAt: row.last_refreshed_at as number,
|
|
34
|
-
createdAt: row.created_at as number,
|
|
35
|
-
enabled: (row.enabled as number) === 1,
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export class ModelRegistry {
|
|
40
|
-
private db: DatabaseSync;
|
|
41
|
-
|
|
42
|
-
constructor(dbPath: string) {
|
|
43
|
-
this.db = openDatabase(dbPath);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
upsertModels(models: DiscoveredModel[]): void {
|
|
47
|
-
const stmt = this.db.prepare(`
|
|
48
|
-
INSERT OR REPLACE INTO discovered_models (
|
|
49
|
-
id, provider_source, model_id, display_name, context_length,
|
|
50
|
-
supports_vision, supports_tools, supports_streaming, supports_image_gen,
|
|
51
|
-
cost_per_1k_input, cost_per_1k_output, strengths, raw_metadata,
|
|
52
|
-
hf_model_card, hf_downloads, hf_likes, hf_trending_score,
|
|
53
|
-
hf_tags, hf_benchmark_scores, hf_inference_providers,
|
|
54
|
-
last_refreshed_at, created_at, enabled
|
|
55
|
-
) VALUES (
|
|
56
|
-
?, ?, ?, ?, ?,
|
|
57
|
-
?, ?, ?, ?,
|
|
58
|
-
?, ?, ?, ?,
|
|
59
|
-
?, ?, ?, ?,
|
|
60
|
-
?, ?, ?,
|
|
61
|
-
?, ?, ?
|
|
62
|
-
)
|
|
63
|
-
`);
|
|
64
|
-
|
|
65
|
-
for (const m of models) {
|
|
66
|
-
stmt.run(
|
|
67
|
-
m.id, m.providerSource, m.modelId, m.displayName, m.contextLength,
|
|
68
|
-
m.supportsVision ? 1 : 0, m.supportsTools ? 1 : 0,
|
|
69
|
-
m.supportsStreaming ? 1 : 0, m.supportsImageGen ? 1 : 0,
|
|
70
|
-
m.costPer1kInput, m.costPer1kOutput,
|
|
71
|
-
JSON.stringify(m.strengths), m.rawMetadata ?? null,
|
|
72
|
-
m.hfModelCard ?? null, m.hfDownloads ?? null,
|
|
73
|
-
m.hfLikes ?? null, m.hfTrendingScore ?? null,
|
|
74
|
-
m.hfTags ? JSON.stringify(m.hfTags) : null,
|
|
75
|
-
m.hfBenchmarkScores ? JSON.stringify(m.hfBenchmarkScores) : null,
|
|
76
|
-
m.hfInferenceProviders ? JSON.stringify(m.hfInferenceProviders) : null,
|
|
77
|
-
m.lastRefreshedAt, m.createdAt,
|
|
78
|
-
m.enabled ? 1 : 0,
|
|
79
|
-
);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
getModels(options?: ModelSearchOptions): DiscoveredModel[] {
|
|
84
|
-
const conditions: string[] = [];
|
|
85
|
-
const params: unknown[] = [];
|
|
86
|
-
|
|
87
|
-
if (options?.source) {
|
|
88
|
-
conditions.push('provider_source = ?');
|
|
89
|
-
params.push(options.source);
|
|
90
|
-
}
|
|
91
|
-
if (options?.query) {
|
|
92
|
-
conditions.push('(display_name LIKE ? OR model_id LIKE ?)');
|
|
93
|
-
const q = `%${options.query}%`;
|
|
94
|
-
params.push(q, q);
|
|
95
|
-
}
|
|
96
|
-
if (options?.supportsVision !== undefined) {
|
|
97
|
-
conditions.push('supports_vision = ?');
|
|
98
|
-
params.push(options.supportsVision ? 1 : 0);
|
|
99
|
-
}
|
|
100
|
-
if (options?.supportsTools !== undefined) {
|
|
101
|
-
conditions.push('supports_tools = ?');
|
|
102
|
-
params.push(options.supportsTools ? 1 : 0);
|
|
103
|
-
}
|
|
104
|
-
if (options?.enabled !== undefined) {
|
|
105
|
-
conditions.push('enabled = ?');
|
|
106
|
-
params.push(options.enabled ? 1 : 0);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
|
|
110
|
-
const limit = options?.limit ?? 200;
|
|
111
|
-
const offset = options?.offset ?? 0;
|
|
112
|
-
|
|
113
|
-
const rows = this.db.prepare(
|
|
114
|
-
`SELECT * FROM discovered_models ${where} ORDER BY display_name ASC LIMIT ? OFFSET ?`,
|
|
115
|
-
).all(...params, limit, offset) as Row[];
|
|
116
|
-
|
|
117
|
-
return rows.map(rowToModel);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
getModel(id: string): DiscoveredModel | undefined {
|
|
121
|
-
const row = this.db.prepare(
|
|
122
|
-
'SELECT * FROM discovered_models WHERE id = ?',
|
|
123
|
-
).get(id) as Row | undefined;
|
|
124
|
-
|
|
125
|
-
return row ? rowToModel(row) : undefined;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Convert discovered models to the format ModelRouter expects.
|
|
130
|
-
* Returns Record<modelId, ModelCapabilities>.
|
|
131
|
-
*/
|
|
132
|
-
toModelCapabilities(source: string): Record<string, ModelCapabilitiesLike> {
|
|
133
|
-
const models = this.getModels({ source, enabled: true });
|
|
134
|
-
const result: Record<string, ModelCapabilitiesLike> = {};
|
|
135
|
-
|
|
136
|
-
for (const m of models) {
|
|
137
|
-
result[m.modelId] = {
|
|
138
|
-
maxContextTokens: m.contextLength,
|
|
139
|
-
supportsVision: m.supportsVision,
|
|
140
|
-
supportsTools: m.supportsTools,
|
|
141
|
-
supportsStreaming: m.supportsStreaming,
|
|
142
|
-
supportsImageGen: m.supportsImageGen,
|
|
143
|
-
costPer1kInput: m.costPer1kInput,
|
|
144
|
-
costPer1kOutput: m.costPer1kOutput,
|
|
145
|
-
strengths: m.strengths,
|
|
146
|
-
isLocal: false,
|
|
147
|
-
};
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
return result;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
search(query: string): DiscoveredModel[] {
|
|
154
|
-
return this.getModels({ query, enabled: true });
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
getTrending(limit = 20): DiscoveredModel[] {
|
|
158
|
-
const rows = this.db.prepare(
|
|
159
|
-
`SELECT * FROM discovered_models
|
|
160
|
-
WHERE hf_trending_score IS NOT NULL AND enabled = 1
|
|
161
|
-
ORDER BY hf_trending_score DESC
|
|
162
|
-
LIMIT ?`,
|
|
163
|
-
).all(limit) as Row[];
|
|
164
|
-
|
|
165
|
-
return rows.map(rowToModel);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
pruneStale(maxAgeMs: number): number {
|
|
169
|
-
const cutoff = Date.now() - maxAgeMs;
|
|
170
|
-
const result = this.db.prepare(
|
|
171
|
-
'DELETE FROM discovered_models WHERE last_refreshed_at < ?',
|
|
172
|
-
).run(cutoff);
|
|
173
|
-
return result.changes;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
setEnabled(id: string, enabled: boolean): void {
|
|
177
|
-
this.db.prepare(
|
|
178
|
-
'UPDATE discovered_models SET enabled = ? WHERE id = ?',
|
|
179
|
-
).run(enabled ? 1 : 0, id);
|
|
180
|
-
}
|
|
181
|
-
}
|
package/src/types.ts
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* A model discovered from an external provider API (OpenRouter, HuggingFace, etc.).
|
|
3
|
-
*/
|
|
4
|
-
export interface DiscoveredModel {
|
|
5
|
-
/** Composite key: `${providerSource}:${modelId}` */
|
|
6
|
-
id: string;
|
|
7
|
-
/** Source provider that discovered this model */
|
|
8
|
-
providerSource: string;
|
|
9
|
-
/** Original model ID from the external API */
|
|
10
|
-
modelId: string;
|
|
11
|
-
displayName: string;
|
|
12
|
-
contextLength: number;
|
|
13
|
-
supportsVision: boolean;
|
|
14
|
-
supportsTools: boolean;
|
|
15
|
-
supportsStreaming: boolean;
|
|
16
|
-
supportsImageGen: boolean;
|
|
17
|
-
costPer1kInput: number;
|
|
18
|
-
costPer1kOutput: number;
|
|
19
|
-
strengths: string[];
|
|
20
|
-
rawMetadata?: string;
|
|
21
|
-
/** HuggingFace-specific fields */
|
|
22
|
-
hfModelCard?: string;
|
|
23
|
-
hfDownloads?: number;
|
|
24
|
-
hfLikes?: number;
|
|
25
|
-
hfTrendingScore?: number;
|
|
26
|
-
hfTags?: string[];
|
|
27
|
-
hfBenchmarkScores?: Record<string, number>;
|
|
28
|
-
hfInferenceProviders?: string[];
|
|
29
|
-
lastRefreshedAt: number;
|
|
30
|
-
createdAt: number;
|
|
31
|
-
enabled: boolean;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export interface ModelSearchOptions {
|
|
35
|
-
source?: string;
|
|
36
|
-
query?: string;
|
|
37
|
-
supportsVision?: boolean;
|
|
38
|
-
supportsTools?: boolean;
|
|
39
|
-
enabled?: boolean;
|
|
40
|
-
limit?: number;
|
|
41
|
-
offset?: number;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Structural type matching ModelCapabilities from @auxiora/providers
|
|
46
|
-
* to avoid a hard dependency on that package.
|
|
47
|
-
*/
|
|
48
|
-
export interface ModelCapabilitiesLike {
|
|
49
|
-
maxContextTokens: number;
|
|
50
|
-
supportsVision: boolean;
|
|
51
|
-
supportsTools: boolean;
|
|
52
|
-
supportsStreaming: boolean;
|
|
53
|
-
supportsImageGen: boolean;
|
|
54
|
-
costPer1kInput: number;
|
|
55
|
-
costPer1kOutput: number;
|
|
56
|
-
strengths: string[];
|
|
57
|
-
isLocal: boolean;
|
|
58
|
-
}
|
package/tests/registry.test.ts
DELETED
|
@@ -1,239 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
-
import { ModelRegistry } from '../src/registry.js';
|
|
3
|
-
import type { DiscoveredModel } from '../src/types.js';
|
|
4
|
-
|
|
5
|
-
function makeModel(overrides: Partial<DiscoveredModel> = {}): DiscoveredModel {
|
|
6
|
-
const now = Date.now();
|
|
7
|
-
return {
|
|
8
|
-
id: 'openrouter:test/model-1',
|
|
9
|
-
providerSource: 'openrouter',
|
|
10
|
-
modelId: 'test/model-1',
|
|
11
|
-
displayName: 'Test Model 1',
|
|
12
|
-
contextLength: 128000,
|
|
13
|
-
supportsVision: true,
|
|
14
|
-
supportsTools: true,
|
|
15
|
-
supportsStreaming: true,
|
|
16
|
-
supportsImageGen: false,
|
|
17
|
-
costPer1kInput: 0.003,
|
|
18
|
-
costPer1kOutput: 0.015,
|
|
19
|
-
strengths: ['reasoning', 'code'],
|
|
20
|
-
lastRefreshedAt: now,
|
|
21
|
-
createdAt: now,
|
|
22
|
-
enabled: true,
|
|
23
|
-
...overrides,
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
describe('ModelRegistry', () => {
|
|
28
|
-
let registry: ModelRegistry;
|
|
29
|
-
|
|
30
|
-
beforeEach(() => {
|
|
31
|
-
registry = new ModelRegistry(':memory:');
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
describe('upsertModels', () => {
|
|
35
|
-
it('should insert models and retrieve them', () => {
|
|
36
|
-
const model = makeModel();
|
|
37
|
-
registry.upsertModels([model]);
|
|
38
|
-
|
|
39
|
-
const result = registry.getModel('openrouter:test/model-1');
|
|
40
|
-
expect(result).toBeDefined();
|
|
41
|
-
expect(result!.displayName).toBe('Test Model 1');
|
|
42
|
-
expect(result!.supportsVision).toBe(true);
|
|
43
|
-
expect(result!.strengths).toEqual(['reasoning', 'code']);
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it('should upsert (update on conflict)', () => {
|
|
47
|
-
const model = makeModel();
|
|
48
|
-
registry.upsertModels([model]);
|
|
49
|
-
|
|
50
|
-
const updated = makeModel({ displayName: 'Updated Name', costPer1kInput: 0.005 });
|
|
51
|
-
registry.upsertModels([updated]);
|
|
52
|
-
|
|
53
|
-
const result = registry.getModel('openrouter:test/model-1');
|
|
54
|
-
expect(result!.displayName).toBe('Updated Name');
|
|
55
|
-
expect(result!.costPer1kInput).toBe(0.005);
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it('should insert multiple models at once', () => {
|
|
59
|
-
const models = [
|
|
60
|
-
makeModel({ id: 'openrouter:a/model-a', modelId: 'a/model-a', displayName: 'Model A' }),
|
|
61
|
-
makeModel({ id: 'openrouter:b/model-b', modelId: 'b/model-b', displayName: 'Model B' }),
|
|
62
|
-
makeModel({ id: 'openrouter:c/model-c', modelId: 'c/model-c', displayName: 'Model C' }),
|
|
63
|
-
];
|
|
64
|
-
registry.upsertModels(models);
|
|
65
|
-
|
|
66
|
-
const all = registry.getModels();
|
|
67
|
-
expect(all).toHaveLength(3);
|
|
68
|
-
});
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
describe('getModels', () => {
|
|
72
|
-
beforeEach(() => {
|
|
73
|
-
registry.upsertModels([
|
|
74
|
-
makeModel({ id: 'openrouter:a/big', modelId: 'a/big', displayName: 'Big Model', providerSource: 'openrouter', supportsVision: true }),
|
|
75
|
-
makeModel({ id: 'openrouter:a/small', modelId: 'a/small', displayName: 'Small Model', providerSource: 'openrouter', supportsVision: false }),
|
|
76
|
-
makeModel({ id: 'huggingface:b/chat', modelId: 'b/chat', displayName: 'Chat Model', providerSource: 'huggingface', supportsVision: false }),
|
|
77
|
-
]);
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
it('should filter by source', () => {
|
|
81
|
-
const result = registry.getModels({ source: 'openrouter' });
|
|
82
|
-
expect(result).toHaveLength(2);
|
|
83
|
-
expect(result.every(m => m.providerSource === 'openrouter')).toBe(true);
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
it('should filter by vision support', () => {
|
|
87
|
-
const result = registry.getModels({ supportsVision: true });
|
|
88
|
-
expect(result).toHaveLength(1);
|
|
89
|
-
expect(result[0]!.displayName).toBe('Big Model');
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
it('should search by query', () => {
|
|
93
|
-
const result = registry.getModels({ query: 'chat' });
|
|
94
|
-
expect(result).toHaveLength(1);
|
|
95
|
-
expect(result[0]!.displayName).toBe('Chat Model');
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
it('should support limit and offset', () => {
|
|
99
|
-
const page1 = registry.getModels({ limit: 2, offset: 0 });
|
|
100
|
-
expect(page1).toHaveLength(2);
|
|
101
|
-
|
|
102
|
-
const page2 = registry.getModels({ limit: 2, offset: 2 });
|
|
103
|
-
expect(page2).toHaveLength(1);
|
|
104
|
-
});
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
describe('getModel', () => {
|
|
108
|
-
it('should return undefined for non-existent model', () => {
|
|
109
|
-
const result = registry.getModel('nonexistent');
|
|
110
|
-
expect(result).toBeUndefined();
|
|
111
|
-
});
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
describe('toModelCapabilities', () => {
|
|
115
|
-
it('should convert to ModelCapabilities format', () => {
|
|
116
|
-
registry.upsertModels([
|
|
117
|
-
makeModel({ id: 'openrouter:a/model', modelId: 'a/model', providerSource: 'openrouter' }),
|
|
118
|
-
]);
|
|
119
|
-
|
|
120
|
-
const caps = registry.toModelCapabilities('openrouter');
|
|
121
|
-
expect(caps['a/model']).toBeDefined();
|
|
122
|
-
expect(caps['a/model']!.maxContextTokens).toBe(128000);
|
|
123
|
-
expect(caps['a/model']!.supportsVision).toBe(true);
|
|
124
|
-
expect(caps['a/model']!.isLocal).toBe(false);
|
|
125
|
-
expect(caps['a/model']!.strengths).toEqual(['reasoning', 'code']);
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
it('should exclude disabled models', () => {
|
|
129
|
-
registry.upsertModels([
|
|
130
|
-
makeModel({ id: 'openrouter:a/enabled', modelId: 'a/enabled', enabled: true }),
|
|
131
|
-
makeModel({ id: 'openrouter:a/disabled', modelId: 'a/disabled', enabled: false }),
|
|
132
|
-
]);
|
|
133
|
-
|
|
134
|
-
const caps = registry.toModelCapabilities('openrouter');
|
|
135
|
-
expect(caps['a/enabled']).toBeDefined();
|
|
136
|
-
expect(caps['a/disabled']).toBeUndefined();
|
|
137
|
-
});
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
describe('search', () => {
|
|
141
|
-
it('should search by display name and model ID', () => {
|
|
142
|
-
registry.upsertModels([
|
|
143
|
-
makeModel({ id: 'or:anthropic/claude-3-opus', modelId: 'anthropic/claude-3-opus', displayName: 'Claude 3 Opus' }),
|
|
144
|
-
makeModel({ id: 'or:meta/llama-3', modelId: 'meta/llama-3', displayName: 'Llama 3' }),
|
|
145
|
-
]);
|
|
146
|
-
|
|
147
|
-
const results = registry.search('claude');
|
|
148
|
-
expect(results).toHaveLength(1);
|
|
149
|
-
expect(results[0]!.displayName).toBe('Claude 3 Opus');
|
|
150
|
-
});
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
describe('getTrending', () => {
|
|
154
|
-
it('should return models sorted by trending score', () => {
|
|
155
|
-
registry.upsertModels([
|
|
156
|
-
makeModel({ id: 'hf:a', modelId: 'a', providerSource: 'huggingface', hfTrendingScore: 0.8 }),
|
|
157
|
-
makeModel({ id: 'hf:b', modelId: 'b', providerSource: 'huggingface', hfTrendingScore: 0.95 }),
|
|
158
|
-
makeModel({ id: 'hf:c', modelId: 'c', providerSource: 'huggingface', hfTrendingScore: 0.5 }),
|
|
159
|
-
]);
|
|
160
|
-
|
|
161
|
-
const trending = registry.getTrending(2);
|
|
162
|
-
expect(trending).toHaveLength(2);
|
|
163
|
-
expect(trending[0]!.modelId).toBe('b');
|
|
164
|
-
expect(trending[1]!.modelId).toBe('a');
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
it('should exclude models without trending score', () => {
|
|
168
|
-
registry.upsertModels([
|
|
169
|
-
makeModel({ id: 'or:x', modelId: 'x', providerSource: 'openrouter' }),
|
|
170
|
-
makeModel({ id: 'hf:y', modelId: 'y', providerSource: 'huggingface', hfTrendingScore: 0.5 }),
|
|
171
|
-
]);
|
|
172
|
-
|
|
173
|
-
const trending = registry.getTrending();
|
|
174
|
-
expect(trending).toHaveLength(1);
|
|
175
|
-
expect(trending[0]!.modelId).toBe('y');
|
|
176
|
-
});
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
describe('pruneStale', () => {
|
|
180
|
-
it('should delete models older than maxAge', () => {
|
|
181
|
-
const old = Date.now() - 10 * 24 * 60 * 60 * 1000; // 10 days ago
|
|
182
|
-
const recent = Date.now();
|
|
183
|
-
|
|
184
|
-
registry.upsertModels([
|
|
185
|
-
makeModel({ id: 'or:old', modelId: 'old', lastRefreshedAt: old }),
|
|
186
|
-
makeModel({ id: 'or:new', modelId: 'new', lastRefreshedAt: recent }),
|
|
187
|
-
]);
|
|
188
|
-
|
|
189
|
-
const pruned = registry.pruneStale(7 * 24 * 60 * 60 * 1000); // 7 days
|
|
190
|
-
expect(pruned).toBe(1);
|
|
191
|
-
|
|
192
|
-
const remaining = registry.getModels();
|
|
193
|
-
expect(remaining).toHaveLength(1);
|
|
194
|
-
expect(remaining[0]!.modelId).toBe('new');
|
|
195
|
-
});
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
describe('setEnabled', () => {
|
|
199
|
-
it('should disable and re-enable a model', () => {
|
|
200
|
-
registry.upsertModels([makeModel()]);
|
|
201
|
-
|
|
202
|
-
registry.setEnabled('openrouter:test/model-1', false);
|
|
203
|
-
let model = registry.getModel('openrouter:test/model-1');
|
|
204
|
-
expect(model!.enabled).toBe(false);
|
|
205
|
-
|
|
206
|
-
registry.setEnabled('openrouter:test/model-1', true);
|
|
207
|
-
model = registry.getModel('openrouter:test/model-1');
|
|
208
|
-
expect(model!.enabled).toBe(true);
|
|
209
|
-
});
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
describe('HuggingFace-specific fields', () => {
|
|
213
|
-
it('should persist and retrieve HF metadata', () => {
|
|
214
|
-
registry.upsertModels([
|
|
215
|
-
makeModel({
|
|
216
|
-
id: 'hf:meta/llama',
|
|
217
|
-
modelId: 'meta/llama',
|
|
218
|
-
providerSource: 'huggingface',
|
|
219
|
-
hfModelCard: '# Llama\n\nA great model.',
|
|
220
|
-
hfDownloads: 1500000,
|
|
221
|
-
hfLikes: 5000,
|
|
222
|
-
hfTrendingScore: 0.92,
|
|
223
|
-
hfTags: ['transformers', 'pytorch', 'llama'],
|
|
224
|
-
hfBenchmarkScores: { mmlu: 0.82, humaneval: 0.71 },
|
|
225
|
-
hfInferenceProviders: ['together', 'fireworks-ai'],
|
|
226
|
-
}),
|
|
227
|
-
]);
|
|
228
|
-
|
|
229
|
-
const model = registry.getModel('hf:meta/llama')!;
|
|
230
|
-
expect(model.hfModelCard).toBe('# Llama\n\nA great model.');
|
|
231
|
-
expect(model.hfDownloads).toBe(1500000);
|
|
232
|
-
expect(model.hfLikes).toBe(5000);
|
|
233
|
-
expect(model.hfTrendingScore).toBe(0.92);
|
|
234
|
-
expect(model.hfTags).toEqual(['transformers', 'pytorch', 'llama']);
|
|
235
|
-
expect(model.hfBenchmarkScores).toEqual({ mmlu: 0.82, humaneval: 0.71 });
|
|
236
|
-
expect(model.hfInferenceProviders).toEqual(['together', 'fireworks-ai']);
|
|
237
|
-
});
|
|
238
|
-
});
|
|
239
|
-
});
|