@skein-js/runtime 0.3.0 → 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.
- package/README.md +5 -5
- package/dist/index.d.ts +23 -4
- package/dist/index.js +40 -7
- package/package.json +8 -8
package/README.md
CHANGED
|
@@ -20,8 +20,8 @@ import { createExpressServer } from "@skein-js/express";
|
|
|
20
20
|
|
|
21
21
|
const runtime = await buildRuntime({
|
|
22
22
|
configPath: "/abs/path/to/langgraph.json",
|
|
23
|
-
store: "postgres", // "memory" | "postgres" (postgres reads
|
|
24
|
-
queue: "redis", // "memory" | "redis" (redis reads
|
|
23
|
+
store: "postgres", // "memory" | "postgres" (postgres reads POSTGRES_URI)
|
|
24
|
+
queue: "redis", // "memory" | "redis" (redis reads REDIS_URI)
|
|
25
25
|
});
|
|
26
26
|
|
|
27
27
|
const server = await createExpressServer({ deps: runtime.deps, cors: runtime.cors });
|
|
@@ -33,13 +33,13 @@ await runtime.dispose();
|
|
|
33
33
|
`store` and `queue` are **required** (no defaults — the CLI supplies its own flag defaults). The
|
|
34
34
|
driver branches:
|
|
35
35
|
|
|
36
|
-
- **`store: "postgres"`** connects `PostgresSkeinStore` (from `
|
|
36
|
+
- **`store: "postgres"`** connects `PostgresSkeinStore` (from `POSTGRES_URI`), runs its migrations,
|
|
37
37
|
and uses `PostgresSaver` as the LangGraph checkpointer.
|
|
38
|
-
- **`queue: "redis"`** uses the BullMQ run queue + Redis Streams/pub-sub event bus (from `
|
|
38
|
+
- **`queue: "redis"`** uses the BullMQ run queue + Redis Streams/pub-sub event bus (from `REDIS_URI`).
|
|
39
39
|
- **`store: "memory"` + `queue: "memory"`** delegates to [`@skein-js/express`](../server-express)'s
|
|
40
40
|
reloadable in-memory runtime, so `skein dev`'s hot-reload and cross-restart state persistence work.
|
|
41
41
|
|
|
42
|
-
A missing `
|
|
42
|
+
A missing `POSTGRES_URI` / `REDIS_URI` throws `RuntimeConfigError`; if assembly fails part-way, any
|
|
43
43
|
resources already created are disposed before rethrowing, so a failed build leaks nothing.
|
|
44
44
|
|
|
45
45
|
Graph hot-reload (`reloadGraphs()`) works in every mode; `snapshotState`/`hydrateState` are present
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ProtocolDeps } from '@skein-js/agent-protocol';
|
|
2
|
-
import { ModuleImporter } from '@skein-js/config';
|
|
2
|
+
import { ModuleImporter, GraphSchemas } from '@skein-js/config';
|
|
3
3
|
import { DevStateSnapshot } from '@skein-js/express';
|
|
4
4
|
import { CorsOptions } from 'cors';
|
|
5
5
|
import { EmbedFunction } from '@skein-js/storage-postgres';
|
|
@@ -13,9 +13,14 @@ interface BuildRuntimeOptions {
|
|
|
13
13
|
configPath: string;
|
|
14
14
|
/** TS-capable importer (e.g. the CLI's vite loader). Omitted for plain JS/Node resolution. */
|
|
15
15
|
importModule?: ModuleImporter;
|
|
16
|
-
/**
|
|
16
|
+
/**
|
|
17
|
+
* Precomputed graph schemas (from `skein build`) for a pre-compiled image — forwarded to
|
|
18
|
+
* `loadConfig` so schema introspection is a map lookup, never a TypeScript parse. Omitted for dev.
|
|
19
|
+
*/
|
|
20
|
+
schemas?: Record<string, GraphSchemas>;
|
|
21
|
+
/** `"memory"` (default dev) or `"postgres"` (reads `POSTGRES_URI`). */
|
|
17
22
|
store: StoreDriver;
|
|
18
|
-
/** `"memory"` (default dev) or `"redis"` (reads `
|
|
23
|
+
/** `"memory"` (default dev) or `"redis"` (reads `REDIS_URI`). */
|
|
19
24
|
queue: QueueDriver;
|
|
20
25
|
}
|
|
21
26
|
interface SkeinRuntime {
|
|
@@ -46,7 +51,21 @@ interface ResolveEmbedOptions {
|
|
|
46
51
|
/** TS-capable importer (the CLI's vite loader) for a custom-function `.ts` path. */
|
|
47
52
|
importModule?: ModuleImporter;
|
|
48
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* Whether a `store.index.embed` value is a custom-function path (`"./embed.ts:fn"`) rather than a
|
|
56
|
+
* `"provider:model"` string — it looks like a path (starts with `.`/`/`) or names a source file.
|
|
57
|
+
* Exported so `skein build` knows which embed forms to bundle (paths) vs leave as provider strings.
|
|
58
|
+
*/
|
|
59
|
+
declare function isCustomFunctionPath(embed: string): boolean;
|
|
60
|
+
/**
|
|
61
|
+
* The npm package a `store.index.embed` value needs **installed at runtime**, or undefined. A
|
|
62
|
+
* `provider:model` embed dynamically imports `@langchain/<provider>` (see {@link resolveProviderEmbed}),
|
|
63
|
+
* but that package is never imported by graph code, so `skein build` won't discover it while bundling —
|
|
64
|
+
* it must pin this package into the production image explicitly. Custom-function paths return undefined
|
|
65
|
+
* (they are bundled), as do unknown providers.
|
|
66
|
+
*/
|
|
67
|
+
declare function embedRuntimePackage(embed: string): string | undefined;
|
|
49
68
|
/** Resolve a `store.index.embed` value to an `EmbedFunction`, honoring both LangGraph forms. */
|
|
50
69
|
declare function resolveEmbed(embed: string, options: ResolveEmbedOptions): Promise<EmbedFunction>;
|
|
51
70
|
|
|
52
|
-
export { type BuildRuntimeOptions, type QueueDriver, type ResolveEmbedOptions, RuntimeConfigError, type SkeinRuntime, type StoreDriver, buildRuntime, resolveEmbed };
|
|
71
|
+
export { type BuildRuntimeOptions, type QueueDriver, type ResolveEmbedOptions, RuntimeConfigError, type SkeinRuntime, type StoreDriver, buildRuntime, embedRuntimePackage, isCustomFunctionPath, resolveEmbed };
|
package/dist/index.js
CHANGED
|
@@ -52,6 +52,12 @@ function isCustomFunctionPath(embed) {
|
|
|
52
52
|
const head = embed.split(":", 1)[0] ?? "";
|
|
53
53
|
return head.startsWith(".") || head.startsWith("/") || /\.([mc]?[jt]s|py)$/.test(head);
|
|
54
54
|
}
|
|
55
|
+
function embedRuntimePackage(embed) {
|
|
56
|
+
if (isCustomFunctionPath(embed)) return void 0;
|
|
57
|
+
const separator = embed.indexOf(":");
|
|
58
|
+
if (separator === -1) return void 0;
|
|
59
|
+
return PROVIDERS[embed.slice(0, separator)]?.package;
|
|
60
|
+
}
|
|
55
61
|
async function resolvePathEmbed(embed, options) {
|
|
56
62
|
const { sourceFile, exportSymbol } = parseGraphSpec(embed, options.configDir);
|
|
57
63
|
const importer = options.importModule ?? ((file) => import(pathToFileURL(file).href));
|
|
@@ -145,9 +151,9 @@ async function resolveStoreIndex(index, options) {
|
|
|
145
151
|
return { dims: index.dims, fields: index.fields, embed };
|
|
146
152
|
}
|
|
147
153
|
async function buildRuntime(options) {
|
|
148
|
-
const { configPath, importModule, store, queue } = options;
|
|
154
|
+
const { configPath, importModule, store, queue, schemas } = options;
|
|
149
155
|
if (store === "memory" && queue === "memory") {
|
|
150
|
-
const runtime = await loadReloadableInMemoryRuntime(configPath, importModule);
|
|
156
|
+
const runtime = await loadReloadableInMemoryRuntime(configPath, importModule, schemas);
|
|
151
157
|
return {
|
|
152
158
|
deps: runtime.deps,
|
|
153
159
|
cors: runtime.cors,
|
|
@@ -158,16 +164,17 @@ async function buildRuntime(options) {
|
|
|
158
164
|
hydrateState: (snapshot) => runtime.hydrateState(snapshot)
|
|
159
165
|
};
|
|
160
166
|
}
|
|
161
|
-
const first = await loadConfig({ configPath, importModule });
|
|
167
|
+
const first = await loadConfig({ configPath, importModule, staticSchemas: schemas });
|
|
162
168
|
const { resolver, reroute } = reroutableGraphResolver(first.graphs);
|
|
163
169
|
const disposers = [];
|
|
164
170
|
const disposeAll = async () => {
|
|
165
171
|
await Promise.allSettled(disposers.map((dispose) => dispose()));
|
|
166
172
|
};
|
|
167
173
|
try {
|
|
174
|
+
const storeTtl = resolveStoreTtl(first.config.store?.ttl);
|
|
168
175
|
const { skeinStore, checkpointer } = await (async () => {
|
|
169
176
|
if (store === "postgres") {
|
|
170
|
-
const databaseUrl = requireEnv("
|
|
177
|
+
const databaseUrl = requireEnv("POSTGRES_URI", "postgres");
|
|
171
178
|
const index = await resolveStoreIndex(first.config.store?.index, {
|
|
172
179
|
configDir: first.configDir,
|
|
173
180
|
importModule
|
|
@@ -175,6 +182,7 @@ async function buildRuntime(options) {
|
|
|
175
182
|
const connectionOptions = postgresConnectionOptions();
|
|
176
183
|
const pgStore = await PostgresSkeinStore.connect(databaseUrl, {
|
|
177
184
|
...index ? { index } : {},
|
|
185
|
+
...storeTtl ? { ttl: storeTtl } : {},
|
|
178
186
|
...connectionOptions
|
|
179
187
|
});
|
|
180
188
|
disposers.push(() => pgStore.close());
|
|
@@ -184,11 +192,24 @@ async function buildRuntime(options) {
|
|
|
184
192
|
await saver.setup();
|
|
185
193
|
return { skeinStore: pgStore, checkpointer: saver };
|
|
186
194
|
}
|
|
187
|
-
return {
|
|
195
|
+
return {
|
|
196
|
+
skeinStore: new MemorySkeinStore(storeTtl ? { ttl: storeTtl } : void 0),
|
|
197
|
+
checkpointer: new MemorySaver()
|
|
198
|
+
};
|
|
188
199
|
})();
|
|
200
|
+
if (storeTtl) {
|
|
201
|
+
const everyMs = (storeTtl.sweepIntervalMinutes ?? 60) * 6e4;
|
|
202
|
+
const sweeper = setInterval(() => {
|
|
203
|
+
skeinStore.store.sweepExpired().catch((error) => {
|
|
204
|
+
console.error("skein: store TTL sweep failed", error);
|
|
205
|
+
});
|
|
206
|
+
}, everyMs);
|
|
207
|
+
sweeper.unref();
|
|
208
|
+
disposers.push(async () => clearInterval(sweeper));
|
|
209
|
+
}
|
|
189
210
|
const { runQueue, bus } = (() => {
|
|
190
211
|
if (queue === "redis") {
|
|
191
|
-
const redisUrl = requireEnv("
|
|
212
|
+
const redisUrl = requireEnv("REDIS_URI", "redis");
|
|
192
213
|
const redisQueue = new RedisRunQueue(redisUrl);
|
|
193
214
|
disposers.push(() => redisQueue.dispose());
|
|
194
215
|
const redisBus = new RedisRunEventBus(redisUrl);
|
|
@@ -212,7 +233,7 @@ async function buildRuntime(options) {
|
|
|
212
233
|
deps,
|
|
213
234
|
cors: corsFromHttpConfig(first.config.http),
|
|
214
235
|
reloadGraphs: async () => {
|
|
215
|
-
reroute((await loadConfig({ configPath, importModule })).graphs);
|
|
236
|
+
reroute((await loadConfig({ configPath, importModule, staticSchemas: schemas })).graphs);
|
|
216
237
|
},
|
|
217
238
|
dispose: disposeAll
|
|
218
239
|
};
|
|
@@ -221,8 +242,20 @@ async function buildRuntime(options) {
|
|
|
221
242
|
throw error;
|
|
222
243
|
}
|
|
223
244
|
}
|
|
245
|
+
function resolveStoreTtl(raw) {
|
|
246
|
+
if (!raw) return void 0;
|
|
247
|
+
const ttl = {};
|
|
248
|
+
if (typeof raw.default_ttl === "number") ttl.defaultTtl = raw.default_ttl;
|
|
249
|
+
if (typeof raw.refresh_on_read === "boolean") ttl.refreshOnRead = raw.refresh_on_read;
|
|
250
|
+
if (typeof raw.sweep_interval_minutes === "number") {
|
|
251
|
+
ttl.sweepIntervalMinutes = raw.sweep_interval_minutes;
|
|
252
|
+
}
|
|
253
|
+
return Object.keys(ttl).length > 0 ? ttl : void 0;
|
|
254
|
+
}
|
|
224
255
|
export {
|
|
225
256
|
RuntimeConfigError,
|
|
226
257
|
buildRuntime,
|
|
258
|
+
embedRuntimePackage,
|
|
259
|
+
isCustomFunctionPath,
|
|
227
260
|
resolveEmbed
|
|
228
261
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@skein-js/runtime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Assembles skein-js ProtocolDeps from langgraph.json + selected drivers (memory/Postgres/Redis).",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Maina Wycliffe <wmmaina7@gmail.com>",
|
|
@@ -42,12 +42,12 @@
|
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"cors": "^2.8.5",
|
|
45
|
-
"@skein-js/agent-protocol": "0.
|
|
46
|
-
"@skein-js/express": "0.
|
|
47
|
-
"@skein-js/config": "0.
|
|
48
|
-
"@skein-js/
|
|
49
|
-
"@skein-js/storage-
|
|
50
|
-
"@skein-js/
|
|
45
|
+
"@skein-js/agent-protocol": "0.5.0",
|
|
46
|
+
"@skein-js/express": "0.5.0",
|
|
47
|
+
"@skein-js/config": "0.5.0",
|
|
48
|
+
"@skein-js/redis": "0.5.0",
|
|
49
|
+
"@skein-js/storage-memory": "0.5.0",
|
|
50
|
+
"@skein-js/storage-postgres": "0.5.0"
|
|
51
51
|
},
|
|
52
52
|
"peerDependencies": {
|
|
53
53
|
"@langchain/langgraph": "^1.4.0",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"@langchain/langgraph": "^1.4.0",
|
|
59
59
|
"@langchain/langgraph-checkpoint-postgres": "^1.0.4",
|
|
60
60
|
"@types/cors": "^2.8.17",
|
|
61
|
-
"@skein-js/test-support": "0.
|
|
61
|
+
"@skein-js/test-support": "0.5.0"
|
|
62
62
|
},
|
|
63
63
|
"publishConfig": {
|
|
64
64
|
"access": "public"
|