@adminforth/agent 1.50.1 → 1.51.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/agent/middleware/apiBasedTools.ts +13 -3
- package/agent/models/AgentModeResolver.ts +9 -0
- package/agent/models/AgentModelFactory.ts +28 -0
- package/agent/runtime/AgentContext.ts +30 -0
- package/agent/runtime/AgentRuntime.ts +68 -0
- package/agent/simpleAgent.ts +2 -129
- package/agent/speech/SpeechTurnService.ts +179 -0
- package/agent/tools/AgentToolProvider.ts +28 -0
- package/agent/tools/navigateUser.ts +2 -2
- package/agent/turn/TurnContextBuilder.ts +36 -0
- package/agent/turn/TurnLifecycleService.ts +29 -0
- package/agent/turn/TurnPersistenceService.ts +33 -0
- package/agent/turn/TurnPromptBuilder.ts +51 -0
- package/agent/turn/TurnStreamConsumer.ts +61 -0
- package/agent/turn/VegaLiteStreamBuffer.ts +90 -0
- package/agent/turn/turnTypes.ts +92 -0
- package/agentTurnService.ts +88 -461
- package/build.log +1 -1
- package/dist/agent/middleware/apiBasedTools.js +9 -2
- package/dist/agent/models/AgentModeResolver.d.ts +9 -0
- package/dist/agent/models/AgentModeResolver.js +9 -0
- package/dist/agent/models/AgentModelFactory.d.ts +7 -0
- package/dist/agent/models/AgentModelFactory.js +36 -0
- package/dist/agent/runtime/AgentContext.d.ts +28 -0
- package/dist/agent/runtime/AgentContext.js +17 -0
- package/dist/agent/runtime/AgentRuntime.d.ts +15 -0
- package/dist/agent/runtime/AgentRuntime.js +57 -0
- package/dist/agent/simpleAgent.d.ts +15 -45
- package/dist/agent/simpleAgent.js +1 -67
- package/dist/agent/speech/SpeechTurnService.d.ts +6 -0
- package/dist/agent/speech/SpeechTurnService.js +168 -0
- package/dist/agent/tools/AgentToolProvider.d.ts +9 -0
- package/dist/agent/tools/AgentToolProvider.js +27 -0
- package/dist/agent/tools/navigateUser.js +1 -1
- package/dist/agent/turn/TurnContextBuilder.d.ts +14 -0
- package/dist/agent/turn/TurnContextBuilder.js +31 -0
- package/dist/agent/turn/TurnLifecycleService.d.ts +17 -0
- package/dist/agent/turn/TurnLifecycleService.js +31 -0
- package/dist/agent/turn/TurnPersistenceService.d.ts +13 -0
- package/dist/agent/turn/TurnPersistenceService.js +35 -0
- package/dist/agent/turn/TurnPromptBuilder.d.ts +19 -0
- package/dist/agent/turn/TurnPromptBuilder.js +43 -0
- package/dist/agent/turn/TurnStreamConsumer.d.ts +10 -0
- package/dist/agent/turn/TurnStreamConsumer.js +78 -0
- package/dist/agent/turn/VegaLiteStreamBuffer.d.ts +7 -0
- package/dist/agent/turn/VegaLiteStreamBuffer.js +87 -0
- package/dist/agent/turn/turnTypes.d.ts +81 -0
- package/dist/agent/turn/turnTypes.js +1 -0
- package/dist/agentTurnService.d.ts +20 -69
- package/dist/agentTurnService.js +60 -373
- package/dist/index.d.ts +1 -0
- package/dist/index.js +22 -7
- package/index.ts +35 -7
- package/package.json +6 -3
package/agentTurnService.ts
CHANGED
|
@@ -1,298 +1,102 @@
|
|
|
1
|
-
import type { AdminUser, AudioAdapter, IAdminForth } from "adminforth";
|
|
2
1
|
import { logger } from "adminforth";
|
|
3
2
|
import { randomUUID } from "crypto";
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
6
|
-
import { createAgentChatModel, callAgent } from "./agent/simpleAgent.js";
|
|
3
|
+
import { AgentModelFactory } from "./agent/models/AgentModelFactory.js";
|
|
4
|
+
import { AgentModeResolver } from "./agent/models/AgentModeResolver.js";
|
|
7
5
|
import { createSequenceDebugCollector } from "./agent/middleware/sequenceDebug.js";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import
|
|
11
|
-
import {
|
|
12
|
-
import
|
|
13
|
-
import {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
adminUser: AdminUser;
|
|
30
|
-
sequenceDebugCollector: ReturnType<typeof createSequenceDebugCollector>;
|
|
31
|
-
emit?: AgentEventEmitter;
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
export type RunAndPersistAgentResponseInput = {
|
|
35
|
-
prompt: string;
|
|
36
|
-
sessionId: string;
|
|
37
|
-
modeName?: string | null;
|
|
38
|
-
userTimeZone: string;
|
|
39
|
-
currentPage?: CurrentPageContext;
|
|
40
|
-
chatSurface?: string;
|
|
41
|
-
adminPublicOrigin?: string;
|
|
42
|
-
abortSignal?: AbortSignal;
|
|
43
|
-
adminUser: AdminUser;
|
|
44
|
-
emit?: AgentEventEmitter;
|
|
45
|
-
failureLogMessage: string;
|
|
46
|
-
abortLogMessage: string;
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
export type RunAndPersistAgentResponseResult = {
|
|
50
|
-
text: string;
|
|
51
|
-
turnId: string;
|
|
52
|
-
aborted: boolean;
|
|
53
|
-
failed: boolean;
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
export type HandleTurnInput = Omit<RunAndPersistAgentResponseInput, "failureLogMessage" | "abortLogMessage"> & {
|
|
57
|
-
emit: AgentEventEmitter;
|
|
58
|
-
failureLogMessage?: string;
|
|
59
|
-
abortLogMessage?: string;
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
export type HandleSpeechTurnInput = Omit<HandleTurnInput, "prompt"> & {
|
|
63
|
-
audioAdapter: AudioAdapter;
|
|
64
|
-
audio: {
|
|
65
|
-
buffer: Buffer;
|
|
66
|
-
filename: string;
|
|
67
|
-
mimeType: string;
|
|
68
|
-
};
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
type AgentTurnServiceOptions = {
|
|
72
|
-
getAdminforth: () => IAdminForth;
|
|
73
|
-
getPluginInstanceId: () => string;
|
|
74
|
-
options: PluginOptions;
|
|
75
|
-
sessionStore: AgentSessionStore;
|
|
76
|
-
getCheckpointer: () => BaseCheckpointSaver;
|
|
77
|
-
getInternalAgentResourceIds: () => string[];
|
|
78
|
-
getAgentSystemPrompt: () => Promise<string>;
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
const VEGA_LITE_FENCE_START = "```vega-lite";
|
|
82
|
-
const COMPLETE_VEGA_LITE_BLOCK_RE = /```vega-lite[\s\S]*?```/;
|
|
6
|
+
import { AgentRuntime } from "./agent/runtime/AgentRuntime.js";
|
|
7
|
+
import { TurnContextBuilder } from "./agent/turn/TurnContextBuilder.js";
|
|
8
|
+
import { TurnLifecycleService } from "./agent/turn/TurnLifecycleService.js";
|
|
9
|
+
import { TurnPromptBuilder } from "./agent/turn/TurnPromptBuilder.js";
|
|
10
|
+
import { TurnStreamConsumer } from "./agent/turn/TurnStreamConsumer.js";
|
|
11
|
+
import type {
|
|
12
|
+
BaseAgentTurnInput,
|
|
13
|
+
HandleTurnInput,
|
|
14
|
+
PreparedAgentTurn,
|
|
15
|
+
RunAndPersistAgentResponseInput,
|
|
16
|
+
RunAndPersistAgentResponseResult,
|
|
17
|
+
} from "./agent/turn/turnTypes.js";
|
|
18
|
+
import { getErrorMessage, isAbortError } from "./errors.js";
|
|
19
|
+
|
|
20
|
+
export type {
|
|
21
|
+
BaseAgentTurnInput,
|
|
22
|
+
HandleSpeechTurnInput,
|
|
23
|
+
HandleTurnInput,
|
|
24
|
+
RunAndPersistAgentResponseInput,
|
|
25
|
+
RunAndPersistAgentResponseResult,
|
|
26
|
+
} from "./agent/turn/turnTypes.js";
|
|
83
27
|
|
|
84
28
|
export class AgentTurnService {
|
|
85
|
-
constructor(
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
createAgentChatModel({
|
|
102
|
-
adapter: selectedMode.completionAdapter,
|
|
103
|
-
maxTokens,
|
|
104
|
-
purpose: "summary",
|
|
105
|
-
}),
|
|
106
|
-
]);
|
|
107
|
-
const model = primaryModelSpec.model;
|
|
108
|
-
const summaryModel = summaryModelSpec.model;
|
|
109
|
-
const modelMiddleware = primaryModelSpec.middleware;
|
|
110
|
-
|
|
111
|
-
const userLanguage = await detectUserLanguage(selectedMode.completionAdapter, input.prompt, input.previousUserMessages)
|
|
112
|
-
.catch((error) => {
|
|
113
|
-
if (input.abortSignal?.aborted || isAbortError(error)) {
|
|
114
|
-
throw error;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
logger.warn(`Failed to detect user language: ${getErrorMessage(error)}`);
|
|
118
|
-
return null;
|
|
119
|
-
});
|
|
120
|
-
const systemPrompt = buildAgentTurnSystemPrompt({
|
|
121
|
-
agentSystemPrompt: await this.serviceOptions.getAgentSystemPrompt(),
|
|
122
|
-
adminUser: input.adminUser,
|
|
123
|
-
usernameField: adminforth.config.auth!.usernameField,
|
|
124
|
-
userLanguage,
|
|
125
|
-
chatSurface: input.chatSurface,
|
|
29
|
+
constructor(
|
|
30
|
+
private readonly lifecycle: TurnLifecycleService,
|
|
31
|
+
private readonly contextBuilder: TurnContextBuilder,
|
|
32
|
+
private readonly modeResolver: AgentModeResolver,
|
|
33
|
+
private readonly modelFactory: AgentModelFactory,
|
|
34
|
+
private readonly promptBuilder: TurnPromptBuilder,
|
|
35
|
+
private readonly runtime: AgentRuntime,
|
|
36
|
+
private readonly streamConsumer: TurnStreamConsumer,
|
|
37
|
+
) {}
|
|
38
|
+
|
|
39
|
+
private async prepareTurn(input: BaseAgentTurnInput): Promise<PreparedAgentTurn> {
|
|
40
|
+
const sequenceDebugCollector = createSequenceDebugCollector();
|
|
41
|
+
const { turnId, previousUserMessages } = await this.lifecycle.start(input);
|
|
42
|
+
const context = await this.contextBuilder.build({
|
|
43
|
+
base: input,
|
|
44
|
+
turnId,
|
|
126
45
|
});
|
|
127
|
-
const apiBasedTools = buildApiBasedTools(
|
|
128
|
-
adminforth,
|
|
129
|
-
this.serviceOptions.getInternalAgentResourceIds(),
|
|
130
|
-
);
|
|
131
46
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
model,
|
|
135
|
-
summaryModel,
|
|
136
|
-
modelMiddleware,
|
|
137
|
-
checkpointer: this.serviceOptions.getCheckpointer(),
|
|
138
|
-
messages: [
|
|
139
|
-
new SystemMessage(systemPrompt),
|
|
140
|
-
new HumanMessage(input.prompt),
|
|
141
|
-
],
|
|
142
|
-
adminUser: input.adminUser,
|
|
143
|
-
adminforth,
|
|
144
|
-
apiBasedTools,
|
|
145
|
-
customComponentsDir: adminforth.config.customization.customComponentsDir ?? "custom",
|
|
146
|
-
pluginCustomFolderPaths: adminforth.activatedPlugins.map((plugin) => plugin.customFolderPath),
|
|
47
|
+
return {
|
|
48
|
+
prompt: input.prompt,
|
|
147
49
|
sessionId: input.sessionId,
|
|
148
|
-
turnId
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
input.sequenceDebugCollector.handleToolCallEvent(event);
|
|
156
|
-
void input.emit?.({
|
|
157
|
-
type: "tool-call",
|
|
158
|
-
data: event,
|
|
159
|
-
});
|
|
50
|
+
turnId,
|
|
51
|
+
previousUserMessages,
|
|
52
|
+
modeName: input.modeName,
|
|
53
|
+
context,
|
|
54
|
+
observability: {
|
|
55
|
+
emit: undefined,
|
|
56
|
+
sequenceDebugSink: sequenceDebugCollector,
|
|
160
57
|
},
|
|
161
|
-
emitAgentEvent: input.emit,
|
|
162
|
-
sequenceDebugSink: input.sequenceDebugCollector,
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
for await (const rawChunk of stream as AsyncIterable<[any, any]>) {
|
|
166
|
-
if (input.abortSignal?.aborted) {
|
|
167
|
-
throw new DOMException("This operation was aborted", "AbortError");
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
const [token, metadata] = rawChunk;
|
|
171
|
-
|
|
172
|
-
const nodeName =
|
|
173
|
-
typeof metadata?.langgraph_node === "string"
|
|
174
|
-
? metadata.langgraph_node
|
|
175
|
-
: "";
|
|
176
|
-
|
|
177
|
-
if (nodeName && !["model", "model_request"].includes(nodeName)) {
|
|
178
|
-
continue;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
const blocks = Array.isArray(token?.contentBlocks)
|
|
182
|
-
? token.contentBlocks
|
|
183
|
-
: Array.isArray(token?.content)
|
|
184
|
-
? token.content
|
|
185
|
-
: [];
|
|
186
|
-
const reasoningDelta = blocks
|
|
187
|
-
.filter((b: any) => b?.type === "reasoning")
|
|
188
|
-
.map((b: any) => String(b.reasoning ?? ""))
|
|
189
|
-
.join("");
|
|
190
|
-
|
|
191
|
-
const textDelta = blocks
|
|
192
|
-
.filter((b: any) => b?.type === "text")
|
|
193
|
-
.map((b: any) => String(b.text ?? ""))
|
|
194
|
-
.join("");
|
|
195
|
-
|
|
196
|
-
if (reasoningDelta) {
|
|
197
|
-
await input.emit?.({
|
|
198
|
-
type: "reasoning-delta",
|
|
199
|
-
delta: reasoningDelta,
|
|
200
|
-
});
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
if (textDelta) {
|
|
204
|
-
fullResponse += textDelta;
|
|
205
|
-
bufferedTextDelta += textDelta;
|
|
206
|
-
|
|
207
|
-
if (
|
|
208
|
-
bufferedTextDelta.includes(VEGA_LITE_FENCE_START) &&
|
|
209
|
-
!COMPLETE_VEGA_LITE_BLOCK_RE.test(bufferedTextDelta)
|
|
210
|
-
) {
|
|
211
|
-
if (!isRenderingVegaLite) {
|
|
212
|
-
isRenderingVegaLite = true;
|
|
213
|
-
await input.emit?.({
|
|
214
|
-
type: "rendering",
|
|
215
|
-
phase: "start",
|
|
216
|
-
label: "Rendering...",
|
|
217
|
-
});
|
|
218
|
-
}
|
|
219
|
-
continue;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
if (isRenderingVegaLite) {
|
|
223
|
-
isRenderingVegaLite = false;
|
|
224
|
-
await input.emit?.({
|
|
225
|
-
type: "rendering",
|
|
226
|
-
phase: "end",
|
|
227
|
-
label: "Rendering...",
|
|
228
|
-
});
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
const streamableLength = bufferedTextDelta.includes(VEGA_LITE_FENCE_START)
|
|
232
|
-
? bufferedTextDelta.length
|
|
233
|
-
: bufferedTextDelta.length - getPartialVegaLiteFenceStartLength(bufferedTextDelta);
|
|
234
|
-
|
|
235
|
-
if (!streamableLength) {
|
|
236
|
-
continue;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
await input.emit?.({
|
|
240
|
-
type: "text-delta",
|
|
241
|
-
delta: bufferedTextDelta.slice(0, streamableLength),
|
|
242
|
-
});
|
|
243
|
-
bufferedTextDelta = bufferedTextDelta.slice(streamableLength);
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
if (isRenderingVegaLite) {
|
|
248
|
-
await input.emit?.({
|
|
249
|
-
type: "rendering",
|
|
250
|
-
phase: "end",
|
|
251
|
-
label: "Rendering...",
|
|
252
|
-
});
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
if (bufferedTextDelta) {
|
|
256
|
-
await input.emit?.({
|
|
257
|
-
type: "text-delta",
|
|
258
|
-
delta: bufferedTextDelta,
|
|
259
|
-
});
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
return {
|
|
263
|
-
text: fullResponse,
|
|
264
58
|
};
|
|
265
59
|
}
|
|
266
60
|
|
|
267
|
-
async
|
|
268
|
-
const
|
|
269
|
-
const
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
61
|
+
private async runAgentTurn(input: PreparedAgentTurn) {
|
|
62
|
+
const selectedMode = this.modeResolver.resolve(input.modeName);
|
|
63
|
+
const [models, messages] = await Promise.all([
|
|
64
|
+
this.modelFactory.create(selectedMode.completionAdapter),
|
|
65
|
+
this.promptBuilder.build({
|
|
66
|
+
prompt: input.prompt,
|
|
67
|
+
previousUserMessages: input.previousUserMessages,
|
|
68
|
+
adminUser: input.context.adminUser,
|
|
69
|
+
completionAdapter: selectedMode.completionAdapter,
|
|
70
|
+
chatSurface: input.context.chatSurface,
|
|
71
|
+
abortSignal: input.context.abortSignal,
|
|
72
|
+
}),
|
|
73
|
+
]);
|
|
74
|
+
const stream = await this.runtime.stream({
|
|
75
|
+
models,
|
|
76
|
+
messages,
|
|
77
|
+
context: input.context,
|
|
78
|
+
observability: input.observability,
|
|
274
79
|
});
|
|
275
|
-
|
|
80
|
+
|
|
81
|
+
return this.streamConsumer.consume({
|
|
82
|
+
stream: stream as AsyncIterable<[any, any]>,
|
|
83
|
+
abortSignal: input.context.abortSignal,
|
|
84
|
+
emit: input.observability.emit,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async runAndPersistAgentResponse(
|
|
89
|
+
input: RunAndPersistAgentResponseInput,
|
|
90
|
+
): Promise<RunAndPersistAgentResponseResult> {
|
|
91
|
+
const preparedTurn = await this.prepareTurn(input);
|
|
92
|
+
preparedTurn.observability.emit = input.emit;
|
|
93
|
+
|
|
276
94
|
let fullResponse = "";
|
|
277
95
|
let aborted = false;
|
|
278
96
|
let failed = false;
|
|
279
97
|
|
|
280
98
|
try {
|
|
281
|
-
const agentResponse = await this.runAgentTurn(
|
|
282
|
-
prompt: input.prompt,
|
|
283
|
-
sessionId: input.sessionId,
|
|
284
|
-
turnId,
|
|
285
|
-
previousUserMessages,
|
|
286
|
-
modeName: input.modeName,
|
|
287
|
-
userTimeZone: input.userTimeZone,
|
|
288
|
-
currentPage: input.currentPage,
|
|
289
|
-
chatSurface: input.chatSurface,
|
|
290
|
-
adminPublicOrigin: input.adminPublicOrigin,
|
|
291
|
-
abortSignal: input.abortSignal,
|
|
292
|
-
adminUser: input.adminUser,
|
|
293
|
-
sequenceDebugCollector,
|
|
294
|
-
emit: input.emit,
|
|
295
|
-
});
|
|
99
|
+
const agentResponse = await this.runAgentTurn(preparedTurn);
|
|
296
100
|
fullResponse = agentResponse.text;
|
|
297
101
|
} catch (error) {
|
|
298
102
|
if (input.abortSignal?.aborted || isAbortError(error)) {
|
|
@@ -305,20 +109,16 @@ export class AgentTurnService {
|
|
|
305
109
|
}
|
|
306
110
|
}
|
|
307
111
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
turnUpdates[options.turnResource.debugField] = sequenceDebugCollector.getHistory();
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
await adminforth.resource(options.turnResource.resourceId).update(turnId, turnUpdates);
|
|
112
|
+
preparedTurn.observability.sequenceDebugSink.flush();
|
|
113
|
+
await this.lifecycle.finish({
|
|
114
|
+
turnId: preparedTurn.turnId,
|
|
115
|
+
responseText: fullResponse,
|
|
116
|
+
debugHistory: preparedTurn.observability.sequenceDebugSink.getHistory(),
|
|
117
|
+
});
|
|
318
118
|
|
|
319
119
|
return {
|
|
320
120
|
text: fullResponse,
|
|
321
|
-
turnId,
|
|
121
|
+
turnId: preparedTurn.turnId,
|
|
322
122
|
aborted,
|
|
323
123
|
failed,
|
|
324
124
|
};
|
|
@@ -365,177 +165,4 @@ export class AgentTurnService {
|
|
|
365
165
|
|
|
366
166
|
return agentResponse;
|
|
367
167
|
}
|
|
368
|
-
|
|
369
|
-
async handleSpeechTurn(input: HandleSpeechTurnInput) {
|
|
370
|
-
let transcription;
|
|
371
|
-
|
|
372
|
-
try {
|
|
373
|
-
transcription = await input.audioAdapter.transcribe({
|
|
374
|
-
buffer: input.audio.buffer,
|
|
375
|
-
filename: input.audio.filename,
|
|
376
|
-
mimeType: input.audio.mimeType,
|
|
377
|
-
language: "auto",
|
|
378
|
-
abortSignal: input.abortSignal,
|
|
379
|
-
});
|
|
380
|
-
} catch (error) {
|
|
381
|
-
if (input.abortSignal?.aborted || isAbortError(error)) {
|
|
382
|
-
logger.info("Agent speech transcription aborted by the client");
|
|
383
|
-
await input.emit({ type: "finish" });
|
|
384
|
-
return null;
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
logger.error(`Agent speech transcription failed:\n${getErrorMessage(error)}`);
|
|
388
|
-
await input.emit({
|
|
389
|
-
type: "error",
|
|
390
|
-
error: "Speech transcription failed. Check server logs for details.",
|
|
391
|
-
});
|
|
392
|
-
await input.emit({ type: "finish" });
|
|
393
|
-
return null;
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
if (input.abortSignal?.aborted) {
|
|
397
|
-
await input.emit({ type: "finish" });
|
|
398
|
-
return null;
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
const prompt = transcription.text;
|
|
402
|
-
if (!prompt) {
|
|
403
|
-
await input.emit({
|
|
404
|
-
type: "error",
|
|
405
|
-
error: "Speech transcription is empty",
|
|
406
|
-
});
|
|
407
|
-
await input.emit({ type: "finish" });
|
|
408
|
-
return null;
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
await input.emit({
|
|
412
|
-
type: "transcript",
|
|
413
|
-
text: transcription.text,
|
|
414
|
-
language: transcription.language,
|
|
415
|
-
});
|
|
416
|
-
|
|
417
|
-
const agentResponse = await this.runAndPersistAgentResponse({
|
|
418
|
-
prompt,
|
|
419
|
-
sessionId: input.sessionId,
|
|
420
|
-
modeName: input.modeName,
|
|
421
|
-
userTimeZone: input.userTimeZone,
|
|
422
|
-
currentPage: input.currentPage,
|
|
423
|
-
chatSurface: input.chatSurface,
|
|
424
|
-
adminPublicOrigin: input.adminPublicOrigin,
|
|
425
|
-
abortSignal: input.abortSignal,
|
|
426
|
-
adminUser: input.adminUser,
|
|
427
|
-
emit: async (event) => {
|
|
428
|
-
if (event.type === "tool-call") {
|
|
429
|
-
await input.emit(event);
|
|
430
|
-
}
|
|
431
|
-
},
|
|
432
|
-
failureLogMessage: input.failureLogMessage ?? "Agent speech response failed",
|
|
433
|
-
abortLogMessage: input.abortLogMessage ?? "Agent speech response aborted by the client",
|
|
434
|
-
});
|
|
435
|
-
|
|
436
|
-
if (agentResponse.aborted) {
|
|
437
|
-
await input.emit({ type: "finish" });
|
|
438
|
-
return agentResponse;
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
if (agentResponse.failed) {
|
|
442
|
-
await input.emit({
|
|
443
|
-
type: "error",
|
|
444
|
-
error: agentResponse.text,
|
|
445
|
-
});
|
|
446
|
-
await input.emit({ type: "finish" });
|
|
447
|
-
return agentResponse;
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
try {
|
|
451
|
-
await input.emit({
|
|
452
|
-
type: "speech-response",
|
|
453
|
-
transcript: {
|
|
454
|
-
text: transcription.text,
|
|
455
|
-
language: transcription.language,
|
|
456
|
-
},
|
|
457
|
-
response: {
|
|
458
|
-
text: agentResponse.text,
|
|
459
|
-
},
|
|
460
|
-
sessionId: input.sessionId,
|
|
461
|
-
turnId: agentResponse.turnId,
|
|
462
|
-
});
|
|
463
|
-
const speech = await input.audioAdapter.synthesize({
|
|
464
|
-
text: sanitizeSpeechText(agentResponse.text),
|
|
465
|
-
stream: true,
|
|
466
|
-
streamFormat: "audio",
|
|
467
|
-
format: "pcm",
|
|
468
|
-
abortSignal: input.abortSignal,
|
|
469
|
-
});
|
|
470
|
-
|
|
471
|
-
await input.emit({
|
|
472
|
-
type: "audio-start",
|
|
473
|
-
mimeType: speech.mimeType,
|
|
474
|
-
format: speech.format,
|
|
475
|
-
sampleRate: 24000,
|
|
476
|
-
channelCount: 1,
|
|
477
|
-
bitsPerSample: 16,
|
|
478
|
-
});
|
|
479
|
-
|
|
480
|
-
const reader = speech.audioStream.getReader();
|
|
481
|
-
const cancelAudioStream = () => {
|
|
482
|
-
void reader.cancel().catch(() => undefined);
|
|
483
|
-
};
|
|
484
|
-
|
|
485
|
-
try {
|
|
486
|
-
input.abortSignal?.addEventListener("abort", cancelAudioStream, { once: true });
|
|
487
|
-
|
|
488
|
-
while (true) {
|
|
489
|
-
if (input.abortSignal?.aborted) {
|
|
490
|
-
await reader.cancel().catch(() => undefined);
|
|
491
|
-
break;
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
const { value, done } = await reader.read();
|
|
495
|
-
|
|
496
|
-
if (done) {
|
|
497
|
-
break;
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
if (input.abortSignal?.aborted) {
|
|
501
|
-
break;
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
await input.emit({
|
|
505
|
-
type: "audio-delta",
|
|
506
|
-
value,
|
|
507
|
-
});
|
|
508
|
-
}
|
|
509
|
-
} finally {
|
|
510
|
-
input.abortSignal?.removeEventListener("abort", cancelAudioStream);
|
|
511
|
-
reader.releaseLock();
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
await input.emit({ type: "audio-done" });
|
|
515
|
-
await input.emit({ type: "finish" });
|
|
516
|
-
return agentResponse;
|
|
517
|
-
} catch (error) {
|
|
518
|
-
if (input.abortSignal?.aborted || isAbortError(error)) {
|
|
519
|
-
logger.info("Agent speech audio streaming aborted by the client");
|
|
520
|
-
} else {
|
|
521
|
-
logger.error(`Agent speech audio streaming failed:\n${getErrorMessage(error)}`);
|
|
522
|
-
await input.emit({
|
|
523
|
-
type: "error",
|
|
524
|
-
error: getErrorMessage(error),
|
|
525
|
-
});
|
|
526
|
-
}
|
|
527
|
-
await input.emit({ type: "finish" });
|
|
528
|
-
return agentResponse;
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
function getPartialVegaLiteFenceStartLength(text: string): number {
|
|
534
|
-
for (let length = Math.min(text.length, VEGA_LITE_FENCE_START.length - 1); length > 0; length -= 1) {
|
|
535
|
-
if (VEGA_LITE_FENCE_START.startsWith(text.slice(-length))) {
|
|
536
|
-
return length;
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
return 0;
|
|
541
168
|
}
|
package/build.log
CHANGED
|
@@ -62,5 +62,5 @@ custom/speech_recognition_frontend/voiceActivityDetection.ts
|
|
|
62
62
|
custom/speech_recognition_frontend/types/
|
|
63
63
|
custom/speech_recognition_frontend/types/voice-activity-detection.d.ts
|
|
64
64
|
|
|
65
|
-
sent 1,670,
|
|
65
|
+
sent 1,670,885 bytes received 921 bytes 3,343,612.00 bytes/sec
|
|
66
66
|
total size is 1,666,790 speedup is 1.00
|
|
@@ -65,7 +65,14 @@ export function createApiBasedToolsMiddleware(apiBasedTools, adminforth) {
|
|
|
65
65
|
var _a, _b, _c, _d;
|
|
66
66
|
const startedAt = Date.now();
|
|
67
67
|
const toolInput = JSON.stringify((_a = request.toolCall.args) !== null && _a !== void 0 ? _a : {});
|
|
68
|
-
const { adminUser,
|
|
68
|
+
const { adminUser, emit, sequenceDebugSink, userTimeZone } = request.runtime.context;
|
|
69
|
+
const emitToolCall = (event) => {
|
|
70
|
+
sequenceDebugSink.handleToolCallEvent(event);
|
|
71
|
+
void (emit === null || emit === void 0 ? void 0 : emit({
|
|
72
|
+
type: "tool-call",
|
|
73
|
+
data: event,
|
|
74
|
+
}));
|
|
75
|
+
};
|
|
69
76
|
const toolArgs = ((_b = request.toolCall.args) !== null && _b !== void 0 ? _b : {});
|
|
70
77
|
let toolInfo;
|
|
71
78
|
if (request.toolCall.name === "fetch_skill") {
|
|
@@ -84,7 +91,7 @@ export function createApiBasedToolsMiddleware(apiBasedTools, adminforth) {
|
|
|
84
91
|
});
|
|
85
92
|
}
|
|
86
93
|
const toolCallTracker = createToolCallTracker({
|
|
87
|
-
emit:
|
|
94
|
+
emit: emitToolCall,
|
|
88
95
|
toolCallId: request.toolCall.id,
|
|
89
96
|
toolName: request.toolCall.name,
|
|
90
97
|
toolInfo,
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { PluginOptions } from "../../types.js";
|
|
2
|
+
export declare class AgentModeResolver {
|
|
3
|
+
private readonly options;
|
|
4
|
+
constructor(options: PluginOptions);
|
|
5
|
+
resolve(modeName?: string | null): {
|
|
6
|
+
name: string;
|
|
7
|
+
completionAdapter: import("../simpleAgent.js").AgentModeCompletionAdapter;
|
|
8
|
+
};
|
|
9
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { CompletionAdapter } from "adminforth";
|
|
2
|
+
import type { AgentTurnModels } from "../turn/turnTypes.js";
|
|
3
|
+
export declare class AgentModelFactory {
|
|
4
|
+
private readonly maxTokens;
|
|
5
|
+
constructor(maxTokens: number);
|
|
6
|
+
create(completionAdapter: CompletionAdapter): Promise<AgentTurnModels>;
|
|
7
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
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
|
+
import { createAgentChatModel } from "../simpleAgent.js";
|
|
11
|
+
export class AgentModelFactory {
|
|
12
|
+
constructor(maxTokens) {
|
|
13
|
+
this.maxTokens = maxTokens;
|
|
14
|
+
}
|
|
15
|
+
create(completionAdapter) {
|
|
16
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
17
|
+
const [primaryModelSpec, summaryModelSpec] = yield Promise.all([
|
|
18
|
+
createAgentChatModel({
|
|
19
|
+
adapter: completionAdapter,
|
|
20
|
+
maxTokens: this.maxTokens,
|
|
21
|
+
purpose: "primary",
|
|
22
|
+
}),
|
|
23
|
+
createAgentChatModel({
|
|
24
|
+
adapter: completionAdapter,
|
|
25
|
+
maxTokens: this.maxTokens,
|
|
26
|
+
purpose: "summary",
|
|
27
|
+
}),
|
|
28
|
+
]);
|
|
29
|
+
return {
|
|
30
|
+
model: primaryModelSpec.model,
|
|
31
|
+
summaryModel: summaryModelSpec.model,
|
|
32
|
+
modelMiddleware: primaryModelSpec.middleware,
|
|
33
|
+
};
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
}
|