zeitlich 0.2.32 → 0.2.33
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 +11 -10
- package/dist/{activities-FIXVz7DT.d.ts → activities-YBD5BaHh.d.ts} +4 -3
- package/dist/{activities-DA-bQM12.d.cts → activities-fnX8-vhR.d.cts} +4 -3
- package/dist/adapters/thread/anthropic/index.cjs +18 -2
- package/dist/adapters/thread/anthropic/index.cjs.map +1 -1
- package/dist/adapters/thread/anthropic/index.d.cts +7 -6
- package/dist/adapters/thread/anthropic/index.d.ts +7 -6
- package/dist/adapters/thread/anthropic/index.js +18 -2
- package/dist/adapters/thread/anthropic/index.js.map +1 -1
- package/dist/adapters/thread/google-genai/index.cjs +29 -8
- package/dist/adapters/thread/google-genai/index.cjs.map +1 -1
- package/dist/adapters/thread/google-genai/index.d.cts +5 -5
- package/dist/adapters/thread/google-genai/index.d.ts +5 -5
- package/dist/adapters/thread/google-genai/index.js +29 -8
- package/dist/adapters/thread/google-genai/index.js.map +1 -1
- package/dist/adapters/thread/google-genai/workflow.d.cts +1 -1
- package/dist/adapters/thread/google-genai/workflow.d.ts +1 -1
- package/dist/adapters/thread/langchain/index.cjs +42 -23
- package/dist/adapters/thread/langchain/index.cjs.map +1 -1
- package/dist/adapters/thread/langchain/index.d.cts +8 -6
- package/dist/adapters/thread/langchain/index.d.ts +8 -6
- package/dist/adapters/thread/langchain/index.js +42 -23
- package/dist/adapters/thread/langchain/index.js.map +1 -1
- package/dist/index.cjs +34 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +24 -9
- package/dist/index.d.ts +24 -9
- package/dist/index.js +33 -5
- package/dist/index.js.map +1 -1
- package/dist/{workflow-D8wK7TJY.d.ts → workflow-D9nNERvs.d.ts} +30 -2
- package/dist/{workflow-BWKQcz9d.d.cts → workflow-Od9vx5Jk.d.cts} +30 -2
- package/dist/workflow.cjs +18 -0
- package/dist/workflow.cjs.map +1 -1
- package/dist/workflow.d.cts +1 -1
- package/dist/workflow.d.ts +1 -1
- package/dist/workflow.js +18 -1
- package/dist/workflow.js.map +1 -1
- package/package.json +1 -1
- package/src/adapters/thread/anthropic/activities.ts +4 -3
- package/src/adapters/thread/anthropic/model-invoker.ts +15 -5
- package/src/adapters/thread/google-genai/activities.ts +4 -3
- package/src/adapters/thread/google-genai/model-invoker.ts +24 -11
- package/src/adapters/thread/langchain/activities.ts +3 -3
- package/src/adapters/thread/langchain/model-invoker.ts +63 -34
- package/src/index.ts +1 -0
- package/src/lib/activity.ts +36 -9
- package/src/lib/model/helpers.ts +1 -0
- package/src/lib/model/index.ts +1 -0
- package/src/lib/model/proxy.ts +50 -0
- package/src/workflow.ts +3 -0
- package/src/lib/.env +0 -1
- package/src/tools/bash/.env +0 -1
package/package.json
CHANGED
|
@@ -107,7 +107,7 @@ export interface AnthropicAdapter {
|
|
|
107
107
|
* export function createActivities(temporalClient: WorkflowClient) {
|
|
108
108
|
* return {
|
|
109
109
|
* ...adapter.createActivities("codingAgent"),
|
|
110
|
-
*
|
|
110
|
+
* ...createRunAgentActivity(temporalClient, adapter.invoker, "codingAgent"),
|
|
111
111
|
* };
|
|
112
112
|
* }
|
|
113
113
|
* ```
|
|
@@ -118,10 +118,11 @@ export interface AnthropicAdapter {
|
|
|
118
118
|
* return {
|
|
119
119
|
* ...adapter.createActivities("codingAgent"),
|
|
120
120
|
* ...adapter.createActivities("researchAgent"),
|
|
121
|
-
*
|
|
122
|
-
*
|
|
121
|
+
* ...createRunAgentActivity(temporalClient, adapter.invoker, "codingAgent"),
|
|
122
|
+
* ...createRunAgentActivity(
|
|
123
123
|
* temporalClient,
|
|
124
124
|
* adapter.createModelInvoker('claude-sonnet-4-20250514'),
|
|
125
|
+
* "researchAgent",
|
|
125
126
|
* ),
|
|
126
127
|
* };
|
|
127
128
|
* }
|
|
@@ -3,6 +3,7 @@ import type Anthropic from "@anthropic-ai/sdk";
|
|
|
3
3
|
import type { SerializableToolDefinition } from "../../../lib/types";
|
|
4
4
|
import type { AgentResponse, ModelInvokerConfig } from "../../../lib/model";
|
|
5
5
|
import { createAnthropicThreadManager, type AnthropicThreadManagerHooks } from "./thread-manager";
|
|
6
|
+
import { getActivityContext } from "../../../lib/activity";
|
|
6
7
|
|
|
7
8
|
export interface AnthropicModelInvokerConfig {
|
|
8
9
|
redis: Redis;
|
|
@@ -27,8 +28,8 @@ function toAnthropicTools(
|
|
|
27
28
|
* Creates an Anthropic model invoker that satisfies the generic
|
|
28
29
|
* `ModelInvoker<Anthropic.Messages.Message>` contract.
|
|
29
30
|
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
31
|
+
* Internally streams the response and emits Temporal heartbeats on each
|
|
32
|
+
* event so that long-running LLM calls remain visible to the scheduler.
|
|
32
33
|
* The caller is responsible for appending the response to the thread.
|
|
33
34
|
*
|
|
34
35
|
* @example
|
|
@@ -44,7 +45,7 @@ function toAnthropicTools(
|
|
|
44
45
|
* model: 'claude-sonnet-4-20250514',
|
|
45
46
|
* });
|
|
46
47
|
*
|
|
47
|
-
* return {
|
|
48
|
+
* return { ...createRunAgentActivity(client, invoker, "myAgent") };
|
|
48
49
|
* ```
|
|
49
50
|
*/
|
|
50
51
|
export function createAnthropicModelInvoker({
|
|
@@ -58,6 +59,7 @@ export function createAnthropicModelInvoker({
|
|
|
58
59
|
config: ModelInvokerConfig,
|
|
59
60
|
): Promise<AgentResponse<Anthropic.Messages.Message>> {
|
|
60
61
|
const { threadId, threadKey, state } = config;
|
|
62
|
+
const { heartbeat, signal } = getActivityContext();
|
|
61
63
|
|
|
62
64
|
const thread = createAnthropicThreadManager({ redis, threadId, key: threadKey, hooks });
|
|
63
65
|
const { messages, system } = await thread.prepareForInvocation();
|
|
@@ -65,13 +67,21 @@ export function createAnthropicModelInvoker({
|
|
|
65
67
|
const anthropicTools = toAnthropicTools(state.tools);
|
|
66
68
|
const tools = anthropicTools.length > 0 ? anthropicTools : undefined;
|
|
67
69
|
|
|
68
|
-
const
|
|
70
|
+
const params: Anthropic.MessageCreateParams = {
|
|
69
71
|
model,
|
|
70
72
|
max_tokens: maxTokens,
|
|
71
73
|
messages,
|
|
72
74
|
...(system ? { system } : {}),
|
|
73
75
|
...(tools ? { tools } : {}),
|
|
74
|
-
}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const stream = client.messages.stream(params, { signal });
|
|
79
|
+
|
|
80
|
+
for await (const _event of stream) {
|
|
81
|
+
heartbeat?.();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const response: Anthropic.Messages.Message = await stream.finalMessage();
|
|
75
85
|
|
|
76
86
|
const toolCalls = response.content.filter(
|
|
77
87
|
(block): block is Anthropic.Messages.ToolUseBlock =>
|
|
@@ -113,7 +113,7 @@ export interface GoogleGenAIAdapter {
|
|
|
113
113
|
* export function createActivities(temporalClient: WorkflowClient) {
|
|
114
114
|
* return {
|
|
115
115
|
* ...adapter.createActivities("codingAgent"),
|
|
116
|
-
*
|
|
116
|
+
* ...createRunAgentActivity(temporalClient, adapter.invoker, "codingAgent"),
|
|
117
117
|
* };
|
|
118
118
|
* }
|
|
119
119
|
* ```
|
|
@@ -124,10 +124,11 @@ export interface GoogleGenAIAdapter {
|
|
|
124
124
|
* return {
|
|
125
125
|
* ...adapter.createActivities("codingAgent"),
|
|
126
126
|
* ...adapter.createActivities("researchAgent"),
|
|
127
|
-
*
|
|
128
|
-
*
|
|
127
|
+
* ...createRunAgentActivity(temporalClient, adapter.invoker, "codingAgent"),
|
|
128
|
+
* ...createRunAgentActivity(
|
|
129
129
|
* temporalClient,
|
|
130
130
|
* adapter.createModelInvoker('gemini-2.5-pro'),
|
|
131
|
+
* "researchAgent",
|
|
131
132
|
* ),
|
|
132
133
|
* };
|
|
133
134
|
* }
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type Redis from "ioredis";
|
|
2
|
-
import type { GoogleGenAI, Content, FunctionDeclaration } from "@google/genai";
|
|
2
|
+
import type { GoogleGenAI, Content, FunctionDeclaration, Part, GenerateContentResponse } from "@google/genai";
|
|
3
3
|
import type { SerializableToolDefinition } from "../../../lib/types";
|
|
4
4
|
import type { AgentResponse, ModelInvokerConfig } from "../../../lib/model";
|
|
5
5
|
import { createGoogleGenAIThreadManager, type GoogleGenAIThreadManagerHooks } from "./thread-manager";
|
|
6
|
+
import { getActivityContext } from "../../../lib/activity";
|
|
6
7
|
|
|
7
8
|
export interface GoogleGenAIModelInvokerConfig {
|
|
8
9
|
redis: Redis;
|
|
@@ -25,8 +26,8 @@ function toFunctionDeclarations(
|
|
|
25
26
|
* Creates a Google GenAI model invoker that satisfies the generic
|
|
26
27
|
* `ModelInvoker<Content>` contract.
|
|
27
28
|
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
29
|
+
* Internally streams the response and emits Temporal heartbeats on each
|
|
30
|
+
* chunk so that long-running LLM calls remain visible to the scheduler.
|
|
30
31
|
* The caller is responsible for appending the response to the thread.
|
|
31
32
|
*
|
|
32
33
|
* @example
|
|
@@ -42,7 +43,7 @@ function toFunctionDeclarations(
|
|
|
42
43
|
* model: 'gemini-2.5-flash',
|
|
43
44
|
* });
|
|
44
45
|
*
|
|
45
|
-
* return {
|
|
46
|
+
* return { ...createRunAgentActivity(client, invoker, "myAgent") };
|
|
46
47
|
* ```
|
|
47
48
|
*/
|
|
48
49
|
export function createGoogleGenAIModelInvoker({
|
|
@@ -55,6 +56,7 @@ export function createGoogleGenAIModelInvoker({
|
|
|
55
56
|
config: ModelInvokerConfig,
|
|
56
57
|
): Promise<AgentResponse<Content>> {
|
|
57
58
|
const { threadId, threadKey, state } = config;
|
|
59
|
+
const { heartbeat, signal } = getActivityContext();
|
|
58
60
|
|
|
59
61
|
const thread = createGoogleGenAIThreadManager({ redis, threadId, key: threadKey, hooks });
|
|
60
62
|
const { contents, systemInstruction } =
|
|
@@ -64,19 +66,30 @@ export function createGoogleGenAIModelInvoker({
|
|
|
64
66
|
const tools =
|
|
65
67
|
functionDeclarations.length > 0 ? [{ functionDeclarations }] : undefined;
|
|
66
68
|
|
|
67
|
-
const
|
|
69
|
+
const stream = await client.models.generateContentStream({
|
|
68
70
|
model,
|
|
69
71
|
contents,
|
|
70
72
|
config: {
|
|
71
73
|
...(systemInstruction ? { systemInstruction } : {}),
|
|
72
74
|
...(tools ? { tools } : {}),
|
|
75
|
+
abortSignal: signal,
|
|
73
76
|
},
|
|
74
77
|
});
|
|
75
78
|
|
|
76
|
-
const
|
|
77
|
-
|
|
79
|
+
const allParts: Part[] = [];
|
|
80
|
+
let lastChunk: GenerateContentResponse | undefined;
|
|
81
|
+
for await (const chunk of stream) {
|
|
82
|
+
lastChunk = chunk;
|
|
83
|
+
allParts.push(...(chunk.candidates?.[0]?.content?.parts ?? []));
|
|
84
|
+
heartbeat?.();
|
|
85
|
+
}
|
|
78
86
|
|
|
79
|
-
|
|
87
|
+
if (!lastChunk) {
|
|
88
|
+
throw new Error("Google GenAI stream ended without producing any chunks");
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const modelContent: Content = { role: "model", parts: allParts };
|
|
92
|
+
const functionCalls = lastChunk.functionCalls ?? [];
|
|
80
93
|
|
|
81
94
|
return {
|
|
82
95
|
message: modelContent,
|
|
@@ -86,9 +99,9 @@ export function createGoogleGenAIModelInvoker({
|
|
|
86
99
|
args: fc.args ?? {},
|
|
87
100
|
})),
|
|
88
101
|
usage: {
|
|
89
|
-
inputTokens:
|
|
90
|
-
outputTokens:
|
|
91
|
-
cachedReadTokens:
|
|
102
|
+
inputTokens: lastChunk.usageMetadata?.promptTokenCount,
|
|
103
|
+
outputTokens: lastChunk.usageMetadata?.candidatesTokenCount,
|
|
104
|
+
cachedReadTokens: lastChunk.usageMetadata?.cachedContentTokenCount,
|
|
92
105
|
},
|
|
93
106
|
};
|
|
94
107
|
};
|
|
@@ -94,7 +94,7 @@ export interface LangChainAdapter {
|
|
|
94
94
|
* export function createActivities(client: WorkflowClient) {
|
|
95
95
|
* return {
|
|
96
96
|
* ...adapter.createActivities("codingAgent"),
|
|
97
|
-
*
|
|
97
|
+
* ...createRunAgentActivity(client, adapter.invoker, "codingAgent"),
|
|
98
98
|
* };
|
|
99
99
|
* }
|
|
100
100
|
* ```
|
|
@@ -105,8 +105,8 @@ export interface LangChainAdapter {
|
|
|
105
105
|
* return {
|
|
106
106
|
* ...adapter.createActivities("codingAgent"),
|
|
107
107
|
* ...adapter.createActivities("researchAgent"),
|
|
108
|
-
*
|
|
109
|
-
*
|
|
108
|
+
* ...createRunAgentActivity(client, adapter.invoker, "codingAgent"),
|
|
109
|
+
* ...createRunAgentActivity(client, adapter.createModelInvoker(claude), "researchAgent"),
|
|
110
110
|
* };
|
|
111
111
|
* }
|
|
112
112
|
* ```
|
|
@@ -3,10 +3,16 @@ import type { AgentResponse, ModelInvokerConfig } from "../../../lib/model";
|
|
|
3
3
|
import type { StoredMessage } from "@langchain/core/messages";
|
|
4
4
|
import { v4 as uuidv4 } from "uuid";
|
|
5
5
|
import type { BaseChatModel } from "@langchain/core/language_models/chat_models";
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
createLangChainThreadManager,
|
|
8
|
+
type LangChainThreadManagerHooks,
|
|
9
|
+
} from "./thread-manager";
|
|
10
|
+
import { getActivityContext } from "../../../lib/activity";
|
|
7
11
|
|
|
8
12
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
9
|
-
export interface LangChainModelInvokerConfig<
|
|
13
|
+
export interface LangChainModelInvokerConfig<
|
|
14
|
+
TModel extends BaseChatModel<any> = BaseChatModel<any>,
|
|
15
|
+
> {
|
|
10
16
|
redis: Redis;
|
|
11
17
|
model: TModel;
|
|
12
18
|
hooks?: LangChainThreadManagerHooks;
|
|
@@ -16,8 +22,10 @@ export interface LangChainModelInvokerConfig<TModel extends BaseChatModel<any> =
|
|
|
16
22
|
* Creates a LangChain-based model invoker that satisfies the generic
|
|
17
23
|
* `ModelInvoker<StoredMessage>` contract.
|
|
18
24
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
25
|
+
* Uses interval-based Temporal heartbeats during model.invoke() to keep
|
|
26
|
+
* long-running LLM calls visible to the scheduler. LangChain's streaming
|
|
27
|
+
* chunk accumulation is unreliable across providers (e.g. reasoning_content
|
|
28
|
+
* blocks don't merge correctly), so we use invoke() for correctness.
|
|
21
29
|
* The caller is responsible for appending the response to the thread.
|
|
22
30
|
*
|
|
23
31
|
* @example
|
|
@@ -29,51 +37,70 @@ export interface LangChainModelInvokerConfig<TModel extends BaseChatModel<any> =
|
|
|
29
37
|
* const model = new ChatAnthropic({ model: "claude-sonnet-4-6" });
|
|
30
38
|
* const invoker = createLangChainModelInvoker({ redis, model });
|
|
31
39
|
*
|
|
32
|
-
* return {
|
|
40
|
+
* return { ...createRunAgentActivity(client, invoker, "myAgent") };
|
|
33
41
|
* ```
|
|
34
42
|
*/
|
|
35
43
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
36
|
-
export function createLangChainModelInvoker<
|
|
37
|
-
|
|
38
|
-
) {
|
|
44
|
+
export function createLangChainModelInvoker<
|
|
45
|
+
TModel extends BaseChatModel<any> = BaseChatModel<any>,
|
|
46
|
+
>({ redis, model, hooks }: LangChainModelInvokerConfig<TModel>) {
|
|
39
47
|
return async function invokeLangChainModel(
|
|
40
|
-
config: ModelInvokerConfig
|
|
48
|
+
config: ModelInvokerConfig
|
|
41
49
|
): Promise<AgentResponse<StoredMessage>> {
|
|
42
50
|
const { threadId, threadKey, agentName, state, metadata } = config;
|
|
51
|
+
const { heartbeat, signal } = getActivityContext();
|
|
43
52
|
|
|
44
|
-
const thread = createLangChainThreadManager({
|
|
53
|
+
const thread = createLangChainThreadManager({
|
|
54
|
+
redis,
|
|
55
|
+
threadId,
|
|
56
|
+
key: threadKey,
|
|
57
|
+
hooks,
|
|
58
|
+
});
|
|
45
59
|
const runId = uuidv4();
|
|
46
60
|
|
|
47
61
|
const { messages } = await thread.prepareForInvocation();
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
62
|
+
|
|
63
|
+
const heartbeatInterval = heartbeat
|
|
64
|
+
? setInterval(() => heartbeat(), 30_000)
|
|
65
|
+
: undefined;
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
const response = await model.invoke(messages, {
|
|
51
69
|
runName: agentName,
|
|
52
70
|
runId,
|
|
53
71
|
metadata: { thread_id: `${agentName}-${threadId}`, ...metadata },
|
|
54
72
|
tools: state.tools,
|
|
55
|
-
|
|
56
|
-
|
|
73
|
+
signal,
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const toolCalls = response.tool_calls ?? [];
|
|
57
77
|
|
|
58
|
-
|
|
78
|
+
const providerUsage =
|
|
79
|
+
(response.response_metadata?.usage as Record<string, unknown>) ?? {};
|
|
59
80
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
81
|
+
return {
|
|
82
|
+
message: response.toDict(),
|
|
83
|
+
rawToolCalls: toolCalls.map((tc) => ({
|
|
84
|
+
id: tc.id,
|
|
85
|
+
name: tc.name,
|
|
86
|
+
args: tc.args,
|
|
87
|
+
})),
|
|
88
|
+
usage: {
|
|
89
|
+
inputTokens: response.usage_metadata?.input_tokens,
|
|
90
|
+
outputTokens: response.usage_metadata?.output_tokens,
|
|
91
|
+
reasonTokens:
|
|
92
|
+
response.usage_metadata?.output_token_details?.reasoning,
|
|
93
|
+
cachedWriteTokens:
|
|
94
|
+
response.usage_metadata?.input_token_details?.cache_creation ||
|
|
95
|
+
(providerUsage.cacheWriteInputTokens as number | undefined),
|
|
96
|
+
cachedReadTokens:
|
|
97
|
+
response.usage_metadata?.input_token_details?.cache_read ||
|
|
98
|
+
(providerUsage.cacheReadInputTokens as number | undefined),
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
} finally {
|
|
102
|
+
if (heartbeatInterval) clearInterval(heartbeatInterval);
|
|
103
|
+
}
|
|
77
104
|
};
|
|
78
105
|
}
|
|
79
106
|
|
|
@@ -83,7 +110,9 @@ export function createLangChainModelInvoker<TModel extends BaseChatModel<any> =
|
|
|
83
110
|
* you don't need to reuse the invoker.
|
|
84
111
|
*/
|
|
85
112
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
86
|
-
export async function invokeLangChainModel<
|
|
113
|
+
export async function invokeLangChainModel<
|
|
114
|
+
TModel extends BaseChatModel<any> = BaseChatModel<any>,
|
|
115
|
+
>({
|
|
87
116
|
redis,
|
|
88
117
|
model,
|
|
89
118
|
hooks,
|
package/src/index.ts
CHANGED
package/src/lib/activity.ts
CHANGED
|
@@ -8,6 +8,22 @@ import type {
|
|
|
8
8
|
ToolHandlerResponse,
|
|
9
9
|
} from "./tool-router/types";
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Safely retrieve Temporal activity heartbeat and cancellation signal.
|
|
13
|
+
* Returns empty object when called outside a Temporal activity (e.g. tests).
|
|
14
|
+
*/
|
|
15
|
+
export function getActivityContext(): {
|
|
16
|
+
heartbeat?: () => void;
|
|
17
|
+
signal?: AbortSignal;
|
|
18
|
+
} {
|
|
19
|
+
try {
|
|
20
|
+
const ctx = Context.current();
|
|
21
|
+
return { heartbeat: () => ctx.heartbeat(), signal: ctx.cancellationSignal };
|
|
22
|
+
} catch {
|
|
23
|
+
return {};
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
11
27
|
/**
|
|
12
28
|
* Query the parent workflow's state from within an activity.
|
|
13
29
|
* Resolves the workflow handle from the current activity context.
|
|
@@ -24,25 +40,36 @@ export async function queryParentWorkflowState<T>(
|
|
|
24
40
|
}
|
|
25
41
|
|
|
26
42
|
/**
|
|
27
|
-
* Wraps a handler into a `RunAgentActivity` by auto-fetching
|
|
28
|
-
* workflow's agent state before each invocation.
|
|
43
|
+
* Wraps a handler into a scope-prefixed `RunAgentActivity` by auto-fetching
|
|
44
|
+
* the parent workflow's agent state before each invocation.
|
|
45
|
+
*
|
|
46
|
+
* Returns a `Record` with a single key `run<Scope>` so it can be spread
|
|
47
|
+
* into the activities object alongside adapter activities.
|
|
48
|
+
*
|
|
49
|
+
* @param scope - Workflow scope used to derive the activity name.
|
|
50
|
+
* `"myAgentWorkflow"` produces `{ runMyAgentWorkflow: fn }`.
|
|
29
51
|
*
|
|
30
52
|
* @example
|
|
31
53
|
* ```typescript
|
|
32
54
|
* import { createRunAgentActivity } from 'zeitlich';
|
|
33
|
-
* import { createLangChainModelInvoker } from 'zeitlich/adapters/thread/langchain';
|
|
34
55
|
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
56
|
+
* return {
|
|
57
|
+
* ...adapter.createActivities("myAgentWorkflow"),
|
|
58
|
+
* ...createRunAgentActivity(client, adapter.invoker, "myAgentWorkflow"),
|
|
59
|
+
* };
|
|
37
60
|
* ```
|
|
38
61
|
*/
|
|
39
62
|
export function createRunAgentActivity<R, S extends BaseAgentState = BaseAgentState>(
|
|
40
63
|
client: WorkflowClient,
|
|
41
64
|
handler: (config: RunAgentConfig & { state: S }) => Promise<R>,
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
65
|
+
scope: string,
|
|
66
|
+
): Record<string, (config: RunAgentConfig) => Promise<R>> {
|
|
67
|
+
const name = `run${scope.charAt(0).toUpperCase()}${scope.slice(1)}`;
|
|
68
|
+
return {
|
|
69
|
+
[name]: async (config: RunAgentConfig) => {
|
|
70
|
+
const state = await queryParentWorkflowState<S>(client);
|
|
71
|
+
return handler({ ...config, state });
|
|
72
|
+
},
|
|
46
73
|
};
|
|
47
74
|
}
|
|
48
75
|
|
package/src/lib/model/helpers.ts
CHANGED
package/src/lib/model/index.ts
CHANGED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow-safe proxy for runAgent activities with LLM-optimised defaults.
|
|
3
|
+
*
|
|
4
|
+
* Resolves the activity name from the scope using the same convention as
|
|
5
|
+
* {@link createRunAgentActivity}: `run<Scope>`.
|
|
6
|
+
* When no scope is provided, defaults to `workflowInfo().workflowType`.
|
|
7
|
+
*
|
|
8
|
+
* Import this from `zeitlich/workflow` in your Temporal workflow files.
|
|
9
|
+
*
|
|
10
|
+
* @typeParam M - SDK-native message type (e.g. `StoredMessage` for LangChain,
|
|
11
|
+
* `Anthropic.Messages.Message` for Anthropic, `Content` for Google GenAI).
|
|
12
|
+
* Must be provided for `SessionResult.finalMessage` to be correctly typed.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```typescript
|
|
16
|
+
* import { proxyRunAgent } from 'zeitlich/workflow';
|
|
17
|
+
* import type { StoredMessage } from '@langchain/core/messages';
|
|
18
|
+
*
|
|
19
|
+
* // Auto-scoped to the current workflow name
|
|
20
|
+
* const runAgent = proxyRunAgent<StoredMessage>();
|
|
21
|
+
*
|
|
22
|
+
* // Explicit scope for subagents
|
|
23
|
+
* const runResearcher = proxyRunAgent<StoredMessage>("Researcher");
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
import { proxyActivities, workflowInfo } from "@temporalio/workflow";
|
|
27
|
+
import type { AgentResponse } from "./types";
|
|
28
|
+
import type { RunAgentConfig } from "../types";
|
|
29
|
+
|
|
30
|
+
export function proxyRunAgent<M = unknown>(
|
|
31
|
+
scope?: string,
|
|
32
|
+
options?: Parameters<typeof proxyActivities>[0],
|
|
33
|
+
): (config: RunAgentConfig) => Promise<AgentResponse<M>> {
|
|
34
|
+
const resolvedScope = scope ?? workflowInfo().workflowType;
|
|
35
|
+
const name = `run${resolvedScope.charAt(0).toUpperCase()}${resolvedScope.slice(1)}`;
|
|
36
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
37
|
+
const acts = proxyActivities<Record<string, (...args: any[]) => any>>(
|
|
38
|
+
options ?? {
|
|
39
|
+
startToCloseTimeout: "10m",
|
|
40
|
+
heartbeatTimeout: "1m",
|
|
41
|
+
retry: {
|
|
42
|
+
maximumAttempts: 3,
|
|
43
|
+
initialInterval: "10s",
|
|
44
|
+
maximumInterval: "2m",
|
|
45
|
+
backoffCoefficient: 3,
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
);
|
|
49
|
+
return acts[name] as (config: RunAgentConfig) => Promise<AgentResponse<M>>;
|
|
50
|
+
}
|
package/src/workflow.ts
CHANGED
|
@@ -133,6 +133,9 @@ export type {
|
|
|
133
133
|
ModelInvokerConfig,
|
|
134
134
|
} from "./lib/model";
|
|
135
135
|
|
|
136
|
+
// Model proxy (workflow-safe proxy with LLM-optimised defaults)
|
|
137
|
+
export { proxyRunAgent } from "./lib/model/proxy";
|
|
138
|
+
|
|
136
139
|
// Subagent types
|
|
137
140
|
export type {
|
|
138
141
|
SubagentConfig,
|
package/src/lib/.env
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
E2B_API_KEY=e2b_39af116424059782e2aee6942fd70237cc2126c9
|
package/src/tools/bash/.env
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
E2B_API_KEY=e2b_39af116424059782e2aee6942fd70237cc2126c9
|