zeitlich 0.2.25 → 0.2.27
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/dist/activities-DE3_q9yq.d.ts +140 -0
- package/dist/activities-p8PDlRIK.d.cts +140 -0
- package/dist/adapters/sandbox/virtual/index.cjs.map +1 -1
- package/dist/adapters/sandbox/virtual/index.d.cts +8 -7
- package/dist/adapters/sandbox/virtual/index.d.ts +8 -7
- package/dist/adapters/sandbox/virtual/index.js.map +1 -1
- package/dist/adapters/sandbox/virtual/workflow.d.cts +3 -2
- package/dist/adapters/sandbox/virtual/workflow.d.ts +3 -2
- package/dist/adapters/thread/anthropic/index.cjs +363 -0
- package/dist/adapters/thread/anthropic/index.cjs.map +1 -0
- package/dist/adapters/thread/anthropic/index.d.cts +151 -0
- package/dist/adapters/thread/anthropic/index.d.ts +151 -0
- package/dist/adapters/thread/anthropic/index.js +358 -0
- package/dist/adapters/thread/anthropic/index.js.map +1 -0
- package/dist/adapters/thread/anthropic/workflow.cjs +38 -0
- package/dist/adapters/thread/anthropic/workflow.cjs.map +1 -0
- package/dist/adapters/thread/anthropic/workflow.d.cts +37 -0
- package/dist/adapters/thread/anthropic/workflow.d.ts +37 -0
- package/dist/adapters/thread/anthropic/workflow.js +36 -0
- package/dist/adapters/thread/anthropic/workflow.js.map +1 -0
- package/dist/adapters/thread/google-genai/index.cjs +102 -99
- package/dist/adapters/thread/google-genai/index.cjs.map +1 -1
- package/dist/adapters/thread/google-genai/index.d.cts +14 -113
- package/dist/adapters/thread/google-genai/index.d.ts +14 -113
- package/dist/adapters/thread/google-genai/index.js +103 -99
- package/dist/adapters/thread/google-genai/index.js.map +1 -1
- package/dist/adapters/thread/google-genai/workflow.cjs +9 -4
- package/dist/adapters/thread/google-genai/workflow.cjs.map +1 -1
- package/dist/adapters/thread/google-genai/workflow.d.cts +10 -5
- package/dist/adapters/thread/google-genai/workflow.d.ts +10 -5
- package/dist/adapters/thread/google-genai/workflow.js +9 -4
- package/dist/adapters/thread/google-genai/workflow.js.map +1 -1
- package/dist/adapters/thread/langchain/index.cjs +73 -63
- package/dist/adapters/thread/langchain/index.cjs.map +1 -1
- package/dist/adapters/thread/langchain/index.d.cts +39 -40
- package/dist/adapters/thread/langchain/index.d.ts +39 -40
- package/dist/adapters/thread/langchain/index.js +73 -64
- package/dist/adapters/thread/langchain/index.js.map +1 -1
- package/dist/adapters/thread/langchain/workflow.cjs +9 -4
- package/dist/adapters/thread/langchain/workflow.cjs.map +1 -1
- package/dist/adapters/thread/langchain/workflow.d.cts +10 -5
- package/dist/adapters/thread/langchain/workflow.d.ts +10 -5
- package/dist/adapters/thread/langchain/workflow.js +9 -4
- package/dist/adapters/thread/langchain/workflow.js.map +1 -1
- package/dist/index.cjs +27 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -12
- package/dist/index.d.ts +13 -12
- package/dist/index.js +28 -11
- package/dist/index.js.map +1 -1
- package/dist/proxy-BK1ydQt0.d.ts +24 -0
- package/dist/proxy-BMAsMHdp.d.cts +24 -0
- package/dist/{queries-DwBe2CAA.d.ts → queries-BCgJ9Sr5.d.ts} +1 -1
- package/dist/{queries-BYGBImeC.d.cts → queries-DwnE2bu3.d.cts} +1 -1
- package/dist/thread-manager-Bh9x847n.d.ts +31 -0
- package/dist/thread-manager-BlHua5_v.d.cts +39 -0
- package/dist/thread-manager-Bz8txKKj.d.cts +31 -0
- package/dist/thread-manager-dzaJHQEA.d.ts +39 -0
- package/dist/types-BfIQABzu.d.cts +73 -0
- package/dist/types-CIkYBoF8.d.ts +73 -0
- package/dist/{types-hmferhc2.d.ts → types-CvJyXDYt.d.ts} +44 -123
- package/dist/{types-LVKmCNds.d.ts → types-DFUNSYbj.d.ts} +1 -1
- package/dist/{types-Bf8KV0Ci.d.cts → types-DRnz-OZp.d.cts} +1 -1
- package/dist/{types-7PeMi1bD.d.cts → types-DSOefLpY.d.cts} +44 -123
- package/dist/{types-D_igp10o.d.cts → types-mCVxKIZb.d.cts} +233 -137
- package/dist/{types-D_igp10o.d.ts → types-mCVxKIZb.d.ts} +233 -137
- package/dist/workflow.cjs +25 -9
- package/dist/workflow.cjs.map +1 -1
- package/dist/workflow.d.cts +11 -11
- package/dist/workflow.d.ts +11 -11
- package/dist/workflow.js +26 -10
- package/dist/workflow.js.map +1 -1
- package/package.json +26 -1
- package/src/adapters/sandbox/virtual/with-virtual-sandbox.ts +8 -3
- package/src/adapters/thread/anthropic/activities.ts +226 -0
- package/src/adapters/thread/anthropic/index.ts +44 -0
- package/src/adapters/thread/anthropic/model-invoker.ts +129 -0
- package/src/adapters/thread/anthropic/proxy.ts +33 -0
- package/src/adapters/thread/anthropic/thread-manager.test.ts +137 -0
- package/src/adapters/thread/anthropic/thread-manager.ts +202 -0
- package/src/adapters/thread/google-genai/activities.ts +110 -33
- package/src/adapters/thread/google-genai/index.ts +3 -1
- package/src/adapters/thread/google-genai/model-invoker.ts +13 -42
- package/src/adapters/thread/google-genai/proxy.ts +6 -34
- package/src/adapters/thread/google-genai/thread-manager.test.ts +159 -0
- package/src/adapters/thread/google-genai/thread-manager.ts +96 -105
- package/src/adapters/thread/langchain/activities.ts +56 -21
- package/src/adapters/thread/langchain/hooks.ts +37 -0
- package/src/adapters/thread/langchain/index.ts +6 -1
- package/src/adapters/thread/langchain/model-invoker.ts +13 -12
- package/src/adapters/thread/langchain/proxy.ts +6 -34
- package/src/adapters/thread/langchain/thread-manager.test.ts +144 -0
- package/src/adapters/thread/langchain/thread-manager.ts +55 -98
- package/src/index.ts +5 -1
- package/src/lib/activity.ts +4 -3
- package/src/lib/hooks/types.ts +12 -12
- package/src/lib/model/types.ts +2 -0
- package/src/lib/session/session-edge-cases.integration.test.ts +24 -6
- package/src/lib/session/session.ts +18 -14
- package/src/lib/session/types.ts +31 -14
- package/src/lib/subagent/handler.ts +15 -8
- package/src/lib/subagent/types.ts +3 -2
- package/src/lib/thread/index.ts +3 -0
- package/src/lib/thread/manager.ts +4 -7
- package/src/lib/thread/proxy.ts +57 -0
- package/src/lib/thread/types.ts +44 -0
- package/src/lib/tool-router/auto-append-sandbox.integration.test.ts +54 -0
- package/src/lib/tool-router/auto-append.ts +5 -2
- package/src/lib/tool-router/router-edge-cases.integration.test.ts +9 -5
- package/src/lib/tool-router/router.ts +13 -7
- package/src/lib/tool-router/types.ts +20 -13
- package/src/lib/tool-router/with-sandbox.ts +4 -3
- package/src/lib/types.ts +7 -14
- package/src/workflow.ts +0 -4
- package/tsup.config.ts +5 -0
- package/dist/types-35POpVfa.d.cts +0 -40
- package/dist/types-35POpVfa.d.ts +0 -40
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import type Redis from "ioredis";
|
|
2
|
+
import type Anthropic from "@anthropic-ai/sdk";
|
|
3
|
+
import type { JsonValue } from "../../../lib/state/types";
|
|
4
|
+
import {
|
|
5
|
+
createThreadManager,
|
|
6
|
+
type ProviderThreadManager,
|
|
7
|
+
type ThreadManagerConfig,
|
|
8
|
+
type ThreadManagerHooks,
|
|
9
|
+
} from "../../../lib/thread";
|
|
10
|
+
|
|
11
|
+
/** SDK-native content type for Anthropic human messages */
|
|
12
|
+
export type AnthropicContent =
|
|
13
|
+
| string
|
|
14
|
+
| Anthropic.Messages.ContentBlockParam[];
|
|
15
|
+
|
|
16
|
+
/** A MessageParam with a unique ID for idempotent Redis storage */
|
|
17
|
+
export interface StoredMessage {
|
|
18
|
+
id: string;
|
|
19
|
+
message: Anthropic.Messages.MessageParam;
|
|
20
|
+
/** System messages are stored separately since Anthropic passes them via config */
|
|
21
|
+
isSystem?: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export type AnthropicThreadManagerHooks = ThreadManagerHooks<StoredMessage, Anthropic.Messages.MessageParam>;
|
|
25
|
+
|
|
26
|
+
export interface AnthropicThreadManagerConfig {
|
|
27
|
+
redis: Redis;
|
|
28
|
+
threadId: string;
|
|
29
|
+
/** Thread key, defaults to 'messages' */
|
|
30
|
+
key?: string;
|
|
31
|
+
hooks?: AnthropicThreadManagerHooks;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Prepared payload ready to send to the Anthropic API */
|
|
35
|
+
export interface AnthropicInvocationPayload {
|
|
36
|
+
messages: Anthropic.Messages.MessageParam[];
|
|
37
|
+
system?: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Thread manager with Anthropic MessageParam convenience helpers */
|
|
41
|
+
export interface AnthropicThreadManager
|
|
42
|
+
extends ProviderThreadManager<StoredMessage, AnthropicContent> {
|
|
43
|
+
appendAssistantMessage(
|
|
44
|
+
id: string,
|
|
45
|
+
content: Anthropic.Messages.ContentBlock[],
|
|
46
|
+
): Promise<void>;
|
|
47
|
+
prepareForInvocation(): Promise<AnthropicInvocationPayload>;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function storedMessageId(msg: StoredMessage): string {
|
|
51
|
+
return msg.id;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Normalise content into an array of ContentBlockParam */
|
|
55
|
+
function toContentBlocks(
|
|
56
|
+
content: AnthropicContent,
|
|
57
|
+
): Anthropic.Messages.ContentBlockParam[] {
|
|
58
|
+
if (typeof content === "string") {
|
|
59
|
+
return [{ type: "text", text: content }];
|
|
60
|
+
}
|
|
61
|
+
return content;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Merge consecutive messages with the same role.
|
|
66
|
+
* The Anthropic API requires alternating user/assistant turns; without
|
|
67
|
+
* merging, multiple sequential tool-result messages would violate this.
|
|
68
|
+
*/
|
|
69
|
+
function mergeConsecutiveMessages(
|
|
70
|
+
messages: Anthropic.Messages.MessageParam[],
|
|
71
|
+
): Anthropic.Messages.MessageParam[] {
|
|
72
|
+
const merged: Anthropic.Messages.MessageParam[] = [];
|
|
73
|
+
for (const msg of messages) {
|
|
74
|
+
const last = merged[merged.length - 1];
|
|
75
|
+
if (last && last.role === msg.role) {
|
|
76
|
+
const lastContent = Array.isArray(last.content)
|
|
77
|
+
? last.content
|
|
78
|
+
: [{ type: "text" as const, text: last.content }];
|
|
79
|
+
const msgContent = Array.isArray(msg.content)
|
|
80
|
+
? msg.content
|
|
81
|
+
: [{ type: "text" as const, text: msg.content }];
|
|
82
|
+
last.content = [...lastContent, ...msgContent];
|
|
83
|
+
} else {
|
|
84
|
+
merged.push({
|
|
85
|
+
...msg,
|
|
86
|
+
content: Array.isArray(msg.content)
|
|
87
|
+
? [...msg.content]
|
|
88
|
+
: msg.content,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return merged;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Creates an Anthropic-specific thread manager that stores StoredMessage
|
|
97
|
+
* instances in Redis and provides convenience helpers for creating and
|
|
98
|
+
* appending typed messages.
|
|
99
|
+
*/
|
|
100
|
+
export function createAnthropicThreadManager(
|
|
101
|
+
config: AnthropicThreadManagerConfig,
|
|
102
|
+
): AnthropicThreadManager {
|
|
103
|
+
const baseConfig: ThreadManagerConfig<StoredMessage> = {
|
|
104
|
+
redis: config.redis,
|
|
105
|
+
threadId: config.threadId,
|
|
106
|
+
key: config.key,
|
|
107
|
+
idOf: storedMessageId,
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const base = createThreadManager(baseConfig);
|
|
111
|
+
|
|
112
|
+
const helpers: Omit<AnthropicThreadManager, keyof typeof base> = {
|
|
113
|
+
async appendUserMessage(
|
|
114
|
+
id: string,
|
|
115
|
+
content: AnthropicContent,
|
|
116
|
+
): Promise<void> {
|
|
117
|
+
await base.append([{
|
|
118
|
+
id,
|
|
119
|
+
message: { role: "user", content: toContentBlocks(content) },
|
|
120
|
+
}]);
|
|
121
|
+
},
|
|
122
|
+
|
|
123
|
+
async appendSystemMessage(id: string, content: string): Promise<void> {
|
|
124
|
+
await base.initialize();
|
|
125
|
+
await base.append([{
|
|
126
|
+
id,
|
|
127
|
+
message: { role: "user", content },
|
|
128
|
+
isSystem: true,
|
|
129
|
+
}]);
|
|
130
|
+
},
|
|
131
|
+
|
|
132
|
+
async appendAssistantMessage(
|
|
133
|
+
id: string,
|
|
134
|
+
content: Anthropic.Messages.ContentBlock[],
|
|
135
|
+
): Promise<void> {
|
|
136
|
+
await base.append([{
|
|
137
|
+
id,
|
|
138
|
+
message: {
|
|
139
|
+
role: "assistant",
|
|
140
|
+
content: content as unknown as Anthropic.Messages.ContentBlockParam[],
|
|
141
|
+
},
|
|
142
|
+
}]);
|
|
143
|
+
},
|
|
144
|
+
|
|
145
|
+
async appendToolResult(
|
|
146
|
+
id: string,
|
|
147
|
+
toolCallId: string,
|
|
148
|
+
_toolName: string,
|
|
149
|
+
content: JsonValue,
|
|
150
|
+
): Promise<void> {
|
|
151
|
+
const toolContent =
|
|
152
|
+
typeof content === "string"
|
|
153
|
+
? content
|
|
154
|
+
: Array.isArray(content)
|
|
155
|
+
? (content as unknown as Anthropic.Messages.ToolResultBlockParam["content"])
|
|
156
|
+
: JSON.stringify(content);
|
|
157
|
+
await base.append([{
|
|
158
|
+
id,
|
|
159
|
+
message: {
|
|
160
|
+
role: "user",
|
|
161
|
+
content: [{
|
|
162
|
+
type: "tool_result" as const,
|
|
163
|
+
tool_use_id: toolCallId,
|
|
164
|
+
content: toolContent,
|
|
165
|
+
}],
|
|
166
|
+
},
|
|
167
|
+
}]);
|
|
168
|
+
},
|
|
169
|
+
|
|
170
|
+
async prepareForInvocation(): Promise<AnthropicInvocationPayload> {
|
|
171
|
+
const stored = await base.load();
|
|
172
|
+
const { onPrepareMessage, onPreparedMessage } = config.hooks ?? {};
|
|
173
|
+
const mapped = onPrepareMessage
|
|
174
|
+
? stored.map((msg, i) => onPrepareMessage(msg, i, stored))
|
|
175
|
+
: stored;
|
|
176
|
+
|
|
177
|
+
let system: string | undefined;
|
|
178
|
+
const conversationMessages: Anthropic.Messages.MessageParam[] = [];
|
|
179
|
+
|
|
180
|
+
for (const item of mapped) {
|
|
181
|
+
if (item.isSystem) {
|
|
182
|
+
system =
|
|
183
|
+
typeof item.message.content === "string"
|
|
184
|
+
? item.message.content
|
|
185
|
+
: undefined;
|
|
186
|
+
} else {
|
|
187
|
+
conversationMessages.push(item.message);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const messages = mergeConsecutiveMessages(conversationMessages);
|
|
192
|
+
return {
|
|
193
|
+
messages: onPreparedMessage
|
|
194
|
+
? messages.map((msg, i) => onPreparedMessage(msg, i, messages))
|
|
195
|
+
: messages,
|
|
196
|
+
...(system ? { system } : {}),
|
|
197
|
+
};
|
|
198
|
+
},
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
return Object.assign(base, helpers);
|
|
202
|
+
}
|
|
@@ -1,33 +1,69 @@
|
|
|
1
1
|
import type Redis from "ioredis";
|
|
2
|
-
import type { GoogleGenAI, Content } from "@google/genai";
|
|
2
|
+
import type { GoogleGenAI, Content, Part } from "@google/genai";
|
|
3
3
|
import type { ToolResultConfig } from "../../../lib/types";
|
|
4
|
-
import type {
|
|
4
|
+
import type {
|
|
5
|
+
ActivityToolHandler,
|
|
6
|
+
RouterContext,
|
|
7
|
+
ToolHandlerResponse,
|
|
8
|
+
} from "../../../lib/tool-router/types";
|
|
5
9
|
import type {
|
|
6
10
|
ThreadOps,
|
|
7
11
|
PrefixedThreadOps,
|
|
8
12
|
ScopedPrefix,
|
|
9
13
|
} from "../../../lib/session/types";
|
|
10
14
|
import type { ModelInvoker } from "../../../lib/model";
|
|
11
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
createGoogleGenAIThreadManager,
|
|
17
|
+
type GoogleGenAIContent,
|
|
18
|
+
type GoogleGenAIThreadManagerHooks,
|
|
19
|
+
} from "./thread-manager";
|
|
12
20
|
import { createGoogleGenAIModelInvoker } from "./model-invoker";
|
|
13
21
|
|
|
14
22
|
const ADAPTER_PREFIX = "googleGenAI" as const;
|
|
15
23
|
|
|
16
24
|
export type GoogleGenAIThreadOps<TScope extends string = ""> =
|
|
17
|
-
PrefixedThreadOps<
|
|
25
|
+
PrefixedThreadOps<
|
|
26
|
+
ScopedPrefix<TScope, typeof ADAPTER_PREFIX>,
|
|
27
|
+
GoogleGenAIContent
|
|
28
|
+
>;
|
|
18
29
|
|
|
19
30
|
export interface GoogleGenAIAdapterConfig {
|
|
20
31
|
redis: Redis;
|
|
21
|
-
client
|
|
32
|
+
client?: GoogleGenAI;
|
|
22
33
|
/** Default model name (e.g. 'gemini-2.5-flash'). If omitted, use `createModelInvoker()` */
|
|
23
34
|
model?: string;
|
|
35
|
+
hooks?: GoogleGenAIThreadManagerHooks;
|
|
24
36
|
}
|
|
25
37
|
|
|
38
|
+
/**
|
|
39
|
+
* Tool response type accepted by the Google GenAI adapter.
|
|
40
|
+
*
|
|
41
|
+
* Handlers can return:
|
|
42
|
+
* - **`string`** — plain text, wrapped in a `functionResponse` part.
|
|
43
|
+
* - **`Record<string, unknown>`** — structured object used as `functionResponse.response`.
|
|
44
|
+
* - **`Part[]`** — pre-built parts used directly as `Content.parts`.
|
|
45
|
+
* The handler is responsible for building correct Part objects (e.g. `functionResponse`,
|
|
46
|
+
* `inlineData`, `text`). Use `context.toolCallId` and `context.toolName` to construct
|
|
47
|
+
* `functionResponse` parts.
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```typescript
|
|
51
|
+
* adapter.wrapHandler(async (args, ctx) => ({
|
|
52
|
+
* toolResponse: [
|
|
53
|
+
* { functionResponse: { id: ctx.toolCallId, name: ctx.toolName, response: { result: "done" } } },
|
|
54
|
+
* { inlineData: { data: base64, mimeType: "image/png" } },
|
|
55
|
+
* ],
|
|
56
|
+
* data: null,
|
|
57
|
+
* }));
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
export type GoogleGenAIToolResponse = string | Record<string, unknown> | Part[];
|
|
61
|
+
|
|
26
62
|
export interface GoogleGenAIAdapter {
|
|
27
63
|
/** Model invoker using the default model (only available when `model` was provided) */
|
|
28
64
|
invoker: ModelInvoker<Content>;
|
|
29
65
|
/** Create an invoker for a specific model name (for multi-model setups) */
|
|
30
|
-
createModelInvoker(model: string): ModelInvoker<Content>;
|
|
66
|
+
createModelInvoker(model: string, client: GoogleGenAI): ModelInvoker<Content>;
|
|
31
67
|
/**
|
|
32
68
|
* Create prefixed thread activities for registration on the worker.
|
|
33
69
|
*
|
|
@@ -43,9 +79,18 @@ export interface GoogleGenAIAdapter {
|
|
|
43
79
|
* // → { googleGenAIResearchAgentInitializeThread, … }
|
|
44
80
|
* ```
|
|
45
81
|
*/
|
|
46
|
-
createActivities<S extends string = "">(
|
|
47
|
-
|
|
48
|
-
|
|
82
|
+
createActivities<S extends string = "">(scope?: S): GoogleGenAIThreadOps<S>;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Identity wrapper that types a tool handler for this adapter.
|
|
86
|
+
* Constrains `toolResponse` to {@link GoogleGenAIToolResponse}.
|
|
87
|
+
*/
|
|
88
|
+
wrapHandler<TArgs, TResult, TContext extends RouterContext = RouterContext>(
|
|
89
|
+
handler: (
|
|
90
|
+
args: TArgs,
|
|
91
|
+
context: TContext
|
|
92
|
+
) => Promise<ToolHandlerResponse<TResult, GoogleGenAIToolResponse>>
|
|
93
|
+
): ActivityToolHandler<TArgs, TResult, TContext, GoogleGenAIToolResponse>;
|
|
49
94
|
}
|
|
50
95
|
|
|
51
96
|
/**
|
|
@@ -91,45 +136,73 @@ export interface GoogleGenAIAdapter {
|
|
|
91
136
|
export function createGoogleGenAIAdapter(
|
|
92
137
|
config: GoogleGenAIAdapterConfig
|
|
93
138
|
): GoogleGenAIAdapter {
|
|
94
|
-
const { redis
|
|
139
|
+
const { redis } = config;
|
|
95
140
|
|
|
96
|
-
const threadOps: ThreadOps = {
|
|
97
|
-
async initializeThread(
|
|
98
|
-
|
|
141
|
+
const threadOps: ThreadOps<GoogleGenAIContent> = {
|
|
142
|
+
async initializeThread(
|
|
143
|
+
threadId: string,
|
|
144
|
+
threadKey?: string
|
|
145
|
+
): Promise<void> {
|
|
146
|
+
const thread = createGoogleGenAIThreadManager({
|
|
147
|
+
redis,
|
|
148
|
+
threadId,
|
|
149
|
+
key: threadKey,
|
|
150
|
+
});
|
|
99
151
|
await thread.initialize();
|
|
100
152
|
},
|
|
101
153
|
|
|
102
154
|
async appendHumanMessage(
|
|
103
155
|
threadId: string,
|
|
104
156
|
id: string,
|
|
105
|
-
content:
|
|
157
|
+
content: GoogleGenAIContent,
|
|
158
|
+
threadKey?: string
|
|
106
159
|
): Promise<void> {
|
|
107
|
-
const thread = createGoogleGenAIThreadManager({
|
|
160
|
+
const thread = createGoogleGenAIThreadManager({
|
|
161
|
+
redis,
|
|
162
|
+
threadId,
|
|
163
|
+
key: threadKey,
|
|
164
|
+
});
|
|
108
165
|
await thread.appendUserMessage(id, content);
|
|
109
166
|
},
|
|
110
167
|
|
|
111
168
|
async appendSystemMessage(
|
|
112
169
|
threadId: string,
|
|
113
170
|
id: string,
|
|
114
|
-
content: string
|
|
171
|
+
content: string,
|
|
172
|
+
threadKey?: string
|
|
115
173
|
): Promise<void> {
|
|
116
|
-
const thread = createGoogleGenAIThreadManager({
|
|
174
|
+
const thread = createGoogleGenAIThreadManager({
|
|
175
|
+
redis,
|
|
176
|
+
threadId,
|
|
177
|
+
key: threadKey,
|
|
178
|
+
});
|
|
117
179
|
await thread.appendSystemMessage(id, content);
|
|
118
180
|
},
|
|
119
181
|
|
|
120
182
|
async appendToolResult(id: string, cfg: ToolResultConfig): Promise<void> {
|
|
121
|
-
const { threadId, toolCallId, toolName, content } = cfg;
|
|
122
|
-
const thread = createGoogleGenAIThreadManager({
|
|
123
|
-
|
|
183
|
+
const { threadId, threadKey, toolCallId, toolName, content } = cfg;
|
|
184
|
+
const thread = createGoogleGenAIThreadManager({
|
|
185
|
+
redis,
|
|
186
|
+
threadId,
|
|
187
|
+
key: threadKey,
|
|
188
|
+
});
|
|
189
|
+
await thread.appendToolResult(
|
|
190
|
+
id,
|
|
191
|
+
toolCallId,
|
|
192
|
+
toolName,
|
|
193
|
+
content as GoogleGenAIToolResponse
|
|
194
|
+
);
|
|
124
195
|
},
|
|
125
196
|
|
|
126
197
|
async forkThread(
|
|
127
198
|
sourceThreadId: string,
|
|
128
|
-
targetThreadId: string
|
|
199
|
+
targetThreadId: string,
|
|
200
|
+
threadKey?: string
|
|
129
201
|
): Promise<void> {
|
|
130
202
|
const thread = createGoogleGenAIThreadManager({
|
|
131
203
|
redis,
|
|
132
204
|
threadId: sourceThreadId,
|
|
205
|
+
key: threadKey,
|
|
133
206
|
});
|
|
134
207
|
await thread.fork(targetThreadId);
|
|
135
208
|
},
|
|
@@ -141,28 +214,32 @@ export function createGoogleGenAIAdapter(
|
|
|
141
214
|
const prefix = scope
|
|
142
215
|
? `${ADAPTER_PREFIX}${scope.charAt(0).toUpperCase()}${scope.slice(1)}`
|
|
143
216
|
: ADAPTER_PREFIX;
|
|
144
|
-
const cap = (s: string): string =>
|
|
145
|
-
s.charAt(0).toUpperCase() + s.slice(1);
|
|
217
|
+
const cap = (s: string): string => s.charAt(0).toUpperCase() + s.slice(1);
|
|
146
218
|
return Object.fromEntries(
|
|
147
219
|
Object.entries(threadOps).map(([k, v]) => [`${prefix}${cap(k)}`, v])
|
|
148
220
|
) as GoogleGenAIThreadOps<S>;
|
|
149
221
|
}
|
|
150
222
|
|
|
151
|
-
const makeInvoker = (
|
|
152
|
-
|
|
223
|
+
const makeInvoker = (
|
|
224
|
+
model: string,
|
|
225
|
+
client: GoogleGenAI
|
|
226
|
+
): ModelInvoker<Content> =>
|
|
227
|
+
createGoogleGenAIModelInvoker({ redis, client, model, hooks: config.hooks });
|
|
153
228
|
|
|
154
|
-
const invoker: ModelInvoker<Content> =
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
"
|
|
160
|
-
|
|
161
|
-
|
|
229
|
+
const invoker: ModelInvoker<Content> =
|
|
230
|
+
config.model && config.client
|
|
231
|
+
? makeInvoker(config.model, config.client)
|
|
232
|
+
: ((() => {
|
|
233
|
+
throw new Error(
|
|
234
|
+
"No default model provided to createGoogleGenAIAdapter. " +
|
|
235
|
+
"Either pass `model` in the config or use `createModelInvoker(model)` instead."
|
|
236
|
+
);
|
|
237
|
+
}) as unknown as ModelInvoker<Content>);
|
|
162
238
|
|
|
163
239
|
return {
|
|
164
240
|
createActivities,
|
|
165
241
|
invoker,
|
|
166
242
|
createModelInvoker: makeInvoker,
|
|
243
|
+
wrapHandler: (handler) => handler,
|
|
167
244
|
};
|
|
168
245
|
}
|
|
@@ -23,14 +23,16 @@ export {
|
|
|
23
23
|
type GoogleGenAIAdapter,
|
|
24
24
|
type GoogleGenAIAdapterConfig,
|
|
25
25
|
type GoogleGenAIThreadOps,
|
|
26
|
+
type GoogleGenAIToolResponse,
|
|
26
27
|
} from "./activities";
|
|
27
28
|
|
|
28
29
|
// Thread manager
|
|
29
30
|
export {
|
|
30
31
|
createGoogleGenAIThreadManager,
|
|
31
|
-
messageContentToParts,
|
|
32
32
|
type GoogleGenAIThreadManager,
|
|
33
33
|
type GoogleGenAIThreadManagerConfig,
|
|
34
|
+
type GoogleGenAIContent,
|
|
35
|
+
type GoogleGenAIInvocationPayload,
|
|
34
36
|
type StoredContent,
|
|
35
37
|
} from "./thread-manager";
|
|
36
38
|
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import type Redis from "ioredis";
|
|
2
2
|
import type { GoogleGenAI, Content, FunctionDeclaration } from "@google/genai";
|
|
3
3
|
import type { SerializableToolDefinition } from "../../../lib/types";
|
|
4
|
-
import type { AgentResponse } from "../../../lib/model";
|
|
5
|
-
import type
|
|
6
|
-
import { createGoogleGenAIThreadManager } from "./thread-manager";
|
|
4
|
+
import type { AgentResponse, ModelInvokerConfig } from "../../../lib/model";
|
|
5
|
+
import { createGoogleGenAIThreadManager, type GoogleGenAIThreadManagerHooks } from "./thread-manager";
|
|
7
6
|
import { v4 as uuidv4 } from "uuid";
|
|
8
7
|
|
|
9
8
|
export interface GoogleGenAIModelInvokerConfig {
|
|
10
9
|
redis: Redis;
|
|
11
10
|
client: GoogleGenAI;
|
|
12
11
|
model: string;
|
|
12
|
+
hooks?: GoogleGenAIThreadManagerHooks;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
function toFunctionDeclarations(
|
|
16
|
-
tools: SerializableToolDefinition[]
|
|
16
|
+
tools: SerializableToolDefinition[],
|
|
17
17
|
): FunctionDeclaration[] {
|
|
18
18
|
return tools.map((t) => ({
|
|
19
19
|
name: t.name,
|
|
@@ -22,24 +22,6 @@ function toFunctionDeclarations(
|
|
|
22
22
|
}));
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
/**
|
|
26
|
-
* Merge consecutive Content objects sharing the same role.
|
|
27
|
-
* The Gemini API requires alternating user/model turns; without
|
|
28
|
-
* merging, multiple sequential tool-result messages would violate this.
|
|
29
|
-
*/
|
|
30
|
-
function mergeConsecutiveContents(contents: Content[]): Content[] {
|
|
31
|
-
const merged: Content[] = [];
|
|
32
|
-
for (const content of contents) {
|
|
33
|
-
const last = merged[merged.length - 1];
|
|
34
|
-
if (last && last.role === content.role) {
|
|
35
|
-
last.parts = [...(last.parts ?? []), ...(content.parts ?? [])];
|
|
36
|
-
} else {
|
|
37
|
-
merged.push({ ...content, parts: [...(content.parts ?? [])] });
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
return merged;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
25
|
/**
|
|
44
26
|
* Creates a Google GenAI model invoker that satisfies the generic
|
|
45
27
|
* `ModelInvoker<Content>` contract.
|
|
@@ -68,29 +50,16 @@ export function createGoogleGenAIModelInvoker({
|
|
|
68
50
|
redis,
|
|
69
51
|
client,
|
|
70
52
|
model,
|
|
53
|
+
hooks,
|
|
71
54
|
}: GoogleGenAIModelInvokerConfig) {
|
|
72
55
|
return async function invokeGoogleGenAIModel(
|
|
73
|
-
config: ModelInvokerConfig
|
|
56
|
+
config: ModelInvokerConfig,
|
|
74
57
|
): Promise<AgentResponse<Content>> {
|
|
75
|
-
const { threadId, state } = config;
|
|
76
|
-
|
|
77
|
-
const thread = createGoogleGenAIThreadManager({ redis, threadId });
|
|
78
|
-
const stored = await thread.load();
|
|
79
|
-
|
|
80
|
-
// Separate system instructions from conversation content.
|
|
81
|
-
// Google GenAI takes system instructions via config, not in the contents array.
|
|
82
|
-
let systemInstruction: string | undefined;
|
|
83
|
-
const conversationContents: Content[] = [];
|
|
84
|
-
|
|
85
|
-
for (const item of stored) {
|
|
86
|
-
if (item.content.role === "system") {
|
|
87
|
-
systemInstruction = item.content.parts?.[0]?.text;
|
|
88
|
-
} else {
|
|
89
|
-
conversationContents.push(item.content);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
58
|
+
const { threadId, threadKey, state } = config;
|
|
92
59
|
|
|
93
|
-
const
|
|
60
|
+
const thread = createGoogleGenAIThreadManager({ redis, threadId, key: threadKey, hooks });
|
|
61
|
+
const { contents, systemInstruction } =
|
|
62
|
+
await thread.prepareForInvocation();
|
|
94
63
|
|
|
95
64
|
const functionDeclarations = toFunctionDeclarations(state.tools);
|
|
96
65
|
const tools =
|
|
@@ -137,13 +106,15 @@ export async function invokeGoogleGenAIModel({
|
|
|
137
106
|
redis,
|
|
138
107
|
client,
|
|
139
108
|
model,
|
|
109
|
+
hooks,
|
|
140
110
|
config,
|
|
141
111
|
}: {
|
|
142
112
|
redis: Redis;
|
|
143
113
|
client: GoogleGenAI;
|
|
144
114
|
model: string;
|
|
115
|
+
hooks?: GoogleGenAIThreadManagerHooks;
|
|
145
116
|
config: ModelInvokerConfig;
|
|
146
117
|
}): Promise<AgentResponse<Content>> {
|
|
147
|
-
const invoker = createGoogleGenAIModelInvoker({ redis, client, model });
|
|
118
|
+
const invoker = createGoogleGenAIModelInvoker({ redis, client, model, hooks });
|
|
148
119
|
return invoker(config);
|
|
149
120
|
}
|
|
@@ -18,44 +18,16 @@
|
|
|
18
18
|
* const threadOps = proxyGoogleGenAIThreadOps("customScope");
|
|
19
19
|
* ```
|
|
20
20
|
*/
|
|
21
|
-
import {
|
|
22
|
-
proxyActivities,
|
|
23
|
-
workflowInfo,
|
|
24
|
-
type ActivityInterfaceFor,
|
|
25
|
-
} from "@temporalio/workflow";
|
|
21
|
+
import { type ActivityInterfaceFor } from "@temporalio/workflow";
|
|
26
22
|
import type { ThreadOps } from "../../../lib/session/types";
|
|
23
|
+
import type { GoogleGenAIContent } from "./thread-manager";
|
|
24
|
+
import { createThreadOpsProxy } from "../../../lib/thread/proxy";
|
|
27
25
|
|
|
28
26
|
const ADAPTER_PREFIX = "googleGenAI";
|
|
29
27
|
|
|
30
28
|
export function proxyGoogleGenAIThreadOps(
|
|
31
29
|
scope?: string,
|
|
32
|
-
options?: Parameters<typeof
|
|
33
|
-
): ActivityInterfaceFor<ThreadOps
|
|
34
|
-
|
|
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>;
|
|
30
|
+
options?: Parameters<typeof createThreadOpsProxy>[2],
|
|
31
|
+
): ActivityInterfaceFor<ThreadOps<GoogleGenAIContent>> {
|
|
32
|
+
return createThreadOpsProxy(ADAPTER_PREFIX, scope, options) as ActivityInterfaceFor<ThreadOps<GoogleGenAIContent>>;
|
|
61
33
|
}
|