@skein-js/runtime 0.5.0 → 0.7.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 +32 -1
- package/dist/index.d.ts +77 -5
- package/dist/index.js +134 -74
- package/package.json +11 -11
package/README.md
CHANGED
|
@@ -50,6 +50,32 @@ Graph hot-reload (`reloadGraphs()`) works in every mode; `snapshotState`/`hydrat
|
|
|
50
50
|
> which uses the in-memory runtime under the hood); `buildRuntime` is the path the CLI uses to add
|
|
51
51
|
> Postgres/Redis.
|
|
52
52
|
|
|
53
|
+
## In-code embedding (Postgres + Redis)
|
|
54
|
+
|
|
55
|
+
`buildRuntime` assembles durable deps **from a `langgraph.json`**. `embedPostgresGraphs` assembles the
|
|
56
|
+
same durable deps **from a graph you already hold in code** — the persistent counterpart to
|
|
57
|
+
[`embedInMemoryGraphs`](../server-kit) (which wires only in-memory drivers). Because it owns
|
|
58
|
+
pools/connections, it's `async` and returns a `dispose()`:
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
import { createExpressServer } from "@skein-js/express";
|
|
62
|
+
import { embedPostgresGraphs } from "@skein-js/runtime";
|
|
63
|
+
import { graph } from "./my-graph.js";
|
|
64
|
+
|
|
65
|
+
const { deps, dispose } = await embedPostgresGraphs({ agent: graph }); // reads POSTGRES_URI / REDIS_URI
|
|
66
|
+
const server = await createExpressServer({ deps });
|
|
67
|
+
await server.listen(2024);
|
|
68
|
+
// …on shutdown:
|
|
69
|
+
await dispose();
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Postgres is required (`POSTGRES_URI` or `postgresUri`). **Redis is optional** — with no `redisUri` /
|
|
73
|
+
`REDIS_URI`, the run queue + event bus fall back to in-memory: state survives a restart, but you're
|
|
74
|
+
limited to a **single instance** (the queue is process-local; streaming isn't fanned across instances).
|
|
75
|
+
Options mirror the low-level knobs: `index` (pgvector), `ttl`, `poolMax`, `sslNoVerify`, and `overrides`
|
|
76
|
+
for non-driver deps (`auth`/`logger`/…). See [docs/embedding.md](../../docs/embedding.md) for the full
|
|
77
|
+
walkthrough.
|
|
78
|
+
|
|
53
79
|
## Install
|
|
54
80
|
|
|
55
81
|
```bash
|
|
@@ -62,10 +88,15 @@ TypeScript graphs/embedders requires passing an `importModule` (the CLI injects
|
|
|
62
88
|
## API
|
|
63
89
|
|
|
64
90
|
- **`buildRuntime(options): Promise<SkeinRuntime>`** — `options`:
|
|
65
|
-
`{ configPath, store, queue, importModule? }`.
|
|
91
|
+
`{ configPath, store, queue, importModule? }`. Durable deps **from a `langgraph.json`**.
|
|
66
92
|
- **`interface SkeinRuntime`** — `{ deps, cors?, reloadGraphs(), dispose(), snapshotState?(), hydrateState?() }`
|
|
67
93
|
(the last two only in all-memory mode).
|
|
68
94
|
- **`type StoreDriver`** = `"memory" | "postgres"` · **`type QueueDriver`** = `"memory" | "redis"`.
|
|
95
|
+
- **`embedPostgresGraphs(graphs, options?): Promise<EmbeddedPostgresRuntime>`** — durable deps **from
|
|
96
|
+
graphs in code** (Postgres + `PostgresSaver`, Redis when configured). Returns `{ deps, dispose() }`.
|
|
97
|
+
`options`: `{ postgresUri?, redisUri?, index?, ttl?, poolMax?, sslNoVerify?, overrides? }`.
|
|
98
|
+
- **`interface EmbedPostgresGraphsOptions`** · **`interface EmbeddedPostgresRuntime`** ·
|
|
99
|
+
re-exported **`type EmbeddableGraph`** (from `@skein-js/server-kit`).
|
|
69
100
|
- **`class RuntimeConfigError`** — thrown when a driver's env var or `store.index.embed` can't be
|
|
70
101
|
resolved.
|
|
71
102
|
- **`resolveEmbed(embed, { configDir, importModule? })`** — resolves a `langgraph.json`
|
package/dist/index.d.ts
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
import { ProtocolDeps } from '@skein-js/agent-protocol';
|
|
1
|
+
import { ProtocolDeps, GraphResolver } from '@skein-js/agent-protocol';
|
|
2
2
|
import { ModuleImporter, GraphSchemas } from '@skein-js/config';
|
|
3
|
-
import { DevStateSnapshot } from '@skein-js/
|
|
3
|
+
import { DevStateSnapshot, EmbeddableGraph } from '@skein-js/server-kit';
|
|
4
|
+
export { EmbeddableGraph } from '@skein-js/server-kit';
|
|
4
5
|
import { CorsOptions } from 'cors';
|
|
5
|
-
import { EmbedFunction } from '@skein-js/storage-postgres';
|
|
6
|
+
import { PostgresSkeinStoreOptions, StoreIndexConfig, EmbedFunction } from '@skein-js/storage-postgres';
|
|
6
7
|
|
|
7
8
|
/** Where protocol resources (assistants/threads/runs/store) and checkpoints are persisted. */
|
|
8
9
|
type StoreDriver = "memory" | "postgres";
|
|
9
10
|
/** Where background runs are queued and stream frames are fanned out. */
|
|
10
11
|
type QueueDriver = "memory" | "redis";
|
|
12
|
+
/** Options for {@link buildRuntime}: which `langgraph.json` to load and which drivers to wire. */
|
|
11
13
|
interface BuildRuntimeOptions {
|
|
12
14
|
/** Absolute path to `langgraph.json`. */
|
|
13
15
|
configPath: string;
|
|
@@ -23,6 +25,10 @@ interface BuildRuntimeOptions {
|
|
|
23
25
|
/** `"memory"` (default dev) or `"redis"` (reads `REDIS_URI`). */
|
|
24
26
|
queue: QueueDriver;
|
|
25
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* The result of {@link buildRuntime}: assembled deps for any adapter's `{ deps }` seam, plus the
|
|
30
|
+
* lifecycle hooks (`reloadGraphs`, `dispose`, and — in all-memory mode — state snapshot/hydrate).
|
|
31
|
+
*/
|
|
26
32
|
interface SkeinRuntime {
|
|
27
33
|
/** Assembled dependency bundle to pass as `createExpressServer({ deps })`. */
|
|
28
34
|
deps: ProtocolDeps;
|
|
@@ -40,6 +46,67 @@ interface SkeinRuntime {
|
|
|
40
46
|
/** Assemble a {@link SkeinRuntime} for the requested driver combination. */
|
|
41
47
|
declare function buildRuntime(options: BuildRuntimeOptions): Promise<SkeinRuntime>;
|
|
42
48
|
|
|
49
|
+
/** Store-item TTL policy — derived from the store's public option so runtime needn't depend on core. */
|
|
50
|
+
type StoreTtl = NonNullable<PostgresSkeinStoreOptions["ttl"]>;
|
|
51
|
+
|
|
52
|
+
/** Options for {@link embedPostgresGraphs} — connection strings, semantic search, TTL, and overrides. */
|
|
53
|
+
interface EmbedPostgresGraphsOptions {
|
|
54
|
+
/** Postgres connection string. Defaults to `process.env.POSTGRES_URI`; throws if neither is set. */
|
|
55
|
+
postgresUri?: string;
|
|
56
|
+
/**
|
|
57
|
+
* Redis connection string. Defaults to `process.env.REDIS_URI`. When **absent**, the run queue and
|
|
58
|
+
* event bus fall back to in-memory — a single durable instance: state survives a restart, but the
|
|
59
|
+
* run queue is process-local and streaming is not fanned across instances, so it is **not
|
|
60
|
+
* horizontally scalable**. Set a Redis URL to run more than one instance.
|
|
61
|
+
*/
|
|
62
|
+
redisUri?: string;
|
|
63
|
+
/**
|
|
64
|
+
* pgvector semantic-search config for the long-term store — a resolved embedder (`dims` + `embed`).
|
|
65
|
+
* Omitted → store search falls back to naive text matching.
|
|
66
|
+
*/
|
|
67
|
+
index?: StoreIndexConfig;
|
|
68
|
+
/** Store-item TTL/expiry policy (with a background sweep). Omitted → items never expire. */
|
|
69
|
+
ttl?: StoreTtl;
|
|
70
|
+
/** Max connections per pool (skein opens two — store + saver). Defaults to env `PG_POOL_MAX`. */
|
|
71
|
+
poolMax?: number;
|
|
72
|
+
/** Disable TLS cert verification (self-signed managed cert). Defaults to env `DATABASE_SSL_NO_VERIFY`. */
|
|
73
|
+
sslNoVerify?: boolean;
|
|
74
|
+
/**
|
|
75
|
+
* Replace or add any NON-driver dep — `auth`, `logger`, `clock`, `logRunActivity`, `runTimeoutMs`,
|
|
76
|
+
* `webhookDispatcher`. The drivers (`store`/`queue`/`bus`/`checkpointer`) and `graphs` are owned by
|
|
77
|
+
* this helper and excluded, so a stray override can't void the durable wiring or the graph source.
|
|
78
|
+
*/
|
|
79
|
+
overrides?: Omit<Partial<ProtocolDeps>, "graphs" | "store" | "queue" | "bus" | "checkpointer">;
|
|
80
|
+
}
|
|
81
|
+
/** The result of {@link embedPostgresGraphs}: the assembled deps and a `dispose()` for the pools it owns. */
|
|
82
|
+
interface EmbeddedPostgresRuntime {
|
|
83
|
+
/** Assembled deps for any adapter's `{ deps }` seam. */
|
|
84
|
+
deps: ProtocolDeps;
|
|
85
|
+
/** Tear down the Postgres pools + any Redis connections + the TTL sweeper. Call on shutdown. */
|
|
86
|
+
dispose(): Promise<void>;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Build a durable `ProtocolDeps` around graphs you already hold in code — Postgres store +
|
|
90
|
+
* `PostgresSaver`, plus Redis queue/bus when a Redis URL is configured — and a `dispose()` to release
|
|
91
|
+
* the pools/connections it opens. Pass a map of compiled graphs (or factories), or a ready
|
|
92
|
+
* {@link GraphResolver}; hand the result's `deps` to any adapter's `{ deps }` seam:
|
|
93
|
+
*
|
|
94
|
+
* ```ts
|
|
95
|
+
* import { embedPostgresGraphs } from "@skein-js/runtime";
|
|
96
|
+
* import { createExpressServer } from "@skein-js/express";
|
|
97
|
+
*
|
|
98
|
+
* const { deps, dispose } = await embedPostgresGraphs({ agent: graph }); // reads POSTGRES_URI / REDIS_URI
|
|
99
|
+
* const server = await createExpressServer({ deps });
|
|
100
|
+
* await server.listen(2024);
|
|
101
|
+
* // …on shutdown:
|
|
102
|
+
* await dispose();
|
|
103
|
+
* ```
|
|
104
|
+
*
|
|
105
|
+
* Postgres is required (`POSTGRES_URI` or `options.postgresUri`). Redis is optional — see
|
|
106
|
+
* {@link EmbedPostgresGraphsOptions.redisUri} for the single-instance caveat when it's omitted.
|
|
107
|
+
*/
|
|
108
|
+
declare function embedPostgresGraphs(graphs: GraphResolver | Record<string, EmbeddableGraph>, options?: EmbedPostgresGraphsOptions): Promise<EmbeddedPostgresRuntime>;
|
|
109
|
+
|
|
43
110
|
/** Thrown when a selected driver's connection env or `store.index.embed` config can't be resolved. */
|
|
44
111
|
declare class RuntimeConfigError extends Error {
|
|
45
112
|
constructor(message: string);
|
|
@@ -64,8 +131,13 @@ declare function isCustomFunctionPath(embed: string): boolean;
|
|
|
64
131
|
* it must pin this package into the production image explicitly. Custom-function paths return undefined
|
|
65
132
|
* (they are bundled), as do unknown providers.
|
|
66
133
|
*/
|
|
67
|
-
declare function
|
|
134
|
+
declare function providerEmbedPackage(embed: string): string | undefined;
|
|
135
|
+
/**
|
|
136
|
+
* @deprecated Renamed to {@link providerEmbedPackage} — it returns the npm package a `provider:model`
|
|
137
|
+
* embed needs installed, not a verb. Kept for back-compat; slated for removal in a future major.
|
|
138
|
+
*/
|
|
139
|
+
declare const embedRuntimePackage: typeof providerEmbedPackage;
|
|
68
140
|
/** Resolve a `store.index.embed` value to an `EmbedFunction`, honoring both LangGraph forms. */
|
|
69
141
|
declare function resolveEmbed(embed: string, options: ResolveEmbedOptions): Promise<EmbedFunction>;
|
|
70
142
|
|
|
71
|
-
export { type BuildRuntimeOptions, type QueueDriver, type ResolveEmbedOptions, RuntimeConfigError, type SkeinRuntime, type StoreDriver, buildRuntime, embedRuntimePackage, isCustomFunctionPath, resolveEmbed };
|
|
143
|
+
export { type BuildRuntimeOptions, type EmbedPostgresGraphsOptions, type EmbeddedPostgresRuntime, type QueueDriver, type ResolveEmbedOptions, RuntimeConfigError, type SkeinRuntime, type StoreDriver, buildRuntime, embedPostgresGraphs, embedRuntimePackage, isCustomFunctionPath, providerEmbedPackage, resolveEmbed };
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
// src/build-runtime.ts
|
|
2
2
|
import { MemorySaver } from "@langchain/langgraph";
|
|
3
|
-
import { PostgresSaver } from "@langchain/langgraph-checkpoint-postgres";
|
|
4
3
|
import {
|
|
5
4
|
loadAuthEngine,
|
|
6
5
|
loadConfig
|
|
@@ -8,9 +7,12 @@ import {
|
|
|
8
7
|
import {
|
|
9
8
|
corsFromHttpConfig,
|
|
10
9
|
loadReloadableInMemoryRuntime
|
|
11
|
-
} from "@skein-js/
|
|
12
|
-
import { RedisRunEventBus, RedisRunQueue } from "@skein-js/redis";
|
|
10
|
+
} from "@skein-js/server-kit";
|
|
13
11
|
import { MemoryRunEventBus, MemoryRunQueue, MemorySkeinStore } from "@skein-js/storage-memory";
|
|
12
|
+
|
|
13
|
+
// src/drivers.ts
|
|
14
|
+
import { PostgresSaver } from "@langchain/langgraph-checkpoint-postgres";
|
|
15
|
+
import { RedisRunEventBus, RedisRunQueue } from "@skein-js/redis";
|
|
14
16
|
import {
|
|
15
17
|
createPostgresPool,
|
|
16
18
|
PostgresSkeinStore
|
|
@@ -24,6 +26,64 @@ var RuntimeConfigError = class extends Error {
|
|
|
24
26
|
}
|
|
25
27
|
};
|
|
26
28
|
|
|
29
|
+
// src/drivers.ts
|
|
30
|
+
async function runDisposers(disposers) {
|
|
31
|
+
await Promise.allSettled(disposers.map((dispose) => dispose()));
|
|
32
|
+
}
|
|
33
|
+
function requireEnv(name, driver) {
|
|
34
|
+
const value = process.env[name];
|
|
35
|
+
if (!value) {
|
|
36
|
+
throw new RuntimeConfigError(`The "${driver}" driver requires ${name} to be set.`);
|
|
37
|
+
}
|
|
38
|
+
return value;
|
|
39
|
+
}
|
|
40
|
+
function postgresConnectionOptions() {
|
|
41
|
+
const options = {};
|
|
42
|
+
const rawMax = process.env["PG_POOL_MAX"];
|
|
43
|
+
if (rawMax !== void 0 && rawMax.trim() !== "") {
|
|
44
|
+
const max = Number(rawMax);
|
|
45
|
+
if (!Number.isInteger(max) || max <= 0) {
|
|
46
|
+
throw new RuntimeConfigError(`PG_POOL_MAX must be a positive integer (got "${rawMax}").`);
|
|
47
|
+
}
|
|
48
|
+
options.poolMax = max;
|
|
49
|
+
}
|
|
50
|
+
const noVerify = process.env["DATABASE_SSL_NO_VERIFY"];
|
|
51
|
+
if (noVerify === "1" || noVerify?.toLowerCase() === "true") options.sslNoVerify = true;
|
|
52
|
+
return options;
|
|
53
|
+
}
|
|
54
|
+
async function connectPostgresStore(args) {
|
|
55
|
+
const { url, index, ttl, connectionOptions, disposers } = args;
|
|
56
|
+
const store = await PostgresSkeinStore.connect(url, {
|
|
57
|
+
...index ? { index } : {},
|
|
58
|
+
...ttl ? { ttl } : {},
|
|
59
|
+
...connectionOptions
|
|
60
|
+
});
|
|
61
|
+
disposers.push(() => store.close());
|
|
62
|
+
await store.migrate();
|
|
63
|
+
const checkpointer = new PostgresSaver(createPostgresPool(url, connectionOptions));
|
|
64
|
+
disposers.push(() => checkpointer.end());
|
|
65
|
+
await checkpointer.setup();
|
|
66
|
+
return { store, checkpointer };
|
|
67
|
+
}
|
|
68
|
+
function connectRedisQueue(args) {
|
|
69
|
+
const { url, disposers } = args;
|
|
70
|
+
const queue = new RedisRunQueue(url);
|
|
71
|
+
disposers.push(() => queue.dispose());
|
|
72
|
+
const bus = new RedisRunEventBus(url);
|
|
73
|
+
disposers.push(() => bus.dispose());
|
|
74
|
+
return { queue, bus };
|
|
75
|
+
}
|
|
76
|
+
function startStoreTtlSweeper(store, ttl, disposers) {
|
|
77
|
+
const everyMs = (ttl.sweepIntervalMinutes ?? 60) * 6e4;
|
|
78
|
+
const sweeper = setInterval(() => {
|
|
79
|
+
store.store.sweepExpired().catch((error) => {
|
|
80
|
+
console.error("skein: store TTL sweep failed", error);
|
|
81
|
+
});
|
|
82
|
+
}, everyMs);
|
|
83
|
+
sweeper.unref();
|
|
84
|
+
disposers.push(async () => clearInterval(sweeper));
|
|
85
|
+
}
|
|
86
|
+
|
|
27
87
|
// src/resolve-embed.ts
|
|
28
88
|
import { pathToFileURL } from "url";
|
|
29
89
|
import { parseGraphSpec } from "@skein-js/config";
|
|
@@ -52,12 +112,13 @@ function isCustomFunctionPath(embed) {
|
|
|
52
112
|
const head = embed.split(":", 1)[0] ?? "";
|
|
53
113
|
return head.startsWith(".") || head.startsWith("/") || /\.([mc]?[jt]s|py)$/.test(head);
|
|
54
114
|
}
|
|
55
|
-
function
|
|
115
|
+
function providerEmbedPackage(embed) {
|
|
56
116
|
if (isCustomFunctionPath(embed)) return void 0;
|
|
57
117
|
const separator = embed.indexOf(":");
|
|
58
118
|
if (separator === -1) return void 0;
|
|
59
119
|
return PROVIDERS[embed.slice(0, separator)]?.package;
|
|
60
120
|
}
|
|
121
|
+
var embedRuntimePackage = providerEmbedPackage;
|
|
61
122
|
async function resolvePathEmbed(embed, options) {
|
|
62
123
|
const { sourceFile, exportSymbol } = parseGraphSpec(embed, options.configDir);
|
|
63
124
|
const importer = options.importModule ?? ((file) => import(pathToFileURL(file).href));
|
|
@@ -106,27 +167,6 @@ async function resolveEmbed(embed, options) {
|
|
|
106
167
|
}
|
|
107
168
|
|
|
108
169
|
// src/build-runtime.ts
|
|
109
|
-
function requireEnv(name, driver) {
|
|
110
|
-
const value = process.env[name];
|
|
111
|
-
if (!value) {
|
|
112
|
-
throw new RuntimeConfigError(`The "${driver}" driver requires ${name} to be set.`);
|
|
113
|
-
}
|
|
114
|
-
return value;
|
|
115
|
-
}
|
|
116
|
-
function postgresConnectionOptions() {
|
|
117
|
-
const options = {};
|
|
118
|
-
const rawMax = process.env["PG_POOL_MAX"];
|
|
119
|
-
if (rawMax !== void 0 && rawMax.trim() !== "") {
|
|
120
|
-
const max = Number(rawMax);
|
|
121
|
-
if (!Number.isInteger(max) || max <= 0) {
|
|
122
|
-
throw new RuntimeConfigError(`PG_POOL_MAX must be a positive integer (got "${rawMax}").`);
|
|
123
|
-
}
|
|
124
|
-
options.poolMax = max;
|
|
125
|
-
}
|
|
126
|
-
const noVerify = process.env["DATABASE_SSL_NO_VERIFY"];
|
|
127
|
-
if (noVerify === "1" || noVerify?.toLowerCase() === "true") options.sslNoVerify = true;
|
|
128
|
-
return options;
|
|
129
|
-
}
|
|
130
170
|
function reroutableGraphResolver(initial) {
|
|
131
171
|
let current = initial;
|
|
132
172
|
return {
|
|
@@ -167,57 +207,24 @@ async function buildRuntime(options) {
|
|
|
167
207
|
const first = await loadConfig({ configPath, importModule, staticSchemas: schemas });
|
|
168
208
|
const { resolver, reroute } = reroutableGraphResolver(first.graphs);
|
|
169
209
|
const disposers = [];
|
|
170
|
-
const disposeAll =
|
|
171
|
-
await Promise.allSettled(disposers.map((dispose) => dispose()));
|
|
172
|
-
};
|
|
210
|
+
const disposeAll = () => runDisposers(disposers);
|
|
173
211
|
try {
|
|
174
212
|
const storeTtl = resolveStoreTtl(first.config.store?.ttl);
|
|
175
|
-
const { skeinStore, checkpointer } =
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
const saver = new PostgresSaver(createPostgresPool(databaseUrl, connectionOptions));
|
|
191
|
-
disposers.push(() => saver.end());
|
|
192
|
-
await saver.setup();
|
|
193
|
-
return { skeinStore: pgStore, checkpointer: saver };
|
|
194
|
-
}
|
|
195
|
-
return {
|
|
196
|
-
skeinStore: new MemorySkeinStore(storeTtl ? { ttl: storeTtl } : void 0),
|
|
197
|
-
checkpointer: new MemorySaver()
|
|
198
|
-
};
|
|
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
|
-
}
|
|
210
|
-
const { runQueue, bus } = (() => {
|
|
211
|
-
if (queue === "redis") {
|
|
212
|
-
const redisUrl = requireEnv("REDIS_URI", "redis");
|
|
213
|
-
const redisQueue = new RedisRunQueue(redisUrl);
|
|
214
|
-
disposers.push(() => redisQueue.dispose());
|
|
215
|
-
const redisBus = new RedisRunEventBus(redisUrl);
|
|
216
|
-
disposers.push(() => redisBus.dispose());
|
|
217
|
-
return { runQueue: redisQueue, bus: redisBus };
|
|
218
|
-
}
|
|
219
|
-
return { runQueue: new MemoryRunQueue(), bus: new MemoryRunEventBus() };
|
|
220
|
-
})();
|
|
213
|
+
const { store: skeinStore, checkpointer } = store === "postgres" ? await connectPostgresStore({
|
|
214
|
+
url: requireEnv("POSTGRES_URI", "postgres"),
|
|
215
|
+
index: await resolveStoreIndex(first.config.store?.index, {
|
|
216
|
+
configDir: first.configDir,
|
|
217
|
+
importModule
|
|
218
|
+
}),
|
|
219
|
+
ttl: storeTtl,
|
|
220
|
+
connectionOptions: postgresConnectionOptions(),
|
|
221
|
+
disposers
|
|
222
|
+
}) : {
|
|
223
|
+
store: new MemorySkeinStore(storeTtl ? { ttl: storeTtl } : void 0),
|
|
224
|
+
checkpointer: new MemorySaver()
|
|
225
|
+
};
|
|
226
|
+
if (storeTtl) startStoreTtlSweeper(skeinStore, storeTtl, disposers);
|
|
227
|
+
const { queue: runQueue, bus } = queue === "redis" ? connectRedisQueue({ url: requireEnv("REDIS_URI", "redis"), disposers }) : { queue: new MemoryRunQueue(), bus: new MemoryRunEventBus() };
|
|
221
228
|
const deps = {
|
|
222
229
|
store: skeinStore,
|
|
223
230
|
graphs: resolver,
|
|
@@ -252,10 +259,63 @@ function resolveStoreTtl(raw) {
|
|
|
252
259
|
}
|
|
253
260
|
return Object.keys(ttl).length > 0 ? ttl : void 0;
|
|
254
261
|
}
|
|
262
|
+
|
|
263
|
+
// src/embed-postgres-graphs.ts
|
|
264
|
+
import { normalizeEmbeddableGraphs } from "@skein-js/server-kit";
|
|
265
|
+
import { MemoryRunEventBus as MemoryRunEventBus2, MemoryRunQueue as MemoryRunQueue2 } from "@skein-js/storage-memory";
|
|
266
|
+
async function embedPostgresGraphs(graphs, options = {}) {
|
|
267
|
+
const disposers = [];
|
|
268
|
+
const dispose = () => runDisposers(disposers);
|
|
269
|
+
try {
|
|
270
|
+
if (options.poolMax !== void 0 && (!Number.isInteger(options.poolMax) || options.poolMax <= 0)) {
|
|
271
|
+
throw new RuntimeConfigError(`poolMax must be a positive integer (got ${options.poolMax}).`);
|
|
272
|
+
}
|
|
273
|
+
const connectionOptions = {
|
|
274
|
+
...postgresConnectionOptions(),
|
|
275
|
+
...options.poolMax !== void 0 ? { poolMax: options.poolMax } : {},
|
|
276
|
+
...options.sslNoVerify !== void 0 ? { sslNoVerify: options.sslNoVerify } : {}
|
|
277
|
+
};
|
|
278
|
+
const { store, checkpointer } = await connectPostgresStore({
|
|
279
|
+
// A blank explicit URI is treated as "not provided" so it falls through to `requireEnv` and gets
|
|
280
|
+
// the actionable RuntimeConfigError, rather than an opaque `pg` error from connecting to "".
|
|
281
|
+
url: blankToUndefined(options.postgresUri) ?? requireEnv("POSTGRES_URI", "postgres"),
|
|
282
|
+
index: options.index,
|
|
283
|
+
ttl: options.ttl,
|
|
284
|
+
connectionOptions,
|
|
285
|
+
disposers
|
|
286
|
+
});
|
|
287
|
+
if (options.ttl) startStoreTtlSweeper(store, options.ttl, disposers);
|
|
288
|
+
const redisUrl = blankToUndefined(options.redisUri) ?? blankToUndefined(process.env["REDIS_URI"]);
|
|
289
|
+
if (!redisUrl) {
|
|
290
|
+
console.warn(
|
|
291
|
+
"skein: embedPostgresGraphs has no Redis URL (redisUri / REDIS_URI) \u2014 using an in-memory run queue + event bus. State is durable in Postgres, but this is a single instance: the queue is process-local and streaming isn't fanned across instances. Set a Redis URL to scale out."
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
const { queue, bus } = redisUrl ? connectRedisQueue({ url: redisUrl, disposers }) : { queue: new MemoryRunQueue2(), bus: new MemoryRunEventBus2() };
|
|
295
|
+
const deps = {
|
|
296
|
+
store,
|
|
297
|
+
graphs: normalizeEmbeddableGraphs(graphs),
|
|
298
|
+
queue,
|
|
299
|
+
bus,
|
|
300
|
+
checkpointer,
|
|
301
|
+
...options.overrides
|
|
302
|
+
// spread LAST, mirroring embedInMemoryGraphs
|
|
303
|
+
};
|
|
304
|
+
return { deps, dispose };
|
|
305
|
+
} catch (error) {
|
|
306
|
+
await dispose();
|
|
307
|
+
throw error;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
function blankToUndefined(value) {
|
|
311
|
+
return value && value.trim() !== "" ? value : void 0;
|
|
312
|
+
}
|
|
255
313
|
export {
|
|
256
314
|
RuntimeConfigError,
|
|
257
315
|
buildRuntime,
|
|
316
|
+
embedPostgresGraphs,
|
|
258
317
|
embedRuntimePackage,
|
|
259
318
|
isCustomFunctionPath,
|
|
319
|
+
providerEmbedPackage,
|
|
260
320
|
resolveEmbed
|
|
261
321
|
};
|
package/package.json
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@skein-js/runtime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.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>",
|
|
7
|
-
"homepage": "https://github.com/
|
|
7
|
+
"homepage": "https://github.com/skein-js/skein-js/tree/main/packages/runtime#readme",
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
10
|
-
"url": "git+https://github.com/
|
|
10
|
+
"url": "git+https://github.com/skein-js/skein-js.git",
|
|
11
11
|
"directory": "packages/runtime"
|
|
12
12
|
},
|
|
13
13
|
"bugs": {
|
|
14
|
-
"url": "https://github.com/
|
|
14
|
+
"url": "https://github.com/skein-js/skein-js/issues"
|
|
15
15
|
},
|
|
16
16
|
"keywords": [
|
|
17
17
|
"typescript",
|
|
@@ -42,12 +42,12 @@
|
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"cors": "^2.8.5",
|
|
45
|
-
"@skein-js/
|
|
46
|
-
"@skein-js/
|
|
47
|
-
"@skein-js/
|
|
48
|
-
"@skein-js/
|
|
49
|
-
"@skein-js/
|
|
50
|
-
"@skein-js/storage-postgres": "0.
|
|
45
|
+
"@skein-js/config": "0.7.0",
|
|
46
|
+
"@skein-js/redis": "0.7.0",
|
|
47
|
+
"@skein-js/server-kit": "0.7.0",
|
|
48
|
+
"@skein-js/storage-memory": "0.7.0",
|
|
49
|
+
"@skein-js/agent-protocol": "0.7.0",
|
|
50
|
+
"@skein-js/storage-postgres": "0.7.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.7.0"
|
|
62
62
|
},
|
|
63
63
|
"publishConfig": {
|
|
64
64
|
"access": "public"
|