rails_ai_promptable 0.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.
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'net/http'
4
+ require 'json'
5
+
6
+ module RailsAIPromptable
7
+ module Providers
8
+ class MistralProvider < BaseProvider
9
+ def initialize(configuration)
10
+ super
11
+ @api_key = configuration.mistral_api_key || configuration.api_key
12
+ @base_url = configuration.mistral_base_url || 'https://api.mistral.ai/v1'
13
+ @timeout = configuration.timeout
14
+ end
15
+
16
+ def generate(prompt:, model:, temperature:, format:)
17
+ uri = URI.parse("#{@base_url}/chat/completions")
18
+ http = Net::HTTP.new(uri.host, uri.port)
19
+ http.use_ssl = uri.scheme == 'https'
20
+ http.read_timeout = @timeout
21
+
22
+ request = Net::HTTP::Post.new(uri.request_uri, {
23
+ 'Content-Type' => 'application/json',
24
+ 'Authorization' => "Bearer #{@api_key}"
25
+ })
26
+
27
+ body = {
28
+ model: model,
29
+ messages: [{ role: 'user', content: prompt }],
30
+ temperature: temperature,
31
+ max_tokens: 2048
32
+ }
33
+
34
+ request.body = body.to_json
35
+
36
+ response = http.request(request)
37
+ parsed = JSON.parse(response.body)
38
+
39
+ if response.code.to_i >= 400
40
+ error_message = parsed.dig('message') || 'Unknown error'
41
+ raise "Mistral API error: #{error_message}"
42
+ end
43
+
44
+ # Extract content (OpenAI-compatible format)
45
+ parsed.dig('choices', 0, 'message', 'content')
46
+ rescue => e
47
+ RailsAIPromptable.configuration.logger.error("[rails_ai_promptable] mistral error: #{e.message}")
48
+ nil
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'net/http'
4
+ require 'json'
5
+
6
+ module RailsAIPromptable
7
+ module Providers
8
+ class OpenAIProvider < BaseProvider
9
+ def initialize(configuration)
10
+ super
11
+ @api_key = configuration.api_key
12
+ @base_url = configuration.openai_base_url
13
+ @timeout = configuration.timeout
14
+ end
15
+
16
+ def generate(prompt:, model:, temperature:, format:)
17
+ uri = URI.parse("#{@base_url}/chat/completions")
18
+ http = Net::HTTP.new(uri.host, uri.port)
19
+ http.use_ssl = uri.scheme == 'https'
20
+ request = Net::HTTP::Post.new(uri.request_uri, initheader = {
21
+ 'Content-Type' => 'application/json',
22
+ 'Authorization' => "Bearer #{@api_key}"
23
+ })
24
+
25
+ body = {
26
+ model: model,
27
+ messages: [{ role: 'user', content: prompt }],
28
+ temperature: temperature
29
+ }
30
+
31
+ request.body = body.to_json
32
+
33
+ response = http.request(request)
34
+ parsed = JSON.parse(response.body)
35
+
36
+ # naive extraction
37
+ parsed.dig('choices', 0, 'message', 'content')
38
+ rescue => e
39
+ RailsAIPromptable.configuration.logger.error("[rails_ai_promptable] openai error: #{e.message}")
40
+ nil
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'net/http'
4
+ require 'json'
5
+
6
+ module RailsAIPromptable
7
+ module Providers
8
+ class OpenRouterProvider < BaseProvider
9
+ def initialize(configuration)
10
+ super
11
+ @api_key = configuration.openrouter_api_key || configuration.api_key
12
+ @base_url = configuration.openrouter_base_url || 'https://openrouter.ai/api/v1'
13
+ @timeout = configuration.timeout
14
+ @app_name = configuration.openrouter_app_name
15
+ @site_url = configuration.openrouter_site_url
16
+ end
17
+
18
+ def generate(prompt:, model:, temperature:, format:)
19
+ uri = URI.parse("#{@base_url}/chat/completions")
20
+ http = Net::HTTP.new(uri.host, uri.port)
21
+ http.use_ssl = uri.scheme == 'https'
22
+ http.read_timeout = @timeout
23
+
24
+ headers = {
25
+ 'Content-Type' => 'application/json',
26
+ 'Authorization' => "Bearer #{@api_key}"
27
+ }
28
+
29
+ # OpenRouter requires these headers for tracking/attribution
30
+ headers['HTTP-Referer'] = @site_url if @site_url
31
+ headers['X-Title'] = @app_name if @app_name
32
+
33
+ request = Net::HTTP::Post.new(uri.request_uri, headers)
34
+
35
+ body = {
36
+ model: model,
37
+ messages: [{ role: 'user', content: prompt }],
38
+ temperature: temperature,
39
+ max_tokens: 2048
40
+ }
41
+
42
+ request.body = body.to_json
43
+
44
+ response = http.request(request)
45
+ parsed = JSON.parse(response.body)
46
+
47
+ if response.code.to_i >= 400
48
+ error_message = parsed.dig('error', 'message') || 'Unknown error'
49
+ raise "OpenRouter API error: #{error_message}"
50
+ end
51
+
52
+ # Extract content (OpenAI-compatible format)
53
+ parsed.dig('choices', 0, 'message', 'content')
54
+ rescue => e
55
+ RailsAIPromptable.configuration.logger.error("[rails_ai_promptable] openrouter error: #{e.message}")
56
+ nil
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsAIPromptable
4
+ module Providers
5
+ autoload :BaseProvider, 'rails_ai_promptable/providers/base_provider'
6
+ autoload :OpenAIProvider, 'rails_ai_promptable/providers/openai_provider'
7
+ autoload :AnthropicProvider, 'rails_ai_promptable/providers/anthropic_provider'
8
+ autoload :GeminiProvider, 'rails_ai_promptable/providers/gemini_provider'
9
+ autoload :CohereProvider, 'rails_ai_promptable/providers/cohere_provider'
10
+ autoload :AzureOpenAIProvider, 'rails_ai_promptable/providers/azure_openai_provider'
11
+ autoload :MistralProvider, 'rails_ai_promptable/providers/mistral_provider'
12
+ autoload :OpenRouterProvider, 'rails_ai_promptable/providers/openrouter_provider'
13
+
14
+ def self.for(provider_sym, configuration)
15
+ case provider_sym.to_sym
16
+ when :openai
17
+ OpenAIProvider.new(configuration)
18
+ when :anthropic, :claude
19
+ AnthropicProvider.new(configuration)
20
+ when :gemini, :google
21
+ GeminiProvider.new(configuration)
22
+ when :cohere
23
+ CohereProvider.new(configuration)
24
+ when :azure_openai, :azure
25
+ AzureOpenAIProvider.new(configuration)
26
+ when :mistral
27
+ MistralProvider.new(configuration)
28
+ when :openrouter
29
+ OpenRouterProvider.new(configuration)
30
+ else
31
+ raise ArgumentError, "Unknown provider: #{provider_sym}. Supported providers: :openai, :anthropic, :gemini, :cohere, :azure_openai, :mistral, :openrouter"
32
+ end
33
+ end
34
+
35
+ # Helper method to list all available providers
36
+ def self.available_providers
37
+ [:openai, :anthropic, :gemini, :cohere, :azure_openai, :mistral, :openrouter]
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+
5
+ module RailsAIPromptable
6
+ class TemplateRegistry
7
+ class << self
8
+ def templates
9
+ @templates ||= {}
10
+ end
11
+
12
+ # Register a template with a name
13
+ def register(name, template)
14
+ templates[name.to_sym] = template
15
+ end
16
+
17
+ # Get a template by name
18
+ def get(name)
19
+ templates[name.to_sym]
20
+ end
21
+
22
+ # Load templates from a YAML file
23
+ def load_from_file(file_path)
24
+ return unless File.exist?(file_path)
25
+
26
+ begin
27
+ loaded_templates = YAML.load_file(file_path)
28
+ return unless loaded_templates.is_a?(Hash)
29
+
30
+ loaded_templates.each do |name, template|
31
+ register(name, template)
32
+ end
33
+ rescue Psych::SyntaxError => e
34
+ # Log the error but don't raise - invalid YAML files are silently skipped
35
+ if defined?(Rails) && Rails.respond_to?(:logger) && Rails.logger
36
+ Rails.logger.warn("[rails_ai_promptable] Failed to load templates from #{file_path}: #{e.message}")
37
+ end
38
+ end
39
+ end
40
+
41
+ # Load templates from a directory
42
+ # Each file should be named <template_name>.yml or <template_name>.txt
43
+ def load_from_directory(directory_path)
44
+ return unless Dir.exist?(directory_path)
45
+
46
+ Dir.glob(File.join(directory_path, '*')).each do |file_path|
47
+ next unless File.file?(file_path)
48
+
49
+ name = File.basename(file_path, '.*')
50
+
51
+ if file_path.end_with?('.yml', '.yaml')
52
+ content = YAML.load_file(file_path)
53
+ template = content.is_a?(Hash) ? content['template'] : content.to_s
54
+ else
55
+ template = File.read(file_path)
56
+ end
57
+
58
+ register(name, template) if template
59
+ end
60
+ end
61
+
62
+ # Clear all registered templates
63
+ def clear!
64
+ @templates = {}
65
+ end
66
+
67
+ # List all registered template names
68
+ def list
69
+ templates.keys
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsAIPromptable
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_ai_promptable/version'
4
+ require 'rails_ai_promptable/configuration'
5
+ require 'rails_ai_promptable/template_registry'
6
+ require 'rails_ai_promptable/promptable'
7
+ require 'rails_ai_promptable/providers'
8
+ require 'rails_ai_promptable/logger'
9
+ require 'rails_ai_promptable/background_job'
10
+
11
+ module RailsAIPromptable
12
+ class << self
13
+ attr_accessor :configuration
14
+
15
+ def configure
16
+ self.configuration ||= Configuration.new
17
+ yield(configuration) if block_given?
18
+ end
19
+
20
+ def client
21
+ @client ||= Providers.for(configuration.provider, configuration)
22
+ end
23
+
24
+ def reset_client!
25
+ @client = nil
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,4 @@
1
+ module RailsAiPromptable
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata ADDED
@@ -0,0 +1,104 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rails_ai_promptable
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Shoaib Malik
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2025-11-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '7.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '7.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: httparty
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0.20'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0.20'
41
+ description: rails_ai_promptable makes it easy to integrate AI-driven features into
42
+ your Rails application. It allows you to define promptable methods, chain context,
43
+ and connect with AI APIs like OpenAI, Anthropic, or local LLMs with minimal setup.
44
+ email:
45
+ - shoaib2109@gmail.com
46
+ executables: []
47
+ extensions: []
48
+ extra_rdoc_files: []
49
+ files:
50
+ - ".rspec"
51
+ - ".rubocop.yml"
52
+ - CHANGELOG.md
53
+ - CODE_OF_CONDUCT.md
54
+ - LICENSE.txt
55
+ - README.md
56
+ - Rakefile
57
+ - lib/generators/rails_ai_promptable/install_generator.rb
58
+ - lib/generators/rails_ai_promptable/templates/POST_INSTALL
59
+ - lib/generators/rails_ai_promptable/templates/rails_ai_promptable.rb.tt
60
+ - lib/rails_ai_promptable.rb
61
+ - lib/rails_ai_promptable/background_job.rb
62
+ - lib/rails_ai_promptable/configuration.rb
63
+ - lib/rails_ai_promptable/logger.rb
64
+ - lib/rails_ai_promptable/promptable.rb
65
+ - lib/rails_ai_promptable/providers.rb
66
+ - lib/rails_ai_promptable/providers/anthropic_provider.rb
67
+ - lib/rails_ai_promptable/providers/azure_openai_provider.rb
68
+ - lib/rails_ai_promptable/providers/base_provider.rb
69
+ - lib/rails_ai_promptable/providers/cohere_provider.rb
70
+ - lib/rails_ai_promptable/providers/gemini_provider.rb
71
+ - lib/rails_ai_promptable/providers/mistral_provider.rb
72
+ - lib/rails_ai_promptable/providers/openai_provider.rb
73
+ - lib/rails_ai_promptable/providers/openrouter_provider.rb
74
+ - lib/rails_ai_promptable/template_registry.rb
75
+ - lib/rails_ai_promptable/version.rb
76
+ - sig/rails_ai_promptable.rbs
77
+ homepage: https://github.com/shoaibmalik786/rails_ai_promptable
78
+ licenses:
79
+ - MIT
80
+ metadata:
81
+ allowed_push_host: https://rubygems.org
82
+ homepage_uri: https://github.com/shoaibmalik786/rails_ai_promptable
83
+ source_code_uri: https://github.com/shoaibmalik786/rails_ai_promptable
84
+ changelog_uri: https://github.com/shoaibmalik786/rails_ai_promptable/blob/main/CHANGELOG.md
85
+ post_install_message:
86
+ rdoc_options: []
87
+ require_paths:
88
+ - lib
89
+ required_ruby_version: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - ">="
92
+ - !ruby/object:Gem::Version
93
+ version: 3.1.0
94
+ required_rubygems_version: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ requirements: []
100
+ rubygems_version: 3.4.10
101
+ signing_key:
102
+ specification_version: 4
103
+ summary: Add AI promptable behavior to your Rails models and classes.
104
+ test_files: []