@adminforth/agent 1.43.29 → 1.44.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/agentEvents.ts +61 -0
- package/agentResponseEvents.ts +1 -206
- package/build.log +1 -1
- package/dist/agent/checkpointer.d.ts +29 -0
- package/dist/agent/languageDetect.d.ts +10 -0
- package/dist/agent/middleware/apiBasedTools.d.ts +3 -0
- package/dist/agent/middleware/openAiResponsesContinuation.d.ts +1 -0
- package/dist/agent/middleware/sequenceDebug.d.ts +46 -0
- package/dist/agent/simpleAgent.d.ts +61 -0
- package/dist/agent/skills/registry.d.ts +13 -0
- package/dist/agent/systemPrompt.d.ts +11 -0
- package/dist/agent/toolCallEvents.d.ts +27 -0
- package/dist/agent/tools/apiTool.d.ts +6 -0
- package/dist/agent/tools/fetchSkill.d.ts +8 -0
- package/dist/agent/tools/fetchToolSchema.d.ts +9 -0
- package/dist/agent/tools/getUserLocation.d.ts +8 -0
- package/dist/agent/tools/index.d.ts +4 -0
- package/dist/agentEvents.d.ts +52 -0
- package/dist/agentEvents.js +1 -0
- package/dist/agentResponseEvents.d.ts +1 -0
- package/dist/agentResponseEvents.js +1 -144
- package/dist/apiBasedTools.d.ts +29 -0
- package/dist/index.d.ts +58 -0
- package/dist/index.js +222 -43
- package/dist/sanitizeSpeechText.d.ts +1 -0
- package/dist/surfaces/web-sse/createSseEventEmitter.d.ts +14 -0
- package/dist/surfaces/web-sse/createSseEventEmitter.js +196 -0
- package/dist/types.d.ts +94 -0
- package/index.ts +250 -46
- package/package.json +2 -2
- package/surfaces/web-sse/createSseEventEmitter.ts +261 -0
- package/tsconfig.json +1 -0
- package/types.ts +6 -0
|
@@ -1,144 +1 @@
|
|
|
1
|
-
|
|
2
|
-
export function createAgentEventStream(res, options = {}) {
|
|
3
|
-
let isStreamClosed = false;
|
|
4
|
-
let activeBlock = null;
|
|
5
|
-
res.writeHead(200, Object.assign({ "Content-Type": "text/event-stream", "Cache-Control": "no-cache", "Connection": "keep-alive" }, (options.vercelAiUiMessageStream
|
|
6
|
-
? { "x-vercel-ai-ui-message-stream": "v1" }
|
|
7
|
-
: {})));
|
|
8
|
-
const stream = {
|
|
9
|
-
send(obj) {
|
|
10
|
-
if (isStreamClosed || res.writableEnded || res.destroyed) {
|
|
11
|
-
return;
|
|
12
|
-
}
|
|
13
|
-
res.write(`data: ${JSON.stringify(obj)}\n\n`);
|
|
14
|
-
},
|
|
15
|
-
endActiveBlock() {
|
|
16
|
-
if (!activeBlock) {
|
|
17
|
-
return;
|
|
18
|
-
}
|
|
19
|
-
stream.send({
|
|
20
|
-
type: `${activeBlock.type}-end`,
|
|
21
|
-
id: activeBlock.id,
|
|
22
|
-
});
|
|
23
|
-
activeBlock = null;
|
|
24
|
-
},
|
|
25
|
-
startBlock(type) {
|
|
26
|
-
if ((activeBlock === null || activeBlock === void 0 ? void 0 : activeBlock.type) === type) {
|
|
27
|
-
return activeBlock.id;
|
|
28
|
-
}
|
|
29
|
-
stream.endActiveBlock();
|
|
30
|
-
const id = randomUUID();
|
|
31
|
-
activeBlock = { type, id };
|
|
32
|
-
stream.send({
|
|
33
|
-
type: `${type}-start`,
|
|
34
|
-
id,
|
|
35
|
-
});
|
|
36
|
-
return id;
|
|
37
|
-
},
|
|
38
|
-
start(messageId) {
|
|
39
|
-
stream.send({
|
|
40
|
-
type: "start",
|
|
41
|
-
messageId,
|
|
42
|
-
});
|
|
43
|
-
},
|
|
44
|
-
textDelta(delta) {
|
|
45
|
-
const textId = stream.startBlock("text");
|
|
46
|
-
stream.send({
|
|
47
|
-
type: "text-delta",
|
|
48
|
-
id: textId,
|
|
49
|
-
delta,
|
|
50
|
-
});
|
|
51
|
-
},
|
|
52
|
-
reasoningDelta(delta) {
|
|
53
|
-
const reasoningId = stream.startBlock("reasoning");
|
|
54
|
-
stream.send({
|
|
55
|
-
type: "reasoning-delta",
|
|
56
|
-
id: reasoningId,
|
|
57
|
-
delta,
|
|
58
|
-
});
|
|
59
|
-
},
|
|
60
|
-
toolCall(event) {
|
|
61
|
-
if (options.closeActiveBlockOnToolStart && event.phase === "start") {
|
|
62
|
-
stream.endActiveBlock();
|
|
63
|
-
}
|
|
64
|
-
stream.send({
|
|
65
|
-
type: "data-tool-call",
|
|
66
|
-
data: event,
|
|
67
|
-
});
|
|
68
|
-
},
|
|
69
|
-
transcript(text, language) {
|
|
70
|
-
stream.send({
|
|
71
|
-
type: "transcript",
|
|
72
|
-
data: {
|
|
73
|
-
text,
|
|
74
|
-
language,
|
|
75
|
-
},
|
|
76
|
-
});
|
|
77
|
-
},
|
|
78
|
-
response(text, sessionId, turnId) {
|
|
79
|
-
stream.send({
|
|
80
|
-
type: "response",
|
|
81
|
-
data: {
|
|
82
|
-
text,
|
|
83
|
-
sessionId,
|
|
84
|
-
turnId,
|
|
85
|
-
},
|
|
86
|
-
});
|
|
87
|
-
},
|
|
88
|
-
speechResponse(transcript, response, sessionId, turnId) {
|
|
89
|
-
stream.send({
|
|
90
|
-
type: "speech-response",
|
|
91
|
-
data: {
|
|
92
|
-
transcript,
|
|
93
|
-
response,
|
|
94
|
-
sessionId,
|
|
95
|
-
turnId,
|
|
96
|
-
},
|
|
97
|
-
});
|
|
98
|
-
},
|
|
99
|
-
audioStart(mimeType, format, sampleRate, channelCount, bitsPerSample) {
|
|
100
|
-
stream.send({
|
|
101
|
-
type: "audio-start",
|
|
102
|
-
data: {
|
|
103
|
-
mimeType,
|
|
104
|
-
format,
|
|
105
|
-
sampleRate,
|
|
106
|
-
channelCount,
|
|
107
|
-
bitsPerSample,
|
|
108
|
-
},
|
|
109
|
-
});
|
|
110
|
-
},
|
|
111
|
-
audioDelta(value) {
|
|
112
|
-
stream.send({
|
|
113
|
-
type: "audio-delta",
|
|
114
|
-
data: {
|
|
115
|
-
base64: Buffer.from(value).toString("base64"),
|
|
116
|
-
},
|
|
117
|
-
});
|
|
118
|
-
},
|
|
119
|
-
audioDone() {
|
|
120
|
-
stream.send({
|
|
121
|
-
type: "audio-done",
|
|
122
|
-
});
|
|
123
|
-
},
|
|
124
|
-
error(error) {
|
|
125
|
-
stream.send({
|
|
126
|
-
type: "error",
|
|
127
|
-
error,
|
|
128
|
-
});
|
|
129
|
-
},
|
|
130
|
-
end() {
|
|
131
|
-
if (isStreamClosed || res.writableEnded || res.destroyed) {
|
|
132
|
-
return;
|
|
133
|
-
}
|
|
134
|
-
stream.endActiveBlock();
|
|
135
|
-
stream.send({
|
|
136
|
-
type: "finish",
|
|
137
|
-
});
|
|
138
|
-
res.write("data: [DONE]\n\n");
|
|
139
|
-
isStreamClosed = true;
|
|
140
|
-
res.end();
|
|
141
|
-
},
|
|
142
|
-
};
|
|
143
|
-
return stream;
|
|
144
|
-
}
|
|
1
|
+
export { createSseEventEmitter } from "./surfaces/web-sse/createSseEventEmitter.js";
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type AdminUser, type IAdminForth } from 'adminforth';
|
|
2
|
+
export type ApiBasedToolCallParams = {
|
|
3
|
+
adminUser?: AdminUser;
|
|
4
|
+
adminuser?: AdminUser;
|
|
5
|
+
abortSignal?: AbortSignal;
|
|
6
|
+
inputs?: Record<string, unknown>;
|
|
7
|
+
userTimeZone?: string;
|
|
8
|
+
acceptLanguage?: string;
|
|
9
|
+
};
|
|
10
|
+
export type ApiBasedTool = {
|
|
11
|
+
description?: string;
|
|
12
|
+
input_schema?: unknown;
|
|
13
|
+
output_schema?: unknown;
|
|
14
|
+
call: (params?: ApiBasedToolCallParams) => Promise<string>;
|
|
15
|
+
};
|
|
16
|
+
export declare function formatApiBasedToolCall(params: {
|
|
17
|
+
adminforth: IAdminForth;
|
|
18
|
+
adminUser?: AdminUser;
|
|
19
|
+
inputs?: Record<string, unknown>;
|
|
20
|
+
toolName: string;
|
|
21
|
+
userTimeZone?: string;
|
|
22
|
+
}): Promise<string | undefined>;
|
|
23
|
+
export declare function prepareApiBasedTools(adminforth: IAdminForth, hiddenResourceIds?: Iterable<string>): Record<string, ApiBasedTool>;
|
|
24
|
+
export declare function serializeApiBasedTool(tool: ApiBasedTool | undefined): {
|
|
25
|
+
description: string | undefined;
|
|
26
|
+
input_schema: unknown;
|
|
27
|
+
output_schema: unknown;
|
|
28
|
+
call: string;
|
|
29
|
+
} | null;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { AdminUser, AdminForthResource, IAdminForth, IHttpServer } from "adminforth";
|
|
2
|
+
import { AdminForthPlugin } from "adminforth";
|
|
3
|
+
import type { PluginOptions } from './types.js';
|
|
4
|
+
import { createSequenceDebugCollector } from "./agent/middleware/sequenceDebug.js";
|
|
5
|
+
import { type PreviousUserMessage } from "./agent/languageDetect.js";
|
|
6
|
+
import type { AgentEventEmitter } from "./agentEvents.js";
|
|
7
|
+
import type { CurrentPageContext } from "./agent/tools/getUserLocation.js";
|
|
8
|
+
export type { AgentEvent, AgentEventEmitter } from "./agentEvents.js";
|
|
9
|
+
type AgentTurnRunInput = {
|
|
10
|
+
prompt: string;
|
|
11
|
+
sessionId: string;
|
|
12
|
+
turnId: string;
|
|
13
|
+
previousUserMessages: PreviousUserMessage[];
|
|
14
|
+
modeName?: string | null;
|
|
15
|
+
userTimeZone: string;
|
|
16
|
+
currentPage?: CurrentPageContext;
|
|
17
|
+
abortSignal?: AbortSignal;
|
|
18
|
+
adminUser: AdminUser;
|
|
19
|
+
sequenceDebugCollector: ReturnType<typeof createSequenceDebugCollector>;
|
|
20
|
+
emit?: AgentEventEmitter;
|
|
21
|
+
};
|
|
22
|
+
type RunAndPersistAgentResponseInput = Omit<AgentTurnRunInput, "turnId" | "sequenceDebugCollector" | "previousUserMessages"> & {
|
|
23
|
+
failureLogMessage: string;
|
|
24
|
+
abortLogMessage: string;
|
|
25
|
+
};
|
|
26
|
+
type HandleTurnInput = Omit<RunAndPersistAgentResponseInput, "failureLogMessage" | "abortLogMessage"> & {
|
|
27
|
+
emit: AgentEventEmitter;
|
|
28
|
+
failureLogMessage?: string;
|
|
29
|
+
abortLogMessage?: string;
|
|
30
|
+
};
|
|
31
|
+
export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
32
|
+
options: PluginOptions;
|
|
33
|
+
agentSystemPromptPromise: Promise<string>;
|
|
34
|
+
private checkpointer;
|
|
35
|
+
private parseBody;
|
|
36
|
+
private createNewTurn;
|
|
37
|
+
private getSessionTurns;
|
|
38
|
+
private getPreviousUserMessages;
|
|
39
|
+
private getChatSurfaceSessionId;
|
|
40
|
+
private getOrCreateChatSurfaceSession;
|
|
41
|
+
private getCheckpointer;
|
|
42
|
+
private getInternalAgentResourceIds;
|
|
43
|
+
constructor(options: PluginOptions);
|
|
44
|
+
modifyResourceConfig(adminforth: IAdminForth, resourceConfig: AdminForthResource): Promise<void>;
|
|
45
|
+
validateConfigAfterDiscover(adminforth: IAdminForth, resourceConfig: AdminForthResource): void;
|
|
46
|
+
instanceUniqueRepresentation(pluginOptions: any): string;
|
|
47
|
+
private runAgentTurn;
|
|
48
|
+
private runAndPersistAgentResponse;
|
|
49
|
+
handleTurn(input: HandleTurnInput): Promise<{
|
|
50
|
+
text: string;
|
|
51
|
+
turnId: any;
|
|
52
|
+
aborted: boolean;
|
|
53
|
+
failed: boolean;
|
|
54
|
+
}>;
|
|
55
|
+
private createChatSurfaceEventEmitter;
|
|
56
|
+
private handleChatSurfaceMessage;
|
|
57
|
+
setupEndpoints(server: IHttpServer): void;
|
|
58
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -24,7 +24,7 @@ import { AdminForthCheckpointSaver } from "./agent/checkpointer.js";
|
|
|
24
24
|
import { createSequenceDebugCollector } from "./agent/middleware/sequenceDebug.js";
|
|
25
25
|
import { detectUserLanguage } from "./agent/languageDetect.js";
|
|
26
26
|
import { prepareApiBasedTools as buildApiBasedTools } from './apiBasedTools.js';
|
|
27
|
-
import {
|
|
27
|
+
import { createSseEventEmitter } from "./surfaces/web-sse/createSseEventEmitter.js";
|
|
28
28
|
import { appendCustomSystemPrompt, buildAgentSystemPrompt, buildAgentTurnSystemPrompt, DEFAULT_AGENT_SYSTEM_PROMPT } from "./agent/systemPrompt.js";
|
|
29
29
|
import { sanitizeSpeechText } from "./sanitizeSpeechText.js";
|
|
30
30
|
const agentResponseBodySchema = z.object({
|
|
@@ -104,6 +104,25 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
104
104
|
}));
|
|
105
105
|
});
|
|
106
106
|
}
|
|
107
|
+
getChatSurfaceSessionId(incoming) {
|
|
108
|
+
return `${incoming.surface}:${incoming.externalConversationId}`;
|
|
109
|
+
}
|
|
110
|
+
getOrCreateChatSurfaceSession(incoming, adminUser) {
|
|
111
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
112
|
+
const sessionId = this.getChatSurfaceSessionId(incoming);
|
|
113
|
+
const sessionResource = this.adminforth.resource(this.options.sessionResource.resourceId);
|
|
114
|
+
const session = yield sessionResource.get([Filters.EQ(this.options.sessionResource.idField, sessionId)]);
|
|
115
|
+
if (session) {
|
|
116
|
+
return sessionId;
|
|
117
|
+
}
|
|
118
|
+
yield sessionResource.create({
|
|
119
|
+
[this.options.sessionResource.idField]: sessionId,
|
|
120
|
+
[this.options.sessionResource.titleField]: incoming.prompt.slice(0, 40) || "New Session",
|
|
121
|
+
[this.options.sessionResource.askerIdField]: adminUser.pk,
|
|
122
|
+
});
|
|
123
|
+
return sessionId;
|
|
124
|
+
});
|
|
125
|
+
}
|
|
107
126
|
getCheckpointer() {
|
|
108
127
|
if (this.checkpointer)
|
|
109
128
|
return this.checkpointer;
|
|
@@ -170,8 +189,11 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
170
189
|
});
|
|
171
190
|
}
|
|
172
191
|
validateConfigAfterDiscover(adminforth, resourceConfig) {
|
|
173
|
-
var _a;
|
|
192
|
+
var _a, _b;
|
|
174
193
|
(_a = this.options.audioAdapter) === null || _a === void 0 ? void 0 : _a.validate();
|
|
194
|
+
for (const chatSurfaceAdapter of (_b = this.options.chatSurfaceAdapters) !== null && _b !== void 0 ? _b : []) {
|
|
195
|
+
chatSurfaceAdapter.validate();
|
|
196
|
+
}
|
|
175
197
|
this.agentSystemPromptPromise = buildAgentSystemPrompt(adminforth, this.getInternalAgentResourceIds())
|
|
176
198
|
.then((systemPrompt) => appendCustomSystemPrompt(systemPrompt, this.options.systemPrompt));
|
|
177
199
|
}
|
|
@@ -238,7 +260,10 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
238
260
|
emitToolCallEvent: (event) => {
|
|
239
261
|
var _a;
|
|
240
262
|
input.sequenceDebugCollector.handleToolCallEvent(event);
|
|
241
|
-
(_a = input.
|
|
263
|
+
void ((_a = input.emit) === null || _a === void 0 ? void 0 : _a.call(input, {
|
|
264
|
+
type: "tool-call",
|
|
265
|
+
data: event,
|
|
266
|
+
}));
|
|
242
267
|
},
|
|
243
268
|
sequenceDebugSink: input.sequenceDebugCollector,
|
|
244
269
|
});
|
|
@@ -271,11 +296,17 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
271
296
|
.map((b) => { var _a; return String((_a = b.text) !== null && _a !== void 0 ? _a : ""); })
|
|
272
297
|
.join("");
|
|
273
298
|
if (reasoningDelta) {
|
|
274
|
-
(_h = input.
|
|
299
|
+
yield ((_h = input.emit) === null || _h === void 0 ? void 0 : _h.call(input, {
|
|
300
|
+
type: "reasoning-delta",
|
|
301
|
+
delta: reasoningDelta,
|
|
302
|
+
}));
|
|
275
303
|
}
|
|
276
304
|
if (textDelta) {
|
|
277
305
|
fullResponse += textDelta;
|
|
278
|
-
(_j = input.
|
|
306
|
+
yield ((_j = input.emit) === null || _j === void 0 ? void 0 : _j.call(input, {
|
|
307
|
+
type: "text-delta",
|
|
308
|
+
delta: textDelta,
|
|
309
|
+
}));
|
|
279
310
|
}
|
|
280
311
|
}
|
|
281
312
|
}
|
|
@@ -293,7 +324,7 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
293
324
|
}
|
|
294
325
|
runAndPersistAgentResponse(input) {
|
|
295
326
|
return __awaiter(this, void 0, void 0, function* () {
|
|
296
|
-
var _a
|
|
327
|
+
var _a;
|
|
297
328
|
const previousUserMessages = yield this.getPreviousUserMessages(input.sessionId);
|
|
298
329
|
const turnId = yield this.createNewTurn(input.sessionId, input.prompt);
|
|
299
330
|
yield this.adminforth.resource(this.options.sessionResource.resourceId).update(input.sessionId, {
|
|
@@ -315,9 +346,7 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
315
346
|
abortSignal: input.abortSignal,
|
|
316
347
|
adminUser: input.adminUser,
|
|
317
348
|
sequenceDebugCollector,
|
|
318
|
-
|
|
319
|
-
emitReasoningDelta: input.emitReasoningDelta,
|
|
320
|
-
emitTextDelta: input.emitTextDelta,
|
|
349
|
+
emit: input.emit,
|
|
321
350
|
});
|
|
322
351
|
fullResponse = agentResponse.text;
|
|
323
352
|
}
|
|
@@ -330,7 +359,6 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
330
359
|
failed = true;
|
|
331
360
|
fullResponse = getErrorMessage(error);
|
|
332
361
|
logger.error(`${input.failureLogMessage}:\n${fullResponse}`);
|
|
333
|
-
(_b = input.emitErrorResponse) === null || _b === void 0 ? void 0 : _b.call(input, fullResponse);
|
|
334
362
|
}
|
|
335
363
|
}
|
|
336
364
|
sequenceDebugCollector.flush();
|
|
@@ -349,7 +377,125 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
349
377
|
};
|
|
350
378
|
});
|
|
351
379
|
}
|
|
380
|
+
handleTurn(input) {
|
|
381
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
382
|
+
var _a, _b;
|
|
383
|
+
yield input.emit({
|
|
384
|
+
type: "turn-started",
|
|
385
|
+
messageId: randomUUID(),
|
|
386
|
+
});
|
|
387
|
+
const agentResponse = yield this.runAndPersistAgentResponse({
|
|
388
|
+
prompt: input.prompt,
|
|
389
|
+
sessionId: input.sessionId,
|
|
390
|
+
modeName: input.modeName,
|
|
391
|
+
userTimeZone: input.userTimeZone,
|
|
392
|
+
currentPage: input.currentPage,
|
|
393
|
+
abortSignal: input.abortSignal,
|
|
394
|
+
adminUser: input.adminUser,
|
|
395
|
+
emit: input.emit,
|
|
396
|
+
failureLogMessage: (_a = input.failureLogMessage) !== null && _a !== void 0 ? _a : "Agent response failed",
|
|
397
|
+
abortLogMessage: (_b = input.abortLogMessage) !== null && _b !== void 0 ? _b : "Agent response aborted",
|
|
398
|
+
});
|
|
399
|
+
if (agentResponse.failed) {
|
|
400
|
+
yield input.emit({
|
|
401
|
+
type: "error",
|
|
402
|
+
error: agentResponse.text,
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
else if (!agentResponse.aborted) {
|
|
406
|
+
yield input.emit({
|
|
407
|
+
type: "response",
|
|
408
|
+
text: agentResponse.text,
|
|
409
|
+
sessionId: input.sessionId,
|
|
410
|
+
turnId: agentResponse.turnId,
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
yield input.emit({
|
|
414
|
+
type: "finish",
|
|
415
|
+
});
|
|
416
|
+
return agentResponse;
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
createChatSurfaceEventEmitter(sink) {
|
|
420
|
+
return (event) => __awaiter(this, void 0, void 0, function* () {
|
|
421
|
+
if (event.type === "text-delta") {
|
|
422
|
+
yield sink.emit({
|
|
423
|
+
type: "text_delta",
|
|
424
|
+
delta: event.delta,
|
|
425
|
+
});
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
if (event.type === "response") {
|
|
429
|
+
yield sink.emit({
|
|
430
|
+
type: "done",
|
|
431
|
+
text: event.text,
|
|
432
|
+
});
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
if (event.type === "error") {
|
|
436
|
+
yield sink.emit({
|
|
437
|
+
type: "error",
|
|
438
|
+
message: event.error,
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
handleChatSurfaceMessage(adapter, incoming, sink) {
|
|
444
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
445
|
+
var _a;
|
|
446
|
+
const adminUser = yield adapter.resolveAdminUser({
|
|
447
|
+
adminforth: this.adminforth,
|
|
448
|
+
incoming,
|
|
449
|
+
});
|
|
450
|
+
if (!adminUser) {
|
|
451
|
+
yield sink.emit({
|
|
452
|
+
type: "error",
|
|
453
|
+
message: "This chat account is not authorized to use AdminForth Agent.",
|
|
454
|
+
});
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
yield this.handleTurn({
|
|
458
|
+
prompt: incoming.prompt,
|
|
459
|
+
sessionId: yield this.getOrCreateChatSurfaceSession(incoming, adminUser),
|
|
460
|
+
modeName: incoming.modeName,
|
|
461
|
+
userTimeZone: (_a = incoming.userTimeZone) !== null && _a !== void 0 ? _a : "UTC",
|
|
462
|
+
adminUser,
|
|
463
|
+
emit: this.createChatSurfaceEventEmitter(sink),
|
|
464
|
+
failureLogMessage: `Agent ${incoming.surface} surface response failed`,
|
|
465
|
+
abortLogMessage: `Agent ${incoming.surface} surface response aborted`,
|
|
466
|
+
});
|
|
467
|
+
});
|
|
468
|
+
}
|
|
352
469
|
setupEndpoints(server) {
|
|
470
|
+
var _a;
|
|
471
|
+
for (const adapter of (_a = this.options.chatSurfaceAdapters) !== null && _a !== void 0 ? _a : []) {
|
|
472
|
+
server.endpoint({
|
|
473
|
+
method: "POST",
|
|
474
|
+
noAuth: true,
|
|
475
|
+
path: `/agent/surface/${adapter.name}/webhook`,
|
|
476
|
+
handler: (ctx) => __awaiter(this, void 0, void 0, function* () {
|
|
477
|
+
var _a;
|
|
478
|
+
const surfaceContext = {
|
|
479
|
+
body: ctx.body,
|
|
480
|
+
headers: ctx.headers,
|
|
481
|
+
abortSignal: ctx.abortSignal,
|
|
482
|
+
rawRequest: ctx._raw_express_req,
|
|
483
|
+
rawResponse: ctx._raw_express_res,
|
|
484
|
+
};
|
|
485
|
+
const incoming = yield adapter.parseIncomingMessage(surfaceContext);
|
|
486
|
+
if (!incoming)
|
|
487
|
+
return { ok: true };
|
|
488
|
+
const sink = yield adapter.createEventSink(surfaceContext, incoming);
|
|
489
|
+
try {
|
|
490
|
+
yield this.handleChatSurfaceMessage(adapter, incoming, sink);
|
|
491
|
+
}
|
|
492
|
+
finally {
|
|
493
|
+
yield ((_a = sink.close) === null || _a === void 0 ? void 0 : _a.call(sink));
|
|
494
|
+
}
|
|
495
|
+
return { ok: true };
|
|
496
|
+
}),
|
|
497
|
+
});
|
|
498
|
+
}
|
|
353
499
|
server.endpoint({
|
|
354
500
|
method: 'POST',
|
|
355
501
|
path: `/agent/get-placeholder-messages`,
|
|
@@ -378,10 +524,11 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
378
524
|
const data = this.parseBody(agentResponseBodySchema, body, response);
|
|
379
525
|
if (!data)
|
|
380
526
|
return;
|
|
381
|
-
const
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
527
|
+
const emit = createSseEventEmitter(_raw_express_res, {
|
|
528
|
+
vercelAiUiMessageStream: true,
|
|
529
|
+
closeActiveBlockOnToolStart: true,
|
|
530
|
+
});
|
|
531
|
+
yield this.handleTurn({
|
|
385
532
|
prompt: data.message,
|
|
386
533
|
sessionId: data.sessionId,
|
|
387
534
|
modeName: data.mode,
|
|
@@ -389,14 +536,10 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
389
536
|
currentPage: data.currentPage,
|
|
390
537
|
abortSignal,
|
|
391
538
|
adminUser: currentAdminUser,
|
|
392
|
-
|
|
393
|
-
emitReasoningDelta: stream.reasoningDelta,
|
|
394
|
-
emitTextDelta: stream.textDelta,
|
|
395
|
-
emitErrorResponse: stream.textDelta,
|
|
539
|
+
emit,
|
|
396
540
|
failureLogMessage: "Agent response streaming failed",
|
|
397
541
|
abortLogMessage: "Agent response streaming aborted by the client",
|
|
398
542
|
});
|
|
399
|
-
stream.end();
|
|
400
543
|
return null;
|
|
401
544
|
})
|
|
402
545
|
});
|
|
@@ -420,7 +563,7 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
420
563
|
response.setStatus(400, "Audio file is required");
|
|
421
564
|
return { error: "Audio file is required" };
|
|
422
565
|
}
|
|
423
|
-
const
|
|
566
|
+
const emit = createSseEventEmitter(_raw_express_res);
|
|
424
567
|
let transcription;
|
|
425
568
|
try {
|
|
426
569
|
transcription = yield audioAdapter.transcribe({
|
|
@@ -434,25 +577,35 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
434
577
|
catch (error) {
|
|
435
578
|
if (abortSignal.aborted || isAbortError(error)) {
|
|
436
579
|
logger.info("Agent speech transcription aborted by the client");
|
|
437
|
-
|
|
580
|
+
yield emit({ type: "finish" });
|
|
438
581
|
return null;
|
|
439
582
|
}
|
|
440
583
|
logger.error(`Agent speech transcription failed:\n${getErrorMessage(error)}`);
|
|
441
|
-
|
|
442
|
-
|
|
584
|
+
yield emit({
|
|
585
|
+
type: "error",
|
|
586
|
+
error: "Speech transcription failed. Check server logs for details.",
|
|
587
|
+
});
|
|
588
|
+
yield emit({ type: "finish" });
|
|
443
589
|
return null;
|
|
444
590
|
}
|
|
445
591
|
if (abortSignal.aborted) {
|
|
446
|
-
|
|
592
|
+
yield emit({ type: "finish" });
|
|
447
593
|
return null;
|
|
448
594
|
}
|
|
449
595
|
const prompt = transcription.text;
|
|
450
596
|
if (!prompt) {
|
|
451
|
-
|
|
452
|
-
|
|
597
|
+
yield emit({
|
|
598
|
+
type: "error",
|
|
599
|
+
error: "Speech transcription is empty",
|
|
600
|
+
});
|
|
601
|
+
yield emit({ type: "finish" });
|
|
453
602
|
return null;
|
|
454
603
|
}
|
|
455
|
-
|
|
604
|
+
yield emit({
|
|
605
|
+
type: "transcript",
|
|
606
|
+
text: transcription.text,
|
|
607
|
+
language: transcription.language,
|
|
608
|
+
});
|
|
456
609
|
const sessionId = data.sessionId;
|
|
457
610
|
const currentPage = data.currentPage;
|
|
458
611
|
const agentResponse = yield this.runAndPersistAgentResponse({
|
|
@@ -463,26 +616,39 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
463
616
|
currentPage,
|
|
464
617
|
abortSignal,
|
|
465
618
|
adminUser: currentAdminUser,
|
|
466
|
-
|
|
619
|
+
emit: (event) => __awaiter(this, void 0, void 0, function* () {
|
|
620
|
+
if (event.type === "tool-call") {
|
|
621
|
+
yield emit(event);
|
|
622
|
+
}
|
|
623
|
+
}),
|
|
467
624
|
failureLogMessage: "Agent speech response failed",
|
|
468
625
|
abortLogMessage: "Agent speech response aborted by the client",
|
|
469
626
|
});
|
|
470
627
|
if (agentResponse.aborted) {
|
|
471
|
-
|
|
628
|
+
yield emit({ type: "finish" });
|
|
472
629
|
return null;
|
|
473
630
|
}
|
|
474
631
|
if (agentResponse.failed) {
|
|
475
|
-
|
|
476
|
-
|
|
632
|
+
yield emit({
|
|
633
|
+
type: "error",
|
|
634
|
+
error: agentResponse.text,
|
|
635
|
+
});
|
|
636
|
+
yield emit({ type: "finish" });
|
|
477
637
|
return null;
|
|
478
638
|
}
|
|
479
639
|
try {
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
640
|
+
yield emit({
|
|
641
|
+
type: "speech-response",
|
|
642
|
+
transcript: {
|
|
643
|
+
text: transcription.text,
|
|
644
|
+
language: transcription.language,
|
|
645
|
+
},
|
|
646
|
+
response: {
|
|
647
|
+
text: agentResponse.text,
|
|
648
|
+
},
|
|
649
|
+
sessionId,
|
|
650
|
+
turnId: agentResponse.turnId,
|
|
651
|
+
});
|
|
486
652
|
const speech = yield audioAdapter.synthesize({
|
|
487
653
|
text: sanitizeSpeechText(agentResponse.text),
|
|
488
654
|
stream: true,
|
|
@@ -490,7 +656,14 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
490
656
|
format: "pcm",
|
|
491
657
|
abortSignal,
|
|
492
658
|
});
|
|
493
|
-
|
|
659
|
+
yield emit({
|
|
660
|
+
type: "audio-start",
|
|
661
|
+
mimeType: speech.mimeType,
|
|
662
|
+
format: speech.format,
|
|
663
|
+
sampleRate: 24000,
|
|
664
|
+
channelCount: 1,
|
|
665
|
+
bitsPerSample: 16,
|
|
666
|
+
});
|
|
494
667
|
const reader = speech.audioStream.getReader();
|
|
495
668
|
const cancelAudioStream = () => {
|
|
496
669
|
void reader.cancel().catch(() => undefined);
|
|
@@ -509,15 +682,18 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
509
682
|
if (abortSignal.aborted) {
|
|
510
683
|
break;
|
|
511
684
|
}
|
|
512
|
-
|
|
685
|
+
yield emit({
|
|
686
|
+
type: "audio-delta",
|
|
687
|
+
value,
|
|
688
|
+
});
|
|
513
689
|
}
|
|
514
690
|
}
|
|
515
691
|
finally {
|
|
516
692
|
abortSignal.removeEventListener("abort", cancelAudioStream);
|
|
517
693
|
reader.releaseLock();
|
|
518
694
|
}
|
|
519
|
-
|
|
520
|
-
|
|
695
|
+
yield emit({ type: "audio-done" });
|
|
696
|
+
yield emit({ type: "finish" });
|
|
521
697
|
return null;
|
|
522
698
|
}
|
|
523
699
|
catch (error) {
|
|
@@ -526,9 +702,12 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
526
702
|
}
|
|
527
703
|
else {
|
|
528
704
|
logger.error(`Agent speech audio streaming failed:\n${error}`);
|
|
529
|
-
|
|
705
|
+
yield emit({
|
|
706
|
+
type: "error",
|
|
707
|
+
error: getErrorMessage(error),
|
|
708
|
+
});
|
|
530
709
|
}
|
|
531
|
-
|
|
710
|
+
yield emit({ type: "finish" });
|
|
532
711
|
return null;
|
|
533
712
|
}
|
|
534
713
|
})
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function sanitizeSpeechText(input: string): string;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { AgentEventEmitter } from "../../agentEvents.js";
|
|
2
|
+
type AgentEventStreamResponse = {
|
|
3
|
+
writeHead: (statusCode: number, headers: Record<string, string>) => void;
|
|
4
|
+
write: (chunk: string) => unknown;
|
|
5
|
+
end: () => unknown;
|
|
6
|
+
writableEnded: boolean;
|
|
7
|
+
destroyed: boolean;
|
|
8
|
+
};
|
|
9
|
+
type AgentEventStreamOptions = {
|
|
10
|
+
vercelAiUiMessageStream?: boolean;
|
|
11
|
+
closeActiveBlockOnToolStart?: boolean;
|
|
12
|
+
};
|
|
13
|
+
export declare function createSseEventEmitter(res: AgentEventStreamResponse, options?: AgentEventStreamOptions): AgentEventEmitter;
|
|
14
|
+
export {};
|