yaicli 0.4.0__py3-none-any.whl → 0.5.0__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 +5 -3
- yaicli/chat.py +396 -0
- yaicli/cli.py +250 -251
- yaicli/client.py +385 -0
- yaicli/config.py +31 -24
- yaicli/console.py +2 -2
- yaicli/const.py +28 -2
- yaicli/entry.py +68 -39
- yaicli/exceptions.py +8 -36
- yaicli/functions/__init__.py +39 -0
- yaicli/functions/buildin/execute_shell_command.py +47 -0
- yaicli/printer.py +145 -225
- yaicli/render.py +1 -1
- yaicli/role.py +231 -0
- yaicli/schemas.py +31 -0
- yaicli/tools.py +103 -0
- yaicli/utils.py +5 -2
- {yaicli-0.4.0.dist-info → yaicli-0.5.0.dist-info}/METADATA +164 -87
- yaicli-0.5.0.dist-info/RECORD +24 -0
- {yaicli-0.4.0.dist-info → yaicli-0.5.0.dist-info}/entry_points.txt +1 -1
- yaicli/chat_manager.py +0 -290
- yaicli/providers/__init__.py +0 -34
- yaicli/providers/base.py +0 -51
- yaicli/providers/cohere.py +0 -136
- yaicli/providers/openai.py +0 -176
- yaicli/roles.py +0 -276
- yaicli-0.4.0.dist-info/RECORD +0 -23
- {yaicli-0.4.0.dist-info → yaicli-0.5.0.dist-info}/WHEEL +0 -0
- {yaicli-0.4.0.dist-info → yaicli-0.5.0.dist-info}/licenses/LICENSE +0 -0
yaicli/entry.py
CHANGED
@@ -3,15 +3,15 @@ from typing import Annotated, Any, Optional
|
|
3
3
|
|
4
4
|
import typer
|
5
5
|
|
6
|
-
from
|
7
|
-
from
|
8
|
-
from
|
9
|
-
from
|
10
|
-
from
|
6
|
+
from .chat import FileChatManager
|
7
|
+
from .config import cfg
|
8
|
+
from .const import DEFAULT_CONFIG_INI, DefaultRoleNames, JustifyEnum
|
9
|
+
from .functions import install_functions, print_functions
|
10
|
+
from .role import RoleManager
|
11
11
|
|
12
12
|
app = typer.Typer(
|
13
13
|
name="yaicli",
|
14
|
-
help="YAICLI -
|
14
|
+
help="YAICLI - Your AI assistant in the command line.",
|
15
15
|
context_settings={"help_option_names": ["-h", "--help"]},
|
16
16
|
pretty_exceptions_enable=False, # Let the CLI handle errors gracefully
|
17
17
|
rich_markup_mode="rich", # Render rich text in help messages
|
@@ -73,6 +73,14 @@ def main(
|
|
73
73
|
min=1,
|
74
74
|
callback=override_config,
|
75
75
|
),
|
76
|
+
stream: bool = typer.Option( # noqa: F841
|
77
|
+
cfg["STREAM"],
|
78
|
+
"--stream/--no-stream",
|
79
|
+
help=f"Specify whether to stream the response. [dim](default: {'stream' if cfg['STREAM'] else 'no-stream'})[/dim]",
|
80
|
+
rich_help_panel="LLM Options",
|
81
|
+
show_default=False,
|
82
|
+
callback=override_config,
|
83
|
+
),
|
76
84
|
# ------------------- Role Options -------------------
|
77
85
|
role: str = typer.Option(
|
78
86
|
DefaultRoleNames.DEFAULT,
|
@@ -118,7 +126,7 @@ def main(
|
|
118
126
|
help="Start in interactive chat mode.",
|
119
127
|
rich_help_panel="Chat Options",
|
120
128
|
),
|
121
|
-
# ------------------- Shell Options -------------------
|
129
|
+
# # ------------------- Shell Options -------------------
|
122
130
|
shell: bool = typer.Option(
|
123
131
|
False,
|
124
132
|
"--shell",
|
@@ -126,7 +134,7 @@ def main(
|
|
126
134
|
help="Generate and optionally execute a shell command (non-interactive).",
|
127
135
|
rich_help_panel="Shell Options",
|
128
136
|
),
|
129
|
-
# ------------------- Code Options -------------------
|
137
|
+
# # ------------------- Code Options -------------------
|
130
138
|
code: bool = typer.Option(
|
131
139
|
False,
|
132
140
|
"--code",
|
@@ -157,7 +165,8 @@ def main(
|
|
157
165
|
),
|
158
166
|
show_reasoning: bool = typer.Option( # noqa: F841
|
159
167
|
cfg["SHOW_REASONING"],
|
160
|
-
|
168
|
+
"--show-reasoning/--hide-reasoning",
|
169
|
+
help=f"Show reasoning content from the LLM. [dim](default: {'show' if cfg['SHOW_REASONING'] else 'hide'})[/dim]",
|
161
170
|
rich_help_panel="Other Options",
|
162
171
|
show_default=False,
|
163
172
|
callback=override_config,
|
@@ -170,6 +179,37 @@ def main(
|
|
170
179
|
rich_help_panel="Other Options",
|
171
180
|
callback=override_config,
|
172
181
|
),
|
182
|
+
# ------------------- Function Options -------------------
|
183
|
+
install_functions: bool = typer.Option( # noqa: F841
|
184
|
+
False,
|
185
|
+
"--install-functions",
|
186
|
+
help="Install default functions.",
|
187
|
+
rich_help_panel="Function Options",
|
188
|
+
callback=install_functions,
|
189
|
+
),
|
190
|
+
list_functions: bool = typer.Option( # noqa: F841
|
191
|
+
False,
|
192
|
+
"--list-functions",
|
193
|
+
help="List all available functions.",
|
194
|
+
rich_help_panel="Function Options",
|
195
|
+
callback=print_functions,
|
196
|
+
),
|
197
|
+
enable_functions: bool = typer.Option( # noqa: F841
|
198
|
+
cfg["ENABLE_FUNCTIONS"],
|
199
|
+
"--enable-functions/--disable-functions",
|
200
|
+
help=f"Enable/disable function calling in API requests [dim](default: {'enabled' if cfg['ENABLE_FUNCTIONS'] else 'disabled'})[/dim]",
|
201
|
+
rich_help_panel="Function Options",
|
202
|
+
show_default=False,
|
203
|
+
callback=override_config,
|
204
|
+
),
|
205
|
+
show_function_output: bool = typer.Option( # noqa: F841
|
206
|
+
cfg["SHOW_FUNCTION_OUTPUT"],
|
207
|
+
"--show-function-output/--hide-function-output",
|
208
|
+
help=f"Show the output of functions [dim](default: {'show' if cfg['SHOW_FUNCTION_OUTPUT'] else 'hide'})[/dim]",
|
209
|
+
rich_help_panel="Function Options",
|
210
|
+
show_default=False,
|
211
|
+
callback=override_config,
|
212
|
+
),
|
173
213
|
):
|
174
214
|
"""YAICLI: Your AI assistant in the command line.
|
175
215
|
|
@@ -179,7 +219,7 @@ def main(
|
|
179
219
|
print(DEFAULT_CONFIG_INI)
|
180
220
|
raise typer.Exit()
|
181
221
|
|
182
|
-
# Combine prompt argument with stdin content if available
|
222
|
+
# # Combine prompt argument with stdin content if available
|
183
223
|
final_prompt = prompt
|
184
224
|
if not sys.stdin.isatty():
|
185
225
|
stdin_content = sys.stdin.read().strip()
|
@@ -194,40 +234,29 @@ def main(
|
|
194
234
|
if chat:
|
195
235
|
print("Warning: --chat is ignored when stdin was redirected.")
|
196
236
|
chat = False
|
237
|
+
if not any([final_prompt, chat]):
|
238
|
+
print(ctx.get_help())
|
239
|
+
return
|
197
240
|
|
198
|
-
#
|
199
|
-
if not any([final_prompt, chat, list_chats, list_roles, create_role]):
|
200
|
-
# If no prompt, not starting chat, and not listing chats or roles, show help
|
201
|
-
typer.echo(ctx.get_help())
|
202
|
-
raise typer.Exit()
|
203
|
-
|
204
|
-
# Use build-in role for --shell or --code mode
|
241
|
+
# # Use build-in role for --shell or --code mode
|
205
242
|
if role and role != DefaultRoleNames.DEFAULT and (shell or code):
|
206
243
|
print("Warning: --role is ignored when --shell or --code is used.")
|
207
244
|
role = DefaultRoleNames.DEFAULT
|
208
245
|
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
except Exception as e:
|
224
|
-
# Catch potential errors during CLI initialization or run
|
225
|
-
print(f"An error occurred: {e}")
|
226
|
-
if verbose:
|
227
|
-
import traceback
|
228
|
-
|
229
|
-
traceback.print_exc()
|
230
|
-
raise typer.Exit(code=1)
|
246
|
+
from yaicli.cli import CLI
|
247
|
+
|
248
|
+
role = CLI.evaluate_role_name(code, shell, role)
|
249
|
+
|
250
|
+
# Instantiate the main CLI class with the specified role
|
251
|
+
cli = CLI(verbose=verbose, role=role)
|
252
|
+
|
253
|
+
# Run the appropriate mode
|
254
|
+
cli.run(
|
255
|
+
chat=chat,
|
256
|
+
shell=shell,
|
257
|
+
code=code,
|
258
|
+
user_input=final_prompt,
|
259
|
+
)
|
231
260
|
|
232
261
|
|
233
262
|
if __name__ == "__main__":
|
yaicli/exceptions.py
CHANGED
@@ -1,46 +1,18 @@
|
|
1
|
-
class
|
2
|
-
"""Base exception for
|
1
|
+
class YaicliError(Exception):
|
2
|
+
"""Base exception for yaicli"""
|
3
3
|
|
4
4
|
def __init__(self, message: str):
|
5
5
|
self.message = message
|
6
6
|
super().__init__(self.message)
|
7
7
|
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
class RoleNotFoundError(YAICLIException):
|
12
|
-
"""Exception raised when a role is not found"""
|
9
|
+
class ChatSaveError(YaicliError):
|
10
|
+
"""Error saving chat"""
|
13
11
|
|
14
|
-
pass
|
15
12
|
|
13
|
+
class ChatLoadError(YaicliError):
|
14
|
+
"""Error loading chat"""
|
16
15
|
|
17
|
-
class RoleAlreadyExistsError(YAICLIException):
|
18
|
-
"""Exception raised when a role already exists"""
|
19
16
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
class RoleCreationError(YAICLIException):
|
24
|
-
"""Exception raised when a role creation fails"""
|
25
|
-
|
26
|
-
pass
|
27
|
-
|
28
|
-
|
29
|
-
###################################
|
30
|
-
# Chat exceptions
|
31
|
-
class ChatNotFoundError(YAICLIException):
|
32
|
-
"""Exception raised when a chat is not found"""
|
33
|
-
|
34
|
-
pass
|
35
|
-
|
36
|
-
|
37
|
-
class ChatSaveError(YAICLIException):
|
38
|
-
"""Exception raised when a chat save fails"""
|
39
|
-
|
40
|
-
pass
|
41
|
-
|
42
|
-
|
43
|
-
class ChatDeleteError(YAICLIException):
|
44
|
-
"""Exception raised when a chat delete fails"""
|
45
|
-
|
46
|
-
pass
|
17
|
+
class ChatDeleteError(YaicliError):
|
18
|
+
"""Error deleting chat"""
|
@@ -0,0 +1,39 @@
|
|
1
|
+
import shutil
|
2
|
+
from pathlib import Path
|
3
|
+
from typing import Any
|
4
|
+
|
5
|
+
from ..console import get_console
|
6
|
+
from ..const import FUNCTIONS_DIR
|
7
|
+
from ..utils import option_callback
|
8
|
+
|
9
|
+
console = get_console()
|
10
|
+
|
11
|
+
|
12
|
+
@option_callback
|
13
|
+
def install_functions(cls, _: Any) -> None:
|
14
|
+
"""Install buildin functions"""
|
15
|
+
cur_dir = Path(__file__).absolute().parent
|
16
|
+
buildin_dir = cur_dir / "buildin"
|
17
|
+
buildin_funcs = [Path(path) for path in buildin_dir.glob("*.py")]
|
18
|
+
console.print("Installing buildin functions...")
|
19
|
+
if not FUNCTIONS_DIR.exists():
|
20
|
+
FUNCTIONS_DIR.mkdir(parents=True)
|
21
|
+
for file in buildin_funcs:
|
22
|
+
if (FUNCTIONS_DIR / file.name).exists():
|
23
|
+
# Skip if function already exists
|
24
|
+
console.print(f"Function {file.name} already exists, skipping.")
|
25
|
+
continue
|
26
|
+
shutil.copy(file, FUNCTIONS_DIR, follow_symlinks=True)
|
27
|
+
console.print(f"Installed {FUNCTIONS_DIR}/{file.name}")
|
28
|
+
|
29
|
+
|
30
|
+
@option_callback
|
31
|
+
def print_functions(cls, _: Any) -> None:
|
32
|
+
"""List all available buildin functions"""
|
33
|
+
if not FUNCTIONS_DIR.exists():
|
34
|
+
console.print("No installed functions found.")
|
35
|
+
return
|
36
|
+
for file in FUNCTIONS_DIR.glob("*.py"):
|
37
|
+
if file.name.startswith("_"):
|
38
|
+
continue
|
39
|
+
console.print(file)
|
@@ -0,0 +1,47 @@
|
|
1
|
+
import subprocess
|
2
|
+
|
3
|
+
from instructor import OpenAISchema
|
4
|
+
from pydantic import Field
|
5
|
+
|
6
|
+
|
7
|
+
class Function(OpenAISchema):
|
8
|
+
"""
|
9
|
+
Execute a shell command and return the output (result).
|
10
|
+
"""
|
11
|
+
|
12
|
+
shell_command: str = Field(
|
13
|
+
...,
|
14
|
+
json_schema_extra={
|
15
|
+
"example": "ls -la",
|
16
|
+
},
|
17
|
+
description="Shell command to execute.",
|
18
|
+
)
|
19
|
+
|
20
|
+
class Config:
|
21
|
+
title = "execute_shell_command"
|
22
|
+
|
23
|
+
@classmethod
|
24
|
+
def execute(cls, shell_command: str) -> str:
|
25
|
+
"""
|
26
|
+
Execute a shell command and return the output (result).
|
27
|
+
|
28
|
+
Args:
|
29
|
+
shell_command (str): shell command to execute.
|
30
|
+
|
31
|
+
Returns:
|
32
|
+
str: exit code and output string.
|
33
|
+
"""
|
34
|
+
# Optional security check
|
35
|
+
dangerous_commands = ["rm -rf", "mkfs", "dd"]
|
36
|
+
if any(cmd in shell_command for cmd in dangerous_commands):
|
37
|
+
return "Error: Dangerous command detected."
|
38
|
+
|
39
|
+
try:
|
40
|
+
process = subprocess.Popen(
|
41
|
+
shell_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True
|
42
|
+
)
|
43
|
+
output, _ = process.communicate()
|
44
|
+
exit_code = process.returncode
|
45
|
+
return f"Exit code: {exit_code}, Output:\n{output}"
|
46
|
+
except Exception as e:
|
47
|
+
return f"Error: {str(e)}"
|