kpi_assembler 0.5.0 → 0.5.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 73810fbb8d6411ed8f52fd84ac84bed1c50372b2fa9cac5bb5bbce43de714130
4
- data.tar.gz: efd60220f7d69ab53a23162048a65226a5ac5f4ab852e6685cde31c0298d4bab
3
+ metadata.gz: f8327e4d084d6aef240991acae7bfed8ed06af1059fb28154e7ed911f4359976
4
+ data.tar.gz: f4d84ae33c76e3b8174358d60509a1e5091703b081f9d4ee73d5317f7940115b
5
5
  SHA512:
6
- metadata.gz: 10baca12a1597969b0bf610da6cbf7b505655b74b55b07d1532a661f7649e13ebc704f8e2a119855871877afa0d7c7f9fad349bf7460189d8ee98ae59f72e3e1
7
- data.tar.gz: bdab7aef95e09eea0ae8d62a622859d0bd096e2c96072e642fd4e164a6835d1535a671b848a346d3e3d32cfa7285c389f304171bc8278e8b4803e50f50357738
6
+ metadata.gz: 992360ae2eb9c585c7e5ea48841feb6ff177d92b827d734038b4abc152e08bb743dd52dd5b51b2f02f75d45e0bb1355b609f206d6f1fa054e8ad75fc661f3bb1
7
+ data.tar.gz: a57880ff32be45512aa1bf409991090549e07192c685647119298f4f5afc4ebb1880afbd168d5ce8f96ef5773daece8ebabab597eecc6e92de2c75057dd166d3
data/README.md CHANGED
@@ -1,82 +1,56 @@
1
1
  # KPIAssembler
2
2
 
3
- KPIAssembler discovers application schemas, proposes useful business metrics,
4
- and deterministically certifies generated SQL before publishing a KPI pack.
3
+ KPIAssembler discovers application schemas, proposes business metrics, and
4
+ certifies generated SQL before a number is trusted.
5
5
 
6
- The LLM proposes; deterministic code certifies. Candidate queries are checked
7
- against the real schema, tenant boundary, SQL safety rules, query planner, and
8
- sample execution before they can be marked certified.
6
+ The LLM proposes. Deterministic code certifies.
9
7
 
10
- ## Features
11
-
12
- - Mountable Rails Engine with an interactive workspace and JSON API
13
- - Standalone Rack application and command-line interface
14
- - SQLite and PostgreSQL schema introspection
15
- - Gemini and Ollama KPI proposal providers
16
- - Schema-driven heuristic fallback when no model is available
17
- - Deterministic SQL, join, division, tenant, and execution checks
18
- - JSON and HTML KPI packs
19
-
20
- ## Install
21
-
22
- Add the gem to your application:
8
+ ## Install (Rails)
23
9
 
24
10
  ```ruby
25
- gem "kpi_assembler"
11
+ # Gemfile
12
+ gem "kpi_assembler", "~> 0.5"
26
13
  ```
27
14
 
28
- Then run:
29
-
30
15
  ```bash
31
16
  bundle install
32
17
  bin/rails generate kpi_assembler:install
33
18
  ```
34
19
 
35
- Mount the engine:
36
-
37
20
  ```ruby
21
+ # config/routes.rb
38
22
  mount KPIAssembler::Engine => "/kpi-assembler"
39
23
  ```
40
24
 
41
- See [Rails Engine integration](docs/rails-engine.md) for configuration and
42
- [standalone integration](docs/integration.md) for Rack, CLI, and API usage.
25
+ Then:
43
26
 
44
- ## LLM configuration
27
+ 1. Edit `config/initializers/kpi_assembler.rb` (database pool, tenant, auth).
28
+ 2. Set `GEMINI_API_KEY` and `KPI_LLM_PROVIDER=gemini` in the host `.env`,
29
+ or use Ollama / `KPI_USE_LLM=false`.
30
+ 3. Restart the app and open `/kpi-assembler`.
45
31
 
46
- Gemini:
32
+ Full walkthrough, env vars, and troubleshooting:
33
+ **[Setup guide](docs/setup.md)**.
47
34
 
48
- ```bash
49
- KPI_LLM_PROVIDER=gemini
50
- GEMINI_API_KEY=your-key
51
- KPI_GEMINI_MODEL=gemini-2.0-flash
52
- ```
35
+ Rails engine details: [docs/rails-engine.md](docs/rails-engine.md).
36
+ Standalone Rack/CLI/API: [docs/integration.md](docs/integration.md).
53
37
 
54
- Ollama:
38
+ ## What it does
55
39
 
56
- ```bash
57
- KPI_LLM_PROVIDER=ollama
58
- KPI_OLLAMA_MODEL=llama3.2:3b
59
- KPI_OLLAMA_URL=http://localhost:11434/api/generate
60
- ```
61
-
62
- Set `KPI_USE_LLM=false` to use schema-driven heuristics only.
63
-
64
- ## Development
65
-
66
- ```bash
67
- bundle install
68
- bundle exec rspec
69
- bundle exec ruby bin/kpi_assembler --sample-db
70
- bundle exec ruby bin/kpi_assembler_web
71
- ```
40
+ 1. **Discover** — introspect tables, columns, and foreign keys
41
+ 2. **Propose** — Gemini, Ollama, or schema heuristics draft KPI SQL
42
+ 3. **Accept** — you choose which candidates to certify
43
+ 4. **Certify** — read-only SELECT, join safety, tenant scope, planner, sample run
44
+ 5. **Publish** — certified pack as JSON (and HTML from the CLI)
72
45
 
73
- The standalone workspace starts at `http://127.0.0.1:9292`.
46
+ Rejected candidates never reach certification. **Draft** means a selected KPI
47
+ failed a certification check.
74
48
 
75
- ## Security
49
+ ## Requirements
76
50
 
77
- Use a read-only database user or replica. Generated candidate SQL is executed
78
- during deterministic certification. Configure tenant scoping and authorization
79
- before exposing the engine or API.
51
+ - Ruby >= 3.0
52
+ - Rails 7 for the engine
53
+ - Read-only database access (replica or reporting user)
80
54
 
81
55
  ## License
82
56
 
data/docs/integration.md CHANGED
@@ -1,7 +1,9 @@
1
1
  # Standalone integration
2
2
 
3
- KPIAssembler can run as a Rack service and expose its workspace and JSON API to
4
- applications written in any language.
3
+ For the Rails engine, start with the [setup guide](setup.md).
4
+
5
+ KPIAssembler can also run as a Rack service and expose its workspace and JSON
6
+ API to applications written in any language.
5
7
 
6
8
  ## Start the service
7
9
 
data/docs/rails-engine.md CHANGED
@@ -1,92 +1,47 @@
1
- # Rails Engine integration
1
+ # Rails Engine notes
2
2
 
3
- KPIAssembler is a mountable Rails Engine. It uses the host application's
4
- session, authorization, and Active Record connection pool.
3
+ Use this after the [setup guide](setup.md). KPIAssembler mounts inside a Rails 7
4
+ app, reuses the host session, and reads through Active Record.
5
5
 
6
- ## Install
7
-
8
- ```ruby
9
- gem "kpi_assembler"
10
- ```
6
+ ## Generator
11
7
 
12
8
  ```bash
13
9
  bundle install
14
10
  bin/rails generate kpi_assembler:install
15
11
  ```
16
12
 
17
- The generator creates `config/initializers/kpi_assembler.rb`.
18
-
19
- ## Configure
20
-
21
- ```ruby
22
- KPIAssembler.configure do |config|
23
- config.connection_provider = lambda do |_controller|
24
- ApplicationRecord.connected_to(role: :reading) do
25
- ApplicationRecord.connection_pool
26
- end
27
- end
28
-
29
- config.parent_controller = "ApplicationController"
30
- config.tenant_column = "account_id"
31
- config.tenant_id_resolver = lambda do |controller|
32
- controller.send(:current_account).id
33
- end
34
-
35
- config.include_tables = []
36
- config.max_tables = 30
37
- config.schema_name = "public"
38
-
39
- config.authorize_with = lambda do |controller|
40
- controller.send(:authenticate_user!)
41
- controller.send(:current_account).present?
42
- end
43
-
44
- config.llm_provider = ENV.fetch("KPI_LLM_PROVIDER", "gemini").to_sym
45
- config.use_llm = ENV["KPI_USE_LLM"] != "false"
46
-
47
- config.gemini_api_key = ENV["GEMINI_API_KEY"]
48
- config.gemini_model = ENV.fetch("KPI_GEMINI_MODEL", "gemini-2.0-flash")
49
-
50
- config.ollama_model = ENV.fetch("KPI_OLLAMA_MODEL", "llama3.2:3b")
51
- config.ollama_url = ENV.fetch(
52
- "KPI_OLLAMA_URL",
53
- "http://localhost:11434/api/generate"
54
- )
55
- end
56
- ```
57
-
58
- Store secrets in the host application's environment rather than the initializer:
59
-
60
- ```bash
61
- KPI_LLM_PROVIDER=gemini
62
- GEMINI_API_KEY=your-key
63
- KPI_GEMINI_MODEL=gemini-2.0-flash
64
- ```
65
-
66
- Restart the Rails server after changing environment variables.
13
+ Creates `config/initializers/kpi_assembler.rb`. The template uses
14
+ `current_company` / `company_id` as placeholders — change them to match your
15
+ app (`current_account`, `tenant_id`, and so on).
67
16
 
68
- ## Mount
69
-
70
- Add the engine to `config/routes.rb`:
17
+ ## Routes
71
18
 
72
19
  ```ruby
73
20
  mount KPIAssembler::Engine => "/kpi-assembler"
74
21
  ```
75
22
 
76
- The mounted endpoints are:
77
-
78
23
  ```text
79
24
  GET/POST /kpi-assembler/api/v1/discover
80
25
  POST /kpi-assembler/api/v1/certify
81
26
  GET /kpi-assembler/api/v1/pack
82
27
  ```
83
28
 
84
- ## Production guidance
29
+ Static UI files are served at `/kpi-assembler/assets/*` outside the host
30
+ controller stack so JavaScript is not blocked as a cross-origin response.
31
+
32
+ ## Connection
33
+
34
+ `connection_provider` must return a pool or connection `KPIAssembler::Connection`
35
+ can wrap: `ActiveRecord::Base.connection_pool`, another pool, or a wrapped
36
+ adapter. Prefer `connected_to(role: :reading)` when the host has a replica.
37
+
38
+ ## Tenancy
39
+
40
+ If `tenant_column` is set and that column exists on a fact table, every
41
+ certified query on that table must include `column = tenant_id`. Gemini often
42
+ omits this; certification then marks the KPI draft.
85
43
 
86
- Point `connection_provider` at a read-only pool. Replace the example
87
- authorization callback with the host application's permission policy. Tenant
88
- scoping must correspond to real columns in the inspected tables.
44
+ ## Packs
89
45
 
90
- The engine currently keeps the latest pack in process memory per tenant.
91
- Production applications should persist versioned packs and define an
92
- invalidation policy.
46
+ The engine stores the latest pack in memory per tenant. Restarting the process
47
+ clears it. Persist packs in the host app before production use.
data/docs/setup.md ADDED
@@ -0,0 +1,236 @@
1
+ # Setup guide
2
+
3
+ KPIAssembler inspects your application schema, proposes KPI definitions, and
4
+ certifies their SQL before anything is published. The model only proposes.
5
+ Deterministic Ruby certifies.
6
+
7
+ There are two install paths:
8
+
9
+ 1. **Rails engine** — mount the workspace inside an existing Rails 7 app
10
+ 2. **Standalone service** — run the Rack UI/API next to any application
11
+
12
+ Most teams should use the Rails engine.
13
+
14
+ ## Requirements
15
+
16
+ - Ruby 3.0 or newer
17
+ - Rails 7.x for the engine (`railties >= 7.0, < 8`)
18
+ - A **read-only** database user, replica, or reporting pool
19
+ - Optional: a [Gemini API key](https://aistudio.google.com/apikey) or a local
20
+ [Ollama](https://ollama.com) daemon
21
+
22
+ Do not point certification at a production write primary.
23
+
24
+ ## Rails engine
25
+
26
+ ### 1. Add the gem
27
+
28
+ ```ruby
29
+ # Gemfile
30
+ gem "kpi_assembler", "~> 0.5"
31
+ ```
32
+
33
+ ```bash
34
+ bundle install
35
+ bin/rails generate kpi_assembler:install
36
+ ```
37
+
38
+ The generator writes `config/initializers/kpi_assembler.rb`.
39
+
40
+ ### 2. Mount the workspace
41
+
42
+ In `config/routes.rb`, mount inside whatever scope already requires a signed-in
43
+ user:
44
+
45
+ ```ruby
46
+ mount KPIAssembler::Engine => "/kpi-assembler"
47
+ ```
48
+
49
+ Open `/kpi-assembler` after signing in.
50
+
51
+ JSON endpoints:
52
+
53
+ ```text
54
+ GET/POST /kpi-assembler/api/v1/discover
55
+ POST /kpi-assembler/api/v1/certify
56
+ GET /kpi-assembler/api/v1/pack
57
+ ```
58
+
59
+ ### 3. Edit the initializer
60
+
61
+ Replace the sample hooks with your app's connection, tenant, and auth.
62
+
63
+ ```ruby
64
+ KPIAssembler.configure do |config|
65
+ # Prefer a read/report pool, not the write primary.
66
+ config.connection_provider = lambda do |_controller|
67
+ ApplicationRecord.connected_to(role: :reading) do
68
+ ApplicationRecord.connection_pool
69
+ end
70
+ end
71
+
72
+ config.parent_controller = "ApplicationController"
73
+
74
+ # Column that exists on fact tables. Omit tenant_column if you have none.
75
+ config.tenant_column = "account_id"
76
+ config.tenant_id_resolver = lambda do |controller|
77
+ controller.send(:current_account).id
78
+ end
79
+
80
+ # Empty allowlist = inspect the live schema (up to max_tables).
81
+ # Names must match the database (people, not Person).
82
+ config.include_tables = []
83
+ config.max_tables = 30
84
+ config.schema_name = "public"
85
+
86
+ config.authorize_with = lambda do |controller|
87
+ controller.send(:authenticate_user!)
88
+ controller.send(:current_account).present?
89
+ end
90
+
91
+ config.llm_provider = ENV.fetch("KPI_LLM_PROVIDER", "gemini").to_sym
92
+ config.use_llm = ENV["KPI_USE_LLM"] != "false"
93
+ config.gemini_api_key = ENV["GEMINI_API_KEY"]
94
+ config.gemini_model = ENV.fetch("KPI_GEMINI_MODEL", "gemini-2.0-flash")
95
+ config.ollama_model = ENV.fetch("KPI_OLLAMA_MODEL", "llama3.2:3b")
96
+ config.ollama_url = ENV.fetch("KPI_OLLAMA_URL", "http://localhost:11434/api/generate")
97
+ end
98
+ ```
99
+
100
+ If you have no `reading` role, return `ActiveRecord::Base.connection_pool`
101
+ (still with a read-only user).
102
+
103
+ ### 4. Set environment variables
104
+
105
+ Put secrets in the host app `.env` (or your secret manager). Do not commit keys.
106
+
107
+ **Gemini (cloud):**
108
+
109
+ ```bash
110
+ KPI_LLM_PROVIDER=gemini
111
+ GEMINI_API_KEY=your-key
112
+ KPI_GEMINI_MODEL=gemini-2.0-flash
113
+ ```
114
+
115
+ You do not set a Gemini URL. The default endpoint is built from the model name.
116
+
117
+ **Ollama (local):**
118
+
119
+ ```bash
120
+ KPI_LLM_PROVIDER=ollama
121
+ KPI_OLLAMA_MODEL=llama3.2:3b
122
+ KPI_OLLAMA_URL=http://localhost:11434/api/generate
123
+ ```
124
+
125
+ Start the daemon first: `ollama serve`, then `ollama pull llama3.2:3b`.
126
+
127
+ **Heuristics only:**
128
+
129
+ ```bash
130
+ KPI_USE_LLM=false
131
+ ```
132
+
133
+ Restart `bin/rails server` after changing env vars or the initializer.
134
+
135
+ ### 5. First run
136
+
137
+ 1. Sign in and open `/kpi-assembler`.
138
+ 2. Click **Discover metrics**. Schema cards and candidate KPIs should appear.
139
+ 3. Select the definitions you want. Rejected items never reach certification.
140
+ 4. Click **Certify**. Certified KPIs become the pack. Failures land in **draft**
141
+ with reasons (unsafe SQL, missing tenant scope, planner error, NULL sample
142
+ replay). Draft is not the same as rejecting a KPI in the UI.
143
+
144
+ The workspace shows whether the proposer was `llm` or `heuristics`, the model
145
+ name, and any tables the model ranked.
146
+
147
+ ## Configuration reference
148
+
149
+ | Setting | Purpose |
150
+ | --- | --- |
151
+ | `connection_provider` | Returns an AR pool or connection used for inspect + certify |
152
+ | `tenant_column` / `tenant_id_resolver` | Injects tenant filters into generated SQL |
153
+ | `include_tables` | Optional allowlist. Empty means inspect the live schema |
154
+ | `max_tables` | Cap after ranking; omitted tables are reported in the UI |
155
+ | `schema_name` | PostgreSQL schema (default `public`) |
156
+ | `parent_controller` | Host controller the engine inherits (auth callbacks) |
157
+ | `authorize_with` | Extra gate; must not return `false` |
158
+ | `llm_provider` | `:gemini` or `:ollama` |
159
+ | `use_llm` | `false` skips the model and uses schema heuristics |
160
+ | `gemini_api_key` / `gemini_model` | Gemini credentials |
161
+ | `ollama_model` / `ollama_url` | Local Ollama |
162
+
163
+ Environment variables the gem reads: `KPI_LLM_PROVIDER`, `KPI_USE_LLM`,
164
+ `GEMINI_API_KEY`, `KPI_GEMINI_MODEL`, `KPI_GEMINI_URL`, `KPI_OLLAMA_MODEL`,
165
+ `KPI_OLLAMA_URL`, `KPI_USE_OLLAMA` (Ollama-only off switch).
166
+
167
+ ## Standalone service
168
+
169
+ Use this when the host app is not Rails, or you want a separate process.
170
+
171
+ ```bash
172
+ gem install kpi_assembler
173
+ # or clone the repo and: bundle install
174
+ ```
175
+
176
+ From the gem source checkout:
177
+
178
+ ```bash
179
+ cp .env.example .env
180
+ # set KPI_DATABASE_URL or KPI_DATABASE_PATH, plus LLM vars
181
+ bundle exec ruby bin/kpi_assembler_web
182
+ ```
183
+
184
+ Opens `http://127.0.0.1:9292`.
185
+
186
+ Useful env vars:
187
+
188
+ ```bash
189
+ HOST=0.0.0.0
190
+ PORT=9292
191
+ KPI_DATABASE_URL=postgres://USER:PASSWORD@HOST:5432/application
192
+ KPI_DATABASE_PATH=/absolute/path/to/application.sqlite3
193
+ KPI_INCLUDE_TABLES=orders,customers,accounts
194
+ KPI_TENANT_COLUMN=account_id
195
+ KPI_TENANT_ID=123
196
+ KPI_ALLOWED_ORIGIN=https://your-app.example
197
+ KPI_LLM_PROVIDER=gemini
198
+ GEMINI_API_KEY=your-key
199
+ ```
200
+
201
+ Embed:
202
+
203
+ ```html
204
+ <script src="http://localhost:9292/kpi-assembler.js"></script>
205
+ <kpi-assembler service-url="http://localhost:9292" height="820px"></kpi-assembler>
206
+ ```
207
+
208
+ CLI against the sample database:
209
+
210
+ ```bash
211
+ bundle exec ruby bin/kpi_assembler --sample-db
212
+ ```
213
+
214
+ See [standalone integration](integration.md) for the JSON API.
215
+
216
+ ## Troubleshooting
217
+
218
+ | Symptom | What to check |
219
+ | --- | --- |
220
+ | Discover does nothing / non-JSON error | Host redirected to login. Send the request while signed in. Confirm `/kpi-assembler/assets/app.js` returns 200. |
221
+ | `heuristics` proposer, Gemini fallback | `GEMINI_API_KEY` missing or HTTP error. The UI shows `proposer_fallback`. |
222
+ | `heuristics` proposer, Ollama fallback | `ollama serve` not running, or model not pulled. Fallback text names installed models. |
223
+ | UI does not show tables you added | `include_tables` names must match the DB. Check `missing_tables` / `omitted_tables` (`max_tables`). |
224
+ | Many KPIs become **draft** after certify | Open `reasons`. Common: missing `tenant_column = id`, invented tables, `NULLIF` producing NULL on empty windows. |
225
+ | `json` / `quirks_mode` on Rails 7 | Pin `gem "json", "< 3"` in the **host** Gemfile. |
226
+ | Slow or truncated LLM output | Lower `max_tables`, or use `gemini-2.0-flash` / a small Ollama model. |
227
+
228
+ ## Security
229
+
230
+ - Authorize the mount the same way as the rest of the app. The extra
231
+ `authorize_with` gate is not a substitute for a real permission check.
232
+ - Certification **executes** candidate `SELECT`s. Use a least-privilege
233
+ read-only role.
234
+ - Tenant SQL is only injected when `tenant_column` exists on the fact table.
235
+ - The latest pack is kept in process memory per tenant. Persist packs yourself
236
+ before relying on this in production.
@@ -19,6 +19,8 @@ module KPIAssembler
19
19
  def print_mount_instruction
20
20
  say "\nMount KPIAssembler in config/routes.rb:", :green
21
21
  say ' mount KPIAssembler::Engine => "/kpi-assembler"'
22
+ say "\nThen set GEMINI_API_KEY (or KPI_LLM_PROVIDER=ollama) and restart."
23
+ say "Setup guide: https://github.com/Akshatsrivastava700/kpi_assembler/blob/main/docs/setup.md"
22
24
  end
23
25
  end
24
26
  end
@@ -42,6 +42,6 @@ KPIAssembler.configure do |config|
42
42
  config.gemini_model = ENV.fetch("KPI_GEMINI_MODEL", "gemini-2.0-flash")
43
43
 
44
44
  # Ollama configuration
45
- config.ollama_model = ENV.fetch("KPI_OLLAMA_MODEL", "llama3")
45
+ config.ollama_model = ENV.fetch("KPI_OLLAMA_MODEL", "llama3.2:3b")
46
46
  config.ollama_url = ENV.fetch("KPI_OLLAMA_URL", "http://localhost:11434/api/generate")
47
47
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module KPIAssembler
4
- VERSION = "0.5.0"
4
+ VERSION = "0.5.1"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kpi_assembler
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - KPIAssembler contributors
@@ -47,6 +47,7 @@ files:
47
47
  - config/routes.rb
48
48
  - docs/integration.md
49
49
  - docs/rails-engine.md
50
+ - docs/setup.md
50
51
  - lib/generators/kpi_assembler/install_generator.rb
51
52
  - lib/generators/kpi_assembler/templates/kpi_assembler.rb
52
53
  - lib/kpi_assembler.rb
@@ -79,6 +80,7 @@ metadata:
79
80
  homepage_uri: https://github.com/Akshatsrivastava700/kpi_assembler
80
81
  source_code_uri: https://github.com/Akshatsrivastava700/kpi_assembler
81
82
  changelog_uri: https://github.com/Akshatsrivastava700/kpi_assembler/releases
83
+ documentation_uri: https://github.com/Akshatsrivastava700/kpi_assembler/blob/main/docs/setup.md
82
84
  post_install_message:
83
85
  rdoc_options: []
84
86
  require_paths: