janito 0.10.0__py3-none-any.whl → 0.11.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/__main__.py +205 -151
- janito/callbacks.py +132 -130
- janito/chat_history.py +117 -0
- janito/config.py +64 -6
- janito/data/instructions.txt +4 -6
- janito/token_report.py +145 -73
- janito/tools/__init__.py +21 -10
- janito/tools/bash.py +22 -0
- janito/tools/decorators.py +101 -84
- janito/tools/delete_file.py +47 -44
- janito/tools/find_files.py +11 -7
- janito/tools/prompt_user.py +26 -0
- janito/tools/replace_file.py +36 -0
- janito/tools/search_text.py +226 -196
- janito/tools/str_replace_editor/editor.py +52 -43
- janito/tools/str_replace_editor/handlers.py +102 -105
- janito/tools/str_replace_editor/utils.py +8 -62
- {janito-0.10.0.dist-info → janito-0.11.0.dist-info}/METADATA +86 -88
- janito-0.11.0.dist-info/RECORD +26 -0
- {janito-0.10.0.dist-info → janito-0.11.0.dist-info}/WHEEL +1 -2
- janito/cli.py +0 -202
- janito-0.10.0.dist-info/RECORD +0 -23
- janito-0.10.0.dist-info/top_level.txt +0 -1
- {janito-0.10.0.dist-info → janito-0.11.0.dist-info}/entry_points.txt +0 -0
- {janito-0.10.0.dist-info → janito-0.11.0.dist-info/licenses}/LICENSE +0 -0
janito/__init__.py
CHANGED
janito/__main__.py
CHANGED
@@ -1,151 +1,205 @@
|
|
1
|
-
"""
|
2
|
-
Main entry point for
|
3
|
-
"""
|
4
|
-
|
5
|
-
import
|
6
|
-
import
|
7
|
-
import
|
8
|
-
|
9
|
-
from
|
10
|
-
|
11
|
-
from
|
12
|
-
from
|
13
|
-
import
|
14
|
-
from
|
15
|
-
import
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
1
|
+
"""
|
2
|
+
Main entry point for Janito.
|
3
|
+
"""
|
4
|
+
import os
|
5
|
+
import sys
|
6
|
+
from typing import Optional
|
7
|
+
import importlib.resources
|
8
|
+
import typer
|
9
|
+
from rich.console import Console
|
10
|
+
import anthropic
|
11
|
+
from janito.config import get_config
|
12
|
+
from janito.chat_history import store_conversation, get_chat_history_context
|
13
|
+
from janito.callbacks import pre_tool_callback, post_tool_callback, text_callback
|
14
|
+
from janito.token_report import generate_token_report
|
15
|
+
from janito.tools import str_replace_editor
|
16
|
+
from janito.tools.bash import bash_tool
|
17
|
+
import claudine
|
18
|
+
|
19
|
+
app = typer.Typer()
|
20
|
+
|
21
|
+
@app.command()
|
22
|
+
def create_tool(name: str = typer.Argument(..., help="Name of the tool to create")):
|
23
|
+
"""
|
24
|
+
Create a new tool with the given name.
|
25
|
+
"""
|
26
|
+
console = Console()
|
27
|
+
|
28
|
+
# Ensure workspace is set
|
29
|
+
workspace_dir = get_config().workspace_dir
|
30
|
+
|
31
|
+
# Create the tools directory if it doesn't exist
|
32
|
+
tools_dir = os.path.join(workspace_dir, "tools")
|
33
|
+
os.makedirs(tools_dir, exist_ok=True)
|
34
|
+
|
35
|
+
# Create the tool file
|
36
|
+
tool_file = os.path.join(tools_dir, f"{name}.py")
|
37
|
+
|
38
|
+
# Check if the file already exists
|
39
|
+
if os.path.exists(tool_file):
|
40
|
+
console.print(f"[bold red]Error:[/bold red] Tool file already exists: {tool_file}")
|
41
|
+
return
|
42
|
+
|
43
|
+
# Create the tool file with a template
|
44
|
+
template = f'''"""
|
45
|
+
{name} tool for Janito.
|
46
|
+
"""
|
47
|
+
|
48
|
+
def {name}(param1: str) -> str:
|
49
|
+
"""
|
50
|
+
Description of the {name} tool.
|
51
|
+
|
52
|
+
Args:
|
53
|
+
param1: Description of param1
|
54
|
+
|
55
|
+
Returns:
|
56
|
+
str: Description of return value
|
57
|
+
"""
|
58
|
+
# TODO: Implement the tool
|
59
|
+
return f"Executed {name} with param1={{param1}}"
|
60
|
+
'''
|
61
|
+
|
62
|
+
with open(tool_file, "w") as f:
|
63
|
+
f.write(template)
|
64
|
+
|
65
|
+
console.print(f"[bold green]Created tool:[/bold green] {tool_file}")
|
66
|
+
|
67
|
+
@app.callback(invoke_without_command=True)
|
68
|
+
def main(ctx: typer.Context,
|
69
|
+
query: Optional[str] = typer.Argument(None, help="Query to send to the claudine agent"),
|
70
|
+
verbose: bool = typer.Option(False, "--verbose", "-v", help="Enable verbose mode with detailed output"),
|
71
|
+
show_tokens: bool = typer.Option(False, "--show-tokens", "-t", help="Show detailed token usage and pricing information"),
|
72
|
+
workspace: Optional[str] = typer.Option(None, "--workspace", "-w", help="Set the workspace directory"),
|
73
|
+
config_str: Optional[str] = typer.Option(None, "--set-config", help="Configuration string in format 'key=value', e.g., 'context=5' for number of history messages to include"),
|
74
|
+
show_config: bool = typer.Option(False, "--show-config", help="Show current configuration")):
|
75
|
+
"""
|
76
|
+
Janito CLI tool. If a query is provided without a command, it will be sent to the claudine agent.
|
77
|
+
"""
|
78
|
+
console = Console()
|
79
|
+
|
80
|
+
# Set verbose mode in config
|
81
|
+
get_config().verbose = verbose
|
82
|
+
|
83
|
+
if workspace:
|
84
|
+
try:
|
85
|
+
print(f"Setting workspace directory to: {workspace}")
|
86
|
+
get_config().workspace_dir = workspace
|
87
|
+
print(f"Workspace directory set to: {get_config().workspace_dir}")
|
88
|
+
except ValueError as e:
|
89
|
+
console.print(f"[bold red]Error:[/bold red] {str(e)}")
|
90
|
+
sys.exit(1)
|
91
|
+
|
92
|
+
# Show current configuration if requested
|
93
|
+
if show_config:
|
94
|
+
config = get_config()
|
95
|
+
console.print("[bold blue]Current Configuration:[/bold blue]")
|
96
|
+
console.print(f"[bold]Workspace Directory:[/bold] {config.workspace_dir}")
|
97
|
+
console.print(f"[bold]Verbose Mode:[/bold] {'Enabled' if config.verbose else 'Disabled'}")
|
98
|
+
console.print(f"[bold]Chat History Context Count:[/bold] {config.history_context_count} messages")
|
99
|
+
# Exit if this was the only operation requested
|
100
|
+
if ctx.invoked_subcommand is None and not query:
|
101
|
+
sys.exit(0)
|
102
|
+
|
103
|
+
# Handle the --set-config parameter
|
104
|
+
if config_str is not None:
|
105
|
+
try:
|
106
|
+
# Parse the config string
|
107
|
+
if "context=" in config_str:
|
108
|
+
context_value = config_str.split("context=")[1].strip()
|
109
|
+
# If there are other configs after context, extract just the number
|
110
|
+
if " " in context_value:
|
111
|
+
context_value = context_value.split(" ")[0]
|
112
|
+
|
113
|
+
try:
|
114
|
+
context_value = int(context_value)
|
115
|
+
if context_value < 0:
|
116
|
+
console.print("[bold red]Error:[/bold red] History context count must be a non-negative integer")
|
117
|
+
return
|
118
|
+
|
119
|
+
get_config().history_context_count = context_value
|
120
|
+
console.print(f"[bold green]Chat history context count set to {context_value} messages[/bold green]")
|
121
|
+
except ValueError:
|
122
|
+
console.print(f"[bold red]Error:[/bold red] Invalid context value: {context_value}. Must be an integer.")
|
123
|
+
else:
|
124
|
+
console.print(f"[bold yellow]Warning:[/bold yellow] Unsupported configuration in: {config_str}")
|
125
|
+
except Exception as e:
|
126
|
+
console.print(f"[bold red]Error:[/bold red] {str(e)}")
|
127
|
+
|
128
|
+
if ctx.invoked_subcommand is None:
|
129
|
+
# If no query provided in command line, read from stdin
|
130
|
+
if not query:
|
131
|
+
console.print("[bold blue]No query provided in command line. Reading from stdin...[/bold blue]")
|
132
|
+
query = sys.stdin.read().strip()
|
133
|
+
|
134
|
+
# Only proceed if we have a query (either from command line or stdin)
|
135
|
+
if query:
|
136
|
+
# Get API key from environment variable or ask the user
|
137
|
+
api_key = os.environ.get("ANTHROPIC_API_KEY")
|
138
|
+
if not api_key:
|
139
|
+
console.print("[bold yellow]Warning:[/bold yellow] ANTHROPIC_API_KEY environment variable not set.")
|
140
|
+
console.print("Please set it or provide your API key now:")
|
141
|
+
api_key = typer.prompt("Anthropic API Key", hide_input=True)
|
142
|
+
|
143
|
+
# Load instructions from file
|
144
|
+
import importlib.resources as pkg_resources
|
145
|
+
try:
|
146
|
+
# For Python 3.9+
|
147
|
+
try:
|
148
|
+
from importlib.resources import files
|
149
|
+
instructions = files('janito.data').joinpath('instructions.txt').read_text(encoding='utf-8')
|
150
|
+
# Fallback for older Python versions
|
151
|
+
except (ImportError, AttributeError):
|
152
|
+
instructions = pkg_resources.read_text('janito.data', 'instructions.txt', encoding='utf-8')
|
153
|
+
except Exception as e:
|
154
|
+
console.print(f"[bold red]Error loading instructions:[/bold red] {str(e)}")
|
155
|
+
instructions = "You are Janito, an AI assistant."
|
156
|
+
|
157
|
+
# Temporarily disable chat history
|
158
|
+
# Get chat history context
|
159
|
+
# chat_history = get_chat_history_context(get_config().history_context_count)
|
160
|
+
# if chat_history:
|
161
|
+
# console.print("[dim]Loaded chat history from previous sessions.[/dim]")
|
162
|
+
# # Append chat history to instructions
|
163
|
+
# instructions = f"{instructions}\n\n{chat_history}"
|
164
|
+
|
165
|
+
# Get tools
|
166
|
+
from janito.tools import get_tools
|
167
|
+
tools_list = get_tools()
|
168
|
+
|
169
|
+
# Initialize the agent with the tools
|
170
|
+
agent = claudine.Agent(
|
171
|
+
api_key=api_key,
|
172
|
+
system_prompt=instructions,
|
173
|
+
callbacks={"pre_tool": pre_tool_callback, "post_tool": post_tool_callback, "text": text_callback},
|
174
|
+
text_editor_tool=str_replace_editor,
|
175
|
+
#bash_tool=bash_tool,
|
176
|
+
tools=tools_list,
|
177
|
+
verbose=verbose
|
178
|
+
)
|
179
|
+
|
180
|
+
# Send the query to the agent
|
181
|
+
try:
|
182
|
+
agent.query(query)
|
183
|
+
|
184
|
+
# Temporarily disable storing conversation in chat history
|
185
|
+
# Store the conversation in chat history
|
186
|
+
# store_conversation(query, response, agent)
|
187
|
+
|
188
|
+
# Print token usage report if show_tokens mode is enabled
|
189
|
+
if show_tokens:
|
190
|
+
generate_token_report(agent, verbose=True)
|
191
|
+
else:
|
192
|
+
# Show basic token usage
|
193
|
+
generate_token_report(agent, verbose=False)
|
194
|
+
|
195
|
+
except anthropic.APIError as e:
|
196
|
+
console.print(f"[bold red]Anthropic API Error:[/bold red] {str(e)}")
|
197
|
+
|
198
|
+
except Exception as e:
|
199
|
+
console.print(f"[bold red]Error:[/bold red] {str(e)}")
|
200
|
+
if verbose:
|
201
|
+
import traceback
|
202
|
+
console.print(traceback.format_exc())
|
203
|
+
|
204
|
+
if __name__ == "__main__":
|
205
|
+
app()
|
janito/callbacks.py
CHANGED
@@ -1,130 +1,132 @@
|
|
1
|
-
"""
|
2
|
-
Callback functions for tool execution in janito.
|
3
|
-
"""
|
4
|
-
|
5
|
-
from typing import Dict, Any, Tuple
|
6
|
-
from rich.console import Console
|
7
|
-
from rich.markdown import Markdown
|
8
|
-
|
9
|
-
from janito.config import get_config
|
10
|
-
from janito.tools import find_files
|
11
|
-
from janito.tools.str_replace_editor.editor import str_replace_editor
|
12
|
-
from janito.tools.delete_file import delete_file
|
13
|
-
from janito.tools.search_text import search_text
|
14
|
-
from janito.tools.decorators import format_tool_label
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
pre_tool_callback.counter
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
content,
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
1
|
+
"""
|
2
|
+
Callback functions for tool execution in janito.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from typing import Dict, Any, Tuple, Optional, List
|
6
|
+
from rich.console import Console
|
7
|
+
from rich.markdown import Markdown
|
8
|
+
|
9
|
+
from janito.config import get_config
|
10
|
+
from janito.tools import find_files
|
11
|
+
from janito.tools.str_replace_editor.editor import str_replace_editor
|
12
|
+
from janito.tools.delete_file import delete_file
|
13
|
+
from janito.tools.search_text import search_text
|
14
|
+
from janito.tools.decorators import format_tool_label
|
15
|
+
|
16
|
+
# Note: ConsoleCallback has been removed as we're using pre_tool and post_tool callbacks directly
|
17
|
+
|
18
|
+
def pre_tool_callback(tool_name: str, tool_input: Dict[str, Any]) -> Tuple[Dict[str, Any], bool]:
|
19
|
+
"""
|
20
|
+
Callback function that runs before a tool is executed.
|
21
|
+
|
22
|
+
Args:
|
23
|
+
tool_name: Name of the tool being called
|
24
|
+
tool_input: Input parameters for the tool
|
25
|
+
|
26
|
+
Returns:
|
27
|
+
Tuple of (modified tool input, whether to cancel the tool call)
|
28
|
+
"""
|
29
|
+
console = Console()
|
30
|
+
|
31
|
+
# Add debug counter only when verbose mode is enabled
|
32
|
+
if get_config().verbose:
|
33
|
+
if not hasattr(pre_tool_callback, "counter"):
|
34
|
+
pre_tool_callback.counter = 1
|
35
|
+
console.print(f"[bold green]DEBUG: Starting tool call #{pre_tool_callback.counter}[/bold green]")
|
36
|
+
|
37
|
+
# Print the tool name and input
|
38
|
+
console.print(f"[bold green]Tool:[/bold green] {tool_name}")
|
39
|
+
console.print(f"[bold green]Input:[/bold green] {tool_input}")
|
40
|
+
else:
|
41
|
+
# For non-debug mode, just print a simple message
|
42
|
+
# Find the tool function
|
43
|
+
tool_func = None
|
44
|
+
if tool_name == "find_files":
|
45
|
+
tool_func = find_files
|
46
|
+
elif tool_name == "str_replace_editor":
|
47
|
+
tool_func = str_replace_editor
|
48
|
+
elif tool_name == "delete_file":
|
49
|
+
tool_func = delete_file
|
50
|
+
elif tool_name == "search_text":
|
51
|
+
tool_func = search_text
|
52
|
+
|
53
|
+
# Format the input for display
|
54
|
+
display_input = ""
|
55
|
+
if "path" in tool_input:
|
56
|
+
display_input = tool_input["path"]
|
57
|
+
elif "file_path" in tool_input:
|
58
|
+
display_input = tool_input["file_path"]
|
59
|
+
|
60
|
+
# Print formatted tool label if available
|
61
|
+
formatted_label = format_tool_label(tool_func, tool_input)
|
62
|
+
if formatted_label:
|
63
|
+
console.print("[bold cyan] Tool:[/bold cyan]", formatted_label, end=" → ")
|
64
|
+
else:
|
65
|
+
console.print("[bold cyan] Tool:[/bold cyan]", f"{tool_name} {display_input}", end=" → ")
|
66
|
+
|
67
|
+
return tool_input, True # Continue with the tool call
|
68
|
+
|
69
|
+
def post_tool_callback(tool_name: str, tool_input: Dict[str, Any], result: Any) -> Any:
|
70
|
+
"""
|
71
|
+
Callback function that runs after a tool is executed.
|
72
|
+
|
73
|
+
Args:
|
74
|
+
tool_name: Name of the tool that was called
|
75
|
+
tool_input: Input parameters for the tool
|
76
|
+
result: Result of the tool call
|
77
|
+
|
78
|
+
Returns:
|
79
|
+
Modified result
|
80
|
+
"""
|
81
|
+
console = Console()
|
82
|
+
|
83
|
+
# Add debug counter only when verbose mode is enabled
|
84
|
+
if get_config().verbose:
|
85
|
+
if not hasattr(post_tool_callback, "counter"):
|
86
|
+
post_tool_callback.counter = 1
|
87
|
+
console.print(f"[bold green]DEBUG: Completed tool call #{post_tool_callback.counter}[/bold green]")
|
88
|
+
post_tool_callback.counter += 1
|
89
|
+
|
90
|
+
# Show the number of lines in the result content
|
91
|
+
if isinstance(result, tuple) and len(result) >= 1:
|
92
|
+
content, is_error = result
|
93
|
+
# Define prefix icon based on is_error
|
94
|
+
icon_prefix = "❌ " if is_error else "✅ "
|
95
|
+
|
96
|
+
if isinstance(content, str):
|
97
|
+
# Count the number of lines in the content
|
98
|
+
line_count = content.count('\n') + 1 if content else 0
|
99
|
+
console.print(f"{icon_prefix}{line_count} items")
|
100
|
+
else:
|
101
|
+
console.print(f"{icon_prefix}{content}")
|
102
|
+
else:
|
103
|
+
# If result is not a tuple, convert to string and count lines
|
104
|
+
result_str = str(result)
|
105
|
+
# Default to success icon when no error status is available
|
106
|
+
icon_prefix = "✅ "
|
107
|
+
line_count = result_str.count('\n') + 1 if result_str else 0
|
108
|
+
console.print(f"{icon_prefix}{line_count} lines")
|
109
|
+
|
110
|
+
return result
|
111
|
+
|
112
|
+
def text_callback(text: str) -> None:
|
113
|
+
"""
|
114
|
+
Callback function that handles text output from the agent.
|
115
|
+
|
116
|
+
Args:
|
117
|
+
text: Text output from the agent
|
118
|
+
|
119
|
+
Returns:
|
120
|
+
None
|
121
|
+
"""
|
122
|
+
console = Console()
|
123
|
+
|
124
|
+
# Add debug counter only when debug mode is enabled
|
125
|
+
if get_config().debug_mode:
|
126
|
+
if not hasattr(text_callback, "counter"):
|
127
|
+
text_callback.counter = 1
|
128
|
+
console.print(f"[bold blue]DEBUG: Text callback #{text_callback.counter}[/bold blue]")
|
129
|
+
text_callback.counter += 1
|
130
|
+
|
131
|
+
# Print the text with markdown formatting
|
132
|
+
console.print("[bold magenta]Janito:[/bold magenta] ", Markdown(text, code_theme="monokai"), end="")
|