@node-llm/orm 0.4.0 → 0.5.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 +38 -2
- package/README.md +94 -8
- package/bin/cli.js +40 -6
- package/dist/adapters/prisma/AgentSession.d.ts +140 -0
- package/dist/adapters/prisma/AgentSession.d.ts.map +1 -0
- package/dist/adapters/prisma/AgentSession.js +284 -0
- package/dist/adapters/prisma/index.d.ts +25 -2
- package/dist/adapters/prisma/index.d.ts.map +1 -1
- package/dist/adapters/prisma/index.js +25 -1
- package/dist/index.d.ts +21 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +21 -1
- package/migrations/README.md +53 -0
- package/migrations/add_agent_session.sql +44 -0
- package/migrations/add_thinking_support.sql +34 -0
- package/package.json +5 -1
- package/schema.prisma +50 -33
- package/src/adapters/prisma/AgentSession.ts +458 -0
- package/src/adapters/prisma/index.ts +33 -2
- package/src/index.ts +21 -1
- package/test/AgentSession.test.ts +204 -0
- package/test/CodeWins.test.ts +116 -0
- package/test/docs/prisma-docs.test.ts +221 -0
- package/test/docs/readme-exports.test.ts +62 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ChatOptions,
|
|
3
|
+
AskOptions,
|
|
4
|
+
NodeLLMCore,
|
|
5
|
+
Agent,
|
|
6
|
+
AgentConfig,
|
|
7
|
+
Message,
|
|
8
|
+
ChatChunk,
|
|
9
|
+
Usage
|
|
10
|
+
} from "@node-llm/core";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Internal interface for dynamic Prisma Client access.
|
|
14
|
+
* We use 'any' here because PrismaClient has no index signature by default,
|
|
15
|
+
* making it hard to access models dynamically by string name.
|
|
16
|
+
*/
|
|
17
|
+
type GenericPrismaClient = any;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Record structure for the LLM Agent Session table.
|
|
21
|
+
*/
|
|
22
|
+
export interface AgentSessionRecord {
|
|
23
|
+
id: string;
|
|
24
|
+
agentClass: string;
|
|
25
|
+
chatId: string;
|
|
26
|
+
metadata?: Record<string, unknown> | null;
|
|
27
|
+
createdAt: Date;
|
|
28
|
+
updatedAt: Date;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Record structure for the LLM Message table.
|
|
33
|
+
*/
|
|
34
|
+
export interface MessageRecord {
|
|
35
|
+
id: string;
|
|
36
|
+
chatId: string;
|
|
37
|
+
role: string;
|
|
38
|
+
content: string | null;
|
|
39
|
+
contentRaw?: string | null;
|
|
40
|
+
thinkingText?: string | null;
|
|
41
|
+
thinkingSignature?: string | null;
|
|
42
|
+
thinkingTokens?: number | null;
|
|
43
|
+
inputTokens?: number | null;
|
|
44
|
+
outputTokens?: number | null;
|
|
45
|
+
modelId?: string | null;
|
|
46
|
+
provider?: string | null;
|
|
47
|
+
createdAt: Date;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Table name customization.
|
|
52
|
+
*/
|
|
53
|
+
export interface TableNames {
|
|
54
|
+
agentSession?: string;
|
|
55
|
+
chat?: string;
|
|
56
|
+
message?: string;
|
|
57
|
+
toolCall?: string;
|
|
58
|
+
request?: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Internal interface for dynamic Prisma model access
|
|
63
|
+
*/
|
|
64
|
+
interface PrismaModel<T = Record<string, unknown>> {
|
|
65
|
+
create(args: { data: Record<string, unknown> }): Promise<T>;
|
|
66
|
+
update(args: { where: { id: string }; data: Record<string, unknown> }): Promise<T>;
|
|
67
|
+
delete(args: { where: { id: string } }): Promise<void>;
|
|
68
|
+
findMany(args: {
|
|
69
|
+
where: Record<string, unknown>;
|
|
70
|
+
orderBy?: Record<string, string>;
|
|
71
|
+
}): Promise<T[]>;
|
|
72
|
+
findUnique(args: { where: { id: string } }): Promise<T | null>;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
type AgentClass<T extends Agent = Agent> = (new (
|
|
76
|
+
overrides?: Partial<AgentConfig & ChatOptions>
|
|
77
|
+
) => T) & {
|
|
78
|
+
name: string;
|
|
79
|
+
model?: string;
|
|
80
|
+
instructions?: string;
|
|
81
|
+
tools?: unknown[];
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* AgentSession - Wraps an Agent instance with persistence capabilities.
|
|
86
|
+
*
|
|
87
|
+
* Follows "Code Wins" sovereignty:
|
|
88
|
+
* - Model, Tools, Instructions come from the Agent class (code)
|
|
89
|
+
* - Message history comes from the database
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* ```typescript
|
|
93
|
+
* // Create a new session
|
|
94
|
+
* const session = await createAgentSession(prisma, llm, SupportAgent, {
|
|
95
|
+
* metadata: { userId: "123" }
|
|
96
|
+
* });
|
|
97
|
+
*
|
|
98
|
+
* // Resume a session
|
|
99
|
+
* const session = await loadAgentSession(prisma, llm, SupportAgent, "sess_abc");
|
|
100
|
+
*
|
|
101
|
+
* // Agent behavior is always defined in code
|
|
102
|
+
* const result = await session.ask("Hello");
|
|
103
|
+
* ```
|
|
104
|
+
*/
|
|
105
|
+
export class AgentSession<T extends Agent = Agent> {
|
|
106
|
+
private currentMessageId: string | null = null;
|
|
107
|
+
private tableNames: Required<TableNames>;
|
|
108
|
+
private debug: boolean;
|
|
109
|
+
|
|
110
|
+
constructor(
|
|
111
|
+
private prisma: any,
|
|
112
|
+
private llm: NodeLLMCore,
|
|
113
|
+
private AgentClass: AgentClass<T>,
|
|
114
|
+
private record: AgentSessionRecord,
|
|
115
|
+
tableNames?: TableNames,
|
|
116
|
+
private agent: T = new AgentClass({
|
|
117
|
+
llm
|
|
118
|
+
}),
|
|
119
|
+
debug: boolean = false
|
|
120
|
+
) {
|
|
121
|
+
this.debug = debug;
|
|
122
|
+
this.tableNames = {
|
|
123
|
+
agentSession: tableNames?.agentSession || "llmAgentSession",
|
|
124
|
+
chat: tableNames?.chat || "llmChat",
|
|
125
|
+
message: tableNames?.message || "llmMessage",
|
|
126
|
+
toolCall: tableNames?.toolCall || "llmToolCall",
|
|
127
|
+
request: tableNames?.request || "llmRequest"
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
private log(...args: any[]) {
|
|
132
|
+
if (this.debug) {
|
|
133
|
+
console.log(`[@node-llm/orm]`, ...args);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** Agent instance (for direct access if needed) */
|
|
138
|
+
get instance(): T {
|
|
139
|
+
return this.agent;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** Session ID for persistence */
|
|
143
|
+
get id(): string {
|
|
144
|
+
return this.record.id;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** Underlying chat ID */
|
|
148
|
+
get chatId(): string {
|
|
149
|
+
return this.record.chatId;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** Session metadata */
|
|
153
|
+
get metadata(): Record<string, unknown> | null | undefined {
|
|
154
|
+
return this.record.metadata;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** Agent class name */
|
|
158
|
+
get agentClass(): string {
|
|
159
|
+
return this.record.agentClass;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** Model ID used by the agent */
|
|
163
|
+
get modelId(): string {
|
|
164
|
+
return this.agent.modelId;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/** Cumulative usage for this session (from agent memory) */
|
|
168
|
+
get totalUsage(): Usage {
|
|
169
|
+
return this.agent.totalUsage;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** Current in-memory message history */
|
|
173
|
+
get history(): readonly Message[] {
|
|
174
|
+
return this.agent.history;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Helper to get a typed Prisma model by its dynamic name.
|
|
179
|
+
*/
|
|
180
|
+
private getModel<R = Record<string, unknown>>(name: string): PrismaModel<R> {
|
|
181
|
+
return getTable(this.prisma, name) as unknown as PrismaModel<R>;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Send a message and persist the conversation.
|
|
186
|
+
*/
|
|
187
|
+
async ask(input: string, options: AskOptions = {}): Promise<MessageRecord> {
|
|
188
|
+
const model = this.getModel<MessageRecord>(this.tableNames.message);
|
|
189
|
+
|
|
190
|
+
// Persist user message
|
|
191
|
+
await model.create({
|
|
192
|
+
data: { chatId: this.chatId, role: "user", content: input }
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
// Create placeholder for assistant message
|
|
196
|
+
const assistantMessage = await model.create({
|
|
197
|
+
data: { chatId: this.chatId, role: "assistant", content: null }
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
this.currentMessageId = assistantMessage.id;
|
|
201
|
+
|
|
202
|
+
try {
|
|
203
|
+
// Get response from agent (uses code-defined config + injected history)
|
|
204
|
+
const response = await this.agent.ask(input, options);
|
|
205
|
+
|
|
206
|
+
// Update assistant message with response
|
|
207
|
+
return await model.update({
|
|
208
|
+
where: { id: assistantMessage.id },
|
|
209
|
+
data: {
|
|
210
|
+
content: response.content,
|
|
211
|
+
contentRaw: JSON.stringify(response.meta),
|
|
212
|
+
inputTokens: response.usage?.input_tokens || 0,
|
|
213
|
+
outputTokens: response.usage?.output_tokens || 0,
|
|
214
|
+
thinkingText: response.thinking?.text || null,
|
|
215
|
+
thinkingSignature: response.thinking?.signature || null,
|
|
216
|
+
thinkingTokens: response.thinking?.tokens || null,
|
|
217
|
+
modelId: response.model || null,
|
|
218
|
+
provider: response.provider || null
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
} catch (error) {
|
|
222
|
+
// Clean up placeholder on error
|
|
223
|
+
await model.delete({ where: { id: assistantMessage.id } });
|
|
224
|
+
throw error;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Stream a response and persist the conversation.
|
|
230
|
+
*/
|
|
231
|
+
async *askStream(
|
|
232
|
+
input: string,
|
|
233
|
+
options: AskOptions = {}
|
|
234
|
+
): AsyncGenerator<ChatChunk, MessageRecord, undefined> {
|
|
235
|
+
const model = this.getModel<MessageRecord>(this.tableNames.message);
|
|
236
|
+
|
|
237
|
+
// Persist user message
|
|
238
|
+
await model.create({
|
|
239
|
+
data: { chatId: this.chatId, role: "user", content: input }
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
// Create placeholder for assistant message
|
|
243
|
+
const assistantMessage = await model.create({
|
|
244
|
+
data: { chatId: this.chatId, role: "assistant", content: null }
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
this.currentMessageId = assistantMessage.id;
|
|
248
|
+
|
|
249
|
+
try {
|
|
250
|
+
const stream = this.agent.stream(input, options);
|
|
251
|
+
|
|
252
|
+
let fullContent = "";
|
|
253
|
+
let lastChunk: ChatChunk | null = null;
|
|
254
|
+
|
|
255
|
+
for await (const chunk of stream) {
|
|
256
|
+
fullContent += chunk.content;
|
|
257
|
+
lastChunk = chunk;
|
|
258
|
+
yield chunk;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Final update with accumulated result
|
|
262
|
+
return await model.update({
|
|
263
|
+
where: { id: assistantMessage.id },
|
|
264
|
+
data: {
|
|
265
|
+
content: fullContent,
|
|
266
|
+
inputTokens: lastChunk?.usage?.input_tokens || 0,
|
|
267
|
+
outputTokens: lastChunk?.usage?.output_tokens || 0,
|
|
268
|
+
thinkingText: lastChunk?.thinking?.text || null,
|
|
269
|
+
thinkingSignature: lastChunk?.thinking?.signature || null,
|
|
270
|
+
thinkingTokens: lastChunk?.thinking?.tokens || null,
|
|
271
|
+
modelId: (lastChunk?.metadata?.model as string) || null,
|
|
272
|
+
provider: (lastChunk?.metadata?.provider as string) || null
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
} catch (error) {
|
|
276
|
+
await model.delete({ where: { id: assistantMessage.id } });
|
|
277
|
+
throw error;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Returns the current full message history for this session.
|
|
283
|
+
*/
|
|
284
|
+
async messages(): Promise<MessageRecord[]> {
|
|
285
|
+
const model = this.getModel<MessageRecord>(this.tableNames.message);
|
|
286
|
+
return await model.findMany({
|
|
287
|
+
where: { chatId: this.chatId },
|
|
288
|
+
orderBy: { createdAt: "asc" }
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Delete the entire session and its history.
|
|
294
|
+
*/
|
|
295
|
+
async delete(): Promise<void> {
|
|
296
|
+
const chatTable = this.getModel(this.tableNames.chat);
|
|
297
|
+
await chatTable.delete({ where: { id: this.chatId } });
|
|
298
|
+
// AgentSession record is deleted via Cascade from LlmChat
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Options for creating a new agent session.
|
|
304
|
+
*/
|
|
305
|
+
export interface CreateAgentSessionOptions {
|
|
306
|
+
metadata?: Record<string, unknown>;
|
|
307
|
+
tableNames?: TableNames;
|
|
308
|
+
debug?: boolean;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Creates a new agent session and its persistent chat record.
|
|
313
|
+
*/
|
|
314
|
+
export async function createAgentSession<T extends Agent>(
|
|
315
|
+
prisma: any,
|
|
316
|
+
llm: NodeLLMCore,
|
|
317
|
+
AgentClass: AgentClass<T>,
|
|
318
|
+
options: CreateAgentSessionOptions = {}
|
|
319
|
+
): Promise<AgentSession<T>> {
|
|
320
|
+
const tableNames = {
|
|
321
|
+
agentSession: options.tableNames?.agentSession || "llmAgentSession",
|
|
322
|
+
chat: options.tableNames?.chat || "llmChat",
|
|
323
|
+
message: options.tableNames?.message || "llmMessage"
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
if (options.debug) {
|
|
327
|
+
console.log(`[@node-llm/orm] createAgentSession: agentClass=${AgentClass.name}`);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// 1. Create underlying LlmChat record
|
|
331
|
+
const chatTable = getTable(prisma, tableNames.chat);
|
|
332
|
+
const chatRecord = (await chatTable.create({
|
|
333
|
+
data: {
|
|
334
|
+
model: AgentClass.model || null,
|
|
335
|
+
provider: null,
|
|
336
|
+
instructions: AgentClass.instructions || null,
|
|
337
|
+
metadata: null // Runtime metadata goes in Chat, session context in AgentSession
|
|
338
|
+
}
|
|
339
|
+
})) as unknown as { id: string };
|
|
340
|
+
|
|
341
|
+
// 2. Create AgentSession record
|
|
342
|
+
const sessionTable = getTable(prisma, tableNames.agentSession);
|
|
343
|
+
const sessionRecord = (await sessionTable.create({
|
|
344
|
+
data: {
|
|
345
|
+
agentClass: AgentClass.name,
|
|
346
|
+
chatId: chatRecord.id,
|
|
347
|
+
metadata: options.metadata || null
|
|
348
|
+
}
|
|
349
|
+
})) as unknown as AgentSessionRecord;
|
|
350
|
+
|
|
351
|
+
return new AgentSession(
|
|
352
|
+
prisma,
|
|
353
|
+
llm,
|
|
354
|
+
AgentClass,
|
|
355
|
+
sessionRecord,
|
|
356
|
+
options.tableNames,
|
|
357
|
+
undefined,
|
|
358
|
+
options.debug
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Options for loading an existing agent session.
|
|
364
|
+
*/
|
|
365
|
+
export interface LoadAgentSessionOptions {
|
|
366
|
+
tableNames?: TableNames;
|
|
367
|
+
debug?: boolean;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Loads an existing agent session and re-instantiates the agent with history.
|
|
372
|
+
*/
|
|
373
|
+
export async function loadAgentSession<T extends Agent>(
|
|
374
|
+
prisma: any,
|
|
375
|
+
llm: NodeLLMCore,
|
|
376
|
+
AgentClass: AgentClass<T>,
|
|
377
|
+
sessionId: string,
|
|
378
|
+
options: LoadAgentSessionOptions = {}
|
|
379
|
+
): Promise<AgentSession<T> | null> {
|
|
380
|
+
const tableNames = {
|
|
381
|
+
agentSession: options.tableNames?.agentSession || "llmAgentSession",
|
|
382
|
+
chat: options.tableNames?.chat || "llmChat",
|
|
383
|
+
message: options.tableNames?.message || "llmMessage"
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
if (options.debug) {
|
|
387
|
+
console.log(`[@node-llm/orm] loadAgentSession: id=${sessionId}`);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// 1. Find session record
|
|
391
|
+
const sessionTable = getTable(prisma, tableNames.agentSession);
|
|
392
|
+
const sessionRecord = (await sessionTable.findUnique({
|
|
393
|
+
where: { id: sessionId }
|
|
394
|
+
})) as unknown as AgentSessionRecord | null;
|
|
395
|
+
|
|
396
|
+
if (!sessionRecord) {
|
|
397
|
+
return null;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// 1.5. Validate Agent Class (Code Wins Sovereignty)
|
|
401
|
+
if (sessionRecord.agentClass !== AgentClass.name) {
|
|
402
|
+
throw new Error(
|
|
403
|
+
`Agent class mismatch: Session "${sessionId}" was created for "${sessionRecord.agentClass}", but is being loaded with "${AgentClass.name}".`
|
|
404
|
+
);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// 2. Load message history
|
|
408
|
+
const messageTable = getTable(prisma, tableNames.message);
|
|
409
|
+
const messages = (await messageTable.findMany({
|
|
410
|
+
where: { chatId: sessionRecord.chatId },
|
|
411
|
+
orderBy: { createdAt: "asc" }
|
|
412
|
+
})) as unknown as MessageRecord[];
|
|
413
|
+
|
|
414
|
+
// 3. Convert DB messages to NodeLLM Message format
|
|
415
|
+
const history: Message[] = messages.map((m) => ({
|
|
416
|
+
role: m.role as "user" | "assistant" | "system",
|
|
417
|
+
content: m.content || ""
|
|
418
|
+
}));
|
|
419
|
+
|
|
420
|
+
// 4. Instantiate agent with injected history and LLM
|
|
421
|
+
// "Code Wins" - model, tools, instructions come from AgentClass
|
|
422
|
+
const agent = new AgentClass({
|
|
423
|
+
llm,
|
|
424
|
+
messages: history
|
|
425
|
+
}) as T;
|
|
426
|
+
|
|
427
|
+
return new AgentSession(
|
|
428
|
+
prisma,
|
|
429
|
+
llm,
|
|
430
|
+
AgentClass,
|
|
431
|
+
sessionRecord,
|
|
432
|
+
options.tableNames,
|
|
433
|
+
agent,
|
|
434
|
+
options.debug
|
|
435
|
+
);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* Dynamic helper to access Prisma models by name.
|
|
440
|
+
* Handles both case-sensitive and case-insensitive lookups for flexibility.
|
|
441
|
+
*/
|
|
442
|
+
function getTable(prisma: GenericPrismaClient, tableName: string): PrismaModel {
|
|
443
|
+
const p = prisma as unknown as Record<string, PrismaModel>;
|
|
444
|
+
|
|
445
|
+
// 1. Direct match
|
|
446
|
+
const table = p[tableName];
|
|
447
|
+
if (table) return table;
|
|
448
|
+
|
|
449
|
+
// 2. Case-insensitive match
|
|
450
|
+
const keys = Object.keys(prisma).filter((k) => !k.startsWith("$") && !k.startsWith("_"));
|
|
451
|
+
const match = keys.find((k) => k.toLowerCase() === tableName.toLowerCase());
|
|
452
|
+
|
|
453
|
+
if (match && p[match]) return p[match];
|
|
454
|
+
|
|
455
|
+
throw new Error(
|
|
456
|
+
`[@node-llm/orm] Prisma table "${tableName}" not found. Available tables: ${keys.join(", ")}`
|
|
457
|
+
);
|
|
458
|
+
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Prisma adapter for NodeLLM ORM.
|
|
5
5
|
* Provides automatic persistence of chats, messages, tool calls, and API requests.
|
|
6
6
|
*
|
|
7
|
-
* @example
|
|
7
|
+
* @example Chat API (low-level)
|
|
8
8
|
* ```typescript
|
|
9
9
|
* import { PrismaClient } from '@prisma/client';
|
|
10
10
|
* import { createLLM } from '@node-llm/core';
|
|
@@ -21,7 +21,38 @@
|
|
|
21
21
|
* const response = await chat.ask('Hello!');
|
|
22
22
|
* console.log(response.content);
|
|
23
23
|
* ```
|
|
24
|
+
*
|
|
25
|
+
* @example AgentSession API (recommended for agents)
|
|
26
|
+
* ```typescript
|
|
27
|
+
* import { Agent } from '@node-llm/core';
|
|
28
|
+
* import { createAgentSession, loadAgentSession } from '@node-llm/orm/prisma';
|
|
29
|
+
*
|
|
30
|
+
* class SupportAgent extends Agent {
|
|
31
|
+
* static model = 'gpt-4.1';
|
|
32
|
+
* static instructions = 'You are a helpful support agent.';
|
|
33
|
+
* }
|
|
34
|
+
*
|
|
35
|
+
* // Create new session
|
|
36
|
+
* const session = await createAgentSession(prisma, llm, SupportAgent, {
|
|
37
|
+
* metadata: { userId: 'user_123' }
|
|
38
|
+
* });
|
|
39
|
+
* await session.ask('Hello!');
|
|
40
|
+
*
|
|
41
|
+
* // Resume later (Code Wins - model/tools from class, history from DB)
|
|
42
|
+
* const session = await loadAgentSession(prisma, llm, SupportAgent, sessionId);
|
|
43
|
+
* await session.ask('Continue our conversation');
|
|
44
|
+
* ```
|
|
24
45
|
*/
|
|
25
46
|
|
|
47
|
+
// Chat API
|
|
26
48
|
export { Chat, createChat, loadChat } from "./Chat.js";
|
|
27
|
-
export type { ChatRecord, MessageRecord, ChatOptions
|
|
49
|
+
export type { ChatRecord, MessageRecord, ChatOptions } from "./Chat.js";
|
|
50
|
+
|
|
51
|
+
// AgentSession API
|
|
52
|
+
export { AgentSession, createAgentSession, loadAgentSession } from "./AgentSession.js";
|
|
53
|
+
export type {
|
|
54
|
+
AgentSessionRecord,
|
|
55
|
+
CreateAgentSessionOptions,
|
|
56
|
+
LoadAgentSessionOptions,
|
|
57
|
+
TableNames // Export from AgentSession which includes agentSession key
|
|
58
|
+
} from "./AgentSession.js";
|
package/src/index.ts
CHANGED
|
@@ -23,13 +23,33 @@
|
|
|
23
23
|
* await chat.ask('Hello!');
|
|
24
24
|
* ```
|
|
25
25
|
*
|
|
26
|
+
* ## Agent Sessions (Recommended for Agents)
|
|
27
|
+
*
|
|
28
|
+
* ```typescript
|
|
29
|
+
* import { Agent } from '@node-llm/core';
|
|
30
|
+
* import { createAgentSession, loadAgentSession } from '@node-llm/orm/prisma';
|
|
31
|
+
*
|
|
32
|
+
* class SupportAgent extends Agent {
|
|
33
|
+
* static model = 'gpt-4.1';
|
|
34
|
+
* static instructions = 'You are a helpful support agent.';
|
|
35
|
+
* }
|
|
36
|
+
*
|
|
37
|
+
* // Create and persist
|
|
38
|
+
* const session = await createAgentSession(prisma, llm, SupportAgent);
|
|
39
|
+
* await session.ask('Hello!');
|
|
40
|
+
*
|
|
41
|
+
* // Resume later (Code Wins - model/tools from class, history from DB)
|
|
42
|
+
* const session = await loadAgentSession(prisma, llm, SupportAgent, sessionId);
|
|
43
|
+
* ```
|
|
44
|
+
*
|
|
26
45
|
* ## Adapters
|
|
27
46
|
*
|
|
28
47
|
* - `@node-llm/orm/prisma` - Prisma adapter (recommended)
|
|
29
48
|
*
|
|
30
49
|
* ## Schema
|
|
31
50
|
*
|
|
32
|
-
* The ORM tracks
|
|
51
|
+
* The ORM tracks five core entities:
|
|
52
|
+
* - **AgentSession** - Links Agent class to persistent Chat (v0.5.0+)
|
|
33
53
|
* - **Chat** - Session container (model, provider, instructions)
|
|
34
54
|
* - **Message** - User/Assistant conversation history
|
|
35
55
|
* - **ToolCall** - Tool executions (name, arguments, results)
|