janito 0.11.0__py3-none-any.whl → 0.12.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 +6 -204
- janito/callbacks.py +34 -132
- janito/cli/__init__.py +6 -0
- janito/cli/agent.py +287 -0
- janito/cli/app.py +86 -0
- janito/cli/commands.py +329 -0
- janito/cli/output.py +29 -0
- janito/cli/utils.py +22 -0
- janito/config.py +338 -121
- janito/data/instructions_template.txt +27 -0
- janito/token_report.py +154 -145
- janito/tools/__init__.py +38 -21
- janito/tools/bash/bash.py +82 -0
- janito/tools/bash/unix_persistent_bash.py +182 -0
- janito/tools/bash/win_persistent_bash.py +306 -0
- janito/tools/decorators.py +2 -13
- janito/tools/delete_file.py +27 -9
- janito/tools/fetch_webpage/__init__.py +34 -0
- janito/tools/fetch_webpage/chunking.py +76 -0
- janito/tools/fetch_webpage/core.py +155 -0
- janito/tools/fetch_webpage/extractors.py +276 -0
- janito/tools/fetch_webpage/news.py +137 -0
- janito/tools/fetch_webpage/utils.py +108 -0
- janito/tools/find_files.py +106 -44
- janito/tools/move_file.py +72 -0
- janito/tools/prompt_user.py +37 -6
- janito/tools/replace_file.py +31 -4
- janito/tools/rich_console.py +139 -0
- janito/tools/search_text.py +33 -21
- janito/tools/str_replace_editor/editor.py +7 -4
- janito/tools/str_replace_editor/handlers/__init__.py +16 -0
- janito/tools/str_replace_editor/handlers/create.py +60 -0
- janito/tools/str_replace_editor/handlers/insert.py +100 -0
- janito/tools/str_replace_editor/handlers/str_replace.py +92 -0
- janito/tools/str_replace_editor/handlers/undo.py +64 -0
- janito/tools/str_replace_editor/handlers/view.py +153 -0
- janito/tools/str_replace_editor/utils.py +0 -1
- janito/tools/usage_tracker.py +136 -0
- janito-0.12.0.dist-info/METADATA +203 -0
- janito-0.12.0.dist-info/RECORD +47 -0
- janito/chat_history.py +0 -117
- janito/data/instructions.txt +0 -4
- janito/tools/bash.py +0 -22
- janito/tools/str_replace_editor/handlers.py +0 -335
- janito-0.11.0.dist-info/METADATA +0 -86
- janito-0.11.0.dist-info/RECORD +0 -26
- {janito-0.11.0.dist-info → janito-0.12.0.dist-info}/WHEEL +0 -0
- {janito-0.11.0.dist-info → janito-0.12.0.dist-info}/entry_points.txt +0 -0
- {janito-0.11.0.dist-info → janito-0.12.0.dist-info}/licenses/LICENSE +0 -0
janito/__init__.py
CHANGED
janito/__main__.py
CHANGED
@@ -1,205 +1,7 @@
|
|
1
|
-
"""
|
2
|
-
Main entry point for Janito.
|
3
|
-
"""
|
4
|
-
import
|
5
|
-
|
6
|
-
|
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__":
|
1
|
+
"""
|
2
|
+
Main entry point for Janito.
|
3
|
+
"""
|
4
|
+
from janito.cli import app
|
5
|
+
|
6
|
+
if __name__ == "__main__":
|
205
7
|
app()
|
janito/callbacks.py
CHANGED
@@ -1,132 +1,34 @@
|
|
1
|
-
"""
|
2
|
-
Callback functions for tool execution in janito.
|
3
|
-
"""
|
4
|
-
|
5
|
-
from
|
6
|
-
from rich.
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
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="")
|
1
|
+
"""
|
2
|
+
Callback functions for tool execution in janito.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from rich.console import Console
|
6
|
+
from rich.markdown import Markdown
|
7
|
+
|
8
|
+
from janito.config import get_config
|
9
|
+
|
10
|
+
# Counter for pre-tool callbacks
|
11
|
+
pre_tool_callbacks = 0
|
12
|
+
|
13
|
+
def text_callback(text: str) -> None:
|
14
|
+
"""
|
15
|
+
Callback function that handles text output from the agent.
|
16
|
+
|
17
|
+
Args:
|
18
|
+
text: Text output from the agent
|
19
|
+
|
20
|
+
Returns:
|
21
|
+
None
|
22
|
+
"""
|
23
|
+
console = Console()
|
24
|
+
|
25
|
+
# Add debug counter only when debug mode is enabled
|
26
|
+
if get_config().debug_mode:
|
27
|
+
if not hasattr(text_callback, "counter"):
|
28
|
+
text_callback.counter = 1
|
29
|
+
console.print(f"[bold blue]DEBUG: Text callback #{text_callback.counter}[/bold blue]")
|
30
|
+
text_callback.counter += 1
|
31
|
+
|
32
|
+
# Print the text with markdown formatting
|
33
|
+
console.print(Markdown(text, code_theme="monokai"), end="")
|
34
|
+
|
janito/cli/__init__.py
ADDED
janito/cli/agent.py
ADDED
@@ -0,0 +1,287 @@
|
|
1
|
+
"""
|
2
|
+
Agent initialization and query handling for Janito CLI.
|
3
|
+
"""
|
4
|
+
import os
|
5
|
+
import sys
|
6
|
+
import json
|
7
|
+
import anthropic
|
8
|
+
import claudine
|
9
|
+
import typer
|
10
|
+
from rich.console import Console
|
11
|
+
from pathlib import Path
|
12
|
+
from jinja2 import Template
|
13
|
+
import importlib.resources as pkg_resources
|
14
|
+
|
15
|
+
from janito.config import get_config, Config
|
16
|
+
from janito.callbacks import text_callback
|
17
|
+
from janito.token_report import generate_token_report
|
18
|
+
from janito.tools import str_replace_editor
|
19
|
+
from janito.tools.bash.bash import bash_tool
|
20
|
+
from janito.cli.output import display_generation_params
|
21
|
+
|
22
|
+
console = Console()
|
23
|
+
|
24
|
+
def get_api_key() -> str:
|
25
|
+
"""
|
26
|
+
Get the API key from global config, environment variable, or user input.
|
27
|
+
|
28
|
+
Returns:
|
29
|
+
str: The API key
|
30
|
+
"""
|
31
|
+
# Get API key from global config, environment variable, or ask the user
|
32
|
+
api_key = Config.get_api_key()
|
33
|
+
|
34
|
+
# If not found in global config, try environment variable
|
35
|
+
if not api_key:
|
36
|
+
api_key = os.environ.get("ANTHROPIC_API_KEY")
|
37
|
+
|
38
|
+
# If still not found, prompt the user
|
39
|
+
if not api_key:
|
40
|
+
console.print("[bold yellow]⚠️ Warning:[/bold yellow] API key not found in global config or ANTHROPIC_API_KEY environment variable.")
|
41
|
+
console.print("🔑 Please set it using --set-api-key or provide your API key now:")
|
42
|
+
api_key = typer.prompt("Anthropic API Key", hide_input=True)
|
43
|
+
|
44
|
+
return api_key
|
45
|
+
|
46
|
+
def load_instructions() -> str:
|
47
|
+
"""
|
48
|
+
Load instructions template and render it with variables.
|
49
|
+
|
50
|
+
Returns:
|
51
|
+
str: The rendered instructions
|
52
|
+
"""
|
53
|
+
import platform
|
54
|
+
|
55
|
+
try:
|
56
|
+
# For Python 3.9+
|
57
|
+
try:
|
58
|
+
from importlib.resources import files
|
59
|
+
template_content = files('janito.data').joinpath('instructions_template.txt').read_text(encoding='utf-8')
|
60
|
+
# Fallback for older Python versions
|
61
|
+
except (ImportError, AttributeError):
|
62
|
+
template_content = pkg_resources.read_text('janito.data', 'instructions_template.txt', encoding='utf-8')
|
63
|
+
|
64
|
+
# Create template variables
|
65
|
+
template_variables = {
|
66
|
+
'platform': platform.system(),
|
67
|
+
'role': get_config().role,
|
68
|
+
# Add any other variables you want to pass to the template here
|
69
|
+
}
|
70
|
+
|
71
|
+
# Create template and render
|
72
|
+
template = Template(template_content)
|
73
|
+
instructions = template.render(**template_variables)
|
74
|
+
|
75
|
+
except Exception as e:
|
76
|
+
console.print(f"[bold red]❌ Error loading instructions template:[/bold red] {str(e)}")
|
77
|
+
# Try to fall back to regular instructions.txt
|
78
|
+
try:
|
79
|
+
# For Python 3.9+
|
80
|
+
try:
|
81
|
+
from importlib.resources import files
|
82
|
+
instructions = files('janito.data').joinpath('instructions.txt').read_text(encoding='utf-8')
|
83
|
+
# Fallback for older Python versions
|
84
|
+
except (ImportError, AttributeError):
|
85
|
+
instructions = pkg_resources.read_text('janito.data', 'instructions.txt', encoding='utf-8')
|
86
|
+
except Exception as e2:
|
87
|
+
console.print(f"[bold red]❌ Error loading fallback instructions:[/bold red] {str(e2)}")
|
88
|
+
instructions = "You are Janito, an AI assistant."
|
89
|
+
|
90
|
+
return instructions
|
91
|
+
|
92
|
+
def initialize_agent(temperature: float, verbose: bool) -> claudine.Agent:
|
93
|
+
"""
|
94
|
+
Initialize the Claude agent with tools and configuration.
|
95
|
+
|
96
|
+
Args:
|
97
|
+
temperature: Temperature value for model generation
|
98
|
+
verbose: Whether to enable verbose mode
|
99
|
+
|
100
|
+
Returns:
|
101
|
+
claudine.Agent: The initialized agent
|
102
|
+
"""
|
103
|
+
# Get API key
|
104
|
+
api_key = get_api_key()
|
105
|
+
|
106
|
+
# Load instructions
|
107
|
+
instructions = load_instructions()
|
108
|
+
|
109
|
+
# Get tools
|
110
|
+
from janito.tools import get_tools, reset_tracker
|
111
|
+
tools_list = get_tools()
|
112
|
+
|
113
|
+
# Reset usage tracker before each query
|
114
|
+
reset_tracker()
|
115
|
+
|
116
|
+
# Use command line parameters if provided (not default values), otherwise use config
|
117
|
+
temp_to_use = temperature if temperature != 0.0 else get_config().temperature
|
118
|
+
|
119
|
+
# Get profile parameters if a profile is set
|
120
|
+
config = get_config()
|
121
|
+
profile_data = None
|
122
|
+
if config.profile:
|
123
|
+
profile_data = config.get_available_profiles()[config.profile]
|
124
|
+
|
125
|
+
# Display generation parameters if verbose mode is enabled
|
126
|
+
if verbose:
|
127
|
+
display_generation_params(temp_to_use, profile_data, temperature)
|
128
|
+
|
129
|
+
# Create config_params dictionary with generation parameters
|
130
|
+
config_params = {
|
131
|
+
"temperature": temp_to_use
|
132
|
+
}
|
133
|
+
|
134
|
+
# Add top_k and top_p from profile if available
|
135
|
+
if profile_data:
|
136
|
+
if "top_k" in profile_data and profile_data["top_k"] != 0:
|
137
|
+
config_params["top_k"] = profile_data["top_k"]
|
138
|
+
if "top_p" in profile_data and profile_data["top_p"] != 0.0:
|
139
|
+
config_params["top_p"] = profile_data["top_p"]
|
140
|
+
|
141
|
+
# Initialize the agent
|
142
|
+
agent = claudine.Agent(
|
143
|
+
api_key=api_key,
|
144
|
+
system_prompt=instructions,
|
145
|
+
callbacks={"text": text_callback},
|
146
|
+
text_editor_tool=str_replace_editor,
|
147
|
+
bash_tool=bash_tool,
|
148
|
+
tools=tools_list,
|
149
|
+
verbose=verbose,
|
150
|
+
max_tokens=8126,
|
151
|
+
max_tool_rounds=100,
|
152
|
+
config_params=config_params,
|
153
|
+
)
|
154
|
+
|
155
|
+
return agent
|
156
|
+
|
157
|
+
def save_messages(agent):
|
158
|
+
"""
|
159
|
+
Save agent messages to .janito/last_message.json
|
160
|
+
|
161
|
+
Args:
|
162
|
+
agent: The claudine agent instance
|
163
|
+
"""
|
164
|
+
try:
|
165
|
+
# Get the workspace directory
|
166
|
+
workspace_dir = Path(get_config().workspace_dir)
|
167
|
+
|
168
|
+
# Create .janito directory if it doesn't exist
|
169
|
+
janito_dir = workspace_dir / ".janito"
|
170
|
+
janito_dir.mkdir(exist_ok=True)
|
171
|
+
|
172
|
+
# Get messages from the agent
|
173
|
+
messages = agent.get_messages()
|
174
|
+
|
175
|
+
# Save messages to file
|
176
|
+
with open(janito_dir / "last_message.json", "w", encoding="utf-8") as f:
|
177
|
+
json.dump(messages, f, ensure_ascii=False, indent=2)
|
178
|
+
|
179
|
+
if get_config().verbose:
|
180
|
+
console.print(f"[bold green]✅ Conversation saved to {janito_dir / 'last_message.json'}[/bold green]")
|
181
|
+
except Exception as e:
|
182
|
+
console.print(f"[bold red]❌ Error saving conversation:[/bold red] {str(e)}")
|
183
|
+
|
184
|
+
def load_messages():
|
185
|
+
"""
|
186
|
+
Load messages from .janito/last_message.json
|
187
|
+
|
188
|
+
Returns:
|
189
|
+
List of message dictionaries or None if file doesn't exist
|
190
|
+
"""
|
191
|
+
try:
|
192
|
+
# Get the workspace directory
|
193
|
+
workspace_dir = Path(get_config().workspace_dir)
|
194
|
+
|
195
|
+
# Check if file exists
|
196
|
+
messages_file = workspace_dir / ".janito" / "last_message.json"
|
197
|
+
if not messages_file.exists():
|
198
|
+
console.print("[bold yellow]⚠️ No previous conversation found[/bold yellow]")
|
199
|
+
return None
|
200
|
+
|
201
|
+
# Load messages from file
|
202
|
+
with open(messages_file, "r", encoding="utf-8") as f:
|
203
|
+
messages = json.load(f)
|
204
|
+
|
205
|
+
if get_config().verbose:
|
206
|
+
console.print(f"[bold green]✅ Loaded previous conversation from {messages_file}[/bold green]")
|
207
|
+
console.print(f"[dim]📝 Conversation has {len(messages)} messages[/dim]")
|
208
|
+
|
209
|
+
return messages
|
210
|
+
except Exception as e:
|
211
|
+
console.print(f"[bold red]❌ Error loading previous conversation:[/bold red] {str(e)}")
|
212
|
+
return None
|
213
|
+
|
214
|
+
def handle_query(query: str, temperature: float, verbose: bool, show_tokens: bool, continue_conversation: bool = False) -> None:
|
215
|
+
"""
|
216
|
+
Handle a query by initializing the agent and sending the query.
|
217
|
+
|
218
|
+
Args:
|
219
|
+
query: The query to send to the agent
|
220
|
+
temperature: Temperature value for model generation
|
221
|
+
verbose: Whether to enable verbose mode
|
222
|
+
show_tokens: Whether to show detailed token usage
|
223
|
+
continue_conversation: Whether to continue the previous conversation
|
224
|
+
"""
|
225
|
+
# Initialize the agent
|
226
|
+
agent = initialize_agent(temperature, verbose)
|
227
|
+
|
228
|
+
# Load previous messages if continuing conversation
|
229
|
+
if continue_conversation:
|
230
|
+
messages = load_messages()
|
231
|
+
if messages:
|
232
|
+
agent.set_messages(messages)
|
233
|
+
console.print("[bold blue]🔄 Continuing previous conversation[/bold blue]")
|
234
|
+
|
235
|
+
# Send the query to the agent
|
236
|
+
try:
|
237
|
+
agent.query(query)
|
238
|
+
|
239
|
+
# Save messages after successful query
|
240
|
+
save_messages(agent)
|
241
|
+
|
242
|
+
# Print token usage report
|
243
|
+
if show_tokens:
|
244
|
+
generate_token_report(agent, verbose=True, interrupted=False)
|
245
|
+
else:
|
246
|
+
# Show basic token usage
|
247
|
+
generate_token_report(agent, verbose=False, interrupted=False)
|
248
|
+
|
249
|
+
# Print tool usage statistics
|
250
|
+
from janito.tools import print_usage_stats
|
251
|
+
print_usage_stats()
|
252
|
+
|
253
|
+
except KeyboardInterrupt:
|
254
|
+
# Handle Ctrl+C by printing token and tool usage information
|
255
|
+
console.print("\n[bold yellow]⚠️ Query interrupted by user (Ctrl+C)[/bold yellow]")
|
256
|
+
|
257
|
+
# Save messages even if interrupted
|
258
|
+
save_messages(agent)
|
259
|
+
|
260
|
+
# Print token usage report (even if interrupted)
|
261
|
+
try:
|
262
|
+
if show_tokens:
|
263
|
+
generate_token_report(agent, verbose=True, interrupted=True)
|
264
|
+
else:
|
265
|
+
# Show basic token usage
|
266
|
+
generate_token_report(agent, verbose=False, interrupted=True)
|
267
|
+
|
268
|
+
# Print tool usage statistics
|
269
|
+
from janito.tools import print_usage_stats
|
270
|
+
print_usage_stats()
|
271
|
+
except Exception as e:
|
272
|
+
console.print(f"[bold red]❌ Error generating usage report:[/bold red] {str(e)}")
|
273
|
+
if verbose:
|
274
|
+
import traceback
|
275
|
+
console.print(traceback.format_exc())
|
276
|
+
|
277
|
+
# Exit with non-zero status to indicate interruption
|
278
|
+
sys.exit(130) # 130 is the standard exit code for SIGINT
|
279
|
+
|
280
|
+
except anthropic.APIError as e:
|
281
|
+
console.print(f"[bold red]❌ Anthropic API Error:[/bold red] {str(e)}")
|
282
|
+
|
283
|
+
except Exception as e:
|
284
|
+
console.print(f"[bold red]❌ Error:[/bold red] {str(e)}")
|
285
|
+
if verbose:
|
286
|
+
import traceback
|
287
|
+
console.print(traceback.format_exc())
|