zeitlich 0.2.45 → 0.2.47
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 +137 -11
- package/dist/{activities-Coafq5zr.d.cts → activities-CPwKoUlD.d.cts} +22 -2
- package/dist/{activities-CrN-ghLo.d.ts → activities-DlaBxNID.d.ts} +22 -2
- package/dist/adapters/thread/anthropic/index.cjs +276 -71
- package/dist/adapters/thread/anthropic/index.cjs.map +1 -1
- package/dist/adapters/thread/anthropic/index.d.cts +62 -8
- package/dist/adapters/thread/anthropic/index.d.ts +62 -8
- package/dist/adapters/thread/anthropic/index.js +275 -72
- package/dist/adapters/thread/anthropic/index.js.map +1 -1
- package/dist/adapters/thread/anthropic/workflow.cjs +38 -20
- package/dist/adapters/thread/anthropic/workflow.cjs.map +1 -1
- package/dist/adapters/thread/anthropic/workflow.d.cts +5 -4
- package/dist/adapters/thread/anthropic/workflow.d.ts +5 -4
- package/dist/adapters/thread/anthropic/workflow.js +38 -20
- package/dist/adapters/thread/anthropic/workflow.js.map +1 -1
- package/dist/adapters/thread/google-genai/index.cjs +171 -69
- package/dist/adapters/thread/google-genai/index.cjs.map +1 -1
- package/dist/adapters/thread/google-genai/index.d.cts +6 -4
- package/dist/adapters/thread/google-genai/index.d.ts +6 -4
- package/dist/adapters/thread/google-genai/index.js +171 -69
- package/dist/adapters/thread/google-genai/index.js.map +1 -1
- package/dist/adapters/thread/google-genai/workflow.cjs +38 -20
- package/dist/adapters/thread/google-genai/workflow.cjs.map +1 -1
- package/dist/adapters/thread/google-genai/workflow.d.cts +7 -4
- package/dist/adapters/thread/google-genai/workflow.d.ts +7 -4
- package/dist/adapters/thread/google-genai/workflow.js +38 -20
- package/dist/adapters/thread/google-genai/workflow.js.map +1 -1
- package/dist/adapters/thread/langchain/index.cjs +170 -66
- package/dist/adapters/thread/langchain/index.cjs.map +1 -1
- package/dist/adapters/thread/langchain/index.d.cts +19 -4
- package/dist/adapters/thread/langchain/index.d.ts +19 -4
- package/dist/adapters/thread/langchain/index.js +170 -66
- package/dist/adapters/thread/langchain/index.js.map +1 -1
- package/dist/adapters/thread/langchain/workflow.cjs +38 -20
- package/dist/adapters/thread/langchain/workflow.cjs.map +1 -1
- package/dist/adapters/thread/langchain/workflow.d.cts +5 -4
- package/dist/adapters/thread/langchain/workflow.d.ts +5 -4
- package/dist/adapters/thread/langchain/workflow.js +38 -20
- package/dist/adapters/thread/langchain/workflow.js.map +1 -1
- package/dist/cold-store-BDgJpwLI.d.ts +114 -0
- package/dist/cold-store-Z2wvK2cV.d.cts +114 -0
- package/dist/index.cjs +440 -67
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +150 -8
- package/dist/index.d.ts +150 -8
- package/dist/index.js +432 -68
- package/dist/index.js.map +1 -1
- package/dist/proxy-CDh3Rsa7.d.cts +40 -0
- package/dist/proxy-Du8ggERu.d.ts +40 -0
- package/dist/{thread-manager-wRVVBFgj.d.cts → thread-manager-BjoYYXgd.d.cts} +8 -2
- package/dist/{thread-manager-BsLO3Fgc.d.cts → thread-manager-D8zKNFZ9.d.cts} +8 -2
- package/dist/{thread-manager-Bi1XlbpJ.d.ts → thread-manager-DtHYws2F.d.ts} +8 -2
- package/dist/{thread-manager-BhkOyQ1I.d.ts → thread-manager-Dw96FKH1.d.ts} +8 -2
- package/dist/{types-C66-BVBr.d.cts → types-BMJrsHo0.d.cts} +17 -1
- package/dist/{types-BkX4HLzi.d.ts → types-CtdOquo3.d.ts} +17 -1
- package/dist/{types-CdALEF3z.d.cts → types-DNEl5uxQ.d.cts} +38 -0
- package/dist/{types-ChAy_jSP.d.ts → types-qQVZfhoT.d.ts} +38 -0
- package/dist/{workflow-DMmiaw6w.d.cts → workflow-BH9ImDGq.d.cts} +48 -2
- package/dist/{workflow-BwT5EybR.d.ts → workflow-Cdw3-RNB.d.ts} +48 -2
- package/dist/workflow.cjs +47 -4
- package/dist/workflow.cjs.map +1 -1
- package/dist/workflow.d.cts +2 -2
- package/dist/workflow.d.ts +2 -2
- package/dist/workflow.js +47 -5
- package/dist/workflow.js.map +1 -1
- package/package.json +14 -3
- package/src/adapters/thread/anthropic/activities.ts +82 -39
- package/src/adapters/thread/anthropic/index.ts +8 -0
- package/src/adapters/thread/anthropic/model-invoker.test.ts +110 -0
- package/src/adapters/thread/anthropic/model-invoker.ts +26 -5
- package/src/adapters/thread/anthropic/prompt-cache.test.ts +134 -0
- package/src/adapters/thread/anthropic/prompt-cache.ts +163 -0
- package/src/adapters/thread/anthropic/proxy.ts +1 -0
- package/src/adapters/thread/anthropic/thread-manager.ts +9 -1
- package/src/adapters/thread/google-genai/activities.ts +64 -40
- package/src/adapters/thread/google-genai/proxy.ts +1 -0
- package/src/adapters/thread/google-genai/thread-manager.ts +9 -1
- package/src/adapters/thread/langchain/activities.ts +63 -36
- package/src/adapters/thread/langchain/proxy.ts +1 -0
- package/src/adapters/thread/langchain/thread-manager.ts +9 -1
- package/src/index.ts +21 -2
- package/src/lib/session/session-edge-cases.integration.test.ts +12 -0
- package/src/lib/session/session.integration.test.ts +138 -0
- package/src/lib/session/session.ts +29 -0
- package/src/lib/session/types.ts +22 -0
- package/src/lib/subagent/define.ts +1 -0
- package/src/lib/subagent/handler.ts +11 -2
- package/src/lib/subagent/subagent.integration.test.ts +139 -0
- package/src/lib/subagent/types.ts +16 -0
- package/src/lib/thread/cold-store.test.ts +221 -0
- package/src/lib/thread/cold-store.ts +269 -0
- package/src/lib/thread/index.ts +32 -0
- package/src/lib/thread/keys.ts +20 -0
- package/src/lib/thread/manager.ts +16 -27
- package/src/lib/thread/proxy.ts +79 -27
- package/src/lib/thread/snapshot.test.ts +443 -0
- package/src/lib/thread/snapshot.ts +163 -0
- package/src/lib/thread/test-utils.ts +228 -0
- package/src/lib/thread/tiered.test.ts +281 -0
- package/src/lib/thread/tiered.ts +135 -0
- package/src/lib/thread/types.ts +16 -0
- package/src/tools/edit/handler.test.ts +177 -0
- package/src/tools/edit/handler.ts +249 -47
- package/src/tools/edit/tool.ts +40 -0
- package/src/tools/task-create/handler.ts +1 -1
- package/src/tools/task-update/handler.ts +1 -1
- package/src/workflow.ts +2 -2
- package/dist/proxy-Bf7uI-Hw.d.cts +0 -24
- package/dist/proxy-COqA95FW.d.ts +0 -24
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import type Anthropic from "@anthropic-ai/sdk";
|
|
2
|
+
|
|
3
|
+
export interface AnthropicPromptCacheOptions {
|
|
4
|
+
/** TTL for the cache checkpoint. Defaults to 5m. */
|
|
5
|
+
ttl?: Anthropic.Messages.CacheControlEphemeral["ttl"];
|
|
6
|
+
/** Claude models support at most 4 cache breakpoints per request. */
|
|
7
|
+
maxBreakpoints?: number;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export type AnthropicPromptCacheConfig = boolean | AnthropicPromptCacheOptions;
|
|
11
|
+
|
|
12
|
+
interface PromptCachePayload {
|
|
13
|
+
messages: Anthropic.Messages.MessageParam[];
|
|
14
|
+
system?: string | Anthropic.Messages.TextBlockParam[];
|
|
15
|
+
tools?: Anthropic.Messages.Tool[];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
type CacheControl = Anthropic.Messages.CacheControlEphemeral;
|
|
19
|
+
type CacheableRecord = Record<string, unknown> & {
|
|
20
|
+
cache_control?: CacheControl | null;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const DEFAULT_MAX_CACHE_BREAKPOINTS = 4;
|
|
24
|
+
const UNCACHEABLE_BLOCK_TYPES = new Set(["thinking", "redacted_thinking"]);
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Resolve model-invoker prompt-cache config. Undefined means the default:
|
|
28
|
+
* enabled with an explicit 5 minute TTL.
|
|
29
|
+
*/
|
|
30
|
+
export function resolvePromptCacheOptions(
|
|
31
|
+
promptCache?: AnthropicPromptCacheConfig
|
|
32
|
+
): AnthropicPromptCacheOptions | undefined {
|
|
33
|
+
if (promptCache === false) return undefined;
|
|
34
|
+
if (promptCache === true || promptCache === undefined) return {};
|
|
35
|
+
return promptCache;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Add an explicit `cache_control` marker to the final cacheable message block.
|
|
40
|
+
*
|
|
41
|
+
* This intentionally uses block-level cache control rather than Anthropic's
|
|
42
|
+
* top-level automatic `cache_control` field because Amazon Bedrock does not
|
|
43
|
+
* support the top-level form. The block-level shape is accepted by both the
|
|
44
|
+
* Anthropic Messages API and Bedrock InvokeModel for Anthropic Claude models.
|
|
45
|
+
*/
|
|
46
|
+
export function addPromptCacheControl<TPayload extends PromptCachePayload>(
|
|
47
|
+
payload: TPayload,
|
|
48
|
+
options: AnthropicPromptCacheOptions = {}
|
|
49
|
+
): TPayload {
|
|
50
|
+
const maxBreakpoints =
|
|
51
|
+
options.maxBreakpoints ?? DEFAULT_MAX_CACHE_BREAKPOINTS;
|
|
52
|
+
if (maxBreakpoints <= 0) return payload;
|
|
53
|
+
|
|
54
|
+
if (countCacheControls(payload) >= maxBreakpoints) return payload;
|
|
55
|
+
|
|
56
|
+
const cacheControl: CacheControl = {
|
|
57
|
+
type: "ephemeral",
|
|
58
|
+
ttl: options.ttl ?? "5m",
|
|
59
|
+
};
|
|
60
|
+
const messages = addCacheControlToLastMessageBlock(
|
|
61
|
+
payload.messages,
|
|
62
|
+
cacheControl
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
if (messages === payload.messages) return payload;
|
|
66
|
+
return { ...payload, messages };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function addCacheControlToLastMessageBlock(
|
|
70
|
+
messages: Anthropic.Messages.MessageParam[],
|
|
71
|
+
cacheControl: CacheControl
|
|
72
|
+
): Anthropic.Messages.MessageParam[] {
|
|
73
|
+
for (
|
|
74
|
+
let messageIndex = messages.length - 1;
|
|
75
|
+
messageIndex >= 0;
|
|
76
|
+
messageIndex--
|
|
77
|
+
) {
|
|
78
|
+
const message = messages[messageIndex];
|
|
79
|
+
if (!message) continue;
|
|
80
|
+
|
|
81
|
+
if (typeof message.content === "string") {
|
|
82
|
+
if (message.content.length === 0) continue;
|
|
83
|
+
return replaceMessage(messages, messageIndex, {
|
|
84
|
+
...message,
|
|
85
|
+
content: [
|
|
86
|
+
{ type: "text", text: message.content, cache_control: cacheControl },
|
|
87
|
+
],
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (!Array.isArray(message.content)) continue;
|
|
92
|
+
|
|
93
|
+
for (
|
|
94
|
+
let blockIndex = message.content.length - 1;
|
|
95
|
+
blockIndex >= 0;
|
|
96
|
+
blockIndex--
|
|
97
|
+
) {
|
|
98
|
+
const block = message.content[blockIndex];
|
|
99
|
+
if (!isCacheableContentBlock(block)) continue;
|
|
100
|
+
if (hasCacheControl(block)) return messages;
|
|
101
|
+
|
|
102
|
+
const content = [...message.content];
|
|
103
|
+
content[blockIndex] = {
|
|
104
|
+
...(block as Record<string, unknown>),
|
|
105
|
+
cache_control: cacheControl,
|
|
106
|
+
} as Anthropic.Messages.ContentBlockParam;
|
|
107
|
+
return replaceMessage(messages, messageIndex, { ...message, content });
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return messages;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function replaceMessage(
|
|
115
|
+
messages: Anthropic.Messages.MessageParam[],
|
|
116
|
+
index: number,
|
|
117
|
+
message: Anthropic.Messages.MessageParam
|
|
118
|
+
): Anthropic.Messages.MessageParam[] {
|
|
119
|
+
const next = [...messages];
|
|
120
|
+
next[index] = message;
|
|
121
|
+
return next;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function isCacheableContentBlock(
|
|
125
|
+
block: Anthropic.Messages.ContentBlockParam | undefined
|
|
126
|
+
): block is Anthropic.Messages.ContentBlockParam & CacheableRecord {
|
|
127
|
+
if (!isRecord(block)) return false;
|
|
128
|
+
const type = typeof block.type === "string" ? block.type : undefined;
|
|
129
|
+
if (type && UNCACHEABLE_BLOCK_TYPES.has(type)) return false;
|
|
130
|
+
if (type === "text" && block.text === "") return false;
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function countCacheControls(payload: PromptCachePayload): number {
|
|
135
|
+
let count = 0;
|
|
136
|
+
|
|
137
|
+
for (const tool of payload.tools ?? []) {
|
|
138
|
+
if (hasCacheControl(tool)) count++;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (Array.isArray(payload.system)) {
|
|
142
|
+
for (const block of payload.system) {
|
|
143
|
+
if (hasCacheControl(block)) count++;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
for (const message of payload.messages) {
|
|
148
|
+
if (!Array.isArray(message.content)) continue;
|
|
149
|
+
for (const block of message.content) {
|
|
150
|
+
if (hasCacheControl(block)) count++;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return count;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function hasCacheControl(value: unknown): value is CacheableRecord {
|
|
158
|
+
return isRecord(value) && value.cache_control != null;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
162
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
163
|
+
}
|
|
@@ -25,6 +25,7 @@ import { createThreadOpsProxy } from "../../../lib/thread/proxy";
|
|
|
25
25
|
import { ADAPTER_ID } from "./adapter-id";
|
|
26
26
|
|
|
27
27
|
export { ADAPTER_ID, type AdapterId } from "./adapter-id";
|
|
28
|
+
export type { ThreadOpsProxyOptions } from "../../../lib/thread/proxy";
|
|
28
29
|
|
|
29
30
|
export function proxyAnthropicThreadOps(
|
|
30
31
|
scope?: string,
|
|
@@ -35,6 +35,12 @@ export interface AnthropicThreadManagerConfig {
|
|
|
35
35
|
/** Thread key, defaults to 'messages' */
|
|
36
36
|
key?: string;
|
|
37
37
|
hooks?: AnthropicThreadManagerHooks;
|
|
38
|
+
/**
|
|
39
|
+
* Override the default thread TTL (90 days). When pairing the
|
|
40
|
+
* adapter with a durable cold tier, a shorter TTL (hours) is
|
|
41
|
+
* typically more appropriate.
|
|
42
|
+
*/
|
|
43
|
+
ttlSeconds?: number;
|
|
38
44
|
}
|
|
39
45
|
|
|
40
46
|
/** Prepared payload ready to send to the Anthropic API */
|
|
@@ -57,7 +63,8 @@ export interface AnthropicThreadManager extends ProviderThreadManager<
|
|
|
57
63
|
prepareForInvocation(): Promise<AnthropicInvocationPayload>;
|
|
58
64
|
}
|
|
59
65
|
|
|
60
|
-
|
|
66
|
+
/** Extract the unique id from a {@link StoredMessage}. */
|
|
67
|
+
export function storedMessageId(msg: StoredMessage): string {
|
|
61
68
|
return msg.id;
|
|
62
69
|
}
|
|
63
70
|
|
|
@@ -113,6 +120,7 @@ export function createAnthropicThreadManager(
|
|
|
113
120
|
threadId: config.threadId,
|
|
114
121
|
key: config.key,
|
|
115
122
|
idOf: storedMessageId,
|
|
123
|
+
...(config.ttlSeconds !== undefined && { ttlSeconds: config.ttlSeconds }),
|
|
116
124
|
};
|
|
117
125
|
|
|
118
126
|
const base = createThreadManager(baseConfig);
|
|
@@ -13,11 +13,15 @@ import type {
|
|
|
13
13
|
ScopedPrefix,
|
|
14
14
|
} from "../../../lib/session/types";
|
|
15
15
|
import type { ModelInvoker } from "../../../lib/model";
|
|
16
|
+
import { createTieredThreadManager } from "../../../lib/thread/tiered";
|
|
17
|
+
import type { ColdThreadStore } from "../../../lib/thread/cold-store";
|
|
16
18
|
import {
|
|
17
19
|
createGoogleGenAIThreadManager,
|
|
20
|
+
storedContentId,
|
|
18
21
|
type GoogleGenAIContent,
|
|
19
22
|
type GoogleGenAISystemContent,
|
|
20
23
|
type GoogleGenAIThreadManagerHooks,
|
|
24
|
+
type StoredContent,
|
|
21
25
|
} from "./thread-manager";
|
|
22
26
|
import { createGoogleGenAIModelInvoker } from "./model-invoker";
|
|
23
27
|
import { ADAPTER_ID } from "./adapter-id";
|
|
@@ -34,6 +38,19 @@ export interface GoogleGenAIAdapterConfig {
|
|
|
34
38
|
/** Default model name (e.g. 'gemini-2.5-flash'). If omitted, use `createModelInvoker()` */
|
|
35
39
|
model?: string;
|
|
36
40
|
hooks?: GoogleGenAIThreadManagerHooks;
|
|
41
|
+
/**
|
|
42
|
+
* Optional durable cold tier (e.g. S3, R2, GCS). When provided,
|
|
43
|
+
* the session hydrates the thread on entry (`continue`/`fork`) and
|
|
44
|
+
* flushes it on every exit path. When omitted, the adapter is
|
|
45
|
+
* Redis-only and `hydrateThread`/`flushThread` activities are no-ops.
|
|
46
|
+
*/
|
|
47
|
+
coldStore?: ColdThreadStore;
|
|
48
|
+
/**
|
|
49
|
+
* Override the default Redis TTL (90 days). When pairing the
|
|
50
|
+
* adapter with a `coldStore`, a shorter TTL (hours) is typically
|
|
51
|
+
* more appropriate.
|
|
52
|
+
*/
|
|
53
|
+
ttlSeconds?: number;
|
|
37
54
|
}
|
|
38
55
|
|
|
39
56
|
/**
|
|
@@ -140,16 +157,34 @@ export function createGoogleGenAIAdapter(
|
|
|
140
157
|
): GoogleGenAIAdapter {
|
|
141
158
|
const { redis } = config;
|
|
142
159
|
|
|
160
|
+
const baseExtras = {
|
|
161
|
+
...(config.ttlSeconds !== undefined && { ttlSeconds: config.ttlSeconds }),
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
const makeProviderThread = (threadId: string, threadKey?: string) =>
|
|
165
|
+
createGoogleGenAIThreadManager({
|
|
166
|
+
redis,
|
|
167
|
+
threadId,
|
|
168
|
+
key: threadKey,
|
|
169
|
+
...baseExtras,
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
const makeTieredBase = (threadId: string, threadKey?: string) =>
|
|
173
|
+
createTieredThreadManager<StoredContent>({
|
|
174
|
+
redis,
|
|
175
|
+
threadId,
|
|
176
|
+
key: threadKey,
|
|
177
|
+
idOf: storedContentId,
|
|
178
|
+
...baseExtras,
|
|
179
|
+
...(config.coldStore && { coldStore: config.coldStore }),
|
|
180
|
+
});
|
|
181
|
+
|
|
143
182
|
const threadOps: ThreadOps<GoogleGenAIContent> = {
|
|
144
183
|
async initializeThread(
|
|
145
184
|
threadId: string,
|
|
146
185
|
threadKey?: string
|
|
147
186
|
): Promise<void> {
|
|
148
|
-
const thread =
|
|
149
|
-
redis,
|
|
150
|
-
threadId,
|
|
151
|
-
key: threadKey,
|
|
152
|
-
});
|
|
187
|
+
const thread = makeProviderThread(threadId, threadKey);
|
|
153
188
|
await thread.initialize();
|
|
154
189
|
},
|
|
155
190
|
|
|
@@ -159,11 +194,7 @@ export function createGoogleGenAIAdapter(
|
|
|
159
194
|
content: GoogleGenAIContent,
|
|
160
195
|
threadKey?: string
|
|
161
196
|
): Promise<void> {
|
|
162
|
-
const thread =
|
|
163
|
-
redis,
|
|
164
|
-
threadId,
|
|
165
|
-
key: threadKey,
|
|
166
|
-
});
|
|
197
|
+
const thread = makeProviderThread(threadId, threadKey);
|
|
167
198
|
await thread.appendUserMessage(id, content);
|
|
168
199
|
},
|
|
169
200
|
|
|
@@ -173,21 +204,13 @@ export function createGoogleGenAIAdapter(
|
|
|
173
204
|
content: GoogleGenAISystemContent,
|
|
174
205
|
threadKey?: string
|
|
175
206
|
): Promise<void> {
|
|
176
|
-
const thread =
|
|
177
|
-
redis,
|
|
178
|
-
threadId,
|
|
179
|
-
key: threadKey,
|
|
180
|
-
});
|
|
207
|
+
const thread = makeProviderThread(threadId, threadKey);
|
|
181
208
|
await thread.appendSystemMessage(id, content);
|
|
182
209
|
},
|
|
183
210
|
|
|
184
211
|
async appendToolResult(id: string, cfg: ToolResultConfig): Promise<void> {
|
|
185
212
|
const { threadId, threadKey, toolCallId, toolName, content } = cfg;
|
|
186
|
-
const thread =
|
|
187
|
-
redis,
|
|
188
|
-
threadId,
|
|
189
|
-
key: threadKey,
|
|
190
|
-
});
|
|
213
|
+
const thread = makeProviderThread(threadId, threadKey);
|
|
191
214
|
await thread.appendToolResult(
|
|
192
215
|
id,
|
|
193
216
|
toolCallId,
|
|
@@ -202,11 +225,7 @@ export function createGoogleGenAIAdapter(
|
|
|
202
225
|
message: Content,
|
|
203
226
|
threadKey?: string
|
|
204
227
|
): Promise<void> {
|
|
205
|
-
const thread =
|
|
206
|
-
redis,
|
|
207
|
-
threadId,
|
|
208
|
-
key: threadKey,
|
|
209
|
-
});
|
|
228
|
+
const thread = makeProviderThread(threadId, threadKey);
|
|
210
229
|
await thread.appendModelContent(id, message.parts ?? []);
|
|
211
230
|
},
|
|
212
231
|
|
|
@@ -220,6 +239,7 @@ export function createGoogleGenAIAdapter(
|
|
|
220
239
|
threadId: sourceThreadId,
|
|
221
240
|
key: threadKey,
|
|
222
241
|
hooks: config.hooks,
|
|
242
|
+
...baseExtras,
|
|
223
243
|
});
|
|
224
244
|
await thread.fork(targetThreadId);
|
|
225
245
|
},
|
|
@@ -229,11 +249,7 @@ export function createGoogleGenAIAdapter(
|
|
|
229
249
|
messageId: string,
|
|
230
250
|
threadKey?: string,
|
|
231
251
|
): Promise<void> {
|
|
232
|
-
const thread =
|
|
233
|
-
redis,
|
|
234
|
-
threadId,
|
|
235
|
-
key: threadKey,
|
|
236
|
-
});
|
|
252
|
+
const thread = makeProviderThread(threadId, threadKey);
|
|
237
253
|
await thread.truncateFromId(messageId);
|
|
238
254
|
},
|
|
239
255
|
|
|
@@ -241,11 +257,7 @@ export function createGoogleGenAIAdapter(
|
|
|
241
257
|
threadId: string,
|
|
242
258
|
threadKey?: string
|
|
243
259
|
): Promise<PersistedThreadState | null> {
|
|
244
|
-
const thread =
|
|
245
|
-
redis,
|
|
246
|
-
threadId,
|
|
247
|
-
key: threadKey,
|
|
248
|
-
});
|
|
260
|
+
const thread = makeProviderThread(threadId, threadKey);
|
|
249
261
|
return thread.loadState();
|
|
250
262
|
},
|
|
251
263
|
|
|
@@ -254,13 +266,25 @@ export function createGoogleGenAIAdapter(
|
|
|
254
266
|
state: PersistedThreadState,
|
|
255
267
|
threadKey?: string
|
|
256
268
|
): Promise<void> {
|
|
257
|
-
const thread =
|
|
258
|
-
redis,
|
|
259
|
-
threadId,
|
|
260
|
-
key: threadKey,
|
|
261
|
-
});
|
|
269
|
+
const thread = makeProviderThread(threadId, threadKey);
|
|
262
270
|
await thread.saveState(state);
|
|
263
271
|
},
|
|
272
|
+
|
|
273
|
+
async hydrateThread(
|
|
274
|
+
threadId: string,
|
|
275
|
+
threadKey?: string
|
|
276
|
+
): Promise<void> {
|
|
277
|
+
if (!config.coldStore) return;
|
|
278
|
+
await makeTieredBase(threadId, threadKey).hydrate();
|
|
279
|
+
},
|
|
280
|
+
|
|
281
|
+
async flushThread(
|
|
282
|
+
threadId: string,
|
|
283
|
+
threadKey?: string
|
|
284
|
+
): Promise<void> {
|
|
285
|
+
if (!config.coldStore) return;
|
|
286
|
+
await makeTieredBase(threadId, threadKey).flush();
|
|
287
|
+
},
|
|
264
288
|
};
|
|
265
289
|
|
|
266
290
|
function createActivities<S extends string = "">(
|
|
@@ -25,6 +25,7 @@ import { createThreadOpsProxy } from "../../../lib/thread/proxy";
|
|
|
25
25
|
import { ADAPTER_ID } from "./adapter-id";
|
|
26
26
|
|
|
27
27
|
export { ADAPTER_ID, type AdapterId } from "./adapter-id";
|
|
28
|
+
export type { ThreadOpsProxyOptions } from "../../../lib/thread/proxy";
|
|
28
29
|
|
|
29
30
|
export function proxyGoogleGenAIThreadOps(
|
|
30
31
|
scope?: string,
|
|
@@ -31,6 +31,12 @@ export interface GoogleGenAIThreadManagerConfig {
|
|
|
31
31
|
/** Thread key, defaults to 'messages' */
|
|
32
32
|
key?: string;
|
|
33
33
|
hooks?: GoogleGenAIThreadManagerHooks;
|
|
34
|
+
/**
|
|
35
|
+
* Override the default thread TTL (90 days). When pairing the
|
|
36
|
+
* adapter with a durable cold tier, a shorter TTL (hours) is
|
|
37
|
+
* typically more appropriate.
|
|
38
|
+
*/
|
|
39
|
+
ttlSeconds?: number;
|
|
34
40
|
}
|
|
35
41
|
|
|
36
42
|
/** Prepared payload ready to send to the Google GenAI API */
|
|
@@ -50,7 +56,8 @@ export interface GoogleGenAIThreadManager extends ProviderThreadManager<
|
|
|
50
56
|
prepareForInvocation(): Promise<GoogleGenAIInvocationPayload>;
|
|
51
57
|
}
|
|
52
58
|
|
|
53
|
-
|
|
59
|
+
/** Extract the unique id from a {@link StoredContent}. */
|
|
60
|
+
export function storedContentId(msg: StoredContent): string {
|
|
54
61
|
return msg.id;
|
|
55
62
|
}
|
|
56
63
|
|
|
@@ -103,6 +110,7 @@ export function createGoogleGenAIThreadManager(
|
|
|
103
110
|
threadId: config.threadId,
|
|
104
111
|
key: config.key,
|
|
105
112
|
idOf: storedContentId,
|
|
113
|
+
...(config.ttlSeconds !== undefined && { ttlSeconds: config.ttlSeconds }),
|
|
106
114
|
};
|
|
107
115
|
|
|
108
116
|
const base = createThreadManager(baseConfig);
|
|
@@ -13,10 +13,13 @@ import type {
|
|
|
13
13
|
ScopedPrefix,
|
|
14
14
|
} from "../../../lib/session/types";
|
|
15
15
|
import type { ModelInvoker } from "../../../lib/model";
|
|
16
|
+
import { createTieredThreadManager } from "../../../lib/thread/tiered";
|
|
17
|
+
import type { ColdThreadStore } from "../../../lib/thread/cold-store";
|
|
16
18
|
import type { StoredMessage } from "@langchain/core/messages";
|
|
17
19
|
import type { BaseChatModel } from "@langchain/core/language_models/chat_models";
|
|
18
20
|
import {
|
|
19
21
|
createLangChainThreadManager,
|
|
22
|
+
storedMessageId,
|
|
20
23
|
type LangChainContent,
|
|
21
24
|
type LangChainSystemContent,
|
|
22
25
|
type LangChainThreadManagerHooks,
|
|
@@ -35,6 +38,19 @@ export interface LangChainAdapterConfig {
|
|
|
35
38
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
36
39
|
model?: BaseChatModel<any>;
|
|
37
40
|
hooks?: LangChainThreadManagerHooks;
|
|
41
|
+
/**
|
|
42
|
+
* Optional durable cold tier (e.g. S3, R2, GCS). When provided,
|
|
43
|
+
* the session hydrates the thread on entry (`continue`/`fork`) and
|
|
44
|
+
* flushes it on every exit path. When omitted, the adapter is
|
|
45
|
+
* Redis-only and `hydrateThread`/`flushThread` activities are no-ops.
|
|
46
|
+
*/
|
|
47
|
+
coldStore?: ColdThreadStore;
|
|
48
|
+
/**
|
|
49
|
+
* Override the default Redis TTL (90 days). When pairing the
|
|
50
|
+
* adapter with a `coldStore`, a shorter TTL (hours) is typically
|
|
51
|
+
* more appropriate.
|
|
52
|
+
*/
|
|
53
|
+
ttlSeconds?: number;
|
|
38
54
|
}
|
|
39
55
|
|
|
40
56
|
/**
|
|
@@ -117,16 +133,34 @@ export function createLangChainAdapter(
|
|
|
117
133
|
): LangChainAdapter {
|
|
118
134
|
const { redis } = config;
|
|
119
135
|
|
|
136
|
+
const baseExtras = {
|
|
137
|
+
...(config.ttlSeconds !== undefined && { ttlSeconds: config.ttlSeconds }),
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
const makeProviderThread = (threadId: string, threadKey?: string) =>
|
|
141
|
+
createLangChainThreadManager({
|
|
142
|
+
redis,
|
|
143
|
+
threadId,
|
|
144
|
+
key: threadKey,
|
|
145
|
+
...baseExtras,
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
const makeTieredBase = (threadId: string, threadKey?: string) =>
|
|
149
|
+
createTieredThreadManager<StoredMessage>({
|
|
150
|
+
redis,
|
|
151
|
+
threadId,
|
|
152
|
+
key: threadKey,
|
|
153
|
+
idOf: storedMessageId,
|
|
154
|
+
...baseExtras,
|
|
155
|
+
...(config.coldStore && { coldStore: config.coldStore }),
|
|
156
|
+
});
|
|
157
|
+
|
|
120
158
|
const threadOps: ThreadOps<LangChainContent> = {
|
|
121
159
|
async initializeThread(
|
|
122
160
|
threadId: string,
|
|
123
161
|
threadKey?: string
|
|
124
162
|
): Promise<void> {
|
|
125
|
-
const thread =
|
|
126
|
-
redis,
|
|
127
|
-
threadId,
|
|
128
|
-
key: threadKey,
|
|
129
|
-
});
|
|
163
|
+
const thread = makeProviderThread(threadId, threadKey);
|
|
130
164
|
await thread.initialize();
|
|
131
165
|
},
|
|
132
166
|
|
|
@@ -136,11 +170,7 @@ export function createLangChainAdapter(
|
|
|
136
170
|
content: LangChainContent,
|
|
137
171
|
threadKey?: string
|
|
138
172
|
): Promise<void> {
|
|
139
|
-
const thread =
|
|
140
|
-
redis,
|
|
141
|
-
threadId,
|
|
142
|
-
key: threadKey,
|
|
143
|
-
});
|
|
173
|
+
const thread = makeProviderThread(threadId, threadKey);
|
|
144
174
|
await thread.appendUserMessage(id, content);
|
|
145
175
|
},
|
|
146
176
|
|
|
@@ -150,21 +180,13 @@ export function createLangChainAdapter(
|
|
|
150
180
|
content: LangChainSystemContent,
|
|
151
181
|
threadKey?: string
|
|
152
182
|
): Promise<void> {
|
|
153
|
-
const thread =
|
|
154
|
-
redis,
|
|
155
|
-
threadId,
|
|
156
|
-
key: threadKey,
|
|
157
|
-
});
|
|
183
|
+
const thread = makeProviderThread(threadId, threadKey);
|
|
158
184
|
await thread.appendSystemMessage(id, content);
|
|
159
185
|
},
|
|
160
186
|
|
|
161
187
|
async appendToolResult(id: string, cfg: ToolResultConfig): Promise<void> {
|
|
162
188
|
const { threadId, threadKey, toolCallId, content } = cfg;
|
|
163
|
-
const thread =
|
|
164
|
-
redis,
|
|
165
|
-
threadId,
|
|
166
|
-
key: threadKey,
|
|
167
|
-
});
|
|
189
|
+
const thread = makeProviderThread(threadId, threadKey);
|
|
168
190
|
await thread.appendToolResult(id, toolCallId, "", content);
|
|
169
191
|
},
|
|
170
192
|
|
|
@@ -174,11 +196,7 @@ export function createLangChainAdapter(
|
|
|
174
196
|
message: StoredMessage,
|
|
175
197
|
threadKey?: string
|
|
176
198
|
): Promise<void> {
|
|
177
|
-
const thread =
|
|
178
|
-
redis,
|
|
179
|
-
threadId,
|
|
180
|
-
key: threadKey,
|
|
181
|
-
});
|
|
199
|
+
const thread = makeProviderThread(threadId, threadKey);
|
|
182
200
|
const patched = { ...message, data: { ...message.data, id } };
|
|
183
201
|
await thread.append([patched]);
|
|
184
202
|
},
|
|
@@ -193,6 +211,7 @@ export function createLangChainAdapter(
|
|
|
193
211
|
threadId: sourceThreadId,
|
|
194
212
|
key: threadKey,
|
|
195
213
|
hooks: config.hooks,
|
|
214
|
+
...baseExtras,
|
|
196
215
|
});
|
|
197
216
|
await thread.fork(targetThreadId);
|
|
198
217
|
},
|
|
@@ -202,7 +221,7 @@ export function createLangChainAdapter(
|
|
|
202
221
|
messageId: string,
|
|
203
222
|
threadKey?: string,
|
|
204
223
|
): Promise<void> {
|
|
205
|
-
const thread =
|
|
224
|
+
const thread = makeProviderThread(threadId, threadKey);
|
|
206
225
|
await thread.truncateFromId(messageId);
|
|
207
226
|
},
|
|
208
227
|
|
|
@@ -210,11 +229,7 @@ export function createLangChainAdapter(
|
|
|
210
229
|
threadId: string,
|
|
211
230
|
threadKey?: string
|
|
212
231
|
): Promise<PersistedThreadState | null> {
|
|
213
|
-
const thread =
|
|
214
|
-
redis,
|
|
215
|
-
threadId,
|
|
216
|
-
key: threadKey,
|
|
217
|
-
});
|
|
232
|
+
const thread = makeProviderThread(threadId, threadKey);
|
|
218
233
|
return thread.loadState();
|
|
219
234
|
},
|
|
220
235
|
|
|
@@ -223,13 +238,25 @@ export function createLangChainAdapter(
|
|
|
223
238
|
state: PersistedThreadState,
|
|
224
239
|
threadKey?: string
|
|
225
240
|
): Promise<void> {
|
|
226
|
-
const thread =
|
|
227
|
-
redis,
|
|
228
|
-
threadId,
|
|
229
|
-
key: threadKey,
|
|
230
|
-
});
|
|
241
|
+
const thread = makeProviderThread(threadId, threadKey);
|
|
231
242
|
await thread.saveState(state);
|
|
232
243
|
},
|
|
244
|
+
|
|
245
|
+
async hydrateThread(
|
|
246
|
+
threadId: string,
|
|
247
|
+
threadKey?: string
|
|
248
|
+
): Promise<void> {
|
|
249
|
+
if (!config.coldStore) return;
|
|
250
|
+
await makeTieredBase(threadId, threadKey).hydrate();
|
|
251
|
+
},
|
|
252
|
+
|
|
253
|
+
async flushThread(
|
|
254
|
+
threadId: string,
|
|
255
|
+
threadKey?: string
|
|
256
|
+
): Promise<void> {
|
|
257
|
+
if (!config.coldStore) return;
|
|
258
|
+
await makeTieredBase(threadId, threadKey).flush();
|
|
259
|
+
},
|
|
233
260
|
};
|
|
234
261
|
|
|
235
262
|
function createActivities<S extends string = "">(
|
|
@@ -25,6 +25,7 @@ import { createThreadOpsProxy } from "../../../lib/thread/proxy";
|
|
|
25
25
|
import { ADAPTER_ID } from "./adapter-id";
|
|
26
26
|
|
|
27
27
|
export { ADAPTER_ID, type AdapterId } from "./adapter-id";
|
|
28
|
+
export type { ThreadOpsProxyOptions } from "../../../lib/thread/proxy";
|
|
28
29
|
|
|
29
30
|
export function proxyLangChainThreadOps(
|
|
30
31
|
scope?: string,
|
|
@@ -34,6 +34,12 @@ export interface LangChainThreadManagerConfig {
|
|
|
34
34
|
/** Thread key, defaults to 'messages' */
|
|
35
35
|
key?: string;
|
|
36
36
|
hooks?: LangChainThreadManagerHooks;
|
|
37
|
+
/**
|
|
38
|
+
* Override the default thread TTL (90 days). When pairing the
|
|
39
|
+
* adapter with a durable cold tier, a shorter TTL (hours) is
|
|
40
|
+
* typically more appropriate.
|
|
41
|
+
*/
|
|
42
|
+
ttlSeconds?: number;
|
|
37
43
|
}
|
|
38
44
|
|
|
39
45
|
/** Prepared payload ready to send to a LangChain chat model */
|
|
@@ -52,7 +58,8 @@ export interface LangChainThreadManager extends ProviderThreadManager<
|
|
|
52
58
|
prepareForInvocation(): Promise<LangChainInvocationPayload>;
|
|
53
59
|
}
|
|
54
60
|
|
|
55
|
-
|
|
61
|
+
/** Extract the unique id from a LangChain {@link StoredMessage}. */
|
|
62
|
+
export function storedMessageId(msg: StoredMessage): string {
|
|
56
63
|
if (msg.type === "tool" && msg.data.tool_call_id) {
|
|
57
64
|
return msg.data.tool_call_id;
|
|
58
65
|
}
|
|
@@ -77,6 +84,7 @@ export function createLangChainThreadManager(
|
|
|
77
84
|
threadId: config.threadId,
|
|
78
85
|
key: config.key,
|
|
79
86
|
idOf: storedMessageId,
|
|
87
|
+
...(config.ttlSeconds !== undefined && { ttlSeconds: config.ttlSeconds }),
|
|
80
88
|
};
|
|
81
89
|
|
|
82
90
|
const base = createThreadManager(baseConfig);
|
package/src/index.ts
CHANGED
|
@@ -33,11 +33,30 @@ export * from "./workflow";
|
|
|
33
33
|
export { FileSystemSkillProvider } from "./lib/skills/fs-provider";
|
|
34
34
|
|
|
35
35
|
// Thread manager (generic, framework-agnostic)
|
|
36
|
-
export {
|
|
36
|
+
export {
|
|
37
|
+
createThreadManager,
|
|
38
|
+
createTieredThreadManager,
|
|
39
|
+
createS3ColdStore,
|
|
40
|
+
encodeSnapshot,
|
|
41
|
+
applySnapshot,
|
|
42
|
+
clearHotTier,
|
|
43
|
+
getThreadStateKey,
|
|
44
|
+
getThreadDedupKey,
|
|
45
|
+
} from "./lib/thread";
|
|
37
46
|
export type {
|
|
38
47
|
BaseThreadManager,
|
|
39
48
|
ProviderThreadManager,
|
|
40
49
|
ThreadManagerConfig,
|
|
50
|
+
TieredThreadManager,
|
|
51
|
+
TieredThreadManagerConfig,
|
|
52
|
+
FlushOptions,
|
|
53
|
+
ColdThreadStore,
|
|
54
|
+
ThreadSnapshot,
|
|
55
|
+
S3LikeClient,
|
|
56
|
+
S3ColdStoreConfig,
|
|
57
|
+
EncodeSnapshotConfig,
|
|
58
|
+
ApplySnapshotConfig,
|
|
59
|
+
ClearHotTierConfig,
|
|
41
60
|
} from "./lib/thread";
|
|
42
61
|
|
|
43
62
|
// Model invoker contract (framework-agnostic)
|
|
@@ -74,7 +93,7 @@ export type { VirtualFsContext } from "./lib/virtual-fs/types";
|
|
|
74
93
|
// Tool handlers (activity implementations)
|
|
75
94
|
// Wrap sandbox handlers with withSandbox(manager, handler) at registration time
|
|
76
95
|
export { bashHandler } from "./tools/bash/handler";
|
|
77
|
-
export { editHandler } from "./tools/edit/handler";
|
|
96
|
+
export { editHandler, multiEditHandler } from "./tools/edit/handler";
|
|
78
97
|
export { globHandler } from "./tools/glob/handler";
|
|
79
98
|
export { readFileHandler } from "./tools/read-file/handler";
|
|
80
99
|
export { writeFileHandler } from "./tools/write-file/handler";
|