@@ -3,214 +3,6 @@ const { sendEvent } = require('@librechat/api');
3
3
const { saveMessage, saveConvo } = require ( '~/models' ) ;
4
4
const discoveryService = require ( '../../services/A2ADiscoveryService' ) ;
5
5
6
- /**
7
- * Poll for task completion and stream updates
8
- */
9
- const pollTaskCompletion = async ( client , taskId , agent , contextId , messageId , res , streaming , req ) => {
10
- const maxPolls = 60 ; // Maximum 5 minutes (60 * 5s)
11
- let pollCount = 0 ;
12
-
13
- const poll = async ( ) => {
14
- try {
15
- if ( pollCount >= maxPolls ) {
16
- throw new Error ( 'Task polling timeout' ) ;
17
- }
18
-
19
- const taskStatus = await client . getTaskStatus ( taskId ) ;
20
- pollCount ++ ;
21
-
22
- // Send status update
23
- if ( streaming ) {
24
- sendEvent ( res , {
25
- type : 'task_update' ,
26
- taskId,
27
- status : taskStatus . status ,
28
- statusMessage : taskStatus . statusMessage ,
29
- pollCount,
30
- } ) ;
31
- }
32
-
33
- // Check if task is complete
34
- const terminalStates = [ 'completed' , 'failed' , 'canceled' ] ;
35
- if ( terminalStates . includes ( taskStatus . status ) ) {
36
- return handleTaskCompletion ( taskStatus , agent , contextId , messageId , res , streaming , req ) ;
37
- }
38
-
39
- // Continue polling
40
- setTimeout ( poll , 5000 ) ; // Poll every 5 seconds
41
-
42
- } catch ( error ) {
43
- console . error ( `Task polling error for ${ taskId } :` , error ) ;
44
- throw error ;
45
- }
46
- } ;
47
-
48
- // Start polling
49
- setTimeout ( poll , 2000 ) ; // Initial delay of 2 seconds
50
- } ;
51
-
52
- /**
53
- * Handle task completion
54
- */
55
- const handleTaskCompletion = async ( taskStatus , agent , contextId , messageId , res , streaming , req ) => {
56
- const responseMessageId = uuidv4 ( ) ;
57
-
58
- // Extract response content from task
59
- let responseText = 'Task completed' ;
60
- if ( taskStatus . history && taskStatus . history . length > 0 ) {
61
- const lastMessage = taskStatus . history [ taskStatus . history . length - 1 ] ;
62
- if ( lastMessage . role === 'agent' && lastMessage . parts ?. [ 0 ] ?. content ) {
63
- responseText = lastMessage . parts [ 0 ] . content ;
64
- }
65
- }
66
-
67
- // Stream response content if available
68
- if ( streaming && responseText !== 'Task completed' ) {
69
- await streamContent ( responseText , res , responseMessageId ) ;
70
- }
71
-
72
- // Send final response using LibreChat's expected format
73
- const finalResponse = {
74
- final : true ,
75
- conversation : {
76
- conversationId : contextId ,
77
- title : `A2A Task with ${ agent . name } ` ,
78
- } ,
79
- requestMessage : {
80
- messageId,
81
- conversationId : contextId ,
82
- parentMessageId : null ,
83
- role : 'user' ,
84
- text : message ,
85
- } ,
86
- responseMessage : {
87
- messageId : responseMessageId ,
88
- conversationId : contextId ,
89
- parentMessageId : messageId ,
90
- role : 'assistant' ,
91
- text : responseText ,
92
- model : `a2a-${ agent . id } ` ,
93
- endpoint : 'a2a' ,
94
- metadata : {
95
- agentId : agent . id ,
96
- agentName : agent . name ,
97
- taskId : taskStatus . id ,
98
- taskStatus : taskStatus . status ,
99
- artifacts : taskStatus . artifacts || [ ] ,
100
- } ,
101
- } ,
102
- } ;
103
-
104
- if ( streaming ) {
105
- sendEvent ( res , finalResponse ) ;
106
-
107
- // Save messages to database after successful task completion
108
- try {
109
- // Save user message
110
- await saveMessage ( req , {
111
- messageId,
112
- conversationId : contextId ,
113
- parentMessageId : null ,
114
- role : 'user' ,
115
- text : finalResponse . requestMessage . text ,
116
- user : req . user ?. id ,
117
- endpoint : 'a2a' ,
118
- model : `a2a-${ agent . id } ` ,
119
- isCreatedByUser : true ,
120
- } , { context : 'A2A Task Chat - User Message' } ) ;
121
-
122
- // Save agent response
123
- const agentMessage = await saveMessage ( req , {
124
- messageId : responseMessageId ,
125
- conversationId : contextId ,
126
- parentMessageId : messageId ,
127
- role : 'assistant' ,
128
- text : responseText ,
129
- user : req . user ?. id ,
130
- endpoint : 'a2a' ,
131
- model : `a2a-${ agent . id } ` ,
132
- metadata : {
133
- agentId : agent . id ,
134
- agentName : agent . name ,
135
- taskId : taskStatus . id ,
136
- taskStatus : taskStatus . status ,
137
- artifacts : taskStatus . artifacts || [ ] ,
138
- } ,
139
- } , { context : 'A2A Task Chat - Agent Response' } ) ;
140
-
141
- // Save conversation metadata
142
- await saveConvo ( req , {
143
- conversationId : contextId ,
144
- endpoint : 'a2a' ,
145
- model : `a2a-${ agent . id } ` ,
146
- title : `A2A Task with ${ agent . name } ` ,
147
- } , { context : 'A2A Task Chat - Conversation' } ) ;
148
-
149
- } catch ( saveError ) {
150
- console . error ( 'Error saving A2A task messages:' , saveError ) ;
151
- // Don't fail the request if saving fails
152
- }
153
- }
154
-
155
- return finalResponse ;
156
- } ;
157
-
158
- /**
159
- * Stream content in chunks using LibreChat's expected format
160
- */
161
- const streamContent = async ( content , res , messageId ) => {
162
- const words = content . split ( ' ' ) ;
163
- const chunkSize = 3 ; // Stream 3 words at a time
164
- let accumulatedText = '' ;
165
-
166
- for ( let i = 0 ; i < words . length ; i += chunkSize ) {
167
- const chunk = words . slice ( i , i + chunkSize ) . join ( ' ' ) ;
168
- accumulatedText += ( i === 0 ? chunk : ' ' + chunk ) ;
169
-
170
- // Use LibreChat's expected streaming format
171
- sendEvent ( res , {
172
- message : true ,
173
- initial : i === 0 ,
174
- messageId,
175
- text : accumulatedText ,
176
- } ) ;
177
-
178
- // Add delay to simulate typing
179
- await new Promise ( resolve => setTimeout ( resolve , 100 ) ) ;
180
- }
181
- } ;
182
-
183
- /**
184
- * Get available A2A agents
185
- */
186
- const getA2AAgents = async ( req , res ) => {
187
- try {
188
- const agents = discoveryService . getRegisteredAgents ( ) ;
189
-
190
- // Format agents for client consumption
191
- const formattedAgents = agents . map ( agent => ( {
192
- id : agent . id ,
193
- name : agent . name ,
194
- description : agent . description ,
195
- status : agent . status ,
196
- capabilities : agent . agentCard ?. capabilities || { } ,
197
- skills : agent . agentCard ?. skills || [ ] ,
198
- transport : agent . preferredTransport ,
199
- lastHealthCheck : agent . lastHealthCheck ,
200
- createdAt : agent . createdAt ,
201
- } ) ) ;
202
-
203
- res . json ( { agents : formattedAgents } ) ;
204
-
205
- } catch ( error ) {
206
- console . error ( 'Error fetching A2A agents:' , error ) ;
207
- res . status ( 500 ) . json ( {
208
- error : 'Failed to fetch A2A agents' ,
209
- message : error . message
210
- } ) ;
211
- }
212
- } ;
213
-
214
6
/**
215
7
* Register a new A2A agent
216
8
*/
@@ -270,6 +62,37 @@ const unregisterA2AAgent = async (req, res) => {
270
62
}
271
63
} ;
272
64
65
+ /**
66
+ * Get available A2A agents
67
+ */
68
+ const getA2AAgents = async ( req , res ) => {
69
+ try {
70
+ const agents = discoveryService . getRegisteredAgents ( ) ;
71
+
72
+ // Format agents for client consumption
73
+ const formattedAgents = agents . map ( agent => ( {
74
+ id : agent . id ,
75
+ name : agent . name ,
76
+ description : agent . description ,
77
+ status : agent . status ,
78
+ capabilities : agent . agentCard ?. capabilities || { } ,
79
+ skills : agent . agentCard ?. skills || [ ] ,
80
+ transport : agent . preferredTransport ,
81
+ lastHealthCheck : agent . lastHealthCheck ,
82
+ createdAt : agent . createdAt ,
83
+ } ) ) ;
84
+
85
+ res . json ( { agents : formattedAgents } ) ;
86
+
87
+ } catch ( error ) {
88
+ console . error ( 'Error fetching A2A agents:' , error ) ;
89
+ res . status ( 500 ) . json ( {
90
+ error : 'Failed to fetch A2A agents' ,
91
+ message : error . message
92
+ } ) ;
93
+ }
94
+ } ;
95
+
273
96
/**
274
97
* Get A2A agent details
275
98
*/
0 commit comments