janito 1.2.0__py3-none-any.whl → 1.3.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/agent.py +27 -10
- janito/agent/config.py +37 -9
- janito/agent/config_utils.py +9 -0
- janito/agent/conversation.py +19 -3
- janito/agent/runtime_config.py +1 -0
- janito/agent/tool_handler.py +154 -52
- janito/agent/tools/__init__.py +9 -8
- janito/agent/tools/ask_user.py +2 -1
- janito/agent/tools/fetch_url.py +27 -35
- janito/agent/tools/file_ops.py +72 -0
- janito/agent/tools/find_files.py +47 -26
- janito/agent/tools/get_lines.py +58 -0
- janito/agent/tools/py_compile.py +26 -0
- janito/agent/tools/python_exec.py +47 -0
- janito/agent/tools/remove_directory.py +38 -0
- janito/agent/tools/replace_text_in_file.py +67 -0
- janito/agent/tools/run_bash_command.py +134 -0
- janito/agent/tools/search_files.py +52 -0
- janito/cli/_print_config.py +12 -12
- janito/cli/arg_parser.py +6 -1
- janito/cli/config_commands.py +56 -8
- janito/cli/runner.py +21 -9
- janito/cli_chat_shell/chat_loop.py +5 -3
- janito/cli_chat_shell/commands.py +34 -37
- janito/cli_chat_shell/config_shell.py +1 -1
- janito/cli_chat_shell/load_prompt.py +1 -1
- janito/cli_chat_shell/session_manager.py +11 -15
- janito/cli_chat_shell/ui.py +17 -8
- janito/render_prompt.py +3 -1
- janito/web/app.py +76 -19
- janito-1.3.0.dist-info/METADATA +142 -0
- janito-1.3.0.dist-info/RECORD +51 -0
- janito/agent/tools/bash_exec.py +0 -58
- janito/agent/tools/create_directory.py +0 -19
- janito/agent/tools/create_file.py +0 -43
- janito/agent/tools/file_str_replace.py +0 -48
- janito/agent/tools/move_file.py +0 -37
- janito/agent/tools/remove_file.py +0 -19
- janito/agent/tools/search_text.py +0 -41
- janito/agent/tools/view_file.py +0 -34
- janito/templates/system_instructions.j2 +0 -38
- janito-1.2.0.dist-info/METADATA +0 -85
- janito-1.2.0.dist-info/RECORD +0 -51
- {janito-1.2.0.dist-info → janito-1.3.0.dist-info}/WHEEL +0 -0
- {janito-1.2.0.dist-info → janito-1.3.0.dist-info}/entry_points.txt +0 -0
- {janito-1.2.0.dist-info → janito-1.3.0.dist-info}/licenses/LICENSE +0 -0
- {janito-1.2.0.dist-info → janito-1.3.0.dist-info}/top_level.txt +0 -0
janito/cli/config_commands.py
CHANGED
@@ -9,6 +9,18 @@ def handle_config_commands(args):
|
|
9
9
|
"""Handle --set-local-config, --set-global-config, --show-config. Exit if any are used."""
|
10
10
|
did_something = False
|
11
11
|
|
12
|
+
if args.run_config:
|
13
|
+
for run_item in args.run_config:
|
14
|
+
try:
|
15
|
+
key, val = run_item.split("=", 1)
|
16
|
+
except ValueError:
|
17
|
+
print("Invalid format for --run-config, expected key=val")
|
18
|
+
sys.exit(1)
|
19
|
+
key = key.strip()
|
20
|
+
if key not in CONFIG_OPTIONS:
|
21
|
+
print(f"Invalid config key: '{key}'. Supported keys are: {', '.join(CONFIG_OPTIONS.keys())}")
|
22
|
+
sys.exit(1)
|
23
|
+
runtime_config.set(key, val.strip())
|
12
24
|
if args.set_local_config:
|
13
25
|
from janito.agent.config import CONFIG_OPTIONS
|
14
26
|
try:
|
@@ -34,17 +46,33 @@ def handle_config_commands(args):
|
|
34
46
|
print("Invalid format for --set-global-config, expected key=val")
|
35
47
|
sys.exit(1)
|
36
48
|
key = key.strip()
|
37
|
-
if key not in CONFIG_OPTIONS:
|
49
|
+
if key not in CONFIG_OPTIONS and not key.startswith("template."):
|
38
50
|
print(f"Invalid config key: '{key}'. Supported keys are: {', '.join(CONFIG_OPTIONS.keys())}")
|
39
51
|
sys.exit(1)
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
52
|
+
if key.startswith('template.'):
|
53
|
+
subkey = key[len('template.'):]
|
54
|
+
template_dict = global_config.get('template', {})
|
55
|
+
template_dict[subkey] = val.strip()
|
56
|
+
global_config.set('template', template_dict)
|
57
|
+
global_config.save()
|
58
|
+
# Remove legacy flat key if present
|
59
|
+
if key in global_config._data:
|
60
|
+
del global_config._data[key]
|
61
|
+
runtime_config.set('template', template_dict)
|
62
|
+
print(f"Global config updated: template.{subkey} = {val.strip()}")
|
63
|
+
did_something = True
|
64
|
+
else:
|
65
|
+
global_config.set(key, val.strip())
|
66
|
+
global_config.save()
|
67
|
+
runtime_config.set(key, val.strip())
|
68
|
+
print(f"Global config updated: {key} = {val.strip()}")
|
69
|
+
did_something = True
|
45
70
|
|
46
71
|
if args.set_api_key:
|
47
|
-
|
72
|
+
# Merge: load full config, update api_key, save all
|
73
|
+
existing = dict(global_config.all())
|
74
|
+
existing["api_key"] = args.set_api_key.strip()
|
75
|
+
global_config._data = existing
|
48
76
|
global_config.save()
|
49
77
|
runtime_config.set("api_key", args.set_api_key.strip())
|
50
78
|
print("Global API key saved.")
|
@@ -64,7 +92,18 @@ def handle_config_commands(args):
|
|
64
92
|
else:
|
65
93
|
from janito.agent.config import get_api_key
|
66
94
|
from janito.agent.runtime_config import unified_config
|
95
|
+
# Handle template as nested dict
|
67
96
|
for key in sorted(local_keys):
|
97
|
+
if key == "template":
|
98
|
+
template_dict = local_config.get("template", {})
|
99
|
+
if template_dict:
|
100
|
+
local_items["template"] = f"({len(template_dict)} keys set)"
|
101
|
+
for tkey, tval in template_dict.items():
|
102
|
+
local_items[f" template.{tkey}"] = tval
|
103
|
+
continue
|
104
|
+
if key.startswith("template."):
|
105
|
+
# Skip legacy flat keys
|
106
|
+
continue
|
68
107
|
if key == "api_key":
|
69
108
|
value = local_config.get("api_key")
|
70
109
|
value = value[:4] + '...' + value[-4:] if value and len(value) > 8 else ('***' if value else None)
|
@@ -72,6 +111,15 @@ def handle_config_commands(args):
|
|
72
111
|
value = unified_config.get(key)
|
73
112
|
local_items[key] = value
|
74
113
|
for key in sorted(global_keys - local_keys):
|
114
|
+
if key == "template":
|
115
|
+
template_dict = global_config.get("template", {})
|
116
|
+
if template_dict:
|
117
|
+
global_items["template"] = f"({len(template_dict)} keys set)"
|
118
|
+
for tkey, tval in template_dict.items():
|
119
|
+
global_items[f" template.{tkey}"] = tval
|
120
|
+
continue
|
121
|
+
if key.startswith("template."):
|
122
|
+
continue
|
75
123
|
if key == "api_key":
|
76
124
|
value = global_config.get("api_key")
|
77
125
|
value = value[:4] + '...' + value[-4:] if value and len(value) > 8 else ('***' if value else None)
|
@@ -102,7 +150,7 @@ def handle_config_commands(args):
|
|
102
150
|
# Special case for system_prompt: show template file if None
|
103
151
|
if key == "system_prompt" and value is None:
|
104
152
|
from pathlib import Path
|
105
|
-
template_path = Path(__file__).parent
|
153
|
+
template_path = Path(__file__).parent / "agent" / "templates" / "system_instructions.j2"
|
106
154
|
print(f"{key} = (default template path: {home_shorten(str(template_path))})")
|
107
155
|
else:
|
108
156
|
print(f"{key} = {value}")
|
janito/cli/runner.py
CHANGED
@@ -31,6 +31,7 @@ def run_cli(args):
|
|
31
31
|
sys.exit(0)
|
32
32
|
|
33
33
|
role = args.role or unified_config.get("role", "software engineer")
|
34
|
+
|
34
35
|
# Ensure runtime_config is updated so chat shell sees the role
|
35
36
|
if args.role:
|
36
37
|
runtime_config.set('role', args.role)
|
@@ -39,21 +40,28 @@ def run_cli(args):
|
|
39
40
|
if getattr(args, 'model', None):
|
40
41
|
runtime_config.set('model', args.model)
|
41
42
|
|
43
|
+
# Set runtime_config['max_tools'] if --max-tools is provided
|
44
|
+
if getattr(args, 'max_tools', None) is not None:
|
45
|
+
runtime_config.set('max_tools', args.max_tools)
|
46
|
+
|
47
|
+
# Set trust mode if enabled
|
48
|
+
if getattr(args, 'trust', False):
|
49
|
+
runtime_config.set('trust', True)
|
50
|
+
|
42
51
|
# New logic for --system-file
|
43
52
|
system_prompt = None
|
44
53
|
if getattr(args, 'system_file', None):
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
except Exception as e:
|
50
|
-
print(f"[red]Failed to read system prompt file:[/red] {e}")
|
51
|
-
sys.exit(1)
|
54
|
+
with open(args.system_file, 'r', encoding='utf-8') as f:
|
55
|
+
system_prompt = f.read()
|
56
|
+
runtime_config.set('system_prompt_file', args.system_file)
|
57
|
+
|
52
58
|
else:
|
53
59
|
system_prompt = args.system_prompt or unified_config.get("system_prompt")
|
54
60
|
if args.system_prompt:
|
55
61
|
runtime_config.set('system_prompt', system_prompt)
|
56
62
|
if system_prompt is None:
|
63
|
+
# Pass full merged config (runtime overrides effective)
|
64
|
+
|
57
65
|
system_prompt = render_system_prompt(role)
|
58
66
|
|
59
67
|
if args.show_system:
|
@@ -72,10 +80,12 @@ def run_cli(args):
|
|
72
80
|
# Always get model from unified_config (which checks runtime_config first)
|
73
81
|
model = unified_config.get('model')
|
74
82
|
base_url = unified_config.get('base_url', 'https://openrouter.ai/api/v1')
|
83
|
+
azure_openai_api_version = unified_config.get('azure_openai_api_version', '2023-05-15')
|
75
84
|
# Handle --enable-tools flag
|
76
85
|
from janito.agent.tool_handler import ToolHandler
|
77
|
-
tool_handler = ToolHandler(verbose=args.verbose_tools, enable_tools=not getattr(args, '
|
78
|
-
|
86
|
+
tool_handler = ToolHandler(verbose=args.verbose_tools, enable_tools=not getattr(args, 'no_tools', False))
|
87
|
+
use_azure_openai = unified_config.get('use_azure_openai', False)
|
88
|
+
agent = Agent(api_key=api_key, model=model, system_prompt=system_prompt, verbose_tools=args.verbose_tools, base_url=base_url, tool_handler=tool_handler, azure_openai_api_version=azure_openai_api_version, use_azure_openai=use_azure_openai)
|
79
89
|
|
80
90
|
# Save runtime max_tokens override if provided
|
81
91
|
if args.max_tokens is not None:
|
@@ -103,10 +113,12 @@ def run_cli(args):
|
|
103
113
|
|
104
114
|
try:
|
105
115
|
try:
|
116
|
+
max_rounds = runtime_config.get('max_rounds', 50)
|
106
117
|
response = agent.chat(
|
107
118
|
messages,
|
108
119
|
on_content=on_content,
|
109
120
|
spinner=True,
|
121
|
+
max_rounds=max_rounds,
|
110
122
|
)
|
111
123
|
if args.verbose_response:
|
112
124
|
import json
|
@@ -10,7 +10,7 @@ from janito.agent.runtime_config import runtime_config
|
|
10
10
|
from janito.agent.conversation import EmptyResponseError, ProviderError
|
11
11
|
|
12
12
|
|
13
|
-
def start_chat_shell(agent, continue_session=False):
|
13
|
+
def start_chat_shell(agent, continue_session=False, max_rounds=50):
|
14
14
|
console = Console()
|
15
15
|
|
16
16
|
# Load input history
|
@@ -51,7 +51,7 @@ def start_chat_shell(agent, continue_session=False):
|
|
51
51
|
if agent.system_prompt and not any(m.get('role') == 'system' for m in messages):
|
52
52
|
messages.insert(0, {"role": "system", "content": agent.system_prompt})
|
53
53
|
|
54
|
-
print_welcome(console, version=__version__)
|
54
|
+
print_welcome(console, version=__version__, continued=continue_session)
|
55
55
|
|
56
56
|
# Toolbar references
|
57
57
|
def get_messages():
|
@@ -77,6 +77,8 @@ def start_chat_shell(agent, continue_session=False):
|
|
77
77
|
|
78
78
|
# Main chat loop
|
79
79
|
while True:
|
80
|
+
# max_rounds is now available for use in the chat loop
|
81
|
+
|
80
82
|
try:
|
81
83
|
if state.get('paste_mode'):
|
82
84
|
console.print('')
|
@@ -126,7 +128,7 @@ def start_chat_shell(agent, continue_session=False):
|
|
126
128
|
console.print(Markdown(content_piece))
|
127
129
|
|
128
130
|
try:
|
129
|
-
response = agent.chat(messages, on_content=on_content, spinner=True)
|
131
|
+
response = agent.chat(messages, on_content=on_content, spinner=True, max_rounds=max_rounds)
|
130
132
|
except KeyboardInterrupt:
|
131
133
|
console.print("[bold yellow]Request interrupted. Returning to prompt.[/bold yellow]")
|
132
134
|
continue
|
@@ -22,46 +22,43 @@ def handle_continue(console, state, **kwargs):
|
|
22
22
|
if not os.path.exists(save_path):
|
23
23
|
console.print('[bold red]No saved conversation found.[/bold red]')
|
24
24
|
return
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
console.print(f'[bold red]Failed to load conversation:[/bold red] {e}')
|
25
|
+
|
26
|
+
with open(save_path, 'r', encoding='utf-8') as f:
|
27
|
+
data = json.load(f)
|
28
|
+
state['messages'].clear()
|
29
|
+
state['messages'].extend(data.get('messages', []))
|
30
|
+
state['history_list'].clear()
|
31
|
+
state['history_list'].extend(data.get('prompts', []))
|
32
|
+
state['mem_history'] = InMemoryHistory()
|
33
|
+
for item in state['history_list']:
|
34
|
+
state['mem_history'].append_string(item)
|
35
|
+
state['last_usage_info'] = data.get('last_usage_info')
|
36
|
+
console.print('[bold green]Conversation restored from last session.[/bold green]')
|
37
|
+
|
39
38
|
|
40
39
|
def handle_history(console, state, *args, **kwargs):
|
41
40
|
messages = state.get('messages', [])
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
except Exception as e:
|
64
|
-
console.print(f"[bold red]Error parsing arguments or displaying history:[/bold red] {e}")
|
41
|
+
if not args:
|
42
|
+
# Default: last 5 messages
|
43
|
+
start = max(0, len(messages) - 5)
|
44
|
+
end = len(messages)
|
45
|
+
elif len(args) == 1:
|
46
|
+
count = int(args[0])
|
47
|
+
start = max(0, len(messages) - count)
|
48
|
+
end = len(messages)
|
49
|
+
elif len(args) >= 2:
|
50
|
+
start = int(args[0])
|
51
|
+
end = int(args[1]) + 1 # inclusive
|
52
|
+
else:
|
53
|
+
start = 0
|
54
|
+
end = len(messages)
|
55
|
+
|
56
|
+
console.print(f"[bold cyan]Showing messages {start} to {end - 1} (total {len(messages)}):[/bold cyan]")
|
57
|
+
for idx, msg in enumerate(messages[start:end], start=start):
|
58
|
+
role = msg.get('role', 'unknown')
|
59
|
+
content = msg.get('content', '')
|
60
|
+
console.print(f"[bold]{idx} [{role}]:[/bold] {content}")
|
61
|
+
|
65
62
|
|
66
63
|
def handle_help(console, **kwargs):
|
67
64
|
console.print("""
|
@@ -58,7 +58,7 @@ def handle_config_shell(console, *args, **kwargs):
|
|
58
58
|
console.print("[bold red]Invalid format, expected key=val[/bold red]")
|
59
59
|
return
|
60
60
|
key = key.strip()
|
61
|
-
if key not in CONFIG_OPTIONS:
|
61
|
+
if key not in CONFIG_OPTIONS and not key.startswith("template."):
|
62
62
|
console.print(f"[bold red]Invalid config key: '{key}'. Supported keys are: {', '.join(CONFIG_OPTIONS.keys())}")
|
63
63
|
return
|
64
64
|
val = val.strip()
|
@@ -7,7 +7,7 @@ def load_prompt(filename=None):
|
|
7
7
|
"""
|
8
8
|
if filename is None:
|
9
9
|
# Default prompt file path (can be customized)
|
10
|
-
filename = os.path.join(os.path.dirname(__file__), '../
|
10
|
+
filename = os.path.join(os.path.dirname(__file__), '../agent/templates/system_instructions.j2')
|
11
11
|
filename = os.path.abspath(filename)
|
12
12
|
if not os.path.exists(filename):
|
13
13
|
raise FileNotFoundError(f"Prompt file not found: {filename}")
|
@@ -6,26 +6,22 @@ from datetime import datetime
|
|
6
6
|
def load_last_summary(path='.janito/last_conversation.json'):
|
7
7
|
if not os.path.exists(path):
|
8
8
|
return None
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
except Exception:
|
14
|
-
return None
|
9
|
+
with open(path, 'r', encoding='utf-8') as f:
|
10
|
+
data = json.load(f)
|
11
|
+
return data
|
12
|
+
|
15
13
|
|
16
14
|
|
17
15
|
def load_last_conversation(path='.janito/last_conversation.json'):
|
18
16
|
if not os.path.exists(path):
|
19
17
|
return [], [], None
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
except Exception:
|
28
|
-
return [], [], None
|
18
|
+
with open(path, 'r', encoding='utf-8') as f:
|
19
|
+
data = json.load(f)
|
20
|
+
messages = data.get('messages', [])
|
21
|
+
prompts = data.get('prompts', [])
|
22
|
+
usage = data.get('last_usage_info')
|
23
|
+
return messages, prompts, usage
|
24
|
+
|
29
25
|
|
30
26
|
|
31
27
|
def save_conversation(messages, prompts, usage_info=None, path='.janito/last_conversation.json'):
|
janito/cli_chat_shell/ui.py
CHANGED
@@ -30,10 +30,11 @@ def print_summary(console, data, continue_session):
|
|
30
30
|
console.print("[bold yellow]Type /continue to restore the last saved conversation.[/bold yellow]")
|
31
31
|
|
32
32
|
|
33
|
-
def print_welcome(console, version=None):
|
33
|
+
def print_welcome(console, version=None, continued=False):
|
34
34
|
version_str = f" (v{version})" if version else ""
|
35
35
|
console.print(f"[bold green]Welcome to Janito{version_str}! Entering chat mode. Type /exit to exit.[/bold green]")
|
36
|
-
|
36
|
+
if not continued:
|
37
|
+
console.print("[yellow]To resume your previous conversation, type /continue at any time.[/yellow]")
|
37
38
|
|
38
39
|
|
39
40
|
def get_toolbar_func(messages_ref, last_usage_info_ref, last_elapsed_ref, model_name=None, role_ref=None):
|
@@ -68,10 +69,8 @@ def get_toolbar_func(messages_ref, last_usage_info_ref, last_elapsed_ref, model_
|
|
68
69
|
from prompt_toolkit.application import get_app
|
69
70
|
|
70
71
|
# Compose first line with Model and Role
|
71
|
-
|
72
|
-
|
73
|
-
except Exception:
|
74
|
-
width = 80 # fallback default
|
72
|
+
width = get_app().output.get_size().columns
|
73
|
+
|
75
74
|
|
76
75
|
model_part = f" Model: <model>{model_name}</model>" if model_name else ""
|
77
76
|
role_part = ""
|
@@ -87,7 +86,7 @@ def get_toolbar_func(messages_ref, last_usage_info_ref, last_elapsed_ref, model_
|
|
87
86
|
first_line_parts.append(role_part)
|
88
87
|
first_line = " | ".join(first_line_parts)
|
89
88
|
|
90
|
-
help_part = "<b>/help</b> for help"
|
89
|
+
help_part = "<b>/help</b> for help | <b>F12</b>: just do it"
|
91
90
|
|
92
91
|
total_len = len(left) + len(help_part) + 3 # separators and spaces
|
93
92
|
if first_line:
|
@@ -110,6 +109,7 @@ def get_toolbar_func(messages_ref, last_usage_info_ref, last_elapsed_ref, model_
|
|
110
109
|
|
111
110
|
|
112
111
|
def get_prompt_session(get_toolbar_func, mem_history):
|
112
|
+
from prompt_toolkit.key_binding import KeyBindings
|
113
113
|
style = Style.from_dict({
|
114
114
|
'bottom-toolbar': 'bg:#333333 #ffffff',
|
115
115
|
'b': 'bold',
|
@@ -125,9 +125,18 @@ def get_prompt_session(get_toolbar_func, mem_history):
|
|
125
125
|
'': 'bg:#000080 #ffffff',
|
126
126
|
})
|
127
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
|
+
|
128
137
|
session = PromptSession(
|
129
138
|
multiline=False,
|
130
|
-
key_bindings=
|
139
|
+
key_bindings=kb,
|
131
140
|
editing_mode=EditingMode.EMACS,
|
132
141
|
bottom_toolbar=get_toolbar_func,
|
133
142
|
style=style,
|
janito/render_prompt.py
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
import jinja2
|
2
2
|
from pathlib import Path
|
3
3
|
|
4
|
+
from janito.agent.runtime_config import unified_config
|
5
|
+
|
4
6
|
def render_system_prompt(role: str) -> str:
|
5
|
-
template_loader = jinja2.FileSystemLoader(searchpath=str(Path(__file__).parent / "templates"))
|
7
|
+
template_loader = jinja2.FileSystemLoader(searchpath=str(Path(__file__).parent / "agent" / "templates"))
|
6
8
|
env = jinja2.Environment(loader=template_loader)
|
7
9
|
template = env.get_template("system_instructions.j2")
|
8
10
|
return template.render(role=role)
|
janito/web/app.py
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
from flask import Flask, request,
|
1
|
+
from flask import Flask, request, Response, send_from_directory, session, jsonify
|
2
2
|
from queue import Queue
|
3
3
|
import json
|
4
4
|
from janito.agent.queued_tool_handler import QueuedToolHandler
|
@@ -8,8 +8,15 @@ from janito.render_prompt import render_system_prompt
|
|
8
8
|
import os
|
9
9
|
import threading
|
10
10
|
|
11
|
-
|
12
|
-
|
11
|
+
from janito.agent.runtime_config import unified_config
|
12
|
+
|
13
|
+
# Render system prompt from config
|
14
|
+
role = unified_config.get("role", "software engineer")
|
15
|
+
system_prompt_override = unified_config.get("system_prompt")
|
16
|
+
if system_prompt_override:
|
17
|
+
system_prompt = system_prompt_override
|
18
|
+
else:
|
19
|
+
system_prompt = render_system_prompt(role)
|
13
20
|
|
14
21
|
app = Flask(
|
15
22
|
__name__,
|
@@ -33,15 +40,59 @@ stream_queue = Queue()
|
|
33
40
|
# Create a QueuedToolHandler with the queue
|
34
41
|
queued_handler = QueuedToolHandler(stream_queue)
|
35
42
|
|
36
|
-
# Instantiate the Agent with
|
43
|
+
# Instantiate the Agent with config-driven parameters
|
37
44
|
agent = Agent(
|
38
|
-
api_key=
|
45
|
+
api_key=unified_config.get("api_key"),
|
46
|
+
model=unified_config.get("model"),
|
47
|
+
base_url=unified_config.get("base_url"),
|
39
48
|
tool_handler=queued_handler
|
40
49
|
)
|
41
50
|
|
42
|
-
@app.route('/
|
43
|
-
def
|
44
|
-
|
51
|
+
@app.route('/get_config')
|
52
|
+
def get_config():
|
53
|
+
# Expose full config for the web app: defaults, effective, runtime (mask api_key)
|
54
|
+
from janito.agent.runtime_config import unified_config
|
55
|
+
from janito.agent.config_defaults import CONFIG_DEFAULTS
|
56
|
+
# Start with defaults
|
57
|
+
config = dict(CONFIG_DEFAULTS)
|
58
|
+
# Overlay effective config
|
59
|
+
config.update(unified_config.effective_cfg.all())
|
60
|
+
# Overlay runtime config (highest priority)
|
61
|
+
config.update(unified_config.runtime_cfg.all())
|
62
|
+
api_key = config.get("api_key")
|
63
|
+
if api_key:
|
64
|
+
config["api_key"] = api_key[:4] + '...' + api_key[-4:] if len(api_key) > 8 else '***'
|
65
|
+
return jsonify(config)
|
66
|
+
|
67
|
+
@app.route('/set_config', methods=['POST'])
|
68
|
+
def set_config():
|
69
|
+
from janito.agent.runtime_config import runtime_config
|
70
|
+
from janito.agent.config import CONFIG_OPTIONS
|
71
|
+
from janito.agent.config_defaults import CONFIG_DEFAULTS
|
72
|
+
data = request.get_json()
|
73
|
+
key = data.get('key')
|
74
|
+
value = data.get('value')
|
75
|
+
if key not in CONFIG_OPTIONS:
|
76
|
+
return jsonify({'status': 'error', 'message': f'Invalid config key: {key}'}), 400
|
77
|
+
# Type coercion based on defaults
|
78
|
+
default = CONFIG_DEFAULTS.get(key)
|
79
|
+
if default is not None and value is not None:
|
80
|
+
try:
|
81
|
+
if isinstance(default, bool):
|
82
|
+
value = bool(value)
|
83
|
+
elif isinstance(default, int):
|
84
|
+
value = int(value)
|
85
|
+
elif isinstance(default, float):
|
86
|
+
value = float(value)
|
87
|
+
# else: leave as string or None
|
88
|
+
except Exception as e:
|
89
|
+
return jsonify({'status': 'error', 'message': f'Invalid value type for {key}: {e}'}), 400
|
90
|
+
runtime_config.set(key, value)
|
91
|
+
# Mask api_key in response
|
92
|
+
resp_value = value
|
93
|
+
if key == 'api_key' and value:
|
94
|
+
resp_value = value[:4] + '...' + value[-4:] if len(value) > 8 else '***'
|
95
|
+
return jsonify({'status': 'ok', 'key': key, 'value': resp_value})
|
45
96
|
|
46
97
|
|
47
98
|
@app.route('/favicon.ico')
|
@@ -91,19 +142,25 @@ def execute_stream():
|
|
91
142
|
conversation.append({"role": "user", "content": user_input})
|
92
143
|
|
93
144
|
def run_agent():
|
94
|
-
response = agent.chat(
|
95
|
-
conversation,
|
96
|
-
on_content=lambda data: stream_queue.put({"type": "content", "content": data.get("content")})
|
97
|
-
)
|
98
|
-
if response and 'content' in response:
|
99
|
-
conversation.append({"role": "assistant", "content": response['content']})
|
100
145
|
try:
|
101
|
-
|
102
|
-
|
103
|
-
|
146
|
+
response = agent.chat(
|
147
|
+
conversation,
|
148
|
+
on_content=lambda data: stream_queue.put({"type": "content", "content": data.get("content")})
|
149
|
+
)
|
150
|
+
if response and 'content' in response:
|
151
|
+
conversation.append({"role": "assistant", "content": response['content']})
|
152
|
+
try:
|
153
|
+
os.makedirs(os.path.dirname(conversation_file), exist_ok=True)
|
154
|
+
with open(conversation_file, 'w') as f:
|
155
|
+
json.dump(conversation, f, indent=2)
|
156
|
+
except Exception as e:
|
157
|
+
print(f"Error saving conversation: {e}")
|
104
158
|
except Exception as e:
|
105
|
-
|
106
|
-
|
159
|
+
import traceback
|
160
|
+
tb = traceback.format_exc()
|
161
|
+
stream_queue.put({"type": "error", "error": str(e), "traceback": tb})
|
162
|
+
finally:
|
163
|
+
stream_queue.put(None)
|
107
164
|
|
108
165
|
threading.Thread(target=run_agent, daemon=True).start()
|
109
166
|
|