@node-llm/orm 0.5.0 → 0.6.0

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/CHANGELOG.md CHANGED
@@ -2,6 +2,22 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [1.12.0] - 2026-02-22 (@node-llm/core)
6
+
7
+ ### Features
8
+
9
+ - **Lazy Agent Evaluation**: Support for defining `instructions` and `tools` as functions that resolve at runtime based on provided `inputs`.
10
+ - **Runtime Agent Inputs**: Pass dynamic context (e.g., user preferences, scope) via `agent.ask(msg, { inputs })` for per-turn configuration overrides.
11
+ - **Agent Telemetry Hooks**: Integrated delegation of life-cycle events (`onToolCallStart`, `onToolCallEnd`, `afterResponse`) from Agents to underlying Chat instances, enabling declarative observability.
12
+
13
+ ## [0.6.0] - 2026-02-22 (@node-llm/orm)
14
+
15
+ ### Features
16
+
17
+ - **Automated Agent Persistence**: `AgentSession` now automatically registers internal hooks to persist tool calls and provider request metrics (tokens, latency) to the database.
18
+ - **Dynamic Metadata Updates**: Added `session.updateMetadata()` for mid-conversation context updates with immediate agent re-resolution.
19
+ - **Proof-of-Concept**: Full integration in HR Chatbot example demonstrating persistent multi-turn agent conversations with a complete audit trail.
20
+
5
21
  ## [1.11.0] - 2026-02-07 (@node-llm/core)
6
22
 
7
23
  ### Features
@@ -38,11 +38,11 @@ export interface TableNames {
38
38
  toolCall?: string;
39
39
  request?: string;
40
40
  }
41
- type AgentClass<T extends Agent = Agent> = (new (overrides?: Partial<AgentConfig & ChatOptions>) => T) & {
41
+ type AgentClass<T extends Agent<any, any> = Agent<any, any>> = (new (overrides?: Partial<AgentConfig<any> & ChatOptions>) => T) & {
42
42
  name: string;
43
43
  model?: string;
44
- instructions?: string;
45
- tools?: unknown[];
44
+ instructions?: unknown;
45
+ tools?: unknown;
46
46
  };
47
47
  /**
48
48
  * AgentSession - Wraps an Agent instance with persistence capabilities.
@@ -50,6 +50,7 @@ type AgentClass<T extends Agent = Agent> = (new (overrides?: Partial<AgentConfig
50
50
  * Follows "Code Wins" sovereignty:
51
51
  * - Model, Tools, Instructions come from the Agent class (code)
52
52
  * - Message history comes from the database
53
+ * - Metadata from DB is injected as 'inputs' for dynamic resolution
53
54
  *
54
55
  * @example
55
56
  * ```typescript
@@ -65,7 +66,7 @@ type AgentClass<T extends Agent = Agent> = (new (overrides?: Partial<AgentConfig
65
66
  * const result = await session.ask("Hello");
66
67
  * ```
67
68
  */
68
- export declare class AgentSession<T extends Agent = Agent> {
69
+ export declare class AgentSession<I extends Record<string, any> = Record<string, any>, T extends Agent<I, any> = Agent<I, any>> {
69
70
  private prisma;
70
71
  private llm;
71
72
  private AgentClass;
@@ -83,7 +84,7 @@ export declare class AgentSession<T extends Agent = Agent> {
83
84
  /** Underlying chat ID */
84
85
  get chatId(): string;
85
86
  /** Session metadata */
86
- get metadata(): Record<string, unknown> | null | undefined;
87
+ get metadata(): I | null | undefined;
87
88
  /** Agent class name */
88
89
  get agentClass(): string;
89
90
  /** Model ID used by the agent */
@@ -96,14 +97,36 @@ export declare class AgentSession<T extends Agent = Agent> {
96
97
  * Helper to get a typed Prisma model by its dynamic name.
97
98
  */
98
99
  private getModel;
100
+ /**
101
+ * Register persistence hooks on the agent.
102
+ */
103
+ private registerHooks;
99
104
  /**
100
105
  * Send a message and persist the conversation.
101
106
  */
102
- ask(input: string, options?: AskOptions): Promise<MessageRecord>;
107
+ ask(message: string, options?: AskOptions & {
108
+ inputs?: I;
109
+ }): Promise<MessageRecord>;
103
110
  /**
104
111
  * Stream a response and persist the conversation.
105
112
  */
106
- askStream(input: string, options?: AskOptions): AsyncGenerator<ChatChunk, MessageRecord, undefined>;
113
+ askStream(message: string, options?: AskOptions & {
114
+ inputs?: I;
115
+ }): AsyncGenerator<ChatChunk, MessageRecord, undefined>;
116
+ /**
117
+ * Returns a usage summary for this chat session.
118
+ */
119
+ stats(): Promise<Usage>;
120
+ /**
121
+ * Add a tool to the session (turn-level).
122
+ */
123
+ withTool(tool: any): this;
124
+ /**
125
+ * Add instructions to the session (turn-level).
126
+ */
127
+ withInstructions(instructions: string, options?: {
128
+ replace?: boolean;
129
+ }): this;
107
130
  /**
108
131
  * Returns the current full message history for this session.
109
132
  */
@@ -112,19 +135,27 @@ export declare class AgentSession<T extends Agent = Agent> {
112
135
  * Delete the entire session and its history.
113
136
  */
114
137
  delete(): Promise<void>;
138
+ /**
139
+ * Update session metadata and re-resolve agent configuration.
140
+ */
141
+ updateMetadata(metadata: Partial<I>): Promise<void>;
115
142
  }
116
143
  /**
117
144
  * Options for creating a new agent session.
118
145
  */
119
- export interface CreateAgentSessionOptions {
120
- metadata?: Record<string, unknown>;
146
+ export interface CreateAgentSessionOptions<I = any> {
147
+ metadata?: I;
121
148
  tableNames?: TableNames;
122
149
  debug?: boolean;
150
+ model?: string;
151
+ provider?: string;
152
+ instructions?: string;
153
+ maxToolCalls?: number;
123
154
  }
124
155
  /**
125
156
  * Creates a new agent session and its persistent chat record.
126
157
  */
127
- export declare function createAgentSession<T extends Agent>(prisma: any, llm: NodeLLMCore, AgentClass: AgentClass<T>, options?: CreateAgentSessionOptions): Promise<AgentSession<T>>;
158
+ export declare function createAgentSession<I extends Record<string, any>, T extends Agent<I, any>>(prisma: any, llm: NodeLLMCore, AgentClass: AgentClass<T>, options?: CreateAgentSessionOptions<I>): Promise<AgentSession<I, T>>;
128
159
  /**
129
160
  * Options for loading an existing agent session.
130
161
  */
@@ -135,6 +166,6 @@ export interface LoadAgentSessionOptions {
135
166
  /**
136
167
  * Loads an existing agent session and re-instantiates the agent with history.
137
168
  */
138
- export declare function loadAgentSession<T extends Agent>(prisma: any, llm: NodeLLMCore, AgentClass: AgentClass<T>, sessionId: string, options?: LoadAgentSessionOptions): Promise<AgentSession<T> | null>;
169
+ export declare function loadAgentSession<I extends Record<string, any>, T extends Agent<I, any>>(prisma: any, llm: NodeLLMCore, AgentClass: AgentClass<T>, sessionId: string, options?: LoadAgentSessionOptions): Promise<AgentSession<I, T> | null>;
139
170
  export {};
140
171
  //# sourceMappingURL=AgentSession.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"AgentSession.d.ts","sourceRoot":"","sources":["../../../src/adapters/prisma/AgentSession.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"AgentSession.d.ts","sourceRoot":"","sources":["../../../src/adapters/prisma/AgentSession.ts"],"names":[],"mappings":"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"}
@@ -4,6 +4,7 @@
4
4
  * Follows "Code Wins" sovereignty:
5
5
  * - Model, Tools, Instructions come from the Agent class (code)
6
6
  * - Message history comes from the database
7
+ * - Metadata from DB is injected as 'inputs' for dynamic resolution
7
8
  *
8
9
  * @example
9
10
  * ```typescript
@@ -29,7 +30,8 @@ export class AgentSession {
29
30
  tableNames;
30
31
  debug;
31
32
  constructor(prisma, llm, AgentClass, record, tableNames, agent = new AgentClass({
32
- llm
33
+ llm,
34
+ inputs: record.metadata
33
35
  }), debug = false) {
34
36
  this.prisma = prisma;
35
37
  this.llm = llm;
@@ -44,6 +46,7 @@ export class AgentSession {
44
46
  toolCall: tableNames?.toolCall || "llmToolCall",
45
47
  request: tableNames?.request || "llmRequest"
46
48
  };
49
+ this.registerHooks();
47
50
  }
48
51
  log(...args) {
49
52
  if (this.debug) {
@@ -88,14 +91,69 @@ export class AgentSession {
88
91
  getModel(name) {
89
92
  return getTable(this.prisma, name);
90
93
  }
94
+ /**
95
+ * Register persistence hooks on the agent.
96
+ */
97
+ registerHooks() {
98
+ this.agent.onToolCallStart(async (toolCall) => {
99
+ if (!this.currentMessageId)
100
+ return;
101
+ const model = this.getModel(this.tableNames.toolCall);
102
+ await model.create({
103
+ data: {
104
+ messageId: this.currentMessageId,
105
+ toolCallId: toolCall.id,
106
+ name: toolCall.function.name,
107
+ arguments: toolCall.function.arguments
108
+ }
109
+ });
110
+ });
111
+ this.agent.onToolCallEnd(async (toolCall, result) => {
112
+ if (!this.currentMessageId)
113
+ return;
114
+ const model = this.getModel(this.tableNames.toolCall);
115
+ try {
116
+ await model.update({
117
+ where: {
118
+ messageId_toolCallId: {
119
+ messageId: this.currentMessageId,
120
+ toolCallId: toolCall.id
121
+ }
122
+ },
123
+ data: {
124
+ result: typeof result === "string" ? result : JSON.stringify(result)
125
+ }
126
+ });
127
+ }
128
+ catch (e) {
129
+ this.log(`Failed to update tool call result: ${e}`);
130
+ }
131
+ });
132
+ this.agent.afterResponse(async (response) => {
133
+ const model = this.getModel(this.tableNames.request);
134
+ await model.create({
135
+ data: {
136
+ chatId: this.chatId,
137
+ messageId: this.currentMessageId,
138
+ provider: response.provider || "unknown",
139
+ model: response.model || "unknown",
140
+ statusCode: 200,
141
+ duration: 0,
142
+ inputTokens: response.usage?.input_tokens || 0,
143
+ outputTokens: response.usage?.output_tokens || 0,
144
+ cost: response.usage?.cost || 0
145
+ }
146
+ });
147
+ });
148
+ }
91
149
  /**
92
150
  * Send a message and persist the conversation.
93
151
  */
94
- async ask(input, options = {}) {
152
+ async ask(message, options = {}) {
95
153
  const model = this.getModel(this.tableNames.message);
96
154
  // Persist user message
97
155
  await model.create({
98
- data: { chatId: this.chatId, role: "user", content: input }
156
+ data: { chatId: this.chatId, role: "user", content: message }
99
157
  });
100
158
  // Create placeholder for assistant message
101
159
  const assistantMessage = await model.create({
@@ -103,8 +161,10 @@ export class AgentSession {
103
161
  });
104
162
  this.currentMessageId = assistantMessage.id;
105
163
  try {
164
+ // Merge turn-level inputs with session metadata
165
+ const inputs = { ...this.record.metadata, ...options.inputs };
106
166
  // Get response from agent (uses code-defined config + injected history)
107
- const response = await this.agent.ask(input, options);
167
+ const response = await this.agent.ask(message, { ...options, inputs });
108
168
  // Update assistant message with response
109
169
  return await model.update({
110
170
  where: { id: assistantMessage.id },
@@ -130,11 +190,11 @@ export class AgentSession {
130
190
  /**
131
191
  * Stream a response and persist the conversation.
132
192
  */
133
- async *askStream(input, options = {}) {
193
+ async *askStream(message, options = {}) {
134
194
  const model = this.getModel(this.tableNames.message);
135
195
  // Persist user message
136
196
  await model.create({
137
- data: { chatId: this.chatId, role: "user", content: input }
197
+ data: { chatId: this.chatId, role: "user", content: message }
138
198
  });
139
199
  // Create placeholder for assistant message
140
200
  const assistantMessage = await model.create({
@@ -142,7 +202,9 @@ export class AgentSession {
142
202
  });
143
203
  this.currentMessageId = assistantMessage.id;
144
204
  try {
145
- const stream = this.agent.stream(input, options);
205
+ // Merge turn-level inputs with session metadata
206
+ const inputs = { ...this.record.metadata, ...options.inputs };
207
+ const stream = this.agent.stream(message, { ...options, inputs });
146
208
  let fullContent = "";
147
209
  let lastChunk = null;
148
210
  for await (const chunk of stream) {
@@ -170,6 +232,40 @@ export class AgentSession {
170
232
  throw error;
171
233
  }
172
234
  }
235
+ /**
236
+ * Returns a usage summary for this chat session.
237
+ */
238
+ async stats() {
239
+ const requestModel = getTable(this.prisma, this.tableNames.request);
240
+ const aggregate = await requestModel.aggregate({
241
+ where: { chatId: this.chatId },
242
+ _sum: {
243
+ inputTokens: true,
244
+ outputTokens: true,
245
+ cost: true
246
+ }
247
+ });
248
+ return {
249
+ input_tokens: Number(aggregate._sum.inputTokens || 0),
250
+ output_tokens: Number(aggregate._sum.outputTokens || 0),
251
+ total_tokens: Number((aggregate._sum.inputTokens || 0) + (aggregate._sum.outputTokens || 0)),
252
+ cost: Number(aggregate._sum.cost || 0)
253
+ };
254
+ }
255
+ /**
256
+ * Add a tool to the session (turn-level).
257
+ */
258
+ withTool(tool) {
259
+ this.agent.use(tool);
260
+ return this;
261
+ }
262
+ /**
263
+ * Add instructions to the session (turn-level).
264
+ */
265
+ withInstructions(instructions, options) {
266
+ this.agent.withInstructions(instructions, options);
267
+ return this;
268
+ }
173
269
  /**
174
270
  * Returns the current full message history for this session.
175
271
  */
@@ -188,6 +284,22 @@ export class AgentSession {
188
284
  await chatTable.delete({ where: { id: this.chatId } });
189
285
  // AgentSession record is deleted via Cascade from LlmChat
190
286
  }
287
+ /**
288
+ * Update session metadata and re-resolve agent configuration.
289
+ */
290
+ async updateMetadata(metadata) {
291
+ const sessionTable = this.getModel(this.tableNames.agentSession);
292
+ const newMetadata = { ...this.record.metadata, ...metadata };
293
+ await sessionTable.update({
294
+ where: { id: this.id },
295
+ data: { metadata: newMetadata }
296
+ });
297
+ this.record.metadata = newMetadata;
298
+ // Apply changes to the underlying agent immediately
299
+ // resolveLazyConfig is private, so we need a cast or make it protected.
300
+ // Given we are in the same package, we can cast.
301
+ this.agent.resolveLazyConfig(newMetadata);
302
+ }
191
303
  }
192
304
  /**
193
305
  * Creates a new agent session and its persistent chat record.
@@ -205,9 +317,10 @@ export async function createAgentSession(prisma, llm, AgentClass, options = {})
205
317
  const chatTable = getTable(prisma, tableNames.chat);
206
318
  const chatRecord = (await chatTable.create({
207
319
  data: {
208
- model: AgentClass.model || null,
209
- provider: null,
210
- instructions: AgentClass.instructions || null,
320
+ model: options.model || AgentClass.model || null,
321
+ provider: options.provider || null,
322
+ instructions: options.instructions ||
323
+ (typeof AgentClass.instructions === "string" ? AgentClass.instructions : null),
211
324
  metadata: null // Runtime metadata goes in Chat, session context in AgentSession
212
325
  }
213
326
  }));
@@ -220,7 +333,16 @@ export async function createAgentSession(prisma, llm, AgentClass, options = {})
220
333
  metadata: options.metadata || null
221
334
  }
222
335
  }));
223
- return new AgentSession(prisma, llm, AgentClass, sessionRecord, options.tableNames, undefined, options.debug);
336
+ // 3. Instantiate Agent with overrides
337
+ const agent = new AgentClass({
338
+ llm,
339
+ inputs: sessionRecord.metadata,
340
+ model: options.model,
341
+ provider: options.provider,
342
+ instructions: options.instructions,
343
+ maxToolCalls: options.maxToolCalls
344
+ });
345
+ return new AgentSession(prisma, llm, AgentClass, sessionRecord, options.tableNames, agent, options.debug);
224
346
  }
225
347
  /**
226
348
  * Loads an existing agent session and re-instantiates the agent with history.
@@ -257,11 +379,13 @@ export async function loadAgentSession(prisma, llm, AgentClass, sessionId, optio
257
379
  role: m.role,
258
380
  content: m.content || ""
259
381
  }));
260
- // 4. Instantiate agent with injected history and LLM
382
+ // 4. Instantiate agent with injected history, LLM, AND metadata (as inputs)
261
383
  // "Code Wins" - model, tools, instructions come from AgentClass
384
+ // Metadata from DB handles the lazy resolution of behavior
262
385
  const agent = new AgentClass({
263
386
  llm,
264
- messages: history
387
+ messages: history,
388
+ inputs: sessionRecord.metadata
265
389
  });
266
390
  return new AgentSession(prisma, llm, AgentClass, sessionRecord, options.tableNames, agent, options.debug);
267
391
  }
@@ -40,12 +40,12 @@ export declare class Chat extends BaseChat {
40
40
  /**
41
41
  * Send a message and persist the conversation.
42
42
  */
43
- ask(input: string, options?: AskOptions): Promise<MessageRecord>;
43
+ ask(message: string, options?: AskOptions): Promise<MessageRecord>;
44
44
  /**
45
45
  * Stream a response and persist the conversation.
46
46
  * Yields ChatChunk objects for full visibility of thinking, content, and tools.
47
47
  */
48
- askStream(input: string, options?: AskOptions): AsyncGenerator<ChatChunk, MessageRecord, undefined>;
48
+ askStream(message: string, options?: AskOptions): AsyncGenerator<ChatChunk, MessageRecord, undefined>;
49
49
  /**
50
50
  * Get all messages for this chat.
51
51
  */
@@ -1 +1 @@
1
- {"version":3,"file":"Chat.d.ts","sourceRoot":"","sources":["../../../src/adapters/prisma/Chat.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAChF,OAAO,EAAE,QAAQ,EAAE,KAAK,UAAU,EAAE,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhF,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,WAAW,EAAE,CAAC;AAE7C,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,qBAAa,IAAK,SAAQ,QAAQ;IAK9B,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,GAAG;IALb,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,iBAAiB,CAAoD;gBAGnE,MAAM,EAAE,YAAY,EACpB,GAAG,EAAE,WAAW,EACxB,MAAM,EAAE,UAAU,EAClB,OAAO,GAAE,WAAgB,EACzB,UAAU,GAAE,UAAe;IAgB7B;;OAEG;YACW,eAAe;IAqH7B;;OAEG;IACG,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,UAAe,GAAG,OAAO,CAAC,aAAa,CAAC;IA8C1E;;;OAGG;IACI,SAAS,CACd,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,UAAe,GACvB,cAAc,CAAC,SAAS,EAAE,aAAa,EAAE,SAAS,CAAC;IAmEtD;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;IAQ1C;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,KAAK,CAAC;CAkB9B;AAuBD;;GAEG;AACH,wBAAsB,UAAU,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACtD,MAAM,EAAE,GAAG,EACX,GAAG,EAAE,WAAW,EAChB,OAAO,GAAE,WAAW,GAAG;IAAE,UAAU,CAAC,EAAE,UAAU,CAAA;CAAE,GAAG,CAAa,GACjE,OAAO,CAAC,IAAI,CAAC,CA2Cf;AAED;;GAEG;AACH,wBAAsB,QAAQ,CAC5B,MAAM,EAAE,GAAG,EACX,GAAG,EAAE,WAAW,EAChB,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,WAAW,GAAG;IAAE,UAAU,CAAC,EAAE,UAAU,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAO,GACvE,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAiBtB"}
1
+ {"version":3,"file":"Chat.d.ts","sourceRoot":"","sources":["../../../src/adapters/prisma/Chat.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAChF,OAAO,EAAE,QAAQ,EAAE,KAAK,UAAU,EAAE,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhF,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,WAAW,EAAE,CAAC;AAE7C,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,qBAAa,IAAK,SAAQ,QAAQ;IAK9B,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,GAAG;IALb,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,iBAAiB,CAAoD;gBAGnE,MAAM,EAAE,YAAY,EACpB,GAAG,EAAE,WAAW,EACxB,MAAM,EAAE,UAAU,EAClB,OAAO,GAAE,WAAgB,EACzB,UAAU,GAAE,UAAe;IAgB7B;;OAEG;YACW,eAAe;IAqH7B;;OAEG;IACG,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,UAAe,GAAG,OAAO,CAAC,aAAa,CAAC;IA8C5E;;;OAGG;IACI,SAAS,CACd,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,UAAe,GACvB,cAAc,CAAC,SAAS,EAAE,aAAa,EAAE,SAAS,CAAC;IAmEtD;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;IAQ1C;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,KAAK,CAAC;CAkB9B;AAuBD;;GAEG;AACH,wBAAsB,UAAU,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACtD,MAAM,EAAE,GAAG,EACX,GAAG,EAAE,WAAW,EAChB,OAAO,GAAE,WAAW,GAAG;IAAE,UAAU,CAAC,EAAE,UAAU,CAAA;CAAE,GAAG,CAAa,GACjE,OAAO,CAAC,IAAI,CAAC,CA2Cf;AAED;;GAEG;AACH,wBAAsB,QAAQ,CAC5B,MAAM,EAAE,GAAG,EACX,GAAG,EAAE,WAAW,EAChB,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,WAAW,GAAG;IAAE,UAAU,CAAC,EAAE,UAAU,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAO,GACvE,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAiBtB"}
@@ -131,10 +131,10 @@ export class Chat extends BaseChat {
131
131
  /**
132
132
  * Send a message and persist the conversation.
133
133
  */
134
- async ask(input, options = {}) {
134
+ async ask(message, options = {}) {
135
135
  const messageModel = this.tables.message;
136
136
  const userMessage = await this.prisma[messageModel].create({
137
- data: { chatId: this.id, role: "user", content: input }
137
+ data: { chatId: this.id, role: "user", content: message }
138
138
  });
139
139
  const assistantMessage = await this.prisma[messageModel].create({
140
140
  data: { chatId: this.id, role: "assistant", content: null }
@@ -149,7 +149,7 @@ export class Chat extends BaseChat {
149
149
  content: m.content || ""
150
150
  }));
151
151
  const coreChat = await this.prepareCoreChat(history, assistantMessage.id);
152
- const response = await coreChat.ask(input, options);
152
+ const response = await coreChat.ask(message, options);
153
153
  return await this.prisma[messageModel].update({
154
154
  where: { id: assistantMessage.id },
155
155
  data: {
@@ -176,10 +176,10 @@ export class Chat extends BaseChat {
176
176
  * Stream a response and persist the conversation.
177
177
  * Yields ChatChunk objects for full visibility of thinking, content, and tools.
178
178
  */
179
- async *askStream(input, options = {}) {
179
+ async *askStream(message, options = {}) {
180
180
  const messageModel = this.tables.message;
181
181
  const userMessage = await this.prisma[messageModel].create({
182
- data: { chatId: this.id, role: "user", content: input }
182
+ data: { chatId: this.id, role: "user", content: message }
183
183
  });
184
184
  const assistantMessage = await this.prisma[messageModel].create({
185
185
  data: { chatId: this.id, role: "assistant", content: null }
@@ -194,7 +194,7 @@ export class Chat extends BaseChat {
194
194
  content: m.content || ""
195
195
  }));
196
196
  const coreChat = await this.prepareCoreChat(history, assistantMessage.id);
197
- const stream = coreChat.stream(input, options);
197
+ const stream = coreChat.stream(message, options);
198
198
  let fullContent = "";
199
199
  let metadata = {};
200
200
  for await (const chunk of stream) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@node-llm/orm",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "Database persistence layer for NodeLLM - Chat, Message, and ToolCall tracking with streaming support",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -56,7 +56,7 @@
56
56
  "author": "NodeLLM Contributors",
57
57
  "license": "MIT",
58
58
  "peerDependencies": {
59
- "@node-llm/core": "^1.10.0",
59
+ "@node-llm/core": "^1.12.0",
60
60
  "@prisma/client": "^5.0.0"
61
61
  },
62
62
  "devDependencies": {