raif 1.1.0 → 1.2.1.pre

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 (74) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +150 -4
  3. data/app/assets/builds/raif.css +26 -1
  4. data/app/assets/stylesheets/raif/loader.scss +27 -1
  5. data/app/models/raif/concerns/llm_response_parsing.rb +22 -16
  6. data/app/models/raif/concerns/llms/anthropic/tool_formatting.rb +56 -0
  7. data/app/models/raif/concerns/llms/{bedrock_claude → bedrock}/message_formatting.rb +4 -4
  8. data/app/models/raif/concerns/llms/bedrock/tool_formatting.rb +37 -0
  9. data/app/models/raif/concerns/llms/message_formatting.rb +7 -6
  10. data/app/models/raif/concerns/llms/open_ai/json_schema_validation.rb +138 -0
  11. data/app/models/raif/concerns/llms/{open_ai → open_ai_completions}/message_formatting.rb +1 -1
  12. data/app/models/raif/concerns/llms/open_ai_completions/tool_formatting.rb +26 -0
  13. data/app/models/raif/concerns/llms/open_ai_responses/message_formatting.rb +43 -0
  14. data/app/models/raif/concerns/llms/open_ai_responses/tool_formatting.rb +42 -0
  15. data/app/models/raif/conversation.rb +17 -4
  16. data/app/models/raif/conversation_entry.rb +18 -2
  17. data/app/models/raif/embedding_models/{bedrock_titan.rb → bedrock.rb} +2 -2
  18. data/app/models/raif/llm.rb +73 -7
  19. data/app/models/raif/llms/anthropic.rb +56 -36
  20. data/app/models/raif/llms/{bedrock_claude.rb → bedrock.rb} +62 -45
  21. data/app/models/raif/llms/open_ai_base.rb +66 -0
  22. data/app/models/raif/llms/open_ai_completions.rb +100 -0
  23. data/app/models/raif/llms/open_ai_responses.rb +144 -0
  24. data/app/models/raif/llms/open_router.rb +44 -44
  25. data/app/models/raif/model_completion.rb +2 -0
  26. data/app/models/raif/model_tool.rb +4 -0
  27. data/app/models/raif/model_tools/provider_managed/base.rb +9 -0
  28. data/app/models/raif/model_tools/provider_managed/code_execution.rb +5 -0
  29. data/app/models/raif/model_tools/provider_managed/image_generation.rb +5 -0
  30. data/app/models/raif/model_tools/provider_managed/web_search.rb +5 -0
  31. data/app/models/raif/streaming_responses/anthropic.rb +63 -0
  32. data/app/models/raif/streaming_responses/bedrock.rb +89 -0
  33. data/app/models/raif/streaming_responses/open_ai_completions.rb +76 -0
  34. data/app/models/raif/streaming_responses/open_ai_responses.rb +54 -0
  35. data/app/views/raif/admin/conversations/_conversation_entry.html.erb +48 -0
  36. data/app/views/raif/admin/conversations/show.html.erb +1 -1
  37. data/app/views/raif/admin/model_completions/_model_completion.html.erb +7 -0
  38. data/app/views/raif/admin/model_completions/index.html.erb +1 -0
  39. data/app/views/raif/admin/model_completions/show.html.erb +28 -0
  40. data/app/views/raif/conversation_entries/_citations.html.erb +9 -0
  41. data/app/views/raif/conversation_entries/_conversation_entry.html.erb +5 -1
  42. data/app/views/raif/conversation_entries/_message.html.erb +4 -0
  43. data/config/locales/admin.en.yml +2 -0
  44. data/config/locales/en.yml +24 -0
  45. data/db/migrate/20250224234252_create_raif_tables.rb +1 -1
  46. data/db/migrate/20250421202149_add_response_format_to_raif_conversations.rb +1 -1
  47. data/db/migrate/20250424200755_add_cost_columns_to_raif_model_completions.rb +1 -1
  48. data/db/migrate/20250424232946_add_created_at_indexes.rb +1 -1
  49. data/db/migrate/20250502155330_add_status_indexes_to_raif_tasks.rb +1 -1
  50. data/db/migrate/20250527213016_add_response_id_and_response_array_to_model_completions.rb +14 -0
  51. data/db/migrate/20250603140622_add_citations_to_raif_model_completions.rb +13 -0
  52. data/db/migrate/20250603202013_add_stream_response_to_raif_model_completions.rb +7 -0
  53. data/lib/generators/raif/conversation/templates/conversation.rb.tt +3 -3
  54. data/lib/generators/raif/install/templates/initializer.rb +14 -2
  55. data/lib/raif/configuration.rb +27 -5
  56. data/lib/raif/embedding_model_registry.rb +1 -1
  57. data/lib/raif/engine.rb +25 -9
  58. data/lib/raif/errors/streaming_error.rb +18 -0
  59. data/lib/raif/errors.rb +1 -0
  60. data/lib/raif/llm_registry.rb +169 -47
  61. data/lib/raif/migration_checker.rb +74 -0
  62. data/lib/raif/utils/html_fragment_processor.rb +170 -0
  63. data/lib/raif/utils.rb +1 -0
  64. data/lib/raif/version.rb +1 -1
  65. data/lib/raif.rb +2 -0
  66. data/spec/support/complex_test_tool.rb +65 -0
  67. data/spec/support/rspec_helpers.rb +66 -0
  68. data/spec/support/test_conversation.rb +18 -0
  69. data/spec/support/test_embedding_model.rb +27 -0
  70. data/spec/support/test_llm.rb +22 -0
  71. data/spec/support/test_model_tool.rb +32 -0
  72. data/spec/support/test_task.rb +45 -0
  73. metadata +52 -8
  74. data/app/models/raif/llms/open_ai.rb +0 -256
@@ -4,13 +4,13 @@ module Raif
4
4
  class Configuration
5
5
  attr_accessor :agent_types,
6
6
  :anthropic_api_key,
7
- :anthropic_bedrock_models_enabled,
7
+ :bedrock_models_enabled,
8
8
  :anthropic_models_enabled,
9
9
  :authorize_admin_controller_action,
10
10
  :authorize_controller_action,
11
11
  :aws_bedrock_model_name_prefix,
12
12
  :aws_bedrock_region,
13
- :aws_bedrock_titan_embedding_models_enabled,
13
+ :bedrock_embedding_models_enabled,
14
14
  :conversation_entries_controller,
15
15
  :conversation_system_prompt_intro,
16
16
  :conversation_types,
@@ -29,20 +29,27 @@ module Raif
29
29
  :open_router_models_enabled,
30
30
  :open_router_app_name,
31
31
  :open_router_site_url,
32
+ :streaming_update_chunk_size_threshold,
32
33
  :task_system_prompt_intro,
33
34
  :user_tool_types
34
35
 
36
+ alias_method :anthropic_bedrock_models_enabled, :bedrock_models_enabled
37
+ alias_method :anthropic_bedrock_models_enabled=, :bedrock_models_enabled=
38
+
39
+ alias_method :aws_bedrock_titan_embedding_models_enabled, :bedrock_embedding_models_enabled
40
+ alias_method :aws_bedrock_titan_embedding_models_enabled=, :bedrock_embedding_models_enabled=
41
+
35
42
  def initialize
36
43
  # Set default config
37
44
  @agent_types = Set.new(["Raif::Agents::ReActAgent", "Raif::Agents::NativeToolCallingAgent"])
38
45
  @anthropic_api_key = ENV["ANTHROPIC_API_KEY"]
39
- @anthropic_bedrock_models_enabled = false
46
+ @bedrock_models_enabled = false
40
47
  @anthropic_models_enabled = ENV["ANTHROPIC_API_KEY"].present?
41
48
  @authorize_admin_controller_action = ->{ false }
42
49
  @authorize_controller_action = ->{ false }
43
50
  @aws_bedrock_region = "us-east-1"
44
51
  @aws_bedrock_model_name_prefix = "us"
45
- @aws_bedrock_titan_embedding_models_enabled = false
52
+ @bedrock_embedding_models_enabled = false
46
53
  @task_system_prompt_intro = "You are a helpful assistant."
47
54
  @conversation_entries_controller = "Raif::ConversationEntriesController"
48
55
  @conversation_system_prompt_intro = "You are a helpful assistant who is collaborating with a teammate."
@@ -66,16 +73,31 @@ module Raif
66
73
  @open_router_models_enabled = ENV["OPENROUTER_API_KEY"].present?
67
74
  @open_router_app_name = nil
68
75
  @open_router_site_url = nil
76
+ @streaming_update_chunk_size_threshold = 25
69
77
  @user_tool_types = []
70
78
  end
71
79
 
72
80
  def validate!
81
+ if Raif.llm_registry.blank?
82
+ puts <<~EOS
83
+
84
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
85
+ No LLMs are enabled in Raif. Make sure you have an API key configured for at least one LLM provider. You can do this by setting an API key in your environment variables or in config/initializers/raif.rb (e.g. ENV["OPENAI_API_KEY"], ENV["ANTHROPIC_API_KEY"], ENV["OPENROUTER_API_KEY"]).
86
+
87
+ See the README for more information: https://github.com/CultivateLabs/raif#setup
88
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
89
+
90
+ EOS
91
+
92
+ return
93
+ end
94
+
73
95
  unless Raif.available_llm_keys.include?(default_llm_model_key.to_sym)
74
96
  raise Raif::Errors::InvalidConfigError,
75
97
  "Raif.config.default_llm_model_key was set to #{default_llm_model_key}, but must be one of: #{Raif.available_llm_keys.join(", ")}"
76
98
  end
77
99
 
78
- unless Raif.available_embedding_model_keys.include?(default_embedding_model_key.to_sym)
100
+ if Raif.embedding_model_registry.present? && !Raif.available_embedding_model_keys.include?(default_embedding_model_key.to_sym)
79
101
  raise Raif::Errors::InvalidConfigError,
80
102
  "Raif.config.default_embedding_model_key was set to #{default_embedding_model_key}, but must be one of: #{Raif.available_embedding_model_keys.join(", ")}" # rubocop:disable Layout/LineLength
81
103
  end
@@ -70,7 +70,7 @@ module Raif
70
70
  default_output_vector_size: 1536,
71
71
  },
72
72
  ],
73
- Raif::EmbeddingModels::BedrockTitan => [
73
+ Raif::EmbeddingModels::Bedrock => [
74
74
  {
75
75
  key: :bedrock_titan_embed_text_v2,
76
76
  api_name: "amazon.titan-embed-text-v2:0",
data/lib/raif/engine.rb CHANGED
@@ -29,8 +29,12 @@ module Raif
29
29
  config.after_initialize do
30
30
  next unless Raif.config.open_ai_models_enabled
31
31
 
32
- Raif.default_llms[Raif::Llms::OpenAi].each do |llm_config|
33
- Raif.register_llm(Raif::Llms::OpenAi, **llm_config)
32
+ Raif.default_llms[Raif::Llms::OpenAiCompletions].each do |llm_config|
33
+ Raif.register_llm(Raif::Llms::OpenAiCompletions, **llm_config)
34
+ end
35
+
36
+ Raif.default_llms[Raif::Llms::OpenAiResponses].each do |llm_config|
37
+ Raif.register_llm(Raif::Llms::OpenAiResponses, **llm_config)
34
38
  end
35
39
  end
36
40
 
@@ -51,12 +55,12 @@ module Raif
51
55
  end
52
56
 
53
57
  config.after_initialize do
54
- next unless Raif.config.anthropic_bedrock_models_enabled
58
+ next unless Raif.config.bedrock_models_enabled
55
59
 
56
60
  require "aws-sdk-bedrockruntime"
57
61
 
58
- Raif.default_llms[Raif::Llms::BedrockClaude].each do |llm_config|
59
- Raif.register_llm(Raif::Llms::BedrockClaude, **llm_config)
62
+ Raif.default_llms[Raif::Llms::Bedrock].each do |llm_config|
63
+ Raif.register_llm(Raif::Llms::Bedrock, **llm_config)
60
64
  end
61
65
  end
62
66
 
@@ -69,12 +73,12 @@ module Raif
69
73
  end
70
74
 
71
75
  config.after_initialize do
72
- next unless Raif.config.aws_bedrock_titan_embedding_models_enabled
76
+ next unless Raif.config.bedrock_embedding_models_enabled
73
77
 
74
78
  require "aws-sdk-bedrockruntime"
75
79
 
76
- Raif.default_embedding_models[Raif::EmbeddingModels::BedrockTitan].each do |embedding_model_config|
77
- Raif.register_embedding_model(Raif::EmbeddingModels::BedrockTitan, **embedding_model_config)
80
+ Raif.default_embedding_models[Raif::EmbeddingModels::Bedrock].each do |embedding_model_config|
81
+ Raif.register_embedding_model(Raif::EmbeddingModels::Bedrock, **embedding_model_config)
78
82
  end
79
83
  end
80
84
 
@@ -84,7 +88,7 @@ module Raif
84
88
  Raif.config.conversation_types += ["Raif::TestConversation"]
85
89
 
86
90
  require "#{Raif::Engine.root}/spec/support/test_llm"
87
- Raif.register_llm(Raif::Llms::Test, key: :raif_test_llm, api_name: "raif-test-llm")
91
+ Raif.register_llm(Raif::Llms::TestLlm, key: :raif_test_llm, api_name: "raif-test-llm")
88
92
 
89
93
  require "#{Raif::Engine.root}/spec/support/test_embedding_model"
90
94
  Raif.register_embedding_model(
@@ -99,6 +103,18 @@ module Raif
99
103
  Raif.config.validate!
100
104
  end
101
105
 
106
+ config.after_initialize do
107
+ # Check to see if the host app is missing any of our migrations
108
+ # and print a warning if they are
109
+ next unless Rails.env.development?
110
+ next if File.basename($PROGRAM_NAME) == "rake"
111
+
112
+ # Skip if we're running inside the engine's own dummy app
113
+ next if Rails.root.to_s.include?("raif/spec/dummy")
114
+
115
+ Raif::MigrationChecker.check_and_warn!
116
+ end
117
+
102
118
  initializer "raif.assets" do
103
119
  if Rails.application.config.respond_to?(:assets)
104
120
  Rails.application.config.assets.precompile += [
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Raif
4
+ module Errors
5
+ class StreamingError < StandardError
6
+ attr_reader :message, :type, :code, :event
7
+
8
+ def initialize(message:, type:, event:, code: nil)
9
+ super
10
+
11
+ @message = message
12
+ @type = type
13
+ @code = code
14
+ @event = event
15
+ end
16
+ end
17
+ end
18
+ end
data/lib/raif/errors.rb CHANGED
@@ -8,3 +8,4 @@ require "raif/errors/open_ai/json_schema_error"
8
8
  require "raif/errors/invalid_model_image_input_error"
9
9
  require "raif/errors/invalid_model_file_input_error"
10
10
  require "raif/errors/unsupported_feature_error"
11
+ require "raif/errors/streaming_error"
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Raif
4
- class << self
5
- attr_accessor :llm_registry
4
+ def self.llm_registry
5
+ @llm_registry ||= {}
6
6
  end
7
7
 
8
8
  def self.register_llm(llm_class, llm_config)
@@ -40,81 +40,170 @@ module Raif
40
40
  end
41
41
 
42
42
  def self.default_llms
43
+ open_ai_models = [
44
+ {
45
+ key: :open_ai_gpt_4o_mini,
46
+ api_name: "gpt-4o-mini",
47
+ input_token_cost: 0.15 / 1_000_000,
48
+ output_token_cost: 0.6 / 1_000_000,
49
+ },
50
+ {
51
+ key: :open_ai_gpt_4o,
52
+ api_name: "gpt-4o",
53
+ input_token_cost: 2.5 / 1_000_000,
54
+ output_token_cost: 10.0 / 1_000_000,
55
+ },
56
+ {
57
+ key: :open_ai_gpt_3_5_turbo,
58
+ api_name: "gpt-3.5-turbo",
59
+ input_token_cost: 0.5 / 1_000_000,
60
+ output_token_cost: 1.5 / 1_000_000,
61
+ model_provider_settings: { supports_structured_outputs: false }
62
+ },
63
+ {
64
+ key: :open_ai_gpt_4_1,
65
+ api_name: "gpt-4.1",
66
+ input_token_cost: 2.0 / 1_000_000,
67
+ output_token_cost: 8.0 / 1_000_000,
68
+ },
69
+ {
70
+ key: :open_ai_gpt_4_1_mini,
71
+ api_name: "gpt-4.1-mini",
72
+ input_token_cost: 0.4 / 1_000_000,
73
+ output_token_cost: 1.6 / 1_000_000,
74
+ },
75
+ {
76
+ key: :open_ai_gpt_4_1_nano,
77
+ api_name: "gpt-4.1-nano",
78
+ input_token_cost: 0.1 / 1_000_000,
79
+ output_token_cost: 0.4 / 1_000_000,
80
+ },
81
+ {
82
+ key: :open_ai_o1,
83
+ api_name: "o1",
84
+ input_token_cost: 15.0 / 1_000_000,
85
+ output_token_cost: 60.0 / 1_000_000,
86
+ model_provider_settings: { supports_temperature: false },
87
+ },
88
+ {
89
+ key: :open_ai_o1_mini,
90
+ api_name: "o1-mini",
91
+ input_token_cost: 1.5 / 1_000_000,
92
+ output_token_cost: 6.0 / 1_000_000,
93
+ model_provider_settings: { supports_temperature: false },
94
+ },
95
+ {
96
+ key: :open_ai_o3,
97
+ api_name: "o3",
98
+ input_token_cost: 2.0 / 1_000_000,
99
+ output_token_cost: 8.0 / 1_000_000,
100
+ model_provider_settings: { supports_temperature: false },
101
+ },
102
+ {
103
+ key: :open_ai_o3_mini,
104
+ api_name: "o3-mini",
105
+ input_token_cost: 1.1 / 1_000_000,
106
+ output_token_cost: 4.4 / 1_000_000,
107
+ model_provider_settings: { supports_temperature: false },
108
+ },
109
+ {
110
+ key: :open_ai_o4_mini,
111
+ api_name: "o4-mini",
112
+ input_token_cost: 1.1 / 1_000_000,
113
+ output_token_cost: 4.4 / 1_000_000,
114
+ model_provider_settings: { supports_temperature: false },
115
+ },
116
+ ]
117
+
118
+ open_ai_responses_models = open_ai_models.dup.map.with_index do |model, _index|
119
+ model.merge(
120
+ key: model[:key].to_s.gsub("open_ai_", "open_ai_responses_").to_sym,
121
+ supported_provider_managed_tools: [
122
+ Raif::ModelTools::ProviderManaged::WebSearch,
123
+ Raif::ModelTools::ProviderManaged::CodeExecution,
124
+ Raif::ModelTools::ProviderManaged::ImageGeneration
125
+ ]
126
+ )
127
+ end
128
+
129
+ # o1-mini is not supported by the OpenAI Responses API.
130
+ open_ai_responses_models.delete_if{|model| model[:key] == :open_ai_o1_mini }
131
+
132
+ # o1-pro and o3-pro are not supported by the OpenAI Completions API, but it is supported by the OpenAI Responses API.
133
+ open_ai_responses_models << {
134
+ key: :open_ai_responses_o1_pro,
135
+ api_name: "o1-pro",
136
+ input_token_cost: 150.0 / 1_000_000,
137
+ output_token_cost: 600.0 / 1_000_000,
138
+ model_provider_settings: { supports_temperature: false },
139
+ }
140
+
141
+ open_ai_responses_models << {
142
+ key: :open_ai_responses_o3_pro,
143
+ api_name: "o3-pro",
144
+ input_token_cost: 20.0 / 1_000_000,
145
+ output_token_cost: 80.0 / 1_000_000,
146
+ model_provider_settings: { supports_temperature: false },
147
+ }
148
+
43
149
  {
44
- Raif::Llms::OpenAi => [
45
- {
46
- key: :open_ai_gpt_4o_mini,
47
- api_name: "gpt-4o-mini",
48
- input_token_cost: 0.15 / 1_000_000,
49
- output_token_cost: 0.6 / 1_000_000,
50
- },
51
- {
52
- key: :open_ai_gpt_4o,
53
- api_name: "gpt-4o",
54
- input_token_cost: 2.5 / 1_000_000,
55
- output_token_cost: 10.0 / 1_000_000,
56
- },
57
- {
58
- key: :open_ai_gpt_3_5_turbo,
59
- api_name: "gpt-3.5-turbo",
60
- input_token_cost: 0.5 / 1_000_000,
61
- output_token_cost: 1.5 / 1_000_000,
62
- model_provider_settings: { supports_structured_outputs: false }
63
- },
64
- {
65
- key: :open_ai_gpt_4_1,
66
- api_name: "gpt-4.1",
67
- input_token_cost: 2.0 / 1_000_000,
68
- output_token_cost: 8.0 / 1_000_000,
69
- },
70
- {
71
- key: :open_ai_gpt_4_1_mini,
72
- api_name: "gpt-4.1-mini",
73
- input_token_cost: 0.4 / 1_000_000,
74
- output_token_cost: 1.6 / 1_000_000,
75
- },
76
- {
77
- key: :open_ai_gpt_4_1_nano,
78
- api_name: "gpt-4.1-nano",
79
- input_token_cost: 0.1 / 1_000_000,
80
- output_token_cost: 0.4 / 1_000_000,
81
- },
82
- ],
150
+ Raif::Llms::OpenAiCompletions => open_ai_models,
151
+ Raif::Llms::OpenAiResponses => open_ai_responses_models,
83
152
  Raif::Llms::Anthropic => [
84
153
  {
85
154
  key: :anthropic_claude_4_sonnet,
86
155
  api_name: "claude-sonnet-4-20250514",
87
156
  input_token_cost: 3.0 / 1_000_000,
88
157
  output_token_cost: 15.0 / 1_000_000,
89
- max_completion_tokens: 8192
158
+ max_completion_tokens: 8192,
159
+ supported_provider_managed_tools: [
160
+ Raif::ModelTools::ProviderManaged::WebSearch,
161
+ Raif::ModelTools::ProviderManaged::CodeExecution
162
+ ]
90
163
  },
91
164
  {
92
165
  key: :anthropic_claude_4_opus,
93
166
  api_name: "claude-opus-4-20250514",
94
167
  input_token_cost: 15.0 / 1_000_000,
95
168
  output_token_cost: 75.0 / 1_000_000,
96
- max_completion_tokens: 8192
169
+ max_completion_tokens: 8192,
170
+ supported_provider_managed_tools: [
171
+ Raif::ModelTools::ProviderManaged::WebSearch,
172
+ Raif::ModelTools::ProviderManaged::CodeExecution
173
+ ]
97
174
  },
98
175
  {
99
176
  key: :anthropic_claude_3_7_sonnet,
100
177
  api_name: "claude-3-7-sonnet-latest",
101
178
  input_token_cost: 3.0 / 1_000_000,
102
179
  output_token_cost: 15.0 / 1_000_000,
103
- max_completion_tokens: 8192
180
+ max_completion_tokens: 8192,
181
+ supported_provider_managed_tools: [
182
+ Raif::ModelTools::ProviderManaged::WebSearch,
183
+ Raif::ModelTools::ProviderManaged::CodeExecution
184
+ ]
104
185
  },
105
186
  {
106
187
  key: :anthropic_claude_3_5_sonnet,
107
188
  api_name: "claude-3-5-sonnet-latest",
108
189
  input_token_cost: 3.0 / 1_000_000,
109
190
  output_token_cost: 15.0 / 1_000_000,
110
- max_completion_tokens: 8192
191
+ max_completion_tokens: 8192,
192
+ supported_provider_managed_tools: [
193
+ Raif::ModelTools::ProviderManaged::WebSearch,
194
+ Raif::ModelTools::ProviderManaged::CodeExecution
195
+ ]
111
196
  },
112
197
  {
113
198
  key: :anthropic_claude_3_5_haiku,
114
199
  api_name: "claude-3-5-haiku-latest",
115
200
  input_token_cost: 0.8 / 1_000_000,
116
201
  output_token_cost: 4.0 / 1_000_000,
117
- max_completion_tokens: 8192
202
+ max_completion_tokens: 8192,
203
+ supported_provider_managed_tools: [
204
+ Raif::ModelTools::ProviderManaged::WebSearch,
205
+ Raif::ModelTools::ProviderManaged::CodeExecution
206
+ ]
118
207
  },
119
208
  {
120
209
  key: :anthropic_claude_3_opus,
@@ -124,7 +213,7 @@ module Raif
124
213
  max_completion_tokens: 4096
125
214
  },
126
215
  ],
127
- Raif::Llms::BedrockClaude => [
216
+ Raif::Llms::Bedrock => [
128
217
  {
129
218
  key: :bedrock_claude_4_sonnet,
130
219
  api_name: "anthropic.claude-sonnet-4-20250514-v1:0",
@@ -167,6 +256,27 @@ module Raif
167
256
  output_token_cost: 0.075 / 1000,
168
257
  max_completion_tokens: 4096
169
258
  },
259
+ {
260
+ key: :bedrock_amazon_nova_micro,
261
+ api_name: "amazon.nova-micro-v1:0",
262
+ input_token_cost: 0.0000115 / 1000,
263
+ output_token_cost: 0.000184 / 1000,
264
+ max_completion_tokens: 4096
265
+ },
266
+ {
267
+ key: :bedrock_amazon_nova_lite,
268
+ api_name: "amazon.nova-lite-v1:0",
269
+ input_token_cost: 0.0000195 / 1000,
270
+ output_token_cost: 0.000312 / 1000,
271
+ max_completion_tokens: 4096
272
+ },
273
+ {
274
+ key: :bedrock_amazon_nova_pro,
275
+ api_name: "amazon.nova-pro-v1:0",
276
+ input_token_cost: 0.0002625 / 1000,
277
+ output_token_cost: 0.0042 / 1000,
278
+ max_completion_tokens: 4096
279
+ }
170
280
  ],
171
281
  Raif::Llms::OpenRouter => [
172
282
  {
@@ -187,6 +297,18 @@ module Raif
187
297
  input_token_cost: 0.02 / 1_000_000,
188
298
  output_token_cost: 0.03 / 1_000_000,
189
299
  },
300
+ {
301
+ key: :open_router_llama_4_maverick,
302
+ api_name: "meta-llama/llama-4-maverick",
303
+ input_token_cost: 0.15 / 1_000_000,
304
+ output_token_cost: 0.60 / 1_000_000,
305
+ },
306
+ {
307
+ key: :open_router_llama_4_scout,
308
+ api_name: "meta-llama/llama-4-scout",
309
+ input_token_cost: 0.08 / 1_000_000,
310
+ output_token_cost: 0.30 / 1_000_000,
311
+ },
190
312
  {
191
313
  key: :open_router_gemini_2_0_flash,
192
314
  api_name: "google/gemini-2.0-flash-001",
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Raif
4
+ module MigrationChecker
5
+ class << self
6
+ def uninstalled_migrations
7
+ engine_migration_names = engine_migration_names_from_context
8
+ ran_migration_names = ran_migration_names_from_host
9
+
10
+ engine_migration_names - ran_migration_names
11
+ end
12
+
13
+ def check_and_warn!
14
+ return unless defined?(Rails) && Rails.application
15
+
16
+ uninstalled = uninstalled_migrations
17
+ return if uninstalled.empty?
18
+
19
+ warning_message = build_warning_message(uninstalled)
20
+
21
+ # Output to both logger and STDOUT to ensure visibility
22
+ Rails.logger&.warn(warning_message)
23
+ warn warning_message
24
+ end
25
+
26
+ private
27
+
28
+ def engine_migration_names_from_context
29
+ engine_paths = Raif::Engine.paths["db/migrate"].existent
30
+ return [] if engine_paths.empty?
31
+
32
+ ActiveRecord::MigrationContext.new(engine_paths).migrations.map(&:name)
33
+ rescue => e
34
+ Rails.logger&.debug("Raif: Could not load engine migrations: #{e.message}")
35
+ []
36
+ end
37
+
38
+ def ran_migration_names_from_host
39
+ return [] unless defined?(Rails) && Rails.application
40
+
41
+ app_paths = Rails.application.paths["db/migrate"].expanded
42
+ return [] if app_paths.empty?
43
+
44
+ ctx = ActiveRecord::MigrationContext.new(app_paths)
45
+ ran_versions = ctx.get_all_versions
46
+ ctx.migrations.select{|m| ran_versions.include?(m.version) }.map(&:name)
47
+ rescue ActiveRecord::NoDatabaseError
48
+ # Database doesn't exist yet, so no migrations have been run
49
+ []
50
+ rescue => e
51
+ Rails.logger&.debug("Raif: Could not load migration status: #{e.message}")
52
+ []
53
+ end
54
+
55
+ def build_warning_message(uninstalled_migration_names)
56
+ <<~WARNING
57
+ \e[33m
58
+ ⚠️ RAIF MIGRATION WARNING ⚠️
59
+
60
+ The following Raif migrations have not been run in your application:
61
+
62
+ #{uninstalled_migration_names.map { |name| " • #{name}" }.join("\n")}
63
+
64
+ To install and run these migrations:
65
+
66
+ rails raif:install:migrations
67
+ rails db:migrate
68
+
69
+ \e[0m
70
+ WARNING
71
+ end
72
+ end
73
+ end
74
+ end