@routstr/sdk 0.3.8 → 0.3.10
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/browser.d.mts +12 -0
- package/dist/browser.d.ts +12 -0
- package/dist/browser.js +6278 -0
- package/dist/browser.js.map +1 -0
- package/dist/browser.mjs +6230 -0
- package/dist/browser.mjs.map +1 -0
- package/dist/bun.d.mts +29 -0
- package/dist/bun.d.ts +29 -0
- package/dist/bun.js +6586 -0
- package/dist/bun.js.map +1 -0
- package/dist/bun.mjs +6532 -0
- package/dist/bun.mjs.map +1 -0
- package/dist/bunSqlite-BMTseLIz.d.ts +18 -0
- package/dist/bunSqlite-D6AreVE2.d.mts +18 -0
- package/dist/client/index.d.mts +63 -41
- package/dist/client/index.d.ts +63 -41
- package/dist/client/index.js +1223 -1658
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +1223 -1659
- package/dist/client/index.mjs.map +1 -1
- package/dist/discovery/index.d.mts +67 -3
- package/dist/discovery/index.d.ts +67 -3
- package/dist/discovery/index.js +242 -79
- package/dist/discovery/index.js.map +1 -1
- package/dist/discovery/index.mjs +242 -79
- package/dist/discovery/index.mjs.map +1 -1
- package/dist/index.d.mts +5 -4
- package/dist/index.d.ts +5 -4
- package/dist/index.js +1975 -2004
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1973 -2001
- package/dist/index.mjs.map +1 -1
- package/dist/node.d.mts +22 -0
- package/dist/node.d.ts +22 -0
- package/dist/node.js +6651 -0
- package/dist/node.js.map +1 -0
- package/dist/node.mjs +6599 -0
- package/dist/node.mjs.map +1 -0
- package/dist/storage/bun.d.mts +16 -0
- package/dist/storage/bun.d.ts +16 -0
- package/dist/storage/bun.js +1801 -0
- package/dist/storage/bun.js.map +1 -0
- package/dist/storage/bun.mjs +1777 -0
- package/dist/storage/bun.mjs.map +1 -0
- package/dist/storage/index.d.mts +30 -30
- package/dist/storage/index.d.ts +30 -30
- package/dist/storage/index.js +393 -625
- package/dist/storage/index.js.map +1 -1
- package/dist/storage/index.mjs +392 -622
- package/dist/storage/index.mjs.map +1 -1
- package/dist/storage/node.d.mts +22 -0
- package/dist/storage/node.d.ts +22 -0
- package/dist/storage/node.js +1864 -0
- package/dist/storage/node.js.map +1 -0
- package/dist/storage/node.mjs +1842 -0
- package/dist/storage/node.mjs.map +1 -0
- package/dist/{store-C6dfj1cc.d.mts → store-BiuM2V9N.d.mts} +14 -0
- package/dist/{store-58VcEUoA.d.ts → store-C8MZlfuz.d.ts} +14 -0
- package/dist/wallet/index.d.mts +4 -0
- package/dist/wallet/index.d.ts +4 -0
- package/dist/wallet/index.js +11 -4
- package/dist/wallet/index.js.map +1 -1
- package/dist/wallet/index.mjs +11 -4
- package/dist/wallet/index.mjs.map +1 -1
- package/package.json +28 -2
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { D as DiscoveryAdapter } from '../interfaces-Cv1k2EUK.mjs';
|
|
2
2
|
import { S as SdkLogger, e as Model, k as ProviderInfo } from '../types-_21yYFZG.mjs';
|
|
3
|
+
import { IEventDatabase, EventStore } from 'applesauce-core';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* ModelManager class for discovering, fetching, and managing models from providers
|
|
@@ -7,6 +8,18 @@ import { S as SdkLogger, e as Model, k as ProviderInfo } from '../types-_21yYFZG
|
|
|
7
8
|
* (lowest cost) across multiple providers
|
|
8
9
|
*/
|
|
9
10
|
|
|
11
|
+
type SqliteStatement = {
|
|
12
|
+
run?: (...params: any[]) => unknown;
|
|
13
|
+
get?: (...params: any[]) => any;
|
|
14
|
+
};
|
|
15
|
+
type PersistentEventDatabase = IEventDatabase & {
|
|
16
|
+
db?: {
|
|
17
|
+
exec: (sql: string) => void;
|
|
18
|
+
prepare: (sql: string) => SqliteStatement;
|
|
19
|
+
};
|
|
20
|
+
close?: () => void;
|
|
21
|
+
};
|
|
22
|
+
type PersistentEventDatabaseFactory = (dbPath: string) => Promise<PersistentEventDatabase> | PersistentEventDatabase;
|
|
10
23
|
/**
|
|
11
24
|
* Configuration for ModelManager
|
|
12
25
|
*/
|
|
@@ -17,12 +30,28 @@ interface ModelManagerConfig {
|
|
|
17
30
|
includeProviderUrls?: string[];
|
|
18
31
|
/** Provider base URLs to exclude */
|
|
19
32
|
excludeProviderUrls?: string[];
|
|
20
|
-
/** Cache TTL in milliseconds (default:
|
|
33
|
+
/** Cache TTL in milliseconds (default: 210 minutes) */
|
|
21
34
|
cacheTTL?: number;
|
|
22
35
|
/** Nostr pubkey for routstr review/model events (kind 38425/38423). Defaults to routstr's key. */
|
|
23
36
|
routstrPubkey?: string;
|
|
37
|
+
/** Nostr relay URLs for provider/model discovery.
|
|
38
|
+
* When set, these relays are used for all Nostr queries (kinds 38421, 38423, 38425).
|
|
39
|
+
* When unset, each method uses its own default relay set. */
|
|
40
|
+
nostrRelays?: string[];
|
|
24
41
|
/** Optional injectable logger */
|
|
25
42
|
logger?: SdkLogger;
|
|
43
|
+
/** Path to database for persistent Nostr event storage.
|
|
44
|
+
* If provided, events fetched by ModelManager from relays (kinds 38421,
|
|
45
|
+
* 38423, 38425) are persisted and survive process restarts. The underlying
|
|
46
|
+
* EventStore can also be accessed for advanced/manual event management.
|
|
47
|
+
*
|
|
48
|
+
* Runtime-specific SQLite implementations are intentionally not imported by
|
|
49
|
+
* the browser-safe default SDK entrypoint. Use @routstr/sdk/node or
|
|
50
|
+
* @routstr/sdk/bun to get a ModelManager preconfigured with a SQLite-backed
|
|
51
|
+
* persistentEventDatabaseFactory, or inject your own factory here. */
|
|
52
|
+
eventStoreDbPath?: string;
|
|
53
|
+
/** Factory used with eventStoreDbPath to create the persistent event DB. */
|
|
54
|
+
persistentEventDatabaseFactory?: PersistentEventDatabaseFactory;
|
|
26
55
|
}
|
|
27
56
|
/**
|
|
28
57
|
* ModelManager handles all model discovery and caching logic
|
|
@@ -35,14 +64,44 @@ declare class ModelManager {
|
|
|
35
64
|
private readonly includeProviderUrls;
|
|
36
65
|
private readonly excludeProviderUrls;
|
|
37
66
|
private readonly routstrPubkey;
|
|
67
|
+
private readonly nostrRelays;
|
|
38
68
|
private readonly logger;
|
|
39
69
|
private providerNodePubkeysByUrl;
|
|
70
|
+
/** Persistent event store for relay-fetched events (null if not configured/initialized) */
|
|
71
|
+
private eventStore;
|
|
72
|
+
private eventStoreDb;
|
|
73
|
+
private eventStoreInitPromise;
|
|
74
|
+
private readonly eventStoreDbPath?;
|
|
75
|
+
private readonly persistentEventDatabaseFactory?;
|
|
40
76
|
constructor(adapter: DiscoveryAdapter, config?: ModelManagerConfig);
|
|
41
77
|
/**
|
|
42
78
|
* Get the list of bootstrapped provider base URLs
|
|
43
79
|
* @returns Array of provider base URLs
|
|
44
80
|
*/
|
|
45
81
|
getBaseUrls(): string[];
|
|
82
|
+
/**
|
|
83
|
+
* Lazily initialize the persistent event store.
|
|
84
|
+
* Returns null if no eventStoreDbPath was provided.
|
|
85
|
+
*/
|
|
86
|
+
private ensureEventStore;
|
|
87
|
+
/**
|
|
88
|
+
* Get the persistent event store, initializing it if configured.
|
|
89
|
+
* Returns null if no eventStoreDbPath was provided.
|
|
90
|
+
*/
|
|
91
|
+
getEventStore(): Promise<EventStore | null>;
|
|
92
|
+
private createPersistentEventDatabase;
|
|
93
|
+
/** Close the persistent event store database handle, if configured. */
|
|
94
|
+
closeEventStore(): void;
|
|
95
|
+
private initializeEventStoreMetadata;
|
|
96
|
+
private markEventFetched;
|
|
97
|
+
private getEventFetchedAt;
|
|
98
|
+
/**
|
|
99
|
+
* Check the persistent event store for fresh cached events.
|
|
100
|
+
* Returns events from SQLite if they were fetched within `maxAge`, otherwise
|
|
101
|
+
* returns empty array (caller should hit relays). Events without local fetch
|
|
102
|
+
* metadata fall back to Nostr created_at for backwards compatibility.
|
|
103
|
+
*/
|
|
104
|
+
private getCachedNostrEvents;
|
|
46
105
|
static init(adapter: DiscoveryAdapter, config?: ModelManagerConfig, options?: {
|
|
47
106
|
torMode?: boolean;
|
|
48
107
|
forceRefresh?: boolean;
|
|
@@ -57,7 +116,12 @@ declare class ModelManager {
|
|
|
57
116
|
*/
|
|
58
117
|
bootstrapProviders(torMode?: boolean, forceRefresh?: boolean): Promise<string[]>;
|
|
59
118
|
/**
|
|
60
|
-
*
|
|
119
|
+
* Resolve Nostr relay URLs for a given use case.
|
|
120
|
+
* Returns user-configured relays if set, otherwise the provided defaults.
|
|
121
|
+
*/
|
|
122
|
+
private getNostrRelays;
|
|
123
|
+
/**
|
|
124
|
+
* Bootstrap providers from Nostr network (kind 38421)
|
|
61
125
|
* @param kind The Nostr kind to fetch
|
|
62
126
|
* @param torMode Whether running in Tor context
|
|
63
127
|
* @returns Array of provider base URLs
|
|
@@ -81,7 +145,7 @@ declare class ModelManager {
|
|
|
81
145
|
* @param baseUrls Current provider base URLs to evaluate
|
|
82
146
|
* @returns Array of provider base URLs disabled by the review set
|
|
83
147
|
*/
|
|
84
|
-
syncReviewedProvidersFromNostr(baseUrls?: string[], providerNodes?: Map<string, Set<string
|
|
148
|
+
syncReviewedProvidersFromNostr(baseUrls?: string[], providerNodes?: Map<string, Set<string>>, forceRefresh?: boolean): Promise<string[]>;
|
|
85
149
|
private addProviderNode;
|
|
86
150
|
/**
|
|
87
151
|
* Fetch models from all providers and select best-priced options
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { D as DiscoveryAdapter } from '../interfaces-iL7CWeG5.js';
|
|
2
2
|
import { S as SdkLogger, e as Model, k as ProviderInfo } from '../types-_21yYFZG.js';
|
|
3
|
+
import { IEventDatabase, EventStore } from 'applesauce-core';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* ModelManager class for discovering, fetching, and managing models from providers
|
|
@@ -7,6 +8,18 @@ import { S as SdkLogger, e as Model, k as ProviderInfo } from '../types-_21yYFZG
|
|
|
7
8
|
* (lowest cost) across multiple providers
|
|
8
9
|
*/
|
|
9
10
|
|
|
11
|
+
type SqliteStatement = {
|
|
12
|
+
run?: (...params: any[]) => unknown;
|
|
13
|
+
get?: (...params: any[]) => any;
|
|
14
|
+
};
|
|
15
|
+
type PersistentEventDatabase = IEventDatabase & {
|
|
16
|
+
db?: {
|
|
17
|
+
exec: (sql: string) => void;
|
|
18
|
+
prepare: (sql: string) => SqliteStatement;
|
|
19
|
+
};
|
|
20
|
+
close?: () => void;
|
|
21
|
+
};
|
|
22
|
+
type PersistentEventDatabaseFactory = (dbPath: string) => Promise<PersistentEventDatabase> | PersistentEventDatabase;
|
|
10
23
|
/**
|
|
11
24
|
* Configuration for ModelManager
|
|
12
25
|
*/
|
|
@@ -17,12 +30,28 @@ interface ModelManagerConfig {
|
|
|
17
30
|
includeProviderUrls?: string[];
|
|
18
31
|
/** Provider base URLs to exclude */
|
|
19
32
|
excludeProviderUrls?: string[];
|
|
20
|
-
/** Cache TTL in milliseconds (default:
|
|
33
|
+
/** Cache TTL in milliseconds (default: 210 minutes) */
|
|
21
34
|
cacheTTL?: number;
|
|
22
35
|
/** Nostr pubkey for routstr review/model events (kind 38425/38423). Defaults to routstr's key. */
|
|
23
36
|
routstrPubkey?: string;
|
|
37
|
+
/** Nostr relay URLs for provider/model discovery.
|
|
38
|
+
* When set, these relays are used for all Nostr queries (kinds 38421, 38423, 38425).
|
|
39
|
+
* When unset, each method uses its own default relay set. */
|
|
40
|
+
nostrRelays?: string[];
|
|
24
41
|
/** Optional injectable logger */
|
|
25
42
|
logger?: SdkLogger;
|
|
43
|
+
/** Path to database for persistent Nostr event storage.
|
|
44
|
+
* If provided, events fetched by ModelManager from relays (kinds 38421,
|
|
45
|
+
* 38423, 38425) are persisted and survive process restarts. The underlying
|
|
46
|
+
* EventStore can also be accessed for advanced/manual event management.
|
|
47
|
+
*
|
|
48
|
+
* Runtime-specific SQLite implementations are intentionally not imported by
|
|
49
|
+
* the browser-safe default SDK entrypoint. Use @routstr/sdk/node or
|
|
50
|
+
* @routstr/sdk/bun to get a ModelManager preconfigured with a SQLite-backed
|
|
51
|
+
* persistentEventDatabaseFactory, or inject your own factory here. */
|
|
52
|
+
eventStoreDbPath?: string;
|
|
53
|
+
/** Factory used with eventStoreDbPath to create the persistent event DB. */
|
|
54
|
+
persistentEventDatabaseFactory?: PersistentEventDatabaseFactory;
|
|
26
55
|
}
|
|
27
56
|
/**
|
|
28
57
|
* ModelManager handles all model discovery and caching logic
|
|
@@ -35,14 +64,44 @@ declare class ModelManager {
|
|
|
35
64
|
private readonly includeProviderUrls;
|
|
36
65
|
private readonly excludeProviderUrls;
|
|
37
66
|
private readonly routstrPubkey;
|
|
67
|
+
private readonly nostrRelays;
|
|
38
68
|
private readonly logger;
|
|
39
69
|
private providerNodePubkeysByUrl;
|
|
70
|
+
/** Persistent event store for relay-fetched events (null if not configured/initialized) */
|
|
71
|
+
private eventStore;
|
|
72
|
+
private eventStoreDb;
|
|
73
|
+
private eventStoreInitPromise;
|
|
74
|
+
private readonly eventStoreDbPath?;
|
|
75
|
+
private readonly persistentEventDatabaseFactory?;
|
|
40
76
|
constructor(adapter: DiscoveryAdapter, config?: ModelManagerConfig);
|
|
41
77
|
/**
|
|
42
78
|
* Get the list of bootstrapped provider base URLs
|
|
43
79
|
* @returns Array of provider base URLs
|
|
44
80
|
*/
|
|
45
81
|
getBaseUrls(): string[];
|
|
82
|
+
/**
|
|
83
|
+
* Lazily initialize the persistent event store.
|
|
84
|
+
* Returns null if no eventStoreDbPath was provided.
|
|
85
|
+
*/
|
|
86
|
+
private ensureEventStore;
|
|
87
|
+
/**
|
|
88
|
+
* Get the persistent event store, initializing it if configured.
|
|
89
|
+
* Returns null if no eventStoreDbPath was provided.
|
|
90
|
+
*/
|
|
91
|
+
getEventStore(): Promise<EventStore | null>;
|
|
92
|
+
private createPersistentEventDatabase;
|
|
93
|
+
/** Close the persistent event store database handle, if configured. */
|
|
94
|
+
closeEventStore(): void;
|
|
95
|
+
private initializeEventStoreMetadata;
|
|
96
|
+
private markEventFetched;
|
|
97
|
+
private getEventFetchedAt;
|
|
98
|
+
/**
|
|
99
|
+
* Check the persistent event store for fresh cached events.
|
|
100
|
+
* Returns events from SQLite if they were fetched within `maxAge`, otherwise
|
|
101
|
+
* returns empty array (caller should hit relays). Events without local fetch
|
|
102
|
+
* metadata fall back to Nostr created_at for backwards compatibility.
|
|
103
|
+
*/
|
|
104
|
+
private getCachedNostrEvents;
|
|
46
105
|
static init(adapter: DiscoveryAdapter, config?: ModelManagerConfig, options?: {
|
|
47
106
|
torMode?: boolean;
|
|
48
107
|
forceRefresh?: boolean;
|
|
@@ -57,7 +116,12 @@ declare class ModelManager {
|
|
|
57
116
|
*/
|
|
58
117
|
bootstrapProviders(torMode?: boolean, forceRefresh?: boolean): Promise<string[]>;
|
|
59
118
|
/**
|
|
60
|
-
*
|
|
119
|
+
* Resolve Nostr relay URLs for a given use case.
|
|
120
|
+
* Returns user-configured relays if set, otherwise the provided defaults.
|
|
121
|
+
*/
|
|
122
|
+
private getNostrRelays;
|
|
123
|
+
/**
|
|
124
|
+
* Bootstrap providers from Nostr network (kind 38421)
|
|
61
125
|
* @param kind The Nostr kind to fetch
|
|
62
126
|
* @param torMode Whether running in Tor context
|
|
63
127
|
* @returns Array of provider base URLs
|
|
@@ -81,7 +145,7 @@ declare class ModelManager {
|
|
|
81
145
|
* @param baseUrls Current provider base URLs to evaluate
|
|
82
146
|
* @returns Array of provider base URLs disabled by the review set
|
|
83
147
|
*/
|
|
84
|
-
syncReviewedProvidersFromNostr(baseUrls?: string[], providerNodes?: Map<string, Set<string
|
|
148
|
+
syncReviewedProvidersFromNostr(baseUrls?: string[], providerNodes?: Map<string, Set<string>>, forceRefresh?: boolean): Promise<string[]>;
|
|
85
149
|
private addProviderNode;
|
|
86
150
|
/**
|
|
87
151
|
* Fetch models from all providers and select best-priced options
|
package/dist/discovery/index.js
CHANGED
|
@@ -42,7 +42,10 @@ var ModelManager = class _ModelManager {
|
|
|
42
42
|
this.includeProviderUrls = config.includeProviderUrls || [];
|
|
43
43
|
this.excludeProviderUrls = config.excludeProviderUrls || [];
|
|
44
44
|
this.routstrPubkey = config.routstrPubkey || "4ad6fa2d16e2a9b576c863b4cf7404a70d4dc320c0c447d10ad6ff58993eacc8";
|
|
45
|
+
this.nostrRelays = config.nostrRelays;
|
|
45
46
|
this.logger = (config.logger ?? consoleLogger).child("ModelManager");
|
|
47
|
+
this.eventStoreDbPath = config.eventStoreDbPath;
|
|
48
|
+
this.persistentEventDatabaseFactory = config.persistentEventDatabaseFactory;
|
|
46
49
|
}
|
|
47
50
|
adapter;
|
|
48
51
|
cacheTTL;
|
|
@@ -50,8 +53,15 @@ var ModelManager = class _ModelManager {
|
|
|
50
53
|
includeProviderUrls;
|
|
51
54
|
excludeProviderUrls;
|
|
52
55
|
routstrPubkey;
|
|
56
|
+
nostrRelays;
|
|
53
57
|
logger;
|
|
54
58
|
providerNodePubkeysByUrl = /* @__PURE__ */ new Map();
|
|
59
|
+
/** Persistent event store for relay-fetched events (null if not configured/initialized) */
|
|
60
|
+
eventStore = null;
|
|
61
|
+
eventStoreDb = null;
|
|
62
|
+
eventStoreInitPromise = null;
|
|
63
|
+
eventStoreDbPath;
|
|
64
|
+
persistentEventDatabaseFactory;
|
|
55
65
|
/**
|
|
56
66
|
* Get the list of bootstrapped provider base URLs
|
|
57
67
|
* @returns Array of provider base URLs
|
|
@@ -59,6 +69,103 @@ var ModelManager = class _ModelManager {
|
|
|
59
69
|
getBaseUrls() {
|
|
60
70
|
return this.adapter.getBaseUrlsList();
|
|
61
71
|
}
|
|
72
|
+
/**
|
|
73
|
+
* Lazily initialize the persistent event store.
|
|
74
|
+
* Returns null if no eventStoreDbPath was provided.
|
|
75
|
+
*/
|
|
76
|
+
async ensureEventStore() {
|
|
77
|
+
if (!this.eventStoreDbPath) return null;
|
|
78
|
+
if (this.eventStore) return this.eventStore;
|
|
79
|
+
if (!this.eventStoreInitPromise) {
|
|
80
|
+
this.eventStoreInitPromise = (async () => {
|
|
81
|
+
try {
|
|
82
|
+
const db = await this.createPersistentEventDatabase();
|
|
83
|
+
this.eventStoreDb = db;
|
|
84
|
+
this.eventStore = new applesauceCore.EventStore({ database: db });
|
|
85
|
+
this.initializeEventStoreMetadata();
|
|
86
|
+
this.logger.log(
|
|
87
|
+
`Persistent event store initialized at ${this.eventStoreDbPath}`
|
|
88
|
+
);
|
|
89
|
+
return this.eventStore;
|
|
90
|
+
} catch (error) {
|
|
91
|
+
this.eventStoreInitPromise = null;
|
|
92
|
+
throw new Error(
|
|
93
|
+
`Persistent Nostr event storage requires a runtime-specific database factory. Use @routstr/sdk/node, @routstr/sdk/bun, inject persistentEventDatabaseFactory, or omit eventStoreDbPath. (${error})`
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
})();
|
|
97
|
+
}
|
|
98
|
+
return this.eventStoreInitPromise;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Get the persistent event store, initializing it if configured.
|
|
102
|
+
* Returns null if no eventStoreDbPath was provided.
|
|
103
|
+
*/
|
|
104
|
+
async getEventStore() {
|
|
105
|
+
return this.ensureEventStore();
|
|
106
|
+
}
|
|
107
|
+
async createPersistentEventDatabase() {
|
|
108
|
+
if (!this.eventStoreDbPath) {
|
|
109
|
+
throw new Error("eventStoreDbPath is required");
|
|
110
|
+
}
|
|
111
|
+
if (!this.persistentEventDatabaseFactory) {
|
|
112
|
+
throw new Error(
|
|
113
|
+
"persistentEventDatabaseFactory is required. Import ModelManager from @routstr/sdk/node or @routstr/sdk/bun for SQLite-backed persistent event storage."
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
return this.persistentEventDatabaseFactory(this.eventStoreDbPath);
|
|
117
|
+
}
|
|
118
|
+
/** Close the persistent event store database handle, if configured. */
|
|
119
|
+
closeEventStore() {
|
|
120
|
+
this.eventStoreDb?.close?.();
|
|
121
|
+
this.eventStore = null;
|
|
122
|
+
this.eventStoreDb = null;
|
|
123
|
+
this.eventStoreInitPromise = null;
|
|
124
|
+
}
|
|
125
|
+
initializeEventStoreMetadata() {
|
|
126
|
+
this.eventStoreDb?.db?.exec(
|
|
127
|
+
`CREATE TABLE IF NOT EXISTS routstr_event_cache_metadata (
|
|
128
|
+
event_id TEXT PRIMARY KEY,
|
|
129
|
+
fetched_at INTEGER NOT NULL
|
|
130
|
+
)`
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
markEventFetched(event, fetchedAt = Date.now()) {
|
|
134
|
+
const db = this.eventStoreDb?.db;
|
|
135
|
+
if (!db) return;
|
|
136
|
+
db.prepare(
|
|
137
|
+
`INSERT INTO routstr_event_cache_metadata (event_id, fetched_at)
|
|
138
|
+
VALUES (?, ?)
|
|
139
|
+
ON CONFLICT(event_id) DO UPDATE SET fetched_at = excluded.fetched_at`
|
|
140
|
+
).run?.(event.id, fetchedAt);
|
|
141
|
+
}
|
|
142
|
+
getEventFetchedAt(event) {
|
|
143
|
+
const db = this.eventStoreDb?.db;
|
|
144
|
+
if (!db) return void 0;
|
|
145
|
+
const row = db.prepare(
|
|
146
|
+
`SELECT fetched_at FROM routstr_event_cache_metadata WHERE event_id = ?`
|
|
147
|
+
).get?.(event.id);
|
|
148
|
+
return typeof row?.fetched_at === "number" ? row.fetched_at : void 0;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Check the persistent event store for fresh cached events.
|
|
152
|
+
* Returns events from SQLite if they were fetched within `maxAge`, otherwise
|
|
153
|
+
* returns empty array (caller should hit relays). Events without local fetch
|
|
154
|
+
* metadata fall back to Nostr created_at for backwards compatibility.
|
|
155
|
+
*/
|
|
156
|
+
async getCachedNostrEvents(filter, maxAge, forceRefresh = false) {
|
|
157
|
+
const eventStore = await this.ensureEventStore();
|
|
158
|
+
if (forceRefresh) return [];
|
|
159
|
+
if (!eventStore) return [];
|
|
160
|
+
const timeline = eventStore.getTimeline(filter);
|
|
161
|
+
if (timeline.length === 0) return [];
|
|
162
|
+
const cutoff = Date.now() - maxAge;
|
|
163
|
+
const freshest = Math.max(
|
|
164
|
+
...timeline.map((e) => this.getEventFetchedAt(e) ?? e.created_at * 1e3)
|
|
165
|
+
);
|
|
166
|
+
if (freshest < cutoff) return [];
|
|
167
|
+
return timeline;
|
|
168
|
+
}
|
|
62
169
|
static async init(adapter, config = {}, options = {}) {
|
|
63
170
|
const manager = new _ModelManager(adapter, config);
|
|
64
171
|
const torMode = options.torMode ?? false;
|
|
@@ -87,19 +194,31 @@ var ModelManager = class _ModelManager {
|
|
|
87
194
|
torMode
|
|
88
195
|
);
|
|
89
196
|
await this.fetchRoutstr21Models(forceRefresh);
|
|
90
|
-
await this.syncReviewedProvidersFromNostr(
|
|
197
|
+
await this.syncReviewedProvidersFromNostr(
|
|
198
|
+
filteredCachedUrls,
|
|
199
|
+
this.providerNodePubkeysByUrl,
|
|
200
|
+
forceRefresh
|
|
201
|
+
);
|
|
91
202
|
return filteredCachedUrls;
|
|
92
203
|
}
|
|
93
204
|
}
|
|
94
205
|
}
|
|
95
206
|
try {
|
|
96
|
-
const nostrProviders = await this.bootstrapFromNostr(
|
|
207
|
+
const nostrProviders = await this.bootstrapFromNostr(
|
|
208
|
+
38421,
|
|
209
|
+
torMode,
|
|
210
|
+
forceRefresh
|
|
211
|
+
);
|
|
97
212
|
if (nostrProviders.length > 0) {
|
|
98
213
|
const filtered = this.filterBaseUrlsForTor(nostrProviders, torMode);
|
|
99
214
|
this.adapter.setBaseUrlsList(filtered);
|
|
100
215
|
this.adapter.setBaseUrlsLastUpdate(Date.now());
|
|
101
216
|
await this.fetchRoutstr21Models(forceRefresh);
|
|
102
|
-
await this.syncReviewedProvidersFromNostr(
|
|
217
|
+
await this.syncReviewedProvidersFromNostr(
|
|
218
|
+
filtered,
|
|
219
|
+
this.providerNodePubkeysByUrl,
|
|
220
|
+
forceRefresh
|
|
221
|
+
);
|
|
103
222
|
return filtered;
|
|
104
223
|
}
|
|
105
224
|
} catch (e) {
|
|
@@ -108,42 +227,59 @@ var ModelManager = class _ModelManager {
|
|
|
108
227
|
return this.bootstrapFromHttp(torMode, forceRefresh);
|
|
109
228
|
}
|
|
110
229
|
/**
|
|
111
|
-
*
|
|
230
|
+
* Resolve Nostr relay URLs for a given use case.
|
|
231
|
+
* Returns user-configured relays if set, otherwise the provided defaults.
|
|
232
|
+
*/
|
|
233
|
+
getNostrRelays(defaults) {
|
|
234
|
+
return this.nostrRelays && this.nostrRelays.length > 0 ? this.nostrRelays : defaults;
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Bootstrap providers from Nostr network (kind 38421)
|
|
112
238
|
* @param kind The Nostr kind to fetch
|
|
113
239
|
* @param torMode Whether running in Tor context
|
|
114
240
|
* @returns Array of provider base URLs
|
|
115
241
|
*/
|
|
116
|
-
async bootstrapFromNostr(kind, torMode) {
|
|
117
|
-
const
|
|
242
|
+
async bootstrapFromNostr(kind, torMode, forceRefresh = false) {
|
|
243
|
+
const relays = this.getNostrRelays([
|
|
118
244
|
"wss://relay.primal.net",
|
|
119
245
|
"wss://nos.lol",
|
|
120
246
|
"wss://relay.damus.io"
|
|
121
|
-
];
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
247
|
+
]);
|
|
248
|
+
const cached = await this.getCachedNostrEvents(
|
|
249
|
+
{ kinds: [kind] },
|
|
250
|
+
this.cacheTTL,
|
|
251
|
+
forceRefresh
|
|
252
|
+
);
|
|
253
|
+
let sessionEvents = cached;
|
|
254
|
+
if (cached.length === 0) {
|
|
255
|
+
const pool = new applesauceRelay.RelayPool();
|
|
256
|
+
const timeoutMs = 5e3;
|
|
257
|
+
await new Promise((resolve) => {
|
|
258
|
+
pool.req(relays, {
|
|
259
|
+
kinds: [kind],
|
|
260
|
+
limit: 100
|
|
261
|
+
}).pipe(
|
|
262
|
+
applesauceRelay.onlyEvents(),
|
|
263
|
+
rxjs.tap((event) => {
|
|
264
|
+
sessionEvents.push(event);
|
|
265
|
+
this.eventStore?.add(event);
|
|
266
|
+
this.markEventFetched(event);
|
|
267
|
+
})
|
|
268
|
+
).subscribe({
|
|
269
|
+
complete: () => {
|
|
270
|
+
resolve();
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
setTimeout(() => {
|
|
136
274
|
resolve();
|
|
137
|
-
}
|
|
275
|
+
}, timeoutMs);
|
|
138
276
|
});
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
});
|
|
143
|
-
const timeline = localEventStore.getTimeline({ kinds: [kind] });
|
|
277
|
+
} else {
|
|
278
|
+
this.logger.log(`Using ${cached.length} cached kind ${kind} events from persistent store`);
|
|
279
|
+
}
|
|
144
280
|
const bases = /* @__PURE__ */ new Set();
|
|
145
281
|
this.providerNodePubkeysByUrl = /* @__PURE__ */ new Map();
|
|
146
|
-
for (const event of
|
|
282
|
+
for (const event of sessionEvents) {
|
|
147
283
|
const eventUrls = [];
|
|
148
284
|
for (const tag of event.tags) {
|
|
149
285
|
if (tag[0] === "u" && typeof tag[1] === "string") {
|
|
@@ -251,7 +387,11 @@ var ModelManager = class _ModelManager {
|
|
|
251
387
|
this.adapter.setBaseUrlsList(list);
|
|
252
388
|
this.adapter.setBaseUrlsLastUpdate(Date.now());
|
|
253
389
|
await this.fetchRoutstr21Models(forceRefresh);
|
|
254
|
-
await this.syncReviewedProvidersFromNostr(
|
|
390
|
+
await this.syncReviewedProvidersFromNostr(
|
|
391
|
+
list,
|
|
392
|
+
this.providerNodePubkeysByUrl,
|
|
393
|
+
forceRefresh
|
|
394
|
+
);
|
|
255
395
|
}
|
|
256
396
|
return list;
|
|
257
397
|
} catch (e) {
|
|
@@ -270,7 +410,7 @@ var ModelManager = class _ModelManager {
|
|
|
270
410
|
* @param baseUrls Current provider base URLs to evaluate
|
|
271
411
|
* @returns Array of provider base URLs disabled by the review set
|
|
272
412
|
*/
|
|
273
|
-
async syncReviewedProvidersFromNostr(baseUrls = this.adapter.getBaseUrlsList(), providerNodes = this.providerNodePubkeysByUrl) {
|
|
413
|
+
async syncReviewedProvidersFromNostr(baseUrls = this.adapter.getBaseUrlsList(), providerNodes = this.providerNodePubkeysByUrl, forceRefresh = false) {
|
|
274
414
|
if (baseUrls.length === 0) return [];
|
|
275
415
|
if (!this.adapter.setDisabledProviders) {
|
|
276
416
|
this.logger.warn(
|
|
@@ -278,30 +418,43 @@ var ModelManager = class _ModelManager {
|
|
|
278
418
|
);
|
|
279
419
|
return [];
|
|
280
420
|
}
|
|
281
|
-
const LGTM_RELAYS = [
|
|
282
|
-
"wss://relay.primal.net",
|
|
283
|
-
"wss://nos.lol",
|
|
284
|
-
"wss://relay.damus.io",
|
|
285
|
-
"wss://relay.routstr.com"
|
|
286
|
-
];
|
|
287
421
|
const reviewedNodePubkeys = /* @__PURE__ */ new Set();
|
|
288
422
|
{
|
|
289
|
-
const
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
)
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
423
|
+
const cached = await this.getCachedNostrEvents(
|
|
424
|
+
{ kinds: [38425], "#t": ["lgtm"], authors: [this.routstrPubkey] },
|
|
425
|
+
this.cacheTTL,
|
|
426
|
+
forceRefresh
|
|
427
|
+
);
|
|
428
|
+
let sessionEvents = cached;
|
|
429
|
+
if (cached.length === 0) {
|
|
430
|
+
const lgtmRelays = this.getNostrRelays([
|
|
431
|
+
"wss://relay.primal.net",
|
|
432
|
+
"wss://nos.lol",
|
|
433
|
+
"wss://relay.damus.io",
|
|
434
|
+
"wss://relay.routstr.com"
|
|
435
|
+
]);
|
|
436
|
+
const pool = new applesauceRelay.RelayPool();
|
|
437
|
+
const timeoutMs = 5e3;
|
|
438
|
+
await new Promise((resolve) => {
|
|
439
|
+
pool.req(lgtmRelays, {
|
|
440
|
+
kinds: [38425],
|
|
441
|
+
"#t": ["lgtm"],
|
|
442
|
+
limit: 500,
|
|
443
|
+
authors: [this.routstrPubkey]
|
|
444
|
+
}).pipe(
|
|
445
|
+
applesauceRelay.onlyEvents(),
|
|
446
|
+
rxjs.tap((event) => {
|
|
447
|
+
sessionEvents.push(event);
|
|
448
|
+
this.eventStore?.add(event);
|
|
449
|
+
this.markEventFetched(event);
|
|
450
|
+
})
|
|
451
|
+
).subscribe({ complete: () => resolve() });
|
|
452
|
+
setTimeout(() => resolve(), timeoutMs);
|
|
453
|
+
});
|
|
454
|
+
} else {
|
|
455
|
+
this.logger.log(`Using ${cached.length} cached kind 38425 events from persistent store`);
|
|
456
|
+
}
|
|
457
|
+
for (const event of sessionEvents) {
|
|
305
458
|
const hasLgtmTag = event.tags.some(
|
|
306
459
|
(tag) => tag[0] === "t" && tag[1]?.toLowerCase() === "lgtm"
|
|
307
460
|
);
|
|
@@ -410,7 +563,7 @@ var ModelManager = class _ModelManager {
|
|
|
410
563
|
if (this.isProviderDownError(error)) {
|
|
411
564
|
this.logger.warn(`Provider ${base} is down right now.`);
|
|
412
565
|
} else {
|
|
413
|
-
this.logger.warn(`
|
|
566
|
+
this.logger.warn(`Provider ${base} unreachable: ${error.message}`);
|
|
414
567
|
}
|
|
415
568
|
this.adapter.setProviderLastUpdate(base, Date.now());
|
|
416
569
|
return { success: false, base };
|
|
@@ -525,39 +678,49 @@ var ModelManager = class _ModelManager {
|
|
|
525
678
|
return cachedModels;
|
|
526
679
|
}
|
|
527
680
|
}
|
|
528
|
-
const
|
|
681
|
+
const relays = this.getNostrRelays([
|
|
529
682
|
"wss://relay.damus.io",
|
|
530
683
|
"wss://nos.lol",
|
|
531
684
|
"wss://relay.routstr.com"
|
|
532
|
-
];
|
|
533
|
-
const
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
685
|
+
]);
|
|
686
|
+
const cached = await this.getCachedNostrEvents(
|
|
687
|
+
{ kinds: [38423], "#d": ["routstr-21-models"], authors: [this.routstrPubkey] },
|
|
688
|
+
this.cacheTTL,
|
|
689
|
+
forceRefresh
|
|
690
|
+
);
|
|
691
|
+
let sessionEvents = cached;
|
|
692
|
+
if (cached.length === 0) {
|
|
693
|
+
const pool = new applesauceRelay.RelayPool();
|
|
694
|
+
const timeoutMs = 5e3;
|
|
695
|
+
await new Promise((resolve) => {
|
|
696
|
+
pool.req(relays, {
|
|
697
|
+
kinds: [38423],
|
|
698
|
+
"#d": ["routstr-21-models"],
|
|
699
|
+
limit: 1,
|
|
700
|
+
authors: [this.routstrPubkey]
|
|
701
|
+
}).pipe(
|
|
702
|
+
applesauceRelay.onlyEvents(),
|
|
703
|
+
rxjs.tap((event2) => {
|
|
704
|
+
sessionEvents.push(event2);
|
|
705
|
+
this.eventStore?.add(event2);
|
|
706
|
+
this.markEventFetched(event2);
|
|
707
|
+
})
|
|
708
|
+
).subscribe({
|
|
709
|
+
complete: () => {
|
|
710
|
+
resolve();
|
|
711
|
+
}
|
|
712
|
+
});
|
|
713
|
+
setTimeout(() => {
|
|
549
714
|
resolve();
|
|
550
|
-
}
|
|
715
|
+
}, timeoutMs);
|
|
551
716
|
});
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
const timeline = localEventStore.getTimeline({ kinds: [38423] });
|
|
557
|
-
if (timeline.length === 0) {
|
|
717
|
+
} else {
|
|
718
|
+
this.logger.log(`Using ${cached.length} cached kind 38423 events from persistent store`);
|
|
719
|
+
}
|
|
720
|
+
if (sessionEvents.length === 0) {
|
|
558
721
|
return cachedModels.length > 0 ? cachedModels : [];
|
|
559
722
|
}
|
|
560
|
-
const event =
|
|
723
|
+
const event = sessionEvents[0];
|
|
561
724
|
try {
|
|
562
725
|
const content = JSON.parse(event.content);
|
|
563
726
|
const models = Array.isArray(content?.models) ? content.models : [];
|