@contractspec/module.ai-chat 3.2.0 → 4.0.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/README.md +70 -0
- package/dist/browser/core/index.js +201 -41
- package/dist/browser/index.js +326 -51
- package/dist/browser/presentation/components/index.js +111 -5
- package/dist/browser/presentation/hooks/index.js +215 -46
- package/dist/browser/presentation/index.js +326 -51
- package/dist/core/chat-service.d.ts +15 -2
- package/dist/core/create-chat-route.d.ts +35 -0
- package/dist/core/create-completion-route.d.ts +16 -0
- package/dist/core/index.d.ts +2 -0
- package/dist/core/index.js +201 -41
- package/dist/core/message-types.d.ts +11 -2
- package/dist/index.js +326 -51
- package/dist/node/core/index.js +201 -41
- package/dist/node/index.js +326 -51
- package/dist/node/presentation/components/index.js +111 -5
- package/dist/node/presentation/hooks/index.js +215 -46
- package/dist/node/presentation/index.js +326 -51
- package/dist/presentation/components/index.js +111 -5
- package/dist/presentation/hooks/index.d.ts +3 -1
- package/dist/presentation/hooks/index.js +215 -46
- package/dist/presentation/hooks/useChat.d.ts +18 -0
- package/dist/presentation/index.js +326 -51
- package/package.json +18 -12
package/dist/node/core/index.js
CHANGED
|
@@ -130,6 +130,9 @@ class ChatService {
|
|
|
130
130
|
systemPrompt;
|
|
131
131
|
maxHistoryMessages;
|
|
132
132
|
onUsage;
|
|
133
|
+
tools;
|
|
134
|
+
sendReasoning;
|
|
135
|
+
sendSources;
|
|
133
136
|
constructor(config) {
|
|
134
137
|
this.provider = config.provider;
|
|
135
138
|
this.context = config.context;
|
|
@@ -137,6 +140,9 @@ class ChatService {
|
|
|
137
140
|
this.systemPrompt = config.systemPrompt ?? DEFAULT_SYSTEM_PROMPT;
|
|
138
141
|
this.maxHistoryMessages = config.maxHistoryMessages ?? 20;
|
|
139
142
|
this.onUsage = config.onUsage;
|
|
143
|
+
this.tools = config.tools;
|
|
144
|
+
this.sendReasoning = config.sendReasoning ?? false;
|
|
145
|
+
this.sendSources = config.sendSources ?? false;
|
|
140
146
|
}
|
|
141
147
|
async send(options) {
|
|
142
148
|
let conversation;
|
|
@@ -161,13 +167,14 @@ class ChatService {
|
|
|
161
167
|
status: "completed",
|
|
162
168
|
attachments: options.attachments
|
|
163
169
|
});
|
|
164
|
-
const
|
|
170
|
+
const messages = this.buildMessages(conversation, options);
|
|
165
171
|
const model = this.provider.getModel();
|
|
166
172
|
try {
|
|
167
173
|
const result = await generateText({
|
|
168
174
|
model,
|
|
169
|
-
|
|
170
|
-
system: this.systemPrompt
|
|
175
|
+
messages,
|
|
176
|
+
system: this.systemPrompt,
|
|
177
|
+
tools: this.tools
|
|
171
178
|
});
|
|
172
179
|
const assistantMessage = await this.store.appendMessage(conversation.id, {
|
|
173
180
|
role: "assistant",
|
|
@@ -223,33 +230,106 @@ class ChatService {
|
|
|
223
230
|
content: "",
|
|
224
231
|
status: "streaming"
|
|
225
232
|
});
|
|
226
|
-
const
|
|
233
|
+
const messages = this.buildMessages(conversation, options);
|
|
227
234
|
const model = this.provider.getModel();
|
|
228
|
-
const
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
235
|
+
const systemPrompt = this.systemPrompt;
|
|
236
|
+
const tools = this.tools;
|
|
237
|
+
const store = this.store;
|
|
238
|
+
const onUsage = this.onUsage;
|
|
232
239
|
async function* streamGenerator() {
|
|
233
240
|
let fullContent = "";
|
|
241
|
+
let fullReasoning = "";
|
|
242
|
+
const toolCallsMap = new Map;
|
|
243
|
+
const sources = [];
|
|
234
244
|
try {
|
|
235
245
|
const result = streamText({
|
|
236
246
|
model,
|
|
237
|
-
|
|
238
|
-
system:
|
|
247
|
+
messages,
|
|
248
|
+
system: systemPrompt,
|
|
249
|
+
tools
|
|
239
250
|
});
|
|
240
|
-
for await (const
|
|
241
|
-
|
|
242
|
-
|
|
251
|
+
for await (const part of result.fullStream) {
|
|
252
|
+
if (part.type === "text-delta") {
|
|
253
|
+
const text = part.text ?? "";
|
|
254
|
+
if (text) {
|
|
255
|
+
fullContent += text;
|
|
256
|
+
yield { type: "text", content: text };
|
|
257
|
+
}
|
|
258
|
+
} else if (part.type === "reasoning-delta") {
|
|
259
|
+
const text = part.text ?? "";
|
|
260
|
+
if (text) {
|
|
261
|
+
fullReasoning += text;
|
|
262
|
+
yield { type: "reasoning", content: text };
|
|
263
|
+
}
|
|
264
|
+
} else if (part.type === "source") {
|
|
265
|
+
const src = part;
|
|
266
|
+
const source = {
|
|
267
|
+
id: src.id,
|
|
268
|
+
title: src.title ?? "",
|
|
269
|
+
url: src.url,
|
|
270
|
+
type: "web"
|
|
271
|
+
};
|
|
272
|
+
sources.push(source);
|
|
273
|
+
yield { type: "source", source };
|
|
274
|
+
} else if (part.type === "tool-call") {
|
|
275
|
+
const toolCall = {
|
|
276
|
+
id: part.toolCallId,
|
|
277
|
+
name: part.toolName,
|
|
278
|
+
args: part.input ?? {},
|
|
279
|
+
status: "running"
|
|
280
|
+
};
|
|
281
|
+
toolCallsMap.set(part.toolCallId, toolCall);
|
|
282
|
+
yield { type: "tool_call", toolCall };
|
|
283
|
+
} else if (part.type === "tool-result") {
|
|
284
|
+
const tc = toolCallsMap.get(part.toolCallId);
|
|
285
|
+
if (tc) {
|
|
286
|
+
tc.result = part.output;
|
|
287
|
+
tc.status = "completed";
|
|
288
|
+
}
|
|
289
|
+
yield {
|
|
290
|
+
type: "tool_result",
|
|
291
|
+
toolResult: {
|
|
292
|
+
toolCallId: part.toolCallId,
|
|
293
|
+
toolName: part.toolName,
|
|
294
|
+
result: part.output
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
} else if (part.type === "tool-error") {
|
|
298
|
+
const tc = toolCallsMap.get(part.toolCallId);
|
|
299
|
+
if (tc) {
|
|
300
|
+
tc.status = "error";
|
|
301
|
+
tc.error = part.error ?? "Tool execution failed";
|
|
302
|
+
}
|
|
303
|
+
} else if (part.type === "finish") {
|
|
304
|
+
const usage = part.usage;
|
|
305
|
+
const inputTokens = usage?.inputTokens ?? 0;
|
|
306
|
+
const outputTokens = usage?.completionTokens ?? 0;
|
|
307
|
+
await store.updateMessage(conversation.id, assistantMessage.id, {
|
|
308
|
+
content: fullContent,
|
|
309
|
+
status: "completed",
|
|
310
|
+
reasoning: fullReasoning || undefined,
|
|
311
|
+
sources: sources.length > 0 ? sources : undefined,
|
|
312
|
+
toolCalls: toolCallsMap.size > 0 ? Array.from(toolCallsMap.values()) : undefined,
|
|
313
|
+
usage: usage ? { inputTokens, outputTokens } : undefined
|
|
314
|
+
});
|
|
315
|
+
onUsage?.({ inputTokens, outputTokens });
|
|
316
|
+
yield {
|
|
317
|
+
type: "done",
|
|
318
|
+
usage: usage ? { inputTokens, outputTokens } : undefined
|
|
319
|
+
};
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
243
322
|
}
|
|
244
|
-
await
|
|
323
|
+
await store.updateMessage(conversation.id, assistantMessage.id, {
|
|
245
324
|
content: fullContent,
|
|
246
|
-
status: "completed"
|
|
325
|
+
status: "completed",
|
|
326
|
+
reasoning: fullReasoning || undefined,
|
|
327
|
+
sources: sources.length > 0 ? sources : undefined,
|
|
328
|
+
toolCalls: toolCallsMap.size > 0 ? Array.from(toolCallsMap.values()) : undefined
|
|
247
329
|
});
|
|
248
|
-
yield {
|
|
249
|
-
type: "done"
|
|
250
|
-
};
|
|
330
|
+
yield { type: "done" };
|
|
251
331
|
} catch (error) {
|
|
252
|
-
await
|
|
332
|
+
await store.updateMessage(conversation.id, assistantMessage.id, {
|
|
253
333
|
content: fullContent,
|
|
254
334
|
status: "error",
|
|
255
335
|
error: {
|
|
@@ -284,48 +364,128 @@ class ChatService {
|
|
|
284
364
|
async deleteConversation(conversationId) {
|
|
285
365
|
return this.store.delete(conversationId);
|
|
286
366
|
}
|
|
287
|
-
|
|
288
|
-
let prompt = "";
|
|
367
|
+
buildMessages(conversation, _options) {
|
|
289
368
|
const historyStart = Math.max(0, conversation.messages.length - this.maxHistoryMessages);
|
|
369
|
+
const messages = [];
|
|
290
370
|
for (let i = historyStart;i < conversation.messages.length; i++) {
|
|
291
371
|
const msg = conversation.messages[i];
|
|
292
372
|
if (!msg)
|
|
293
373
|
continue;
|
|
294
|
-
if (msg.role === "user"
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
let content = options.content;
|
|
301
|
-
if (options.attachments?.length) {
|
|
302
|
-
const attachmentInfo = options.attachments.map((a) => {
|
|
303
|
-
if (a.type === "file" || a.type === "code") {
|
|
304
|
-
return `
|
|
374
|
+
if (msg.role === "user") {
|
|
375
|
+
let content = msg.content;
|
|
376
|
+
if (msg.attachments?.length) {
|
|
377
|
+
const attachmentInfo = msg.attachments.map((a) => {
|
|
378
|
+
if (a.type === "file" || a.type === "code") {
|
|
379
|
+
return `
|
|
305
380
|
|
|
306
381
|
### ${a.name}
|
|
307
382
|
\`\`\`
|
|
308
|
-
${a.content}
|
|
383
|
+
${a.content ?? ""}
|
|
309
384
|
\`\`\``;
|
|
310
|
-
|
|
311
|
-
|
|
385
|
+
}
|
|
386
|
+
return `
|
|
312
387
|
|
|
313
388
|
[Attachment: ${a.name}]`;
|
|
314
|
-
|
|
315
|
-
|
|
389
|
+
}).join("");
|
|
390
|
+
content += attachmentInfo;
|
|
391
|
+
}
|
|
392
|
+
messages.push({ role: "user", content });
|
|
393
|
+
} else if (msg.role === "assistant") {
|
|
394
|
+
if (msg.toolCalls?.length) {
|
|
395
|
+
messages.push({
|
|
396
|
+
role: "assistant",
|
|
397
|
+
content: msg.content || "",
|
|
398
|
+
toolCalls: msg.toolCalls.map((tc) => ({
|
|
399
|
+
type: "tool-call",
|
|
400
|
+
toolCallId: tc.id,
|
|
401
|
+
toolName: tc.name,
|
|
402
|
+
args: tc.args
|
|
403
|
+
}))
|
|
404
|
+
});
|
|
405
|
+
messages.push({
|
|
406
|
+
role: "tool",
|
|
407
|
+
content: msg.toolCalls.map((tc) => ({
|
|
408
|
+
type: "tool-result",
|
|
409
|
+
toolCallId: tc.id,
|
|
410
|
+
toolName: tc.name,
|
|
411
|
+
output: tc.result
|
|
412
|
+
}))
|
|
413
|
+
});
|
|
414
|
+
} else {
|
|
415
|
+
messages.push({ role: "assistant", content: msg.content });
|
|
416
|
+
}
|
|
417
|
+
}
|
|
316
418
|
}
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
Assistant:`;
|
|
320
|
-
return prompt;
|
|
419
|
+
return messages;
|
|
321
420
|
}
|
|
322
421
|
}
|
|
323
422
|
function createChatService(config) {
|
|
324
423
|
return new ChatService(config);
|
|
325
424
|
}
|
|
425
|
+
// src/core/create-chat-route.ts
|
|
426
|
+
import {
|
|
427
|
+
convertToModelMessages,
|
|
428
|
+
streamText as streamText2
|
|
429
|
+
} from "ai";
|
|
430
|
+
var DEFAULT_SYSTEM_PROMPT2 = `You are a helpful AI assistant.`;
|
|
431
|
+
function createChatRoute(options) {
|
|
432
|
+
const { provider, systemPrompt = DEFAULT_SYSTEM_PROMPT2, tools } = options;
|
|
433
|
+
return async (req) => {
|
|
434
|
+
if (req.method !== "POST") {
|
|
435
|
+
return new Response("Method not allowed", { status: 405 });
|
|
436
|
+
}
|
|
437
|
+
let body;
|
|
438
|
+
try {
|
|
439
|
+
body = await req.json();
|
|
440
|
+
} catch {
|
|
441
|
+
return new Response("Invalid JSON body", { status: 400 });
|
|
442
|
+
}
|
|
443
|
+
const messages = body.messages ?? [];
|
|
444
|
+
if (!Array.isArray(messages) || messages.length === 0) {
|
|
445
|
+
return new Response("messages array required", { status: 400 });
|
|
446
|
+
}
|
|
447
|
+
const model = provider.getModel();
|
|
448
|
+
const result = streamText2({
|
|
449
|
+
model,
|
|
450
|
+
messages: await convertToModelMessages(messages),
|
|
451
|
+
system: systemPrompt,
|
|
452
|
+
tools
|
|
453
|
+
});
|
|
454
|
+
return result.toUIMessageStreamResponse();
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
// src/core/create-completion-route.ts
|
|
458
|
+
import { streamText as streamText3 } from "ai";
|
|
459
|
+
function createCompletionRoute(options) {
|
|
460
|
+
const { provider, systemPrompt } = options;
|
|
461
|
+
return async (req) => {
|
|
462
|
+
if (req.method !== "POST") {
|
|
463
|
+
return new Response("Method not allowed", { status: 405 });
|
|
464
|
+
}
|
|
465
|
+
let body;
|
|
466
|
+
try {
|
|
467
|
+
body = await req.json();
|
|
468
|
+
} catch {
|
|
469
|
+
return new Response("Invalid JSON body", { status: 400 });
|
|
470
|
+
}
|
|
471
|
+
const prompt = body.prompt ?? "";
|
|
472
|
+
if (!prompt || typeof prompt !== "string") {
|
|
473
|
+
return new Response("prompt string required", { status: 400 });
|
|
474
|
+
}
|
|
475
|
+
const model = provider.getModel();
|
|
476
|
+
const result = streamText3({
|
|
477
|
+
model,
|
|
478
|
+
prompt,
|
|
479
|
+
system: systemPrompt
|
|
480
|
+
});
|
|
481
|
+
return result.toTextStreamResponse();
|
|
482
|
+
};
|
|
483
|
+
}
|
|
326
484
|
export {
|
|
327
485
|
createInMemoryConversationStore,
|
|
486
|
+
createCompletionRoute,
|
|
328
487
|
createChatService,
|
|
488
|
+
createChatRoute,
|
|
329
489
|
InMemoryConversationStore,
|
|
330
490
|
ChatService
|
|
331
491
|
};
|