rails-nl2sql 0.2.0 → 0.2.2
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.
- checksums.yaml +4 -4
- data/README.md +34 -1
- data/lib/generators/rails/nl2sql/templates/rails_nl2sql.rb +4 -1
- data/lib/generators/rails_nl2sql/install/templates/rails_nl2sql.rb +4 -1
- data/lib/rails/nl2sql/prompts/default.yml.erb +13 -0
- data/lib/rails/nl2sql/providers/anthropic_provider.rb +23 -0
- data/lib/rails/nl2sql/providers/base.rb +13 -0
- data/lib/rails/nl2sql/providers/llama_provider.rb +23 -0
- data/lib/rails/nl2sql/providers/openai_provider.rb +18 -0
- data/lib/rails/nl2sql/query_generator.rb +59 -115
- data/lib/rails/nl2sql/version.rb +1 -1
- data/lib/rails/nl2sql.rb +37 -33
- data/rails-nl2sql.gemspec +3 -2
- metadata +24 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2ffb9e53bea983b462fc1c550a4b53623bd9ff2934ba8a38776121833866ea45
|
4
|
+
data.tar.gz: 1cb0973477ea334d95e719e202879334c6f79505a199f08e8a1957b4c344384b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2ca2b1516ffe66ced4783bbe75f96d9b8801e6be6d1d070deba162b3c410f0dc77c643fcb666a30bc137e1395b317a453552a7f7ed5a14b250f008ad3d44d205
|
7
|
+
data.tar.gz: 4bbea3200d74e19cb3d5df7cfd3e5c4ce248c61cf2c5ad53e7b943eb283b89bd24fe5fac3c43e925f05ea1b98f269eb19fef518a4bd5f3f6567ab1ad30433180
|
data/README.md
CHANGED
@@ -28,7 +28,10 @@ This will create an initializer file at `config/initializers/rails_nl2sql.rb`. Y
|
|
28
28
|
# config/initializers/rails_nl2sql.rb
|
29
29
|
Rails::Nl2sql.configure do |config|
|
30
30
|
config.api_key = ENV["OPENAI_API_KEY"] # It's recommended to use an environment variable
|
31
|
-
# config.model = "
|
31
|
+
# config.model = "gpt-3.5-turbo-instruct" # Optional
|
32
|
+
# config.provider = Rails::Nl2sql::Providers::OpenaiProvider.new(api_key: config.api_key)
|
33
|
+
# config.prompt_template_path = Rails::Nl2sql.prompt_template_path
|
34
|
+
# config.max_schema_lines = 200
|
32
35
|
end
|
33
36
|
```
|
34
37
|
|
@@ -82,6 +85,36 @@ You can clear the cached schema if your database changes:
|
|
82
85
|
Rails::Nl2sql::SchemaBuilder.clear_cache!
|
83
86
|
```
|
84
87
|
|
88
|
+
## Pluggable LLM Providers
|
89
|
+
|
90
|
+
Rails NL2SQL ships with a simple adapter system so you can use different large language model providers.
|
91
|
+
By default the gem uses OpenAI, but you can plug in others like Anthropic or a local Llama‑based HTTP endpoint.
|
92
|
+
|
93
|
+
```ruby
|
94
|
+
Rails::Nl2sql.configure do |config|
|
95
|
+
config.provider = Rails::Nl2sql::Providers::AnthropicProvider.new(api_key: ENV['ANTHROPIC_KEY'])
|
96
|
+
end
|
97
|
+
```
|
98
|
+
|
99
|
+
## Prompt Templates
|
100
|
+
|
101
|
+
The prompts used to talk to the LLM are defined in a YAML/ERB template. You can override this template
|
102
|
+
to enforce your own naming conventions or add company specific instructions.
|
103
|
+
|
104
|
+
```yaml
|
105
|
+
system: |
|
106
|
+
Custom system prompt text...
|
107
|
+
user: |
|
108
|
+
Query: <%= input %>
|
109
|
+
```
|
110
|
+
|
111
|
+
Set the path via `config.prompt_template_path`.
|
112
|
+
|
113
|
+
## Context Window Management
|
114
|
+
|
115
|
+
Large schemas can exceed the model context window. Use `config.max_schema_lines` to automatically truncate
|
116
|
+
the schema snippet sent to the model. Only the first N lines are included.
|
117
|
+
|
85
118
|
## Development
|
86
119
|
|
87
120
|
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
@@ -1,4 +1,7 @@
|
|
1
1
|
Rails::Nl2sql.configure do |config|
|
2
2
|
config.api_key = "YOUR_API_KEY"
|
3
|
-
# config.model = "
|
3
|
+
# config.model = "gpt-3.5-turbo-instruct"
|
4
|
+
# config.provider = Rails::Nl2sql::Providers::OpenaiProvider.new(api_key: config.api_key)
|
5
|
+
# config.prompt_template_path = Rails::Nl2sql.prompt_template_path
|
6
|
+
# config.max_schema_lines = 200
|
4
7
|
end
|
@@ -1,4 +1,7 @@
|
|
1
1
|
Rails::Nl2sql.configure do |config|
|
2
2
|
config.api_key = "YOUR_API_KEY"
|
3
|
-
# config.model = "
|
3
|
+
# config.model = "gpt-3.5-turbo-instruct"
|
4
|
+
# config.provider = Rails::Nl2sql::Providers::OpenaiProvider.new(api_key: config.api_key)
|
5
|
+
# config.prompt_template_path = Rails::Nl2sql.prompt_template_path
|
6
|
+
# config.max_schema_lines = 200
|
4
7
|
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
system: |
|
2
|
+
You are an expert SQL assistant specializing in generating dynamic queries based on natural language.
|
3
|
+
Your primary goal is to generate **correct, safe, and executable <%= db_server %> SQL queries** based on user questions.
|
4
|
+
|
5
|
+
---
|
6
|
+
**DATABASE CONTEXT (SCHEMA):**
|
7
|
+
<%= retrieved_context %>
|
8
|
+
---
|
9
|
+
Follow best practices and never generate DML or DDL statements.
|
10
|
+
Respond only with SQL.
|
11
|
+
user: |
|
12
|
+
Here is the USER QUESTION:
|
13
|
+
<%= input %>
|
@@ -0,0 +1,23 @@
|
|
1
|
+
begin
|
2
|
+
require 'anthropic'
|
3
|
+
rescue LoadError
|
4
|
+
warn 'Anthropic gem not installed; AnthropicProvider will not work'
|
5
|
+
end
|
6
|
+
|
7
|
+
module Rails
|
8
|
+
module Nl2sql
|
9
|
+
module Providers
|
10
|
+
class AnthropicProvider < Base
|
11
|
+
def initialize(api_key:, model: 'claude-3-opus-20240229')
|
12
|
+
raise 'anthropic gem missing' unless defined?(::Anthropic::Client)
|
13
|
+
@client = ::Anthropic::Client.new(api_key: api_key)
|
14
|
+
@model = model
|
15
|
+
end
|
16
|
+
|
17
|
+
def complete(prompt:, **params)
|
18
|
+
@client.completions(model: @model, prompt: prompt, **params)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'uri'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module Rails
|
6
|
+
module Nl2sql
|
7
|
+
module Providers
|
8
|
+
class LlamaProvider < Base
|
9
|
+
def initialize(endpoint:, model: nil)
|
10
|
+
@uri = URI.parse(endpoint)
|
11
|
+
@model = model
|
12
|
+
end
|
13
|
+
|
14
|
+
def complete(prompt:, **_params)
|
15
|
+
http = Net::HTTP.new(@uri.host, @uri.port)
|
16
|
+
http.use_ssl = @uri.scheme == 'https'
|
17
|
+
response = http.post(@uri.path, {prompt: prompt, model: @model}.to_json, 'Content-Type' => 'application/json')
|
18
|
+
JSON.parse(response.body)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'openai'
|
2
|
+
|
3
|
+
module Rails
|
4
|
+
module Nl2sql
|
5
|
+
module Providers
|
6
|
+
class OpenaiProvider < Base
|
7
|
+
def initialize(api_key:, model: 'gpt-3.5-turbo-instruct')
|
8
|
+
@client = ::OpenAI::Client.new(access_token: api_key)
|
9
|
+
@model = model
|
10
|
+
end
|
11
|
+
|
12
|
+
def complete(prompt:, **params)
|
13
|
+
@client.completions(parameters: {model: @model, prompt: prompt}.merge(params))
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -1,184 +1,128 @@
|
|
1
|
-
require "
|
2
|
-
|
1
|
+
require "erb"
|
2
|
+
require "yaml"
|
3
3
|
module Rails
|
4
4
|
module Nl2sql
|
5
5
|
class QueryGenerator
|
6
|
-
|
7
|
-
|
6
|
+
DEFAULT_MODEL = 'gpt-3.5-turbo-instruct'
|
7
|
+
|
8
|
+
def initialize(provider: nil, model: DEFAULT_MODEL)
|
9
|
+
@provider = provider || Rails::Nl2sql.provider || default_provider(model)
|
8
10
|
@model = model
|
9
11
|
end
|
10
12
|
|
11
|
-
def generate_query(prompt, schema, db_server =
|
13
|
+
def generate_query(prompt, schema, db_server = 'PostgreSQL', tables = nil)
|
12
14
|
retrieved_context = build_context(schema, tables)
|
13
|
-
|
14
|
-
|
15
|
-
puts "=== SCHEMA CONTEXT BEING SENT TO AI ==="
|
16
|
-
puts retrieved_context
|
17
|
-
puts "=== END SCHEMA CONTEXT ==="
|
18
|
-
|
19
|
-
system_prompt = build_system_prompt(db_server, retrieved_context)
|
20
|
-
user_prompt = build_user_prompt(prompt)
|
21
|
-
|
15
|
+
|
16
|
+
system_prompt, user_prompt = build_prompts(prompt, db_server, retrieved_context)
|
22
17
|
full_prompt = "#{system_prompt}\n\n#{user_prompt}"
|
23
18
|
|
24
|
-
response = @
|
25
|
-
|
26
|
-
|
27
|
-
prompt: full_prompt,
|
28
|
-
max_tokens: 500,
|
29
|
-
temperature: 0.1
|
30
|
-
}
|
31
|
-
)
|
32
|
-
|
33
|
-
generated_query = response.dig("choices", 0, "text")&.strip
|
34
|
-
|
35
|
-
# Clean up the response to remove markdown formatting
|
19
|
+
response = @provider.complete(prompt: full_prompt, max_tokens: 500, temperature: 0.1)
|
20
|
+
generated_query = extract_text(response)
|
21
|
+
|
36
22
|
generated_query = clean_sql_response(generated_query)
|
37
|
-
|
38
|
-
# Safety check
|
39
23
|
validate_query_safety(generated_query)
|
40
|
-
|
24
|
+
|
41
25
|
generated_query
|
42
26
|
end
|
43
27
|
|
44
28
|
private
|
45
29
|
|
30
|
+
def default_provider(model)
|
31
|
+
Providers::OpenaiProvider.new(api_key: Rails::Nl2sql.api_key, model: model)
|
32
|
+
end
|
33
|
+
|
34
|
+
def extract_text(response)
|
35
|
+
if response.is_a?(Hash)
|
36
|
+
response.dig('choices', 0, 'text')&.strip
|
37
|
+
else
|
38
|
+
nil
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
46
42
|
def build_context(schema, tables)
|
47
|
-
if tables&.any?
|
48
|
-
|
49
|
-
filtered_schema = filter_schema_by_tables(schema, tables)
|
50
|
-
filtered_schema
|
43
|
+
context = if tables&.any?
|
44
|
+
filter_schema_by_tables(schema, tables)
|
51
45
|
else
|
52
46
|
schema
|
53
47
|
end
|
48
|
+
apply_context_window(context)
|
49
|
+
end
|
50
|
+
|
51
|
+
def apply_context_window(context)
|
52
|
+
max_lines = Rails::Nl2sql.max_schema_lines
|
53
|
+
return context unless max_lines
|
54
|
+
|
55
|
+
lines = context.split("\n")
|
56
|
+
return context if lines.length <= max_lines
|
57
|
+
|
58
|
+
lines.first(max_lines).join("\n")
|
54
59
|
end
|
55
60
|
|
56
61
|
def filter_schema_by_tables(schema, tables)
|
57
|
-
# Simple filtering - in a real implementation, this would be more sophisticated
|
58
62
|
lines = schema.split("\n")
|
59
63
|
filtered_lines = []
|
60
64
|
current_table = nil
|
61
65
|
include_current = false
|
62
|
-
|
66
|
+
|
63
67
|
lines.each do |line|
|
64
68
|
if line.match(/CREATE TABLE (\w+)/)
|
65
|
-
current_table =
|
69
|
+
current_table = Regexp.last_match(1)
|
66
70
|
include_current = tables.include?(current_table)
|
67
71
|
end
|
68
|
-
|
69
|
-
if include_current || line.strip.empty?
|
70
|
-
filtered_lines << line
|
71
|
-
end
|
72
|
+
|
73
|
+
filtered_lines << line if include_current || line.strip.empty?
|
72
74
|
end
|
73
|
-
|
74
|
-
filtered_lines.join("\n")
|
75
|
-
end
|
76
75
|
|
77
|
-
|
78
|
-
<<~PROMPT
|
79
|
-
You are an expert SQL assistant specializing in generating dynamic queries based on natural language.
|
80
|
-
Your primary goal is to generate **correct, safe, and executable #{db_server} SQL queries** based on user questions.
|
81
|
-
|
82
|
-
---
|
83
|
-
**DATABASE CONTEXT (SCHEMA):**
|
84
|
-
You are provided with relevant schema details from the database, retrieved to help you.
|
85
|
-
**STRICTLY adhere to this provided schema context.** Do not use any tables or columns not explicitly listed here.
|
86
|
-
#{retrieved_context}
|
87
|
-
|
88
|
-
---
|
89
|
-
**SQL GENERATION RULES:**
|
90
|
-
1. **SQL Dialect:** All generated SQL must be valid **#{db_server} syntax**.
|
91
|
-
* For limiting results, use LIMIT (e.g., LIMIT 10) instead of TOP.
|
92
|
-
* Be mindful of #{db_server}'s specific function names (e.g., COUNT(*), MAX()) and behaviors.
|
93
|
-
* For subqueries that return a single value to be used in a WHERE clause, ensure they are correctly formatted for #{db_server}.
|
94
|
-
2. **Schema Adherence:** Only use table names and column names that are explicitly present in the provided context. Do not invent names.
|
95
|
-
3. **Valid JOIN Paths:** All `JOIN` operations must be based on valid foreign key relationships. The provided schema context explicitly details many of these.
|
96
|
-
4. **Safety First:** Absolutely **DO NOT** generate any DDL (CREATE, ALTER, DROP) or DML (INSERT, UPDATE, DELETE) statements. Only `SELECT` queries are permitted.
|
97
|
-
5. **CRITICAL: Handling Missing/Empty Text Data:**
|
98
|
-
* When a user asks about "missing," "no," "empty," or "null" values for a TEXT column (like 'email', 'phone', 'address', 'company', 'fax'), generate a `WHERE` clause that explicitly checks for **both `IS NULL` and `= ''` (an empty string)**.
|
99
|
-
* **Example:** To find agents with no email, the query should be `SELECT first_name, last_name FROM agents WHERE email IS NULL OR email = '';`
|
100
|
-
* This is essential
|
101
|
-
6. **Ambiguity:** If a user question is ambiguous or requires more information to form a precise SQL query, clearly state that you need clarification and ask for more details. Do not guess.
|
102
|
-
|
103
|
-
**RESPOND WITH ONLY THE SQL QUERY - NO EXPLANATIONS, NO MARKDOWN FORMATTING, NO CODE BLOCKS, NO ADDITIONAL TEXT.**
|
104
|
-
PROMPT
|
76
|
+
filtered_lines.join("\n")
|
105
77
|
end
|
106
78
|
|
107
|
-
def
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
79
|
+
def build_prompts(input, db_server, retrieved_context)
|
80
|
+
template = Rails::Nl2sql.prompt_template
|
81
|
+
system_prompt = ERB.new(template['system']).result(binding)
|
82
|
+
user_prompt = ERB.new(template['user']).result(binding)
|
83
|
+
[system_prompt, user_prompt]
|
112
84
|
end
|
113
85
|
|
114
86
|
def clean_sql_response(query)
|
115
87
|
return query unless query
|
116
88
|
|
117
|
-
# Remove markdown code blocks
|
118
89
|
query = query.gsub(/```sql\n?/, '')
|
119
90
|
query = query.gsub(/```\n?/, '')
|
120
|
-
|
121
|
-
# Remove any leading/trailing whitespace
|
122
91
|
query = query.strip
|
123
|
-
|
124
|
-
# Remove any explanatory text before or after the query
|
125
|
-
# Look for common patterns like "Here's the SQL query:" or "The query is:"
|
126
92
|
query = query.gsub(/^.*?(SELECT|WITH|INSERT|UPDATE|DELETE|CREATE|DROP|ALTER)/i, '\1')
|
127
|
-
|
128
|
-
# Remove any trailing explanatory text after the query
|
129
|
-
# Split by newlines and take only the SQL part
|
130
93
|
lines = query.split("\n")
|
131
94
|
sql_lines = []
|
132
|
-
|
95
|
+
|
133
96
|
lines.each do |line|
|
134
97
|
line = line.strip
|
135
|
-
# Skip empty lines or lines that look like explanations
|
136
98
|
next if line.empty?
|
137
99
|
next if line.match(/^(here|this|the query|explanation|note)/i)
|
138
|
-
|
100
|
+
|
139
101
|
sql_lines << line
|
140
102
|
end
|
141
|
-
|
142
|
-
# Rejoin the SQL lines
|
103
|
+
|
143
104
|
cleaned_query = sql_lines.join("\n").strip
|
144
|
-
|
145
|
-
# Ensure it ends with a semicolon if it's a complete query
|
146
|
-
if cleaned_query.match(/^(SELECT|WITH)/i) && !cleaned_query.end_with?(';')
|
147
|
-
cleaned_query += ';'
|
148
|
-
end
|
149
|
-
|
105
|
+
cleaned_query += ';' if cleaned_query.match(/^(SELECT|WITH)/i) && !cleaned_query.end_with?(';')
|
150
106
|
cleaned_query
|
151
107
|
end
|
152
108
|
|
153
109
|
def validate_query_safety(query)
|
154
110
|
return unless query
|
155
111
|
|
156
|
-
banned_keywords = [
|
157
|
-
"delete", "drop", "truncate", "update", "insert", "alter",
|
158
|
-
"exec", "execute", "create", "merge", "replace", "into"
|
159
|
-
]
|
160
|
-
|
112
|
+
banned_keywords = %w[delete drop truncate update insert alter exec execute create merge replace into]
|
161
113
|
banned_phrases = [
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
114
|
+
'ignore previous instructions', 'pretend you are', 'i am the admin',
|
115
|
+
'you are no longer bound', 'bypass the rules', 'run this instead',
|
116
|
+
'for testing, run', 'no safety constraints', 'show me a dangerous query',
|
117
|
+
'this is a dev environment', 'drop all data', 'delete all users', 'wipe the database'
|
166
118
|
]
|
167
119
|
|
168
120
|
query_lower = query.downcase
|
169
|
-
|
170
|
-
# Check for banned keywords
|
171
121
|
banned_keywords.each do |keyword|
|
172
|
-
if query_lower.include?(keyword)
|
173
|
-
raise Rails::Nl2sql::Error, "Query contains banned keyword: #{keyword}"
|
174
|
-
end
|
122
|
+
raise Rails::Nl2sql::Error, "Query contains banned keyword: #{keyword}" if query_lower.include?(keyword)
|
175
123
|
end
|
176
|
-
|
177
|
-
# Check for banned phrases
|
178
124
|
banned_phrases.each do |phrase|
|
179
|
-
if query_lower.include?(phrase)
|
180
|
-
raise Rails::Nl2sql::Error, "Query contains banned phrase: #{phrase}"
|
181
|
-
end
|
125
|
+
raise Rails::Nl2sql::Error, "Query contains banned phrase: #{phrase}" if query_lower.include?(phrase)
|
182
126
|
end
|
183
127
|
end
|
184
128
|
end
|
data/lib/rails/nl2sql/version.rb
CHANGED
data/lib/rails/nl2sql.rb
CHANGED
@@ -1,9 +1,15 @@
|
|
1
1
|
require "rails/nl2sql/version"
|
2
|
+
require "rails/nl2sql/providers/base"
|
3
|
+
require "rails/nl2sql/providers/openai_provider"
|
4
|
+
require "rails/nl2sql/providers/anthropic_provider"
|
5
|
+
require "rails/nl2sql/providers/llama_provider"
|
2
6
|
require "rails/nl2sql/query_generator"
|
3
7
|
require "rails/nl2sql/schema_builder"
|
4
8
|
require "rails/nl2sql/query_validator"
|
5
9
|
require "rails/nl2sql/active_record_extension"
|
6
10
|
require "rails/nl2sql/railtie" if defined?(Rails)
|
11
|
+
require 'yaml'
|
12
|
+
require 'erb'
|
7
13
|
|
8
14
|
module Rails
|
9
15
|
module Nl2sql
|
@@ -12,67 +18,65 @@ module Rails
|
|
12
18
|
class << self
|
13
19
|
attr_accessor :api_key
|
14
20
|
attr_accessor :model
|
21
|
+
attr_accessor :provider
|
22
|
+
attr_accessor :max_schema_lines
|
23
|
+
|
24
|
+
def prompt_template_path=(path)
|
25
|
+
@prompt_template = nil
|
26
|
+
@prompt_template_path = path
|
27
|
+
end
|
28
|
+
|
29
|
+
def prompt_template_path
|
30
|
+
@prompt_template_path || File.expand_path('nl2sql/prompts/default.yml.erb', __dir__)
|
31
|
+
end
|
15
32
|
end
|
16
|
-
|
33
|
+
|
34
|
+
@model = 'gpt-3.5-turbo-instruct'
|
35
|
+
@max_schema_lines = 200
|
17
36
|
|
18
37
|
def self.configure
|
19
38
|
yield self
|
20
39
|
end
|
21
40
|
|
41
|
+
def self.prompt_template
|
42
|
+
@prompt_template ||= begin
|
43
|
+
erb = ERB.new(File.read(prompt_template_path))
|
44
|
+
YAML.safe_load(erb.result)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
22
48
|
class Processor
|
23
49
|
def self.execute(natural_language_query, options = {})
|
24
|
-
# Get database type
|
25
50
|
db_server = SchemaBuilder.get_database_type
|
26
|
-
|
27
|
-
# Build schema with optional table filtering
|
28
51
|
schema = SchemaBuilder.build_schema(options)
|
29
|
-
|
30
|
-
# Debug: Show what schema is being built
|
31
|
-
puts "=== RAW SCHEMA FROM BUILDER ==="
|
32
|
-
puts schema
|
33
|
-
puts "=== END RAW SCHEMA ==="
|
34
|
-
|
35
|
-
# Extract tables for filtering if specified
|
36
52
|
tables = options[:tables]
|
37
|
-
|
38
|
-
|
39
|
-
query_generator = QueryGenerator.new(Rails::Nl2sql.api_key, Rails::Nl2sql.model)
|
53
|
+
|
54
|
+
query_generator = QueryGenerator.new(model: Rails::Nl2sql.model)
|
40
55
|
generated_query = query_generator.generate_query(
|
41
|
-
natural_language_query,
|
42
|
-
schema,
|
43
|
-
db_server,
|
56
|
+
natural_language_query,
|
57
|
+
schema,
|
58
|
+
db_server,
|
44
59
|
tables
|
45
60
|
)
|
46
61
|
|
47
|
-
# Validate the generated query
|
48
62
|
QueryValidator.validate(generated_query)
|
49
|
-
|
50
|
-
# Execute the query
|
51
63
|
ActiveRecord::Base.connection.execute(generated_query)
|
52
64
|
end
|
53
65
|
|
54
66
|
def self.generate_query_only(natural_language_query, options = {})
|
55
|
-
# Get database type
|
56
67
|
db_server = SchemaBuilder.get_database_type
|
57
|
-
|
58
|
-
# Build schema with optional table filtering
|
59
68
|
schema = SchemaBuilder.build_schema(options)
|
60
|
-
|
61
|
-
# Extract tables for filtering if specified
|
62
69
|
tables = options[:tables]
|
63
|
-
|
64
|
-
|
65
|
-
query_generator = QueryGenerator.new(Rails::Nl2sql.api_key, Rails::Nl2sql.model)
|
70
|
+
|
71
|
+
query_generator = QueryGenerator.new(model: Rails::Nl2sql.model)
|
66
72
|
generated_query = query_generator.generate_query(
|
67
|
-
natural_language_query,
|
68
|
-
schema,
|
69
|
-
db_server,
|
73
|
+
natural_language_query,
|
74
|
+
schema,
|
75
|
+
db_server,
|
70
76
|
tables
|
71
77
|
)
|
72
78
|
|
73
|
-
# Validate the generated query
|
74
79
|
QueryValidator.validate(generated_query)
|
75
|
-
|
76
80
|
generated_query
|
77
81
|
end
|
78
82
|
|
data/rails-nl2sql.gemspec
CHANGED
@@ -37,8 +37,9 @@ Gem::Specification.new do |spec|
|
|
37
37
|
spec.require_paths = ["lib"]
|
38
38
|
|
39
39
|
spec.add_dependency "openai", "~> 0.3"
|
40
|
-
spec.
|
40
|
+
spec.add_dependency "anthropic", "~> 0.1"
|
41
|
+
spec.add_development_dependency "bundler", "~> 2.0"
|
41
42
|
spec.add_development_dependency "rake", "~> 10.0"
|
42
43
|
spec.add_development_dependency "rspec-rails", "~> 6.0"
|
43
|
-
spec.add_dependency "railties", "
|
44
|
+
spec.add_dependency "railties", "~> 6.0"
|
44
45
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rails-nl2sql
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Russell Van Curen
|
@@ -24,18 +24,32 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: anthropic
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.1'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.1'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: bundler
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
30
44
|
requirements:
|
31
|
-
- - "
|
45
|
+
- - "~>"
|
32
46
|
- !ruby/object:Gem::Version
|
33
47
|
version: '2.0'
|
34
48
|
type: :development
|
35
49
|
prerelease: false
|
36
50
|
version_requirements: !ruby/object:Gem::Requirement
|
37
51
|
requirements:
|
38
|
-
- - "
|
52
|
+
- - "~>"
|
39
53
|
- !ruby/object:Gem::Version
|
40
54
|
version: '2.0'
|
41
55
|
- !ruby/object:Gem::Dependency
|
@@ -70,14 +84,14 @@ dependencies:
|
|
70
84
|
name: railties
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
72
86
|
requirements:
|
73
|
-
- - "
|
87
|
+
- - "~>"
|
74
88
|
- !ruby/object:Gem::Version
|
75
89
|
version: '6.0'
|
76
90
|
type: :runtime
|
77
91
|
prerelease: false
|
78
92
|
version_requirements: !ruby/object:Gem::Requirement
|
79
93
|
requirements:
|
80
|
-
- - "
|
94
|
+
- - "~>"
|
81
95
|
- !ruby/object:Gem::Version
|
82
96
|
version: '6.0'
|
83
97
|
description: This gem provides an easy way to integrate natural language to SQL functionality
|
@@ -105,6 +119,11 @@ files:
|
|
105
119
|
- lib/rails-nl2sql.rb
|
106
120
|
- lib/rails/nl2sql.rb
|
107
121
|
- lib/rails/nl2sql/active_record_extension.rb
|
122
|
+
- lib/rails/nl2sql/prompts/default.yml.erb
|
123
|
+
- lib/rails/nl2sql/providers/anthropic_provider.rb
|
124
|
+
- lib/rails/nl2sql/providers/base.rb
|
125
|
+
- lib/rails/nl2sql/providers/llama_provider.rb
|
126
|
+
- lib/rails/nl2sql/providers/openai_provider.rb
|
108
127
|
- lib/rails/nl2sql/query_generator.rb
|
109
128
|
- lib/rails/nl2sql/query_validator.rb
|
110
129
|
- lib/rails/nl2sql/railtie.rb
|