janito 0.14.0__py3-none-any.whl → 1.0.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.
Files changed (98) hide show
  1. janito/__init__.py +1 -5
  2. janito/__main__.py +3 -5
  3. janito/agent/__init__.py +1 -0
  4. janito/agent/agent.py +96 -0
  5. janito/agent/config.py +113 -0
  6. janito/agent/config_defaults.py +10 -0
  7. janito/agent/conversation.py +107 -0
  8. janito/agent/queued_tool_handler.py +16 -0
  9. janito/agent/runtime_config.py +30 -0
  10. janito/agent/tool_handler.py +124 -0
  11. janito/agent/tools/__init__.py +11 -0
  12. janito/agent/tools/ask_user.py +63 -0
  13. janito/agent/tools/bash_exec.py +58 -0
  14. janito/agent/tools/create_directory.py +19 -0
  15. janito/agent/tools/create_file.py +43 -0
  16. janito/agent/tools/fetch_url.py +48 -0
  17. janito/agent/tools/file_str_replace.py +48 -0
  18. janito/agent/tools/find_files.py +37 -0
  19. janito/agent/tools/gitignore_utils.py +40 -0
  20. janito/agent/tools/move_file.py +37 -0
  21. janito/agent/tools/remove_file.py +19 -0
  22. janito/agent/tools/rich_live.py +37 -0
  23. janito/agent/tools/rich_utils.py +31 -0
  24. janito/agent/tools/search_text.py +41 -0
  25. janito/agent/tools/view_file.py +34 -0
  26. janito/cli/__init__.py +0 -6
  27. janito/cli/_print_config.py +68 -0
  28. janito/cli/_utils.py +8 -0
  29. janito/cli/arg_parser.py +26 -0
  30. janito/cli/config_commands.py +131 -0
  31. janito/cli/logging_setup.py +27 -0
  32. janito/cli/main.py +39 -0
  33. janito/cli/runner.py +135 -0
  34. janito/cli_chat_shell/__init__.py +1 -0
  35. janito/cli_chat_shell/chat_loop.py +147 -0
  36. janito/cli_chat_shell/commands.py +202 -0
  37. janito/cli_chat_shell/config_shell.py +75 -0
  38. janito/cli_chat_shell/load_prompt.py +15 -0
  39. janito/cli_chat_shell/session_manager.py +60 -0
  40. janito/cli_chat_shell/ui.py +136 -0
  41. janito/render_prompt.py +12 -0
  42. janito/templates/system_instructions.j2 +36 -0
  43. janito/web/__init__.py +0 -0
  44. janito/web/__main__.py +17 -0
  45. janito/web/app.py +132 -0
  46. janito-1.0.0.dist-info/METADATA +144 -0
  47. janito-1.0.0.dist-info/RECORD +51 -0
  48. {janito-0.14.0.dist-info → janito-1.0.0.dist-info}/WHEEL +2 -1
  49. janito-1.0.0.dist-info/entry_points.txt +2 -0
  50. {janito-0.14.0.dist-info → janito-1.0.0.dist-info}/licenses/LICENSE +2 -2
  51. janito-1.0.0.dist-info/top_level.txt +1 -0
  52. janito/callbacks.py +0 -34
  53. janito/cli/agent/__init__.py +0 -7
  54. janito/cli/agent/conversation.py +0 -149
  55. janito/cli/agent/initialization.py +0 -172
  56. janito/cli/agent/query.py +0 -108
  57. janito/cli/agent.py +0 -12
  58. janito/cli/app.py +0 -182
  59. janito/cli/commands/__init__.py +0 -12
  60. janito/cli/commands/config.py +0 -242
  61. janito/cli/commands/history.py +0 -119
  62. janito/cli/commands/profile.py +0 -72
  63. janito/cli/commands/validation.py +0 -24
  64. janito/cli/commands/workspace.py +0 -31
  65. janito/cli/commands.py +0 -12
  66. janito/cli/output.py +0 -29
  67. janito/cli/utils.py +0 -22
  68. janito/config.py +0 -375
  69. janito/data/instructions_template.txt +0 -31
  70. janito/token_report.py +0 -154
  71. janito/tools/__init__.py +0 -44
  72. janito/tools/bash/bash.py +0 -84
  73. janito/tools/bash/unix_persistent_bash.py +0 -184
  74. janito/tools/bash/win_persistent_bash.py +0 -308
  75. janito/tools/decorators.py +0 -90
  76. janito/tools/delete_file.py +0 -65
  77. janito/tools/fetch_webpage/__init__.py +0 -23
  78. janito/tools/fetch_webpage/core.py +0 -182
  79. janito/tools/find_files.py +0 -220
  80. janito/tools/move_file.py +0 -72
  81. janito/tools/prompt_user.py +0 -57
  82. janito/tools/replace_file.py +0 -63
  83. janito/tools/rich_console.py +0 -176
  84. janito/tools/search_text.py +0 -226
  85. janito/tools/str_replace_editor/__init__.py +0 -6
  86. janito/tools/str_replace_editor/editor.py +0 -55
  87. janito/tools/str_replace_editor/handlers/__init__.py +0 -16
  88. janito/tools/str_replace_editor/handlers/create.py +0 -60
  89. janito/tools/str_replace_editor/handlers/insert.py +0 -100
  90. janito/tools/str_replace_editor/handlers/str_replace.py +0 -94
  91. janito/tools/str_replace_editor/handlers/undo.py +0 -64
  92. janito/tools/str_replace_editor/handlers/view.py +0 -159
  93. janito/tools/str_replace_editor/utils.py +0 -33
  94. janito/tools/think.py +0 -37
  95. janito/tools/usage_tracker.py +0 -137
  96. janito-0.14.0.dist-info/METADATA +0 -396
  97. janito-0.14.0.dist-info/RECORD +0 -53
  98. janito-0.14.0.dist-info/entry_points.txt +0 -2
janito/cli/commands.py DELETED
@@ -1,12 +0,0 @@
1
- """
2
- Command handling logic for Janito CLI.
3
- This module serves as a compatibility layer for the reorganized commands module.
4
- """
5
- # Re-export the functions from the new module structure
6
- from janito.cli.commands import handle_config_commands, validate_parameters, handle_history
7
-
8
- __all__ = [
9
- "handle_config_commands",
10
- "validate_parameters",
11
- "handle_history",
12
- ]
janito/cli/output.py DELETED
@@ -1,29 +0,0 @@
1
- """
2
- Output formatting and display for Janito CLI.
3
- """
4
- from rich.console import Console
5
- from janito.config import get_config
6
-
7
- console = Console()
8
-
9
- def display_generation_params(
10
- temp_to_use: float,
11
- profile_data: dict = None,
12
- temperature: float = 0.0
13
- ) -> None:
14
- """
15
- Display generation parameters in verbose mode.
16
-
17
- Args:
18
- temp_to_use: The temperature value being used
19
- profile_data: The profile data if a profile is being used
20
- temperature: The temperature value from command line
21
- """
22
- # Show profile information if one is active
23
- config = get_config()
24
- if config.profile:
25
- if not profile_data:
26
- profile_data = config.get_available_profiles()[config.profile]
27
- console.print(f"[dim]👤 Using profile: {config.profile} - {profile_data['description']}[/dim]")
28
-
29
- # Temperature, top_k, and top_p information is hidden
janito/cli/utils.py DELETED
@@ -1,22 +0,0 @@
1
- """
2
- Utility functions for the CLI module.
3
- """
4
- import platform
5
- from rich.console import Console
6
-
7
- console = Console()
8
-
9
- def get_stdin_termination_hint():
10
- """
11
- Returns a user-friendly message about how to terminate stdin input
12
- based on the current platform.
13
-
14
- Returns:
15
- str: A message with the key sequence to terminate stdin input
16
- """
17
- system = platform.system()
18
-
19
- if system == "Windows":
20
- return "[bold yellow]Press Ctrl+Z followed by Enter to terminate input[/bold yellow]"
21
- else: # Unix-like systems (Linux, macOS)
22
- return "[bold yellow]Press Ctrl+D to terminate input[/bold yellow]"
janito/config.py DELETED
@@ -1,375 +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
- cls._instance._no_tools = False # New no-tools mode setting
58
- # Set technical profile as default
59
- profile_data = PROFILES["technical"]
60
- cls._instance._temperature = profile_data["temperature"]
61
- cls._instance._profile = "technical"
62
- cls._instance._role = "software engineer"
63
- cls._instance._gitbash_path = None # Default to None for auto-detection
64
- cls._instance._load_config()
65
- return cls._instance
66
-
67
- def _load_config(self) -> None:
68
- """Load configuration from file."""
69
- config_path = Path(self._workspace_dir) / ".janito" / "config.json"
70
- if config_path.exists():
71
- try:
72
- with open(config_path, "r", encoding="utf-8") as f:
73
- config_data = json.load(f)
74
- # Chat history context feature has been removed
75
- if "debug_mode" in config_data:
76
- self._verbose = config_data["debug_mode"]
77
- if "ask_mode" in config_data:
78
- self._ask_mode = config_data["ask_mode"]
79
- if "trust_mode" in config_data:
80
- self._trust_mode = config_data["trust_mode"]
81
- if "temperature" in config_data:
82
- self._temperature = config_data["temperature"]
83
- if "profile" in config_data:
84
- self._profile = config_data["profile"]
85
- if "role" in config_data:
86
- self._role = config_data["role"]
87
- if "gitbash_path" in config_data:
88
- self._gitbash_path = config_data["gitbash_path"]
89
- except Exception as e:
90
- print(f"Warning: Failed to load configuration: {str(e)}")
91
-
92
- def _save_config(self) -> None:
93
- """Save configuration to file."""
94
- config_dir = Path(self._workspace_dir) / ".janito"
95
- config_dir.mkdir(parents=True, exist_ok=True)
96
- config_path = config_dir / "config.json"
97
-
98
- config_data = {
99
- # Chat history context feature has been removed
100
- "verbose": self._verbose,
101
- "ask_mode": self._ask_mode,
102
- # trust_mode is not saved as it's a per-session setting
103
- "temperature": self._temperature,
104
- "role": self._role
105
- }
106
-
107
- # Save profile name if one is set
108
- if self._profile:
109
- config_data["profile"] = self._profile
110
-
111
- # Save GitBash path if one is set
112
- if self._gitbash_path:
113
- config_data["gitbash_path"] = self._gitbash_path
114
-
115
- try:
116
- with open(config_path, "w", encoding="utf-8") as f:
117
- json.dump(config_data, f, indent=2)
118
- except Exception as e:
119
- print(f"Warning: Failed to save configuration: {str(e)}")
120
-
121
- def set_profile(self, profile_name: str) -> None:
122
- """Set parameter values based on a predefined profile.
123
-
124
- Args:
125
- profile_name: Name of the profile to use (precise, balanced, conversational, creative, technical)
126
-
127
- Raises:
128
- ValueError: If the profile name is not recognized
129
- """
130
- profile_name = profile_name.lower()
131
- if profile_name not in PROFILES:
132
- valid_profiles = ", ".join(PROFILES.keys())
133
- raise ValueError(f"Unknown profile: {profile_name}. Valid profiles are: {valid_profiles}")
134
-
135
- profile = PROFILES[profile_name]
136
- self._temperature = profile["temperature"]
137
- self._profile = profile_name
138
- self._save_config()
139
-
140
- @property
141
- def profile(self) -> Optional[str]:
142
- """Get the current profile name."""
143
- return self._profile
144
-
145
- @staticmethod
146
- def get_available_profiles() -> Dict[str, Dict[str, Any]]:
147
- """Get all available predefined profiles."""
148
- return PROFILES
149
-
150
- @staticmethod
151
- def set_api_key(api_key: str) -> None:
152
- """Set the API key in the global configuration file.
153
-
154
- Args:
155
- api_key: The Anthropic API key to store
156
-
157
- Returns:
158
- None
159
- """
160
- # Create .janito directory in user's home directory if it doesn't exist
161
- home_dir = Path.home()
162
- config_dir = home_dir / ".janito"
163
- config_dir.mkdir(parents=True, exist_ok=True)
164
-
165
- # Create or update the config.json file
166
- config_path = config_dir / "config.json"
167
-
168
- # Load existing config if it exists
169
- config_data = {}
170
- if config_path.exists():
171
- try:
172
- with open(config_path, "r", encoding="utf-8") as f:
173
- config_data = json.load(f)
174
- except Exception as e:
175
- print(f"Warning: Failed to load global configuration: {str(e)}")
176
-
177
- # Update the API key
178
- config_data["api_key"] = api_key
179
-
180
- # Save the updated config
181
- try:
182
- with open(config_path, "w", encoding="utf-8") as f:
183
- json.dump(config_data, f, indent=2)
184
- print(f"API key saved to {config_path}")
185
- except Exception as e:
186
- raise ValueError(f"Failed to save API key: {str(e)}")
187
-
188
- @staticmethod
189
- def get_api_key() -> Optional[str]:
190
- """Get the API key from the global configuration file.
191
-
192
- Returns:
193
- The API key if found, None otherwise
194
- """
195
- # Look for config.json in user's home directory
196
- home_dir = Path.home()
197
- config_path = home_dir / ".janito" / "config.json"
198
-
199
- if config_path.exists():
200
- try:
201
- with open(config_path, "r", encoding="utf-8") as f:
202
- config_data = json.load(f)
203
- return config_data.get("api_key")
204
- except Exception:
205
- # Silently fail and return None
206
- pass
207
-
208
- return None
209
-
210
- @property
211
- def workspace_dir(self) -> str:
212
- """Get the current workspace directory."""
213
- return self._workspace_dir
214
-
215
- @workspace_dir.setter
216
- def workspace_dir(self, path: str) -> None:
217
- """Set the workspace directory."""
218
- # Convert to absolute path if not already
219
- if not os.path.isabs(path):
220
- path = os.path.normpath(os.path.abspath(path))
221
- else:
222
- # Ensure Windows paths are properly formatted
223
- path = os.path.normpath(path)
224
-
225
- # Check if the directory exists
226
- if not os.path.isdir(path):
227
- create_dir = typer.confirm(f"Workspace directory does not exist: {path}\nDo you want to create it?")
228
- if create_dir:
229
- try:
230
- os.makedirs(path, exist_ok=True)
231
- print(f"Created workspace directory: {path}")
232
- except Exception as e:
233
- raise ValueError(f"Failed to create workspace directory: {str(e)}") from e
234
- else:
235
- raise ValueError(f"Workspace directory does not exist: {path}")
236
-
237
- self._workspace_dir = path
238
-
239
- @property
240
- def verbose(self) -> bool:
241
- """Get the verbose mode status."""
242
- return self._verbose
243
-
244
- @verbose.setter
245
- def verbose(self, value: bool) -> None:
246
- """Set the verbose mode status."""
247
- self._verbose = value
248
-
249
- # For backward compatibility
250
- @property
251
- def debug_mode(self) -> bool:
252
- """Get the debug mode status (alias for verbose)."""
253
- return self._verbose
254
-
255
- @debug_mode.setter
256
- def debug_mode(self, value: bool) -> None:
257
- """Set the debug mode status (alias for verbose)."""
258
- self._verbose = value
259
-
260
- # Chat history context feature has been removed
261
-
262
- @property
263
- def ask_mode(self) -> bool:
264
- """Get the ask mode status."""
265
- return self._ask_mode
266
-
267
- @ask_mode.setter
268
- def ask_mode(self, value: bool) -> None:
269
- """Set the ask mode status."""
270
- self._ask_mode = value
271
- self._save_config()
272
-
273
- @property
274
- def trust_mode(self) -> bool:
275
- """Get the trust mode status."""
276
- return self._trust_mode
277
-
278
- @trust_mode.setter
279
- def trust_mode(self, value: bool) -> None:
280
- """Set the trust mode status.
281
-
282
- Note: This setting is not persisted to config file
283
- as it's meant to be a per-session setting.
284
- """
285
- self._trust_mode = value
286
- # Don't save to config file - this is a per-session setting
287
-
288
- @property
289
- def no_tools(self) -> bool:
290
- """Get the no-tools mode status."""
291
- return self._no_tools
292
-
293
- @no_tools.setter
294
- def no_tools(self, value: bool) -> None:
295
- """Set the no-tools mode status.
296
-
297
- Note: This setting is not persisted to config file
298
- as it's meant to be a per-session setting.
299
- """
300
- self._no_tools = value
301
- # Don't save to config file - this is a per-session setting
302
-
303
- @property
304
- def temperature(self) -> float:
305
- """Get the temperature value for model generation."""
306
- return self._temperature
307
-
308
- @temperature.setter
309
- def temperature(self, value: float) -> None:
310
- """Set the temperature value for model generation."""
311
- if value < 0.0 or value > 1.0:
312
- raise ValueError("Temperature must be between 0.0 and 1.0")
313
- self._temperature = value
314
- self._save_config()
315
-
316
- # top_k and top_p are now only accessible through profiles
317
-
318
- @property
319
- def role(self) -> str:
320
- """Get the role for the assistant."""
321
- return self._role
322
-
323
- @role.setter
324
- def role(self, value: str) -> None:
325
- """Set the role for the assistant."""
326
- self._role = value
327
- self._save_config()
328
-
329
- @property
330
- def gitbash_path(self) -> Optional[str]:
331
- """Get the path to the GitBash executable."""
332
- return self._gitbash_path
333
-
334
- @gitbash_path.setter
335
- def gitbash_path(self, value: Optional[str]) -> None:
336
- """Set the path to the GitBash executable.
337
-
338
- Args:
339
- value: Path to the GitBash executable, or None to use auto-detection
340
- """
341
- # If a path is provided, verify it exists
342
- if value is not None and not os.path.exists(value):
343
- raise ValueError(f"GitBash executable not found at: {value}")
344
-
345
- self._gitbash_path = value
346
- self._save_config()
347
-
348
- def reset_config(self) -> bool:
349
- """Reset configuration by removing the config file.
350
-
351
- Returns:
352
- bool: True if the config file was removed, False if it didn't exist
353
- """
354
- config_path = Path(self._workspace_dir) / ".janito" / "config.json"
355
- if config_path.exists():
356
- config_path.unlink()
357
- # Reset instance variables to defaults
358
- self._verbose = False
359
- # Chat history context feature has been removed
360
- self._ask_mode = False
361
- self._trust_mode = False
362
- self._no_tools = False
363
- # Set technical profile as default
364
- profile_data = PROFILES["technical"]
365
- self._temperature = profile_data["temperature"]
366
- self._profile = "technical"
367
- self._role = "software engineer"
368
- self._gitbash_path = None # Reset to auto-detection
369
- return True
370
- return False
371
-
372
- # Convenience function to get the config instance
373
- def get_config() -> Config:
374
- """Get the singleton Config instance."""
375
- return Config()
@@ -1,31 +0,0 @@
1
- You are a {{ role }}, using the name Janito .
2
- You will be assisting an user using a computer system on a {{ platform }} platform.
3
- You can find more about the current project using the tools in the workspace directory.
4
- If the question is related to the project, use the tools using the relative path, filename instead of /filename.
5
-
6
- If creating or editing files with a large number of lines, organize them into smaller files.
7
- If creating or editing files in an existing directory check surrounding files for the used patterns.
8
-
9
- # Structure Discovery (docs/STRUCTURE.md from current directory)
10
- Always start exploring the project by viewing for the file docs/STRUCTURE.md.
11
- Do not track files or directories wich are in .gitignore in the structure.
12
- At the end of responding to the user, update the structure file based on the files and directories you have interacted with,
13
- be precise focusing on the most important files and directories, avoid adding extra information like architecture or design patterns.
14
-
15
- # Tools
16
- The bash tool does not support commands which will require user input.
17
- Use the bash tool to get the current date or time when needed.
18
- Prefer the str_replace_editor tool to view directories and file contents.
19
-
20
- </IMPORTANT>
21
- Call the user_prompt tool when:
22
- - There are multiple options to apply a certain change
23
- - The next operation risk is moderated or high
24
- - The implementation plan is complex, requiring a review
25
- Proceed according to the user answer.
26
- <IMPORTANT/>
27
-
28
- When changing code in Python files, be mindful about the need to review the imports specially when new type hints are used (eg. Optional, Tuple, List, Dict, etc).
29
- After performing changes to a project in interfaces which are exposed to the user, respond to the user with a short summary on how to verify the changes. eg. "run cmd xpto", prefer to provide a command to run instead of a description.
30
- When displaying commands in instructions to the user, consider their platform.
31
- When creating html pages which refer to images that should be manually placed by the user, instead of broken links provide a frame with a placeholder image.
janito/token_report.py DELETED
@@ -1,154 +0,0 @@
1
- """
2
- Module for generating token usage reports.
3
- """
4
-
5
- from rich.console import Console
6
-
7
- def generate_token_report(agent, verbose=False, interrupted=False):
8
- """
9
- Generate a token usage report.
10
-
11
- Args:
12
- agent: The Claude agent instance
13
- verbose: Whether to show detailed token usage information
14
- interrupted: Whether the request was interrupted
15
-
16
- Returns:
17
- None - prints the report to the console
18
- """
19
- console = Console()
20
- usage = agent.get_tokens()
21
- cost = agent.get_token_cost()
22
-
23
- text_usage = usage.text_usage
24
- tools_usage = usage.tools_usage
25
-
26
- if verbose:
27
- total_usage = usage.total_usage
28
-
29
- # Get costs from the cost object
30
- text_input_cost = cost.input_cost
31
- text_output_cost = cost.output_cost
32
- text_cache_creation_cost = cost.cache_creation_cost
33
- text_cache_read_cost = cost.cache_read_cost
34
-
35
- tools_input_cost = cost.input_cost
36
- tools_output_cost = cost.output_cost
37
- tools_cache_creation_cost = cost.cache_creation_cost
38
- tools_cache_read_cost = cost.cache_read_cost
39
-
40
- # Format costs
41
- def format_cost(cost):
42
- return f"{cost * 100:.2f}¢ USD" if cost < 1.0 else f"${cost:.6f} USD"
43
-
44
- console.print("\n[bold blue]📊 Detailed Token Usage:[/bold blue]")
45
- console.print(f"📝 Text Input tokens: {text_usage.input_tokens}")
46
- console.print(f"📤 Text Output tokens: {text_usage.output_tokens}")
47
- console.print(f"💾 Text Cache Creation tokens: {text_usage.cache_creation_input_tokens}")
48
- console.print(f"📖 Text Cache Read tokens: {text_usage.cache_read_input_tokens}")
49
- console.print(f"📋 Text Total tokens: {text_usage.input_tokens + text_usage.output_tokens + text_usage.cache_creation_input_tokens + text_usage.cache_read_input_tokens}")
50
-
51
- console.print(f"🔧 Tool Input tokens: {tools_usage.input_tokens}")
52
- console.print(f"🔨 Tool Output tokens: {tools_usage.output_tokens}")
53
- console.print(f"💾 Tool Cache Creation tokens: {tools_usage.cache_creation_input_tokens}")
54
- console.print(f"📖 Tool Cache Read tokens: {tools_usage.cache_read_input_tokens}")
55
- console.print(f"🧰 Tool Total tokens: {tools_usage.input_tokens + tools_usage.output_tokens + tools_usage.cache_creation_input_tokens + tools_usage.cache_read_input_tokens}")
56
-
57
- console.print(f"🔢 Total tokens: {total_usage.input_tokens + total_usage.output_tokens + total_usage.cache_creation_input_tokens + total_usage.cache_read_input_tokens}")
58
-
59
- console.print("\n[bold blue]💰 Pricing Information:[/bold blue]")
60
- console.print(f"📝 Text Input cost: {format_cost(text_input_cost)}")
61
- console.print(f"📤 Text Output cost: {format_cost(text_output_cost)}")
62
- console.print(f"💾 Text Cache Creation cost: {format_cost(text_cache_creation_cost)}")
63
- console.print(f"📖 Text Cache Read cost: {format_cost(text_cache_read_cost)}")
64
- console.print(f"📋 Text Total cost: {format_cost(text_input_cost + text_output_cost + text_cache_creation_cost + text_cache_read_cost)}")
65
-
66
- console.print(f"🔧 Tool Input cost: {format_cost(tools_input_cost)}")
67
- console.print(f"🔨 Tool Output cost: {format_cost(tools_output_cost)}")
68
- console.print(f"💾 Tool Cache Creation cost: {format_cost(tools_cache_creation_cost)}")
69
- console.print(f"📖 Tool Cache Read cost: {format_cost(tools_cache_read_cost)}")
70
- console.print(f"🧰 Tool Total cost: {format_cost(tools_input_cost + tools_output_cost + tools_cache_creation_cost + tools_cache_read_cost)}")
71
-
72
- total_cost_text = f"💵 Total cost: {format_cost(text_input_cost + text_output_cost + text_cache_creation_cost + text_cache_read_cost + tools_input_cost + tools_output_cost + tools_cache_creation_cost + tools_cache_read_cost)}"
73
- if interrupted:
74
- total_cost_text += " (interrupted request not accounted)"
75
- console.print(total_cost_text)
76
-
77
- # Show cache delta if available
78
- if hasattr(cost, 'cache_delta') and cost.cache_delta:
79
- cache_delta = cost.cache_delta
80
- console.print(f"\n[bold green]💰 Cache Savings:[/bold green] {format_cost(cache_delta)}")
81
-
82
- # Calculate percentage savings
83
- total_cost_without_cache = cost.total_cost + cache_delta
84
- if total_cost_without_cache > 0:
85
- savings_percentage = (cache_delta / total_cost_without_cache) * 100
86
- console.print(f"[bold green]📊 Cache Savings Percentage:[/bold green] {savings_percentage:.2f}%")
87
- console.print(f"[bold green]💸 Cost without cache:[/bold green] {format_cost(total_cost_without_cache)}")
88
- console.print(f"[bold green]💲 Cost with cache:[/bold green] {format_cost(cost.total_cost)}")
89
-
90
- # Per-tool breakdown
91
- if usage.by_tool:
92
- console.print("\n[bold blue]🔧 Per-Tool Breakdown:[/bold blue]")
93
- try:
94
- if hasattr(cost, 'by_tool') and cost.by_tool:
95
- for tool_name, tool_usage in usage.by_tool.items():
96
- tool_input_cost = cost.by_tool[tool_name].input_cost
97
- tool_output_cost = cost.by_tool[tool_name].output_cost
98
- tool_cache_creation_cost = cost.by_tool[tool_name].cache_creation_cost
99
- tool_cache_read_cost = cost.by_tool[tool_name].cache_read_cost
100
- tool_total_cost = tool_input_cost + tool_output_cost + tool_cache_creation_cost + tool_cache_read_cost
101
-
102
- console.print(f" 🔧 Tool: {tool_name}")
103
- console.print(f" 📥 Input tokens: {tool_usage.input_tokens}")
104
- console.print(f" 📤 Output tokens: {tool_usage.output_tokens}")
105
- console.print(f" 💾 Cache Creation tokens: {tool_usage.cache_creation_input_tokens}")
106
- console.print(f" 📖 Cache Read tokens: {tool_usage.cache_read_input_tokens}")
107
- console.print(f" 🔢 Total tokens: {tool_usage.input_tokens + tool_usage.output_tokens + tool_usage.cache_creation_input_tokens + tool_usage.cache_read_input_tokens}")
108
- console.print(f" 💵 Total cost: {format_cost(tool_total_cost)}")
109
- else:
110
- # Calculate costs manually for each tool if cost.by_tool is not available
111
- for tool_name, tool_usage in usage.by_tool.items():
112
- # Estimate costs based on overall pricing
113
- total_tokens = tool_usage.input_tokens + tool_usage.output_tokens + tool_usage.cache_creation_input_tokens + tool_usage.cache_read_input_tokens
114
- estimated_cost = (total_tokens / (usage.total_usage.total_tokens + usage.total_usage.total_cache_tokens)) * cost.total_cost if usage.total_usage.total_tokens > 0 else 0
115
-
116
- console.print(f" 🔧 Tool: {tool_name}")
117
- console.print(f" 📥 Input tokens: {tool_usage.input_tokens}")
118
- console.print(f" 📤 Output tokens: {tool_usage.output_tokens}")
119
- console.print(f" 💾 Cache Creation tokens: {tool_usage.cache_creation_input_tokens}")
120
- console.print(f" 📖 Cache Read tokens: {tool_usage.cache_read_input_tokens}")
121
- console.print(f" 🔢 Total tokens: {tool_usage.input_tokens + tool_usage.output_tokens + tool_usage.cache_creation_input_tokens + tool_usage.cache_read_input_tokens}")
122
- console.print(f" 💵 Total cost: {format_cost(estimated_cost)}")
123
- except Exception as e:
124
- console.print(f"❌ Error: {str(e)}")
125
- else:
126
- total_tokens = (text_usage.input_tokens + text_usage.output_tokens +
127
- text_usage.cache_creation_input_tokens + text_usage.cache_read_input_tokens +
128
- tools_usage.input_tokens + tools_usage.output_tokens +
129
- tools_usage.cache_creation_input_tokens + tools_usage.cache_read_input_tokens)
130
-
131
- # Format costs
132
- def format_cost(cost):
133
- return f"{cost * 100:.2f}¢ USD" if cost < 1.0 else f"${cost:.6f} USD"
134
-
135
- # Prepare summary message
136
- cost_text = f"Cost: {format_cost(cost.total_cost)}"
137
- if interrupted:
138
- cost_text += " (interrupted request not accounted)"
139
-
140
- summary = f"Total tokens: {total_tokens} | {cost_text}"
141
-
142
- # Add cache savings if available
143
- if hasattr(cost, 'cache_delta') and cost.cache_delta != 0:
144
- cache_delta = cost.cache_delta
145
- total_cost_without_cache = cost.total_cost + cache_delta
146
- savings_percentage = 0
147
- if total_cost_without_cache > 0:
148
- savings_percentage = (cache_delta / total_cost_without_cache) * 100
149
-
150
- summary += f" | Cache savings: {format_cost(cache_delta)} ({savings_percentage:.1f}%)"
151
-
152
- # Display with a rule
153
- console.rule("[blue]Token Usage[/blue]")
154
- console.print(f"[blue]{summary}[/blue]", justify="center")
janito/tools/__init__.py DELETED
@@ -1,44 +0,0 @@
1
- """
2
- Janito tools package.
3
- """
4
-
5
- from .str_replace_editor import str_replace_editor
6
- from .find_files import find_files
7
- from .delete_file import delete_file
8
- from .search_text import search_text
9
- from .replace_file import replace_file
10
- from .prompt_user import prompt_user
11
- from .move_file import move_file
12
- from janito.tools.fetch_webpage import fetch_webpage
13
- from .think import think
14
- from .usage_tracker import get_tracker, reset_tracker, print_usage_stats
15
- from janito.config import get_config
16
-
17
- __all__ = ["str_replace_editor", "find_files", "delete_file", "search_text", "replace_file",
18
- "prompt_user", "move_file", "fetch_webpage", "think", "get_tools",
19
- "get_tracker", "reset_tracker", "print_usage_stats"]
20
-
21
- def get_tools():
22
- """
23
- Get a list of all available tools.
24
-
25
- Returns:
26
- List of tool functions (excluding str_replace_editor which is passed separately)
27
- If no_tools mode is enabled, returns an empty list
28
- If ask_mode is enabled, only returns tools that don't perform changes
29
- """
30
- # If no_tools mode is enabled, return an empty list
31
- if get_config().no_tools:
32
- return []
33
-
34
- # Tools that only read or view but don't modify anything
35
- read_only_tools = [find_files, search_text, prompt_user, fetch_webpage, think]
36
-
37
- # Tools that modify the filesystem
38
- write_tools = [delete_file, replace_file, move_file]
39
-
40
- # If ask_mode is enabled, only return read-only tools
41
- if get_config().ask_mode:
42
- return read_only_tools
43
- else:
44
- return read_only_tools + write_tools