janito 1.4.0__py3-none-any.whl → 1.5.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/agent/__init__.py +0 -1
- janito/agent/agent.py +7 -25
- janito/agent/config.py +4 -6
- janito/agent/config_defaults.py +2 -2
- janito/agent/content_handler.py +0 -0
- janito/agent/conversation.py +63 -37
- janito/agent/message_handler.py +18 -0
- janito/agent/openai_schema_generator.py +116 -0
- janito/agent/queued_message_handler.py +32 -0
- janito/agent/rich_tool_handler.py +43 -0
- janito/agent/runtime_config.py +1 -1
- janito/agent/templates/system_instructions.j2 +10 -4
- janito/agent/tool_registry.py +92 -0
- janito/agent/tools/append_text_to_file.py +41 -0
- janito/agent/tools/ask_user.py +16 -3
- janito/agent/tools/create_directory.py +31 -0
- janito/agent/tools/create_file.py +52 -0
- janito/agent/tools/fetch_url.py +23 -8
- janito/agent/tools/find_files.py +40 -21
- janito/agent/tools/get_file_outline.py +26 -8
- janito/agent/tools/get_lines.py +53 -19
- janito/agent/tools/move_file.py +50 -0
- janito/agent/tools/py_compile.py +27 -11
- janito/agent/tools/python_exec.py +43 -14
- janito/agent/tools/remove_directory.py +23 -7
- janito/agent/tools/remove_file.py +38 -0
- janito/agent/tools/replace_text_in_file.py +40 -17
- janito/agent/tools/run_bash_command.py +107 -80
- janito/agent/tools/search_files.py +38 -19
- janito/agent/tools/tool_base.py +30 -3
- janito/agent/tools/tools_utils.py +11 -0
- janito/agent/tools/utils.py +0 -1
- janito/cli/_print_config.py +1 -1
- janito/cli/arg_parser.py +2 -1
- janito/cli/config_commands.py +3 -6
- janito/cli/main.py +2 -2
- janito/cli/runner.py +18 -14
- janito/cli_chat_shell/chat_loop.py +10 -15
- janito/cli_chat_shell/commands.py +8 -3
- janito/cli_chat_shell/config_shell.py +0 -3
- janito/cli_chat_shell/session_manager.py +11 -0
- janito/cli_chat_shell/ui.py +12 -113
- janito/render_prompt.py +0 -1
- janito/rich_utils.py +30 -0
- janito/web/app.py +10 -12
- janito-1.5.0.dist-info/METADATA +176 -0
- janito-1.5.0.dist-info/RECORD +64 -0
- janito/agent/queued_tool_handler.py +0 -16
- janito/agent/tool_handler.py +0 -196
- janito/agent/tools/file_ops.py +0 -114
- janito/agent/tools/rich_utils.py +0 -31
- janito-1.4.0.dist-info/METADATA +0 -142
- janito-1.4.0.dist-info/RECORD +0 -55
- {janito-1.4.0.dist-info → janito-1.5.0.dist-info}/WHEEL +0 -0
- {janito-1.4.0.dist-info → janito-1.5.0.dist-info}/entry_points.txt +0 -0
- {janito-1.4.0.dist-info → janito-1.5.0.dist-info}/licenses/LICENSE +0 -0
- {janito-1.4.0.dist-info → janito-1.5.0.dist-info}/top_level.txt +0 -0
janito/cli/runner.py
CHANGED
@@ -1,14 +1,11 @@
|
|
1
1
|
import sys
|
2
|
-
import os
|
3
2
|
from rich.console import Console
|
4
|
-
from rich.markdown import Markdown
|
5
3
|
from janito.render_prompt import render_system_prompt
|
6
4
|
from janito.agent.agent import Agent
|
7
5
|
from janito.agent.conversation import MaxRoundsExceededError, EmptyResponseError, ProviderError
|
8
6
|
from janito.agent.runtime_config import unified_config, runtime_config
|
9
7
|
from janito.agent.config import get_api_key
|
10
8
|
from janito import __version__
|
11
|
-
from rich.rule import Rule
|
12
9
|
|
13
10
|
|
14
11
|
def format_tokens(n):
|
@@ -56,8 +53,8 @@ def run_cli(args):
|
|
56
53
|
runtime_config.set('system_prompt_file', args.system_file)
|
57
54
|
|
58
55
|
else:
|
59
|
-
system_prompt = args.
|
60
|
-
if args.
|
56
|
+
system_prompt = args.system or unified_config.get("system_prompt")
|
57
|
+
if args.system:
|
61
58
|
runtime_config.set('system_prompt', system_prompt)
|
62
59
|
if system_prompt is None:
|
63
60
|
# Pass full merged config (runtime overrides effective)
|
@@ -81,11 +78,18 @@ def run_cli(args):
|
|
81
78
|
model = unified_config.get('model')
|
82
79
|
base_url = unified_config.get('base_url', 'https://openrouter.ai/api/v1')
|
83
80
|
azure_openai_api_version = unified_config.get('azure_openai_api_version', '2023-05-15')
|
84
|
-
# Handle
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
81
|
+
# Handle vanilla mode
|
82
|
+
vanilla_mode = getattr(args, 'vanilla', False)
|
83
|
+
if vanilla_mode:
|
84
|
+
runtime_config.set('vanilla_mode', True)
|
85
|
+
system_prompt = None
|
86
|
+
runtime_config.set('system_prompt', None)
|
87
|
+
# Only set temperature if explicitly provided
|
88
|
+
if args.temperature is None:
|
89
|
+
runtime_config.set('temperature', None)
|
90
|
+
else:
|
91
|
+
runtime_config.set('vanilla_mode', False)
|
92
|
+
agent = Agent(api_key=api_key, model=model, system_prompt=system_prompt, verbose_tools=args.verbose_tools, base_url=base_url, azure_openai_api_version=azure_openai_api_version, use_azure_openai=unified_config.get('use_azure_openai', False))
|
89
93
|
|
90
94
|
# Save runtime max_tokens override if provided
|
91
95
|
if args.max_tokens is not None:
|
@@ -100,10 +104,10 @@ def run_cli(args):
|
|
100
104
|
prompt = args.prompt
|
101
105
|
|
102
106
|
console = Console()
|
107
|
+
from janito.agent.rich_tool_handler import MessageHandler
|
108
|
+
message_handler = MessageHandler()
|
103
109
|
|
104
|
-
|
105
|
-
content = data.get("content", "")
|
106
|
-
console.print(Markdown(content))
|
110
|
+
# Removed on_content logic; use message_handler pattern only
|
107
111
|
|
108
112
|
messages = []
|
109
113
|
if agent.system_prompt:
|
@@ -116,7 +120,7 @@ def run_cli(args):
|
|
116
120
|
max_rounds = runtime_config.get('max_rounds', 50)
|
117
121
|
response = agent.chat(
|
118
122
|
messages,
|
119
|
-
|
123
|
+
message_handler=message_handler,
|
120
124
|
spinner=True,
|
121
125
|
max_rounds=max_rounds,
|
122
126
|
)
|
@@ -1,5 +1,4 @@
|
|
1
|
-
from
|
2
|
-
from rich.markdown import Markdown
|
1
|
+
from janito.agent.rich_tool_handler import MessageHandler
|
3
2
|
from prompt_toolkit.history import InMemoryHistory
|
4
3
|
from .session_manager import load_last_conversation, load_input_history
|
5
4
|
from .ui import print_welcome, get_toolbar_func, get_prompt_session
|
@@ -11,7 +10,8 @@ from janito.agent.conversation import EmptyResponseError, ProviderError
|
|
11
10
|
|
12
11
|
|
13
12
|
def start_chat_shell(agent, continue_session=False, max_rounds=50):
|
14
|
-
|
13
|
+
message_handler = MessageHandler()
|
14
|
+
console = message_handler.console
|
15
15
|
|
16
16
|
# Load input history
|
17
17
|
history_list = load_input_history()
|
@@ -45,7 +45,7 @@ def start_chat_shell(agent, continue_session=False, max_rounds=50):
|
|
45
45
|
state['messages'] = messages
|
46
46
|
state['last_usage_info'] = last_usage_info
|
47
47
|
state['mem_history'] = mem_history
|
48
|
-
|
48
|
+
message_handler.handle_message({'type': 'success', 'message': 'Restored last saved conversation.'})
|
49
49
|
|
50
50
|
# Add system prompt if needed
|
51
51
|
if agent.system_prompt and not any(m.get('role') == 'system' for m in messages):
|
@@ -97,10 +97,10 @@ def start_chat_shell(agent, continue_session=False, max_rounds=50):
|
|
97
97
|
try:
|
98
98
|
confirm = input("Do you really want to exit? (y/n): ").strip().lower()
|
99
99
|
except KeyboardInterrupt:
|
100
|
-
|
100
|
+
message_handler.handle_message({'type': 'error', 'message': 'Exiting...'})
|
101
101
|
break
|
102
102
|
if confirm == 'y':
|
103
|
-
|
103
|
+
message_handler.handle_message({'type': 'error', 'message': 'Exiting...'})
|
104
104
|
break
|
105
105
|
else:
|
106
106
|
continue
|
@@ -121,22 +121,17 @@ def start_chat_shell(agent, continue_session=False, max_rounds=50):
|
|
121
121
|
import time
|
122
122
|
start_time = time.time()
|
123
123
|
|
124
|
-
# Define streaming content handler
|
125
|
-
def on_content(chunk):
|
126
|
-
content_piece = chunk.get('content')
|
127
|
-
if content_piece:
|
128
|
-
console.print(Markdown(content_piece))
|
129
124
|
|
130
125
|
try:
|
131
|
-
response = agent.chat(messages,
|
126
|
+
response = agent.chat(messages, spinner=True, max_rounds=max_rounds, message_handler=message_handler)
|
132
127
|
except KeyboardInterrupt:
|
133
|
-
|
128
|
+
message_handler.handle_message({'type': 'info', 'message': 'Request interrupted. Returning to prompt.'})
|
134
129
|
continue
|
135
130
|
except ProviderError as e:
|
136
|
-
|
131
|
+
message_handler.handle_message({'type': 'error', 'message': f'Provider error: {e}'})
|
137
132
|
continue
|
138
133
|
except EmptyResponseError as e:
|
139
|
-
|
134
|
+
message_handler.handle_message({'type': 'error', 'message': f'Error: {e}'})
|
140
135
|
continue
|
141
136
|
last_elapsed = time.time() - start_time
|
142
137
|
|
@@ -4,7 +4,6 @@ import json
|
|
4
4
|
from prompt_toolkit.history import InMemoryHistory
|
5
5
|
from janito.render_prompt import render_system_prompt
|
6
6
|
from .load_prompt import load_prompt
|
7
|
-
from janito.agent.config import effective_config
|
8
7
|
|
9
8
|
|
10
9
|
def handle_exit(console, **kwargs):
|
@@ -171,6 +170,7 @@ def handle_role(console, *args, **kwargs):
|
|
171
170
|
console.print(f"[bold green]System role updated to:[/bold green] {new_role}")
|
172
171
|
|
173
172
|
|
173
|
+
|
174
174
|
COMMAND_HANDLERS = {
|
175
175
|
"/history": handle_history,
|
176
176
|
"/continue": handle_continue,
|
@@ -180,12 +180,17 @@ COMMAND_HANDLERS = {
|
|
180
180
|
"/help": handle_help,
|
181
181
|
"/multi": handle_multi,
|
182
182
|
"/system": handle_system,
|
183
|
-
|
183
|
+
}
|
184
|
+
|
185
|
+
if not runtime_config.get('vanilla_mode', False):
|
186
|
+
COMMAND_HANDLERS["/role"] = handle_role
|
187
|
+
|
188
|
+
COMMAND_HANDLERS.update({
|
184
189
|
"/clear": handle_clear,
|
185
190
|
"/reset": handle_reset,
|
186
191
|
"/config": handle_config_shell,
|
187
192
|
"/reload": handle_reload,
|
188
|
-
}
|
193
|
+
})
|
189
194
|
|
190
195
|
|
191
196
|
def handle_command(command, console, **kwargs):
|
@@ -1,9 +1,6 @@
|
|
1
1
|
from janito.agent.config import local_config, global_config, CONFIG_OPTIONS
|
2
2
|
from janito.agent.config_defaults import CONFIG_DEFAULTS
|
3
3
|
from janito.agent.runtime_config import unified_config, runtime_config
|
4
|
-
from janito.cli._print_config import print_config_items
|
5
|
-
from rich import print
|
6
|
-
import sys
|
7
4
|
|
8
5
|
def handle_config_shell(console, *args, **kwargs):
|
9
6
|
"""
|
@@ -54,3 +54,14 @@ def save_input_history(history_list):
|
|
54
54
|
history_file = os.path.join(history_dir, f'{today_str}.json')
|
55
55
|
with open(history_file, 'w', encoding='utf-8') as f:
|
56
56
|
json.dump(history_list, f, indent=2)
|
57
|
+
|
58
|
+
def last_conversation_exists(path='.janito/last_conversation.json'):
|
59
|
+
if not os.path.exists(path):
|
60
|
+
return False
|
61
|
+
try:
|
62
|
+
with open(path, 'r', encoding='utf-8') as f:
|
63
|
+
data = json.load(f)
|
64
|
+
messages = data.get('messages', [])
|
65
|
+
return bool(messages)
|
66
|
+
except Exception:
|
67
|
+
return False
|
janito/cli_chat_shell/ui.py
CHANGED
@@ -1,11 +1,9 @@
|
|
1
|
-
from rich.console import Console
|
2
|
-
from rich.markdown import Markdown
|
3
1
|
from prompt_toolkit import PromptSession
|
4
|
-
from prompt_toolkit.key_binding import KeyBindings
|
5
2
|
from prompt_toolkit.enums import EditingMode
|
6
3
|
from prompt_toolkit.formatted_text import HTML
|
7
4
|
from prompt_toolkit.styles import Style
|
8
|
-
from
|
5
|
+
from janito.agent.runtime_config import runtime_config
|
6
|
+
from .session_manager import last_conversation_exists
|
9
7
|
|
10
8
|
|
11
9
|
def print_summary(console, data, continue_session):
|
@@ -26,120 +24,21 @@ def print_summary(console, data, continue_session):
|
|
26
24
|
ctok = usage.get('completion_tokens')
|
27
25
|
tot = (ptok or 0) + (ctok or 0)
|
28
26
|
console.print(f"Tokens - Prompt: {ptok}, Completion: {ctok}, Total: {tot}")
|
29
|
-
if
|
27
|
+
# Only print /continue suggestion if a last conversation exists
|
28
|
+
if not continue_session and last_conversation_exists():
|
30
29
|
console.print("[bold yellow]Type /continue to restore the last saved conversation.[/bold yellow]")
|
31
30
|
|
32
31
|
|
33
32
|
def print_welcome(console, version=None, continued=False):
|
34
33
|
version_str = f" (v{version})" if version else ""
|
35
|
-
|
36
|
-
if
|
34
|
+
vanilla_mode = runtime_config.get('vanilla_mode', False)
|
35
|
+
if vanilla_mode:
|
36
|
+
console.print(f"[bold magenta]Welcome to Janito{version_str} in [white on magenta]VANILLA MODE[/white on magenta]! Tools, system prompt, and temperature are disabled unless overridden.[/bold magenta]")
|
37
|
+
else:
|
38
|
+
console.print(f"[bold green]Welcome to Janito{version_str}! Entering chat mode. Type /exit to exit.[/bold green]")
|
39
|
+
# Only print /continue suggestion if a last conversation exists
|
40
|
+
if not continued and last_conversation_exists():
|
37
41
|
console.print("[yellow]To resume your previous conversation, type /continue at any time.[/yellow]")
|
38
42
|
|
39
43
|
|
40
|
-
|
41
|
-
def format_tokens(n):
|
42
|
-
if n is None:
|
43
|
-
return "?"
|
44
|
-
if n >= 1_000_000:
|
45
|
-
return f"{n/1_000_000:.1f}m"
|
46
|
-
if n >= 1_000:
|
47
|
-
return f"{n/1_000:.1f}k"
|
48
|
-
return str(n)
|
49
|
-
|
50
|
-
def get_toolbar():
|
51
|
-
left = f' Messages: <msg_count>{len(messages_ref())}</msg_count>'
|
52
|
-
usage = last_usage_info_ref()
|
53
|
-
last_elapsed = last_elapsed_ref()
|
54
|
-
if usage:
|
55
|
-
prompt_tokens = usage.get('prompt_tokens')
|
56
|
-
completion_tokens = usage.get('completion_tokens')
|
57
|
-
total_tokens = (prompt_tokens or 0) + (completion_tokens or 0)
|
58
|
-
speed = None
|
59
|
-
if last_elapsed and last_elapsed > 0:
|
60
|
-
speed = total_tokens / last_elapsed
|
61
|
-
left += (
|
62
|
-
f" | Tokens: In=<tokens_in>{format_tokens(prompt_tokens)}</tokens_in> / "
|
63
|
-
f"Out=<tokens_out>{format_tokens(completion_tokens)}</tokens_out> / "
|
64
|
-
f"Total=<tokens_total>{format_tokens(total_tokens)}</tokens_total>"
|
65
|
-
)
|
66
|
-
if speed is not None:
|
67
|
-
left += f", speed=<speed>{speed:.1f}</speed> tokens/sec"
|
68
|
-
|
69
|
-
from prompt_toolkit.application import get_app
|
70
|
-
|
71
|
-
# Compose first line with Model and Role
|
72
|
-
width = get_app().output.get_size().columns
|
73
|
-
|
74
|
-
|
75
|
-
model_part = f" Model: <model>{model_name}</model>" if model_name else ""
|
76
|
-
role_part = ""
|
77
|
-
if role_ref:
|
78
|
-
role = role_ref()
|
79
|
-
if role:
|
80
|
-
role_part = f"Role: <b>{role}</b>"
|
81
|
-
|
82
|
-
first_line_parts = []
|
83
|
-
if model_part:
|
84
|
-
first_line_parts.append(model_part)
|
85
|
-
if role_part:
|
86
|
-
first_line_parts.append(role_part)
|
87
|
-
first_line = " | ".join(first_line_parts)
|
88
|
-
|
89
|
-
help_part = "<b>/help</b> for help | <b>F12</b>: just do it"
|
90
|
-
|
91
|
-
total_len = len(left) + len(help_part) + 3 # separators and spaces
|
92
|
-
if first_line:
|
93
|
-
total_len += len(first_line) + 3
|
94
|
-
|
95
|
-
if total_len < width:
|
96
|
-
padding = ' ' * (width - total_len)
|
97
|
-
second_line = f"{left}{padding} | {help_part}"
|
98
|
-
else:
|
99
|
-
second_line = f"{left} | {help_part}"
|
100
|
-
|
101
|
-
if first_line:
|
102
|
-
toolbar_text = first_line + "\n" + second_line
|
103
|
-
else:
|
104
|
-
toolbar_text = second_line
|
105
|
-
|
106
|
-
return HTML(toolbar_text)
|
107
|
-
|
108
|
-
return get_toolbar
|
109
|
-
|
110
|
-
|
111
|
-
def get_prompt_session(get_toolbar_func, mem_history):
|
112
|
-
from prompt_toolkit.key_binding import KeyBindings
|
113
|
-
style = Style.from_dict({
|
114
|
-
'bottom-toolbar': 'bg:#333333 #ffffff',
|
115
|
-
'b': 'bold',
|
116
|
-
'prompt': 'bold bg:#000080 #ffffff',
|
117
|
-
'model': 'bold bg:#005f5f #ffffff', # distinct background/foreground
|
118
|
-
'msg_count': 'bg:#333333 #ffff00 bold',
|
119
|
-
'tokens_in': 'ansicyan bold',
|
120
|
-
'tokens_out': 'ansigreen bold',
|
121
|
-
'tokens_total': 'ansiyellow bold',
|
122
|
-
'speed': 'ansimagenta bold',
|
123
|
-
'right': 'bg:#005f5f #ffffff',
|
124
|
-
'input': 'bg:#000080 #ffffff',
|
125
|
-
'': 'bg:#000080 #ffffff',
|
126
|
-
})
|
127
|
-
|
128
|
-
kb = KeyBindings()
|
129
|
-
|
130
|
-
@kb.add('f12')
|
131
|
-
def _(event):
|
132
|
-
"""When F12 is pressed, send 'just do it' as input immediately."""
|
133
|
-
buf = event.app.current_buffer
|
134
|
-
buf.text = 'just do it'
|
135
|
-
buf.validate_and_handle()
|
136
|
-
|
137
|
-
session = PromptSession(
|
138
|
-
multiline=False,
|
139
|
-
key_bindings=kb,
|
140
|
-
editing_mode=EditingMode.EMACS,
|
141
|
-
bottom_toolbar=get_toolbar_func,
|
142
|
-
style=style,
|
143
|
-
history=mem_history
|
144
|
-
)
|
145
|
-
return session
|
44
|
+
# ... rest of the file remains unchanged ...
|
janito/render_prompt.py
CHANGED
janito/rich_utils.py
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
"""
|
2
|
+
Utilities for working with the Rich library.
|
3
|
+
"""
|
4
|
+
from rich.markdown import Markdown
|
5
|
+
from rich.text import Text
|
6
|
+
from rich.console import Console
|
7
|
+
|
8
|
+
def print_markdown(console: Console, message: str):
|
9
|
+
console.print(Markdown(message))
|
10
|
+
|
11
|
+
def print_info(console: Console, message: str):
|
12
|
+
console.print(message, style="cyan", end="")
|
13
|
+
|
14
|
+
def print_success(console: Console, message: str):
|
15
|
+
console.print(message, style="bold green", end="\n")
|
16
|
+
|
17
|
+
def print_error(console: Console, message: str):
|
18
|
+
console.print(message, style="bold red", end="\n")
|
19
|
+
|
20
|
+
def print_warning(console: Console, message: str):
|
21
|
+
console.print(message, style="bold yellow", end="\n")
|
22
|
+
|
23
|
+
def print_magenta(console: Console, message: str):
|
24
|
+
console.print(message, style="magenta", end="\n")
|
25
|
+
|
26
|
+
def print_stdout(console: Console, message: str):
|
27
|
+
console.print(Text(message, style="on #003300", no_wrap=True, overflow=None), end="")
|
28
|
+
|
29
|
+
def print_stderr(console: Console, message: str):
|
30
|
+
console.print(Text(message, style="on #330000", no_wrap=True, overflow=None), end="")
|
janito/web/app.py
CHANGED
@@ -1,12 +1,13 @@
|
|
1
|
-
from flask import Flask, request, Response, send_from_directory,
|
1
|
+
from flask import Flask, request, Response, send_from_directory, jsonify, render_template
|
2
2
|
from queue import Queue
|
3
3
|
import json
|
4
|
-
from janito.agent.
|
4
|
+
from janito.agent.queued_message_handler import QueuedMessageHandler
|
5
5
|
from janito.agent.agent import Agent
|
6
|
-
from janito.agent.config import get_api_key
|
7
6
|
from janito.render_prompt import render_system_prompt
|
8
7
|
import os
|
9
8
|
import threading
|
9
|
+
import traceback
|
10
|
+
import sys
|
10
11
|
|
11
12
|
from janito.agent.runtime_config import unified_config
|
12
13
|
|
@@ -37,21 +38,20 @@ conversation = None
|
|
37
38
|
# Global event queue for streaming
|
38
39
|
stream_queue = Queue()
|
39
40
|
|
40
|
-
# Create a
|
41
|
-
|
41
|
+
# Create a QueuedMessageHandler with the queue
|
42
|
+
message_handler = QueuedMessageHandler(stream_queue)
|
42
43
|
|
43
|
-
# Instantiate the Agent with config-driven parameters
|
44
|
+
# Instantiate the Agent with config-driven parameters (no tool_handler)
|
44
45
|
agent = Agent(
|
45
46
|
api_key=unified_config.get("api_key"),
|
46
47
|
model=unified_config.get("model"),
|
47
|
-
base_url=unified_config.get("base_url")
|
48
|
-
tool_handler=queued_handler
|
48
|
+
base_url=unified_config.get("base_url")
|
49
49
|
)
|
50
50
|
|
51
51
|
@app.route('/get_config')
|
52
52
|
def get_config():
|
53
53
|
# Expose full config for the web app: defaults, effective, runtime (mask api_key)
|
54
|
-
from janito.agent.runtime_config import unified_config
|
54
|
+
from janito.agent.runtime_config import unified_config # Kept here: avoids circular import at module level
|
55
55
|
from janito.agent.config_defaults import CONFIG_DEFAULTS
|
56
56
|
# Start with defaults
|
57
57
|
config = dict(CONFIG_DEFAULTS)
|
@@ -145,7 +145,7 @@ def execute_stream():
|
|
145
145
|
try:
|
146
146
|
response = agent.chat(
|
147
147
|
conversation,
|
148
|
-
|
148
|
+
message_handler=message_handler
|
149
149
|
)
|
150
150
|
if response and 'content' in response:
|
151
151
|
conversation.append({"role": "assistant", "content": response['content']})
|
@@ -156,7 +156,6 @@ def execute_stream():
|
|
156
156
|
except Exception as e:
|
157
157
|
print(f"Error saving conversation: {e}")
|
158
158
|
except Exception as e:
|
159
|
-
import traceback
|
160
159
|
tb = traceback.format_exc()
|
161
160
|
stream_queue.put({"type": "error", "error": str(e), "traceback": tb})
|
162
161
|
finally:
|
@@ -174,7 +173,6 @@ def execute_stream():
|
|
174
173
|
else:
|
175
174
|
message = json.dumps(content)
|
176
175
|
yield f"data: {message}\n\n"
|
177
|
-
import sys
|
178
176
|
sys.stdout.flush()
|
179
177
|
|
180
178
|
return Response(
|
@@ -0,0 +1,176 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: janito
|
3
|
+
Version: 1.5.0
|
4
|
+
Summary: A Natural Programming Language Agent,
|
5
|
+
Author-email: João Pinto <joao.pinto@gmail.com>
|
6
|
+
License: MIT
|
7
|
+
Project-URL: homepage, https://github.com/joaompinto/janito
|
8
|
+
Project-URL: repository, https://github.com/joaompinto/janito
|
9
|
+
Keywords: agent,framework,tools,automation
|
10
|
+
Classifier: Programming Language :: Python :: 3.10
|
11
|
+
Classifier: Operating System :: OS Independent
|
12
|
+
Requires-Python: >=3.10
|
13
|
+
Description-Content-Type: text/markdown
|
14
|
+
License-File: LICENSE
|
15
|
+
Requires-Dist: beautifulsoup4
|
16
|
+
Requires-Dist: docstring-parser
|
17
|
+
Requires-Dist: flask
|
18
|
+
Requires-Dist: jinja2
|
19
|
+
Requires-Dist: openai
|
20
|
+
Requires-Dist: pathspec
|
21
|
+
Requires-Dist: prompt_toolkit
|
22
|
+
Requires-Dist: requests
|
23
|
+
Requires-Dist: rich
|
24
|
+
Dynamic: license-file
|
25
|
+
|
26
|
+
# 🚀 Janito: Natural Programming Language Agent
|
27
|
+
|
28
|
+
**Current Version: 1.5.x**
|
29
|
+
See [docs/CHANGELOG.md](docs/CHANGELOG.md) and [RELEASE_NOTES_1.5.md](./RELEASE_NOTES_1.5.md) for details on the latest release.
|
30
|
+
|
31
|
+
Janito is an AI-powered assistant for the command line and web that interprets natural language instructions to edit code, manage files, and analyze projects using patterns and tools designed by experienced software engineers. It prioritizes transparency, interactive clarification, and precise, reviewable changes.
|
32
|
+
|
33
|
+
For a technical overview, see the [Architecture Guide](docs/ARCHITECTURE.md).
|
34
|
+
|
35
|
+
---
|
36
|
+
|
37
|
+
## ⚡ Quick Start
|
38
|
+
|
39
|
+
## 🖥️ Supported Human Interfaces
|
40
|
+
Janito supports multiple ways for users to interact with the agent:
|
41
|
+
|
42
|
+
- **CLI (Command Line Interface):** Run single prompts or commands directly from your terminal (e.g., `janito "Refactor the data processing module"`).
|
43
|
+
- **CLI Chat Shell:** Start an interactive chat session in your terminal for conversational workflows (`janito`).
|
44
|
+
- **Web Interface:** Launch a browser-based UI for chat and project management (`janito --web`).
|
45
|
+
|
46
|
+
|
47
|
+

|
48
|
+
|
49
|
+
### 🛠️ Common CLI Modifiers
|
50
|
+
You can alter Janito's behavior in any interface using these flags:
|
51
|
+
|
52
|
+
- `--system` / `--system-file`: Override or customize the system prompt for the session.
|
53
|
+
- `--no-tools`: Disable all tool usage (Janito will only use the language model, no file/code/shell actions).
|
54
|
+
- `--vanilla`: Disables tools, system prompt, and temperature settings for a pure LLM chat experience.
|
55
|
+
|
56
|
+
These modifiers can be combined with any interface mode for tailored workflows.
|
57
|
+
|
58
|
+
|
59
|
+
Run a one-off prompt:
|
60
|
+
```bash
|
61
|
+
janito "Refactor the data processing module to improve readability."
|
62
|
+
```
|
63
|
+
|
64
|
+
Or start the interactive chat shell:
|
65
|
+
```bash
|
66
|
+
janito
|
67
|
+
```
|
68
|
+
|
69
|
+
While in the chat shell, you can use special commands like `/reload` to reload the system prompt from a file without restarting your session. See the documentation for more shell commands.
|
70
|
+
|
71
|
+
Launch the web UI:
|
72
|
+
```bash
|
73
|
+
janito --web
|
74
|
+
```
|
75
|
+
|
76
|
+
---
|
77
|
+
|
78
|
+
## ✨ Key Features
|
79
|
+
- 📝 **Code Editing via Natural Language:** Modify, create, or delete code files simply by describing the changes.
|
80
|
+
- 📁 **File & Directory Management:** Navigate, create, move, or remove files and folders.
|
81
|
+
- 🧠 **Context-Aware:** Understands your project structure for precise edits.
|
82
|
+
- 💬 **Interactive User Prompts:** Asks for clarification when needed.
|
83
|
+
- 🧩 **Extensible Tooling:** Built-in tools for file operations, shell commands, directory and file management, Python code execution and validation, text replacement, and more.
|
84
|
+
- See [janito/agent/tools/README.md](janito/agent/tools/README.md) for the full list of built-in tools and their usage details. For the message handler model, see [docs/MESSAGE_HANDLER_MODEL.md](docs/MESSAGE_HANDLER_MODEL.md).
|
85
|
+
- 🌐 **Web Interface (In Development):** Simple web UI for streaming responses and tool progress.
|
86
|
+
|
87
|
+
## 📦 Installation
|
88
|
+
|
89
|
+
### Requirements
|
90
|
+
- Python 3.10+
|
91
|
+
|
92
|
+
|
93
|
+
### Contributing & Developer Guide
|
94
|
+
|
95
|
+
If you want to extend Janito or add new tools, see the [Developer Guide](docs/README_DEV.md) for instructions, tool registration requirements, and code style guidelines. For the full list of built-in tools and their usage, see the [Tools Reference](janito/agent/tools/README.md).
|
96
|
+
|
97
|
+
|
98
|
+
|
99
|
+
For the full changelog, see [docs/CHANGELOG.md](docs/CHANGELOG.md).
|
100
|
+
|
101
|
+
...
|
102
|
+
|
103
|
+
### Configuration & CLI Options
|
104
|
+
|
105
|
+
See [docs/CONFIGURATION.md](docs/CONFIGURATION.md) for all configuration parameters, CLI flags, and advanced usage details. All CLI and configuration options have been moved there for clarity and maintainability.
|
106
|
+
|
107
|
+
|
108
|
+
### Obtaining an API Key from OpenRouter
|
109
|
+
|
110
|
+
To use Janito with OpenRouter, you need an API key:
|
111
|
+
|
112
|
+
1. Visit https://openrouter.ai and sign up for an account.
|
113
|
+
2. After logging in, go to your account dashboard.
|
114
|
+
3. Navigate to the "API Keys" section.
|
115
|
+
4. Click "Create new key" and copy the generated API key.
|
116
|
+
5. Set your API key in Janito using:
|
117
|
+
```bash
|
118
|
+
python -m janito --set-api-key YOUR_OPENROUTER_KEY
|
119
|
+
```
|
120
|
+
Or add it to your configuration file as `api_key`.
|
121
|
+
|
122
|
+
**Keep your API key secure and do not share it publicly.**
|
123
|
+
|
124
|
+
### Using Azure OpenAI
|
125
|
+
|
126
|
+
For details on using models hosted on Azure OpenAI, see [docs/AZURE_OPENAI.md](docs/AZURE_OPENAI.md).
|
127
|
+
|
128
|
+
|
129
|
+
|
130
|
+
---
|
131
|
+
|
132
|
+
## 🧩 System Prompt & Role
|
133
|
+
|
134
|
+
Janito operates using a system prompt template that defines its behavior, communication style, and capabilities. By default, Janito assumes the role of a "software engineer"—this means its responses and actions are tailored to the expectations and best practices of professional software engineering.
|
135
|
+
|
136
|
+
- **Role:** You can customize the agent's role (e.g., "data scientist", "DevOps engineer") using the `--role` flag or config. The default is `software engineer`.
|
137
|
+
- **System Prompt Template:** The system prompt is rendered from a Jinja2 template (see `janito/agent/templates/system_instructions.j2`). This template governs how the agent interprets instructions, interacts with files, and communicates with users.
|
138
|
+
- **Customization & Precedence:** Advanced users can override the system prompt with the `--system` flag (raw string), or point to a custom file using `--system-file`. The precedence is: `--system-file` > `--system`/config > default template.
|
139
|
+
|
140
|
+
The default template ensures the agent:
|
141
|
+
- Prioritizes safe, reviewable, and minimal changes
|
142
|
+
- Asks for clarification when instructions are ambiguous
|
143
|
+
- Provides concise plans before taking action
|
144
|
+
- Documents any changes made
|
145
|
+
|
146
|
+
For more details or to customize the prompt, see the template file at `janito/agent/templates/system_instructions.j2` and the architecture overview in [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md).
|
147
|
+
|
148
|
+
---
|
149
|
+
|
150
|
+
|
151
|
+
## 🥛 Vanilla Mode
|
152
|
+
|
153
|
+
Janito supports a "vanilla mode" for pure LLM interaction:
|
154
|
+
|
155
|
+
- No tools: Disables all tool use (no file operations, shell commands, etc.).
|
156
|
+
- No system prompt: The LLM receives only your input, with no system prompt or role injected.
|
157
|
+
- No temperature set: The temperature parameter is not set (unless you explicitly provide `-t`/`--temperature`).
|
158
|
+
|
159
|
+
Activate vanilla mode with the CLI flag:
|
160
|
+
|
161
|
+
```bash
|
162
|
+
python -m janito --vanilla "Your prompt here"
|
163
|
+
```
|
164
|
+
|
165
|
+
Or in chat shell mode:
|
166
|
+
|
167
|
+
```bash
|
168
|
+
python -m janito --vanilla
|
169
|
+
```
|
170
|
+
|
171
|
+
Vanilla mode is ideal for:
|
172
|
+
- Testing raw model behavior
|
173
|
+
- Comparing LLM output with and without agent guidance
|
174
|
+
- Ensuring no agent-side intervention or context is added
|
175
|
+
|
176
|
+
> Note: Vanilla mode is a runtime switch and does not change the Agent API or class signatures. It is controlled via CLI/config only.
|