janito 2.29.0__py3-none-any.whl → 2.31.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/README.md +3 -3
- janito/agent/setup_agent.py +21 -35
- janito/agent/templates/profiles/system_prompt_template_Developer_with_Python_Tools.txt.j2 +6 -0
- janito/agent/templates/profiles/system_prompt_template_developer.txt.j2 +6 -0
- janito/agent/templates/profiles/system_prompt_template_market_analyst.txt.j2 +7 -1
- janito/agent/templates/profiles/system_prompt_template_model_conversation_without_tools_or_context.txt.j2 +7 -1
- janito/cli/chat_mode/session.py +19 -51
- janito/cli/cli_commands/list_plugins.py +99 -75
- janito/cli/cli_commands/show_system_prompt.py +8 -3
- janito/cli/core/runner.py +2 -2
- janito/cli/main_cli.py +9 -15
- janito/cli/prompt_core.py +2 -0
- janito/cli/single_shot_mode/handler.py +2 -0
- janito/llm/agent.py +6 -1
- janito/provider_registry.py +1 -1
- janito/providers/openai/provider.py +1 -1
- janito/tools/adapters/local/ask_user.py +3 -1
- janito/tools/adapters/local/fetch_url.py +20 -28
- janito/tools/adapters/local/replace_text_in_file.py +9 -3
- janito/tools/adapters/local/search_text/core.py +2 -2
- janito/tools/loop_protection_decorator.py +12 -16
- janito/tools/tools_adapter.py +18 -4
- {janito-2.29.0.dist-info → janito-2.31.0.dist-info}/METADATA +1 -1
- {janito-2.29.0.dist-info → janito-2.31.0.dist-info}/RECORD +28 -29
- janito/cli/chat_mode/session_profile_select.py +0 -182
- {janito-2.29.0.dist-info → janito-2.31.0.dist-info}/WHEEL +0 -0
- {janito-2.29.0.dist-info → janito-2.31.0.dist-info}/entry_points.txt +0 -0
- {janito-2.29.0.dist-info → janito-2.31.0.dist-info}/licenses/LICENSE +0 -0
- {janito-2.29.0.dist-info → janito-2.31.0.dist-info}/top_level.txt +0 -0
janito/README.md
CHANGED
@@ -116,11 +116,11 @@ Janito includes powerful built-in tools for:
|
|
116
116
|
- System commands
|
117
117
|
- And more...
|
118
118
|
|
119
|
-
### Profiles
|
119
|
+
### Profiles
|
120
120
|
Use predefined system prompts:
|
121
121
|
```bash
|
122
|
-
janito --
|
123
|
-
janito --
|
122
|
+
janito --developer "Create a REST API" # Same as --profile developer
|
123
|
+
janito --market "Analyze market trends" # Same as --profile market-analyst
|
124
124
|
```
|
125
125
|
|
126
126
|
### Environment Variables
|
janito/agent/setup_agent.py
CHANGED
@@ -25,10 +25,14 @@ def _load_template_content(profile, templates_dir):
|
|
25
25
|
|
26
26
|
Spaces in the profile name are converted to underscores to align with the file-naming convention (e.g. "Developer with Python Tools" ➜ "Developer_with_Python_Tools" (matches: system_prompt_template_Developer_with_Python_Tools.txt.j2)).
|
27
27
|
"""
|
28
|
-
|
29
|
-
sanitized_profile = re.sub(r"\s+", "_", profile.strip()) if profile else profile
|
30
|
-
|
28
|
+
sanitized_profile = re.sub(r"\s+", "_", profile.strip())
|
31
29
|
template_filename = f"system_prompt_template_{sanitized_profile}.txt.j2"
|
30
|
+
|
31
|
+
return _find_template_file(template_filename, templates_dir)
|
32
|
+
|
33
|
+
|
34
|
+
def _find_template_file(template_filename, templates_dir):
|
35
|
+
"""Find and load template file from various locations."""
|
32
36
|
template_path = templates_dir / template_filename
|
33
37
|
|
34
38
|
# 1) Check local templates directory
|
@@ -96,39 +100,9 @@ def _load_template_content(profile, templates_dir):
|
|
96
100
|
)
|
97
101
|
|
98
102
|
raise FileNotFoundError(error_msg)
|
99
|
-
# Replace spaces in profile name with underscores for filename resolution
|
100
|
-
sanitized_profile = re.sub(r"\\s+", "_", profile.strip()) if profile else profile
|
101
|
-
"""
|
102
|
-
Loads the template content for the given profile from the specified directory or package resources.
|
103
|
-
If the profile template is not found in the default locations, tries to load from the user profiles directory ~/.janito/profiles.
|
104
|
-
"""
|
105
|
-
|
106
|
-
# Sanitize profile name by replacing spaces with underscores to match filename conventions
|
107
|
-
sanitized_profile = re.sub(r"\\s+", "_", profile.strip())
|
108
|
-
template_filename = f"system_prompt_template_{sanitized_profile}.txt.j2"
|
109
|
-
template_path = templates_dir / template_filename
|
110
|
-
if template_path.exists():
|
111
|
-
with open(template_path, "r", encoding="utf-8") as file:
|
112
|
-
return file.read(), template_path
|
113
|
-
# Try package import fallback
|
114
|
-
try:
|
115
|
-
with importlib.resources.files("janito.agent.templates.profiles").joinpath(
|
116
|
-
template_filename
|
117
|
-
).open("r", encoding="utf-8") as file:
|
118
|
-
return file.read(), template_path
|
119
|
-
except (FileNotFoundError, ModuleNotFoundError, AttributeError):
|
120
|
-
# Try user profiles directory
|
121
|
-
user_profiles_dir = Path(os.path.expanduser("~/.janito/profiles"))
|
122
|
-
user_template_path = user_profiles_dir / profile
|
123
|
-
if user_template_path.exists():
|
124
|
-
with open(user_template_path, "r", encoding="utf-8") as file:
|
125
|
-
return file.read(), user_template_path
|
126
|
-
raise FileNotFoundError(
|
127
|
-
f"[janito] Could not find profile-specific template '{template_filename}' in {template_path} nor in janito.agent.templates.profiles package nor in user profiles directory {user_template_path}."
|
128
|
-
)
|
129
103
|
|
130
104
|
|
131
|
-
def _prepare_template_context(role, profile, allowed_permissions):
|
105
|
+
def _prepare_template_context(role, profile, allowed_permissions, args=None):
|
132
106
|
"""
|
133
107
|
Prepares the context dictionary for Jinja2 template rendering.
|
134
108
|
"""
|
@@ -148,6 +122,11 @@ def _prepare_template_context(role, profile, allowed_permissions):
|
|
148
122
|
perm_str += "x"
|
149
123
|
allowed_permissions = perm_str or None
|
150
124
|
context["allowed_permissions"] = allowed_permissions
|
125
|
+
|
126
|
+
# Add emoji flag for system prompt
|
127
|
+
context["emoji_enabled"] = (
|
128
|
+
getattr(args, "emoji", False) if "args" in locals() else False
|
129
|
+
)
|
151
130
|
# Inject platform info if execute permission is present
|
152
131
|
if allowed_permissions and "x" in allowed_permissions:
|
153
132
|
pd = PlatformDiscovery()
|
@@ -171,6 +150,11 @@ def _prepare_template_context(role, profile, allowed_permissions):
|
|
171
150
|
else:
|
172
151
|
context["allowed_sites_info"] = f"Restricted to: {', '.join(allowed_sites)}"
|
173
152
|
|
153
|
+
# Add emoji flag for system prompt
|
154
|
+
context["emoji_enabled"] = (
|
155
|
+
getattr(args, "emoji", False) if "args" in locals() else False
|
156
|
+
)
|
157
|
+
|
174
158
|
return context
|
175
159
|
|
176
160
|
|
@@ -267,7 +251,9 @@ def setup_agent(
|
|
267
251
|
template_content, template_path = _load_template_content(profile, templates_dir)
|
268
252
|
|
269
253
|
template = Template(template_content)
|
270
|
-
context = _prepare_template_context(
|
254
|
+
context = _prepare_template_context(
|
255
|
+
role, profile, allowed_permissions, locals().get("args")
|
256
|
+
)
|
271
257
|
|
272
258
|
# Debug output if requested
|
273
259
|
debug_flag = False
|
@@ -88,4 +88,10 @@ You are: {{ role | default('developer') }}
|
|
88
88
|
{# Trying to prevent surrogates generation, found this frequently in gpt4.1/windows #}
|
89
89
|
- While writing code, if you need an emoji or special Unicode character in a string, then insert the actual character (e.g., 📖) directly instead of using surrogate pairs or escape sequences.
|
90
90
|
{% endif %}
|
91
|
+
{% if emoji_enabled %}
|
92
|
+
## Emoji Usage
|
93
|
+
- Feel free to use emojis in your responses to make them more engaging and expressive 🎉
|
94
|
+
- Use appropriate emojis to enhance communication: ✅ for success, ⚠️ for warnings, 🔄 for progress, etc.
|
95
|
+
- Emojis should complement the message, not replace clear explanations
|
96
|
+
{% endif %}
|
91
97
|
|
@@ -78,4 +78,10 @@ You are: {{ role | default('software developer') }}
|
|
78
78
|
{# Trying to prevent surrogates generation, found this frequently in gpt4.1/windows #}
|
79
79
|
- While writing code, if you need an emoji or special Unicode character in a string, then insert the actual character (e.g., 📖) directly instead of using surrogate pairs or escape sequences.
|
80
80
|
{% endif %}
|
81
|
+
{% if emoji_enabled %}
|
82
|
+
## Emoji Usage
|
83
|
+
- Feel free to use emojis in your responses to make them more engaging and expressive 🎉
|
84
|
+
- Use appropriate emojis to enhance communication: ✅ for success, ⚠️ for warnings, 🔄 for progress, etc.
|
85
|
+
- Emojis should complement the message, not replace clear explanations
|
86
|
+
{% endif %}
|
81
87
|
|
@@ -107,4 +107,10 @@ When asked about specific stocks or market movements, provide detailed analysis
|
|
107
107
|
{% endfor %}
|
108
108
|
{% endif %}
|
109
109
|
|
110
|
-
## Guidelines
|
110
|
+
## Guidelines
|
111
|
+
{% if emoji_enabled %}
|
112
|
+
## Emoji Usage
|
113
|
+
- Feel free to use emojis in your responses to make them more engaging and expressive 📊
|
114
|
+
- Use appropriate emojis to enhance communication: 📈 for uptrends, 📉 for downtrends, 💰 for profits, ⚠️ for warnings, etc.
|
115
|
+
- Emojis should complement the message, not replace clear financial analysis
|
116
|
+
{% endif %}
|
@@ -50,4 +50,10 @@ You are: {{ role | default('helpful assistant') }}
|
|
50
50
|
## Guidelines
|
51
51
|
- Provide helpful, accurate, and concise responses
|
52
52
|
- Ask clarifying questions when needed
|
53
|
-
- Maintain a friendly and professional tone
|
53
|
+
- Maintain a friendly and professional tone
|
54
|
+
{% if emoji_enabled %}
|
55
|
+
## Emoji Usage
|
56
|
+
- Feel free to use emojis in your responses to make them more engaging and expressive 😊
|
57
|
+
- Use appropriate emojis to enhance communication: ✅ for confirmations, ❓ for questions, 💡 for ideas, etc.
|
58
|
+
- Emojis should complement the message, not replace clear communication
|
59
|
+
{% endif %}
|
janito/cli/chat_mode/session.py
CHANGED
@@ -119,9 +119,7 @@ class ChatSession:
|
|
119
119
|
profile_system_prompt = None
|
120
120
|
no_tools_mode = False
|
121
121
|
|
122
|
-
profile = self._determine_profile(
|
123
|
-
profile, role_arg, python_profile, market_profile
|
124
|
-
)
|
122
|
+
profile = self._determine_profile(profile, python_profile, market_profile)
|
125
123
|
|
126
124
|
if (
|
127
125
|
profile is None
|
@@ -136,31 +134,8 @@ class ChatSession:
|
|
136
134
|
if skip_profile_selection:
|
137
135
|
profile = "Developer with Python Tools" # Default for non-interactive commands
|
138
136
|
else:
|
139
|
-
try:
|
140
|
-
from janito.cli.chat_mode.session_profile_select import (
|
141
|
-
select_profile,
|
142
|
-
)
|
143
|
-
|
144
|
-
result = select_profile()
|
145
|
-
if isinstance(result, dict):
|
146
|
-
profile = result.get("profile")
|
147
|
-
profile_system_prompt = result.get("profile_system_prompt")
|
148
|
-
no_tools_mode = result.get("no_tools_mode", False)
|
149
|
-
elif isinstance(result, str) and result.startswith("role:"):
|
150
|
-
role = result[len("role:") :].strip()
|
151
|
-
profile = "Developer with Python Tools"
|
152
|
-
else:
|
153
|
-
profile = (
|
154
|
-
"Developer with Python Tools"
|
155
|
-
if result == "Developer"
|
156
|
-
else result
|
157
|
-
)
|
158
|
-
except ImportError:
|
159
|
-
profile = "Raw Model Session (no tools, no context)"
|
160
|
-
if role_arg is not None:
|
161
|
-
role = role_arg
|
162
|
-
if profile is None:
|
163
137
|
profile = "Developer with Python Tools"
|
138
|
+
|
164
139
|
return profile, role, profile_system_prompt, no_tools_mode
|
165
140
|
|
166
141
|
def _create_conversation_history(self):
|
@@ -308,25 +283,6 @@ class ChatSession:
|
|
308
283
|
)
|
309
284
|
)
|
310
285
|
|
311
|
-
for candidate in candidates:
|
312
|
-
try:
|
313
|
-
if not candidate:
|
314
|
-
continue
|
315
|
-
parsed = urlparse(str(candidate))
|
316
|
-
host = parsed.netloc or parsed.path
|
317
|
-
if host:
|
318
|
-
backend_hostname = host
|
319
|
-
break
|
320
|
-
except Exception:
|
321
|
-
backend_hostname = str(candidate)
|
322
|
-
break
|
323
|
-
|
324
|
-
self.console.print(
|
325
|
-
Rule(
|
326
|
-
f"[bold blue]Model: {model_name} ({provider_name}) | Backend: {backend_hostname}[/bold blue]"
|
327
|
-
)
|
328
|
-
)
|
329
|
-
|
330
286
|
self._prompt_handler.run_prompt(cmd_input)
|
331
287
|
end_time = time.time()
|
332
288
|
elapsed = end_time - start_time
|
@@ -337,6 +293,8 @@ class ChatSession:
|
|
337
293
|
print_token_message_summary(
|
338
294
|
self.console, self.msg_count, usage, elapsed=elapsed
|
339
295
|
)
|
296
|
+
# Send terminal bell character to trigger TUI bell after printing token summary
|
297
|
+
print("\a", end="", flush=True)
|
340
298
|
if final_event and hasattr(final_event, "metadata"):
|
341
299
|
exit_reason = (
|
342
300
|
final_event.metadata.get("exit_reason")
|
@@ -356,18 +314,18 @@ class ChatSession:
|
|
356
314
|
def _extract_args(self, args):
|
357
315
|
"""Extract profile and role arguments from args."""
|
358
316
|
profile = getattr(args, "profile", None) if args is not None else None
|
359
|
-
role_arg =
|
317
|
+
role_arg = None
|
360
318
|
python_profile = (
|
361
319
|
getattr(args, "developer", False) if args is not None else False
|
362
320
|
)
|
363
321
|
market_profile = getattr(args, "market", False) if args is not None else False
|
364
322
|
return profile, role_arg, python_profile, market_profile
|
365
323
|
|
366
|
-
def _determine_profile(self, profile,
|
324
|
+
def _determine_profile(self, profile, python_profile, market_profile):
|
367
325
|
"""Determine the profile based on flags and arguments."""
|
368
|
-
if python_profile and profile is None
|
326
|
+
if python_profile and profile is None:
|
369
327
|
return "Developer with Python Tools"
|
370
|
-
if market_profile and profile is None
|
328
|
+
if market_profile and profile is None:
|
371
329
|
return "Market Analyst"
|
372
330
|
return profile
|
373
331
|
|
@@ -517,9 +475,19 @@ class ChatSession:
|
|
517
475
|
else:
|
518
476
|
duration_str = f"{session_duration/3600:.1f}h"
|
519
477
|
|
478
|
+
# Format tokens in k/m/t as appropriate
|
479
|
+
if total_tokens >= 1_000_000_000:
|
480
|
+
token_str = f"{total_tokens/1_000_000_000:.1f}t"
|
481
|
+
elif total_tokens >= 1_000_000:
|
482
|
+
token_str = f"{total_tokens/1_000_000:.1f}m"
|
483
|
+
elif total_tokens >= 1_000:
|
484
|
+
token_str = f"{total_tokens/1_000:.1f}k"
|
485
|
+
else:
|
486
|
+
token_str = f"{total_tokens}"
|
487
|
+
|
520
488
|
self.console.print(f"[bold yellow]Session completed![/bold yellow]")
|
521
489
|
self.console.print(
|
522
|
-
f"[dim]Session time: {duration_str} | Total tokens: {
|
490
|
+
f"[dim]Session time: {duration_str} | Total tokens: {token_str}[/dim]"
|
523
491
|
)
|
524
492
|
self.console.print("[bold yellow]Goodbye![/bold yellow]")
|
525
493
|
|
@@ -14,80 +14,104 @@ def handle_list_plugins(args: argparse.Namespace) -> None:
|
|
14
14
|
"""List plugins command handler."""
|
15
15
|
|
16
16
|
if getattr(args, "list_plugins_available", False):
|
17
|
-
|
18
|
-
available = list_available_plugins()
|
19
|
-
builtin_plugins = BuiltinPluginRegistry.list_builtin_plugins()
|
20
|
-
|
21
|
-
if available or builtin_plugins:
|
22
|
-
print("Available plugins:")
|
23
|
-
|
24
|
-
# Show builtin plugins first
|
25
|
-
if builtin_plugins:
|
26
|
-
print(" Builtin plugins:")
|
27
|
-
for plugin in builtin_plugins:
|
28
|
-
print(f" - {plugin} [BUILTIN]")
|
29
|
-
|
30
|
-
# Show other available plugins
|
31
|
-
other_plugins = [p for p in available if p not in builtin_plugins]
|
32
|
-
if other_plugins:
|
33
|
-
print(" External plugins:")
|
34
|
-
for plugin in other_plugins:
|
35
|
-
print(f" - {plugin}")
|
36
|
-
else:
|
37
|
-
print("No plugins found in search paths")
|
38
|
-
print("Search paths:")
|
39
|
-
print(f" - {os.getcwd()}/plugins")
|
40
|
-
print(f" - {os.path.expanduser('~')}/.janito/plugins")
|
17
|
+
_list_available_plugins()
|
41
18
|
elif getattr(args, "list_resources", False):
|
42
|
-
|
43
|
-
manager = PluginManager()
|
44
|
-
all_resources = manager.list_all_resources()
|
45
|
-
|
46
|
-
if all_resources:
|
47
|
-
print("Plugin Resources:")
|
48
|
-
for plugin_name, resources in all_resources.items():
|
49
|
-
metadata = manager.get_plugin_metadata(plugin_name)
|
50
|
-
print(
|
51
|
-
f"\n{plugin_name} v{metadata.version if metadata else 'unknown'}:"
|
52
|
-
)
|
53
|
-
|
54
|
-
# Group resources by type
|
55
|
-
tools = [r for r in resources if r["type"] == "tool"]
|
56
|
-
commands = [r for r in resources if r["type"] == "command"]
|
57
|
-
configs = [r for r in resources if r["type"] == "config"]
|
58
|
-
|
59
|
-
if tools:
|
60
|
-
print(" Tools:")
|
61
|
-
for tool in tools:
|
62
|
-
print(f" - {tool['name']}: {tool['description']}")
|
63
|
-
|
64
|
-
if commands:
|
65
|
-
print(" Commands:")
|
66
|
-
for cmd in commands:
|
67
|
-
print(f" - {cmd['name']}: {cmd['description']}")
|
68
|
-
|
69
|
-
if configs:
|
70
|
-
print(" Configuration:")
|
71
|
-
for config in configs:
|
72
|
-
print(f" - {config['name']}: {config['description']}")
|
73
|
-
else:
|
74
|
-
print("No plugins loaded")
|
19
|
+
_list_plugin_resources()
|
75
20
|
else:
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
21
|
+
_list_loaded_plugins()
|
22
|
+
|
23
|
+
|
24
|
+
def _list_available_plugins():
|
25
|
+
"""List available plugins."""
|
26
|
+
available = list_available_plugins()
|
27
|
+
builtin_plugins = BuiltinPluginRegistry.list_builtin_plugins()
|
28
|
+
|
29
|
+
if available or builtin_plugins:
|
30
|
+
print("Available plugins:")
|
31
|
+
_print_builtin_plugins(builtin_plugins)
|
32
|
+
_print_external_plugins(available, builtin_plugins)
|
33
|
+
else:
|
34
|
+
print("No plugins found in search paths")
|
35
|
+
print("Search paths:")
|
36
|
+
print(f" - {os.getcwd()}/plugins")
|
37
|
+
print(f" - {os.path.expanduser('~')}/.janito/plugins")
|
38
|
+
|
39
|
+
|
40
|
+
def _print_builtin_plugins(builtin_plugins):
|
41
|
+
"""Print builtin plugins."""
|
42
|
+
if builtin_plugins:
|
43
|
+
print(" Builtin plugins:")
|
44
|
+
for plugin in builtin_plugins:
|
45
|
+
print(f" - {plugin} [BUILTIN]")
|
46
|
+
|
47
|
+
|
48
|
+
def _print_external_plugins(available, builtin_plugins):
|
49
|
+
"""Print external plugins."""
|
50
|
+
other_plugins = [p for p in available if p not in builtin_plugins]
|
51
|
+
if other_plugins:
|
52
|
+
print(" External plugins:")
|
53
|
+
for plugin in other_plugins:
|
54
|
+
print(f" - {plugin}")
|
55
|
+
|
56
|
+
|
57
|
+
def _list_plugin_resources():
|
58
|
+
"""List all resources from loaded plugins."""
|
59
|
+
manager = PluginManager()
|
60
|
+
all_resources = manager.list_all_resources()
|
61
|
+
|
62
|
+
if all_resources:
|
63
|
+
print("Plugin Resources:")
|
64
|
+
for plugin_name, resources in all_resources.items():
|
65
|
+
metadata = manager.get_plugin_metadata(plugin_name)
|
66
|
+
print(f"\n{plugin_name} v{metadata.version if metadata else 'unknown'}:")
|
67
|
+
_print_resources_by_type(resources)
|
68
|
+
else:
|
69
|
+
print("No plugins loaded")
|
70
|
+
|
71
|
+
|
72
|
+
def _print_resources_by_type(resources):
|
73
|
+
"""Print resources grouped by type."""
|
74
|
+
tools = [r for r in resources if r["type"] == "tool"]
|
75
|
+
commands = [r for r in resources if r["type"] == "command"]
|
76
|
+
configs = [r for r in resources if r["type"] == "config"]
|
77
|
+
|
78
|
+
if tools:
|
79
|
+
print(" Tools:")
|
80
|
+
for tool in tools:
|
81
|
+
print(f" - {tool['name']}: {tool['description']}")
|
82
|
+
|
83
|
+
if commands:
|
84
|
+
print(" Commands:")
|
85
|
+
for cmd in commands:
|
86
|
+
print(f" - {cmd['name']}: {cmd['description']}")
|
87
|
+
|
88
|
+
if configs:
|
89
|
+
print(" Configuration:")
|
90
|
+
for config in configs:
|
91
|
+
print(f" - {config['name']}: {config['description']}")
|
92
|
+
|
93
|
+
|
94
|
+
def _list_loaded_plugins():
|
95
|
+
"""List loaded plugins."""
|
96
|
+
manager = PluginManager()
|
97
|
+
loaded = manager.list_plugins()
|
98
|
+
|
99
|
+
if loaded:
|
100
|
+
print("Loaded plugins:")
|
101
|
+
for plugin_name in loaded:
|
102
|
+
_print_plugin_details(manager, plugin_name)
|
103
|
+
else:
|
104
|
+
print("No plugins loaded")
|
105
|
+
|
106
|
+
|
107
|
+
def _print_plugin_details(manager, plugin_name):
|
108
|
+
"""Print details for a loaded plugin."""
|
109
|
+
metadata = manager.get_plugin_metadata(plugin_name)
|
110
|
+
is_builtin = BuiltinPluginRegistry.is_builtin(plugin_name)
|
111
|
+
if metadata:
|
112
|
+
builtin_tag = " [BUILTIN]" if is_builtin else ""
|
113
|
+
print(f" - {metadata.name} v{metadata.version}{builtin_tag}")
|
114
|
+
print(f" {metadata.description}")
|
115
|
+
if metadata.author:
|
116
|
+
print(f" Author: {metadata.author}")
|
117
|
+
print()
|
@@ -35,6 +35,7 @@ def _prepare_context(args, agent_role, allowed_permissions):
|
|
35
35
|
context["role"] = agent_role or "developer"
|
36
36
|
context["profile"] = getattr(args, "profile", None)
|
37
37
|
context["allowed_permissions"] = allowed_permissions
|
38
|
+
context["emoji_enabled"] = getattr(args, "emoji", False)
|
38
39
|
if allowed_permissions and "x" in allowed_permissions:
|
39
40
|
pd = PlatformDiscovery()
|
40
41
|
context["platform"] = pd.get_platform_name()
|
@@ -122,6 +123,10 @@ def handle_show_system_prompt(args):
|
|
122
123
|
if profile is None and getattr(args, "market", False):
|
123
124
|
profile = "Market Analyst"
|
124
125
|
|
126
|
+
# Handle --developer flag mapping to Developer With Python Tools profile
|
127
|
+
if profile is None and getattr(args, "developer", False):
|
128
|
+
profile = "Developer With Python Tools"
|
129
|
+
|
125
130
|
if not profile:
|
126
131
|
print(
|
127
132
|
"[janito] No profile specified. The main agent runs without a system prompt template.\n"
|
@@ -152,9 +157,9 @@ def handle_show_system_prompt(args):
|
|
152
157
|
system_prompt = template.render(**context)
|
153
158
|
system_prompt = re.sub(r"\n{3,}", "\n\n", system_prompt)
|
154
159
|
|
155
|
-
|
156
|
-
|
157
|
-
)
|
160
|
+
# Use the actual profile name for display, not the resolved value
|
161
|
+
display_profile = profile or "main"
|
162
|
+
print(f"\n--- System Prompt (resolved, profile: {display_profile}) ---\n")
|
158
163
|
print(system_prompt)
|
159
164
|
print("\n-------------------------------\n")
|
160
165
|
if agent_role:
|
janito/cli/core/runner.py
CHANGED
@@ -109,8 +109,8 @@ def prepare_llm_driver_config(args, modifiers):
|
|
109
109
|
llm_driver_config = LLMDriverConfig(**driver_config_data)
|
110
110
|
if getattr(llm_driver_config, "verbose_api", None):
|
111
111
|
pass
|
112
|
-
|
113
|
-
agent_role = modifiers.get("
|
112
|
+
|
113
|
+
agent_role = modifiers.get("profile") or "developer"
|
114
114
|
return provider, llm_driver_config, agent_role
|
115
115
|
|
116
116
|
|
janito/cli/main_cli.py
CHANGED
@@ -51,14 +51,6 @@ definition = [
|
|
51
51
|
"help": "Start with the Market Analyst profile (equivalent to --profile 'Market Analyst')",
|
52
52
|
},
|
53
53
|
),
|
54
|
-
(
|
55
|
-
["--role"],
|
56
|
-
{
|
57
|
-
"metavar": "ROLE",
|
58
|
-
"help": "Select the developer role name (overrides profile, e.g. 'python-expert').",
|
59
|
-
"default": None,
|
60
|
-
},
|
61
|
-
),
|
62
54
|
(
|
63
55
|
["-W", "--workdir"],
|
64
56
|
{
|
@@ -205,6 +197,13 @@ definition = [
|
|
205
197
|
"help": "Set the reasoning effort for models that support it (low, medium, high, none)",
|
206
198
|
},
|
207
199
|
),
|
200
|
+
(
|
201
|
+
["--emoji"],
|
202
|
+
{
|
203
|
+
"action": "store_true",
|
204
|
+
"help": "Enable emoji usage in responses to make output more engaging and expressive",
|
205
|
+
},
|
206
|
+
),
|
208
207
|
(["user_prompt"], {"nargs": argparse.REMAINDER, "help": "Prompt to submit"}),
|
209
208
|
(
|
210
209
|
["-e", "--event-log"],
|
@@ -244,7 +243,6 @@ definition = [
|
|
244
243
|
MODIFIER_KEYS = [
|
245
244
|
"provider",
|
246
245
|
"model",
|
247
|
-
"role",
|
248
246
|
"profile",
|
249
247
|
"developer",
|
250
248
|
"market",
|
@@ -257,6 +255,7 @@ MODIFIER_KEYS = [
|
|
257
255
|
"exec",
|
258
256
|
"read",
|
259
257
|
"write",
|
258
|
+
"emoji",
|
260
259
|
]
|
261
260
|
SETTER_KEYS = ["set", "set_provider", "set_api_key", "unset"]
|
262
261
|
GETTER_KEYS = [
|
@@ -372,9 +371,7 @@ class JanitoCLI:
|
|
372
371
|
for k in MODIFIER_KEYS
|
373
372
|
if getattr(self.args, k, None) is not None
|
374
373
|
}
|
375
|
-
|
376
|
-
if getattr(self.args, "role", None):
|
377
|
-
modifiers["role"] = getattr(self.args, "role")
|
374
|
+
|
378
375
|
return modifiers
|
379
376
|
|
380
377
|
def classify(self):
|
@@ -420,9 +417,6 @@ class JanitoCLI:
|
|
420
417
|
self.args.exec = True
|
421
418
|
# Remove the /rwx prefix from the prompt
|
422
419
|
self.args.user_prompt = self.args.user_prompt[1:]
|
423
|
-
elif self.args.user_prompt and self.args.user_prompt[0].startswith("/"):
|
424
|
-
# Skip LLM processing for other commands that start with /
|
425
|
-
return
|
426
420
|
|
427
421
|
# If running in single shot mode and --profile is not provided, default to 'developer' profile
|
428
422
|
# Skip profile selection for list commands that don't need it
|
janito/cli/prompt_core.py
CHANGED
@@ -216,6 +216,8 @@ class PromptHandler:
|
|
216
216
|
if on_event and final_event is not None:
|
217
217
|
on_event(final_event)
|
218
218
|
global_event_bus.publish(final_event)
|
219
|
+
# Terminal bell moved to token summary printing in session.py and handler.py
|
220
|
+
pass # print('\a', end='', flush=True)
|
219
221
|
except KeyboardInterrupt:
|
220
222
|
# Capture user interrupt / cancellation
|
221
223
|
self.console.print("[red]Interrupted by the user.[/red]")
|
@@ -125,6 +125,8 @@ class PromptHandler:
|
|
125
125
|
print_token_message_summary(
|
126
126
|
shared_console, msg_count=1, usage=usage, elapsed=elapsed
|
127
127
|
)
|
128
|
+
# Send terminal bell character to trigger TUI bell after printing token summary
|
129
|
+
print("\a", end="", flush=True)
|
128
130
|
self._cleanup_driver_and_console()
|
129
131
|
|
130
132
|
def _cleanup_driver_and_console(self):
|
janito/llm/agent.py
CHANGED
@@ -244,7 +244,12 @@ class LLMAgent:
|
|
244
244
|
f"[agent] [DEBUG] Tool call detected: {getattr(part, 'name', repr(part))} with arguments: {getattr(part, 'arguments', None)}"
|
245
245
|
)
|
246
246
|
tool_calls.append(part)
|
247
|
-
|
247
|
+
try:
|
248
|
+
result = self.tools_adapter.execute_function_call_message_part(part)
|
249
|
+
except Exception as e:
|
250
|
+
# Catch any exception during tool execution and return as string
|
251
|
+
# instead of letting it propagate to the user
|
252
|
+
result = str(e)
|
248
253
|
tool_results.append(result)
|
249
254
|
if tool_calls:
|
250
255
|
# Prepare tool_calls message for assistant
|
janito/provider_registry.py
CHANGED
@@ -41,7 +41,7 @@ class ProviderRegistry:
|
|
41
41
|
rows.append(info[:3])
|
42
42
|
|
43
43
|
# Group providers by openness (open-source first, then proprietary)
|
44
|
-
open_providers = {"cerebras", "deepseek", "alibaba", "
|
44
|
+
open_providers = {"cerebras", "deepseek", "alibaba", "moonshotai", "zai"}
|
45
45
|
|
46
46
|
def sort_key(row):
|
47
47
|
provider_name = row[0]
|
@@ -18,7 +18,7 @@ class OpenAIProvider(LLMProvider):
|
|
18
18
|
MAINTAINER = "João Pinto <janito@ikignosis.org>"
|
19
19
|
MODEL_SPECS = MODEL_SPECS
|
20
20
|
DEFAULT_MODEL = (
|
21
|
-
"gpt-
|
21
|
+
"gpt-4.1" # Options: gpt-4.1, gpt-4o, o3-mini, o4-mini, gpt-5, gpt-5-nano
|
22
22
|
)
|
23
23
|
|
24
24
|
def __init__(
|
@@ -5,6 +5,7 @@ from janito.tools.loop_protection_decorator import protect_against_loops
|
|
5
5
|
from rich import print as rich_print
|
6
6
|
from janito.i18n import tr
|
7
7
|
from rich.panel import Panel
|
8
|
+
from rich.markdown import Markdown
|
8
9
|
from prompt_toolkit import PromptSession
|
9
10
|
from prompt_toolkit.key_binding import KeyBindings
|
10
11
|
from prompt_toolkit.enums import EditingMode
|
@@ -36,7 +37,7 @@ class AskUserTool(ToolBase):
|
|
36
37
|
def run(self, question: str) -> str:
|
37
38
|
|
38
39
|
print() # Print an empty line before the question panel
|
39
|
-
rich_print(Panel.fit(question, title=tr("Question"), style="cyan"))
|
40
|
+
rich_print(Panel.fit(Markdown(question), title=tr("Question"), style="cyan"))
|
40
41
|
|
41
42
|
bindings = KeyBindings()
|
42
43
|
mode = {"multiline": False}
|
@@ -107,4 +108,5 @@ class AskUserTool(ToolBase):
|
|
107
108
|
rich_print(
|
108
109
|
"[yellow]Warning: Some characters in your input were not valid UTF-8 and have been replaced.[/yellow]"
|
109
110
|
)
|
111
|
+
print("\a", end="", flush=True) # Print bell character
|
110
112
|
return sanitized
|
@@ -196,25 +196,15 @@ class FetchUrlTool(ToolBase):
|
|
196
196
|
whitelist_manager = get_url_whitelist_manager()
|
197
197
|
|
198
198
|
if not whitelist_manager.is_url_allowed(url):
|
199
|
-
error_message = tr(
|
200
|
-
"Warning: URL blocked by whitelist: {url}",
|
201
|
-
url=url,
|
202
|
-
)
|
199
|
+
error_message = tr("Blocked")
|
203
200
|
self.report_error(
|
204
|
-
tr(
|
205
|
-
"❗ URL blocked by whitelist: {url}",
|
206
|
-
url=url,
|
207
|
-
),
|
201
|
+
tr("❗ Blocked"),
|
208
202
|
ReportAction.READ,
|
209
203
|
)
|
210
204
|
return error_message
|
211
205
|
|
212
206
|
# Check session cache first
|
213
207
|
if url in self.session_cache:
|
214
|
-
self.report_warning(
|
215
|
-
tr("ℹ️ Using session cache"),
|
216
|
-
ReportAction.READ,
|
217
|
-
)
|
218
208
|
return self.session_cache[url]
|
219
209
|
|
220
210
|
# Check persistent cache for known errors
|
@@ -258,9 +248,8 @@ class FetchUrlTool(ToolBase):
|
|
258
248
|
status_code = http_err.response.status_code if http_err.response else None
|
259
249
|
if status_code and 400 <= status_code < 500:
|
260
250
|
error_message = tr(
|
261
|
-
"
|
251
|
+
"HTTP {status_code}",
|
262
252
|
status_code=status_code,
|
263
|
-
url=url,
|
264
253
|
)
|
265
254
|
# Cache 403 and 404 errors
|
266
255
|
if status_code in [403, 404]:
|
@@ -268,9 +257,8 @@ class FetchUrlTool(ToolBase):
|
|
268
257
|
|
269
258
|
self.report_error(
|
270
259
|
tr(
|
271
|
-
"❗ HTTP {status_code}
|
260
|
+
"❗ HTTP {status_code}",
|
272
261
|
status_code=status_code,
|
273
|
-
url=url,
|
274
262
|
),
|
275
263
|
ReportAction.READ,
|
276
264
|
)
|
@@ -278,25 +266,21 @@ class FetchUrlTool(ToolBase):
|
|
278
266
|
else:
|
279
267
|
self.report_error(
|
280
268
|
tr(
|
281
|
-
"❗ HTTP
|
282
|
-
|
283
|
-
err=str(http_err),
|
269
|
+
"❗ HTTP {status_code}",
|
270
|
+
status_code=status_code or "Error",
|
284
271
|
),
|
285
272
|
ReportAction.READ,
|
286
273
|
)
|
287
274
|
return tr(
|
288
|
-
"
|
289
|
-
|
290
|
-
err=str(http_err),
|
275
|
+
"HTTP {status_code}",
|
276
|
+
status_code=status_code or "Error",
|
291
277
|
)
|
292
278
|
except Exception as err:
|
293
279
|
self.report_error(
|
294
|
-
tr("❗ Error
|
280
|
+
tr("❗ Error"),
|
295
281
|
ReportAction.READ,
|
296
282
|
)
|
297
|
-
return tr(
|
298
|
-
"Warning: Error fetching URL: {url}: {err}", url=url, err=str(err)
|
299
|
-
)
|
283
|
+
return tr("Error")
|
300
284
|
|
301
285
|
def _extract_and_clean_text(self, html_content: str) -> str:
|
302
286
|
"""Extract and clean text from HTML content."""
|
@@ -370,7 +354,11 @@ class FetchUrlTool(ToolBase):
|
|
370
354
|
cookies=cookies,
|
371
355
|
follow_redirects=follow_redirects,
|
372
356
|
)
|
373
|
-
if
|
357
|
+
if (
|
358
|
+
html_content.startswith("HTTP ")
|
359
|
+
or html_content == "Error"
|
360
|
+
or html_content == "Blocked"
|
361
|
+
):
|
374
362
|
return html_content
|
375
363
|
|
376
364
|
try:
|
@@ -399,7 +387,11 @@ class FetchUrlTool(ToolBase):
|
|
399
387
|
cookies=cookies,
|
400
388
|
follow_redirects=follow_redirects,
|
401
389
|
)
|
402
|
-
if
|
390
|
+
if (
|
391
|
+
html_content.startswith("HTTP ")
|
392
|
+
or html_content == "Error"
|
393
|
+
or html_content == "Blocked"
|
394
|
+
):
|
403
395
|
return html_content
|
404
396
|
|
405
397
|
# Extract and clean text
|
@@ -159,16 +159,22 @@ class ReplaceTextInFileTool(ToolBase):
|
|
159
159
|
)
|
160
160
|
return warning, concise_warning
|
161
161
|
|
162
|
-
def _report_success(self, match_lines):
|
162
|
+
def _report_success(self, match_lines, line_delta_str=""):
|
163
163
|
"""Report success with line numbers where replacements occurred."""
|
164
164
|
if match_lines:
|
165
165
|
lines_str = ", ".join(str(line_no) for line_no in match_lines)
|
166
166
|
self.report_success(
|
167
|
-
tr(
|
167
|
+
tr(
|
168
|
+
" ✅ replaced at {lines_str}{delta}",
|
169
|
+
lines_str=lines_str,
|
170
|
+
delta=line_delta_str,
|
171
|
+
),
|
168
172
|
ReportAction.CREATE,
|
169
173
|
)
|
170
174
|
else:
|
171
|
-
self.report_success(
|
175
|
+
self.report_success(
|
176
|
+
tr(" ✅ replaced{delta}", delta=line_delta_str), ReportAction.CREATE
|
177
|
+
)
|
172
178
|
|
173
179
|
def _get_line_delta_str(self, content, new_content):
|
174
180
|
"""Return a string describing the net line change after replacement."""
|
@@ -98,7 +98,7 @@ class SearchTextTool(ToolBase):
|
|
98
98
|
if max_depth > 0:
|
99
99
|
info_str += tr(" [max_depth={max_depth}]", max_depth=max_depth)
|
100
100
|
if count_only:
|
101
|
-
info_str += " [count]"
|
101
|
+
info_str += " [count-only]"
|
102
102
|
self.report_action(info_str, ReportAction.READ)
|
103
103
|
if os.path.isfile(search_path):
|
104
104
|
dir_output, dir_limit_reached, per_file_counts = self._handle_file(
|
@@ -144,7 +144,7 @@ class SearchTextTool(ToolBase):
|
|
144
144
|
file_word_max = file_word + (" (max)" if dir_limit_reached else "")
|
145
145
|
self.report_success(
|
146
146
|
tr(
|
147
|
-
" ✅ {count} {file_word}
|
147
|
+
" ✅ {count} {file_word}/{num_files} {file_label}",
|
148
148
|
count=count,
|
149
149
|
file_word=file_word_max,
|
150
150
|
num_files=num_files,
|
@@ -119,22 +119,18 @@ def protect_against_loops(
|
|
119
119
|
current_time - timestamp <= time_window
|
120
120
|
for timestamp in _decorator_call_tracker[op_name]
|
121
121
|
):
|
122
|
-
#
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
raise RuntimeError(error_msg)
|
136
|
-
|
137
|
-
_report_error_and_raise(args, op_name)
|
122
|
+
# Return loop protection message as string instead of raising exception
|
123
|
+
error_msg = f"Loop protection: Too many {op_name} operations in a short time period ({max_calls} calls in {time_window}s). Please try a different approach or wait before retrying."
|
124
|
+
|
125
|
+
# Try to report the error through the tool's reporting mechanism
|
126
|
+
tool_instance = args[0] if args else None
|
127
|
+
if hasattr(tool_instance, "report_error"):
|
128
|
+
try:
|
129
|
+
tool_instance.report_error(error_msg)
|
130
|
+
except Exception:
|
131
|
+
pass # If reporting fails, we still return the message
|
132
|
+
|
133
|
+
return error_msg
|
138
134
|
|
139
135
|
# Record this call
|
140
136
|
if op_name not in _decorator_call_tracker:
|
janito/tools/tools_adapter.py
CHANGED
@@ -460,9 +460,9 @@ class ToolsAdapterBase:
|
|
460
460
|
raise ToolCallException(tool_name, error_msg, arguments=arguments)
|
461
461
|
|
462
462
|
def _handle_execution_error(self, tool_name, request_id, exception, arguments):
|
463
|
-
# Check if this is a loop protection error that should
|
463
|
+
# Check if this is a loop protection error that should trigger a new strategy
|
464
464
|
if isinstance(exception, RuntimeError) and "Loop protection:" in str(exception):
|
465
|
-
error_msg = str(exception)
|
465
|
+
error_msg = str(exception)
|
466
466
|
if self._event_bus:
|
467
467
|
self._event_bus.publish(
|
468
468
|
ToolCallError(
|
@@ -473,8 +473,22 @@ class ToolsAdapterBase:
|
|
473
473
|
arguments=arguments,
|
474
474
|
)
|
475
475
|
)
|
476
|
-
# Return the
|
477
|
-
return error_msg
|
476
|
+
# Return the loop protection message as string to trigger new strategy
|
477
|
+
return f"Loop protection triggered - requesting new strategy: {error_msg}"
|
478
|
+
|
479
|
+
# Check if this is a string return from loop protection (new behavior)
|
480
|
+
if isinstance(exception, str) and "Loop protection:" in exception:
|
481
|
+
error_msg = str(exception)
|
482
|
+
if self._event_bus:
|
483
|
+
self._event_bus.publish(
|
484
|
+
ToolCallError(
|
485
|
+
tool_name=tool_name,
|
486
|
+
request_id=request_id,
|
487
|
+
error=error_msg,
|
488
|
+
arguments=arguments,
|
489
|
+
)
|
490
|
+
)
|
491
|
+
return f"Loop protection triggered - requesting new strategy: {error_msg}"
|
478
492
|
|
479
493
|
error_msg = f"Exception during execution of tool '{tool_name}': {exception}"
|
480
494
|
if self._event_bus:
|
@@ -1,4 +1,4 @@
|
|
1
|
-
janito/README.md,sha256=
|
1
|
+
janito/README.md,sha256=Kd4GcEYIt04520J2AIMCZbp1enAGRzlLswCfyi1g5AY,4737
|
2
2
|
janito/__init__.py,sha256=a0pFui3A_AfWJiUfg93yE-Vf4868bqG3y9yg2fkTIuY,244
|
3
3
|
janito/__main__.py,sha256=lPQ8kAyYfyeS1KopmJ8EVY5g1YswlIqCS615mM_B_rM,70
|
4
4
|
janito/_version.py,sha256=PtAVr2K9fOS5sv6aXzmcb7UaR5NLGMFOofL7Ndjh75o,2344
|
@@ -16,21 +16,21 @@ janito/perf_singleton.py,sha256=g1h0Sdf4ydzegeEpJlMhQt4H0GQZ2hryXrdYOTL-b30,113
|
|
16
16
|
janito/performance_collector.py,sha256=RYu4av16Trj3RljJZ8-2Gbn1KlGdJUosrcVFYtwviNI,6285
|
17
17
|
janito/platform_discovery.py,sha256=JN3kC7hkxdvuj-AyrJTlbbDJjtNHke3fdlZDqGi_uz0,4621
|
18
18
|
janito/provider_config.py,sha256=acn2FEgWsEIyi2AxZiuCLoP2rXDd-nXcP5VB4CZHaeE,3189
|
19
|
-
janito/provider_registry.py,sha256=
|
19
|
+
janito/provider_registry.py,sha256=VMxwIatPxrkCo-racXm9UxMd37iCyEdi_viotqaspXo,6927
|
20
20
|
janito/report_events.py,sha256=q4OR_jTZNfcqaQF_fzTjgqo6_VlUIxSGWfhpT4nJWcw,938
|
21
21
|
janito/shell.bak.zip,sha256=hznHbmgfkAkjuQDJ3w73XPQh05yrtUZQxLmtGbanbYU,22
|
22
22
|
janito/utils.py,sha256=eXSsMgM69YyzahgCNrJQLcEbB8ssLI1MQqaa20ONxbE,376
|
23
|
-
janito/agent/setup_agent.py,sha256=
|
24
|
-
janito/agent/templates/profiles/system_prompt_template_Developer_with_Python_Tools.txt.j2,sha256=
|
25
|
-
janito/agent/templates/profiles/system_prompt_template_developer.txt.j2,sha256=
|
26
|
-
janito/agent/templates/profiles/system_prompt_template_market_analyst.txt.j2,sha256=
|
27
|
-
janito/agent/templates/profiles/system_prompt_template_model_conversation_without_tools_or_context.txt.j2,sha256=
|
23
|
+
janito/agent/setup_agent.py,sha256=Qo9fm9O5IUd65ao4KODOMd6f7baw_mEiluj1pBNxrIg,12228
|
24
|
+
janito/agent/templates/profiles/system_prompt_template_Developer_with_Python_Tools.txt.j2,sha256=Q6p57GakGJv4damUITO0iq8rwfhPxVlek6y3I7nnkK8,3931
|
25
|
+
janito/agent/templates/profiles/system_prompt_template_developer.txt.j2,sha256=lG6ihPRoGqJ0ho2jb1sgBPs9itwpSYpy9CrLjXwZxB0,3614
|
26
|
+
janito/agent/templates/profiles/system_prompt_template_market_analyst.txt.j2,sha256=TCoZITPBOMvN3zA6QXg6UCNrHWj_iDKRxAREwYev5Ks,4123
|
27
|
+
janito/agent/templates/profiles/system_prompt_template_model_conversation_without_tools_or_context.txt.j2,sha256=ynhuZESiVvmwHW0pKNUCu9wXexYkFxs6vf4AcC_r24g,1589
|
28
28
|
janito/cli/__init__.py,sha256=xaPDOrWphBbCR63Xpcx_yfpXSJIlCaaICc4j2qpWqrM,194
|
29
29
|
janito/cli/config.py,sha256=HkZ14701HzIqrvaNyDcDhGlVHfpX_uHlLp2rHmhRm_k,872
|
30
30
|
janito/cli/console.py,sha256=gJolqzWL7jEPLxeuH-CwBDRFpXt976KdZOEAB2tdBDs,64
|
31
31
|
janito/cli/main.py,sha256=s5odou0txf8pzTf1ADk2yV7T5m8B6cejJ81e7iu776U,312
|
32
|
-
janito/cli/main_cli.py,sha256=
|
33
|
-
janito/cli/prompt_core.py,sha256=
|
32
|
+
janito/cli/main_cli.py,sha256=FXpoybaEbDRlK5-oMOMlcdM7TpVfnOdr6-mOkgJHeTo,16648
|
33
|
+
janito/cli/prompt_core.py,sha256=s7FInCOlz3CMAsARcNWMyWXGep--zeySx22m29b2l7Q,11799
|
34
34
|
janito/cli/prompt_handler.py,sha256=SnPTlL64noeAMGlI08VBDD5IDD8jlVMIYA4-fS8zVLg,215
|
35
35
|
janito/cli/prompt_setup.py,sha256=s48gvNfZhKjsEhf4EzL1tKIGm4wDidPMDvlM6TAPYes,2116
|
36
36
|
janito/cli/rich_terminal_reporter.py,sha256=K48Ywwj6xz_NikuezzBmYJM1PANmQD-G48sE4NjQhn0,6835
|
@@ -40,8 +40,7 @@ janito/cli/chat_mode/bindings.py,sha256=odjc5_-YW1t2FRhBUNRNoBMoQIg5sMz3ktV7xG0A
|
|
40
40
|
janito/cli/chat_mode/chat_entry.py,sha256=RFdPd23jsA2DMHRacpjAdwI_1dFBaWrtnwyQEgb2fHA,475
|
41
41
|
janito/cli/chat_mode/prompt_style.py,sha256=vsqQ9xxmrYjj1pWuVe9CayQf39fo2EIXrkKPkflSVn4,805
|
42
42
|
janito/cli/chat_mode/script_runner.py,sha256=WFTFVWzg_VQrD2Ujj02XWjscfGgHwmjBeRxaEjWw9ps,6505
|
43
|
-
janito/cli/chat_mode/session.py,sha256=
|
44
|
-
janito/cli/chat_mode/session_profile_select.py,sha256=bEM8Q41c6taxxMCalXlLMUO18y6WRB84zaVEb-j6Wj4,6441
|
43
|
+
janito/cli/chat_mode/session.py,sha256=RA4vQVVNEIWsl3gG3j08FioqUEpv7_j5hjd3fmQRvd4,18678
|
45
44
|
janito/cli/chat_mode/toolbar.py,sha256=SzdWAJdcY1g2rTPZCPL6G5X8jO6ZQYjwko2-nw54_nU,3397
|
46
45
|
janito/cli/chat_mode/shell/autocomplete.py,sha256=lE68MaVaodbA2VfUM0_YLqQVLBJAE_BJsd5cMtwuD-g,793
|
47
46
|
janito/cli/chat_mode/shell/commands.bak.zip,sha256=I7GFjXg2ORT5NzFpicH1vQ3kchhduQsZinzqo0xO8wU,74238
|
@@ -82,7 +81,7 @@ janito/cli/cli_commands/enable_disable_plugin.py,sha256=IIEg5Gz2aAW_7BKrMQTXSGF0
|
|
82
81
|
janito/cli/cli_commands/list_config.py,sha256=oiQEGaGPjwjG-PrOcakpNMbbqISTsBEs7rkGH3ceQsI,1179
|
83
82
|
janito/cli/cli_commands/list_drivers.py,sha256=r2ENykUcvf_9XYp6LHd3RvLXGXyVUA6oe_Pr0dyv92I,5124
|
84
83
|
janito/cli/cli_commands/list_models.py,sha256=QF3Wa7OhNcJFKeBxaw0C_rDfsvJFNb-siz5uorajBvo,1595
|
85
|
-
janito/cli/cli_commands/list_plugins.py,sha256=
|
84
|
+
janito/cli/cli_commands/list_plugins.py,sha256=lha2XX7AKIGtFattATpJgsEKRSxRULPHXI1vNSQiQcg,3846
|
86
85
|
janito/cli/cli_commands/list_profiles.py,sha256=O4k6U9iCEeNH3lM-NP_XX_E9W0h__hheLSn23241dkA,3538
|
87
86
|
janito/cli/cli_commands/list_providers.py,sha256=oilrBjNL5mot1nz45XQQY6oeiSxoNvphhQYspNcEJpw,391
|
88
87
|
janito/cli/cli_commands/list_providers_region.py,sha256=qrMj_gtgEMty8UH0P_O5SgWCVJ9ZKxGUp_GdsE4_EH4,2548
|
@@ -92,16 +91,16 @@ janito/cli/cli_commands/model_utils.py,sha256=4t2ZN8DYA8jxluXHiiliV8gMbF_90nKGtg
|
|
92
91
|
janito/cli/cli_commands/ping_providers.py,sha256=hetZAKKZzQYRpRDT5OvRTOe4jYUVNZGjo8gFoyeRA3I,1921
|
93
92
|
janito/cli/cli_commands/set_api_key.py,sha256=IR_hUcLjK-2oJmiIVdjc8epPsQAzqEN9MS7lSTVqmKM,1060
|
94
93
|
janito/cli/cli_commands/show_config.py,sha256=ammzVEqJQCAdWFRrhI1zjjmzgTCH2l38REoT4gYJPP0,3467
|
95
|
-
janito/cli/cli_commands/show_system_prompt.py,sha256=
|
94
|
+
janito/cli/cli_commands/show_system_prompt.py,sha256=WQclY_bmJrHbIBRU1qx1WV4VyooyXVx_XQyX_4Rb1hs,6335
|
96
95
|
janito/cli/core/__init__.py,sha256=YH95fhgY9yBX8RgqX9dSrEkl4exjV0T4rbmJ6xUpG-Y,196
|
97
96
|
janito/cli/core/event_logger.py,sha256=1X6lR0Ax7AgF8HlPWFoY5Ystuu7Bh4ooTo78vXzeGB0,2008
|
98
97
|
janito/cli/core/getters.py,sha256=opmcSz86J-I95Klsh0c4y6lsYvNakrvRqvuA0o5ARWI,2869
|
99
98
|
janito/cli/core/model_guesser.py,sha256=V7LBkIllSp_tP9-2B1gcl5b4b-La7mrOvE3AZQQm8lk,1716
|
100
|
-
janito/cli/core/runner.py,sha256=
|
99
|
+
janito/cli/core/runner.py,sha256=gi8xke6re9AoHHNCivV50i0eUAliw8QTUdXyqMkMplM,9044
|
101
100
|
janito/cli/core/setters.py,sha256=zjSUxy6iUzcrmEunFk7dA90KqbMaq2O7LTGP8wn2HxA,5378
|
102
101
|
janito/cli/core/unsetters.py,sha256=FEw9gCt0vRvoCt0kRSNfVB2tzi_TqppJIx2nHPP59-k,2012
|
103
102
|
janito/cli/single_shot_mode/__init__.py,sha256=Ct99pKe9tINzVW6oedZJfzfZQKWpXz-weSSCn0hrwHY,115
|
104
|
-
janito/cli/single_shot_mode/handler.py,sha256=
|
103
|
+
janito/cli/single_shot_mode/handler.py,sha256=aOTh9s7HyHBLs2sgquEVbVhFWC_7Udu4u_9LoQ0f78E,5667
|
105
104
|
janito/docs/GETTING_STARTED.md,sha256=Yx3vi1LQWyDWlE_JYuz4V9EL-Gh4WU6cOBqCr8XidF4,4960
|
106
105
|
janito/drivers/dashscope.bak.zip,sha256=9Pv4Xyciju8jO1lEMFVgYXexoZkxmDO3Ig6vw3ODfL8,4936
|
107
106
|
janito/drivers/openai_responses.bak.zip,sha256=E43eDCHGa2tCtdjzj_pMnWDdnsOZzj8BJTR5tJp8wcM,13352
|
@@ -122,7 +121,7 @@ janito/i18n/messages.py,sha256=fBuwOTFoygyHPkYphm6Y0r1iE8497Z4iryVAmPhMEkg,1851
|
|
122
121
|
janito/i18n/pt.py,sha256=NlTgpDSftUfFG7FGbs7TK54vQlJVMyaZDHGcWjelwMc,4168
|
123
122
|
janito/llm/README.md,sha256=6GRqCu_a9va5HCB1YqNqbshyWKFyAGlnXugrjom-xj8,1213
|
124
123
|
janito/llm/__init__.py,sha256=dpyVH51qVRCw-PDyAFLAxq0zd4jl5MDcuV6Cri0D-dQ,134
|
125
|
-
janito/llm/agent.py,sha256=
|
124
|
+
janito/llm/agent.py,sha256=T0JfeMoOudTWsHwWCcaocrHyq9k0TvkL4_YePlXvZfo,21269
|
126
125
|
janito/llm/auth.py,sha256=8Dl_orUEPhn2X6XjkO2Nr-j1HFT2YDxk1qJl9hSFI88,2286
|
127
126
|
janito/llm/auth_utils.py,sha256=7GH7bIScKhVWJW6ugcDrJLcYRamj5dl_l8N1rrvR4Ws,663
|
128
127
|
janito/llm/driver.py,sha256=stiicPe_MXTuWW4q6MSwK7PCj8UZcA_30pGACu6xYUQ,10039
|
@@ -169,7 +168,7 @@ janito/providers/moonshot/model_info.py,sha256=PpdUkmuR7g6SyiEzS9nePskPjn5xI1ZM2
|
|
169
168
|
janito/providers/moonshot/provider.py,sha256=LJxNoC7Oo-ZoFKs2ulK2lXzUEx7kV-79HJ8JG4J-UWU,3856
|
170
169
|
janito/providers/openai/__init__.py,sha256=f0m16-sIqScjL9Mp4A0CQBZx6H3PTEy0cnE08jeaB5U,38
|
171
170
|
janito/providers/openai/model_info.py,sha256=VTkq3xcx2vk0tXlFVHQxKeFzl-DL1T1J2elVOEwCdHI,4265
|
172
|
-
janito/providers/openai/provider.py,sha256=
|
171
|
+
janito/providers/openai/provider.py,sha256=VBU5S5eSpqE3qX-nHdxLwsp7usgvUFzOsPzKJKu1-10,4763
|
173
172
|
janito/providers/openai/schema_generator.py,sha256=hTqeLcPTR8jeKn5DUUpo7b-EZ-V-g1WwXiX7MbHnFzE,2234
|
174
173
|
janito/providers/zai/__init__.py,sha256=qtIr9_QBFaXG8xB6cRDGhS7se6ir11CWseI9azLMRBo,24
|
175
174
|
janito/providers/zai/model_info.py,sha256=ldwD8enpxXv1G-YsDw4YJn31YsVueQ4vj5HgoYvnPxo,1183
|
@@ -186,7 +185,7 @@ janito/tools/base.py,sha256=R38A9xWYh3JRYZMDSom2d1taNDy9J7HpLbZo9X2wH_o,316
|
|
186
185
|
janito/tools/disabled_tools.py,sha256=Tx__16wtMWZ9z34cYLdH1gukwot5MCL-9kLjd5MPX6Y,2110
|
187
186
|
janito/tools/inspect_registry.py,sha256=Jo7PrMPRKLuR-j_mBAk9PBcTzeJf1eQrS1ChGofgQk0,538
|
188
187
|
janito/tools/loop_protection.py,sha256=WQ2Cqt459vXvrO0T1EqkEHynHlRkPzfaC83RSmXzjkM,4718
|
189
|
-
janito/tools/loop_protection_decorator.py,sha256=
|
188
|
+
janito/tools/loop_protection_decorator.py,sha256=R1j2ouscKbVcDm2wlxRZ6zQuKExgj633ijeDq4j0oO0,6457
|
190
189
|
janito/tools/outline_file.bak.zip,sha256=EeI2cBXCwTdWVgJDNiroxKeYlkjwo6NLKeXz3J-2iZI,15607
|
191
190
|
janito/tools/path_security.py,sha256=40b0hV0X3449Dht93A04Q3c9AYSsBQsBFy2BjzM83lA,8214
|
192
191
|
janito/tools/path_utils.py,sha256=Rg5GE4kiu7rky6I2KTtivW6wPXzc9Qmq0_lOjwkPYlI,832
|
@@ -197,18 +196,18 @@ janito/tools/tool_events.py,sha256=czRtC2TYakAySBZvfHS_Q6_NY_7_krxzAzAL1ggRFWA,1
|
|
197
196
|
janito/tools/tool_run_exception.py,sha256=43yWgTaGBGEtRteo6FvTrane6fEVGo9FU1uOdjMRWJE,525
|
198
197
|
janito/tools/tool_use_tracker.py,sha256=IaEmA22D6RuL1xMUCScOMGv0crLPwEJVGmj49cydIaM,2662
|
199
198
|
janito/tools/tool_utils.py,sha256=alPm9DvtXSw_zPRKvP5GjbebPRf_nfvmWk2TNlL5Cws,1219
|
200
|
-
janito/tools/tools_adapter.py,sha256=
|
199
|
+
janito/tools/tools_adapter.py,sha256=F1Wkji222dY53HMaZWf3vqVas1Bimm3UXERKvxF54Ew,21687
|
201
200
|
janito/tools/tools_schema.py,sha256=rGrKrmpPNR07VXHAJ_haGBRRO-YGLOF51BlYRep9AAQ,4415
|
202
201
|
janito/tools/url_whitelist.py,sha256=0CPLkHTp5HgnwgjxwgXnJmwPeZQ30q4j3YjW59hiUUE,4295
|
203
202
|
janito/tools/adapters/__init__.py,sha256=XKixOKtUJs1R-rGwGDXSLVLg5-Kp090gvWbsseWT4LI,92
|
204
203
|
janito/tools/adapters/local/__init__.py,sha256=8xJw8Qv3T_wwkiGBVVgs9p7pH1ONIAipccEUqY2II8A,2231
|
205
204
|
janito/tools/adapters/local/adapter.py,sha256=u4nLHTaYdwZXMi1J8lsKvlG6rOmdq9xjey_3zeyCG4k,8707
|
206
|
-
janito/tools/adapters/local/ask_user.py,sha256
|
205
|
+
janito/tools/adapters/local/ask_user.py,sha256=-shjMRKrRe7HNHM2w_6YAl7eEgl8QXaIV6LKrUDEBxU,4060
|
207
206
|
janito/tools/adapters/local/copy_file.py,sha256=SBJm19Ipe5dqRE1Mxl6JSrn4bNmfObVnDr5b1mcEu6c,3682
|
208
207
|
janito/tools/adapters/local/create_directory.py,sha256=LxwqQEsnOrEphCIoaMRRx9P9bu0MzidP3Fc5q6letxc,2584
|
209
208
|
janito/tools/adapters/local/create_file.py,sha256=nZf8iPScO9_nrvmHwXqOcqpLZkLABTh9uLVNddC4PCk,3760
|
210
209
|
janito/tools/adapters/local/delete_text_in_file.py,sha256=uEeedRxXAR7_CqUc_qhbEdM0OzRi_pgnP-iDjs2Zvjk,5087
|
211
|
-
janito/tools/adapters/local/fetch_url.py,sha256=
|
210
|
+
janito/tools/adapters/local/fetch_url.py,sha256=bkrFFE41h8pWepqnFpmsJ1gF0oDDd5htBsEwFniSKMQ,16064
|
212
211
|
janito/tools/adapters/local/find_files.py,sha256=Zbag3aP34vc7ffJh8bOqAwXj3KiZhV--uzTVHtNb-fI,6250
|
213
212
|
janito/tools/adapters/local/move_file.py,sha256=LMGm8bn3NNyIPJG4vrlO09smXQcgzA09EwoooZxkIA8,4695
|
214
213
|
janito/tools/adapters/local/open_html_in_browser.py,sha256=XqICIwVx5vEE77gHkaNAC-bAeEEy0DBmDksATiL-sRY,2101
|
@@ -220,7 +219,7 @@ janito/tools/adapters/local/read_chart.py,sha256=qQebp_MEE_x2AL_pl85uA58A4lbhLQs
|
|
220
219
|
janito/tools/adapters/local/read_files.py,sha256=LzlNrQEadYF8dF97Wm8AHde2nuqbMkN5vVskQLhvFdA,2329
|
221
220
|
janito/tools/adapters/local/remove_directory.py,sha256=DEhHdmwJRl5Yp9eEukIIooMrpATCtXcv5HmaDRn8vH8,1913
|
222
221
|
janito/tools/adapters/local/remove_file.py,sha256=Imra4jGkBfAd6pnUAmbUsjN0exj2vzZWuNRXq_GOMsI,2093
|
223
|
-
janito/tools/adapters/local/replace_text_in_file.py,sha256=
|
222
|
+
janito/tools/adapters/local/replace_text_in_file.py,sha256=zJIDecviF2YRpWxbvhtka4Iaje-QYhcaqQX1PxWolzE,10966
|
224
223
|
janito/tools/adapters/local/run_bash_command.py,sha256=7fABqAeAu7WJwzzwHmT54_m5OSwPMcgpQ74lQhPG7TA,7955
|
225
224
|
janito/tools/adapters/local/run_powershell_command.py,sha256=uQSJVQe40wSGbesyvZxDmIKJthAbDJFaxXm1dEN3gBs,9313
|
226
225
|
janito/tools/adapters/local/view_file.py,sha256=cBKcbwbfH-UMyvQ7PmYTgsshcFmorjWtyH1kaYi7oNY,7379
|
@@ -231,7 +230,7 @@ janito/tools/adapters/local/get_file_outline/markdown_outline.py,sha256=bXEBg0D9
|
|
231
230
|
janito/tools/adapters/local/get_file_outline/python_outline.py,sha256=RAcf9Vxec08lA06drYaNre5HCJ2lTzrRAskZ3rlyE-U,10326
|
232
231
|
janito/tools/adapters/local/get_file_outline/search_outline.py,sha256=bski24TpnJVf3L0TNzkx3HfvaXwttQl4EVkwk2POQOw,1348
|
233
232
|
janito/tools/adapters/local/search_text/__init__.py,sha256=FEYpF5tTtf0fiAyRGIGSn-kV-MJDkhdFIbus16mYW8Y,34
|
234
|
-
janito/tools/adapters/local/search_text/core.py,sha256=
|
233
|
+
janito/tools/adapters/local/search_text/core.py,sha256=obCq4WoJ4ld2kpd57pCyt91wpXVsrvQ0PBTSL8Lxpk0,7882
|
235
234
|
janito/tools/adapters/local/search_text/match_lines.py,sha256=RLR8fZFP-Q57rY0fTENbMItmt3dJZiYX0otmGHVRjfw,2131
|
236
235
|
janito/tools/adapters/local/search_text/pattern_utils.py,sha256=D7vtAr8oT0tGV0C_UUarAXS9XQtP-MTYmmc8Yg8iVTg,2362
|
237
236
|
janito/tools/adapters/local/search_text/traverse_directory.py,sha256=EpL1qywAV0H29pm8-QsHrjKchKP4i4sRUOENVuNptCo,4000
|
@@ -247,9 +246,9 @@ janito/tools/adapters/local/validate_file_syntax/ps1_validator.py,sha256=TeIkPt0
|
|
247
246
|
janito/tools/adapters/local/validate_file_syntax/python_validator.py,sha256=BfCO_K18qy92m-2ZVvHsbEU5e11OPo1pO9Vz4G4616E,130
|
248
247
|
janito/tools/adapters/local/validate_file_syntax/xml_validator.py,sha256=AijlsP_PgNuC8ZbGsC5vOTt3Jur76otQzkd_7qR0QFY,284
|
249
248
|
janito/tools/adapters/local/validate_file_syntax/yaml_validator.py,sha256=TgyI0HRL6ug_gBcWEm5TGJJuA4E34ZXcIzMpAbv3oJs,155
|
250
|
-
janito-2.
|
251
|
-
janito-2.
|
252
|
-
janito-2.
|
253
|
-
janito-2.
|
254
|
-
janito-2.
|
255
|
-
janito-2.
|
249
|
+
janito-2.31.0.dist-info/licenses/LICENSE,sha256=GSAKapQH5ZIGWlpQTA7v5YrfECyaxaohUb1vJX-qepw,1090
|
250
|
+
janito-2.31.0.dist-info/METADATA,sha256=PTDIEejdQE8T6pYMV-_SZubwGBauxg0aER9EbzwAt_s,16945
|
251
|
+
janito-2.31.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
252
|
+
janito-2.31.0.dist-info/entry_points.txt,sha256=wIo5zZxbmu4fC-ZMrsKD0T0vq7IqkOOLYhrqRGypkx4,48
|
253
|
+
janito-2.31.0.dist-info/top_level.txt,sha256=m0NaVCq0-ivxbazE2-ND0EA9Hmuijj_OGkmCbnBcCig,7
|
254
|
+
janito-2.31.0.dist-info/RECORD,,
|
@@ -1,182 +0,0 @@
|
|
1
|
-
import os
|
2
|
-
from pathlib import Path
|
3
|
-
import questionary
|
4
|
-
from questionary import Style
|
5
|
-
import os
|
6
|
-
from pathlib import Path
|
7
|
-
from prompt_toolkit.formatted_text import HTML
|
8
|
-
from prompt_toolkit import PromptSession
|
9
|
-
from prompt_toolkit.key_binding import KeyBindings
|
10
|
-
from prompt_toolkit.enums import EditingMode
|
11
|
-
from prompt_toolkit.formatted_text import HTML
|
12
|
-
from .prompt_style import chat_shell_style
|
13
|
-
|
14
|
-
|
15
|
-
"""
|
16
|
-
Profile selection logic for Janito Chat CLI using questionary.
|
17
|
-
"""
|
18
|
-
|
19
|
-
|
20
|
-
def _handle_raw_model_session_no_tools():
|
21
|
-
return {
|
22
|
-
"profile": "model_conversation_without_tools_or_context",
|
23
|
-
"profile_system_prompt": None,
|
24
|
-
"no_tools_mode": True,
|
25
|
-
} # Raw Model Session (no tools, no context)
|
26
|
-
|
27
|
-
|
28
|
-
def _handle_using_role():
|
29
|
-
role_name = questionary.text("Enter the role name:").ask()
|
30
|
-
return f"role:{role_name}"
|
31
|
-
|
32
|
-
|
33
|
-
def _get_toolbar(mode):
|
34
|
-
if mode["multiline"]:
|
35
|
-
return HTML(
|
36
|
-
"<b>Multiline mode (Esc+Enter to submit). Type /single to switch.</b>"
|
37
|
-
)
|
38
|
-
else:
|
39
|
-
return HTML(
|
40
|
-
"<b>Single-line mode (Enter to submit). Type /multi for multiline.</b>"
|
41
|
-
)
|
42
|
-
|
43
|
-
|
44
|
-
def _handle_custom_system_prompt():
|
45
|
-
print(
|
46
|
-
"\n[Custom System Prompt]\nPlease enter the message that will be used as the model system prompt. This will define how the AI behaves for this session.\nYou can use /multi for multiline mode, and /single to return to single-line mode.\n"
|
47
|
-
)
|
48
|
-
mode = {"multiline": False}
|
49
|
-
bindings = KeyBindings()
|
50
|
-
|
51
|
-
@bindings.add("c-r")
|
52
|
-
def _(event):
|
53
|
-
pass
|
54
|
-
|
55
|
-
@bindings.add("f12")
|
56
|
-
def _(event):
|
57
|
-
buf = event.app.current_buffer
|
58
|
-
buf.text = "Do It"
|
59
|
-
buf.validate_and_handle()
|
60
|
-
|
61
|
-
session = PromptSession(
|
62
|
-
multiline=False,
|
63
|
-
key_bindings=bindings,
|
64
|
-
editing_mode=EditingMode.EMACS,
|
65
|
-
bottom_toolbar=lambda: _get_toolbar(mode),
|
66
|
-
style=chat_shell_style,
|
67
|
-
)
|
68
|
-
prompt_icon = HTML("<inputline>📝 </inputline>")
|
69
|
-
while True:
|
70
|
-
try:
|
71
|
-
response = session.prompt(prompt_icon)
|
72
|
-
except KeyboardInterrupt:
|
73
|
-
print("[Custom System Prompt] Exited by the user.")
|
74
|
-
import sys
|
75
|
-
|
76
|
-
sys.exit(0)
|
77
|
-
if not mode["multiline"] and response.strip() == "/multi":
|
78
|
-
mode["multiline"] = True
|
79
|
-
session.multiline = True
|
80
|
-
continue
|
81
|
-
elif mode["multiline"] and response.strip() == "/single":
|
82
|
-
mode["multiline"] = False
|
83
|
-
session.multiline = False
|
84
|
-
continue
|
85
|
-
else:
|
86
|
-
sanitized = response.strip()
|
87
|
-
try:
|
88
|
-
sanitized.encode("utf-8")
|
89
|
-
except UnicodeEncodeError:
|
90
|
-
sanitized = sanitized.encode("utf-8", errors="replace").decode("utf-8")
|
91
|
-
return {"profile": None, "profile_system_prompt": sanitized}
|
92
|
-
|
93
|
-
|
94
|
-
def _load_user_profiles():
|
95
|
-
user_profiles_dir = Path.home() / ".janito" / "profiles"
|
96
|
-
profiles = {}
|
97
|
-
if user_profiles_dir.exists() and user_profiles_dir.is_dir():
|
98
|
-
for profile_file in user_profiles_dir.glob("*"):
|
99
|
-
if profile_file.is_file():
|
100
|
-
try:
|
101
|
-
with open(profile_file, "r", encoding="utf-8") as f:
|
102
|
-
profiles[profile_file.stem] = f.read().strip()
|
103
|
-
except Exception:
|
104
|
-
# Ignore unreadable files
|
105
|
-
pass
|
106
|
-
return profiles
|
107
|
-
|
108
|
-
|
109
|
-
def select_profile():
|
110
|
-
user_profiles = _load_user_profiles()
|
111
|
-
choices = [
|
112
|
-
"Developer with Python Tools",
|
113
|
-
"Developer",
|
114
|
-
"Market Analyst",
|
115
|
-
"Custom system prompt...",
|
116
|
-
"Raw Model Session (no tools, no context)",
|
117
|
-
]
|
118
|
-
# Add user profiles to choices
|
119
|
-
if user_profiles:
|
120
|
-
choices.extend(user_profiles.keys())
|
121
|
-
|
122
|
-
custom_style = Style(
|
123
|
-
[
|
124
|
-
("highlighted", "bg:#00aaff #ffffff"), # background for item under cursor
|
125
|
-
("question", "fg:#00aaff bold"),
|
126
|
-
]
|
127
|
-
)
|
128
|
-
answer = questionary.select(
|
129
|
-
"Select a profile to use:", choices=choices, default=None, style=custom_style
|
130
|
-
).ask()
|
131
|
-
|
132
|
-
if not answer:
|
133
|
-
import sys
|
134
|
-
|
135
|
-
sys.exit(0)
|
136
|
-
|
137
|
-
if answer == "Raw Model Session (no tools, no context)":
|
138
|
-
return _handle_raw_model_session_no_tools()
|
139
|
-
elif answer == "Custom system prompt...":
|
140
|
-
return _handle_custom_system_prompt()
|
141
|
-
elif answer in user_profiles:
|
142
|
-
# Return the content of the user profile as a custom system prompt
|
143
|
-
return {"profile": None, "profile_system_prompt": user_profiles[answer]}
|
144
|
-
elif answer == "Developer":
|
145
|
-
# Return the content of the built-in Developer profile prompt
|
146
|
-
from pathlib import Path
|
147
|
-
from jinja2 import Template
|
148
|
-
from janito.agent.setup_agent import _prepare_template_context
|
149
|
-
|
150
|
-
# Get the absolute path relative to the current script location
|
151
|
-
current_dir = Path(__file__).parent
|
152
|
-
template_path = (
|
153
|
-
current_dir
|
154
|
-
/ "../../agent/templates/profiles/system_prompt_template_developer.txt.j2"
|
155
|
-
)
|
156
|
-
with open(template_path, "r", encoding="utf-8") as f:
|
157
|
-
template_content = f.read()
|
158
|
-
|
159
|
-
template = Template(template_content)
|
160
|
-
context = _prepare_template_context("developer", "Developer", None)
|
161
|
-
prompt = template.render(**context)
|
162
|
-
return {"profile": "Developer", "profile_system_prompt": prompt}
|
163
|
-
elif answer == "Market Analyst":
|
164
|
-
# Return the content of the built-in Market Analyst profile prompt
|
165
|
-
from pathlib import Path
|
166
|
-
from jinja2 import Template
|
167
|
-
from janito.agent.setup_agent import _prepare_template_context
|
168
|
-
|
169
|
-
# Get the absolute path relative to the current script location
|
170
|
-
current_dir = Path(__file__).parent
|
171
|
-
template_path = (
|
172
|
-
current_dir
|
173
|
-
/ "../../agent/templates/profiles/system_prompt_template_market_analyst.txt.j2"
|
174
|
-
)
|
175
|
-
with open(template_path, "r", encoding="utf-8") as f:
|
176
|
-
template_content = f.read()
|
177
|
-
|
178
|
-
template = Template(template_content)
|
179
|
-
context = _prepare_template_context("market_analyst", "Market Analyst", None)
|
180
|
-
prompt = template.render(**context)
|
181
|
-
return {"profile": "Market Analyst", "profile_system_prompt": prompt}
|
182
|
-
return answer
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|