@adminforth/agent 2.0.10 → 2.1.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/README.md +333 -10
- package/dist/application/ports.d.ts +66 -0
- package/dist/application/runTurnUseCase.d.ts +68 -0
- package/dist/application/runTurnUseCase.js +358 -0
- package/dist/{agentEvents.d.ts → domain/agentEvents.d.ts} +1 -1
- package/dist/{agent → domain}/systemPrompt.js +1 -1
- package/dist/{agent/turn → domain}/turnTypes.d.ts +41 -37
- package/dist/{agent/turn/VegaLiteStreamBuffer.d.ts → domain/vegaLiteStreamBuffer.d.ts} +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +40 -35
- package/dist/{agent/runtime/AgentContext.d.ts → llm/agentContext.d.ts} +3 -3
- package/dist/{agent/simpleAgent.d.ts → llm/agentModels.d.ts} +18 -16
- package/dist/llm/agentModels.js +65 -0
- package/dist/{agent/runtime/AgentRuntime.d.ts → llm/agentRuntime.d.ts} +11 -2
- package/dist/{agent/runtime/AgentRuntime.js → llm/agentRuntime.js} +38 -6
- package/dist/llm/langGraphLlm.d.ts +25 -0
- package/dist/llm/langGraphLlm.js +54 -0
- package/dist/{agent/middleware/apiBasedTools.d.ts → llm/middleware/apiToolsMiddleware.d.ts} +1 -1
- package/dist/{agent/middleware/apiBasedTools.js → llm/middleware/apiToolsMiddleware.js} +8 -14
- package/dist/{agent → llm}/middleware/sequenceDebug.d.ts +1 -1
- package/dist/{agent/models/AgentModelFactory.d.ts → llm/modelFactory.d.ts} +1 -1
- package/dist/{agent/models/AgentModelFactory.js → llm/modelFactory.js} +1 -1
- package/dist/llm/streamAdapter.d.ts +18 -0
- package/dist/llm/streamAdapter.js +117 -0
- package/dist/{sessionStore.d.ts → persistence/sessionStore.d.ts} +12 -2
- package/dist/{sessionStore.js → persistence/sessionStore.js} +33 -0
- package/dist/{agent/tools/AgentToolProvider.d.ts → tools/agentToolProvider.d.ts} +1 -1
- package/dist/{agent/tools/AgentToolProvider.js → tools/agentToolProvider.js} +1 -1
- package/dist/{apiBasedTools.js → tools/apiBasedTools.js} +0 -3
- package/dist/{agent/tools → tools}/apiTool.d.ts +1 -1
- package/dist/{agent/tools → tools}/fetchSkill.js +1 -1
- package/dist/{agent/tools → tools}/fetchToolSchema.d.ts +1 -1
- package/dist/{agent/tools → tools}/index.d.ts +1 -1
- package/dist/{agent/tools → tools}/navigateUser.d.ts +3 -3
- package/dist/{endpoints → transport/http}/context.d.ts +2 -2
- package/dist/transport/http/context.js +1 -0
- package/dist/{endpoints/core.js → transport/http/coreEndpoints.js} +3 -3
- package/dist/{endpoints/sessions.js → transport/http/sessionEndpoints.js} +7 -1
- package/dist/transport/sse/sseWriter.d.ts +29 -0
- package/dist/{surfaces/web-sse/createSseEventEmitter.js → transport/sse/sseWriter.js} +15 -0
- package/dist/{chatSurfaceService.d.ts → transport/surfaces/chatSurfaceService.d.ts} +3 -3
- package/dist/{chatSurfaceService.js → transport/surfaces/chatSurfaceService.js} +7 -5
- package/dist/{agent/speech/SpeechTurnService.d.ts → transport/surfaces/speechTurnService.d.ts} +1 -1
- package/dist/{agent/speech/SpeechTurnService.js → transport/surfaces/speechTurnService.js} +2 -2
- package/dist/types.d.ts +10 -4
- package/package.json +11 -2
- package/.woodpecker/buildRelease.sh +0 -13
- package/.woodpecker/buildSlackNotify.sh +0 -46
- package/.woodpecker/release.yml +0 -57
- package/agent/checkpointer.ts +0 -329
- package/agent/languageDetect.ts +0 -92
- package/agent/middleware/apiBasedTools.ts +0 -174
- package/agent/middleware/openAiResponsesContinuation.ts +0 -92
- package/agent/middleware/sequenceDebug.ts +0 -429
- package/agent/models/AgentModeResolver.ts +0 -9
- package/agent/models/AgentModelFactory.ts +0 -28
- package/agent/runtime/AgentContext.ts +0 -30
- package/agent/runtime/AgentRuntime.ts +0 -89
- package/agent/simpleAgent.ts +0 -199
- package/agent/skills/registry.ts +0 -162
- package/agent/speech/SpeechTurnService.ts +0 -179
- package/agent/systemPrompt.ts +0 -133
- package/agent/toolCallEvents.ts +0 -135
- package/agent/tools/AgentToolProvider.ts +0 -28
- package/agent/tools/apiTool.ts +0 -66
- package/agent/tools/fetchSkill.ts +0 -68
- package/agent/tools/fetchToolSchema.ts +0 -47
- package/agent/tools/getUserLocation.ts +0 -45
- package/agent/tools/index.ts +0 -31
- package/agent/tools/navigateUser.ts +0 -210
- package/agent/turn/TurnContextBuilder.ts +0 -36
- package/agent/turn/TurnLifecycleService.ts +0 -47
- package/agent/turn/TurnPersistenceService.ts +0 -33
- package/agent/turn/TurnPromptBuilder.ts +0 -51
- package/agent/turn/TurnStreamConsumer.ts +0 -69
- package/agent/turn/VegaLiteStreamBuffer.ts +0 -90
- package/agent/turn/turnTypes.ts +0 -100
- package/agentEvents.ts +0 -75
- package/agentTurnService.ts +0 -314
- package/apiBasedTools.ts +0 -712
- package/build.log +0 -67
- package/chatSurfaceService.ts +0 -319
- package/custom/ChatFooter.vue +0 -173
- package/custom/ChatHeader.vue +0 -78
- package/custom/ChatSurface.vue +0 -160
- package/custom/CustomAutoScrollContainer.vue +0 -164
- package/custom/SessionsHistory.vue +0 -88
- package/custom/chat.ts +0 -82
- package/custom/composables/agentAudio/utils.ts +0 -220
- package/custom/composables/agentStore/constants.ts +0 -19
- package/custom/composables/agentStore/pageContext.ts +0 -8
- package/custom/composables/agentStore/useAgentChat.ts +0 -241
- package/custom/composables/agentStore/useAgentPlaceholder.ts +0 -142
- package/custom/composables/agentStore/useAgentSessions.ts +0 -350
- package/custom/composables/useAgentAudio.ts +0 -272
- package/custom/composables/useAgentStore.ts +0 -510
- package/custom/composables/useAgentTransitions.ts +0 -58
- package/custom/conversation_area/ConversationArea.vue +0 -301
- package/custom/conversation_area/MessageRenderer.vue +0 -49
- package/custom/conversation_area/ProcessingTimeline.vue +0 -286
- package/custom/conversation_area/ReasoningRenderer.vue +0 -79
- package/custom/conversation_area/SystemMessageRenderer.vue +0 -28
- package/custom/conversation_area/TextRenderer.vue +0 -182
- package/custom/conversation_area/ThreeDotsAnimation.vue +0 -35
- package/custom/conversation_area/ToolApprovalRenderer.vue +0 -98
- package/custom/conversation_area/ToolRenderer.vue +0 -178
- package/custom/conversation_area/ToolsGroup.vue +0 -59
- package/custom/env.d.ts +0 -7
- package/custom/incremark_code_renderers/IncremarkShikiCodeBlock.vue +0 -393
- package/custom/incremark_code_renderers/incremarkCodeHighlight.ts +0 -285
- package/custom/incremark_code_renderers/incremarkRenderer.ts +0 -653
- package/custom/incremark_code_renderers/renderIncremarkMarkdown.ts +0 -118
- package/custom/package.json +0 -30
- package/custom/pnpm-lock.yaml +0 -2394
- package/custom/public/agentAudio/agent-processing.mp3 +0 -0
- package/custom/skills/analyze_data/SKILL.md +0 -25
- package/custom/skills/charts/SKILL.md +0 -193
- package/custom/skills/fetch_data/SKILL.md +0 -31
- package/custom/skills/mutate_data/SKILL.md +0 -121
- package/custom/speech_recognition_frontend/AudioLines.vue +0 -43
- package/custom/speech_recognition_frontend/MicrophoneButon.vue +0 -185
- package/custom/speech_recognition_frontend/types/voice-activity-detection.d.ts +0 -22
- package/custom/speech_recognition_frontend/voiceActivityDetection.ts +0 -62
- package/custom/tsconfig.json +0 -15
- package/custom/types.ts +0 -108
- package/custom/utils.ts +0 -38
- package/dist/agent/middleware/openAiResponsesContinuation.d.ts +0 -1
- package/dist/agent/middleware/openAiResponsesContinuation.js +0 -66
- package/dist/agent/models/AgentModeResolver.d.ts +0 -9
- package/dist/agent/models/AgentModeResolver.js +0 -9
- package/dist/agent/simpleAgent.js +0 -118
- package/dist/agent/turn/TurnContextBuilder.d.ts +0 -14
- package/dist/agent/turn/TurnContextBuilder.js +0 -31
- package/dist/agent/turn/TurnLifecycleService.d.ts +0 -24
- package/dist/agent/turn/TurnLifecycleService.js +0 -47
- package/dist/agent/turn/TurnPersistenceService.d.ts +0 -13
- package/dist/agent/turn/TurnPersistenceService.js +0 -35
- package/dist/agent/turn/TurnPromptBuilder.d.ts +0 -19
- package/dist/agent/turn/TurnPromptBuilder.js +0 -43
- package/dist/agent/turn/TurnStreamConsumer.d.ts +0 -11
- package/dist/agent/turn/TurnStreamConsumer.js +0 -84
- package/dist/agentTurnService.d.ts +0 -24
- package/dist/agentTurnService.js +0 -269
- package/dist/surfaces/web-sse/createSseEventEmitter.d.ts +0 -14
- package/endpoints/chatSurfaces.ts +0 -66
- package/endpoints/context.ts +0 -52
- package/endpoints/core.ts +0 -181
- package/endpoints/sessions.ts +0 -183
- package/errors.ts +0 -10
- package/index.ts +0 -192
- package/pnpm-workspace.yaml +0 -4
- package/sanitizeSpeechText.ts +0 -32
- package/sessionStore.ts +0 -105
- package/surfaces/web-sse/createSseEventEmitter.ts +0 -311
- package/tsconfig.json +0 -14
- package/types.ts +0 -127
- /package/dist/{agent/turn/turnTypes.js → application/ports.js} +0 -0
- /package/dist/{agentEvents.js → domain/agentEvents.js} +0 -0
- /package/dist/{agent → domain}/languageDetect.d.ts +0 -0
- /package/dist/{agent → domain}/languageDetect.js +0 -0
- /package/dist/{agent → domain}/systemPrompt.d.ts +0 -0
- /package/dist/{agent → domain}/toolCallEvents.d.ts +0 -0
- /package/dist/{agent → domain}/toolCallEvents.js +0 -0
- /package/dist/{endpoints/context.js → domain/turnTypes.js} +0 -0
- /package/dist/{agent/turn/VegaLiteStreamBuffer.js → domain/vegaLiteStreamBuffer.js} +0 -0
- /package/dist/{agent/runtime/AgentContext.js → llm/agentContext.js} +0 -0
- /package/dist/{agent → llm}/middleware/sequenceDebug.js +0 -0
- /package/dist/{agent/checkpointer.d.ts → persistence/checkpointStore.d.ts} +0 -0
- /package/dist/{agent/checkpointer.js → persistence/checkpointStore.js} +0 -0
- /package/dist/{errors.d.ts → shared/errors.d.ts} +0 -0
- /package/dist/{errors.js → shared/errors.js} +0 -0
- /package/dist/{sanitizeSpeechText.d.ts → shared/sanitizeSpeechText.d.ts} +0 -0
- /package/dist/{sanitizeSpeechText.js → shared/sanitizeSpeechText.js} +0 -0
- /package/dist/{apiBasedTools.d.ts → tools/apiBasedTools.d.ts} +0 -0
- /package/dist/{agent/tools → tools}/apiTool.js +0 -0
- /package/dist/{agent/tools → tools}/fetchSkill.d.ts +0 -0
- /package/dist/{agent/tools → tools}/fetchToolSchema.js +0 -0
- /package/dist/{agent/tools → tools}/getUserLocation.d.ts +0 -0
- /package/dist/{agent/tools → tools}/getUserLocation.js +0 -0
- /package/dist/{agent/tools → tools}/index.js +0 -0
- /package/dist/{agent/tools → tools}/navigateUser.js +0 -0
- /package/dist/{agent → tools}/skills/registry.d.ts +0 -0
- /package/dist/{agent → tools}/skills/registry.js +0 -0
- /package/dist/{endpoints/chatSurfaces.d.ts → transport/http/chatSurfaceEndpoints.d.ts} +0 -0
- /package/dist/{endpoints/chatSurfaces.js → transport/http/chatSurfaceEndpoints.js} +0 -0
- /package/dist/{endpoints/core.d.ts → transport/http/coreEndpoints.d.ts} +0 -0
- /package/dist/{endpoints/sessions.d.ts → transport/http/sessionEndpoints.d.ts} +0 -0
package/agent/simpleAgent.ts
DELETED
|
@@ -1,199 +0,0 @@
|
|
|
1
|
-
import type { BaseChatModel } from "@langchain/core/language_models/chat_models";
|
|
2
|
-
import {
|
|
3
|
-
logger,
|
|
4
|
-
type CompletionAdapter,
|
|
5
|
-
} from "adminforth";
|
|
6
|
-
import { BaseCallbackHandler } from "@langchain/core/callbacks/base";
|
|
7
|
-
import type { LLMResult } from "@langchain/core/outputs";
|
|
8
|
-
import {
|
|
9
|
-
createSequenceDebugMiddleware,
|
|
10
|
-
} from "./middleware/sequenceDebug.js";
|
|
11
|
-
|
|
12
|
-
export type AgentChatModel = BaseChatModel<any, any>;
|
|
13
|
-
export type AgentModelPurpose = "primary" | "summary";
|
|
14
|
-
export type AgentModeCompletionAdapter = CompletionAdapter & {
|
|
15
|
-
getLangChainAgentSpec(params: {
|
|
16
|
-
maxTokens: number;
|
|
17
|
-
purpose: AgentModelPurpose;
|
|
18
|
-
}): Promise<{
|
|
19
|
-
model: unknown;
|
|
20
|
-
middleware?: unknown[];
|
|
21
|
-
}> | {
|
|
22
|
-
model: unknown;
|
|
23
|
-
middleware?: unknown[];
|
|
24
|
-
};
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
export type AgentMiddleware = ReturnType<typeof createSequenceDebugMiddleware>;
|
|
28
|
-
|
|
29
|
-
type AgentChatModelSpec = {
|
|
30
|
-
model: AgentChatModel;
|
|
31
|
-
middleware: AgentMiddleware[];
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
type LlmOutputTokenUsage = {
|
|
35
|
-
promptTokens?: unknown;
|
|
36
|
-
completionTokens?: unknown;
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
type MessageUsageMetadata = {
|
|
40
|
-
input_tokens?: unknown;
|
|
41
|
-
output_tokens?: unknown;
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
type PendingLlmRun = {
|
|
45
|
-
startedAt: number;
|
|
46
|
-
firstTokenAt?: number;
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
function isLangChainAgentCompletionAdapter(
|
|
50
|
-
adapter: CompletionAdapter,
|
|
51
|
-
): adapter is AgentModeCompletionAdapter {
|
|
52
|
-
return typeof (adapter as AgentModeCompletionAdapter)
|
|
53
|
-
.getLangChainAgentSpec === "function";
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
async function getAgentChatModelSpec(params: {
|
|
57
|
-
adapter: AgentModeCompletionAdapter;
|
|
58
|
-
maxTokens: number;
|
|
59
|
-
purpose: AgentModelPurpose;
|
|
60
|
-
}): Promise<AgentChatModelSpec> {
|
|
61
|
-
const spec = await params.adapter.getLangChainAgentSpec({
|
|
62
|
-
maxTokens: params.maxTokens,
|
|
63
|
-
purpose: params.purpose,
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
return {
|
|
67
|
-
model: spec.model as AgentChatModel,
|
|
68
|
-
middleware: (spec.middleware ?? []) as AgentMiddleware[],
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function getFiniteNumber(value: unknown) {
|
|
73
|
-
return typeof value === "number" && Number.isFinite(value)
|
|
74
|
-
? value
|
|
75
|
-
: undefined;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function extractTokenUsage(output: LLMResult) {
|
|
79
|
-
const llmOutputTokenUsage = (
|
|
80
|
-
output.llmOutput as { tokenUsage?: LlmOutputTokenUsage } | undefined
|
|
81
|
-
)?.tokenUsage;
|
|
82
|
-
|
|
83
|
-
const promptTokens = getFiniteNumber(llmOutputTokenUsage?.promptTokens);
|
|
84
|
-
const completionTokens = getFiniteNumber(
|
|
85
|
-
llmOutputTokenUsage?.completionTokens,
|
|
86
|
-
);
|
|
87
|
-
|
|
88
|
-
if (promptTokens !== undefined || completionTokens !== undefined) {
|
|
89
|
-
return {
|
|
90
|
-
InputTokens: promptTokens ?? 0,
|
|
91
|
-
outputTokens: completionTokens ?? 0,
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
let InputTokens = 0;
|
|
96
|
-
let outputTokens = 0;
|
|
97
|
-
|
|
98
|
-
for (const generationBatch of output.generations) {
|
|
99
|
-
for (const generation of generationBatch) {
|
|
100
|
-
if (!("message" in generation) || !generation.message) {
|
|
101
|
-
continue;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const message = generation.message as {
|
|
105
|
-
usage_metadata?: MessageUsageMetadata;
|
|
106
|
-
response_metadata?: {
|
|
107
|
-
tokenUsage?: LlmOutputTokenUsage;
|
|
108
|
-
};
|
|
109
|
-
};
|
|
110
|
-
|
|
111
|
-
InputTokens +=
|
|
112
|
-
getFiniteNumber(message.usage_metadata?.input_tokens) ??
|
|
113
|
-
getFiniteNumber(message.response_metadata?.tokenUsage?.promptTokens) ??
|
|
114
|
-
0;
|
|
115
|
-
outputTokens +=
|
|
116
|
-
getFiniteNumber(message.usage_metadata?.output_tokens) ??
|
|
117
|
-
getFiniteNumber(
|
|
118
|
-
message.response_metadata?.tokenUsage?.completionTokens,
|
|
119
|
-
) ??
|
|
120
|
-
0;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
return { InputTokens, outputTokens };
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
class AgentLlmMetricsLogger extends BaseCallbackHandler {
|
|
128
|
-
name = "AgentLlmMetricsLogger";
|
|
129
|
-
lc_prefer_streaming = true;
|
|
130
|
-
|
|
131
|
-
private readonly pendingRuns = new Map<string, PendingLlmRun>();
|
|
132
|
-
|
|
133
|
-
async handleLLMStart(_llm: unknown, _prompts: string[], runId: string) {
|
|
134
|
-
this.pendingRuns.set(runId, { startedAt: Date.now() });
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
async handleLLMNewToken(
|
|
138
|
-
_token: string,
|
|
139
|
-
_chunk: unknown,
|
|
140
|
-
runId: string,
|
|
141
|
-
) {
|
|
142
|
-
const pendingRun = this.pendingRuns.get(runId);
|
|
143
|
-
|
|
144
|
-
if (!pendingRun || pendingRun.firstTokenAt !== undefined) {
|
|
145
|
-
return;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
pendingRun.firstTokenAt = Date.now();
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
async handleLLMEnd(output: LLMResult, runId: string) {
|
|
152
|
-
const pendingRun = this.pendingRuns.get(runId);
|
|
153
|
-
|
|
154
|
-
if (!pendingRun) {
|
|
155
|
-
return;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
this.pendingRuns.delete(runId);
|
|
159
|
-
|
|
160
|
-
const finishedAt = Date.now();
|
|
161
|
-
const rtt = finishedAt - pendingRun.startedAt;
|
|
162
|
-
const ttft =
|
|
163
|
-
pendingRun.firstTokenAt === undefined
|
|
164
|
-
? rtt
|
|
165
|
-
: pendingRun.firstTokenAt - pendingRun.startedAt;
|
|
166
|
-
const { InputTokens, outputTokens } = extractTokenUsage(output);
|
|
167
|
-
|
|
168
|
-
logger.info(
|
|
169
|
-
{ InputTokens, outputTokens, ttft, rtt },
|
|
170
|
-
"LLM call finished",
|
|
171
|
-
);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
async handleLLMError(_error: unknown, runId: string) {
|
|
175
|
-
this.pendingRuns.delete(runId);
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
export function createAgentLlmMetricsLogger() {
|
|
180
|
-
return new AgentLlmMetricsLogger();
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
export async function createAgentChatModel(params: {
|
|
184
|
-
adapter: CompletionAdapter;
|
|
185
|
-
maxTokens: number;
|
|
186
|
-
purpose: AgentModelPurpose;
|
|
187
|
-
}) {
|
|
188
|
-
if (!isLangChainAgentCompletionAdapter(params.adapter)) {
|
|
189
|
-
throw new Error(
|
|
190
|
-
"AdminForth Agent requires completionAdapter to implement getLangChainAgentSpec({ maxTokens, purpose }).",
|
|
191
|
-
);
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
return await getAgentChatModelSpec({
|
|
195
|
-
adapter: params.adapter,
|
|
196
|
-
maxTokens: params.maxTokens,
|
|
197
|
-
purpose: params.purpose,
|
|
198
|
-
});
|
|
199
|
-
}
|
package/agent/skills/registry.ts
DELETED
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
import { readdir, readFile } from "fs/promises";
|
|
2
|
-
import path from "path";
|
|
3
|
-
import { parse as parseYaml } from "yaml";
|
|
4
|
-
|
|
5
|
-
const SKILL_MARKDOWN_FILENAME = "SKILL.md";
|
|
6
|
-
const SKILL_FRONTMATTER_SEPARATOR = "\n---\n";
|
|
7
|
-
|
|
8
|
-
export interface AgentSkillManifest {
|
|
9
|
-
directoryName: string;
|
|
10
|
-
name: string;
|
|
11
|
-
description: string;
|
|
12
|
-
instructions: string;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function normalizePluginSkillDirectoryPaths(pluginCustomFolderPaths: string[] = []) {
|
|
16
|
-
return Array.from(new Set(
|
|
17
|
-
pluginCustomFolderPaths
|
|
18
|
-
.map((pluginCustomFolderPath) => path.resolve(pluginCustomFolderPath, "skills")),
|
|
19
|
-
));
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function parseSkillManifest(directoryName: string, markdown: string): AgentSkillManifest {
|
|
23
|
-
const [frontmatterBlock, instructions = ""] = markdown.split("\r\n").join("\n").split(
|
|
24
|
-
SKILL_FRONTMATTER_SEPARATOR,
|
|
25
|
-
2,
|
|
26
|
-
);
|
|
27
|
-
const metadata = parseYaml(frontmatterBlock) as {
|
|
28
|
-
name?: string;
|
|
29
|
-
description?: string;
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
return {
|
|
33
|
-
directoryName,
|
|
34
|
-
name: metadata.name ?? directoryName,
|
|
35
|
-
description: metadata.description ?? "",
|
|
36
|
-
instructions: instructions.trim(),
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
async function readSkillManifest(skillsDirectoryPath: string, directoryName: string) {
|
|
41
|
-
const markdown = await readFile(
|
|
42
|
-
path.join(skillsDirectoryPath, directoryName, SKILL_MARKDOWN_FILENAME),
|
|
43
|
-
"utf8",
|
|
44
|
-
);
|
|
45
|
-
|
|
46
|
-
return parseSkillManifest(directoryName, markdown);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
async function listDirectorySkillManifests(skillsDirectoryPath: string) {
|
|
50
|
-
try {
|
|
51
|
-
const entries = await readdir(skillsDirectoryPath, { withFileTypes: true });
|
|
52
|
-
|
|
53
|
-
return await Promise.all(
|
|
54
|
-
entries
|
|
55
|
-
.filter((entry) => entry.isDirectory())
|
|
56
|
-
.map((entry) => entry.name)
|
|
57
|
-
.sort()
|
|
58
|
-
.map((directoryName) => readSkillManifest(skillsDirectoryPath, directoryName)),
|
|
59
|
-
);
|
|
60
|
-
} catch (error) {
|
|
61
|
-
if ((error as NodeJS.ErrnoException).code === "ENOENT") {
|
|
62
|
-
return [];
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
throw error;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function mergeSkillManifests(skillGroups: AgentSkillManifest[][]) {
|
|
70
|
-
return Array.from(
|
|
71
|
-
new Map(
|
|
72
|
-
skillGroups.flat().map((skill) => [
|
|
73
|
-
`${skill.name}:${skill.directoryName}`,
|
|
74
|
-
skill,
|
|
75
|
-
]),
|
|
76
|
-
).values(),
|
|
77
|
-
);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export function getProjectSkillsDirectoryPath(customComponentsDir: string) {
|
|
81
|
-
return path.resolve(customComponentsDir, "skills");
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function getProjectSkillDirectoryPaths(
|
|
85
|
-
customComponentsDir: string,
|
|
86
|
-
pluginCustomFolderPaths: string[] = [],
|
|
87
|
-
) {
|
|
88
|
-
return Array.from(new Set([
|
|
89
|
-
getProjectSkillsDirectoryPath(customComponentsDir),
|
|
90
|
-
...normalizePluginSkillDirectoryPaths(pluginCustomFolderPaths),
|
|
91
|
-
]));
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
export async function listProjectSkillManifests(
|
|
95
|
-
customComponentsDir: string,
|
|
96
|
-
pluginCustomFolderPaths: string[] = [],
|
|
97
|
-
) {
|
|
98
|
-
return mergeSkillManifests(
|
|
99
|
-
await Promise.all(
|
|
100
|
-
getProjectSkillDirectoryPaths(customComponentsDir, pluginCustomFolderPaths).map(
|
|
101
|
-
listDirectorySkillManifests,
|
|
102
|
-
),
|
|
103
|
-
),
|
|
104
|
-
);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
export async function listSkillManifests(
|
|
108
|
-
customComponentsDir: string,
|
|
109
|
-
pluginCustomFolderPaths: string[] = [],
|
|
110
|
-
) {
|
|
111
|
-
return await listProjectSkillManifests(customComponentsDir, pluginCustomFolderPaths);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
export async function loadSkillManifest(
|
|
115
|
-
skillName: string,
|
|
116
|
-
customComponentsDir: string,
|
|
117
|
-
pluginCustomFolderPaths: string[] = [],
|
|
118
|
-
) {
|
|
119
|
-
const manifests = await listSkillManifests(customComponentsDir, pluginCustomFolderPaths);
|
|
120
|
-
|
|
121
|
-
return (
|
|
122
|
-
manifests.find(
|
|
123
|
-
(manifest) =>
|
|
124
|
-
manifest.name === skillName || manifest.directoryName === skillName,
|
|
125
|
-
) ?? null
|
|
126
|
-
);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
export async function loadSkillMarkdown(
|
|
130
|
-
skillName: string,
|
|
131
|
-
customComponentsDir: string,
|
|
132
|
-
pluginCustomFolderPaths: string[] = [],
|
|
133
|
-
) {
|
|
134
|
-
const manifest = await loadSkillManifest(
|
|
135
|
-
skillName,
|
|
136
|
-
customComponentsDir,
|
|
137
|
-
pluginCustomFolderPaths,
|
|
138
|
-
);
|
|
139
|
-
|
|
140
|
-
if (!manifest) {
|
|
141
|
-
return null;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
const directories = [
|
|
145
|
-
...getProjectSkillDirectoryPaths(customComponentsDir, pluginCustomFolderPaths),
|
|
146
|
-
];
|
|
147
|
-
|
|
148
|
-
for (const skillsDirectoryPath of directories) {
|
|
149
|
-
try {
|
|
150
|
-
return await readFile(
|
|
151
|
-
path.join(skillsDirectoryPath, manifest.directoryName, SKILL_MARKDOWN_FILENAME),
|
|
152
|
-
"utf8",
|
|
153
|
-
);
|
|
154
|
-
} catch (error) {
|
|
155
|
-
if ((error as NodeJS.ErrnoException).code !== "ENOENT") {
|
|
156
|
-
throw error;
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
return null;
|
|
162
|
-
}
|
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
import { logger } from "adminforth";
|
|
2
|
-
import { getErrorMessage, isAbortError } from "../../errors.js";
|
|
3
|
-
import { sanitizeSpeechText } from "../../sanitizeSpeechText.js";
|
|
4
|
-
import type {
|
|
5
|
-
RunAndPersistAgentResponseInput,
|
|
6
|
-
RunAndPersistAgentResponseResult,
|
|
7
|
-
SpeechAgentTurnInput,
|
|
8
|
-
} from "../turn/turnTypes.js";
|
|
9
|
-
|
|
10
|
-
export class SpeechTurnService {
|
|
11
|
-
constructor(
|
|
12
|
-
private readonly runAndPersistAgentResponse: (
|
|
13
|
-
input: RunAndPersistAgentResponseInput,
|
|
14
|
-
) => Promise<RunAndPersistAgentResponseResult>,
|
|
15
|
-
) {}
|
|
16
|
-
|
|
17
|
-
async handle(input: SpeechAgentTurnInput) {
|
|
18
|
-
let transcription;
|
|
19
|
-
|
|
20
|
-
try {
|
|
21
|
-
transcription = await input.audioAdapter.transcribe({
|
|
22
|
-
buffer: input.audio.buffer,
|
|
23
|
-
filename: input.audio.filename,
|
|
24
|
-
mimeType: input.audio.mimeType,
|
|
25
|
-
language: "auto",
|
|
26
|
-
abortSignal: input.abortSignal,
|
|
27
|
-
});
|
|
28
|
-
} catch (error) {
|
|
29
|
-
if (input.abortSignal?.aborted || isAbortError(error)) {
|
|
30
|
-
logger.info("Agent speech transcription aborted by the client");
|
|
31
|
-
await input.emit({ type: "finish" });
|
|
32
|
-
return null;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
logger.error(`Agent speech transcription failed:\n${getErrorMessage(error)}`);
|
|
36
|
-
await input.emit({
|
|
37
|
-
type: "error",
|
|
38
|
-
error: "Speech transcription failed. Check server logs for details.",
|
|
39
|
-
});
|
|
40
|
-
await input.emit({ type: "finish" });
|
|
41
|
-
return null;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (input.abortSignal?.aborted) {
|
|
45
|
-
await input.emit({ type: "finish" });
|
|
46
|
-
return null;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const prompt = transcription.text;
|
|
50
|
-
if (!prompt) {
|
|
51
|
-
await input.emit({
|
|
52
|
-
type: "error",
|
|
53
|
-
error: "Speech transcription is empty",
|
|
54
|
-
});
|
|
55
|
-
await input.emit({ type: "finish" });
|
|
56
|
-
return null;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
await input.emit({
|
|
60
|
-
type: "transcript",
|
|
61
|
-
text: transcription.text,
|
|
62
|
-
language: transcription.language,
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
const agentResponse = await this.runAndPersistAgentResponse({
|
|
66
|
-
prompt,
|
|
67
|
-
sessionId: input.sessionId,
|
|
68
|
-
modeName: input.modeName,
|
|
69
|
-
userTimeZone: input.userTimeZone,
|
|
70
|
-
currentPage: input.currentPage,
|
|
71
|
-
chatSurface: input.chatSurface,
|
|
72
|
-
adminPublicOrigin: input.adminPublicOrigin,
|
|
73
|
-
abortSignal: input.abortSignal,
|
|
74
|
-
adminUser: input.adminUser,
|
|
75
|
-
emit: async (event) => {
|
|
76
|
-
if (event.type === "tool-call") {
|
|
77
|
-
await input.emit(event);
|
|
78
|
-
}
|
|
79
|
-
},
|
|
80
|
-
failureLogMessage: input.failureLogMessage ?? "Agent speech response failed",
|
|
81
|
-
abortLogMessage: input.abortLogMessage ?? "Agent speech response aborted by the client",
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
if (agentResponse.aborted) {
|
|
85
|
-
await input.emit({ type: "finish" });
|
|
86
|
-
return agentResponse;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
if (agentResponse.failed) {
|
|
90
|
-
await input.emit({
|
|
91
|
-
type: "error",
|
|
92
|
-
error: agentResponse.text,
|
|
93
|
-
});
|
|
94
|
-
await input.emit({ type: "finish" });
|
|
95
|
-
return agentResponse;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
try {
|
|
99
|
-
await input.emit({
|
|
100
|
-
type: "speech-response",
|
|
101
|
-
transcript: {
|
|
102
|
-
text: transcription.text,
|
|
103
|
-
language: transcription.language,
|
|
104
|
-
},
|
|
105
|
-
response: {
|
|
106
|
-
text: agentResponse.text,
|
|
107
|
-
},
|
|
108
|
-
sessionId: input.sessionId,
|
|
109
|
-
turnId: agentResponse.turnId,
|
|
110
|
-
});
|
|
111
|
-
const speech = await input.audioAdapter.synthesize({
|
|
112
|
-
text: sanitizeSpeechText(agentResponse.text),
|
|
113
|
-
stream: true,
|
|
114
|
-
streamFormat: "audio",
|
|
115
|
-
format: "pcm",
|
|
116
|
-
abortSignal: input.abortSignal,
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
await input.emit({
|
|
120
|
-
type: "audio-start",
|
|
121
|
-
mimeType: speech.mimeType,
|
|
122
|
-
format: speech.format,
|
|
123
|
-
sampleRate: 24000,
|
|
124
|
-
channelCount: 1,
|
|
125
|
-
bitsPerSample: 16,
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
const reader = speech.audioStream.getReader();
|
|
129
|
-
const cancelAudioStream = () => {
|
|
130
|
-
void reader.cancel().catch(() => undefined);
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
try {
|
|
134
|
-
input.abortSignal?.addEventListener("abort", cancelAudioStream, { once: true });
|
|
135
|
-
|
|
136
|
-
while (true) {
|
|
137
|
-
if (input.abortSignal?.aborted) {
|
|
138
|
-
await reader.cancel().catch(() => undefined);
|
|
139
|
-
break;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
const { value, done } = await reader.read();
|
|
143
|
-
|
|
144
|
-
if (done) {
|
|
145
|
-
break;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
if (input.abortSignal?.aborted) {
|
|
149
|
-
break;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
await input.emit({
|
|
153
|
-
type: "audio-delta",
|
|
154
|
-
value,
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
} finally {
|
|
158
|
-
input.abortSignal?.removeEventListener("abort", cancelAudioStream);
|
|
159
|
-
reader.releaseLock();
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
await input.emit({ type: "audio-done" });
|
|
163
|
-
await input.emit({ type: "finish" });
|
|
164
|
-
return agentResponse;
|
|
165
|
-
} catch (error) {
|
|
166
|
-
if (input.abortSignal?.aborted || isAbortError(error)) {
|
|
167
|
-
logger.info("Agent speech audio streaming aborted by the client");
|
|
168
|
-
} else {
|
|
169
|
-
logger.error(`Agent speech audio streaming failed:\n${getErrorMessage(error)}`);
|
|
170
|
-
await input.emit({
|
|
171
|
-
type: "error",
|
|
172
|
-
error: getErrorMessage(error),
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
await input.emit({ type: "finish" });
|
|
176
|
-
return agentResponse;
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
}
|
package/agent/systemPrompt.ts
DELETED
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
import type { AdminForthResource, AdminUser, IAdminForth } from "adminforth";
|
|
2
|
-
import type { DetectedLanguage } from "./languageDetect.js";
|
|
3
|
-
import {
|
|
4
|
-
listProjectSkillManifests,
|
|
5
|
-
type AgentSkillManifest,
|
|
6
|
-
} from "./skills/registry.js";
|
|
7
|
-
|
|
8
|
-
export const DEFAULT_AGENT_SYSTEM_PROMPT = [
|
|
9
|
-
"You are helpful AI Assistant for Admin Panel.",
|
|
10
|
-
|
|
11
|
-
// about admin
|
|
12
|
-
"Admin panel has resources which represent some physical data storage (e.g. table/collection), each resource defines list of columns.",
|
|
13
|
-
"Each resource stores data records. Record represents a data item of resource.",
|
|
14
|
-
|
|
15
|
-
//about user
|
|
16
|
-
"Assume user is not technical so does not talk to him in terms of API calls, databases/sql/json etc.",
|
|
17
|
-
|
|
18
|
-
// prevent extra talk
|
|
19
|
-
"Try to achieve user's goal with as few steps as possible. Talk with him only when you need some important decision, otherwise act immediately and call tools asap",
|
|
20
|
-
|
|
21
|
-
// tone of voice
|
|
22
|
-
"Be warm, friendly, and sincere.",
|
|
23
|
-
"Keep responses short, clear, and practical.",
|
|
24
|
-
"Answer only what is needed.",
|
|
25
|
-
"Do not add extra explanations or suggestions unless the user asks.",
|
|
26
|
-
"Adapt to the user's tone and style of speaking, mirroring their vibe and wording.",
|
|
27
|
-
"if the user speaks casually, you should respond casually too",
|
|
28
|
-
"Before calling a dangerous tool, briefly describe the exact action, target, and important changes in chat.",
|
|
29
|
-
"Do not ask the user for textual confirmation; dangerous tools are approved by the runtime approval UI.",
|
|
30
|
-
].join(" ");
|
|
31
|
-
|
|
32
|
-
export function appendCustomSystemPrompt(
|
|
33
|
-
systemPrompt: string,
|
|
34
|
-
customSystemPrompt?: string,
|
|
35
|
-
) {
|
|
36
|
-
const normalizedCustomSystemPrompt = customSystemPrompt?.trim();
|
|
37
|
-
|
|
38
|
-
if (!normalizedCustomSystemPrompt) {
|
|
39
|
-
return systemPrompt;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return `${systemPrompt}\n\n${normalizedCustomSystemPrompt}`;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function formatLanguagePrompt(language: DetectedLanguage | null) {
|
|
46
|
-
if (!language || language.ambiguous) {
|
|
47
|
-
return "Respond in the user's language.";
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return `Respond in ${language.language} (${language.code}).`;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function formatAdminUserPrompt(adminUser: AdminUser, usernameField: string) {
|
|
54
|
-
const adminUserContext = {
|
|
55
|
-
id: adminUser.pk,
|
|
56
|
-
email: adminUser.dbUser[usernameField],
|
|
57
|
-
};
|
|
58
|
-
return [
|
|
59
|
-
"Current admin user context:",
|
|
60
|
-
JSON.stringify(adminUserContext, null, 2),
|
|
61
|
-
"Use this admin user email when the user asks to send information to themselves, the current admin, or the logged-in user.",
|
|
62
|
-
].join("\n");
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export function buildAgentTurnSystemPrompt(input: {
|
|
66
|
-
agentSystemPrompt: string;
|
|
67
|
-
adminUser: AdminUser;
|
|
68
|
-
usernameField: string;
|
|
69
|
-
userLanguage: DetectedLanguage | null;
|
|
70
|
-
chatSurface?: string;
|
|
71
|
-
}) {
|
|
72
|
-
return [
|
|
73
|
-
input.agentSystemPrompt,
|
|
74
|
-
formatAdminUserPrompt(input.adminUser, input.usernameField),
|
|
75
|
-
input.chatSurface
|
|
76
|
-
? `Current chat surface: ${input.chatSurface}. The user is not in the AdminForth web UI, so tools cannot move their browser. When navigate_user returns a link, send that link to the user.`
|
|
77
|
-
: "",
|
|
78
|
-
formatLanguagePrompt(input.userLanguage),
|
|
79
|
-
].filter(Boolean).join("\n\n");
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
function formatResources(resources: AdminForthResource[]) {
|
|
83
|
-
return resources
|
|
84
|
-
.map((resource) => `- resourceId: ${resource.resourceId}\n label: ${resource.label}`)
|
|
85
|
-
.join("\n");
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function formatSkills(skills: AgentSkillManifest[], label: "skill_name" | "tool_name") {
|
|
89
|
-
return skills
|
|
90
|
-
.map((skill) => `- ${label}: ${skill.name}\n description: ${skill.description}`)
|
|
91
|
-
.join("\n");
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
export async function buildAgentSystemPrompt(
|
|
95
|
-
adminforth: IAdminForth,
|
|
96
|
-
hiddenResourceIds: Iterable<string> = [],
|
|
97
|
-
) {
|
|
98
|
-
const customComponentsDir = adminforth.config.customization.customComponentsDir ?? "custom";
|
|
99
|
-
const pluginCustomFolderPaths = adminforth.activatedPlugins
|
|
100
|
-
.map((plugin) => plugin.customFolderPath);
|
|
101
|
-
const skills = await listProjectSkillManifests(
|
|
102
|
-
customComponentsDir,
|
|
103
|
-
pluginCustomFolderPaths,
|
|
104
|
-
);
|
|
105
|
-
const adminBasePath = adminforth.config.baseUrlSlashed;
|
|
106
|
-
const hiddenResourceIdSet = new Set(hiddenResourceIds);
|
|
107
|
-
const visibleResources = adminforth.config.resources.filter(
|
|
108
|
-
(resource) => !hiddenResourceIdSet.has(resource.resourceId),
|
|
109
|
-
);
|
|
110
|
-
const sections = [
|
|
111
|
-
DEFAULT_AGENT_SYSTEM_PROMPT,
|
|
112
|
-
`ADMIN_BASE_PATH: ${adminBasePath}`,
|
|
113
|
-
`List of resources:\n${formatResources(visibleResources)}`,
|
|
114
|
-
skills.length > 0
|
|
115
|
-
? `You have skills set:\n${formatSkills(skills, "skill_name")}`
|
|
116
|
-
: "",
|
|
117
|
-
"Before using any skill, call fetch_skill to load its full instructions.",
|
|
118
|
-
"The fetched skill response starts with 'Tools mentioned in this skill'. Read that list first.",
|
|
119
|
-
"You can use get_resource immediately to inspect resource structure and column names.",
|
|
120
|
-
"If the user wants to create, update, delete, or run actions on records, load mutate_data first.",
|
|
121
|
-
"If the user wants to fetch records, load fetch_data first. If the user wants analytics or charts, load analyze_data first.",
|
|
122
|
-
"Only call fetch_tool_schema for tool names that are explicitly mentioned in a fetched skill and are not already available as base tools.",
|
|
123
|
-
"If a fetched skill lists a non-base tool you need, call fetch_tool_schema for it immediately instead of telling the user the tool is unavailable.",
|
|
124
|
-
"For example: for record creation load mutate_data, read its tool list, call fetch_tool_schema for create_record, describe the planned record, and then use create_record.",
|
|
125
|
-
"When fetch_tool_schema succeeds, that tool becomes available on the next step.",
|
|
126
|
-
"All admin links must be root-relative and start with '/'.",
|
|
127
|
-
"Build record links as '/resource/{resourceId}/show/{primary key}'. Never use bare 'resource/{resourceId}/show/{primary key}' without the leading slash.",
|
|
128
|
-
"When the user asks to open or show a page in the AdminForth UI, call navigate_user instead of only sending a link.",
|
|
129
|
-
"Try to call as many tools as possible in parallel in one step.",
|
|
130
|
-
];
|
|
131
|
-
|
|
132
|
-
return sections.filter(Boolean).join("\n\n");
|
|
133
|
-
}
|