psadk 1.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/CHANGELOG.md +11 -0
- package/LICENSE +21 -0
- package/README.md +205 -0
- package/build/main/index.d.ts +6 -0
- package/build/main/index.js +23 -0
- package/build/main/lib/ILogger.d.ts +9 -0
- package/build/main/lib/ILogger.js +25 -0
- package/build/main/lib/PSAI.d.ts +29 -0
- package/build/main/lib/PSAI.js +383 -0
- package/build/main/lib/PSAgent.d.ts +215 -0
- package/build/main/lib/PSAgent.js +990 -0
- package/build/main/lib/PSAgentConfig.d.ts +23 -0
- package/build/main/lib/PSAgentConfig.js +3 -0
- package/build/main/lib/PSAgentLogger.d.ts +13 -0
- package/build/main/lib/PSAgentLogger.js +33 -0
- package/build/main/lib/PSAgentTask.d.ts +12 -0
- package/build/main/lib/PSAgentTask.js +30 -0
- package/build/main/lib/PSAgentTool.d.ts +56 -0
- package/build/main/lib/PSAgentTool.js +84 -0
- package/build/main/lib/ai.d.ts +11 -0
- package/build/main/lib/ai.js +127 -0
- package/build/main/lib/async.d.ts +20 -0
- package/build/main/lib/async.js +36 -0
- package/build/main/lib/getServiceToken.d.ts +1 -0
- package/build/main/lib/getServiceToken.js +3 -0
- package/build/main/lib/jwt.d.ts +41 -0
- package/build/main/lib/jwt.js +73 -0
- package/build/main/lib/number.d.ts +43 -0
- package/build/main/lib/number.js +56 -0
- package/build/main/lib/tools/PSAentTool.d.ts +7 -0
- package/build/main/lib/tools/PSAentTool.js +78 -0
- package/build/main/lib/tools/PSArtifactTool copy.d.ts +7 -0
- package/build/main/lib/tools/PSArtifactTool copy.js +78 -0
- package/build/main/lib/tools/PSArtifactTool.d.ts +7 -0
- package/build/main/lib/tools/PSArtifactTool.js +79 -0
- package/build/main/lib/tools/PSSubAgentTool.d.ts +20 -0
- package/build/main/lib/tools/PSSubAgentTool.js +163 -0
- package/build/module/index.d.ts +6 -0
- package/build/module/index.js +7 -0
- package/build/module/lib/PSAI.d.ts +25 -0
- package/build/module/lib/PSAI.js +314 -0
- package/build/module/lib/PSAgent.d.ts +215 -0
- package/build/module/lib/PSAgent.js +992 -0
- package/build/module/lib/async.d.ts +20 -0
- package/build/module/lib/async.js +32 -0
- package/build/module/lib/number.d.ts +43 -0
- package/build/module/lib/number.js +51 -0
- package/package.json +122 -0
|
@@ -0,0 +1,992 @@
|
|
|
1
|
+
import { PSAgentTool } from "./PSAgentTool";
|
|
2
|
+
import { z } from "zod/v4";
|
|
3
|
+
import { v4 as uuidv4 } from 'uuid'; // For generating unique IDs
|
|
4
|
+
import os from "os";
|
|
5
|
+
import { PSAgentTask } from "./PSAgentTask";
|
|
6
|
+
import { DefaultRequestHandler, InMemoryTaskStore, } from "@a2a-js/sdk/server";
|
|
7
|
+
import { A2AExpressApp } from "@a2a-js/sdk/server/express";
|
|
8
|
+
import express from "express";
|
|
9
|
+
import fs from "node:fs";
|
|
10
|
+
import path from "node:path";
|
|
11
|
+
import yaml from "yaml";
|
|
12
|
+
import { PSAI } from "./PSAI";
|
|
13
|
+
// fetch-retry can also wrap Node.js's native fetch API implementation:
|
|
14
|
+
const fetch = require('fetch-retry')(global.fetch);
|
|
15
|
+
export async function sleep(ms) {
|
|
16
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* PSAgent class to manage AI agents with tools and tasks
|
|
20
|
+
* Implements AgentExecutor interface from A2A SDK
|
|
21
|
+
* Supports starting, resuming, cancelling agents and executing tools
|
|
22
|
+
* Manages agent state, tools, tasks and interactions with A2A platform
|
|
23
|
+
* Includes methods for A2A server setup and event fetching
|
|
24
|
+
*
|
|
25
|
+
* uses the token from config if not PS_API_TOKEN env variable
|
|
26
|
+
* adds x-client-id, x-project-id, x-workspace-id from env variables
|
|
27
|
+
* PS_CLIENT_ID, PS_PROJECT_ID, PS_WORKSPACE_ID respectively for authentication headers
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```typescript
|
|
31
|
+
* import { PSAI, PSAgent, PSAgentLogger } from 'psadk';
|
|
32
|
+
* import dotenv from 'dotenv';
|
|
33
|
+
* dotenv.config();
|
|
34
|
+
* async function main() {
|
|
35
|
+
* const token = await PSAI.getServiceToken({});
|
|
36
|
+
* const HelloAgent = new PSAgent({
|
|
37
|
+
* name: 'HelloAgent',
|
|
38
|
+
* description: 'A simple hello world agent',
|
|
39
|
+
* system_prompt: 'You are a helpful assistant that greets users.',
|
|
40
|
+
* model: 'gpt-5',
|
|
41
|
+
* tools: [],
|
|
42
|
+
* log: new PSAgentLogger({ level: 'info' }),
|
|
43
|
+
* token: token,
|
|
44
|
+
* port: 41241,
|
|
45
|
+
* });
|
|
46
|
+
* const { app, port } = HelloAgent.getServer();
|
|
47
|
+
* app.listen(port, () => {
|
|
48
|
+
* console.log('HelloAgent is running on http://localhost:${port}');
|
|
49
|
+
* });
|
|
50
|
+
* }
|
|
51
|
+
* main();
|
|
52
|
+
* ```
|
|
53
|
+
* @see PSAgentConfig, PSAgentTool, PSAgentTask
|
|
54
|
+
*
|
|
55
|
+
* @see
|
|
56
|
+
* https://dev.lionis.ai/docs/api/v1/agents/create-agent
|
|
57
|
+
* https://pscode.lioncloud.net/ai-tso/psadk_samples
|
|
58
|
+
* https://a2aproject.com/docs/getting-started/quickstart
|
|
59
|
+
*
|
|
60
|
+
*/
|
|
61
|
+
export class PSAgent {
|
|
62
|
+
config;
|
|
63
|
+
tools = [];
|
|
64
|
+
tasks = [];
|
|
65
|
+
agentId;
|
|
66
|
+
correlationId;
|
|
67
|
+
log;
|
|
68
|
+
cancelledTasks = new Set();
|
|
69
|
+
cancelTask = async (taskId, _eventBus) => {
|
|
70
|
+
this.cancelledTasks.add(taskId);
|
|
71
|
+
// The execute loop is responsible for publishing the final state
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* create a new PSAgent with the given config
|
|
75
|
+
* @param config PSAgentConfig with optional logger
|
|
76
|
+
*/
|
|
77
|
+
constructor(config) {
|
|
78
|
+
this.config = config;
|
|
79
|
+
this.log = config.log;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Get the Agent Class
|
|
83
|
+
* @returns Agent class from config or default "LlmAgent"
|
|
84
|
+
*/
|
|
85
|
+
get AgentClass() {
|
|
86
|
+
return this.config.agent_class || "LlmAgent";
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Get the Name of the Agent
|
|
90
|
+
*
|
|
91
|
+
* @returns Name of the agent from config
|
|
92
|
+
*/
|
|
93
|
+
get Name() {
|
|
94
|
+
return this.config.name;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Get the Description of the Agent
|
|
98
|
+
* @returns Description of the agent from config
|
|
99
|
+
*/
|
|
100
|
+
get Description() {
|
|
101
|
+
return this.config.description;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Get the Instruction(System Prompt) of the Agent
|
|
105
|
+
* @returns Instruction(System prompt) of the agent from config
|
|
106
|
+
*/
|
|
107
|
+
get Instruction() {
|
|
108
|
+
return this.config.instruction;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Get the Model of the Agent
|
|
112
|
+
* @returns Model of the agent from config
|
|
113
|
+
*/
|
|
114
|
+
get Model() {
|
|
115
|
+
return this.config.model;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Get the Input Schema of the Agent
|
|
119
|
+
* @returns Input schema of the agent from config
|
|
120
|
+
*/
|
|
121
|
+
get InputSchema() {
|
|
122
|
+
return this.config.input_schema;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Get the Output Schema of the Agent
|
|
126
|
+
* @returns Output schema of the agent from config
|
|
127
|
+
*/
|
|
128
|
+
get OutputSchema() {
|
|
129
|
+
return this.config.output_schema;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Get the Token for the Agent
|
|
133
|
+
* @returns Token of the agent from config
|
|
134
|
+
*/
|
|
135
|
+
get Token() {
|
|
136
|
+
return this.config.token;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Set the Token for the Agent
|
|
140
|
+
* @param value Token string to set in config
|
|
141
|
+
*/
|
|
142
|
+
set Token(value) {
|
|
143
|
+
this.config.token = value;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Get the Tools for the Agent
|
|
147
|
+
* Includes both config tools and dynamically added tools
|
|
148
|
+
* @returns Array of PSAgentTool instances
|
|
149
|
+
*/
|
|
150
|
+
get Tools() {
|
|
151
|
+
return [...this.config.tools || [], ...this.tools];
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Get the Tasks for the Agent
|
|
155
|
+
* Includes both config tasks and dynamically added tasks
|
|
156
|
+
* @returns Array of PSAgentTask instances
|
|
157
|
+
*/
|
|
158
|
+
get Tasks() {
|
|
159
|
+
return [...this.config.tasks || [], ...this.tasks];
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Get the Agent ID after starting the agent
|
|
163
|
+
* @returns Agent ID string or undefined if not started
|
|
164
|
+
*/
|
|
165
|
+
get AgentId() {
|
|
166
|
+
return this.agentId;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Get the Correlation ID after starting the agent
|
|
170
|
+
* @returns Correlation ID string or undefined if not started
|
|
171
|
+
*/
|
|
172
|
+
get CorrelationId() {
|
|
173
|
+
return this.correlationId;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Add tools to the agent dynamically
|
|
177
|
+
* @param tool PSAgentTool to add to the agent
|
|
178
|
+
*/
|
|
179
|
+
addTool(tool) {
|
|
180
|
+
if (!this.tools) {
|
|
181
|
+
this.tools = [];
|
|
182
|
+
}
|
|
183
|
+
let toolIndex = this.config.tools?.findIndex(t => t.Name === tool.Name) || -1;
|
|
184
|
+
if (toolIndex !== -1) {
|
|
185
|
+
throw new Error(`Tool with name ${tool.Name} already exists in config`);
|
|
186
|
+
}
|
|
187
|
+
toolIndex = this.tools.findIndex(t => t.Name === tool.Name);
|
|
188
|
+
if (toolIndex !== -1) {
|
|
189
|
+
throw new Error(`Tool with name ${tool.Name} already exists in agent`);
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
this.tools.push(tool);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
// get token from config or environment variable
|
|
196
|
+
async getToken() {
|
|
197
|
+
if (this.config.token)
|
|
198
|
+
return this.config.token;
|
|
199
|
+
if (this.config.getToken)
|
|
200
|
+
return await this.config.getToken();
|
|
201
|
+
return process.env.PS_API_TOKEN || '';
|
|
202
|
+
}
|
|
203
|
+
// Private method to get auth headers
|
|
204
|
+
// uses the token from config if not PS_API_TOKEN env variable
|
|
205
|
+
// adds x-client-id, x-project-id, x-workspace-id from env variables
|
|
206
|
+
// PS_CLIENT_ID, PS_PROJECT_ID, PS_WORKSPACE_ID respectively
|
|
207
|
+
async getAuthHeaders() {
|
|
208
|
+
const headers = {};
|
|
209
|
+
headers['Authorization'] = `Bearer ${await this.getToken()}`;
|
|
210
|
+
headers["x-client-id"] = process.env.PS_CLIENT_ID || "";
|
|
211
|
+
headers["x-project-id"] = process.env.PS_PROJECT_ID || "";
|
|
212
|
+
headers["x-workspace-id"] = process.env.PS_WORKSPACE_ID || "";
|
|
213
|
+
if (this.config.additional_headers) {
|
|
214
|
+
Object.assign(headers, this.config.additional_headers);
|
|
215
|
+
}
|
|
216
|
+
return headers;
|
|
217
|
+
}
|
|
218
|
+
// Get base URL from environment variable or use default
|
|
219
|
+
// uses env variable PS_API_URL if set, otherwise defaults to https://dev.lionis.ai
|
|
220
|
+
getBaseUrl() {
|
|
221
|
+
const baseUrl = process.env.PS_API_URL || 'https://dev.lionis.ai';
|
|
222
|
+
return baseUrl.replace(/\/+$/, ''); // Remove trailing slashes
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* start a new temp agent on the agent platform
|
|
226
|
+
* @param input input to the agent the schema should match InputSchema if provided
|
|
227
|
+
* @param history history of messages for agent context
|
|
228
|
+
* @param log logger to use for logging, defaults to agent's logger
|
|
229
|
+
* @returns Correlation ID of the started agent
|
|
230
|
+
*/
|
|
231
|
+
async start(input, history, log) {
|
|
232
|
+
log = log || this.log;
|
|
233
|
+
const ischema = this.InputSchema ? this.InputSchema : z.toJSONSchema(z.object({
|
|
234
|
+
query: z.string().meta({
|
|
235
|
+
title: "query",
|
|
236
|
+
description: "The query by the user."
|
|
237
|
+
}),
|
|
238
|
+
// history: z.array(z.object({
|
|
239
|
+
// role: z.enum(["user", "model"]).meta({
|
|
240
|
+
// title: "role",
|
|
241
|
+
// description: "Role of the message sender"
|
|
242
|
+
// }),
|
|
243
|
+
// content: z.string().meta({
|
|
244
|
+
// title: "content",
|
|
245
|
+
// description: "Content of the message"
|
|
246
|
+
// })
|
|
247
|
+
// })).optional().meta({
|
|
248
|
+
// title: "history",
|
|
249
|
+
// description: "List of the User and Assistant chat history"
|
|
250
|
+
// })
|
|
251
|
+
}).meta({
|
|
252
|
+
title: "request_body",
|
|
253
|
+
description: "The request body parameters for the agent.",
|
|
254
|
+
}), { "io": "input" });
|
|
255
|
+
//check if the input confirms to the input schema
|
|
256
|
+
// const parseResult = z.object(ischema).safeParse(input);
|
|
257
|
+
// if (!parseResult.success) {
|
|
258
|
+
// log?.error('Input validation failed:', parseResult.error.message);
|
|
259
|
+
// throw new Error(`Input validation failed: ${parseResult.error.issues.map(issue=>issue.message).join(', ')}`);
|
|
260
|
+
// }
|
|
261
|
+
if (ischema.properties) {
|
|
262
|
+
Object.assign(ischema.properties, {
|
|
263
|
+
history: {
|
|
264
|
+
type: "array",
|
|
265
|
+
items: {
|
|
266
|
+
type: "object"
|
|
267
|
+
},
|
|
268
|
+
description: "List of the User and Assistant chat history",
|
|
269
|
+
title: "Chat history"
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
// console.log("input schema:", JSON.stringify(ischema, null, 2));
|
|
274
|
+
const output_schema = this.OutputSchema || z.toJSONSchema(z.object({
|
|
275
|
+
response: z.string().meta({
|
|
276
|
+
title: "Response",
|
|
277
|
+
description: "The response from the agent",
|
|
278
|
+
}),
|
|
279
|
+
}).meta({
|
|
280
|
+
title: "Agent_Response",
|
|
281
|
+
description: "The response from the agent after processing the input",
|
|
282
|
+
}), { io: "output" });
|
|
283
|
+
const body = {
|
|
284
|
+
agent: {
|
|
285
|
+
name: this.Name,
|
|
286
|
+
description: this.Description,
|
|
287
|
+
system_prompt: this.Instruction,
|
|
288
|
+
llm: this.Model || "gpt-5",
|
|
289
|
+
input_schema: ischema,
|
|
290
|
+
output_schema: output_schema,
|
|
291
|
+
},
|
|
292
|
+
input_params: Object.assign(input, { history: history || [] }),
|
|
293
|
+
tasks: this.Tasks.map(task => ({
|
|
294
|
+
name: task.Name,
|
|
295
|
+
description: task.Description
|
|
296
|
+
})),
|
|
297
|
+
tools: {
|
|
298
|
+
new_tools: this.Tools.map(tool => ({
|
|
299
|
+
name: tool.Name,
|
|
300
|
+
description: tool.Description,
|
|
301
|
+
type: tool.Type,
|
|
302
|
+
local_execution: tool.LocalExecution,
|
|
303
|
+
hitl: tool.HITL,
|
|
304
|
+
inputs: tool.Inputs
|
|
305
|
+
})),
|
|
306
|
+
existing_tools: []
|
|
307
|
+
},
|
|
308
|
+
planning: {
|
|
309
|
+
plan_required: this.config.show_plan || false,
|
|
310
|
+
hitl: this.config.hitl_for_plan || false
|
|
311
|
+
},
|
|
312
|
+
};
|
|
313
|
+
try {
|
|
314
|
+
const headers = await this.getAuthHeaders();
|
|
315
|
+
// log?.debug('Starting agent with headers:', headers);
|
|
316
|
+
// log?.debug('Starting agent with body:', JSON.stringify(body.input_params, null, 2));
|
|
317
|
+
const response = await fetch(`${this.getBaseUrl()}/api/v1/agents/temp_agent`, {
|
|
318
|
+
method: 'POST',
|
|
319
|
+
headers: {
|
|
320
|
+
'Content-Type': 'application/json',
|
|
321
|
+
...headers
|
|
322
|
+
},
|
|
323
|
+
body: JSON.stringify(body),
|
|
324
|
+
retries: 3,
|
|
325
|
+
retryOn: function (attempt, error, response) {
|
|
326
|
+
// retry on any network error, or 4xx or 5xx status codes
|
|
327
|
+
if (error !== null || response.status > 400 && attempt < 4) {
|
|
328
|
+
log?.warn(`retrying execute agent, attempt number ${attempt + 1}, status: ${response.status}`, error);
|
|
329
|
+
return true;
|
|
330
|
+
}
|
|
331
|
+
return false;
|
|
332
|
+
},
|
|
333
|
+
retryDelay: function (attempt, _error, _response) {
|
|
334
|
+
return Math.pow(2, attempt) * 1000; // 1000, 2000, 4000
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
if (!response.ok) {
|
|
338
|
+
let errorDetails;
|
|
339
|
+
try {
|
|
340
|
+
errorDetails = await response.text();
|
|
341
|
+
}
|
|
342
|
+
catch (e) {
|
|
343
|
+
throw new Error(`Unable to start agent. status: ${response.status}`);
|
|
344
|
+
}
|
|
345
|
+
if (errorDetails) {
|
|
346
|
+
throw new Error(`Unable to start agent. status: ${response.status}, details: ${JSON.stringify(errorDetails)}`);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
const responseData = (await response.json());
|
|
350
|
+
this.agentId = responseData.data.agent_id;
|
|
351
|
+
this.correlationId = responseData.data.correlation_id;
|
|
352
|
+
log?.info(`Agent started with ID: ${this.agentId}, Correlation ID: ${this.correlationId}`);
|
|
353
|
+
return this.correlationId;
|
|
354
|
+
}
|
|
355
|
+
catch (error) {
|
|
356
|
+
log?.error('Unable to start agent:', error);
|
|
357
|
+
throw error;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* resume the agent with tool output after a tool execution or human input
|
|
362
|
+
* @param tooloutput output from the tool execution to send back to the agent
|
|
363
|
+
* @returns
|
|
364
|
+
*/
|
|
365
|
+
async resume(tooloutput) {
|
|
366
|
+
const log = this.log;
|
|
367
|
+
if (!this.agentId) {
|
|
368
|
+
throw new Error("Agent ID is not set");
|
|
369
|
+
}
|
|
370
|
+
try {
|
|
371
|
+
const response = await fetch(`${this.getBaseUrl()}/api/v1/agents/${this.agentId}/resume`, {
|
|
372
|
+
method: 'POST',
|
|
373
|
+
headers: {
|
|
374
|
+
'Content-Type': 'application/json',
|
|
375
|
+
...await this.getAuthHeaders()
|
|
376
|
+
},
|
|
377
|
+
body: JSON.stringify({
|
|
378
|
+
"correlation_id": this.CorrelationId,
|
|
379
|
+
"tool_output_override": tooloutput,
|
|
380
|
+
}),
|
|
381
|
+
retries: 3,
|
|
382
|
+
retryOn: function (attempt, error, response) {
|
|
383
|
+
// retry on any network error, or 4xx or 5xx status codes
|
|
384
|
+
if (error !== null || response.status >= 400 && attempt < 4) {
|
|
385
|
+
log?.warn(`retrying resume, attempt number ${attempt + 1} ${response.status}`, error);
|
|
386
|
+
return true;
|
|
387
|
+
}
|
|
388
|
+
return false;
|
|
389
|
+
},
|
|
390
|
+
retryDelay: function (attempt, _error, _response) {
|
|
391
|
+
return Math.pow(2, attempt) * 1000; // 1000, 2000, 4000
|
|
392
|
+
}
|
|
393
|
+
});
|
|
394
|
+
if (!response.ok) {
|
|
395
|
+
log?.error("Error resuming agent:", await response.json());
|
|
396
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
397
|
+
}
|
|
398
|
+
const resumeData = await response.json();
|
|
399
|
+
return resumeData;
|
|
400
|
+
}
|
|
401
|
+
catch (error) {
|
|
402
|
+
log?.error("Error resuming agent:", error);
|
|
403
|
+
log?.error("Correlation ID:", this.correlationId);
|
|
404
|
+
log?.error("Tool Output:", tooloutput);
|
|
405
|
+
throw new Error(`Failed to resume agent: ${error instanceof Error ? error.message : String(error)}`);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* deletes the agent running on the platform
|
|
410
|
+
* @param log optional logger to use for logging, defaults to agent's logger
|
|
411
|
+
*/
|
|
412
|
+
async cancel(log) {
|
|
413
|
+
if (!this.AgentId) {
|
|
414
|
+
throw new Error("Agent is not started");
|
|
415
|
+
}
|
|
416
|
+
try {
|
|
417
|
+
const response = await fetch(`${this.getBaseUrl()}/api/v1/agents/${this.agentId}`, {
|
|
418
|
+
method: 'DELETE',
|
|
419
|
+
headers: {
|
|
420
|
+
'Content-Type': 'application/json',
|
|
421
|
+
...await this.getAuthHeaders()
|
|
422
|
+
},
|
|
423
|
+
retries: 3,
|
|
424
|
+
retryOn: function (attempt, error, response) {
|
|
425
|
+
// retry on any network error, or 4xx or 5xx status codes
|
|
426
|
+
if (error !== null || response.status >= 400 && attempt < 4) {
|
|
427
|
+
log?.warn(`retrying cancel agent, attempt number ${attempt + 1}`);
|
|
428
|
+
return true;
|
|
429
|
+
}
|
|
430
|
+
return false;
|
|
431
|
+
},
|
|
432
|
+
retryDelay: function (attempt, _error, _response) {
|
|
433
|
+
return Math.pow(2, attempt) * 1000; // 1000, 2000, 4000
|
|
434
|
+
}
|
|
435
|
+
});
|
|
436
|
+
if (!response.ok) {
|
|
437
|
+
let errorDetails;
|
|
438
|
+
try {
|
|
439
|
+
errorDetails = await response.json();
|
|
440
|
+
log?.error(await response.json());
|
|
441
|
+
}
|
|
442
|
+
catch (e) {
|
|
443
|
+
throw new Error(`Unable to cancel agent status: ${response.status}`);
|
|
444
|
+
}
|
|
445
|
+
if (errorDetails) {
|
|
446
|
+
throw new Error(`Unable to cancel agent status: ${response.status}, details: ${JSON.stringify(errorDetails)}`);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
log?.info(`Agent ${this.agentId} cancelled successfully`);
|
|
450
|
+
}
|
|
451
|
+
catch (error) {
|
|
452
|
+
log?.error('Error cancelling agent:', error);
|
|
453
|
+
throw error;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
457
|
+
* Gets all the events for the current agent run identified correlation ID
|
|
458
|
+
* @returns Array of event objects for the current agent's correlation ID
|
|
459
|
+
* @see https://dev.lionis.ai/api/v1/events/events?correlationId={{correlation_id}}
|
|
460
|
+
*/
|
|
461
|
+
async getEvents() {
|
|
462
|
+
if (!this.CorrelationId) {
|
|
463
|
+
throw new Error("Correlation ID is not set");
|
|
464
|
+
}
|
|
465
|
+
try {
|
|
466
|
+
const response = await fetch(`${this.getBaseUrl()}/api/v1/events/events?correlationId=${this.correlationId}`, {
|
|
467
|
+
method: 'GET',
|
|
468
|
+
headers: {
|
|
469
|
+
'Content-Type': 'application/json',
|
|
470
|
+
...await this.getAuthHeaders()
|
|
471
|
+
},
|
|
472
|
+
retries: 3,
|
|
473
|
+
retryOn: function (attempt, error, response) {
|
|
474
|
+
// retry on any network error, or 4xx or 5xx status codes
|
|
475
|
+
if (error !== null || response.status >= 400 && attempt < 4) {
|
|
476
|
+
//log?.warn(`retrying getEvents, attempt number ${attempt + 1}`);
|
|
477
|
+
return true;
|
|
478
|
+
}
|
|
479
|
+
return false;
|
|
480
|
+
},
|
|
481
|
+
retryDelay: function (attempt, _error, _response) {
|
|
482
|
+
return Math.pow(2, attempt) * 1000; // 1000, 2000, 4000
|
|
483
|
+
}
|
|
484
|
+
});
|
|
485
|
+
if (!response.ok) {
|
|
486
|
+
//log.error("Error fetching agent events:", await response.json());
|
|
487
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
488
|
+
}
|
|
489
|
+
const eventsData = await response.json();
|
|
490
|
+
return eventsData;
|
|
491
|
+
}
|
|
492
|
+
catch (error) {
|
|
493
|
+
//log.error("Error fetching agent events:", error);
|
|
494
|
+
throw new Error(`Failed to fetch agent events: ${error instanceof Error ? error.message : String(error)}`);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
/**
|
|
498
|
+
* Invoke the tool with the given name and input
|
|
499
|
+
* @remarks
|
|
500
|
+
* The input object should match the tool's input schema
|
|
501
|
+
*
|
|
502
|
+
* @param toolName name of the tool to execute
|
|
503
|
+
* @param input input object for the tool execution should match tool's input schema
|
|
504
|
+
* @param log logger to use for logging, defaults to agent's logger
|
|
505
|
+
* @returns output from the tool execution or error if any
|
|
506
|
+
*
|
|
507
|
+
* @example
|
|
508
|
+
* Example input for PSArtifactTool:
|
|
509
|
+
* ```json
|
|
510
|
+
* {
|
|
511
|
+
* "name": "example_artifact",
|
|
512
|
+
* "action": "write",
|
|
513
|
+
* "content": "This is an example artifact content."
|
|
514
|
+
* }
|
|
515
|
+
* ```
|
|
516
|
+
*/
|
|
517
|
+
async executeTool(toolName, input, log) {
|
|
518
|
+
let results = { error: null };
|
|
519
|
+
const tool = this.Tools.find(t => t.Name === toolName);
|
|
520
|
+
if (!tool) {
|
|
521
|
+
results.error = new Error(`Tool with name ${toolName} not found`);
|
|
522
|
+
}
|
|
523
|
+
else {
|
|
524
|
+
try {
|
|
525
|
+
results.result = await tool.run(input, log);
|
|
526
|
+
}
|
|
527
|
+
catch (error) {
|
|
528
|
+
results.result = undefined;
|
|
529
|
+
results.error = new Error(`${error instanceof Error ? error.message : String(error)}`);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
try {
|
|
533
|
+
if (results.error) {
|
|
534
|
+
await this.resume({ error: results.error.message || results.error.toString() });
|
|
535
|
+
}
|
|
536
|
+
else {
|
|
537
|
+
await this.resume(results.result || { output: "No result returned from tool execution" });
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
catch (error) {
|
|
541
|
+
results.error = new Error(`Error resuming agent after tool execution: ${error instanceof Error ? error.message : String(error)}`);
|
|
542
|
+
}
|
|
543
|
+
return results;
|
|
544
|
+
}
|
|
545
|
+
/**
|
|
546
|
+
*
|
|
547
|
+
* @param requestContext
|
|
548
|
+
* @param eventBus
|
|
549
|
+
* @returns
|
|
550
|
+
*/
|
|
551
|
+
async execute(requestContext, eventBus) {
|
|
552
|
+
const log = this.log;
|
|
553
|
+
const userMessage = requestContext.userMessage;
|
|
554
|
+
const existingTask = requestContext.task;
|
|
555
|
+
const taskId = existingTask?.id || uuidv4();
|
|
556
|
+
const contextId = userMessage.contextId || existingTask?.contextId || uuidv4();
|
|
557
|
+
log?.info(`[${this.Name}:AgentExecutor] Processing message ${userMessage.messageId} for task ${taskId} (context: ${contextId})`);
|
|
558
|
+
// 1. Publish initial Task event if it's a new task
|
|
559
|
+
if (!existingTask) {
|
|
560
|
+
const initialTask = {
|
|
561
|
+
kind: 'task',
|
|
562
|
+
id: taskId,
|
|
563
|
+
contextId: contextId,
|
|
564
|
+
status: {
|
|
565
|
+
state: 'submitted',
|
|
566
|
+
timestamp: new Date().toISOString(),
|
|
567
|
+
},
|
|
568
|
+
history: [userMessage],
|
|
569
|
+
metadata: userMessage.metadata,
|
|
570
|
+
artifacts: [], // Initialize artifacts array
|
|
571
|
+
};
|
|
572
|
+
eventBus.publish(initialTask);
|
|
573
|
+
}
|
|
574
|
+
// 2. Publish "working" status update
|
|
575
|
+
let workingStatusUpdate = {
|
|
576
|
+
kind: 'status-update',
|
|
577
|
+
taskId: taskId,
|
|
578
|
+
contextId: contextId,
|
|
579
|
+
status: {
|
|
580
|
+
state: 'working',
|
|
581
|
+
message: {
|
|
582
|
+
kind: 'message',
|
|
583
|
+
role: 'agent',
|
|
584
|
+
messageId: uuidv4(),
|
|
585
|
+
parts: [{ kind: 'text', text: 'Initializing the agent...' }],
|
|
586
|
+
taskId: taskId,
|
|
587
|
+
contextId: contextId,
|
|
588
|
+
},
|
|
589
|
+
timestamp: new Date().toISOString(),
|
|
590
|
+
},
|
|
591
|
+
final: false,
|
|
592
|
+
};
|
|
593
|
+
eventBus.publish(workingStatusUpdate);
|
|
594
|
+
// 3. Prepare messages for history
|
|
595
|
+
const history = existingTask?.history ? [...existingTask.history] : [];
|
|
596
|
+
if (!history.find(m => m.messageId === userMessage.messageId)) {
|
|
597
|
+
history.push(userMessage);
|
|
598
|
+
}
|
|
599
|
+
log?.debug(`[${this.Name}:AgentExecutor] Total messages in history: ${history.length}`);
|
|
600
|
+
const messages = history
|
|
601
|
+
.map((m) => ({
|
|
602
|
+
role: (m.role === 'agent' ? 'model' : 'user'),
|
|
603
|
+
content: m.parts
|
|
604
|
+
.filter((p) => p.kind === 'text' && !!p.text)
|
|
605
|
+
.map((p) => p.text).join('\n'),
|
|
606
|
+
// .map((p) => ({
|
|
607
|
+
// text: (p as TextPart).text,
|
|
608
|
+
// })),
|
|
609
|
+
}))
|
|
610
|
+
.filter((m) => m.content.length > 0);
|
|
611
|
+
if (messages.length === 0) {
|
|
612
|
+
log?.warn(`[${this.Name}:AgentExecutor] No valid text messages found in history for task ${taskId}.`);
|
|
613
|
+
const failureUpdate = {
|
|
614
|
+
kind: 'status-update',
|
|
615
|
+
taskId: taskId,
|
|
616
|
+
contextId: contextId,
|
|
617
|
+
status: {
|
|
618
|
+
state: 'failed',
|
|
619
|
+
message: {
|
|
620
|
+
kind: 'message',
|
|
621
|
+
role: 'agent',
|
|
622
|
+
messageId: uuidv4(),
|
|
623
|
+
parts: [{ kind: 'text', text: 'No input message found to process.' }],
|
|
624
|
+
taskId: taskId,
|
|
625
|
+
contextId: contextId,
|
|
626
|
+
},
|
|
627
|
+
timestamp: new Date().toISOString(),
|
|
628
|
+
},
|
|
629
|
+
final: true,
|
|
630
|
+
};
|
|
631
|
+
eventBus.publish(failureUpdate);
|
|
632
|
+
return;
|
|
633
|
+
}
|
|
634
|
+
try {
|
|
635
|
+
let input = {};
|
|
636
|
+
if (userMessage.parts && userMessage.parts.length > 0) {
|
|
637
|
+
for (const part of userMessage.parts) {
|
|
638
|
+
if (part.kind === 'text' && part.text) {
|
|
639
|
+
input['query'] = part.text;
|
|
640
|
+
}
|
|
641
|
+
// TODO: handle other part kinds (e.g., images, files) here as needed
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
if (this.Tools.length === 0) {
|
|
645
|
+
const ai = new PSAI(this.Token || '', this.getBaseUrl(), log);
|
|
646
|
+
const llmResponse = await ai.llm(input.query, this.Model);
|
|
647
|
+
//console.log("llmResponse:", llmResponse);
|
|
648
|
+
const completedUpdate = {
|
|
649
|
+
kind: 'status-update',
|
|
650
|
+
taskId: taskId,
|
|
651
|
+
contextId: contextId,
|
|
652
|
+
status: {
|
|
653
|
+
state: 'completed',
|
|
654
|
+
message: {
|
|
655
|
+
kind: 'message',
|
|
656
|
+
role: 'agent',
|
|
657
|
+
messageId: uuidv4(),
|
|
658
|
+
parts: [{ kind: 'text', text: llmResponse.choices[0].message.content }],
|
|
659
|
+
taskId: taskId,
|
|
660
|
+
contextId: contextId,
|
|
661
|
+
},
|
|
662
|
+
timestamp: new Date().toISOString(),
|
|
663
|
+
},
|
|
664
|
+
final: true,
|
|
665
|
+
};
|
|
666
|
+
eventBus.publish(completedUpdate);
|
|
667
|
+
return;
|
|
668
|
+
}
|
|
669
|
+
this.log?.debug(`Agent input: ${JSON.stringify(input)}`);
|
|
670
|
+
await this.start(input, messages.slice(0, -1), log);
|
|
671
|
+
workingStatusUpdate = {
|
|
672
|
+
kind: 'status-update',
|
|
673
|
+
taskId: taskId,
|
|
674
|
+
contextId: contextId,
|
|
675
|
+
status: {
|
|
676
|
+
state: 'working',
|
|
677
|
+
message: {
|
|
678
|
+
kind: 'message',
|
|
679
|
+
role: 'agent',
|
|
680
|
+
messageId: uuidv4(),
|
|
681
|
+
parts: [{ kind: 'text', text: `started new agent ${this.AgentId}, correlationid: ${this.correlationId}` }],
|
|
682
|
+
taskId: taskId,
|
|
683
|
+
contextId: contextId,
|
|
684
|
+
},
|
|
685
|
+
timestamp: new Date().toISOString(),
|
|
686
|
+
},
|
|
687
|
+
final: false,
|
|
688
|
+
};
|
|
689
|
+
eventBus.publish(workingStatusUpdate);
|
|
690
|
+
let events;
|
|
691
|
+
let lastevent = {
|
|
692
|
+
timestamp: Date.now(),
|
|
693
|
+
};
|
|
694
|
+
let processedEvents = 0;
|
|
695
|
+
do {
|
|
696
|
+
do {
|
|
697
|
+
if (this.cancelledTasks.has(taskId)) {
|
|
698
|
+
log?.info(`[${this.Name}:AgentExecutor] Task ${taskId} has been cancelled.`);
|
|
699
|
+
// Publish the final 'canceled' status.
|
|
700
|
+
const cancelledUpdate = {
|
|
701
|
+
kind: "status-update",
|
|
702
|
+
taskId: taskId,
|
|
703
|
+
contextId: contextId,
|
|
704
|
+
status: { state: "canceled", timestamp: new Date().toISOString() },
|
|
705
|
+
final: true,
|
|
706
|
+
};
|
|
707
|
+
eventBus.publish(cancelledUpdate);
|
|
708
|
+
eventBus.finished();
|
|
709
|
+
// Clean up and exit.
|
|
710
|
+
this.cancelledTasks.delete(taskId);
|
|
711
|
+
return;
|
|
712
|
+
}
|
|
713
|
+
await sleep(500);
|
|
714
|
+
//log?.info(`[${this.Name}:AgentExecutor] Fetching events for correlation ID ${this.CorrelationId}`);
|
|
715
|
+
events = await this.getEvents();
|
|
716
|
+
if (events.length > processedEvents) {
|
|
717
|
+
events = events.slice(processedEvents); // get only new events
|
|
718
|
+
}
|
|
719
|
+
} while (events.length === 0);
|
|
720
|
+
const event = events[0];
|
|
721
|
+
if (lastevent.timestamp !== event.timestamp) {
|
|
722
|
+
//log?.debug(`[${this.Name}:AgentExecutor] New event: ${event.timestamp} Last Event: ${lastevent.timestamp} `);
|
|
723
|
+
lastevent = event;
|
|
724
|
+
if (lastevent.agentStatus === "waiting") {
|
|
725
|
+
if (lastevent.tool.startsWith('mcp_')) {
|
|
726
|
+
}
|
|
727
|
+
else if (lastevent.tool) {
|
|
728
|
+
log?.info(`[${this.Name}:AgentExecutor] toolcall: ${lastevent.tool}, ts:${lastevent.timestamp}, c:${processedEvents}\n`);
|
|
729
|
+
let workingStatusUpdate = {
|
|
730
|
+
kind: 'status-update',
|
|
731
|
+
taskId: taskId,
|
|
732
|
+
contextId: contextId,
|
|
733
|
+
status: {
|
|
734
|
+
state: 'working',
|
|
735
|
+
message: {
|
|
736
|
+
kind: 'message',
|
|
737
|
+
role: 'agent',
|
|
738
|
+
messageId: uuidv4(),
|
|
739
|
+
parts: [{ kind: 'text', text: `invoking tool ${lastevent.tool}(${JSON.stringify(lastevent.toolInput.request_body)})...\n` }],
|
|
740
|
+
taskId: taskId,
|
|
741
|
+
contextId: contextId,
|
|
742
|
+
metadata: {
|
|
743
|
+
data: lastevent.toolInput.request_body
|
|
744
|
+
}
|
|
745
|
+
},
|
|
746
|
+
timestamp: new Date().toISOString(),
|
|
747
|
+
},
|
|
748
|
+
final: false,
|
|
749
|
+
};
|
|
750
|
+
eventBus.publish(workingStatusUpdate);
|
|
751
|
+
const res = await this.executeTool(lastevent.tool, lastevent.toolInput.request_body, log);
|
|
752
|
+
let statusMsg = `Tool ${lastevent.tool} executed `;
|
|
753
|
+
if (res.error) {
|
|
754
|
+
log?.error(`${res.error}`);
|
|
755
|
+
statusMsg += `with error: ${res.error.message || res.error.toString()}`;
|
|
756
|
+
}
|
|
757
|
+
else {
|
|
758
|
+
statusMsg += `successfully.`;
|
|
759
|
+
}
|
|
760
|
+
workingStatusUpdate = {
|
|
761
|
+
kind: 'status-update',
|
|
762
|
+
taskId: taskId,
|
|
763
|
+
contextId: contextId,
|
|
764
|
+
status: {
|
|
765
|
+
state: 'working',
|
|
766
|
+
message: {
|
|
767
|
+
kind: 'message',
|
|
768
|
+
role: 'agent',
|
|
769
|
+
messageId: uuidv4(),
|
|
770
|
+
parts: [{ kind: 'text', text: statusMsg }],
|
|
771
|
+
taskId: taskId,
|
|
772
|
+
contextId: contextId,
|
|
773
|
+
},
|
|
774
|
+
timestamp: new Date().toISOString(),
|
|
775
|
+
},
|
|
776
|
+
final: false,
|
|
777
|
+
};
|
|
778
|
+
eventBus.publish(workingStatusUpdate);
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
else if (lastevent.agentStatus === "pending") {
|
|
782
|
+
//log(`\n\n#### ${lastevent.agentStatus}:\n`);
|
|
783
|
+
}
|
|
784
|
+
else if (lastevent.agentStatus === "in-progress") {
|
|
785
|
+
if (lastevent.tool === 'generate_plan_tool' && lastevent.toolOutput) {
|
|
786
|
+
log?.info("```log-Agent_Plan\n" + lastevent.toolOutput + "\n```");
|
|
787
|
+
}
|
|
788
|
+
else if (lastevent.message.indexOf("resume")) {
|
|
789
|
+
//log.info(`.`);
|
|
790
|
+
}
|
|
791
|
+
else
|
|
792
|
+
log?.info(`\n\n#### ${lastevent.message}:\n`);
|
|
793
|
+
}
|
|
794
|
+
else if (lastevent.agentStatus === "completed") {
|
|
795
|
+
log?.info(`Agent Execution Completed`);
|
|
796
|
+
log?.info(`---`);
|
|
797
|
+
log?.done(lastevent.output.response);
|
|
798
|
+
}
|
|
799
|
+
else {
|
|
800
|
+
log?.warn(`#### ${lastevent.agentStatus}`);
|
|
801
|
+
log?.warn('last event:', lastevent);
|
|
802
|
+
log?.error(`Agent execution failed: ${JSON.stringify(lastevent)}`);
|
|
803
|
+
log?.done(' Agent execution failed ');
|
|
804
|
+
}
|
|
805
|
+
processedEvents++;
|
|
806
|
+
}
|
|
807
|
+
} while (lastevent?.agentStatus !== "failed" && lastevent?.agentStatus !== "completed");
|
|
808
|
+
if (lastevent?.agentStatus === "failed") {
|
|
809
|
+
const failureUpdate = {
|
|
810
|
+
kind: 'status-update',
|
|
811
|
+
taskId: taskId,
|
|
812
|
+
contextId: contextId,
|
|
813
|
+
status: {
|
|
814
|
+
state: 'failed',
|
|
815
|
+
message: {
|
|
816
|
+
kind: 'message',
|
|
817
|
+
role: 'agent',
|
|
818
|
+
messageId: uuidv4(),
|
|
819
|
+
parts: [{ kind: 'text', text: `Agent execution failed: ${lastevent?.message || ''}` }],
|
|
820
|
+
taskId: taskId,
|
|
821
|
+
contextId: contextId,
|
|
822
|
+
},
|
|
823
|
+
timestamp: new Date().toISOString(),
|
|
824
|
+
},
|
|
825
|
+
final: true,
|
|
826
|
+
};
|
|
827
|
+
eventBus.publish(failureUpdate);
|
|
828
|
+
}
|
|
829
|
+
else if (lastevent?.agentStatus === "completed") {
|
|
830
|
+
const completedUpdate = {
|
|
831
|
+
kind: 'status-update',
|
|
832
|
+
taskId: taskId,
|
|
833
|
+
contextId: contextId,
|
|
834
|
+
status: {
|
|
835
|
+
state: 'completed',
|
|
836
|
+
message: {
|
|
837
|
+
kind: 'message',
|
|
838
|
+
role: 'agent',
|
|
839
|
+
messageId: uuidv4(),
|
|
840
|
+
parts: [{ kind: 'text', text: lastevent.output.response }],
|
|
841
|
+
taskId: taskId,
|
|
842
|
+
contextId: contextId,
|
|
843
|
+
},
|
|
844
|
+
timestamp: new Date().toISOString(),
|
|
845
|
+
},
|
|
846
|
+
final: true,
|
|
847
|
+
};
|
|
848
|
+
eventBus.publish(completedUpdate);
|
|
849
|
+
}
|
|
850
|
+
return;
|
|
851
|
+
}
|
|
852
|
+
catch (error) {
|
|
853
|
+
log?.error(`[${this.Name}:AgentExecutor] Error processing task ${taskId}:`, error);
|
|
854
|
+
const errorUpdate = {
|
|
855
|
+
kind: 'status-update',
|
|
856
|
+
taskId: taskId,
|
|
857
|
+
contextId: contextId,
|
|
858
|
+
status: {
|
|
859
|
+
state: 'failed',
|
|
860
|
+
message: {
|
|
861
|
+
kind: 'message',
|
|
862
|
+
role: 'agent',
|
|
863
|
+
messageId: uuidv4(),
|
|
864
|
+
parts: [{ kind: 'text', text: `Error processing your message: ${error instanceof Error ? error.message : String(error)}` }],
|
|
865
|
+
taskId: taskId,
|
|
866
|
+
contextId: contextId,
|
|
867
|
+
},
|
|
868
|
+
timestamp: new Date().toISOString(),
|
|
869
|
+
},
|
|
870
|
+
final: true,
|
|
871
|
+
};
|
|
872
|
+
eventBus.publish(errorUpdate);
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
// Function to get the host IP address
|
|
876
|
+
getHostIPAddress() {
|
|
877
|
+
const interfaces = os.networkInterfaces();
|
|
878
|
+
for (const interfaceName in interfaces) {
|
|
879
|
+
for (const interfaceInfo of interfaces[interfaceName] || []) {
|
|
880
|
+
if (interfaceInfo.family === 'IPv4' && !interfaceInfo.internal) {
|
|
881
|
+
return interfaceInfo.address;
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
return 'localhost';
|
|
886
|
+
}
|
|
887
|
+
/**
|
|
888
|
+
* Port for A2A express server
|
|
889
|
+
* @returns Port number from config or default 41241
|
|
890
|
+
*/
|
|
891
|
+
getPort() {
|
|
892
|
+
return this.config.port || 41241;
|
|
893
|
+
}
|
|
894
|
+
/**
|
|
895
|
+
*
|
|
896
|
+
* @param taskStore Optional TaskStore, defaults to InMemoryTaskStore
|
|
897
|
+
* @returns A2A Express app
|
|
898
|
+
*/
|
|
899
|
+
getServer(taskStore = new InMemoryTaskStore()) {
|
|
900
|
+
// Set up the server.
|
|
901
|
+
const agentExecutor = this;
|
|
902
|
+
const requestHandler = new DefaultRequestHandler(this.getAgentCard(), taskStore, agentExecutor);
|
|
903
|
+
const appBuilder = new A2AExpressApp(requestHandler);
|
|
904
|
+
const expressApp = appBuilder.setupRoutes(express());
|
|
905
|
+
return { app: expressApp, port: this.getPort() };
|
|
906
|
+
}
|
|
907
|
+
getAgentCard() {
|
|
908
|
+
return {
|
|
909
|
+
version: '0.0.1', // Incremented version
|
|
910
|
+
protocolVersion: '0.3.0',
|
|
911
|
+
name: this.Name,
|
|
912
|
+
description: this.Description,
|
|
913
|
+
// Adjust the base URL and port in A2AExpressApp
|
|
914
|
+
url: `http://${this.getHostIPAddress()}:${this.getPort()}/`,
|
|
915
|
+
provider: {
|
|
916
|
+
organization: 'PS',
|
|
917
|
+
url: 'https://example.com/a2a-agent' // Added provider URL
|
|
918
|
+
},
|
|
919
|
+
capabilities: {
|
|
920
|
+
streaming: true, // The new framework supports streaming
|
|
921
|
+
pushNotifications: false, // Assuming not implemented for this agent yet
|
|
922
|
+
stateTransitionHistory: true, // Agent uses history
|
|
923
|
+
},
|
|
924
|
+
// authentication: null, // Property 'authentication' does not exist on type 'AgentCard'.
|
|
925
|
+
securitySchemes: undefined, // Or define actual security schemes if any
|
|
926
|
+
security: undefined,
|
|
927
|
+
defaultInputModes: ['text'],
|
|
928
|
+
defaultOutputModes: ['text', 'task-status'], // task-status is a common output mode
|
|
929
|
+
skills: [
|
|
930
|
+
{
|
|
931
|
+
id: this.Name.toLowerCase().replace(/\s+/g, '-'),
|
|
932
|
+
name: this.Name,
|
|
933
|
+
description: this.Description,
|
|
934
|
+
tags: ['psagent'],
|
|
935
|
+
examples: this.config.examples || [],
|
|
936
|
+
inputModes: ['text'], // Explicitly defining for skill
|
|
937
|
+
outputModes: ['text', 'task-status'] // Explicitly defining for skill
|
|
938
|
+
},
|
|
939
|
+
...this.tasks.map(task => ({
|
|
940
|
+
id: task.Name.toLowerCase().replace(/\s+/g, '-'),
|
|
941
|
+
name: task.Name,
|
|
942
|
+
description: task.Description,
|
|
943
|
+
tags: ['psagent-task'],
|
|
944
|
+
examples: task.Examples,
|
|
945
|
+
inputModes: ['text'], // Assuming text input for tasks
|
|
946
|
+
outputModes: ['text', 'task-status'] // Assuming text and status output for tasks
|
|
947
|
+
}))
|
|
948
|
+
],
|
|
949
|
+
supportsAuthenticatedExtendedCard: false,
|
|
950
|
+
};
|
|
951
|
+
}
|
|
952
|
+
static FromConfigFile(config_path) {
|
|
953
|
+
if (!fs.existsSync(config_path)) {
|
|
954
|
+
throw new Error(`Config file ${config_path} does not exist`);
|
|
955
|
+
}
|
|
956
|
+
const ext = path.extname(config_path).toLowerCase();
|
|
957
|
+
let configData;
|
|
958
|
+
if (ext === '.json') {
|
|
959
|
+
const fileContent = fs.readFileSync(config_path, 'utf-8');
|
|
960
|
+
configData = JSON.parse(fileContent);
|
|
961
|
+
}
|
|
962
|
+
else if (ext === '.yaml' || ext === '.yml') {
|
|
963
|
+
const fileContent = fs.readFileSync(config_path, 'utf-8');
|
|
964
|
+
configData = yaml.parse(fileContent);
|
|
965
|
+
}
|
|
966
|
+
else {
|
|
967
|
+
throw new Error('Unsupported config file format. Use JSON or YAML.');
|
|
968
|
+
}
|
|
969
|
+
// Basic validation of required fields
|
|
970
|
+
const requiredFields = ['name', 'description', 'instruction'];
|
|
971
|
+
for (const field of requiredFields) {
|
|
972
|
+
if (!configData[field]) {
|
|
973
|
+
throw new Error(`Missing required field '${field}' in config file.`);
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
// Convert tools and tasks if present
|
|
977
|
+
if (configData.tools && Array.isArray(configData.tools)) {
|
|
978
|
+
configData.tools = configData.tools.map((toolConfig) => new PSAgentTool(toolConfig));
|
|
979
|
+
}
|
|
980
|
+
else {
|
|
981
|
+
configData.tools = [];
|
|
982
|
+
}
|
|
983
|
+
if (configData.tasks && Array.isArray(configData.tasks)) {
|
|
984
|
+
configData.tasks = configData.tasks.map((taskConfig) => new PSAgentTask(taskConfig.name, taskConfig.description));
|
|
985
|
+
}
|
|
986
|
+
else {
|
|
987
|
+
configData.tasks = [];
|
|
988
|
+
}
|
|
989
|
+
return new PSAgent(configData);
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUFNBZ2VudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9saWIvUFNBZ2VudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFDQSxPQUFPLEVBQXNCLFdBQVcsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUVoRSxPQUFPLEVBQUUsQ0FBQyxFQUFFLE1BQU0sUUFBUSxDQUFDO0FBQzNCLE9BQU8sRUFBRSxFQUFFLElBQUksTUFBTSxFQUFFLE1BQU0sTUFBTSxDQUFDLENBQUMsNEJBQTRCO0FBQ2pFLE9BQU8sRUFBRSxNQUFNLElBQUksQ0FBQztBQUNwQixPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBTzVDLE9BQU8sRUFJTCxxQkFBcUIsRUFFckIsaUJBQWlCLEdBQ2xCLE1BQU0sb0JBQW9CLENBQUM7QUFDNUIsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBQzNELE9BQU8sT0FBTyxNQUFNLFNBQVMsQ0FBQztBQUM5QixPQUFPLEVBQUUsTUFBTSxTQUFTLENBQUM7QUFDekIsT0FBTyxJQUFJLE1BQU0sV0FBVyxDQUFDO0FBQzdCLE9BQU8sSUFBSSxNQUFNLE1BQU0sQ0FBQztBQUN4QixPQUFPLEVBQUUsSUFBSSxFQUFFLE1BQU0sUUFBUSxDQUFDO0FBQzlCLHVFQUF1RTtBQUN2RSxNQUFNLEtBQUssR0FBRyxPQUFPLENBQUMsYUFBYSxDQUFDLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDO0FBRW5ELE1BQU0sQ0FBQyxLQUFLLFVBQVUsS0FBSyxDQUFDLEVBQVU7SUFDcEMsT0FBTyxJQUFJLE9BQU8sQ0FBQyxDQUFDLE9BQU8sRUFBRSxFQUFFLENBQUMsVUFBVSxDQUFDLE9BQU8sRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDO0FBQzNELENBQUM7QUFFRDs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBMENHO0FBQ0gsTUFBTSxPQUFPLE9BQU87SUFD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
|