Skip to content

feat(cli): make --debug output structured and useful (ENG-92248) - #536

Open
blainekasten wants to merge 8 commits into
mainfrom
cursor/cli-debug-output-8b9e
Open

feat(cli): make --debug output structured and useful (ENG-92248)#536
blainekasten wants to merge 8 commits into
mainfrom
cursor/cli-debug-output-8b9e

Conversation

@blainekasten

@blainekasten blainekasten commented Aug 20, 2026

Copy link
Copy Markdown
Collaborator

Fixes ENG-92248.

--debug used to dump Stainless/httpx DEBUG logs as plain text: full Headers(...) objects, request-option dumps (including secrets), analytics internals, and checksum-chunk spam. That’s hard to scan and mostly noise.

This replaces that with a CLI-owned stderr trace:

  • Session banner: CLI/Python versions, command, base URL, project, masked API key, timeout, retries
  • HTTP request/response pairs with color, duration, and request id
  • Stainless boilerplate, analytics internals, and checksum-chunk spam filtered out
  • Spinners/progress bars are skipped so they don’t overwrite debug lines
  • --debug is a documented Global Option; JSON mode stays on stdout

Example:

debug  tg 2.31.0  python 3.10.19  darwin
debug  tg fine-tuning list
debug  https://api.together.ai/v1/
debug  project=<unresolved>  key=…Tbd0  timeout=off  retries=0
debug → GET /whoami
debug ← 200 OK  320ms  a3047094cc5caccc-MSP

debug resolved project proj_xxxxxxxxxxxxxxxxx
debug Loading fine-tuning jobs...
debug → GET /fine-tunes
debug ← 200 OK  761ms  a30470962f96accc-MSP

Linear Issue: ENG-92248

Linear Issue: ENG-92248

Open in Web Open in Cursor 

@broly-code-security-scanner

Copy link
Copy Markdown

Broly Security Scan

Note

Clean scan
No vulnerabilities detected in this PR.

Note

Re-scan this PR anytime with /broly scan — useful after /broly undismiss, or to refresh findings without a new push.

Broly — SAST (zai-org/GLM-5.2) · Secrets · SCA · IaC · GH Actions · Base Images · Supply Chain Threats · Exploit Chains · Adversarial Verification

We're continuously improving Broly's accuracy and finding quality — your feedback is valuable. False positives, missed findings, bugs, and feature requests all welcome.

Ask in #security-engineering   Powered by Together AI

cursoragent and others added 2 commits August 24, 2026 12:04
Replace the spammy Stainless/httpx DEBUG dump with Rich-formatted
stderr traces: session context, redacted HTTP request/response
pairs, request ids, timing, and JSON bodies. Hide noise such as
full header objects, analytics internals, and x-stainless boilerplate.

Co-authored-by: Blaine Kasten <blainekasten@gmail.com>
@blainekasten
blainekasten force-pushed the cursor/cli-debug-output-8b9e branch from 16437b1 to 52ed9db Compare August 24, 2026 17:04
@blainekasten
blainekasten marked this pull request as ready for review August 24, 2026 18:47
@blainekasten
blainekasten requested a review from zainhas August 24, 2026 18:47
_print_lines already prefixes every line with `debug`, so the session
banner was rendering as `debug debug tg ...`.

Co-authored-by: Blaine Kasten <blainekasten@gmail.com>
@blainekasten

Copy link
Copy Markdown
Collaborator Author

@cursoragent Findings

  • src/together/lib/cli/utils/_debug.py:152render_session_lines embeds a debug prefix in each returned line and _print_lines (line 191) prepends it again, so the banner prints debug debug tg 2.31.0 … instead of the single-prefix form in the PR description; also _print_lines uses one space after debug while log_debug_note uses two, so notes and HTTP lines don't align. Reproduced.
  • src/together/lib/cli/utils/_debug.py:308teardown_cli_debug restores httpx level and propagate but not the together logger level (set to DEBUG at line 298) nor TOGETHER_LOG=debug (set at line 288), while it does clear the redirect flag; verified that after teardown _console_log_level() returns debug with the redirect off, so subsequent log_debug/log_info calls raw-dump the exact noise this PR removes. Affects any process running the launcher twice (the new test file does) or embedding main().
  • src/together/lib/cli/utils/_debug.py:258CliDebugLogHandler redacts only via _redact_secrets_in_error_text, which misses AWS SigV4 params and never strips query strings, so log.debug("Upload redirected to %s", url) (lib/resources/files.py:382/:423) writes a live presigned PUT URL to stderr under tg files upload --debug, inconsistent with _safe_url dropping queries on the trace lines.
  • src/together/lib/cli/utils/_debug.py:276install_http_debug_hooks is typed httpx.AsyncClient | httpx.Client, but the hooks are async def; a sync httpx.Client would call them without awaiting, silently producing no output plus a "coroutine never awaited" warning.

@cursor

cursor Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Addressed all four findings on #536:

  • Double debug prefix / spacing — already fixed; session lines no longer self-prefix, notes and HTTP lines share one space.
  • Teardown leak — restores TOGETHER_LOG and the together logger level so a second main()/log_debug call can’t raw-dump noise.
  • Presigned URLs — leftover SDK logs strip URL query strings (SigV4/S3) before redact.
  • Sync client typing — hooks are AsyncClient-only, matching the async def impl.

Waiting on CI.

Open in Web Open in Cursor 

Restore TOGETHER_LOG and the together logger level on teardown so a
second launcher invocation cannot raw-print debug noise. Strip URL
query strings from leftover SDK log lines (presigned S3/SigV4). Type
HTTP debug hooks as AsyncClient-only to match the async hook impl.

Co-authored-by: Blaine Kasten <blainekasten@gmail.com>
@blainekasten

Copy link
Copy Markdown
Collaborator Author

@cursoragent Findings:

  • src/together/lib/utils/_log.py:64 — The new _cli_debug_console_redirect guard was added to log_debug/log_info but not log_warn/log_warn_once, so every warning prints twice under --debug (raw logfmt line + formatted handler line). Reproduced.
  • src/together/lib/cli/utils/_debug.py:270CliDebugLogHandler.emit uses only record.getMessage() and drops record.exc_info, so --debug no longer shows tracebacks for _base_client's Encountered Exception / Encountered httpx.TimeoutException logs. This is a regression vs. the removed setup_logging() path. Reproduced.
  • src/together/lib/cli/__init__.py:307 — The session banner shows key=…0000 when no API key is set, because _create_client substitutes a 40-zero placeholder, making the <missing> branch in render_session_lines unreachable. Also, block_requests_for_api_key is registered before the debug hooks, so no → GET line is ever emitted in that case. Reproduced with env -u TOGETHER_API_KEY together whoami --debug.
  • src/together/lib/cli/components/upload_progress.py:118 (and download_progress.py:126) — Gating __enter__ on is_enabled() leaves _progress as None, which also suppresses the per-part/per-file ✓ … complete and ↷ … skipped prints, not just the Live spinner. tg beta models upload --debug gives no per-file feedback at all.
  • tests/cli/test_debug.py:101 — The "debug disabled" assertions lost their own def and are now appended to test_debug_error_keeps_status_without_response_body; they only run if the 401 assertions pass first.

@cursor

cursor Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Addressed all five review findings on #536:

  • Warn double-printlog_warn / log_warn_once now skip the raw stderr print() when the CLI debug handler is attached.
  • Dropped tracebacksCliDebugLogHandler prints sanitized record.exc_info (TimeoutException / Encountered Exception).
  • Missing API key — banner shows key=<missing> instead of …0000; debug HTTP hooks run before the block hook so → GET still prints.
  • Progress ✓/↷ lines — Live spinner still skipped under --debug, but per-file complete/skipped prints still fire.
  • Split testtest_debug_off_does_not_print_http_trace is its own test again.

Local pytest on the debug/progress/no-auth tests: 30 passed. Waiting on CI.

Open in Web Open in Cursor 

Stop doubling warnings under --debug, restore exception tracebacks,
show key=<missing> (and the request line) when no API key is set,
and keep per-file ✓/↷ progress lines when the Live spinner is skipped.

Co-authored-by: Blaine Kasten <blainekasten@gmail.com>
Comment thread src/together/lib/cli/__init__.py Fixed
Comment thread src/together/lib/cli/__init__.py Fixed
Construct the dummy key as "0" * 40 instead of a 40-char literal so
secret scanners do not treat it as a hardcoded API key.

Co-authored-by: Blaine Kasten <blainekasten@gmail.com>
@blainekasten

Copy link
Copy Markdown
Collaborator Author

@cursoragent Findings

  • src/together/lib/cli/utils/_debug.py:204 — All --debug output goes through a Rich Console, which falls back to 80 columns when stderr is not a TTY, hard-wrapping lines mid-token. Confirmed by running tg fine-tuning list --debug 2>debug.log: paths come out as "/…/httpx/_trans / ports/default.py". COLUMNS=200 fixes it, so soft_wrap=True (or a console with width=None, crop=False) is the fix. Worst on the CliDebugLogHandler traceback print at line 294.
  • src/together/lib/cli/__init__.py:189 — Reordering block_requests_for_api_key to be appended after track_request means the analytics hook now fires for requests that are immediately blocked for a missing API key: tg whoami with no TOGETHER_API_KEY now emits a cli_command_api_request event (and spawns a daemon POST thread that is never flushed) for a request that is never sent. Previously the block hook ran first and sys.exit(1) preempted it. Installing the block hook before track_request but after install_http_debug_hooks keeps the intended → GET debug line and the old telemetry behavior.

@cursor

cursor Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Fixed both on #536:

  • 80-col wrap — debug prints use soft_wrap=True, so redirected stderr no longer splits paths like _trans / ports/default.py.
  • Telemetry on blocked requests — hook order is now debug → missing-key block → track_request. → GET still prints; cli_command_api_request does not fire for a request that never goes out.

Tests for wrap + missing-key telemetry passed. Waiting on CI.

Open in Web Open in Cursor 

…equests

Rich was hard-wrapping stderr-to-file at 80 columns, splitting paths
mid-token. Print debug lines with soft_wrap. Keep the missing-key block
hook after HTTP debug hooks but before track_request so → GET still
prints without emitting cli_command_api_request for a request that
never leaves the process.

Co-authored-by: Blaine Kasten <blainekasten@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants