@arcote.tech/arc-chat 0.5.0 → 0.5.2
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/package.json +6 -6
- package/src/aggregates/message.ts +272 -43
- package/src/chat-builder.ts +243 -83
- package/src/index.ts +4 -22
- package/src/listeners/ai-generation-listener.ts +322 -249
- package/src/react/chat-component.tsx +457 -0
- package/src/react/index.ts +2 -3
- package/src/react/use-chat.ts +1 -260
- package/src/routes/chat-stream-route.ts +4 -10
- package/src/streaming/stream-registry.ts +92 -124
- package/src/tools/ask-questions.tsx +107 -0
- package/src/routes/tool-results-route.ts +0 -49
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arcote.tech/arc-chat",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.5.
|
|
4
|
+
"version": "0.5.2",
|
|
5
5
|
"private": false,
|
|
6
6
|
"description": "Chat module with AI integration for Arc framework",
|
|
7
7
|
"main": "./src/index.ts",
|
|
@@ -10,11 +10,11 @@
|
|
|
10
10
|
"type-check": "tsc --noEmit"
|
|
11
11
|
},
|
|
12
12
|
"peerDependencies": {
|
|
13
|
-
"@arcote.tech/arc": "^0.5.
|
|
14
|
-
"@arcote.tech/arc-ai": "^0.5.
|
|
15
|
-
"@arcote.tech/arc-auth": "^0.5.
|
|
16
|
-
"@arcote.tech/arc-ds": "^0.5.
|
|
17
|
-
"@arcote.tech/platform": "^0.5.
|
|
13
|
+
"@arcote.tech/arc": "^0.5.2",
|
|
14
|
+
"@arcote.tech/arc-ai": "^0.5.2",
|
|
15
|
+
"@arcote.tech/arc-auth": "^0.5.2",
|
|
16
|
+
"@arcote.tech/arc-ds": "^0.5.2",
|
|
17
|
+
"@arcote.tech/platform": "^0.5.2",
|
|
18
18
|
"lucide-react": ">=0.400.0",
|
|
19
19
|
"react": ">=18.0.0",
|
|
20
20
|
"typescript": "^5.0.0"
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
/// <reference path="../arc.d.ts" />
|
|
2
2
|
import {
|
|
3
3
|
aggregate,
|
|
4
|
+
boolean,
|
|
4
5
|
date,
|
|
5
6
|
id,
|
|
6
7
|
string,
|
|
7
8
|
type ArcId,
|
|
8
9
|
} from "@arcote.tech/arc";
|
|
9
10
|
import type { Token } from "@arcote.tech/arc-auth";
|
|
10
|
-
|
|
11
|
+
// Stream sessions are managed by listeners, not mutations
|
|
11
12
|
|
|
12
|
-
// ─── ID
|
|
13
|
+
// ─── ID ──────────────���───────────────────────────────────────────
|
|
13
14
|
|
|
14
15
|
export const createMessageId = <const Name extends string>(data: {
|
|
15
16
|
name: Name;
|
|
@@ -41,11 +42,16 @@ export const createMessageAggregate = <
|
|
|
41
42
|
role: string(),
|
|
42
43
|
content: string(),
|
|
43
44
|
model: string().optional(),
|
|
44
|
-
|
|
45
|
-
|
|
45
|
+
toolName: string().optional(),
|
|
46
|
+
toolCallId: string().optional(),
|
|
47
|
+
sessionId: string().optional(),
|
|
48
|
+
previousResponseId: string().optional(),
|
|
49
|
+
isGenerating: boolean().optional(),
|
|
46
50
|
usage: string().optional(),
|
|
47
51
|
createdAt: date(),
|
|
48
52
|
})
|
|
53
|
+
|
|
54
|
+
// ─── messageSent — user sends a message ─────────────────────
|
|
49
55
|
.publicEvent(
|
|
50
56
|
"messageSent",
|
|
51
57
|
{
|
|
@@ -54,7 +60,8 @@ export const createMessageAggregate = <
|
|
|
54
60
|
sessionId: string(),
|
|
55
61
|
role: string(),
|
|
56
62
|
content: string(),
|
|
57
|
-
model: string(),
|
|
63
|
+
model: string().optional(),
|
|
64
|
+
isGenerating: boolean().optional(),
|
|
58
65
|
},
|
|
59
66
|
async (ctx, event) => {
|
|
60
67
|
const p = event.payload;
|
|
@@ -63,22 +70,24 @@ export const createMessageAggregate = <
|
|
|
63
70
|
role: p.role,
|
|
64
71
|
content: p.content,
|
|
65
72
|
model: p.model,
|
|
73
|
+
sessionId: p.sessionId,
|
|
74
|
+
isGenerating: p.isGenerating,
|
|
66
75
|
createdAt: event.createdAt,
|
|
67
76
|
});
|
|
68
77
|
},
|
|
69
78
|
)
|
|
70
79
|
|
|
80
|
+
// ─── assistantResponded — AI generates text ─────────────────
|
|
71
81
|
.publicEvent(
|
|
72
|
-
"
|
|
82
|
+
"assistantResponded",
|
|
73
83
|
{
|
|
74
84
|
messageId,
|
|
75
85
|
scopeId,
|
|
76
86
|
sessionId: string(),
|
|
77
87
|
content: string(),
|
|
78
88
|
model: string().optional(),
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
usage: string().optional(),
|
|
89
|
+
previousResponseId: string().optional(),
|
|
90
|
+
isGenerating: boolean().optional(),
|
|
82
91
|
},
|
|
83
92
|
async (ctx, event) => {
|
|
84
93
|
const p = event.payload;
|
|
@@ -87,28 +96,122 @@ export const createMessageAggregate = <
|
|
|
87
96
|
role: "assistant",
|
|
88
97
|
content: p.content,
|
|
89
98
|
model: p.model,
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
99
|
+
sessionId: p.sessionId,
|
|
100
|
+
previousResponseId: p.previousResponseId,
|
|
101
|
+
isGenerating: p.isGenerating,
|
|
93
102
|
createdAt: event.createdAt,
|
|
94
103
|
});
|
|
95
104
|
},
|
|
96
105
|
)
|
|
97
106
|
|
|
107
|
+
// ─── toolCalled — AI invokes a tool ─────────────────────────
|
|
108
|
+
.publicEvent(
|
|
109
|
+
"toolCalled",
|
|
110
|
+
{
|
|
111
|
+
messageId,
|
|
112
|
+
scopeId,
|
|
113
|
+
sessionId: string(),
|
|
114
|
+
toolName: string(),
|
|
115
|
+
toolCallId: string(),
|
|
116
|
+
content: string(),
|
|
117
|
+
previousResponseId: string().optional(),
|
|
118
|
+
},
|
|
119
|
+
async (ctx, event) => {
|
|
120
|
+
const p = event.payload;
|
|
121
|
+
await ctx.set(p.messageId, {
|
|
122
|
+
scopeId: p.scopeId,
|
|
123
|
+
role: "tool_call",
|
|
124
|
+
content: p.content,
|
|
125
|
+
toolName: p.toolName,
|
|
126
|
+
toolCallId: p.toolCallId,
|
|
127
|
+
sessionId: p.sessionId,
|
|
128
|
+
previousResponseId: p.previousResponseId,
|
|
129
|
+
createdAt: event.createdAt,
|
|
130
|
+
});
|
|
131
|
+
},
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
// ─── toolExecuted — server tool returns result ──────────────
|
|
135
|
+
.publicEvent(
|
|
136
|
+
"toolExecuted",
|
|
137
|
+
{
|
|
138
|
+
messageId,
|
|
139
|
+
scopeId,
|
|
140
|
+
sessionId: string(),
|
|
141
|
+
toolName: string(),
|
|
142
|
+
toolCallId: string(),
|
|
143
|
+
content: string(),
|
|
144
|
+
isError: boolean().optional(),
|
|
145
|
+
},
|
|
146
|
+
async (ctx, event) => {
|
|
147
|
+
const p = event.payload;
|
|
148
|
+
await ctx.set(p.messageId, {
|
|
149
|
+
scopeId: p.scopeId,
|
|
150
|
+
role: "tool_result",
|
|
151
|
+
content: p.content,
|
|
152
|
+
toolName: p.toolName,
|
|
153
|
+
toolCallId: p.toolCallId,
|
|
154
|
+
sessionId: p.sessionId,
|
|
155
|
+
createdAt: event.createdAt,
|
|
156
|
+
});
|
|
157
|
+
},
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
// ─── userResponded — user answers interactive tool ──────────
|
|
161
|
+
.publicEvent(
|
|
162
|
+
"userResponded",
|
|
163
|
+
{
|
|
164
|
+
messageId,
|
|
165
|
+
scopeId,
|
|
166
|
+
sessionId: string(),
|
|
167
|
+
toolName: string(),
|
|
168
|
+
toolCallId: string(),
|
|
169
|
+
content: string(),
|
|
170
|
+
},
|
|
171
|
+
async (ctx, event) => {
|
|
172
|
+
const p = event.payload;
|
|
173
|
+
await ctx.set(p.messageId, {
|
|
174
|
+
scopeId: p.scopeId,
|
|
175
|
+
role: "tool_result",
|
|
176
|
+
content: p.content,
|
|
177
|
+
toolName: p.toolName,
|
|
178
|
+
toolCallId: p.toolCallId,
|
|
179
|
+
sessionId: p.sessionId,
|
|
180
|
+
createdAt: event.createdAt,
|
|
181
|
+
});
|
|
182
|
+
},
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
// ─── generationCompleted — AI loop finished ────��────────────
|
|
186
|
+
.publicEvent(
|
|
187
|
+
"generationCompleted",
|
|
188
|
+
{
|
|
189
|
+
messageId,
|
|
190
|
+
sessionId: string(),
|
|
191
|
+
usage: string().optional(),
|
|
192
|
+
},
|
|
193
|
+
async (ctx, event) => {
|
|
194
|
+
const p = event.payload;
|
|
195
|
+
await ctx.set(p.messageId, {
|
|
196
|
+
isGenerating: false,
|
|
197
|
+
usage: p.usage,
|
|
198
|
+
} as any);
|
|
199
|
+
},
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
// ─── sendMessage — user sends message, creates session ──────
|
|
98
203
|
.mutateMethod(
|
|
99
204
|
"sendMessage",
|
|
100
205
|
(fn) => fn.withParams({
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
206
|
+
scopeId,
|
|
207
|
+
content: string().minLength(1),
|
|
208
|
+
model: string(),
|
|
209
|
+
}).handle(
|
|
210
|
+
ONLY_SERVER &&
|
|
106
211
|
(async (ctx, params) => {
|
|
107
212
|
const userMsgId = messageId.generate();
|
|
108
213
|
const sessionId = `session_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
|
|
109
214
|
|
|
110
|
-
createStreamSession(sessionId);
|
|
111
|
-
|
|
112
215
|
await ctx.messageSent.emit({
|
|
113
216
|
messageId: userMsgId,
|
|
114
217
|
scopeId: params.scopeId,
|
|
@@ -117,42 +220,168 @@ export const createMessageAggregate = <
|
|
|
117
220
|
content: params.content,
|
|
118
221
|
model: params.model,
|
|
119
222
|
});
|
|
120
|
-
|
|
121
|
-
return {
|
|
122
|
-
messageId: userMsgId,
|
|
123
|
-
sessionId,
|
|
124
|
-
};
|
|
223
|
+
return { messageId: userMsgId, sessionId };
|
|
125
224
|
}),
|
|
126
|
-
|
|
225
|
+
),
|
|
226
|
+
)
|
|
127
227
|
|
|
228
|
+
// ─── saveAssistantMessage ─────��─────────────────────────────
|
|
128
229
|
.mutateMethod(
|
|
129
|
-
"
|
|
230
|
+
"saveAssistantMessage",
|
|
130
231
|
(fn) => fn.withParams({
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
ONLY_SERVER &&
|
|
232
|
+
scopeId,
|
|
233
|
+
sessionId: string(),
|
|
234
|
+
content: string(),
|
|
235
|
+
model: string().optional(),
|
|
236
|
+
previousResponseId: string().optional(),
|
|
237
|
+
isGenerating: boolean().optional(),
|
|
238
|
+
}).handle(
|
|
239
|
+
ONLY_SERVER &&
|
|
140
240
|
(async (ctx, params) => {
|
|
141
|
-
const
|
|
142
|
-
await ctx.
|
|
143
|
-
messageId:
|
|
241
|
+
const msgId = messageId.generate();
|
|
242
|
+
await ctx.assistantResponded.emit({
|
|
243
|
+
messageId: msgId,
|
|
144
244
|
scopeId: params.scopeId,
|
|
145
245
|
sessionId: params.sessionId,
|
|
146
246
|
content: params.content,
|
|
147
247
|
model: params.model,
|
|
148
|
-
|
|
149
|
-
|
|
248
|
+
previousResponseId: params.previousResponseId,
|
|
249
|
+
isGenerating: params.isGenerating,
|
|
250
|
+
});
|
|
251
|
+
return { messageId: msgId };
|
|
252
|
+
}),
|
|
253
|
+
),
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
// ─── saveToolCall ────────────���──────────────────────────────
|
|
257
|
+
.mutateMethod(
|
|
258
|
+
"saveToolCall",
|
|
259
|
+
(fn) => fn.withParams({
|
|
260
|
+
scopeId,
|
|
261
|
+
sessionId: string(),
|
|
262
|
+
toolName: string(),
|
|
263
|
+
toolCallId: string(),
|
|
264
|
+
content: string(),
|
|
265
|
+
previousResponseId: string().optional(),
|
|
266
|
+
}).handle(
|
|
267
|
+
ONLY_SERVER &&
|
|
268
|
+
(async (ctx, params) => {
|
|
269
|
+
const msgId = messageId.generate();
|
|
270
|
+
await ctx.toolCalled.emit({
|
|
271
|
+
messageId: msgId,
|
|
272
|
+
scopeId: params.scopeId,
|
|
273
|
+
sessionId: params.sessionId,
|
|
274
|
+
toolName: params.toolName,
|
|
275
|
+
toolCallId: params.toolCallId,
|
|
276
|
+
content: params.content,
|
|
277
|
+
previousResponseId: params.previousResponseId,
|
|
278
|
+
});
|
|
279
|
+
return { messageId: msgId };
|
|
280
|
+
}),
|
|
281
|
+
),
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
// ─── saveToolResult — server tool executed ──────────────────
|
|
285
|
+
.mutateMethod(
|
|
286
|
+
"saveToolResult",
|
|
287
|
+
(fn) => fn.withParams({
|
|
288
|
+
scopeId,
|
|
289
|
+
sessionId: string(),
|
|
290
|
+
toolName: string(),
|
|
291
|
+
toolCallId: string(),
|
|
292
|
+
content: string(),
|
|
293
|
+
isError: boolean().optional(),
|
|
294
|
+
}).handle(
|
|
295
|
+
ONLY_SERVER &&
|
|
296
|
+
(async (ctx, params) => {
|
|
297
|
+
const msgId = messageId.generate();
|
|
298
|
+
await ctx.toolExecuted.emit({
|
|
299
|
+
messageId: msgId,
|
|
300
|
+
scopeId: params.scopeId,
|
|
301
|
+
sessionId: params.sessionId,
|
|
302
|
+
toolName: params.toolName,
|
|
303
|
+
toolCallId: params.toolCallId,
|
|
304
|
+
content: params.content,
|
|
305
|
+
isError: params.isError,
|
|
306
|
+
});
|
|
307
|
+
return { messageId: msgId };
|
|
308
|
+
}),
|
|
309
|
+
),
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
// ─── completeGeneration ─────────────────────────────────────
|
|
313
|
+
.mutateMethod(
|
|
314
|
+
"completeGeneration",
|
|
315
|
+
(fn) => fn.withParams({
|
|
316
|
+
generationMessageId: messageId,
|
|
317
|
+
sessionId: string(),
|
|
318
|
+
usage: string().optional(),
|
|
319
|
+
}).handle(
|
|
320
|
+
ONLY_SERVER &&
|
|
321
|
+
(async (ctx, params) => {
|
|
322
|
+
await ctx.generationCompleted.emit({
|
|
323
|
+
messageId: params.generationMessageId,
|
|
324
|
+
sessionId: params.sessionId,
|
|
150
325
|
usage: params.usage,
|
|
151
326
|
});
|
|
152
|
-
return {
|
|
327
|
+
return { ok: true };
|
|
328
|
+
}),
|
|
329
|
+
),
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
// ─── respondToTool — user answers interactive tool ──────────
|
|
333
|
+
.mutateMethod(
|
|
334
|
+
"respondToTool",
|
|
335
|
+
(fn) => fn.withParams({
|
|
336
|
+
scopeId,
|
|
337
|
+
toolCallId: string(),
|
|
338
|
+
toolName: string(),
|
|
339
|
+
result: string(),
|
|
340
|
+
}).handle(
|
|
341
|
+
ONLY_SERVER &&
|
|
342
|
+
(async (ctx, params) => {
|
|
343
|
+
const msgId = messageId.generate();
|
|
344
|
+
const sessionId = `session_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
|
|
345
|
+
|
|
346
|
+
await ctx.userResponded.emit({
|
|
347
|
+
messageId: msgId,
|
|
348
|
+
scopeId: params.scopeId,
|
|
349
|
+
sessionId,
|
|
350
|
+
toolName: params.toolName,
|
|
351
|
+
toolCallId: params.toolCallId,
|
|
352
|
+
content: params.result,
|
|
353
|
+
});
|
|
354
|
+
return { messageId: msgId, sessionId };
|
|
153
355
|
}),
|
|
154
|
-
|
|
356
|
+
),
|
|
357
|
+
)
|
|
358
|
+
|
|
359
|
+
// ─── startStage — initiate stage with greeting ──────────────
|
|
360
|
+
.mutateMethod(
|
|
361
|
+
"startStage",
|
|
362
|
+
(fn) => fn.withParams({
|
|
363
|
+
scopeId,
|
|
364
|
+
model: string().optional(),
|
|
365
|
+
}).handle(
|
|
366
|
+
ONLY_SERVER &&
|
|
367
|
+
(async (ctx, params) => {
|
|
368
|
+
const userMsgId = messageId.generate();
|
|
369
|
+
const sessionId = `session_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
|
|
370
|
+
|
|
371
|
+
await ctx.messageSent.emit({
|
|
372
|
+
messageId: userMsgId,
|
|
373
|
+
scopeId: params.scopeId,
|
|
374
|
+
sessionId,
|
|
375
|
+
role: "user",
|
|
376
|
+
content: "Rozpocznij ten etap. Przywitaj się i zadaj pierwsze pytanie.",
|
|
377
|
+
model: params.model ?? "gpt-5.4-nano",
|
|
378
|
+
});
|
|
379
|
+
return { messageId: userMsgId, sessionId };
|
|
380
|
+
}),
|
|
381
|
+
),
|
|
382
|
+
)
|
|
155
383
|
|
|
384
|
+
// ─── getByScope ─────────────────────────────────────────────
|
|
156
385
|
.clientQuery(
|
|
157
386
|
"getByScope",
|
|
158
387
|
(fn) => fn
|
|
@@ -162,7 +391,7 @@ export const createMessageAggregate = <
|
|
|
162
391
|
),
|
|
163
392
|
)
|
|
164
393
|
|
|
165
|
-
.protectBy(userToken, (p) => ({
|
|
394
|
+
.protectBy(userToken, (p: { workspaceId: string }) => ({ scopeId: p.workspaceId }));
|
|
166
395
|
};
|
|
167
396
|
|
|
168
397
|
export type MessageAggregate = ReturnType<typeof createMessageAggregate>;
|