asktive_record 0.1.0 → 0.1.4

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 (4) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +94 -65
  3. data/lib/asktive_record/version.rb +1 -1
  4. metadata +11 -37
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2e20e30ee66329ea0c41cc3e7392accac2ff9484ce4042d8646923ea4974dad1
4
- data.tar.gz: 44f27094d7ef5df1435007c1d12e4b83223bf07cf213b0630a1c5871e97d5381
3
+ metadata.gz: 06cd09084c657a6e24c587a1e19fff6ac6325bbb8d0f96eac05fca3fa7c26873
4
+ data.tar.gz: 5223d7f782965e07fc635cd32911f34571a9da511c4b8473441f11623681b998
5
5
  SHA512:
6
- metadata.gz: 247b0fa1a3aca1a5b06b60efcf69ab77d6ebd5fdd5b1e3c4d289607641944aa2d14a64c8a18796f0d8724a7ff60b90721d61d02668a72b1b289b40974953d843
7
- data.tar.gz: b282ee2516300e3cb83694ebedcd87d457e310922674b19e6a067a84f0c50e867b4aa8540593ad2393ae08a47d1ba4b8a55b49de3b8d81fbdc0c189f27045582
6
+ metadata.gz: d95e9df03b249f4bc6b7d9b0020ea709e66f1b09b75fcfdb11a097157f4284c446dfc6d8c9adfbe246637a2a79740095052a64f60e16aad209837d4a12210efe
7
+ data.tar.gz: ae81db4d959bfd54ab3d53c7eff851269aae93fa3955e1acfdd1a7653667056d67803f00cda10b1d00702bb17e550640ede456ce6d2bf0fe37dc7f0d93e740fe
data/README.md CHANGED
@@ -1,16 +1,17 @@
1
- # AsktiveRecord: Natural Language Database Querying for Ruby on Rails
1
+ # AsktiveRecord: A Ruby gem that lets your data answer like a human
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/asktive_record.svg)](https://badge.fury.io/rb/asktive_record) <!-- Placeholder: Update once published -->
4
4
  [![Build Status](https://github.com/rpossan/asktive_record/actions/workflows/main.yml/badge.svg)](https://github.com/rpossan/asktive_record/actions/workflows/main.yml) <!-- Placeholder: Update with correct repo path -->
5
5
 
6
- **AsktiveRecord** is a Ruby gem designed to bridge the gap between human language and database queries. It integrates with Large Language Models (LLMs) like OpenAI's ChatGPT to allow developers to query their Rails application's database using natural language.
7
-
8
- Imagine your users (or even you, the developer!) asking questions like "*Show me the last five users who signed up*" or "*Which products had the most sales last month?*" and getting back the actual data, powered by an LLM translating these questions into SQL.
6
+ > **AsktiveRecord** is a Ruby gem designed to bridge the gap between human language and database queries. It lets you interact with your Rails database as if you were having a conversation with a knowledgeable assistant. Instead of writing SQL or chaining ActiveRecord methods, you simply ask questions in plain English—like (or any language) "Who are my newest users?" or "What products sold the most last month?"—and get clear, human-friendly answers. AsktiveRecord translates your questions into database queries behind the scenes, so you can focus on what you want to know, not how to write the query.
9
7
 
10
8
  ## Features
11
9
 
12
10
  * **Natural Language to SQL**: Convert human-readable questions into SQL queries.
13
11
  * **LLM Integration**: Currently supports OpenAI's ChatGPT, with a design that allows for future expansion to other LLMs (e.g., Gemini).
12
+ * **Get Answers, Not Just Data**: Use the `.answer` method to get concise, human-readable responses to your queries, rather than raw data or SQL.
13
+ * **Avoid ActiveRecord Chaining and SQL**: No need to write complex ActiveRecord queries or SQL statements. Just ask your question in natural language.
14
+ * **Works with Multiple Languages**: While the gem is designed with English in mind, it can handle queries in other languages, depending on the LLM's capabilities.
14
15
  * **Flexible Querying Options**:
15
16
  * Use with specific models (e.g., `User.ask("query")`)
16
17
  * Use with service classes to query any table (e.g., `AskService.ask("query")`)
@@ -58,8 +59,6 @@ After installing the gem, you need to run the installer to generate the configur
58
59
 
59
60
  ```bash
60
61
  $ bundle exec rails generate asktive_record:install
61
- # or, if you're not in a full Rails app context for the generator (less common for this gem):
62
- # $ bundle exec asktive_record:install (This might require further setup for standalone use)
63
62
  ```
64
63
 
65
64
  This will create an initializer file at `config/initializers/asktive_record.rb`.
@@ -110,10 +109,20 @@ Run the setup command to help AsktiveRecord understand your database structure.
110
109
 
111
110
  ```bash
112
111
  $ bundle exec rails generate asktive_record:setup
113
- # or
114
- # $ bundle exec asktive_record:setup
115
112
  ```
116
113
 
114
+ If your app uses a custom schema file or a non-standard schema location, you can specify the path in your configuration. For example, if your schema is located at `db/custom_schema.rb`, update your initializer:
115
+
116
+ ```ruby
117
+ AsktiveRecord.configure do |config|
118
+ config.db_schema_path = "db/custom_schema.rb"
119
+ config.skip_dump_schema = true # If your app uses a legacy schema or doesn't need to dump it using rails db:schema:dump (default is false)
120
+ end
121
+ ```
122
+
123
+ This ensures AsktiveRecord reads the correct schema file when providing context to the LLM. Make sure the specified file accurately reflects your database structure.
124
+
125
+
117
126
  This step ensures that the LLM has the necessary context about your tables and columns to generate accurate SQL queries. The schema content is passed with each query to the LLM in the current version.
118
127
 
119
128
  ## Usage
@@ -123,25 +132,32 @@ AsktiveRecord offers two ways to query your database using natural language:
123
132
  ### 1. Model-Specific Querying
124
133
 
125
134
  This approach ties queries to specific models, ideal when you know which table you want to query.
135
+ If you want to apply AsktiveRecord for all your Rails models, add the `include AsktiveRecord` line in your `ApplicationRecord` or specific models. This allows you to use the `.ask` method directly on those models.
126
136
 
127
137
  ```ruby
128
138
  # First, include AsktiveRecord in your ApplicationRecord or specific models
129
139
  class ApplicationRecord < ActiveRecord::Base
130
140
  primary_abstract_class
131
- # Include AsktiveRecord here if you want all models to have the .ask method
132
- # include AsktiveRecord
141
+ include AsktiveRecord
133
142
  end
134
143
 
135
- # Then, in your specific model, activate AsktiveRecord
144
+ # Or in a specific model
145
+ # In this case, you can query the User model directly for the model table. All queries will be scoped to the users table.
136
146
  class User < ApplicationRecord
137
- include AsktiveRecord # Include the module
138
- asktive_record # Activate model-specific setup (optional, future use)
147
+ include AsktiveRecord
139
148
  end
140
149
 
141
150
  # Now you can query the User model directly
142
- natural_query = "Find the last five users who signed up"
143
- asktive_record_query = User.ask(natural_query)
144
- # => Returns a Query object with SQL targeting the users table
151
+ query = User.ask("Show me the last five users who signed up")
152
+ # => Returns a Query object with SQL targeting the users table, not the sql executed yet
153
+
154
+ # Call the execute method to run the query on the database
155
+ results = query.execute
156
+ # => Returns an array of User objects (if the query is a SELECT) or raises an
157
+
158
+ # If you want to execute the query and get the response like human use the method answer
159
+ results = query.answer
160
+ # => Returns a string with the answer to the question, e.g., "The last five users who signed up are: [User1, User2, User3, User4, User5]"
145
161
  ```
146
162
 
147
163
  ### 2. Service-Class Querying (Any Table)
@@ -156,74 +172,62 @@ class AskService
156
172
  end
157
173
 
158
174
  # Now you can query any table through this service
159
- natural_query = "Which is the last user created?"
160
- asktive_record_query = AskService.ask(natural_query)
161
- # => Returns a Query object with SQL targeting the users table
175
+ asktive_record_query = AskService.ask("Which is the last user created?")
176
+ # => Returns a Query object with SQL targeting the users table, not the sql executed yet
162
177
 
163
- natural_query = "Which is the cheapest product?"
164
- asktive_record_query = AskService.ask(natural_query)
165
- # => Returns a Query object with SQL targeting the products table
178
+ asktive_record_query = AskService.ask("Which is the cheapest product?").execute
179
+ # => Returns an ActiveRecord::Result object (array of hashes) with the cheapest product details
166
180
 
167
- natural_query = "Show me products with their categories"
168
- asktive_record_query = AskService.ask(natural_query)
181
+ asktive_record_query = AskService.ask("Show me products with their categories").answer
169
182
  # => Returns a Query object with SQL that might include JOINs between products and categories
183
+ # => Returns a string with the answer to the question, e.g., "The products with their categories are: [Product1, Product2, ...]"
170
184
  ```
171
185
 
172
- You can also use AsktiveRecord directly for one-off queries:
186
+ ### Working with Query Results
173
187
 
188
+ Once you have executed a query, you can work with the results. The `execute` method returns different types of results based on the context:
189
+ * If the query is from a model (e.g., `User.ask(...)`), it returns an array of model instances (e.g., `User` objects).
190
+ * If the query is from a service class (e.g., `AskService.ask(...)`), it returns an `ActiveRecord::Result` object, which is an array of hashes representing the query results.
174
191
  ```ruby
175
- natural_query = "Show me all active subscriptions with their users"
176
- asktive_record_query = AsktiveRecord.ask(natural_query)
192
+ # Example of working with results from a model query
193
+ query = User.ask("Who are my newest users?")
194
+ results = query.execute
195
+ # => results is an array of User objects
177
196
  ```
178
197
 
179
- ### Working with Query Results
198
+ ### The `AsktiveRecord::Query` Object
199
+
200
+ ### The `.answer` Method
201
+
202
+ The `.answer` method provides a human-friendly, natural language response to your query, instead of returning raw data or SQL. When you call `.answer` on a query object, AsktiveRecord executes the query and uses the LLM to generate a concise, readable answer based on the results.
203
+
204
+ ### Example Usage
180
205
 
181
- Regardless of which approach you use, you'll get a `AsktiveRecord::Query` object that you can work with:
182
206
 
183
207
  ```ruby
184
- # 1. Get the generated SQL
185
- puts "LLM Generated SQL: #{asktive_record_query.raw_sql}"
186
- # => LLM Generated SQL: SELECT * FROM users ORDER BY created_at DESC LIMIT 5
187
-
188
- # 2. (Recommended) Sanitize the query
189
- begin
190
- asktive_record_query.sanitize! # Raises AsktiveRecord::SanitizationError if it's not a SELECT query by default
191
- puts "Sanitized SQL: #{asktive_record_query.sanitized_sql}"
192
- rescue AsktiveRecord::SanitizationError => e
193
- puts "Error: #{e.message}"
194
- # Handle error, maybe log it or don't execute the query
195
- return
196
- end
208
+ # Using a service class to ask a question
209
+ response = AskService.ask("Which is the cheapest product?").answer
210
+ # => "The cheapest product is the Earphone."
197
211
 
198
- # 3. Execute the query
199
- begin
200
- results = asktive_record_query.execute
201
- # Process the results
202
- # - If executed via a model (e.g., User.ask), results will be an array of User objects.
203
- # - If executed via a service class (e.g., AskService.ask), results will be an array of Hashes (from ActiveRecord::Result).
204
- results.each do |record|
205
- puts record.inspect
206
- end
207
- rescue AsktiveRecord::QueryExecutionError => e
208
- puts "Error executing query: #{e.message}"
209
- # Handle database execution errors
210
- end
211
- ```
212
+ # Using a model to ask a question
213
+ response = User.ask("Who signed up most recently?").answer
214
+ # => "The most recently signed up user is Alice Smith."
212
215
 
213
- ### Advanced Options
216
+ # Asking for a summary
217
+ response = AskService.ask("How many orders were placed last week?").answer
218
+ # => "There were 42 orders placed last week."
219
+ ```
214
220
 
215
- When using the service-class approach, you can provide additional options:
221
+ Tip: You can get the query param and interpolates it into the ask method to get a more specific answer. For example, if you want to know the last user created, you can do:
216
222
 
217
223
  ```ruby
218
- # Specify a target model for execution (useful if you want ActiveRecord objects back)
219
- # Note: This currently doesn't change the execution method but might in the future.
220
- asktive_record_query = AskService.ask("Show me all products", model: Product)
221
-
222
- # Specify a target table name for the LLM prompt
223
- asktive_record_query = AskService.ask("Show me all items on sale", table_name: "products")
224
+ customer = Customer.find(params[:id])
225
+ query = "Which is my most sold product?"
226
+ response = AskService.ask("For the customer #{customer.id}, #{query}").answer
227
+ # => "The most sold product for customer ABC is the Premium Widget."
224
228
  ```
225
229
 
226
- ### The `AsktiveRecord::Query` Object
230
+ The `.answer` method is ideal when you want a direct, human-readable summary, rather than an array of records or a SQL query.
227
231
 
228
232
  The `ask()` method returns an instance of `AsktiveRecord::Query`. This object has a few useful methods:
229
233
 
@@ -235,6 +239,31 @@ The `ask()` method returns an instance of `AsktiveRecord::Query`. This object ha
235
239
  * If the query originated from a service class (e.g., `AskService.ask(...)`), it uses `ActiveRecord::Base.connection.select_all` (for SELECT) or `execute` and returns an `ActiveRecord::Result` object (array of hashes) or connection-specific results.
236
240
  * `to_s`: Returns the `sanitized_sql` (or `raw_sql` if `sanitized_sql` hasn't been modified from raw).
237
241
 
242
+ ## Logging
243
+ AsktiveRecord provides logging to help you debug and monitor natural language queries, generated SQL, and results. By default, logs are sent to the Rails logger at the `:info` level.
244
+
245
+ ### Example Log Output
246
+
247
+ When you run a query, you might see logs like:
248
+
249
+ ```
250
+ [AsktiveRecord] Received question: "Who are my newest users?"
251
+ [AsktiveRecord] Generated SQL: SELECT * FROM users ORDER BY created_at DESC LIMIT 5;
252
+ [AsktiveRecord] Sanitized SQL: SELECT * FROM users ORDER BY created_at DESC LIMIT 5;
253
+ [AsktiveRecord] Executing SQL via User.find_by_sql
254
+ [AsktiveRecord] Query results: [#<User id: 1, name: "Alice", ...>, ...]
255
+ ```
256
+
257
+ When using the `.answer` method:
258
+
259
+ ```
260
+ [AsktiveRecord] Received question: "How many orders were placed last week?"
261
+ [AsktiveRecord] Generated SQL: SELECT COUNT(*) FROM orders WHERE created_at >= '2024-06-01' AND created_at < '2024-06-08';
262
+ [AsktiveRecord] Query results: [{"count"=>42}]
263
+ [AsktiveRecord] LLM answer: "There were 42 orders placed last week."
264
+ ```
265
+
266
+
238
267
  ## Supported LLMs
239
268
 
240
269
  * **Currently**: OpenAI (ChatGPT models like `gpt-3.5-turbo`, `gpt-4`).
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AsktiveRecord
4
- VERSION = "0.1.0"
4
+ VERSION = "0.1.4"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: asktive_record
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - rpossan
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-06-24 00:00:00.000000000 Z
11
+ date: 2025-06-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: railties
@@ -66,20 +66,6 @@ dependencies:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '2.0'
69
- - !ruby/object:Gem::Dependency
70
- name: debug
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - "~>"
74
- - !ruby/object:Gem::Version
75
- version: '1.0'
76
- type: :development
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - "~>"
81
- - !ruby/object:Gem::Version
82
- version: '1.0'
83
69
  - !ruby/object:Gem::Dependency
84
70
  name: rake
85
71
  requirement: !ruby/object:Gem::Requirement
@@ -108,25 +94,14 @@ dependencies:
108
94
  - - "~>"
109
95
  - !ruby/object:Gem::Version
110
96
  version: '3.0'
111
- - !ruby/object:Gem::Dependency
112
- name: rubocop
113
- requirement: !ruby/object:Gem::Requirement
114
- requirements:
115
- - - "~>"
116
- - !ruby/object:Gem::Version
117
- version: '1.0'
118
- type: :development
119
- prerelease: false
120
- version_requirements: !ruby/object:Gem::Requirement
121
- requirements:
122
- - - "~>"
123
- - !ruby/object:Gem::Version
124
- version: '1.0'
125
- description: AsktiveRecord allows developers to integrate Large Language Models (like
126
- OpenAI's ChatGPT) into their Ruby on Rails applications, enabling natural language
127
- querying against their database. It provides tools to configure the LLM, upload
128
- database schema for context, and convert human-like questions into executable SQL
129
- queries.
97
+ description: AsktiveRecord is a Ruby gem designed to bridge the gap between human
98
+ language and database queries. It lets you interact with your Rails database as
99
+ if you were having a conversation with a knowledgeable assistant. Instead of
100
+ writing SQL or chaining ActiveRecord methods, you simply ask questions in plain
101
+ English—like (or any language) 'Who are my newest users?' or 'What products sold
102
+ the most last month?' — and get clear, human-friendly answers. AsktiveRecord translates
103
+ your questions into database queries behind the scenes, so you can focus on what
104
+ you want to know, not how to write the query.
130
105
  email:
131
106
  - ronaldo.possan@gmail.com
132
107
  executables: []
@@ -5700,6 +5675,5 @@ requirements: []
5700
5675
  rubygems_version: 3.5.11
5701
5676
  signing_key:
5702
5677
  specification_version: 4
5703
- summary: A Ruby gem that integrates with LLMs to translate natural language queries
5704
- into SQL.
5678
+ summary: A Ruby gem that lets your data answer like a human
5705
5679
  test_files: []