Class: Raif::Agent

Direct Known Subclasses

Raif::Agents::NativeToolCallingAgent

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Concerns::RunWith

deserialize_run_with_value, gid_string?, locate_gid, serialize_run_with_value

Methods included from Concerns::AgentInferenceStats

#total_completion_tokens, #total_cost, #total_output_token_cost, #total_prompt_token_cost, #total_prompt_tokens, #total_tokens_sum

Methods included from Concerns::HasRuntimeDuration

#runtime_duration, #runtime_duration_seconds, #runtime_ended_at

Methods included from Concerns::HasAvailableModelTools

#available_model_tools_map

Methods included from Concerns::HasRequestedLanguage

#requested_language_name, #system_prompt_language_preference

Methods included from Concerns::HasLlm

#default_llm_model_key, #llm

Methods included from Concerns::HasPromptTemplates

#build_prompt

Methods inherited from ApplicationRecord

table_name_prefix, where_json_not_blank

Instance Attribute Details

#on_conversation_history_entryObject

Returns the value of attribute on_conversation_history_entry.



73
74
75
# File 'app/models/raif/agent.rb', line 73

def on_conversation_history_entry
  @on_conversation_history_entry
end

Instance Method Details

#final_iteration?Boolean

Returns:

  • (Boolean)


159
160
161
# File 'app/models/raif/agent.rb', line 159

def final_iteration?
  iteration_count == max_iterations
end

#model_tool_invocation_countsObject

Per-tool invocation counts for the admin agent show page, keyed by tool class name (e.g. "Raif::ModelTools::ProviderManaged::WebSearch") so the _list partial can look up counts[tool_class.name].

Combines two sources:

  • Developer-managed tools persist as Raif::ModelToolInvocation rows via Raif::ModelTool.invoke_tool.
  • Provider-managed tools (e.g. Anthropic / OpenAI web_search) never reach that path - they live as server_tool_use / web_search_call blocks in each completion's response_array and are surfaced via ModelCompletion#provider_managed_tool_calls.


174
175
176
177
178
179
180
181
182
183
184
185
186
187
# File 'app/models/raif/agent.rb', line 174

def model_tool_invocation_counts
  counts = raif_model_tool_invocations.group(:tool_type).count

  raif_model_completions.find_each do |completion|
    Array(completion.provider_managed_tool_calls).each do |call|
      tool_klass = completion.available_model_tools_map[call["tool_name"]]
      next unless tool_klass

      counts[tool_klass.name] = (counts[tool_klass.name] || 0) + 1
    end
  end

  counts
end

#run!(&block) ⇒ Raif::Agent

Runs the agent and returns a Raif::Agent. If a block is given, it will be called each time a new entry is added to the agent's conversation history. The block will receive the Raif::Agent and the new entry as arguments: agent = Raif::Agent.new( task: task, tools: [Raif::ModelTools::WikipediaSearch, Raif::ModelTools::FetchUrl], creator: creator )

agent.run! do |conversation_history_entry| Turbo::StreamsChannel.broadcast_append_to( :my_agent_channel, target: "agent-progress", partial: "my_partial_displaying_agent_progress", locals: { agent: agent, conversation_history_entry: conversation_history_entry } ) end

The conversation_history_entry will be a hash with "role" and "content" keys: { "role" => "assistant", "content" => "a message here" }

Parameters:

  • block (Proc)

    Optional block to be called each time a new entry to the agent's conversation history is generated

Returns:



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# File 'app/models/raif/agent.rb', line 98

def run!(&block)
  self.on_conversation_history_entry = block_given? ? block : nil
  self.started_at = Time.current
  save!

  logger.debug <<~DEBUG
    --------------------------------
    Starting Agent Run
    --------------------------------
    System Prompt:
    #{system_prompt}

    Task: #{task}
  DEBUG

  add_conversation_history_entry(Raif::Messages::UserMessage.new(content: task).to_h)

  while iteration_count < max_iterations
    update_columns(iteration_count: iteration_count + 1)

    # Update the system prompt on each iteration in case it has changed since the last iteration
    self.system_prompt = build_system_prompt

    # Hook for subclasses to perform actions before the LLM chat (e.g., add warnings)
    before_iteration_llm_chat

    model_completion = llm.chat(
      messages: conversation_history,
      source: self,
      system_prompt: system_prompt,
      available_model_tools: native_model_tools,
      tool_choice: tool_choice_for_iteration,
      anthropic_prompt_caching_enabled: self.class.anthropic_prompt_caching_enabled,
      bedrock_prompt_caching_enabled: self.class.bedrock_prompt_caching_enabled
    )

    logger.debug <<~DEBUG
      --------------------------------
      Agent iteration #{iteration_count}
      Messages:
      #{JSON.pretty_generate(conversation_history)}

      Response:
      #{model_completion.raw_response}
      --------------------------------
    DEBUG

    process_iteration_model_completion(model_completion)
    break if final_answer.present? || failed?
  end

  finalize_run!
  final_answer
rescue StandardError => e
  self.failed_at ||= Time.current
  self.failure_reason ||= e.message
  save!

  raise
end