raif 1.0.0 → 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.
Files changed (86) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +200 -41
  3. data/app/assets/stylesheets/raif/admin/stats.scss +12 -0
  4. data/app/controllers/raif/admin/application_controller.rb +14 -0
  5. data/app/controllers/raif/admin/stats/tasks_controller.rb +25 -0
  6. data/app/controllers/raif/admin/stats_controller.rb +19 -0
  7. data/app/controllers/raif/admin/tasks_controller.rb +18 -2
  8. data/app/controllers/raif/conversations_controller.rb +5 -1
  9. data/app/models/raif/agent.rb +11 -9
  10. data/app/models/raif/agents/native_tool_calling_agent.rb +11 -1
  11. data/app/models/raif/agents/re_act_agent.rb +6 -0
  12. data/app/models/raif/concerns/has_available_model_tools.rb +1 -1
  13. data/app/models/raif/concerns/json_schema_definition.rb +28 -0
  14. data/app/models/raif/concerns/llm_response_parsing.rb +23 -1
  15. data/app/models/raif/concerns/llm_temperature.rb +17 -0
  16. data/app/models/raif/concerns/llms/anthropic/message_formatting.rb +51 -0
  17. data/app/models/raif/concerns/llms/bedrock_claude/message_formatting.rb +70 -0
  18. data/app/models/raif/concerns/llms/message_formatting.rb +41 -0
  19. data/app/models/raif/concerns/llms/open_ai/message_formatting.rb +41 -0
  20. data/app/models/raif/conversation.rb +11 -3
  21. data/app/models/raif/conversation_entry.rb +22 -6
  22. data/app/models/raif/embedding_model.rb +22 -0
  23. data/app/models/raif/embedding_models/bedrock_titan.rb +34 -0
  24. data/app/models/raif/embedding_models/open_ai.rb +40 -0
  25. data/app/models/raif/llm.rb +39 -6
  26. data/app/models/raif/llms/anthropic.rb +23 -28
  27. data/app/models/raif/llms/bedrock_claude.rb +33 -19
  28. data/app/models/raif/llms/open_ai.rb +14 -17
  29. data/app/models/raif/llms/open_router.rb +93 -0
  30. data/app/models/raif/model_completion.rb +21 -2
  31. data/app/models/raif/model_file_input.rb +113 -0
  32. data/app/models/raif/model_image_input.rb +4 -0
  33. data/app/models/raif/model_tool.rb +77 -51
  34. data/app/models/raif/model_tool_invocation.rb +8 -6
  35. data/app/models/raif/model_tools/agent_final_answer.rb +18 -27
  36. data/app/models/raif/model_tools/fetch_url.rb +27 -36
  37. data/app/models/raif/model_tools/wikipedia_search.rb +46 -55
  38. data/app/models/raif/task.rb +71 -16
  39. data/app/views/layouts/raif/admin.html.erb +10 -0
  40. data/app/views/raif/admin/agents/show.html.erb +3 -1
  41. data/app/views/raif/admin/conversations/_conversation.html.erb +1 -1
  42. data/app/views/raif/admin/conversations/show.html.erb +3 -1
  43. data/app/views/raif/admin/model_completions/_model_completion.html.erb +1 -0
  44. data/app/views/raif/admin/model_completions/index.html.erb +1 -0
  45. data/app/views/raif/admin/model_completions/show.html.erb +30 -3
  46. data/app/views/raif/admin/stats/index.html.erb +128 -0
  47. data/app/views/raif/admin/stats/tasks/index.html.erb +45 -0
  48. data/app/views/raif/admin/tasks/_task.html.erb +5 -4
  49. data/app/views/raif/admin/tasks/index.html.erb +20 -2
  50. data/app/views/raif/admin/tasks/show.html.erb +3 -1
  51. data/app/views/raif/conversation_entries/_conversation_entry.html.erb +18 -14
  52. data/app/views/raif/conversation_entries/_form.html.erb +1 -1
  53. data/app/views/raif/conversation_entries/_form_with_available_tools.html.erb +4 -4
  54. data/app/views/raif/conversation_entries/_message.html.erb +10 -3
  55. data/config/locales/admin.en.yml +14 -0
  56. data/config/locales/en.yml +25 -3
  57. data/config/routes.rb +6 -0
  58. data/db/migrate/20250421202149_add_response_format_to_raif_conversations.rb +7 -0
  59. data/db/migrate/20250424200755_add_cost_columns_to_raif_model_completions.rb +14 -0
  60. data/db/migrate/20250424232946_add_created_at_indexes.rb +11 -0
  61. data/db/migrate/20250502155330_add_status_indexes_to_raif_tasks.rb +14 -0
  62. data/db/migrate/20250507155314_add_retry_count_to_raif_model_completions.rb +7 -0
  63. data/lib/generators/raif/agent/agent_generator.rb +22 -12
  64. data/lib/generators/raif/agent/templates/agent.rb.tt +3 -3
  65. data/lib/generators/raif/agent/templates/application_agent.rb.tt +7 -0
  66. data/lib/generators/raif/conversation/conversation_generator.rb +10 -0
  67. data/lib/generators/raif/conversation/templates/application_conversation.rb.tt +7 -0
  68. data/lib/generators/raif/conversation/templates/conversation.rb.tt +13 -11
  69. data/lib/generators/raif/install/templates/initializer.rb +50 -6
  70. data/lib/generators/raif/model_tool/model_tool_generator.rb +0 -5
  71. data/lib/generators/raif/model_tool/templates/model_tool.rb.tt +69 -56
  72. data/lib/generators/raif/task/templates/task.rb.tt +34 -23
  73. data/lib/raif/configuration.rb +40 -3
  74. data/lib/raif/embedding_model_registry.rb +83 -0
  75. data/lib/raif/engine.rb +34 -1
  76. data/lib/raif/errors/{open_ai/api_error.rb → invalid_model_file_input_error.rb} +1 -3
  77. data/lib/raif/errors/{anthropic/api_error.rb → invalid_model_image_input_error.rb} +1 -3
  78. data/lib/raif/errors/unsupported_feature_error.rb +8 -0
  79. data/lib/raif/errors.rb +3 -2
  80. data/lib/raif/json_schema_builder.rb +104 -0
  81. data/lib/raif/llm_registry.rb +205 -0
  82. data/lib/raif/version.rb +1 -1
  83. data/lib/raif.rb +5 -32
  84. data/lib/tasks/raif_tasks.rake +9 -4
  85. metadata +32 -19
  86. data/lib/raif/default_llms.rb +0 -37
@@ -0,0 +1,45 @@
1
+ <%= link_to raif.admin_stats_path do %>
2
+ &laquo; <%= t("raif.admin.stats.tasks.back_to_stats") %>
3
+ <% end %>
4
+
5
+ <div class="d-flex justify-content-between align-items-center my-4">
6
+ <h1 class="mb-0"><%= t("raif.admin.stats.tasks.title") %></h1>
7
+
8
+ <div class="period-filter">
9
+ <%= form_tag raif.admin_stats_tasks_path, method: :get, id: "period_filter_form", class: "d-flex align-items-center" do %>
10
+ <%= select_tag :period,
11
+ options_for_select(
12
+ [
13
+ [t("raif.admin.common.period_day"), "day"],
14
+ [t("raif.admin.common.period_week"), "week"],
15
+ [t("raif.admin.common.period_month"), "month"],
16
+ [t("raif.admin.common.period_all"), "all"]
17
+ ],
18
+ @selected_period
19
+ ),
20
+ class: "form-select form-select-sm me-2" %>
21
+ <%= submit_tag t("raif.admin.common.update"), class: "btn btn-sm btn-primary" %>
22
+ <% end %>
23
+ </div>
24
+ </div>
25
+
26
+ <div class="table-responsive">
27
+ <table class="table table-striped table-hover">
28
+ <thead class="table-light">
29
+ <tr>
30
+ <th><%= t("raif.admin.common.type") %></th>
31
+ <th><%= t("raif.admin.common.count") %></th>
32
+ <th><%= t("raif.admin.common.est_cost") %></th>
33
+ </tr>
34
+ </thead>
35
+ <tbody>
36
+ <% @task_counts_by_type.each do |type, count| %>
37
+ <tr>
38
+ <td><%= type %></td>
39
+ <td><%= number_with_delimiter(count) %></td>
40
+ <td><%= number_to_currency(@task_costs_by_type[type] || 0, precision: 6) %></td>
41
+ </tr>
42
+ <% end %>
43
+ </tbody>
44
+ </table>
45
+ </div>
@@ -2,14 +2,15 @@
2
2
  <td><%= link_to "##{task.id}", raif.admin_task_path(task) %></td>
3
3
  <td><%= task.type %></td>
4
4
  <td><small class="text-muted"><%= task.created_at.rfc822 %></small></td>
5
- <td><%= task.creator_type %> #<%= task.creator_id %></td>
5
+ <td><%= task.creator.try(:raif_display_name) || "#{task.creator_type} ##{task.creator_id}" %></td>
6
6
  <td><%= task.llm_model_key %></td>
7
7
  <td>
8
- <% if task.completed_at? %>
8
+ <% case task.status %>
9
+ <% when :completed %>
9
10
  <span class="badge bg-success"><%= t("raif.admin.common.completed") %></span>
10
- <% elsif task.failed_at? %>
11
+ <% when :failed %>
11
12
  <span class="badge bg-danger"><%= t("raif.admin.common.failed") %></span>
12
- <% elsif task.started_at? %>
13
+ <% when :in_progress %>
13
14
  <span class="badge bg-warning text-dark"><%= t("raif.admin.common.in_progress") %></span>
14
15
  <% else %>
15
16
  <span class="badge bg-secondary"><%= t("raif.admin.common.pending") %></span>
@@ -4,10 +4,28 @@
4
4
  <div class="col-12">
5
5
  <%= form_tag raif.admin_tasks_path, method: :get, class: "mb-4" do %>
6
6
  <div class="row align-items-end">
7
- <div class="col-md-6">
7
+ <div class="col-md-4">
8
8
  <div class="form-group">
9
+ <label for="task_types"><%= t("raif.admin.common.type") %></label>
9
10
  <%= select_tag :task_types,
10
- options_for_select(@task_types.map{|type| [type, type] }, @selected_types),
11
+ options_for_select([["All", "all"]] + @task_types.map{|type| [type, type] }, @selected_type),
12
+ { class: "form-select" } %>
13
+ </div>
14
+ </div>
15
+ <div class="col-md-4">
16
+ <div class="form-group">
17
+ <label for="task_statuses"><%= t("raif.admin.common.status") %></label>
18
+ <%= select_tag :task_statuses,
19
+ options_for_select(
20
+ [
21
+ [t("raif.admin.common.all"), :all],
22
+ [t("raif.admin.common.completed"), :completed],
23
+ [t("raif.admin.common.failed"), :failed],
24
+ [t("raif.admin.common.in_progress"), :in_progress],
25
+ [t("raif.admin.common.pending"), :pending]
26
+ ],
27
+ @selected_statuses
28
+ ),
11
29
  { class: "form-select" } %>
12
30
  </div>
13
31
  </div>
@@ -14,7 +14,9 @@
14
14
  </div>
15
15
  <div class="row mb-3">
16
16
  <div class="col-md-3"><strong><%= t("raif.admin.common.creator") %>:</strong></div>
17
- <div class="col-md-9"><%= @task.creator_type %> #<%= @task.creator_id %></div>
17
+ <div class="col-md-9">
18
+ <%= @task.creator.try(:raif_display_name) || "#{@task.creator_type} ##{@task.creator_id}" %>
19
+ </div>
18
20
  </div>
19
21
  <div class="row mb-3">
20
22
  <div class="col-md-3"><strong><%= t("raif.admin.common.model") %>:</strong></div>
@@ -1,26 +1,30 @@
1
- <div id="<%= dom_id(conversation_entry) %>" class="my-2">
1
+ <div id="<%= dom_id(conversation_entry) %>" class="my-2 conversation-entry">
2
2
  <%= render "raif/conversation_entries/message",
3
3
  conversation_entry: conversation_entry,
4
4
  content: conversation_entry.user_message,
5
- message_type: :user %>
5
+ message_type: :user if conversation_entry.user_message.present? %>
6
6
 
7
- <% if conversation_entry.completed? || conversation_entry.generating_response? %>
8
- <%= render "raif/conversation_entries/message",
9
- conversation_entry: conversation_entry,
10
- content: conversation_entry.generating_response? ? content_tag(:span, "", class: "raif-loader") : conversation_entry.model_response_message,
11
- message_type: :model_response %>
12
-
13
- <% conversation_entry.raif_model_tool_invocations.select(&:renderable?).each do |ti| %>
14
- <div class="mb-4 container">
15
- <%= render ti, conversation_entry: conversation_entry %>
16
- </div>
17
- <% end %>
18
- <% elsif conversation_entry.failed? %>
7
+ <% if conversation_entry.failed? %>
19
8
  <div class="mb-4 container">
20
9
  <%= render "raif/conversation_entries/message",
21
10
  conversation_entry: conversation_entry,
22
11
  content: t("raif.common.there_was_an_error_generating_this_response"),
23
12
  message_type: :model_response %>
24
13
  </div>
14
+ <% elsif conversation_entry.generating_response? %>
15
+ <%= render "raif/conversation_entries/message",
16
+ conversation_entry: conversation_entry,
17
+ content: content_tag(:span, "", class: "raif-loader"),
18
+ message_type: :model_response %>
19
+
20
+ <% elsif conversation_entry.completed? %>
21
+ <%= render "raif/conversation_entries/message",
22
+ conversation_entry: conversation_entry,
23
+ content: conversation_entry.model_response_message,
24
+ message_type: :model_response if conversation_entry.model_response_message.present? %>
25
+
26
+ <% conversation_entry.raif_model_tool_invocations.select(&:renderable?).each do |ti| %>
27
+ <%= render ti, conversation_entry: conversation_entry %>
28
+ <% end %>
25
29
  <% end %>
26
30
  </div>
@@ -10,7 +10,7 @@
10
10
  <% end %>
11
11
  <% end %>
12
12
 
13
- <div class="d-flex">
13
+ <div class="d-flex px-2">
14
14
  <%= f.text_field :user_message,
15
15
  class: "form-control me-2",
16
16
  placeholder: conversation_entry.raif_user_tool_invocation&.message_input_placeholder.presence || t("raif.common.type_your_message"),
@@ -1,4 +1,4 @@
1
- <hr class="mb-2 mx-n5">
2
-
3
- <%= render "raif/conversations/available_user_tools", conversation: conversation %>
4
- <%= render "raif/conversation_entries/form", conversation: conversation, conversation_entry: conversation_entry %>
1
+ <div class="border-top pt-2">
2
+ <%= render "raif/conversations/available_user_tools", conversation: conversation %>
3
+ <%= render "raif/conversation_entries/form", conversation: conversation, conversation_entry: conversation_entry %>
4
+ </div>
@@ -1,12 +1,19 @@
1
- <div class="d-flex mb-2 chat-message">
2
- <div class="d-flex w-100 <%= "justify-content-end" if message_type == :user %>">
1
+ <div class="d-flex my-2 px-3 <%= "justify-content-end" if message_type == :user %>">
2
+ <div class="d-flex <%= "chat-message-content" if message_type == :user %> <%= "received-message-content" if message_type == :model_response %>">
3
3
  <% if message_type == :model_response %>
4
4
  <%= render "raif/conversation_entries/model_response_avatar", conversation_entry: local_assigns[:conversation_entry] %>
5
5
  <% end %>
6
6
 
7
7
  <% if content.present? %>
8
8
  <div class="mb-1 rounded-2 p-3 <%= message_type == :user ? "bg-primary text-white" : "border" %>">
9
- <%= simple_format content %>
9
+ <% case local_assigns[:conversation_entry]&.response_format %>
10
+ <% when "text" %>
11
+ <%= simple_format content %>
12
+ <% when "html" %>
13
+ <%= sanitize content %>
14
+ <% else %>
15
+ <%= content %>
16
+ <% end %>
10
17
  </div>
11
18
  <% end %>
12
19
 
@@ -8,6 +8,7 @@ en:
8
8
  title: 'Agent #%{id}'
9
9
  common:
10
10
  agents: Agents
11
+ all: All
11
12
  arguments: Arguments
12
13
  at: at
13
14
  completed: Completed
@@ -16,11 +17,13 @@ en:
16
17
  conversation_entries: Conversation Entries
17
18
  conversation_history: Conversation History
18
19
  conversations: Conversations
20
+ count: Count
19
21
  created_at: Created At
20
22
  creator: Creator
21
23
  details: Details
22
24
  entries_count: Entries Count
23
25
  entry: Entry
26
+ est_cost: est. cost
24
27
  failed: Failed
25
28
  failed_at: Failed At
26
29
  filter: Filter
@@ -44,6 +47,10 @@ en:
44
47
  no_tasks: No tasks found.
45
48
  no_tool_calls: No tool calls
46
49
  pending: Pending
50
+ period_all: All time
51
+ period_day: Past 24 hours
52
+ period_month: Past month
53
+ period_week: Past week
47
54
  prettified: Prettified
48
55
  prompt: Prompt
49
56
  prompt_tokens: Prompt Tokens
@@ -58,6 +65,7 @@ en:
58
65
  since: Since
59
66
  source: Source
60
67
  started_at: Started At
68
+ stats: Stats
61
69
  status: Status
62
70
  system_prompt: System Prompt
63
71
  task: Task
@@ -67,8 +75,10 @@ en:
67
75
  tool_invocations: Tool Invocations
68
76
  tool_name: Tool Name
69
77
  tool_type: Tool Type
78
+ total_cost: Total Cost
70
79
  total_tokens: Total Tokens
71
80
  type: Type
81
+ update: Update
72
82
  user_message: User Message
73
83
  conversations:
74
84
  show:
@@ -85,6 +95,10 @@ en:
85
95
  show:
86
96
  back_to_model_tool_invocations: Back to Model Tool Invocations
87
97
  title: 'Model Tool Invocation #%{id}'
98
+ stats:
99
+ tasks:
100
+ back_to_stats: Back to Stats
101
+ title: Task Stats
88
102
  tasks:
89
103
  show:
90
104
  back_to_tasks: Back to Tasks
@@ -2,9 +2,14 @@
2
2
  en:
3
3
  raif:
4
4
  agents:
5
- errors:
6
- available_model_tools:
7
- too_short: must have at least 1 tool
5
+ native_tool_calling_agent:
6
+ errors:
7
+ available_model_tools:
8
+ too_short: must have at least 1 tool in addition to the agent_final_answer tool
9
+ re_act_agent:
10
+ errors:
11
+ available_model_tools:
12
+ too_short: must have at least 1 tool
8
13
  common:
9
14
  send: Send
10
15
  there_was_an_error_generating_this_response: There was an error generating this response
@@ -12,6 +17,11 @@ en:
12
17
  type_your_message: Type your message...
13
18
  conversation:
14
19
  initial_chat_message: Hello, how can I help you today?
20
+ embedding_model_names:
21
+ bedrock_titan_embed_text_v2: AWS Bedrock Titan Text Embeddings v2
22
+ open_ai_text_embedding_3_large: OpenAI Text Embedding 3 Large
23
+ open_ai_text_embedding_3_small: OpenAI Text Embedding 3 Small
24
+ open_ai_text_embedding_ada_002: OpenAI Text Embedding Ada 002
15
25
  languages:
16
26
  ar: Arabic
17
27
  da: Danish
@@ -40,11 +50,23 @@ en:
40
50
  anthropic_claude_3_5_sonnet: Anthropic Claude 3.5 Sonnet
41
51
  anthropic_claude_3_7_sonnet: Anthropic Claude 3.7 Sonnet
42
52
  anthropic_claude_3_opus: Anthropic Claude 3 Opus
53
+ anthropic_claude_4_opus: Anthropic Claude 4 Opus
54
+ anthropic_claude_4_sonnet: Anthropic Claude 4 Sonnet
43
55
  bedrock_claude_3_5_haiku: Anthropic Claude 3.5 Haiku (via AWS Bedrock)
44
56
  bedrock_claude_3_5_sonnet: Anthropic Claude 3.5 Sonnet (via AWS Bedrock)
45
57
  bedrock_claude_3_7_sonnet: Anthropic Claude 3.7 Sonnet (via AWS Bedrock)
46
58
  bedrock_claude_3_opus: Anthropic Claude 3 Opus (via AWS Bedrock)
59
+ bedrock_claude_4_opus: Anthropic Claude 4 Opus (via AWS Bedrock)
60
+ bedrock_claude_4_sonnet: Anthropic Claude 4 Sonnet (via AWS Bedrock)
47
61
  open_ai_gpt_3_5_turbo: OpenAI GPT-3.5 Turbo
62
+ open_ai_gpt_4_1: OpenAI GPT-4.1
63
+ open_ai_gpt_4_1_mini: OpenAI GPT-4.1 Mini
64
+ open_ai_gpt_4_1_nano: OpenAI GPT-4.1 Nano
48
65
  open_ai_gpt_4o: OpenAI GPT-4o
49
66
  open_ai_gpt_4o_mini: OpenAI GPT-4o Mini
67
+ open_router_claude_3_7_sonnet: Anthropic Claude 3.7 Sonnet (via OpenRouter)
68
+ open_router_deepseek_chat_v3: DeepSeek Chat v3 (via OpenRouter)
69
+ open_router_gemini_2_0_flash: Google Gemini 2.0 Flash (via OpenRouter)
70
+ open_router_llama_3_1_8b_instruct: Meta Llama 3.1 8B Instruct (via OpenRouter)
71
+ open_router_llama_3_3_70b_instruct: Meta Llama 3.3 70B Instruct (via OpenRouter)
50
72
  raif_test_llm: Raif Test LLM
data/config/routes.rb CHANGED
@@ -13,6 +13,12 @@ Raif::Engine.routes.draw do
13
13
 
14
14
  namespace :admin do
15
15
  root to: redirect("admin/model_completions")
16
+ resources :stats, only: [:index]
17
+
18
+ namespace :stats do
19
+ resources :tasks, only: [:index]
20
+ end
21
+
16
22
  resources :tasks, only: [:index, :show]
17
23
  resources :conversations, only: [:index, :show]
18
24
  resources :model_completions, only: [:index, :show]
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddResponseFormatToRaifConversations < ActiveRecord::Migration[8.0]
4
+ def change
5
+ add_column :raif_conversations, :response_format, :integer, default: 0, null: false
6
+ end
7
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddCostColumnsToRaifModelCompletions < ActiveRecord::Migration[8.0]
4
+ # If you need to backfill cost columns for existing records:
5
+ # Raif::ModelCompletion.find_each do |model_completion|
6
+ # model_completion.calculate_costs
7
+ # model_completion.save(validate: false)
8
+ # end
9
+ def change
10
+ add_column :raif_model_completions, :prompt_token_cost, :decimal, precision: 10, scale: 6
11
+ add_column :raif_model_completions, :output_token_cost, :decimal, precision: 10, scale: 6
12
+ add_column :raif_model_completions, :total_cost, :decimal, precision: 10, scale: 6
13
+ end
14
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddCreatedAtIndexes < ActiveRecord::Migration[8.0]
4
+ def change
5
+ add_index :raif_model_completions, :created_at
6
+ add_index :raif_tasks, :created_at
7
+ add_index :raif_conversations, :created_at
8
+ add_index :raif_conversation_entries, :created_at
9
+ add_index :raif_agents, :created_at
10
+ end
11
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddStatusIndexesToRaifTasks < ActiveRecord::Migration[8.0]
4
+ def change
5
+ add_index :raif_tasks, :completed_at
6
+ add_index :raif_tasks, :failed_at
7
+ add_index :raif_tasks, :started_at
8
+
9
+ # Index for type + status combinations which will be common in the admin interface
10
+ add_index :raif_tasks, [:type, :completed_at]
11
+ add_index :raif_tasks, [:type, :failed_at]
12
+ add_index :raif_tasks, [:type, :started_at]
13
+ end
14
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddRetryCountToRaifModelCompletions < ActiveRecord::Migration[7.1]
4
+ def change
5
+ add_column :raif_model_completions, :retry_count, :integer, default: 0, null: false
6
+ end
7
+ end
@@ -1,22 +1,32 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Raif
4
- class AgentGenerator < Rails::Generators::NamedBase
5
- source_root File.expand_path("templates", __dir__)
6
- desc "Creates a new Raif::Agent subclass in app/models/raif/agents"
4
+ module Generators
5
+ class AgentGenerator < Rails::Generators::NamedBase
6
+ source_root File.expand_path("templates", __dir__)
7
+ desc "Creates a new Raif::Agent subclass in app/models/raif/agents"
7
8
 
8
- def create_agent
9
- template "agent.rb.tt", "app/models/raif/agents/#{file_name}.rb"
10
- end
9
+ def create_application_agent
10
+ template "application_agent.rb.tt", "app/models/raif/application_agent.rb" unless File.exist?("app/models/raif/application_agent.rb")
11
+ end
11
12
 
12
- private
13
+ def create_agent
14
+ template "agent.rb.tt", "app/models/raif/agents/#{file_name}.rb"
15
+ end
13
16
 
14
- def class_name
15
- name.classify
16
- end
17
+ def create_directory
18
+ empty_directory "app/models/raif/agents" unless File.directory?("app/models/raif/agents")
19
+ end
20
+
21
+ private
22
+
23
+ def class_name
24
+ name.classify
25
+ end
17
26
 
18
- def file_name
19
- name.underscore
27
+ def file_name
28
+ name.underscore
29
+ end
20
30
  end
21
31
  end
22
32
  end
@@ -2,15 +2,15 @@
2
2
 
3
3
  module Raif
4
4
  module Agents
5
- class <%= class_name %> < Raif::Agent
5
+ class <%= class_name %> < Raif::ApplicationAgent
6
6
  # If you want to always include a certain set of model tools with this agent type,
7
7
  # uncomment this callback to populate the available_model_tools attribute with your desired model tools.
8
- # before_create -> {
8
+ # def populate_default_model_tools
9
9
  # self.available_model_tools ||= [
10
10
  # Raif::ModelTools::WikipediaSearchTool,
11
11
  # Raif::ModelTools::FetchUrlTool
12
12
  # ]
13
- # }
13
+ # end
14
14
 
15
15
  # Enter your agent's system prompt here. Alternatively, you can change your agent's superclass
16
16
  # to an existing agent types (like Raif::Agents::ReActAgent) to utilize an existing system prompt.
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Raif
4
+ class ApplicationAgent < Raif::Agent
5
+ # Add any shared agent behavior here
6
+ end
7
+ end
@@ -7,6 +7,16 @@ module Raif
7
7
 
8
8
  desc "Creates a new conversation type in the app/models/raif/conversations directory"
9
9
 
10
+ class_option :response_format,
11
+ type: :string,
12
+ default: "text",
13
+ desc: "Response format for the task (text, html, or json)"
14
+
15
+ def create_application_conversation
16
+ template "application_conversation.rb.tt",
17
+ "app/models/raif/application_conversation.rb" unless File.exist?("app/models/raif/application_conversation.rb")
18
+ end
19
+
10
20
  def create_conversation_file
11
21
  template "conversation.rb.tt", File.join("app/models/raif/conversations", "#{file_name}.rb")
12
22
  end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Raif
4
+ class ApplicationConversation < Raif::Conversation
5
+ # Add any shared conversation behavior here
6
+ end
7
+ end
@@ -2,28 +2,23 @@
2
2
 
3
3
  module Raif
4
4
  module Conversations
5
- class <%= class_name %> < Raif::Conversation
5
+ class <%= class_name %> < Raif::ApplicationConversation
6
+ # Set the response format for the task. Options are :html, :text, or :json.
7
+ # If you set this to something other than :text, make sure to include instructions to the model in your system prompt
8
+ llm_response_format :<%= options[:response_format] %>
9
+
6
10
  # If you want to always include a certain set of model tools with this conversation type,
7
11
  # uncomment this callback to populate the available_model_tools attribute with your desired model tools.
8
12
  # before_create -> { self.available_model_tools = ["Raif::ModelTools::Example"] }
9
13
 
10
14
  # Override the methods below to customize the system prompt for this conversation type.
11
- # Raif::Conversation expects a JSON response with a message key from the model, so make sure your system prompt instructs the model to respond accordingly.
12
15
  # def system_prompt_intro
13
16
  # Raif.config.conversation_system_prompt_intro
14
17
  # end
15
-
18
+
16
19
  # def build_system_prompt
17
20
  # <<~PROMPT
18
21
  # #{system_prompt_intro}
19
- #
20
- # # Your Responses
21
- # Your responses should always be in JSON format with a "message" field containing your response to your collaborator. For example:
22
- # {
23
- # "message": "Your response message"
24
- # }
25
- # #{tool_usage_system_prompt}
26
- # #{system_prompt_reminders}
27
22
  # #{system_prompt_language_preference}
28
23
  # PROMPT
29
24
  # end
@@ -32,6 +27,13 @@ module Raif
32
27
  # def initial_chat_message
33
28
  # I18n.t("#{self.class.name.underscore.gsub("/", ".")}.initial_chat_message")
34
29
  # end
30
+
31
+ # This method will be called when receing a model response to a Raif::ConversationEntry
32
+ # By default, it just passes the model response message through, but you can override
33
+ # for custom response message processing
34
+ # def process_model_response_message(message:, entry:)
35
+ # message
36
+ # end
35
37
  end
36
38
  end
37
39
  end
@@ -4,23 +4,47 @@ Raif.configure do |config|
4
4
  # Your OpenAI API key. Defaults to ENV["OPENAI_API_KEY"]
5
5
  # config.open_ai_api_key = ENV["OPENAI_API_KEY"]
6
6
 
7
- # Whether OpenAI models are enabled. Defaults to true
8
- # config.open_ai_models_enabled = true
7
+ # Whether OpenAI models are enabled.
8
+ # config.open_ai_models_enabled = ENV["OPENAI_API_KEY"].present?
9
+
10
+ # Whether OpenAI embedding models are enabled.
11
+ # config.open_ai_embedding_models_enabled = ENV["OPENAI_API_KEY"].present?
9
12
 
10
13
  # Your Anthropic API key. Defaults to ENV["ANTHROPIC_API_KEY"]
11
14
  # config.anthropic_api_key = ENV["ANTHROPIC_API_KEY"]
12
15
 
13
- # Whether Anthropic models are enabled. Defaults to true
14
- # config.anthropic_models_enabled = true
16
+ # Whether Anthropic models are enabled.
17
+ # config.anthropic_models_enabled = ENV["ANTHROPIC_API_KEY"].present?
15
18
 
16
- # Whether Anthropic models via AWS Bedrock are enabled. Defaults to true
17
- # config.anthropic_bedrock_models_enabled = true
19
+ # Whether Anthropic models via AWS Bedrock are enabled. Defaults to false
20
+ # config.anthropic_bedrock_models_enabled = false
18
21
 
19
22
  # The AWS Bedrock region to use. Defaults to "us-east-1"
20
23
  # config.aws_bedrock_region = "us-east-1"
21
24
 
25
+ # Prefix to apply to the model name in AWS Bedrock API calls (e.g. us.anthropic.claude-3-5-haiku-20241022-v1:0)
26
+ # config.aws_bedrock_model_name_prefix = "us"
27
+
28
+ # Whether Titan embedding models are enabled. Defaults to false
29
+ # config.aws_bedrock_titan_embedding_models_enabled = false
30
+
31
+ # Your OpenRouter API key. Defaults to ENV["OPENROUTER_API_KEY"]
32
+ # config.open_router_api_key = ENV["OPENROUTER_API_KEY"]
33
+
34
+ # Whether OpenRouter models are enabled.
35
+ # config.open_router_models_enabled = ENV["OPENROUTER_API_KEY"].present?
36
+
37
+ # The app name to include in OpenRouter API requests headers. Optional.
38
+ # config.open_router_app_name = "My App"
39
+
40
+ # The site URL to include in OpenRouter API requests headers. Optional.
41
+ # config.open_router_site_url = "https://myapp.com"
42
+
22
43
  # The default LLM model to use. Defaults to "open_ai_gpt_4o"
23
44
  # Available keys:
45
+ # open_ai_gpt_4_1
46
+ # open_ai_gpt_4_1_mini
47
+ # open_ai_gpt_4_1_nano
24
48
  # open_ai_gpt_4o_mini
25
49
  # open_ai_gpt_4o
26
50
  # open_ai_gpt_3_5_turbo
@@ -32,8 +56,24 @@ Raif.configure do |config|
32
56
  # bedrock_claude_3_7_sonnet
33
57
  # bedrock_claude_3_5_haiku
34
58
  # bedrock_claude_3_opus
59
+ # open_router_claude_3_7_sonnet
60
+ # open_router_llama_3_3_70b_instruct
61
+ # open_router_llama_3_1_8b_instruct
62
+ # open_router_gemini_2_0_flash
63
+ # open_router_deepseek_chat_v3
64
+ #
35
65
  # config.default_llm_model_key = "open_ai_gpt_4o"
36
66
 
67
+ # The default embedding model to use when calling Raif.generate_embedding!
68
+ # Defaults to "open_ai_text_embedding_3_small"
69
+ # Available keys:
70
+ # open_ai_text_embedding_3_small
71
+ # open_ai_text_embedding_3_large
72
+ # open_ai_text_embedding_ada_002
73
+ # bedrock_titan_embed_text_v2
74
+ #
75
+ # config.default_embedding_model_key = "open_ai_text_embedding_3_small"
76
+
37
77
  # A lambda that returns true if the current user is authorized to access admin controllers.
38
78
  # By default it returns false, so you must implement this in your application to use the admin controllers.
39
79
  # If your application's user model has an admin? method, you could use something like this:
@@ -46,9 +86,13 @@ Raif.configure do |config|
46
86
 
47
87
  # The system prompt intro for Raif::Task instances. Defaults to "You are a helpful assistant."
48
88
  # config.task_system_prompt_intro = "You are a helpful assistant."
89
+ # Or you can use a lambda to return a dynamic system prompt intro:
90
+ # config.task_system_prompt_intro = ->(task){ "You are a helpful assistant. Today's date is #{Date.today.strftime('%B %d, %Y')}." }
49
91
 
50
92
  # The system prompt intro for Raif::Conversation instances. Defaults to "You are a helpful assistant who is collaborating with a teammate."
51
93
  # config.conversation_system_prompt_intro = "You are a helpful assistant who is collaborating with a teammate."
94
+ # Or you can use a lambda to return a dynamic system prompt intro:
95
+ # config.conversation_system_prompt_intro = ->(conversation){ "You are a helpful assistant talking to #{conversation.creator.email}. Today's date is #{Date.today.strftime('%B %d, %Y')}." }
52
96
 
53
97
  # The conversation types that are available. Defaults to ["Raif::Conversation"]
54
98
  # If you want to use custom conversation types that inherits from Raif::Conversation, you can add them here.
@@ -15,11 +15,6 @@ module Raif
15
15
  say_status :success, "Model tool created successfully", :green
16
16
  say "\nYou can now implement your model tool in:"
17
17
  say " app/models/raif/model_tools/#{file_name}.rb"
18
- say "\nImportant methods to implement:"
19
- say " - example_model_invocation: An example of how to invoke the tool, to be provided to the LLM"
20
- say " - tool_arguments_schema: JSON schema for validating arguments when the tool is invoked"
21
- say " - tool_description: A brief description of what the tool does, to be provided to the LLM"
22
- say " - process_invocation: The main method that executes the tool's functionality"
23
18
  end
24
19
 
25
20
  end