@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/src/chat-builder.ts
CHANGED
|
@@ -1,99 +1,259 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
context,
|
|
3
|
+
arcFunction,
|
|
4
|
+
type ArcContextElement,
|
|
5
|
+
type ArcId,
|
|
6
|
+
type ArcFunction,
|
|
7
|
+
type DefaultFunctionData,
|
|
8
|
+
type Merge,
|
|
9
|
+
type $type,
|
|
10
|
+
} from "@arcote.tech/arc";
|
|
2
11
|
import type { AccountId, Token } from "@arcote.tech/arc-auth";
|
|
3
|
-
import type { AIConfig } from "@arcote.tech/arc-ai";
|
|
4
|
-
import
|
|
12
|
+
import type { AIConfig, ArcToolAny } from "@arcote.tech/arc-ai";
|
|
13
|
+
import { tool as createToolFactory } from "@arcote.tech/arc-ai";
|
|
14
|
+
import type { ArcTokenAny } from "@arcote.tech/arc";
|
|
15
|
+
import type { FnProtection, FnProtectionCheck } from "@arcote.tech/arc";
|
|
5
16
|
import { createMessageId, createMessageAggregate } from "./aggregates/message";
|
|
6
|
-
import { createAiGenerationListener } from "./listeners/ai-generation-listener";
|
|
17
|
+
import { createAiGenerationListener, createAiResumeListener } from "./listeners/ai-generation-listener";
|
|
7
18
|
import { createChatStreamRoute } from "./routes/chat-stream-route";
|
|
8
|
-
import {
|
|
19
|
+
import { createChatComponent } from "./react/chat-component";
|
|
20
|
+
import type { ComponentType } from "react";
|
|
9
21
|
|
|
10
|
-
// ───
|
|
22
|
+
// ─── Chat Data ──────────────────────────────────────────────────
|
|
11
23
|
|
|
12
|
-
export interface
|
|
13
|
-
|
|
14
|
-
|
|
24
|
+
export interface ArcChatData {
|
|
25
|
+
name: string;
|
|
26
|
+
identifyBy: ArcId<any> | null;
|
|
27
|
+
accountId: AccountId | null;
|
|
28
|
+
userToken: Token | null;
|
|
29
|
+
protectBy: Token | null;
|
|
30
|
+
protectByCheck: ((p: any) => any) | null;
|
|
31
|
+
ai: AIConfig | null;
|
|
32
|
+
instruction: ArcFunction<any> | null;
|
|
33
|
+
tools: ArcToolAny[];
|
|
34
|
+
maxExecutionCount: number;
|
|
35
|
+
toolChoice: "auto" | "required" | { type: "function"; name: string };
|
|
15
36
|
}
|
|
16
37
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
identifyBy:
|
|
20
|
-
|
|
21
|
-
|
|
38
|
+
const defaultChatData = {
|
|
39
|
+
name: "",
|
|
40
|
+
identifyBy: null,
|
|
41
|
+
accountId: null,
|
|
42
|
+
userToken: null,
|
|
43
|
+
protectBy: null,
|
|
44
|
+
protectByCheck: null,
|
|
45
|
+
ai: null,
|
|
46
|
+
instruction: null,
|
|
47
|
+
tools: [],
|
|
48
|
+
maxExecutionCount: 10,
|
|
49
|
+
toolChoice: "auto" as const,
|
|
50
|
+
} as const satisfies ArcChatData;
|
|
22
51
|
|
|
23
|
-
|
|
24
|
-
instructions: string;
|
|
25
|
-
tools?: ArcToolAny[];
|
|
26
|
-
clientTools?: ArcToolAny[];
|
|
27
|
-
}
|
|
52
|
+
type DefaultChatData = typeof defaultChatData;
|
|
28
53
|
|
|
29
|
-
// ───
|
|
54
|
+
// ─── ArcChat Builder ────────────────────────────────────────────
|
|
30
55
|
|
|
31
|
-
export
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
export class ArcChat<const Data extends ArcChatData = DefaultChatData> {
|
|
57
|
+
readonly data: Data;
|
|
58
|
+
|
|
59
|
+
constructor(data: Data) {
|
|
60
|
+
this.data = data;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
identifyBy<Id extends ArcId<any>>(id: Id) {
|
|
64
|
+
return new ArcChat<Merge<Data, { identifyBy: Id }>>({
|
|
65
|
+
...this.data,
|
|
66
|
+
identifyBy: id,
|
|
67
|
+
} as any);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
accountId(id: AccountId) {
|
|
71
|
+
return new ArcChat<Merge<Data, { accountId: typeof id }>>({
|
|
72
|
+
...this.data,
|
|
73
|
+
accountId: id,
|
|
74
|
+
} as any);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
userToken(token: Token) {
|
|
78
|
+
return new ArcChat<Merge<Data, { userToken: typeof token }>>({
|
|
79
|
+
...this.data,
|
|
80
|
+
userToken: token,
|
|
81
|
+
} as any);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
protectBy<T extends ArcTokenAny>(token: T, check: FnProtectionCheck<T>) {
|
|
85
|
+
return new ArcChat<Merge<Data, { protectBy: T; protectByCheck: typeof check }>>({
|
|
86
|
+
...this.data,
|
|
87
|
+
protectBy: token,
|
|
88
|
+
protectByCheck: check,
|
|
89
|
+
} as any);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
ai(config: AIConfig) {
|
|
93
|
+
return new ArcChat<Merge<Data, { ai: AIConfig }>>({
|
|
94
|
+
...this.data,
|
|
95
|
+
ai: config,
|
|
96
|
+
} as any);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
instruction(
|
|
100
|
+
configure: (fn: ArcFunction<DefaultFunctionData>) => ArcFunction<any>,
|
|
101
|
+
) {
|
|
102
|
+
const instructionFn = configure(arcFunction());
|
|
103
|
+
return new ArcChat<Merge<Data, { instruction: typeof instructionFn }>>({
|
|
104
|
+
...this.data,
|
|
105
|
+
instruction: instructionFn,
|
|
106
|
+
} as any);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
maxExecutionCount(count: number) {
|
|
110
|
+
return new ArcChat<Merge<Data, { maxExecutionCount: typeof count }>>({
|
|
111
|
+
...this.data,
|
|
112
|
+
maxExecutionCount: count,
|
|
113
|
+
} as any);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
toolChoice(choice: "auto" | "required" | { type: "function"; name: string }) {
|
|
117
|
+
return new ArcChat<Merge<Data, { toolChoice: typeof choice }>>({
|
|
118
|
+
...this.data,
|
|
119
|
+
toolChoice: choice,
|
|
120
|
+
} as any);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
createTool<const N extends string>(name: N) {
|
|
124
|
+
type IdType = Data["identifyBy"] extends ArcId<any>
|
|
125
|
+
? $type<Data["identifyBy"]>
|
|
126
|
+
: string;
|
|
127
|
+
const t = createToolFactory(name);
|
|
128
|
+
if (this.data.identifyBy) {
|
|
129
|
+
return t.identifyBy(this.data.identifyBy) as any as ReturnType<typeof t.identifyBy> & { __brand: IdType };
|
|
130
|
+
}
|
|
131
|
+
return t as any;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
useTools<Tools extends ArcToolAny[]>(tools: Tools) {
|
|
135
|
+
return new ArcChat<Merge<Data, { tools: Tools }>>({
|
|
136
|
+
...this.data,
|
|
137
|
+
tools,
|
|
138
|
+
} as any);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
build() {
|
|
142
|
+
const {
|
|
143
|
+
name,
|
|
144
|
+
identifyBy,
|
|
145
|
+
accountId,
|
|
146
|
+
userToken,
|
|
147
|
+
protectBy: protectByToken,
|
|
148
|
+
protectByCheck,
|
|
149
|
+
ai: aiConfig,
|
|
150
|
+
instruction: instructionFn,
|
|
151
|
+
tools,
|
|
152
|
+
maxExecutionCount,
|
|
153
|
+
toolChoice,
|
|
154
|
+
} = this.data;
|
|
155
|
+
|
|
156
|
+
if (!name) throw new Error("ArcChat: name is required");
|
|
157
|
+
if (!identifyBy) throw new Error("ArcChat: identifyBy is required");
|
|
158
|
+
if (!accountId) throw new Error("ArcChat: accountId is required");
|
|
159
|
+
if (!userToken) throw new Error("ArcChat: userToken is required");
|
|
160
|
+
if (!aiConfig) throw new Error("ArcChat: ai is required");
|
|
161
|
+
|
|
162
|
+
const messageId = createMessageId({ name });
|
|
163
|
+
|
|
164
|
+
const Message = createMessageAggregate({
|
|
165
|
+
name,
|
|
166
|
+
messageId,
|
|
167
|
+
scopeId: identifyBy,
|
|
168
|
+
accountId,
|
|
169
|
+
userToken: protectByToken ?? userToken,
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// Collect query/mutate from instruction + tools
|
|
173
|
+
const allQueryElements: ArcContextElement<any>[] = [];
|
|
174
|
+
const allMutationElements: ArcContextElement<any>[] = [];
|
|
175
|
+
|
|
176
|
+
if (instructionFn) {
|
|
177
|
+
for (const el of instructionFn.data.queryElements || []) {
|
|
178
|
+
if (!allQueryElements.includes(el)) allQueryElements.push(el);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
for (const t of tools) {
|
|
183
|
+
for (const el of t.queryElements) {
|
|
184
|
+
if (!allQueryElements.includes(el)) allQueryElements.push(el);
|
|
185
|
+
}
|
|
186
|
+
for (const el of t.mutationElements) {
|
|
187
|
+
if (!allMutationElements.includes(el)) allMutationElements.push(el);
|
|
59
188
|
}
|
|
60
189
|
}
|
|
190
|
+
|
|
191
|
+
const serverTools = tools.filter((t) => t.isServerTool);
|
|
192
|
+
const interactiveTools = tools.filter((t) => t.isInteractiveTool);
|
|
193
|
+
|
|
194
|
+
// Add ledger element to mutation deps if billing configured
|
|
195
|
+
const billingElements: ArcContextElement<any>[] = [];
|
|
196
|
+
if (aiConfig.billing) {
|
|
197
|
+
for (const el of aiConfig.billing.ledger.elements) {
|
|
198
|
+
if (!allMutationElements.includes(el)) allMutationElements.push(el);
|
|
199
|
+
if (!allQueryElements.includes(el)) allQueryElements.push(el);
|
|
200
|
+
billingElements.push(el);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const listenerConfig = {
|
|
205
|
+
name,
|
|
206
|
+
messageElement: Message,
|
|
207
|
+
resolveProvider: aiConfig.resolveProvider,
|
|
208
|
+
instruction: instructionFn ?? undefined,
|
|
209
|
+
serverTools,
|
|
210
|
+
interactiveTools,
|
|
211
|
+
allQueryElements,
|
|
212
|
+
allMutationElements,
|
|
213
|
+
maxExecutionCount,
|
|
214
|
+
toolChoice: toolChoice !== "auto" ? toolChoice : undefined,
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
const aiListener = createAiGenerationListener(listenerConfig);
|
|
218
|
+
const aiResumeListener = createAiResumeListener(listenerConfig);
|
|
219
|
+
|
|
220
|
+
const streamRoute = createChatStreamRoute({
|
|
221
|
+
name,
|
|
222
|
+
userToken,
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
const elements: ArcContextElement<any>[] = [
|
|
226
|
+
Message,
|
|
227
|
+
aiListener,
|
|
228
|
+
aiResumeListener,
|
|
229
|
+
streamRoute,
|
|
230
|
+
];
|
|
231
|
+
|
|
232
|
+
function toReactComponent(): ComponentType<{ scope: any; identifyBy: string }> {
|
|
233
|
+
return createChatComponent({
|
|
234
|
+
chatName: name,
|
|
235
|
+
tools,
|
|
236
|
+
messageElementName: `${name}Messages`,
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return {
|
|
241
|
+
context: context(elements),
|
|
242
|
+
elements,
|
|
243
|
+
messageId,
|
|
244
|
+
Message,
|
|
245
|
+
toReactComponent,
|
|
246
|
+
};
|
|
61
247
|
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// ─── Factory ────────────────────────────────────────────────────
|
|
62
251
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
tools: allTools,
|
|
69
|
-
clientTools: config.clientTools ?? [],
|
|
70
|
-
toolMutationElements: allMutationElements,
|
|
71
|
-
maxExecutionCount: config.maxExecutionCount ?? 10,
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
const streamRoute = createChatStreamRoute({
|
|
75
|
-
name: config.name,
|
|
76
|
-
userToken: config.userToken,
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
const toolResultsRoute = createToolResultsRoute({
|
|
80
|
-
name: config.name,
|
|
81
|
-
userToken: config.userToken,
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
const elements: ArcContextElement<any>[] = [
|
|
85
|
-
Message,
|
|
86
|
-
aiListener,
|
|
87
|
-
streamRoute,
|
|
88
|
-
toolResultsRoute,
|
|
89
|
-
];
|
|
90
|
-
|
|
91
|
-
return {
|
|
92
|
-
context: context(elements),
|
|
93
|
-
elements,
|
|
94
|
-
messageId,
|
|
95
|
-
Message,
|
|
96
|
-
};
|
|
252
|
+
export function chat<const Name extends string>(name: Name) {
|
|
253
|
+
return new ArcChat<Merge<DefaultChatData, { name: Name }>>({
|
|
254
|
+
...defaultChatData,
|
|
255
|
+
name,
|
|
256
|
+
} as any);
|
|
97
257
|
}
|
|
98
258
|
|
|
99
|
-
export type ChatConfig = ReturnType<
|
|
259
|
+
export type ChatConfig = ReturnType<ArcChat<any>["build"]>;
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// --- Builder API ---
|
|
2
|
-
export { chat } from "./chat-builder";
|
|
3
|
-
export type { ChatConfig,
|
|
2
|
+
export { chat, ArcChat } from "./chat-builder";
|
|
3
|
+
export type { ChatConfig, ArcChatData } from "./chat-builder";
|
|
4
4
|
|
|
5
5
|
// --- Aggregate factories & types ---
|
|
6
6
|
export { createMessageAggregate, createMessageId } from "./aggregates/message";
|
|
@@ -20,24 +20,6 @@ export type { AiGenerationListenerConfig } from "./listeners/ai-generation-liste
|
|
|
20
20
|
|
|
21
21
|
// --- Routes ---
|
|
22
22
|
export { createChatStreamRoute } from "./routes/chat-stream-route";
|
|
23
|
-
export { createToolResultsRoute } from "./routes/tool-results-route";
|
|
24
23
|
|
|
25
|
-
// ---
|
|
26
|
-
export {
|
|
27
|
-
Chat,
|
|
28
|
-
ChatMessage,
|
|
29
|
-
ChatInput,
|
|
30
|
-
QuestionTabs,
|
|
31
|
-
ToolUseBlock,
|
|
32
|
-
useChat,
|
|
33
|
-
} from "./react";
|
|
34
|
-
export type {
|
|
35
|
-
ChatMessageData,
|
|
36
|
-
ChatModel,
|
|
37
|
-
SendMessageOptions,
|
|
38
|
-
ToolUse,
|
|
39
|
-
Question,
|
|
40
|
-
QuestionAnswers,
|
|
41
|
-
UseChatConfig,
|
|
42
|
-
UseChatReturn,
|
|
43
|
-
} from "./react";
|
|
24
|
+
// --- Reusable tools ---
|
|
25
|
+
export { askQuestions } from "./tools/ask-questions";
|