Class: PhaseoAgentSdk::Agent

Inherits:
Object
  • Object
show all
Defined in:
lib/phaseo_agent_sdk.rb

Instance Method Summary collapse

Constructor Details

#initialize(definition) ⇒ Agent

Returns a new instance of Agent.



258
259
260
261
262
263
264
# File 'lib/phaseo_agent_sdk.rb', line 258

def initialize(definition)
  @definition = definition
  @definition.tools ||= []
  @definition.max_steps ||= 12
  @definition.stop_when ||= []
  @definition.tools.each { |tool| tool.on_error ||= "fail-run" }
end

Instance Method Details

#continue_run(run: nil, run_id: nil, client:, human_input: nil, context: nil, model: nil, preset: nil, max_steps: nil, model_retry: nil, tool_execution: nil, on_event: nil, devtools: nil, approvals: [], rejections: [], tool_outputs: [], state: nil) ⇒ Object



312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
# File 'lib/phaseo_agent_sdk.rb', line 312

def continue_run(run: nil, run_id: nil, client:, human_input: nil, context: nil, model: nil, preset: nil, max_steps: nil,
                 model_retry: nil, tool_execution: nil, on_event: nil, devtools: nil, approvals: [], rejections: [], tool_outputs: [], state: nil)
  run ||= state&.load(run_id);raise "A run or state accessor with run_id is required" unless run
  raise "Run #{run.run.id} belongs to agent #{run.run.agent_id}" unless run.run.agent_id == @definition.id
  if run.run.status == "waiting_for_human" && human_input.to_s.empty? && Array(run.run.pause&.pending_tool_calls).empty?
    raise "Run #{run.run.id} is waiting for human input"
  end
  started_at = Time.now
  previous_status = run.run.status
  unless human_input.to_s.empty?
    run.run.messages << Message.new(role: "user", content: human_input)
    run.run.pause = nil
  else
    approved=approvals.to_h{|item|[item.is_a?(String)?item:item.tool_call_id,item]}; rejected=rejections.to_h{|item|[item.is_a?(String)?item:item.tool_call_id,item]}; outputs=tool_outputs.to_h{|item|[item.tool_call_id,item.output]}; tools=@definition.tools.to_h{|item|[item.id,item]}
    Array(run.run.pause&.pending_tool_calls).each do |pending|
call=pending.call; tool=tools[call.name]; runtime=RuntimeContext.new(run_id:run.run.id,agent_id:run.run.agent_id,step_index:run.run.step_count-1,context:run.run.context,tool_call:call,set_context:->(value){run.run.context=value})
      if rejected.key?(call.id); reason=rejected[call.id].is_a?(String) ? "Tool call rejected" : (rejected[call.id].reason||"Tool call rejected"); run.run.messages<<Message.new(role:"tool",name:tool.id,tool_call_id:call.id,content:JSON.generate(error:reason),is_error:true); next; end
if pending.kind=="approval"; raise "Missing approval decision for tool call #{call.id}" unless approved.key?(call.id); run.run.messages<<execute_tool(tool,call,run.run,run.run.step_count-1,on_event);run.run.next_turn_params=tool.next_turn_params if tool.next_turn_params;next
      else; raise "Missing output for tool call #{call.id}" unless outputs.key?(call.id); value=outputs[call.id]; value=tool.on_response_received.call(value,runtime) if tool.on_response_received; end
value=validate(tool.output_schema,value,"tool output"); run.run.messages<<Message.new(role:"tool",name:tool.id,tool_call_id:call.id,content:value.is_a?(String)?value:JSON.generate(value));run.run.next_turn_params=tool.next_turn_params if tool.next_turn_params
    end
    run.run.pause=nil
  end
  run.run.status = "running"
  run.run.updated_at = now_iso
  emit(on_event, "run.resumed", run.run, previous_status: previous_status)
  begin
    result = execute(
      run.run, run.steps.dup, client: client, context: context.nil? ? run.run.context : context,
      model: model, preset: preset, max_steps: max_steps, model_retry: model_retry,
      tool_execution: tool_execution, on_event: on_event
    )
    capture_devtools(result, "agent.continue", started_at, devtools)
    state.save(result) if state
    result
  rescue StandardError => e
    capture_devtools(nil, "agent.continue", started_at, devtools, e, run.run.id)
    raise
  end
end

#continue_stream(**options) ⇒ Object



353
354
355
356
357
358
# File 'lib/phaseo_agent_sdk.rb', line 353

def continue_stream(**options)
  StreamResult.new do|emit|
	client=options.fetch(:client);if client.respond_to?(:stream);source=client;client=Class.new do;define_method(:generate) do|request|;text=String.new;completed=nil;source.stream(request).each{|event|text<<event[:delta].to_s if event[:type]=="response.output_text.delta";completed=event[:response] if event[:type]=="response.completed";emit.call(event)};completed||ModelResponse.new(message:Message.new(role:"assistant",content:text));end;end.new;end
	continue_run(**options.merge(client:client,on_event:emit))
  end
end

#run(input:, client:, context: nil, model: nil, preset: nil, max_steps: nil, model_retry: nil, tool_execution: nil, on_event: nil, devtools: nil, state: nil) ⇒ Object



266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
# File 'lib/phaseo_agent_sdk.rb', line 266

def run(input:, client:, context: nil, model: nil, preset: nil, max_steps: nil, model_retry: nil,
        tool_execution: nil, on_event: nil, devtools: nil, state: nil)
  started_at = Time.now
  run_id = SecureRandom.uuid
  created_at = now_iso
  messages = []
  messages << Message.new(role: "system", content: @definition.instructions) if @definition.instructions.is_a?(String) && !@definition.instructions.empty?
  messages << Message.new(role: "user", content: input.is_a?(String) ? input : JSON.pretty_generate(input))
  run_record = RunRecord.new(
    id: run_id, agent_id: @definition.id, status: "queued", input: input, context: context,
    messages: messages, step_count: 0, created_at: created_at, updated_at: created_at,
    usage: { input_tokens: 0, output_tokens: 0, cached_tokens: 0, total_tokens: 0, cost: 0.0 }
  )
  emit(on_event, "run.started", run_record)
  begin
    result = execute(
      run_record, [], client: client, context: context, model: model, preset: preset,
      max_steps: max_steps, model_retry: model_retry, tool_execution: tool_execution,
      on_event: on_event
    )
    capture_devtools(result, "agent.run", started_at, devtools)
    state.save(result) if state
    result
  rescue StandardError => e
    capture_devtools(nil, "agent.run", started_at, devtools, e, run_id)
    raise
  end
end

#stream(input:, client:, context: nil, state: nil) ⇒ Object



295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
# File 'lib/phaseo_agent_sdk.rb', line 295

def stream(input:, client:, context: nil, state: nil)
  StreamResult.new do|emit|
	handler=emit
	if client.respond_to?(:stream)
	  source=client
	  client=Class.new do
		define_method(:generate) do |request|
		  text=String.new;completed=nil
		  source.stream(request).each do |event|;text<<event[:delta].to_s if event[:type]=="response.output_text.delta";completed=event[:response] if event[:type]=="response.completed";handler.call(event);end
		  completed||ModelResponse.new(message:Message.new(role:"assistant",content:text))
		end
	  end.new
	end
	run(input:input,client:client,context:context,on_event:handler,state:state)
  end
end