@node-llm/orm 0.5.0 → 0.7.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 +72 -0
- package/dist/adapters/prisma/AgentSession.d.ts +42 -11
- package/dist/adapters/prisma/AgentSession.d.ts.map +1 -1
- package/dist/adapters/prisma/AgentSession.js +139 -13
- package/dist/adapters/prisma/Chat.d.ts +2 -2
- package/dist/adapters/prisma/Chat.d.ts.map +1 -1
- package/dist/adapters/prisma/Chat.js +6 -6
- package/package.json +2 -2
- package/src/adapters/prisma/AgentSession.ts +178 -32
- package/src/adapters/prisma/Chat.ts +6 -6
- package/test/AgentSession.test.ts +131 -3
- package/test/CodeWins.test.ts +1 -0
- package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,78 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [1.15.0] - 2026-03-28 (@node-llm/core)
|
|
6
|
+
|
|
7
|
+
### Features
|
|
8
|
+
|
|
9
|
+
- **Schema Self-Correction Middleware**: Automated detection and repair of LLM validation failures.
|
|
10
|
+
- Intercepts Zod validation errors in structured outputs and provides specific re-prompt feedback.
|
|
11
|
+
- Integrated with Agent tool-calling to automatically recover from malformed tool arguments.
|
|
12
|
+
- Configurable `maxRetries` (default: 2) for multi-round correction loops.
|
|
13
|
+
- **Middleware Lifecycle Directives**: Core middleware system now supports `RETRY`, `REPLACE`, `STOP`, and `CONTINUE` directives for fine-grained execution flow control.
|
|
14
|
+
- **Agent Middleware DSL**: Declarative support for middlewares in `Agent` classes and `defineAgent()`.
|
|
15
|
+
|
|
16
|
+
### Improvements
|
|
17
|
+
|
|
18
|
+
- **Production-Grade Type Safety**: Hardened `Agent.ts` and `Chat.ts` types to remove usage of `any`, improving reliability and developer ergonomics.
|
|
19
|
+
|
|
20
|
+
## [0.7.0] - 2026-03-28 (@node-llm/orm)
|
|
21
|
+
|
|
22
|
+
### Features
|
|
23
|
+
|
|
24
|
+
- **Enhanced Tool Persistence**: Correctly capture schema-validated tool arguments in `AgentSession` hooks.
|
|
25
|
+
- **Improved Type Safety**: Resolved generic constraint issues in `createAgentSession` to better support user-defined agent interfaces.
|
|
26
|
+
|
|
27
|
+
## [1.14.1] - 2026-03-14 (@node-llm/core)
|
|
28
|
+
|
|
29
|
+
### Bug Fixes
|
|
30
|
+
|
|
31
|
+
- **Config Propagation**: Fixed `requestTimeout`, `maxTokens`, and `maxToolCalls` not being passed from `createLLM()` config to `Chat` instances (fixes #47).
|
|
32
|
+
- **Node.js 22+ Compatibility**: Changed JSON import syntax from `assert` to `with` for import attributes (requires Node.js 20.10+).
|
|
33
|
+
|
|
34
|
+
### Security
|
|
35
|
+
|
|
36
|
+
- Updated `rollup` to 4.59.0 to fix CVE path traversal vulnerability.
|
|
37
|
+
- Updated `undici` to 7.24.1 to fix CVE-2026-1526 WebSocket DoS vulnerability.
|
|
38
|
+
- Updated `flatted` to 3.4.1 to fix CVE DoS vulnerability.
|
|
39
|
+
|
|
40
|
+
## [1.14.0] - 2026-03-03 (@node-llm/core)
|
|
41
|
+
|
|
42
|
+
### Features
|
|
43
|
+
|
|
44
|
+
- **Mistral Provider**: Full integration with Mistral AI API.
|
|
45
|
+
- Chat completions with streaming support.
|
|
46
|
+
- Magistral reasoning models with thinking/reasoning extraction.
|
|
47
|
+
- Tool calling and structured output with JSON schema.
|
|
48
|
+
- Embeddings via `mistral-embed` model.
|
|
49
|
+
- Vision support with Pixtral models.
|
|
50
|
+
- Audio transcription.
|
|
51
|
+
- Content moderation.
|
|
52
|
+
- Model discovery.
|
|
53
|
+
- **Unified API**: Mistral follows the same provider-agnostic interface as OpenAI, Anthropic, Gemini, etc.
|
|
54
|
+
|
|
55
|
+
### Documentation
|
|
56
|
+
|
|
57
|
+
- Added Mistral provider guide with configuration and examples.
|
|
58
|
+
- Updated all feature documentation to include Mistral support.
|
|
59
|
+
- Added 10 Mistral example scripts covering chat, streaming, reasoning, tools, structured output, embeddings, vision, transcription, moderation, and model discovery.
|
|
60
|
+
|
|
61
|
+
## [1.12.0] - 2026-02-22 (@node-llm/core)
|
|
62
|
+
|
|
63
|
+
### Features
|
|
64
|
+
|
|
65
|
+
- **Lazy Agent Evaluation**: Support for defining `instructions` and `tools` as functions that resolve at runtime based on provided `inputs`.
|
|
66
|
+
- **Runtime Agent Inputs**: Pass dynamic context (e.g., user preferences, scope) via `agent.ask(msg, { inputs })` for per-turn configuration overrides.
|
|
67
|
+
- **Agent Telemetry Hooks**: Integrated delegation of life-cycle events (`onToolCallStart`, `onToolCallEnd`, `afterResponse`) from Agents to underlying Chat instances, enabling declarative observability.
|
|
68
|
+
|
|
69
|
+
## [0.6.0] - 2026-02-22 (@node-llm/orm)
|
|
70
|
+
|
|
71
|
+
### Features
|
|
72
|
+
|
|
73
|
+
- **Automated Agent Persistence**: `AgentSession` now automatically registers internal hooks to persist tool calls and provider request metrics (tokens, latency) to the database.
|
|
74
|
+
- **Dynamic Metadata Updates**: Added `session.updateMetadata()` for mid-conversation context updates with immediate agent re-resolution.
|
|
75
|
+
- **Proof-of-Concept**: Full integration in HR Chatbot example demonstrating persistent multi-turn agent conversations with a complete audit trail.
|
|
76
|
+
|
|
5
77
|
## [1.11.0] - 2026-02-07 (@node-llm/core)
|
|
6
78
|
|
|
7
79
|
### 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
|
|
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?:
|
|
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():
|
|
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(
|
|
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(
|
|
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?:
|
|
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
|
|
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
|
|
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":"AAAA,OAAO,EACL,WAAW,EACX,UAAU,EACV,WAAW,EACX,KAAK,EACL,WAAW,EACX,OAAO,EACP,SAAS,EACT,KAAK,
|
|
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,71 @@ 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 (tc) => {
|
|
99
|
+
const toolCall = tc;
|
|
100
|
+
if (!this.currentMessageId)
|
|
101
|
+
return;
|
|
102
|
+
const model = this.getModel(this.tableNames.toolCall);
|
|
103
|
+
await model.create({
|
|
104
|
+
data: {
|
|
105
|
+
messageId: this.currentMessageId,
|
|
106
|
+
toolCallId: toolCall.id,
|
|
107
|
+
name: toolCall.function.name,
|
|
108
|
+
arguments: toolCall.function.arguments
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
this.agent.onToolCallEnd(async (tc, result) => {
|
|
113
|
+
const toolCall = tc;
|
|
114
|
+
if (!this.currentMessageId)
|
|
115
|
+
return;
|
|
116
|
+
const model = this.getModel(this.tableNames.toolCall);
|
|
117
|
+
try {
|
|
118
|
+
await model.update({
|
|
119
|
+
where: {
|
|
120
|
+
messageId_toolCallId: {
|
|
121
|
+
messageId: this.currentMessageId,
|
|
122
|
+
toolCallId: toolCall.id
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
data: {
|
|
126
|
+
result: typeof result === "string" ? result : JSON.stringify(result)
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
catch (e) {
|
|
131
|
+
this.log(`Failed to update tool call result: ${e}`);
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
this.agent.afterResponse(async (response) => {
|
|
135
|
+
const model = this.getModel(this.tableNames.request);
|
|
136
|
+
await model.create({
|
|
137
|
+
data: {
|
|
138
|
+
chatId: this.chatId,
|
|
139
|
+
messageId: this.currentMessageId,
|
|
140
|
+
provider: response.provider || "unknown",
|
|
141
|
+
model: response.model || "unknown",
|
|
142
|
+
statusCode: 200,
|
|
143
|
+
duration: 0,
|
|
144
|
+
inputTokens: response.usage?.input_tokens || 0,
|
|
145
|
+
outputTokens: response.usage?.output_tokens || 0,
|
|
146
|
+
cost: response.usage?.cost || 0
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
}
|
|
91
151
|
/**
|
|
92
152
|
* Send a message and persist the conversation.
|
|
93
153
|
*/
|
|
94
|
-
async ask(
|
|
154
|
+
async ask(message, options = {}) {
|
|
95
155
|
const model = this.getModel(this.tableNames.message);
|
|
96
156
|
// Persist user message
|
|
97
157
|
await model.create({
|
|
98
|
-
data: { chatId: this.chatId, role: "user", content:
|
|
158
|
+
data: { chatId: this.chatId, role: "user", content: message }
|
|
99
159
|
});
|
|
100
160
|
// Create placeholder for assistant message
|
|
101
161
|
const assistantMessage = await model.create({
|
|
@@ -103,8 +163,10 @@ export class AgentSession {
|
|
|
103
163
|
});
|
|
104
164
|
this.currentMessageId = assistantMessage.id;
|
|
105
165
|
try {
|
|
166
|
+
// Merge turn-level inputs with session metadata
|
|
167
|
+
const inputs = { ...this.record.metadata, ...options.inputs };
|
|
106
168
|
// Get response from agent (uses code-defined config + injected history)
|
|
107
|
-
const response = await this.agent.ask(
|
|
169
|
+
const response = await this.agent.ask(message, { ...options, inputs });
|
|
108
170
|
// Update assistant message with response
|
|
109
171
|
return await model.update({
|
|
110
172
|
where: { id: assistantMessage.id },
|
|
@@ -130,11 +192,11 @@ export class AgentSession {
|
|
|
130
192
|
/**
|
|
131
193
|
* Stream a response and persist the conversation.
|
|
132
194
|
*/
|
|
133
|
-
async *askStream(
|
|
195
|
+
async *askStream(message, options = {}) {
|
|
134
196
|
const model = this.getModel(this.tableNames.message);
|
|
135
197
|
// Persist user message
|
|
136
198
|
await model.create({
|
|
137
|
-
data: { chatId: this.chatId, role: "user", content:
|
|
199
|
+
data: { chatId: this.chatId, role: "user", content: message }
|
|
138
200
|
});
|
|
139
201
|
// Create placeholder for assistant message
|
|
140
202
|
const assistantMessage = await model.create({
|
|
@@ -142,7 +204,9 @@ export class AgentSession {
|
|
|
142
204
|
});
|
|
143
205
|
this.currentMessageId = assistantMessage.id;
|
|
144
206
|
try {
|
|
145
|
-
|
|
207
|
+
// Merge turn-level inputs with session metadata
|
|
208
|
+
const inputs = { ...this.record.metadata, ...options.inputs };
|
|
209
|
+
const stream = this.agent.stream(message, { ...options, inputs });
|
|
146
210
|
let fullContent = "";
|
|
147
211
|
let lastChunk = null;
|
|
148
212
|
for await (const chunk of stream) {
|
|
@@ -170,6 +234,40 @@ export class AgentSession {
|
|
|
170
234
|
throw error;
|
|
171
235
|
}
|
|
172
236
|
}
|
|
237
|
+
/**
|
|
238
|
+
* Returns a usage summary for this chat session.
|
|
239
|
+
*/
|
|
240
|
+
async stats() {
|
|
241
|
+
const requestModel = getTable(this.prisma, this.tableNames.request);
|
|
242
|
+
const aggregate = await requestModel.aggregate({
|
|
243
|
+
where: { chatId: this.chatId },
|
|
244
|
+
_sum: {
|
|
245
|
+
inputTokens: true,
|
|
246
|
+
outputTokens: true,
|
|
247
|
+
cost: true
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
return {
|
|
251
|
+
input_tokens: Number(aggregate._sum.inputTokens || 0),
|
|
252
|
+
output_tokens: Number(aggregate._sum.outputTokens || 0),
|
|
253
|
+
total_tokens: Number((aggregate._sum.inputTokens || 0) + (aggregate._sum.outputTokens || 0)),
|
|
254
|
+
cost: Number(aggregate._sum.cost || 0)
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Add a tool to the session (turn-level).
|
|
259
|
+
*/
|
|
260
|
+
withTool(tool) {
|
|
261
|
+
this.agent.use(tool);
|
|
262
|
+
return this;
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Add instructions to the session (turn-level).
|
|
266
|
+
*/
|
|
267
|
+
withInstructions(instructions, options) {
|
|
268
|
+
this.agent.withInstructions(instructions, options);
|
|
269
|
+
return this;
|
|
270
|
+
}
|
|
173
271
|
/**
|
|
174
272
|
* Returns the current full message history for this session.
|
|
175
273
|
*/
|
|
@@ -188,6 +286,22 @@ export class AgentSession {
|
|
|
188
286
|
await chatTable.delete({ where: { id: this.chatId } });
|
|
189
287
|
// AgentSession record is deleted via Cascade from LlmChat
|
|
190
288
|
}
|
|
289
|
+
/**
|
|
290
|
+
* Update session metadata and re-resolve agent configuration.
|
|
291
|
+
*/
|
|
292
|
+
async updateMetadata(metadata) {
|
|
293
|
+
const sessionTable = this.getModel(this.tableNames.agentSession);
|
|
294
|
+
const newMetadata = { ...this.record.metadata, ...metadata };
|
|
295
|
+
await sessionTable.update({
|
|
296
|
+
where: { id: this.id },
|
|
297
|
+
data: { metadata: newMetadata }
|
|
298
|
+
});
|
|
299
|
+
this.record.metadata = newMetadata;
|
|
300
|
+
// Apply changes to the underlying agent immediately
|
|
301
|
+
// resolveLazyConfig is private, so we need a cast or make it protected.
|
|
302
|
+
// Given we are in the same package, we can cast.
|
|
303
|
+
this.agent.resolveLazyConfig(newMetadata);
|
|
304
|
+
}
|
|
191
305
|
}
|
|
192
306
|
/**
|
|
193
307
|
* Creates a new agent session and its persistent chat record.
|
|
@@ -205,9 +319,10 @@ export async function createAgentSession(prisma, llm, AgentClass, options = {})
|
|
|
205
319
|
const chatTable = getTable(prisma, tableNames.chat);
|
|
206
320
|
const chatRecord = (await chatTable.create({
|
|
207
321
|
data: {
|
|
208
|
-
model: AgentClass.model || null,
|
|
209
|
-
provider: null,
|
|
210
|
-
instructions:
|
|
322
|
+
model: options.model || AgentClass.model || null,
|
|
323
|
+
provider: options.provider || null,
|
|
324
|
+
instructions: options.instructions ||
|
|
325
|
+
(typeof AgentClass.instructions === "string" ? AgentClass.instructions : null),
|
|
211
326
|
metadata: null // Runtime metadata goes in Chat, session context in AgentSession
|
|
212
327
|
}
|
|
213
328
|
}));
|
|
@@ -220,7 +335,16 @@ export async function createAgentSession(prisma, llm, AgentClass, options = {})
|
|
|
220
335
|
metadata: options.metadata || null
|
|
221
336
|
}
|
|
222
337
|
}));
|
|
223
|
-
|
|
338
|
+
// 3. Instantiate Agent with overrides
|
|
339
|
+
const agent = new AgentClass({
|
|
340
|
+
llm,
|
|
341
|
+
inputs: sessionRecord.metadata,
|
|
342
|
+
model: options.model,
|
|
343
|
+
provider: options.provider,
|
|
344
|
+
instructions: options.instructions,
|
|
345
|
+
maxToolCalls: options.maxToolCalls
|
|
346
|
+
});
|
|
347
|
+
return new AgentSession(prisma, llm, AgentClass, sessionRecord, options.tableNames, agent, options.debug);
|
|
224
348
|
}
|
|
225
349
|
/**
|
|
226
350
|
* Loads an existing agent session and re-instantiates the agent with history.
|
|
@@ -257,11 +381,13 @@ export async function loadAgentSession(prisma, llm, AgentClass, sessionId, optio
|
|
|
257
381
|
role: m.role,
|
|
258
382
|
content: m.content || ""
|
|
259
383
|
}));
|
|
260
|
-
// 4. Instantiate agent with injected history
|
|
384
|
+
// 4. Instantiate agent with injected history, LLM, AND metadata (as inputs)
|
|
261
385
|
// "Code Wins" - model, tools, instructions come from AgentClass
|
|
386
|
+
// Metadata from DB handles the lazy resolution of behavior
|
|
262
387
|
const agent = new AgentClass({
|
|
263
388
|
llm,
|
|
264
|
-
messages: history
|
|
389
|
+
messages: history,
|
|
390
|
+
inputs: sessionRecord.metadata
|
|
265
391
|
});
|
|
266
392
|
return new AgentSession(prisma, llm, AgentClass, sessionRecord, options.tableNames, agent, options.debug);
|
|
267
393
|
}
|
|
@@ -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(
|
|
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(
|
|
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,
|
|
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(
|
|
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:
|
|
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(
|
|
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(
|
|
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:
|
|
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(
|
|
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.
|
|
3
|
+
"version": "0.7.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.
|
|
59
|
+
"@node-llm/core": "^1.15.0",
|
|
60
60
|
"@prisma/client": "^5.0.0"
|
|
61
61
|
},
|
|
62
62
|
"devDependencies": {
|