zeitlich 0.2.21 → 0.2.22
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 +70 -55
- package/dist/adapters/sandbox/daytona/index.cjs +3 -0
- package/dist/adapters/sandbox/daytona/index.cjs.map +1 -1
- package/dist/adapters/sandbox/daytona/index.d.cts +2 -1
- package/dist/adapters/sandbox/daytona/index.d.ts +2 -1
- package/dist/adapters/sandbox/daytona/index.js +3 -0
- package/dist/adapters/sandbox/daytona/index.js.map +1 -1
- package/dist/adapters/sandbox/daytona/workflow.cjs +32 -0
- package/dist/adapters/sandbox/daytona/workflow.cjs.map +1 -0
- package/dist/adapters/sandbox/daytona/workflow.d.cts +27 -0
- package/dist/adapters/sandbox/daytona/workflow.d.ts +27 -0
- package/dist/adapters/sandbox/daytona/workflow.js +30 -0
- package/dist/adapters/sandbox/daytona/workflow.js.map +1 -0
- package/dist/adapters/sandbox/inmemory/index.cjs +4 -1
- package/dist/adapters/sandbox/inmemory/index.cjs.map +1 -1
- package/dist/adapters/sandbox/inmemory/index.d.cts +3 -2
- package/dist/adapters/sandbox/inmemory/index.d.ts +3 -2
- package/dist/adapters/sandbox/inmemory/index.js +4 -1
- package/dist/adapters/sandbox/inmemory/index.js.map +1 -1
- package/dist/adapters/sandbox/inmemory/workflow.cjs +32 -0
- package/dist/adapters/sandbox/inmemory/workflow.cjs.map +1 -0
- package/dist/adapters/sandbox/inmemory/workflow.d.cts +25 -0
- package/dist/adapters/sandbox/inmemory/workflow.d.ts +25 -0
- package/dist/adapters/sandbox/inmemory/workflow.js +30 -0
- package/dist/adapters/sandbox/inmemory/workflow.js.map +1 -0
- package/dist/adapters/sandbox/virtual/index.cjs +3 -0
- package/dist/adapters/sandbox/virtual/index.cjs.map +1 -1
- package/dist/adapters/sandbox/virtual/index.d.cts +6 -4
- package/dist/adapters/sandbox/virtual/index.d.ts +6 -4
- package/dist/adapters/sandbox/virtual/index.js +3 -0
- package/dist/adapters/sandbox/virtual/index.js.map +1 -1
- package/dist/adapters/sandbox/virtual/workflow.cjs +32 -0
- package/dist/adapters/sandbox/virtual/workflow.cjs.map +1 -0
- package/dist/adapters/sandbox/virtual/workflow.d.cts +27 -0
- package/dist/adapters/sandbox/virtual/workflow.d.ts +27 -0
- package/dist/adapters/sandbox/virtual/workflow.js +30 -0
- package/dist/adapters/sandbox/virtual/workflow.js.map +1 -0
- package/dist/adapters/thread/google-genai/index.cjs +9 -1
- package/dist/adapters/thread/google-genai/index.cjs.map +1 -1
- package/dist/adapters/thread/google-genai/index.d.cts +30 -18
- package/dist/adapters/thread/google-genai/index.d.ts +30 -18
- package/dist/adapters/thread/google-genai/index.js +9 -1
- package/dist/adapters/thread/google-genai/index.js.map +1 -1
- package/dist/adapters/thread/google-genai/workflow.cjs +33 -0
- package/dist/adapters/thread/google-genai/workflow.cjs.map +1 -0
- package/dist/adapters/thread/google-genai/workflow.d.cts +32 -0
- package/dist/adapters/thread/google-genai/workflow.d.ts +32 -0
- package/dist/adapters/thread/google-genai/workflow.js +31 -0
- package/dist/adapters/thread/google-genai/workflow.js.map +1 -0
- package/dist/adapters/thread/langchain/index.cjs +9 -1
- package/dist/adapters/thread/langchain/index.cjs.map +1 -1
- package/dist/adapters/thread/langchain/index.d.cts +26 -15
- package/dist/adapters/thread/langchain/index.d.ts +26 -15
- package/dist/adapters/thread/langchain/index.js +9 -1
- package/dist/adapters/thread/langchain/index.js.map +1 -1
- package/dist/adapters/thread/langchain/workflow.cjs +33 -0
- package/dist/adapters/thread/langchain/workflow.cjs.map +1 -0
- package/dist/adapters/thread/langchain/workflow.d.cts +32 -0
- package/dist/adapters/thread/langchain/workflow.d.ts +32 -0
- package/dist/adapters/thread/langchain/workflow.js +31 -0
- package/dist/adapters/thread/langchain/workflow.js.map +1 -0
- package/dist/index.cjs +36 -34
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +35 -14
- package/dist/index.d.ts +35 -14
- package/dist/index.js +38 -34
- package/dist/index.js.map +1 -1
- package/dist/queries-Bw6WEPMw.d.cts +44 -0
- package/dist/queries-C27raDaB.d.ts +44 -0
- package/dist/{queries-CHa2iv_I.d.cts → types-BJ8itUAl.d.cts} +2 -43
- package/dist/{types-BkAYmc96.d.ts → types-C5bkx6kQ.d.ts} +33 -5
- package/dist/{types-CES_30qx.d.cts → types-ClsHhtwL.d.cts} +33 -5
- package/dist/{queries-6Avfh74U.d.ts → types-ENYCKFBk.d.ts} +2 -43
- package/dist/{types-BMRzfELQ.d.cts → types-HBosetv3.d.cts} +15 -1
- package/dist/{types-BMRzfELQ.d.ts → types-HBosetv3.d.ts} +15 -1
- package/dist/workflow.cjs +4 -30
- package/dist/workflow.cjs.map +1 -1
- package/dist/workflow.d.cts +13 -41
- package/dist/workflow.d.ts +13 -41
- package/dist/workflow.js +6 -30
- package/dist/workflow.js.map +1 -1
- package/package.json +53 -1
- package/src/adapters/sandbox/daytona/index.ts +4 -0
- package/src/adapters/sandbox/daytona/proxy.ts +55 -0
- package/src/adapters/sandbox/e2b/filesystem.ts +147 -0
- package/src/adapters/sandbox/e2b/index.ts +159 -0
- package/src/adapters/sandbox/e2b/types.ts +23 -0
- package/src/adapters/sandbox/inmemory/index.ts +5 -1
- package/src/adapters/sandbox/inmemory/proxy.ts +53 -0
- package/src/adapters/sandbox/virtual/provider.ts +5 -1
- package/src/adapters/sandbox/virtual/proxy.ts +52 -0
- package/src/adapters/thread/google-genai/activities.ts +51 -17
- package/src/adapters/thread/google-genai/index.ts +1 -0
- package/src/adapters/thread/google-genai/proxy.ts +61 -0
- package/src/adapters/thread/langchain/activities.ts +47 -14
- package/src/adapters/thread/langchain/index.ts +1 -0
- package/src/adapters/thread/langchain/proxy.ts +61 -0
- package/src/lib/sandbox/manager.ts +40 -6
- package/src/lib/sandbox/sandbox.test.ts +12 -11
- package/src/lib/sandbox/types.ts +18 -0
- package/src/lib/session/index.ts +3 -5
- package/src/lib/session/session-edge-cases.integration.test.ts +45 -34
- package/src/lib/session/session.integration.test.ts +40 -48
- package/src/lib/session/session.ts +4 -66
- package/src/lib/session/types.ts +32 -1
- package/src/lib/subagent/define.ts +1 -1
- package/src/lib/subagent/handler.ts +9 -2
- package/src/lib/subagent/index.ts +1 -0
- package/src/lib/subagent/subagent.integration.test.ts +62 -0
- package/src/lib/subagent/types.ts +7 -2
- package/src/lib/tool-router/router-edge-cases.integration.test.ts +4 -1
- package/src/lib/tool-router/router.integration.test.ts +4 -1
- package/src/lib/workflow.test.ts +19 -10
- package/src/lib/workflow.ts +4 -1
- package/src/tools/bash/bash.test.ts +16 -7
- package/src/workflow.ts +6 -14
- package/tsup.config.ts +6 -0
|
@@ -2,11 +2,20 @@ import type Redis from "ioredis";
|
|
|
2
2
|
import type { GoogleGenAI, Content } from "@google/genai";
|
|
3
3
|
import type { ToolResultConfig } from "../../../lib/types";
|
|
4
4
|
import type { MessageContent } from "../../../lib/types";
|
|
5
|
-
import type {
|
|
5
|
+
import type {
|
|
6
|
+
ThreadOps,
|
|
7
|
+
PrefixedThreadOps,
|
|
8
|
+
ScopedPrefix,
|
|
9
|
+
} from "../../../lib/session/types";
|
|
6
10
|
import type { ModelInvoker } from "../../../lib/model";
|
|
7
11
|
import { createGoogleGenAIThreadManager } from "./thread-manager";
|
|
8
12
|
import { createGoogleGenAIModelInvoker } from "./model-invoker";
|
|
9
13
|
|
|
14
|
+
const ADAPTER_PREFIX = "googleGenAI" as const;
|
|
15
|
+
|
|
16
|
+
export type GoogleGenAIThreadOps<TScope extends string = ""> =
|
|
17
|
+
PrefixedThreadOps<ScopedPrefix<TScope, typeof ADAPTER_PREFIX>>;
|
|
18
|
+
|
|
10
19
|
export interface GoogleGenAIAdapterConfig {
|
|
11
20
|
redis: Redis;
|
|
12
21
|
client: GoogleGenAI;
|
|
@@ -15,21 +24,37 @@ export interface GoogleGenAIAdapterConfig {
|
|
|
15
24
|
}
|
|
16
25
|
|
|
17
26
|
export interface GoogleGenAIAdapter {
|
|
18
|
-
/** Thread operations (register these as Temporal activities on the worker) */
|
|
19
|
-
threadOps: ThreadOps;
|
|
20
27
|
/** Model invoker using the default model (only available when `model` was provided) */
|
|
21
28
|
invoker: ModelInvoker<Content>;
|
|
22
29
|
/** Create an invoker for a specific model name (for multi-model setups) */
|
|
23
30
|
createModelInvoker(model: string): ModelInvoker<Content>;
|
|
31
|
+
/**
|
|
32
|
+
* Create prefixed thread activities for registration on the worker.
|
|
33
|
+
*
|
|
34
|
+
* @param scope - Workflow name appended to the adapter prefix.
|
|
35
|
+
* Use different scopes for the main agent vs subagents to avoid collisions.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```typescript
|
|
39
|
+
* adapter.createActivities("codingAgent")
|
|
40
|
+
* // → { googleGenAICodingAgentInitializeThread, googleGenAICodingAgentAppendHumanMessage, … }
|
|
41
|
+
*
|
|
42
|
+
* adapter.createActivities("researchAgent")
|
|
43
|
+
* // → { googleGenAIResearchAgentInitializeThread, … }
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
createActivities<S extends string = "">(
|
|
47
|
+
scope?: S
|
|
48
|
+
): GoogleGenAIThreadOps<S>;
|
|
24
49
|
}
|
|
25
50
|
|
|
26
51
|
/**
|
|
27
52
|
* Creates a Google GenAI adapter that bundles thread operations and model
|
|
28
53
|
* invocation using the `@google/genai` SDK.
|
|
29
54
|
*
|
|
30
|
-
*
|
|
31
|
-
* the worker. The `invoker` (or invokers created via
|
|
32
|
-
* should be wrapped with `createRunAgentActivity
|
|
55
|
+
* Use `createActivities(scope)` to register scoped thread operations as
|
|
56
|
+
* Temporal activities on the worker. The `invoker` (or invokers created via
|
|
57
|
+
* `createModelInvoker`) should be wrapped with `createRunAgentActivity`.
|
|
33
58
|
*
|
|
34
59
|
* @example
|
|
35
60
|
* ```typescript
|
|
@@ -42,27 +67,23 @@ export interface GoogleGenAIAdapter {
|
|
|
42
67
|
*
|
|
43
68
|
* export function createActivities(temporalClient: WorkflowClient) {
|
|
44
69
|
* return {
|
|
45
|
-
* ...adapter.
|
|
46
|
-
*
|
|
70
|
+
* ...adapter.createActivities("codingAgent"),
|
|
71
|
+
* runCodingAgent: createRunAgentActivity(temporalClient, adapter.invoker),
|
|
47
72
|
* };
|
|
48
73
|
* }
|
|
49
74
|
* ```
|
|
50
75
|
*
|
|
51
|
-
* @example Multi-
|
|
76
|
+
* @example Multi-agent worker (main + subagent share the adapter)
|
|
52
77
|
* ```typescript
|
|
53
|
-
* const adapter = createGoogleGenAIAdapter({ redis, client });
|
|
54
|
-
*
|
|
55
78
|
* export function createActivities(temporalClient: WorkflowClient) {
|
|
56
79
|
* return {
|
|
57
|
-
* ...adapter.
|
|
80
|
+
* ...adapter.createActivities("codingAgent"),
|
|
81
|
+
* ...adapter.createActivities("researchAgent"),
|
|
82
|
+
* runCodingAgent: createRunAgentActivity(temporalClient, adapter.invoker),
|
|
58
83
|
* runResearchAgent: createRunAgentActivity(
|
|
59
84
|
* temporalClient,
|
|
60
85
|
* adapter.createModelInvoker('gemini-2.5-pro'),
|
|
61
86
|
* ),
|
|
62
|
-
* runFastAgent: createRunAgentActivity(
|
|
63
|
-
* temporalClient,
|
|
64
|
-
* adapter.createModelInvoker('gemini-2.5-flash'),
|
|
65
|
-
* ),
|
|
66
87
|
* };
|
|
67
88
|
* }
|
|
68
89
|
* ```
|
|
@@ -114,6 +135,19 @@ export function createGoogleGenAIAdapter(
|
|
|
114
135
|
},
|
|
115
136
|
};
|
|
116
137
|
|
|
138
|
+
function createActivities<S extends string = "">(
|
|
139
|
+
scope?: S
|
|
140
|
+
): GoogleGenAIThreadOps<S> {
|
|
141
|
+
const prefix = scope
|
|
142
|
+
? `${ADAPTER_PREFIX}${scope.charAt(0).toUpperCase()}${scope.slice(1)}`
|
|
143
|
+
: ADAPTER_PREFIX;
|
|
144
|
+
const cap = (s: string): string =>
|
|
145
|
+
s.charAt(0).toUpperCase() + s.slice(1);
|
|
146
|
+
return Object.fromEntries(
|
|
147
|
+
Object.entries(threadOps).map(([k, v]) => [`${prefix}${cap(k)}`, v])
|
|
148
|
+
) as GoogleGenAIThreadOps<S>;
|
|
149
|
+
}
|
|
150
|
+
|
|
117
151
|
const makeInvoker = (model: string): ModelInvoker<Content> =>
|
|
118
152
|
createGoogleGenAIModelInvoker({ redis, client, model });
|
|
119
153
|
|
|
@@ -127,7 +161,7 @@ export function createGoogleGenAIAdapter(
|
|
|
127
161
|
}) as unknown as ModelInvoker<Content>);
|
|
128
162
|
|
|
129
163
|
return {
|
|
130
|
-
|
|
164
|
+
createActivities,
|
|
131
165
|
invoker,
|
|
132
166
|
createModelInvoker: makeInvoker,
|
|
133
167
|
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow-safe proxy for Google GenAI thread operations.
|
|
3
|
+
*
|
|
4
|
+
* Import this from `zeitlich/adapters/thread/google-genai/workflow`
|
|
5
|
+
* in your Temporal workflow files.
|
|
6
|
+
*
|
|
7
|
+
* By default the scope is derived from `workflowInfo().workflowType`,
|
|
8
|
+
* so activities are automatically namespaced per workflow.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* import { proxyGoogleGenAIThreadOps } from 'zeitlich/adapters/thread/google-genai/workflow';
|
|
13
|
+
*
|
|
14
|
+
* // Auto-scoped to the current workflow name
|
|
15
|
+
* const threadOps = proxyGoogleGenAIThreadOps();
|
|
16
|
+
*
|
|
17
|
+
* // Explicit scope override
|
|
18
|
+
* const threadOps = proxyGoogleGenAIThreadOps("customScope");
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
import {
|
|
22
|
+
proxyActivities,
|
|
23
|
+
workflowInfo,
|
|
24
|
+
type ActivityInterfaceFor,
|
|
25
|
+
} from "@temporalio/workflow";
|
|
26
|
+
import type { ThreadOps } from "../../../lib/session/types";
|
|
27
|
+
|
|
28
|
+
const ADAPTER_PREFIX = "googleGenAI";
|
|
29
|
+
|
|
30
|
+
export function proxyGoogleGenAIThreadOps(
|
|
31
|
+
scope?: string,
|
|
32
|
+
options?: Parameters<typeof proxyActivities>[0]
|
|
33
|
+
): ActivityInterfaceFor<ThreadOps> {
|
|
34
|
+
const resolvedScope = scope ?? workflowInfo().workflowType;
|
|
35
|
+
|
|
36
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
37
|
+
const acts = proxyActivities<Record<string, (...args: any[]) => any>>(
|
|
38
|
+
options ?? {
|
|
39
|
+
startToCloseTimeout: "10s",
|
|
40
|
+
retry: {
|
|
41
|
+
maximumAttempts: 6,
|
|
42
|
+
initialInterval: "5s",
|
|
43
|
+
maximumInterval: "15m",
|
|
44
|
+
backoffCoefficient: 4,
|
|
45
|
+
},
|
|
46
|
+
}
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
const prefix =
|
|
50
|
+
`${ADAPTER_PREFIX}${resolvedScope.charAt(0).toUpperCase()}${resolvedScope.slice(1)}`;
|
|
51
|
+
const p = (key: string): string =>
|
|
52
|
+
`${prefix}${key.charAt(0).toUpperCase()}${key.slice(1)}`;
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
initializeThread: acts[p("initializeThread")],
|
|
56
|
+
appendHumanMessage: acts[p("appendHumanMessage")],
|
|
57
|
+
appendToolResult: acts[p("appendToolResult")],
|
|
58
|
+
appendSystemMessage: acts[p("appendSystemMessage")],
|
|
59
|
+
forkThread: acts[p("forkThread")],
|
|
60
|
+
} as ActivityInterfaceFor<ThreadOps>;
|
|
61
|
+
}
|
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
import type Redis from "ioredis";
|
|
2
2
|
import type { ToolResultConfig } from "../../../lib/types";
|
|
3
3
|
import type { MessageContent } from "@langchain/core/messages";
|
|
4
|
-
import type {
|
|
4
|
+
import type {
|
|
5
|
+
ThreadOps,
|
|
6
|
+
PrefixedThreadOps,
|
|
7
|
+
ScopedPrefix,
|
|
8
|
+
} from "../../../lib/session/types";
|
|
5
9
|
import type { ModelInvoker } from "../../../lib/model";
|
|
6
10
|
import type { StoredMessage } from "@langchain/core/messages";
|
|
7
11
|
import type { BaseChatModel } from "@langchain/core/language_models/chat_models";
|
|
8
12
|
import { createLangChainThreadManager } from "./thread-manager";
|
|
9
13
|
import { createLangChainModelInvoker } from "./model-invoker";
|
|
10
14
|
|
|
15
|
+
const ADAPTER_PREFIX = "langChain" as const;
|
|
16
|
+
|
|
17
|
+
export type LangChainThreadOps<TScope extends string = ""> =
|
|
18
|
+
PrefixedThreadOps<ScopedPrefix<TScope, typeof ADAPTER_PREFIX>>;
|
|
19
|
+
|
|
11
20
|
export interface LangChainAdapterConfig {
|
|
12
21
|
redis: Redis;
|
|
13
22
|
/** Optional default model — if omitted, use `createModelInvoker()` to create invokers later */
|
|
@@ -16,22 +25,34 @@ export interface LangChainAdapterConfig {
|
|
|
16
25
|
}
|
|
17
26
|
|
|
18
27
|
export interface LangChainAdapter {
|
|
19
|
-
/** Thread operations (register these as Temporal activities on the worker) */
|
|
20
|
-
threadOps: ThreadOps;
|
|
21
28
|
/** Model invoker using the default model (only available when `model` was provided) */
|
|
22
29
|
invoker: ModelInvoker<StoredMessage>;
|
|
23
30
|
/** Create an invoker for a specific model (for multi-model setups) */
|
|
24
31
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
25
32
|
createModelInvoker(model: BaseChatModel<any>): ModelInvoker<StoredMessage>;
|
|
33
|
+
/**
|
|
34
|
+
* Create prefixed thread activities for registration on the worker.
|
|
35
|
+
*
|
|
36
|
+
* @param scope - Workflow name appended to the adapter prefix.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```typescript
|
|
40
|
+
* adapter.createActivities("codingAgent")
|
|
41
|
+
* // → { langChainCodingAgentInitializeThread, langChainCodingAgentAppendHumanMessage, … }
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
createActivities<S extends string = "">(
|
|
45
|
+
scope?: S
|
|
46
|
+
): LangChainThreadOps<S>;
|
|
26
47
|
}
|
|
27
48
|
|
|
28
49
|
/**
|
|
29
50
|
* Creates a LangChain adapter that bundles thread operations and model
|
|
30
51
|
* invocation using a consistent message format (StoredMessage).
|
|
31
52
|
*
|
|
32
|
-
*
|
|
33
|
-
* the worker. The `invoker` (or invokers created via
|
|
34
|
-
* should be wrapped with `createRunAgentActivity
|
|
53
|
+
* Use `createActivities(scope)` to register scoped thread operations as
|
|
54
|
+
* Temporal activities on the worker. The `invoker` (or invokers created via
|
|
55
|
+
* `createModelInvoker`) should be wrapped with `createRunAgentActivity`.
|
|
35
56
|
*
|
|
36
57
|
* @example
|
|
37
58
|
* ```typescript
|
|
@@ -42,21 +63,20 @@ export interface LangChainAdapter {
|
|
|
42
63
|
*
|
|
43
64
|
* export function createActivities(client: WorkflowClient) {
|
|
44
65
|
* return {
|
|
45
|
-
* ...adapter.
|
|
46
|
-
*
|
|
66
|
+
* ...adapter.createActivities("codingAgent"),
|
|
67
|
+
* runCodingAgent: createRunAgentActivity(client, adapter.invoker),
|
|
47
68
|
* };
|
|
48
69
|
* }
|
|
49
70
|
* ```
|
|
50
71
|
*
|
|
51
|
-
* @example Multi-
|
|
72
|
+
* @example Multi-agent worker
|
|
52
73
|
* ```typescript
|
|
53
|
-
* const adapter = createLangChainAdapter({ redis });
|
|
54
|
-
*
|
|
55
74
|
* export function createActivities(client: WorkflowClient) {
|
|
56
75
|
* return {
|
|
57
|
-
* ...adapter.
|
|
76
|
+
* ...adapter.createActivities("codingAgent"),
|
|
77
|
+
* ...adapter.createActivities("researchAgent"),
|
|
78
|
+
* runCodingAgent: createRunAgentActivity(client, adapter.invoker),
|
|
58
79
|
* runResearchAgent: createRunAgentActivity(client, adapter.createModelInvoker(claude)),
|
|
59
|
-
* runWriterAgent: createRunAgentActivity(client, adapter.createModelInvoker(gpt4)),
|
|
60
80
|
* };
|
|
61
81
|
* }
|
|
62
82
|
* ```
|
|
@@ -108,6 +128,19 @@ export function createLangChainAdapter(
|
|
|
108
128
|
},
|
|
109
129
|
};
|
|
110
130
|
|
|
131
|
+
function createActivities<S extends string = "">(
|
|
132
|
+
scope?: S
|
|
133
|
+
): LangChainThreadOps<S> {
|
|
134
|
+
const prefix = scope
|
|
135
|
+
? `${ADAPTER_PREFIX}${scope.charAt(0).toUpperCase()}${scope.slice(1)}`
|
|
136
|
+
: ADAPTER_PREFIX;
|
|
137
|
+
const cap = (s: string): string =>
|
|
138
|
+
s.charAt(0).toUpperCase() + s.slice(1);
|
|
139
|
+
return Object.fromEntries(
|
|
140
|
+
Object.entries(threadOps).map(([k, v]) => [`${prefix}${cap(k)}`, v])
|
|
141
|
+
) as LangChainThreadOps<S>;
|
|
142
|
+
}
|
|
143
|
+
|
|
111
144
|
const makeInvoker = (
|
|
112
145
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
113
146
|
model: BaseChatModel<any>
|
|
@@ -124,7 +157,7 @@ export function createLangChainAdapter(
|
|
|
124
157
|
};
|
|
125
158
|
|
|
126
159
|
return {
|
|
127
|
-
|
|
160
|
+
createActivities,
|
|
128
161
|
invoker,
|
|
129
162
|
createModelInvoker: makeInvoker,
|
|
130
163
|
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow-safe proxy for LangChain thread operations.
|
|
3
|
+
*
|
|
4
|
+
* Import this from `zeitlich/adapters/thread/langchain/workflow`
|
|
5
|
+
* in your Temporal workflow files.
|
|
6
|
+
*
|
|
7
|
+
* By default the scope is derived from `workflowInfo().workflowType`,
|
|
8
|
+
* so activities are automatically namespaced per workflow.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* import { proxyLangChainThreadOps } from 'zeitlich/adapters/thread/langchain/workflow';
|
|
13
|
+
*
|
|
14
|
+
* // Auto-scoped to the current workflow name
|
|
15
|
+
* const threadOps = proxyLangChainThreadOps();
|
|
16
|
+
*
|
|
17
|
+
* // Explicit scope override
|
|
18
|
+
* const threadOps = proxyLangChainThreadOps("customScope");
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
import {
|
|
22
|
+
proxyActivities,
|
|
23
|
+
workflowInfo,
|
|
24
|
+
type ActivityInterfaceFor,
|
|
25
|
+
} from "@temporalio/workflow";
|
|
26
|
+
import type { ThreadOps } from "../../../lib/session/types";
|
|
27
|
+
|
|
28
|
+
const ADAPTER_PREFIX = "langChain";
|
|
29
|
+
|
|
30
|
+
export function proxyLangChainThreadOps(
|
|
31
|
+
scope?: string,
|
|
32
|
+
options?: Parameters<typeof proxyActivities>[0]
|
|
33
|
+
): ActivityInterfaceFor<ThreadOps> {
|
|
34
|
+
const resolvedScope = scope ?? workflowInfo().workflowType;
|
|
35
|
+
|
|
36
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
37
|
+
const acts = proxyActivities<Record<string, (...args: any[]) => any>>(
|
|
38
|
+
options ?? {
|
|
39
|
+
startToCloseTimeout: "10s",
|
|
40
|
+
retry: {
|
|
41
|
+
maximumAttempts: 6,
|
|
42
|
+
initialInterval: "5s",
|
|
43
|
+
maximumInterval: "15m",
|
|
44
|
+
backoffCoefficient: 4,
|
|
45
|
+
},
|
|
46
|
+
}
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
const prefix =
|
|
50
|
+
`${ADAPTER_PREFIX}${resolvedScope.charAt(0).toUpperCase()}${resolvedScope.slice(1)}`;
|
|
51
|
+
const p = (key: string): string =>
|
|
52
|
+
`${prefix}${key.charAt(0).toUpperCase()}${key.slice(1)}`;
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
initializeThread: acts[p("initializeThread")],
|
|
56
|
+
appendHumanMessage: acts[p("appendHumanMessage")],
|
|
57
|
+
appendToolResult: acts[p("appendToolResult")],
|
|
58
|
+
appendSystemMessage: acts[p("appendSystemMessage")],
|
|
59
|
+
forkThread: acts[p("forkThread")],
|
|
60
|
+
} as ActivityInterfaceFor<ThreadOps>;
|
|
61
|
+
}
|
|
@@ -2,6 +2,7 @@ import type {
|
|
|
2
2
|
Sandbox,
|
|
3
3
|
SandboxCreateOptions,
|
|
4
4
|
SandboxOps,
|
|
5
|
+
PrefixedSandboxOps,
|
|
5
6
|
SandboxProvider,
|
|
6
7
|
SandboxSnapshot,
|
|
7
8
|
} from "./types";
|
|
@@ -16,16 +17,18 @@ import type {
|
|
|
16
17
|
* ```typescript
|
|
17
18
|
* const manager = new SandboxManager(new InMemorySandboxProvider());
|
|
18
19
|
* const activities = {
|
|
19
|
-
* ...manager.createActivities(),
|
|
20
|
+
* ...manager.createActivities("CodingAgent"),
|
|
20
21
|
* bashHandler: withSandbox(manager, bashHandler),
|
|
21
22
|
* };
|
|
23
|
+
* // registers: inMemoryCodingAgentCreateSandbox, …
|
|
22
24
|
* ```
|
|
23
25
|
*/
|
|
24
26
|
export class SandboxManager<
|
|
25
27
|
TOptions extends SandboxCreateOptions = SandboxCreateOptions,
|
|
26
28
|
TSandbox extends Sandbox = Sandbox,
|
|
29
|
+
TId extends string = string,
|
|
27
30
|
> {
|
|
28
|
-
constructor(private provider: SandboxProvider<TOptions, TSandbox>) {}
|
|
31
|
+
constructor(private provider: SandboxProvider<TOptions, TSandbox> & { readonly id: TId }) {}
|
|
29
32
|
|
|
30
33
|
async create(
|
|
31
34
|
options?: TOptions
|
|
@@ -51,12 +54,36 @@ export class SandboxManager<
|
|
|
51
54
|
return sandbox.id;
|
|
52
55
|
}
|
|
53
56
|
|
|
57
|
+
async fork(sandboxId: string): Promise<string> {
|
|
58
|
+
const sandbox = await this.provider.fork(sandboxId);
|
|
59
|
+
return sandbox.id;
|
|
60
|
+
}
|
|
61
|
+
|
|
54
62
|
/**
|
|
55
|
-
* Returns Temporal activity functions
|
|
56
|
-
*
|
|
63
|
+
* Returns Temporal activity functions with prefixed names.
|
|
64
|
+
*
|
|
65
|
+
* The provider's `id` is automatically prepended, so you only need
|
|
66
|
+
* to pass the workflow/scope name. Use the matching `proxy*SandboxOps()`
|
|
67
|
+
* helper from the adapter's `/workflow` entrypoint on the workflow side.
|
|
68
|
+
*
|
|
69
|
+
* @param scope - Workflow name (appended to the provider id)
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* ```typescript
|
|
73
|
+
* const manager = new SandboxManager(new InMemorySandboxProvider());
|
|
74
|
+
* manager.createActivities("CodingAgent");
|
|
75
|
+
* // registers: inMemoryCodingAgentCreateSandbox, inMemoryCodingAgentDestroySandbox, …
|
|
76
|
+
*
|
|
77
|
+
* const vmgr = new SandboxManager(new VirtualSandboxProvider(resolver));
|
|
78
|
+
* vmgr.createActivities("CodingAgent");
|
|
79
|
+
* // registers: virtualCodingAgentCreateSandbox, …
|
|
80
|
+
* ```
|
|
57
81
|
*/
|
|
58
|
-
createActivities
|
|
59
|
-
|
|
82
|
+
createActivities<S extends string>(
|
|
83
|
+
scope: S
|
|
84
|
+
): PrefixedSandboxOps<`${TId}${Capitalize<S>}`, TOptions> {
|
|
85
|
+
const prefix = `${this.provider.id}${scope.charAt(0).toUpperCase()}${scope.slice(1)}`;
|
|
86
|
+
const ops: SandboxOps<TOptions> = {
|
|
60
87
|
createSandbox: async (
|
|
61
88
|
options?: TOptions
|
|
62
89
|
): Promise<{
|
|
@@ -71,6 +98,13 @@ export class SandboxManager<
|
|
|
71
98
|
snapshotSandbox: async (sandboxId: string): Promise<SandboxSnapshot> => {
|
|
72
99
|
return this.snapshot(sandboxId);
|
|
73
100
|
},
|
|
101
|
+
forkSandbox: async (sandboxId: string): Promise<string> => {
|
|
102
|
+
return this.fork(sandboxId);
|
|
103
|
+
},
|
|
74
104
|
};
|
|
105
|
+
const cap = (s: string): string => s.charAt(0).toUpperCase() + s.slice(1);
|
|
106
|
+
return Object.fromEntries(
|
|
107
|
+
Object.entries(ops).map(([k, v]) => [`${prefix}${cap(k)}`, v])
|
|
108
|
+
) as PrefixedSandboxOps<`${TId}${Capitalize<S>}`, TOptions>;
|
|
75
109
|
}
|
|
76
110
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { describe, expect, it, beforeEach } from "vitest";
|
|
2
2
|
import { SandboxManager } from "./manager";
|
|
3
3
|
import { InMemorySandboxProvider } from "../../adapters/sandbox/inmemory/index";
|
|
4
|
-
import { SandboxNotFoundError } from "./types";
|
|
4
|
+
import { SandboxNotFoundError, type Sandbox, type SandboxCreateOptions } from "./types";
|
|
5
5
|
|
|
6
6
|
describe("SandboxManager", () => {
|
|
7
|
-
let manager: SandboxManager
|
|
7
|
+
let manager: SandboxManager<SandboxCreateOptions, Sandbox, "inMemory">;
|
|
8
8
|
|
|
9
9
|
beforeEach(() => {
|
|
10
10
|
manager = new SandboxManager(new InMemorySandboxProvider());
|
|
@@ -54,7 +54,7 @@ describe("SandboxManager", () => {
|
|
|
54
54
|
|
|
55
55
|
const snapshot = await manager.snapshot(sandboxId);
|
|
56
56
|
expect(snapshot.sandboxId).toBe(sandboxId);
|
|
57
|
-
expect(snapshot.providerId).toBe("
|
|
57
|
+
expect(snapshot.providerId).toBe("inMemory");
|
|
58
58
|
|
|
59
59
|
await manager.destroy(sandboxId);
|
|
60
60
|
await expect(manager.getSandbox(sandboxId)).rejects.toThrow(SandboxNotFoundError);
|
|
@@ -68,16 +68,17 @@ describe("SandboxManager", () => {
|
|
|
68
68
|
expect(extra).toBe("world");
|
|
69
69
|
});
|
|
70
70
|
|
|
71
|
-
it("createActivities returns SandboxOps-shaped object", async () => {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
expect(activities.
|
|
75
|
-
expect(activities.
|
|
71
|
+
it("createActivities returns prefixed SandboxOps-shaped object", async () => {
|
|
72
|
+
// provider.id is "inMemory", scope is "Test" → prefix "inMemoryTest"
|
|
73
|
+
const activities = manager.createActivities("Test");
|
|
74
|
+
expect(activities.inMemoryTestCreateSandbox).toBeTypeOf("function");
|
|
75
|
+
expect(activities.inMemoryTestDestroySandbox).toBeTypeOf("function");
|
|
76
|
+
expect(activities.inMemoryTestSnapshotSandbox).toBeTypeOf("function");
|
|
76
77
|
|
|
77
|
-
const { sandboxId } = await activities.
|
|
78
|
+
const { sandboxId } = await activities.inMemoryTestCreateSandbox();
|
|
78
79
|
await expect(manager.getSandbox(sandboxId)).resolves.toBeTruthy();
|
|
79
80
|
|
|
80
|
-
await activities.
|
|
81
|
+
await activities.inMemoryTestDestroySandbox(sandboxId);
|
|
81
82
|
await expect(manager.getSandbox(sandboxId)).rejects.toThrow(
|
|
82
83
|
SandboxNotFoundError,
|
|
83
84
|
);
|
|
@@ -85,7 +86,7 @@ describe("SandboxManager", () => {
|
|
|
85
86
|
});
|
|
86
87
|
|
|
87
88
|
describe("InMemorySandboxProvider", () => {
|
|
88
|
-
let manager: SandboxManager
|
|
89
|
+
let manager: SandboxManager<SandboxCreateOptions, Sandbox, "inMemory">;
|
|
89
90
|
|
|
90
91
|
beforeEach(() => {
|
|
91
92
|
manager = new SandboxManager(new InMemorySandboxProvider());
|
package/src/lib/sandbox/types.ts
CHANGED
|
@@ -127,6 +127,7 @@ export interface SandboxProvider<
|
|
|
127
127
|
destroy(sandboxId: string): Promise<void>;
|
|
128
128
|
snapshot(sandboxId: string): Promise<SandboxSnapshot>;
|
|
129
129
|
restore(snapshot: SandboxSnapshot): Promise<Sandbox>;
|
|
130
|
+
fork(sandboxId: string): Promise<Sandbox>;
|
|
130
131
|
}
|
|
131
132
|
|
|
132
133
|
// ============================================================================
|
|
@@ -141,8 +142,25 @@ export interface SandboxOps<
|
|
|
141
142
|
): Promise<{ sandboxId: string; stateUpdate?: Record<string, unknown> }>;
|
|
142
143
|
destroySandbox(sandboxId: string): Promise<void>;
|
|
143
144
|
snapshotSandbox(sandboxId: string): Promise<SandboxSnapshot>;
|
|
145
|
+
forkSandbox(sandboxId: string): Promise<string>;
|
|
144
146
|
}
|
|
145
147
|
|
|
148
|
+
/**
|
|
149
|
+
* Maps generic {@link SandboxOps} method names to adapter-prefixed names.
|
|
150
|
+
*
|
|
151
|
+
* @example
|
|
152
|
+
* ```typescript
|
|
153
|
+
* type InMemOps = PrefixedSandboxOps<"inMemory">;
|
|
154
|
+
* // → { inMemoryCreateSandbox, inMemoryDestroySandbox, inMemorySnapshotSandbox }
|
|
155
|
+
* ```
|
|
156
|
+
*/
|
|
157
|
+
export type PrefixedSandboxOps<
|
|
158
|
+
TPrefix extends string,
|
|
159
|
+
TOptions extends SandboxCreateOptions = SandboxCreateOptions,
|
|
160
|
+
> = {
|
|
161
|
+
[K in keyof SandboxOps<TOptions> as `${TPrefix}${Capitalize<K & string>}`]: SandboxOps<TOptions>[K];
|
|
162
|
+
};
|
|
163
|
+
|
|
146
164
|
// ============================================================================
|
|
147
165
|
// Errors
|
|
148
166
|
// ============================================================================
|
package/src/lib/session/index.ts
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
export {
|
|
2
|
-
createSession,
|
|
3
|
-
proxyDefaultThreadOps,
|
|
4
|
-
proxySandboxOps,
|
|
5
|
-
} from "./session";
|
|
1
|
+
export { createSession } from "./session";
|
|
6
2
|
|
|
7
3
|
export type {
|
|
8
4
|
ThreadOps,
|
|
5
|
+
PrefixedThreadOps,
|
|
6
|
+
ScopedPrefix,
|
|
9
7
|
SessionConfig,
|
|
10
8
|
ZeitlichSession,
|
|
11
9
|
} from "./types";
|