mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-09-05 08:10:39 +00:00
Fix cli-table ignoring NO_COLOR environment variable
cli-table defaults to ColorChoice::Always, emitting ANSI escape codes regardless of NO_COLOR. Fix all 5 call sites to: 1. Pass use_color to title cell .bold() instead of hardcoding true 2. Set .color_choice(Never) when colors are disabled 3. Use .display() instead of the free print_stdout/print_stderr functions (which re-wrap with Always defaults) Affected commands: model list, model test, ps list, system df, rewind. The model test snapshots are now clean plaintext. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
e2a8463c2e
commit
f01d74c692
5 changed files with 146 additions and 103 deletions
|
|
@ -1,7 +1,7 @@
|
|||
use anyhow::Context;
|
||||
use anyhow::Result;
|
||||
use cli_table::format::{Border, Separator};
|
||||
use cli_table::{Cell, CellStruct, Color, Style, Table, print_stderr};
|
||||
use cli_table::{Cell, CellStruct, Color, Style, Table};
|
||||
use fabro_config::FabroSettingsExt;
|
||||
use fabro_git_storage::gitobj::Store;
|
||||
use fabro_util::terminal::Styles;
|
||||
|
|
@ -61,9 +61,9 @@ pub(crate) fn print_timeline(timeline: &RunTimeline, styles: &Styles) {
|
|||
|
||||
let use_color = styles.use_color;
|
||||
let title = vec![
|
||||
"@".cell().bold(true),
|
||||
"Node".cell().bold(true),
|
||||
"Details".cell().bold(true),
|
||||
"@".cell().bold(use_color),
|
||||
"Node".cell().bold(use_color),
|
||||
"Details".cell().bold(use_color),
|
||||
];
|
||||
|
||||
let rows: Vec<Vec<CellStruct>> = timeline
|
||||
|
|
@ -100,10 +100,19 @@ pub(crate) fn print_timeline(timeline: &RunTimeline, styles: &Styles) {
|
|||
})
|
||||
.collect();
|
||||
|
||||
let color_choice = if use_color {
|
||||
cli_table::ColorChoice::Auto
|
||||
} else {
|
||||
cli_table::ColorChoice::Never
|
||||
};
|
||||
let table = rows
|
||||
.table()
|
||||
.title(title)
|
||||
.color_choice(color_choice)
|
||||
.border(Border::builder().build())
|
||||
.separator(Separator::builder().build());
|
||||
let _ = print_stderr(table);
|
||||
#[allow(clippy::print_stderr)]
|
||||
if let Ok(display) = table.display() {
|
||||
eprintln!("{display}");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use std::path::Path;
|
|||
use anyhow::Result;
|
||||
use chrono::Utc;
|
||||
use cli_table::format::{Border, Separator};
|
||||
use cli_table::{Cell, CellStruct, Color, Style, Table, print_stdout};
|
||||
use cli_table::{Cell, CellStruct, Color, Style, Table};
|
||||
use fabro_config::FabroSettingsExt;
|
||||
use fabro_util::terminal::Styles;
|
||||
|
||||
|
|
@ -68,12 +68,12 @@ pub(crate) async fn list_command(
|
|||
let use_color = styles.use_color;
|
||||
let now = Utc::now();
|
||||
let title = vec![
|
||||
"RUN ID".cell().bold(true),
|
||||
"WORKFLOW".cell().bold(true),
|
||||
"STATUS".cell().bold(true),
|
||||
"DIRECTORY".cell().bold(true),
|
||||
"DURATION".cell().bold(true),
|
||||
"GOAL".cell().bold(true),
|
||||
"RUN ID".cell().bold(use_color),
|
||||
"WORKFLOW".cell().bold(use_color),
|
||||
"STATUS".cell().bold(use_color),
|
||||
"DIRECTORY".cell().bold(use_color),
|
||||
"DURATION".cell().bold(use_color),
|
||||
"GOAL".cell().bold(use_color),
|
||||
];
|
||||
|
||||
let rows: Vec<Vec<CellStruct>> = display_runs
|
||||
|
|
@ -112,12 +112,19 @@ pub(crate) async fn list_command(
|
|||
})
|
||||
.collect();
|
||||
|
||||
let color_choice = if use_color {
|
||||
cli_table::ColorChoice::Auto
|
||||
} else {
|
||||
cli_table::ColorChoice::Never
|
||||
};
|
||||
let table = rows
|
||||
.table()
|
||||
.title(title)
|
||||
.color_choice(color_choice)
|
||||
.border(Border::builder().build())
|
||||
.separator(Separator::builder().build());
|
||||
print_stdout(table)?;
|
||||
#[allow(clippy::print_stdout)]
|
||||
println!("{}", table.display()?);
|
||||
|
||||
eprintln!("\n{} run(s) listed.", display_runs.len());
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use std::path::Path;
|
|||
use anyhow::Result;
|
||||
use chrono::{DateTime, Utc};
|
||||
use cli_table::format::{Border, Justify, Separator};
|
||||
use cli_table::{Cell, CellStruct, Style, Table, print_stdout};
|
||||
use cli_table::{Cell, CellStruct, Style, Table};
|
||||
use fabro_config::FabroSettingsExt;
|
||||
|
||||
use fabro_workflows::run_lookup::{logs_base, runs_base, scan_runs_combined};
|
||||
|
|
@ -121,12 +121,19 @@ async fn df_from(
|
|||
};
|
||||
let log_reclaim_pct = if total_log_size > 0 { 100 } else { 0 };
|
||||
|
||||
let use_color = console::colors_enabled();
|
||||
let color_choice = if use_color {
|
||||
cli_table::ColorChoice::Auto
|
||||
} else {
|
||||
cli_table::ColorChoice::Never
|
||||
};
|
||||
|
||||
let summary_title = vec![
|
||||
"TYPE".cell().bold(true),
|
||||
"COUNT".cell().bold(true).justify(Justify::Right),
|
||||
"ACTIVE".cell().bold(true).justify(Justify::Right),
|
||||
"SIZE".cell().bold(true).justify(Justify::Right),
|
||||
"RECLAIMABLE".cell().bold(true).justify(Justify::Right),
|
||||
"TYPE".cell().bold(use_color),
|
||||
"COUNT".cell().bold(use_color).justify(Justify::Right),
|
||||
"ACTIVE".cell().bold(use_color).justify(Justify::Right),
|
||||
"SIZE".cell().bold(use_color).justify(Justify::Right),
|
||||
"RECLAIMABLE".cell().bold(use_color).justify(Justify::Right),
|
||||
];
|
||||
let summary_rows: Vec<Vec<CellStruct>> = vec![
|
||||
vec![
|
||||
|
|
@ -160,9 +167,11 @@ async fn df_from(
|
|||
let summary_table = summary_rows
|
||||
.table()
|
||||
.title(summary_title)
|
||||
.color_choice(color_choice)
|
||||
.border(Border::builder().build())
|
||||
.separator(Separator::builder().build());
|
||||
print_stdout(summary_table)?;
|
||||
#[allow(clippy::print_stdout)]
|
||||
println!("{}", summary_table.display()?);
|
||||
|
||||
println!();
|
||||
println!("Data directory: {}", data_dir.display());
|
||||
|
|
@ -173,11 +182,11 @@ async fn df_from(
|
|||
|
||||
println!();
|
||||
let verbose_title = vec![
|
||||
"RUN ID".cell().bold(true),
|
||||
"WORKFLOW".cell().bold(true),
|
||||
"STATUS".cell().bold(true),
|
||||
"AGE".cell().bold(true).justify(Justify::Right),
|
||||
"SIZE".cell().bold(true).justify(Justify::Right),
|
||||
"RUN ID".cell().bold(use_color),
|
||||
"WORKFLOW".cell().bold(use_color),
|
||||
"STATUS".cell().bold(use_color),
|
||||
"AGE".cell().bold(use_color).justify(Justify::Right),
|
||||
"SIZE".cell().bold(use_color).justify(Justify::Right),
|
||||
];
|
||||
|
||||
let now = Utc::now();
|
||||
|
|
@ -213,9 +222,11 @@ async fn df_from(
|
|||
let verbose_table = verbose_rows
|
||||
.table()
|
||||
.title(verbose_title)
|
||||
.color_choice(color_choice)
|
||||
.border(Border::builder().build())
|
||||
.separator(Separator::builder().build());
|
||||
print_stdout(verbose_table)?;
|
||||
#[allow(clippy::print_stdout)]
|
||||
println!("{}", verbose_table.display()?);
|
||||
println!();
|
||||
println!("* = reclaimable");
|
||||
|
||||
|
|
|
|||
|
|
@ -36,28 +36,28 @@ fn bare() {
|
|||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
[0m [0m[0m[0m[1mMODEL [0m [0m[0m [0m[0m[0m[1mPROVIDER [0m [0m[0m [0m[0m[0m[1mALIASES [0m [0m[0m [0m[0m[0m[1mCONTEXT[0m [0m[0m [0m[0m[0m[1m COST[0m [0m[0m [0m[0m[0m[1m SPEED[0m [0m
|
||||
[0m[0m [0m[0m[0mclaude-opus-4-6 [0m [0m[0m [0m[0m[0manthropic[0m [0m[0m [0m[0m[0mopus, claude-opus [0m [0m[0m [0m[0m[0m 1m[0m [0m[0m [0m[0m[0m $15.0 / $75.0[0m [0m[0m [0m[0m[0m 25 tok/s[0m [0m
|
||||
[0m[0m [0m[0m[0mclaude-sonnet-4-5 [0m [0m[0m [0m[0m[0manthropic[0m [0m[0m [0m[0m[0m [0m [0m[0m [0m[0m[0m 200k[0m [0m[0m [0m[0m[0m $3.0 / $15.0[0m [0m[0m [0m[0m[0m 50 tok/s[0m [0m
|
||||
[0m[0m [0m[0m[0mclaude-sonnet-4-6 [0m [0m[0m [0m[0m[0manthropic[0m [0m[0m [0m[0m[0msonnet, claude-sonnet [0m [0m[0m [0m[0m[0m 200k[0m [0m[0m [0m[0m[0m $3.0 / $15.0[0m [0m[0m [0m[0m[0m 50 tok/s[0m [0m
|
||||
[0m[0m [0m[0m[0mclaude-haiku-4-5 [0m [0m[0m [0m[0m[0manthropic[0m [0m[0m [0m[0m[0mhaiku, claude-haiku [0m [0m[0m [0m[0m[0m 200k[0m [0m[0m [0m[0m[0m $0.8 / $4.0[0m [0m[0m [0m[0m[0m 100 tok/s[0m [0m
|
||||
[0m[0m [0m[0m[0mgpt-5.2 [0m [0m[0m [0m[0m[0mopenai [0m [0m[0m [0m[0m[0mgpt5 [0m [0m[0m [0m[0m[0m 1m[0m [0m[0m [0m[0m[0m $1.8 / $14.0[0m [0m[0m [0m[0m[0m 65 tok/s[0m [0m
|
||||
[0m[0m [0m[0m[0mgpt-5-mini [0m [0m[0m [0m[0m[0mopenai [0m [0m[0m [0m[0m[0mgpt5-mini [0m [0m[0m [0m[0m[0m 1m[0m [0m[0m [0m[0m[0m $0.2 / $2.0[0m [0m[0m [0m[0m[0m 70 tok/s[0m [0m
|
||||
[0m[0m [0m[0m[0mgpt-5.2-codex [0m [0m[0m [0m[0m[0mopenai [0m [0m[0m [0m[0m[0m [0m [0m[0m [0m[0m[0m 1m[0m [0m[0m [0m[0m[0m $1.8 / $14.0[0m [0m[0m [0m[0m[0m 100 tok/s[0m [0m
|
||||
[0m[0m [0m[0m[0mgpt-5.3-codex [0m [0m[0m [0m[0m[0mopenai [0m [0m[0m [0m[0m[0mcodex [0m [0m[0m [0m[0m[0m 1m[0m [0m[0m [0m[0m[0m $1.8 / $14.0[0m [0m[0m [0m[0m[0m 100 tok/s[0m [0m
|
||||
[0m[0m [0m[0m[0mgpt-5.3-codex-spark [0m [0m[0m [0m[0m[0mopenai [0m [0m[0m [0m[0m[0mcodex-spark [0m [0m[0m [0m[0m[0m 131k[0m [0m[0m [0m[0m[0m - / -[0m [0m[0m [0m[0m[0m1000 tok/s[0m [0m
|
||||
[0m[0m [0m[0m[0mgpt-5.4 [0m [0m[0m [0m[0m[0mopenai [0m [0m[0m [0m[0m[0mgpt54, gpt-54 [0m [0m[0m [0m[0m[0m 1m[0m [0m[0m [0m[0m[0m $2.5 / $15.0[0m [0m[0m [0m[0m[0m 70 tok/s[0m [0m
|
||||
[0m[0m [0m[0m[0mgpt-5.4-pro [0m [0m[0m [0m[0m[0mopenai [0m [0m[0m [0m[0m[0mgpt54-pro, gpt-54-pro [0m [0m[0m [0m[0m[0m 1m[0m [0m[0m [0m[0m[0m$30.0 / $180.0[0m [0m[0m [0m[0m[0m 20 tok/s[0m [0m
|
||||
[0m[0m [0m[0m[0mgpt-5.4-mini [0m [0m[0m [0m[0m[0mopenai [0m [0m[0m [0m[0m[0mgpt54-mini, gpt-54-mini[0m [0m[0m [0m[0m[0m 400k[0m [0m[0m [0m[0m[0m $0.8 / $4.5[0m [0m[0m [0m[0m[0m 140 tok/s[0m [0m
|
||||
[0m[0m [0m[0m[0mgemini-3.1-pro-preview [0m [0m[0m [0m[0m[0mgemini [0m [0m[0m [0m[0m[0mgemini-pro [0m [0m[0m [0m[0m[0m 1m[0m [0m[0m [0m[0m[0m $2.0 / $12.0[0m [0m[0m [0m[0m[0m 85 tok/s[0m [0m
|
||||
[0m[0m [0m[0m[0mgemini-3.1-pro-preview-customtools[0m [0m[0m [0m[0m[0mgemini [0m [0m[0m [0m[0m[0mgemini-customtools [0m [0m[0m [0m[0m[0m 1m[0m [0m[0m [0m[0m[0m $2.0 / $12.0[0m [0m[0m [0m[0m[0m 85 tok/s[0m [0m
|
||||
[0m[0m [0m[0m[0mgemini-3-flash-preview [0m [0m[0m [0m[0m[0mgemini [0m [0m[0m [0m[0m[0mgemini-flash [0m [0m[0m [0m[0m[0m 1m[0m [0m[0m [0m[0m[0m $0.5 / $3.0[0m [0m[0m [0m[0m[0m 150 tok/s[0m [0m
|
||||
[0m[0m [0m[0m[0mgemini-3.1-flash-lite-preview [0m [0m[0m [0m[0m[0mgemini [0m [0m[0m [0m[0m[0mgemini-flash-lite [0m [0m[0m [0m[0m[0m 1m[0m [0m[0m [0m[0m[0m $0.2 / $1.5[0m [0m[0m [0m[0m[0m 200 tok/s[0m [0m
|
||||
[0m[0m [0m[0m[0mkimi-k2.5 [0m [0m[0m [0m[0m[0mkimi [0m [0m[0m [0m[0m[0mkimi [0m [0m[0m [0m[0m[0m 262k[0m [0m[0m [0m[0m[0m $0.6 / $3.0[0m [0m[0m [0m[0m[0m 50 tok/s[0m [0m
|
||||
[0m[0m [0m[0m[0mglm-4.7 [0m [0m[0m [0m[0m[0mzai [0m [0m[0m [0m[0m[0mglm, glm4 [0m [0m[0m [0m[0m[0m 203k[0m [0m[0m [0m[0m[0m $0.6 / $2.2[0m [0m[0m [0m[0m[0m 100 tok/s[0m [0m
|
||||
[0m[0m [0m[0m[0mminimax-m2.5 [0m [0m[0m [0m[0m[0mminimax [0m [0m[0m [0m[0m[0mminimax [0m [0m[0m [0m[0m[0m 197k[0m [0m[0m [0m[0m[0m $0.3 / $1.2[0m [0m[0m [0m[0m[0m 45 tok/s[0m [0m
|
||||
[0m[0m [0m[0m[0mmercury-2 [0m [0m[0m [0m[0m[0minception[0m [0m[0m [0m[0m[0mmercury [0m [0m[0m [0m[0m[0m 131k[0m [0m[0m [0m[0m[0m $0.2 / $0.8[0m [0m[0m [0m[0m[0m1000 tok/s[0m [0m
|
||||
[0m----- stderr -----
|
||||
MODEL PROVIDER ALIASES CONTEXT COST SPEED
|
||||
claude-opus-4-6 anthropic opus, claude-opus 1m $15.0 / $75.0 25 tok/s
|
||||
claude-sonnet-4-5 anthropic 200k $3.0 / $15.0 50 tok/s
|
||||
claude-sonnet-4-6 anthropic sonnet, claude-sonnet 200k $3.0 / $15.0 50 tok/s
|
||||
claude-haiku-4-5 anthropic haiku, claude-haiku 200k $0.8 / $4.0 100 tok/s
|
||||
gpt-5.2 openai gpt5 1m $1.8 / $14.0 65 tok/s
|
||||
gpt-5-mini openai gpt5-mini 1m $0.2 / $2.0 70 tok/s
|
||||
gpt-5.2-codex openai 1m $1.8 / $14.0 100 tok/s
|
||||
gpt-5.3-codex openai codex 1m $1.8 / $14.0 100 tok/s
|
||||
gpt-5.3-codex-spark openai codex-spark 131k - / - 1000 tok/s
|
||||
gpt-5.4 openai gpt54, gpt-54 1m $2.5 / $15.0 70 tok/s
|
||||
gpt-5.4-pro openai gpt54-pro, gpt-54-pro 1m $30.0 / $180.0 20 tok/s
|
||||
gpt-5.4-mini openai gpt54-mini, gpt-54-mini 400k $0.8 / $4.5 140 tok/s
|
||||
gemini-3.1-pro-preview gemini gemini-pro 1m $2.0 / $12.0 85 tok/s
|
||||
gemini-3.1-pro-preview-customtools gemini gemini-customtools 1m $2.0 / $12.0 85 tok/s
|
||||
gemini-3-flash-preview gemini gemini-flash 1m $0.5 / $3.0 150 tok/s
|
||||
gemini-3.1-flash-lite-preview gemini gemini-flash-lite 1m $0.2 / $1.5 200 tok/s
|
||||
kimi-k2.5 kimi kimi 262k $0.6 / $3.0 50 tok/s
|
||||
glm-4.7 zai glm, glm4 203k $0.6 / $2.2 100 tok/s
|
||||
minimax-m2.5 minimax minimax 197k $0.3 / $1.2 45 tok/s
|
||||
mercury-2 inception mercury 131k $0.2 / $0.8 1000 tok/s
|
||||
----- stderr -----
|
||||
");
|
||||
}
|
||||
|
||||
|
|
@ -70,28 +70,28 @@ fn list() {
|
|||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
[0m [0m[0m[0m[1mMODEL [0m [0m[0m [0m[0m[0m[1mPROVIDER [0m [0m[0m [0m[0m[0m[1mALIASES [0m [0m[0m [0m[0m[0m[1mCONTEXT[0m [0m[0m [0m[0m[0m[1m COST[0m [0m[0m [0m[0m[0m[1m SPEED[0m [0m
|
||||
[0m[0m [0m[0m[0mclaude-opus-4-6 [0m [0m[0m [0m[0m[0manthropic[0m [0m[0m [0m[0m[0mopus, claude-opus [0m [0m[0m [0m[0m[0m 1m[0m [0m[0m [0m[0m[0m $15.0 / $75.0[0m [0m[0m [0m[0m[0m 25 tok/s[0m [0m
|
||||
[0m[0m [0m[0m[0mclaude-sonnet-4-5 [0m [0m[0m [0m[0m[0manthropic[0m [0m[0m [0m[0m[0m [0m [0m[0m [0m[0m[0m 200k[0m [0m[0m [0m[0m[0m $3.0 / $15.0[0m [0m[0m [0m[0m[0m 50 tok/s[0m [0m
|
||||
[0m[0m [0m[0m[0mclaude-sonnet-4-6 [0m [0m[0m [0m[0m[0manthropic[0m [0m[0m [0m[0m[0msonnet, claude-sonnet [0m [0m[0m [0m[0m[0m 200k[0m [0m[0m [0m[0m[0m $3.0 / $15.0[0m [0m[0m [0m[0m[0m 50 tok/s[0m [0m
|
||||
[0m[0m [0m[0m[0mclaude-haiku-4-5 [0m [0m[0m [0m[0m[0manthropic[0m [0m[0m [0m[0m[0mhaiku, claude-haiku [0m [0m[0m [0m[0m[0m 200k[0m [0m[0m [0m[0m[0m $0.8 / $4.0[0m [0m[0m [0m[0m[0m 100 tok/s[0m [0m
|
||||
[0m[0m [0m[0m[0mgpt-5.2 [0m [0m[0m [0m[0m[0mopenai [0m [0m[0m [0m[0m[0mgpt5 [0m [0m[0m [0m[0m[0m 1m[0m [0m[0m [0m[0m[0m $1.8 / $14.0[0m [0m[0m [0m[0m[0m 65 tok/s[0m [0m
|
||||
[0m[0m [0m[0m[0mgpt-5-mini [0m [0m[0m [0m[0m[0mopenai [0m [0m[0m [0m[0m[0mgpt5-mini [0m [0m[0m [0m[0m[0m 1m[0m [0m[0m [0m[0m[0m $0.2 / $2.0[0m [0m[0m [0m[0m[0m 70 tok/s[0m [0m
|
||||
[0m[0m [0m[0m[0mgpt-5.2-codex [0m [0m[0m [0m[0m[0mopenai [0m [0m[0m [0m[0m[0m [0m [0m[0m [0m[0m[0m 1m[0m [0m[0m [0m[0m[0m $1.8 / $14.0[0m [0m[0m [0m[0m[0m 100 tok/s[0m [0m
|
||||
[0m[0m [0m[0m[0mgpt-5.3-codex [0m [0m[0m [0m[0m[0mopenai [0m [0m[0m [0m[0m[0mcodex [0m [0m[0m [0m[0m[0m 1m[0m [0m[0m [0m[0m[0m $1.8 / $14.0[0m [0m[0m [0m[0m[0m 100 tok/s[0m [0m
|
||||
[0m[0m [0m[0m[0mgpt-5.3-codex-spark [0m [0m[0m [0m[0m[0mopenai [0m [0m[0m [0m[0m[0mcodex-spark [0m [0m[0m [0m[0m[0m 131k[0m [0m[0m [0m[0m[0m - / -[0m [0m[0m [0m[0m[0m1000 tok/s[0m [0m
|
||||
[0m[0m [0m[0m[0mgpt-5.4 [0m [0m[0m [0m[0m[0mopenai [0m [0m[0m [0m[0m[0mgpt54, gpt-54 [0m [0m[0m [0m[0m[0m 1m[0m [0m[0m [0m[0m[0m $2.5 / $15.0[0m [0m[0m [0m[0m[0m 70 tok/s[0m [0m
|
||||
[0m[0m [0m[0m[0mgpt-5.4-pro [0m [0m[0m [0m[0m[0mopenai [0m [0m[0m [0m[0m[0mgpt54-pro, gpt-54-pro [0m [0m[0m [0m[0m[0m 1m[0m [0m[0m [0m[0m[0m$30.0 / $180.0[0m [0m[0m [0m[0m[0m 20 tok/s[0m [0m
|
||||
[0m[0m [0m[0m[0mgpt-5.4-mini [0m [0m[0m [0m[0m[0mopenai [0m [0m[0m [0m[0m[0mgpt54-mini, gpt-54-mini[0m [0m[0m [0m[0m[0m 400k[0m [0m[0m [0m[0m[0m $0.8 / $4.5[0m [0m[0m [0m[0m[0m 140 tok/s[0m [0m
|
||||
[0m[0m [0m[0m[0mgemini-3.1-pro-preview [0m [0m[0m [0m[0m[0mgemini [0m [0m[0m [0m[0m[0mgemini-pro [0m [0m[0m [0m[0m[0m 1m[0m [0m[0m [0m[0m[0m $2.0 / $12.0[0m [0m[0m [0m[0m[0m 85 tok/s[0m [0m
|
||||
[0m[0m [0m[0m[0mgemini-3.1-pro-preview-customtools[0m [0m[0m [0m[0m[0mgemini [0m [0m[0m [0m[0m[0mgemini-customtools [0m [0m[0m [0m[0m[0m 1m[0m [0m[0m [0m[0m[0m $2.0 / $12.0[0m [0m[0m [0m[0m[0m 85 tok/s[0m [0m
|
||||
[0m[0m [0m[0m[0mgemini-3-flash-preview [0m [0m[0m [0m[0m[0mgemini [0m [0m[0m [0m[0m[0mgemini-flash [0m [0m[0m [0m[0m[0m 1m[0m [0m[0m [0m[0m[0m $0.5 / $3.0[0m [0m[0m [0m[0m[0m 150 tok/s[0m [0m
|
||||
[0m[0m [0m[0m[0mgemini-3.1-flash-lite-preview [0m [0m[0m [0m[0m[0mgemini [0m [0m[0m [0m[0m[0mgemini-flash-lite [0m [0m[0m [0m[0m[0m 1m[0m [0m[0m [0m[0m[0m $0.2 / $1.5[0m [0m[0m [0m[0m[0m 200 tok/s[0m [0m
|
||||
[0m[0m [0m[0m[0mkimi-k2.5 [0m [0m[0m [0m[0m[0mkimi [0m [0m[0m [0m[0m[0mkimi [0m [0m[0m [0m[0m[0m 262k[0m [0m[0m [0m[0m[0m $0.6 / $3.0[0m [0m[0m [0m[0m[0m 50 tok/s[0m [0m
|
||||
[0m[0m [0m[0m[0mglm-4.7 [0m [0m[0m [0m[0m[0mzai [0m [0m[0m [0m[0m[0mglm, glm4 [0m [0m[0m [0m[0m[0m 203k[0m [0m[0m [0m[0m[0m $0.6 / $2.2[0m [0m[0m [0m[0m[0m 100 tok/s[0m [0m
|
||||
[0m[0m [0m[0m[0mminimax-m2.5 [0m [0m[0m [0m[0m[0mminimax [0m [0m[0m [0m[0m[0mminimax [0m [0m[0m [0m[0m[0m 197k[0m [0m[0m [0m[0m[0m $0.3 / $1.2[0m [0m[0m [0m[0m[0m 45 tok/s[0m [0m
|
||||
[0m[0m [0m[0m[0mmercury-2 [0m [0m[0m [0m[0m[0minception[0m [0m[0m [0m[0m[0mmercury [0m [0m[0m [0m[0m[0m 131k[0m [0m[0m [0m[0m[0m $0.2 / $0.8[0m [0m[0m [0m[0m[0m1000 tok/s[0m [0m
|
||||
[0m----- stderr -----
|
||||
MODEL PROVIDER ALIASES CONTEXT COST SPEED
|
||||
claude-opus-4-6 anthropic opus, claude-opus 1m $15.0 / $75.0 25 tok/s
|
||||
claude-sonnet-4-5 anthropic 200k $3.0 / $15.0 50 tok/s
|
||||
claude-sonnet-4-6 anthropic sonnet, claude-sonnet 200k $3.0 / $15.0 50 tok/s
|
||||
claude-haiku-4-5 anthropic haiku, claude-haiku 200k $0.8 / $4.0 100 tok/s
|
||||
gpt-5.2 openai gpt5 1m $1.8 / $14.0 65 tok/s
|
||||
gpt-5-mini openai gpt5-mini 1m $0.2 / $2.0 70 tok/s
|
||||
gpt-5.2-codex openai 1m $1.8 / $14.0 100 tok/s
|
||||
gpt-5.3-codex openai codex 1m $1.8 / $14.0 100 tok/s
|
||||
gpt-5.3-codex-spark openai codex-spark 131k - / - 1000 tok/s
|
||||
gpt-5.4 openai gpt54, gpt-54 1m $2.5 / $15.0 70 tok/s
|
||||
gpt-5.4-pro openai gpt54-pro, gpt-54-pro 1m $30.0 / $180.0 20 tok/s
|
||||
gpt-5.4-mini openai gpt54-mini, gpt-54-mini 400k $0.8 / $4.5 140 tok/s
|
||||
gemini-3.1-pro-preview gemini gemini-pro 1m $2.0 / $12.0 85 tok/s
|
||||
gemini-3.1-pro-preview-customtools gemini gemini-customtools 1m $2.0 / $12.0 85 tok/s
|
||||
gemini-3-flash-preview gemini gemini-flash 1m $0.5 / $3.0 150 tok/s
|
||||
gemini-3.1-flash-lite-preview gemini gemini-flash-lite 1m $0.2 / $1.5 200 tok/s
|
||||
kimi-k2.5 kimi kimi 262k $0.6 / $3.0 50 tok/s
|
||||
glm-4.7 zai glm, glm4 203k $0.6 / $2.2 100 tok/s
|
||||
minimax-m2.5 minimax minimax 197k $0.3 / $1.2 45 tok/s
|
||||
mercury-2 inception mercury 131k $0.2 / $0.8 1000 tok/s
|
||||
----- stderr -----
|
||||
");
|
||||
}
|
||||
|
||||
|
|
@ -104,12 +104,12 @@ fn list_provider() {
|
|||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
[0m [0m[0m[0m[1mMODEL [0m [0m[0m [0m[0m[0m[1mPROVIDER [0m [0m[0m [0m[0m[0m[1mALIASES [0m [0m[0m [0m[0m[0m[1mCONTEXT[0m [0m[0m [0m[0m[0m[1m COST[0m [0m[0m [0m[0m[0m[1m SPEED[0m [0m
|
||||
[0m[0m [0m[0m[0mclaude-opus-4-6 [0m [0m[0m [0m[0m[0manthropic[0m [0m[0m [0m[0m[0mopus, claude-opus [0m [0m[0m [0m[0m[0m 1m[0m [0m[0m [0m[0m[0m$15.0 / $75.0[0m [0m[0m [0m[0m[0m 25 tok/s[0m [0m
|
||||
[0m[0m [0m[0m[0mclaude-sonnet-4-5[0m [0m[0m [0m[0m[0manthropic[0m [0m[0m [0m[0m[0m [0m [0m[0m [0m[0m[0m 200k[0m [0m[0m [0m[0m[0m $3.0 / $15.0[0m [0m[0m [0m[0m[0m 50 tok/s[0m [0m
|
||||
[0m[0m [0m[0m[0mclaude-sonnet-4-6[0m [0m[0m [0m[0m[0manthropic[0m [0m[0m [0m[0m[0msonnet, claude-sonnet[0m [0m[0m [0m[0m[0m 200k[0m [0m[0m [0m[0m[0m $3.0 / $15.0[0m [0m[0m [0m[0m[0m 50 tok/s[0m [0m
|
||||
[0m[0m [0m[0m[0mclaude-haiku-4-5 [0m [0m[0m [0m[0m[0manthropic[0m [0m[0m [0m[0m[0mhaiku, claude-haiku [0m [0m[0m [0m[0m[0m 200k[0m [0m[0m [0m[0m[0m $0.8 / $4.0[0m [0m[0m [0m[0m[0m100 tok/s[0m [0m
|
||||
[0m----- stderr -----
|
||||
MODEL PROVIDER ALIASES CONTEXT COST SPEED
|
||||
claude-opus-4-6 anthropic opus, claude-opus 1m $15.0 / $75.0 25 tok/s
|
||||
claude-sonnet-4-5 anthropic 200k $3.0 / $15.0 50 tok/s
|
||||
claude-sonnet-4-6 anthropic sonnet, claude-sonnet 200k $3.0 / $15.0 50 tok/s
|
||||
claude-haiku-4-5 anthropic haiku, claude-haiku 200k $0.8 / $4.0 100 tok/s
|
||||
----- stderr -----
|
||||
");
|
||||
}
|
||||
|
||||
|
|
@ -122,9 +122,9 @@ fn list_query() {
|
|||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
[0m [0m[0m[0m[1mMODEL [0m [0m[0m [0m[0m[0m[1mPROVIDER [0m [0m[0m [0m[0m[0m[1mALIASES [0m [0m[0m [0m[0m[0m[1mCONTEXT[0m [0m[0m [0m[0m[0m[1m COST[0m [0m[0m [0m[0m[0m[1m SPEED[0m [0m
|
||||
[0m[0m [0m[0m[0mclaude-opus-4-6[0m [0m[0m [0m[0m[0manthropic[0m [0m[0m [0m[0m[0mopus, claude-opus[0m [0m[0m [0m[0m[0m 1m[0m [0m[0m [0m[0m[0m$15.0 / $75.0[0m [0m[0m [0m[0m[0m25 tok/s[0m [0m
|
||||
[0m----- stderr -----
|
||||
MODEL PROVIDER ALIASES CONTEXT COST SPEED
|
||||
claude-opus-4-6 anthropic opus, claude-opus 1m $15.0 / $75.0 25 tok/s
|
||||
----- stderr -----
|
||||
");
|
||||
}
|
||||
|
||||
|
|
@ -137,11 +137,11 @@ fn list_query_aliases() {
|
|||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
[0m [0m[0m[0m[1mMODEL [0m [0m[0m [0m[0m[0m[1mPROVIDER[0m [0m[0m [0m[0m[0m[1mALIASES [0m [0m[0m [0m[0m[0m[1mCONTEXT[0m [0m[0m [0m[0m[0m[1m COST[0m [0m[0m [0m[0m[0m[1m SPEED[0m [0m
|
||||
[0m[0m [0m[0m[0mgpt-5.2-codex [0m [0m[0m [0m[0m[0mopenai [0m [0m[0m [0m[0m[0m [0m [0m[0m [0m[0m[0m 1m[0m [0m[0m [0m[0m[0m$1.8 / $14.0[0m [0m[0m [0m[0m[0m 100 tok/s[0m [0m
|
||||
[0m[0m [0m[0m[0mgpt-5.3-codex [0m [0m[0m [0m[0m[0mopenai [0m [0m[0m [0m[0m[0mcodex [0m [0m[0m [0m[0m[0m 1m[0m [0m[0m [0m[0m[0m$1.8 / $14.0[0m [0m[0m [0m[0m[0m 100 tok/s[0m [0m
|
||||
[0m[0m [0m[0m[0mgpt-5.3-codex-spark[0m [0m[0m [0m[0m[0mopenai [0m [0m[0m [0m[0m[0mcodex-spark[0m [0m[0m [0m[0m[0m 131k[0m [0m[0m [0m[0m[0m - / -[0m [0m[0m [0m[0m[0m1000 tok/s[0m [0m
|
||||
[0m----- stderr -----
|
||||
MODEL PROVIDER ALIASES CONTEXT COST SPEED
|
||||
gpt-5.2-codex openai 1m $1.8 / $14.0 100 tok/s
|
||||
gpt-5.3-codex openai codex 1m $1.8 / $14.0 100 tok/s
|
||||
gpt-5.3-codex-spark openai codex-spark 131k - / - 1000 tok/s
|
||||
----- stderr -----
|
||||
");
|
||||
}
|
||||
|
||||
|
|
@ -154,8 +154,8 @@ fn list_query_case_insensitive() {
|
|||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
[0m [0m[0m[0m[1mMODEL [0m [0m[0m [0m[0m[0m[1mPROVIDER [0m [0m[0m [0m[0m[0m[1mALIASES [0m [0m[0m [0m[0m[0m[1mCONTEXT[0m [0m[0m [0m[0m[0m[1m COST[0m [0m[0m [0m[0m[0m[1m SPEED[0m [0m
|
||||
[0m[0m [0m[0m[0mclaude-opus-4-6[0m [0m[0m [0m[0m[0manthropic[0m [0m[0m [0m[0m[0mopus, claude-opus[0m [0m[0m [0m[0m[0m 1m[0m [0m[0m [0m[0m[0m$15.0 / $75.0[0m [0m[0m [0m[0m[0m25 tok/s[0m [0m
|
||||
[0m----- stderr -----
|
||||
MODEL PROVIDER ALIASES CONTEXT COST SPEED
|
||||
claude-opus-4-6 anthropic opus, claude-opus 1m $15.0 / $75.0 25 tok/s
|
||||
----- stderr -----
|
||||
");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use std::time::Duration;
|
|||
use anyhow::{Context, Result, bail};
|
||||
use clap::{Args, Subcommand};
|
||||
use cli_table::format::{Border, Justify, Separator};
|
||||
use cli_table::{Cell, CellStruct, Color, Style, Table, print_stdout};
|
||||
use cli_table::{Cell, CellStruct, Color, Style, Table};
|
||||
use futures::{StreamExt, stream};
|
||||
use serde::Deserialize;
|
||||
use tokio::task;
|
||||
|
|
@ -122,6 +122,14 @@ fn color_if(use_color: bool, color: Color) -> Option<Color> {
|
|||
if use_color { Some(color) } else { None }
|
||||
}
|
||||
|
||||
fn color_choice(use_color: bool) -> cli_table::ColorChoice {
|
||||
if use_color {
|
||||
cli_table::ColorChoice::Auto
|
||||
} else {
|
||||
cli_table::ColorChoice::Never
|
||||
}
|
||||
}
|
||||
|
||||
fn model_row(model: &Model, use_color: bool) -> Vec<CellStruct> {
|
||||
let aliases = model.aliases.join(", ");
|
||||
let cost = format!(
|
||||
|
|
@ -149,14 +157,14 @@ fn model_row(model: &Model, use_color: bool) -> Vec<CellStruct> {
|
|||
]
|
||||
}
|
||||
|
||||
fn models_title() -> Vec<CellStruct> {
|
||||
fn models_title(use_color: bool) -> Vec<CellStruct> {
|
||||
vec![
|
||||
"MODEL".cell().bold(true),
|
||||
"PROVIDER".cell().bold(true),
|
||||
"ALIASES".cell().bold(true),
|
||||
"CONTEXT".cell().bold(true).justify(Justify::Right),
|
||||
"COST".cell().bold(true).justify(Justify::Right),
|
||||
"SPEED".cell().bold(true).justify(Justify::Right),
|
||||
"MODEL".cell().bold(use_color),
|
||||
"PROVIDER".cell().bold(use_color),
|
||||
"ALIASES".cell().bold(use_color),
|
||||
"CONTEXT".cell().bold(use_color).justify(Justify::Right),
|
||||
"COST".cell().bold(use_color).justify(Justify::Right),
|
||||
"SPEED".cell().bold(use_color).justify(Justify::Right),
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -166,10 +174,14 @@ fn print_models_table(models: &[Model], s: &Styles) {
|
|||
let rows: Vec<Vec<CellStruct>> = models.iter().map(|m| model_row(m, use_color)).collect();
|
||||
let table = rows
|
||||
.table()
|
||||
.title(models_title())
|
||||
.title(models_title(use_color))
|
||||
.color_choice(color_choice(use_color))
|
||||
.border(Border::builder().build())
|
||||
.separator(Separator::builder().build());
|
||||
let _ = print_stdout(table);
|
||||
#[allow(clippy::print_stdout)]
|
||||
{
|
||||
println!("{}", table.display().unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
fn read_stdin_prompt() -> Option<String> {
|
||||
|
|
@ -948,8 +960,8 @@ async fn test_models_via_server(
|
|||
}
|
||||
|
||||
let use_color = s.use_color;
|
||||
let mut title = models_title();
|
||||
title.push("RESULT".cell().bold(true));
|
||||
let mut title = models_title(use_color);
|
||||
title.push("RESULT".cell().bold(use_color));
|
||||
|
||||
let mut rows: Vec<Vec<CellStruct>> = Vec::new();
|
||||
let mut failures = 0u32;
|
||||
|
|
@ -985,9 +997,11 @@ async fn test_models_via_server(
|
|||
let table = rows
|
||||
.table()
|
||||
.title(title)
|
||||
.color_choice(color_choice(use_color))
|
||||
.border(Border::builder().build())
|
||||
.separator(Separator::builder().build());
|
||||
print_stdout(table)?;
|
||||
#[allow(clippy::print_stdout)]
|
||||
println!("{}", table.display()?);
|
||||
|
||||
if failures > 0 {
|
||||
bail!("{failures} model(s) failed");
|
||||
|
|
@ -1134,8 +1148,8 @@ async fn test_models(
|
|||
sorted_results.sort_by_key(|(idx, _, _)| *idx);
|
||||
|
||||
let use_color = s.use_color;
|
||||
let mut title = models_title();
|
||||
title.push("RESULT".cell().bold(true));
|
||||
let mut title = models_title(use_color);
|
||||
title.push("RESULT".cell().bold(use_color));
|
||||
|
||||
let mut rows: Vec<Vec<CellStruct>> = Vec::new();
|
||||
let mut failures = 0u32;
|
||||
|
|
@ -1155,9 +1169,11 @@ async fn test_models(
|
|||
let table = rows
|
||||
.table()
|
||||
.title(title)
|
||||
.color_choice(color_choice(use_color))
|
||||
.border(Border::builder().build())
|
||||
.separator(Separator::builder().build());
|
||||
print_stdout(table)?;
|
||||
#[allow(clippy::print_stdout)]
|
||||
println!("{}", table.display()?);
|
||||
|
||||
if failures > 0 {
|
||||
bail!("{failures} model(s) failed");
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue