mirror of
https://github.com/kvcache-ai/ktransformers.git
synced 2026-03-19 12:57:22 +00:00
* [feat]: redesign kt run interactive configuration with i18n support - Redesign kt run with 8-step interactive flow (model selection, inference method, NUMA/CPU, GPU experts, KV cache, GPU/TP selection, parsers, host/port) - Add configuration save/load system (~/.ktransformers/run_configs.yaml) - Add i18n support for kt chat (en/zh translations) - Add universal input validators with auto-retry and Chinese comma support - Add port availability checker with auto-suggestion - Add parser configuration (--tool-call-parser, --reasoning-parser) - Remove tuna command and clean up redundant files - Fix: variable reference bug in run.py, filter to show only MoE models * [feat]: unify model selection UI and enable shared experts fusion by default - Unify kt run model selection table with kt model list display * Add Total size, MoE Size, Repo, and SHA256 status columns * Use consistent formatting and styling * Improve user decision-making with more information - Enable --disable-shared-experts-fusion by default * Change default value from False to True * Users can still override with --enable-shared-experts-fusion * [feat]: improve kt chat with performance metrics and better CJK support - Add performance metrics display after each response * Total time, TTFT (Time To First Token), TPOT (Time Per Output Token) * Accurate input/output token counts using model tokenizer * Fallback to estimation if tokenizer unavailable * Metrics shown in dim style (not prominent) - Fix Chinese character input issues * Replace Prompt.ask() with console.input() for better CJK support * Fixes backspace deletion showing half-characters - Suppress NumPy subnormal warnings * Filter "The value of the smallest subnormal" warnings * Cleaner CLI output on certain hardware environments * [fix]: correct TTFT measurement in kt chat - Move start_time initialization before API call - Previously start_time was set when receiving first chunk, causing TTFT ≈ 0ms - Now correctly measures time from request sent to first token received * [docs]: 添加 Clawdbot 集成指南 - KTransformers 企业级 AI 助手部署方案 * [docs]: 强调推荐使用 Kimi K2.5 作为核心模型,突出企业级推理能力 * [docs]: 添加 Clawdbot 飞书接入教程链接 * [feat]: improve CLI table display, model verification, and chat experience - Add sequence number (#) column to all model tables by default - Filter kt edit to show only MoE GPU models (exclude AMX) - Extend kt model verify to check *.json and *.py files in addition to weights - Fix re-verification bug where repaired files caused false failures - Suppress tokenizer debug output in kt chat token counting * [fix]: fix cpu cores. --------- Co-authored-by: skqliao <skqliao@gmail.com>
217 lines
6.7 KiB
Python
217 lines
6.7 KiB
Python
"""
|
||
Input validation utilities with retry mechanism.
|
||
|
||
Provides robust input validation with automatic retry on failure.
|
||
"""
|
||
|
||
from typing import Optional, List, Callable, Any
|
||
from rich.console import Console
|
||
from rich.prompt import Prompt
|
||
|
||
console = Console()
|
||
|
||
|
||
def prompt_int_with_retry(
|
||
message: str,
|
||
default: Optional[int] = None,
|
||
min_val: Optional[int] = None,
|
||
max_val: Optional[int] = None,
|
||
validator: Optional[Callable[[int], bool]] = None,
|
||
validator_error_msg: Optional[str] = None,
|
||
) -> int:
|
||
"""Prompt for integer input with validation and retry.
|
||
|
||
Args:
|
||
message: Prompt message
|
||
default: Default value (optional)
|
||
min_val: Minimum allowed value (optional)
|
||
max_val: Maximum allowed value (optional)
|
||
validator: Custom validation function (optional)
|
||
validator_error_msg: Error message for custom validator (optional)
|
||
|
||
Returns:
|
||
Validated integer value
|
||
"""
|
||
while True:
|
||
# Build prompt with default
|
||
if default is not None:
|
||
prompt_text = f"{message} [{default}]"
|
||
else:
|
||
prompt_text = message
|
||
|
||
# Get input
|
||
user_input = Prompt.ask(prompt_text, default=str(default) if default is not None else None)
|
||
|
||
# Try to parse as integer
|
||
try:
|
||
value = int(user_input)
|
||
except ValueError:
|
||
console.print(f"[red]✗ Invalid input. Please enter a valid integer.[/red]")
|
||
console.print()
|
||
continue
|
||
|
||
# Validate range
|
||
if min_val is not None and value < min_val:
|
||
console.print(f"[red]✗ Value must be at least {min_val}[/red]")
|
||
console.print()
|
||
continue
|
||
|
||
if max_val is not None and value > max_val:
|
||
console.print(f"[red]✗ Value must be at most {max_val}[/red]")
|
||
console.print()
|
||
continue
|
||
|
||
# Custom validation
|
||
if validator is not None:
|
||
if not validator(value):
|
||
error_msg = validator_error_msg or "Invalid value"
|
||
console.print(f"[red]✗ {error_msg}[/red]")
|
||
console.print()
|
||
continue
|
||
|
||
# All validations passed
|
||
return value
|
||
|
||
|
||
def prompt_float_with_retry(
|
||
message: str,
|
||
default: Optional[float] = None,
|
||
min_val: Optional[float] = None,
|
||
max_val: Optional[float] = None,
|
||
) -> float:
|
||
"""Prompt for float input with validation and retry.
|
||
|
||
Args:
|
||
message: Prompt message
|
||
default: Default value (optional)
|
||
min_val: Minimum allowed value (optional)
|
||
max_val: Maximum allowed value (optional)
|
||
|
||
Returns:
|
||
Validated float value
|
||
"""
|
||
while True:
|
||
# Build prompt with default
|
||
if default is not None:
|
||
prompt_text = f"{message} [{default}]"
|
||
else:
|
||
prompt_text = message
|
||
|
||
# Get input
|
||
user_input = Prompt.ask(prompt_text, default=str(default) if default is not None else None)
|
||
|
||
# Try to parse as float
|
||
try:
|
||
value = float(user_input)
|
||
except ValueError:
|
||
console.print(f"[red]✗ Invalid input. Please enter a valid number.[/red]")
|
||
console.print()
|
||
continue
|
||
|
||
# Validate range
|
||
if min_val is not None and value < min_val:
|
||
console.print(f"[red]✗ Value must be at least {min_val}[/red]")
|
||
console.print()
|
||
continue
|
||
|
||
if max_val is not None and value > max_val:
|
||
console.print(f"[red]✗ Value must be at most {max_val}[/red]")
|
||
console.print()
|
||
continue
|
||
|
||
# All validations passed
|
||
return value
|
||
|
||
|
||
def prompt_choice_with_retry(
|
||
message: str,
|
||
choices: List[str],
|
||
default: Optional[str] = None,
|
||
) -> str:
|
||
"""Prompt for choice input with validation and retry.
|
||
|
||
Args:
|
||
message: Prompt message
|
||
choices: List of valid choices
|
||
default: Default choice (optional)
|
||
|
||
Returns:
|
||
Selected choice
|
||
"""
|
||
while True:
|
||
# Get input
|
||
user_input = Prompt.ask(message, default=default)
|
||
|
||
# Validate choice
|
||
if user_input not in choices:
|
||
console.print(f"[red]✗ Invalid choice. Please select from: {', '.join(choices)}[/red]")
|
||
console.print()
|
||
continue
|
||
|
||
return user_input
|
||
|
||
|
||
def prompt_int_list_with_retry(
|
||
message: str,
|
||
default: Optional[str] = None,
|
||
min_val: Optional[int] = None,
|
||
max_val: Optional[int] = None,
|
||
validator: Optional[Callable[[List[int]], tuple[bool, Optional[str]]]] = None,
|
||
) -> List[int]:
|
||
"""Prompt for comma-separated integer list with validation and retry.
|
||
|
||
Args:
|
||
message: Prompt message
|
||
default: Default value as string (e.g., "0,1,2,3")
|
||
min_val: Minimum allowed value for each integer (optional)
|
||
max_val: Maximum allowed value for each integer (optional)
|
||
validator: Custom validation function that returns (is_valid, error_message) (optional)
|
||
|
||
Returns:
|
||
List of validated integers
|
||
"""
|
||
while True:
|
||
# Get input
|
||
user_input = Prompt.ask(message, default=default)
|
||
|
||
# Clean input: support Chinese comma and spaces
|
||
user_input_cleaned = user_input.replace(",", ",").replace(" ", "")
|
||
|
||
# Try to parse as integers
|
||
try:
|
||
values = [int(x.strip()) for x in user_input_cleaned.split(",") if x.strip()]
|
||
except ValueError:
|
||
console.print(f"[red]✗ Invalid format. Please enter numbers separated by commas.[/red]")
|
||
console.print()
|
||
continue
|
||
|
||
# Validate each value's range
|
||
invalid_values = []
|
||
for value in values:
|
||
if min_val is not None and value < min_val:
|
||
invalid_values.append(value)
|
||
elif max_val is not None and value > max_val:
|
||
invalid_values.append(value)
|
||
|
||
if invalid_values:
|
||
if min_val is not None and max_val is not None:
|
||
console.print(f"[red]✗ Invalid value(s): {invalid_values}[/red]")
|
||
console.print(f"[yellow]Valid range: {min_val}-{max_val}[/yellow]")
|
||
elif min_val is not None:
|
||
console.print(f"[red]✗ Value(s) must be at least {min_val}: {invalid_values}[/red]")
|
||
elif max_val is not None:
|
||
console.print(f"[red]✗ Value(s) must be at most {max_val}: {invalid_values}[/red]")
|
||
console.print()
|
||
continue
|
||
|
||
# Custom validation
|
||
if validator is not None:
|
||
is_valid, error_msg = validator(values)
|
||
if not is_valid:
|
||
console.print(f"[red]✗ {error_msg}[/red]")
|
||
console.print()
|
||
continue
|
||
|
||
# All validations passed
|
||
return values
|