tabletalk 0.2.0__tar.gz → 0.2.1__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.
- {tabletalk-0.2.0 → tabletalk-0.2.1}/PKG-INFO +1 -1
- {tabletalk-0.2.0 → tabletalk-0.2.1}/examples/ecommerce/README.md +46 -0
- tabletalk-0.2.1/examples/ecommerce/dbt_project/dbt_project.yml +16 -0
- tabletalk-0.2.1/examples/ecommerce/dbt_project/models/marts/fct_orders.sql +15 -0
- tabletalk-0.2.1/examples/ecommerce/dbt_project/models/marts/fct_orders.yml +17 -0
- tabletalk-0.2.1/examples/ecommerce/dbt_project/models/sources.yml +22 -0
- tabletalk-0.2.1/examples/ecommerce/dbt_project/models/staging/stg_customers.sql +10 -0
- tabletalk-0.2.1/examples/ecommerce/dbt_project/models/staging/stg_orders.sql +10 -0
- tabletalk-0.2.1/examples/ecommerce/dbt_project/profiles.yml +13 -0
- tabletalk-0.2.1/examples/ecommerce/ecommerce.duckdb +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/examples/ecommerce/manifest/customers.txt +1 -1
- {tabletalk-0.2.0 → tabletalk-0.2.1}/examples/ecommerce/manifest/inventory.txt +3 -3
- {tabletalk-0.2.0 → tabletalk-0.2.1}/examples/ecommerce/manifest/marketing.txt +2 -2
- tabletalk-0.2.1/examples/ecommerce/manifest/sales.txt +9 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/pyproject.toml +1 -1
- {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/app.py +16 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/providers/mysql_provider.py +1 -1
- {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/static/index.html +12 -1
- tabletalk-0.2.0/examples/ecommerce/manifest/sales.txt +0 -9
- {tabletalk-0.2.0 → tabletalk-0.2.1}/.github/workflows/db_provider_tests.yml +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/.github/workflows/publish.yml +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/.gitignore +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/.pre-commit-config.yaml +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/.python-version +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/LICENSE +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/README.md +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/contexts/default_context.yaml +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/docs/Makefile +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/docs/conf.py +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/docs/dev/contexts.rst +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/docs/dev/local_development.rst +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/docs/dev/providers.rst +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/docs/dev/querying.rst +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/docs/index.rst +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/docs/make.bat +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/examples/ecommerce/agents/customer_analyst.yaml +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/examples/ecommerce/agents/inventory_manager.yaml +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/examples/ecommerce/agents/marketing_analyst.yaml +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/examples/ecommerce/agents/revenue_analyst.yaml +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/examples/ecommerce/contexts/customers.yaml +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/examples/ecommerce/contexts/inventory.yaml +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/examples/ecommerce/contexts/marketing.yaml +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/examples/ecommerce/contexts/sales.yaml +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/examples/ecommerce/seed.py +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/examples/ecommerce/tabletalk.yaml +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/__init__.py +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/cli.py +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/factories.py +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/interfaces.py +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/main.py +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/profiles.py +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/providers/__init__.py +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/providers/anthropic_provider.py +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/providers/azuresql_provider.py +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/providers/bigquery_provider.py +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/providers/duckdb_provider.py +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/providers/openai_provider.py +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/providers/postgres_provider.py +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/providers/snowflake_provider.py +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/providers/sqlite_provider.py +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/static/logo.jpg +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/tests/__init__.py +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/tests/conftest.py +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/tests/providers/__init__.py +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/tests/providers/test_azuresql.py +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/tests/providers/test_bigquery.py +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/tests/providers/test_duckdb.py +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/tests/providers/test_mysql.py +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/tests/providers/test_postgres.py +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/tests/providers/test_snowflake.py +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/tests/providers/test_sqlite.py +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/tests/test_app.py +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/tests/test_cli.py +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/tests/test_factories.py +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/tests/test_interfaces.py +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/tests/test_profiles.py +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/tests/test_utils.py +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/utils.py +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk.yaml +0 -0
- {tabletalk-0.2.0 → tabletalk-0.2.1}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tabletalk
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: Natural language to SQL — the dbt companion for AI-powered data workflows
|
|
5
5
|
Project-URL: Homepage, https://github.com/wtbates99/tabletalk
|
|
6
6
|
Project-URL: Repository, https://github.com/wtbates99/tabletalk
|
|
@@ -198,6 +198,52 @@ Rerunning `tabletalk apply` reconciles the manifest with the current schema.
|
|
|
198
198
|
|
|
199
199
|
---
|
|
200
200
|
|
|
201
|
+
## dbt integration
|
|
202
|
+
|
|
203
|
+
The `dbt_project/` folder is a minimal dbt project that points to the same
|
|
204
|
+
DuckDB database. Use it to demonstrate how tabletalk reads from an existing
|
|
205
|
+
dbt `profiles.yml`.
|
|
206
|
+
|
|
207
|
+
```
|
|
208
|
+
dbt_project/
|
|
209
|
+
├── dbt_project.yml # dbt project config (profile: ecommerce)
|
|
210
|
+
├── profiles.yml # copy to ~/.dbt/profiles.yml
|
|
211
|
+
└── models/
|
|
212
|
+
├── sources.yml # declares the 8 raw tables as dbt sources
|
|
213
|
+
├── staging/
|
|
214
|
+
│ ├── stg_orders.sql
|
|
215
|
+
│ └── stg_customers.sql
|
|
216
|
+
└── marts/
|
|
217
|
+
├── fct_orders.sql # enriched orders mart (customer name, item count)
|
|
218
|
+
└── fct_orders.yml # schema + tests
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
**To demo the dbt → tabletalk flow:**
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
# 1. Update the path in dbt_project/profiles.yml to point to your ecommerce.duckdb
|
|
225
|
+
# e.g. path: /Users/yourname/tabletalk/examples/ecommerce/ecommerce.duckdb
|
|
226
|
+
|
|
227
|
+
# 2. Copy the profile to ~/.dbt/
|
|
228
|
+
cp dbt_project/profiles.yml ~/.dbt/profiles.yml
|
|
229
|
+
|
|
230
|
+
# 3. (Optional) Run dbt to create the marts in the database
|
|
231
|
+
pip install dbt-duckdb
|
|
232
|
+
cd dbt_project && dbt run && cd ..
|
|
233
|
+
|
|
234
|
+
# 4. Import the connection into tabletalk
|
|
235
|
+
tabletalk connect --from-dbt ecommerce
|
|
236
|
+
|
|
237
|
+
# 5. Reference the saved profile in tabletalk.yaml
|
|
238
|
+
# profile: ecommerce
|
|
239
|
+
|
|
240
|
+
# 6. Compile and query
|
|
241
|
+
tabletalk apply
|
|
242
|
+
tabletalk serve
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
201
247
|
## Connecting to a real database
|
|
202
248
|
|
|
203
249
|
To connect to a production database instead of DuckDB:
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
name: ecommerce
|
|
2
|
+
version: '1.0.0'
|
|
3
|
+
config-version: 2
|
|
4
|
+
|
|
5
|
+
profile: ecommerce
|
|
6
|
+
|
|
7
|
+
model-paths: ["models"]
|
|
8
|
+
target-path: "target"
|
|
9
|
+
clean-targets: ["target", "dbt_packages"]
|
|
10
|
+
|
|
11
|
+
models:
|
|
12
|
+
ecommerce:
|
|
13
|
+
staging:
|
|
14
|
+
+materialized: view
|
|
15
|
+
marts:
|
|
16
|
+
+materialized: table
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
select
|
|
2
|
+
o.id as order_id,
|
|
3
|
+
o.customer_id,
|
|
4
|
+
c.name as customer_name,
|
|
5
|
+
c.city,
|
|
6
|
+
o.status,
|
|
7
|
+
o.total_amount,
|
|
8
|
+
o.discount_amount,
|
|
9
|
+
o.total_amount - o.discount_amount as net_revenue,
|
|
10
|
+
count(oi.id) as item_count,
|
|
11
|
+
o.created_at
|
|
12
|
+
from {{ ref('stg_orders') }} o
|
|
13
|
+
left join {{ source('main', 'customers') }} c on c.id = o.customer_id
|
|
14
|
+
left join {{ source('main', 'order_items') }} oi on oi.order_id = o.id
|
|
15
|
+
group by 1, 2, 3, 4, 5, 6, 7, 8, 10
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
|
|
3
|
+
models:
|
|
4
|
+
- name: fct_orders
|
|
5
|
+
description: "One row per order — enriched with customer info and item count"
|
|
6
|
+
columns:
|
|
7
|
+
- name: order_id
|
|
8
|
+
description: "Primary key"
|
|
9
|
+
tests:
|
|
10
|
+
- unique
|
|
11
|
+
- not_null
|
|
12
|
+
- name: customer_name
|
|
13
|
+
description: "Denormalized from customers table"
|
|
14
|
+
- name: net_revenue
|
|
15
|
+
description: "total_amount minus discount_amount"
|
|
16
|
+
- name: item_count
|
|
17
|
+
description: "Number of line items in the order"
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
|
|
3
|
+
sources:
|
|
4
|
+
- name: main
|
|
5
|
+
description: "Raw ecommerce tables from the DuckDB database"
|
|
6
|
+
tables:
|
|
7
|
+
- name: customers
|
|
8
|
+
description: "Registered customer accounts"
|
|
9
|
+
- name: orders
|
|
10
|
+
description: "Customer orders with status and totals"
|
|
11
|
+
- name: order_items
|
|
12
|
+
description: "Line items within each order"
|
|
13
|
+
- name: products
|
|
14
|
+
description: "Product catalogue with pricing and cost"
|
|
15
|
+
- name: categories
|
|
16
|
+
description: "Product category taxonomy"
|
|
17
|
+
- name: inventory
|
|
18
|
+
description: "Stock levels by product and warehouse"
|
|
19
|
+
- name: campaigns
|
|
20
|
+
description: "Marketing campaigns with budget and spend"
|
|
21
|
+
- name: campaign_conversions
|
|
22
|
+
description: "Customer conversions attributed to campaigns"
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Copy this file to ~/.dbt/profiles.yml
|
|
2
|
+
# Then run: tabletalk connect --from-dbt ecommerce
|
|
3
|
+
#
|
|
4
|
+
# Update the `path` to the absolute path of your ecommerce.duckdb file.
|
|
5
|
+
# Example: /Users/yourname/tabletalk/examples/ecommerce/ecommerce.duckdb
|
|
6
|
+
|
|
7
|
+
ecommerce:
|
|
8
|
+
target: dev
|
|
9
|
+
outputs:
|
|
10
|
+
dev:
|
|
11
|
+
type: duckdb
|
|
12
|
+
path: /path/to/tabletalk/examples/ecommerce/ecommerce.duckdb
|
|
13
|
+
threads: 4
|
|
Binary file
|
|
@@ -2,4 +2,4 @@ DATA_SOURCE: duckdb - Ecommerce platform — customers, orders, products, invent
|
|
|
2
2
|
CONTEXT: customers - Customer profiles, account status, and lifetime value. Use this context to answer questions about customer acquisition, demographics, activity, and segmentation. (v1.0)
|
|
3
3
|
DATASET: main - Primary ecommerce schema
|
|
4
4
|
TABLES:
|
|
5
|
-
main.customers|Registered customer accounts. Each row is one customer. Columns: id (PK), name, email (unique), phone, city, country, signup_date, active (boolean), lifetime_value (running total of orders).|id:I[PK]|name:S|email:S|phone:S|city:S|country:S|signup_date:D|active:B|lifetime_value:N
|
|
5
|
+
main.customers|Registered customer accounts. Each row is one customer. Columns: id (PK), name, email (unique), phone, city, country, signup_date, active (boolean), lifetime_value (running total of orders).|id:I[PK]|name:S|email:S|phone:S|city:S|country:S|signup_date:D|active:B|lifetime_value:N
|
|
@@ -2,6 +2,6 @@ DATA_SOURCE: duckdb - Ecommerce platform — customers, orders, products, invent
|
|
|
2
2
|
CONTEXT: inventory - Stock levels, reorder thresholds, and product catalogue. Use this context to monitor warehouse quantities and flag products that need restocking. (v1.0)
|
|
3
3
|
DATASET: main - Primary ecommerce schema
|
|
4
4
|
TABLES:
|
|
5
|
-
main.inventory|Current stock quantities by product and warehouse. reorder_point is the minimum quantity that triggers a purchase order. When quantity <= reorder_point, the product needs restocking. FK: product_id → products.id.|id:I[PK]|product_id:I
|
|
6
|
-
main.products|Full product catalogue including cost and active status. active=FALSE means the product is discontinued (may still have inventory). FK: category_id → categories.id.|id:I[PK]|sku:S|name:S|description:S|price:N|cost:N|category_id:I
|
|
7
|
-
main.categories|Product categories: Electronics, Clothing, Books, Home. Use for category-level inventory summaries.|id:I[PK]|name:S|slug:S|description:S
|
|
5
|
+
main.inventory|Current stock quantities by product and warehouse. reorder_point is the minimum quantity that triggers a purchase order. When quantity <= reorder_point, the product needs restocking. FK: product_id → products.id.|id:I[PK]|product_id:I|warehouse:S|quantity:I|reorder_point:I|updated_at:TS
|
|
6
|
+
main.products|Full product catalogue including cost and active status. active=FALSE means the product is discontinued (may still have inventory). FK: category_id → categories.id.|id:I[PK]|sku:S|name:S|description:S|price:N|cost:N|category_id:I|active:B|created_at:D
|
|
7
|
+
main.categories|Product categories: Electronics, Clothing, Books, Home. Use for category-level inventory summaries.|id:I[PK]|name:S|slug:S|description:S
|
|
@@ -3,5 +3,5 @@ CONTEXT: marketing - Campaign performance, spend tracking, and customer acquisit
|
|
|
3
3
|
DATASET: main - Primary ecommerce schema
|
|
4
4
|
TABLES:
|
|
5
5
|
main.campaigns|Marketing campaigns. channel is 'email' or 'social'. budget is the planned spend; spend is the actual spend to date. active=FALSE means the campaign has ended.|id:I[PK]|name:S|channel:S|budget:N|spend:N|start_date:D|end_date:D|active:B
|
|
6
|
-
main.campaign_conversions|One row per customer conversion attributed to a campaign. revenue is the order value attributed to this conversion. FK: campaign_id → campaigns.id, customer_id → customers.id.|id:I[PK]|campaign_id:I
|
|
7
|
-
main.customers|Customer data used for acquisition analysis. Join on customer_id to get signup_date, city, and lifetime_value.|id:I[PK]|name:S|email:S|phone:S|city:S|country:S|signup_date:D|active:B|lifetime_value:N
|
|
6
|
+
main.campaign_conversions|One row per customer conversion attributed to a campaign. revenue is the order value attributed to this conversion. FK: campaign_id → campaigns.id, customer_id → customers.id.|id:I[PK]|campaign_id:I|customer_id:I|converted_at:TS|revenue:N
|
|
7
|
+
main.customers|Customer data used for acquisition analysis. Join on customer_id to get signup_date, city, and lifetime_value.|id:I[PK]|name:S|email:S|phone:S|city:S|country:S|signup_date:D|active:B|lifetime_value:N
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
DATA_SOURCE: duckdb - Ecommerce platform — customers, orders, products, inventory, and marketing. DuckDB backend. Run `python seed.py` to initialise.
|
|
2
|
+
CONTEXT: sales - Order processing, revenue, and product sales. Joins orders, line items, customers, and products for end-to-end sales analysis. (v1.0)
|
|
3
|
+
DATASET: main - Primary ecommerce schema
|
|
4
|
+
TABLES:
|
|
5
|
+
main.orders|Customer orders. status can be: pending, shipped, delivered, cancelled. total_amount is the final charged amount (after discounts). discount_amount is the absolute value of any discount applied.|id:I[PK]|customer_id:I|status:S|total_amount:N|discount_amount:N|shipping_address:S|created_at:TS|shipped_at:TS|delivered_at:TS
|
|
6
|
+
main.order_items|Individual line items within an order. FK: order_id → orders.id, product_id → products.id. unit_price is the price at time of purchase (may differ from current product price).|id:I[PK]|order_id:I|product_id:I|quantity:I|unit_price:N|discount:N
|
|
7
|
+
main.customers|Customer accounts. Join to orders on customer_id = customers.id. Use city/country for geographic segmentation.|id:I[PK]|name:S|email:S|phone:S|city:S|country:S|signup_date:D|active:B|lifetime_value:N
|
|
8
|
+
main.products|Product catalogue. Join to order_items on product_id = products.id. price is the current retail price. cost is the COGS for margin analysis.|id:I[PK]|sku:S|name:S|description:S|price:N|cost:N|category_id:I|active:B|created_at:D
|
|
9
|
+
main.categories|Product categories. Join to products on category_id = categories.id. Use for category-level revenue breakdowns.|id:I[PK]|name:S|slug:S|description:S
|
|
@@ -346,6 +346,22 @@ def get_history() -> Union[Tuple[Response, int], Response]:
|
|
|
346
346
|
return jsonify({"history": [], "error": str(e)})
|
|
347
347
|
|
|
348
348
|
|
|
349
|
+
# ── Config ─────────────────────────────────────────────────────────────────────
|
|
350
|
+
|
|
351
|
+
@app.route("/config")
|
|
352
|
+
def get_config() -> Union[Tuple[Response, int], Response]:
|
|
353
|
+
"""Return LLM provider and model name for display in the UI."""
|
|
354
|
+
try:
|
|
355
|
+
qs = _get_session()
|
|
356
|
+
llm = qs.config.get("llm", {})
|
|
357
|
+
return jsonify({
|
|
358
|
+
"provider": llm.get("provider", "unknown"),
|
|
359
|
+
"model": llm.get("model", "unknown"),
|
|
360
|
+
})
|
|
361
|
+
except Exception:
|
|
362
|
+
return jsonify({"provider": "unknown", "model": "unknown"})
|
|
363
|
+
|
|
364
|
+
|
|
349
365
|
# ── Legacy non-streaming query (kept for backward compat) ─────────────────────
|
|
350
366
|
|
|
351
367
|
@app.route("/query", methods=["POST"])
|
|
@@ -100,7 +100,7 @@ class MySQLProvider(DatabaseProvider):
|
|
|
100
100
|
# Primary keys for the schema in one query
|
|
101
101
|
cursor.execute(
|
|
102
102
|
"""
|
|
103
|
-
SELECT TABLE_NAME, COLUMN_NAME
|
|
103
|
+
SELECT kcu.TABLE_NAME, kcu.COLUMN_NAME
|
|
104
104
|
FROM information_schema.KEY_COLUMN_USAGE kcu
|
|
105
105
|
JOIN information_schema.TABLE_CONSTRAINTS tc
|
|
106
106
|
ON kcu.CONSTRAINT_NAME = tc.CONSTRAINT_NAME
|
|
@@ -75,6 +75,8 @@ input,textarea{font-family:var(--font);outline:none;color:var(--text)}
|
|
|
75
75
|
}
|
|
76
76
|
.sb-logo{font-size:16px;font-weight:600;letter-spacing:-.3px}
|
|
77
77
|
.sb-logo span{color:var(--accent)}
|
|
78
|
+
.sb-logo-row{display:flex;flex-direction:column;gap:3px}
|
|
79
|
+
#model-badge{font-size:10px;color:var(--muted);letter-spacing:.02em;font-family:var(--mono)}
|
|
78
80
|
.sb-actions{display:flex;gap:6px}
|
|
79
81
|
.icon-btn{
|
|
80
82
|
padding:5px 7px;
|
|
@@ -531,7 +533,10 @@ table.rt tr:hover td{background:var(--elevated)}
|
|
|
531
533
|
<!-- Sidebar -->
|
|
532
534
|
<aside id="sidebar">
|
|
533
535
|
<div class="sb-header">
|
|
534
|
-
<div class="sb-logo">
|
|
536
|
+
<div class="sb-logo-row">
|
|
537
|
+
<div class="sb-logo">table<span>talk</span></div>
|
|
538
|
+
<div id="model-badge"></div>
|
|
539
|
+
</div>
|
|
535
540
|
<div class="sb-actions">
|
|
536
541
|
<button class="icon-btn" id="theme-btn" title="Toggle theme">☀</button>
|
|
537
542
|
<button class="icon-btn" id="new-chat-btn" title="New conversation">✎</button>
|
|
@@ -1264,6 +1269,12 @@ $('refresh-history').onclick = loadHistory;
|
|
|
1264
1269
|
|
|
1265
1270
|
// ── Init ───────────────────────────────────────────────────────
|
|
1266
1271
|
loadManifests();
|
|
1272
|
+
(async () => {
|
|
1273
|
+
try {
|
|
1274
|
+
const { provider, model } = await api('/config');
|
|
1275
|
+
$('model-badge').textContent = `${provider} · ${model}`;
|
|
1276
|
+
} catch {}
|
|
1277
|
+
})();
|
|
1267
1278
|
msgInput.focus();
|
|
1268
1279
|
})();
|
|
1269
1280
|
</script>
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
DATA_SOURCE: duckdb - Ecommerce platform — customers, orders, products, inventory, and marketing. DuckDB backend. Run `python seed.py` to initialise.
|
|
2
|
-
CONTEXT: sales - Order processing, revenue, and product sales. Joins orders, line items, customers, and products for end-to-end sales analysis. (v1.0)
|
|
3
|
-
DATASET: main - Primary ecommerce schema
|
|
4
|
-
TABLES:
|
|
5
|
-
main.orders|Customer orders. status can be: pending, shipped, delivered, cancelled. total_amount is the final charged amount (after discounts). discount_amount is the absolute value of any discount applied.|id:I[PK]|customer_id:I[FK:customers.id]|status:S|total_amount:N|discount_amount:N|shipping_address:S|created_at:TS|shipped_at:TS|delivered_at:TS
|
|
6
|
-
main.order_items|Individual line items within an order. FK: order_id → orders.id, product_id → products.id. unit_price is the price at time of purchase (may differ from current product price).|id:I[PK]|order_id:I[FK:orders.id]|product_id:I[FK:products.id]|quantity:I|unit_price:N|discount:N
|
|
7
|
-
main.customers|Customer accounts. Join to orders on customer_id = customers.id. Use city/country for geographic segmentation.|id:I[PK]|name:S|email:S|phone:S|city:S|country:S|signup_date:D|active:B|lifetime_value:N
|
|
8
|
-
main.products|Product catalogue. Join to order_items on product_id = products.id. price is the current retail price. cost is the COGS for margin analysis.|id:I[PK]|sku:S|name:S|description:S|price:N|cost:N|category_id:I[FK:categories.id]|active:B|created_at:D
|
|
9
|
-
main.categories|Product categories. Join to products on category_id = categories.id. Use for category-level revenue breakdowns.|id:I[PK]|name:S|slug:S|description:S
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|