janito 1.9.0__py3-none-any.whl → 1.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/agent/api_exceptions.py +4 -0
- janito/agent/config.py +1 -1
- janito/agent/config_defaults.py +2 -26
- janito/agent/conversation.py +163 -122
- janito/agent/conversation_api.py +246 -168
- janito/agent/conversation_ui.py +1 -1
- janito/agent/{conversation_history.py → llm_conversation_history.py} +30 -1
- janito/agent/openai_client.py +38 -23
- janito/agent/openai_schema_generator.py +162 -129
- janito/agent/platform_discovery.py +134 -77
- janito/agent/profile_manager.py +5 -5
- janito/agent/rich_message_handler.py +80 -31
- janito/agent/templates/profiles/system_prompt_template_base.txt.j2 +20 -4
- janito/agent/test_openai_schema_generator.py +93 -0
- janito/agent/tool_base.py +7 -2
- janito/agent/tool_executor.py +54 -49
- janito/agent/tool_registry.py +5 -2
- janito/agent/tool_use_tracker.py +26 -5
- janito/agent/tools/__init__.py +8 -3
- janito/agent/tools/create_directory.py +3 -1
- janito/agent/tools/create_file.py +7 -1
- janito/agent/tools/fetch_url.py +40 -3
- janito/agent/tools/find_files.py +29 -14
- janito/agent/tools/get_file_outline/core.py +7 -8
- janito/agent/tools/get_file_outline/python_outline.py +139 -95
- janito/agent/tools/get_file_outline/search_outline.py +3 -1
- janito/agent/tools/get_lines.py +98 -64
- janito/agent/tools/move_file.py +59 -31
- janito/agent/tools/open_url.py +31 -0
- janito/agent/tools/present_choices.py +3 -1
- janito/agent/tools/python_command_runner.py +149 -0
- janito/agent/tools/python_file_runner.py +147 -0
- janito/agent/tools/python_stdin_runner.py +153 -0
- janito/agent/tools/remove_directory.py +3 -1
- janito/agent/tools/remove_file.py +5 -1
- janito/agent/tools/replace_file.py +12 -2
- janito/agent/tools/replace_text_in_file.py +195 -149
- janito/agent/tools/run_bash_command.py +30 -69
- janito/agent/tools/run_powershell_command.py +138 -105
- janito/agent/tools/search_text/__init__.py +1 -0
- janito/agent/tools/search_text/core.py +176 -0
- janito/agent/tools/search_text/match_lines.py +58 -0
- janito/agent/tools/search_text/pattern_utils.py +65 -0
- janito/agent/tools/search_text/traverse_directory.py +127 -0
- janito/agent/tools/validate_file_syntax/core.py +43 -30
- janito/agent/tools/validate_file_syntax/html_validator.py +21 -5
- janito/agent/tools/validate_file_syntax/markdown_validator.py +77 -34
- janito/agent/tools_utils/action_type.py +7 -0
- janito/agent/tools_utils/dir_walk_utils.py +3 -2
- janito/agent/tools_utils/formatting.py +47 -21
- janito/agent/tools_utils/gitignore_utils.py +89 -40
- janito/agent/tools_utils/test_gitignore_utils.py +46 -0
- janito/agent/tools_utils/utils.py +7 -1
- janito/cli/_print_config.py +63 -61
- janito/cli/arg_parser.py +13 -12
- janito/cli/cli_main.py +137 -147
- janito/cli/config_commands.py +112 -109
- janito/cli/main.py +152 -174
- janito/cli/one_shot.py +40 -26
- janito/i18n/__init__.py +1 -1
- janito/rich_utils.py +46 -8
- janito/shell/commands/__init__.py +2 -4
- janito/shell/commands/conversation_restart.py +3 -1
- janito/shell/commands/edit.py +3 -0
- janito/shell/commands/history_view.py +3 -3
- janito/shell/commands/lang.py +3 -0
- janito/shell/commands/livelogs.py +5 -3
- janito/shell/commands/prompt.py +6 -0
- janito/shell/commands/session.py +3 -0
- janito/shell/commands/session_control.py +3 -0
- janito/shell/commands/termweb_log.py +8 -0
- janito/shell/commands/tools.py +3 -0
- janito/shell/commands/track.py +36 -0
- janito/shell/commands/utility.py +13 -18
- janito/shell/commands/verbose.py +3 -4
- janito/shell/input_history.py +62 -0
- janito/shell/main.py +160 -181
- janito/shell/session/config.py +83 -75
- janito/shell/session/manager.py +0 -21
- janito/shell/ui/interactive.py +97 -75
- janito/termweb/static/editor.css +32 -33
- janito/termweb/static/editor.css.bak +140 -22
- janito/termweb/static/editor.html +12 -7
- janito/termweb/static/editor.html.bak +16 -11
- janito/termweb/static/editor.js +94 -40
- janito/termweb/static/editor.js.bak +97 -65
- janito/termweb/static/index.html +1 -2
- janito/termweb/static/index.html.bak +1 -1
- janito/termweb/static/termweb.css +1 -22
- janito/termweb/static/termweb.css.bak +6 -4
- janito/termweb/static/termweb.js +0 -6
- janito/termweb/static/termweb.js.bak +1 -2
- janito/tests/test_rich_utils.py +44 -0
- janito/web/app.py +0 -75
- {janito-1.9.0.dist-info → janito-1.11.0.dist-info}/METADATA +61 -42
- janito-1.11.0.dist-info/RECORD +163 -0
- {janito-1.9.0.dist-info → janito-1.11.0.dist-info}/WHEEL +1 -1
- janito/agent/providers.py +0 -77
- janito/agent/tools/run_python_command.py +0 -161
- janito/agent/tools/search_text.py +0 -204
- janito/shell/commands/sum.py +0 -49
- janito-1.9.0.dist-info/RECORD +0 -151
- {janito-1.9.0.dist-info → janito-1.11.0.dist-info}/entry_points.txt +0 -0
- {janito-1.9.0.dist-info → janito-1.11.0.dist-info}/licenses/LICENSE +0 -0
- {janito-1.9.0.dist-info → janito-1.11.0.dist-info}/top_level.txt +0 -0
janito/cli/cli_main.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
import sys
|
2
|
+
from janito.agent.llm_conversation_history import LLMConversationHistory
|
2
3
|
import socket
|
3
4
|
from janito.agent.profile_manager import AgentProfileManager
|
4
5
|
from janito.agent.runtime_config import unified_config, runtime_config
|
@@ -17,19 +18,11 @@ def is_port_free(port):
|
|
17
18
|
return s.connect_ex(("localhost", port)) != 0
|
18
19
|
|
19
20
|
|
20
|
-
def
|
21
|
-
if args.version:
|
22
|
-
print(f"janito version {__version__}")
|
23
|
-
sys.exit(0)
|
24
|
-
|
25
|
-
# Set vanilla mode if -V/--vanilla is passed
|
21
|
+
def normalize_args(args):
|
26
22
|
if getattr(args, "vanilla", False):
|
27
23
|
runtime_config.set("vanilla_mode", True)
|
28
|
-
|
29
|
-
# Set no_tools_tracking if --ntt is passed
|
30
24
|
if getattr(args, "ntt", False):
|
31
25
|
runtime_config.set("no_tools_tracking", True)
|
32
|
-
# Normalize all verbose flags into runtime_config
|
33
26
|
for flag in [
|
34
27
|
"verbose_http",
|
35
28
|
"verbose_http_raw",
|
@@ -37,58 +30,49 @@ def run_cli(args):
|
|
37
30
|
"verbose_reason",
|
38
31
|
"verbose_tools",
|
39
32
|
"verbose_events",
|
40
|
-
"
|
33
|
+
"verbose_messages",
|
41
34
|
]:
|
42
35
|
if hasattr(args, flag):
|
43
36
|
runtime_config.set(flag, getattr(args, flag, False))
|
44
|
-
|
45
|
-
|
46
|
-
if args.role:
|
47
|
-
runtime_config.set("role", args.role)
|
37
|
+
if getattr(args, "trust_tools", False):
|
38
|
+
runtime_config.set("trust_tools", True)
|
48
39
|
if getattr(args, "model", None):
|
49
40
|
runtime_config.set("model", args.model)
|
50
41
|
if getattr(args, "max_tools", None) is not None:
|
51
42
|
runtime_config.set("max_tools", args.max_tools)
|
52
|
-
if getattr(args, "
|
53
|
-
runtime_config.set("
|
54
|
-
if
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
lang = getattr(args, "lang", None) or runtime_config.get("lang", "en")
|
61
|
-
profile_manager = AgentProfileManager(
|
43
|
+
if getattr(args, "verbose_reason", False):
|
44
|
+
runtime_config.set("verbose_reason", True)
|
45
|
+
if getattr(args, "max_tokens", None) is not None:
|
46
|
+
runtime_config.set("max_tokens", args.max_tokens)
|
47
|
+
|
48
|
+
|
49
|
+
def setup_profile_manager(args, role, interaction_mode, profile, lang):
|
50
|
+
return AgentProfileManager(
|
62
51
|
api_key=get_api_key(),
|
63
52
|
model=unified_config.get("model"),
|
64
53
|
role=role,
|
65
54
|
profile_name=profile,
|
66
55
|
interaction_mode=interaction_mode,
|
67
56
|
verbose_tools=args.verbose_tools,
|
68
|
-
base_url=unified_config.get("base_url", "
|
57
|
+
base_url=unified_config.get("base_url", ""),
|
69
58
|
azure_openai_api_version=unified_config.get(
|
70
59
|
"azure_openai_api_version", "2023-05-15"
|
71
60
|
),
|
72
61
|
use_azure_openai=unified_config.get("use_azure_openai", False),
|
73
62
|
lang=lang,
|
74
63
|
)
|
75
|
-
profile_manager.refresh_prompt()
|
76
|
-
if getattr(args, "show_system", False):
|
77
|
-
print(profile_manager.render_prompt())
|
78
|
-
sys.exit(0)
|
79
|
-
if args.max_tokens is not None:
|
80
|
-
runtime_config.set("max_tokens", args.max_tokens)
|
81
|
-
if getattr(args, "verbose_reason", False):
|
82
|
-
runtime_config.set("verbose_reason", True)
|
83
64
|
|
84
|
-
|
65
|
+
|
66
|
+
def handle_termweb(args, interaction_mode):
|
85
67
|
termweb_proc = None
|
86
68
|
selected_port = None
|
69
|
+
termweb_stdout_path = None
|
70
|
+
termweb_stderr_path = None
|
87
71
|
if (
|
88
72
|
not getattr(args, "no_termweb", False)
|
89
73
|
and interaction_mode == "chat"
|
90
74
|
and not runtime_config.get("vanilla_mode", False)
|
91
|
-
and not getattr(args, "input_arg", None)
|
75
|
+
and not getattr(args, "input_arg", None)
|
92
76
|
):
|
93
77
|
default_port = 8088
|
94
78
|
max_port = 8100
|
@@ -122,54 +106,131 @@ def run_cli(args):
|
|
122
106
|
termweb_proc, started, termweb_stdout_path, termweb_stderr_path = start_termweb(
|
123
107
|
selected_port
|
124
108
|
)
|
125
|
-
# Store last running port in .janito/config.json if started
|
126
109
|
if started:
|
127
110
|
from janito.agent.config import local_config
|
128
111
|
|
129
112
|
local_config.set("termweb_last_running_port", selected_port)
|
130
113
|
local_config.save()
|
114
|
+
return termweb_proc, termweb_stdout_path, termweb_stderr_path
|
115
|
+
|
116
|
+
|
117
|
+
def handle_continue_session(args):
|
118
|
+
continue_session = False
|
119
|
+
session_id = None
|
120
|
+
if getattr(args, "input_arg", None) or getattr(args, "continue_session", False):
|
121
|
+
_cont = getattr(args, "continue_session", False)
|
122
|
+
if _cont:
|
123
|
+
continue_session = True
|
124
|
+
session_id = getattr(args, "input_arg", None)
|
125
|
+
if session_id is None:
|
126
|
+
import os
|
127
|
+
import glob
|
128
|
+
|
129
|
+
chat_hist_dir = (
|
130
|
+
os.path.join(os.path.expanduser("~"), ".janito", "chat_history")
|
131
|
+
if not os.path.isabs(".janito")
|
132
|
+
else os.path.join(".janito", "chat_history")
|
133
|
+
)
|
134
|
+
if not os.path.exists(chat_hist_dir):
|
135
|
+
session_id = None
|
136
|
+
else:
|
137
|
+
files = glob.glob(os.path.join(chat_hist_dir, "*.json"))
|
138
|
+
if files:
|
139
|
+
latest = max(files, key=os.path.getmtime)
|
140
|
+
session_id = os.path.splitext(os.path.basename(latest))[0]
|
141
|
+
else:
|
142
|
+
session_id = None
|
143
|
+
else:
|
144
|
+
continue_session = False
|
145
|
+
session_id = None
|
146
|
+
return continue_session, session_id
|
147
|
+
|
148
|
+
|
149
|
+
def handle_prompt_mode(args, profile_manager):
|
150
|
+
prompt = getattr(args, "input_arg", None)
|
151
|
+
from rich.console import Console
|
152
|
+
from janito.agent.rich_message_handler import RichMessageHandler
|
153
|
+
|
154
|
+
console = Console()
|
155
|
+
message_handler = RichMessageHandler()
|
156
|
+
messages = []
|
157
|
+
system_prompt_override = runtime_config.get("system_prompt_template")
|
158
|
+
if system_prompt_override:
|
159
|
+
if not runtime_config.get("vanilla_mode", False) or getattr(
|
160
|
+
args, "system", None
|
161
|
+
):
|
162
|
+
messages.append({"role": "system", "content": system_prompt_override})
|
163
|
+
elif profile_manager.system_prompt_template and not runtime_config.get(
|
164
|
+
"vanilla_mode", False
|
165
|
+
):
|
166
|
+
messages.append(
|
167
|
+
{"role": "system", "content": profile_manager.system_prompt_template}
|
168
|
+
)
|
169
|
+
messages.append({"role": "user", "content": prompt})
|
170
|
+
import time
|
171
|
+
|
172
|
+
info_start_time = None
|
173
|
+
if getattr(args, "info", False):
|
174
|
+
info_start_time = time.time()
|
175
|
+
try:
|
176
|
+
max_rounds = 100
|
177
|
+
result = profile_manager.agent.chat(
|
178
|
+
LLMConversationHistory(messages),
|
179
|
+
message_handler=message_handler,
|
180
|
+
spinner=True,
|
181
|
+
max_rounds=max_rounds,
|
182
|
+
tool_user=getattr(args, "tool_user", False),
|
183
|
+
)
|
184
|
+
if (
|
185
|
+
getattr(args, "info", False)
|
186
|
+
and info_start_time is not None
|
187
|
+
and result is not None
|
188
|
+
):
|
189
|
+
usage_info = result.get("usage")
|
190
|
+
total_tokens = usage_info.get("total_tokens") if usage_info else None
|
191
|
+
prompt_tokens = usage_info.get("prompt_tokens") if usage_info else None
|
192
|
+
completion_tokens = (
|
193
|
+
usage_info.get("completion_tokens") if usage_info else None
|
194
|
+
)
|
195
|
+
elapsed = time.time() - info_start_time
|
196
|
+
console.print(
|
197
|
+
f"[bold green]Total tokens:[/] [yellow]{total_tokens}[/yellow] [bold green]| Input:[/] [cyan]{prompt_tokens}[/cyan] [bold green]| Output:[/] [magenta]{completion_tokens}[/magenta] [bold green]| Elapsed:[/] [yellow]{elapsed:.2f}s[/yellow]",
|
198
|
+
style="dim",
|
199
|
+
)
|
200
|
+
except MaxRoundsExceededError:
|
201
|
+
console.print("[red]Max conversation rounds exceeded.[/red]")
|
202
|
+
except ProviderError as e:
|
203
|
+
console.print(f"[red]Provider error:[/red] {e}")
|
204
|
+
except EmptyResponseError as e:
|
205
|
+
console.print(f"[red]Error:[/red] {e}")
|
206
|
+
|
131
207
|
|
132
|
-
|
208
|
+
def run_cli(args):
|
209
|
+
if args.version:
|
210
|
+
print(f"janito version {__version__}")
|
211
|
+
sys.exit(0)
|
212
|
+
normalize_args(args)
|
213
|
+
role = args.role or unified_config.get("role", "software engineer")
|
214
|
+
if args.role:
|
215
|
+
runtime_config.set("role", args.role)
|
216
|
+
interaction_mode = "chat" if not getattr(args, "prompt", None) else "prompt"
|
217
|
+
profile = "base"
|
218
|
+
lang = getattr(args, "lang", None) or runtime_config.get("lang", "en")
|
219
|
+
profile_manager = setup_profile_manager(args, role, interaction_mode, profile, lang)
|
220
|
+
profile_manager.refresh_prompt()
|
221
|
+
if getattr(args, "show_system", False):
|
222
|
+
print(profile_manager.render_prompt())
|
223
|
+
sys.exit(0)
|
224
|
+
termweb_proc, termweb_stdout_path, termweb_stderr_path = handle_termweb(
|
225
|
+
args, interaction_mode
|
226
|
+
)
|
133
227
|
try:
|
134
|
-
|
135
|
-
livereload_stderr_path = None
|
136
|
-
continue_session = False
|
137
|
-
session_id = None
|
228
|
+
continue_session, session_id = handle_continue_session(args)
|
138
229
|
if getattr(args, "input_arg", None):
|
139
230
|
from janito.cli.one_shot import run_oneshot_mode
|
140
231
|
|
141
232
|
run_oneshot_mode(args, profile_manager, runtime_config)
|
142
233
|
return
|
143
|
-
if not getattr(args, "input_arg", None) or getattr(
|
144
|
-
args, "continue_session", False
|
145
|
-
):
|
146
|
-
# Determine continue_session and session_id
|
147
|
-
_cont = getattr(args, "continue_session", False)
|
148
|
-
if _cont:
|
149
|
-
continue_session = True
|
150
|
-
session_id = getattr(args, "input_arg", None)
|
151
|
-
if session_id is None:
|
152
|
-
# Find the most recent session id from .janito/chat_history/*.json
|
153
|
-
import os
|
154
|
-
import glob
|
155
|
-
|
156
|
-
chat_hist_dir = (
|
157
|
-
os.path.join(os.path.expanduser("~"), ".janito", "chat_history")
|
158
|
-
if not os.path.isabs(".janito")
|
159
|
-
else os.path.join(".janito", "chat_history")
|
160
|
-
)
|
161
|
-
if not os.path.exists(chat_hist_dir):
|
162
|
-
session_id = None
|
163
|
-
else:
|
164
|
-
files = glob.glob(os.path.join(chat_hist_dir, "*.json"))
|
165
|
-
if files:
|
166
|
-
latest = max(files, key=os.path.getmtime)
|
167
|
-
session_id = os.path.splitext(os.path.basename(latest))[0]
|
168
|
-
else:
|
169
|
-
session_id = None
|
170
|
-
else:
|
171
|
-
continue_session = False
|
172
|
-
session_id = None
|
173
234
|
import time
|
174
235
|
|
175
236
|
info_start_time = None
|
@@ -179,18 +240,10 @@ def run_cli(args):
|
|
179
240
|
profile_manager,
|
180
241
|
continue_session=continue_session,
|
181
242
|
session_id=session_id,
|
182
|
-
termweb_stdout_path=
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
termweb_stderr_path if "termweb_stderr_path" in locals() else None
|
187
|
-
),
|
188
|
-
livereload_stdout_path=(
|
189
|
-
livereload_stdout_path if "livereload_stdout_path" in locals() else None
|
190
|
-
),
|
191
|
-
livereload_stderr_path=(
|
192
|
-
livereload_stderr_path if "livereload_stderr_path" in locals() else None
|
193
|
-
),
|
243
|
+
termweb_stdout_path=termweb_stdout_path,
|
244
|
+
termweb_stderr_path=termweb_stderr_path,
|
245
|
+
livereload_stdout_path=None,
|
246
|
+
livereload_stderr_path=None,
|
194
247
|
)
|
195
248
|
if (
|
196
249
|
getattr(args, "info", False)
|
@@ -200,75 +253,12 @@ def run_cli(args):
|
|
200
253
|
elapsed = time.time() - info_start_time
|
201
254
|
from rich.console import Console
|
202
255
|
|
203
|
-
console = Console()
|
204
256
|
total_tokens = usage_info.get("total_tokens")
|
257
|
+
console = Console()
|
205
258
|
console.print(
|
206
259
|
f"[bold green]Total tokens used:[/] [yellow]{total_tokens}[/yellow] [bold green]| Elapsed time:[/] [yellow]{elapsed:.2f}s[/yellow]"
|
207
260
|
)
|
208
261
|
sys.exit(0)
|
209
|
-
# --- Prompt mode ---
|
210
|
-
prompt = getattr(args, "input_arg", None)
|
211
|
-
from rich.console import Console
|
212
|
-
from janito.agent.rich_message_handler import RichMessageHandler
|
213
|
-
|
214
|
-
console = Console()
|
215
|
-
message_handler = RichMessageHandler()
|
216
|
-
messages = []
|
217
|
-
system_prompt_override = runtime_config.get("system_prompt_template")
|
218
|
-
if system_prompt_override:
|
219
|
-
# Só adiciona system prompt se NÃO for vanilla, ou se foi explicitamente passado via --system
|
220
|
-
if not runtime_config.get("vanilla_mode", False) or getattr(
|
221
|
-
args, "system", None
|
222
|
-
):
|
223
|
-
messages.append({"role": "system", "content": system_prompt_override})
|
224
|
-
elif profile_manager.system_prompt_template and not runtime_config.get(
|
225
|
-
"vanilla_mode", False
|
226
|
-
):
|
227
|
-
messages.append(
|
228
|
-
{"role": "system", "content": profile_manager.system_prompt_template}
|
229
|
-
)
|
230
|
-
messages.append({"role": "user", "content": prompt})
|
231
|
-
import time
|
232
|
-
|
233
|
-
info_start_time = None
|
234
|
-
if getattr(args, "info", False):
|
235
|
-
info_start_time = time.time()
|
236
|
-
try:
|
237
|
-
max_rounds = 100
|
238
|
-
from janito.agent.conversation_history import ConversationHistory
|
239
|
-
|
240
|
-
result = profile_manager.agent.chat(
|
241
|
-
ConversationHistory(messages),
|
242
|
-
message_handler=message_handler,
|
243
|
-
spinner=True,
|
244
|
-
max_rounds=max_rounds,
|
245
|
-
stream=getattr(args, "stream", False),
|
246
|
-
)
|
247
|
-
if (
|
248
|
-
getattr(args, "info", False)
|
249
|
-
and info_start_time is not None
|
250
|
-
and result is not None
|
251
|
-
):
|
252
|
-
usage_info = result.get("usage")
|
253
|
-
total_tokens = usage_info.get("total_tokens") if usage_info else None
|
254
|
-
prompt_tokens = usage_info.get("prompt_tokens") if usage_info else None
|
255
|
-
completion_tokens = (
|
256
|
-
usage_info.get("completion_tokens") if usage_info else None
|
257
|
-
)
|
258
|
-
elapsed = time.time() - info_start_time
|
259
|
-
from rich.console import Console
|
260
|
-
|
261
|
-
console = Console()
|
262
|
-
console.print(
|
263
|
-
f"[bold green]Total tokens:[/] [yellow]{total_tokens}[/yellow] [bold green]| Input:[/] [cyan]{prompt_tokens}[/cyan] [bold green]| Output:[/] [magenta]{completion_tokens}[/magenta] [bold green]| Elapsed:[/] [yellow]{elapsed:.2f}s[/yellow]",
|
264
|
-
style="dim",
|
265
|
-
)
|
266
|
-
except MaxRoundsExceededError:
|
267
|
-
console.print("[red]Max conversation rounds exceeded.[/red]")
|
268
|
-
except ProviderError as e:
|
269
|
-
console.print(f"[red]Provider error:[/red] {e}")
|
270
|
-
except EmptyResponseError as e:
|
271
|
-
console.print(f"[red]Error:[/red] {e}")
|
272
262
|
except KeyboardInterrupt:
|
273
263
|
from rich.console import Console
|
274
264
|
|
janito/cli/config_commands.py
CHANGED
@@ -4,12 +4,11 @@ from janito.agent.runtime_config import unified_config, runtime_config
|
|
4
4
|
from janito.agent.config_defaults import CONFIG_DEFAULTS
|
5
5
|
from rich import print
|
6
6
|
from ._utils import home_shorten
|
7
|
+
import os
|
8
|
+
from pathlib import Path
|
7
9
|
|
8
10
|
|
9
|
-
def
|
10
|
-
"""Handle --set-local-config, --set-global-config, --show-config. Exit if any are used."""
|
11
|
-
did_something = False
|
12
|
-
|
11
|
+
def handle_run_config(args):
|
13
12
|
if args.run_config:
|
14
13
|
for run_item in args.run_config:
|
15
14
|
try:
|
@@ -24,6 +23,11 @@ def handle_config_commands(args):
|
|
24
23
|
)
|
25
24
|
sys.exit(1)
|
26
25
|
runtime_config.set(key, val.strip())
|
26
|
+
return True
|
27
|
+
return False
|
28
|
+
|
29
|
+
|
30
|
+
def handle_set_local_config(args):
|
27
31
|
if args.set_local_config:
|
28
32
|
try:
|
29
33
|
key, val = args.set_local_config.split("=", 1)
|
@@ -40,8 +44,11 @@ def handle_config_commands(args):
|
|
40
44
|
local_config.save()
|
41
45
|
runtime_config.set(key, val.strip())
|
42
46
|
print(f"Local config updated: {key} = {val.strip()}")
|
43
|
-
|
47
|
+
return True
|
48
|
+
return False
|
49
|
+
|
44
50
|
|
51
|
+
def handle_set_global_config(args):
|
45
52
|
if args.set_global_config:
|
46
53
|
try:
|
47
54
|
key, val = args.set_global_config.split("=", 1)
|
@@ -65,129 +72,111 @@ def handle_config_commands(args):
|
|
65
72
|
del global_config._data[key]
|
66
73
|
runtime_config.set("template", template_dict)
|
67
74
|
print(f"Global config updated: template.{subkey} = {val.strip()}")
|
68
|
-
|
75
|
+
return True
|
69
76
|
else:
|
70
77
|
global_config.set(key, val.strip())
|
71
78
|
global_config.save()
|
72
79
|
runtime_config.set(key, val.strip())
|
73
80
|
print(f"Global config updated: {key} = {val.strip()}")
|
74
|
-
|
81
|
+
return True
|
82
|
+
return False
|
75
83
|
|
84
|
+
|
85
|
+
def handle_set_api_key(args):
|
76
86
|
if args.set_api_key:
|
77
|
-
# Merge: load full config, update api_key, save all
|
78
87
|
existing = dict(global_config.all())
|
79
88
|
existing["api_key"] = args.set_api_key.strip()
|
80
89
|
global_config._data = existing
|
81
90
|
global_config.save()
|
82
91
|
runtime_config.set("api_key", args.set_api_key.strip())
|
83
92
|
print("Global API key saved.")
|
84
|
-
|
93
|
+
return True
|
94
|
+
return False
|
95
|
+
|
85
96
|
|
97
|
+
def handle_show_config(args):
|
86
98
|
if args.show_config:
|
87
|
-
local_items =
|
88
|
-
global_items =
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
# Handle template as nested dict
|
98
|
-
for key in sorted(local_keys):
|
99
|
-
if key == "template":
|
100
|
-
template_dict = local_config.get("template", {})
|
101
|
-
if template_dict:
|
102
|
-
local_items["template"] = f"({len(template_dict)} keys set)"
|
103
|
-
for tkey, tval in template_dict.items():
|
104
|
-
local_items[f" template.{tkey}"] = tval
|
105
|
-
continue
|
106
|
-
if key.startswith("template."):
|
107
|
-
# Skip legacy flat keys
|
108
|
-
continue
|
109
|
-
if key == "api_key":
|
110
|
-
value = local_config.get("api_key")
|
111
|
-
value = (
|
112
|
-
value[:4] + "..." + value[-4:]
|
113
|
-
if value and len(value) > 8
|
114
|
-
else ("***" if value else None)
|
115
|
-
)
|
116
|
-
else:
|
117
|
-
value = unified_config.get(key)
|
118
|
-
local_items[key] = value
|
119
|
-
for key in sorted(global_keys - local_keys):
|
120
|
-
if key == "template":
|
121
|
-
template_dict = global_config.get("template", {})
|
122
|
-
if template_dict:
|
123
|
-
global_items["template"] = f"({len(template_dict)} keys set)"
|
124
|
-
for tkey, tval in template_dict.items():
|
125
|
-
global_items[f" template.{tkey}"] = tval
|
126
|
-
continue
|
127
|
-
if key.startswith("template."):
|
128
|
-
continue
|
129
|
-
if key == "api_key":
|
130
|
-
value = global_config.get("api_key")
|
131
|
-
value = (
|
132
|
-
value[:4] + "..." + value[-4:]
|
133
|
-
if value and len(value) > 8
|
134
|
-
else ("***" if value else None)
|
135
|
-
)
|
136
|
-
else:
|
137
|
-
value = unified_config.get(key)
|
138
|
-
global_items[key] = value
|
139
|
-
|
140
|
-
# Mask API key
|
141
|
-
for cfg in (local_items, global_items):
|
142
|
-
if "api_key" in cfg and cfg["api_key"]:
|
143
|
-
val = cfg["api_key"]
|
144
|
-
cfg["api_key"] = (
|
145
|
-
val[:4] + "..." + val[-4:] if len(val) > 8 else "***"
|
146
|
-
)
|
147
|
-
|
148
|
-
# Print local config
|
149
|
-
from ._print_config import print_config_items
|
150
|
-
|
151
|
-
print_config_items(
|
152
|
-
local_items, color_label="[cyan]🏠 Local Configuration[/cyan]"
|
153
|
-
)
|
99
|
+
local_items = _collect_config_items(local_config, unified_config, True)
|
100
|
+
global_items = _collect_config_items(
|
101
|
+
global_config, unified_config, False, set(local_items.keys())
|
102
|
+
)
|
103
|
+
_mask_api_keys(local_items)
|
104
|
+
_mask_api_keys(global_items)
|
105
|
+
_print_config_items(local_items, global_items)
|
106
|
+
_print_default_items(local_items, global_items)
|
107
|
+
return True
|
108
|
+
return False
|
154
109
|
|
155
|
-
|
156
|
-
|
157
|
-
|
110
|
+
|
111
|
+
def _collect_config_items(config, unified_config, is_local, exclude_keys=None):
|
112
|
+
items = {}
|
113
|
+
keys = set(config.all().keys())
|
114
|
+
if exclude_keys:
|
115
|
+
keys = keys - set(exclude_keys)
|
116
|
+
for key in sorted(keys):
|
117
|
+
if key == "template":
|
118
|
+
template_dict = config.get("template", {})
|
119
|
+
if template_dict:
|
120
|
+
items["template"] = f"({len(template_dict)} keys set)"
|
121
|
+
for tkey, tval in template_dict.items():
|
122
|
+
items[f" template.{tkey}"] = tval
|
123
|
+
continue
|
124
|
+
if key.startswith("template."):
|
125
|
+
continue
|
126
|
+
if key == "api_key":
|
127
|
+
value = config.get("api_key")
|
128
|
+
value = (
|
129
|
+
value[:4] + "..." + value[-4:]
|
130
|
+
if value and len(value) > 8
|
131
|
+
else ("***" if value else None)
|
158
132
|
)
|
133
|
+
else:
|
134
|
+
value = unified_config.get(key)
|
135
|
+
items[key] = value
|
136
|
+
return items
|
137
|
+
|
138
|
+
|
139
|
+
def _mask_api_keys(cfg):
|
140
|
+
if "api_key" in cfg and cfg["api_key"]:
|
141
|
+
val = cfg["api_key"]
|
142
|
+
cfg["api_key"] = val[:4] + "..." + val[-4:] if len(val) > 8 else "***"
|
159
143
|
|
160
|
-
# Show defaults for unset keys
|
161
|
-
shown_keys = set(local_items.keys()) | set(global_items.keys())
|
162
|
-
default_items = {
|
163
|
-
k: v
|
164
|
-
for k, v in CONFIG_DEFAULTS.items()
|
165
|
-
if k not in shown_keys and k != "api_key"
|
166
|
-
}
|
167
|
-
if default_items:
|
168
|
-
print("[green]🟢 Defaults (not set in config files)[/green]")
|
169
|
-
for key, value in default_items.items():
|
170
|
-
# Special case for system_prompt: show template file if None
|
171
|
-
if key == "system_prompt" and value is None:
|
172
|
-
from pathlib import Path
|
173
|
-
|
174
|
-
template_path = (
|
175
|
-
Path(__file__).parent
|
176
|
-
/ "agent"
|
177
|
-
/ "templates"
|
178
|
-
/ "system_prompt_template_default.j2"
|
179
|
-
)
|
180
|
-
print(
|
181
|
-
f"{key} = (default template path: {home_shorten(str(template_path))})"
|
182
|
-
)
|
183
|
-
else:
|
184
|
-
print(f"{key} = {value}")
|
185
|
-
print()
|
186
|
-
did_something = True
|
187
|
-
|
188
|
-
import os
|
189
|
-
from pathlib import Path
|
190
144
|
|
145
|
+
def _print_config_items(local_items, global_items):
|
146
|
+
from ._print_config import print_config_items
|
147
|
+
|
148
|
+
print_config_items(local_items, color_label="[cyan]🏠 Local Configuration[/cyan]")
|
149
|
+
print_config_items(
|
150
|
+
global_items, color_label="[yellow]🌐 Global Configuration[/yellow]"
|
151
|
+
)
|
152
|
+
|
153
|
+
|
154
|
+
def _print_default_items(local_items, global_items):
|
155
|
+
shown_keys = set(local_items.keys()) | set(global_items.keys())
|
156
|
+
default_items = {
|
157
|
+
k: v
|
158
|
+
for k, v in CONFIG_DEFAULTS.items()
|
159
|
+
if k not in shown_keys and k != "api_key"
|
160
|
+
}
|
161
|
+
if default_items:
|
162
|
+
print("[green]🟢 Defaults (not set in config files)[/green]")
|
163
|
+
for key, value in default_items.items():
|
164
|
+
if key == "system_prompt" and value is None:
|
165
|
+
template_path = (
|
166
|
+
Path(__file__).parent
|
167
|
+
/ "agent"
|
168
|
+
/ "templates"
|
169
|
+
/ "system_prompt_template_default.j2"
|
170
|
+
)
|
171
|
+
print(
|
172
|
+
f"{key} = (default template path: {home_shorten(str(template_path))})"
|
173
|
+
)
|
174
|
+
else:
|
175
|
+
print(f"{key} = {value}")
|
176
|
+
print()
|
177
|
+
|
178
|
+
|
179
|
+
def handle_config_reset_local(args):
|
191
180
|
if getattr(args, "config_reset_local", False):
|
192
181
|
local_path = Path(".janito/config.json")
|
193
182
|
if local_path.exists():
|
@@ -196,6 +185,9 @@ def handle_config_commands(args):
|
|
196
185
|
else:
|
197
186
|
print(f"Local config file does not exist: {local_path}")
|
198
187
|
sys.exit(0)
|
188
|
+
|
189
|
+
|
190
|
+
def handle_config_reset_global(args):
|
199
191
|
if getattr(args, "config_reset_global", False):
|
200
192
|
global_path = Path.home() / ".janito/config.json"
|
201
193
|
if global_path.exists():
|
@@ -204,5 +196,16 @@ def handle_config_commands(args):
|
|
204
196
|
else:
|
205
197
|
print(f"Global config file does not exist: {global_path}")
|
206
198
|
sys.exit(0)
|
199
|
+
|
200
|
+
|
201
|
+
def handle_config_commands(args):
|
202
|
+
did_something = False
|
203
|
+
did_something |= handle_run_config(args)
|
204
|
+
did_something |= handle_set_local_config(args)
|
205
|
+
did_something |= handle_set_global_config(args)
|
206
|
+
did_something |= handle_set_api_key(args)
|
207
|
+
did_something |= handle_show_config(args)
|
208
|
+
handle_config_reset_local(args)
|
209
|
+
handle_config_reset_global(args)
|
207
210
|
if did_something:
|
208
211
|
sys.exit(0)
|