yaicli 0.4.0__py3-none-any.whl → 0.5.1__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.
yaicli/roles.py DELETED
@@ -1,276 +0,0 @@
1
- import json
2
- from pathlib import Path
3
- from typing import Any, Dict, Optional, Union
4
-
5
- import typer
6
- from rich.console import Console
7
- from rich.prompt import Prompt
8
- from rich.table import Table
9
-
10
- from yaicli.config import cfg
11
- from yaicli.console import get_console
12
- from yaicli.const import DEFAULT_ROLES, ROLES_DIR, DefaultRoleNames
13
- from yaicli.exceptions import RoleAlreadyExistsError, RoleCreationError
14
- from yaicli.utils import detect_os, detect_shell, option_callback
15
-
16
-
17
- class Role:
18
- def __init__(
19
- self, name: str, prompt: str, variables: Optional[Dict[str, Any]] = None, filepath: Optional[str] = None
20
- ):
21
- self.name = name
22
- self._prompt = prompt
23
- if not variables:
24
- variables = {"_os": detect_os(cfg), "_shell": detect_shell(cfg)}
25
- self.variables = variables
26
- self.filepath = filepath
27
-
28
- self.prompt = self._prompt.format(**self.variables)
29
-
30
- def to_dict(self) -> Dict[str, Any]:
31
- """Convert Role to dictionary for serialization"""
32
- return {
33
- "name": self.name,
34
- "prompt": self._prompt,
35
- }
36
-
37
- @classmethod
38
- def from_dict(cls, role_id: str, data: Dict[str, Any], filepath: Optional[str] = None) -> "Role":
39
- """Create Role object from dictionary"""
40
- return cls(
41
- name=data.get("name", role_id),
42
- prompt=data.get("prompt", ""),
43
- variables=data.get("variables", {}),
44
- filepath=filepath,
45
- )
46
-
47
- def __str__(self):
48
- return f"Role(name={self.name}, prompt={self.prompt[:30]}...)"
49
-
50
-
51
- class RoleManager:
52
- roles_dir: Path = ROLES_DIR
53
- console: Console = get_console()
54
- _roles: Optional[Dict[str, Role]] = None
55
-
56
- def __new__(cls):
57
- """Singleton class for RoleManager"""
58
- if not hasattr(cls, "instance"):
59
- cls.instance = super().__new__(cls)
60
- return cls.instance
61
-
62
- @property
63
- def roles(self) -> Dict[str, Role]:
64
- if self._roles is None:
65
- self._roles = self._load_roles()
66
- return self._roles
67
-
68
- def _load_roles(self) -> Dict[str, Role]:
69
- """Load all role configurations"""
70
- roles: Dict[str, Role] = {}
71
- self.roles_dir.mkdir(parents=True, exist_ok=True)
72
-
73
- # Check if any role files exist
74
- role_files: list[Path] = list(self.roles_dir.glob("*.json"))
75
-
76
- if not role_files:
77
- # Fast path: no existing roles, just create defaults
78
- for role_id, role_config in DEFAULT_ROLES.items():
79
- role_file = self.roles_dir / f"{role_id}.json"
80
- filepath = str(role_file)
81
- roles[role_id] = Role.from_dict(role_id, role_config, filepath)
82
- with role_file.open("w", encoding="utf-8") as f:
83
- json.dump(role_config, f, indent=2)
84
- return roles
85
-
86
- # Load existing role files
87
- for role_file in role_files:
88
- role_id = role_file.stem
89
- filepath = str(role_file)
90
- try:
91
- with role_file.open("r", encoding="utf-8") as f:
92
- role_data = json.load(f)
93
- roles[role_id] = Role.from_dict(role_id, role_data, filepath)
94
- except Exception as e:
95
- self.console.print(f"Error loading role {role_id}: {e}", style="red")
96
-
97
- # Ensure default roles exist
98
- for role_id, role_config in DEFAULT_ROLES.items():
99
- if role_id not in roles:
100
- # Default role doesn't exist locally, create it
101
- role_file = self.roles_dir / f"{role_id}.json"
102
- filepath = str(role_file)
103
- roles[role_id] = Role.from_dict(role_id, role_config, filepath)
104
- with role_file.open("w", encoding="utf-8") as f:
105
- json.dump(role_config, f, indent=2)
106
- else:
107
- # TODO: Maybe not necessary
108
- # Check if the local role's prompt differs from the built-in one
109
- local_role = roles[role_id]
110
- builtin_prompt = role_config.get("prompt", "")
111
-
112
- # Only show warning if ROLE_MODIFY_WARNING is enabled
113
- if cfg["ROLE_MODIFY_WARNING"]:
114
- if local_role._prompt != builtin_prompt:
115
- self.console.print(
116
- f"[yellow]Warning:[/yellow] Local role '{role_id}' has a different prompt than the built-in role.",
117
- style="yellow",
118
- )
119
- self.console.print(
120
- "To reset to the built-in version, delete the local role file with: "
121
- f'[bold]ai --delete-role "{role_id}"[/bold]',
122
- style="dim",
123
- )
124
- self.console.print(
125
- "To disable this warning, set [bold]ROLE_MODIFY_WARNING=false[/bold] in your config file.",
126
- style="dim",
127
- )
128
-
129
- return roles
130
-
131
- @classmethod
132
- @option_callback
133
- def print_list_option(cls, _: Any):
134
- """Print the list of roles.
135
- This method is a cli option callback.
136
- """
137
- table = Table(show_header=True, show_footer=False)
138
- table.add_column("Name", style="dim")
139
- table.add_column("Filepath", style="dim")
140
- for file in sorted(cls.roles_dir.glob("*.json"), key=lambda f: f.stat().st_mtime):
141
- table.add_row(file.stem, str(file))
142
- cls.console.print(table)
143
- cls.console.print("Use `ai --show-role <name>` to view a role.", style="dim")
144
-
145
- def list_roles(self) -> list:
146
- """List all available roles info"""
147
- roles_list = []
148
- for role_id, role in sorted(self.roles.items()):
149
- roles_list.append(
150
- {
151
- "id": role_id,
152
- "name": role.name,
153
- "prompt": role.prompt,
154
- "is_default": role_id in DEFAULT_ROLES,
155
- "filepath": role.filepath,
156
- }
157
- )
158
- return roles_list
159
-
160
- @classmethod
161
- @option_callback
162
- def show_role_option(cls, name: str):
163
- """Show a role's prompt.
164
- This method is a cli option callback.
165
- """
166
- self = cls()
167
- role = self.get_role(name)
168
- if not role:
169
- self.console.print(f"Role '{name}' does not exist", style="red")
170
- return
171
- self.console.print(role.prompt)
172
-
173
- def get_role(self, role_id: str) -> Optional[Role]:
174
- """Get role by ID"""
175
- return self.roles.get(role_id)
176
-
177
- @classmethod
178
- def check_id_ok(cls, param: typer.CallbackParam, role_id: str):
179
- """Check if role exists by ID.
180
- This method is a cli option callback.
181
- If role does not exist, exit with error.
182
- """
183
- if not role_id or role_id == param.default:
184
- return role_id
185
- self = cls()
186
- if not self.role_exists(role_id):
187
- self.console.print(f"Role '{role_id}' does not exist", style="red")
188
- raise typer.Abort()
189
- return role_id
190
-
191
- def role_exists(self, role_id: str) -> bool:
192
- """Check if role exists"""
193
- return role_id in self.roles
194
-
195
- def save_role(self, role_id: str, role: Role) -> None:
196
- """Save role configuration"""
197
- try:
198
- self.roles[role_id] = role
199
- role_file = self.roles_dir / f"{role_id}.json"
200
- role.filepath = str(role_file)
201
- with role_file.open("w", encoding="utf-8") as f:
202
- json.dump(role.to_dict(), f, indent=2)
203
- except Exception as e:
204
- raise RoleCreationError(f"RoleCreationError {e}") from e
205
-
206
- @classmethod
207
- @option_callback
208
- def create_role_option(cls, name: str):
209
- """Create a new role and save it to file.
210
- This method is a cli option callback.
211
- """
212
- self = cls()
213
- if name in self.roles:
214
- self.console.print(f"Role '{name}' already exists", style="yellow")
215
- return
216
- description = Prompt.ask("Enter role description")
217
-
218
- # Format the prompt as "You are {role_id}, {description}"
219
- prompt = f"You are {name}, {description}"
220
-
221
- role = Role(name=name, prompt=prompt)
222
- self.create_role(name, role)
223
- self.console.print(f"Role '{name}' created successfully", style="green")
224
-
225
- def create_role(self, role_id: str, role: Union[Role, Dict[str, Any]]) -> None:
226
- """Create a new role and save it to file"""
227
- if role_id in self.roles:
228
- raise RoleAlreadyExistsError(f"Role '{role_id}' already exists")
229
- if isinstance(role, dict):
230
- if "name" not in role or "prompt" not in role:
231
- raise RoleCreationError("Role must have 'name' and 'prompt' keys")
232
- # Convert dict to Role object
233
- role = Role.from_dict(role_id, role)
234
- self.save_role(role_id, role)
235
-
236
- @classmethod
237
- @option_callback
238
- def delete_role_option(cls, name: str):
239
- """Delete a role and its file.
240
- This method is a cli option callback.
241
- """
242
- self = cls()
243
- if self.delete_role(name):
244
- self.console.print(f"Role '{name}' deleted successfully", style="green")
245
-
246
- def delete_role(self, role_id: str) -> bool:
247
- """Delete a role and its file"""
248
- if role_id not in self.roles:
249
- self.console.print(f"Role '{role_id}' does not exist", style="red")
250
- return False
251
-
252
- try:
253
- role = self.roles[role_id]
254
- if role.filepath:
255
- Path(role.filepath).unlink(missing_ok=True)
256
- del self.roles[role_id]
257
- return True
258
- except Exception as e:
259
- self.console.print(f"Error deleting role: {e}", style="red")
260
- return False
261
-
262
- def get_system_prompt(self, role_id: str) -> str:
263
- """Get prompt from file by role ID"""
264
- role = self.get_role(role_id)
265
- if not role:
266
- # Fall back to default role if specified role doesn't exist
267
- self.console.print(f"Role {role_id} not found, using default role", style="yellow")
268
- role = self.get_role(DefaultRoleNames.DEFAULT)
269
- if not role:
270
- # Last resort fallback
271
- default_config = DEFAULT_ROLES[DefaultRoleNames.DEFAULT]
272
- role = Role.from_dict(DefaultRoleNames.DEFAULT, default_config)
273
-
274
- # Create a copy of the role with system variables
275
- system_role = Role(name=role.name, prompt=role._prompt)
276
- return system_role.prompt
@@ -1,23 +0,0 @@
1
- pyproject.toml,sha256=qBb73j9dLZ13fGtQDN81nxfqfpUVs5HNDq5hyfwxOtQ,1540
2
- yaicli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
- yaicli/chat_manager.py,sha256=I7BAMz91FLYT6x69wbomtAGLx0WsoTwS4Wo0MgP6P9I,10644
4
- yaicli/cli.py,sha256=kqUXSDQej4lMefI2QQpM-Do01Be6CUphh65_Nu7sDck,22948
5
- yaicli/config.py,sha256=nfnrMz_Q78oHqDVvWSrQRGFyBl-q5MScvnYlQRWDQ_g,6515
6
- yaicli/console.py,sha256=291F4hGksJtxYpg_mehepCIJ-eB2MaDNIyv1JAMgJ1Y,1985
7
- yaicli/const.py,sha256=WjNzJAP37XizJYyd-LBTWxZAfJ2Nj7mj-eECPMOr3Do,6927
8
- yaicli/entry.py,sha256=Yp0Z--x-7dowrz-h8hJJ4_BoCzuDjS11NcM8YgFzUoY,7460
9
- yaicli/exceptions.py,sha256=ndedSdE0uaxxHrWN944BkbhMfRMSMxGDfmqmCKCGJco,924
10
- yaicli/history.py,sha256=s-57X9FMsaQHF7XySq1gGH_jpd_cHHTYafYu2ECuG6M,2472
11
- yaicli/printer.py,sha256=nXpralD5qZJQga3OTdEPhj22g7UoF-4mJbZeOtWXojo,12430
12
- yaicli/render.py,sha256=mB1OT9859_PTwI9f-KY802lPaeQXKRw6ls_5jN21jWc,511
13
- yaicli/roles.py,sha256=LdPqpdBKQr_eX7PV7iwRtFT0_trmWXVvZUmb3OMhK0w,10582
14
- yaicli/utils.py,sha256=MLvb-C5n19AD9Z1nW4Z3Z43ZKNH8STxQmNDnL7mq26E,4490
15
- yaicli/providers/__init__.py,sha256=zYXcnV8HdNeLEPRJEeSgSboNuphkdA1PrbB8MkDErAk,1243
16
- yaicli/providers/base.py,sha256=Q6QoxrIVqDYcUp1-FY5O-LT33rKSb1jeXGnrbd4Xlog,1870
17
- yaicli/providers/cohere.py,sha256=m_HIHm6NEd1VG4wCmDKYn3KHo2jzC98FsfxRzjWR8-M,5411
18
- yaicli/providers/openai.py,sha256=CKNN3wDsnCEOwZAMrARi43-AV65WgJ7AvHONgvpLZw8,8541
19
- yaicli-0.4.0.dist-info/METADATA,sha256=oeusTm4BZ1Dvow0IF_c2urp_onnV41OgE7QzwcuP09c,43917
20
- yaicli-0.4.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
21
- yaicli-0.4.0.dist-info/entry_points.txt,sha256=iMhGm3btBaqrknQoF6WCg5sdx69ZyNSC73tRpCcbcLw,63
22
- yaicli-0.4.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
23
- yaicli-0.4.0.dist-info/RECORD,,
File without changes