@arcote.tech/arc-chat 0.4.9 → 0.5.1
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 +68 -78
- package/src/chat-builder.ts +75 -51
- package/src/index.ts +22 -4
- package/src/listeners/ai-generation-listener.ts +293 -0
- package/src/react/index.ts +4 -0
- package/src/react/use-chat.ts +260 -0
- package/src/routes/chat-stream-route.ts +31 -0
- package/src/routes/tool-results-route.ts +49 -0
- package/src/streaming/stream-registry.ts +146 -0
- package/src/aggregates/conversation.ts +0 -151
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arcote.tech/arc-chat",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.5.1",
|
|
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.
|
|
14
|
-
"@arcote.tech/arc-ai": "^0.
|
|
15
|
-
"@arcote.tech/arc-auth": "^0.
|
|
16
|
-
"@arcote.tech/arc-ds": "^0.
|
|
17
|
-
"@arcote.tech/platform": "^0.
|
|
13
|
+
"@arcote.tech/arc": "^0.5.1",
|
|
14
|
+
"@arcote.tech/arc-ai": "^0.5.1",
|
|
15
|
+
"@arcote.tech/arc-auth": "^0.5.1",
|
|
16
|
+
"@arcote.tech/arc-ds": "^0.5.1",
|
|
17
|
+
"@arcote.tech/platform": "^0.5.1",
|
|
18
18
|
"lucide-react": ">=0.400.0",
|
|
19
19
|
"react": ">=18.0.0",
|
|
20
20
|
"typescript": "^5.0.0"
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
type ArcId,
|
|
8
8
|
} from "@arcote.tech/arc";
|
|
9
9
|
import type { Token } from "@arcote.tech/arc-auth";
|
|
10
|
-
import
|
|
10
|
+
import { createStreamSession } from "../streaming/stream-registry";
|
|
11
11
|
|
|
12
12
|
// ─── ID ──────────────────────────────────────────────────────────
|
|
13
13
|
|
|
@@ -24,10 +24,9 @@ export type MessageId<Name extends string = string> = ReturnType<
|
|
|
24
24
|
export type MessageAggregateData = {
|
|
25
25
|
name: string;
|
|
26
26
|
messageId: ArcId<any>;
|
|
27
|
-
|
|
27
|
+
scopeId: ArcId<any>;
|
|
28
28
|
accountId: ArcId<any>;
|
|
29
29
|
userToken: Token;
|
|
30
|
-
resolveProvider: (model: string) => LLMProvider | undefined;
|
|
31
30
|
};
|
|
32
31
|
|
|
33
32
|
export const createMessageAggregate = <
|
|
@@ -35,31 +34,35 @@ export const createMessageAggregate = <
|
|
|
35
34
|
>(
|
|
36
35
|
data: Data,
|
|
37
36
|
) => {
|
|
38
|
-
const { messageId,
|
|
37
|
+
const { messageId, scopeId, userToken } = data;
|
|
39
38
|
|
|
40
39
|
return aggregate(`${data.name}Messages`, messageId, {
|
|
41
|
-
|
|
40
|
+
scopeId,
|
|
42
41
|
role: string(),
|
|
43
42
|
content: string(),
|
|
43
|
+
model: string().optional(),
|
|
44
44
|
toolCalls: string().optional(),
|
|
45
45
|
toolResults: string().optional(),
|
|
46
|
-
|
|
46
|
+
usage: string().optional(),
|
|
47
47
|
createdAt: date(),
|
|
48
48
|
})
|
|
49
49
|
.publicEvent(
|
|
50
50
|
"messageSent",
|
|
51
51
|
{
|
|
52
52
|
messageId,
|
|
53
|
-
|
|
53
|
+
scopeId,
|
|
54
|
+
sessionId: string(),
|
|
54
55
|
role: string(),
|
|
55
56
|
content: string(),
|
|
57
|
+
model: string(),
|
|
56
58
|
},
|
|
57
59
|
async (ctx, event) => {
|
|
58
60
|
const p = event.payload;
|
|
59
61
|
await ctx.set(p.messageId, {
|
|
60
|
-
|
|
62
|
+
scopeId: p.scopeId,
|
|
61
63
|
role: p.role,
|
|
62
64
|
content: p.content,
|
|
65
|
+
model: p.model,
|
|
63
66
|
createdAt: event.createdAt,
|
|
64
67
|
});
|
|
65
68
|
},
|
|
@@ -69,21 +72,24 @@ export const createMessageAggregate = <
|
|
|
69
72
|
"assistantMessageCompleted",
|
|
70
73
|
{
|
|
71
74
|
messageId,
|
|
72
|
-
|
|
75
|
+
scopeId,
|
|
76
|
+
sessionId: string(),
|
|
73
77
|
content: string(),
|
|
74
|
-
|
|
78
|
+
model: string().optional(),
|
|
75
79
|
toolCalls: string().optional(),
|
|
76
80
|
toolResults: string().optional(),
|
|
81
|
+
usage: string().optional(),
|
|
77
82
|
},
|
|
78
83
|
async (ctx, event) => {
|
|
79
84
|
const p = event.payload;
|
|
80
85
|
await ctx.set(p.messageId, {
|
|
81
|
-
|
|
86
|
+
scopeId: p.scopeId,
|
|
82
87
|
role: "assistant",
|
|
83
88
|
content: p.content,
|
|
84
|
-
|
|
89
|
+
model: p.model,
|
|
85
90
|
toolCalls: p.toolCalls,
|
|
86
91
|
toolResults: p.toolResults,
|
|
92
|
+
usage: p.usage,
|
|
87
93
|
createdAt: event.createdAt,
|
|
88
94
|
});
|
|
89
95
|
},
|
|
@@ -91,88 +97,72 @@ export const createMessageAggregate = <
|
|
|
91
97
|
|
|
92
98
|
.mutateMethod(
|
|
93
99
|
"sendMessage",
|
|
94
|
-
{
|
|
95
|
-
|
|
96
|
-
conversationId,
|
|
100
|
+
(fn) => fn.withParams({
|
|
101
|
+
scopeId,
|
|
97
102
|
content: string().minLength(1),
|
|
98
|
-
model: string()
|
|
99
|
-
|
|
100
|
-
webSearch: string().optional(),
|
|
101
|
-
},
|
|
102
|
-
},
|
|
103
|
+
model: string(),
|
|
104
|
+
}).handle(
|
|
103
105
|
ONLY_SERVER &&
|
|
104
106
|
(async (ctx, params) => {
|
|
105
107
|
const userMsgId = messageId.generate();
|
|
108
|
+
const sessionId = `session_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
|
|
109
|
+
|
|
110
|
+
createStreamSession(sessionId);
|
|
111
|
+
|
|
106
112
|
await ctx.messageSent.emit({
|
|
107
113
|
messageId: userMsgId,
|
|
108
|
-
|
|
114
|
+
scopeId: params.scopeId,
|
|
115
|
+
sessionId,
|
|
109
116
|
role: "user",
|
|
110
117
|
content: params.content,
|
|
118
|
+
model: params.model,
|
|
111
119
|
});
|
|
112
120
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
121
|
+
return {
|
|
122
|
+
messageId: userMsgId,
|
|
123
|
+
sessionId,
|
|
124
|
+
};
|
|
125
|
+
}),
|
|
126
|
+
))
|
|
116
127
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
const assistantMsgId = messageId.generate();
|
|
145
|
-
await ctx.assistantMessageCompleted.emit({
|
|
146
|
-
messageId: assistantMsgId,
|
|
147
|
-
conversationId: params.conversationId,
|
|
148
|
-
content: result.content,
|
|
149
|
-
toolCalls: result.toolCalls.length
|
|
150
|
-
? JSON.stringify(result.toolCalls)
|
|
151
|
-
: undefined,
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
return {
|
|
155
|
-
messageId: assistantMsgId,
|
|
156
|
-
content: result.content,
|
|
157
|
-
toolCalls: result.toolCalls,
|
|
158
|
-
finishReason: result.finishReason,
|
|
159
|
-
};
|
|
160
|
-
} catch (error) {
|
|
161
|
-
const errorMessage =
|
|
162
|
-
error instanceof Error ? error.message : "Unknown error";
|
|
163
|
-
return { error: errorMessage };
|
|
164
|
-
}
|
|
128
|
+
.mutateMethod(
|
|
129
|
+
"completeAssistantMessage",
|
|
130
|
+
(fn) => fn.withParams({
|
|
131
|
+
scopeId,
|
|
132
|
+
sessionId: string(),
|
|
133
|
+
content: string(),
|
|
134
|
+
model: string().optional(),
|
|
135
|
+
toolCalls: string().optional(),
|
|
136
|
+
toolResults: string().optional(),
|
|
137
|
+
usage: string().optional(),
|
|
138
|
+
}).handle(
|
|
139
|
+
ONLY_SERVER &&
|
|
140
|
+
(async (ctx, params) => {
|
|
141
|
+
const assistantMsgId = messageId.generate();
|
|
142
|
+
await ctx.assistantMessageCompleted.emit({
|
|
143
|
+
messageId: assistantMsgId,
|
|
144
|
+
scopeId: params.scopeId,
|
|
145
|
+
sessionId: params.sessionId,
|
|
146
|
+
content: params.content,
|
|
147
|
+
model: params.model,
|
|
148
|
+
toolCalls: params.toolCalls,
|
|
149
|
+
toolResults: params.toolResults,
|
|
150
|
+
usage: params.usage,
|
|
151
|
+
});
|
|
152
|
+
return { messageId: assistantMsgId };
|
|
165
153
|
}),
|
|
166
|
-
)
|
|
154
|
+
))
|
|
167
155
|
|
|
168
156
|
.clientQuery(
|
|
169
|
-
"
|
|
170
|
-
|
|
171
|
-
|
|
157
|
+
"getByScope",
|
|
158
|
+
(fn) => fn
|
|
159
|
+
.withParams({ scopeId: string() })
|
|
160
|
+
.handle(async (ctx, params) =>
|
|
161
|
+
ctx.$query.find({ where: { scopeId: params.scopeId } }),
|
|
162
|
+
),
|
|
172
163
|
)
|
|
173
164
|
|
|
174
|
-
.protectBy(userToken, (p) => ({ accountId: p.accountId }))
|
|
175
|
-
;
|
|
165
|
+
.protectBy(userToken, (p) => ({ accountId: p.accountId }));
|
|
176
166
|
};
|
|
177
167
|
|
|
178
168
|
export type MessageAggregate = ReturnType<typeof createMessageAggregate>;
|
package/src/chat-builder.ts
CHANGED
|
@@ -1,75 +1,99 @@
|
|
|
1
|
-
import {
|
|
2
|
-
context,
|
|
3
|
-
type ArcAggregateElement,
|
|
4
|
-
type ArcContextElement,
|
|
5
|
-
} from "@arcote.tech/arc";
|
|
1
|
+
import { context, type ArcContextElement, type ArcId } from "@arcote.tech/arc";
|
|
6
2
|
import type { AccountId, Token } from "@arcote.tech/arc-auth";
|
|
7
|
-
import type {
|
|
8
|
-
import {
|
|
3
|
+
import type { AIConfig } from "@arcote.tech/arc-ai";
|
|
4
|
+
import type { ArcToolAny, ToolContext } from "@arcote.tech/arc-ai";
|
|
9
5
|
import { createMessageId, createMessageAggregate } from "./aggregates/message";
|
|
6
|
+
import { createAiGenerationListener } from "./listeners/ai-generation-listener";
|
|
7
|
+
import { createChatStreamRoute } from "./routes/chat-stream-route";
|
|
8
|
+
import { createToolResultsRoute } from "./routes/tool-results-route";
|
|
10
9
|
|
|
11
|
-
|
|
12
|
-
ConvId,
|
|
13
|
-
MsgId,
|
|
14
|
-
Conversation extends ArcAggregateElement,
|
|
15
|
-
Message extends ArcAggregateElement,
|
|
16
|
-
Elements extends ArcContextElement<any>[],
|
|
17
|
-
> {
|
|
18
|
-
constructor(
|
|
19
|
-
private readonly _name: string,
|
|
20
|
-
readonly conversationId: ConvId,
|
|
21
|
-
readonly messageId: MsgId,
|
|
22
|
-
readonly Conversation: Conversation,
|
|
23
|
-
readonly Message: Message,
|
|
24
|
-
readonly elements: Elements,
|
|
25
|
-
) {}
|
|
10
|
+
// ─── Prepare Callback ───────────────────────────────────────────
|
|
26
11
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
12
|
+
export interface PrepareContext {
|
|
13
|
+
query: (element: ArcContextElement<any>) => any;
|
|
14
|
+
mutate: (element: ArcContextElement<any>) => any;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface PrepareParams {
|
|
18
|
+
content: string;
|
|
19
|
+
identifyBy: string;
|
|
20
|
+
model: string;
|
|
36
21
|
}
|
|
37
22
|
|
|
38
|
-
export
|
|
39
|
-
|
|
23
|
+
export interface PrepareResult {
|
|
24
|
+
instructions: string;
|
|
25
|
+
tools?: ArcToolAny[];
|
|
26
|
+
clientTools?: ArcToolAny[];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// ─── Chat Factory ───────────────────────────────────────────────
|
|
30
|
+
|
|
31
|
+
export function chat(config: {
|
|
32
|
+
name: string;
|
|
33
|
+
identifyBy: ArcId<any>;
|
|
40
34
|
accountId: AccountId;
|
|
41
35
|
userToken: Token;
|
|
42
|
-
ai:
|
|
36
|
+
ai: AIConfig;
|
|
37
|
+
tools?: ArcToolAny[];
|
|
38
|
+
clientTools?: ArcToolAny[];
|
|
39
|
+
prepare?: ((ctx: PrepareContext, params: PrepareParams) => Promise<PrepareResult>) | false;
|
|
40
|
+
maxExecutionCount?: number;
|
|
43
41
|
}) {
|
|
44
|
-
const conversationId = createConversationId({ name: config.name });
|
|
45
42
|
const messageId = createMessageId({ name: config.name });
|
|
46
43
|
|
|
47
|
-
const
|
|
44
|
+
const Message = createMessageAggregate({
|
|
48
45
|
name: config.name,
|
|
49
|
-
|
|
46
|
+
messageId,
|
|
47
|
+
scopeId: config.identifyBy,
|
|
50
48
|
accountId: config.accountId,
|
|
51
49
|
userToken: config.userToken,
|
|
52
50
|
});
|
|
53
|
-
const conversationElement = Conversation;
|
|
54
51
|
|
|
55
|
-
|
|
52
|
+
// Collect all mutation elements from server tools for listener registration
|
|
53
|
+
const allTools = config.tools ?? [];
|
|
54
|
+
const allMutationElements: ArcContextElement<any>[] = [];
|
|
55
|
+
for (const tool of allTools) {
|
|
56
|
+
for (const el of tool.mutationElements) {
|
|
57
|
+
if (!allMutationElements.includes(el)) {
|
|
58
|
+
allMutationElements.push(el);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const aiListener = createAiGenerationListener({
|
|
56
64
|
name: config.name,
|
|
57
|
-
|
|
58
|
-
conversationId,
|
|
59
|
-
accountId: config.accountId,
|
|
60
|
-
userToken: config.userToken,
|
|
65
|
+
messageElement: Message,
|
|
61
66
|
resolveProvider: config.ai.resolveProvider,
|
|
67
|
+
prepare: config.prepare || undefined,
|
|
68
|
+
tools: allTools,
|
|
69
|
+
clientTools: config.clientTools ?? [],
|
|
70
|
+
toolMutationElements: allMutationElements,
|
|
71
|
+
maxExecutionCount: config.maxExecutionCount ?? 10,
|
|
62
72
|
});
|
|
63
|
-
const messageElement = Message;
|
|
64
73
|
|
|
65
|
-
const
|
|
74
|
+
const streamRoute = createChatStreamRoute({
|
|
75
|
+
name: config.name,
|
|
76
|
+
userToken: config.userToken,
|
|
77
|
+
});
|
|
66
78
|
|
|
67
|
-
|
|
68
|
-
config.name,
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
79
|
+
const toolResultsRoute = createToolResultsRoute({
|
|
80
|
+
name: config.name,
|
|
81
|
+
userToken: config.userToken,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const elements: ArcContextElement<any>[] = [
|
|
72
85
|
Message,
|
|
86
|
+
aiListener,
|
|
87
|
+
streamRoute,
|
|
88
|
+
toolResultsRoute,
|
|
89
|
+
];
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
context: context(elements),
|
|
73
93
|
elements,
|
|
74
|
-
|
|
94
|
+
messageId,
|
|
95
|
+
Message,
|
|
96
|
+
};
|
|
75
97
|
}
|
|
98
|
+
|
|
99
|
+
export type ChatConfig = ReturnType<typeof chat>;
|
package/src/index.ts
CHANGED
|
@@ -1,19 +1,35 @@
|
|
|
1
1
|
// --- Builder API ---
|
|
2
|
-
export { chat
|
|
2
|
+
export { chat } from "./chat-builder";
|
|
3
|
+
export type { ChatConfig, PrepareContext, PrepareParams, PrepareResult } from "./chat-builder";
|
|
3
4
|
|
|
4
5
|
// --- Aggregate factories & types ---
|
|
5
|
-
export { createConversationAggregate, createConversationId } from "./aggregates/conversation";
|
|
6
|
-
export type { ConversationAggregate, ConversationId } from "./aggregates/conversation";
|
|
7
6
|
export { createMessageAggregate, createMessageId } from "./aggregates/message";
|
|
8
7
|
export type { MessageAggregate, MessageId } from "./aggregates/message";
|
|
9
8
|
|
|
10
|
-
// ---
|
|
9
|
+
// --- Streaming ---
|
|
10
|
+
export {
|
|
11
|
+
createStreamSession,
|
|
12
|
+
getStreamSession,
|
|
13
|
+
deleteStreamSession,
|
|
14
|
+
} from "./streaming/stream-registry";
|
|
15
|
+
export type { StreamSession } from "./streaming/stream-registry";
|
|
16
|
+
|
|
17
|
+
// --- Listener ---
|
|
18
|
+
export { createAiGenerationListener } from "./listeners/ai-generation-listener";
|
|
19
|
+
export type { AiGenerationListenerConfig } from "./listeners/ai-generation-listener";
|
|
20
|
+
|
|
21
|
+
// --- Routes ---
|
|
22
|
+
export { createChatStreamRoute } from "./routes/chat-stream-route";
|
|
23
|
+
export { createToolResultsRoute } from "./routes/tool-results-route";
|
|
24
|
+
|
|
25
|
+
// --- React components & hooks ---
|
|
11
26
|
export {
|
|
12
27
|
Chat,
|
|
13
28
|
ChatMessage,
|
|
14
29
|
ChatInput,
|
|
15
30
|
QuestionTabs,
|
|
16
31
|
ToolUseBlock,
|
|
32
|
+
useChat,
|
|
17
33
|
} from "./react";
|
|
18
34
|
export type {
|
|
19
35
|
ChatMessageData,
|
|
@@ -22,4 +38,6 @@ export type {
|
|
|
22
38
|
ToolUse,
|
|
23
39
|
Question,
|
|
24
40
|
QuestionAnswers,
|
|
41
|
+
UseChatConfig,
|
|
42
|
+
UseChatReturn,
|
|
25
43
|
} from "./react";
|