agentic-slayer 0.1.0rc2__tar.gz
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.
- agentic_slayer-0.1.0rc2/LICENSE +21 -0
- agentic_slayer-0.1.0rc2/PKG-INFO +418 -0
- agentic_slayer-0.1.0rc2/README.md +383 -0
- agentic_slayer-0.1.0rc2/pyproject.toml +64 -0
- agentic_slayer-0.1.0rc2/slayer/__init__.py +3 -0
- agentic_slayer-0.1.0rc2/slayer/__main__.py +5 -0
- agentic_slayer-0.1.0rc2/slayer/api/__init__.py +0 -0
- agentic_slayer-0.1.0rc2/slayer/api/server.py +158 -0
- agentic_slayer-0.1.0rc2/slayer/cli.py +231 -0
- agentic_slayer-0.1.0rc2/slayer/client/__init__.py +0 -0
- agentic_slayer-0.1.0rc2/slayer/client/slayer_client.py +73 -0
- agentic_slayer-0.1.0rc2/slayer/core/__init__.py +0 -0
- agentic_slayer-0.1.0rc2/slayer/core/enums.py +105 -0
- agentic_slayer-0.1.0rc2/slayer/core/formula.py +366 -0
- agentic_slayer-0.1.0rc2/slayer/core/models.py +110 -0
- agentic_slayer-0.1.0rc2/slayer/core/query.py +129 -0
- agentic_slayer-0.1.0rc2/slayer/engine/__init__.py +0 -0
- agentic_slayer-0.1.0rc2/slayer/engine/enriched.py +123 -0
- agentic_slayer-0.1.0rc2/slayer/engine/ingestion.py +451 -0
- agentic_slayer-0.1.0rc2/slayer/engine/query_engine.py +334 -0
- agentic_slayer-0.1.0rc2/slayer/mcp/__init__.py +0 -0
- agentic_slayer-0.1.0rc2/slayer/mcp/server.py +710 -0
- agentic_slayer-0.1.0rc2/slayer/sql/__init__.py +0 -0
- agentic_slayer-0.1.0rc2/slayer/sql/client.py +83 -0
- agentic_slayer-0.1.0rc2/slayer/sql/generator.py +468 -0
- agentic_slayer-0.1.0rc2/slayer/storage/__init__.py +0 -0
- agentic_slayer-0.1.0rc2/slayer/storage/base.py +32 -0
- agentic_slayer-0.1.0rc2/slayer/storage/sqlite_storage.py +80 -0
- agentic_slayer-0.1.0rc2/slayer/storage/yaml_storage.py +75 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 MotleyAI
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: agentic-slayer
|
|
3
|
+
Version: 0.1.0rc2
|
|
4
|
+
Summary: A lightweight, agent-first semantic layer for AI agents
|
|
5
|
+
License: MIT
|
|
6
|
+
Keywords: semantic-layer,sql,ai,mcp,data
|
|
7
|
+
Author: MotleyAI
|
|
8
|
+
Requires-Python: >=3.11,<4.0
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
17
|
+
Classifier: Topic :: Database
|
|
18
|
+
Provides-Extra: all
|
|
19
|
+
Provides-Extra: api
|
|
20
|
+
Provides-Extra: client
|
|
21
|
+
Provides-Extra: docs
|
|
22
|
+
Provides-Extra: mcp
|
|
23
|
+
Requires-Dist: fastapi (>=0.100) ; extra == "api" or extra == "all"
|
|
24
|
+
Requires-Dist: httpx (>=0.24) ; extra == "client" or extra == "all"
|
|
25
|
+
Requires-Dist: mcp (>=1.0) ; extra == "mcp" or extra == "all"
|
|
26
|
+
Requires-Dist: mkdocs-material (>=9.0) ; extra == "docs"
|
|
27
|
+
Requires-Dist: pandas (>=2.0) ; extra == "client" or extra == "all"
|
|
28
|
+
Requires-Dist: pydantic (>=2.0)
|
|
29
|
+
Requires-Dist: pyyaml (>=6.0)
|
|
30
|
+
Requires-Dist: sqlalchemy (>=2.0)
|
|
31
|
+
Requires-Dist: sqlglot (>=20.0)
|
|
32
|
+
Requires-Dist: uvicorn (>=0.20) ; extra == "api" or extra == "all"
|
|
33
|
+
Description-Content-Type: text/markdown
|
|
34
|
+
|
|
35
|
+
# SLayer — a semantic layer by Motley
|
|
36
|
+
|
|
37
|
+
[](https://pypi.org/project/agentic-slayer/)
|
|
38
|
+
[](https://pypi.org/project/agentic-slayer/)
|
|
39
|
+
[](https://agentic-slayer.readthedocs.io/)
|
|
40
|
+
[](LICENSE)
|
|
41
|
+
|
|
42
|
+
A lightweight open-source semantic layer for AI agents and humans
|
|
43
|
+
|
|
44
|
+
## Quick Start
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
# Install
|
|
48
|
+
pip install semantic-slayer[all]
|
|
49
|
+
|
|
50
|
+
# Start the HTTP server
|
|
51
|
+
slayer serve --models-dir ./my_models
|
|
52
|
+
|
|
53
|
+
# Or set up stdio MCP for an agent like Claude Code
|
|
54
|
+
# (the agent spawns slayer as a subprocess — you don't run this manually)
|
|
55
|
+
claude mcp add slayer -- slayer mcp --models-dir ./my_models
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## What is SLayer?
|
|
59
|
+
|
|
60
|
+
SLayer sits between your data and your apps or AI agents. Instead of writing raw SQL, agents describe what data they want — measures, dimensions, filters — and SLayer handles the rest.
|
|
61
|
+
|
|
62
|
+
**Key features:**
|
|
63
|
+
- **Agent-first design** — MCP, Python SDK, and REST API interfaces
|
|
64
|
+
- **Datasource-agnostic** — Postgres, MySQL, BigQuery, Snowflake, and more via sqlglot
|
|
65
|
+
- **`fields` API** — Describe derived metrics with formulas (`"revenue / count"`, `"cumsum(revenue)"`, `"time_shift(revenue, -1, 'year')"`) in a single list
|
|
66
|
+
- **Auto-ingestion with rollup joins** — Connect to a DB, introspect schema, generate denormalized models with FK-based LEFT JOINs automatically
|
|
67
|
+
- **Incremental model editing** — Add/remove measures and dimensions without replacing the full model
|
|
68
|
+
- **Lightweight** — Minimal dependencies, easy to set up and extend
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
## Interfaces
|
|
72
|
+
|
|
73
|
+
### REST API
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
# Query
|
|
77
|
+
curl -X POST http://localhost:5143/query \
|
|
78
|
+
-H "Content-Type: application/json" \
|
|
79
|
+
-d '{"model": "orders", "fields": [{"formula": "count"}], "dimensions": [{"name": "status"}]}'
|
|
80
|
+
|
|
81
|
+
# List models (returns name + description)
|
|
82
|
+
curl http://localhost:5143/models
|
|
83
|
+
|
|
84
|
+
# Get a single datasource (credentials masked)
|
|
85
|
+
curl http://localhost:5143/datasources/my_postgres
|
|
86
|
+
|
|
87
|
+
# Health check
|
|
88
|
+
curl http://localhost:5143/health
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### MCP Server
|
|
92
|
+
|
|
93
|
+
SLayer supports two MCP transports:
|
|
94
|
+
|
|
95
|
+
**Stdio** — the agent spawns SLayer as a subprocess (for Claude Code, Cursor, etc.). You do not run `slayer mcp` manually; instead, register it with your agent:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
# Register with Claude Code (the agent will spawn the process itself)
|
|
99
|
+
claude mcp add slayer -- slayer mcp --models-dir ./my_models
|
|
100
|
+
|
|
101
|
+
# If slayer is in a virtualenv, use the full path to the executable:
|
|
102
|
+
# poetry env info -p # prints e.g. /home/user/.venvs/slayer-xyz
|
|
103
|
+
# claude mcp add slayer -- /home/user/.venvs/slayer-xyz/bin/slayer mcp --models-dir /path/to/my_models
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
**SSE (Server-Sent Events)** — MCP over HTTP, served alongside the REST API on `/mcp`. You run `slayer serve` yourself, then point the agent at the URL:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
# 1. Start the server (REST API + MCP SSE)
|
|
110
|
+
slayer serve --models-dir ./my_models
|
|
111
|
+
# REST API at http://localhost:5143/query, /models, etc.
|
|
112
|
+
# MCP SSE at http://localhost:5143/mcp/sse
|
|
113
|
+
|
|
114
|
+
# 2. In a separate terminal, register the remote MCP endpoint with your agent
|
|
115
|
+
claude mcp add slayer-remote --transport sse --url http://localhost:5143/mcp/sse
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Both transports expose the same tools — no duplication.
|
|
119
|
+
|
|
120
|
+
MCP tools:
|
|
121
|
+
|
|
122
|
+
| Tool | Description |
|
|
123
|
+
|------|-------------|
|
|
124
|
+
| `datasource_summary` | List all datasources and their models with schemas (dimensions, measures) |
|
|
125
|
+
| `inspect_model` | Detailed model info with sample data |
|
|
126
|
+
| `query` | Execute semantic queries |
|
|
127
|
+
| `create_model` | Create a new model from table/SQL |
|
|
128
|
+
| `edit_model` | Edit an existing model: update metadata, add measures/dimensions, remove fields — all in one call |
|
|
129
|
+
| `delete_model` | Delete a model |
|
|
130
|
+
| `create_datasource` | Configure a database connection (with connection test and auto-ingestion; set `auto_ingest=false` to skip) |
|
|
131
|
+
| `list_datasources` | List configured datasources |
|
|
132
|
+
| `describe_datasource` | Show datasource details, test connection, list schemas |
|
|
133
|
+
| `list_tables` | Explore tables in a database |
|
|
134
|
+
| `edit_datasource` | Edit an existing datasource config |
|
|
135
|
+
| `delete_datasource` | Remove a datasource |
|
|
136
|
+
| `ingest_datasource_models` | Auto-generate models from DB schema |
|
|
137
|
+
|
|
138
|
+
Typical agent workflow:
|
|
139
|
+
1. `create_datasource` (auto-ingests models by default) → `datasource_summary` → `inspect_model` → `query`
|
|
140
|
+
2. Or with `auto_ingest=false`: `create_datasource` → `describe_datasource` → `ingest_datasource_models` → `datasource_summary` → `query`
|
|
141
|
+
|
|
142
|
+
### Python Client
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
from slayer.client.slayer_client import SlayerClient
|
|
146
|
+
from slayer.core.query import SlayerQuery, ColumnRef
|
|
147
|
+
|
|
148
|
+
# Remote mode (connects to running server)
|
|
149
|
+
client = SlayerClient(url="http://localhost:5143")
|
|
150
|
+
|
|
151
|
+
# Or local mode (no server needed)
|
|
152
|
+
from slayer.storage.yaml_storage import YAMLStorage
|
|
153
|
+
client = SlayerClient(storage=YAMLStorage(base_dir="./my_models"))
|
|
154
|
+
|
|
155
|
+
# Query data
|
|
156
|
+
query = SlayerQuery(
|
|
157
|
+
model="orders",
|
|
158
|
+
fields=[{"formula": "count"}, {"formula": "revenue_sum"}],
|
|
159
|
+
dimensions=[ColumnRef(name="status")],
|
|
160
|
+
limit=10,
|
|
161
|
+
)
|
|
162
|
+
df = client.query_df(query)
|
|
163
|
+
print(df)
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### CLI Query
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
# Run a query directly from the terminal
|
|
170
|
+
slayer query '{"model": "orders", "fields": [{"formula": "count"}], "dimensions": [{"name": "status"}]}'
|
|
171
|
+
|
|
172
|
+
# Or from a file
|
|
173
|
+
slayer query @query.json --format json
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
## Models
|
|
178
|
+
|
|
179
|
+
Models are defined as YAML files. Add an optional `description` to help users and agents understand complex models:
|
|
180
|
+
|
|
181
|
+
```yaml
|
|
182
|
+
name: orders
|
|
183
|
+
sql_table: public.orders
|
|
184
|
+
data_source: my_postgres
|
|
185
|
+
description: "Core orders table with revenue metrics"
|
|
186
|
+
|
|
187
|
+
dimensions:
|
|
188
|
+
- name: id
|
|
189
|
+
sql: id
|
|
190
|
+
type: number
|
|
191
|
+
primary_key: true
|
|
192
|
+
- name: status
|
|
193
|
+
sql: status
|
|
194
|
+
type: string
|
|
195
|
+
- name: created_at
|
|
196
|
+
sql: created_at
|
|
197
|
+
type: time
|
|
198
|
+
|
|
199
|
+
measures:
|
|
200
|
+
- name: count
|
|
201
|
+
type: count
|
|
202
|
+
- name: revenue_sum
|
|
203
|
+
sql: amount
|
|
204
|
+
type: sum
|
|
205
|
+
- name: revenue_avg
|
|
206
|
+
sql: amount
|
|
207
|
+
type: avg
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
## Fields
|
|
212
|
+
|
|
213
|
+
The `fields` parameter specifies what data columns to return. Each field has a `formula` string, an optional `name`, and an optional `label` (human-readable display name):
|
|
214
|
+
|
|
215
|
+
```json
|
|
216
|
+
{
|
|
217
|
+
"model": "orders",
|
|
218
|
+
"dimensions": [{"name": "status"}],
|
|
219
|
+
"time_dimensions": [{"dimension": {"name": "created_at"}, "granularity": "month"}],
|
|
220
|
+
"fields": [
|
|
221
|
+
{"formula": "count"},
|
|
222
|
+
{"formula": "revenue_sum"},
|
|
223
|
+
{"formula": "revenue_sum / count", "name": "aov", "label": "Average Order Value"},
|
|
224
|
+
{"formula": "cumsum(revenue_sum)"},
|
|
225
|
+
{"formula": "change_pct(revenue_sum)"},
|
|
226
|
+
{"formula": "last(revenue_sum)", "name": "latest_rev"},
|
|
227
|
+
{"formula": "time_shift(revenue_sum, -1, 'year')", "name": "rev_last_year"},
|
|
228
|
+
{"formula": "time_shift(revenue_sum, -2)", "name": "rev_2_periods_ago"},
|
|
229
|
+
{"formula": "rank(revenue_sum)"},
|
|
230
|
+
{"formula": "change(cumsum(revenue_sum))", "name": "cumsum_delta"}
|
|
231
|
+
]
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Formulas are parsed using Python's `ast` module (see `slayer/core/formula.py`). Available functions: `cumsum`, `time_shift`, `change`, `change_pct`, `rank`, `last` (most recent value via `FIRST_VALUE`). `time_shift(x, offset)` without granularity is row-based (negative = look back, positive = look forward); `time_shift(x, offset, granularity)` is calendar-based (e.g., year-over-year via self-join CTE). Formulas support arbitrary nesting — e.g., `change(cumsum(revenue))` or `cumsum(revenue) / count`.
|
|
236
|
+
|
|
237
|
+
Functions that need ordering over time resolve the time dimension via: query `main_time_dimension` -> query `time_dimensions` (if exactly one) -> model `default_time_dimension` -> error. The `time_shift` function also uses a granularity argument (year, month, quarter, etc.) and generates a self-join CTE.
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
## Filters
|
|
241
|
+
|
|
242
|
+
Filters use simple formula strings — no verbose JSON objects:
|
|
243
|
+
|
|
244
|
+
```json
|
|
245
|
+
{
|
|
246
|
+
"model": "orders",
|
|
247
|
+
"fields": [{"formula": "count"}, {"formula": "revenue_sum"}],
|
|
248
|
+
"filters": [
|
|
249
|
+
"status == 'completed'",
|
|
250
|
+
"amount > 100"
|
|
251
|
+
]
|
|
252
|
+
}
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
**Operators**: `==`, `!=`, `>`, `>=`, `<`, `<=`, `in`, `is None`, `is not None`
|
|
256
|
+
|
|
257
|
+
**Boolean logic**: combine with `and`, `or`, `not` in a single string:
|
|
258
|
+
```json
|
|
259
|
+
"filters": ["status == 'completed' or status == 'pending'"]
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
**Functions**: `contains(col, 'val')`, `starts_with(col, 'val')`, `ends_with(col, 'val')`, `between(col, 'a', 'b')`. Filters on measures (e.g., `"count > 10"`) are automatically routed to HAVING.
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
## Auto-Ingestion
|
|
266
|
+
|
|
267
|
+
Connect to a database and generate models automatically. SLayer introspects the schema, detects foreign key relationships, and creates denormalized models with rollup-style LEFT JOINs.
|
|
268
|
+
|
|
269
|
+
For example, given tables `orders → customers → regions` (via FKs), the `orders` model will automatically include:
|
|
270
|
+
- Rolled-up dimensions: `customers__name`, `regions__name`, etc.
|
|
271
|
+
- Count-distinct measures: `customers__count`, `regions__count`
|
|
272
|
+
- A SQL query with transitive LEFT JOINs baked in
|
|
273
|
+
|
|
274
|
+
```bash
|
|
275
|
+
# Via CLI
|
|
276
|
+
slayer ingest --datasource my_postgres --schema public
|
|
277
|
+
|
|
278
|
+
# Via API
|
|
279
|
+
curl -X POST http://localhost:5143/ingest \
|
|
280
|
+
-d '{"datasource": "my_postgres", "schema_name": "public"}'
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
Via MCP, agents can do this conversationally:
|
|
284
|
+
1. `create_datasource(name="mydb", type="postgres", host="localhost", database="app", username="user", password="pass")`
|
|
285
|
+
2. `ingest_datasource_models(datasource_name="mydb", schema_name="public")`
|
|
286
|
+
3. `datasource_summary()` → `inspect_model(model_name="orders")` → `query(...)`
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
## Configuration
|
|
290
|
+
|
|
291
|
+
Datasources are configured as individual YAML files in the `datasources/` directory:
|
|
292
|
+
|
|
293
|
+
```yaml
|
|
294
|
+
# datasources/my_postgres.yaml
|
|
295
|
+
name: my_postgres
|
|
296
|
+
type: postgres
|
|
297
|
+
host: ${DB_HOST}
|
|
298
|
+
port: 5432
|
|
299
|
+
database: ${DB_NAME}
|
|
300
|
+
username: ${DB_USER}
|
|
301
|
+
password: ${DB_PASSWORD}
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
Environment variable references (`${VAR}`) are resolved at read time. Both `username` and `user` field names are accepted.
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
## Storage Backends
|
|
308
|
+
|
|
309
|
+
SLayer ships with two storage backends:
|
|
310
|
+
|
|
311
|
+
- **YAMLStorage** (default) — models and datasources as YAML files on disk. Great for version control.
|
|
312
|
+
- **SQLiteStorage** — everything in a single SQLite file. Good for embedded use or when you don't want to manage files.
|
|
313
|
+
|
|
314
|
+
```python
|
|
315
|
+
from slayer.storage.yaml_storage import YAMLStorage
|
|
316
|
+
from slayer.storage.sqlite_storage import SQLiteStorage
|
|
317
|
+
|
|
318
|
+
# YAML files in a directory
|
|
319
|
+
storage = YAMLStorage(base_dir="./slayer_data")
|
|
320
|
+
|
|
321
|
+
# Single SQLite file
|
|
322
|
+
storage = SQLiteStorage(db_path="./slayer.db")
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
Both implement the `StorageBackend` protocol, so you can swap them freely or write your own:
|
|
326
|
+
|
|
327
|
+
```python
|
|
328
|
+
from slayer.storage.base import StorageBackend
|
|
329
|
+
|
|
330
|
+
class MyCustomStorage(StorageBackend):
|
|
331
|
+
def save_model(self, model): ...
|
|
332
|
+
def get_model(self, name): ...
|
|
333
|
+
def list_models(self): ...
|
|
334
|
+
def delete_model(self, name): ...
|
|
335
|
+
# same for datasources
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
Pass any backend to `create_app()`, `create_mcp_server()`, or `SlayerClient(storage=...)`.
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
## Examples
|
|
342
|
+
|
|
343
|
+
The `examples/` directory contains runnable examples that also serve as integration tests:
|
|
344
|
+
|
|
345
|
+
| Example | Description | How to run |
|
|
346
|
+
|---------|-------------|------------|
|
|
347
|
+
| [embedded](examples/embedded/) | SQLite, no server needed | `python examples/embedded/run.py` |
|
|
348
|
+
| [postgres](examples/postgres/) | Docker Compose with Postgres + REST API | `cd examples/postgres && docker compose up -d` |
|
|
349
|
+
| [mysql](examples/mysql/) | Docker Compose with MySQL + REST API | `cd examples/mysql && docker compose up -d` |
|
|
350
|
+
| [clickhouse](examples/clickhouse/) | Docker Compose with ClickHouse + REST API | `cd examples/clickhouse && docker compose up -d` |
|
|
351
|
+
|
|
352
|
+
Each example includes a `verify.py` script that runs assertions against the seeded data.
|
|
353
|
+
|
|
354
|
+
All examples use a shared seed dataset (`examples/seed.py`) with a small e-commerce schema: regions, customers, products, and orders (68 orders across 12 months). The embedded example includes derived column demo queries using `fields`.
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
## Claude Code Skills
|
|
358
|
+
|
|
359
|
+
SLayer includes Claude Code skills in `.claude/skills/` to help Claude understand the codebase:
|
|
360
|
+
|
|
361
|
+
- **slayer-overview** — architecture, package structure, MCP tools list
|
|
362
|
+
- **slayer-query** — how to construct queries with fields, dimensions, filters, time dimensions
|
|
363
|
+
- **slayer-models** — model definitions, datasource configs, auto-ingestion, incremental editing
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
## Development
|
|
367
|
+
|
|
368
|
+
```bash
|
|
369
|
+
# Install with all extras
|
|
370
|
+
poetry install -E all
|
|
371
|
+
|
|
372
|
+
# Run tests
|
|
373
|
+
poetry run pytest
|
|
374
|
+
|
|
375
|
+
# Lint
|
|
376
|
+
poetry run ruff check slayer/ tests/
|
|
377
|
+
|
|
378
|
+
# Start dev server
|
|
379
|
+
poetry run slayer serve
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
## Why SLayer?
|
|
384
|
+
|
|
385
|
+
### SLayer is embeddable
|
|
386
|
+
|
|
387
|
+
Besides being a standalone service, SLayer is also a Python module. That's why it can also be directly imported and used in Python applications with no network communication involved.
|
|
388
|
+
|
|
389
|
+
For example, using it in a multi-tenant application could be as simple as:
|
|
390
|
+
|
|
391
|
+
```python
|
|
392
|
+
client = SlayerClient(storage=MyStorage(tenant_id=...))
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
No need for setting up network and auth at all.
|
|
396
|
+
|
|
397
|
+
### SLayer models can be updated on the fly
|
|
398
|
+
|
|
399
|
+
In SLayer, models are treated as a dynamic part of the process, rather than something that is preconfigured and frozen.
|
|
400
|
+
Models can be created or edited at any time and immediately queried.
|
|
401
|
+
We expose tools for interacting with the models via API, CLI and MCP.
|
|
402
|
+
|
|
403
|
+
### SLayer is flexible
|
|
404
|
+
|
|
405
|
+
Slayer is agnostic to the datasource type: it can be an SQL database, a BI tool or even a REST API.
|
|
406
|
+
Adapters for popular databases are included, but adding a new one just takes implementing 3 straightforward methods: a query-to-datasource-input translator and a result-to-dataframe parser. Please open a pull request if you write one!
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
## Known limitations
|
|
410
|
+
|
|
411
|
+
SLayer currently has no caching or pre-aggregation engine.
|
|
412
|
+
If you need to process lots of requests to large databases at sub-second latency, consider adding a caching layer or pre-aggregation engine.
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
## License
|
|
416
|
+
|
|
417
|
+
MIT — see [LICENSE](LICENSE).
|
|
418
|
+
|