thoughtleaders-cli 0.7.7__py3-none-any.whl → 0.7.9__py3-none-any.whl

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: thoughtleaders-cli
3
- Version: 0.7.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-query**: a base rate plus a multiplier extra for every expensive table referenced, plus a flat per-row charge for every expensive column read. Sensitive fields (demographics, channel outreach emails) are expensive. Run `tl describe show db --json` to see the live `pg_expensive` map, and check `usage.credit_rate` in the response envelope after a query to see what your query was actually charged.
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 are flat-rate (no per-column charges), so the estimate is the volume curve at the query's row ceiling (`LIMIT` for Firebolt, `size` — or the aggregation doc cap — for Elasticsearch).
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
 
@@ -1,4 +1,4 @@
1
- tl_cli/__init__.py,sha256=h3L6t4qZYlTZLCm42Wl8W0xKRHEl0qObP17gk7Gu6co,112
1
+ tl_cli/__init__.py,sha256=RyYKVEHqW2aW2bUZIlXiUBzgHq32W88EpLiVGP_wdoY,112
2
2
  tl_cli/_completions.py,sha256=kOyEUqC26vbYvyXWi513WX8fF73qQLR5WWuRSe_wqyk,164
3
3
  tl_cli/_typer_utils.py,sha256=ZiZsCVmEznPvBw-dYbr3tu3zWZ0iN6kjoQmK3gMqD28,860
4
4
  tl_cli/config.py,sha256=UV_OYTXuQnAIqbi_oVCXx0hhIdZWR678RRapVv51UwQ,1859
@@ -24,7 +24,7 @@ tl_cli/commands/channels.py,sha256=ALw2fgJL3w0dpYp3A41OECL0odenhSV16vFA7la7aQI,1
24
24
  tl_cli/commands/credits.py,sha256=2xCht2e420LmaFBKNdKoMz8GlTh31qSWSlJAnVzoZic,7308
25
25
  tl_cli/commands/db.py,sha256=rdIQrxT7sdrPEnBbByNHvPr2X6iIg-wb19X9bWYwDRc,5053
26
26
  tl_cli/commands/deals.py,sha256=ZK9yneInsC6DXoCPS65oyLoVR0eRW1xdRlEN7oRp1pc,2174
27
- tl_cli/commands/describe.py,sha256=Ox2B1hoVuJ6pJc_x5BJjAhEJhlae-el9zwoJKutw3X8,12232
27
+ tl_cli/commands/describe.py,sha256=3lURv4NllM5qPeMEBbejrIxiMzsyTwpJIz211-wuGCU,12362
28
28
  tl_cli/commands/doctor.py,sha256=KUKglwhMc7B26XXy_3M0LkHu7wqfFO5T0YPHO1SH1VY,9024
29
29
  tl_cli/commands/matches.py,sha256=K5o6B8FLECp7825dU4W3X8n-wuXvGJz57xpQPXeXQ-0,2886
30
30
  tl_cli/commands/proposals.py,sha256=khsjorluIfgrJ22DiwzIAFcYD4JbirjkOBz1KuQ0Sdk,2918
@@ -37,16 +37,16 @@ tl_cli/commands/sponsorships.py,sha256=MWjyaReMMhmVKAbrCBCVw_J6dzkML_TIo_2kyPOYQ
37
37
  tl_cli/commands/uploads.py,sha256=Tf9tqAEm9FGe3A7sr_EDX9OzdNInCmrWNr10wWGuMUo,1526
38
38
  tl_cli/commands/whoami.py,sha256=aUXwBRwh1vAGrvz8CKGfHYtEOKJCIDfwrGesKAwYZMk,7866
39
39
  tl_cli/output/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
40
- tl_cli/output/formatter.py,sha256=pqlKmb2nZ1Z2e1A9m8l5mgVemJinVAP4in1tUzFWHno,22522
40
+ tl_cli/output/formatter.py,sha256=zWwcg4yovMXLaduxu8skpDjPVLTaGZAmwtYwjpZDg1w,22766
41
41
  tl_cli/_plugin/.claude-plugin/marketplace.json,sha256=l56PMmyjfGXNGlV30wRyOAe74B6gJNCVNCxgsBbSNxc,446
42
- tl_cli/_plugin/.claude-plugin/plugin.json,sha256=8iSJKEv5_GctH0e5WgihxC_sRAUF3ggyt3PgPy30mks,466
42
+ tl_cli/_plugin/.claude-plugin/plugin.json,sha256=_8EmcFkEoRJ9s3aXU-37EW7NHunlsZ4DpuQoNuEUe4M,466
43
43
  tl_cli/_plugin/agents/tl-analyst.md,sha256=6J3X3NANkWg6OOUCvNirkN4ulIk80KSumPncDUBt75E,6761
44
44
  tl_cli/_plugin/agents/youtube-comment-classifier.md,sha256=S5lr_htA98FIX0su8FJ2ntiHfbdK8OB2NQKC4lTnQcw,2178
45
45
  tl_cli/_plugin/hooks/hooks.json,sha256=FSWibw1xAjA-suFV3fR8btIb2kQ82LQ08otTr-NpmFw,835
46
46
  tl_cli/_plugin/hooks/scripts/load-tl-skill.mjs,sha256=EBsyZ-caei-CBJsRtqzJXJs_20O3H22MuVmDpu96umo,805
47
47
  tl_cli/_plugin/hooks/scripts/post-usage.sh,sha256=WVvZLkZik6lbeZ20Kh-wgm4JkRFHFN0Uwl4C8S3Y0sY,759
48
48
  tl_cli/_plugin/hooks/scripts/pre-check.sh,sha256=E9KeuXy6yeHEBOnOFW4hDW-Et-Dbp1Oh--3WXKfOX78,898
49
- tl_cli/_plugin/skills/tl/SKILL.md,sha256=eYxfXFqRuLBh38mxgz74VR08keEVM2T9HCySHfv3W9E,61447
49
+ tl_cli/_plugin/skills/tl/SKILL.md,sha256=B9UMrGJduF3nOxESSicKYM5K5G91NcCbmYQjQh6faVk,61661
50
50
  tl_cli/_plugin/skills/tl/references/business-glossary.md,sha256=FCS-qBOGpdJCmHdglRGRjAuTQAtzpxJNpMkEWThuvlI,17779
51
51
  tl_cli/_plugin/skills/tl/references/elasticsearch-schema.md,sha256=OpHvixZ8UcZYJd8GdwgumryFyKwPAxj3AvPkl1QreMY,9316
52
52
  tl_cli/_plugin/skills/tl/references/firebolt-schema.md,sha256=KagpSWWEWIRfsAWz271PvAqVbSPvWLoogWhCA_XFSZw,10642
@@ -110,8 +110,8 @@ tl_cli/_plugin/skills/tl-top-partnerships/SKILL.md,sha256=hvH05hIaGlc0RfTE0GLBtD
110
110
  tl_cli/_plugin/skills/tl-top-partnerships/scripts/top_partnerships.py,sha256=_13W6-HuD_jtl7AWQQcZQ0SQO9qODMymlcL-1s4-VwU,13248
111
111
  tl_cli/_plugin/skills/tl-views-guarantee/SKILL.md,sha256=IH7q1WJDWri9TWJMiga1FMGJO_GKSbWwaDS6CVNZ9c0,9270
112
112
  tl_cli/_plugin/skills/tl-views-guarantee/scripts/vg.py,sha256=Qp5poinHEqh9374anq0bLtlxj2YL6ipBicaT960-Cws,15825
113
- thoughtleaders_cli-0.7.7.dist-info/METADATA,sha256=oVKTRCUXd5CRJp-aFkXFoiAFLBk0TU8W7Jdn4QRnoxg,19234
114
- thoughtleaders_cli-0.7.7.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
115
- thoughtleaders_cli-0.7.7.dist-info/entry_points.txt,sha256=umZp-1BkGkHDG0bNZXpTXrjwW0HGf9IDFN40eAWuuvg,39
116
- thoughtleaders_cli-0.7.7.dist-info/licenses/LICENSE,sha256=RUfdfLsn6jygiyrnnVUHt6r4IPwr2rbDm9Kixgtu8fo,1071
117
- thoughtleaders_cli-0.7.7.dist-info/RECORD,,
113
+ thoughtleaders_cli-0.7.9.dist-info/METADATA,sha256=HT32FXdT3lXEBRlUn12KmLqht_YEK6ZXfplL6qovbqM,19387
114
+ thoughtleaders_cli-0.7.9.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
115
+ thoughtleaders_cli-0.7.9.dist-info/entry_points.txt,sha256=umZp-1BkGkHDG0bNZXpTXrjwW0HGf9IDFN40eAWuuvg,39
116
+ thoughtleaders_cli-0.7.9.dist-info/licenses/LICENSE,sha256=RUfdfLsn6jygiyrnnVUHt6r4IPwr2rbDm9Kixgtu8fo,1071
117
+ thoughtleaders_cli-0.7.9.dist-info/RECORD,,
tl_cli/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  """ThoughtLeaders CLI — query sponsorship data, channels, brands, and intelligence."""
2
2
 
3
- __version__ = "0.7.7"
3
+ __version__ = "0.7.9"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tl-cli",
3
- "version": "0.7.7",
3
+ "version": "0.7.9",
4
4
  "description": "ThoughtLeaders CLI — query sponsorship deals, channels, brands, uploads, and intelligence from the terminal",
5
5
  "author": {
6
6
  "name": "ThoughtLeaders",
@@ -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-query.** The credit cost for a `tl db pg` call is a base rate plus a multiplier extra for every expensive table referenced, plus a **flat per-row charge** for every expensive column read (an expensive column costs its configured value for every row returned). Most tables and columns are not expensive; sensitive ones (e.g. demographics, channel outreach emails) cost more. Run `tl describe show db --json` to see the live `pg_expensive` map, and check `usage.credit_rate` / `usage.pricing` in the response envelope after a query to see what your query was actually charged.
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 multiplier + 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-column charges, so the estimate is just the volume curve at the row ceiling (`LIMIT` for Firebolt; `size`, or the aggregation doc cap, for Elasticsearch).
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
- - 'curve' (one mode, mult=R) → "curve (×R)"
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 curve/per-result and
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 == "curve":
167
- return f"curve ×{rate}"
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 expensive-items pricing when the server included it
260
- # (db resource only).
261
- _print_pg_expensive_section(credits.get("pg_expensive"))
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 _print_pg_expensive_section(expensive: object) -> None:
265
- """Render the `credits.pg_expensive` block as a flat dotted-path table.
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 extras
270
- visible in one sorted scan, with the base rate clearly distinguished
271
- from the per-table and per-column extras a query may or may not
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(expensive, dict) or not expensive:
274
+ if not isinstance(pricing, dict) or not pricing:
275
275
  return
276
276
  rows: list[tuple[str, float]] = []
277
- for section, body in expensive.items():
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 expensive items (live)")
287
+ sub = Table(title="PG per-row pricing (live)")
288
288
  sub.add_column("Path", style="bold")
289
- sub.add_column("Extra", justify="right")
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. For the actual cost "
295
- "of a specific query (before running it), use[/dim] "
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 volume-curve cost shows.
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 scales with the volume curve)"
570
+ "(no row limit set — cost is linear in rows returned)"
571
571
  )
572
- console.print(f" Multiplier (base + expensive tables): {multiplier}")
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("expensive_tables") or {}
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="Expensive items this query touches")
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("Extra", justify="right")
591
+ sub.add_column("Rate", justify="right")
586
592
  for name, val in sorted(tables.items()):
587
- sub.add_row(name, "table (multiplier)", _fmt_credits(val))
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)