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
@@ -0,0 +1,170 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Utility class for processing HTML fragments with various cleaning and transformation operations.
4
+ #
5
+ # This class provides methods for sanitizing HTML content, converting markdown links to HTML,
6
+ # processing existing HTML links (adding target="_blank", stripping tracking parameters),
7
+ # and removing tracking parameters from URLs.
8
+ class Raif::Utils::HtmlFragmentProcessor
9
+ # List of common tracking parameters to remove from URLs
10
+ TRACKING_PARAMS = %w[
11
+ utm_source
12
+ utm_medium
13
+ utm_campaign
14
+ utm_term
15
+ utm_content
16
+ utm_id
17
+ ]
18
+
19
+ class << self
20
+ # Cleans and sanitizes an HTML fragment by removing empty text nodes and dangerous content.
21
+ #
22
+ # @param html [String, Nokogiri::HTML::DocumentFragment] The HTML content to clean
23
+ # @param allowed_tags [Array<String>, nil] Array of allowed HTML tags. Defaults to Rails HTML5 safe list
24
+ # @param allowed_attributes [Array<String>, nil] Array of allowed HTML attributes. Defaults to Rails HTML5 safe list
25
+ # @return [String] Cleaned and sanitized HTML string
26
+ #
27
+ # @example
28
+ # clean_html_fragment("<script>alert('xss')</script><p>Safe content</p>")
29
+ # # => "<p>Safe content</p>"
30
+ #
31
+ # @example With custom allowed tags
32
+ # clean_html_fragment("<p>Para</p><div>Div</div>", allowed_tags: %w[p])
33
+ # # => "<p>Para</p>Div"
34
+ def clean_html_fragment(html, allowed_tags: nil, allowed_attributes: nil)
35
+ fragment = html.is_a?(Nokogiri::HTML::DocumentFragment) ? html : Nokogiri::HTML.fragment(html)
36
+
37
+ fragment.traverse do |node|
38
+ if node.text? && node.text.strip.empty?
39
+ node.remove
40
+ end
41
+ end
42
+
43
+ allowed_tags = allowed_tags.presence || Rails::HTML5::SafeListSanitizer.allowed_tags
44
+ allowed_attributes = allowed_attributes.presence || Rails::HTML5::SafeListSanitizer.allowed_attributes
45
+
46
+ ActionController::Base.helpers.sanitize(fragment.to_html, tags: allowed_tags, attributes: allowed_attributes).strip
47
+ end
48
+
49
+ # Converts markdown-style links to HTML anchor tags with target="_blank" and rel="noopener".
50
+ #
51
+ # Converts [text](url) format to <a href="url" target="_blank" rel="noopener">text</a>.
52
+ # Also strips tracking parameters from the URLs.
53
+ #
54
+ # @param text [String] The text content that may contain markdown links
55
+ # @return [String] HTML with markdown links converted to anchor tags
56
+ #
57
+ # @example
58
+ # convert_markdown_links_to_html("Check out [Google](https://google.com) for search.")
59
+ # # => 'Check out <a href="https://google.com" target="_blank" rel="noopener">Google</a> for search.'
60
+ #
61
+ # @example With tracking parameters
62
+ # convert_markdown_links_to_html("[Example](https://example.com?utm_source=test&param=keep)")
63
+ # # => '<a href="https://example.com?param=keep" target="_blank" rel="noopener">Example</a>'
64
+ def convert_markdown_links_to_html(text)
65
+ # Convert markdown links [text](url) to HTML links <a href="url" target="_blank" rel="noopener">text</a>
66
+ text.gsub(/\[([^\]]*)\]\(([^)]+)\)/) do |_match|
67
+ text = ::Regexp.last_match(1)
68
+ url = ::Regexp.last_match(2)
69
+ clean_url = strip_tracking_parameters(url)
70
+ %(<a href="#{CGI.escapeHTML(clean_url)}" target="_blank" rel="noopener">#{CGI.escapeHTML(text)}</a>)
71
+ end
72
+ end
73
+
74
+ # Processes existing HTML links by optionally adding target="_blank" and stripping tracking parameters.
75
+ #
76
+ # This method provides fine-grained control over link processing with configurable options
77
+ # for both target="_blank" addition and tracking parameter removal.
78
+ #
79
+ # @param html [String, Nokogiri::HTML::DocumentFragment] The HTML content containing links to process
80
+ # @param add_target_blank [Boolean] Whether to add target="_blank" and rel="noopener" to links (required)
81
+ # @param strip_tracking_parameters [Boolean] Whether to remove tracking parameters from URLs (required)
82
+ # @return [String] Processed HTML with modified links
83
+ #
84
+ # @example Default behavior (adds target="_blank" and strips tracking params)
85
+ # process_links('<a href="https://example.com?utm_source=test">Link</a>', add_target_blank: true, strip_tracking_parameters: true)
86
+ # # => '<a href="https://example.com" target="_blank" rel="noopener">Link</a>'
87
+ #
88
+ # @example Only strip tracking parameters
89
+ # process_links(html, add_target_blank: false, strip_tracking_parameters: true)
90
+ # # => '<a href="https://example.com">Link</a>'
91
+ #
92
+ # @example Only add target="_blank"
93
+ # process_links(html, add_target_blank: true, strip_tracking_parameters: false)
94
+ # # => '<a href="https://example.com?utm_source=test" target="_blank" rel="noopener">Link</a>'
95
+ #
96
+ # @example No processing
97
+ # process_links(html, add_target_blank: false, strip_tracking_parameters: false)
98
+ # # => Original HTML unchanged
99
+ def process_links(html, add_target_blank:, strip_tracking_parameters:)
100
+ fragment = html.is_a?(Nokogiri::HTML::DocumentFragment) ? html : Nokogiri::HTML.fragment(html)
101
+
102
+ fragment.css("a").each do |link|
103
+ if add_target_blank
104
+ link["target"] = "_blank"
105
+ link["rel"] = "noopener"
106
+ end
107
+
108
+ if strip_tracking_parameters
109
+ link["href"] = strip_tracking_parameters(link["href"])
110
+ end
111
+ end
112
+
113
+ fragment.to_html
114
+ end
115
+
116
+ # Removes tracking parameters (UTM parameters) from a URL.
117
+ #
118
+ # Preserves all non-tracking query parameters and handles various URL formats including
119
+ # relative URLs, absolute URLs, and malformed URLs gracefully.
120
+ #
121
+ # @param url [String] The URL to clean
122
+ # @return [String] URL with tracking parameters removed, or original URL if parsing fails
123
+ #
124
+ # @example
125
+ # strip_tracking_parameters("https://example.com?utm_source=google&page=1")
126
+ # # => "https://example.com?page=1"
127
+ #
128
+ # @example Removes all tracking parameters
129
+ # strip_tracking_parameters("https://example.com?utm_source=test&utm_medium=cpc")
130
+ # # => "https://example.com"
131
+ #
132
+ # @example Preserves fragments
133
+ # strip_tracking_parameters("https://example.com?utm_source=test&page=1#section")
134
+ # # => "https://example.com?page=1#section"
135
+ #
136
+ # @example Handles relative URLs
137
+ # strip_tracking_parameters("/path?utm_source=test&param=keep")
138
+ # # => "/path?param=keep"
139
+ def strip_tracking_parameters(url)
140
+ return url if url.blank?
141
+ return url unless url.include?("?")
142
+
143
+ begin
144
+ uri = URI.parse(url)
145
+ return url unless uri.query
146
+
147
+ # Only process URLs that have a valid scheme and host, or are relative URLs
148
+ unless uri.scheme || url.start_with?("/", "#")
149
+ return url
150
+ end
151
+
152
+ # Parse query parameters and filter out tracking ones
153
+ params = URI.decode_www_form(uri.query)
154
+ clean_params = params.reject { |param, _| TRACKING_PARAMS.include?(param.downcase) }
155
+
156
+ # Rebuild the URL
157
+ uri.query = if clean_params.empty?
158
+ nil
159
+ else
160
+ URI.encode_www_form(clean_params)
161
+ end
162
+
163
+ uri.to_s
164
+ rescue URI::InvalidURIError
165
+ # If URL parsing fails, return the original URL
166
+ url
167
+ end
168
+ end
169
+ end
170
+ end
data/lib/raif/utils.rb CHANGED
@@ -3,4 +3,5 @@
3
3
  module Raif::Utils
4
4
  require "raif/utils/readable_content_extractor"
5
5
  require "raif/utils/html_to_markdown_converter"
6
+ require "raif/utils/html_fragment_processor"
6
7
  end
data/lib/raif/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Raif
4
- VERSION = "1.1.0"
4
+ VERSION = "1.2.1.pre"
5
5
  end
data/lib/raif.rb CHANGED
@@ -9,8 +9,10 @@ require "raif/utils"
9
9
  require "raif/llm_registry"
10
10
  require "raif/embedding_model_registry"
11
11
  require "raif/json_schema_builder"
12
+ require "raif/migration_checker"
12
13
 
13
14
  require "faraday"
15
+ require "event_stream_parser"
14
16
  require "json-schema"
15
17
  require "loofah"
16
18
  require "pagy"
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Raif::ModelTools::ComplexTestTool < Raif::ModelTool
4
+ tool_arguments_schema do
5
+ string :title, description: "The title of the operation", minLength: 3
6
+
7
+ object :settings, description: "Configuration settings" do
8
+ boolean :enabled, description: "Whether the tool is enabled"
9
+ integer :priority, description: "Priority level (1-10)", minimum: 1, maximum: 10
10
+ array :tags, description: "Associated tags" do
11
+ items type: "string"
12
+ end
13
+ end
14
+
15
+ array :products, description: "List of products" do
16
+ object do
17
+ integer :id, description: "Product identifier"
18
+ string :name, description: "Product name"
19
+ number :price, description: "Product price", minimum: 0
20
+ end
21
+ end
22
+ end
23
+
24
+ example_model_invocation do
25
+ {
26
+ "name" => tool_name,
27
+ "arguments" => {
28
+ "title" => "Daily Inventory Update",
29
+ "settings" => {
30
+ "enabled" => true,
31
+ "priority" => 5,
32
+ "tags" => ["inventory", "daily-update", "retail"]
33
+ },
34
+ "items" => [
35
+ { "id" => 101, "name" => "Wireless Mouse", "price" => 25.99 },
36
+ { "id" => 102, "name" => "Keyboard", "price" => 45.0 },
37
+ { "id" => 103, "name" => "USB-C Cable", "price" => 9.99 }
38
+ ]
39
+ }
40
+ }
41
+ end
42
+
43
+ tool_description do
44
+ "An example tool demonstrating complex schema capabilities"
45
+ end
46
+
47
+ def self.process_invocation(tool_invocation)
48
+ # This would be the actual implementation
49
+ # For demonstration purposes, just echo back the arguments
50
+ tool_invocation.update!(
51
+ result: {
52
+ message: "Received complex example request",
53
+ arguments: tool_invocation.tool_arguments
54
+ }
55
+ )
56
+
57
+ tool_invocation.result
58
+ end
59
+
60
+ def self.observation_for_invocation(tool_invocation)
61
+ return "No results" unless tool_invocation.result.present?
62
+
63
+ "Successfully processed request for: #{tool_invocation.tool_arguments["title"]}"
64
+ end
65
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Raif
4
+ module RspecHelpers
5
+
6
+ def stubbed_llm(llm_model_key, &block)
7
+ test_llm = Raif.llm(llm_model_key.to_sym)
8
+
9
+ allow(test_llm).to receive(:perform_model_completion!) do |model_completion|
10
+ result = block.call(model_completion.messages, model_completion)
11
+ model_completion.raw_response = result if result.is_a?(String)
12
+ model_completion.completion_tokens = rand(100..2000)
13
+ model_completion.prompt_tokens = rand(100..2000)
14
+ model_completion.total_tokens = model_completion.completion_tokens + model_completion.prompt_tokens
15
+ model_completion.save!
16
+
17
+ model_completion
18
+ end
19
+
20
+ test_llm
21
+ end
22
+
23
+ def stub_raif_task(task, &block)
24
+ allow(Raif.config).to receive(:llm_api_requests_enabled){ true }
25
+
26
+ if task.is_a?(Raif::Task)
27
+ allow(task).to receive(:llm){ stubbed_llm(task.llm_model_key, &block) }
28
+ else
29
+ allow_any_instance_of(task).to receive(:llm) do |task_instance|
30
+ stubbed_llm(task_instance.llm_model_key, &block)
31
+ end
32
+ end
33
+ end
34
+
35
+ def stub_raif_conversation(conversation, &block)
36
+ allow(Raif.config).to receive(:llm_api_requests_enabled){ true }
37
+
38
+ if conversation.is_a?(Raif::Conversation)
39
+ allow(conversation).to receive(:llm){ stubbed_llm(conversation.llm_model_key, &block) }
40
+ else
41
+ allow_any_instance_of(conversation).to receive(:llm) do |conversation_instance|
42
+ stubbed_llm(conversation_instance.llm_model_key, &block)
43
+ end
44
+ end
45
+ end
46
+
47
+ def stub_raif_agent(agent, &block)
48
+ allow(Raif.config).to receive(:llm_api_requests_enabled){ true }
49
+
50
+ if agent.is_a?(Raif::Agent)
51
+ allow(agent).to receive(:llm){ stubbed_llm(agent.llm_model_key, &block) }
52
+ else
53
+ allow_any_instance_of(agent).to receive(:llm) do |agent_instance|
54
+ stubbed_llm(agent_instance.llm_model_key, &block)
55
+ end
56
+ end
57
+ end
58
+
59
+ def stub_raif_llm(llm, &block)
60
+ llm.chat_handler = block
61
+ allow(Raif.config).to receive(:llm_api_requests_enabled){ true }
62
+ llm
63
+ end
64
+
65
+ end
66
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Raif::TestConversation < Raif::Conversation
4
+
5
+ before_create :populate_available_model_tools
6
+
7
+ def populate_available_model_tools
8
+ self.available_model_tools = [
9
+ "Raif::TestModelTool",
10
+ "Raif::ModelTools::WikipediaSearch",
11
+ ]
12
+ end
13
+
14
+ def process_model_response_message(message:, entry:)
15
+ message.gsub("jerk", "[REDACTED]")
16
+ end
17
+
18
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Raif
4
+ module EmbeddingModels
5
+ class Test < Raif::EmbeddingModel
6
+ attr_accessor :embedding_handler
7
+
8
+ def generate_embedding!(input, dimensions: nil)
9
+ if input.is_a?(Array)
10
+ input.map { |text| generate_test_embedding!(text, dimensions:) }
11
+ else
12
+ generate_test_embedding!(input, dimensions:)
13
+ end
14
+ end
15
+
16
+ def generate_test_embedding!(input, dimensions: nil)
17
+ if embedding_handler.present?
18
+ embedding_handler.call(input, dimensions)
19
+ else
20
+ dimensions ||= default_output_vector_size
21
+ Array.new(dimensions) { rand(-1.0..1.0) }
22
+ end
23
+ end
24
+
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Raif
4
+ module Llms
5
+ class TestLlm < Raif::Llm
6
+ include Raif::Concerns::Llms::OpenAiCompletions::MessageFormatting
7
+
8
+ attr_accessor :chat_handler
9
+
10
+ def perform_model_completion!(model_completion)
11
+ result = chat_handler.call(model_completion.messages, model_completion)
12
+ model_completion.raw_response = result if result.is_a?(String)
13
+ model_completion.completion_tokens = rand(100..2000)
14
+ model_completion.prompt_tokens = rand(100..2000)
15
+ model_completion.total_tokens = model_completion.completion_tokens + model_completion.prompt_tokens
16
+ model_completion.save!
17
+
18
+ model_completion
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Raif::TestModelTool < Raif::ModelTool
4
+ tool_arguments_schema do
5
+ array :items do
6
+ object do
7
+ string :title, description: "The title of the item"
8
+ string :description
9
+ end
10
+ end
11
+ end
12
+
13
+ example_model_invocation do
14
+ {
15
+ "name": tool_name,
16
+ "arguments": { "items": [{ "title": "foo", "description": "bar" }] }
17
+ }
18
+ end
19
+
20
+ def self.process_invocation(tool_arguments)
21
+ end
22
+
23
+ tool_description do
24
+ "Mock Tool Description"
25
+ end
26
+
27
+ def self.observation_for_invocation(tool_invocation)
28
+ return if tool_invocation.result.blank?
29
+
30
+ "Mock Observation for #{tool_invocation.id}. Result was: #{tool_invocation.result["status"]}"
31
+ end
32
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Raif::TestTask < Raif::Task
4
+ llm_temperature 0.5
5
+
6
+ def build_prompt
7
+ "Tell me a joke"
8
+ end
9
+
10
+ def build_system_prompt
11
+ super + "\nYou are also good at telling jokes."
12
+ end
13
+ end
14
+
15
+ class Raif::TestJsonTask < Raif::Task
16
+ llm_response_format :json
17
+ llm_temperature 0.75
18
+
19
+ json_response_schema do
20
+ string :joke
21
+ string :answer
22
+ end
23
+
24
+ def build_prompt
25
+ "Tell me a joke"
26
+ end
27
+
28
+ def build_system_prompt
29
+ super + "\nYou are also good at telling jokes. Your response should be a JSON object with the following keys: joke, answer."
30
+ end
31
+ end
32
+
33
+ class Raif::TestHtmlTask < Raif::Task
34
+ llm_response_format :html
35
+ llm_response_allowed_tags %w[p b i u s a]
36
+ llm_response_allowed_attributes %w[style href]
37
+
38
+ def build_prompt
39
+ "Tell me a joke"
40
+ end
41
+
42
+ def build_system_prompt
43
+ super + "\nYou are also good at telling jokes. Your response should be an HTML snippet that is formatted with basic HTML tags."
44
+ end
45
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: raif
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.2.1.pre
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Roesch
8
8
  - Brian Leslie
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-05-23 00:00:00.000000000 Z
11
+ date: 1980-01-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aws-sdk-bedrockruntime
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: event_stream_parser
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '1.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '1.0'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: faraday
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -175,18 +189,26 @@ files:
175
189
  - app/models/raif/concerns/llm_response_parsing.rb
176
190
  - app/models/raif/concerns/llm_temperature.rb
177
191
  - app/models/raif/concerns/llms/anthropic/message_formatting.rb
178
- - app/models/raif/concerns/llms/bedrock_claude/message_formatting.rb
192
+ - app/models/raif/concerns/llms/anthropic/tool_formatting.rb
193
+ - app/models/raif/concerns/llms/bedrock/message_formatting.rb
194
+ - app/models/raif/concerns/llms/bedrock/tool_formatting.rb
179
195
  - app/models/raif/concerns/llms/message_formatting.rb
180
- - app/models/raif/concerns/llms/open_ai/message_formatting.rb
196
+ - app/models/raif/concerns/llms/open_ai/json_schema_validation.rb
197
+ - app/models/raif/concerns/llms/open_ai_completions/message_formatting.rb
198
+ - app/models/raif/concerns/llms/open_ai_completions/tool_formatting.rb
199
+ - app/models/raif/concerns/llms/open_ai_responses/message_formatting.rb
200
+ - app/models/raif/concerns/llms/open_ai_responses/tool_formatting.rb
181
201
  - app/models/raif/conversation.rb
182
202
  - app/models/raif/conversation_entry.rb
183
203
  - app/models/raif/embedding_model.rb
184
- - app/models/raif/embedding_models/bedrock_titan.rb
204
+ - app/models/raif/embedding_models/bedrock.rb
185
205
  - app/models/raif/embedding_models/open_ai.rb
186
206
  - app/models/raif/llm.rb
187
207
  - app/models/raif/llms/anthropic.rb
188
- - app/models/raif/llms/bedrock_claude.rb
189
- - app/models/raif/llms/open_ai.rb
208
+ - app/models/raif/llms/bedrock.rb
209
+ - app/models/raif/llms/open_ai_base.rb
210
+ - app/models/raif/llms/open_ai_completions.rb
211
+ - app/models/raif/llms/open_ai_responses.rb
190
212
  - app/models/raif/llms/open_router.rb
191
213
  - app/models/raif/model_completion.rb
192
214
  - app/models/raif/model_file_input.rb
@@ -195,7 +217,15 @@ files:
195
217
  - app/models/raif/model_tool_invocation.rb
196
218
  - app/models/raif/model_tools/agent_final_answer.rb
197
219
  - app/models/raif/model_tools/fetch_url.rb
220
+ - app/models/raif/model_tools/provider_managed/base.rb
221
+ - app/models/raif/model_tools/provider_managed/code_execution.rb
222
+ - app/models/raif/model_tools/provider_managed/image_generation.rb
223
+ - app/models/raif/model_tools/provider_managed/web_search.rb
198
224
  - app/models/raif/model_tools/wikipedia_search.rb
225
+ - app/models/raif/streaming_responses/anthropic.rb
226
+ - app/models/raif/streaming_responses/bedrock.rb
227
+ - app/models/raif/streaming_responses/open_ai_completions.rb
228
+ - app/models/raif/streaming_responses/open_ai_responses.rb
199
229
  - app/models/raif/task.rb
200
230
  - app/models/raif/user_tool_invocation.rb
201
231
  - app/views/layouts/raif/admin.html.erb
@@ -218,6 +248,7 @@ files:
218
248
  - app/views/raif/admin/tasks/_task.html.erb
219
249
  - app/views/raif/admin/tasks/index.html.erb
220
250
  - app/views/raif/admin/tasks/show.html.erb
251
+ - app/views/raif/conversation_entries/_citations.html.erb
221
252
  - app/views/raif/conversation_entries/_conversation_entry.html.erb
222
253
  - app/views/raif/conversation_entries/_form.html.erb
223
254
  - app/views/raif/conversation_entries/_form_with_available_tools.html.erb
@@ -242,6 +273,9 @@ files:
242
273
  - db/migrate/20250424232946_add_created_at_indexes.rb
243
274
  - db/migrate/20250502155330_add_status_indexes_to_raif_tasks.rb
244
275
  - db/migrate/20250507155314_add_retry_count_to_raif_model_completions.rb
276
+ - db/migrate/20250527213016_add_response_id_and_response_array_to_model_completions.rb
277
+ - db/migrate/20250603140622_add_citations_to_raif_model_completions.rb
278
+ - db/migrate/20250603202013_add_stream_response_to_raif_model_completions.rb
245
279
  - lib/generators/raif/agent/agent_generator.rb
246
280
  - lib/generators/raif/agent/templates/agent.rb.tt
247
281
  - lib/generators/raif/agent/templates/application_agent.rb.tt
@@ -268,16 +302,26 @@ files:
268
302
  - lib/raif/errors/invalid_model_image_input_error.rb
269
303
  - lib/raif/errors/invalid_user_tool_type_error.rb
270
304
  - lib/raif/errors/open_ai/json_schema_error.rb
305
+ - lib/raif/errors/streaming_error.rb
271
306
  - lib/raif/errors/unsupported_feature_error.rb
272
307
  - lib/raif/json_schema_builder.rb
273
308
  - lib/raif/languages.rb
274
309
  - lib/raif/llm_registry.rb
310
+ - lib/raif/migration_checker.rb
275
311
  - lib/raif/rspec.rb
276
312
  - lib/raif/utils.rb
313
+ - lib/raif/utils/html_fragment_processor.rb
277
314
  - lib/raif/utils/html_to_markdown_converter.rb
278
315
  - lib/raif/utils/readable_content_extractor.rb
279
316
  - lib/raif/version.rb
280
317
  - lib/tasks/raif_tasks.rake
318
+ - spec/support/complex_test_tool.rb
319
+ - spec/support/rspec_helpers.rb
320
+ - spec/support/test_conversation.rb
321
+ - spec/support/test_embedding_model.rb
322
+ - spec/support/test_llm.rb
323
+ - spec/support/test_model_tool.rb
324
+ - spec/support/test_task.rb
281
325
  homepage: https://github.com/cultivatelabs/raif
282
326
  licenses:
283
327
  - MIT
@@ -300,7 +344,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
300
344
  - !ruby/object:Gem::Version
301
345
  version: '0'
302
346
  requirements: []
303
- rubygems_version: 3.6.2
347
+ rubygems_version: 3.6.7
304
348
  specification_version: 4
305
349
  summary: Raif (Ruby AI Framework) is a Rails engine that helps you add AI-powered
306
350
  features to your Rails apps, such as tasks, conversations, and agents.