janito 0.13.0__py3-none-any.whl → 0.15.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.
- janito/__init__.py +1 -1
- janito/cli/agent/__init__.py +7 -0
- janito/cli/agent/conversation.py +149 -0
- janito/cli/agent/initialization.py +168 -0
- janito/cli/agent/query.py +112 -0
- janito/cli/agent.py +7 -395
- janito/cli/app.py +103 -19
- janito/cli/commands/__init__.py +12 -0
- janito/cli/commands/config.py +30 -0
- janito/cli/commands/history.py +119 -0
- janito/cli/commands/profile.py +93 -0
- janito/cli/commands/validation.py +24 -0
- janito/cli/commands/workspace.py +31 -0
- janito/cli/commands.py +9 -326
- janito/config/README.md +104 -0
- janito/config/__init__.py +16 -0
- janito/config/cli/__init__.py +28 -0
- janito/config/cli/commands.py +397 -0
- janito/config/cli/validators.py +77 -0
- janito/config/core/__init__.py +23 -0
- janito/config/core/file_operations.py +90 -0
- janito/config/core/properties.py +316 -0
- janito/config/core/singleton.py +282 -0
- janito/config/profiles/__init__.py +8 -0
- janito/config/profiles/definitions.py +38 -0
- janito/config/profiles/manager.py +80 -0
- janito/data/instructions_template.txt +12 -6
- janito/tools/__init__.py +8 -2
- janito/tools/bash/bash.py +80 -7
- janito/tools/bash/unix_persistent_bash.py +32 -1
- janito/tools/bash/win_persistent_bash.py +34 -1
- janito/tools/fetch_webpage/__init__.py +22 -33
- janito/tools/fetch_webpage/core.py +182 -155
- janito/tools/move_file.py +1 -1
- janito/tools/search_text.py +225 -239
- janito/tools/str_replace_editor/handlers/view.py +14 -8
- janito/tools/think.py +37 -0
- janito/tools/usage_tracker.py +1 -0
- {janito-0.13.0.dist-info → janito-0.15.0.dist-info}/METADATA +204 -23
- janito-0.15.0.dist-info/RECORD +64 -0
- janito/config.py +0 -358
- janito/test_file.py +0 -4
- janito/tools/fetch_webpage/chunking.py +0 -76
- janito/tools/fetch_webpage/extractors.py +0 -276
- janito/tools/fetch_webpage/news.py +0 -137
- janito/tools/fetch_webpage/utils.py +0 -108
- janito-0.13.0.dist-info/RECORD +0 -47
- {janito-0.13.0.dist-info → janito-0.15.0.dist-info}/WHEEL +0 -0
- {janito-0.13.0.dist-info → janito-0.15.0.dist-info}/entry_points.txt +0 -0
- {janito-0.13.0.dist-info → janito-0.15.0.dist-info}/licenses/LICENSE +0 -0
janito/config.py
DELETED
@@ -1,358 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Configuration module for Janito.
|
3
|
-
Provides a singleton Config class to access configuration values.
|
4
|
-
"""
|
5
|
-
import os
|
6
|
-
import json
|
7
|
-
from pathlib import Path
|
8
|
-
import typer
|
9
|
-
from typing import Dict, Any, Optional
|
10
|
-
|
11
|
-
# Predefined parameter profiles
|
12
|
-
PROFILES = {
|
13
|
-
"precise": {
|
14
|
-
"temperature": 0.2,
|
15
|
-
"top_p": 0.85,
|
16
|
-
"top_k": 20,
|
17
|
-
"description": "Factual answers, documentation, structured data, avoiding hallucinations"
|
18
|
-
},
|
19
|
-
"balanced": {
|
20
|
-
"temperature": 0.5,
|
21
|
-
"top_p": 0.9,
|
22
|
-
"top_k": 40,
|
23
|
-
"description": "Professional writing, summarization, everyday tasks with moderate creativity"
|
24
|
-
},
|
25
|
-
"conversational": {
|
26
|
-
"temperature": 0.7,
|
27
|
-
"top_p": 0.9,
|
28
|
-
"top_k": 45,
|
29
|
-
"description": "Natural dialogue, educational content, support conversations"
|
30
|
-
},
|
31
|
-
"creative": {
|
32
|
-
"temperature": 0.9,
|
33
|
-
"top_p": 0.95,
|
34
|
-
"top_k": 70,
|
35
|
-
"description": "Storytelling, brainstorming, marketing copy, poetry"
|
36
|
-
},
|
37
|
-
"technical": {
|
38
|
-
"temperature": 0.3,
|
39
|
-
"top_p": 0.95,
|
40
|
-
"top_k": 15,
|
41
|
-
"description": "Code generation, debugging, decision analysis, technical problem-solving"
|
42
|
-
}
|
43
|
-
}
|
44
|
-
|
45
|
-
class Config:
|
46
|
-
"""Singleton configuration class for Janito."""
|
47
|
-
_instance = None
|
48
|
-
|
49
|
-
def __new__(cls):
|
50
|
-
if cls._instance is None:
|
51
|
-
cls._instance = super(Config, cls).__new__(cls)
|
52
|
-
cls._instance._workspace_dir = os.getcwd()
|
53
|
-
cls._instance._verbose = False
|
54
|
-
# Chat history context feature has been removed
|
55
|
-
cls._instance._ask_mode = False
|
56
|
-
cls._instance._trust_mode = False # New trust mode setting
|
57
|
-
# Set technical profile as default
|
58
|
-
profile_data = PROFILES["technical"]
|
59
|
-
cls._instance._temperature = profile_data["temperature"]
|
60
|
-
cls._instance._profile = "technical"
|
61
|
-
cls._instance._role = "software engineer"
|
62
|
-
cls._instance._gitbash_path = None # Default to None for auto-detection
|
63
|
-
cls._instance._load_config()
|
64
|
-
return cls._instance
|
65
|
-
|
66
|
-
def _load_config(self) -> None:
|
67
|
-
"""Load configuration from file."""
|
68
|
-
config_path = Path(self._workspace_dir) / ".janito" / "config.json"
|
69
|
-
if config_path.exists():
|
70
|
-
try:
|
71
|
-
with open(config_path, "r", encoding="utf-8") as f:
|
72
|
-
config_data = json.load(f)
|
73
|
-
# Chat history context feature has been removed
|
74
|
-
if "debug_mode" in config_data:
|
75
|
-
self._verbose = config_data["debug_mode"]
|
76
|
-
if "ask_mode" in config_data:
|
77
|
-
self._ask_mode = config_data["ask_mode"]
|
78
|
-
if "trust_mode" in config_data:
|
79
|
-
self._trust_mode = config_data["trust_mode"]
|
80
|
-
if "temperature" in config_data:
|
81
|
-
self._temperature = config_data["temperature"]
|
82
|
-
if "profile" in config_data:
|
83
|
-
self._profile = config_data["profile"]
|
84
|
-
if "role" in config_data:
|
85
|
-
self._role = config_data["role"]
|
86
|
-
if "gitbash_path" in config_data:
|
87
|
-
self._gitbash_path = config_data["gitbash_path"]
|
88
|
-
except Exception as e:
|
89
|
-
print(f"Warning: Failed to load configuration: {str(e)}")
|
90
|
-
|
91
|
-
def _save_config(self) -> None:
|
92
|
-
"""Save configuration to file."""
|
93
|
-
config_dir = Path(self._workspace_dir) / ".janito"
|
94
|
-
config_dir.mkdir(parents=True, exist_ok=True)
|
95
|
-
config_path = config_dir / "config.json"
|
96
|
-
|
97
|
-
config_data = {
|
98
|
-
# Chat history context feature has been removed
|
99
|
-
"verbose": self._verbose,
|
100
|
-
"ask_mode": self._ask_mode,
|
101
|
-
# trust_mode is not saved as it's a per-session setting
|
102
|
-
"temperature": self._temperature,
|
103
|
-
"role": self._role
|
104
|
-
}
|
105
|
-
|
106
|
-
# Save profile name if one is set
|
107
|
-
if self._profile:
|
108
|
-
config_data["profile"] = self._profile
|
109
|
-
|
110
|
-
# Save GitBash path if one is set
|
111
|
-
if self._gitbash_path:
|
112
|
-
config_data["gitbash_path"] = self._gitbash_path
|
113
|
-
|
114
|
-
try:
|
115
|
-
with open(config_path, "w", encoding="utf-8") as f:
|
116
|
-
json.dump(config_data, f, indent=2)
|
117
|
-
except Exception as e:
|
118
|
-
print(f"Warning: Failed to save configuration: {str(e)}")
|
119
|
-
|
120
|
-
def set_profile(self, profile_name: str) -> None:
|
121
|
-
"""Set parameter values based on a predefined profile.
|
122
|
-
|
123
|
-
Args:
|
124
|
-
profile_name: Name of the profile to use (precise, balanced, conversational, creative, technical)
|
125
|
-
|
126
|
-
Raises:
|
127
|
-
ValueError: If the profile name is not recognized
|
128
|
-
"""
|
129
|
-
profile_name = profile_name.lower()
|
130
|
-
if profile_name not in PROFILES:
|
131
|
-
valid_profiles = ", ".join(PROFILES.keys())
|
132
|
-
raise ValueError(f"Unknown profile: {profile_name}. Valid profiles are: {valid_profiles}")
|
133
|
-
|
134
|
-
profile = PROFILES[profile_name]
|
135
|
-
self._temperature = profile["temperature"]
|
136
|
-
self._profile = profile_name
|
137
|
-
self._save_config()
|
138
|
-
|
139
|
-
@property
|
140
|
-
def profile(self) -> Optional[str]:
|
141
|
-
"""Get the current profile name."""
|
142
|
-
return self._profile
|
143
|
-
|
144
|
-
@staticmethod
|
145
|
-
def get_available_profiles() -> Dict[str, Dict[str, Any]]:
|
146
|
-
"""Get all available predefined profiles."""
|
147
|
-
return PROFILES
|
148
|
-
|
149
|
-
@staticmethod
|
150
|
-
def set_api_key(api_key: str) -> None:
|
151
|
-
"""Set the API key in the global configuration file.
|
152
|
-
|
153
|
-
Args:
|
154
|
-
api_key: The Anthropic API key to store
|
155
|
-
|
156
|
-
Returns:
|
157
|
-
None
|
158
|
-
"""
|
159
|
-
# Create .janito directory in user's home directory if it doesn't exist
|
160
|
-
home_dir = Path.home()
|
161
|
-
config_dir = home_dir / ".janito"
|
162
|
-
config_dir.mkdir(parents=True, exist_ok=True)
|
163
|
-
|
164
|
-
# Create or update the config.json file
|
165
|
-
config_path = config_dir / "config.json"
|
166
|
-
|
167
|
-
# Load existing config if it exists
|
168
|
-
config_data = {}
|
169
|
-
if config_path.exists():
|
170
|
-
try:
|
171
|
-
with open(config_path, "r", encoding="utf-8") as f:
|
172
|
-
config_data = json.load(f)
|
173
|
-
except Exception as e:
|
174
|
-
print(f"Warning: Failed to load global configuration: {str(e)}")
|
175
|
-
|
176
|
-
# Update the API key
|
177
|
-
config_data["api_key"] = api_key
|
178
|
-
|
179
|
-
# Save the updated config
|
180
|
-
try:
|
181
|
-
with open(config_path, "w", encoding="utf-8") as f:
|
182
|
-
json.dump(config_data, f, indent=2)
|
183
|
-
print(f"API key saved to {config_path}")
|
184
|
-
except Exception as e:
|
185
|
-
raise ValueError(f"Failed to save API key: {str(e)}")
|
186
|
-
|
187
|
-
@staticmethod
|
188
|
-
def get_api_key() -> Optional[str]:
|
189
|
-
"""Get the API key from the global configuration file.
|
190
|
-
|
191
|
-
Returns:
|
192
|
-
The API key if found, None otherwise
|
193
|
-
"""
|
194
|
-
# Look for config.json in user's home directory
|
195
|
-
home_dir = Path.home()
|
196
|
-
config_path = home_dir / ".janito" / "config.json"
|
197
|
-
|
198
|
-
if config_path.exists():
|
199
|
-
try:
|
200
|
-
with open(config_path, "r", encoding="utf-8") as f:
|
201
|
-
config_data = json.load(f)
|
202
|
-
return config_data.get("api_key")
|
203
|
-
except Exception:
|
204
|
-
# Silently fail and return None
|
205
|
-
pass
|
206
|
-
|
207
|
-
return None
|
208
|
-
|
209
|
-
@property
|
210
|
-
def workspace_dir(self) -> str:
|
211
|
-
"""Get the current workspace directory."""
|
212
|
-
return self._workspace_dir
|
213
|
-
|
214
|
-
@workspace_dir.setter
|
215
|
-
def workspace_dir(self, path: str) -> None:
|
216
|
-
"""Set the workspace directory."""
|
217
|
-
# Convert to absolute path if not already
|
218
|
-
if not os.path.isabs(path):
|
219
|
-
path = os.path.normpath(os.path.abspath(path))
|
220
|
-
else:
|
221
|
-
# Ensure Windows paths are properly formatted
|
222
|
-
path = os.path.normpath(path)
|
223
|
-
|
224
|
-
# Check if the directory exists
|
225
|
-
if not os.path.isdir(path):
|
226
|
-
create_dir = typer.confirm(f"Workspace directory does not exist: {path}\nDo you want to create it?")
|
227
|
-
if create_dir:
|
228
|
-
try:
|
229
|
-
os.makedirs(path, exist_ok=True)
|
230
|
-
print(f"Created workspace directory: {path}")
|
231
|
-
except Exception as e:
|
232
|
-
raise ValueError(f"Failed to create workspace directory: {str(e)}") from e
|
233
|
-
else:
|
234
|
-
raise ValueError(f"Workspace directory does not exist: {path}")
|
235
|
-
|
236
|
-
self._workspace_dir = path
|
237
|
-
|
238
|
-
@property
|
239
|
-
def verbose(self) -> bool:
|
240
|
-
"""Get the verbose mode status."""
|
241
|
-
return self._verbose
|
242
|
-
|
243
|
-
@verbose.setter
|
244
|
-
def verbose(self, value: bool) -> None:
|
245
|
-
"""Set the verbose mode status."""
|
246
|
-
self._verbose = value
|
247
|
-
|
248
|
-
# For backward compatibility
|
249
|
-
@property
|
250
|
-
def debug_mode(self) -> bool:
|
251
|
-
"""Get the debug mode status (alias for verbose)."""
|
252
|
-
return self._verbose
|
253
|
-
|
254
|
-
@debug_mode.setter
|
255
|
-
def debug_mode(self, value: bool) -> None:
|
256
|
-
"""Set the debug mode status (alias for verbose)."""
|
257
|
-
self._verbose = value
|
258
|
-
|
259
|
-
# Chat history context feature has been removed
|
260
|
-
|
261
|
-
@property
|
262
|
-
def ask_mode(self) -> bool:
|
263
|
-
"""Get the ask mode status."""
|
264
|
-
return self._ask_mode
|
265
|
-
|
266
|
-
@ask_mode.setter
|
267
|
-
def ask_mode(self, value: bool) -> None:
|
268
|
-
"""Set the ask mode status."""
|
269
|
-
self._ask_mode = value
|
270
|
-
self._save_config()
|
271
|
-
|
272
|
-
@property
|
273
|
-
def trust_mode(self) -> bool:
|
274
|
-
"""Get the trust mode status."""
|
275
|
-
return self._trust_mode
|
276
|
-
|
277
|
-
@trust_mode.setter
|
278
|
-
def trust_mode(self, value: bool) -> None:
|
279
|
-
"""Set the trust mode status.
|
280
|
-
|
281
|
-
Note: This setting is not persisted to config file
|
282
|
-
as it's meant to be a per-session setting.
|
283
|
-
"""
|
284
|
-
self._trust_mode = value
|
285
|
-
# Don't save to config file - this is a per-session setting
|
286
|
-
|
287
|
-
@property
|
288
|
-
def temperature(self) -> float:
|
289
|
-
"""Get the temperature value for model generation."""
|
290
|
-
return self._temperature
|
291
|
-
|
292
|
-
@temperature.setter
|
293
|
-
def temperature(self, value: float) -> None:
|
294
|
-
"""Set the temperature value for model generation."""
|
295
|
-
if value < 0.0 or value > 1.0:
|
296
|
-
raise ValueError("Temperature must be between 0.0 and 1.0")
|
297
|
-
self._temperature = value
|
298
|
-
self._save_config()
|
299
|
-
|
300
|
-
# top_k and top_p are now only accessible through profiles
|
301
|
-
|
302
|
-
@property
|
303
|
-
def role(self) -> str:
|
304
|
-
"""Get the role for the assistant."""
|
305
|
-
return self._role
|
306
|
-
|
307
|
-
@role.setter
|
308
|
-
def role(self, value: str) -> None:
|
309
|
-
"""Set the role for the assistant."""
|
310
|
-
self._role = value
|
311
|
-
self._save_config()
|
312
|
-
|
313
|
-
@property
|
314
|
-
def gitbash_path(self) -> Optional[str]:
|
315
|
-
"""Get the path to the GitBash executable."""
|
316
|
-
return self._gitbash_path
|
317
|
-
|
318
|
-
@gitbash_path.setter
|
319
|
-
def gitbash_path(self, value: Optional[str]) -> None:
|
320
|
-
"""Set the path to the GitBash executable.
|
321
|
-
|
322
|
-
Args:
|
323
|
-
value: Path to the GitBash executable, or None to use auto-detection
|
324
|
-
"""
|
325
|
-
# If a path is provided, verify it exists
|
326
|
-
if value is not None and not os.path.exists(value):
|
327
|
-
raise ValueError(f"GitBash executable not found at: {value}")
|
328
|
-
|
329
|
-
self._gitbash_path = value
|
330
|
-
self._save_config()
|
331
|
-
|
332
|
-
def reset_config(self) -> bool:
|
333
|
-
"""Reset configuration by removing the config file.
|
334
|
-
|
335
|
-
Returns:
|
336
|
-
bool: True if the config file was removed, False if it didn't exist
|
337
|
-
"""
|
338
|
-
config_path = Path(self._workspace_dir) / ".janito" / "config.json"
|
339
|
-
if config_path.exists():
|
340
|
-
config_path.unlink()
|
341
|
-
# Reset instance variables to defaults
|
342
|
-
self._verbose = False
|
343
|
-
# Chat history context feature has been removed
|
344
|
-
self._ask_mode = False
|
345
|
-
self._trust_mode = False
|
346
|
-
# Set technical profile as default
|
347
|
-
profile_data = PROFILES["technical"]
|
348
|
-
self._temperature = profile_data["temperature"]
|
349
|
-
self._profile = "technical"
|
350
|
-
self._role = "software engineer"
|
351
|
-
self._gitbash_path = None # Reset to auto-detection
|
352
|
-
return True
|
353
|
-
return False
|
354
|
-
|
355
|
-
# Convenience function to get the config instance
|
356
|
-
def get_config() -> Config:
|
357
|
-
"""Get the singleton Config instance."""
|
358
|
-
return Config()
|
janito/test_file.py
DELETED
@@ -1,76 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Functions for chunking large content into manageable pieces.
|
3
|
-
"""
|
4
|
-
|
5
|
-
from typing import List
|
6
|
-
from janito.tools.rich_console import print_info, print_success
|
7
|
-
|
8
|
-
|
9
|
-
def chunk_large_content(text: str, chunk_size: int = 4000, overlap: int = 500) -> List[str]:
|
10
|
-
"""
|
11
|
-
Split very large text content into manageable chunks suitable for LLM processing.
|
12
|
-
|
13
|
-
Args:
|
14
|
-
text: The text to chunk
|
15
|
-
chunk_size: Target size for each chunk in characters
|
16
|
-
overlap: Number of characters to overlap between chunks
|
17
|
-
|
18
|
-
Returns:
|
19
|
-
List of text chunks
|
20
|
-
"""
|
21
|
-
if not text or len(text) <= chunk_size:
|
22
|
-
return [text] if text else []
|
23
|
-
|
24
|
-
print_info(f"Chunking {len(text)} characters of text into ~{chunk_size} character chunks", "Content Chunking")
|
25
|
-
|
26
|
-
# Try to split on paragraph breaks first
|
27
|
-
paragraphs = text.split('\n\n')
|
28
|
-
chunks = []
|
29
|
-
current_chunk = ""
|
30
|
-
|
31
|
-
for para in paragraphs:
|
32
|
-
# If adding this paragraph would exceed chunk size
|
33
|
-
if len(current_chunk) + len(para) + 2 > chunk_size:
|
34
|
-
# If current chunk is not empty, add it to chunks
|
35
|
-
if current_chunk:
|
36
|
-
chunks.append(current_chunk)
|
37
|
-
# Start new chunk with overlap from previous chunk
|
38
|
-
if overlap > 0 and len(current_chunk) > overlap:
|
39
|
-
current_chunk = current_chunk[-overlap:] + "\n\n" + para
|
40
|
-
else:
|
41
|
-
current_chunk = para
|
42
|
-
else:
|
43
|
-
# If paragraph itself is bigger than chunk size, split it
|
44
|
-
if len(para) > chunk_size:
|
45
|
-
words = para.split()
|
46
|
-
temp_chunk = ""
|
47
|
-
for word in words:
|
48
|
-
if len(temp_chunk) + len(word) + 1 > chunk_size:
|
49
|
-
chunks.append(temp_chunk)
|
50
|
-
# Start new chunk with overlap
|
51
|
-
if overlap > 0 and len(temp_chunk) > overlap:
|
52
|
-
temp_chunk = temp_chunk[-overlap:] + " " + word
|
53
|
-
else:
|
54
|
-
temp_chunk = word
|
55
|
-
else:
|
56
|
-
if temp_chunk:
|
57
|
-
temp_chunk += " " + word
|
58
|
-
else:
|
59
|
-
temp_chunk = word
|
60
|
-
if temp_chunk:
|
61
|
-
current_chunk = temp_chunk
|
62
|
-
else:
|
63
|
-
chunks.append(para)
|
64
|
-
else:
|
65
|
-
# Add paragraph to current chunk
|
66
|
-
if current_chunk:
|
67
|
-
current_chunk += "\n\n" + para
|
68
|
-
else:
|
69
|
-
current_chunk = para
|
70
|
-
|
71
|
-
# Don't forget the last chunk
|
72
|
-
if current_chunk:
|
73
|
-
chunks.append(current_chunk)
|
74
|
-
|
75
|
-
print_success(f"Text chunked into {len(chunks)} segments", "Content Chunking")
|
76
|
-
return chunks
|