Skip to content

Commit 492a02f

Browse files
committed
Merge remote-tracking branch 'origin/main' into claude/installation-paths-ci-Uhfly
# Conflicts: # aai_cli/output.py
2 parents 5cd59bf + ab3c0b3 commit 492a02f

24 files changed

Lines changed: 607 additions & 62 deletions

aai_cli/commands/account.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -182,9 +182,9 @@ def render(d: dict[str, object]) -> object:
182182
shown_with_breakdown = [(item, _line_items_summary(item)) for item in shown]
183183
show_breakdown = any(summary for _, summary in shown_with_breakdown)
184184
table = (
185-
Table("period", "total", "breakdown", header_style="aai.heading")
185+
output.data_table("period", "total", "breakdown")
186186
if show_breakdown
187-
else Table("period", "total", header_style="aai.heading")
187+
else output.data_table("period", "total")
188188
)
189189
hidden_count = len(items) - len(shown)
190190
for item, breakdown in shown_with_breakdown:
@@ -230,7 +230,7 @@ def body(state: AppState, json_mode: bool) -> None:
230230
data = ams.get_rate_limits(account_id, jwt)
231231

232232
def render(d: dict[str, object]) -> Table:
233-
table = Table("service", "limit", header_style="aai.heading")
233+
table = output.data_table("service", "limit")
234234
for limit in jsonshape.mapping_list(d.get("rate_limits")):
235235
table.add_row(
236236
escape(str(limit.get("service", ""))),

aai_cli/commands/audit.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import typer
77
from rich.console import Group
88
from rich.markup import escape
9-
from rich.table import Table
109
from rich.text import Text
1110

1211
from aai_cli import help_panels, jsonshape, output, timeparse
@@ -141,7 +140,7 @@ def render(data: list[dict[str, object]]) -> object:
141140
)
142141
return Text(message, style="aai.muted")
143142

144-
table = Table("when (UTC)", "event", "resource", "actor", header_style="aai.heading")
143+
table = output.data_table("when (UTC)", "event", "resource", "actor")
145144
for entry in shown:
146145
table.add_row(
147146
escape(_format_time(entry.get("log_time"))),

aai_cli/commands/keys.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ def body(state: AppState, json_mode: bool) -> None:
6161
)
6262

6363
def render(data: list[dict[str, object]]) -> Table:
64-
table = Table("id", "name", "project", "key", "disabled", header_style="aai.heading")
64+
table = output.data_table("id", "name", "project", "key", "disabled")
6565
for row in data:
6666
table.add_row(
6767
str(row["id"]),

aai_cli/commands/login.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,9 +126,7 @@ def body(state: AppState, json_mode: bool) -> None:
126126
account_id = config.get_account_id(profile)
127127

128128
def render(_d: dict[str, object]) -> Table:
129-
table = Table.grid(padding=(0, 3))
130-
table.add_column(style="aai.muted")
131-
table.add_column()
129+
table = output.detail_table()
132130
table.add_row("Profile", escape(profile))
133131
table.add_row("Env", escape(env))
134132
table.add_row("API key", escape(masked))

aai_cli/commands/sessions.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,13 +56,12 @@ def body(state: AppState, json_mode: bool) -> None:
5656
rows = _session_rows(payload.get("data"))
5757

5858
def render(data: list[dict[str, object]]) -> Table:
59-
table = Table(
59+
table = output.data_table(
6060
"session id",
6161
"status",
6262
"created",
6363
"audio (s)",
6464
"model",
65-
header_style="aai.heading",
6665
)
6766
for s in data:
6867
status_str = str(s["status"])
@@ -99,10 +98,11 @@ def body(state: AppState, json_mode: bool) -> None:
9998
data = ams.get_streaming(session_id, jwt)
10099

101100
def render(d: dict[str, object]) -> Table:
102-
table = Table(show_header=False)
101+
table = output.detail_table()
103102
for field in _DETAIL_FIELDS:
104103
value = d.get(field)
105-
table.add_row(field, escape("" if value is None else str(value)))
104+
label = field.replace("_", " ")
105+
table.add_row(label, escape("" if value is None else str(value)))
106106
return table
107107

108108
output.emit(data, render, json_mode=json_mode)

aai_cli/commands/stream.py

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -66,20 +66,22 @@ def stream(
6666
speech_model: str = typer.Option(
6767
DEFAULT_SPEECH_MODEL, "--speech-model", help="Streaming speech model."
6868
),
69-
encoding: str | None = typer.Option(None, "--encoding", help="pcm_s16le or pcm_mulaw."),
69+
encoding: str | None = typer.Option(
70+
None, "--encoding", help="Audio encoding: pcm_s16le or pcm_mulaw."
71+
),
7072
language_detection: bool | None = typer.Option(
7173
None, "--language-detection", help="Auto-detect the spoken language."
7274
),
7375
domain: str | None = typer.Option(None, "--domain", help="Domain preset (e.g. medical)."),
7476
# turn detection
7577
end_of_turn_confidence_threshold: float | None = typer.Option(
76-
None, "--end-of-turn-confidence-threshold", help="0-1 end-of-turn confidence."
78+
None, "--end-of-turn-confidence-threshold", help="End-of-turn confidence (0-1)."
7779
),
7880
min_turn_silence: int | None = typer.Option(
79-
None, "--min-turn-silence", help="Min turn silence (ms)."
81+
None, "--min-turn-silence", help="Min silence to end a turn (ms)."
8082
),
8183
max_turn_silence: int | None = typer.Option(
82-
None, "--max-turn-silence", help="Max turn silence (ms)."
84+
None, "--max-turn-silence", help="Max silence before ending a turn (ms)."
8385
),
8486
vad_threshold: float | None = typer.Option(
8587
None, "--vad-threshold", help="Voice-activity threshold."
@@ -99,7 +101,9 @@ def stream(
99101
),
100102
speaker_labels: bool | None = typer.Option(None, "--speaker-labels", help="Label speakers."),
101103
max_speakers: int | None = typer.Option(None, "--max-speakers", help="Max speakers."),
102-
voice_focus: str | None = typer.Option(None, "--voice-focus", help="near_field or far_field."),
104+
voice_focus: str | None = typer.Option(
105+
None, "--voice-focus", help="Voice focus: near_field or far_field."
106+
),
103107
voice_focus_threshold: float | None = typer.Option(
104108
None, "--voice-focus-threshold", help="Voice-focus threshold."
105109
),
@@ -108,7 +112,7 @@ def stream(
108112
None, "--redact-pii-policy", help="Comma-separated PII policies."
109113
),
110114
redact_pii_sub: str | None = typer.Option(
111-
None, "--redact-pii-sub", help="hash or entity_name."
115+
None, "--redact-pii-sub", help="Replace redacted PII with: hash or entity_name."
112116
),
113117
inactivity_timeout: int | None = typer.Option(
114118
None, "--inactivity-timeout", help="Auto-close after N seconds idle."
@@ -125,7 +129,9 @@ def stream(
125129
None, "--config-file", help="JSON file of streaming fields."
126130
),
127131
# existing
128-
prompt: str | None = typer.Option(None, "--prompt", help="Bias the speech model (u3-pro)."),
132+
prompt: str | None = typer.Option(
133+
None, "--prompt", help="Prompt to bias the speech model (u3-pro)."
134+
),
129135
llm_prompt: list[str] | None = typer.Option(
130136
None,
131137
"--llm",
@@ -150,8 +156,9 @@ def stream(
150156
) -> None:
151157
"""Transcribe live audio in real time — from your mic, a file, or a URL.
152158
153-
--prompt biases the speech model. --llm-gateway-prompt transforms the full
154-
transcript through LLM Gateway once the stream ends (e.g. "summarize the call").
159+
--prompt biases the speech model. --llm runs a prompt over the live transcript
160+
through LLM Gateway, refreshing the answer on every finalized turn (e.g.
161+
"summarize action items").
155162
"""
156163

157164
def body(state: AppState, json_mode: bool) -> None:

aai_cli/commands/transcribe.py

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ def transcribe(
8282
sample: bool = typer.Option(False, "--sample", help="Use the hosted wildfires.mp3 sample."),
8383
# model & language
8484
speech_model: str | None = typer.Option(
85-
None, "--speech-model", help="best, nano, slam-1, universal."
85+
None, "--speech-model", help="Speech model: best, nano, slam-1, or universal."
8686
),
8787
language_code: str | None = typer.Option(
8888
None, "--language-code", help="Force a language (e.g. en_us)."
@@ -96,22 +96,26 @@ def transcribe(
9696
temperature: float | None = typer.Option(
9797
None, "--temperature", help="Speech model temperature."
9898
),
99-
prompt: str | None = typer.Option(None, "--prompt", help="Bias the speech model (u3-pro)."),
99+
prompt: str | None = typer.Option(
100+
None, "--prompt", help="Prompt to bias the speech model (u3-pro)."
101+
),
100102
# formatting
101103
punctuate: bool | None = typer.Option(
102104
None, "--punctuate/--no-punctuate", help="Add punctuation."
103105
),
104106
format_text: bool | None = typer.Option(
105-
None, "--format-text/--no-format-text", help="Format text."
107+
None, "--format-text/--no-format-text", help="Apply text formatting (casing, numbers)."
108+
),
109+
disfluencies: bool | None = typer.Option(
110+
None, "--disfluencies", help="Keep filler words (e.g. um, uh)."
106111
),
107-
disfluencies: bool | None = typer.Option(None, "--disfluencies", help="Keep filler words."),
108112
# speakers & channels
109113
speaker_labels: bool = typer.Option(False, "--speaker-labels", help="Enable diarization."),
110114
speakers_expected: int | None = typer.Option(
111115
None, "--speakers-expected", help="Hint speaker count."
112116
),
113117
multichannel: bool | None = typer.Option(
114-
None, "--multichannel", help="Transcribe each channel."
118+
None, "--multichannel", help="Transcribe each audio channel separately."
115119
),
116120
# guardrails
117121
redact_pii: bool | None = typer.Option(
@@ -121,7 +125,7 @@ def transcribe(
121125
None, "--redact-pii-policy", help="Comma-separated PII policies (e.g. person_name,...)."
122126
),
123127
redact_pii_sub: str | None = typer.Option(
124-
None, "--redact-pii-sub", help="Substitution: hash or entity_name."
128+
None, "--redact-pii-sub", help="Replace redacted PII with: hash or entity_name."
125129
),
126130
redact_pii_audio: bool | None = typer.Option(
127131
None, "--redact-pii-audio", help="Also redact audio."
@@ -133,20 +137,20 @@ def transcribe(
133137
None, "--content-safety", help="Detect sensitive content."
134138
),
135139
content_safety_confidence: int | None = typer.Option(
136-
None, "--content-safety-confidence", help="Confidence threshold 25-100."
140+
None, "--content-safety-confidence", help="Content-safety confidence threshold (25-100)."
137141
),
138142
speech_threshold: float | None = typer.Option(
139-
None, "--speech-threshold", help="Minimum speech proportion 0-1."
143+
None, "--speech-threshold", help="Minimum proportion of speech required (0-1)."
140144
),
141145
# analysis
142146
summarization: bool | None = typer.Option(
143147
None, "--summarization", help="Summarize the transcript."
144148
),
145149
summary_model: str | None = typer.Option(
146-
None, "--summary-model", help="informative/conversational/catchy."
150+
None, "--summary-model", help="Summary model: informative, conversational, or catchy."
147151
),
148152
summary_type: str | None = typer.Option(
149-
None, "--summary-type", help="bullets/gist/headline/paragraph."
153+
None, "--summary-type", help="Summary format: bullets, gist, headline, or paragraph."
150154
),
151155
auto_chapters: bool | None = typer.Option(None, "--auto-chapters", help="Generate chapters."),
152156
sentiment_analysis: bool | None = typer.Option(

aai_cli/commands/transcripts.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ def body(state: AppState, json_mode: bool) -> None:
7676
rows = client.list_transcripts(api_key, limit=limit)
7777

7878
def render(data: list[dict[str, object]]) -> Table:
79-
table = Table("id", "status", "created", header_style="aai.heading")
79+
table = output.data_table("id", "status", "created")
8080
for row in data:
8181
status = str(row["status"])
8282
table.add_row(

aai_cli/output.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
from collections.abc import Callable
77
from typing import TYPE_CHECKING
88

9+
from rich import box
910
from rich.markup import escape
11+
from rich.table import Table
1012

1113
from aai_cli import theme
1214
from aai_cli.errors import UsageError
@@ -90,6 +92,31 @@ def heading(text: str) -> str:
9092
return f"[aai.heading]{text}[/aai.heading]"
9193

9294

95+
def data_table(*columns: str) -> Table:
96+
"""A list table with the one consistent, minimal look used CLI-wide.
97+
98+
Headers render in the brand heading style with a single rule beneath them and
99+
no surrounding box — the quiet, scannable style the Vercel/Supabase CLIs use.
100+
Defined once here so every listing command (`transcripts list`, `keys list`,
101+
`sessions list`, `usage`, `limits`, `audit`) shares the same table, rather than
102+
each re-deriving Rich's heavy default box.
103+
"""
104+
return Table(*columns, box=box.SIMPLE_HEAD, header_style="aai.heading", pad_edge=False)
105+
106+
107+
def detail_table() -> Table:
108+
"""A borderless label/value grid for single-record views (`whoami`, `sessions get`).
109+
110+
The label column is muted so the values read as the content and the pair scans
111+
as a definition list, not a boxed table. Centralizes what was two divergent
112+
one-off tables into one look.
113+
"""
114+
table = Table.grid(padding=(0, 3))
115+
table.add_column(style="aai.muted")
116+
table.add_column()
117+
return table
118+
119+
93120
def emit[T](data: T, human_renderer: Callable[[T], object], *, json_mode: bool) -> None:
94121
if json_mode:
95122
print(json.dumps(data, default=str))

0 commit comments

Comments
 (0)