janito 2.3.1__py3-none-any.whl → 2.4.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 +92 -18
- 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/session.py +80 -94
- janito/cli/chat_mode/session_profile_select.py +80 -0
- janito/cli/chat_mode/shell/commands/__init__.py +13 -7
- janito/cli/chat_mode/shell/commands/_priv_check.py +5 -0
- janito/cli/chat_mode/shell/commands/conversation_restart.py +30 -0
- janito/cli/chat_mode/shell/commands/execute.py +42 -0
- janito/cli/chat_mode/shell/commands/help.py +6 -3
- janito/cli/chat_mode/shell/commands/model.py +28 -0
- 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/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 +25 -18
- janito/cli/core/setters.py +10 -1
- janito/cli/main_cli.py +28 -5
- janito/cli/prompt_core.py +18 -2
- janito/cli/prompt_setup.py +56 -0
- janito/cli/single_shot_mode/handler.py +14 -73
- janito/cli/verbose_output.py +1 -1
- janito/config_manager.py +125 -112
- janito/drivers/dashscope.bak.zip +0 -0
- janito/drivers/openai/README.md +20 -0
- janito/drivers/openai_responses.bak.zip +0 -0
- janito/event_bus/event.py +2 -2
- 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/azure_openai/model_info.py +16 -16
- janito/providers/dashscope.bak.zip +0 -0
- 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 +2 -2
- 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 -1
- janito/tools/tool_utils.py +4 -6
- janito/tools/tools_adapter.py +25 -20
- {janito-2.3.1.dist-info → janito-2.4.0.dist-info}/METADATA +51 -16
- {janito-2.3.1.dist-info → janito-2.4.0.dist-info}/RECORD +88 -74
- 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.4.0.dist-info}/WHEEL +0 -0
- {janito-2.3.1.dist-info → janito-2.4.0.dist-info}/entry_points.txt +0 -0
- {janito-2.3.1.dist-info → janito-2.4.0.dist-info}/licenses/LICENSE +0 -0
- {janito-2.3.1.dist-info → janito-2.4.0.dist-info}/top_level.txt +0 -0
@@ -2,48 +2,32 @@
|
|
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, exec_enabled=False):
|
13
|
+
def __init__(self, args, provider_instance, llm_driver_config, role=None, exec_enabled=False, 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
18
|
self.exec_enabled = exec_enabled
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
self.agent = create_configured_agent(
|
19
|
+
# Instantiate agent together with prompt handler using the shared helper
|
20
|
+
self.agent, self.generic_handler = setup_agent_and_prompt_handler(
|
21
|
+
args=args,
|
28
22
|
provider_instance=provider_instance,
|
29
23
|
llm_driver_config=llm_driver_config,
|
30
24
|
role=role,
|
31
25
|
verbose_tools=getattr(args, "verbose_tools", False),
|
32
26
|
verbose_agent=getattr(args, "verbose_agent", False),
|
33
27
|
exec_enabled=exec_enabled,
|
28
|
+
allowed_permissions=allowed_permissions,
|
29
|
+
profile=getattr(args, "profile", None),
|
34
30
|
)
|
35
|
-
# Setup conversation/history if needed
|
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
|
45
|
-
)
|
46
|
-
self.generic_handler.agent = self.agent
|
47
31
|
|
48
32
|
def handle(self) -> None:
|
49
33
|
import traceback
|
@@ -75,56 +59,13 @@ class PromptHandler:
|
|
75
59
|
self._post_prompt_actions()
|
76
60
|
|
77
61
|
def _post_prompt_actions(self):
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
from janito.perf_singleton import performance_collector
|
84
|
-
|
85
|
-
token_info = performance_collector.get_last_request_usage()
|
86
|
-
from rich.rule import Rule
|
87
|
-
from rich import print as rich_print
|
88
|
-
from janito.cli.utils import format_tokens
|
89
|
-
|
90
|
-
if token_info:
|
91
|
-
if isinstance(token_info, dict):
|
92
|
-
token_str = " | ".join(
|
93
|
-
f"{k}: {format_tokens(v) if isinstance(v, int) else v}"
|
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]")
|
62
|
+
# Align with chat mode: only print token usage summary
|
63
|
+
from janito.formatting_token import print_token_message_summary
|
64
|
+
from janito.perf_singleton import performance_collector
|
65
|
+
usage = performance_collector.get_last_request_usage()
|
66
|
+
print_token_message_summary(shared_console, msg_count=1, usage=usage)
|
103
67
|
self._cleanup_driver_and_console()
|
104
68
|
|
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
69
|
|
129
70
|
def _cleanup_driver_and_console(self):
|
130
71
|
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
|
@@ -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/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.
|
janito/llm/agent.py
CHANGED
@@ -94,6 +94,21 @@ class LLMAgent:
|
|
94
94
|
autoescape=select_autoescape(),
|
95
95
|
)
|
96
96
|
template = env.get_template(Path(self.system_prompt_template).name)
|
97
|
+
# Refresh allowed_permissions in context before rendering
|
98
|
+
from janito.tools.permissions import get_global_allowed_permissions
|
99
|
+
from janito.tools.tool_base import ToolPermissions
|
100
|
+
perms = get_global_allowed_permissions()
|
101
|
+
if isinstance(perms, ToolPermissions):
|
102
|
+
perm_str = ""
|
103
|
+
if perms.read:
|
104
|
+
perm_str += "r"
|
105
|
+
if perms.write:
|
106
|
+
perm_str += "w"
|
107
|
+
if perms.execute:
|
108
|
+
perm_str += "x"
|
109
|
+
self._template_vars["allowed_permissions"] = perm_str or None
|
110
|
+
else:
|
111
|
+
self._template_vars["allowed_permissions"] = perms
|
97
112
|
self.system_prompt = template.render(**self._template_vars)
|
98
113
|
|
99
114
|
def get_system_prompt(self) -> str:
|
@@ -165,13 +180,10 @@ class LLMAgent:
|
|
165
180
|
if getattr(self, "verbose_agent", False):
|
166
181
|
print("[agent] [DEBUG] Entered _process_next_response")
|
167
182
|
elapsed = 0.0
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
except KeyboardInterrupt:
|
173
|
-
self._handle_keyboard_interrupt()
|
174
|
-
return None, False
|
183
|
+
if getattr(self, "verbose_agent", False):
|
184
|
+
print("[agent] [DEBUG] Waiting for event from output_queue...")
|
185
|
+
# Let KeyboardInterrupt propagate to caller
|
186
|
+
return self._poll_for_event(poll_timeout, max_wait_time)
|
175
187
|
|
176
188
|
def _poll_for_event(self, poll_timeout, max_wait_time):
|
177
189
|
elapsed = 0.0
|
@@ -201,15 +213,6 @@ class LLMAgent:
|
|
201
213
|
]:
|
202
214
|
return (event, False)
|
203
215
|
|
204
|
-
def _handle_keyboard_interrupt(self):
|
205
|
-
if hasattr(self, "input_queue") and self.input_queue is not None:
|
206
|
-
from janito.driver_events import RequestFinished
|
207
|
-
|
208
|
-
cancel_event = RequestFinished(
|
209
|
-
status=RequestStatus.CANCELLED,
|
210
|
-
reason="User interrupted (KeyboardInterrupt)",
|
211
|
-
)
|
212
|
-
self.input_queue.put(cancel_event)
|
213
216
|
|
214
217
|
def _get_event_from_output_queue(self, poll_timeout):
|
215
218
|
try:
|
@@ -302,6 +305,13 @@ class LLMAgent:
|
|
302
305
|
and hasattr(self.driver, "clear_output_queue")
|
303
306
|
):
|
304
307
|
self.driver.clear_output_queue()
|
308
|
+
# Drain input queue before sending new messages
|
309
|
+
if (
|
310
|
+
hasattr(self, "driver")
|
311
|
+
and self.driver
|
312
|
+
and hasattr(self.driver, "clear_input_queue")
|
313
|
+
):
|
314
|
+
self.driver.clear_input_queue()
|
305
315
|
"""
|
306
316
|
Main agent conversation loop supporting function/tool calls and conversation history extension, now as a blocking event-driven loop with event publishing.
|
307
317
|
|
@@ -329,6 +339,7 @@ class LLMAgent:
|
|
329
339
|
try:
|
330
340
|
result, added_tool_results = self._process_next_response()
|
331
341
|
except KeyboardInterrupt:
|
342
|
+
# Propagate the interrupt to the caller, but signal the driver to cancel first
|
332
343
|
cancel_event.set()
|
333
344
|
raise
|
334
345
|
if getattr(self, "verbose_agent", False):
|
@@ -420,6 +431,59 @@ class LLMAgent:
|
|
420
431
|
return self.llm_provider.model_name
|
421
432
|
return "?"
|
422
433
|
|
434
|
+
def reset_driver_config_to_model_defaults(self, model_name: str):
|
435
|
+
"""
|
436
|
+
Reset all driver config fields to the model's defaults for the current provider (overwriting any user customizations).
|
437
|
+
"""
|
438
|
+
provider = self.llm_provider
|
439
|
+
# Find model spec
|
440
|
+
model_spec = None
|
441
|
+
if hasattr(provider, "MODEL_SPECS"):
|
442
|
+
model_spec = provider.MODEL_SPECS.get(model_name)
|
443
|
+
if not model_spec:
|
444
|
+
raise ValueError(f"Model '{model_name}' not found in provider MODEL_SPECS.")
|
445
|
+
# Overwrite all config fields with model defaults
|
446
|
+
config = getattr(provider, "driver_config", None)
|
447
|
+
if config is None:
|
448
|
+
return
|
449
|
+
config.model = model_name
|
450
|
+
# Standard fields, with safe conversion for int fields
|
451
|
+
def safe_int(val):
|
452
|
+
try:
|
453
|
+
if val is None or val == "N/A":
|
454
|
+
return None
|
455
|
+
return int(val)
|
456
|
+
except Exception:
|
457
|
+
return None
|
458
|
+
def safe_float(val):
|
459
|
+
try:
|
460
|
+
if val is None or val == "N/A":
|
461
|
+
return None
|
462
|
+
return float(val)
|
463
|
+
except Exception:
|
464
|
+
return None
|
465
|
+
config.temperature = safe_float(getattr(model_spec, "default_temp", None))
|
466
|
+
config.max_tokens = safe_int(getattr(model_spec, "max_response", None))
|
467
|
+
config.max_completion_tokens = safe_int(getattr(model_spec, "max_cot", None))
|
468
|
+
# Optionally reset other fields to None/defaults
|
469
|
+
config.top_p = None
|
470
|
+
config.presence_penalty = None
|
471
|
+
config.frequency_penalty = None
|
472
|
+
config.stop = None
|
473
|
+
config.reasoning_effort = None
|
474
|
+
# Update driver if present
|
475
|
+
if self.driver is not None:
|
476
|
+
if hasattr(self.driver, "model_name"):
|
477
|
+
self.driver.model_name = model_name
|
478
|
+
if hasattr(self.driver, "config"):
|
479
|
+
self.driver.config = config
|
480
|
+
|
481
|
+
def change_model(self, model_name: str):
|
482
|
+
"""
|
483
|
+
Change the model for the agent's provider and driver config, and update the driver if present.
|
484
|
+
"""
|
485
|
+
self.reset_driver_config_to_model_defaults(model_name)
|
486
|
+
|
423
487
|
def join_driver(self, timeout=None):
|
424
488
|
"""
|
425
489
|
Wait for the driver's background thread to finish. Call this before exiting to avoid daemon thread shutdown errors.
|