zeitlich 0.2.14 → 0.2.15
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 +12 -12
- package/dist/adapters/thread/google-genai/index.cjs +22 -0
- package/dist/adapters/thread/google-genai/index.cjs.map +1 -1
- package/dist/adapters/thread/google-genai/index.d.cts +2 -2
- package/dist/adapters/thread/google-genai/index.d.ts +2 -2
- package/dist/adapters/thread/google-genai/index.js +22 -0
- package/dist/adapters/thread/google-genai/index.js.map +1 -1
- package/dist/adapters/thread/langchain/index.cjs +22 -0
- package/dist/adapters/thread/langchain/index.cjs.map +1 -1
- package/dist/adapters/thread/langchain/index.d.cts +2 -2
- package/dist/adapters/thread/langchain/index.d.ts +2 -2
- package/dist/adapters/thread/langchain/index.js +22 -0
- package/dist/adapters/thread/langchain/index.js.map +1 -1
- package/dist/index.cjs +36 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +36 -8
- package/dist/index.js.map +1 -1
- package/dist/{types-B9ljZewB.d.cts → types-35POpVfa.d.cts} +6 -0
- package/dist/{types-B9ljZewB.d.ts → types-35POpVfa.d.ts} +6 -0
- package/dist/{types-B4C9txdq.d.ts → types-BWvIYK28.d.ts} +2 -0
- package/dist/{types-GpMU4b0w.d.cts → types-Dje1TdH6.d.cts} +2 -0
- package/dist/workflow.cjs +21 -8
- 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 +21 -8
- package/dist/workflow.js.map +1 -1
- package/package.json +7 -3
- package/src/adapters/thread/google-genai/activities.ts +11 -0
- package/src/adapters/thread/langchain/activities.ts +11 -0
- package/src/lib/session/session.ts +7 -2
- package/src/lib/session/types.ts +2 -0
- package/src/lib/subagent/register.ts +22 -7
- package/src/lib/thread/manager.ts +20 -1
- package/src/lib/thread/types.ts +6 -0
package/README.md
CHANGED
|
@@ -433,7 +433,7 @@ The `Subagent` tool is automatically added when subagents are configured, allowi
|
|
|
433
433
|
|
|
434
434
|
### Thread Continuation
|
|
435
435
|
|
|
436
|
-
By default, each session initializes a fresh thread. To continue an existing thread (e.g., resuming a conversation after a workflow completes), pass `continueThread: true` along with the previous `threadId`:
|
|
436
|
+
By default, each session initializes a fresh thread. To continue from an existing thread (e.g., resuming a conversation after a workflow completes), pass `continueThread: true` along with the previous `threadId`:
|
|
437
437
|
|
|
438
438
|
```typescript
|
|
439
439
|
import { createSession } from "zeitlich/workflow";
|
|
@@ -444,14 +444,16 @@ const session = await createSession({
|
|
|
444
444
|
// ... other config
|
|
445
445
|
});
|
|
446
446
|
|
|
447
|
-
// Later — new workflow
|
|
447
|
+
// Later — new workflow forks the previous thread
|
|
448
448
|
const resumedSession = await createSession({
|
|
449
|
-
threadId: savedThreadId, //
|
|
450
|
-
continueThread: true, //
|
|
449
|
+
threadId: savedThreadId, // the thread to continue from
|
|
450
|
+
continueThread: true, // fork into a new thread with the old messages
|
|
451
451
|
// ... other config
|
|
452
452
|
});
|
|
453
453
|
```
|
|
454
454
|
|
|
455
|
+
When `continueThread` is true the session **forks** the provided thread — it copies all messages into a new thread and operates on the copy. The original thread is never mutated, so multiple sessions can safely continue from the same thread in parallel.
|
|
456
|
+
|
|
455
457
|
`getShortId()` produces compact, workflow-deterministic IDs (~12 base-62 chars) that are more token-efficient than UUIDs.
|
|
456
458
|
|
|
457
459
|
#### Subagent Thread Continuation
|
|
@@ -459,26 +461,24 @@ const resumedSession = await createSession({
|
|
|
459
461
|
Subagents can opt in to thread continuation via `allowThreadContinuation`. When enabled, the parent agent can pass a `threadId` to resume a previous subagent conversation:
|
|
460
462
|
|
|
461
463
|
```typescript
|
|
462
|
-
import {
|
|
464
|
+
import { type SubagentWorkflow } from "zeitlich/workflow";
|
|
463
465
|
|
|
464
466
|
// Subagent workflow that supports continuation
|
|
465
467
|
export const researcherWorkflow: SubagentWorkflow = async ({
|
|
466
468
|
prompt,
|
|
467
|
-
|
|
469
|
+
previousThreadId,
|
|
468
470
|
}) => {
|
|
469
|
-
const effectiveThreadId = threadId ?? getShortId();
|
|
470
|
-
|
|
471
471
|
const session = await createSession({
|
|
472
|
-
threadId:
|
|
473
|
-
continueThread: !!
|
|
472
|
+
threadId: previousThreadId, // undefined → fresh thread, set → fork
|
|
473
|
+
continueThread: !!previousThreadId,
|
|
474
474
|
// ... other config
|
|
475
475
|
});
|
|
476
476
|
|
|
477
|
-
const { finalMessage } = await session.runSession({ stateManager });
|
|
477
|
+
const { threadId, finalMessage } = await session.runSession({ stateManager });
|
|
478
478
|
return {
|
|
479
479
|
toolResponse: finalMessage ? extractText(finalMessage) : "No response",
|
|
480
480
|
data: null,
|
|
481
|
-
threadId
|
|
481
|
+
threadId,
|
|
482
482
|
};
|
|
483
483
|
};
|
|
484
484
|
|
|
@@ -64,6 +64,21 @@ function createThreadManager(config) {
|
|
|
64
64
|
await redis.expire(redisKey, THREAD_TTL_SECONDS);
|
|
65
65
|
}
|
|
66
66
|
},
|
|
67
|
+
async fork(newThreadId) {
|
|
68
|
+
await assertThreadExists();
|
|
69
|
+
const data = await redis.lrange(redisKey, 0, -1);
|
|
70
|
+
const forked = createThreadManager({
|
|
71
|
+
...config,
|
|
72
|
+
threadId: newThreadId
|
|
73
|
+
});
|
|
74
|
+
await forked.initialize();
|
|
75
|
+
if (data.length > 0) {
|
|
76
|
+
const newKey = getThreadKey(newThreadId, key);
|
|
77
|
+
await redis.rpush(newKey, ...data);
|
|
78
|
+
await redis.expire(newKey, THREAD_TTL_SECONDS);
|
|
79
|
+
}
|
|
80
|
+
return forked;
|
|
81
|
+
},
|
|
67
82
|
async delete() {
|
|
68
83
|
await redis.del(redisKey, metaKey);
|
|
69
84
|
}
|
|
@@ -260,6 +275,13 @@ function createGoogleGenAIAdapter(config) {
|
|
|
260
275
|
const { threadId, toolCallId, toolName, content } = cfg;
|
|
261
276
|
const thread = createGoogleGenAIThreadManager({ redis, threadId });
|
|
262
277
|
await thread.appendToolResult(toolCallId, toolName, content);
|
|
278
|
+
},
|
|
279
|
+
async forkThread(sourceThreadId, targetThreadId) {
|
|
280
|
+
const thread = createGoogleGenAIThreadManager({
|
|
281
|
+
redis,
|
|
282
|
+
threadId: sourceThreadId
|
|
283
|
+
});
|
|
284
|
+
await thread.fork(targetThreadId);
|
|
263
285
|
}
|
|
264
286
|
};
|
|
265
287
|
const makeInvoker = (model) => createGoogleGenAIModelInvoker({ redis, client, model });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/lib/thread/manager.ts","../../../../src/adapters/thread/google-genai/thread-manager.ts","../../../../src/adapters/thread/google-genai/model-invoker.ts","../../../../src/adapters/thread/google-genai/activities.ts"],"names":["invokeGoogleGenAIModel"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { ThreadManagerConfig, BaseThreadManager } from \"./types\";\n\nconst THREAD_TTL_SECONDS = 60 * 60 * 24 * 90; // 90 days\n\n/**\n * Lua script for atomic idempotent append.\n * Checks a dedup key; if it exists the message was already appended and we\n * return 0. Otherwise appends all messages to the list, sets TTL on both\n * the list and the dedup key, and returns 1.\n *\n * KEYS[1] = dedup key, KEYS[2] = list key\n * ARGV[1] = TTL seconds, ARGV[2..N] = serialised messages\n */\nconst APPEND_IDEMPOTENT_SCRIPT = `\nif redis.call('EXISTS', KEYS[1]) == 1 then\n return 0\nend\nfor i = 2, #ARGV do\n redis.call('RPUSH', KEYS[2], ARGV[i])\nend\nredis.call('EXPIRE', KEYS[2], tonumber(ARGV[1]))\nredis.call('SET', KEYS[1], '1', 'EX', tonumber(ARGV[1]))\nreturn 1\n`;\n\nfunction getThreadKey(threadId: string, key: string): string {\n return `thread:${threadId}:${key}`;\n}\n\n/**\n * Creates a generic thread manager for handling conversation state in Redis.\n * Framework-agnostic — works with any serializable message type.\n */\nexport function createThreadManager<T>(\n config: ThreadManagerConfig<T>,\n): BaseThreadManager<T> {\n const {\n redis,\n threadId,\n key = \"messages\",\n serialize = (m: T): string => JSON.stringify(m),\n deserialize = (raw: string): T => JSON.parse(raw) as T,\n idOf,\n } = config;\n const redisKey = getThreadKey(threadId, key);\n const metaKey = getThreadKey(threadId, `${key}:meta`);\n\n async function assertThreadExists(): Promise<void> {\n const exists = await redis.exists(metaKey);\n if (!exists) {\n throw new Error(`Thread \"${threadId}\" (key: ${key}) does not exist`);\n }\n }\n\n return {\n async initialize(): Promise<void> {\n await redis.del(redisKey);\n await redis.set(metaKey, \"1\", \"EX\", THREAD_TTL_SECONDS);\n },\n\n async load(): Promise<T[]> {\n await assertThreadExists();\n const data = await redis.lrange(redisKey, 0, -1);\n return data.map(deserialize);\n },\n\n async append(messages: T[]): Promise<void> {\n if (messages.length === 0) return;\n await assertThreadExists();\n\n if (idOf) {\n const dedupId = messages.map(idOf).join(\":\");\n const dedupKey = getThreadKey(threadId, `dedup:${dedupId}`);\n await redis.eval(\n APPEND_IDEMPOTENT_SCRIPT,\n 2,\n dedupKey,\n redisKey,\n String(THREAD_TTL_SECONDS),\n ...messages.map(serialize),\n );\n } else {\n await redis.rpush(redisKey, ...messages.map(serialize));\n await redis.expire(redisKey, THREAD_TTL_SECONDS);\n }\n },\n\n async delete(): Promise<void> {\n await redis.del(redisKey, metaKey);\n },\n };\n}\n","import type Redis from \"ioredis\";\nimport type { Content, Part } from \"@google/genai\";\nimport {\n createThreadManager,\n type BaseThreadManager,\n type ThreadManagerConfig,\n} from \"../../../lib/thread\";\nimport type { MessageContent, ToolMessageContent } from \"../../../lib/types\";\n\n/** A Content with a unique ID for idempotent Redis storage */\nexport interface StoredContent {\n id: string;\n content: Content;\n}\n\nexport interface GoogleGenAIThreadManagerConfig {\n redis: Redis;\n threadId: string;\n /** Thread key, defaults to 'messages' */\n key?: string;\n}\n\n/** Thread manager with Google GenAI Content convenience helpers */\nexport interface GoogleGenAIThreadManager\n extends BaseThreadManager<StoredContent> {\n createUserContent(content: string | MessageContent): StoredContent;\n createSystemContent(content: string): StoredContent;\n createModelContent(parts: Part[]): StoredContent;\n createToolResponseContent(\n toolCallId: string,\n toolName: string,\n content: ToolMessageContent,\n ): StoredContent;\n appendUserMessage(content: string | MessageContent): Promise<void>;\n appendSystemMessage(content: string): Promise<void>;\n appendModelContent(parts: Part[]): Promise<void>;\n appendToolResult(\n toolCallId: string,\n toolName: string,\n content: ToolMessageContent,\n ): Promise<void>;\n}\n\nfunction storedContentId(msg: StoredContent): string {\n return msg.id;\n}\n\n/** Convert zeitlich MessageContent to Google GenAI Part[] */\nexport function messageContentToParts(\n content: string | MessageContent,\n): Part[] {\n if (typeof content === \"string\") {\n return [{ text: content }];\n }\n if (Array.isArray(content)) {\n return content.map((part) => {\n if (part.type === \"text\") {\n return { text: part.text as string };\n }\n return part as unknown as Part;\n });\n }\n return [{ text: String(content) }];\n}\n\n/** Parse ToolMessageContent into a Record suitable for functionResponse */\nfunction parseToolResponse(\n content: ToolMessageContent,\n): Record<string, unknown> {\n if (typeof content === \"string\") {\n try {\n const parsed: unknown = JSON.parse(content);\n return typeof parsed === \"object\" && parsed !== null\n ? (parsed as Record<string, unknown>)\n : { result: content };\n } catch {\n return { result: content };\n }\n }\n return { result: content };\n}\n\n/**\n * Creates a Google GenAI-specific thread manager that stores StoredContent\n * instances in Redis and provides convenience helpers for creating and\n * appending typed Content messages.\n */\nexport function createGoogleGenAIThreadManager(\n config: GoogleGenAIThreadManagerConfig,\n): GoogleGenAIThreadManager {\n const baseConfig: ThreadManagerConfig<StoredContent> = {\n redis: config.redis,\n threadId: config.threadId,\n key: config.key,\n idOf: storedContentId,\n };\n\n const base = createThreadManager(baseConfig);\n\n const helpers = {\n createUserContent(content: string | MessageContent): StoredContent {\n return {\n id: crypto.randomUUID(),\n content: { role: \"user\", parts: messageContentToParts(content) },\n };\n },\n\n createSystemContent(content: string): StoredContent {\n return {\n id: crypto.randomUUID(),\n content: { role: \"system\", parts: [{ text: content }] },\n };\n },\n\n createModelContent(parts: Part[]): StoredContent {\n return {\n id: crypto.randomUUID(),\n content: { role: \"model\", parts },\n };\n },\n\n createToolResponseContent(\n toolCallId: string,\n toolName: string,\n content: ToolMessageContent,\n ): StoredContent {\n return {\n id: crypto.randomUUID(),\n content: {\n role: \"user\",\n parts: [\n {\n functionResponse: {\n id: toolCallId,\n name: toolName,\n response: parseToolResponse(content),\n },\n },\n ],\n },\n };\n },\n\n async appendUserMessage(content: string | MessageContent): Promise<void> {\n await base.append([helpers.createUserContent(content)]);\n },\n\n async appendSystemMessage(content: string): Promise<void> {\n await base.initialize();\n await base.append([helpers.createSystemContent(content)]);\n },\n\n async appendModelContent(parts: Part[]): Promise<void> {\n await base.append([helpers.createModelContent(parts)]);\n },\n\n async appendToolResult(\n toolCallId: string,\n toolName: string,\n content: ToolMessageContent,\n ): Promise<void> {\n await base.append([\n helpers.createToolResponseContent(toolCallId, toolName, content),\n ]);\n },\n };\n\n return Object.assign(base, helpers);\n}\n","import type Redis from \"ioredis\";\nimport type {\n GoogleGenAI,\n Content,\n FunctionDeclaration,\n} from \"@google/genai\";\nimport type { SerializableToolDefinition } from \"../../../lib/types\";\nimport type { AgentResponse } from \"../../../lib/model\";\nimport type { ModelInvokerConfig } from \"../../../lib/model\";\nimport { createGoogleGenAIThreadManager } from \"./thread-manager\";\n\nexport interface GoogleGenAIModelInvokerConfig {\n redis: Redis;\n client: GoogleGenAI;\n model: string;\n}\n\nfunction toFunctionDeclarations(\n tools: SerializableToolDefinition[],\n): FunctionDeclaration[] {\n return tools.map((t) => ({\n name: t.name,\n description: t.description,\n parametersJsonSchema: t.schema,\n }));\n}\n\n/**\n * Merge consecutive Content objects sharing the same role.\n * The Gemini API requires alternating user/model turns; without\n * merging, multiple sequential tool-result messages would violate this.\n */\nfunction mergeConsecutiveContents(contents: Content[]): Content[] {\n const merged: Content[] = [];\n for (const content of contents) {\n const last = merged[merged.length - 1];\n if (last && last.role === content.role) {\n last.parts = [...(last.parts ?? []), ...(content.parts ?? [])];\n } else {\n merged.push({ ...content, parts: [...(content.parts ?? [])] });\n }\n }\n return merged;\n}\n\n/**\n * Creates a Google GenAI model invoker that satisfies the generic\n * `ModelInvoker<Content>` contract.\n *\n * Loads the conversation thread from Redis, invokes the Gemini model via\n * `client.models.generateContent`, appends the AI response, and returns\n * a normalised AgentResponse.\n *\n * @example\n * ```typescript\n * import { createGoogleGenAIModelInvoker } from 'zeitlich/adapters/thread/google-genai';\n * import { createRunAgentActivity } from 'zeitlich';\n * import { GoogleGenAI } from '@google/genai';\n *\n * const client = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY });\n * const invoker = createGoogleGenAIModelInvoker({\n * redis,\n * client,\n * model: 'gemini-2.5-flash',\n * });\n *\n * return { runAgent: createRunAgentActivity(client, invoker) };\n * ```\n */\nexport function createGoogleGenAIModelInvoker({\n redis,\n client,\n model,\n}: GoogleGenAIModelInvokerConfig) {\n return async function invokeGoogleGenAIModel(\n config: ModelInvokerConfig,\n ): Promise<AgentResponse<Content>> {\n const { threadId, state } = config;\n\n const thread = createGoogleGenAIThreadManager({ redis, threadId });\n const stored = await thread.load();\n\n // Separate system instructions from conversation content.\n // Google GenAI takes system instructions via config, not in the contents array.\n let systemInstruction: string | undefined;\n const conversationContents: Content[] = [];\n\n for (const item of stored) {\n if (item.content.role === \"system\") {\n systemInstruction = item.content.parts?.[0]?.text;\n } else {\n conversationContents.push(item.content);\n }\n }\n\n const contents = mergeConsecutiveContents(conversationContents);\n\n const functionDeclarations = toFunctionDeclarations(state.tools);\n const tools =\n functionDeclarations.length > 0\n ? [{ functionDeclarations }]\n : undefined;\n\n const response = await client.models.generateContent({\n model,\n contents,\n config: {\n ...(systemInstruction ? { systemInstruction } : {}),\n ...(tools ? { tools } : {}),\n },\n });\n\n const responseParts = response.candidates?.[0]?.content?.parts ?? [];\n const modelContent: Content = { role: \"model\", parts: responseParts };\n\n await thread.appendModelContent(responseParts);\n\n const functionCalls = response.functionCalls ?? [];\n\n return {\n message: modelContent,\n rawToolCalls: functionCalls.map((fc) => ({\n id: fc.id,\n name: fc.name ?? \"\",\n args: fc.args ?? {},\n })),\n usage: {\n inputTokens: response.usageMetadata?.promptTokenCount,\n outputTokens: response.usageMetadata?.candidatesTokenCount,\n cachedReadTokens: response.usageMetadata?.cachedContentTokenCount,\n },\n };\n };\n}\n\n/**\n * Standalone function for one-shot Google GenAI model invocation.\n * Convenience wrapper around createGoogleGenAIModelInvoker for cases\n * where you don't need to reuse the invoker.\n */\nexport async function invokeGoogleGenAIModel({\n redis,\n client,\n model,\n config,\n}: {\n redis: Redis;\n client: GoogleGenAI;\n model: string;\n config: ModelInvokerConfig;\n}): Promise<AgentResponse<Content>> {\n const invoker = createGoogleGenAIModelInvoker({ redis, client, model });\n return invoker(config);\n}\n","import type Redis from \"ioredis\";\nimport type { GoogleGenAI, Content } from \"@google/genai\";\nimport type { ToolResultConfig } from \"../../../lib/types\";\nimport type { MessageContent } from \"../../../lib/types\";\nimport type { ThreadOps } from \"../../../lib/session/types\";\nimport type { ModelInvoker } from \"../../../lib/model\";\nimport { createGoogleGenAIThreadManager } from \"./thread-manager\";\nimport { createGoogleGenAIModelInvoker } from \"./model-invoker\";\n\nexport interface GoogleGenAIAdapterConfig {\n redis: Redis;\n client: GoogleGenAI;\n /** Default model name (e.g. 'gemini-2.5-flash'). If omitted, use `createModelInvoker()` */\n model?: string;\n}\n\nexport interface GoogleGenAIAdapter {\n /** Thread operations (register these as Temporal activities on the worker) */\n threadOps: ThreadOps;\n /** Model invoker using the default model (only available when `model` was provided) */\n invoker: ModelInvoker<Content>;\n /** Create an invoker for a specific model name (for multi-model setups) */\n createModelInvoker(model: string): ModelInvoker<Content>;\n}\n\n/**\n * Creates a Google GenAI adapter that bundles thread operations and model\n * invocation using the `@google/genai` SDK.\n *\n * The returned `threadOps` should be registered as Temporal activities on\n * the worker. The `invoker` (or invokers created via `createModelInvoker`)\n * should be wrapped with `createRunAgentActivity` for per-agent activities.\n *\n * @example\n * ```typescript\n * import { createGoogleGenAIAdapter } from 'zeitlich/adapters/thread/google-genai';\n * import { createRunAgentActivity } from 'zeitlich';\n * import { GoogleGenAI } from '@google/genai';\n *\n * const client = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY });\n * const adapter = createGoogleGenAIAdapter({ redis, client, model: 'gemini-2.5-flash' });\n *\n * export function createActivities(temporalClient: WorkflowClient) {\n * return {\n * ...adapter.threadOps,\n * runAgent: createRunAgentActivity(temporalClient, adapter.invoker),\n * };\n * }\n * ```\n *\n * @example Multi-model setup\n * ```typescript\n * const adapter = createGoogleGenAIAdapter({ redis, client });\n *\n * export function createActivities(temporalClient: WorkflowClient) {\n * return {\n * ...adapter.threadOps,\n * runResearchAgent: createRunAgentActivity(\n * temporalClient,\n * adapter.createModelInvoker('gemini-2.5-pro'),\n * ),\n * runFastAgent: createRunAgentActivity(\n * temporalClient,\n * adapter.createModelInvoker('gemini-2.5-flash'),\n * ),\n * };\n * }\n * ```\n */\nexport function createGoogleGenAIAdapter(\n config: GoogleGenAIAdapterConfig,\n): GoogleGenAIAdapter {\n const { redis, client } = config;\n\n const threadOps: ThreadOps = {\n async initializeThread(threadId: string): Promise<void> {\n const thread = createGoogleGenAIThreadManager({ redis, threadId });\n await thread.initialize();\n },\n\n async appendHumanMessage(\n threadId: string,\n content: string | MessageContent,\n ): Promise<void> {\n const thread = createGoogleGenAIThreadManager({ redis, threadId });\n await thread.appendUserMessage(content);\n },\n\n async appendSystemMessage(\n threadId: string,\n content: string,\n ): Promise<void> {\n const thread = createGoogleGenAIThreadManager({ redis, threadId });\n await thread.appendSystemMessage(content);\n },\n\n async appendToolResult(cfg: ToolResultConfig): Promise<void> {\n const { threadId, toolCallId, toolName, content } = cfg;\n const thread = createGoogleGenAIThreadManager({ redis, threadId });\n await thread.appendToolResult(toolCallId, toolName, content);\n },\n };\n\n const makeInvoker = (model: string): ModelInvoker<Content> =>\n createGoogleGenAIModelInvoker({ redis, client, model });\n\n const invoker: ModelInvoker<Content> = config.model\n ? makeInvoker(config.model)\n : ((() => {\n throw new Error(\n \"No default model provided to createGoogleGenAIAdapter. \" +\n \"Either pass `model` in the config or use `createModelInvoker(model)` instead.\",\n );\n }) as unknown as ModelInvoker<Content>);\n\n return {\n threadOps,\n invoker,\n createModelInvoker: makeInvoker,\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../../../src/lib/thread/manager.ts","../../../../src/adapters/thread/google-genai/thread-manager.ts","../../../../src/adapters/thread/google-genai/model-invoker.ts","../../../../src/adapters/thread/google-genai/activities.ts"],"names":["invokeGoogleGenAIModel"],"mappings":"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","file":"index.cjs","sourcesContent":["import type {\n ThreadManagerConfig,\n BaseThreadManager,\n} from \"./types\";\n\nconst THREAD_TTL_SECONDS = 60 * 60 * 24 * 90; // 90 days\n\n/**\n * Lua script for atomic idempotent append.\n * Checks a dedup key; if it exists the message was already appended and we\n * return 0. Otherwise appends all messages to the list, sets TTL on both\n * the list and the dedup key, and returns 1.\n *\n * KEYS[1] = dedup key, KEYS[2] = list key\n * ARGV[1] = TTL seconds, ARGV[2..N] = serialised messages\n */\nconst APPEND_IDEMPOTENT_SCRIPT = `\nif redis.call('EXISTS', KEYS[1]) == 1 then\n return 0\nend\nfor i = 2, #ARGV do\n redis.call('RPUSH', KEYS[2], ARGV[i])\nend\nredis.call('EXPIRE', KEYS[2], tonumber(ARGV[1]))\nredis.call('SET', KEYS[1], '1', 'EX', tonumber(ARGV[1]))\nreturn 1\n`;\n\nfunction getThreadKey(threadId: string, key: string): string {\n return `thread:${threadId}:${key}`;\n}\n\n/**\n * Creates a generic thread manager for handling conversation state in Redis.\n * Framework-agnostic — works with any serializable message type.\n */\nexport function createThreadManager<T>(\n config: ThreadManagerConfig<T>,\n): BaseThreadManager<T> {\n const {\n redis,\n threadId,\n key = \"messages\",\n serialize = (m: T): string => JSON.stringify(m),\n deserialize = (raw: string): T => JSON.parse(raw) as T,\n idOf,\n } = config;\n const redisKey = getThreadKey(threadId, key);\n const metaKey = getThreadKey(threadId, `${key}:meta`);\n\n async function assertThreadExists(): Promise<void> {\n const exists = await redis.exists(metaKey);\n if (!exists) {\n throw new Error(`Thread \"${threadId}\" (key: ${key}) does not exist`);\n }\n }\n\n return {\n async initialize(): Promise<void> {\n await redis.del(redisKey);\n await redis.set(metaKey, \"1\", \"EX\", THREAD_TTL_SECONDS);\n },\n\n async load(): Promise<T[]> {\n await assertThreadExists();\n const data = await redis.lrange(redisKey, 0, -1);\n return data.map(deserialize);\n },\n\n async append(messages: T[]): Promise<void> {\n if (messages.length === 0) return;\n await assertThreadExists();\n\n if (idOf) {\n const dedupId = messages.map(idOf).join(\":\");\n const dedupKey = getThreadKey(threadId, `dedup:${dedupId}`);\n await redis.eval(\n APPEND_IDEMPOTENT_SCRIPT,\n 2,\n dedupKey,\n redisKey,\n String(THREAD_TTL_SECONDS),\n ...messages.map(serialize),\n );\n } else {\n await redis.rpush(redisKey, ...messages.map(serialize));\n await redis.expire(redisKey, THREAD_TTL_SECONDS);\n }\n },\n\n async fork(newThreadId: string): Promise<BaseThreadManager<T>> {\n await assertThreadExists();\n const data = await redis.lrange(redisKey, 0, -1);\n const forked = createThreadManager({\n ...config,\n threadId: newThreadId,\n });\n await forked.initialize();\n if (data.length > 0) {\n const newKey = getThreadKey(newThreadId, key);\n await redis.rpush(newKey, ...data);\n await redis.expire(newKey, THREAD_TTL_SECONDS);\n }\n return forked;\n },\n\n async delete(): Promise<void> {\n await redis.del(redisKey, metaKey);\n },\n };\n}\n","import type Redis from \"ioredis\";\nimport type { Content, Part } from \"@google/genai\";\nimport {\n createThreadManager,\n type BaseThreadManager,\n type ThreadManagerConfig,\n} from \"../../../lib/thread\";\nimport type { MessageContent, ToolMessageContent } from \"../../../lib/types\";\n\n/** A Content with a unique ID for idempotent Redis storage */\nexport interface StoredContent {\n id: string;\n content: Content;\n}\n\nexport interface GoogleGenAIThreadManagerConfig {\n redis: Redis;\n threadId: string;\n /** Thread key, defaults to 'messages' */\n key?: string;\n}\n\n/** Thread manager with Google GenAI Content convenience helpers */\nexport interface GoogleGenAIThreadManager\n extends BaseThreadManager<StoredContent> {\n createUserContent(content: string | MessageContent): StoredContent;\n createSystemContent(content: string): StoredContent;\n createModelContent(parts: Part[]): StoredContent;\n createToolResponseContent(\n toolCallId: string,\n toolName: string,\n content: ToolMessageContent,\n ): StoredContent;\n appendUserMessage(content: string | MessageContent): Promise<void>;\n appendSystemMessage(content: string): Promise<void>;\n appendModelContent(parts: Part[]): Promise<void>;\n appendToolResult(\n toolCallId: string,\n toolName: string,\n content: ToolMessageContent,\n ): Promise<void>;\n}\n\nfunction storedContentId(msg: StoredContent): string {\n return msg.id;\n}\n\n/** Convert zeitlich MessageContent to Google GenAI Part[] */\nexport function messageContentToParts(\n content: string | MessageContent,\n): Part[] {\n if (typeof content === \"string\") {\n return [{ text: content }];\n }\n if (Array.isArray(content)) {\n return content.map((part) => {\n if (part.type === \"text\") {\n return { text: part.text as string };\n }\n return part as unknown as Part;\n });\n }\n return [{ text: String(content) }];\n}\n\n/** Parse ToolMessageContent into a Record suitable for functionResponse */\nfunction parseToolResponse(\n content: ToolMessageContent,\n): Record<string, unknown> {\n if (typeof content === \"string\") {\n try {\n const parsed: unknown = JSON.parse(content);\n return typeof parsed === \"object\" && parsed !== null\n ? (parsed as Record<string, unknown>)\n : { result: content };\n } catch {\n return { result: content };\n }\n }\n return { result: content };\n}\n\n/**\n * Creates a Google GenAI-specific thread manager that stores StoredContent\n * instances in Redis and provides convenience helpers for creating and\n * appending typed Content messages.\n */\nexport function createGoogleGenAIThreadManager(\n config: GoogleGenAIThreadManagerConfig,\n): GoogleGenAIThreadManager {\n const baseConfig: ThreadManagerConfig<StoredContent> = {\n redis: config.redis,\n threadId: config.threadId,\n key: config.key,\n idOf: storedContentId,\n };\n\n const base = createThreadManager(baseConfig);\n\n const helpers = {\n createUserContent(content: string | MessageContent): StoredContent {\n return {\n id: crypto.randomUUID(),\n content: { role: \"user\", parts: messageContentToParts(content) },\n };\n },\n\n createSystemContent(content: string): StoredContent {\n return {\n id: crypto.randomUUID(),\n content: { role: \"system\", parts: [{ text: content }] },\n };\n },\n\n createModelContent(parts: Part[]): StoredContent {\n return {\n id: crypto.randomUUID(),\n content: { role: \"model\", parts },\n };\n },\n\n createToolResponseContent(\n toolCallId: string,\n toolName: string,\n content: ToolMessageContent,\n ): StoredContent {\n return {\n id: crypto.randomUUID(),\n content: {\n role: \"user\",\n parts: [\n {\n functionResponse: {\n id: toolCallId,\n name: toolName,\n response: parseToolResponse(content),\n },\n },\n ],\n },\n };\n },\n\n async appendUserMessage(content: string | MessageContent): Promise<void> {\n await base.append([helpers.createUserContent(content)]);\n },\n\n async appendSystemMessage(content: string): Promise<void> {\n await base.initialize();\n await base.append([helpers.createSystemContent(content)]);\n },\n\n async appendModelContent(parts: Part[]): Promise<void> {\n await base.append([helpers.createModelContent(parts)]);\n },\n\n async appendToolResult(\n toolCallId: string,\n toolName: string,\n content: ToolMessageContent,\n ): Promise<void> {\n await base.append([\n helpers.createToolResponseContent(toolCallId, toolName, content),\n ]);\n },\n };\n\n return Object.assign(base, helpers);\n}\n","import type Redis from \"ioredis\";\nimport type {\n GoogleGenAI,\n Content,\n FunctionDeclaration,\n} from \"@google/genai\";\nimport type { SerializableToolDefinition } from \"../../../lib/types\";\nimport type { AgentResponse } from \"../../../lib/model\";\nimport type { ModelInvokerConfig } from \"../../../lib/model\";\nimport { createGoogleGenAIThreadManager } from \"./thread-manager\";\n\nexport interface GoogleGenAIModelInvokerConfig {\n redis: Redis;\n client: GoogleGenAI;\n model: string;\n}\n\nfunction toFunctionDeclarations(\n tools: SerializableToolDefinition[],\n): FunctionDeclaration[] {\n return tools.map((t) => ({\n name: t.name,\n description: t.description,\n parametersJsonSchema: t.schema,\n }));\n}\n\n/**\n * Merge consecutive Content objects sharing the same role.\n * The Gemini API requires alternating user/model turns; without\n * merging, multiple sequential tool-result messages would violate this.\n */\nfunction mergeConsecutiveContents(contents: Content[]): Content[] {\n const merged: Content[] = [];\n for (const content of contents) {\n const last = merged[merged.length - 1];\n if (last && last.role === content.role) {\n last.parts = [...(last.parts ?? []), ...(content.parts ?? [])];\n } else {\n merged.push({ ...content, parts: [...(content.parts ?? [])] });\n }\n }\n return merged;\n}\n\n/**\n * Creates a Google GenAI model invoker that satisfies the generic\n * `ModelInvoker<Content>` contract.\n *\n * Loads the conversation thread from Redis, invokes the Gemini model via\n * `client.models.generateContent`, appends the AI response, and returns\n * a normalised AgentResponse.\n *\n * @example\n * ```typescript\n * import { createGoogleGenAIModelInvoker } from 'zeitlich/adapters/thread/google-genai';\n * import { createRunAgentActivity } from 'zeitlich';\n * import { GoogleGenAI } from '@google/genai';\n *\n * const client = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY });\n * const invoker = createGoogleGenAIModelInvoker({\n * redis,\n * client,\n * model: 'gemini-2.5-flash',\n * });\n *\n * return { runAgent: createRunAgentActivity(client, invoker) };\n * ```\n */\nexport function createGoogleGenAIModelInvoker({\n redis,\n client,\n model,\n}: GoogleGenAIModelInvokerConfig) {\n return async function invokeGoogleGenAIModel(\n config: ModelInvokerConfig,\n ): Promise<AgentResponse<Content>> {\n const { threadId, state } = config;\n\n const thread = createGoogleGenAIThreadManager({ redis, threadId });\n const stored = await thread.load();\n\n // Separate system instructions from conversation content.\n // Google GenAI takes system instructions via config, not in the contents array.\n let systemInstruction: string | undefined;\n const conversationContents: Content[] = [];\n\n for (const item of stored) {\n if (item.content.role === \"system\") {\n systemInstruction = item.content.parts?.[0]?.text;\n } else {\n conversationContents.push(item.content);\n }\n }\n\n const contents = mergeConsecutiveContents(conversationContents);\n\n const functionDeclarations = toFunctionDeclarations(state.tools);\n const tools =\n functionDeclarations.length > 0\n ? [{ functionDeclarations }]\n : undefined;\n\n const response = await client.models.generateContent({\n model,\n contents,\n config: {\n ...(systemInstruction ? { systemInstruction } : {}),\n ...(tools ? { tools } : {}),\n },\n });\n\n const responseParts = response.candidates?.[0]?.content?.parts ?? [];\n const modelContent: Content = { role: \"model\", parts: responseParts };\n\n await thread.appendModelContent(responseParts);\n\n const functionCalls = response.functionCalls ?? [];\n\n return {\n message: modelContent,\n rawToolCalls: functionCalls.map((fc) => ({\n id: fc.id,\n name: fc.name ?? \"\",\n args: fc.args ?? {},\n })),\n usage: {\n inputTokens: response.usageMetadata?.promptTokenCount,\n outputTokens: response.usageMetadata?.candidatesTokenCount,\n cachedReadTokens: response.usageMetadata?.cachedContentTokenCount,\n },\n };\n };\n}\n\n/**\n * Standalone function for one-shot Google GenAI model invocation.\n * Convenience wrapper around createGoogleGenAIModelInvoker for cases\n * where you don't need to reuse the invoker.\n */\nexport async function invokeGoogleGenAIModel({\n redis,\n client,\n model,\n config,\n}: {\n redis: Redis;\n client: GoogleGenAI;\n model: string;\n config: ModelInvokerConfig;\n}): Promise<AgentResponse<Content>> {\n const invoker = createGoogleGenAIModelInvoker({ redis, client, model });\n return invoker(config);\n}\n","import type Redis from \"ioredis\";\nimport type { GoogleGenAI, Content } from \"@google/genai\";\nimport type { ToolResultConfig } from \"../../../lib/types\";\nimport type { MessageContent } from \"../../../lib/types\";\nimport type { ThreadOps } from \"../../../lib/session/types\";\nimport type { ModelInvoker } from \"../../../lib/model\";\nimport { createGoogleGenAIThreadManager } from \"./thread-manager\";\nimport { createGoogleGenAIModelInvoker } from \"./model-invoker\";\n\nexport interface GoogleGenAIAdapterConfig {\n redis: Redis;\n client: GoogleGenAI;\n /** Default model name (e.g. 'gemini-2.5-flash'). If omitted, use `createModelInvoker()` */\n model?: string;\n}\n\nexport interface GoogleGenAIAdapter {\n /** Thread operations (register these as Temporal activities on the worker) */\n threadOps: ThreadOps;\n /** Model invoker using the default model (only available when `model` was provided) */\n invoker: ModelInvoker<Content>;\n /** Create an invoker for a specific model name (for multi-model setups) */\n createModelInvoker(model: string): ModelInvoker<Content>;\n}\n\n/**\n * Creates a Google GenAI adapter that bundles thread operations and model\n * invocation using the `@google/genai` SDK.\n *\n * The returned `threadOps` should be registered as Temporal activities on\n * the worker. The `invoker` (or invokers created via `createModelInvoker`)\n * should be wrapped with `createRunAgentActivity` for per-agent activities.\n *\n * @example\n * ```typescript\n * import { createGoogleGenAIAdapter } from 'zeitlich/adapters/thread/google-genai';\n * import { createRunAgentActivity } from 'zeitlich';\n * import { GoogleGenAI } from '@google/genai';\n *\n * const client = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY });\n * const adapter = createGoogleGenAIAdapter({ redis, client, model: 'gemini-2.5-flash' });\n *\n * export function createActivities(temporalClient: WorkflowClient) {\n * return {\n * ...adapter.threadOps,\n * runAgent: createRunAgentActivity(temporalClient, adapter.invoker),\n * };\n * }\n * ```\n *\n * @example Multi-model setup\n * ```typescript\n * const adapter = createGoogleGenAIAdapter({ redis, client });\n *\n * export function createActivities(temporalClient: WorkflowClient) {\n * return {\n * ...adapter.threadOps,\n * runResearchAgent: createRunAgentActivity(\n * temporalClient,\n * adapter.createModelInvoker('gemini-2.5-pro'),\n * ),\n * runFastAgent: createRunAgentActivity(\n * temporalClient,\n * adapter.createModelInvoker('gemini-2.5-flash'),\n * ),\n * };\n * }\n * ```\n */\nexport function createGoogleGenAIAdapter(\n config: GoogleGenAIAdapterConfig,\n): GoogleGenAIAdapter {\n const { redis, client } = config;\n\n const threadOps: ThreadOps = {\n async initializeThread(threadId: string): Promise<void> {\n const thread = createGoogleGenAIThreadManager({ redis, threadId });\n await thread.initialize();\n },\n\n async appendHumanMessage(\n threadId: string,\n content: string | MessageContent,\n ): Promise<void> {\n const thread = createGoogleGenAIThreadManager({ redis, threadId });\n await thread.appendUserMessage(content);\n },\n\n async appendSystemMessage(\n threadId: string,\n content: string,\n ): Promise<void> {\n const thread = createGoogleGenAIThreadManager({ redis, threadId });\n await thread.appendSystemMessage(content);\n },\n\n async appendToolResult(cfg: ToolResultConfig): Promise<void> {\n const { threadId, toolCallId, toolName, content } = cfg;\n const thread = createGoogleGenAIThreadManager({ redis, threadId });\n await thread.appendToolResult(toolCallId, toolName, content);\n },\n\n async forkThread(\n sourceThreadId: string,\n targetThreadId: string,\n ): Promise<void> {\n const thread = createGoogleGenAIThreadManager({\n redis,\n threadId: sourceThreadId,\n });\n await thread.fork(targetThreadId);\n },\n };\n\n const makeInvoker = (model: string): ModelInvoker<Content> =>\n createGoogleGenAIModelInvoker({ redis, client, model });\n\n const invoker: ModelInvoker<Content> = config.model\n ? makeInvoker(config.model)\n : ((() => {\n throw new Error(\n \"No default model provided to createGoogleGenAIAdapter. \" +\n \"Either pass `model` in the config or use `createModelInvoker(model)` instead.\",\n );\n }) as unknown as ModelInvoker<Content>);\n\n return {\n threadOps,\n invoker,\n createModelInvoker: makeInvoker,\n };\n}\n"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import Redis from 'ioredis';
|
|
2
2
|
import { Content, GoogleGenAI, Part } from '@google/genai';
|
|
3
|
-
import { T as ThreadOps, M as ModelInvoker, a as ModelInvokerConfig, A as AgentResponse } from '../../../types-
|
|
4
|
-
import { B as BaseThreadManager } from '../../../types-
|
|
3
|
+
import { T as ThreadOps, M as ModelInvoker, a as ModelInvokerConfig, A as AgentResponse } from '../../../types-Dje1TdH6.cjs';
|
|
4
|
+
import { B as BaseThreadManager } from '../../../types-35POpVfa.cjs';
|
|
5
5
|
import { M as MessageContent, T as ToolMessageContent } from '../../../types-BMXzv7TN.cjs';
|
|
6
6
|
import '@temporalio/common';
|
|
7
7
|
import 'zod';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import Redis from 'ioredis';
|
|
2
2
|
import { Content, GoogleGenAI, Part } from '@google/genai';
|
|
3
|
-
import { T as ThreadOps, M as ModelInvoker, a as ModelInvokerConfig, A as AgentResponse } from '../../../types-
|
|
4
|
-
import { B as BaseThreadManager } from '../../../types-
|
|
3
|
+
import { T as ThreadOps, M as ModelInvoker, a as ModelInvokerConfig, A as AgentResponse } from '../../../types-BWvIYK28.js';
|
|
4
|
+
import { B as BaseThreadManager } from '../../../types-35POpVfa.js';
|
|
5
5
|
import { M as MessageContent, T as ToolMessageContent } from '../../../types-BMXzv7TN.js';
|
|
6
6
|
import '@temporalio/common';
|
|
7
7
|
import 'zod';
|
|
@@ -62,6 +62,21 @@ function createThreadManager(config) {
|
|
|
62
62
|
await redis.expire(redisKey, THREAD_TTL_SECONDS);
|
|
63
63
|
}
|
|
64
64
|
},
|
|
65
|
+
async fork(newThreadId) {
|
|
66
|
+
await assertThreadExists();
|
|
67
|
+
const data = await redis.lrange(redisKey, 0, -1);
|
|
68
|
+
const forked = createThreadManager({
|
|
69
|
+
...config,
|
|
70
|
+
threadId: newThreadId
|
|
71
|
+
});
|
|
72
|
+
await forked.initialize();
|
|
73
|
+
if (data.length > 0) {
|
|
74
|
+
const newKey = getThreadKey(newThreadId, key);
|
|
75
|
+
await redis.rpush(newKey, ...data);
|
|
76
|
+
await redis.expire(newKey, THREAD_TTL_SECONDS);
|
|
77
|
+
}
|
|
78
|
+
return forked;
|
|
79
|
+
},
|
|
65
80
|
async delete() {
|
|
66
81
|
await redis.del(redisKey, metaKey);
|
|
67
82
|
}
|
|
@@ -258,6 +273,13 @@ function createGoogleGenAIAdapter(config) {
|
|
|
258
273
|
const { threadId, toolCallId, toolName, content } = cfg;
|
|
259
274
|
const thread = createGoogleGenAIThreadManager({ redis, threadId });
|
|
260
275
|
await thread.appendToolResult(toolCallId, toolName, content);
|
|
276
|
+
},
|
|
277
|
+
async forkThread(sourceThreadId, targetThreadId) {
|
|
278
|
+
const thread = createGoogleGenAIThreadManager({
|
|
279
|
+
redis,
|
|
280
|
+
threadId: sourceThreadId
|
|
281
|
+
});
|
|
282
|
+
await thread.fork(targetThreadId);
|
|
261
283
|
}
|
|
262
284
|
};
|
|
263
285
|
const makeInvoker = (model) => createGoogleGenAIModelInvoker({ redis, client, model });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/lib/thread/manager.ts","../../../../src/adapters/thread/google-genai/thread-manager.ts","../../../../src/adapters/thread/google-genai/model-invoker.ts","../../../../src/adapters/thread/google-genai/activities.ts"],"names":["invokeGoogleGenAIModel"],"mappings":"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","file":"index.js","sourcesContent":["import type { ThreadManagerConfig, BaseThreadManager } from \"./types\";\n\nconst THREAD_TTL_SECONDS = 60 * 60 * 24 * 90; // 90 days\n\n/**\n * Lua script for atomic idempotent append.\n * Checks a dedup key; if it exists the message was already appended and we\n * return 0. Otherwise appends all messages to the list, sets TTL on both\n * the list and the dedup key, and returns 1.\n *\n * KEYS[1] = dedup key, KEYS[2] = list key\n * ARGV[1] = TTL seconds, ARGV[2..N] = serialised messages\n */\nconst APPEND_IDEMPOTENT_SCRIPT = `\nif redis.call('EXISTS', KEYS[1]) == 1 then\n return 0\nend\nfor i = 2, #ARGV do\n redis.call('RPUSH', KEYS[2], ARGV[i])\nend\nredis.call('EXPIRE', KEYS[2], tonumber(ARGV[1]))\nredis.call('SET', KEYS[1], '1', 'EX', tonumber(ARGV[1]))\nreturn 1\n`;\n\nfunction getThreadKey(threadId: string, key: string): string {\n return `thread:${threadId}:${key}`;\n}\n\n/**\n * Creates a generic thread manager for handling conversation state in Redis.\n * Framework-agnostic — works with any serializable message type.\n */\nexport function createThreadManager<T>(\n config: ThreadManagerConfig<T>,\n): BaseThreadManager<T> {\n const {\n redis,\n threadId,\n key = \"messages\",\n serialize = (m: T): string => JSON.stringify(m),\n deserialize = (raw: string): T => JSON.parse(raw) as T,\n idOf,\n } = config;\n const redisKey = getThreadKey(threadId, key);\n const metaKey = getThreadKey(threadId, `${key}:meta`);\n\n async function assertThreadExists(): Promise<void> {\n const exists = await redis.exists(metaKey);\n if (!exists) {\n throw new Error(`Thread \"${threadId}\" (key: ${key}) does not exist`);\n }\n }\n\n return {\n async initialize(): Promise<void> {\n await redis.del(redisKey);\n await redis.set(metaKey, \"1\", \"EX\", THREAD_TTL_SECONDS);\n },\n\n async load(): Promise<T[]> {\n await assertThreadExists();\n const data = await redis.lrange(redisKey, 0, -1);\n return data.map(deserialize);\n },\n\n async append(messages: T[]): Promise<void> {\n if (messages.length === 0) return;\n await assertThreadExists();\n\n if (idOf) {\n const dedupId = messages.map(idOf).join(\":\");\n const dedupKey = getThreadKey(threadId, `dedup:${dedupId}`);\n await redis.eval(\n APPEND_IDEMPOTENT_SCRIPT,\n 2,\n dedupKey,\n redisKey,\n String(THREAD_TTL_SECONDS),\n ...messages.map(serialize),\n );\n } else {\n await redis.rpush(redisKey, ...messages.map(serialize));\n await redis.expire(redisKey, THREAD_TTL_SECONDS);\n }\n },\n\n async delete(): Promise<void> {\n await redis.del(redisKey, metaKey);\n },\n };\n}\n","import type Redis from \"ioredis\";\nimport type { Content, Part } from \"@google/genai\";\nimport {\n createThreadManager,\n type BaseThreadManager,\n type ThreadManagerConfig,\n} from \"../../../lib/thread\";\nimport type { MessageContent, ToolMessageContent } from \"../../../lib/types\";\n\n/** A Content with a unique ID for idempotent Redis storage */\nexport interface StoredContent {\n id: string;\n content: Content;\n}\n\nexport interface GoogleGenAIThreadManagerConfig {\n redis: Redis;\n threadId: string;\n /** Thread key, defaults to 'messages' */\n key?: string;\n}\n\n/** Thread manager with Google GenAI Content convenience helpers */\nexport interface GoogleGenAIThreadManager\n extends BaseThreadManager<StoredContent> {\n createUserContent(content: string | MessageContent): StoredContent;\n createSystemContent(content: string): StoredContent;\n createModelContent(parts: Part[]): StoredContent;\n createToolResponseContent(\n toolCallId: string,\n toolName: string,\n content: ToolMessageContent,\n ): StoredContent;\n appendUserMessage(content: string | MessageContent): Promise<void>;\n appendSystemMessage(content: string): Promise<void>;\n appendModelContent(parts: Part[]): Promise<void>;\n appendToolResult(\n toolCallId: string,\n toolName: string,\n content: ToolMessageContent,\n ): Promise<void>;\n}\n\nfunction storedContentId(msg: StoredContent): string {\n return msg.id;\n}\n\n/** Convert zeitlich MessageContent to Google GenAI Part[] */\nexport function messageContentToParts(\n content: string | MessageContent,\n): Part[] {\n if (typeof content === \"string\") {\n return [{ text: content }];\n }\n if (Array.isArray(content)) {\n return content.map((part) => {\n if (part.type === \"text\") {\n return { text: part.text as string };\n }\n return part as unknown as Part;\n });\n }\n return [{ text: String(content) }];\n}\n\n/** Parse ToolMessageContent into a Record suitable for functionResponse */\nfunction parseToolResponse(\n content: ToolMessageContent,\n): Record<string, unknown> {\n if (typeof content === \"string\") {\n try {\n const parsed: unknown = JSON.parse(content);\n return typeof parsed === \"object\" && parsed !== null\n ? (parsed as Record<string, unknown>)\n : { result: content };\n } catch {\n return { result: content };\n }\n }\n return { result: content };\n}\n\n/**\n * Creates a Google GenAI-specific thread manager that stores StoredContent\n * instances in Redis and provides convenience helpers for creating and\n * appending typed Content messages.\n */\nexport function createGoogleGenAIThreadManager(\n config: GoogleGenAIThreadManagerConfig,\n): GoogleGenAIThreadManager {\n const baseConfig: ThreadManagerConfig<StoredContent> = {\n redis: config.redis,\n threadId: config.threadId,\n key: config.key,\n idOf: storedContentId,\n };\n\n const base = createThreadManager(baseConfig);\n\n const helpers = {\n createUserContent(content: string | MessageContent): StoredContent {\n return {\n id: crypto.randomUUID(),\n content: { role: \"user\", parts: messageContentToParts(content) },\n };\n },\n\n createSystemContent(content: string): StoredContent {\n return {\n id: crypto.randomUUID(),\n content: { role: \"system\", parts: [{ text: content }] },\n };\n },\n\n createModelContent(parts: Part[]): StoredContent {\n return {\n id: crypto.randomUUID(),\n content: { role: \"model\", parts },\n };\n },\n\n createToolResponseContent(\n toolCallId: string,\n toolName: string,\n content: ToolMessageContent,\n ): StoredContent {\n return {\n id: crypto.randomUUID(),\n content: {\n role: \"user\",\n parts: [\n {\n functionResponse: {\n id: toolCallId,\n name: toolName,\n response: parseToolResponse(content),\n },\n },\n ],\n },\n };\n },\n\n async appendUserMessage(content: string | MessageContent): Promise<void> {\n await base.append([helpers.createUserContent(content)]);\n },\n\n async appendSystemMessage(content: string): Promise<void> {\n await base.initialize();\n await base.append([helpers.createSystemContent(content)]);\n },\n\n async appendModelContent(parts: Part[]): Promise<void> {\n await base.append([helpers.createModelContent(parts)]);\n },\n\n async appendToolResult(\n toolCallId: string,\n toolName: string,\n content: ToolMessageContent,\n ): Promise<void> {\n await base.append([\n helpers.createToolResponseContent(toolCallId, toolName, content),\n ]);\n },\n };\n\n return Object.assign(base, helpers);\n}\n","import type Redis from \"ioredis\";\nimport type {\n GoogleGenAI,\n Content,\n FunctionDeclaration,\n} from \"@google/genai\";\nimport type { SerializableToolDefinition } from \"../../../lib/types\";\nimport type { AgentResponse } from \"../../../lib/model\";\nimport type { ModelInvokerConfig } from \"../../../lib/model\";\nimport { createGoogleGenAIThreadManager } from \"./thread-manager\";\n\nexport interface GoogleGenAIModelInvokerConfig {\n redis: Redis;\n client: GoogleGenAI;\n model: string;\n}\n\nfunction toFunctionDeclarations(\n tools: SerializableToolDefinition[],\n): FunctionDeclaration[] {\n return tools.map((t) => ({\n name: t.name,\n description: t.description,\n parametersJsonSchema: t.schema,\n }));\n}\n\n/**\n * Merge consecutive Content objects sharing the same role.\n * The Gemini API requires alternating user/model turns; without\n * merging, multiple sequential tool-result messages would violate this.\n */\nfunction mergeConsecutiveContents(contents: Content[]): Content[] {\n const merged: Content[] = [];\n for (const content of contents) {\n const last = merged[merged.length - 1];\n if (last && last.role === content.role) {\n last.parts = [...(last.parts ?? []), ...(content.parts ?? [])];\n } else {\n merged.push({ ...content, parts: [...(content.parts ?? [])] });\n }\n }\n return merged;\n}\n\n/**\n * Creates a Google GenAI model invoker that satisfies the generic\n * `ModelInvoker<Content>` contract.\n *\n * Loads the conversation thread from Redis, invokes the Gemini model via\n * `client.models.generateContent`, appends the AI response, and returns\n * a normalised AgentResponse.\n *\n * @example\n * ```typescript\n * import { createGoogleGenAIModelInvoker } from 'zeitlich/adapters/thread/google-genai';\n * import { createRunAgentActivity } from 'zeitlich';\n * import { GoogleGenAI } from '@google/genai';\n *\n * const client = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY });\n * const invoker = createGoogleGenAIModelInvoker({\n * redis,\n * client,\n * model: 'gemini-2.5-flash',\n * });\n *\n * return { runAgent: createRunAgentActivity(client, invoker) };\n * ```\n */\nexport function createGoogleGenAIModelInvoker({\n redis,\n client,\n model,\n}: GoogleGenAIModelInvokerConfig) {\n return async function invokeGoogleGenAIModel(\n config: ModelInvokerConfig,\n ): Promise<AgentResponse<Content>> {\n const { threadId, state } = config;\n\n const thread = createGoogleGenAIThreadManager({ redis, threadId });\n const stored = await thread.load();\n\n // Separate system instructions from conversation content.\n // Google GenAI takes system instructions via config, not in the contents array.\n let systemInstruction: string | undefined;\n const conversationContents: Content[] = [];\n\n for (const item of stored) {\n if (item.content.role === \"system\") {\n systemInstruction = item.content.parts?.[0]?.text;\n } else {\n conversationContents.push(item.content);\n }\n }\n\n const contents = mergeConsecutiveContents(conversationContents);\n\n const functionDeclarations = toFunctionDeclarations(state.tools);\n const tools =\n functionDeclarations.length > 0\n ? [{ functionDeclarations }]\n : undefined;\n\n const response = await client.models.generateContent({\n model,\n contents,\n config: {\n ...(systemInstruction ? { systemInstruction } : {}),\n ...(tools ? { tools } : {}),\n },\n });\n\n const responseParts = response.candidates?.[0]?.content?.parts ?? [];\n const modelContent: Content = { role: \"model\", parts: responseParts };\n\n await thread.appendModelContent(responseParts);\n\n const functionCalls = response.functionCalls ?? [];\n\n return {\n message: modelContent,\n rawToolCalls: functionCalls.map((fc) => ({\n id: fc.id,\n name: fc.name ?? \"\",\n args: fc.args ?? {},\n })),\n usage: {\n inputTokens: response.usageMetadata?.promptTokenCount,\n outputTokens: response.usageMetadata?.candidatesTokenCount,\n cachedReadTokens: response.usageMetadata?.cachedContentTokenCount,\n },\n };\n };\n}\n\n/**\n * Standalone function for one-shot Google GenAI model invocation.\n * Convenience wrapper around createGoogleGenAIModelInvoker for cases\n * where you don't need to reuse the invoker.\n */\nexport async function invokeGoogleGenAIModel({\n redis,\n client,\n model,\n config,\n}: {\n redis: Redis;\n client: GoogleGenAI;\n model: string;\n config: ModelInvokerConfig;\n}): Promise<AgentResponse<Content>> {\n const invoker = createGoogleGenAIModelInvoker({ redis, client, model });\n return invoker(config);\n}\n","import type Redis from \"ioredis\";\nimport type { GoogleGenAI, Content } from \"@google/genai\";\nimport type { ToolResultConfig } from \"../../../lib/types\";\nimport type { MessageContent } from \"../../../lib/types\";\nimport type { ThreadOps } from \"../../../lib/session/types\";\nimport type { ModelInvoker } from \"../../../lib/model\";\nimport { createGoogleGenAIThreadManager } from \"./thread-manager\";\nimport { createGoogleGenAIModelInvoker } from \"./model-invoker\";\n\nexport interface GoogleGenAIAdapterConfig {\n redis: Redis;\n client: GoogleGenAI;\n /** Default model name (e.g. 'gemini-2.5-flash'). If omitted, use `createModelInvoker()` */\n model?: string;\n}\n\nexport interface GoogleGenAIAdapter {\n /** Thread operations (register these as Temporal activities on the worker) */\n threadOps: ThreadOps;\n /** Model invoker using the default model (only available when `model` was provided) */\n invoker: ModelInvoker<Content>;\n /** Create an invoker for a specific model name (for multi-model setups) */\n createModelInvoker(model: string): ModelInvoker<Content>;\n}\n\n/**\n * Creates a Google GenAI adapter that bundles thread operations and model\n * invocation using the `@google/genai` SDK.\n *\n * The returned `threadOps` should be registered as Temporal activities on\n * the worker. The `invoker` (or invokers created via `createModelInvoker`)\n * should be wrapped with `createRunAgentActivity` for per-agent activities.\n *\n * @example\n * ```typescript\n * import { createGoogleGenAIAdapter } from 'zeitlich/adapters/thread/google-genai';\n * import { createRunAgentActivity } from 'zeitlich';\n * import { GoogleGenAI } from '@google/genai';\n *\n * const client = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY });\n * const adapter = createGoogleGenAIAdapter({ redis, client, model: 'gemini-2.5-flash' });\n *\n * export function createActivities(temporalClient: WorkflowClient) {\n * return {\n * ...adapter.threadOps,\n * runAgent: createRunAgentActivity(temporalClient, adapter.invoker),\n * };\n * }\n * ```\n *\n * @example Multi-model setup\n * ```typescript\n * const adapter = createGoogleGenAIAdapter({ redis, client });\n *\n * export function createActivities(temporalClient: WorkflowClient) {\n * return {\n * ...adapter.threadOps,\n * runResearchAgent: createRunAgentActivity(\n * temporalClient,\n * adapter.createModelInvoker('gemini-2.5-pro'),\n * ),\n * runFastAgent: createRunAgentActivity(\n * temporalClient,\n * adapter.createModelInvoker('gemini-2.5-flash'),\n * ),\n * };\n * }\n * ```\n */\nexport function createGoogleGenAIAdapter(\n config: GoogleGenAIAdapterConfig,\n): GoogleGenAIAdapter {\n const { redis, client } = config;\n\n const threadOps: ThreadOps = {\n async initializeThread(threadId: string): Promise<void> {\n const thread = createGoogleGenAIThreadManager({ redis, threadId });\n await thread.initialize();\n },\n\n async appendHumanMessage(\n threadId: string,\n content: string | MessageContent,\n ): Promise<void> {\n const thread = createGoogleGenAIThreadManager({ redis, threadId });\n await thread.appendUserMessage(content);\n },\n\n async appendSystemMessage(\n threadId: string,\n content: string,\n ): Promise<void> {\n const thread = createGoogleGenAIThreadManager({ redis, threadId });\n await thread.appendSystemMessage(content);\n },\n\n async appendToolResult(cfg: ToolResultConfig): Promise<void> {\n const { threadId, toolCallId, toolName, content } = cfg;\n const thread = createGoogleGenAIThreadManager({ redis, threadId });\n await thread.appendToolResult(toolCallId, toolName, content);\n },\n };\n\n const makeInvoker = (model: string): ModelInvoker<Content> =>\n createGoogleGenAIModelInvoker({ redis, client, model });\n\n const invoker: ModelInvoker<Content> = config.model\n ? makeInvoker(config.model)\n : ((() => {\n throw new Error(\n \"No default model provided to createGoogleGenAIAdapter. \" +\n \"Either pass `model` in the config or use `createModelInvoker(model)` instead.\",\n );\n }) as unknown as ModelInvoker<Content>);\n\n return {\n threadOps,\n invoker,\n createModelInvoker: makeInvoker,\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../../../src/lib/thread/manager.ts","../../../../src/adapters/thread/google-genai/thread-manager.ts","../../../../src/adapters/thread/google-genai/model-invoker.ts","../../../../src/adapters/thread/google-genai/activities.ts"],"names":["invokeGoogleGenAIModel"],"mappings":"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","file":"index.js","sourcesContent":["import type {\n ThreadManagerConfig,\n BaseThreadManager,\n} from \"./types\";\n\nconst THREAD_TTL_SECONDS = 60 * 60 * 24 * 90; // 90 days\n\n/**\n * Lua script for atomic idempotent append.\n * Checks a dedup key; if it exists the message was already appended and we\n * return 0. Otherwise appends all messages to the list, sets TTL on both\n * the list and the dedup key, and returns 1.\n *\n * KEYS[1] = dedup key, KEYS[2] = list key\n * ARGV[1] = TTL seconds, ARGV[2..N] = serialised messages\n */\nconst APPEND_IDEMPOTENT_SCRIPT = `\nif redis.call('EXISTS', KEYS[1]) == 1 then\n return 0\nend\nfor i = 2, #ARGV do\n redis.call('RPUSH', KEYS[2], ARGV[i])\nend\nredis.call('EXPIRE', KEYS[2], tonumber(ARGV[1]))\nredis.call('SET', KEYS[1], '1', 'EX', tonumber(ARGV[1]))\nreturn 1\n`;\n\nfunction getThreadKey(threadId: string, key: string): string {\n return `thread:${threadId}:${key}`;\n}\n\n/**\n * Creates a generic thread manager for handling conversation state in Redis.\n * Framework-agnostic — works with any serializable message type.\n */\nexport function createThreadManager<T>(\n config: ThreadManagerConfig<T>,\n): BaseThreadManager<T> {\n const {\n redis,\n threadId,\n key = \"messages\",\n serialize = (m: T): string => JSON.stringify(m),\n deserialize = (raw: string): T => JSON.parse(raw) as T,\n idOf,\n } = config;\n const redisKey = getThreadKey(threadId, key);\n const metaKey = getThreadKey(threadId, `${key}:meta`);\n\n async function assertThreadExists(): Promise<void> {\n const exists = await redis.exists(metaKey);\n if (!exists) {\n throw new Error(`Thread \"${threadId}\" (key: ${key}) does not exist`);\n }\n }\n\n return {\n async initialize(): Promise<void> {\n await redis.del(redisKey);\n await redis.set(metaKey, \"1\", \"EX\", THREAD_TTL_SECONDS);\n },\n\n async load(): Promise<T[]> {\n await assertThreadExists();\n const data = await redis.lrange(redisKey, 0, -1);\n return data.map(deserialize);\n },\n\n async append(messages: T[]): Promise<void> {\n if (messages.length === 0) return;\n await assertThreadExists();\n\n if (idOf) {\n const dedupId = messages.map(idOf).join(\":\");\n const dedupKey = getThreadKey(threadId, `dedup:${dedupId}`);\n await redis.eval(\n APPEND_IDEMPOTENT_SCRIPT,\n 2,\n dedupKey,\n redisKey,\n String(THREAD_TTL_SECONDS),\n ...messages.map(serialize),\n );\n } else {\n await redis.rpush(redisKey, ...messages.map(serialize));\n await redis.expire(redisKey, THREAD_TTL_SECONDS);\n }\n },\n\n async fork(newThreadId: string): Promise<BaseThreadManager<T>> {\n await assertThreadExists();\n const data = await redis.lrange(redisKey, 0, -1);\n const forked = createThreadManager({\n ...config,\n threadId: newThreadId,\n });\n await forked.initialize();\n if (data.length > 0) {\n const newKey = getThreadKey(newThreadId, key);\n await redis.rpush(newKey, ...data);\n await redis.expire(newKey, THREAD_TTL_SECONDS);\n }\n return forked;\n },\n\n async delete(): Promise<void> {\n await redis.del(redisKey, metaKey);\n },\n };\n}\n","import type Redis from \"ioredis\";\nimport type { Content, Part } from \"@google/genai\";\nimport {\n createThreadManager,\n type BaseThreadManager,\n type ThreadManagerConfig,\n} from \"../../../lib/thread\";\nimport type { MessageContent, ToolMessageContent } from \"../../../lib/types\";\n\n/** A Content with a unique ID for idempotent Redis storage */\nexport interface StoredContent {\n id: string;\n content: Content;\n}\n\nexport interface GoogleGenAIThreadManagerConfig {\n redis: Redis;\n threadId: string;\n /** Thread key, defaults to 'messages' */\n key?: string;\n}\n\n/** Thread manager with Google GenAI Content convenience helpers */\nexport interface GoogleGenAIThreadManager\n extends BaseThreadManager<StoredContent> {\n createUserContent(content: string | MessageContent): StoredContent;\n createSystemContent(content: string): StoredContent;\n createModelContent(parts: Part[]): StoredContent;\n createToolResponseContent(\n toolCallId: string,\n toolName: string,\n content: ToolMessageContent,\n ): StoredContent;\n appendUserMessage(content: string | MessageContent): Promise<void>;\n appendSystemMessage(content: string): Promise<void>;\n appendModelContent(parts: Part[]): Promise<void>;\n appendToolResult(\n toolCallId: string,\n toolName: string,\n content: ToolMessageContent,\n ): Promise<void>;\n}\n\nfunction storedContentId(msg: StoredContent): string {\n return msg.id;\n}\n\n/** Convert zeitlich MessageContent to Google GenAI Part[] */\nexport function messageContentToParts(\n content: string | MessageContent,\n): Part[] {\n if (typeof content === \"string\") {\n return [{ text: content }];\n }\n if (Array.isArray(content)) {\n return content.map((part) => {\n if (part.type === \"text\") {\n return { text: part.text as string };\n }\n return part as unknown as Part;\n });\n }\n return [{ text: String(content) }];\n}\n\n/** Parse ToolMessageContent into a Record suitable for functionResponse */\nfunction parseToolResponse(\n content: ToolMessageContent,\n): Record<string, unknown> {\n if (typeof content === \"string\") {\n try {\n const parsed: unknown = JSON.parse(content);\n return typeof parsed === \"object\" && parsed !== null\n ? (parsed as Record<string, unknown>)\n : { result: content };\n } catch {\n return { result: content };\n }\n }\n return { result: content };\n}\n\n/**\n * Creates a Google GenAI-specific thread manager that stores StoredContent\n * instances in Redis and provides convenience helpers for creating and\n * appending typed Content messages.\n */\nexport function createGoogleGenAIThreadManager(\n config: GoogleGenAIThreadManagerConfig,\n): GoogleGenAIThreadManager {\n const baseConfig: ThreadManagerConfig<StoredContent> = {\n redis: config.redis,\n threadId: config.threadId,\n key: config.key,\n idOf: storedContentId,\n };\n\n const base = createThreadManager(baseConfig);\n\n const helpers = {\n createUserContent(content: string | MessageContent): StoredContent {\n return {\n id: crypto.randomUUID(),\n content: { role: \"user\", parts: messageContentToParts(content) },\n };\n },\n\n createSystemContent(content: string): StoredContent {\n return {\n id: crypto.randomUUID(),\n content: { role: \"system\", parts: [{ text: content }] },\n };\n },\n\n createModelContent(parts: Part[]): StoredContent {\n return {\n id: crypto.randomUUID(),\n content: { role: \"model\", parts },\n };\n },\n\n createToolResponseContent(\n toolCallId: string,\n toolName: string,\n content: ToolMessageContent,\n ): StoredContent {\n return {\n id: crypto.randomUUID(),\n content: {\n role: \"user\",\n parts: [\n {\n functionResponse: {\n id: toolCallId,\n name: toolName,\n response: parseToolResponse(content),\n },\n },\n ],\n },\n };\n },\n\n async appendUserMessage(content: string | MessageContent): Promise<void> {\n await base.append([helpers.createUserContent(content)]);\n },\n\n async appendSystemMessage(content: string): Promise<void> {\n await base.initialize();\n await base.append([helpers.createSystemContent(content)]);\n },\n\n async appendModelContent(parts: Part[]): Promise<void> {\n await base.append([helpers.createModelContent(parts)]);\n },\n\n async appendToolResult(\n toolCallId: string,\n toolName: string,\n content: ToolMessageContent,\n ): Promise<void> {\n await base.append([\n helpers.createToolResponseContent(toolCallId, toolName, content),\n ]);\n },\n };\n\n return Object.assign(base, helpers);\n}\n","import type Redis from \"ioredis\";\nimport type {\n GoogleGenAI,\n Content,\n FunctionDeclaration,\n} from \"@google/genai\";\nimport type { SerializableToolDefinition } from \"../../../lib/types\";\nimport type { AgentResponse } from \"../../../lib/model\";\nimport type { ModelInvokerConfig } from \"../../../lib/model\";\nimport { createGoogleGenAIThreadManager } from \"./thread-manager\";\n\nexport interface GoogleGenAIModelInvokerConfig {\n redis: Redis;\n client: GoogleGenAI;\n model: string;\n}\n\nfunction toFunctionDeclarations(\n tools: SerializableToolDefinition[],\n): FunctionDeclaration[] {\n return tools.map((t) => ({\n name: t.name,\n description: t.description,\n parametersJsonSchema: t.schema,\n }));\n}\n\n/**\n * Merge consecutive Content objects sharing the same role.\n * The Gemini API requires alternating user/model turns; without\n * merging, multiple sequential tool-result messages would violate this.\n */\nfunction mergeConsecutiveContents(contents: Content[]): Content[] {\n const merged: Content[] = [];\n for (const content of contents) {\n const last = merged[merged.length - 1];\n if (last && last.role === content.role) {\n last.parts = [...(last.parts ?? []), ...(content.parts ?? [])];\n } else {\n merged.push({ ...content, parts: [...(content.parts ?? [])] });\n }\n }\n return merged;\n}\n\n/**\n * Creates a Google GenAI model invoker that satisfies the generic\n * `ModelInvoker<Content>` contract.\n *\n * Loads the conversation thread from Redis, invokes the Gemini model via\n * `client.models.generateContent`, appends the AI response, and returns\n * a normalised AgentResponse.\n *\n * @example\n * ```typescript\n * import { createGoogleGenAIModelInvoker } from 'zeitlich/adapters/thread/google-genai';\n * import { createRunAgentActivity } from 'zeitlich';\n * import { GoogleGenAI } from '@google/genai';\n *\n * const client = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY });\n * const invoker = createGoogleGenAIModelInvoker({\n * redis,\n * client,\n * model: 'gemini-2.5-flash',\n * });\n *\n * return { runAgent: createRunAgentActivity(client, invoker) };\n * ```\n */\nexport function createGoogleGenAIModelInvoker({\n redis,\n client,\n model,\n}: GoogleGenAIModelInvokerConfig) {\n return async function invokeGoogleGenAIModel(\n config: ModelInvokerConfig,\n ): Promise<AgentResponse<Content>> {\n const { threadId, state } = config;\n\n const thread = createGoogleGenAIThreadManager({ redis, threadId });\n const stored = await thread.load();\n\n // Separate system instructions from conversation content.\n // Google GenAI takes system instructions via config, not in the contents array.\n let systemInstruction: string | undefined;\n const conversationContents: Content[] = [];\n\n for (const item of stored) {\n if (item.content.role === \"system\") {\n systemInstruction = item.content.parts?.[0]?.text;\n } else {\n conversationContents.push(item.content);\n }\n }\n\n const contents = mergeConsecutiveContents(conversationContents);\n\n const functionDeclarations = toFunctionDeclarations(state.tools);\n const tools =\n functionDeclarations.length > 0\n ? [{ functionDeclarations }]\n : undefined;\n\n const response = await client.models.generateContent({\n model,\n contents,\n config: {\n ...(systemInstruction ? { systemInstruction } : {}),\n ...(tools ? { tools } : {}),\n },\n });\n\n const responseParts = response.candidates?.[0]?.content?.parts ?? [];\n const modelContent: Content = { role: \"model\", parts: responseParts };\n\n await thread.appendModelContent(responseParts);\n\n const functionCalls = response.functionCalls ?? [];\n\n return {\n message: modelContent,\n rawToolCalls: functionCalls.map((fc) => ({\n id: fc.id,\n name: fc.name ?? \"\",\n args: fc.args ?? {},\n })),\n usage: {\n inputTokens: response.usageMetadata?.promptTokenCount,\n outputTokens: response.usageMetadata?.candidatesTokenCount,\n cachedReadTokens: response.usageMetadata?.cachedContentTokenCount,\n },\n };\n };\n}\n\n/**\n * Standalone function for one-shot Google GenAI model invocation.\n * Convenience wrapper around createGoogleGenAIModelInvoker for cases\n * where you don't need to reuse the invoker.\n */\nexport async function invokeGoogleGenAIModel({\n redis,\n client,\n model,\n config,\n}: {\n redis: Redis;\n client: GoogleGenAI;\n model: string;\n config: ModelInvokerConfig;\n}): Promise<AgentResponse<Content>> {\n const invoker = createGoogleGenAIModelInvoker({ redis, client, model });\n return invoker(config);\n}\n","import type Redis from \"ioredis\";\nimport type { GoogleGenAI, Content } from \"@google/genai\";\nimport type { ToolResultConfig } from \"../../../lib/types\";\nimport type { MessageContent } from \"../../../lib/types\";\nimport type { ThreadOps } from \"../../../lib/session/types\";\nimport type { ModelInvoker } from \"../../../lib/model\";\nimport { createGoogleGenAIThreadManager } from \"./thread-manager\";\nimport { createGoogleGenAIModelInvoker } from \"./model-invoker\";\n\nexport interface GoogleGenAIAdapterConfig {\n redis: Redis;\n client: GoogleGenAI;\n /** Default model name (e.g. 'gemini-2.5-flash'). If omitted, use `createModelInvoker()` */\n model?: string;\n}\n\nexport interface GoogleGenAIAdapter {\n /** Thread operations (register these as Temporal activities on the worker) */\n threadOps: ThreadOps;\n /** Model invoker using the default model (only available when `model` was provided) */\n invoker: ModelInvoker<Content>;\n /** Create an invoker for a specific model name (for multi-model setups) */\n createModelInvoker(model: string): ModelInvoker<Content>;\n}\n\n/**\n * Creates a Google GenAI adapter that bundles thread operations and model\n * invocation using the `@google/genai` SDK.\n *\n * The returned `threadOps` should be registered as Temporal activities on\n * the worker. The `invoker` (or invokers created via `createModelInvoker`)\n * should be wrapped with `createRunAgentActivity` for per-agent activities.\n *\n * @example\n * ```typescript\n * import { createGoogleGenAIAdapter } from 'zeitlich/adapters/thread/google-genai';\n * import { createRunAgentActivity } from 'zeitlich';\n * import { GoogleGenAI } from '@google/genai';\n *\n * const client = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY });\n * const adapter = createGoogleGenAIAdapter({ redis, client, model: 'gemini-2.5-flash' });\n *\n * export function createActivities(temporalClient: WorkflowClient) {\n * return {\n * ...adapter.threadOps,\n * runAgent: createRunAgentActivity(temporalClient, adapter.invoker),\n * };\n * }\n * ```\n *\n * @example Multi-model setup\n * ```typescript\n * const adapter = createGoogleGenAIAdapter({ redis, client });\n *\n * export function createActivities(temporalClient: WorkflowClient) {\n * return {\n * ...adapter.threadOps,\n * runResearchAgent: createRunAgentActivity(\n * temporalClient,\n * adapter.createModelInvoker('gemini-2.5-pro'),\n * ),\n * runFastAgent: createRunAgentActivity(\n * temporalClient,\n * adapter.createModelInvoker('gemini-2.5-flash'),\n * ),\n * };\n * }\n * ```\n */\nexport function createGoogleGenAIAdapter(\n config: GoogleGenAIAdapterConfig,\n): GoogleGenAIAdapter {\n const { redis, client } = config;\n\n const threadOps: ThreadOps = {\n async initializeThread(threadId: string): Promise<void> {\n const thread = createGoogleGenAIThreadManager({ redis, threadId });\n await thread.initialize();\n },\n\n async appendHumanMessage(\n threadId: string,\n content: string | MessageContent,\n ): Promise<void> {\n const thread = createGoogleGenAIThreadManager({ redis, threadId });\n await thread.appendUserMessage(content);\n },\n\n async appendSystemMessage(\n threadId: string,\n content: string,\n ): Promise<void> {\n const thread = createGoogleGenAIThreadManager({ redis, threadId });\n await thread.appendSystemMessage(content);\n },\n\n async appendToolResult(cfg: ToolResultConfig): Promise<void> {\n const { threadId, toolCallId, toolName, content } = cfg;\n const thread = createGoogleGenAIThreadManager({ redis, threadId });\n await thread.appendToolResult(toolCallId, toolName, content);\n },\n\n async forkThread(\n sourceThreadId: string,\n targetThreadId: string,\n ): Promise<void> {\n const thread = createGoogleGenAIThreadManager({\n redis,\n threadId: sourceThreadId,\n });\n await thread.fork(targetThreadId);\n },\n };\n\n const makeInvoker = (model: string): ModelInvoker<Content> =>\n createGoogleGenAIModelInvoker({ redis, client, model });\n\n const invoker: ModelInvoker<Content> = config.model\n ? makeInvoker(config.model)\n : ((() => {\n throw new Error(\n \"No default model provided to createGoogleGenAIAdapter. \" +\n \"Either pass `model` in the config or use `createModelInvoker(model)` instead.\",\n );\n }) as unknown as ModelInvoker<Content>);\n\n return {\n threadOps,\n invoker,\n createModelInvoker: makeInvoker,\n };\n}\n"]}
|
|
@@ -113,6 +113,21 @@ function createThreadManager(config) {
|
|
|
113
113
|
await redis.expire(redisKey, THREAD_TTL_SECONDS);
|
|
114
114
|
}
|
|
115
115
|
},
|
|
116
|
+
async fork(newThreadId) {
|
|
117
|
+
await assertThreadExists();
|
|
118
|
+
const data = await redis.lrange(redisKey, 0, -1);
|
|
119
|
+
const forked = createThreadManager({
|
|
120
|
+
...config,
|
|
121
|
+
threadId: newThreadId
|
|
122
|
+
});
|
|
123
|
+
await forked.initialize();
|
|
124
|
+
if (data.length > 0) {
|
|
125
|
+
const newKey = getThreadKey(newThreadId, key);
|
|
126
|
+
await redis.rpush(newKey, ...data);
|
|
127
|
+
await redis.expire(newKey, THREAD_TTL_SECONDS);
|
|
128
|
+
}
|
|
129
|
+
return forked;
|
|
130
|
+
},
|
|
116
131
|
async delete() {
|
|
117
132
|
await redis.del(redisKey, metaKey);
|
|
118
133
|
}
|
|
@@ -245,6 +260,13 @@ function createLangChainAdapter(config) {
|
|
|
245
260
|
const { threadId, toolCallId, content } = cfg;
|
|
246
261
|
const thread = createLangChainThreadManager({ redis, threadId });
|
|
247
262
|
await thread.appendToolMessage(content, toolCallId);
|
|
263
|
+
},
|
|
264
|
+
async forkThread(sourceThreadId, targetThreadId) {
|
|
265
|
+
const thread = createLangChainThreadManager({
|
|
266
|
+
redis,
|
|
267
|
+
threadId: sourceThreadId
|
|
268
|
+
});
|
|
269
|
+
await thread.fork(targetThreadId);
|
|
248
270
|
}
|
|
249
271
|
};
|
|
250
272
|
const makeInvoker = (model) => createLangChainModelInvoker({ redis, model });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../node_modules/uuid/dist/esm/stringify.js","../../../../node_modules/uuid/dist/esm/rng.js","../../../../node_modules/uuid/dist/esm/native.js","../../../../node_modules/uuid/dist/esm/v4.js","../../../../src/lib/thread/manager.ts","../../../../src/adapters/thread/langchain/thread-manager.ts","../../../../src/adapters/thread/langchain/model-invoker.ts","../../../../src/adapters/thread/langchain/activities.ts"],"names":["randomFillSync","randomUUID","HumanMessage","SystemMessage","AIMessage","ToolMessage","invokeLangChainModel","messages","mapStoredMessagesToChatMessages"],"mappings":"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","file":"index.cjs","sourcesContent":["import validate from './validate.js';\nconst byteToHex = [];\nfor (let i = 0; i < 256; ++i) {\n byteToHex.push((i + 0x100).toString(16).slice(1));\n}\nexport function unsafeStringify(arr, offset = 0) {\n return (byteToHex[arr[offset + 0]] +\n byteToHex[arr[offset + 1]] +\n byteToHex[arr[offset + 2]] +\n byteToHex[arr[offset + 3]] +\n '-' +\n byteToHex[arr[offset + 4]] +\n byteToHex[arr[offset + 5]] +\n '-' +\n byteToHex[arr[offset + 6]] +\n byteToHex[arr[offset + 7]] +\n '-' +\n byteToHex[arr[offset + 8]] +\n byteToHex[arr[offset + 9]] +\n '-' +\n byteToHex[arr[offset + 10]] +\n byteToHex[arr[offset + 11]] +\n byteToHex[arr[offset + 12]] +\n byteToHex[arr[offset + 13]] +\n byteToHex[arr[offset + 14]] +\n byteToHex[arr[offset + 15]]).toLowerCase();\n}\nfunction stringify(arr, offset = 0) {\n const uuid = unsafeStringify(arr, offset);\n if (!validate(uuid)) {\n throw TypeError('Stringified UUID is invalid');\n }\n return uuid;\n}\nexport default stringify;\n","import { randomFillSync } from 'crypto';\nconst rnds8Pool = new Uint8Array(256);\nlet poolPtr = rnds8Pool.length;\nexport default function rng() {\n if (poolPtr > rnds8Pool.length - 16) {\n randomFillSync(rnds8Pool);\n poolPtr = 0;\n }\n return rnds8Pool.slice(poolPtr, (poolPtr += 16));\n}\n","import { randomUUID } from 'crypto';\nexport default { randomUUID };\n","import native from './native.js';\nimport rng from './rng.js';\nimport { unsafeStringify } from './stringify.js';\nfunction v4(options, buf, offset) {\n if (native.randomUUID && !buf && !options) {\n return native.randomUUID();\n }\n options = options || {};\n const rnds = options.random ?? options.rng?.() ?? rng();\n if (rnds.length < 16) {\n throw new Error('Random bytes length must be >= 16');\n }\n rnds[6] = (rnds[6] & 0x0f) | 0x40;\n rnds[8] = (rnds[8] & 0x3f) | 0x80;\n if (buf) {\n offset = offset || 0;\n if (offset < 0 || offset + 16 > buf.length) {\n throw new RangeError(`UUID byte range ${offset}:${offset + 15} is out of buffer bounds`);\n }\n for (let i = 0; i < 16; ++i) {\n buf[offset + i] = rnds[i];\n }\n return buf;\n }\n return unsafeStringify(rnds);\n}\nexport default v4;\n","import type { ThreadManagerConfig, BaseThreadManager } from \"./types\";\n\nconst THREAD_TTL_SECONDS = 60 * 60 * 24 * 90; // 90 days\n\n/**\n * Lua script for atomic idempotent append.\n * Checks a dedup key; if it exists the message was already appended and we\n * return 0. Otherwise appends all messages to the list, sets TTL on both\n * the list and the dedup key, and returns 1.\n *\n * KEYS[1] = dedup key, KEYS[2] = list key\n * ARGV[1] = TTL seconds, ARGV[2..N] = serialised messages\n */\nconst APPEND_IDEMPOTENT_SCRIPT = `\nif redis.call('EXISTS', KEYS[1]) == 1 then\n return 0\nend\nfor i = 2, #ARGV do\n redis.call('RPUSH', KEYS[2], ARGV[i])\nend\nredis.call('EXPIRE', KEYS[2], tonumber(ARGV[1]))\nredis.call('SET', KEYS[1], '1', 'EX', tonumber(ARGV[1]))\nreturn 1\n`;\n\nfunction getThreadKey(threadId: string, key: string): string {\n return `thread:${threadId}:${key}`;\n}\n\n/**\n * Creates a generic thread manager for handling conversation state in Redis.\n * Framework-agnostic — works with any serializable message type.\n */\nexport function createThreadManager<T>(\n config: ThreadManagerConfig<T>,\n): BaseThreadManager<T> {\n const {\n redis,\n threadId,\n key = \"messages\",\n serialize = (m: T): string => JSON.stringify(m),\n deserialize = (raw: string): T => JSON.parse(raw) as T,\n idOf,\n } = config;\n const redisKey = getThreadKey(threadId, key);\n const metaKey = getThreadKey(threadId, `${key}:meta`);\n\n async function assertThreadExists(): Promise<void> {\n const exists = await redis.exists(metaKey);\n if (!exists) {\n throw new Error(`Thread \"${threadId}\" (key: ${key}) does not exist`);\n }\n }\n\n return {\n async initialize(): Promise<void> {\n await redis.del(redisKey);\n await redis.set(metaKey, \"1\", \"EX\", THREAD_TTL_SECONDS);\n },\n\n async load(): Promise<T[]> {\n await assertThreadExists();\n const data = await redis.lrange(redisKey, 0, -1);\n return data.map(deserialize);\n },\n\n async append(messages: T[]): Promise<void> {\n if (messages.length === 0) return;\n await assertThreadExists();\n\n if (idOf) {\n const dedupId = messages.map(idOf).join(\":\");\n const dedupKey = getThreadKey(threadId, `dedup:${dedupId}`);\n await redis.eval(\n APPEND_IDEMPOTENT_SCRIPT,\n 2,\n dedupKey,\n redisKey,\n String(THREAD_TTL_SECONDS),\n ...messages.map(serialize),\n );\n } else {\n await redis.rpush(redisKey, ...messages.map(serialize));\n await redis.expire(redisKey, THREAD_TTL_SECONDS);\n }\n },\n\n async delete(): Promise<void> {\n await redis.del(redisKey, metaKey);\n },\n };\n}\n","import type Redis from \"ioredis\";\nimport {\n type $InferMessageContent,\n AIMessage,\n HumanMessage,\n type MessageContent,\n type MessageStructure,\n type StoredMessage,\n SystemMessage,\n ToolMessage,\n} from \"@langchain/core/messages\";\nimport { v4 as uuidv4 } from \"uuid\";\nimport {\n createThreadManager,\n type BaseThreadManager,\n type ThreadManagerConfig,\n} from \"../../../lib/thread\";\n\nexport type LangChainToolMessageContent = $InferMessageContent<\n MessageStructure,\n \"tool\"\n>;\n\nexport interface LangChainThreadManagerConfig {\n redis: Redis;\n threadId: string;\n /** Thread key, defaults to 'messages' */\n key?: string;\n}\n\n/** Thread manager with LangChain StoredMessage convenience helpers */\nexport interface LangChainThreadManager extends BaseThreadManager<StoredMessage> {\n createHumanMessage(content: string | MessageContent): StoredMessage;\n createSystemMessage(content: string): StoredMessage;\n createAIMessage(\n content: string | MessageContent,\n kwargs?: { header?: string; options?: string[]; multiSelect?: boolean },\n ): StoredMessage;\n createToolMessage(\n content: LangChainToolMessageContent,\n toolCallId: string,\n ): StoredMessage;\n appendHumanMessage(content: string | MessageContent): Promise<void>;\n appendSystemMessage(content: string): Promise<void>;\n appendToolMessage(\n content: LangChainToolMessageContent,\n toolCallId: string,\n ): Promise<void>;\n appendAIMessage(content: string | MessageContent): Promise<void>;\n}\n\nfunction storedMessageId(msg: StoredMessage): string {\n return msg.data.id ?? \"\";\n}\n\n/**\n * Creates a LangChain-specific thread manager that stores StoredMessage\n * instances in Redis and provides convenience helpers for creating and\n * appending typed LangChain messages.\n */\nexport function createLangChainThreadManager(\n config: LangChainThreadManagerConfig,\n): LangChainThreadManager {\n const baseConfig: ThreadManagerConfig<StoredMessage> = {\n redis: config.redis,\n threadId: config.threadId,\n key: config.key,\n idOf: storedMessageId,\n };\n\n const base = createThreadManager(baseConfig);\n\n const helpers = {\n createHumanMessage(content: string | MessageContent): StoredMessage {\n return new HumanMessage({\n id: uuidv4(),\n content: content as string,\n }).toDict();\n },\n\n createSystemMessage(content: string): StoredMessage {\n return new SystemMessage({\n id: uuidv4(),\n content: content as string,\n }).toDict();\n },\n\n createAIMessage(\n content: string,\n kwargs?: { header?: string; options?: string[]; multiSelect?: boolean },\n ): StoredMessage {\n return new AIMessage({\n id: uuidv4(),\n content,\n additional_kwargs: kwargs\n ? {\n header: kwargs.header,\n options: kwargs.options,\n multiSelect: kwargs.multiSelect,\n }\n : undefined,\n }).toDict();\n },\n\n createToolMessage(\n content: LangChainToolMessageContent,\n toolCallId: string,\n ): StoredMessage {\n return new ToolMessage({\n id: uuidv4(),\n content: content as MessageContent,\n tool_call_id: toolCallId,\n }).toDict();\n },\n\n async appendHumanMessage(content: string | MessageContent): Promise<void> {\n const message = helpers.createHumanMessage(content);\n await base.append([message]);\n },\n\n async appendToolMessage(\n content: LangChainToolMessageContent,\n toolCallId: string,\n ): Promise<void> {\n const message = helpers.createToolMessage(content, toolCallId);\n await base.append([message]);\n },\n\n async appendAIMessage(content: string | MessageContent): Promise<void> {\n const message = helpers.createAIMessage(content as string);\n await base.append([message]);\n },\n\n async appendSystemMessage(content: string): Promise<void> {\n const message = helpers.createSystemMessage(content);\n await base.initialize();\n await base.append([message]);\n },\n };\n\n return Object.assign(base, helpers);\n}\n","import type Redis from \"ioredis\";\nimport type { AgentResponse } from \"../../../lib/model\";\nimport type { ModelInvokerConfig } from \"../../../lib/model\";\nimport { mapStoredMessagesToChatMessages } from \"@langchain/core/messages\";\nimport type { StoredMessage } from \"@langchain/core/messages\";\nimport { v4 as uuidv4 } from \"uuid\";\nimport type { BaseChatModel } from \"@langchain/core/language_models/chat_models\";\nimport { createLangChainThreadManager } from \"./thread-manager\";\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport interface LangChainModelInvokerConfig<TModel extends BaseChatModel<any> = BaseChatModel<any>> {\n redis: Redis;\n model: TModel;\n}\n\n/**\n * Creates a LangChain-based model invoker that satisfies the generic\n * `ModelInvoker<StoredMessage>` contract.\n *\n * Loads the conversation thread from Redis, invokes a LangChain chat model,\n * appends the AI response, and returns a normalised AgentResponse.\n *\n * @example\n * ```typescript\n * import { createLangChainModelInvoker } from 'zeitlich/adapters/thread/langchain';\n * import { createRunAgentActivity } from 'zeitlich';\n * import { ChatAnthropic } from '@langchain/anthropic';\n *\n * const model = new ChatAnthropic({ model: \"claude-sonnet-4-6\" });\n * const invoker = createLangChainModelInvoker({ redis, model });\n *\n * return { runAgent: createRunAgentActivity(client, invoker) };\n * ```\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function createLangChainModelInvoker<TModel extends BaseChatModel<any> = BaseChatModel<any>>(\n { redis, model }: LangChainModelInvokerConfig<TModel>,\n) {\n return async function invokeLangChainModel(\n config: ModelInvokerConfig\n ): Promise<AgentResponse<StoredMessage>> {\n const { threadId, agentName, state, metadata } = config;\n\n const thread = createLangChainThreadManager({ redis, threadId });\n const runId = uuidv4();\n\n const messages = await thread.load();\n const response = await model.invoke(\n [...mapStoredMessagesToChatMessages(messages)],\n {\n runName: agentName,\n runId,\n metadata: { thread_id: `${agentName}-${threadId}`, ...metadata },\n tools: state.tools,\n }\n );\n\n await thread.append([response.toDict()]);\n\n const toolCalls = response.tool_calls ?? [];\n\n return {\n message: response.toDict(),\n rawToolCalls: toolCalls.map((tc) => ({\n id: tc.id,\n name: tc.name,\n args: tc.args,\n })),\n usage: {\n inputTokens: response.usage_metadata?.input_tokens,\n outputTokens: response.usage_metadata?.output_tokens,\n reasonTokens: response.usage_metadata?.output_token_details?.reasoning,\n cachedWriteTokens:\n response.usage_metadata?.input_token_details?.cache_creation,\n cachedReadTokens:\n response.usage_metadata?.input_token_details?.cache_read,\n },\n };\n };\n}\n\n/**\n * Standalone function for one-shot LangChain model invocation.\n * Convenience wrapper around createLangChainModelInvoker for cases where\n * you don't need to reuse the invoker.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport async function invokeLangChainModel<TModel extends BaseChatModel<any> = BaseChatModel<any>>({\n redis,\n model,\n config,\n}: {\n redis: Redis;\n config: ModelInvokerConfig;\n model: TModel;\n}): Promise<AgentResponse<StoredMessage>> {\n const invoker = createLangChainModelInvoker({ redis, model });\n return invoker(config);\n}\n","import type Redis from \"ioredis\";\nimport type { ToolResultConfig } from \"../../../lib/types\";\nimport type { MessageContent } from \"@langchain/core/messages\";\nimport type { ThreadOps } from \"../../../lib/session/types\";\nimport type { ModelInvoker } from \"../../../lib/model\";\nimport type { StoredMessage } from \"@langchain/core/messages\";\nimport type { BaseChatModel } from \"@langchain/core/language_models/chat_models\";\nimport { createLangChainThreadManager } from \"./thread-manager\";\nimport { createLangChainModelInvoker } from \"./model-invoker\";\n\nexport interface LangChainAdapterConfig {\n redis: Redis;\n /** Optional default model — if omitted, use `createModelInvoker()` to create invokers later */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n model?: BaseChatModel<any>;\n}\n\nexport interface LangChainAdapter {\n /** Thread operations (register these as Temporal activities on the worker) */\n threadOps: ThreadOps;\n /** Model invoker using the default model (only available when `model` was provided) */\n invoker: ModelInvoker<StoredMessage>;\n /** Create an invoker for a specific model (for multi-model setups) */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n createModelInvoker(model: BaseChatModel<any>): ModelInvoker<StoredMessage>;\n}\n\n/**\n * Creates a LangChain adapter that bundles thread operations and model\n * invocation using a consistent message format (StoredMessage).\n *\n * The returned `threadOps` should be registered as Temporal activities on\n * the worker. The `invoker` (or invokers created via `createModelInvoker`)\n * should be wrapped with `createRunAgentActivity` for per-agent activities.\n *\n * @example\n * ```typescript\n * import { createLangChainAdapter } from 'zeitlich/adapters/thread/langchain';\n * import { createRunAgentActivity } from 'zeitlich';\n *\n * const adapter = createLangChainAdapter({ redis, model });\n *\n * export function createActivities(client: WorkflowClient) {\n * return {\n * ...adapter.threadOps,\n * runAgent: createRunAgentActivity(client, adapter.invoker),\n * };\n * }\n * ```\n *\n * @example Multi-model setup\n * ```typescript\n * const adapter = createLangChainAdapter({ redis });\n *\n * export function createActivities(client: WorkflowClient) {\n * return {\n * ...adapter.threadOps,\n * runResearchAgent: createRunAgentActivity(client, adapter.createModelInvoker(claude)),\n * runWriterAgent: createRunAgentActivity(client, adapter.createModelInvoker(gpt4)),\n * };\n * }\n * ```\n */\nexport function createLangChainAdapter(\n config: LangChainAdapterConfig\n): LangChainAdapter {\n const { redis } = config;\n\n const threadOps: ThreadOps = {\n async initializeThread(threadId: string): Promise<void> {\n const thread = createLangChainThreadManager({ redis, threadId });\n await thread.initialize();\n },\n\n async appendHumanMessage(\n threadId: string,\n content: string | MessageContent\n ): Promise<void> {\n const thread = createLangChainThreadManager({ redis, threadId });\n await thread.appendHumanMessage(content);\n },\n\n async appendSystemMessage(\n threadId: string,\n content: string\n ): Promise<void> {\n const thread = createLangChainThreadManager({ redis, threadId });\n await thread.appendSystemMessage(content);\n },\n\n async appendToolResult(cfg: ToolResultConfig): Promise<void> {\n const { threadId, toolCallId, content } = cfg;\n const thread = createLangChainThreadManager({ redis, threadId });\n await thread.appendToolMessage(content, toolCallId);\n },\n };\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const makeInvoker = (model: BaseChatModel<any>): ModelInvoker<StoredMessage> =>\n createLangChainModelInvoker({ redis, model });\n\n const invoker: ModelInvoker<StoredMessage> = config.model\n ? makeInvoker(config.model)\n : ((() => {\n throw new Error(\n \"No default model provided to createLangChainAdapter. \" +\n \"Either pass `model` in the config or use `createModelInvoker(model)` instead.\"\n );\n }) as unknown as ModelInvoker<StoredMessage>);\n\n return {\n threadOps,\n invoker,\n createModelInvoker: makeInvoker,\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../../../node_modules/uuid/dist/esm/stringify.js","../../../../node_modules/uuid/dist/esm/rng.js","../../../../node_modules/uuid/dist/esm/native.js","../../../../node_modules/uuid/dist/esm/v4.js","../../../../src/lib/thread/manager.ts","../../../../src/adapters/thread/langchain/thread-manager.ts","../../../../src/adapters/thread/langchain/model-invoker.ts","../../../../src/adapters/thread/langchain/activities.ts"],"names":["randomFillSync","randomUUID","HumanMessage","SystemMessage","AIMessage","ToolMessage","invokeLangChainModel","messages","mapStoredMessagesToChatMessages"],"mappings":"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","file":"index.cjs","sourcesContent":["import validate from './validate.js';\nconst byteToHex = [];\nfor (let i = 0; i < 256; ++i) {\n byteToHex.push((i + 0x100).toString(16).slice(1));\n}\nexport function unsafeStringify(arr, offset = 0) {\n return (byteToHex[arr[offset + 0]] +\n byteToHex[arr[offset + 1]] +\n byteToHex[arr[offset + 2]] +\n byteToHex[arr[offset + 3]] +\n '-' +\n byteToHex[arr[offset + 4]] +\n byteToHex[arr[offset + 5]] +\n '-' +\n byteToHex[arr[offset + 6]] +\n byteToHex[arr[offset + 7]] +\n '-' +\n byteToHex[arr[offset + 8]] +\n byteToHex[arr[offset + 9]] +\n '-' +\n byteToHex[arr[offset + 10]] +\n byteToHex[arr[offset + 11]] +\n byteToHex[arr[offset + 12]] +\n byteToHex[arr[offset + 13]] +\n byteToHex[arr[offset + 14]] +\n byteToHex[arr[offset + 15]]).toLowerCase();\n}\nfunction stringify(arr, offset = 0) {\n const uuid = unsafeStringify(arr, offset);\n if (!validate(uuid)) {\n throw TypeError('Stringified UUID is invalid');\n }\n return uuid;\n}\nexport default stringify;\n","import { randomFillSync } from 'crypto';\nconst rnds8Pool = new Uint8Array(256);\nlet poolPtr = rnds8Pool.length;\nexport default function rng() {\n if (poolPtr > rnds8Pool.length - 16) {\n randomFillSync(rnds8Pool);\n poolPtr = 0;\n }\n return rnds8Pool.slice(poolPtr, (poolPtr += 16));\n}\n","import { randomUUID } from 'crypto';\nexport default { randomUUID };\n","import native from './native.js';\nimport rng from './rng.js';\nimport { unsafeStringify } from './stringify.js';\nfunction v4(options, buf, offset) {\n if (native.randomUUID && !buf && !options) {\n return native.randomUUID();\n }\n options = options || {};\n const rnds = options.random ?? options.rng?.() ?? rng();\n if (rnds.length < 16) {\n throw new Error('Random bytes length must be >= 16');\n }\n rnds[6] = (rnds[6] & 0x0f) | 0x40;\n rnds[8] = (rnds[8] & 0x3f) | 0x80;\n if (buf) {\n offset = offset || 0;\n if (offset < 0 || offset + 16 > buf.length) {\n throw new RangeError(`UUID byte range ${offset}:${offset + 15} is out of buffer bounds`);\n }\n for (let i = 0; i < 16; ++i) {\n buf[offset + i] = rnds[i];\n }\n return buf;\n }\n return unsafeStringify(rnds);\n}\nexport default v4;\n","import type {\n ThreadManagerConfig,\n BaseThreadManager,\n} from \"./types\";\n\nconst THREAD_TTL_SECONDS = 60 * 60 * 24 * 90; // 90 days\n\n/**\n * Lua script for atomic idempotent append.\n * Checks a dedup key; if it exists the message was already appended and we\n * return 0. Otherwise appends all messages to the list, sets TTL on both\n * the list and the dedup key, and returns 1.\n *\n * KEYS[1] = dedup key, KEYS[2] = list key\n * ARGV[1] = TTL seconds, ARGV[2..N] = serialised messages\n */\nconst APPEND_IDEMPOTENT_SCRIPT = `\nif redis.call('EXISTS', KEYS[1]) == 1 then\n return 0\nend\nfor i = 2, #ARGV do\n redis.call('RPUSH', KEYS[2], ARGV[i])\nend\nredis.call('EXPIRE', KEYS[2], tonumber(ARGV[1]))\nredis.call('SET', KEYS[1], '1', 'EX', tonumber(ARGV[1]))\nreturn 1\n`;\n\nfunction getThreadKey(threadId: string, key: string): string {\n return `thread:${threadId}:${key}`;\n}\n\n/**\n * Creates a generic thread manager for handling conversation state in Redis.\n * Framework-agnostic — works with any serializable message type.\n */\nexport function createThreadManager<T>(\n config: ThreadManagerConfig<T>,\n): BaseThreadManager<T> {\n const {\n redis,\n threadId,\n key = \"messages\",\n serialize = (m: T): string => JSON.stringify(m),\n deserialize = (raw: string): T => JSON.parse(raw) as T,\n idOf,\n } = config;\n const redisKey = getThreadKey(threadId, key);\n const metaKey = getThreadKey(threadId, `${key}:meta`);\n\n async function assertThreadExists(): Promise<void> {\n const exists = await redis.exists(metaKey);\n if (!exists) {\n throw new Error(`Thread \"${threadId}\" (key: ${key}) does not exist`);\n }\n }\n\n return {\n async initialize(): Promise<void> {\n await redis.del(redisKey);\n await redis.set(metaKey, \"1\", \"EX\", THREAD_TTL_SECONDS);\n },\n\n async load(): Promise<T[]> {\n await assertThreadExists();\n const data = await redis.lrange(redisKey, 0, -1);\n return data.map(deserialize);\n },\n\n async append(messages: T[]): Promise<void> {\n if (messages.length === 0) return;\n await assertThreadExists();\n\n if (idOf) {\n const dedupId = messages.map(idOf).join(\":\");\n const dedupKey = getThreadKey(threadId, `dedup:${dedupId}`);\n await redis.eval(\n APPEND_IDEMPOTENT_SCRIPT,\n 2,\n dedupKey,\n redisKey,\n String(THREAD_TTL_SECONDS),\n ...messages.map(serialize),\n );\n } else {\n await redis.rpush(redisKey, ...messages.map(serialize));\n await redis.expire(redisKey, THREAD_TTL_SECONDS);\n }\n },\n\n async fork(newThreadId: string): Promise<BaseThreadManager<T>> {\n await assertThreadExists();\n const data = await redis.lrange(redisKey, 0, -1);\n const forked = createThreadManager({\n ...config,\n threadId: newThreadId,\n });\n await forked.initialize();\n if (data.length > 0) {\n const newKey = getThreadKey(newThreadId, key);\n await redis.rpush(newKey, ...data);\n await redis.expire(newKey, THREAD_TTL_SECONDS);\n }\n return forked;\n },\n\n async delete(): Promise<void> {\n await redis.del(redisKey, metaKey);\n },\n };\n}\n","import type Redis from \"ioredis\";\nimport {\n type $InferMessageContent,\n AIMessage,\n HumanMessage,\n type MessageContent,\n type MessageStructure,\n type StoredMessage,\n SystemMessage,\n ToolMessage,\n} from \"@langchain/core/messages\";\nimport { v4 as uuidv4 } from \"uuid\";\nimport {\n createThreadManager,\n type BaseThreadManager,\n type ThreadManagerConfig,\n} from \"../../../lib/thread\";\n\nexport type LangChainToolMessageContent = $InferMessageContent<\n MessageStructure,\n \"tool\"\n>;\n\nexport interface LangChainThreadManagerConfig {\n redis: Redis;\n threadId: string;\n /** Thread key, defaults to 'messages' */\n key?: string;\n}\n\n/** Thread manager with LangChain StoredMessage convenience helpers */\nexport interface LangChainThreadManager extends BaseThreadManager<StoredMessage> {\n createHumanMessage(content: string | MessageContent): StoredMessage;\n createSystemMessage(content: string): StoredMessage;\n createAIMessage(\n content: string | MessageContent,\n kwargs?: { header?: string; options?: string[]; multiSelect?: boolean },\n ): StoredMessage;\n createToolMessage(\n content: LangChainToolMessageContent,\n toolCallId: string,\n ): StoredMessage;\n appendHumanMessage(content: string | MessageContent): Promise<void>;\n appendSystemMessage(content: string): Promise<void>;\n appendToolMessage(\n content: LangChainToolMessageContent,\n toolCallId: string,\n ): Promise<void>;\n appendAIMessage(content: string | MessageContent): Promise<void>;\n}\n\nfunction storedMessageId(msg: StoredMessage): string {\n return msg.data.id ?? \"\";\n}\n\n/**\n * Creates a LangChain-specific thread manager that stores StoredMessage\n * instances in Redis and provides convenience helpers for creating and\n * appending typed LangChain messages.\n */\nexport function createLangChainThreadManager(\n config: LangChainThreadManagerConfig,\n): LangChainThreadManager {\n const baseConfig: ThreadManagerConfig<StoredMessage> = {\n redis: config.redis,\n threadId: config.threadId,\n key: config.key,\n idOf: storedMessageId,\n };\n\n const base = createThreadManager(baseConfig);\n\n const helpers = {\n createHumanMessage(content: string | MessageContent): StoredMessage {\n return new HumanMessage({\n id: uuidv4(),\n content: content as string,\n }).toDict();\n },\n\n createSystemMessage(content: string): StoredMessage {\n return new SystemMessage({\n id: uuidv4(),\n content: content as string,\n }).toDict();\n },\n\n createAIMessage(\n content: string,\n kwargs?: { header?: string; options?: string[]; multiSelect?: boolean },\n ): StoredMessage {\n return new AIMessage({\n id: uuidv4(),\n content,\n additional_kwargs: kwargs\n ? {\n header: kwargs.header,\n options: kwargs.options,\n multiSelect: kwargs.multiSelect,\n }\n : undefined,\n }).toDict();\n },\n\n createToolMessage(\n content: LangChainToolMessageContent,\n toolCallId: string,\n ): StoredMessage {\n return new ToolMessage({\n id: uuidv4(),\n content: content as MessageContent,\n tool_call_id: toolCallId,\n }).toDict();\n },\n\n async appendHumanMessage(content: string | MessageContent): Promise<void> {\n const message = helpers.createHumanMessage(content);\n await base.append([message]);\n },\n\n async appendToolMessage(\n content: LangChainToolMessageContent,\n toolCallId: string,\n ): Promise<void> {\n const message = helpers.createToolMessage(content, toolCallId);\n await base.append([message]);\n },\n\n async appendAIMessage(content: string | MessageContent): Promise<void> {\n const message = helpers.createAIMessage(content as string);\n await base.append([message]);\n },\n\n async appendSystemMessage(content: string): Promise<void> {\n const message = helpers.createSystemMessage(content);\n await base.initialize();\n await base.append([message]);\n },\n };\n\n return Object.assign(base, helpers);\n}\n","import type Redis from \"ioredis\";\nimport type { AgentResponse } from \"../../../lib/model\";\nimport type { ModelInvokerConfig } from \"../../../lib/model\";\nimport { mapStoredMessagesToChatMessages } from \"@langchain/core/messages\";\nimport type { StoredMessage } from \"@langchain/core/messages\";\nimport { v4 as uuidv4 } from \"uuid\";\nimport type { BaseChatModel } from \"@langchain/core/language_models/chat_models\";\nimport { createLangChainThreadManager } from \"./thread-manager\";\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport interface LangChainModelInvokerConfig<TModel extends BaseChatModel<any> = BaseChatModel<any>> {\n redis: Redis;\n model: TModel;\n}\n\n/**\n * Creates a LangChain-based model invoker that satisfies the generic\n * `ModelInvoker<StoredMessage>` contract.\n *\n * Loads the conversation thread from Redis, invokes a LangChain chat model,\n * appends the AI response, and returns a normalised AgentResponse.\n *\n * @example\n * ```typescript\n * import { createLangChainModelInvoker } from 'zeitlich/adapters/thread/langchain';\n * import { createRunAgentActivity } from 'zeitlich';\n * import { ChatAnthropic } from '@langchain/anthropic';\n *\n * const model = new ChatAnthropic({ model: \"claude-sonnet-4-6\" });\n * const invoker = createLangChainModelInvoker({ redis, model });\n *\n * return { runAgent: createRunAgentActivity(client, invoker) };\n * ```\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function createLangChainModelInvoker<TModel extends BaseChatModel<any> = BaseChatModel<any>>(\n { redis, model }: LangChainModelInvokerConfig<TModel>,\n) {\n return async function invokeLangChainModel(\n config: ModelInvokerConfig\n ): Promise<AgentResponse<StoredMessage>> {\n const { threadId, agentName, state, metadata } = config;\n\n const thread = createLangChainThreadManager({ redis, threadId });\n const runId = uuidv4();\n\n const messages = await thread.load();\n const response = await model.invoke(\n [...mapStoredMessagesToChatMessages(messages)],\n {\n runName: agentName,\n runId,\n metadata: { thread_id: `${agentName}-${threadId}`, ...metadata },\n tools: state.tools,\n }\n );\n\n await thread.append([response.toDict()]);\n\n const toolCalls = response.tool_calls ?? [];\n\n return {\n message: response.toDict(),\n rawToolCalls: toolCalls.map((tc) => ({\n id: tc.id,\n name: tc.name,\n args: tc.args,\n })),\n usage: {\n inputTokens: response.usage_metadata?.input_tokens,\n outputTokens: response.usage_metadata?.output_tokens,\n reasonTokens: response.usage_metadata?.output_token_details?.reasoning,\n cachedWriteTokens:\n response.usage_metadata?.input_token_details?.cache_creation,\n cachedReadTokens:\n response.usage_metadata?.input_token_details?.cache_read,\n },\n };\n };\n}\n\n/**\n * Standalone function for one-shot LangChain model invocation.\n * Convenience wrapper around createLangChainModelInvoker for cases where\n * you don't need to reuse the invoker.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport async function invokeLangChainModel<TModel extends BaseChatModel<any> = BaseChatModel<any>>({\n redis,\n model,\n config,\n}: {\n redis: Redis;\n config: ModelInvokerConfig;\n model: TModel;\n}): Promise<AgentResponse<StoredMessage>> {\n const invoker = createLangChainModelInvoker({ redis, model });\n return invoker(config);\n}\n","import type Redis from \"ioredis\";\nimport type { ToolResultConfig } from \"../../../lib/types\";\nimport type { MessageContent } from \"@langchain/core/messages\";\nimport type { ThreadOps } from \"../../../lib/session/types\";\nimport type { ModelInvoker } from \"../../../lib/model\";\nimport type { StoredMessage } from \"@langchain/core/messages\";\nimport type { BaseChatModel } from \"@langchain/core/language_models/chat_models\";\nimport { createLangChainThreadManager } from \"./thread-manager\";\nimport { createLangChainModelInvoker } from \"./model-invoker\";\n\nexport interface LangChainAdapterConfig {\n redis: Redis;\n /** Optional default model — if omitted, use `createModelInvoker()` to create invokers later */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n model?: BaseChatModel<any>;\n}\n\nexport interface LangChainAdapter {\n /** Thread operations (register these as Temporal activities on the worker) */\n threadOps: ThreadOps;\n /** Model invoker using the default model (only available when `model` was provided) */\n invoker: ModelInvoker<StoredMessage>;\n /** Create an invoker for a specific model (for multi-model setups) */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n createModelInvoker(model: BaseChatModel<any>): ModelInvoker<StoredMessage>;\n}\n\n/**\n * Creates a LangChain adapter that bundles thread operations and model\n * invocation using a consistent message format (StoredMessage).\n *\n * The returned `threadOps` should be registered as Temporal activities on\n * the worker. The `invoker` (or invokers created via `createModelInvoker`)\n * should be wrapped with `createRunAgentActivity` for per-agent activities.\n *\n * @example\n * ```typescript\n * import { createLangChainAdapter } from 'zeitlich/adapters/thread/langchain';\n * import { createRunAgentActivity } from 'zeitlich';\n *\n * const adapter = createLangChainAdapter({ redis, model });\n *\n * export function createActivities(client: WorkflowClient) {\n * return {\n * ...adapter.threadOps,\n * runAgent: createRunAgentActivity(client, adapter.invoker),\n * };\n * }\n * ```\n *\n * @example Multi-model setup\n * ```typescript\n * const adapter = createLangChainAdapter({ redis });\n *\n * export function createActivities(client: WorkflowClient) {\n * return {\n * ...adapter.threadOps,\n * runResearchAgent: createRunAgentActivity(client, adapter.createModelInvoker(claude)),\n * runWriterAgent: createRunAgentActivity(client, adapter.createModelInvoker(gpt4)),\n * };\n * }\n * ```\n */\nexport function createLangChainAdapter(\n config: LangChainAdapterConfig\n): LangChainAdapter {\n const { redis } = config;\n\n const threadOps: ThreadOps = {\n async initializeThread(threadId: string): Promise<void> {\n const thread = createLangChainThreadManager({ redis, threadId });\n await thread.initialize();\n },\n\n async appendHumanMessage(\n threadId: string,\n content: string | MessageContent\n ): Promise<void> {\n const thread = createLangChainThreadManager({ redis, threadId });\n await thread.appendHumanMessage(content);\n },\n\n async appendSystemMessage(\n threadId: string,\n content: string\n ): Promise<void> {\n const thread = createLangChainThreadManager({ redis, threadId });\n await thread.appendSystemMessage(content);\n },\n\n async appendToolResult(cfg: ToolResultConfig): Promise<void> {\n const { threadId, toolCallId, content } = cfg;\n const thread = createLangChainThreadManager({ redis, threadId });\n await thread.appendToolMessage(content, toolCallId);\n },\n\n async forkThread(\n sourceThreadId: string,\n targetThreadId: string,\n ): Promise<void> {\n const thread = createLangChainThreadManager({\n redis,\n threadId: sourceThreadId,\n });\n await thread.fork(targetThreadId);\n },\n };\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const makeInvoker = (model: BaseChatModel<any>): ModelInvoker<StoredMessage> =>\n createLangChainModelInvoker({ redis, model });\n\n const invoker: ModelInvoker<StoredMessage> = config.model\n ? makeInvoker(config.model)\n : ((() => {\n throw new Error(\n \"No default model provided to createLangChainAdapter. \" +\n \"Either pass `model` in the config or use `createModelInvoker(model)` instead.\"\n );\n }) as unknown as ModelInvoker<StoredMessage>);\n\n return {\n threadOps,\n invoker,\n createModelInvoker: makeInvoker,\n };\n}\n"]}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import Redis from 'ioredis';
|
|
2
|
-
import { T as ThreadOps, M as ModelInvoker, a as ModelInvokerConfig, A as AgentResponse } from '../../../types-
|
|
2
|
+
import { T as ThreadOps, M as ModelInvoker, a as ModelInvokerConfig, A as AgentResponse } from '../../../types-Dje1TdH6.cjs';
|
|
3
3
|
import { StoredMessage, MessageContent, $InferMessageContent, MessageStructure } from '@langchain/core/messages';
|
|
4
4
|
import { BaseChatModel } from '@langchain/core/language_models/chat_models';
|
|
5
|
-
import { B as BaseThreadManager } from '../../../types-
|
|
5
|
+
import { B as BaseThreadManager } from '../../../types-35POpVfa.cjs';
|
|
6
6
|
import '@temporalio/common';
|
|
7
7
|
import '../../../types-BMXzv7TN.cjs';
|
|
8
8
|
import 'zod';
|