yaicli 0.5.6__py3-none-any.whl → 0.5.8__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 +1 -1
- yaicli/chat.py +4 -2
- yaicli/cli.py +1 -1
- yaicli/client.py +7 -1
- yaicli/config.py +17 -17
- yaicli/const.py +16 -16
- yaicli/printer.py +1 -1
- {yaicli-0.5.6.dist-info → yaicli-0.5.8.dist-info}/METADATA +28 -1
- {yaicli-0.5.6.dist-info → yaicli-0.5.8.dist-info}/RECORD +12 -12
- {yaicli-0.5.6.dist-info → yaicli-0.5.8.dist-info}/WHEEL +0 -0
- {yaicli-0.5.6.dist-info → yaicli-0.5.8.dist-info}/entry_points.txt +0 -0
- {yaicli-0.5.6.dist-info → yaicli-0.5.8.dist-info}/licenses/LICENSE +0 -0
pyproject.toml
CHANGED
yaicli/chat.py
CHANGED
@@ -10,8 +10,8 @@ from rich.table import Table
|
|
10
10
|
|
11
11
|
from .config import cfg
|
12
12
|
from .console import YaiConsole, get_console
|
13
|
-
from .schemas import ChatMessage
|
14
13
|
from .exceptions import ChatDeleteError, ChatLoadError, ChatSaveError
|
14
|
+
from .schemas import ChatMessage
|
15
15
|
from .utils import option_callback
|
16
16
|
|
17
17
|
console: YaiConsole = get_console()
|
@@ -68,7 +68,9 @@ class Chat:
|
|
68
68
|
data = json.load(f)
|
69
69
|
self.title = data.get("title", self.title)
|
70
70
|
self.date = data.get("date", self.date)
|
71
|
-
self.history = [
|
71
|
+
self.history = [
|
72
|
+
ChatMessage(role=msg["role"], content=msg["content"]) for msg in data.get("history", [])
|
73
|
+
]
|
72
74
|
return True
|
73
75
|
except (json.JSONDecodeError, OSError) as e:
|
74
76
|
raise ChatLoadError(f"Error loading chat: {e}") from e
|
yaicli/cli.py
CHANGED
@@ -17,7 +17,7 @@ from rich.panel import Panel
|
|
17
17
|
from rich.prompt import Prompt
|
18
18
|
|
19
19
|
from .chat import Chat, FileChatManager, chat_mgr
|
20
|
-
from .client import
|
20
|
+
from .client import ChatMessage, LitellmClient
|
21
21
|
from .config import cfg
|
22
22
|
from .console import get_console
|
23
23
|
from .const import (
|
yaicli/client.py
CHANGED
@@ -11,7 +11,7 @@ from rich.panel import Panel
|
|
11
11
|
|
12
12
|
from .config import cfg
|
13
13
|
from .console import get_console
|
14
|
-
from .schemas import
|
14
|
+
from .schemas import ChatMessage, LLMResponse, ToolCall
|
15
15
|
from .tools import (
|
16
16
|
Function,
|
17
17
|
FunctionName,
|
@@ -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
@@ -1,8 +1,9 @@
|
|
1
1
|
import configparser
|
2
|
+
import json
|
2
3
|
from dataclasses import dataclass
|
3
4
|
from functools import lru_cache
|
4
5
|
from os import getenv
|
5
|
-
from typing import
|
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) ->
|
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
|
-
|
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 =
|
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
|
-
|
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 '{
|
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(
|
161
|
-
|
162
|
-
converted_value = target_type(
|
163
|
-
|
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
@@ -1,4 +1,11 @@
|
|
1
|
-
|
1
|
+
try:
|
2
|
+
from enum import StrEnum
|
3
|
+
except ImportError:
|
4
|
+
from enum import Enum
|
5
|
+
|
6
|
+
class StrEnum(str, Enum):
|
7
|
+
"""Compatible with python below 3.11"""
|
8
|
+
|
2
9
|
from pathlib import Path
|
3
10
|
from tempfile import gettempdir
|
4
11
|
from typing import Any, Literal, Optional
|
@@ -51,6 +58,8 @@ DEFAULT_MAX_HISTORY: int = 500
|
|
51
58
|
DEFAULT_AUTO_SUGGEST: BOOL_STR = "true"
|
52
59
|
DEFAULT_SHOW_REASONING: BOOL_STR = "true"
|
53
60
|
DEFAULT_TIMEOUT: int = 60
|
61
|
+
DEFAULT_EXTRA_HEADERS: str = "{}"
|
62
|
+
DEFAULT_EXTRA_BODY: str = "{}"
|
54
63
|
DEFAULT_INTERACTIVE_ROUND: int = 25
|
55
64
|
DEFAULT_CHAT_HISTORY_DIR: Path = Path(gettempdir()) / "yaicli/chats"
|
56
65
|
DEFAULT_MAX_SAVED_CHATS = 20
|
@@ -61,21 +70,6 @@ DEFAULT_SHOW_FUNCTION_OUTPUT: BOOL_STR = "true"
|
|
61
70
|
DEFAULT_REASONING_EFFORT: Optional[Literal["low", "high", "medium"]] = None
|
62
71
|
|
63
72
|
|
64
|
-
class EventTypeEnum(StrEnum):
|
65
|
-
"""Enumeration of possible event types from the SSE stream."""
|
66
|
-
|
67
|
-
ERROR = "error"
|
68
|
-
REASONING = "reasoning"
|
69
|
-
REASONING_END = "reasoning_end"
|
70
|
-
CONTENT = "content"
|
71
|
-
FINISH = "finish"
|
72
|
-
TOOL_CALL_START = "tool_call_start"
|
73
|
-
TOOL_CALL_DELTA = "tool_call_delta"
|
74
|
-
TOOL_CALL_END = "tool_call_end"
|
75
|
-
TOOL_RESULT = "tool_result"
|
76
|
-
TOOL_CALLS_FINISH = "tool_calls_finish"
|
77
|
-
|
78
|
-
|
79
73
|
SHELL_PROMPT = """You are YAICLI, a shell command generator.
|
80
74
|
The context conversation may contain other types of messages,
|
81
75
|
but you should only respond with a single valid {_shell} shell command for {_os}.
|
@@ -130,6 +124,8 @@ DEFAULT_CONFIG_MAP = {
|
|
130
124
|
"TOP_P": {"value": DEFAULT_TOP_P, "env_key": "YAI_TOP_P", "type": float},
|
131
125
|
"MAX_TOKENS": {"value": DEFAULT_MAX_TOKENS, "env_key": "YAI_MAX_TOKENS", "type": int},
|
132
126
|
"TIMEOUT": {"value": DEFAULT_TIMEOUT, "env_key": "YAI_TIMEOUT", "type": int},
|
127
|
+
"EXTRA_HEADERS": {"value": DEFAULT_EXTRA_HEADERS, "env_key": "YAI_EXTRA_HEADERS", "type": dict},
|
128
|
+
"EXTRA_BODY": {"value": DEFAULT_EXTRA_BODY, "env_key": "YAI_EXTRA_BODY", "type": dict},
|
133
129
|
"REASONING_EFFORT": {"value": DEFAULT_REASONING_EFFORT, "env_key": "YAI_REASONING_EFFORT", "type": str},
|
134
130
|
"INTERACTIVE_ROUND": {
|
135
131
|
"value": DEFAULT_INTERACTIVE_ROUND,
|
@@ -174,6 +170,10 @@ TEMPERATURE={DEFAULT_CONFIG_MAP["TEMPERATURE"]["value"]}
|
|
174
170
|
TOP_P={DEFAULT_CONFIG_MAP["TOP_P"]["value"]}
|
175
171
|
MAX_TOKENS={DEFAULT_CONFIG_MAP["MAX_TOKENS"]["value"]}
|
176
172
|
TIMEOUT={DEFAULT_CONFIG_MAP["TIMEOUT"]["value"]}
|
173
|
+
# json string
|
174
|
+
EXTRA_HEADERS=
|
175
|
+
# json string
|
176
|
+
EXTRA_BODY=
|
177
177
|
REASONING_EFFORT=
|
178
178
|
|
179
179
|
# Interactive mode parameters
|
yaicli/printer.py
CHANGED
@@ -8,8 +8,8 @@ from rich.live import Live
|
|
8
8
|
from .client import RefreshLive
|
9
9
|
from .config import Config, get_config
|
10
10
|
from .console import YaiConsole, get_console
|
11
|
-
from .schemas import ChatMessage
|
12
11
|
from .render import Markdown, plain_formatter
|
12
|
+
from .schemas import ChatMessage
|
13
13
|
|
14
14
|
if TYPE_CHECKING:
|
15
15
|
from .schemas import LLMResponse
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: yaicli
|
3
|
-
Version: 0.5.
|
3
|
+
Version: 0.5.8
|
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
|

|
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,15 +1,15 @@
|
|
1
|
-
pyproject.toml,sha256=
|
1
|
+
pyproject.toml,sha256=uIesu0u5n1nVFJ4MJ4ZO4G9hr4EGlwcysyKybIG3PyM,1963
|
2
2
|
yaicli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
|
-
yaicli/chat.py,sha256=
|
4
|
-
yaicli/cli.py,sha256=
|
5
|
-
yaicli/client.py,sha256=
|
6
|
-
yaicli/config.py,sha256=
|
3
|
+
yaicli/chat.py,sha256=_emvZEdgMBth2nQGaNWPf0P45oW2k3bpuIwqsxFcM5A,13676
|
4
|
+
yaicli/cli.py,sha256=u4rq04CdLiL-IBzQ0IimPyFAh9XZg9YUTuvM4CxE3pE,22985
|
5
|
+
yaicli/client.py,sha256=kdEI4HP1zZQHUf-7kNbE9ItpyRN8zcAGyKeBSb1R4Ro,16565
|
6
|
+
yaicli/config.py,sha256=HrWYcelLXE61XX719eVcuuo3292xxf1BNQznWdvjQFQ,6535
|
7
7
|
yaicli/console.py,sha256=vARPJd-3lafutsQWrGntQVjLrYqaJD3qisN82pmuhjU,1973
|
8
|
-
yaicli/const.py,sha256=
|
8
|
+
yaicli/const.py,sha256=mt_6m2jo5-tHEmk3DxKCEc5ek9DcyQGHFAn_HEeMP3k,8155
|
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
|
12
|
-
yaicli/printer.py,sha256=
|
12
|
+
yaicli/printer.py,sha256=J-QPrb0q4Zrx18vwBGIBDQk7RzT4wXJPwdJ9oXLoEVg,8369
|
13
13
|
yaicli/render.py,sha256=k8o2P8fI44PJlyQbs7gmMiu2x2prwajdWn5JIt15BIA,505
|
14
14
|
yaicli/role.py,sha256=PfwiVJIlzg7EzlvMM-kIy6vBK0d5d_J4M1I_fIZGnWk,7399
|
15
15
|
yaicli/schemas.py,sha256=PiuSY7ORZaA4OL_tYm0inwqirHp5M-F3zcCipLwsH9E,571
|
@@ -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.
|
21
|
-
yaicli-0.5.
|
22
|
-
yaicli-0.5.
|
23
|
-
yaicli-0.5.
|
24
|
-
yaicli-0.5.
|
20
|
+
yaicli-0.5.8.dist-info/METADATA,sha256=BRZEKpxpZ6kPABv88FGl4ipt8glgJdAn-9ZxiDLAAeo,50349
|
21
|
+
yaicli-0.5.8.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
22
|
+
yaicli-0.5.8.dist-info/entry_points.txt,sha256=iYVyQP0PJIm9tQnlQheqT435kK_xdGoi5j9aswGV9hA,66
|
23
|
+
yaicli-0.5.8.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
24
|
+
yaicli-0.5.8.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|