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.
Files changed (80) hide show
  1. {tabletalk-0.2.0 → tabletalk-0.2.1}/PKG-INFO +1 -1
  2. {tabletalk-0.2.0 → tabletalk-0.2.1}/examples/ecommerce/README.md +46 -0
  3. tabletalk-0.2.1/examples/ecommerce/dbt_project/dbt_project.yml +16 -0
  4. tabletalk-0.2.1/examples/ecommerce/dbt_project/models/marts/fct_orders.sql +15 -0
  5. tabletalk-0.2.1/examples/ecommerce/dbt_project/models/marts/fct_orders.yml +17 -0
  6. tabletalk-0.2.1/examples/ecommerce/dbt_project/models/sources.yml +22 -0
  7. tabletalk-0.2.1/examples/ecommerce/dbt_project/models/staging/stg_customers.sql +10 -0
  8. tabletalk-0.2.1/examples/ecommerce/dbt_project/models/staging/stg_orders.sql +10 -0
  9. tabletalk-0.2.1/examples/ecommerce/dbt_project/profiles.yml +13 -0
  10. tabletalk-0.2.1/examples/ecommerce/ecommerce.duckdb +0 -0
  11. {tabletalk-0.2.0 → tabletalk-0.2.1}/examples/ecommerce/manifest/customers.txt +1 -1
  12. {tabletalk-0.2.0 → tabletalk-0.2.1}/examples/ecommerce/manifest/inventory.txt +3 -3
  13. {tabletalk-0.2.0 → tabletalk-0.2.1}/examples/ecommerce/manifest/marketing.txt +2 -2
  14. tabletalk-0.2.1/examples/ecommerce/manifest/sales.txt +9 -0
  15. {tabletalk-0.2.0 → tabletalk-0.2.1}/pyproject.toml +1 -1
  16. {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/app.py +16 -0
  17. {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/providers/mysql_provider.py +1 -1
  18. {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/static/index.html +12 -1
  19. tabletalk-0.2.0/examples/ecommerce/manifest/sales.txt +0 -9
  20. {tabletalk-0.2.0 → tabletalk-0.2.1}/.github/workflows/db_provider_tests.yml +0 -0
  21. {tabletalk-0.2.0 → tabletalk-0.2.1}/.github/workflows/publish.yml +0 -0
  22. {tabletalk-0.2.0 → tabletalk-0.2.1}/.gitignore +0 -0
  23. {tabletalk-0.2.0 → tabletalk-0.2.1}/.pre-commit-config.yaml +0 -0
  24. {tabletalk-0.2.0 → tabletalk-0.2.1}/.python-version +0 -0
  25. {tabletalk-0.2.0 → tabletalk-0.2.1}/LICENSE +0 -0
  26. {tabletalk-0.2.0 → tabletalk-0.2.1}/README.md +0 -0
  27. {tabletalk-0.2.0 → tabletalk-0.2.1}/contexts/default_context.yaml +0 -0
  28. {tabletalk-0.2.0 → tabletalk-0.2.1}/docs/Makefile +0 -0
  29. {tabletalk-0.2.0 → tabletalk-0.2.1}/docs/conf.py +0 -0
  30. {tabletalk-0.2.0 → tabletalk-0.2.1}/docs/dev/contexts.rst +0 -0
  31. {tabletalk-0.2.0 → tabletalk-0.2.1}/docs/dev/local_development.rst +0 -0
  32. {tabletalk-0.2.0 → tabletalk-0.2.1}/docs/dev/providers.rst +0 -0
  33. {tabletalk-0.2.0 → tabletalk-0.2.1}/docs/dev/querying.rst +0 -0
  34. {tabletalk-0.2.0 → tabletalk-0.2.1}/docs/index.rst +0 -0
  35. {tabletalk-0.2.0 → tabletalk-0.2.1}/docs/make.bat +0 -0
  36. {tabletalk-0.2.0 → tabletalk-0.2.1}/examples/ecommerce/agents/customer_analyst.yaml +0 -0
  37. {tabletalk-0.2.0 → tabletalk-0.2.1}/examples/ecommerce/agents/inventory_manager.yaml +0 -0
  38. {tabletalk-0.2.0 → tabletalk-0.2.1}/examples/ecommerce/agents/marketing_analyst.yaml +0 -0
  39. {tabletalk-0.2.0 → tabletalk-0.2.1}/examples/ecommerce/agents/revenue_analyst.yaml +0 -0
  40. {tabletalk-0.2.0 → tabletalk-0.2.1}/examples/ecommerce/contexts/customers.yaml +0 -0
  41. {tabletalk-0.2.0 → tabletalk-0.2.1}/examples/ecommerce/contexts/inventory.yaml +0 -0
  42. {tabletalk-0.2.0 → tabletalk-0.2.1}/examples/ecommerce/contexts/marketing.yaml +0 -0
  43. {tabletalk-0.2.0 → tabletalk-0.2.1}/examples/ecommerce/contexts/sales.yaml +0 -0
  44. {tabletalk-0.2.0 → tabletalk-0.2.1}/examples/ecommerce/seed.py +0 -0
  45. {tabletalk-0.2.0 → tabletalk-0.2.1}/examples/ecommerce/tabletalk.yaml +0 -0
  46. {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/__init__.py +0 -0
  47. {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/cli.py +0 -0
  48. {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/factories.py +0 -0
  49. {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/interfaces.py +0 -0
  50. {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/main.py +0 -0
  51. {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/profiles.py +0 -0
  52. {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/providers/__init__.py +0 -0
  53. {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/providers/anthropic_provider.py +0 -0
  54. {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/providers/azuresql_provider.py +0 -0
  55. {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/providers/bigquery_provider.py +0 -0
  56. {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/providers/duckdb_provider.py +0 -0
  57. {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/providers/openai_provider.py +0 -0
  58. {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/providers/postgres_provider.py +0 -0
  59. {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/providers/snowflake_provider.py +0 -0
  60. {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/providers/sqlite_provider.py +0 -0
  61. {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/static/logo.jpg +0 -0
  62. {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/tests/__init__.py +0 -0
  63. {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/tests/conftest.py +0 -0
  64. {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/tests/providers/__init__.py +0 -0
  65. {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/tests/providers/test_azuresql.py +0 -0
  66. {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/tests/providers/test_bigquery.py +0 -0
  67. {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/tests/providers/test_duckdb.py +0 -0
  68. {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/tests/providers/test_mysql.py +0 -0
  69. {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/tests/providers/test_postgres.py +0 -0
  70. {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/tests/providers/test_snowflake.py +0 -0
  71. {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/tests/providers/test_sqlite.py +0 -0
  72. {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/tests/test_app.py +0 -0
  73. {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/tests/test_cli.py +0 -0
  74. {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/tests/test_factories.py +0 -0
  75. {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/tests/test_interfaces.py +0 -0
  76. {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/tests/test_profiles.py +0 -0
  77. {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/tests/test_utils.py +0 -0
  78. {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk/utils.py +0 -0
  79. {tabletalk-0.2.0 → tabletalk-0.2.1}/tabletalk.yaml +0 -0
  80. {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.0
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,10 @@
1
+ select
2
+ id,
3
+ name,
4
+ email,
5
+ city,
6
+ country,
7
+ signup_date,
8
+ active,
9
+ lifetime_value
10
+ from {{ source('main', 'customers') }}
@@ -0,0 +1,10 @@
1
+ select
2
+ id,
3
+ customer_id,
4
+ status,
5
+ total_amount,
6
+ discount_amount,
7
+ created_at,
8
+ shipped_at,
9
+ delivered_at
10
+ from {{ source('main', 'orders') }}
@@ -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
@@ -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[FK:products.id]|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[FK:categories.id]|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
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[FK:campaigns.id]|customer_id:I[FK:customers.id]|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
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
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "tabletalk"
7
- version = "0.2.0"
7
+ version = "0.2.1"
8
8
  description = "Natural language to SQL — the dbt companion for AI-powered data workflows"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -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">table<span>talk</span></div>
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