|
3 | 3 | import subprocess |
4 | 4 | import re |
5 | 5 | import json |
| 6 | +import shlex |
6 | 7 | from typing import Dict, Any, Set, Optional |
7 | 8 | from backend.logging_config import get_logger |
8 | 9 |
|
|
12 | 13 | _supported_flags_cache: Dict[str, Set[str]] = {} |
13 | 14 |
|
14 | 15 |
|
| 16 | +def _quote_arg_if_needed(arg: str) -> str: |
| 17 | + """ |
| 18 | + Quote an argument if it contains spaces, special characters, or is a complex value. |
| 19 | + |
| 20 | + Args: |
| 21 | + arg: The argument value to potentially quote |
| 22 | + |
| 23 | + Returns: |
| 24 | + The argument, quoted if necessary |
| 25 | + """ |
| 26 | + if not isinstance(arg, str): |
| 27 | + arg = str(arg) |
| 28 | + |
| 29 | + # Always quote if it contains spaces, quotes, or shell special characters |
| 30 | + if any(char in arg for char in [' ', '\t', '\n', '|', '&', ';', '(', ')', '<', '>', '*', '?', '[', ']', '{', '}', '$', '`', '\\']): |
| 31 | + # Use shlex.quote for proper shell escaping |
| 32 | + return shlex.quote(arg) |
| 33 | + |
| 34 | + # Quote regex patterns (common in --override-tensor) |
| 35 | + if re.search(r'[.*+?^${}|()\[\]\\]', arg): |
| 36 | + return shlex.quote(arg) |
| 37 | + |
| 38 | + # Quote if it starts with a dash (could be confused with a flag) |
| 39 | + if arg.startswith('-') and not arg.startswith('--'): |
| 40 | + return shlex.quote(arg) |
| 41 | + |
| 42 | + return arg |
| 43 | + |
| 44 | + |
15 | 45 | def _coerce_model_config(config_value: Optional[Any]) -> Dict[str, Any]: |
16 | 46 | if not config_value: |
17 | 47 | return {} |
@@ -242,7 +272,9 @@ def generate_llama_swap_config(models: Dict[str, Dict[str, Any]], llama_server_p |
242 | 272 | logger.debug(f"Model {proxy_model_name}: jinja={config.get('jinja')} (type: {type(config.get('jinja'))})") |
243 | 273 |
|
244 | 274 | # Build llama.cpp command arguments |
245 | | - cmd_args = [llama_server_path, "--model", model_path, "--port", "${PORT}"] |
| 275 | + # Quote model path if it contains spaces or special characters |
| 276 | + quoted_model_path = _quote_arg_if_needed(model_path) |
| 277 | + cmd_args = [llama_server_path, "--model", quoted_model_path, "--port", "${PORT}"] |
246 | 278 |
|
247 | 279 | # Default values to skip (these cause errors if flag isn't supported) |
248 | 280 | default_values = { |
@@ -374,7 +406,11 @@ def generate_llama_swap_config(models: Dict[str, Dict[str, Any]], llama_server_p |
374 | 406 | logger.debug(f"Skipping duplicate --temp flag for {proxy_model_name}") |
375 | 407 | continue |
376 | 408 | temp_flag_added = True |
377 | | - cmd_args.extend([flag_options[0], str(value)]) |
| 409 | + # Quote complex values (grammar, json_schema, yaml, etc.) |
| 410 | + value_str = str(value) |
| 411 | + if key in ("grammar", "json_schema", "yaml") or flag_options[0] in ("--grammar", "--json-schema", "--yaml"): |
| 412 | + value_str = _quote_arg_if_needed(value_str) |
| 413 | + cmd_args.extend([flag_options[0], value_str]) |
378 | 414 |
|
379 | 415 | # Special handling: MoE offload flags |
380 | 416 | # Check for direct cpu_moe or n_cpu_moe parameters first (these take precedence) |
@@ -442,7 +478,9 @@ def generate_llama_swap_config(models: Dict[str, Dict[str, Any]], llama_server_p |
442 | 478 | llama_cpp_config = model_data["config"] |
443 | 479 |
|
444 | 480 | # Build llama.cpp command arguments (using full path to llama-server) |
445 | | - cmd_args = [llama_server_path, "--model", model_path, "--port", "${PORT}"] |
| 481 | + # Quote model path if it contains spaces or special characters |
| 482 | + quoted_model_path = _quote_arg_if_needed(model_path) |
| 483 | + cmd_args = [llama_server_path, "--model", quoted_model_path, "--port", "${PORT}"] |
446 | 484 |
|
447 | 485 | # Default values to skip (these cause errors if flag isn't supported) |
448 | 486 | default_values = { |
@@ -569,7 +607,11 @@ def generate_llama_swap_config(models: Dict[str, Dict[str, Any]], llama_server_p |
569 | 607 | logger.debug(f"Skipping duplicate --temp flag") |
570 | 608 | continue |
571 | 609 | temp_flag_added = True |
572 | | - cmd_args.extend([flag_options[0], str(value)]) |
| 610 | + # Quote complex values (grammar, json_schema, yaml, etc.) |
| 611 | + value_str = str(value) |
| 612 | + if key in ("grammar", "json_schema", "yaml") or flag_options[0] in ("--grammar", "--json-schema", "--yaml"): |
| 613 | + value_str = _quote_arg_if_needed(value_str) |
| 614 | + cmd_args.extend([flag_options[0], value_str]) |
573 | 615 |
|
574 | 616 | # Special handling: MoE offload flags |
575 | 617 | # Check for direct cpu_moe or n_cpu_moe parameters first (these take precedence) |
|
0 commit comments