agentic-api 1.0.1

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.
@@ -0,0 +1,288 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.slug = exports.openaiInstance = void 0;
4
+ exports.injectTransferTools = injectTransferTools;
5
+ exports.handleTransferCall = handleTransferCall;
6
+ exports.callForSystemReview = callForSystemReview;
7
+ const prompts_1 = require("./prompts");
8
+ const system_1 = require("./agents/system");
9
+ const princing_openai_1 = require("./princing.openai");
10
+ let _openaiInstance;
11
+ const openaiInstance = function (openai) {
12
+ if (openai) {
13
+ _openaiInstance = openai;
14
+ }
15
+ if (!_openaiInstance) {
16
+ throw new Error('OpenAI instance has not been initialized');
17
+ }
18
+ return _openaiInstance;
19
+ };
20
+ exports.openaiInstance = openaiInstance;
21
+ const slug = (text) => {
22
+ return text.toLowerCase().replace(/ /g, '-').replace(/[^a-z0-9àâçéèêëîïôûù-]+/g, '').replace(/-{2,}/g, '-');
23
+ };
24
+ exports.slug = slug;
25
+ /**
26
+ * This defines and adds "transferAgents" tool dynamically based on the specified downstreamAgents on each agent.
27
+ */
28
+ function injectTransferTools(agentDefs) {
29
+ // Iterate over each agent definition
30
+ agentDefs.forEach((agentDef) => {
31
+ if (agentDef.ready) {
32
+ return;
33
+ }
34
+ const downstreamAgents = agentDef.downstreamAgents || [];
35
+ //
36
+ // Add the think tool if maxSteps is set
37
+ // https://www.aihero.dev/implementing-anthropics-think-tool-in-typescript
38
+ if (agentDef.maxSteps && agentDef.maxSteps > 0) {
39
+ const description = `
40
+ Use the tool as a helper to think about something with a plan of multiple steps.
41
+ It will not obtain new information or change the database, but just append the thought to the log.
42
+ Use it when complex reasoning or some cache memory is needed.
43
+ To avoid infinite thinking, the tool is limited to ${agentDef.maxSteps} calls.
44
+
45
+ It takes two arguments:
46
+ - thought (str): A thought to think about.
47
+ - step (int): The incremented step number that constrains the global thinking with a limitation of ${agentDef.maxSteps} calls.
48
+ `;
49
+ const think = {
50
+ type: "function",
51
+ function: {
52
+ name: "thinking",
53
+ description,
54
+ strict: true,
55
+ parameters: {
56
+ type: "object",
57
+ properties: {
58
+ justification: {
59
+ type: "string",
60
+ description: "The reasoning why this thinking is needed.",
61
+ },
62
+ thought: {
63
+ type: "string",
64
+ description: "A thought to think about.",
65
+ },
66
+ step: {
67
+ type: "number",
68
+ description: "The incremented step number that constrains the global thinking.",
69
+ },
70
+ },
71
+ required: ["justification", "thought", "step"],
72
+ additionalProperties: false,
73
+ },
74
+ }
75
+ };
76
+ // Ensure the agent has a tools array
77
+ if (!agentDef.tools) {
78
+ agentDef.tools = [];
79
+ }
80
+ console.log('--- DBG addTools', agentDef.name, 'with think step', agentDef.maxSteps);
81
+ // Add the newly created tool to the current agent's tools
82
+ agentDef.tools.push(think);
83
+ }
84
+ // Only proceed if there are downstream agents
85
+ if (downstreamAgents.length > 0) {
86
+ // Build a list of downstream agents and their descriptions for the prompt
87
+ const availableAgentsList = downstreamAgents
88
+ .map((dAgent) => `- ${dAgent.name}: ${dAgent.publicDescription ?? "No description"} ${dAgent.human ? '(human agent, less specialized)' : ''}`)
89
+ .join("\n");
90
+ // Create the transfer_agent tool specific to this agent
91
+ const transferAgentTool = {
92
+ type: "function",
93
+ function: {
94
+ name: "transferAgents",
95
+ description: `${prompts_1.transferAgentPrompt.replace('__AGENT_NAME__', agentDef.name)}
96
+ ${availableAgentsList}
97
+ `,
98
+ parameters: {
99
+ type: "object",
100
+ properties: {
101
+ rationale_for_transfer: {
102
+ type: "string",
103
+ description: "The reasoning why this transfer is needed.",
104
+ },
105
+ conversation_context: {
106
+ type: "string",
107
+ description: "Relevant context from the conversation that will help the recipient perform the correct action.",
108
+ },
109
+ destination_agent: {
110
+ type: "string",
111
+ description: "The more specialized destination_agent that should handle the user’s intended request.",
112
+ enum: downstreamAgents.map((dAgent) => dAgent.name),
113
+ },
114
+ confidence: {
115
+ type: "number",
116
+ description: "The confidence that the transfer to a more appropriate agent is needed."
117
+ },
118
+ },
119
+ required: [
120
+ "rationale_for_transfer",
121
+ "conversation_context",
122
+ "destination_agent",
123
+ "confidence",
124
+ ],
125
+ additionalProperties: false,
126
+ },
127
+ strict: true,
128
+ },
129
+ };
130
+ // Ensure the agent has a tools array
131
+ if (!agentDef.tools) {
132
+ agentDef.tools = [];
133
+ }
134
+ // Add the newly created tool to the current agent's tools
135
+ agentDef.tools.push(transferAgentTool);
136
+ // console.log('--- DBG injectTransferTools',agentDef.name,'with new transfer',JSON.stringify(transferAgentTool.function.parameters,null,2 ));
137
+ }
138
+ // so .stringify doesn't break with circular dependencies
139
+ agentDef.downstreamAgents = agentDef.downstreamAgents?.map(({ name, publicDescription, human }) => ({
140
+ name,
141
+ publicDescription,
142
+ human
143
+ }));
144
+ agentDef.ready = true;
145
+ });
146
+ return agentDefs;
147
+ }
148
+ //
149
+ // Fonction pour gérer un appel de fonction retourné par OpenAI
150
+ // le contexte est un objet qui contient des informations supplémentaires de session
151
+ async function handleTransferCall(memory, functionCallParams, session) {
152
+ const { agents } = memory;
153
+ const currentAgentName = memory.currentAgent?.name;
154
+ const args = JSON.parse(functionCallParams?.function?.arguments || '{}');
155
+ // console.log("↩️ Appel de fonction à gérer :",currentAgent?.name,'->', functionCallParams.function.name,'::',args);
156
+ if (functionCallParams.function.name === "transferAgents") {
157
+ if (args.confidence < 0.7) {
158
+ return {
159
+ content: "Le transfert n'est pas nécessaire, tu DOIS répondre directement",
160
+ feedback: args.rationale_for_transfer,
161
+ did_transfer: false,
162
+ name: functionCallParams.function.name
163
+ };
164
+ }
165
+ const destinationAgentName = args.destination_agent;
166
+ console.log(`🤖 Transfert de l'agent "${currentAgentName}" vers "${destinationAgentName}" (${args?.confidence})`, '::', args?.rationale_for_transfer);
167
+ // For debug, we don't want to transfer to the same agent
168
+ if (currentAgentName === destinationAgentName) {
169
+ console.log("❌ Le transfert sur moi-même n'est pas nécessaire", destinationAgentName, args);
170
+ memory.messages.slice(1).forEach(m => {
171
+ process.stdout.write(`-- DBG ---> 🤖 ${m.role}: ${m.content}\n`);
172
+ });
173
+ }
174
+ //
175
+ // agent transfer to the destination agent
176
+ const destinationAgent = agents.find((agent) => agent.name === destinationAgentName);
177
+ if (destinationAgent) {
178
+ //console.log(`🤖 Agent "${destinationAgent.name}" en préparation`);
179
+ memory.currentAgent = destinationAgent;
180
+ return {
181
+ feedback: args.rationale_for_transfer,
182
+ destination_agent: destinationAgentName,
183
+ source_agent: currentAgentName,
184
+ did_transfer: true,
185
+ name: functionCallParams.function.name
186
+ };
187
+ }
188
+ else {
189
+ console.log("❌ Agent non trouvé !");
190
+ return {
191
+ feedback: "L'agent destination n'a pas été trouvé.",
192
+ did_transfer: false,
193
+ name: functionCallParams.function.name
194
+ };
195
+ }
196
+ }
197
+ if (functionCallParams.function.name === "thinking") {
198
+ const { justification, thought, step } = args;
199
+ console.log('🤖', memory.currentAgent?.name, '->', functionCallParams.function.name, '::', thought, '(step', step, ')');
200
+ return {
201
+ feedback: justification,
202
+ content: `Pour l'étape ${step}, j'ai pensé à "${thought}"`,
203
+ name: functionCallParams.function.name,
204
+ usage: 0
205
+ };
206
+ }
207
+ //
208
+ // looking on agents tool logic
209
+ const agent = memory.currentAgent;
210
+ if (agent?.toolLogic && agent.toolLogic[functionCallParams.function.name]) {
211
+ const { content, usage } = await agent.toolLogic[functionCallParams.function.name](args, session);
212
+ console.log('🤖', agent.name, '->', functionCallParams.function.name, '::usage', usage);
213
+ return {
214
+ content,
215
+ name: functionCallParams.function.name,
216
+ usage
217
+ };
218
+ }
219
+ else {
220
+ // whit thinking, we hask the agent to restart the conversation
221
+ // console.log('🤖',agent.name,'->',agent.tools[0]);
222
+ console.log("❌ Cette fonction n'existe pas !", agent.name, functionCallParams.function.name);
223
+ return {
224
+ feedback: `Humm, cette fonction "${functionCallParams.function.name}" n'existe pas!`,
225
+ name: functionCallParams.function.name,
226
+ content: `Cette fonction "${functionCallParams.function.name}" n'existe pas, tu ne dois plus l'appeler et TU DOIS répondre à la question.`
227
+ };
228
+ }
229
+ }
230
+ /**
231
+ * Calls the system review agent to analyze a prompt
232
+ *
233
+ * This function sends a prompt to the system review agent and returns its analysis.
234
+ * The system review agent evaluates prompts based on various criteria like clarity,
235
+ * identity, scope, decision logic, etc.
236
+ *
237
+ * @param {string} prompt - The prompt text to be analyzed by the system review agent
238
+ * @param {Object} params - Parameters for the API call
239
+ * @param {Object} params.openai - The OpenAI client instance
240
+ * @param {Object} params.defaultOptions - Default options to be merged with agent model options
241
+ * @returns {Promise<string>} The analysis response from the system review agent
242
+ */
243
+ async function callForSystemReview(prompt, params) {
244
+ const { openai, defaultOptions } = params;
245
+ const agent = system_1.system;
246
+ const options = Object.assign({}, agent.model, defaultOptions || {});
247
+ const jsonOptions = `
248
+ - L'option JSON a été activée, tu dois respecter le format brut suivant \`
249
+ {
250
+ directives: [
251
+ {"directive": "...","criteria": [{"criterion": "...","score": "...","comment": "..."}]},
252
+ ...
253
+ ]
254
+ }
255
+ \``;
256
+ //
257
+ // add the initial agent to his memory
258
+ // Handle two-shot prompting: if instructions is an array, use the first part as a system message
259
+ const instructions = agent.instructions;
260
+ options.messages = [
261
+ { role: "system", content: instructions }
262
+ ];
263
+ if (options.response_format?.type == "json_object") {
264
+ options.messages.push({ role: "user", content: jsonOptions });
265
+ }
266
+ options.messages.push({ role: "user", content: `tu dois analyser toutes les directives:\n ${prompt}` });
267
+ let thinking = 1;
268
+ //
269
+ // loop until the agent has finished thinking
270
+ do {
271
+ const response = await openai.chat.completions.create(options);
272
+ const cost = (0, princing_openai_1.calculateCost)(options.model, response.usage);
273
+ if (options.response_format?.type == "json_object") {
274
+ const content = JSON.parse(response.choices[0].message.content || '{}');
275
+ return { content, cost };
276
+ }
277
+ // Handle two-shot prompting: if instructions is an array, send the second part as a user message
278
+ // This allows for more complex agent behavior by providing additional context or instructions
279
+ // after the initial response, similar to chain-of-thought prompting
280
+ if (!Array.isArray(agent.instructions) || thinking >= agent.instructions.length) {
281
+ const content = response.choices[0].message.content;
282
+ return { content, cost };
283
+ }
284
+ const instructions = agent.instructions[thinking];
285
+ options.messages.push({ role: "user", content: instructions });
286
+ thinking++;
287
+ } while (true);
288
+ }
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "agentic-api",
3
+ "version": "1.0.1",
4
+ "description": "API pour l'orchestration d'agents intelligents avec séquences et escalades automatiques",
5
+ "main": "dist/src/index.js",
6
+ "types": "dist/src/index.d.ts",
7
+ "files": [
8
+ "dist/src"
9
+ ],
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "test": "jest",
13
+ "lint": "eslint 'src/**/*.ts'",
14
+ "prepublishOnly": "npm run build",
15
+ "extract:pdf": "texttopdf "
16
+ },
17
+ "optionalDependencies": {
18
+ "python3": ">=3.5"
19
+ },
20
+ "dependencies": {
21
+ "@mozilla/readability": "^0.6.0",
22
+ "dotenv": "^16.4.7",
23
+ "hnswlib-node": "^3.0.0",
24
+ "jsdom": "^26.0.0",
25
+ "openai": "^4.89.0"
26
+ },
27
+ "devDependencies": {
28
+ "@types/jest": "^29.5.14",
29
+ "@types/jsdom": "^21.1.7",
30
+ "@types/node": "^22.13.2",
31
+ "eslint": "^8.56.0",
32
+ "jest": "^29.7.0",
33
+ "ts-jest": "^29.2.5",
34
+ "typescript": "^5.7.3"
35
+ },
36
+ "keywords": [
37
+ "agent",
38
+ "llm",
39
+ "openai",
40
+ "rag",
41
+ "ai"
42
+ ],
43
+ "author": "Olivier Evalet",
44
+ "license": "MIT"
45
+ }