11from __future__ import annotations
22
33import json
4+ import re
45import sys
56from functools import wraps
67from typing import Any , Callable , Dict , List , Literal , Sequence , TypeVar , Union
78
89import click
10+ from tabulate import tabulate
911
1012from together import Together
1113from together .error import InvalidRequestError
@@ -187,12 +189,18 @@ def create(
187189 availability_zone = availability_zone ,
188190 )
189191 except InvalidRequestError as e :
190- print_api_error (e )
191- if "check the hardware api" in str (e ).lower ():
192+ if (
193+ "check the hardware api" in str (e .args [0 ]).lower ()
194+ or "invalid hardware provided" in str (e .args [0 ]).lower ()
195+ or "the selected configuration" in str (e .args [0 ]).lower ()
196+ ):
197+ click .secho ("Invalid hardware selected." , fg = "red" , err = True )
198+ click .echo ("\n Available hardware options:" )
192199 fetch_and_print_hardware_options (
193200 client = client , model = model , print_json = False , available = True
194201 )
195-
202+ else :
203+ print_api_error (e )
196204 sys .exit (1 )
197205
198206 # Print detailed information to stderr
@@ -259,38 +267,34 @@ def hardware(client: Together, model: str | None, json: bool, available: bool) -
259267 fetch_and_print_hardware_options (client , model , json , available )
260268
261269
262- def format_hardware_table (
270+ def _format_hardware_options (
263271 hardware_options : Sequence [HardwareWithStatus ],
264272 show_availability : bool = True ,
265273) -> None :
266- """Print hardware options in a formatted table."""
274+ """Print hardware options in a formatted table using tabulate ."""
267275 if not hardware_options :
268276 click .echo (" No hardware options found." , err = True )
269277 return
270278
271- # Calculate column widths
272- id_width = max (len (h .id ) for h in hardware_options )
273- id_width = max (id_width , len ("HARDWARE ID" ))
274-
275- # Print header
276- if show_availability :
277- header = f" { 'HARDWARE ID' :<{id_width }} { 'GPU' :<12} { 'COUNT' :<5} { 'MEMORY' :<8} { 'STATUS' :<12} { 'PRICE' :<12} "
278- separator = f" { '-' * id_width } { '-' * 12 } { '-' * 5 } { '-' * 8 } { '-' * 12 } { '-' * 12 } "
279- else :
280- header = f" { 'HARDWARE ID' :<{id_width }} { 'GPU' :<12} { 'COUNT' :<5} { 'MEMORY' :<8} { 'PRICE' :<12} "
281- separator = f" { '-' * id_width } { '-' * 12 } { '-' * 5 } { '-' * 8 } { '-' * 12 } "
279+ display_list : List [Dict [str , Any ]] = []
282280
283- click .echo (header , err = True )
284- click .echo (separator , err = True )
285-
286- # Print each hardware option
287281 for hw in hardware_options :
288- gpu_type = hw .specs .gpu_type if hw .specs else "N/A"
289- gpu_count = str (hw .specs .gpu_count ) if hw .specs else "N/A"
290- gpu_memory = f"{ int (hw .specs .gpu_memory )} GB" if hw .specs else "N/A"
291- price = f"${ hw .pricing .cents_per_minute / 100 :.2f} /min" if hw .pricing else "N/A"
282+ data : Dict [str , Any ] = {
283+ "Hardware ID" : hw .id ,
284+ "GPU" : (
285+ re .sub (r"\-\d+[a-zA-Z][a-zA-Z]$" , "" , hw .specs .gpu_type )
286+ if hw .specs and hw .specs .gpu_type
287+ else "N/A"
288+ ),
289+ "Memory" : f"{ int (hw .specs .gpu_memory )} GB" if hw .specs else "N/A" ,
290+ "Count" : hw .specs .gpu_count if hw .specs else "N/A" ,
291+ "Price (per minute)" : (
292+ f"${ hw .pricing .cents_per_minute / 100 :.2f} " if hw .pricing else "N/A"
293+ ),
294+ }
292295
293296 if show_availability :
297+ status_display = "—"
294298 if hw .availability :
295299 status = hw .availability .status
296300 # Add visual indicators for status
@@ -300,14 +304,11 @@ def format_hardware_table(
300304 status_display = click .style ("✗ unavailable" , fg = "red" )
301305 else : # insufficient
302306 status_display = click .style ("⚠ insufficient" , fg = "yellow" )
303- else :
304- status_display = "—"
307+ data ["Availability" ] = status_display
305308
306- row = f" { hw .id :<{id_width }} { gpu_type :<12} { gpu_count :<5} { gpu_memory :<8} { status_display :<23} { price :<12} "
307- else :
308- row = f" { hw .id :<{id_width }} { gpu_type :<12} { gpu_count :<5} { gpu_memory :<8} { price :<12} "
309+ display_list .append (data )
309310
310- click .echo (row , err = True )
311+ click .echo (tabulate ( display_list , headers = "keys" , numalign = "left" ) )
311312
312313
313314def fetch_and_print_hardware_options (
@@ -323,9 +324,17 @@ def fetch_and_print_hardware_options(
323324 if hardware .availability is not None
324325 and hardware .availability .status == "available"
325326 ]
326- message = f"Available hardware options for model '{ model } ':" if model else "Available hardware options:"
327+ message = (
328+ f"Available hardware options for model '{ model } ':"
329+ if model
330+ else "Available hardware options:"
331+ )
327332 else :
328- message = f"Hardware options for model '{ model } ':" if model else "All hardware options:"
333+ message = (
334+ f"Hardware options for model '{ model } ':"
335+ if model
336+ else "All hardware options:"
337+ )
329338
330339 click .echo (message , err = True )
331340 click .echo ("" , err = True )
@@ -336,7 +345,7 @@ def fetch_and_print_hardware_options(
336345 else :
337346 # Show availability column only when model is specified (availability info is only returned with model filter)
338347 show_availability = model is not None
339- format_hardware_table (hardware_options , show_availability = show_availability )
348+ _format_hardware_options (hardware_options , show_availability = show_availability )
340349
341350
342351@endpoints .command ()
0 commit comments