thoughtleaders-cli 0.7.7__tar.gz → 0.7.9__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.
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/.claude-plugin/plugin.json +1 -1
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/.github/workflows/python-publish.yml +4 -4
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/PKG-INFO +3 -3
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/README.md +2 -2
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/pyproject.toml +1 -1
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl/SKILL.md +2 -2
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/src/tl_cli/__init__.py +1 -1
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/src/tl_cli/commands/describe.py +20 -19
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/src/tl_cli/output/formatter.py +13 -7
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/tests/test_describe.py +19 -18
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/tests/test_output.py +7 -7
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/.claude-plugin/marketplace.json +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/.gitignore +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/AGENTS.md +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/API.md +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/CLAUDE.md +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/LICENSE +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/agents/tl-analyst.md +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/agents/youtube-comment-classifier.md +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/hooks/hooks.json +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/hooks/scripts/load-tl-skill.mjs +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/hooks/scripts/post-usage.sh +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/hooks/scripts/pre-check.sh +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl/references/business-glossary.md +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl/references/elasticsearch-schema.md +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl/references/firebolt-schema.md +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl/references/postgres-schema.md +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-channel-authenticity/.gitignore +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-channel-authenticity/SKILL.md +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-channel-authenticity/references/comment-patterns.md +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-channel-authenticity/references/peer-cohort.md +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-channel-authenticity/references/red-flags.md +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-channel-authenticity/references/scoring.md +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-channel-authenticity/scripts/_io_utf8.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-channel-authenticity/scripts/analyze_channel.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-channel-authenticity/scripts/anomaly_detector.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-channel-authenticity/scripts/comment_analyzer.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-channel-authenticity/scripts/comment_scraper.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-channel-authenticity/scripts/engagement_ratios.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-channel-authenticity/scripts/peer_cohort.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-channel-authenticity/scripts/report.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-channel-authenticity/scripts/resolve_channel.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-channel-authenticity/scripts/score.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-channel-authenticity/scripts/tl_cli.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-channel-authenticity/scripts/video_integrity.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-channel-authenticity/scripts/view_curves.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-import/SKILL.md +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-keyword-research/SKILL.md +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-keyword-research/scripts/probe.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-report-builder/SKILL.md +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-report-builder/examples/e2e_findings.md +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-report-builder/examples/golden_queries.md +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-report-builder/references/columns_brands.md +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-report-builder/references/columns_channels.md +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-report-builder/references/columns_content.md +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-report-builder/references/columns_sponsorships.md +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-report-builder/references/intelligence_filterset_schema.json +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-report-builder/references/intelligence_widget_schema.json +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-report-builder/references/report_glossary.md +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-report-builder/references/sortable_columns.json +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-report-builder/references/sponsorship_filterset_schema.json +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-report-builder/references/sponsorship_widget_schema.json +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-report-builder/references/widgets.md +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-report-builder/tools/column_builder.md +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-report-builder/tools/database_query.md +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-report-builder/tools/name_resolver.md +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-report-builder/tools/sample_judge.md +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-report-builder/tools/similar_channels.md +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-report-builder/tools/topic_matcher.md +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-report-builder/tools/widget_builder.md +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-save-report/SKILL.md +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-save-report/references/columns_brands.md +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-save-report/references/columns_channels.md +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-save-report/references/columns_content.md +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-save-report/references/columns_sponsorships.md +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-save-report/references/intelligence_filterset_schema.json +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-save-report/references/intelligence_widget_schema.json +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-save-report/references/report_glossary.md +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-save-report/references/sortable_columns.json +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-save-report/references/sponsorship_filterset_schema.json +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-save-report/references/sponsorship_widget_schema.json +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-save-report/references/widgets.md +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-top-partnerships/SKILL.md +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-top-partnerships/scripts/top_partnerships.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-views-guarantee/SKILL.md +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-views-guarantee/scripts/vg.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/src/tl_cli/_completions.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/src/tl_cli/_typer_utils.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/src/tl_cli/auth/__init__.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/src/tl_cli/auth/commands.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/src/tl_cli/auth/login.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/src/tl_cli/auth/pkce.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/src/tl_cli/auth/token_store.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/src/tl_cli/client/__init__.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/src/tl_cli/client/errors.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/src/tl_cli/client/http.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/src/tl_cli/commands/__init__.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/src/tl_cli/commands/_comments_common.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/src/tl_cli/commands/balance.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/src/tl_cli/commands/brands.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/src/tl_cli/commands/bulk_import.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/src/tl_cli/commands/changelog.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/src/tl_cli/commands/channels.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/src/tl_cli/commands/credits.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/src/tl_cli/commands/db.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/src/tl_cli/commands/deals.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/src/tl_cli/commands/doctor.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/src/tl_cli/commands/matches.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/src/tl_cli/commands/proposals.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/src/tl_cli/commands/recommender.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/src/tl_cli/commands/reports.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/src/tl_cli/commands/schema.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/src/tl_cli/commands/setup.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/src/tl_cli/commands/snapshots.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/src/tl_cli/commands/sponsorships.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/src/tl_cli/commands/uploads.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/src/tl_cli/commands/whoami.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/src/tl_cli/config.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/src/tl_cli/filters.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/src/tl_cli/hints.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/src/tl_cli/main.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/src/tl_cli/output/__init__.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/src/tl_cli/self_update.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/tests/__init__.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/tests/test_auth.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/tests/test_filters.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/tests/test_http_auth.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/tests/test_reports.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/tests/test_setup.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/tests/test_sponsorships.py +0 -0
- {thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/uv.lock +0 -0
|
@@ -11,8 +11,8 @@ jobs:
|
|
|
11
11
|
release-build:
|
|
12
12
|
runs-on: ubuntu-latest
|
|
13
13
|
steps:
|
|
14
|
-
- uses: actions/checkout@
|
|
15
|
-
- uses: actions/setup-python@
|
|
14
|
+
- uses: actions/checkout@v6
|
|
15
|
+
- uses: actions/setup-python@v6
|
|
16
16
|
with:
|
|
17
17
|
python-version: "3.12"
|
|
18
18
|
|
|
@@ -30,7 +30,7 @@ jobs:
|
|
|
30
30
|
python -m pip install --upgrade build
|
|
31
31
|
python -m build
|
|
32
32
|
|
|
33
|
-
- uses: actions/upload-artifact@
|
|
33
|
+
- uses: actions/upload-artifact@v7
|
|
34
34
|
with:
|
|
35
35
|
name: release-dists
|
|
36
36
|
path: dist/
|
|
@@ -44,7 +44,7 @@ jobs:
|
|
|
44
44
|
name: pypi
|
|
45
45
|
url: https://pypi.org/project/thoughtleaders-cli/${{ github.event.release.name }}
|
|
46
46
|
steps:
|
|
47
|
-
- uses: actions/download-artifact@
|
|
47
|
+
- uses: actions/download-artifact@v8
|
|
48
48
|
with:
|
|
49
49
|
name: release-dists
|
|
50
50
|
path: dist/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: thoughtleaders-cli
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.9
|
|
4
4
|
Summary: ThoughtLeaders CLI — query sponsorship data, channels, brands, and intelligence
|
|
5
5
|
Project-URL: Homepage, https://thoughtleaders.io
|
|
6
6
|
Project-URL: Repository, https://github.com/ThoughtLeaders-io/thoughtleaders-cli
|
|
@@ -211,9 +211,9 @@ tl describe show sponsorships --filters # Available filters for sponsorships
|
|
|
211
211
|
tl balance # Your credit balance
|
|
212
212
|
```
|
|
213
213
|
|
|
214
|
-
`tl db pg` is priced **per-
|
|
214
|
+
`tl db pg` is priced **per-row**: the per-row rate is the **sum of the rates of the tables the query touches** (default 1.0/row; some tables are cheaper or dearer), plus a flat per-row charge for every expensive column read (demographics, channel outreach emails), all times the rows returned. Aggregate queries (`count`/`GROUP BY`) add a surcharge proportional to the rows they aggregate. Run `tl describe show db --json` to see the live `pg_pricing` map, and check `usage.credit_rate` in the response envelope after a query to see what your query was actually charged.
|
|
215
215
|
|
|
216
|
-
To preview a query's cost **before** running it, add `--pricing`: `tl db pg "SELECT … LIMIT 100" --pricing` runs only the planner's `EXPLAIN`, prints the cost breakdown and an upper-bound estimate (at the query's `LIMIT`), and costs a flat **1 credit** — the query itself never executes. Works with `--json` too. `--pricing` is also available on `tl db fb` and `tl db es`; those backends
|
|
216
|
+
To preview a query's cost **before** running it, add `--pricing`: `tl db pg "SELECT … LIMIT 100" --pricing` runs only the planner's `EXPLAIN`, prints the cost breakdown and an upper-bound estimate (at the query's `LIMIT`), and costs a flat **1 credit** — the query itself never executes. Works with `--json` too. `--pricing` is also available on `tl db fb` and `tl db es`; those backends have no per-table or per-column charges, so the estimate is the flat per-row rate at the query's row ceiling (`LIMIT` for Firebolt, `size` — or the aggregation doc cap — for Elasticsearch).
|
|
217
217
|
|
|
218
218
|
# Terminology
|
|
219
219
|
|
|
@@ -183,9 +183,9 @@ tl describe show sponsorships --filters # Available filters for sponsorships
|
|
|
183
183
|
tl balance # Your credit balance
|
|
184
184
|
```
|
|
185
185
|
|
|
186
|
-
`tl db pg` is priced **per-
|
|
186
|
+
`tl db pg` is priced **per-row**: the per-row rate is the **sum of the rates of the tables the query touches** (default 1.0/row; some tables are cheaper or dearer), plus a flat per-row charge for every expensive column read (demographics, channel outreach emails), all times the rows returned. Aggregate queries (`count`/`GROUP BY`) add a surcharge proportional to the rows they aggregate. Run `tl describe show db --json` to see the live `pg_pricing` map, and check `usage.credit_rate` in the response envelope after a query to see what your query was actually charged.
|
|
187
187
|
|
|
188
|
-
To preview a query's cost **before** running it, add `--pricing`: `tl db pg "SELECT … LIMIT 100" --pricing` runs only the planner's `EXPLAIN`, prints the cost breakdown and an upper-bound estimate (at the query's `LIMIT`), and costs a flat **1 credit** — the query itself never executes. Works with `--json` too. `--pricing` is also available on `tl db fb` and `tl db es`; those backends
|
|
188
|
+
To preview a query's cost **before** running it, add `--pricing`: `tl db pg "SELECT … LIMIT 100" --pricing` runs only the planner's `EXPLAIN`, prints the cost breakdown and an upper-bound estimate (at the query's `LIMIT`), and costs a flat **1 credit** — the query itself never executes. Works with `--json` too. `--pricing` is also available on `tl db fb` and `tl db es`; those backends have no per-table or per-column charges, so the estimate is the flat per-row rate at the query's row ceiling (`LIMIT` for Firebolt, `size` — or the aggregation doc cap — for Elasticsearch).
|
|
189
189
|
|
|
190
190
|
# Terminology
|
|
191
191
|
|
|
@@ -415,9 +415,9 @@ tl db pg "SELECT b.name, COUNT(*) AS deals
|
|
|
415
415
|
|
|
416
416
|
If unsure about what information to find where, read the [references/postgresql-schema.md](references/postgresql-schema.md) file for instructions. Use just `tl pg schema` to see the entire SQL schema.
|
|
417
417
|
|
|
418
|
-
**PG cost is per-
|
|
418
|
+
**PG cost is per-row.** The credit cost for a `tl db pg` call is its per-row rate — the **sum of the rates of the tables the query touches** (default 1.0/row; some tables are cheaper or dearer) plus a **flat per-row charge** for every expensive column read — times the rows returned. So a join pays for each table it reads, and an expensive column costs its configured value for every row returned. Aggregate queries (`count`/`GROUP BY`) add a surcharge proportional to the estimated rows aggregated. Sensitive columns (e.g. demographics, channel outreach emails) cost more per row. Run `tl describe show db --json` to see the live `pg_pricing` map, and check `usage.credit_rate` / `usage.pricing` in the response envelope after a query to see what your query was actually charged.
|
|
419
419
|
|
|
420
|
-
**Preview cost before running.** Add `--pricing` to estimate a query's cost without executing it: `tl db pg "SELECT … LIMIT 100" --pricing` runs only `EXPLAIN`, prints the
|
|
420
|
+
**Preview cost before running.** Add `--pricing` to estimate a query's cost without executing it: `tl db pg "SELECT … LIMIT 100" --pricing` runs only `EXPLAIN`, prints the per-row rate + per-row breakdown and an upper-bound cost (at the query's LIMIT), and costs a flat 1 credit. Use this before large or expensive-column queries. Works with `--json`. `--pricing` also works on `tl db fb` and `tl db es` — those backends have no per-table or per-column charges, so the estimate is just the flat per-row rate at the row ceiling (`LIMIT` for Firebolt; `size`, or the aggregation doc cap, for Elasticsearch).
|
|
421
421
|
|
|
422
422
|
### Three sources, each authoritative for different things
|
|
423
423
|
|
|
@@ -124,11 +124,11 @@ def _summarise_modes(credits: dict) -> tuple[str, str, bool]:
|
|
|
124
124
|
- 'free' → "free"
|
|
125
125
|
- 'flat' → "<rate> per call"
|
|
126
126
|
- 'linear-per-result' (one mode) → "<rate> × n (per result)"
|
|
127
|
-
- '
|
|
127
|
+
- 'per-row' (one mode, rate=R) → "R/row"
|
|
128
128
|
- mixed (e.g. channels has detail / history / similar at different rates)
|
|
129
129
|
→ per-mode "<mode> R" joined with commas
|
|
130
130
|
|
|
131
|
-
The typical-cost column uses the n=100 example for
|
|
131
|
+
The typical-cost column uses the n=100 example for per-row/per-result and
|
|
132
132
|
the flat rate for flat. Free shows '-'.
|
|
133
133
|
"""
|
|
134
134
|
modes = _modes_block(credits)
|
|
@@ -163,8 +163,8 @@ def _format_single_mode_label(mode_name: str, payload: dict, *, terse: bool = Fa
|
|
|
163
163
|
return f"{_fmt_credits(rate)}/call" if terse else f"{_fmt_credits(rate)} per call"
|
|
164
164
|
if model == "linear-per-result":
|
|
165
165
|
return f"{_fmt_credits(rate)}×n" if terse else f"{_fmt_credits(rate)} × n (per result)"
|
|
166
|
-
if model == "
|
|
167
|
-
return f"
|
|
166
|
+
if model == "per-row":
|
|
167
|
+
return f"{_fmt_credits(rate)}/row"
|
|
168
168
|
return f"{model} ({_fmt_credits(rate)})"
|
|
169
169
|
|
|
170
170
|
|
|
@@ -256,25 +256,25 @@ def _print_pricing_section(credits: dict) -> None:
|
|
|
256
256
|
"Estimate using the examples above before running with a large limit."
|
|
257
257
|
)
|
|
258
258
|
|
|
259
|
-
# Surface live PG
|
|
260
|
-
# (db resource only).
|
|
261
|
-
|
|
259
|
+
# Surface live PG per-table / per-column pricing when the server included
|
|
260
|
+
# it (db resource only).
|
|
261
|
+
_print_pg_pricing_section(credits.get("pg_pricing"))
|
|
262
262
|
|
|
263
263
|
|
|
264
|
-
def
|
|
265
|
-
"""Render the `credits.
|
|
264
|
+
def _print_pg_pricing_section(pricing: object) -> None:
|
|
265
|
+
"""Render the `credits.pg_pricing` block as a flat dotted-path table.
|
|
266
266
|
|
|
267
267
|
The server emits a three-level nested structure
|
|
268
268
|
``{base: {pg: float}, tables: {name: float}, columns: {"t.c": float}}``;
|
|
269
|
-
flattening each leaf to ``<section>.<key>`` keeps the live
|
|
270
|
-
|
|
271
|
-
from the per-table and per-column extras a query
|
|
272
|
-
incur.
|
|
269
|
+
flattening each leaf to ``<section>.<key>`` keeps the live rates visible
|
|
270
|
+
in one sorted scan, with the default per-row rate (``default.pg``)
|
|
271
|
+
distinguished from the per-table rates and per-column extras a query
|
|
272
|
+
may or may not incur.
|
|
273
273
|
"""
|
|
274
|
-
if not isinstance(
|
|
274
|
+
if not isinstance(pricing, dict) or not pricing:
|
|
275
275
|
return
|
|
276
276
|
rows: list[tuple[str, float]] = []
|
|
277
|
-
for section, body in
|
|
277
|
+
for section, body in pricing.items():
|
|
278
278
|
if not isinstance(body, dict):
|
|
279
279
|
# Forward-compat: an unexpected leaf type — surface as-is
|
|
280
280
|
# under the section name rather than dropping it silently.
|
|
@@ -284,15 +284,16 @@ def _print_pg_expensive_section(expensive: object) -> None:
|
|
|
284
284
|
rows.append((f"{section}.{key}", val))
|
|
285
285
|
if not rows:
|
|
286
286
|
return
|
|
287
|
-
sub = Table(title="PG
|
|
287
|
+
sub = Table(title="PG per-row pricing (live)")
|
|
288
288
|
sub.add_column("Path", style="bold")
|
|
289
|
-
sub.add_column("
|
|
289
|
+
sub.add_column("Rate", justify="right")
|
|
290
290
|
for path, val in sorted(rows):
|
|
291
291
|
sub.add_row(path, _fmt_credits(val))
|
|
292
292
|
console.print(sub)
|
|
293
293
|
console.print(
|
|
294
|
-
"[dim]These are the rates, not a per-query total.
|
|
295
|
-
"of
|
|
294
|
+
"[dim]These are the per-table / per-column rates, not a per-query total. "
|
|
295
|
+
"A query's per-row rate is the sum of the rates of the tables it touches. "
|
|
296
|
+
"For the actual cost of a specific query (before running it), use[/dim] "
|
|
296
297
|
"[cyan]tl db pg \"SELECT ...\" --pricing[/cyan][dim].[/dim]"
|
|
297
298
|
)
|
|
298
299
|
|
|
@@ -540,7 +540,7 @@ def output_pricing_estimate(data: dict, fmt: str) -> None:
|
|
|
540
540
|
|
|
541
541
|
Firebolt and Elasticsearch have no per-column extras — their estimate
|
|
542
542
|
carries `per_row_extra=0` and empty expensive-item maps, so the
|
|
543
|
-
breakdown table is skipped and only the
|
|
543
|
+
breakdown table is skipped and only the per-row cost shows.
|
|
544
544
|
A `limit`/cost of `None` (e.g. a Firebolt query with no `LIMIT`) means
|
|
545
545
|
the row count is unbounded and the cost can't be pinned ahead of time.
|
|
546
546
|
"""
|
|
@@ -567,24 +567,30 @@ def output_pricing_estimate(data: dict, fmt: str) -> None:
|
|
|
567
567
|
else:
|
|
568
568
|
console.print(
|
|
569
569
|
" Estimated cost: [yellow]depends on rows returned[/yellow] "
|
|
570
|
-
"(no row limit set — cost
|
|
570
|
+
"(no row limit set — cost is linear in rows returned)"
|
|
571
571
|
)
|
|
572
|
-
console.print(f"
|
|
572
|
+
console.print(f" Per-row rate (sum of table rates): {multiplier}")
|
|
573
573
|
console.print(f" Per-row extra (expensive columns): {per_row}")
|
|
574
|
+
agg_surcharge = est.get("agg_surcharge")
|
|
575
|
+
if agg_surcharge:
|
|
576
|
+
console.print(
|
|
577
|
+
f" Aggregate surcharge (flat): {agg_surcharge} "
|
|
578
|
+
f"[dim](≈{est.get('aggregated_rows', 0):,} rows aggregated)[/dim]"
|
|
579
|
+
)
|
|
574
580
|
if planner_rows is not None:
|
|
575
581
|
console.print(
|
|
576
582
|
f" [dim]Planner row estimate (pre-LIMIT): {planner_rows:,}[/dim]"
|
|
577
583
|
)
|
|
578
584
|
|
|
579
|
-
tables = est.get("
|
|
585
|
+
tables = est.get("table_rates") or {}
|
|
580
586
|
columns = est.get("expensive_columns") or {}
|
|
581
587
|
if tables or columns:
|
|
582
|
-
sub = Table(title="
|
|
588
|
+
sub = Table(title="Per-row rates this query touches")
|
|
583
589
|
sub.add_column("Item", style="bold")
|
|
584
590
|
sub.add_column("Kind")
|
|
585
|
-
sub.add_column("
|
|
591
|
+
sub.add_column("Rate", justify="right")
|
|
586
592
|
for name, val in sorted(tables.items()):
|
|
587
|
-
sub.add_row(name, "table (
|
|
593
|
+
sub.add_row(name, "table (per row)", f"{_fmt_credits(val)}/row")
|
|
588
594
|
for path, val in sorted(columns.items()):
|
|
589
595
|
sub.add_row(path, "column (per row)", f"{_fmt_credits(val)}/row")
|
|
590
596
|
console.print(sub)
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
"""Tests for `tl describe` output helpers."""
|
|
2
2
|
|
|
3
|
-
from tl_cli.commands.describe import
|
|
3
|
+
from tl_cli.commands.describe import _print_pg_pricing_section
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
# Sample shape that the server actually emits under `credits.
|
|
7
|
-
# three sections (
|
|
8
|
-
|
|
9
|
-
"
|
|
10
|
-
"tables": {"thoughtleaders_channel": 3.0},
|
|
6
|
+
# Sample shape that the server actually emits under `credits.pg_pricing`:
|
|
7
|
+
# three sections (default / tables / columns), each a flat dict of leaves.
|
|
8
|
+
_LIVE_PG_PRICING = {
|
|
9
|
+
"default": {"pg": 1.0},
|
|
10
|
+
"tables": {"thoughtleaders_adlink": 0.1, "thoughtleaders_channel": 3.0},
|
|
11
11
|
"columns": {
|
|
12
12
|
"thoughtleaders_channel.outreach_email": 80.0,
|
|
13
13
|
"thoughtleaders_channel.demographic_male_share": 50.0,
|
|
@@ -18,21 +18,22 @@ _LIVE_PG_EXPENSIVE = {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
|
|
21
|
-
class
|
|
21
|
+
class TestPrintPgPricingSection:
|
|
22
22
|
def test_missing_block_renders_nothing(self, capsys):
|
|
23
|
-
|
|
23
|
+
_print_pg_pricing_section(None)
|
|
24
24
|
assert capsys.readouterr().out == ""
|
|
25
25
|
|
|
26
26
|
def test_empty_dict_renders_nothing(self, capsys):
|
|
27
|
-
|
|
27
|
+
_print_pg_pricing_section({})
|
|
28
28
|
assert capsys.readouterr().out == ""
|
|
29
29
|
|
|
30
30
|
def test_nested_block_flattens_to_dotted_paths(self, capsys):
|
|
31
|
-
|
|
31
|
+
_print_pg_pricing_section(_LIVE_PG_PRICING)
|
|
32
32
|
out = capsys.readouterr().out
|
|
33
33
|
# Section headers prefix every leaf key.
|
|
34
|
-
assert "
|
|
34
|
+
assert "default.pg" in out
|
|
35
35
|
assert "tables.thoughtleaders_channel" in out
|
|
36
|
+
assert "tables.thoughtleaders_adlink" in out
|
|
36
37
|
assert "columns.thoughtleaders_channel.outreach_email" in out
|
|
37
38
|
assert "columns.thoughtleaders_channel.demographic_male_share" in out
|
|
38
39
|
# The numeric values pass through `_fmt_credits` (integer-valued
|
|
@@ -41,10 +42,10 @@ class TestPrintPgExpensiveSection:
|
|
|
41
42
|
assert "50" in out
|
|
42
43
|
assert "3" in out
|
|
43
44
|
# Title appears.
|
|
44
|
-
assert "PG
|
|
45
|
+
assert "PG per-row pricing (live)" in out
|
|
45
46
|
|
|
46
47
|
def test_directs_user_to_pricing_flag(self, capsys):
|
|
47
|
-
|
|
48
|
+
_print_pg_pricing_section(_LIVE_PG_PRICING)
|
|
48
49
|
out = capsys.readouterr().out
|
|
49
50
|
# The note points at the per-query estimator and clarifies these
|
|
50
51
|
# are rates, not a total.
|
|
@@ -53,18 +54,18 @@ class TestPrintPgExpensiveSection:
|
|
|
53
54
|
|
|
54
55
|
def test_sections_sort_into_dotted_order(self, capsys):
|
|
55
56
|
"""Entries sort lexicographically by their flattened dotted path,
|
|
56
|
-
so `
|
|
57
|
-
|
|
57
|
+
so `columns.*` rows precede `default.*`, which precede `tables.*`."""
|
|
58
|
+
_print_pg_pricing_section(_LIVE_PG_PRICING)
|
|
58
59
|
out = capsys.readouterr().out
|
|
59
|
-
base_pos = out.index("base.pg")
|
|
60
60
|
cols_pos = out.index("columns.thoughtleaders_channel.outreach_email")
|
|
61
|
+
default_pos = out.index("default.pg")
|
|
61
62
|
tables_pos = out.index("tables.thoughtleaders_channel")
|
|
62
|
-
assert
|
|
63
|
+
assert cols_pos < default_pos < tables_pos
|
|
63
64
|
|
|
64
65
|
def test_unexpected_leaf_type_surfaces_under_section_name(self, capsys):
|
|
65
66
|
"""Forward-compat: if a future server adds a non-dict section
|
|
66
67
|
we render it as-is rather than dropping it silently."""
|
|
67
|
-
|
|
68
|
+
_print_pg_pricing_section({"scalar_section": 42, "tables": {"t": 1.0}})
|
|
68
69
|
out = capsys.readouterr().out
|
|
69
70
|
assert "scalar_section" in out
|
|
70
71
|
assert "tables.t" in out
|
|
@@ -377,10 +377,10 @@ class TestPgPricingEstimate:
|
|
|
377
377
|
|
|
378
378
|
_SAMPLE = {
|
|
379
379
|
"pricing_estimate": {
|
|
380
|
-
"
|
|
380
|
+
"default": 1.4,
|
|
381
381
|
"multiplier": 4.4,
|
|
382
382
|
"per_row_extra": 280.0,
|
|
383
|
-
"
|
|
383
|
+
"table_rates": {"thoughtleaders_channel": 3.0},
|
|
384
384
|
"expensive_columns": {
|
|
385
385
|
"thoughtleaders_channel.outreach_email": 80.0,
|
|
386
386
|
"thoughtleaders_channel.demographic_male_share": 50.0,
|
|
@@ -404,7 +404,7 @@ class TestPgPricingEstimate:
|
|
|
404
404
|
assert "280" in out # per-row extra
|
|
405
405
|
assert "thoughtleaders_channel.outreach_email" in out
|
|
406
406
|
assert "80/row" in out
|
|
407
|
-
assert "table (
|
|
407
|
+
assert "table (per row)" in out
|
|
408
408
|
|
|
409
409
|
def test_json_mode_dumps_full_envelope(self, capsys):
|
|
410
410
|
import json
|
|
@@ -430,8 +430,8 @@ class TestPgPricingEstimate:
|
|
|
430
430
|
from tl_cli.output.formatter import output_pricing_estimate
|
|
431
431
|
data = {
|
|
432
432
|
"pricing_estimate": {
|
|
433
|
-
"
|
|
434
|
-
"
|
|
433
|
+
"default": 1.4, "multiplier": 1.4, "per_row_extra": 0.0,
|
|
434
|
+
"table_rates": {}, "expensive_columns": {},
|
|
435
435
|
"limit": 10, "planner_estimated_rows": 3,
|
|
436
436
|
"estimated_cost_at_limit": 3.8,
|
|
437
437
|
},
|
|
@@ -447,8 +447,8 @@ class TestPgPricingEstimate:
|
|
|
447
447
|
from tl_cli.output.formatter import output_pricing_estimate
|
|
448
448
|
data = {
|
|
449
449
|
"pricing_estimate": {
|
|
450
|
-
"
|
|
451
|
-
"
|
|
450
|
+
"default": 1.4, "multiplier": 1.4, "per_row_extra": 0.0,
|
|
451
|
+
"table_rates": {}, "expensive_columns": {},
|
|
452
452
|
"limit": None, "planner_estimated_rows": None,
|
|
453
453
|
"estimated_cost_at_limit": None,
|
|
454
454
|
},
|
|
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
|
{thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl/references/business-glossary.md
RENAMED
|
File without changes
|
{thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl/references/elasticsearch-schema.md
RENAMED
|
File without changes
|
{thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl/references/firebolt-schema.md
RENAMED
|
File without changes
|
{thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl/references/postgres-schema.md
RENAMED
|
File without changes
|
{thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-channel-authenticity/.gitignore
RENAMED
|
File without changes
|
{thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-channel-authenticity/SKILL.md
RENAMED
|
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
|
{thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-keyword-research/scripts/probe.py
RENAMED
|
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
|
{thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-report-builder/references/widgets.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-report-builder/tools/sample_judge.md
RENAMED
|
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
|
{thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-save-report/references/widgets.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/skills/tl-views-guarantee/scripts/vg.py
RENAMED
|
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
|
{thoughtleaders_cli-0.7.7 → thoughtleaders_cli-0.7.9}/src/tl_cli/commands/_comments_common.py
RENAMED
|
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
|