@adminforth/agent 2.0.10 → 2.2.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 +71 -0
- package/dist/application/runTurnUseCase.js +364 -0
- package/dist/custom/ChatFooter.vue +58 -30
- package/dist/custom/SteerDebugPanel.vue +45 -0
- package/dist/custom/SteerQueue.vue +54 -0
- package/dist/custom/composables/agentStore/constants.ts +1 -0
- package/dist/custom/composables/agentStore/useAgentSessions.ts +35 -16
- package/dist/custom/composables/agentStore/useAgentSteerQueue.ts +114 -0
- package/dist/custom/composables/useAgentStore.ts +46 -1
- package/dist/custom/conversation_area/MessageRenderer.vue +7 -1
- package/dist/custom/speech_recognition_frontend/AudioLines.vue +1 -1
- package/dist/custom/speech_recognition_frontend/MicrophoneButon.vue +3 -3
- package/dist/{agentEvents.d.ts → domain/agentEvents.d.ts} +5 -1
- package/dist/domain/steerBuffer.d.ts +33 -0
- package/dist/domain/steerBuffer.js +51 -0
- 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 +4 -2
- package/dist/index.js +49 -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} +14 -2
- package/dist/{agent/runtime/AgentRuntime.js → llm/agentRuntime.js} +41 -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/llm/middleware/steerMiddleware.d.ts +21 -0
- package/dist/llm/middleware/steerMiddleware.js +50 -0
- 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} +19 -2
- package/dist/{sessionStore.js → persistence/sessionStore.js} +53 -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 +12 -4
- package/dist/transport/http/context.js +1 -0
- package/dist/{endpoints/core.js → transport/http/coreEndpoints.js} +23 -3
- package/dist/{endpoints/sessions.js → transport/http/sessionEndpoints.js} +36 -1
- package/dist/transport/sse/sseWriter.d.ts +29 -0
- package/dist/{surfaces/web-sse/createSseEventEmitter.js → transport/sse/sseWriter.js} +27 -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
|
@@ -2,7 +2,7 @@ import type { ComputedRef, Ref, ShallowRef } from 'vue';
|
|
|
2
2
|
import { callAdminForthApi } from '@/utils';
|
|
3
3
|
import type { Chat } from '../../chat';
|
|
4
4
|
import type { IAgentSession, ISessionsListItem, IPart } from '../../types';
|
|
5
|
-
import { PRE_SESSION_ID } from './constants';
|
|
5
|
+
import { PRE_SESSION_ID, STEER_PERSIST_PREFIX } from './constants';
|
|
6
6
|
import { i18nInstance } from '@/i18n';
|
|
7
7
|
|
|
8
8
|
type AdminforthLike = {
|
|
@@ -50,14 +50,36 @@ export function createAgentSessionManager({
|
|
|
50
50
|
|
|
51
51
|
function saveCurrentSessionInCache() {
|
|
52
52
|
if (currentSession.value) {
|
|
53
|
-
currentSession.value.messages = currentChat.value?.messages.map((m: any) =>
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
53
|
+
currentSession.value.messages = currentChat.value?.messages.map((m: any) => {
|
|
54
|
+
const text = m.parts.map((p: IPart) => p.type === 'text' ? p.text : '').join('');
|
|
55
|
+
if (m.role === 'user' && m.metadata?.steer) {
|
|
56
|
+
return { role: 'user', text: `${STEER_PERSIST_PREFIX}${text}` };
|
|
57
|
+
}
|
|
58
|
+
return { role: m.role, text };
|
|
59
|
+
}) || [];
|
|
57
60
|
sessions.value[currentSession.value.sessionId] = currentSession.value;
|
|
58
61
|
}
|
|
59
62
|
}
|
|
60
63
|
|
|
64
|
+
function mapStoredMessage(m: any): any[] {
|
|
65
|
+
if (m.role === 'user' && typeof m.text === 'string' && m.text.includes(STEER_PERSIST_PREFIX)) {
|
|
66
|
+
const [prompt, ...steers] = m.text.split(STEER_PERSIST_PREFIX);
|
|
67
|
+
const mapped: any[] = [];
|
|
68
|
+
if (prompt) {
|
|
69
|
+
mapped.push({ role: 'user', parts: [{ type: 'text', text: prompt, state: 'done' }] });
|
|
70
|
+
}
|
|
71
|
+
for (const steer of steers) {
|
|
72
|
+
mapped.push({
|
|
73
|
+
role: 'user',
|
|
74
|
+
metadata: { steer: true },
|
|
75
|
+
parts: [{ type: 'text', text: steer, state: 'done' }],
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
return mapped;
|
|
79
|
+
}
|
|
80
|
+
return [{ role: m.role, parts: [{ type: 'text', text: m.text, state: 'done' }] }];
|
|
81
|
+
}
|
|
82
|
+
|
|
61
83
|
async function fetchSession(sessionId: string) {
|
|
62
84
|
try {
|
|
63
85
|
const res = await callAdminForthApi({
|
|
@@ -85,14 +107,7 @@ export function createAgentSessionManager({
|
|
|
85
107
|
currentSession.value = sessions.value[sessionId];
|
|
86
108
|
setCurrentChat(sessionId);
|
|
87
109
|
if (currentChat.value.messages.length === 0) {
|
|
88
|
-
currentChat.value.messages = currentSession.value?.messages.
|
|
89
|
-
role: m.role,
|
|
90
|
-
parts:[{
|
|
91
|
-
type: 'text',
|
|
92
|
-
text: m.text,
|
|
93
|
-
state: 'done',
|
|
94
|
-
}]
|
|
95
|
-
}));
|
|
110
|
+
currentChat.value.messages = currentSession.value?.messages.flatMap(mapStoredMessage);
|
|
96
111
|
}
|
|
97
112
|
}
|
|
98
113
|
|
|
@@ -130,8 +145,10 @@ export function createAgentSessionManager({
|
|
|
130
145
|
}
|
|
131
146
|
}
|
|
132
147
|
|
|
133
|
-
async function sendMessage() {
|
|
134
|
-
|
|
148
|
+
async function sendMessage(explicitMessage?: string) {
|
|
149
|
+
// `explicitMessage` is used to drain a buffered (queued) message; without it we send
|
|
150
|
+
// whatever is currently typed in the input.
|
|
151
|
+
const message = (explicitMessage ?? trimmedUserMessage.value).trim();
|
|
135
152
|
if (!message || isMessageInputBlocked.value) {
|
|
136
153
|
return;
|
|
137
154
|
}
|
|
@@ -147,7 +164,9 @@ export function createAgentSessionManager({
|
|
|
147
164
|
currentChat.value?.sendMessage({
|
|
148
165
|
text: message,
|
|
149
166
|
});
|
|
150
|
-
|
|
167
|
+
if (explicitMessage === undefined) {
|
|
168
|
+
userMessageInput.value = '';
|
|
169
|
+
}
|
|
151
170
|
}
|
|
152
171
|
|
|
153
172
|
async function createPreSession() {
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import type { Ref, ShallowRef } from 'vue';
|
|
2
|
+
import { ref } from 'vue';
|
|
3
|
+
import { callAdminForthApi } from '@/utils';
|
|
4
|
+
import type { Chat } from '../../chat';
|
|
5
|
+
import type { IMessage } from '../../types';
|
|
6
|
+
import { PRE_SESSION_ID } from './constants';
|
|
7
|
+
|
|
8
|
+
export type QueuedMessage = {
|
|
9
|
+
id: string;
|
|
10
|
+
text: string;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
type CreateAgentSteerQueueOptions = {
|
|
14
|
+
activeSessionId: Ref<string | null>;
|
|
15
|
+
currentChat: ShallowRef<Chat<any> | null | undefined>;
|
|
16
|
+
sendMessage: (text: string) => void | Promise<void>;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export function createAgentSteerQueue({
|
|
20
|
+
activeSessionId,
|
|
21
|
+
currentChat,
|
|
22
|
+
sendMessage,
|
|
23
|
+
}: CreateAgentSteerQueueOptions) {
|
|
24
|
+
const queue = ref<QueuedMessage[]>([]);
|
|
25
|
+
|
|
26
|
+
function enqueue(text: string) {
|
|
27
|
+
const message = text.trim();
|
|
28
|
+
if (!message) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
queue.value.push({ id: crypto.randomUUID(), text: message });
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function removeQueuedMessage(id: string) {
|
|
35
|
+
queue.value = queue.value.filter((item: QueuedMessage) => item.id !== id);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function injectSteerMessage(text: string) {
|
|
39
|
+
const chat = currentChat.value;
|
|
40
|
+
if (!chat) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const steerMessage: IMessage = {
|
|
44
|
+
role: 'user',
|
|
45
|
+
metadata: { steer: true },
|
|
46
|
+
parts: [{ type: 'text', text, state: 'done' }],
|
|
47
|
+
};
|
|
48
|
+
const messages = chat.messages;
|
|
49
|
+
const lastMessage = messages[messages.length - 1];
|
|
50
|
+
if (lastMessage?.role === 'assistant') {
|
|
51
|
+
messages.splice(messages.length - 1, 0, steerMessage);
|
|
52
|
+
} else {
|
|
53
|
+
messages.push(steerMessage);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Persist the steer by appending it onto the running turn's prompt (display only — the
|
|
58
|
+
// LLM already has it via the running turn + checkpointer, so this never re-enters the
|
|
59
|
+
// model context). The backend adds the marker; the reload path splits it back out.
|
|
60
|
+
async function persistSteerMessage(sessionId: string, text: string) {
|
|
61
|
+
try {
|
|
62
|
+
const res = await callAdminForthApi({
|
|
63
|
+
method: 'POST',
|
|
64
|
+
path: '/agent/append-steer-to-turn',
|
|
65
|
+
body: { sessionId, message: text },
|
|
66
|
+
});
|
|
67
|
+
if (res?.error) {
|
|
68
|
+
console.error('Error persisting steer message:', res.error);
|
|
69
|
+
}
|
|
70
|
+
} catch (error) {
|
|
71
|
+
console.error('Error persisting steer message', error);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async function steerQueuedMessage(id: string) {
|
|
76
|
+
const item = queue.value.find((candidate: QueuedMessage) => candidate.id === id);
|
|
77
|
+
const sessionId = activeSessionId.value;
|
|
78
|
+
if (!item || !sessionId || sessionId === PRE_SESSION_ID) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
removeQueuedMessage(id);
|
|
82
|
+
injectSteerMessage(item.text);
|
|
83
|
+
void persistSteerMessage(sessionId, item.text);
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
const res = await callAdminForthApi({
|
|
87
|
+
method: 'POST',
|
|
88
|
+
path: '/agent/steer',
|
|
89
|
+
body: { sessionId, message: item.text },
|
|
90
|
+
});
|
|
91
|
+
if (res?.error) {
|
|
92
|
+
console.error('Error steering agent:', res.error);
|
|
93
|
+
}
|
|
94
|
+
} catch (error) {
|
|
95
|
+
console.error('Error steering agent', error);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function flushNext() {
|
|
100
|
+
const next = queue.value.shift();
|
|
101
|
+
if (!next) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
void sendMessage(next.text);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
queue,
|
|
109
|
+
enqueue,
|
|
110
|
+
removeQueuedMessage,
|
|
111
|
+
steerQueuedMessage,
|
|
112
|
+
flushNext,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
import { createAgentChatManager } from './agentStore/useAgentChat';
|
|
19
19
|
import { createAgentPlaceholderController } from './agentStore/useAgentPlaceholder';
|
|
20
20
|
import { createAgentSessionManager } from './agentStore/useAgentSessions';
|
|
21
|
+
import { createAgentSteerQueue } from './agentStore/useAgentSteerQueue';
|
|
21
22
|
|
|
22
23
|
export const useAgentStore = defineStore('agent', () => {
|
|
23
24
|
const agentTransitions = useAgentTransitions();
|
|
@@ -220,6 +221,42 @@ export const useAgentStore = defineStore('agent', () => {
|
|
|
220
221
|
setCurrentChat,
|
|
221
222
|
});
|
|
222
223
|
|
|
224
|
+
// "Turn active" = a request is in flight (submitted or streaming) or paused for HITL
|
|
225
|
+
// approval. Messages submitted during this window are buffered rather than sent.
|
|
226
|
+
const isTurnActive = computed(() => {
|
|
227
|
+
const status = (currentChat.value as any)?.status;
|
|
228
|
+
return status === 'submitted' || status === 'streaming' || hasPendingToolApproval.value;
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
const steerBuffer = createAgentSteerQueue({
|
|
232
|
+
activeSessionId,
|
|
233
|
+
currentChat,
|
|
234
|
+
sendMessage,
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
// Submitting while a turn is active buffers the message (it shows up in the steer
|
|
238
|
+
// queue, where the user can steer it or let it send); otherwise it sends right away.
|
|
239
|
+
function submitUserMessage() {
|
|
240
|
+
const message = trimmedUserMessage.value;
|
|
241
|
+
if (!message) {
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
if (isTurnActive.value) {
|
|
245
|
+
steerBuffer.enqueue(message);
|
|
246
|
+
userMessageInput.value = '';
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
return sendMessage();
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// When the active turn finishes, drain the next buffered message as a normal send
|
|
253
|
+
// (one per turn, FIFO). Messages the user explicitly steered were already removed.
|
|
254
|
+
watch(isTurnActive, (active: boolean, wasActive: boolean) => {
|
|
255
|
+
if (wasActive && !active) {
|
|
256
|
+
steerBuffer.flushNext();
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
|
|
223
260
|
watch(() => viewportWidth.value, (newWidth) => {
|
|
224
261
|
if (isFullScreen.value) {
|
|
225
262
|
setChatWidth(newWidth, false);
|
|
@@ -505,6 +542,14 @@ export const useAgentStore = defineStore('agent', () => {
|
|
|
505
542
|
addDataToolCallMessage,
|
|
506
543
|
openAgentPage,
|
|
507
544
|
setCurrentChatStatus,
|
|
508
|
-
updateLastAgentMessage
|
|
545
|
+
updateLastAgentMessage,
|
|
546
|
+
//_________-Steer queue-_____________
|
|
547
|
+
submitUserMessage,
|
|
548
|
+
isTurnActive,
|
|
549
|
+
steerQueue: steerBuffer.queue,
|
|
550
|
+
steerQueuedMessage: steerBuffer.steerQueuedMessage,
|
|
551
|
+
removeQueuedMessage: steerBuffer.removeQueuedMessage,
|
|
552
|
+
enqueueSteerMessage: steerBuffer.enqueue,
|
|
553
|
+
//___________________________________
|
|
509
554
|
}
|
|
510
555
|
})
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
2
|
+
<div
|
|
3
|
+
v-if="message.role === 'user' && message.metadata?.steer"
|
|
4
|
+
class="self-end mr-4 mb-0.5 text-xs italic text-gray-400 dark:text-gray-500"
|
|
5
|
+
>
|
|
6
|
+
{{ $t('Steering instruction') }}
|
|
7
|
+
</div>
|
|
8
|
+
<ProcessingTimeline
|
|
3
9
|
:message="message"
|
|
4
10
|
:isLastMessageInChat="isLastMessageInChat"
|
|
5
11
|
/>
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
<script setup lang="ts">
|
|
12
12
|
import { computed } from 'vue';
|
|
13
13
|
|
|
14
|
-
const IDLE_LINE_HEIGHTS = [0.
|
|
14
|
+
const IDLE_LINE_HEIGHTS = [0.4, 0.8, 0.55, 0.4];
|
|
15
15
|
const RECORDING_LINE_WEIGHTS = [0.45, 1, 0.75, 0.9, 0.55];
|
|
16
16
|
const MIN_RECORDING_HEIGHT = 0.25;
|
|
17
17
|
const MAX_RECORDING_DELTA = 0.9;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div
|
|
3
3
|
v-show="!agentStore.isResponseInProgress || isAudioChatMode"
|
|
4
|
-
class="absolute bottom-
|
|
4
|
+
class="absolute bottom-1 flex items-center justify-center z-10 gap-4"
|
|
5
5
|
:class="[!agentStore.isAudioChatMode ? 'right-16': agentStore.isMobile ? 'right-1/2 translate-x-1/2' : 'right-1/2 translate-x-1/2']"
|
|
6
6
|
>
|
|
7
7
|
<button
|
|
@@ -14,10 +14,10 @@
|
|
|
14
14
|
</button>
|
|
15
15
|
<button
|
|
16
16
|
v-else
|
|
17
|
-
class="h-
|
|
17
|
+
class="h-7 bg-lightPrimary dark:bg-darkPrimary
|
|
18
18
|
hover:opacity-90 rounded-full flex items-center justify-center
|
|
19
19
|
transition-all duration-300 ease-in-out overflow-hidden"
|
|
20
|
-
:class="[isAudioChatMode ? 'w-32 px-2': 'w-
|
|
20
|
+
:class="[isAudioChatMode ? 'w-32 px-2': 'w-7']"
|
|
21
21
|
@click="toggleChatMode"
|
|
22
22
|
>
|
|
23
23
|
<div class="w-5 h-5 flex items-center justify-center">
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ToolCallEvent } from "./
|
|
1
|
+
import type { ToolCallEvent } from "./toolCallEvents.js";
|
|
2
2
|
export type AgentEvent = {
|
|
3
3
|
type: "turn-started";
|
|
4
4
|
messageId: string;
|
|
@@ -19,6 +19,10 @@ export type AgentEvent = {
|
|
|
19
19
|
type: "interrupt";
|
|
20
20
|
sessionId: string;
|
|
21
21
|
interrupt: unknown;
|
|
22
|
+
} | {
|
|
23
|
+
type: "steer-applied";
|
|
24
|
+
count: number;
|
|
25
|
+
ids: string[];
|
|
22
26
|
} | {
|
|
23
27
|
type: "open-page";
|
|
24
28
|
targetPath: string;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/** A single steering instruction the user sent while a turn was already running. */
|
|
2
|
+
export type SteerMessage = {
|
|
3
|
+
id: string;
|
|
4
|
+
text: string;
|
|
5
|
+
/** Epoch ms the steer was enqueued; useful for ordering/debugging. */
|
|
6
|
+
at: number;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* In-process, per-session buffer of steering instructions.
|
|
10
|
+
*
|
|
11
|
+
* While an agent turn is streaming (thinking / calling tools) the user may send
|
|
12
|
+
* one or more extra instructions ("steers"). Each is enqueued here by the
|
|
13
|
+
* `/agent/steer` endpoint and drained by the `beforeModel` steer middleware just
|
|
14
|
+
* before the next model call in the same running loop, so the model course-corrects
|
|
15
|
+
* mid-turn instead of the user having to abort and restart.
|
|
16
|
+
*
|
|
17
|
+
* The buffer is deliberately in-process: a steer only makes sense for a turn that
|
|
18
|
+
* is *currently running*, and that turn's SSE stream is held open on exactly one
|
|
19
|
+
* instance/process. In a multi-instance deployment the `/agent/steer` request must
|
|
20
|
+
* therefore reach the same instance running the turn (same constraint the in-memory
|
|
21
|
+
* HITL cache / MemorySaver already carry when no persistent checkpointer is set).
|
|
22
|
+
*/
|
|
23
|
+
export declare class SteerBuffer {
|
|
24
|
+
private readonly buffers;
|
|
25
|
+
/** Enqueue a steering instruction for a session. Returns the created entry. */
|
|
26
|
+
add(sessionId: string, text: string): SteerMessage;
|
|
27
|
+
/** Number of steers currently buffered for a session. */
|
|
28
|
+
size(sessionId: string): number;
|
|
29
|
+
/** Return every buffered steer for a session and remove them from the buffer. */
|
|
30
|
+
drain(sessionId: string): SteerMessage[];
|
|
31
|
+
/** Discard any buffered steers for a session (e.g. when its turn ends unconsumed). */
|
|
32
|
+
clear(sessionId: string): void;
|
|
33
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { randomUUID } from "crypto";
|
|
2
|
+
/**
|
|
3
|
+
* In-process, per-session buffer of steering instructions.
|
|
4
|
+
*
|
|
5
|
+
* While an agent turn is streaming (thinking / calling tools) the user may send
|
|
6
|
+
* one or more extra instructions ("steers"). Each is enqueued here by the
|
|
7
|
+
* `/agent/steer` endpoint and drained by the `beforeModel` steer middleware just
|
|
8
|
+
* before the next model call in the same running loop, so the model course-corrects
|
|
9
|
+
* mid-turn instead of the user having to abort and restart.
|
|
10
|
+
*
|
|
11
|
+
* The buffer is deliberately in-process: a steer only makes sense for a turn that
|
|
12
|
+
* is *currently running*, and that turn's SSE stream is held open on exactly one
|
|
13
|
+
* instance/process. In a multi-instance deployment the `/agent/steer` request must
|
|
14
|
+
* therefore reach the same instance running the turn (same constraint the in-memory
|
|
15
|
+
* HITL cache / MemorySaver already carry when no persistent checkpointer is set).
|
|
16
|
+
*/
|
|
17
|
+
export class SteerBuffer {
|
|
18
|
+
constructor() {
|
|
19
|
+
this.buffers = new Map();
|
|
20
|
+
}
|
|
21
|
+
/** Enqueue a steering instruction for a session. Returns the created entry. */
|
|
22
|
+
add(sessionId, text) {
|
|
23
|
+
const message = { id: randomUUID(), text, at: Date.now() };
|
|
24
|
+
const existing = this.buffers.get(sessionId);
|
|
25
|
+
if (existing) {
|
|
26
|
+
existing.push(message);
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
this.buffers.set(sessionId, [message]);
|
|
30
|
+
}
|
|
31
|
+
return message;
|
|
32
|
+
}
|
|
33
|
+
/** Number of steers currently buffered for a session. */
|
|
34
|
+
size(sessionId) {
|
|
35
|
+
var _a, _b;
|
|
36
|
+
return (_b = (_a = this.buffers.get(sessionId)) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0;
|
|
37
|
+
}
|
|
38
|
+
/** Return every buffered steer for a session and remove them from the buffer. */
|
|
39
|
+
drain(sessionId) {
|
|
40
|
+
const messages = this.buffers.get(sessionId);
|
|
41
|
+
if (!messages || messages.length === 0) {
|
|
42
|
+
return [];
|
|
43
|
+
}
|
|
44
|
+
this.buffers.delete(sessionId);
|
|
45
|
+
return messages;
|
|
46
|
+
}
|
|
47
|
+
/** Discard any buffered steers for a session (e.g. when its turn ends unconsumed). */
|
|
48
|
+
clear(sessionId) {
|
|
49
|
+
this.buffers.delete(sessionId);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -7,7 +7,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
7
7
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
|
-
import { listProjectSkillManifests, } from "
|
|
10
|
+
import { listProjectSkillManifests, } from "../tools/skills/registry.js";
|
|
11
11
|
export const DEFAULT_AGENT_SYSTEM_PROMPT = [
|
|
12
12
|
"You are helpful AI Assistant for Admin Panel.",
|
|
13
13
|
// about admin
|
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
import type { AdminUser, AudioAdapter } from "adminforth";
|
|
2
|
-
import type { Messages } from "@langchain/langgraph";
|
|
3
|
-
import type { Command } from "@langchain/langgraph";
|
|
4
|
-
import type { AgentChatModel, AgentMiddleware } from "../simpleAgent.js";
|
|
5
|
-
import type { SequenceDebugCollector } from "../middleware/sequenceDebug.js";
|
|
6
|
-
import type { PreviousUserMessage } from "../languageDetect.js";
|
|
7
2
|
import type { CurrentPageContext } from "../tools/getUserLocation.js";
|
|
8
|
-
import type { AgentEventEmitter } from "
|
|
3
|
+
import type { AgentEventEmitter } from "./agentEvents.js";
|
|
4
|
+
/**
|
|
5
|
+
* Minimal sink the application layer uses to persist per-turn debug traces. The
|
|
6
|
+
* concrete implementation (the sequence-debug collector) lives in the llm layer;
|
|
7
|
+
* the domain/application layers depend only on this narrow interface, so the
|
|
8
|
+
* dependency direction stays llm -> application/domain.
|
|
9
|
+
*/
|
|
10
|
+
export type DebugSink = {
|
|
11
|
+
flush(): void;
|
|
12
|
+
getHistory(): unknown[];
|
|
13
|
+
};
|
|
14
|
+
/** A pending human-in-the-loop approval, normalized (provider-agnostic). */
|
|
15
|
+
export type PendingInterrupt = {
|
|
16
|
+
id: string;
|
|
17
|
+
count: number;
|
|
18
|
+
};
|
|
9
19
|
export type BaseAgentTurnInput = {
|
|
10
20
|
prompt: string;
|
|
11
21
|
sessionId: string;
|
|
@@ -46,37 +56,7 @@ export type AgentTurnContext = {
|
|
|
46
56
|
};
|
|
47
57
|
export type AgentTurnObservability = {
|
|
48
58
|
emit?: AgentEventEmitter;
|
|
49
|
-
sequenceDebugSink:
|
|
50
|
-
};
|
|
51
|
-
export type PreparedAgentTurn = {
|
|
52
|
-
prompt: string;
|
|
53
|
-
sessionId: string;
|
|
54
|
-
turnId: string;
|
|
55
|
-
previousUserMessages: PreviousUserMessage[];
|
|
56
|
-
modeName?: string | null;
|
|
57
|
-
context: AgentTurnContext;
|
|
58
|
-
observability: AgentTurnObservability;
|
|
59
|
-
resume?: {
|
|
60
|
-
decision: "approve" | "reject";
|
|
61
|
-
interrupts?: {
|
|
62
|
-
id: string;
|
|
63
|
-
count: number;
|
|
64
|
-
}[];
|
|
65
|
-
};
|
|
66
|
-
initialResponse?: string;
|
|
67
|
-
};
|
|
68
|
-
export type AgentTurnModels = {
|
|
69
|
-
model: AgentChatModel;
|
|
70
|
-
summaryModel: AgentChatModel;
|
|
71
|
-
modelMiddleware?: AgentMiddleware[];
|
|
72
|
-
};
|
|
73
|
-
export type AgentRuntimeRunInput = {
|
|
74
|
-
models: AgentTurnModels;
|
|
75
|
-
input: {
|
|
76
|
-
messages: Messages;
|
|
77
|
-
} | Command;
|
|
78
|
-
context: AgentTurnContext;
|
|
79
|
-
observability: AgentTurnObservability;
|
|
59
|
+
sequenceDebugSink: DebugSink;
|
|
80
60
|
};
|
|
81
61
|
export type RunAndPersistAgentResponseInput = BaseAgentTurnInput & {
|
|
82
62
|
emit?: AgentEventEmitter;
|
|
@@ -92,3 +72,27 @@ export type RunAndPersistAgentResponseResult = {
|
|
|
92
72
|
};
|
|
93
73
|
export type HandleTurnInput = TextAgentTurnInput;
|
|
94
74
|
export type HandleSpeechTurnInput = SpeechAgentTurnInput;
|
|
75
|
+
/**
|
|
76
|
+
* Provider-agnostic message passed from the application layer to the LLM port.
|
|
77
|
+
* The infrastructure adapter maps it onto concrete LangChain messages.
|
|
78
|
+
*/
|
|
79
|
+
export type AgentMessage = {
|
|
80
|
+
role: "system" | "user";
|
|
81
|
+
content: string;
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* Normalized unit of a streamed agent turn. The LLM port converts the raw
|
|
85
|
+
* LangGraph stream into these typed chunks so the application layer never
|
|
86
|
+
* touches LangGraph's tuple/`any` stream shape.
|
|
87
|
+
*/
|
|
88
|
+
export type AgentStreamChunk = {
|
|
89
|
+
kind: "text";
|
|
90
|
+
delta: string;
|
|
91
|
+
} | {
|
|
92
|
+
kind: "reasoning";
|
|
93
|
+
delta: string;
|
|
94
|
+
} | {
|
|
95
|
+
kind: "interrupt";
|
|
96
|
+
interrupt: unknown;
|
|
97
|
+
descriptors: PendingInterrupt[];
|
|
98
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { AdminForthResource, IAdminForth, IHttpServer } from "adminforth";
|
|
2
2
|
import { AdminForthPlugin } from "adminforth";
|
|
3
3
|
import type { PluginOptions } from './types.js';
|
|
4
|
-
export type { AgentEvent, AgentEventEmitter } from "./agentEvents.js";
|
|
4
|
+
export type { AgentEvent, AgentEventEmitter } from "./domain/agentEvents.js";
|
|
5
5
|
export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
6
6
|
options: PluginOptions;
|
|
7
7
|
agentSystemPromptPromise: Promise<string>;
|
|
@@ -9,11 +9,13 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
9
9
|
private agentSystemPrompt;
|
|
10
10
|
private checkpointer;
|
|
11
11
|
private sessionStore;
|
|
12
|
-
private
|
|
12
|
+
private steerBuffer;
|
|
13
|
+
private runTurnUseCase;
|
|
13
14
|
private speechTurnService;
|
|
14
15
|
private chatSurfaceService;
|
|
15
16
|
private getCheckpointer;
|
|
16
17
|
private getInternalAgentResourceIds;
|
|
18
|
+
private getAgentSystemPrompt;
|
|
17
19
|
constructor(options: PluginOptions);
|
|
18
20
|
modifyGlobalConfig(adminforth: IAdminForth): Promise<void>;
|
|
19
21
|
validateConfigAfterDiscover(adminforth: IAdminForth, resourceConfig: AdminForthResource): void;
|