@adminforth/agent 2.0.9 → 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.
Files changed (187) hide show
  1. package/README.md +333 -10
  2. package/dist/application/ports.d.ts +66 -0
  3. package/dist/application/runTurnUseCase.d.ts +68 -0
  4. package/dist/application/runTurnUseCase.js +358 -0
  5. package/dist/{agentEvents.d.ts → domain/agentEvents.d.ts} +1 -1
  6. package/dist/{agent → domain}/systemPrompt.js +1 -1
  7. package/dist/{agent/turn → domain}/turnTypes.d.ts +41 -37
  8. package/dist/{agent/turn/VegaLiteStreamBuffer.d.ts → domain/vegaLiteStreamBuffer.d.ts} +1 -1
  9. package/dist/index.d.ts +3 -2
  10. package/dist/index.js +40 -35
  11. package/dist/{agent/runtime/AgentContext.d.ts → llm/agentContext.d.ts} +3 -3
  12. package/dist/{agent/simpleAgent.d.ts → llm/agentModels.d.ts} +18 -16
  13. package/dist/llm/agentModels.js +65 -0
  14. package/dist/{agent/runtime/AgentRuntime.d.ts → llm/agentRuntime.d.ts} +11 -2
  15. package/dist/{agent/runtime/AgentRuntime.js → llm/agentRuntime.js} +38 -6
  16. package/dist/llm/langGraphLlm.d.ts +25 -0
  17. package/dist/llm/langGraphLlm.js +54 -0
  18. package/dist/{agent/middleware/apiBasedTools.d.ts → llm/middleware/apiToolsMiddleware.d.ts} +1 -1
  19. package/dist/{agent/middleware/apiBasedTools.js → llm/middleware/apiToolsMiddleware.js} +8 -14
  20. package/dist/{agent → llm}/middleware/sequenceDebug.d.ts +1 -1
  21. package/dist/{agent/models/AgentModelFactory.d.ts → llm/modelFactory.d.ts} +1 -1
  22. package/dist/{agent/models/AgentModelFactory.js → llm/modelFactory.js} +1 -1
  23. package/dist/llm/streamAdapter.d.ts +18 -0
  24. package/dist/llm/streamAdapter.js +117 -0
  25. package/dist/{sessionStore.d.ts → persistence/sessionStore.d.ts} +12 -2
  26. package/dist/{sessionStore.js → persistence/sessionStore.js} +33 -0
  27. package/dist/{agent/tools/AgentToolProvider.d.ts → tools/agentToolProvider.d.ts} +1 -1
  28. package/dist/{agent/tools/AgentToolProvider.js → tools/agentToolProvider.js} +1 -1
  29. package/dist/{apiBasedTools.js → tools/apiBasedTools.js} +10 -6
  30. package/dist/{agent/tools → tools}/apiTool.d.ts +1 -1
  31. package/dist/{agent/tools → tools}/fetchSkill.js +1 -1
  32. package/dist/{agent/tools → tools}/fetchToolSchema.d.ts +1 -1
  33. package/dist/{agent/tools → tools}/index.d.ts +1 -1
  34. package/dist/{agent/tools → tools}/navigateUser.d.ts +3 -3
  35. package/dist/{endpoints → transport/http}/context.d.ts +2 -2
  36. package/dist/transport/http/context.js +1 -0
  37. package/dist/{endpoints/core.js → transport/http/coreEndpoints.js} +3 -3
  38. package/dist/{endpoints/sessions.js → transport/http/sessionEndpoints.js} +7 -1
  39. package/dist/transport/sse/sseWriter.d.ts +29 -0
  40. package/dist/{surfaces/web-sse/createSseEventEmitter.js → transport/sse/sseWriter.js} +15 -0
  41. package/dist/{chatSurfaceService.d.ts → transport/surfaces/chatSurfaceService.d.ts} +3 -3
  42. package/dist/{chatSurfaceService.js → transport/surfaces/chatSurfaceService.js} +7 -5
  43. package/dist/{agent/speech/SpeechTurnService.d.ts → transport/surfaces/speechTurnService.d.ts} +1 -1
  44. package/dist/{agent/speech/SpeechTurnService.js → transport/surfaces/speechTurnService.js} +2 -2
  45. package/dist/types.d.ts +10 -4
  46. package/package.json +11 -2
  47. package/.woodpecker/buildRelease.sh +0 -13
  48. package/.woodpecker/buildSlackNotify.sh +0 -46
  49. package/.woodpecker/release.yml +0 -57
  50. package/agent/checkpointer.ts +0 -329
  51. package/agent/languageDetect.ts +0 -92
  52. package/agent/middleware/apiBasedTools.ts +0 -174
  53. package/agent/middleware/openAiResponsesContinuation.ts +0 -92
  54. package/agent/middleware/sequenceDebug.ts +0 -429
  55. package/agent/models/AgentModeResolver.ts +0 -9
  56. package/agent/models/AgentModelFactory.ts +0 -28
  57. package/agent/runtime/AgentContext.ts +0 -30
  58. package/agent/runtime/AgentRuntime.ts +0 -89
  59. package/agent/simpleAgent.ts +0 -199
  60. package/agent/skills/registry.ts +0 -162
  61. package/agent/speech/SpeechTurnService.ts +0 -179
  62. package/agent/systemPrompt.ts +0 -133
  63. package/agent/toolCallEvents.ts +0 -135
  64. package/agent/tools/AgentToolProvider.ts +0 -28
  65. package/agent/tools/apiTool.ts +0 -66
  66. package/agent/tools/fetchSkill.ts +0 -68
  67. package/agent/tools/fetchToolSchema.ts +0 -47
  68. package/agent/tools/getUserLocation.ts +0 -45
  69. package/agent/tools/index.ts +0 -31
  70. package/agent/tools/navigateUser.ts +0 -210
  71. package/agent/turn/TurnContextBuilder.ts +0 -36
  72. package/agent/turn/TurnLifecycleService.ts +0 -47
  73. package/agent/turn/TurnPersistenceService.ts +0 -33
  74. package/agent/turn/TurnPromptBuilder.ts +0 -51
  75. package/agent/turn/TurnStreamConsumer.ts +0 -69
  76. package/agent/turn/VegaLiteStreamBuffer.ts +0 -90
  77. package/agent/turn/turnTypes.ts +0 -100
  78. package/agentEvents.ts +0 -75
  79. package/agentTurnService.ts +0 -314
  80. package/apiBasedTools.ts +0 -702
  81. package/build.log +0 -67
  82. package/chatSurfaceService.ts +0 -319
  83. package/custom/ChatFooter.vue +0 -173
  84. package/custom/ChatHeader.vue +0 -78
  85. package/custom/ChatSurface.vue +0 -160
  86. package/custom/CustomAutoScrollContainer.vue +0 -164
  87. package/custom/SessionsHistory.vue +0 -88
  88. package/custom/chat.ts +0 -82
  89. package/custom/composables/agentAudio/utils.ts +0 -220
  90. package/custom/composables/agentStore/constants.ts +0 -19
  91. package/custom/composables/agentStore/pageContext.ts +0 -8
  92. package/custom/composables/agentStore/useAgentChat.ts +0 -241
  93. package/custom/composables/agentStore/useAgentPlaceholder.ts +0 -142
  94. package/custom/composables/agentStore/useAgentSessions.ts +0 -350
  95. package/custom/composables/useAgentAudio.ts +0 -272
  96. package/custom/composables/useAgentStore.ts +0 -510
  97. package/custom/composables/useAgentTransitions.ts +0 -58
  98. package/custom/conversation_area/ConversationArea.vue +0 -301
  99. package/custom/conversation_area/MessageRenderer.vue +0 -49
  100. package/custom/conversation_area/ProcessingTimeline.vue +0 -286
  101. package/custom/conversation_area/ReasoningRenderer.vue +0 -79
  102. package/custom/conversation_area/SystemMessageRenderer.vue +0 -28
  103. package/custom/conversation_area/TextRenderer.vue +0 -182
  104. package/custom/conversation_area/ThreeDotsAnimation.vue +0 -35
  105. package/custom/conversation_area/ToolApprovalRenderer.vue +0 -98
  106. package/custom/conversation_area/ToolRenderer.vue +0 -178
  107. package/custom/conversation_area/ToolsGroup.vue +0 -59
  108. package/custom/env.d.ts +0 -7
  109. package/custom/incremark_code_renderers/IncremarkShikiCodeBlock.vue +0 -393
  110. package/custom/incremark_code_renderers/incremarkCodeHighlight.ts +0 -285
  111. package/custom/incremark_code_renderers/incremarkRenderer.ts +0 -653
  112. package/custom/incremark_code_renderers/renderIncremarkMarkdown.ts +0 -118
  113. package/custom/package.json +0 -30
  114. package/custom/pnpm-lock.yaml +0 -2394
  115. package/custom/public/agentAudio/agent-processing.mp3 +0 -0
  116. package/custom/skills/analyze_data/SKILL.md +0 -25
  117. package/custom/skills/charts/SKILL.md +0 -193
  118. package/custom/skills/fetch_data/SKILL.md +0 -31
  119. package/custom/skills/mutate_data/SKILL.md +0 -121
  120. package/custom/speech_recognition_frontend/AudioLines.vue +0 -43
  121. package/custom/speech_recognition_frontend/MicrophoneButon.vue +0 -185
  122. package/custom/speech_recognition_frontend/types/voice-activity-detection.d.ts +0 -22
  123. package/custom/speech_recognition_frontend/voiceActivityDetection.ts +0 -62
  124. package/custom/tsconfig.json +0 -15
  125. package/custom/types.ts +0 -108
  126. package/custom/utils.ts +0 -38
  127. package/dist/agent/middleware/openAiResponsesContinuation.d.ts +0 -1
  128. package/dist/agent/middleware/openAiResponsesContinuation.js +0 -66
  129. package/dist/agent/models/AgentModeResolver.d.ts +0 -9
  130. package/dist/agent/models/AgentModeResolver.js +0 -9
  131. package/dist/agent/simpleAgent.js +0 -118
  132. package/dist/agent/turn/TurnContextBuilder.d.ts +0 -14
  133. package/dist/agent/turn/TurnContextBuilder.js +0 -31
  134. package/dist/agent/turn/TurnLifecycleService.d.ts +0 -24
  135. package/dist/agent/turn/TurnLifecycleService.js +0 -47
  136. package/dist/agent/turn/TurnPersistenceService.d.ts +0 -13
  137. package/dist/agent/turn/TurnPersistenceService.js +0 -35
  138. package/dist/agent/turn/TurnPromptBuilder.d.ts +0 -19
  139. package/dist/agent/turn/TurnPromptBuilder.js +0 -43
  140. package/dist/agent/turn/TurnStreamConsumer.d.ts +0 -11
  141. package/dist/agent/turn/TurnStreamConsumer.js +0 -84
  142. package/dist/agentTurnService.d.ts +0 -24
  143. package/dist/agentTurnService.js +0 -269
  144. package/dist/surfaces/web-sse/createSseEventEmitter.d.ts +0 -14
  145. package/endpoints/chatSurfaces.ts +0 -66
  146. package/endpoints/context.ts +0 -52
  147. package/endpoints/core.ts +0 -181
  148. package/endpoints/sessions.ts +0 -183
  149. package/errors.ts +0 -10
  150. package/index.ts +0 -192
  151. package/pnpm-workspace.yaml +0 -4
  152. package/sanitizeSpeechText.ts +0 -32
  153. package/sessionStore.ts +0 -105
  154. package/surfaces/web-sse/createSseEventEmitter.ts +0 -311
  155. package/tsconfig.json +0 -14
  156. package/types.ts +0 -127
  157. /package/dist/{agent/turn/turnTypes.js → application/ports.js} +0 -0
  158. /package/dist/{agentEvents.js → domain/agentEvents.js} +0 -0
  159. /package/dist/{agent → domain}/languageDetect.d.ts +0 -0
  160. /package/dist/{agent → domain}/languageDetect.js +0 -0
  161. /package/dist/{agent → domain}/systemPrompt.d.ts +0 -0
  162. /package/dist/{agent → domain}/toolCallEvents.d.ts +0 -0
  163. /package/dist/{agent → domain}/toolCallEvents.js +0 -0
  164. /package/dist/{endpoints/context.js → domain/turnTypes.js} +0 -0
  165. /package/dist/{agent/turn/VegaLiteStreamBuffer.js → domain/vegaLiteStreamBuffer.js} +0 -0
  166. /package/dist/{agent/runtime/AgentContext.js → llm/agentContext.js} +0 -0
  167. /package/dist/{agent → llm}/middleware/sequenceDebug.js +0 -0
  168. /package/dist/{agent/checkpointer.d.ts → persistence/checkpointStore.d.ts} +0 -0
  169. /package/dist/{agent/checkpointer.js → persistence/checkpointStore.js} +0 -0
  170. /package/dist/{errors.d.ts → shared/errors.d.ts} +0 -0
  171. /package/dist/{errors.js → shared/errors.js} +0 -0
  172. /package/dist/{sanitizeSpeechText.d.ts → shared/sanitizeSpeechText.d.ts} +0 -0
  173. /package/dist/{sanitizeSpeechText.js → shared/sanitizeSpeechText.js} +0 -0
  174. /package/dist/{apiBasedTools.d.ts → tools/apiBasedTools.d.ts} +0 -0
  175. /package/dist/{agent/tools → tools}/apiTool.js +0 -0
  176. /package/dist/{agent/tools → tools}/fetchSkill.d.ts +0 -0
  177. /package/dist/{agent/tools → tools}/fetchToolSchema.js +0 -0
  178. /package/dist/{agent/tools → tools}/getUserLocation.d.ts +0 -0
  179. /package/dist/{agent/tools → tools}/getUserLocation.js +0 -0
  180. /package/dist/{agent/tools → tools}/index.js +0 -0
  181. /package/dist/{agent/tools → tools}/navigateUser.js +0 -0
  182. /package/dist/{agent → tools}/skills/registry.d.ts +0 -0
  183. /package/dist/{agent → tools}/skills/registry.js +0 -0
  184. /package/dist/{endpoints/chatSurfaces.d.ts → transport/http/chatSurfaceEndpoints.d.ts} +0 -0
  185. /package/dist/{endpoints/chatSurfaces.js → transport/http/chatSurfaceEndpoints.js} +0 -0
  186. /package/dist/{endpoints/core.d.ts → transport/http/coreEndpoints.d.ts} +0 -0
  187. /package/dist/{endpoints/sessions.d.ts → transport/http/sessionEndpoints.d.ts} +0 -0
@@ -1,220 +0,0 @@
1
-
2
- const ctx = new AudioContext();
3
- let standbySource: AudioBufferSourceNode | null = null;
4
- let isAudioUnlocked = false;
5
-
6
- const DEFAULT_PCM_SAMPLE_RATE = 24000;
7
- const DEFAULT_PCM_CHANNEL_COUNT = 1;
8
- const DEFAULT_PCM_BITS_PER_SAMPLE = 16;
9
-
10
- export type ChatResponseAudioPlayback = {
11
- nextStartTime: number;
12
- activeSources: Set<AudioBufferSourceNode>;
13
- pendingSourceCount: number;
14
- pendingPcmBytes: Uint8Array;
15
- isDone: boolean;
16
- isStopped: boolean;
17
- sampleRate: number;
18
- channelCount: number;
19
- bitsPerSample: number;
20
- onEnded?: () => void;
21
- };
22
-
23
- export async function unlockAudio() {
24
- if (isAudioUnlocked) {
25
- return;
26
- }
27
- console.log('Unlocking audio context by playing silent audio');
28
- await ctx.resume();
29
-
30
- const buffer = ctx.createBuffer(1, 1, 22050);
31
- const source = ctx.createBufferSource();
32
-
33
- source.buffer = buffer;
34
- source.connect(ctx.destination);
35
- source.start(0);
36
- isAudioUnlocked = true;
37
- console.log('Audio context unlocked');
38
- }
39
-
40
- export async function startStandByAudio() {
41
- const standByAudio = new URL(
42
- '../../public/agentAudio/agent-processing.mp3',
43
- import.meta.url
44
- ).href;
45
- const response = await fetch(standByAudio);
46
- console.log('status', response.status);
47
- console.log('content-type', response.headers.get('content-type'));
48
- console.log('response', response);
49
-
50
- const arrayBuffer = await response.arrayBuffer();
51
- const audioBuffer = await ctx.decodeAudioData(arrayBuffer);
52
-
53
- const source = ctx.createBufferSource();
54
- standbySource = source;
55
-
56
- source.buffer = audioBuffer;
57
- source.connect(ctx.destination);
58
- console.log('Playing standby audio');
59
- source.start();
60
- }
61
-
62
- export function createChatResponseAudioPlayback(options: {
63
- sampleRate?: number;
64
- channelCount?: number;
65
- bitsPerSample?: number;
66
- onEnded?: () => void;
67
- } = {}): ChatResponseAudioPlayback {
68
- return {
69
- nextStartTime: ctx.currentTime,
70
- activeSources: new Set(),
71
- pendingSourceCount: 0,
72
- pendingPcmBytes: new Uint8Array(0),
73
- isDone: false,
74
- isStopped: false,
75
- sampleRate: options.sampleRate ?? DEFAULT_PCM_SAMPLE_RATE,
76
- channelCount: options.channelCount ?? DEFAULT_PCM_CHANNEL_COUNT,
77
- bitsPerSample: options.bitsPerSample ?? DEFAULT_PCM_BITS_PER_SAMPLE,
78
- onEnded: options.onEnded,
79
- };
80
- }
81
-
82
- export function playChatResponseCurrentChunks({
83
- playback,
84
- chunks,
85
- }: {
86
- playback: ChatResponseAudioPlayback;
87
- chunks: ArrayBuffer[];
88
- }) {
89
- console.log('Playing chat response audio chunks:', chunks.length);
90
- void ctx.resume().catch(() => undefined);
91
-
92
- for (const chunk of chunks) {
93
- if (playback.isStopped || !chunk.byteLength) {
94
- continue;
95
- }
96
-
97
- const pcmBytes = concatUint8Arrays(playback.pendingPcmBytes, new Uint8Array(chunk));
98
- const bytesPerFrame = playback.channelCount * (playback.bitsPerSample / 8);
99
- const alignedByteLength = pcmBytes.byteLength - (pcmBytes.byteLength % bytesPerFrame);
100
-
101
- playback.pendingPcmBytes = pcmBytes.slice(alignedByteLength);
102
-
103
- if (!alignedByteLength) {
104
- continue;
105
- }
106
-
107
- const audioBuffer = createAudioBufferFromPcmChunk(
108
- playback,
109
- pcmBytes.subarray(0, alignedByteLength)
110
- );
111
- const source = ctx.createBufferSource();
112
- const startTime = Math.max(playback.nextStartTime, ctx.currentTime);
113
-
114
- source.buffer = audioBuffer;
115
- source.connect(ctx.destination);
116
-
117
- playback.activeSources.add(source);
118
- playback.pendingSourceCount += 1;
119
- playback.nextStartTime = startTime + audioBuffer.duration;
120
-
121
- source.addEventListener(
122
- 'ended',
123
- () => {
124
- playback.activeSources.delete(source);
125
- playback.pendingSourceCount = Math.max(0, playback.pendingSourceCount - 1);
126
-
127
- if (!playback.isStopped && playback.isDone && playback.pendingSourceCount === 0) {
128
- playback.onEnded?.();
129
- }
130
- },
131
- { once: true }
132
- );
133
-
134
- source.start(startTime);
135
- }
136
- }
137
-
138
- export function finishChatResponseAudio(playback: ChatResponseAudioPlayback | null) {
139
- if (!playback || playback.isStopped) {
140
- return;
141
- }
142
- console.log('Finishing chat response audio playback');
143
- playback.isDone = true;
144
-
145
- if (playback.pendingSourceCount === 0) {
146
- playback.onEnded?.();
147
- }
148
- }
149
-
150
- export function stopChatResponseAudio(playback: ChatResponseAudioPlayback | null) {
151
- if (!playback || playback.isStopped) {
152
- return;
153
- }
154
- console.log('Stopping chat response audio playback');
155
- playback.isStopped = true;
156
-
157
- for (const source of playback.activeSources) {
158
- source.stop();
159
- }
160
-
161
- playback.activeSources.clear();
162
- playback.pendingSourceCount = 0;
163
- playback.pendingPcmBytes = new Uint8Array(0);
164
- playback.nextStartTime = ctx.currentTime;
165
- }
166
-
167
- function createAudioBufferFromPcmChunk(
168
- playback: ChatResponseAudioPlayback,
169
- chunk: Uint8Array
170
- ) {
171
- if (playback.bitsPerSample !== DEFAULT_PCM_BITS_PER_SAMPLE) {
172
- throw new Error(`Unsupported PCM bit depth: ${playback.bitsPerSample}`);
173
- }
174
-
175
- const bytesPerSample = playback.bitsPerSample / 8;
176
- const frameCount = chunk.byteLength / playback.channelCount / bytesPerSample;
177
- const audioBuffer = ctx.createBuffer(
178
- playback.channelCount,
179
- frameCount,
180
- playback.sampleRate
181
- );
182
- const pcm = new DataView(chunk.buffer, chunk.byteOffset, chunk.byteLength);
183
- const channelData = Array.from(
184
- { length: playback.channelCount },
185
- (_, channelIndex) => audioBuffer.getChannelData(channelIndex)
186
- );
187
-
188
- for (let frameIndex = 0; frameIndex < frameCount; frameIndex += 1) {
189
- for (let channelIndex = 0; channelIndex < playback.channelCount; channelIndex += 1) {
190
- const sampleOffset =
191
- (frameIndex * playback.channelCount + channelIndex) * bytesPerSample;
192
- const sample = pcm.getInt16(sampleOffset, true) / 32768;
193
-
194
- channelData[channelIndex][frameIndex] = sample;
195
- }
196
- }
197
-
198
- return audioBuffer;
199
- }
200
-
201
- function concatUint8Arrays(left: Uint8Array, right: Uint8Array) {
202
- if (!left.byteLength) {
203
- return right;
204
- }
205
-
206
- const combined = new Uint8Array(left.byteLength + right.byteLength);
207
-
208
- combined.set(left, 0);
209
- combined.set(right, left.byteLength);
210
-
211
- return combined;
212
- }
213
-
214
- export function endStandByAudio() {
215
- if (standbySource) {
216
- console.log('Ending standby audio');
217
- standbySource.stop();
218
- standbySource = null;
219
- }
220
- }
@@ -1,19 +0,0 @@
1
- export type AgentMode = {
2
- name: string;
3
- };
4
-
5
- export const DEFAULT_CHAT_WIDTH = 30;
6
- export const MAX_WIDTH = 60;
7
- export const MIN_WIDTH = 25;
8
-
9
- export const DEFAULT_TEXTAREA_PLACEHOLDER = 'Type a message...';
10
- export const PLACEHOLDER_TYPING_DELAY_MS = 60;
11
- export const PLACEHOLDER_DELETING_DELAY_MS = 35;
12
- export const PLACEHOLDER_HOLD_DELAY_MS = 3000;
13
- export const PRE_SESSION_ID = 'pre-session';
14
-
15
- export enum RESERVED_SYSTEM_MESSAGE_CONTENT {
16
- START_AUDIO_CHAT = 'START_AUDIO_CHAT',
17
- END_AUDIO_CHAT = 'END_AUDIO_CHAT',
18
- AGENT_RESPONSE_ABORTED = 'AGENT_RESPONSE_ABORTED'
19
- }
@@ -1,8 +0,0 @@
1
- export function getCurrentPageContext() {
2
- return {
3
- path: window.location.pathname,
4
- fullPath: `${window.location.pathname}${window.location.search}${window.location.hash}`,
5
- title: document.title,
6
- url: window.location.href,
7
- };
8
- }
@@ -1,241 +0,0 @@
1
- import { DefaultChatTransport } from 'ai';
2
- import { shallowRef, type Ref } from 'vue';
3
- import { Chat } from '../../chat';
4
- import { getCurrentPageContext } from './pageContext';
5
- // const { DefaultChatTransport } = await import('ai');
6
-
7
- type AgentImportMeta = ImportMeta & {
8
- env: {
9
- VITE_ADMINFORTH_PUBLIC_PATH?: string;
10
- };
11
- };
12
-
13
- type CreateAgentChatManagerOptions = {
14
- lastMessage: Ref<string>;
15
- activeModeName: Ref<string | null>;
16
- onOpenPage: (targetPath: string) => void;
17
- onToolApprovalRequest: (sessionId: string, interrupt: unknown) => void;
18
- };
19
-
20
- export function createAgentChatManager({
21
- lastMessage,
22
- activeModeName,
23
- onOpenPage,
24
- onToolApprovalRequest,
25
- }: CreateAgentChatManagerOptions) {
26
- const chats = new Map<string, Chat<any>>();
27
- const currentChat = shallowRef<Chat<any> | null>();
28
- const agentApiBase = `${(import.meta as AgentImportMeta).env.VITE_ADMINFORTH_PUBLIC_PATH || ''}/adminapi/v1/agent`;
29
-
30
- function replaceLastMessage(message: any) {
31
- const chat = currentChat.value;
32
-
33
- if (!chat) {
34
- return;
35
- }
36
-
37
- chat.messages.splice(chat.messages.length - 1, 1, message);
38
- }
39
-
40
- function getOrCreateAssistantMessage() {
41
- const chat = currentChat.value;
42
- const lastChatMessage = chat?.lastMessage;
43
-
44
- if (lastChatMessage?.role === 'assistant') {
45
- return lastChatMessage;
46
- }
47
-
48
- const assistantMessage = {
49
- role: 'assistant',
50
- parts: [],
51
- };
52
-
53
- chat?.messages.push(assistantMessage);
54
- return assistantMessage;
55
- }
56
-
57
- function appendTextDelta(delta: string) {
58
- const assistantMessage = getOrCreateAssistantMessage();
59
- const lastPart = assistantMessage.parts.at(-1);
60
-
61
- if (lastPart?.type === 'text') {
62
- lastPart.text = `${lastPart.text ?? ''}${delta}`;
63
- lastPart.state = 'streaming';
64
- } else {
65
- assistantMessage.parts.push({
66
- type: 'text',
67
- text: delta,
68
- state: 'streaming',
69
- });
70
- }
71
-
72
- replaceLastMessage(assistantMessage);
73
- }
74
-
75
- function finishTextPart() {
76
- const assistantMessage = currentChat.value?.lastMessage;
77
- const lastPart = assistantMessage?.parts.at(-1);
78
-
79
- if (assistantMessage?.role === 'assistant' && lastPart?.type === 'text') {
80
- lastPart.state = 'done';
81
- replaceLastMessage(assistantMessage);
82
- }
83
- }
84
-
85
- function appendDataPart(type: string, data: unknown) {
86
- const assistantMessage = getOrCreateAssistantMessage();
87
-
88
- assistantMessage.parts.push({ type, data });
89
- replaceLastMessage(assistantMessage);
90
- }
91
-
92
- function handleRealtimeChatData(dataPart: any) {
93
- if (dataPart?.type === 'data-open-page' && typeof dataPart.data?.targetPath === 'string') {
94
- onOpenPage(dataPart.data.targetPath);
95
- return;
96
- }
97
-
98
- if (dataPart?.type === 'data-interrupt' && typeof dataPart.data?.sessionId === 'string') {
99
- onToolApprovalRequest(dataPart.data.sessionId, dataPart.data.interrupt);
100
- }
101
- }
102
-
103
- function handleManualApprovalStreamPart(dataPart: any) {
104
- if (dataPart?.type === 'text-delta' && typeof dataPart.delta === 'string') {
105
- appendTextDelta(dataPart.delta);
106
- return;
107
- }
108
-
109
- if (dataPart?.type === 'text-end') {
110
- finishTextPart();
111
- return;
112
- }
113
-
114
- if (dataPart?.type === 'data-tool-call') {
115
- appendDataPart('data-tool-call', dataPart.data);
116
- return;
117
- }
118
-
119
- if (dataPart?.type === 'data-rendering') {
120
- appendDataPart('data-rendering', dataPart.data);
121
- return;
122
- }
123
-
124
- if (dataPart?.type === 'data-open-page' && typeof dataPart.data?.targetPath === 'string') {
125
- onOpenPage(dataPart.data.targetPath);
126
- return;
127
- }
128
-
129
- if (dataPart?.type === 'data-interrupt' && typeof dataPart.data?.sessionId === 'string') {
130
- onToolApprovalRequest(dataPart.data.sessionId, dataPart.data.interrupt);
131
- }
132
- }
133
-
134
- async function consumeAgentStream(response: Response) {
135
- const reader = response.body?.getReader();
136
-
137
- if (!reader) {
138
- return;
139
- }
140
-
141
- const decoder = new TextDecoder();
142
- let buffer = '';
143
-
144
- while (true) {
145
- const { value, done } = await reader.read();
146
-
147
- if (done) {
148
- break;
149
- }
150
-
151
- buffer += decoder.decode(value, { stream: true });
152
- const events = buffer.split('\n\n');
153
- buffer = events.pop() ?? '';
154
-
155
- for (const event of events) {
156
- const data = event
157
- .split('\n')
158
- .filter(line => line.startsWith('data:'))
159
- .map(line => line.slice(5).trim())
160
- .join('\n');
161
-
162
- if (!data || data === '[DONE]') {
163
- continue;
164
- }
165
-
166
- handleManualApprovalStreamPart(JSON.parse(data));
167
- }
168
- }
169
- }
170
-
171
- function setCurrentChat(sessionId: string) {
172
- if (chats.has(sessionId)) {
173
- currentChat.value = chats.get(sessionId) || null;
174
- } else {
175
- const newChat = new Chat({
176
- transport: new DefaultChatTransport({
177
- api: `${agentApiBase}/response`,
178
- credentials: 'include',
179
- prepareSendMessagesRequest({ messages }: any) {
180
- const message = lastMessage.value;
181
- const body = {
182
- message,
183
- sessionId,
184
- timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,
185
- mode: activeModeName.value,
186
- currentPage: getCurrentPageContext(),
187
- };
188
-
189
- return {
190
- headers: {
191
- Accept: 'text/event-stream',
192
- 'x-vercel-ai-ui-message-stream': 'v1',
193
- },
194
- body
195
- };
196
- }
197
- }),
198
- onError(error: unknown) {
199
- console.error('Chat error:', error);
200
- appendTextDelta(`Error: ${error instanceof Error ? error.message : String(error)}`);
201
- },
202
- onData: handleRealtimeChatData,
203
- });
204
- chats.set(sessionId, newChat);
205
- currentChat.value = newChat;
206
- }
207
- }
208
-
209
- function abortCurrentChatRequest() {
210
- currentChat.value?.stop();
211
- }
212
-
213
- async function submitToolApproval(sessionId: string, decision: 'approve' | 'reject') {
214
- const response = await fetch(`${agentApiBase}/approval`, {
215
- method: 'POST',
216
- credentials: 'include',
217
- headers: {
218
- Accept: 'text/event-stream',
219
- 'Content-Type': 'application/json',
220
- 'x-vercel-ai-ui-message-stream': 'v1',
221
- },
222
- body: JSON.stringify({
223
- sessionId,
224
- decision,
225
- }),
226
- });
227
-
228
- if (!response.ok) {
229
- throw new Error(`Agent approval failed with status ${response.status}`);
230
- }
231
-
232
- await consumeAgentStream(response);
233
- }
234
-
235
- return {
236
- currentChat,
237
- setCurrentChat,
238
- abortCurrentChatRequest,
239
- submitToolApproval,
240
- };
241
- }
@@ -1,142 +0,0 @@
1
- import { ref, watch, type Ref } from 'vue';
2
- import { callAdminForthApi } from '@/utils';
3
- import {
4
- DEFAULT_TEXTAREA_PLACEHOLDER,
5
- PLACEHOLDER_DELETING_DELAY_MS,
6
- PLACEHOLDER_HOLD_DELAY_MS,
7
- PLACEHOLDER_TYPING_DELAY_MS,
8
- } from './constants';
9
-
10
- type CreateAgentPlaceholderControllerOptions = {
11
- userMessageInput: Ref<unknown>;
12
- };
13
-
14
- export function createAgentPlaceholderController({
15
- userMessageInput,
16
- }: CreateAgentPlaceholderControllerOptions) {
17
- const userMessagePlaceholder = ref(DEFAULT_TEXTAREA_PLACEHOLDER);
18
- const placeholderMessages = ref<string[]>([]);
19
- const hasTypedMessageInPageSession = ref(false);
20
-
21
- let placeholderAnimationTimer: ReturnType<typeof setTimeout> | null = null;
22
-
23
- function clearPlaceholderAnimationTimer() {
24
- if (placeholderAnimationTimer !== null) {
25
- clearTimeout(placeholderAnimationTimer);
26
- placeholderAnimationTimer = null;
27
- }
28
- }
29
-
30
- function resetPlaceholder() {
31
- clearPlaceholderAnimationTimer();
32
- userMessagePlaceholder.value = DEFAULT_TEXTAREA_PLACEHOLDER;
33
- }
34
-
35
- function stopPlaceholderAnimation() {
36
- resetPlaceholder();
37
- }
38
-
39
- function startPlaceholderAnimation(messages: string[]) {
40
- clearPlaceholderAnimationTimer();
41
-
42
- if (!messages.length) {
43
- userMessagePlaceholder.value = DEFAULT_TEXTAREA_PLACEHOLDER;
44
- return;
45
- }
46
-
47
- let messageIndex = 0;
48
- let visibleLength = 0;
49
- let isDeleting = false;
50
-
51
- const animate = () => {
52
- const currentMessage = messages[messageIndex];
53
-
54
- if (!currentMessage) {
55
- resetPlaceholder();
56
- return;
57
- }
58
-
59
- if (!isDeleting) {
60
- visibleLength += 1;
61
- userMessagePlaceholder.value = currentMessage.slice(0, visibleLength);
62
-
63
- if (visibleLength >= currentMessage.length) {
64
- isDeleting = true;
65
- placeholderAnimationTimer = setTimeout(animate, PLACEHOLDER_HOLD_DELAY_MS);
66
- return;
67
- }
68
-
69
- placeholderAnimationTimer = setTimeout(animate, PLACEHOLDER_TYPING_DELAY_MS);
70
- return;
71
- }
72
-
73
- visibleLength -= 1;
74
- userMessagePlaceholder.value = currentMessage.slice(0, Math.max(visibleLength, 0));
75
-
76
- if (visibleLength <= 0) {
77
- isDeleting = false;
78
- messageIndex = (messageIndex + 1) % messages.length;
79
- placeholderAnimationTimer = setTimeout(animate, PLACEHOLDER_TYPING_DELAY_MS);
80
- return;
81
- }
82
-
83
- placeholderAnimationTimer = setTimeout(animate, PLACEHOLDER_DELETING_DELAY_MS);
84
- };
85
-
86
- animate();
87
- }
88
-
89
- async function fetchPlaceholderMessages() {
90
- if (hasTypedMessageInPageSession.value) {
91
- stopPlaceholderAnimation();
92
- return;
93
- }
94
-
95
- try {
96
- const res = await callAdminForthApi({
97
- method: 'POST',
98
- path: '/agent/get-placeholder-messages',
99
- });
100
-
101
- if (res.error) {
102
- console.error('Error fetching placeholder messages:', res.error);
103
- placeholderMessages.value = [];
104
- resetPlaceholder();
105
- return;
106
- }
107
-
108
- placeholderMessages.value = Array.isArray(res.messages)
109
- ? res.messages.filter((message: unknown): message is string => typeof message === 'string' && message.length > 0)
110
- : [];
111
-
112
- if (!placeholderMessages.value.length) {
113
- resetPlaceholder();
114
- return;
115
- }
116
-
117
- startPlaceholderAnimation(placeholderMessages.value);
118
- } catch (error) {
119
- console.error('Error fetching placeholder messages', error);
120
- placeholderMessages.value = [];
121
- resetPlaceholder();
122
- }
123
- }
124
-
125
- watch(userMessageInput, (newVal: unknown) => {
126
- if (hasTypedMessageInPageSession.value) {
127
- return;
128
- }
129
-
130
- if (typeof newVal === 'string' && newVal.trim() !== '') {
131
- hasTypedMessageInPageSession.value = true;
132
- stopPlaceholderAnimation();
133
- }
134
- });
135
-
136
- return {
137
- userMessagePlaceholder,
138
- hasTypedMessageInPageSession,
139
- fetchPlaceholderMessages,
140
- stopPlaceholderAnimation,
141
- };
142
- }