@@ -5,18 +5,6 @@ const { RunResult } = require('./result');
55 */
66class Runner {
77
8- /**
9- * Run input guardrails
10- * @private
11- */
12-
13-
14- /**
15- * Run output guardrails
16- * @private
17- */
18-
19-
208 /**
219 * Run an agent with the given input
2210 * @param {Agent } startingAgent - The agent to run
@@ -32,66 +20,91 @@ class Runner {
3220 maxTurns = 10 ,
3321 runConfig = { }
3422 } = { } ) {
35- // Initialize state
23+ // Utilitários privados
24+ const prepareTools = agent => [
25+ ...agent . tools . map ( tool => ( {
26+ type : 'function' ,
27+ function : {
28+ name : tool . name ,
29+ description : tool . description ,
30+ parameters : tool . parameters || { type : 'object' , properties : { } }
31+ }
32+ } ) ) ,
33+ ...agent . handoffs . map ( handoff => ( {
34+ type : 'function' ,
35+ function : {
36+ name : `transfer_to_${ handoff . name . replace ( / [ ^ a - z A - Z 0 - 9 _ - ] / g, '_' ) } ` ,
37+ description : handoff . handoffDescription || `Transfer to the ${ handoff . name } agent` ,
38+ parameters : { type : 'object' , properties : { } }
39+ }
40+ } ) )
41+ ] ;
42+
43+ const runGuardrails = async ( type , agent , data , context ) => {
44+ const results = [ ] ;
45+ for ( const guardrail of agent ?. guardrails ?. [ type ] || [ ] ) {
46+ const result = await guardrail . run ( agent , data , context ) ;
47+ if ( result ) results . push ( guardrail . name ) ;
48+ }
49+ return results ;
50+ } ;
51+
52+ const processToolCalls = async ( agent , assistantMessage , messages , context ) => {
53+ for ( const toolCall of assistantMessage . tool_calls ) {
54+ // O id pode estar em toolCall.id ou toolCall.function.id dependendo do formato
55+ const id = toolCall . id || ( toolCall . function && toolCall . function . id ) ;
56+ const { name, arguments : args } = toolCall . function ;
57+ if ( name . startsWith ( 'transfer_to_' ) ) {
58+ const handoffAgentName = name . replace ( 'transfer_to_' , '' ) ;
59+ const handoffAgent = agent . handoffs . find ( h => h . name . replace ( / [ ^ a - z A - Z 0 - 9 _ - ] / g, '_' ) === handoffAgentName ) ;
60+ if ( handoffAgent ) {
61+ messages . push ( { role : 'tool' , tool_call_id : id , content : `Handoff to ${ handoffAgent . name } successful` } ) ;
62+ return { handoffAgent, messages } ;
63+ }
64+ } else {
65+ const tool = agent . tools . find ( t => t . name === name ) ;
66+ if ( tool ) {
67+ try {
68+ const result = await tool . execute ( JSON . parse ( args ) ) ;
69+ messages . push ( { role : 'tool' , tool_call_id : id , content : typeof result === 'string' ? result : JSON . stringify ( result ) } ) ;
70+ } catch ( error ) {
71+ messages . push ( { role : 'tool' , tool_call_id : id , content : `Error: ${ error . message } ` } ) ;
72+ }
73+ } else {
74+ // Caso não encontre a ferramenta, ainda assim envie uma mensagem de erro com tool_call_id
75+ messages . push ( { role : 'tool' , tool_call_id : id , content : `Error: tool '${ name } ' not found` } ) ;
76+ }
77+ }
78+ }
79+ return { handoffAgent : null , messages } ;
80+ } ;
81+
82+ // Estado inicial
3683 let currentTurn = 0 ;
3784 let currentAgent = startingAgent ;
38- let messages = [ ] ;
85+ let messages = typeof input === 'string' ? [ { role : 'user' , content : input } ] : [ ... input ] ;
3986 let finalOutput = null ;
40- const inputGuardrailResults = [ ] ;
41- const outputGuardrailResults = [ ] ;
87+ let inputGuardrailResults = [ ] ;
88+ let outputGuardrailResults = [ ] ;
4289
43- // Validate provider configuration
90+ // Validação
4491 currentAgent . llmProvider . validateConfig ( ) ;
4592
46- // Add the initial user message
47- if ( typeof input === 'string' ) {
48- messages . push ( { role : 'user' , content : input } ) ;
49- } else {
50- messages = [ ...input ] ;
51- }
93+ // Guardrails de entrada
94+ inputGuardrailResults = await runGuardrails ( 'input' , currentAgent , messages , context ) ;
5295
53- // Run input guardrails
54- for ( const guardrail of currentAgent ?. guardrails ?. input || [ ] ) {
55- const result = await guardrail . run ( currentAgent , messages , context ) ;
56- if ( result ) {
57- inputGuardrailResults . push ( guardrail . name ) ;
58- }
59- }
60-
61- // Main agent loop
96+ // Loop principal
6297 while ( currentTurn < maxTurns ) {
6398 currentTurn ++ ;
6499 console . log ( `Running agent ${ currentAgent . name } (turn ${ currentTurn } )` ) ;
65100
66- // Prepare tools for the model
67- const tools = [
68- ...currentAgent . tools . map ( tool => ( {
69- type : 'function' ,
70- function : {
71- name : tool . name ,
72- description : tool . description ,
73- parameters : tool . parameters || { type : 'object' , properties : { } }
74- }
75- } ) ) ,
76- ...currentAgent . handoffs . map ( handoff => ( {
77- type : 'function' ,
78- function : {
79- name : `transfer_to_${ handoff . name . replace ( / [ ^ a - z A - Z 0 - 9 _ - ] / g, '_' ) } ` ,
80- description : handoff . handoffDescription || `Transfer to the ${ handoff . name } agent` ,
81- parameters : { type : 'object' , properties : { } }
82- }
83- } ) )
84- ] ;
85-
86- // Add system message if not already present
101+ // System message
87102 if ( ! messages . some ( msg => msg . role === 'system' ) ) {
88- messages . unshift ( {
89- role : 'system' ,
90- content : currentAgent . instructions
91- } ) ;
103+ messages . unshift ( { role : 'system' , content : currentAgent . instructions } ) ;
92104 }
93105
94- // Call the model using the provider
106+ // Chamada ao modelo
107+ const tools = prepareTools ( currentAgent ) ;
95108 const response = await currentAgent . llmProvider . complete (
96109 messages ,
97110 {
@@ -100,98 +113,40 @@ class Runner {
100113 ...currentAgent . modelSettings
101114 }
102115 ) ;
103-
104116 const assistantMessage = response ;
105117 messages . push ( assistantMessage ) ;
106118
107- // Check for tool calls
119+ // Tool calls
108120 if ( assistantMessage . tool_calls && assistantMessage . tool_calls . length > 0 ) {
109-
110- // console.log("Tool calls detected:", assistantMessage.tool_calls);
111-
112- for ( const toolCall of assistantMessage . tool_calls ) {
113- const { name, arguments : args } = toolCall . function ;
114-
115- // Check if it's a handoff
116- if ( name . startsWith ( 'transfer_to_' ) ) {
117- const handoffAgentName = name . replace ( 'transfer_to_' , '' ) ;
118- const handoffAgent = currentAgent . handoffs . find ( h =>
119- h . name . replace ( / [ ^ a - z A - Z 0 - 9 _ - ] / g, '_' ) === handoffAgentName ) ;
120-
121- if ( handoffAgent ) {
122- // Add a tool response message for the handoff
123- messages . push ( {
124- role : 'tool' ,
125- tool_call_id : toolCall . id ,
126- content : `Handoff to ${ handoffAgent . name } successful`
127- } ) ;
128-
129- // Perform handoff
130- currentAgent = handoffAgent ;
131-
132- // Reset messages for the new agent, keeping only the user's input
133- const userMessages = messages . filter ( msg => msg . role === 'user' ) ;
134- messages = [
135- { role : 'system' , content : handoffAgent . instructions } ,
136- ...userMessages
137- ] ;
138-
139- // Get immediate response from new agent
140- const handoffResponse = await handoffAgent . llmProvider . complete (
141- messages ,
142- handoffAgent . modelSettings
143- ) ;
144-
145- // Set final output and add response to messages
146- finalOutput = handoffResponse . content ;
147- messages . push ( handoffResponse ) ;
148-
149- // Exit the main loop since we have a final response
150- currentTurn = maxTurns ;
151- break ;
152- }
153- } else {
154- // Regular tool call
155- const tool = currentAgent . tools . find ( t => t . name === name ) ;
156- if ( tool ) {
157- try {
158- const result = await tool . execute ( JSON . parse ( args ) ) ;
159- messages . push ( {
160- role : 'tool' ,
161- tool_call_id : toolCall . id ,
162- content : typeof result === 'string' ? result : JSON . stringify ( result )
163- } ) ;
164- } catch ( error ) {
165- messages . push ( {
166- role : 'tool' ,
167- tool_call_id : toolCall . id ,
168- content : `Error: ${ error . message } `
169- } ) ;
170- }
171- }
172- }
121+ const { handoffAgent, messages : updatedMessages } = await processToolCalls ( currentAgent , assistantMessage , messages , context ) ;
122+ messages = updatedMessages ;
123+ if ( handoffAgent ) {
124+ currentAgent = handoffAgent ;
125+ const userMessages = messages . filter ( msg => msg . role === 'user' ) ;
126+ messages = [
127+ { role : 'system' , content : handoffAgent . instructions } ,
128+ ...userMessages
129+ ] ;
130+ const handoffResponse = await handoffAgent . llmProvider . complete (
131+ messages ,
132+ handoffAgent . modelSettings
133+ ) ;
134+ finalOutput = handoffResponse . content ;
135+ messages . push ( handoffResponse ) ;
136+ break ;
173137 }
174138 } else {
175- // No tool calls, this is a final output
176139 finalOutput = assistantMessage . content ;
177-
178- // If we have a final output, we're done
179140 break ;
180141 }
181142 }
182143
183- // Check if we exceeded max turns
184144 if ( currentTurn >= maxTurns && ! finalOutput ) {
185145 throw new Error ( `Max turns (${ maxTurns } ) exceeded` ) ;
186146 }
187147
188- // Run output guardrails
189- for ( const guardrail of currentAgent ?. guardrails ?. output || [ ] ) {
190- const result = await guardrail . run ( currentAgent , finalOutput , context ) ;
191- if ( result ) {
192- outputGuardrailResults . push ( guardrail . name ) ;
193- }
194- }
148+ // Guardrails de saída
149+ outputGuardrailResults = await runGuardrails ( 'output' , currentAgent , finalOutput , context ) ;
195150
196151 const result = new RunResult ( {
197152 input,
@@ -203,13 +158,11 @@ class Runner {
203158 output : outputGuardrailResults
204159 }
205160 } ) ;
206- // setImmediate(() => process.exit(0));
207161 return result ;
208-
209162 }
210163
211164 /**
212- * Run an agent synchronously (for environments that don't support async/await)
165+ * Run an agent sincronamente (para ambientes sem suporte a async/await)
213166 */
214167 static runSync ( startingAgent , input , options = { } ) {
215168 return Runner . run ( startingAgent , input , options ) ;
0 commit comments