@adminforth/agent 1.45.0 → 1.46.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/agentTurnService.ts +526 -0
- package/build.log +1 -1
- package/chatSurfaceService.ts +370 -0
- package/dist/agentTurnService.d.ts +70 -0
- package/dist/agentTurnService.js +453 -0
- package/dist/chatSurfaceService.d.ts +32 -0
- package/dist/chatSurfaceService.js +265 -0
- package/dist/endpoints/chatSurfaces.d.ts +3 -0
- package/dist/endpoints/chatSurfaces.js +91 -0
- package/dist/endpoints/context.d.ts +30 -0
- package/dist/endpoints/context.js +1 -0
- package/dist/endpoints/core.d.ts +3 -0
- package/dist/endpoints/core.js +106 -0
- package/dist/endpoints/sessions.d.ts +3 -0
- package/dist/endpoints/sessions.js +177 -0
- package/dist/errors.d.ts +2 -0
- package/dist/errors.js +9 -0
- package/dist/index.d.ts +4 -47
- package/dist/index.js +37 -917
- package/dist/sessionStore.d.ts +19 -0
- package/dist/sessionStore.js +83 -0
- package/endpoints/chatSurfaces.ts +93 -0
- package/endpoints/context.ts +66 -0
- package/endpoints/core.ts +113 -0
- package/endpoints/sessions.ts +183 -0
- package/errors.ts +10 -0
- package/index.ts +48 -1053
- package/package.json +1 -1
- package/sessionStore.ts +94 -0
- package/agentResponseEvents.ts +0 -1
- package/dist/agentResponseEvents.d.ts +0 -1
- package/dist/agentResponseEvents.js +0 -1
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
var __asyncValues = (this && this.__asyncValues) || function (o) {
|
|
11
|
+
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
|
|
12
|
+
var m = o[Symbol.asyncIterator], i;
|
|
13
|
+
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
|
|
14
|
+
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
|
|
15
|
+
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
|
|
16
|
+
};
|
|
17
|
+
import { logger } from "adminforth";
|
|
18
|
+
import { randomUUID } from "crypto";
|
|
19
|
+
import { HumanMessage, SystemMessage } from "langchain";
|
|
20
|
+
import { createAgentChatModel, callAgent } from "./agent/simpleAgent.js";
|
|
21
|
+
import { createSequenceDebugCollector } from "./agent/middleware/sequenceDebug.js";
|
|
22
|
+
import { detectUserLanguage } from "./agent/languageDetect.js";
|
|
23
|
+
import { prepareApiBasedTools as buildApiBasedTools } from "./apiBasedTools.js";
|
|
24
|
+
import { buildAgentTurnSystemPrompt } from "./agent/systemPrompt.js";
|
|
25
|
+
import { isAbortError, getErrorMessage } from "./errors.js";
|
|
26
|
+
import { sanitizeSpeechText } from "./sanitizeSpeechText.js";
|
|
27
|
+
const VEGA_LITE_FENCE_START = "```vega-lite";
|
|
28
|
+
const COMPLETE_VEGA_LITE_BLOCK_RE = /```vega-lite[\s\S]*?```/;
|
|
29
|
+
export class AgentTurnService {
|
|
30
|
+
constructor(serviceOptions) {
|
|
31
|
+
this.serviceOptions = serviceOptions;
|
|
32
|
+
}
|
|
33
|
+
runAgentTurn(input) {
|
|
34
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
35
|
+
var _a, e_1, _b, _c;
|
|
36
|
+
var _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
|
|
37
|
+
const adminforth = this.serviceOptions.getAdminforth();
|
|
38
|
+
const options = this.serviceOptions.options;
|
|
39
|
+
let fullResponse = "";
|
|
40
|
+
let bufferedTextDelta = "";
|
|
41
|
+
let isRenderingVegaLite = false;
|
|
42
|
+
const maxTokens = (_d = options.maxTokens) !== null && _d !== void 0 ? _d : 1000;
|
|
43
|
+
const selectedMode = (_e = options.modes.find((mode) => mode.name === input.modeName)) !== null && _e !== void 0 ? _e : options.modes[0];
|
|
44
|
+
const [primaryModelSpec, summaryModelSpec] = yield Promise.all([
|
|
45
|
+
createAgentChatModel({
|
|
46
|
+
adapter: selectedMode.completionAdapter,
|
|
47
|
+
maxTokens,
|
|
48
|
+
purpose: "primary",
|
|
49
|
+
}),
|
|
50
|
+
createAgentChatModel({
|
|
51
|
+
adapter: selectedMode.completionAdapter,
|
|
52
|
+
maxTokens,
|
|
53
|
+
purpose: "summary",
|
|
54
|
+
}),
|
|
55
|
+
]);
|
|
56
|
+
const model = primaryModelSpec.model;
|
|
57
|
+
const summaryModel = summaryModelSpec.model;
|
|
58
|
+
const modelMiddleware = primaryModelSpec.middleware;
|
|
59
|
+
const userLanguage = yield detectUserLanguage(selectedMode.completionAdapter, input.prompt, input.previousUserMessages)
|
|
60
|
+
.catch((error) => {
|
|
61
|
+
var _a;
|
|
62
|
+
if (((_a = input.abortSignal) === null || _a === void 0 ? void 0 : _a.aborted) || isAbortError(error)) {
|
|
63
|
+
throw error;
|
|
64
|
+
}
|
|
65
|
+
logger.warn(`Failed to detect user language: ${getErrorMessage(error)}`);
|
|
66
|
+
return null;
|
|
67
|
+
});
|
|
68
|
+
const systemPrompt = buildAgentTurnSystemPrompt({
|
|
69
|
+
agentSystemPrompt: yield this.serviceOptions.getAgentSystemPrompt(),
|
|
70
|
+
adminUser: input.adminUser,
|
|
71
|
+
usernameField: adminforth.config.auth.usernameField,
|
|
72
|
+
userLanguage,
|
|
73
|
+
});
|
|
74
|
+
const apiBasedTools = buildApiBasedTools(adminforth, this.serviceOptions.getInternalAgentResourceIds());
|
|
75
|
+
const stream = yield callAgent({
|
|
76
|
+
name: `adminforth-agent-${this.serviceOptions.getPluginInstanceId()}`,
|
|
77
|
+
model,
|
|
78
|
+
summaryModel,
|
|
79
|
+
modelMiddleware,
|
|
80
|
+
checkpointer: this.serviceOptions.getCheckpointer(),
|
|
81
|
+
messages: [
|
|
82
|
+
new SystemMessage(systemPrompt),
|
|
83
|
+
new HumanMessage(input.prompt),
|
|
84
|
+
],
|
|
85
|
+
adminUser: input.adminUser,
|
|
86
|
+
adminforth,
|
|
87
|
+
apiBasedTools,
|
|
88
|
+
customComponentsDir: (_f = adminforth.config.customization.customComponentsDir) !== null && _f !== void 0 ? _f : "custom",
|
|
89
|
+
sessionId: input.sessionId,
|
|
90
|
+
turnId: input.turnId,
|
|
91
|
+
currentPage: input.currentPage,
|
|
92
|
+
userTimeZone: input.userTimeZone,
|
|
93
|
+
abortSignal: input.abortSignal,
|
|
94
|
+
emitToolCallEvent: (event) => {
|
|
95
|
+
var _a;
|
|
96
|
+
input.sequenceDebugCollector.handleToolCallEvent(event);
|
|
97
|
+
void ((_a = input.emit) === null || _a === void 0 ? void 0 : _a.call(input, {
|
|
98
|
+
type: "tool-call",
|
|
99
|
+
data: event,
|
|
100
|
+
}));
|
|
101
|
+
},
|
|
102
|
+
sequenceDebugSink: input.sequenceDebugCollector,
|
|
103
|
+
});
|
|
104
|
+
try {
|
|
105
|
+
for (var _p = true, _q = __asyncValues(stream), _r; _r = yield _q.next(), _a = _r.done, !_a; _p = true) {
|
|
106
|
+
_c = _r.value;
|
|
107
|
+
_p = false;
|
|
108
|
+
const rawChunk = _c;
|
|
109
|
+
if ((_g = input.abortSignal) === null || _g === void 0 ? void 0 : _g.aborted) {
|
|
110
|
+
throw new DOMException("This operation was aborted", "AbortError");
|
|
111
|
+
}
|
|
112
|
+
const [token, metadata] = rawChunk;
|
|
113
|
+
const nodeName = typeof (metadata === null || metadata === void 0 ? void 0 : metadata.langgraph_node) === "string"
|
|
114
|
+
? metadata.langgraph_node
|
|
115
|
+
: "";
|
|
116
|
+
if (nodeName && !["model", "model_request"].includes(nodeName)) {
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
const blocks = Array.isArray(token === null || token === void 0 ? void 0 : token.contentBlocks)
|
|
120
|
+
? token.contentBlocks
|
|
121
|
+
: Array.isArray(token === null || token === void 0 ? void 0 : token.content)
|
|
122
|
+
? token.content
|
|
123
|
+
: [];
|
|
124
|
+
const reasoningDelta = blocks
|
|
125
|
+
.filter((b) => (b === null || b === void 0 ? void 0 : b.type) === "reasoning")
|
|
126
|
+
.map((b) => { var _a; return String((_a = b.reasoning) !== null && _a !== void 0 ? _a : ""); })
|
|
127
|
+
.join("");
|
|
128
|
+
const textDelta = blocks
|
|
129
|
+
.filter((b) => (b === null || b === void 0 ? void 0 : b.type) === "text")
|
|
130
|
+
.map((b) => { var _a; return String((_a = b.text) !== null && _a !== void 0 ? _a : ""); })
|
|
131
|
+
.join("");
|
|
132
|
+
if (reasoningDelta) {
|
|
133
|
+
yield ((_h = input.emit) === null || _h === void 0 ? void 0 : _h.call(input, {
|
|
134
|
+
type: "reasoning-delta",
|
|
135
|
+
delta: reasoningDelta,
|
|
136
|
+
}));
|
|
137
|
+
}
|
|
138
|
+
if (textDelta) {
|
|
139
|
+
fullResponse += textDelta;
|
|
140
|
+
bufferedTextDelta += textDelta;
|
|
141
|
+
if (bufferedTextDelta.includes(VEGA_LITE_FENCE_START) &&
|
|
142
|
+
!COMPLETE_VEGA_LITE_BLOCK_RE.test(bufferedTextDelta)) {
|
|
143
|
+
if (!isRenderingVegaLite) {
|
|
144
|
+
isRenderingVegaLite = true;
|
|
145
|
+
yield ((_j = input.emit) === null || _j === void 0 ? void 0 : _j.call(input, {
|
|
146
|
+
type: "rendering",
|
|
147
|
+
phase: "start",
|
|
148
|
+
label: "Rendering...",
|
|
149
|
+
}));
|
|
150
|
+
}
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
if (isRenderingVegaLite) {
|
|
154
|
+
isRenderingVegaLite = false;
|
|
155
|
+
yield ((_k = input.emit) === null || _k === void 0 ? void 0 : _k.call(input, {
|
|
156
|
+
type: "rendering",
|
|
157
|
+
phase: "end",
|
|
158
|
+
label: "Rendering...",
|
|
159
|
+
}));
|
|
160
|
+
}
|
|
161
|
+
const streamableLength = bufferedTextDelta.includes(VEGA_LITE_FENCE_START)
|
|
162
|
+
? bufferedTextDelta.length
|
|
163
|
+
: bufferedTextDelta.length - getPartialVegaLiteFenceStartLength(bufferedTextDelta);
|
|
164
|
+
if (!streamableLength) {
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
yield ((_l = input.emit) === null || _l === void 0 ? void 0 : _l.call(input, {
|
|
168
|
+
type: "text-delta",
|
|
169
|
+
delta: bufferedTextDelta.slice(0, streamableLength),
|
|
170
|
+
}));
|
|
171
|
+
bufferedTextDelta = bufferedTextDelta.slice(streamableLength);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
176
|
+
finally {
|
|
177
|
+
try {
|
|
178
|
+
if (!_p && !_a && (_b = _q.return)) yield _b.call(_q);
|
|
179
|
+
}
|
|
180
|
+
finally { if (e_1) throw e_1.error; }
|
|
181
|
+
}
|
|
182
|
+
if (isRenderingVegaLite) {
|
|
183
|
+
yield ((_m = input.emit) === null || _m === void 0 ? void 0 : _m.call(input, {
|
|
184
|
+
type: "rendering",
|
|
185
|
+
phase: "end",
|
|
186
|
+
label: "Rendering...",
|
|
187
|
+
}));
|
|
188
|
+
}
|
|
189
|
+
if (bufferedTextDelta) {
|
|
190
|
+
yield ((_o = input.emit) === null || _o === void 0 ? void 0 : _o.call(input, {
|
|
191
|
+
type: "text-delta",
|
|
192
|
+
delta: bufferedTextDelta,
|
|
193
|
+
}));
|
|
194
|
+
}
|
|
195
|
+
return {
|
|
196
|
+
text: fullResponse,
|
|
197
|
+
};
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
runAndPersistAgentResponse(input) {
|
|
201
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
202
|
+
var _a;
|
|
203
|
+
const adminforth = this.serviceOptions.getAdminforth();
|
|
204
|
+
const options = this.serviceOptions.options;
|
|
205
|
+
const previousUserMessages = yield this.serviceOptions.sessionStore.getPreviousUserMessages(input.sessionId);
|
|
206
|
+
const turnId = yield this.serviceOptions.sessionStore.createNewTurn(input.sessionId, input.prompt);
|
|
207
|
+
yield adminforth.resource(options.sessionResource.resourceId).update(input.sessionId, {
|
|
208
|
+
[options.sessionResource.createdAtField]: new Date().toISOString(),
|
|
209
|
+
});
|
|
210
|
+
const sequenceDebugCollector = createSequenceDebugCollector();
|
|
211
|
+
let fullResponse = "";
|
|
212
|
+
let aborted = false;
|
|
213
|
+
let failed = false;
|
|
214
|
+
try {
|
|
215
|
+
const agentResponse = yield this.runAgentTurn({
|
|
216
|
+
prompt: input.prompt,
|
|
217
|
+
sessionId: input.sessionId,
|
|
218
|
+
turnId,
|
|
219
|
+
previousUserMessages,
|
|
220
|
+
modeName: input.modeName,
|
|
221
|
+
userTimeZone: input.userTimeZone,
|
|
222
|
+
currentPage: input.currentPage,
|
|
223
|
+
abortSignal: input.abortSignal,
|
|
224
|
+
adminUser: input.adminUser,
|
|
225
|
+
sequenceDebugCollector,
|
|
226
|
+
emit: input.emit,
|
|
227
|
+
});
|
|
228
|
+
fullResponse = agentResponse.text;
|
|
229
|
+
}
|
|
230
|
+
catch (error) {
|
|
231
|
+
if (((_a = input.abortSignal) === null || _a === void 0 ? void 0 : _a.aborted) || isAbortError(error)) {
|
|
232
|
+
aborted = true;
|
|
233
|
+
logger.info(input.abortLogMessage);
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
failed = true;
|
|
237
|
+
fullResponse = getErrorMessage(error);
|
|
238
|
+
logger.error(`${input.failureLogMessage}:\n${fullResponse}`);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
sequenceDebugCollector.flush();
|
|
242
|
+
const turnUpdates = {
|
|
243
|
+
[options.turnResource.responseField]: fullResponse,
|
|
244
|
+
};
|
|
245
|
+
if (options.turnResource.debugField) {
|
|
246
|
+
turnUpdates[options.turnResource.debugField] = sequenceDebugCollector.getHistory();
|
|
247
|
+
}
|
|
248
|
+
yield adminforth.resource(options.turnResource.resourceId).update(turnId, turnUpdates);
|
|
249
|
+
return {
|
|
250
|
+
text: fullResponse,
|
|
251
|
+
turnId,
|
|
252
|
+
aborted,
|
|
253
|
+
failed,
|
|
254
|
+
};
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
handleTurn(input) {
|
|
258
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
259
|
+
var _a, _b;
|
|
260
|
+
yield input.emit({
|
|
261
|
+
type: "turn-started",
|
|
262
|
+
messageId: randomUUID(),
|
|
263
|
+
});
|
|
264
|
+
const agentResponse = yield this.runAndPersistAgentResponse({
|
|
265
|
+
prompt: input.prompt,
|
|
266
|
+
sessionId: input.sessionId,
|
|
267
|
+
modeName: input.modeName,
|
|
268
|
+
userTimeZone: input.userTimeZone,
|
|
269
|
+
currentPage: input.currentPage,
|
|
270
|
+
abortSignal: input.abortSignal,
|
|
271
|
+
adminUser: input.adminUser,
|
|
272
|
+
emit: input.emit,
|
|
273
|
+
failureLogMessage: (_a = input.failureLogMessage) !== null && _a !== void 0 ? _a : "Agent response failed",
|
|
274
|
+
abortLogMessage: (_b = input.abortLogMessage) !== null && _b !== void 0 ? _b : "Agent response aborted",
|
|
275
|
+
});
|
|
276
|
+
if (agentResponse.failed) {
|
|
277
|
+
yield input.emit({
|
|
278
|
+
type: "error",
|
|
279
|
+
error: agentResponse.text,
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
else if (!agentResponse.aborted) {
|
|
283
|
+
yield input.emit({
|
|
284
|
+
type: "response",
|
|
285
|
+
text: agentResponse.text,
|
|
286
|
+
sessionId: input.sessionId,
|
|
287
|
+
turnId: agentResponse.turnId,
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
yield input.emit({
|
|
291
|
+
type: "finish",
|
|
292
|
+
});
|
|
293
|
+
return agentResponse;
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
handleSpeechTurn(input) {
|
|
297
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
298
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
299
|
+
let transcription;
|
|
300
|
+
try {
|
|
301
|
+
transcription = yield input.audioAdapter.transcribe({
|
|
302
|
+
buffer: input.audio.buffer,
|
|
303
|
+
filename: input.audio.filename,
|
|
304
|
+
mimeType: input.audio.mimeType,
|
|
305
|
+
language: "auto",
|
|
306
|
+
abortSignal: input.abortSignal,
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
catch (error) {
|
|
310
|
+
if (((_a = input.abortSignal) === null || _a === void 0 ? void 0 : _a.aborted) || isAbortError(error)) {
|
|
311
|
+
logger.info("Agent speech transcription aborted by the client");
|
|
312
|
+
yield input.emit({ type: "finish" });
|
|
313
|
+
return null;
|
|
314
|
+
}
|
|
315
|
+
logger.error(`Agent speech transcription failed:\n${getErrorMessage(error)}`);
|
|
316
|
+
yield input.emit({
|
|
317
|
+
type: "error",
|
|
318
|
+
error: "Speech transcription failed. Check server logs for details.",
|
|
319
|
+
});
|
|
320
|
+
yield input.emit({ type: "finish" });
|
|
321
|
+
return null;
|
|
322
|
+
}
|
|
323
|
+
if ((_b = input.abortSignal) === null || _b === void 0 ? void 0 : _b.aborted) {
|
|
324
|
+
yield input.emit({ type: "finish" });
|
|
325
|
+
return null;
|
|
326
|
+
}
|
|
327
|
+
const prompt = transcription.text;
|
|
328
|
+
if (!prompt) {
|
|
329
|
+
yield input.emit({
|
|
330
|
+
type: "error",
|
|
331
|
+
error: "Speech transcription is empty",
|
|
332
|
+
});
|
|
333
|
+
yield input.emit({ type: "finish" });
|
|
334
|
+
return null;
|
|
335
|
+
}
|
|
336
|
+
yield input.emit({
|
|
337
|
+
type: "transcript",
|
|
338
|
+
text: transcription.text,
|
|
339
|
+
language: transcription.language,
|
|
340
|
+
});
|
|
341
|
+
const agentResponse = yield this.runAndPersistAgentResponse({
|
|
342
|
+
prompt,
|
|
343
|
+
sessionId: input.sessionId,
|
|
344
|
+
modeName: input.modeName,
|
|
345
|
+
userTimeZone: input.userTimeZone,
|
|
346
|
+
currentPage: input.currentPage,
|
|
347
|
+
abortSignal: input.abortSignal,
|
|
348
|
+
adminUser: input.adminUser,
|
|
349
|
+
emit: (event) => __awaiter(this, void 0, void 0, function* () {
|
|
350
|
+
if (event.type === "tool-call") {
|
|
351
|
+
yield input.emit(event);
|
|
352
|
+
}
|
|
353
|
+
}),
|
|
354
|
+
failureLogMessage: (_c = input.failureLogMessage) !== null && _c !== void 0 ? _c : "Agent speech response failed",
|
|
355
|
+
abortLogMessage: (_d = input.abortLogMessage) !== null && _d !== void 0 ? _d : "Agent speech response aborted by the client",
|
|
356
|
+
});
|
|
357
|
+
if (agentResponse.aborted) {
|
|
358
|
+
yield input.emit({ type: "finish" });
|
|
359
|
+
return agentResponse;
|
|
360
|
+
}
|
|
361
|
+
if (agentResponse.failed) {
|
|
362
|
+
yield input.emit({
|
|
363
|
+
type: "error",
|
|
364
|
+
error: agentResponse.text,
|
|
365
|
+
});
|
|
366
|
+
yield input.emit({ type: "finish" });
|
|
367
|
+
return agentResponse;
|
|
368
|
+
}
|
|
369
|
+
try {
|
|
370
|
+
yield input.emit({
|
|
371
|
+
type: "speech-response",
|
|
372
|
+
transcript: {
|
|
373
|
+
text: transcription.text,
|
|
374
|
+
language: transcription.language,
|
|
375
|
+
},
|
|
376
|
+
response: {
|
|
377
|
+
text: agentResponse.text,
|
|
378
|
+
},
|
|
379
|
+
sessionId: input.sessionId,
|
|
380
|
+
turnId: agentResponse.turnId,
|
|
381
|
+
});
|
|
382
|
+
const speech = yield input.audioAdapter.synthesize({
|
|
383
|
+
text: sanitizeSpeechText(agentResponse.text),
|
|
384
|
+
stream: true,
|
|
385
|
+
streamFormat: "audio",
|
|
386
|
+
format: "pcm",
|
|
387
|
+
abortSignal: input.abortSignal,
|
|
388
|
+
});
|
|
389
|
+
yield input.emit({
|
|
390
|
+
type: "audio-start",
|
|
391
|
+
mimeType: speech.mimeType,
|
|
392
|
+
format: speech.format,
|
|
393
|
+
sampleRate: 24000,
|
|
394
|
+
channelCount: 1,
|
|
395
|
+
bitsPerSample: 16,
|
|
396
|
+
});
|
|
397
|
+
const reader = speech.audioStream.getReader();
|
|
398
|
+
const cancelAudioStream = () => {
|
|
399
|
+
void reader.cancel().catch(() => undefined);
|
|
400
|
+
};
|
|
401
|
+
try {
|
|
402
|
+
(_e = input.abortSignal) === null || _e === void 0 ? void 0 : _e.addEventListener("abort", cancelAudioStream, { once: true });
|
|
403
|
+
while (true) {
|
|
404
|
+
if ((_f = input.abortSignal) === null || _f === void 0 ? void 0 : _f.aborted) {
|
|
405
|
+
yield reader.cancel().catch(() => undefined);
|
|
406
|
+
break;
|
|
407
|
+
}
|
|
408
|
+
const { value, done } = yield reader.read();
|
|
409
|
+
if (done) {
|
|
410
|
+
break;
|
|
411
|
+
}
|
|
412
|
+
if ((_g = input.abortSignal) === null || _g === void 0 ? void 0 : _g.aborted) {
|
|
413
|
+
break;
|
|
414
|
+
}
|
|
415
|
+
yield input.emit({
|
|
416
|
+
type: "audio-delta",
|
|
417
|
+
value,
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
finally {
|
|
422
|
+
(_h = input.abortSignal) === null || _h === void 0 ? void 0 : _h.removeEventListener("abort", cancelAudioStream);
|
|
423
|
+
reader.releaseLock();
|
|
424
|
+
}
|
|
425
|
+
yield input.emit({ type: "audio-done" });
|
|
426
|
+
yield input.emit({ type: "finish" });
|
|
427
|
+
return agentResponse;
|
|
428
|
+
}
|
|
429
|
+
catch (error) {
|
|
430
|
+
if (((_j = input.abortSignal) === null || _j === void 0 ? void 0 : _j.aborted) || isAbortError(error)) {
|
|
431
|
+
logger.info("Agent speech audio streaming aborted by the client");
|
|
432
|
+
}
|
|
433
|
+
else {
|
|
434
|
+
logger.error(`Agent speech audio streaming failed:\n${getErrorMessage(error)}`);
|
|
435
|
+
yield input.emit({
|
|
436
|
+
type: "error",
|
|
437
|
+
error: getErrorMessage(error),
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
yield input.emit({ type: "finish" });
|
|
441
|
+
return agentResponse;
|
|
442
|
+
}
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
function getPartialVegaLiteFenceStartLength(text) {
|
|
447
|
+
for (let length = Math.min(text.length, VEGA_LITE_FENCE_START.length - 1); length > 0; length -= 1) {
|
|
448
|
+
if (VEGA_LITE_FENCE_START.startsWith(text.slice(-length))) {
|
|
449
|
+
return length;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
return 0;
|
|
453
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { AdminUser, ChatSurfaceAdapter, ChatSurfaceEventSink, ChatSurfaceIncomingMessage, IAdminForth } from "adminforth";
|
|
2
|
+
import type { HandleTurnInput, RunAndPersistAgentResponseInput, RunAndPersistAgentResponseResult } from "./agentTurnService.js";
|
|
3
|
+
import type { PluginOptions } from "./types.js";
|
|
4
|
+
import type { AgentSessionStore } from "./sessionStore.js";
|
|
5
|
+
type ChatSurfaceConnectAction = {
|
|
6
|
+
type: "url";
|
|
7
|
+
label: string;
|
|
8
|
+
url: string;
|
|
9
|
+
};
|
|
10
|
+
export type ChatSurfaceAdapterWithConnectAction = ChatSurfaceAdapter & {
|
|
11
|
+
createConnectAction?(input: {
|
|
12
|
+
token: string;
|
|
13
|
+
}): ChatSurfaceConnectAction | Promise<ChatSurfaceConnectAction>;
|
|
14
|
+
};
|
|
15
|
+
export declare class ChatSurfaceService {
|
|
16
|
+
private getAdminforth;
|
|
17
|
+
private options;
|
|
18
|
+
private sessionStore;
|
|
19
|
+
private handleTurn;
|
|
20
|
+
private runAndPersistAgentResponse;
|
|
21
|
+
private linkTokens;
|
|
22
|
+
constructor(getAdminforth: () => IAdminForth, options: PluginOptions, sessionStore: AgentSessionStore, handleTurn: (input: HandleTurnInput) => Promise<unknown>, runAndPersistAgentResponse: (input: RunAndPersistAgentResponseInput) => Promise<RunAndPersistAgentResponseResult>);
|
|
23
|
+
getConnectActionAdapters(): ChatSurfaceAdapterWithConnectAction[];
|
|
24
|
+
createLinkToken(surface: string, adminUser: AdminUser): `${string}-${string}-${string}-${string}-${string}`;
|
|
25
|
+
private consumeLinkToken;
|
|
26
|
+
private createEventEmitter;
|
|
27
|
+
private handleLink;
|
|
28
|
+
private handleAudioMessage;
|
|
29
|
+
private handleAgentSurfaceResponse;
|
|
30
|
+
handleMessage(adapter: ChatSurfaceAdapter, incoming: ChatSurfaceIncomingMessage, sink: ChatSurfaceEventSink): Promise<void>;
|
|
31
|
+
}
|
|
32
|
+
export {};
|