yaicli 0.5.6__py3-none-any.whl → 0.5.7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
pyproject.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "yaicli"
3
- version = "0.5.6"
3
+ version = "0.5.7"
4
4
  description = "A simple CLI tool to interact with LLM"
5
5
  authors = [{ name = "belingud", email = "im.victor@qq.com" }]
6
6
  readme = "README.md"
yaicli/client.py CHANGED
@@ -46,6 +46,8 @@ class LitellmClient:
46
46
  def __post_init__(self) -> None:
47
47
  """Initialize OpenAI client"""
48
48
  self.pre_tool_call_id = None
49
+ if cfg["PROVIDER"] == "openrouter":
50
+ cfg["EXTRA_HEADERS"].update({"X-Title": "Yaicli", "HTTP-Referer": "https://github.com/belingud/yaicli"})
49
51
 
50
52
  def _convert_messages(self, messages: List[ChatMessage]) -> List[Dict[str, Any]]:
51
53
  """Convert message format to OpenAI API required format"""
@@ -135,6 +137,10 @@ class LitellmClient:
135
137
  }
136
138
 
137
139
  # Add optional parameters
140
+ if cfg["EXTRA_HEADERS"]:
141
+ params["extra_headers"] = cfg["EXTRA_HEADERS"]
142
+ if cfg["EXTRA_BODY"]:
143
+ params["extra_body"] = cfg["EXTRA_BODY"]
138
144
  if cfg["ENABLE_FUNCTIONS"]:
139
145
  params["tools"] = self._convert_functions(list_functions())
140
146
  params["tool_choice"] = "auto"
yaicli/config.py CHANGED
@@ -2,7 +2,8 @@ import configparser
2
2
  from dataclasses import dataclass
3
3
  from functools import lru_cache
4
4
  from os import getenv
5
- from typing import Any, Optional
5
+ import json
6
+ from typing import Optional
6
7
 
7
8
  from rich import get_console
8
9
  from rich.console import Console
@@ -70,17 +71,12 @@ class Config(dict):
70
71
  self._load_from_env()
71
72
  self._apply_type_conversion()
72
73
 
73
- def _load_defaults(self) -> dict[str, Any]:
74
- """Load default configuration values as strings.
75
-
76
- Returns:
77
- Dictionary with default configuration values
78
- """
74
+ def _load_defaults(self) -> None:
75
+ """Load default configuration values as strings."""
79
76
  defaults = {k: v["value"] for k, v in DEFAULT_CONFIG_MAP.items()}
80
77
  self.update(defaults)
81
- return defaults
82
78
 
83
- def _ensure_version_updated_config_keys(self):
79
+ def _ensure_version_updated_config_keys(self) -> None:
84
80
  """Ensure configuration keys added in version updates exist in the config file.
85
81
  Appends missing keys to the config file if they don't exist.
86
82
  """
@@ -133,7 +129,7 @@ class Config(dict):
133
129
  Updates the configuration dictionary in-place with properly typed values.
134
130
  Falls back to default values if conversion fails.
135
131
  """
136
- default_values_str = {k: v["value"] for k, v in DEFAULT_CONFIG_MAP.items()}
132
+ default_values_map = {k: v["value"] for k, v in DEFAULT_CONFIG_MAP.items()}
137
133
 
138
134
  for key, config_info in DEFAULT_CONFIG_MAP.items():
139
135
  target_type = config_info["type"]
@@ -142,25 +138,29 @@ class Config(dict):
142
138
 
143
139
  try:
144
140
  if raw_value is None:
145
- raw_value = default_values_str.get(key, "")
141
+ raw_value = default_values_map.get(key, "")
146
142
  if target_type is bool:
147
143
  converted_value = str2bool(raw_value)
148
144
  elif target_type in (int, float, str):
149
145
  converted_value = target_type(raw_value)
150
- except (ValueError, TypeError) as e:
146
+ elif target_type is dict and raw_value:
147
+ converted_value = json.loads(raw_value)
148
+ except (ValueError, TypeError, json.JSONDecodeError) as e:
151
149
  self.console.print(
152
150
  f"[yellow]Warning:[/] Invalid value '{raw_value}' for '{key}'. "
153
- f"Expected type '{target_type.__name__}'. Using default value '{default_values_str[key]}'. Error: {e}",
151
+ f"Expected type '{target_type.__name__}'. Using default value '{default_values_map[key]}'. Error: {e}",
154
152
  style="dim",
155
153
  justify=self["JUSTIFY"],
156
154
  )
157
155
  # Fallback to default string value if conversion fails
158
156
  try:
159
157
  if target_type is bool:
160
- converted_value = str2bool(default_values_str[key])
161
- else:
162
- converted_value = target_type(default_values_str[key])
163
- except (ValueError, TypeError):
158
+ converted_value = str2bool(default_values_map[key])
159
+ elif target_type in (int, float, str):
160
+ converted_value = target_type(default_values_map[key])
161
+ elif target_type is dict:
162
+ converted_value = json.loads(default_values_map[key])
163
+ except (ValueError, TypeError, json.JSONDecodeError):
164
164
  # If default also fails (unlikely), keep the raw merged value or a sensible default
165
165
  self.console.print(
166
166
  f"[red]Error:[/red] Could not convert default value for '{key}'. Using raw value.",
yaicli/const.py CHANGED
@@ -51,6 +51,8 @@ DEFAULT_MAX_HISTORY: int = 500
51
51
  DEFAULT_AUTO_SUGGEST: BOOL_STR = "true"
52
52
  DEFAULT_SHOW_REASONING: BOOL_STR = "true"
53
53
  DEFAULT_TIMEOUT: int = 60
54
+ DEFAULT_EXTRA_HEADERS: str = "{}"
55
+ DEFAULT_EXTRA_BODY: str = "{}"
54
56
  DEFAULT_INTERACTIVE_ROUND: int = 25
55
57
  DEFAULT_CHAT_HISTORY_DIR: Path = Path(gettempdir()) / "yaicli/chats"
56
58
  DEFAULT_MAX_SAVED_CHATS = 20
@@ -130,6 +132,8 @@ DEFAULT_CONFIG_MAP = {
130
132
  "TOP_P": {"value": DEFAULT_TOP_P, "env_key": "YAI_TOP_P", "type": float},
131
133
  "MAX_TOKENS": {"value": DEFAULT_MAX_TOKENS, "env_key": "YAI_MAX_TOKENS", "type": int},
132
134
  "TIMEOUT": {"value": DEFAULT_TIMEOUT, "env_key": "YAI_TIMEOUT", "type": int},
135
+ "EXTRA_HEADERS": {"value": DEFAULT_EXTRA_HEADERS, "env_key": "YAI_EXTRA_HEADERS", "type": dict},
136
+ "EXTRA_BODY": {"value": DEFAULT_EXTRA_BODY, "env_key": "YAI_EXTRA_BODY", "type": dict},
133
137
  "REASONING_EFFORT": {"value": DEFAULT_REASONING_EFFORT, "env_key": "YAI_REASONING_EFFORT", "type": str},
134
138
  "INTERACTIVE_ROUND": {
135
139
  "value": DEFAULT_INTERACTIVE_ROUND,
@@ -174,6 +178,10 @@ TEMPERATURE={DEFAULT_CONFIG_MAP["TEMPERATURE"]["value"]}
174
178
  TOP_P={DEFAULT_CONFIG_MAP["TOP_P"]["value"]}
175
179
  MAX_TOKENS={DEFAULT_CONFIG_MAP["MAX_TOKENS"]["value"]}
176
180
  TIMEOUT={DEFAULT_CONFIG_MAP["TIMEOUT"]["value"]}
181
+ # json string
182
+ EXTRA_HEADERS=
183
+ # json string
184
+ EXTRA_BODY=
177
185
  REASONING_EFFORT=
178
186
 
179
187
  # Interactive mode parameters
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: yaicli
3
- Version: 0.5.6
3
+ Version: 0.5.7
4
4
  Summary: A simple CLI tool to interact with LLM
5
5
  Project-URL: Homepage, https://github.com/belingud/yaicli
6
6
  Project-URL: Repository, https://github.com/belingud/yaicli
@@ -392,6 +392,9 @@ SHOW_FUNCTION_OUTPUT=true
392
392
  | `OS_NAME` | Operating system | `auto` | `YAI_OS_NAME` |
393
393
  | `STREAM` | Enable streaming | `true` | `YAI_STREAM` |
394
394
  | `TIMEOUT` | API timeout (seconds) | `60` | `YAI_TIMEOUT` |
395
+ | `EXTRA_HEADERS` | Extra headers | - | `YAI_EXTRA_HEADERS` |
396
+ | `EXTRA_BODY` | Extra body | - | `YAI_EXTRA_BODY` |
397
+ | `REASONING_EFFORT` | Reasoning effort | - | `YAI_REASONING_EFFORT` |
395
398
  | `INTERACTIVE_ROUND` | Interactive mode rounds | `25` | `YAI_INTERACTIVE_ROUND` |
396
399
  | `CODE_THEME` | Syntax highlighting theme | `monokai` | `YAI_CODE_THEME` |
397
400
  | `TEMPERATURE` | Response randomness | `0.7` | `YAI_TEMPERATURE` |
@@ -449,6 +452,28 @@ Browse available themes at: https://pygments.org/styles/
449
452
 
450
453
  ![monokia theme example](artwork/monokia.png)
451
454
 
455
+ ### Extra Headers and Body
456
+
457
+ You can add extra headers and body to the API request by setting `EXTRA_HEADERS` and `EXTRA_BODY` in the config file.
458
+ The value should be valid json string.
459
+
460
+ ```ini
461
+ EXTRA_HEADERS={"X-Extra-Header": "value"}
462
+ EXTRA_BODY={"extra_key": "extra_value"}
463
+ ```
464
+
465
+ Example: If you want to disable Qwen3's thinking behavior, you can add the following to the config file.
466
+
467
+ ```ini
468
+ EXTRA_BODY={"enable_thinking": false}
469
+ ```
470
+
471
+ Or just limit thinking tokens:
472
+
473
+ ```ini
474
+ EXTRA_BODY={"thinking_budget": 4096}
475
+ ```
476
+
452
477
  ## 🚀 Usage
453
478
 
454
479
  ### Quick Start
@@ -904,6 +929,8 @@ YAICLI is designed with a modular architecture that separates concerns and makes
904
929
  | [Typer](https://typer.tiangolo.com/) | Command-line interface with type hints |
905
930
  | [Rich](https://rich.readthedocs.io/) | Terminal formatting and beautiful display |
906
931
  | [prompt_toolkit](https://python-prompt-toolkit.readthedocs.io/) | Interactive input with history and auto-completion |
932
+ | [litellm](https://litellm.ai/) | LLM provider compatibility |
933
+ | [json-repair](https://github.com/mangiucugna/json_repair) | Repair llm function call arguments |
907
934
 
908
935
  ## 👨‍💻 Contributing
909
936
 
@@ -1,11 +1,11 @@
1
- pyproject.toml,sha256=6p1uC3kyuCkJEw7qEwIFWQ2wgiAV9fqpDmGr6u9hsAs,1963
1
+ pyproject.toml,sha256=PCKvDYSn9V2qnxplRgsrbYZfcPjEzj1PvguJVwEtDo0,1963
2
2
  yaicli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  yaicli/chat.py,sha256=DeTmOeBPU-oiOAIaDj2h-auJor0GyVVhrViLYF6zGIM,13638
4
4
  yaicli/cli.py,sha256=1Flt0FgrKzbabjJpZJCcvnUjXaaLwyD2paRyMixEQV0,22985
5
- yaicli/client.py,sha256=fKUDmn9s7tF9Q2wIB8WhbsjFYIpV0E29t_Vw0qVmVbI,16229
6
- yaicli/config.py,sha256=_mp8P6zXyrdp4TzBfHraOCkjv5DMZMOwiEhQnFYWwZA,6321
5
+ yaicli/client.py,sha256=PzfoW0lZ2QHxt2IBLGl-l6CGpq98MsxPiag0QxwRKsw,16565
6
+ yaicli/config.py,sha256=65k7gERkOIJ5q4GN-CU7VYZgupShFgsMHT0eWPtCh_U,6535
7
7
  yaicli/console.py,sha256=vARPJd-3lafutsQWrGntQVjLrYqaJD3qisN82pmuhjU,1973
8
- yaicli/const.py,sha256=FYW8cNqFzZwnYbgr_HXZSzSS8OIU_UsFIn4SZ0zOJ8U,8129
8
+ yaicli/const.py,sha256=47I8I5hkJmLtPAhmesMvg7sydDSG_MB94BoRPtdBrUg,8442
9
9
  yaicli/entry.py,sha256=gKzN8Yar3tpBd2Z2a80gD3k0W4Sf3lL7jdyws-2y-H0,8687
10
10
  yaicli/exceptions.py,sha256=WBYg8OTJJzaj7lt6HE7ZyBoe5T6A3yZRNCRfWd4iN0c,372
11
11
  yaicli/history.py,sha256=s-57X9FMsaQHF7XySq1gGH_jpd_cHHTYafYu2ECuG6M,2472
@@ -17,8 +17,8 @@ yaicli/tools.py,sha256=d-5LXbEB-1Uq5VKSgwlAiNDVOGrHkku2DpmZoorq1zw,3098
17
17
  yaicli/utils.py,sha256=bpo3Xhozpxsaci3FtEIKZ32l4ZdyWMsrHjYGX0tB4J4,4541
18
18
  yaicli/functions/__init__.py,sha256=_FJooQ9GkijG8xLwuU0cr5GBrGnC9Nc6bnCeUjrsT0k,1271
19
19
  yaicli/functions/buildin/execute_shell_command.py,sha256=unl1-F8p6QZajeHdA0u5UpURMJM0WhdWMUWCCCHVRcI,1320
20
- yaicli-0.5.6.dist-info/METADATA,sha256=Vxp8bZrO89wYwWhtXB00o_2XVyF-6UyprAQd8bfVh1g,49194
21
- yaicli-0.5.6.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
22
- yaicli-0.5.6.dist-info/entry_points.txt,sha256=iYVyQP0PJIm9tQnlQheqT435kK_xdGoi5j9aswGV9hA,66
23
- yaicli-0.5.6.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
24
- yaicli-0.5.6.dist-info/RECORD,,
20
+ yaicli-0.5.7.dist-info/METADATA,sha256=VCCVoup7CdV0bKytlc9UtO2tLtAePXbm9faHoYZHRJo,50349
21
+ yaicli-0.5.7.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
22
+ yaicli-0.5.7.dist-info/entry_points.txt,sha256=iYVyQP0PJIm9tQnlQheqT435kK_xdGoi5j9aswGV9hA,66
23
+ yaicli-0.5.7.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
24
+ yaicli-0.5.7.dist-info/RECORD,,
File without changes