zeitlich 0.2.19 → 0.2.21

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.
Files changed (64) hide show
  1. package/dist/adapters/sandbox/daytona/index.cjs +25 -10
  2. package/dist/adapters/sandbox/daytona/index.cjs.map +1 -1
  3. package/dist/adapters/sandbox/daytona/index.d.cts +4 -1
  4. package/dist/adapters/sandbox/daytona/index.d.ts +4 -1
  5. package/dist/adapters/sandbox/daytona/index.js +25 -10
  6. package/dist/adapters/sandbox/daytona/index.js.map +1 -1
  7. package/dist/adapters/sandbox/virtual/index.d.cts +4 -3
  8. package/dist/adapters/sandbox/virtual/index.d.ts +4 -3
  9. package/dist/adapters/thread/google-genai/index.cjs +69 -24
  10. package/dist/adapters/thread/google-genai/index.cjs.map +1 -1
  11. package/dist/adapters/thread/google-genai/index.d.cts +10 -10
  12. package/dist/adapters/thread/google-genai/index.d.ts +10 -10
  13. package/dist/adapters/thread/google-genai/index.js +69 -24
  14. package/dist/adapters/thread/google-genai/index.js.map +1 -1
  15. package/dist/adapters/thread/langchain/index.cjs +76 -69
  16. package/dist/adapters/thread/langchain/index.cjs.map +1 -1
  17. package/dist/adapters/thread/langchain/index.d.cts +11 -11
  18. package/dist/adapters/thread/langchain/index.d.ts +11 -11
  19. package/dist/adapters/thread/langchain/index.js +76 -69
  20. package/dist/adapters/thread/langchain/index.js.map +1 -1
  21. package/dist/index.cjs +198 -118
  22. package/dist/index.cjs.map +1 -1
  23. package/dist/index.d.cts +14 -14
  24. package/dist/index.d.ts +14 -14
  25. package/dist/index.js +198 -118
  26. package/dist/index.js.map +1 -1
  27. package/dist/{queries-D8T4pEeu.d.ts → queries-6Avfh74U.d.ts} +1 -1
  28. package/dist/{queries-D22uWTOb.d.cts → queries-CHa2iv_I.d.cts} +1 -1
  29. package/dist/{types-CxWLeJTB.d.ts → types-BkAYmc96.d.ts} +6 -6
  30. package/dist/{types-CCfJb5Jl.d.cts → types-CES_30qx.d.cts} +6 -6
  31. package/dist/{types-DjT78Sdp.d.cts → types-YbL7JpEA.d.cts} +4 -2
  32. package/dist/{types-DjT78Sdp.d.ts → types-YbL7JpEA.d.ts} +4 -2
  33. package/dist/workflow.cjs +68 -34
  34. package/dist/workflow.cjs.map +1 -1
  35. package/dist/workflow.d.cts +16 -12
  36. package/dist/workflow.d.ts +16 -12
  37. package/dist/workflow.js +68 -34
  38. package/dist/workflow.js.map +1 -1
  39. package/package.json +1 -1
  40. package/src/adapters/sandbox/daytona/filesystem.ts +21 -12
  41. package/src/adapters/sandbox/daytona/index.ts +24 -23
  42. package/src/adapters/thread/google-genai/activities.ts +11 -9
  43. package/src/adapters/thread/google-genai/model-invoker.ts +6 -11
  44. package/src/adapters/thread/google-genai/thread-manager.ts +44 -29
  45. package/src/adapters/thread/langchain/activities.ts +6 -4
  46. package/src/adapters/thread/langchain/thread-manager.ts +55 -27
  47. package/src/lib/session/session-edge-cases.integration.test.ts +20 -2
  48. package/src/lib/session/session.integration.test.ts +16 -2
  49. package/src/lib/session/session.ts +7 -5
  50. package/src/lib/session/types.ts +9 -3
  51. package/src/lib/subagent/handler.ts +1 -1
  52. package/src/lib/subagent/subagent.integration.test.ts +5 -4
  53. package/src/lib/subagent/tool.ts +1 -1
  54. package/src/lib/thread/index.ts +0 -1
  55. package/src/lib/tool-router/auto-append-sandbox.integration.test.ts +20 -21
  56. package/src/lib/tool-router/auto-append.ts +3 -2
  57. package/src/lib/tool-router/router-edge-cases.integration.test.ts +64 -23
  58. package/src/lib/tool-router/router.integration.test.ts +60 -23
  59. package/src/lib/tool-router/router.ts +58 -29
  60. package/src/lib/tool-router/types.ts +12 -7
  61. package/src/lib/workflow.test.ts +18 -6
  62. package/src/lib/workflow.ts +13 -3
  63. package/src/tools/task-create/handler.ts +3 -6
  64. package/src/workflow.ts +2 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zeitlich",
3
- "version": "0.2.19",
3
+ "version": "0.2.21",
4
4
  "description": "[EXPERIMENTAL] An opinionated AI agent implementation for Temporal",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -19,7 +19,7 @@ export class DaytonaSandboxFileSystem implements SandboxFileSystem {
19
19
 
20
20
  constructor(
21
21
  private sandbox: DaytonaSdkSandbox,
22
- workspaceBase = "/home/daytona",
22
+ workspaceBase = "/home/daytona"
23
23
  ) {
24
24
  this.workspaceBase = posix.resolve("/", workspaceBase);
25
25
  }
@@ -49,10 +49,18 @@ export class DaytonaSandboxFileSystem implements SandboxFileSystem {
49
49
  await this.sandbox.fs.uploadFile(buf, norm);
50
50
  }
51
51
 
52
- async appendFile(
53
- path: string,
54
- content: string | Uint8Array,
52
+ async writeFiles(
53
+ files: { path: string; content: string | Uint8Array }[]
55
54
  ): Promise<void> {
55
+ await this.sandbox.fs.uploadFiles(
56
+ files.map((f) => ({
57
+ source: Buffer.from(f.content),
58
+ destination: f.path,
59
+ }))
60
+ );
61
+ }
62
+
63
+ async appendFile(path: string, content: string | Uint8Array): Promise<void> {
56
64
  const norm = this.normalisePath(path);
57
65
  let existing: Buffer;
58
66
  try {
@@ -89,10 +97,7 @@ export class DaytonaSandboxFileSystem implements SandboxFileSystem {
89
97
  };
90
98
  }
91
99
 
92
- async mkdir(
93
- path: string,
94
- _options?: { recursive?: boolean },
95
- ): Promise<void> {
100
+ async mkdir(path: string, _options?: { recursive?: boolean }): Promise<void> {
96
101
  const norm = this.normalisePath(path);
97
102
  await this.sandbox.fs.createFolder(norm, "755");
98
103
  }
@@ -116,7 +121,7 @@ export class DaytonaSandboxFileSystem implements SandboxFileSystem {
116
121
 
117
122
  async rm(
118
123
  path: string,
119
- options?: { recursive?: boolean; force?: boolean },
124
+ options?: { recursive?: boolean; force?: boolean }
120
125
  ): Promise<void> {
121
126
  const norm = this.normalisePath(path);
122
127
  try {
@@ -129,7 +134,7 @@ export class DaytonaSandboxFileSystem implements SandboxFileSystem {
129
134
  async cp(
130
135
  src: string,
131
136
  dest: string,
132
- options?: { recursive?: boolean },
137
+ options?: { recursive?: boolean }
133
138
  ): Promise<void> {
134
139
  const normSrc = this.normalisePath(src);
135
140
  const normDest = this.normalisePath(dest);
@@ -138,9 +143,13 @@ export class DaytonaSandboxFileSystem implements SandboxFileSystem {
138
143
  if (!options?.recursive) {
139
144
  throw new Error(`EISDIR: is a directory (use recursive): ${src}`);
140
145
  }
141
- await this.sandbox.process.executeCommand(`cp -r "${normSrc}" "${normDest}"`);
146
+ await this.sandbox.process.executeCommand(
147
+ `cp -r "${normSrc}" "${normDest}"`
148
+ );
142
149
  } else {
143
- await this.sandbox.process.executeCommand(`cp "${normSrc}" "${normDest}"`);
150
+ await this.sandbox.process.executeCommand(
151
+ `cp "${normSrc}" "${normDest}"`
152
+ );
144
153
  }
145
154
  }
146
155
 
@@ -1,7 +1,4 @@
1
- import {
2
- Daytona,
3
- type Sandbox as DaytonaSdkSandbox,
4
- } from "@daytonaio/sdk";
1
+ import { Daytona, type Sandbox as DaytonaSdkSandbox } from "@daytonaio/sdk";
5
2
  import type {
6
3
  Sandbox,
7
4
  SandboxCapabilities,
@@ -38,7 +35,7 @@ class DaytonaSandboxImpl implements Sandbox {
38
35
  constructor(
39
36
  readonly id: string,
40
37
  private sdkSandbox: DaytonaSdkSandbox,
41
- workspaceBase = "/home/daytona",
38
+ workspaceBase = "/home/daytona"
42
39
  ) {
43
40
  this.fs = new DaytonaSandboxFileSystem(sdkSandbox, workspaceBase);
44
41
  }
@@ -48,7 +45,7 @@ class DaytonaSandboxImpl implements Sandbox {
48
45
  command,
49
46
  options?.cwd,
50
47
  options?.env,
51
- options?.timeout,
48
+ options?.timeout
52
49
  );
53
50
 
54
51
  return {
@@ -67,9 +64,10 @@ class DaytonaSandboxImpl implements Sandbox {
67
64
  // DaytonaSandboxProvider
68
65
  // ============================================================================
69
66
 
70
- export class DaytonaSandboxProvider
71
- implements SandboxProvider<DaytonaSandboxCreateOptions, DaytonaSandbox>
72
- {
67
+ export class DaytonaSandboxProvider implements SandboxProvider<
68
+ DaytonaSandboxCreateOptions,
69
+ DaytonaSandbox
70
+ > {
73
71
  readonly id = "daytona";
74
72
  readonly capabilities: SandboxCapabilities = {
75
73
  filesystem: true,
@@ -79,7 +77,6 @@ export class DaytonaSandboxProvider
79
77
 
80
78
  private client: Daytona;
81
79
  private readonly defaultWorkspaceBase: string;
82
- private workspaceBaseById = new Map<string, string>();
83
80
 
84
81
  constructor(config?: DaytonaSandboxConfig) {
85
82
  this.client = new Daytona(config);
@@ -87,7 +84,7 @@ export class DaytonaSandboxProvider
87
84
  }
88
85
 
89
86
  async create(
90
- options?: DaytonaSandboxCreateOptions,
87
+ options?: DaytonaSandboxCreateOptions
91
88
  ): Promise<SandboxCreateResult> {
92
89
  const sdkSandbox = await this.client.create(
93
90
  {
@@ -99,21 +96,24 @@ export class DaytonaSandboxProvider
99
96
  autoArchiveInterval: options?.autoArchiveInterval,
100
97
  autoDeleteInterval: options?.autoDeleteInterval,
101
98
  },
102
- { timeout: options?.timeout ?? 60 },
99
+ { timeout: options?.timeout ?? 60 }
103
100
  );
104
101
 
105
102
  const workspaceBase = options?.workspaceBase ?? this.defaultWorkspaceBase;
106
- this.workspaceBaseById.set(sdkSandbox.id, workspaceBase);
103
+
107
104
  const sandbox = new DaytonaSandboxImpl(
108
105
  sdkSandbox.id,
109
106
  sdkSandbox,
110
- workspaceBase,
107
+ workspaceBase
111
108
  );
112
109
 
113
110
  if (options?.initialFiles) {
114
- for (const [path, content] of Object.entries(options.initialFiles)) {
115
- await sandbox.fs.writeFile(path, content);
116
- }
111
+ await sandbox.fs.writeFiles(
112
+ Object.entries(options.initialFiles).map(([path, content]) => ({
113
+ path,
114
+ content,
115
+ }))
116
+ );
117
117
  }
118
118
 
119
119
  return { sandbox };
@@ -122,9 +122,11 @@ export class DaytonaSandboxProvider
122
122
  async get(sandboxId: string): Promise<DaytonaSandbox> {
123
123
  try {
124
124
  const sdkSandbox = await this.client.get(sandboxId);
125
- const workspaceBase =
126
- this.workspaceBaseById.get(sandboxId) ?? this.defaultWorkspaceBase;
127
- return new DaytonaSandboxImpl(sdkSandbox.id, sdkSandbox, workspaceBase);
125
+ return new DaytonaSandboxImpl(
126
+ sdkSandbox.id,
127
+ sdkSandbox,
128
+ this.defaultWorkspaceBase
129
+ );
128
130
  } catch {
129
131
  throw new SandboxNotFoundError(sandboxId);
130
132
  }
@@ -134,7 +136,6 @@ export class DaytonaSandboxProvider
134
136
  try {
135
137
  const sdkSandbox = await this.client.get(sandboxId);
136
138
  await this.client.delete(sdkSandbox);
137
- this.workspaceBaseById.delete(sandboxId);
138
139
  } catch {
139
140
  // Already gone
140
141
  }
@@ -142,13 +143,13 @@ export class DaytonaSandboxProvider
142
143
 
143
144
  async snapshot(_sandboxId: string): Promise<SandboxSnapshot> {
144
145
  throw new SandboxNotSupportedError(
145
- "snapshot (use Daytona's native snapshot API directly)",
146
+ "snapshot (use Daytona's native snapshot API directly)"
146
147
  );
147
148
  }
148
149
 
149
150
  async restore(_snapshot: SandboxSnapshot): Promise<never> {
150
151
  throw new SandboxNotSupportedError(
151
- "restore (use Daytona's native snapshot API directly)",
152
+ "restore (use Daytona's native snapshot API directly)"
152
153
  );
153
154
  }
154
155
  }
@@ -68,7 +68,7 @@ export interface GoogleGenAIAdapter {
68
68
  * ```
69
69
  */
70
70
  export function createGoogleGenAIAdapter(
71
- config: GoogleGenAIAdapterConfig,
71
+ config: GoogleGenAIAdapterConfig
72
72
  ): GoogleGenAIAdapter {
73
73
  const { redis, client } = config;
74
74
 
@@ -80,29 +80,31 @@ export function createGoogleGenAIAdapter(
80
80
 
81
81
  async appendHumanMessage(
82
82
  threadId: string,
83
- content: string | MessageContent,
83
+ id: string,
84
+ content: string | MessageContent
84
85
  ): Promise<void> {
85
86
  const thread = createGoogleGenAIThreadManager({ redis, threadId });
86
- await thread.appendUserMessage(content);
87
+ await thread.appendUserMessage(id, content);
87
88
  },
88
89
 
89
90
  async appendSystemMessage(
90
91
  threadId: string,
91
- content: string,
92
+ id: string,
93
+ content: string
92
94
  ): Promise<void> {
93
95
  const thread = createGoogleGenAIThreadManager({ redis, threadId });
94
- await thread.appendSystemMessage(content);
96
+ await thread.appendSystemMessage(id, content);
95
97
  },
96
98
 
97
- async appendToolResult(cfg: ToolResultConfig): Promise<void> {
99
+ async appendToolResult(id: string, cfg: ToolResultConfig): Promise<void> {
98
100
  const { threadId, toolCallId, toolName, content } = cfg;
99
101
  const thread = createGoogleGenAIThreadManager({ redis, threadId });
100
- await thread.appendToolResult(toolCallId, toolName, content);
102
+ await thread.appendToolResult(id, toolCallId, toolName, content);
101
103
  },
102
104
 
103
105
  async forkThread(
104
106
  sourceThreadId: string,
105
- targetThreadId: string,
107
+ targetThreadId: string
106
108
  ): Promise<void> {
107
109
  const thread = createGoogleGenAIThreadManager({
108
110
  redis,
@@ -120,7 +122,7 @@ export function createGoogleGenAIAdapter(
120
122
  : ((() => {
121
123
  throw new Error(
122
124
  "No default model provided to createGoogleGenAIAdapter. " +
123
- "Either pass `model` in the config or use `createModelInvoker(model)` instead.",
125
+ "Either pass `model` in the config or use `createModelInvoker(model)` instead."
124
126
  );
125
127
  }) as unknown as ModelInvoker<Content>);
126
128
 
@@ -1,13 +1,10 @@
1
1
  import type Redis from "ioredis";
2
- import type {
3
- GoogleGenAI,
4
- Content,
5
- FunctionDeclaration,
6
- } from "@google/genai";
2
+ import type { GoogleGenAI, Content, FunctionDeclaration } from "@google/genai";
7
3
  import type { SerializableToolDefinition } from "../../../lib/types";
8
4
  import type { AgentResponse } from "../../../lib/model";
9
5
  import type { ModelInvokerConfig } from "../../../lib/model";
10
6
  import { createGoogleGenAIThreadManager } from "./thread-manager";
7
+ import { v4 as uuidv4 } from "uuid";
11
8
 
12
9
  export interface GoogleGenAIModelInvokerConfig {
13
10
  redis: Redis;
@@ -16,7 +13,7 @@ export interface GoogleGenAIModelInvokerConfig {
16
13
  }
17
14
 
18
15
  function toFunctionDeclarations(
19
- tools: SerializableToolDefinition[],
16
+ tools: SerializableToolDefinition[]
20
17
  ): FunctionDeclaration[] {
21
18
  return tools.map((t) => ({
22
19
  name: t.name,
@@ -73,7 +70,7 @@ export function createGoogleGenAIModelInvoker({
73
70
  model,
74
71
  }: GoogleGenAIModelInvokerConfig) {
75
72
  return async function invokeGoogleGenAIModel(
76
- config: ModelInvokerConfig,
73
+ config: ModelInvokerConfig
77
74
  ): Promise<AgentResponse<Content>> {
78
75
  const { threadId, state } = config;
79
76
 
@@ -97,9 +94,7 @@ export function createGoogleGenAIModelInvoker({
97
94
 
98
95
  const functionDeclarations = toFunctionDeclarations(state.tools);
99
96
  const tools =
100
- functionDeclarations.length > 0
101
- ? [{ functionDeclarations }]
102
- : undefined;
97
+ functionDeclarations.length > 0 ? [{ functionDeclarations }] : undefined;
103
98
 
104
99
  const response = await client.models.generateContent({
105
100
  model,
@@ -113,7 +108,7 @@ export function createGoogleGenAIModelInvoker({
113
108
  const responseParts = response.candidates?.[0]?.content?.parts ?? [];
114
109
  const modelContent: Content = { role: "model", parts: responseParts };
115
110
 
116
- await thread.appendModelContent(responseParts);
111
+ await thread.appendModelContent(uuidv4(), responseParts);
117
112
 
118
113
  const functionCalls = response.functionCalls ?? [];
119
114
 
@@ -21,23 +21,30 @@ export interface GoogleGenAIThreadManagerConfig {
21
21
  }
22
22
 
23
23
  /** Thread manager with Google GenAI Content convenience helpers */
24
- export interface GoogleGenAIThreadManager
25
- extends BaseThreadManager<StoredContent> {
26
- createUserContent(content: string | MessageContent): StoredContent;
27
- createSystemContent(content: string): StoredContent;
28
- createModelContent(parts: Part[]): StoredContent;
24
+ export interface GoogleGenAIThreadManager extends BaseThreadManager<StoredContent> {
25
+ createUserContent(
26
+ id: string,
27
+ content: string | MessageContent
28
+ ): StoredContent;
29
+ createSystemContent(id: string, content: string): StoredContent;
30
+ createModelContent(id: string, parts: Part[]): StoredContent;
29
31
  createToolResponseContent(
32
+ id: string,
30
33
  toolCallId: string,
31
34
  toolName: string,
32
- content: ToolMessageContent,
35
+ content: ToolMessageContent
33
36
  ): StoredContent;
34
- appendUserMessage(content: string | MessageContent): Promise<void>;
35
- appendSystemMessage(content: string): Promise<void>;
36
- appendModelContent(parts: Part[]): Promise<void>;
37
+ appendUserMessage(
38
+ id: string,
39
+ content: string | MessageContent
40
+ ): Promise<void>;
41
+ appendSystemMessage(id: string, content: string): Promise<void>;
42
+ appendModelContent(id: string, parts: Part[]): Promise<void>;
37
43
  appendToolResult(
44
+ id: string,
38
45
  toolCallId: string,
39
46
  toolName: string,
40
- content: ToolMessageContent,
47
+ content: ToolMessageContent
41
48
  ): Promise<void>;
42
49
  }
43
50
 
@@ -47,7 +54,7 @@ function storedContentId(msg: StoredContent): string {
47
54
 
48
55
  /** Convert zeitlich MessageContent to Google GenAI Part[] */
49
56
  export function messageContentToParts(
50
- content: string | MessageContent,
57
+ content: string | MessageContent
51
58
  ): Part[] {
52
59
  if (typeof content === "string") {
53
60
  return [{ text: content }];
@@ -65,7 +72,7 @@ export function messageContentToParts(
65
72
 
66
73
  /** Parse ToolMessageContent into a Record suitable for functionResponse */
67
74
  function parseToolResponse(
68
- content: ToolMessageContent,
75
+ content: ToolMessageContent
69
76
  ): Record<string, unknown> {
70
77
  if (typeof content === "string") {
71
78
  try {
@@ -86,7 +93,7 @@ function parseToolResponse(
86
93
  * appending typed Content messages.
87
94
  */
88
95
  export function createGoogleGenAIThreadManager(
89
- config: GoogleGenAIThreadManagerConfig,
96
+ config: GoogleGenAIThreadManagerConfig
90
97
  ): GoogleGenAIThreadManager {
91
98
  const baseConfig: ThreadManagerConfig<StoredContent> = {
92
99
  redis: config.redis,
@@ -98,34 +105,38 @@ export function createGoogleGenAIThreadManager(
98
105
  const base = createThreadManager(baseConfig);
99
106
 
100
107
  const helpers = {
101
- createUserContent(content: string | MessageContent): StoredContent {
108
+ createUserContent(
109
+ id: string,
110
+ content: string | MessageContent
111
+ ): StoredContent {
102
112
  return {
103
- id: crypto.randomUUID(),
113
+ id,
104
114
  content: { role: "user", parts: messageContentToParts(content) },
105
115
  };
106
116
  },
107
117
 
108
- createSystemContent(content: string): StoredContent {
118
+ createSystemContent(id: string, content: string): StoredContent {
109
119
  return {
110
- id: crypto.randomUUID(),
120
+ id,
111
121
  content: { role: "system", parts: [{ text: content }] },
112
122
  };
113
123
  },
114
124
 
115
- createModelContent(parts: Part[]): StoredContent {
125
+ createModelContent(id: string, parts: Part[]): StoredContent {
116
126
  return {
117
- id: crypto.randomUUID(),
127
+ id,
118
128
  content: { role: "model", parts },
119
129
  };
120
130
  },
121
131
 
122
132
  createToolResponseContent(
133
+ id: string,
123
134
  toolCallId: string,
124
135
  toolName: string,
125
- content: ToolMessageContent,
136
+ content: ToolMessageContent
126
137
  ): StoredContent {
127
138
  return {
128
- id: crypto.randomUUID(),
139
+ id,
129
140
  content: {
130
141
  role: "user",
131
142
  parts: [
@@ -141,26 +152,30 @@ export function createGoogleGenAIThreadManager(
141
152
  };
142
153
  },
143
154
 
144
- async appendUserMessage(content: string | MessageContent): Promise<void> {
145
- await base.append([helpers.createUserContent(content)]);
155
+ async appendUserMessage(
156
+ id: string,
157
+ content: string | MessageContent
158
+ ): Promise<void> {
159
+ await base.append([helpers.createUserContent(id, content)]);
146
160
  },
147
161
 
148
- async appendSystemMessage(content: string): Promise<void> {
162
+ async appendSystemMessage(id: string, content: string): Promise<void> {
149
163
  await base.initialize();
150
- await base.append([helpers.createSystemContent(content)]);
164
+ await base.append([helpers.createSystemContent(id, content)]);
151
165
  },
152
166
 
153
- async appendModelContent(parts: Part[]): Promise<void> {
154
- await base.append([helpers.createModelContent(parts)]);
167
+ async appendModelContent(id: string, parts: Part[]): Promise<void> {
168
+ await base.append([helpers.createModelContent(id, parts)]);
155
169
  },
156
170
 
157
171
  async appendToolResult(
172
+ id: string,
158
173
  toolCallId: string,
159
174
  toolName: string,
160
- content: ToolMessageContent,
175
+ content: ToolMessageContent
161
176
  ): Promise<void> {
162
177
  await base.append([
163
- helpers.createToolResponseContent(toolCallId, toolName, content),
178
+ helpers.createToolResponseContent(id, toolCallId, toolName, content),
164
179
  ]);
165
180
  },
166
181
  };
@@ -74,24 +74,26 @@ export function createLangChainAdapter(
74
74
 
75
75
  async appendHumanMessage(
76
76
  threadId: string,
77
+ id: string,
77
78
  content: string | MessageContent
78
79
  ): Promise<void> {
79
80
  const thread = createLangChainThreadManager({ redis, threadId });
80
- await thread.appendHumanMessage(content);
81
+ await thread.appendHumanMessage(id, content);
81
82
  },
82
83
 
83
84
  async appendSystemMessage(
84
85
  threadId: string,
86
+ id: string,
85
87
  content: string
86
88
  ): Promise<void> {
87
89
  const thread = createLangChainThreadManager({ redis, threadId });
88
- await thread.appendSystemMessage(content);
90
+ await thread.appendSystemMessage(id, content);
89
91
  },
90
92
 
91
- async appendToolResult(cfg: ToolResultConfig): Promise<void> {
93
+ async appendToolResult(id: string, cfg: ToolResultConfig): Promise<void> {
92
94
  const { threadId, toolCallId, content } = cfg;
93
95
  const thread = createLangChainThreadManager({ redis, threadId });
94
- await thread.appendToolMessage(content, toolCallId);
96
+ await thread.appendToolMessage(id, content, toolCallId);
95
97
  },
96
98
 
97
99
  async forkThread(
@@ -9,7 +9,6 @@ import {
9
9
  SystemMessage,
10
10
  ToolMessage,
11
11
  } from "@langchain/core/messages";
12
- import { v4 as uuidv4 } from "uuid";
13
12
  import {
14
13
  createThreadManager,
15
14
  type BaseThreadManager,
@@ -30,27 +29,44 @@ export interface LangChainThreadManagerConfig {
30
29
 
31
30
  /** Thread manager with LangChain StoredMessage convenience helpers */
32
31
  export interface LangChainThreadManager extends BaseThreadManager<StoredMessage> {
33
- createHumanMessage(content: string | MessageContent): StoredMessage;
34
- createSystemMessage(content: string): StoredMessage;
32
+ createHumanMessage(
33
+ id: string,
34
+ content: string | MessageContent
35
+ ): StoredMessage;
36
+ createSystemMessage(id: string, content: string): StoredMessage;
35
37
  createAIMessage(
38
+ id: string,
36
39
  content: string | MessageContent,
37
- kwargs?: { header?: string; options?: string[]; multiSelect?: boolean },
40
+ kwargs?: { header?: string; options?: string[]; multiSelect?: boolean }
38
41
  ): StoredMessage;
39
42
  createToolMessage(
43
+ id: string,
40
44
  content: LangChainToolMessageContent,
41
- toolCallId: string,
45
+ toolCallId: string
42
46
  ): StoredMessage;
43
- appendHumanMessage(content: string | MessageContent): Promise<void>;
44
- appendSystemMessage(content: string): Promise<void>;
47
+ appendHumanMessage(
48
+ id: string,
49
+ content: string | MessageContent
50
+ ): Promise<void>;
51
+ appendSystemMessage(id: string, content: string): Promise<void>;
45
52
  appendToolMessage(
53
+ id: string,
46
54
  content: LangChainToolMessageContent,
47
- toolCallId: string,
55
+ toolCallId: string
48
56
  ): Promise<void>;
49
- appendAIMessage(content: string | MessageContent): Promise<void>;
57
+ appendAIMessage(id: string, content: string | MessageContent): Promise<void>;
50
58
  }
51
59
 
52
60
  function storedMessageId(msg: StoredMessage): string {
53
- return msg.data.id ?? "";
61
+ if (msg.type === "tool" && msg.data.tool_call_id) {
62
+ return msg.data.tool_call_id;
63
+ }
64
+
65
+ if (msg.data.id) {
66
+ return msg.data.id;
67
+ }
68
+
69
+ throw new Error("No id found for message");
54
70
  }
55
71
 
56
72
  /**
@@ -59,7 +75,7 @@ function storedMessageId(msg: StoredMessage): string {
59
75
  * appending typed LangChain messages.
60
76
  */
61
77
  export function createLangChainThreadManager(
62
- config: LangChainThreadManagerConfig,
78
+ config: LangChainThreadManagerConfig
63
79
  ): LangChainThreadManager {
64
80
  const baseConfig: ThreadManagerConfig<StoredMessage> = {
65
81
  redis: config.redis,
@@ -71,26 +87,30 @@ export function createLangChainThreadManager(
71
87
  const base = createThreadManager(baseConfig);
72
88
 
73
89
  const helpers = {
74
- createHumanMessage(content: string | MessageContent): StoredMessage {
90
+ createHumanMessage(
91
+ id: string,
92
+ content: string | MessageContent
93
+ ): StoredMessage {
75
94
  return new HumanMessage({
76
- id: uuidv4(),
95
+ id,
77
96
  content: content as string,
78
97
  }).toDict();
79
98
  },
80
99
 
81
- createSystemMessage(content: string): StoredMessage {
100
+ createSystemMessage(id: string, content: string): StoredMessage {
82
101
  return new SystemMessage({
83
- id: uuidv4(),
102
+ id,
84
103
  content: content as string,
85
104
  }).toDict();
86
105
  },
87
106
 
88
107
  createAIMessage(
108
+ id: string,
89
109
  content: string,
90
- kwargs?: { header?: string; options?: string[]; multiSelect?: boolean },
110
+ kwargs?: { header?: string; options?: string[]; multiSelect?: boolean }
91
111
  ): StoredMessage {
92
112
  return new AIMessage({
93
- id: uuidv4(),
113
+ id,
94
114
  content,
95
115
  additional_kwargs: kwargs
96
116
  ? {
@@ -103,36 +123,44 @@ export function createLangChainThreadManager(
103
123
  },
104
124
 
105
125
  createToolMessage(
126
+ id: string,
106
127
  content: LangChainToolMessageContent,
107
- toolCallId: string,
128
+ toolCallId: string
108
129
  ): StoredMessage {
109
130
  return new ToolMessage({
110
- id: uuidv4(),
131
+ id,
111
132
  content: content as MessageContent,
112
133
  tool_call_id: toolCallId,
113
134
  }).toDict();
114
135
  },
115
136
 
116
- async appendHumanMessage(content: string | MessageContent): Promise<void> {
117
- const message = helpers.createHumanMessage(content);
137
+ async appendHumanMessage(
138
+ id: string,
139
+ content: string | MessageContent
140
+ ): Promise<void> {
141
+ const message = helpers.createHumanMessage(id, content);
118
142
  await base.append([message]);
119
143
  },
120
144
 
121
145
  async appendToolMessage(
146
+ id: string,
122
147
  content: LangChainToolMessageContent,
123
- toolCallId: string,
148
+ toolCallId: string
124
149
  ): Promise<void> {
125
- const message = helpers.createToolMessage(content, toolCallId);
150
+ const message = helpers.createToolMessage(id, content, toolCallId);
126
151
  await base.append([message]);
127
152
  },
128
153
 
129
- async appendAIMessage(content: string | MessageContent): Promise<void> {
130
- const message = helpers.createAIMessage(content as string);
154
+ async appendAIMessage(
155
+ id: string,
156
+ content: string | MessageContent
157
+ ): Promise<void> {
158
+ const message = helpers.createAIMessage(id, content as string);
131
159
  await base.append([message]);
132
160
  },
133
161
 
134
- async appendSystemMessage(content: string): Promise<void> {
135
- const message = helpers.createSystemMessage(content);
162
+ async appendSystemMessage(id: string, content: string): Promise<void> {
163
+ const message = helpers.createSystemMessage(id, content);
136
164
  await base.initialize();
137
165
  await base.append([message]);
138
166
  },