77"""
88import sys
99import warnings
10+ from typing import Optional
1011
1112# Suppress urllib3 NotOpenSSLWarning on systems with LibreSSL
1213warnings .filterwarnings (
1819import typer # noqa: E402
1920
2021import dashscope # noqa: E402
22+ from dashscope .cli .common import err_console # noqa: E402
23+ from dashscope .common .error import AuthenticationError # noqa: E402
2124from dashscope .cli import ( # noqa: E402
25+ application ,
26+ assistant_files ,
27+ assistants ,
28+ code_generation ,
2229 deployments ,
30+ embeddings ,
2331 files ,
2432 fine_tunes ,
2533 generation ,
34+ image_generation ,
35+ image_synthesis ,
36+ messages ,
37+ models ,
38+ multimodal_conversation ,
39+ multimodal_embedding ,
2640 oss ,
41+ rerank ,
42+ runs ,
43+ speech_synthesis ,
44+ steps ,
45+ threads ,
46+ tokenization ,
47+ transcription ,
48+ understanding ,
49+ video_synthesis ,
2750)
2851
2952
6992 "--page_size" : "--page-size" ,
7093}
7194
95+ _TOP_LEVEL_COMMANDS = {
96+ "generation" ,
97+ "ft" ,
98+ "fine-tunes" ,
99+ "files" ,
100+ "deployments" ,
101+ "oss" ,
102+ "rerank" ,
103+ "embeddings" ,
104+ "tokenization" ,
105+ "models" ,
106+ "understanding" ,
107+ "application" ,
108+ "code-generation" ,
109+ "image-synthesis" ,
110+ "video-synthesis" ,
111+ "image-generation" ,
112+ "multimodal-conversation" ,
113+ "multimodal-embedding" ,
114+ "transcription" ,
115+ "speech-synthesis" ,
116+ "assistants" ,
117+ "assistant-files" ,
118+ "threads" ,
119+ "messages" ,
120+ "runs" ,
121+ "steps" ,
122+ "rl" ,
123+ "agentic-rl" ,
124+ }
125+
126+
127+ _COMMANDS_WITH_LOCAL_API_KEY = {"oss" , "rl" , "agentic-rl" }
128+ _LEGACY_COMMANDS_WITH_SIZE_OPTION = {
129+ "files.list" ,
130+ "fine_tunes.list" ,
131+ "deployments.list" ,
132+ }
133+
134+
135+ def _translate_param_name (arg ):
136+ """Translate legacy underscore option names to Typer hyphen names."""
137+ if arg in _PARAM_MAP :
138+ return _PARAM_MAP [arg ]
139+ if arg .startswith ("--" ) and "=" in arg :
140+ option_name , option_value = arg .split ("=" , 1 )
141+ if option_name in _PARAM_MAP :
142+ return f"{ _PARAM_MAP [option_name ]} ={ option_value } "
143+ return arg
144+
72145
73146def _translate_legacy_args (argv ):
74147 """Translate legacy argparse command format to Typer format.
@@ -81,59 +154,95 @@ def _translate_legacy_args(argv):
81154 if len (argv ) < 2 :
82155 return argv
83156
84- new_argv = [argv [0 ]] # Keep program name
85- i = 1
86-
87- # Check if first arg is a legacy command
88- if i < len (argv ) and argv [i ] in _COMMAND_MAP :
89- # Split "fine_tunes.call" into ["fine-tunes", "call"]
90- new_cmd = _COMMAND_MAP [argv [i ]].split ()
91- new_argv .extend (new_cmd )
92- i += 1
157+ is_legacy_command = argv [1 ] in _COMMAND_MAP
158+ if not is_legacy_command :
159+ return [argv [0 ]] + [_translate_param_name (arg ) for arg in argv [1 :]]
93160
94- # Process remaining args
161+ command_args = _COMMAND_MAP [argv [1 ]].split ()
162+ top_level_args = []
163+ translated_args = []
164+ i = 2
95165 while i < len (argv ):
96166 arg = argv [i ]
97-
98- # Translate parameter names
99- if arg in _PARAM_MAP :
100- new_argv .append (_PARAM_MAP [arg ])
101- elif arg .startswith ("--" ) and "=" in arg :
102- opt , val = arg .split ("=" , 1 )
103- if opt in _PARAM_MAP :
104- new_argv .append (f"{ _PARAM_MAP [opt ]} ={ val } " )
167+ translated_arg = _translate_param_name (arg )
168+ if argv [1 ] in _LEGACY_COMMANDS_WITH_SIZE_OPTION :
169+ if translated_arg == "--page-size" :
170+ translated_arg = "--size"
171+ elif translated_arg .startswith ("--page-size=" ):
172+ translated_arg = translated_arg .replace (
173+ "--page-size=" ,
174+ "--size=" ,
175+ 1 ,
176+ )
177+
178+ if translated_arg == "--api-key" :
179+ top_level_args .append ("--api-key" )
180+ if i + 1 < len (argv ):
181+ top_level_args .append (argv [i + 1 ])
182+ i += 2
105183 else :
106- new_argv .append (arg )
107- else :
108- new_argv .append (arg )
184+ i += 1
185+ continue
109186
187+ if translated_arg .startswith ("--api-key=" ):
188+ top_level_args .append (translated_arg )
189+ i += 1
190+ continue
191+
192+ translated_args .append (translated_arg )
110193 i += 1
111194
112- return new_argv
195+ return [argv [0 ]] + top_level_args + command_args + translated_args
196+
197+
198+ def _exit_missing_api_key_value (option_name ):
199+ err_console .print (
200+ f"[red]Error:[/red] Option '{ option_name } ' requires an argument." ,
201+ )
202+ sys .exit (2 )
113203
114204
115205def _extract_global_api_key (argv ):
116206 """Extract global -k/--api-key from argv and set dashscope.api_key.
117207
118- Returns modified argv with api-key args removed.
208+ For backward compatibility, global api-key can appear before or after most
209+ command names. Commands that define their own local api-key option are left
210+ untouched once their command name has been reached.
119211 """
120212 new_argv = []
121213 i = 0
214+ current_command = None
122215 while i < len (argv ):
123216 arg = argv [i ]
124217
125- # Check for -k or --api-key
218+ if i > 0 and current_command is None and arg in _TOP_LEVEL_COMMANDS :
219+ current_command = arg
220+
221+ should_parse_global_api_key = (
222+ current_command not in _COMMANDS_WITH_LOCAL_API_KEY
223+ )
224+
126225 if arg in ("-k" , "--api-key" ):
127- # Next arg should be the key value
128- if i + 1 < len (argv ):
129- dashscope .api_key = argv [i + 1 ]
130- i += 2 # Skip both -k and the value
226+ if i + 1 >= len (argv ):
227+ _exit_missing_api_key_value (arg )
228+ next_arg = argv [i + 1 ]
229+ if not next_arg or next_arg .startswith ("-" ):
230+ _exit_missing_api_key_value (arg )
231+ if should_parse_global_api_key :
232+ if next_arg in _TOP_LEVEL_COMMANDS :
233+ _exit_missing_api_key_value (arg )
234+ dashscope .api_key = next_arg
235+ i += 2
236+ continue
237+
238+ if arg .startswith ("--api-key=" ):
239+ api_key = arg .split ("=" , 1 )[1 ]
240+ if not api_key :
241+ _exit_missing_api_key_value ("--api-key" )
242+ if should_parse_global_api_key :
243+ dashscope .api_key = api_key
244+ i += 1
131245 continue
132- elif arg .startswith ("--api-key=" ):
133- # Handle --api-key=value format
134- dashscope .api_key = arg .split ("=" , 1 )[1 ]
135- i += 1
136- continue
137246
138247 new_argv .append (arg )
139248 i += 1
@@ -153,13 +262,48 @@ def _extract_global_api_key(argv):
153262 rich_markup_mode = "rich" ,
154263)
155264
265+
266+ @app .callback ()
267+ def callback (
268+ api_key : Optional [str ] = typer .Option (
269+ None ,
270+ "-k" ,
271+ "--api-key" ,
272+ help = "DashScope API Key." ,
273+ ),
274+ ):
275+ """Configure global CLI options."""
276+ if api_key :
277+ dashscope .api_key = api_key
278+
279+
156280# Register sub-command groups
157281app .add_typer (generation .app )
158282app .add_typer (fine_tunes .app , name = "ft" )
159283app .add_typer (fine_tunes .app , name = "fine-tunes" , hidden = True )
160284app .add_typer (files .app )
161285app .add_typer (deployments .app )
162286app .add_typer (oss .app )
287+ app .add_typer (rerank .app )
288+ app .add_typer (embeddings .app )
289+ app .add_typer (tokenization .app )
290+ app .add_typer (models .app )
291+ app .add_typer (understanding .app )
292+ app .add_typer (application .app )
293+ app .add_typer (code_generation .app )
294+ app .add_typer (image_synthesis .app )
295+ app .add_typer (video_synthesis .app )
296+ app .add_typer (image_generation .app )
297+ app .add_typer (multimodal_conversation .app )
298+ app .add_typer (multimodal_embedding .app )
299+ app .add_typer (transcription .app )
300+ app .add_typer (speech_synthesis .app )
301+ app .add_typer (assistants .app )
302+ app .add_typer (assistant_files .app )
303+ app .add_typer (threads .app )
304+ app .add_typer (messages .app )
305+ app .add_typer (runs .app )
306+ app .add_typer (steps .app )
163307
164308
165309def _register_rl_app ():
@@ -176,24 +320,41 @@ def _register_rl_app():
176320 name = "rl" ,
177321 help = "🚀 Agentic RL fine-tuning commands" ,
178322 )
179- except ImportError :
180- pass
181- except Exception :
182- pass
323+ app .add_typer (
324+ rl_app ,
325+ name = "agentic-rl" ,
326+ help = "🚀 Agentic RL fine-tuning commands" ,
327+ hidden = True ,
328+ )
329+ except ImportError as exception :
330+ err_console .print (
331+ "[yellow]Warning:[/yellow] Failed to register rl command: "
332+ f"{ exception } " ,
333+ )
334+ except Exception as exception :
335+ err_console .print (
336+ "[yellow]Warning:[/yellow] Failed to register rl command: "
337+ f"{ exception } " ,
338+ )
183339
184340
185341_register_rl_app ()
186342
187343
188344def main ():
189345 """Entry point for the ``dashscope`` console script."""
190- # Extract global api-key parameter FIRST
191- argv = _extract_global_api_key (sys .argv )
346+ # Translate legacy command format first so legacy --api_key can be treated
347+ # as the global --api-key option.
348+ argv = _translate_legacy_args (sys .argv )
192349
193- # Then translate legacy command format
194- argv = _translate_legacy_args (argv )
350+ # Extract global api-key parameter after legacy argument normalization.
351+ argv = _extract_global_api_key (argv )
195352
196353 # Update sys.argv for Typer
197354 sys .argv = argv
198355
199- app ()
356+ try :
357+ app ()
358+ except AuthenticationError as exception :
359+ err_console .print (f"[red]Error:[/red] { exception } " )
360+ sys .exit (1 )
0 commit comments