janito 2.3.1__py3-none-any.whl → 2.5.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- janito/__init__.py +1 -1
- janito/_version.py +57 -0
- janito/agent/setup_agent.py +95 -21
- janito/agent/templates/profiles/system_prompt_template_assistant.txt.j2 +1 -0
- janito/agent/templates/profiles/system_prompt_template_developer.txt.j2 +44 -0
- janito/cli/chat_mode/bindings.py +21 -2
- janito/cli/chat_mode/chat_entry.py +2 -3
- janito/cli/chat_mode/prompt_style.py +5 -0
- janito/cli/chat_mode/script_runner.py +153 -0
- janito/cli/chat_mode/session.py +128 -122
- janito/cli/chat_mode/session_profile_select.py +80 -0
- janito/cli/chat_mode/shell/commands/__init__.py +19 -9
- janito/cli/chat_mode/shell/commands/_priv_check.py +5 -0
- janito/cli/chat_mode/shell/commands/bang.py +36 -0
- janito/cli/chat_mode/shell/commands/conversation_restart.py +31 -24
- janito/cli/chat_mode/shell/commands/execute.py +42 -0
- janito/cli/chat_mode/shell/commands/help.py +7 -4
- janito/cli/chat_mode/shell/commands/model.py +28 -0
- janito/cli/chat_mode/shell/commands/prompt.py +0 -8
- janito/cli/chat_mode/shell/commands/read.py +37 -0
- janito/cli/chat_mode/shell/commands/tools.py +45 -18
- janito/cli/chat_mode/shell/commands/write.py +37 -0
- janito/cli/chat_mode/shell/commands.bak.zip +0 -0
- janito/cli/chat_mode/shell/input_history.py +1 -1
- janito/cli/chat_mode/shell/session/manager.py +0 -68
- janito/cli/chat_mode/shell/session.bak.zip +0 -0
- janito/cli/chat_mode/toolbar.py +44 -27
- janito/cli/cli_commands/list_tools.py +44 -11
- janito/cli/cli_commands/model_utils.py +95 -95
- janito/cli/cli_commands/show_system_prompt.py +57 -14
- janito/cli/config.py +5 -6
- janito/cli/core/getters.py +33 -33
- janito/cli/core/runner.py +27 -20
- janito/cli/core/setters.py +10 -1
- janito/cli/main_cli.py +40 -10
- janito/cli/prompt_core.py +18 -2
- janito/cli/prompt_setup.py +56 -0
- janito/cli/rich_terminal_reporter.py +21 -6
- janito/cli/single_shot_mode/handler.py +24 -77
- janito/cli/verbose_output.py +1 -1
- janito/config_manager.py +125 -112
- janito/drivers/dashscope.bak.zip +0 -0
- janito/drivers/driver_registry.py +0 -2
- janito/drivers/openai/README.md +20 -0
- janito/drivers/openai_responses.bak.zip +0 -0
- janito/event_bus/event.py +2 -2
- janito/formatting_token.py +7 -6
- janito/i18n/pt.py +0 -1
- janito/llm/README.md +23 -0
- janito/llm/agent.py +80 -16
- janito/llm/auth.py +63 -63
- janito/llm/driver.py +8 -0
- janito/provider_registry.py +178 -176
- janito/providers/__init__.py +0 -2
- janito/providers/azure_openai/model_info.py +16 -16
- janito/providers/dashscope.bak.zip +0 -0
- janito/providers/provider_static_info.py +0 -3
- janito/providers/registry.py +26 -26
- janito/shell.bak.zip +0 -0
- janito/tools/DOCSTRING_STANDARD.txt +33 -0
- janito/tools/README.md +3 -0
- janito/tools/__init__.py +20 -6
- janito/tools/adapters/local/__init__.py +65 -62
- janito/tools/adapters/local/adapter.py +18 -35
- janito/tools/adapters/local/ask_user.py +3 -4
- janito/tools/adapters/local/copy_file.py +2 -2
- janito/tools/adapters/local/create_directory.py +2 -2
- janito/tools/adapters/local/create_file.py +2 -2
- janito/tools/adapters/local/delete_text_in_file.py +2 -2
- janito/tools/adapters/local/fetch_url.py +2 -2
- janito/tools/adapters/local/find_files.py +2 -1
- janito/tools/adapters/local/get_file_outline/core.py +2 -2
- janito/tools/adapters/local/get_file_outline/search_outline.py +2 -2
- janito/tools/adapters/local/move_file.py +2 -2
- janito/tools/adapters/local/open_html_in_browser.py +2 -1
- janito/tools/adapters/local/open_url.py +2 -2
- janito/tools/adapters/local/python_code_run.py +3 -3
- janito/tools/adapters/local/python_command_run.py +3 -3
- janito/tools/adapters/local/python_file_run.py +3 -3
- janito/tools/adapters/local/remove_directory.py +2 -2
- janito/tools/adapters/local/remove_file.py +2 -2
- janito/tools/adapters/local/replace_text_in_file.py +2 -2
- janito/tools/adapters/local/run_bash_command.py +3 -3
- janito/tools/adapters/local/run_powershell_command.py +3 -3
- janito/tools/adapters/local/search_text/core.py +2 -2
- janito/tools/adapters/local/validate_file_syntax/core.py +3 -3
- janito/tools/adapters/local/view_file.py +2 -1
- janito/tools/outline_file.bak.zip +0 -0
- janito/tools/permissions.py +45 -0
- janito/tools/permissions_parse.py +12 -0
- janito/tools/tool_base.py +14 -11
- janito/tools/tool_utils.py +4 -6
- janito/tools/tools_adapter.py +25 -20
- {janito-2.3.1.dist-info → janito-2.5.0.dist-info}/METADATA +46 -24
- {janito-2.3.1.dist-info → janito-2.5.0.dist-info}/RECORD +99 -82
- janito/agent/templates/profiles/system_prompt_template_base_pt.txt.j2 +0 -13
- janito/agent/templates/profiles/system_prompt_template_main.txt.j2 +0 -37
- janito/cli/chat_mode/shell/commands/edit.py +0 -25
- janito/cli/chat_mode/shell/commands/exec.py +0 -27
- janito/cli/chat_mode/shell/commands/termweb_log.py +0 -92
- janito/cli/termweb_starter.py +0 -122
- janito/termweb/app.py +0 -95
- janito/version.py +0 -4
- {janito-2.3.1.dist-info → janito-2.5.0.dist-info}/WHEEL +0 -0
- {janito-2.3.1.dist-info → janito-2.5.0.dist-info}/entry_points.txt +0 -0
- {janito-2.3.1.dist-info → janito-2.5.0.dist-info}/licenses/LICENSE +0 -0
- {janito-2.3.1.dist-info → janito-2.5.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,56 @@
|
|
1
|
+
"""
|
2
|
+
Shared utilities to set up an agent together with a GenericPromptHandler that
|
3
|
+
both single–shot and chat modes can reuse. Having one central place avoids the
|
4
|
+
code duplication that previously existed in `chat_mode.session.ChatSession` and
|
5
|
+
`single_shot_mode.handler.PromptHandler`.
|
6
|
+
"""
|
7
|
+
from __future__ import annotations
|
8
|
+
|
9
|
+
from janito.agent.setup_agent import create_configured_agent
|
10
|
+
from janito.cli.prompt_core import (
|
11
|
+
PromptHandler as GenericPromptHandler,
|
12
|
+
)
|
13
|
+
from typing import Any, Optional
|
14
|
+
|
15
|
+
|
16
|
+
def setup_agent_and_prompt_handler(
|
17
|
+
*,
|
18
|
+
args: Any,
|
19
|
+
provider_instance: Any,
|
20
|
+
llm_driver_config: Any,
|
21
|
+
role: Optional[str] = None,
|
22
|
+
verbose_tools: bool = False,
|
23
|
+
verbose_agent: bool = False,
|
24
|
+
|
25
|
+
allowed_permissions: Optional[list[str]] = None,
|
26
|
+
profile: Optional[str] = None,
|
27
|
+
profile_system_prompt: Optional[str] = None,
|
28
|
+
conversation_history: Any = None,
|
29
|
+
):
|
30
|
+
"""Create a configured *agent* as well as a *GenericPromptHandler* bound to
|
31
|
+
that agent and return them as a tuple.
|
32
|
+
|
33
|
+
This helper consolidates the repetitive boiler-plate that was scattered
|
34
|
+
across *single-shot* and *chat* modes – both of which need an agent plus a
|
35
|
+
prompt handler that points to that agent.
|
36
|
+
"""
|
37
|
+
agent = create_configured_agent(
|
38
|
+
provider_instance=provider_instance,
|
39
|
+
llm_driver_config=llm_driver_config,
|
40
|
+
role=role,
|
41
|
+
verbose_tools=verbose_tools,
|
42
|
+
verbose_agent=verbose_agent,
|
43
|
+
|
44
|
+
allowed_permissions=allowed_permissions,
|
45
|
+
profile=profile,
|
46
|
+
profile_system_prompt=profile_system_prompt,
|
47
|
+
)
|
48
|
+
|
49
|
+
prompt_handler = GenericPromptHandler(
|
50
|
+
args=args,
|
51
|
+
conversation_history=conversation_history,
|
52
|
+
provider_instance=provider_instance,
|
53
|
+
)
|
54
|
+
prompt_handler.agent = agent
|
55
|
+
|
56
|
+
return agent, prompt_handler
|
@@ -10,6 +10,8 @@ from janito.event_bus.bus import event_bus
|
|
10
10
|
from janito.llm import message_parts
|
11
11
|
|
12
12
|
|
13
|
+
import sys
|
14
|
+
|
13
15
|
class RichTerminalReporter(EventHandlerBase):
|
14
16
|
"""
|
15
17
|
Handles UI rendering for janito events using Rich.
|
@@ -63,8 +65,15 @@ class RichTerminalReporter(EventHandlerBase):
|
|
63
65
|
self.console.print(Markdown(part.content))
|
64
66
|
self.console.file.flush()
|
65
67
|
|
68
|
+
def delete_current_line(self):
|
69
|
+
"""
|
70
|
+
Clears the entire current line in the terminal and returns the cursor to column 1.
|
71
|
+
"""
|
72
|
+
sys.stdout.write('\033[2K\r')
|
73
|
+
sys.stdout.flush()
|
74
|
+
|
66
75
|
def on_RequestFinished(self, event):
|
67
|
-
self.
|
76
|
+
self.delete_current_line()
|
68
77
|
self._waiting_printed = False
|
69
78
|
response = getattr(event, "response", None)
|
70
79
|
error = getattr(event, "error", None)
|
@@ -99,14 +108,14 @@ class RichTerminalReporter(EventHandlerBase):
|
|
99
108
|
if not msg or not subtype:
|
100
109
|
return
|
101
110
|
if subtype == ReportSubtype.ACTION_INFO:
|
102
|
-
|
111
|
+
# Use orange for modification actions, cyan otherwise
|
112
|
+
modification_actions = (
|
103
113
|
getattr(ReportAction, "UPDATE", None),
|
104
114
|
getattr(ReportAction, "WRITE", None),
|
105
115
|
getattr(ReportAction, "DELETE", None),
|
106
|
-
)
|
107
|
-
|
108
|
-
|
109
|
-
self.console.print(msg, end="")
|
116
|
+
)
|
117
|
+
style = "orange1" if getattr(event, "action", None) in modification_actions else "cyan"
|
118
|
+
self.console.print(Text(msg, style=style), end="")
|
110
119
|
self.console.file.flush()
|
111
120
|
elif subtype in (
|
112
121
|
ReportSubtype.SUCCESS,
|
@@ -115,6 +124,12 @@ class RichTerminalReporter(EventHandlerBase):
|
|
115
124
|
):
|
116
125
|
self.console.print(msg)
|
117
126
|
self.console.file.flush()
|
127
|
+
elif subtype == ReportSubtype.STDOUT:
|
128
|
+
self.console.print(Text(msg, style="on dark_green"))
|
129
|
+
self.console.file.flush()
|
130
|
+
elif subtype == ReportSubtype.STDERR:
|
131
|
+
self.console.print(Text(msg, style="on red"))
|
132
|
+
self.console.file.flush()
|
118
133
|
else:
|
119
134
|
self.console.print(msg)
|
120
135
|
self.console.file.flush()
|
@@ -2,48 +2,31 @@
|
|
2
2
|
PromptHandler: Handles prompt submission and response formatting for janito CLI (one-shot prompt execution).
|
3
3
|
"""
|
4
4
|
|
5
|
-
import
|
6
|
-
|
7
|
-
from janito.cli.
|
8
|
-
from janito.cli.verbose_output import (
|
9
|
-
print_verbose_header,
|
10
|
-
print_performance,
|
11
|
-
handle_exception,
|
12
|
-
)
|
5
|
+
from __future__ import annotations
|
6
|
+
|
7
|
+
from janito.cli.prompt_setup import setup_agent_and_prompt_handler
|
13
8
|
import janito.tools # Ensure all tools are registered
|
14
9
|
from janito.cli.console import shared_console
|
15
10
|
|
16
11
|
|
17
12
|
class PromptHandler:
|
18
|
-
def __init__(self, args, provider_instance, llm_driver_config, role=None,
|
13
|
+
def __init__(self, args, provider_instance, llm_driver_config, role=None, allowed_permissions=None):
|
19
14
|
self.args = args
|
20
15
|
self.provider_instance = provider_instance
|
21
16
|
self.llm_driver_config = llm_driver_config
|
22
17
|
self.role = role
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
# DEBUG: Print exec_enabled propagation
|
27
|
-
self.agent = create_configured_agent(
|
18
|
+
# Instantiate agent together with prompt handler using the shared helper
|
19
|
+
self.agent, self.generic_handler = setup_agent_and_prompt_handler(
|
20
|
+
args=args,
|
28
21
|
provider_instance=provider_instance,
|
29
22
|
llm_driver_config=llm_driver_config,
|
30
23
|
role=role,
|
31
24
|
verbose_tools=getattr(args, "verbose_tools", False),
|
32
25
|
verbose_agent=getattr(args, "verbose_agent", False),
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
# Dynamically enable/disable execution tools in the registry
|
37
|
-
try:
|
38
|
-
registry = __import__('janito.tools', fromlist=['get_local_tools_adapter']).get_local_tools_adapter()
|
39
|
-
if hasattr(registry, 'set_execution_tools_enabled'):
|
40
|
-
registry.set_execution_tools_enabled(exec_enabled)
|
41
|
-
except Exception as e:
|
42
|
-
shared_console.print(f"[yellow]Warning: Could not update execution tools dynamically in single-shot mode: {e}[/yellow]")
|
43
|
-
self.generic_handler = GenericPromptHandler(
|
44
|
-
args, [], provider_instance=provider_instance
|
26
|
+
|
27
|
+
allowed_permissions=allowed_permissions,
|
28
|
+
profile=getattr(args, "profile", None),
|
45
29
|
)
|
46
|
-
self.generic_handler.agent = self.agent
|
47
30
|
|
48
31
|
def handle(self) -> None:
|
49
32
|
import traceback
|
@@ -58,13 +41,18 @@ class PromptHandler:
|
|
58
41
|
shared_console.print(
|
59
42
|
"[yellow]Warning: Some characters in your input were not valid UTF-8 and have been replaced.[/yellow]"
|
60
43
|
)
|
44
|
+
import time
|
61
45
|
try:
|
46
|
+
start_time = time.time()
|
62
47
|
self.generic_handler.handle_prompt(
|
63
48
|
sanitized,
|
64
49
|
args=self.args,
|
65
50
|
print_header=True,
|
66
51
|
raw=getattr(self.args, "raw", False),
|
67
52
|
)
|
53
|
+
end_time = time.time()
|
54
|
+
elapsed = end_time - start_time
|
55
|
+
self._post_prompt_actions(elapsed=elapsed)
|
68
56
|
if hasattr(self.args, "verbose_agent") and self.args.verbose_agent:
|
69
57
|
print("[debug] handle_prompt() completed without exception.")
|
70
58
|
except Exception as e:
|
@@ -72,59 +60,18 @@ class PromptHandler:
|
|
72
60
|
f"[error] Exception occurred in handle_prompt: {type(e).__name__}: {e}"
|
73
61
|
)
|
74
62
|
traceback.print_exc()
|
75
|
-
self._post_prompt_actions()
|
76
|
-
|
77
|
-
def _post_prompt_actions(self):
|
78
|
-
final_event = getattr(self.agent, "last_event", None)
|
79
|
-
if final_event is not None:
|
80
|
-
self._print_exit_reason_and_parts(final_event)
|
81
|
-
# --- BEGIN: Print token info in rich rule if --verbose is set ---
|
82
|
-
if hasattr(self.args, "verbose") and self.args.verbose:
|
83
|
-
from janito.perf_singleton import performance_collector
|
84
63
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
for k, v in token_info.items()
|
95
|
-
)
|
96
|
-
else:
|
97
|
-
token_str = str(token_info)
|
98
|
-
rich_print(Rule(f"[bold cyan]Token Usage[/bold cyan] {token_str}"))
|
99
|
-
else:
|
100
|
-
rich_print(Rule("[cyan]No token usage info available.[/cyan]"))
|
101
|
-
else:
|
102
|
-
shared_console.print("[yellow]No output produced by the model.[/yellow]")
|
64
|
+
def _post_prompt_actions(self, elapsed=None):
|
65
|
+
# Align with chat mode: only print token usage summary
|
66
|
+
import sys
|
67
|
+
from janito.formatting_token import print_token_message_summary
|
68
|
+
from janito.perf_singleton import performance_collector
|
69
|
+
usage = performance_collector.get_last_request_usage()
|
70
|
+
# If running in stdin mode, do not print token usage
|
71
|
+
if sys.stdin.isatty():
|
72
|
+
print_token_message_summary(shared_console, msg_count=1, usage=usage, elapsed=elapsed)
|
103
73
|
self._cleanup_driver_and_console()
|
104
74
|
|
105
|
-
def _print_exit_reason_and_parts(self, final_event):
|
106
|
-
exit_reason = (
|
107
|
-
getattr(final_event, "metadata", {}).get("exit_reason")
|
108
|
-
if hasattr(final_event, "metadata")
|
109
|
-
else None
|
110
|
-
)
|
111
|
-
if exit_reason:
|
112
|
-
print(f"[bold yellow]Exit reason: {exit_reason}[/bold yellow]")
|
113
|
-
parts = getattr(final_event, "parts", None)
|
114
|
-
if not exit_reason:
|
115
|
-
if parts is None or len(parts) == 0:
|
116
|
-
shared_console.print(
|
117
|
-
"[yellow]No output produced by the model.[/yellow]"
|
118
|
-
)
|
119
|
-
else:
|
120
|
-
if hasattr(self.args, "verbose_agent") and self.args.verbose_agent:
|
121
|
-
print(
|
122
|
-
"[yellow]No user-visible output. Model returned the following parts:"
|
123
|
-
)
|
124
|
-
for idx, part in enumerate(parts):
|
125
|
-
print(
|
126
|
-
f" [part {idx}] type: {type(part).__name__} | content: {getattr(part, 'content', repr(part))}"
|
127
|
-
)
|
128
75
|
|
129
76
|
def _cleanup_driver_and_console(self):
|
130
77
|
if hasattr(self.agent, "join_driver"):
|
janito/cli/verbose_output.py
CHANGED
@@ -6,7 +6,7 @@ from rich import print as rich_print
|
|
6
6
|
from rich.align import Align
|
7
7
|
from rich.panel import Panel
|
8
8
|
from rich.text import Text
|
9
|
-
from janito
|
9
|
+
from janito import __version__ as VERSION
|
10
10
|
from janito.cli.utils import format_tokens
|
11
11
|
|
12
12
|
|
janito/config_manager.py
CHANGED
@@ -1,112 +1,125 @@
|
|
1
|
-
import json
|
2
|
-
from pathlib import Path
|
3
|
-
from threading import Lock
|
4
|
-
|
5
|
-
|
6
|
-
class ConfigManager:
|
7
|
-
"""
|
8
|
-
Unified configuration manager supporting:
|
9
|
-
- Defaults
|
10
|
-
- File-based configuration
|
11
|
-
- Runtime overrides (e.g., CLI args)
|
12
|
-
"""
|
13
|
-
|
14
|
-
_instance = None
|
15
|
-
_lock = Lock()
|
16
|
-
|
17
|
-
def __new__(cls, *args, **kwargs):
|
18
|
-
with cls._lock:
|
19
|
-
if not cls._instance:
|
20
|
-
cls._instance = super(ConfigManager, cls).__new__(cls)
|
21
|
-
return cls._instance
|
22
|
-
|
23
|
-
def __init__(self, config_path=None, defaults=None, runtime_overrides=None):
|
24
|
-
# Lazy single-init
|
25
|
-
if hasattr(self, "_initialized") and self._initialized:
|
26
|
-
return
|
27
|
-
self._initialized = True
|
28
|
-
|
29
|
-
self.config_path = Path(config_path or Path.home() / ".janito" / "config.json")
|
30
|
-
self.defaults = dict(defaults) if defaults else {}
|
31
|
-
self.file_config = {}
|
32
|
-
self.runtime_overrides = dict(runtime_overrides) if runtime_overrides else {}
|
33
|
-
self._load_file_config()
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
def
|
59
|
-
self.
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
if
|
87
|
-
|
88
|
-
|
89
|
-
self.
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
self.file_config
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
1
|
+
import json
|
2
|
+
from pathlib import Path
|
3
|
+
from threading import Lock
|
4
|
+
|
5
|
+
|
6
|
+
class ConfigManager:
|
7
|
+
"""
|
8
|
+
Unified configuration manager supporting:
|
9
|
+
- Defaults
|
10
|
+
- File-based configuration
|
11
|
+
- Runtime overrides (e.g., CLI args)
|
12
|
+
"""
|
13
|
+
|
14
|
+
_instance = None
|
15
|
+
_lock = Lock()
|
16
|
+
|
17
|
+
def __new__(cls, *args, **kwargs):
|
18
|
+
with cls._lock:
|
19
|
+
if not cls._instance:
|
20
|
+
cls._instance = super(ConfigManager, cls).__new__(cls)
|
21
|
+
return cls._instance
|
22
|
+
|
23
|
+
def __init__(self, config_path=None, defaults=None, runtime_overrides=None):
|
24
|
+
# Lazy single-init
|
25
|
+
if hasattr(self, "_initialized") and self._initialized:
|
26
|
+
return
|
27
|
+
self._initialized = True
|
28
|
+
|
29
|
+
self.config_path = Path(config_path or Path.home() / ".janito" / "config.json")
|
30
|
+
self.defaults = dict(defaults) if defaults else {}
|
31
|
+
self.file_config = {}
|
32
|
+
self.runtime_overrides = dict(runtime_overrides) if runtime_overrides else {}
|
33
|
+
self._load_file_config()
|
34
|
+
self._apply_tool_permissions_on_startup()
|
35
|
+
|
36
|
+
def _apply_tool_permissions_on_startup(self):
|
37
|
+
# On startup, read tool_permissions from config and set global permissions
|
38
|
+
perm_str = self.file_config.get("tool_permissions")
|
39
|
+
if perm_str:
|
40
|
+
try:
|
41
|
+
from janito.tools.permissions_parse import parse_permissions_string
|
42
|
+
from janito.tools.permissions import set_global_allowed_permissions
|
43
|
+
perms = parse_permissions_string(perm_str)
|
44
|
+
set_global_allowed_permissions(perms)
|
45
|
+
except Exception as e:
|
46
|
+
print(f"Warning: Failed to apply tool_permissions from config: {e}")
|
47
|
+
|
48
|
+
def _load_file_config(self):
|
49
|
+
if self.config_path.exists():
|
50
|
+
with open(self.config_path, "r", encoding="utf-8") as f:
|
51
|
+
try:
|
52
|
+
self.file_config = json.load(f)
|
53
|
+
except Exception:
|
54
|
+
self.file_config = {}
|
55
|
+
else:
|
56
|
+
self.file_config = {}
|
57
|
+
|
58
|
+
def save(self):
|
59
|
+
self.config_path.parent.mkdir(parents=True, exist_ok=True)
|
60
|
+
with open(self.config_path, "w", encoding="utf-8") as f:
|
61
|
+
json.dump(self.file_config, f, indent=2)
|
62
|
+
f.write("\n")
|
63
|
+
|
64
|
+
def get(self, key, default=None):
|
65
|
+
# Precedence: runtime_overrides > file_config > defaults
|
66
|
+
for layer in (self.runtime_overrides, self.file_config, self.defaults):
|
67
|
+
if key in layer and layer[key] is not None:
|
68
|
+
return layer[key]
|
69
|
+
return default
|
70
|
+
|
71
|
+
def runtime_set(self, key, value):
|
72
|
+
self.runtime_overrides[key] = value
|
73
|
+
|
74
|
+
def file_set(self, key, value):
|
75
|
+
# Always reload, update, and persist
|
76
|
+
self._load_file_config()
|
77
|
+
self.file_config[key] = value
|
78
|
+
with open(self.config_path, "w", encoding="utf-8") as f:
|
79
|
+
json.dump(self.file_config, f, indent=2)
|
80
|
+
f.write("\n")
|
81
|
+
|
82
|
+
def all(self, layered=False):
|
83
|
+
merged = dict(self.defaults)
|
84
|
+
merged.update(self.file_config)
|
85
|
+
merged.update(self.runtime_overrides)
|
86
|
+
if layered:
|
87
|
+
# Only file+runtime, i.e., what is saved to disk
|
88
|
+
d = dict(self.file_config)
|
89
|
+
d.update(self.runtime_overrides)
|
90
|
+
return d
|
91
|
+
return merged
|
92
|
+
|
93
|
+
# Namespaced provider/model config
|
94
|
+
def get_provider_config(self, provider, default=None):
|
95
|
+
providers = self.file_config.get("providers") or {}
|
96
|
+
return providers.get(provider) or (default or {})
|
97
|
+
|
98
|
+
def set_provider_config(self, provider, key, value):
|
99
|
+
if "providers" not in self.file_config:
|
100
|
+
self.file_config["providers"] = {}
|
101
|
+
if provider not in self.file_config["providers"]:
|
102
|
+
self.file_config["providers"][provider] = {}
|
103
|
+
self.file_config["providers"][provider][key] = value
|
104
|
+
|
105
|
+
def get_provider_model_config(self, provider, model, default=None):
|
106
|
+
return (
|
107
|
+
self.file_config.get("providers")
|
108
|
+
or {}.get(provider, {}).get("models", {}).get(model)
|
109
|
+
or (default or {})
|
110
|
+
)
|
111
|
+
|
112
|
+
def set_provider_model_config(self, provider, model, key, value):
|
113
|
+
if "providers" not in self.file_config:
|
114
|
+
self.file_config["providers"] = {}
|
115
|
+
if provider not in self.file_config["providers"]:
|
116
|
+
self.file_config["providers"][provider] = {}
|
117
|
+
if "models" not in self.file_config["providers"][provider]:
|
118
|
+
self.file_config["providers"][provider]["models"] = {}
|
119
|
+
if model not in self.file_config["providers"][provider]["models"]:
|
120
|
+
self.file_config["providers"][provider]["models"][model] = {}
|
121
|
+
self.file_config["providers"][provider]["models"][model][key] = value
|
122
|
+
|
123
|
+
# Support loading runtime overrides after init (e.g. after parsing CLI args)
|
124
|
+
def apply_runtime_overrides(self, overrides_dict):
|
125
|
+
self.runtime_overrides.update(overrides_dict)
|
Binary file
|
@@ -8,13 +8,11 @@ from typing import Dict, Type
|
|
8
8
|
# --- Import driver classes ---
|
9
9
|
from janito.drivers.anthropic.driver import AnthropicModelDriver
|
10
10
|
from janito.drivers.azure_openai.driver import AzureOpenAIModelDriver
|
11
|
-
from janito.drivers.mistralai.driver import MistralAIModelDriver
|
12
11
|
from janito.drivers.openai.driver import OpenAIModelDriver
|
13
12
|
|
14
13
|
_DRIVER_REGISTRY: Dict[str, Type] = {
|
15
14
|
"AnthropicModelDriver": AnthropicModelDriver,
|
16
15
|
"AzureOpenAIModelDriver": AzureOpenAIModelDriver,
|
17
|
-
"MistralAIModelDriver": MistralAIModelDriver,
|
18
16
|
"OpenAIModelDriver": OpenAIModelDriver,
|
19
17
|
}
|
20
18
|
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# OpenAI Driver Debugging
|
2
|
+
|
3
|
+
## HTTP Debugging via Environment Variable
|
4
|
+
|
5
|
+
To debug HTTP requests and responses for the OpenAI driver, set the environment variable `OPENAI_DEBUG_HTTP=1` before running your application. This will print the full HTTP request and response bodies to the console for troubleshooting purposes.
|
6
|
+
|
7
|
+
**Example (PowerShell):**
|
8
|
+
|
9
|
+
```
|
10
|
+
$env:OPENAI_DEBUG_HTTP=1
|
11
|
+
python your_app.py
|
12
|
+
```
|
13
|
+
|
14
|
+
**Example (bash):**
|
15
|
+
|
16
|
+
```
|
17
|
+
OPENAI_DEBUG_HTTP=1 python your_app.py
|
18
|
+
```
|
19
|
+
|
20
|
+
This feature is implemented in `janito/drivers/openai/driver.py` and works by wrapping the OpenAI client HTTP transport with a debug logger when the environment variable is set.
|
Binary file
|
janito/event_bus/event.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import attr
|
2
2
|
from typing import ClassVar
|
3
|
-
from datetime import datetime
|
3
|
+
from datetime import datetime, timezone
|
4
4
|
|
5
5
|
|
6
6
|
@attr.s(auto_attribs=True, kw_only=True)
|
@@ -12,4 +12,4 @@ class Event:
|
|
12
12
|
"""
|
13
13
|
|
14
14
|
category: ClassVar[str] = "generic"
|
15
|
-
timestamp: datetime = attr.ib(factory=datetime.
|
15
|
+
timestamp: datetime = attr.ib(factory=lambda: datetime.now(timezone.utc))
|
janito/formatting_token.py
CHANGED
@@ -25,9 +25,9 @@ def format_tokens(n, tag=None, use_rich=False):
|
|
25
25
|
return val
|
26
26
|
|
27
27
|
|
28
|
-
def format_token_message_summary(msg_count, usage, width=96, use_rich=False):
|
28
|
+
def format_token_message_summary(msg_count, usage, width=96, use_rich=False, elapsed=None):
|
29
29
|
"""
|
30
|
-
Returns a string (rich or pt markup) summarizing message count
|
30
|
+
Returns a string (rich or pt markup) summarizing message count, last token usage, and elapsed time.
|
31
31
|
"""
|
32
32
|
left = f" Messages: {'[' if use_rich else '<'}msg_count{']' if use_rich else '>'}{msg_count}{'[/msg_count]' if use_rich else '</msg_count>'}"
|
33
33
|
tokens_part = ""
|
@@ -40,15 +40,16 @@ def format_token_message_summary(msg_count, usage, width=96, use_rich=False):
|
|
40
40
|
f"Completion: {format_tokens(completion_tokens, 'tokens_out', use_rich)}, "
|
41
41
|
f"Total: {format_tokens(total_tokens, 'tokens_total', use_rich)}"
|
42
42
|
)
|
43
|
-
|
43
|
+
elapsed_part = f" | Elapsed: [cyan]{elapsed:.2f}s[/cyan]" if elapsed is not None else ""
|
44
|
+
return f"{left}{tokens_part}{elapsed_part}"
|
44
45
|
|
45
46
|
|
46
|
-
def print_token_message_summary(console, msg_count=None, usage=None, width=96):
|
47
|
-
"""Prints the summary using rich markup, using defaults from perf_singleton if not given."""
|
47
|
+
def print_token_message_summary(console, msg_count=None, usage=None, width=96, elapsed=None):
|
48
|
+
"""Prints the summary using rich markup, using defaults from perf_singleton if not given. Optionally includes elapsed time."""
|
48
49
|
if usage is None:
|
49
50
|
usage = performance_collector.get_last_request_usage()
|
50
51
|
if msg_count is None:
|
51
52
|
msg_count = performance_collector.get_total_turns() or 0
|
52
|
-
line = format_token_message_summary(msg_count, usage, width, use_rich=True)
|
53
|
+
line = format_token_message_summary(msg_count, usage, width, use_rich=True, elapsed=elapsed)
|
53
54
|
if line.strip():
|
54
55
|
console.print(Rule(line))
|
janito/i18n/pt.py
CHANGED
@@ -3,7 +3,6 @@ translations = {
|
|
3
3
|
"36107ed78ab25f6fb12ad8ce13018cd1ce6735d1": "Iniciando servidor web...",
|
4
4
|
"70a0d194687568a47aa617fd85036ace1e69a982": "Deseja realmente sair? (s/n): ",
|
5
5
|
"5c9ebcbbd7632ecb328bd52958b17158afaa32c6": "F12 = Ação Rápida (segue a ação recomendada)",
|
6
|
-
"e4034394acca752c021b2ab50f60da8273e3c314": "TermWeb iniciado... Disponível em http://localhost:{selected_port}",
|
7
6
|
"fe21121e2934234b68d19b2757532117d440c1e3": "Chave de API não encontrada. Por favor, configure 'api_key' no seu arquivo de configuração.",
|
8
7
|
"c9e3759b1756eba35b381ce2b72cd659e132b01f": "Olá, {name}!",
|
9
8
|
"ca1fee2f55baabdc2e4b0e9529c89ee024e62079": "Nenhum prompt fornecido nas mensagens",
|
janito/llm/README.md
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# janito.llm Package
|
2
|
+
|
3
|
+
This directory contains generic, provider-agnostic classes and methods for working with Large Language Models (LLMs) in the `janito` framework. Its purpose is to provide base abstractions that can be extended by provider-specific implementations.
|
4
|
+
|
5
|
+
## Scope and Contents
|
6
|
+
|
7
|
+
- **driver.py**
|
8
|
+
- Contains `LLMDriver`, an abstract base class defining the core methods for LLM drivers. Subclasses should implement provider/model-specific logic, while benefiting from consistent streaming and event interfaces.
|
9
|
+
- **provider.py**
|
10
|
+
- Contains `LLMProvider`, an abstract base class for LLM API providers. This outlines the required interface for integrating new providers and retrieving model info or driver classes.
|
11
|
+
|
12
|
+
## Usage
|
13
|
+
- Extend these base classes when adding new drivers or providers.
|
14
|
+
- Do not include provider-specific logic here; only generic mechanisms, patterns, or utilities applicable to any LLM integration belong in this package.
|
15
|
+
|
16
|
+
## Example
|
17
|
+
```python
|
18
|
+
from janito.llm.driver import LLMDriver
|
19
|
+
from janito.llm.provider import LLMProvider
|
20
|
+
```
|
21
|
+
|
22
|
+
---
|
23
|
+
This README clarifies the intention of the `llm` package as the generic/static contract for LLM drivers and providers.
|