janito 1.5.2__py3-none-any.whl → 1.6.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/__main__.py +0 -1
- janito/agent/config.py +11 -10
- janito/agent/config_defaults.py +3 -2
- janito/agent/conversation.py +93 -119
- janito/agent/conversation_api.py +98 -0
- janito/agent/conversation_exceptions.py +12 -0
- janito/agent/conversation_tool_calls.py +22 -0
- janito/agent/conversation_ui.py +17 -0
- janito/agent/message_handler.py +8 -9
- janito/agent/{agent.py → openai_client.py} +48 -16
- janito/agent/openai_schema_generator.py +53 -37
- janito/agent/profile_manager.py +172 -0
- janito/agent/queued_message_handler.py +13 -14
- janito/agent/rich_live.py +32 -0
- janito/agent/rich_message_handler.py +64 -0
- janito/agent/runtime_config.py +6 -1
- janito/agent/{tools/tool_base.py → tool_base.py} +15 -8
- janito/agent/tool_registry.py +118 -132
- janito/agent/tools/__init__.py +41 -2
- janito/agent/tools/ask_user.py +43 -33
- janito/agent/tools/create_directory.py +18 -16
- janito/agent/tools/create_file.py +31 -36
- janito/agent/tools/fetch_url.py +23 -19
- janito/agent/tools/find_files.py +40 -36
- janito/agent/tools/get_file_outline.py +100 -22
- janito/agent/tools/get_lines.py +40 -32
- janito/agent/tools/gitignore_utils.py +9 -6
- janito/agent/tools/move_file.py +22 -13
- janito/agent/tools/py_compile_file.py +40 -0
- janito/agent/tools/remove_directory.py +34 -24
- janito/agent/tools/remove_file.py +22 -20
- janito/agent/tools/replace_file.py +51 -0
- janito/agent/tools/replace_text_in_file.py +69 -42
- janito/agent/tools/rich_live.py +9 -2
- janito/agent/tools/run_bash_command.py +155 -107
- janito/agent/tools/run_python_command.py +139 -0
- janito/agent/tools/search_files.py +51 -34
- janito/agent/tools/tools_utils.py +4 -2
- janito/agent/tools/utils.py +6 -2
- janito/cli/_print_config.py +42 -16
- janito/cli/_utils.py +1 -0
- janito/cli/arg_parser.py +182 -29
- janito/cli/config_commands.py +54 -22
- janito/cli/logging_setup.py +9 -3
- janito/cli/main.py +11 -10
- janito/cli/runner/__init__.py +2 -0
- janito/cli/runner/cli_main.py +148 -0
- janito/cli/runner/config.py +33 -0
- janito/cli/runner/formatting.py +12 -0
- janito/cli/runner/scan.py +44 -0
- janito/cli_chat_shell/__init__.py +0 -1
- janito/cli_chat_shell/chat_loop.py +71 -92
- janito/cli_chat_shell/chat_state.py +38 -0
- janito/cli_chat_shell/chat_ui.py +43 -0
- janito/cli_chat_shell/commands/__init__.py +45 -0
- janito/cli_chat_shell/commands/config.py +22 -0
- janito/cli_chat_shell/commands/history_reset.py +29 -0
- janito/cli_chat_shell/commands/session.py +48 -0
- janito/cli_chat_shell/commands/session_control.py +12 -0
- janito/cli_chat_shell/commands/system.py +73 -0
- janito/cli_chat_shell/commands/utility.py +29 -0
- janito/cli_chat_shell/config_shell.py +39 -10
- janito/cli_chat_shell/load_prompt.py +5 -2
- janito/cli_chat_shell/session_manager.py +24 -27
- janito/cli_chat_shell/ui.py +75 -40
- janito/rich_utils.py +15 -2
- janito/web/__main__.py +10 -2
- janito/web/app.py +88 -52
- {janito-1.5.2.dist-info → janito-1.6.0.dist-info}/METADATA +76 -11
- janito-1.6.0.dist-info/RECORD +81 -0
- {janito-1.5.2.dist-info → janito-1.6.0.dist-info}/WHEEL +1 -1
- janito/agent/rich_tool_handler.py +0 -43
- janito/agent/templates/system_instructions.j2 +0 -38
- janito/agent/tool_auto_imports.py +0 -5
- janito/agent/tools/append_text_to_file.py +0 -41
- janito/agent/tools/py_compile.py +0 -39
- janito/agent/tools/python_exec.py +0 -83
- janito/cli/runner.py +0 -137
- janito/cli_chat_shell/commands.py +0 -204
- janito/render_prompt.py +0 -13
- janito-1.5.2.dist-info/RECORD +0 -66
- {janito-1.5.2.dist-info → janito-1.6.0.dist-info}/entry_points.txt +0 -0
- {janito-1.5.2.dist-info → janito-1.6.0.dist-info}/licenses/LICENSE +0 -0
- {janito-1.5.2.dist-info → janito-1.6.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,22 @@
|
|
1
|
+
def handle_reload(console, *args, **kwargs):
|
2
|
+
from ..load_prompt import load_prompt
|
3
|
+
|
4
|
+
agent = kwargs.get("agent")
|
5
|
+
state = kwargs.get("state")
|
6
|
+
filename = args[0] if args else None
|
7
|
+
try:
|
8
|
+
prompt_text = load_prompt(filename)
|
9
|
+
if hasattr(agent, "system_prompt_template"):
|
10
|
+
agent.system_prompt_template = prompt_text
|
11
|
+
# Update the first system message in the conversation if present
|
12
|
+
messages = state.get("messages") if state else None
|
13
|
+
if messages:
|
14
|
+
for msg in messages:
|
15
|
+
if msg.get("role") == "system":
|
16
|
+
msg["content"] = prompt_text
|
17
|
+
break
|
18
|
+
console.print(
|
19
|
+
f"[bold green]System prompt reloaded from {'default file' if not filename else filename}![/bold green]"
|
20
|
+
)
|
21
|
+
except Exception as e:
|
22
|
+
console.print(f"[bold red]Failed to reload system prompt:[/bold red] {e}")
|
@@ -0,0 +1,29 @@
|
|
1
|
+
from prompt_toolkit.history import InMemoryHistory
|
2
|
+
import os
|
3
|
+
|
4
|
+
|
5
|
+
def handle_reset(console, state, **kwargs):
|
6
|
+
save_path = os.path.join(".janito", "last_conversation.json")
|
7
|
+
|
8
|
+
# Clear in-memory conversation and prompt history
|
9
|
+
state["messages"].clear()
|
10
|
+
state["history_list"].clear()
|
11
|
+
state["mem_history"] = InMemoryHistory()
|
12
|
+
state["last_usage_info"] = None
|
13
|
+
state["last_elapsed"] = None
|
14
|
+
|
15
|
+
# Delete saved conversation file if exists
|
16
|
+
if os.path.exists(save_path):
|
17
|
+
try:
|
18
|
+
os.remove(save_path)
|
19
|
+
console.print(
|
20
|
+
"[bold yellow]Deleted saved conversation history.[/bold yellow]"
|
21
|
+
)
|
22
|
+
except Exception as e:
|
23
|
+
console.print(
|
24
|
+
f"[bold red]Failed to delete saved conversation:[/bold red] {e}"
|
25
|
+
)
|
26
|
+
else:
|
27
|
+
console.print("[bold yellow]No saved conversation to delete.[/bold yellow]")
|
28
|
+
|
29
|
+
console.print("[bold green]Conversation history has been reset.[/bold green]")
|
@@ -0,0 +1,48 @@
|
|
1
|
+
from prompt_toolkit.history import InMemoryHistory
|
2
|
+
import os
|
3
|
+
import json
|
4
|
+
|
5
|
+
|
6
|
+
def handle_continue(console, state, **kwargs):
|
7
|
+
save_path = os.path.join(".janito", "last_conversation.json")
|
8
|
+
if not os.path.exists(save_path):
|
9
|
+
console.print("[bold red]No saved conversation found.[/bold red]")
|
10
|
+
return
|
11
|
+
|
12
|
+
with open(save_path, "r", encoding="utf-8") as f:
|
13
|
+
data = json.load(f)
|
14
|
+
state["messages"].clear()
|
15
|
+
state["messages"].extend(data.get("messages", []))
|
16
|
+
state["history_list"].clear()
|
17
|
+
state["history_list"].extend(data.get("prompts", []))
|
18
|
+
state["mem_history"] = InMemoryHistory()
|
19
|
+
for item in state["history_list"]:
|
20
|
+
state["mem_history"].append_string(item)
|
21
|
+
state["last_usage_info"] = data.get("last_usage_info")
|
22
|
+
console.print("[bold green]Conversation restored from last session.[/bold green]")
|
23
|
+
|
24
|
+
|
25
|
+
def handle_history(console, state, *args, **kwargs):
|
26
|
+
messages = state.get("messages", [])
|
27
|
+
if not args:
|
28
|
+
# Default: last 5 messages
|
29
|
+
start = max(0, len(messages) - 5)
|
30
|
+
end = len(messages)
|
31
|
+
elif len(args) == 1:
|
32
|
+
count = int(args[0])
|
33
|
+
start = max(0, len(messages) - count)
|
34
|
+
end = len(messages)
|
35
|
+
elif len(args) >= 2:
|
36
|
+
start = int(args[0])
|
37
|
+
end = int(args[1]) + 1 # inclusive
|
38
|
+
else:
|
39
|
+
start = 0
|
40
|
+
end = len(messages)
|
41
|
+
|
42
|
+
console.print(
|
43
|
+
f"[bold cyan]Showing messages {start} to {end - 1} (total {len(messages)}):[/bold cyan]"
|
44
|
+
)
|
45
|
+
for idx, msg in enumerate(messages[start:end], start=start):
|
46
|
+
role = msg.get("role", "unknown")
|
47
|
+
content = msg.get("content", "")
|
48
|
+
console.print(f"[bold]{idx} [{role}]:[/bold] {content}")
|
@@ -0,0 +1,12 @@
|
|
1
|
+
import os
|
2
|
+
import sys
|
3
|
+
|
4
|
+
|
5
|
+
def handle_exit(console, **kwargs):
|
6
|
+
console.print("[bold red]Exiting chat mode.[/bold red]")
|
7
|
+
sys.exit(0)
|
8
|
+
|
9
|
+
|
10
|
+
def handle_restart(console, **kwargs):
|
11
|
+
console.print("[bold yellow]Restarting CLI...[/bold yellow]")
|
12
|
+
os.execv(sys.executable, [sys.executable, "-m", "janito"] + sys.argv[1:])
|
@@ -0,0 +1,73 @@
|
|
1
|
+
from janito.agent.runtime_config import runtime_config
|
2
|
+
|
3
|
+
|
4
|
+
def handle_system(console, **kwargs):
|
5
|
+
profile_manager = kwargs.get("profile_manager")
|
6
|
+
prompt = profile_manager.system_prompt_template if profile_manager else None
|
7
|
+
if not prompt and profile_manager:
|
8
|
+
prompt = profile_manager.render_prompt()
|
9
|
+
console.print(f"[bold magenta]System Prompt:[/bold magenta]\n{prompt}")
|
10
|
+
|
11
|
+
|
12
|
+
def handle_role(console, *args, **kwargs):
|
13
|
+
state = kwargs.get("state")
|
14
|
+
profile_manager = kwargs.get("profile_manager")
|
15
|
+
if not args:
|
16
|
+
console.print("[bold red]Usage: /role <new role description>[/bold red]")
|
17
|
+
return
|
18
|
+
new_role = " ".join(args)
|
19
|
+
if profile_manager:
|
20
|
+
profile_manager.set_role(new_role)
|
21
|
+
# Update system message in conversation
|
22
|
+
found = False
|
23
|
+
for msg in state["messages"]:
|
24
|
+
if msg.get("role") == "system":
|
25
|
+
msg["content"] = (
|
26
|
+
profile_manager.system_prompt_template if profile_manager else new_role
|
27
|
+
)
|
28
|
+
found = True
|
29
|
+
break
|
30
|
+
if not found:
|
31
|
+
state["messages"].insert(0, {"role": "system", "content": new_role})
|
32
|
+
# Also store the raw role string
|
33
|
+
if profile_manager:
|
34
|
+
setattr(profile_manager, "role_name", new_role)
|
35
|
+
runtime_config.set("role", new_role)
|
36
|
+
console.print(f"[bold green]System role updated to:[/bold green] {new_role}")
|
37
|
+
|
38
|
+
|
39
|
+
def handle_style(console, *args, **kwargs):
|
40
|
+
"""/style <new_style> - Change the interaction style (e.g., default, technical)"""
|
41
|
+
state = kwargs.get("state")
|
42
|
+
profile_manager = kwargs.get("profile_manager")
|
43
|
+
if not args:
|
44
|
+
current = getattr(profile_manager, "interaction_style", "default")
|
45
|
+
console.print(f"[bold green]Current style:[/bold green] {current}")
|
46
|
+
return
|
47
|
+
new_style = args[0]
|
48
|
+
if profile_manager:
|
49
|
+
profile_manager.set_interaction_style(new_style)
|
50
|
+
# Update system message in conversation
|
51
|
+
found = False
|
52
|
+
for msg in state["messages"]:
|
53
|
+
if msg.get("role") == "system":
|
54
|
+
msg["content"] = (
|
55
|
+
profile_manager.system_prompt_template
|
56
|
+
if profile_manager
|
57
|
+
else msg["content"]
|
58
|
+
)
|
59
|
+
found = True
|
60
|
+
break
|
61
|
+
if not found:
|
62
|
+
state["messages"].insert(
|
63
|
+
0,
|
64
|
+
{
|
65
|
+
"role": "system",
|
66
|
+
"content": (
|
67
|
+
profile_manager.system_prompt_template
|
68
|
+
if profile_manager
|
69
|
+
else new_style
|
70
|
+
),
|
71
|
+
},
|
72
|
+
)
|
73
|
+
console.print(f"[bold green]Interaction style updated to:[/bold green] {new_style}")
|
@@ -0,0 +1,29 @@
|
|
1
|
+
def handle_help(console, **kwargs):
|
2
|
+
console.print(
|
3
|
+
"""
|
4
|
+
[bold green]Available commands:[/bold green]
|
5
|
+
/exit, exit - Exit chat mode
|
6
|
+
/restart - Restart the CLI
|
7
|
+
/help - Show this help message
|
8
|
+
/continue - Restore last saved conversation
|
9
|
+
/reset - Reset conversation history
|
10
|
+
/system - Show the system prompt
|
11
|
+
/role - Change the system role
|
12
|
+
/clear - Clear the terminal screen
|
13
|
+
/multi - Provide multiline input as next message
|
14
|
+
/config - Show or set configuration (see: /config show, /config set local|global key=value)
|
15
|
+
"""
|
16
|
+
)
|
17
|
+
|
18
|
+
|
19
|
+
def handle_clear(console, **kwargs):
|
20
|
+
import os
|
21
|
+
|
22
|
+
os.system("cls" if os.name == "nt" else "clear")
|
23
|
+
|
24
|
+
|
25
|
+
def handle_multi(console, state, **kwargs):
|
26
|
+
console.print(
|
27
|
+
"[bold yellow]Multiline mode activated. Provide or write your text and press Esc + Enter to submit.[/bold yellow]"
|
28
|
+
)
|
29
|
+
state["paste_mode"] = True
|
@@ -2,6 +2,7 @@ from janito.agent.config import local_config, global_config, CONFIG_OPTIONS
|
|
2
2
|
from janito.agent.config_defaults import CONFIG_DEFAULTS
|
3
3
|
from janito.agent.runtime_config import unified_config, runtime_config
|
4
4
|
|
5
|
+
|
5
6
|
def handle_config_shell(console, *args, **kwargs):
|
6
7
|
"""
|
7
8
|
/config show
|
@@ -11,13 +12,22 @@ def handle_config_shell(console, *args, **kwargs):
|
|
11
12
|
/config reset global
|
12
13
|
"""
|
13
14
|
if not args or args[0] not in ("show", "set", "reset"):
|
14
|
-
console.print(
|
15
|
+
console.print(
|
16
|
+
"[bold red]Usage:[/bold red] /config show | /config set local|global key=value | /config reset local|global"
|
17
|
+
)
|
15
18
|
return
|
16
19
|
|
17
20
|
if args[0] == "show":
|
18
21
|
# Show config, unified with CLI
|
19
22
|
from janito.cli._print_config import print_full_config
|
20
|
-
|
23
|
+
|
24
|
+
print_full_config(
|
25
|
+
local_config,
|
26
|
+
global_config,
|
27
|
+
unified_config,
|
28
|
+
CONFIG_DEFAULTS,
|
29
|
+
console=console,
|
30
|
+
)
|
21
31
|
return
|
22
32
|
|
23
33
|
if args[0] == "reset":
|
@@ -26,27 +36,38 @@ def handle_config_shell(console, *args, **kwargs):
|
|
26
36
|
return
|
27
37
|
import os
|
28
38
|
from pathlib import Path
|
39
|
+
|
29
40
|
scope = args[1]
|
30
41
|
if scope == "local":
|
31
|
-
local_path = Path(
|
42
|
+
local_path = Path(".janito/config.json")
|
32
43
|
if local_path.exists():
|
33
44
|
os.remove(local_path)
|
34
45
|
console.print(f"[green]Removed local config file:[/green] {local_path}")
|
35
46
|
else:
|
36
|
-
console.print(
|
47
|
+
console.print(
|
48
|
+
f"[yellow]Local config file does not exist:[/yellow] {local_path}"
|
49
|
+
)
|
37
50
|
elif scope == "global":
|
38
|
-
global_path = Path.home() /
|
51
|
+
global_path = Path.home() / ".janito/config.json"
|
39
52
|
if global_path.exists():
|
40
53
|
os.remove(global_path)
|
41
|
-
console.print(
|
54
|
+
console.print(
|
55
|
+
f"[green]Removed global config file:[/green] {global_path}"
|
56
|
+
)
|
42
57
|
else:
|
43
|
-
console.print(
|
44
|
-
|
58
|
+
console.print(
|
59
|
+
f"[yellow]Global config file does not exist:[/yellow] {global_path}"
|
60
|
+
)
|
61
|
+
console.print(
|
62
|
+
"[bold yellow]Please use /restart for changes to take full effect.[/bold yellow]"
|
63
|
+
)
|
45
64
|
return
|
46
65
|
|
47
66
|
if args[0] == "set":
|
48
67
|
if len(args) < 3 or args[1] not in ("local", "global"):
|
49
|
-
console.print(
|
68
|
+
console.print(
|
69
|
+
"[bold red]Usage:[/bold red] /config set local|global key=value"
|
70
|
+
)
|
50
71
|
return
|
51
72
|
scope = args[1]
|
52
73
|
try:
|
@@ -56,7 +77,9 @@ def handle_config_shell(console, *args, **kwargs):
|
|
56
77
|
return
|
57
78
|
key = key.strip()
|
58
79
|
if key not in CONFIG_OPTIONS and not key.startswith("template."):
|
59
|
-
console.print(
|
80
|
+
console.print(
|
81
|
+
f"[bold red]Invalid config key: '{key}'. Supported keys are: {', '.join(CONFIG_OPTIONS.keys())}"
|
82
|
+
)
|
60
83
|
return
|
61
84
|
val = val.strip()
|
62
85
|
if scope == "local":
|
@@ -64,9 +87,15 @@ def handle_config_shell(console, *args, **kwargs):
|
|
64
87
|
local_config.save()
|
65
88
|
runtime_config.set(key, val)
|
66
89
|
console.print(f"[green]Local config updated:[/green] {key} = {val}")
|
90
|
+
console.print(
|
91
|
+
"[bold yellow]Please use /restart for changes to take full effect.[/bold yellow]"
|
92
|
+
)
|
67
93
|
elif scope == "global":
|
68
94
|
global_config.set(key, val)
|
69
95
|
global_config.save()
|
70
96
|
runtime_config.set(key, val)
|
71
97
|
console.print(f"[green]Global config updated:[/green] {key} = {val}")
|
98
|
+
console.print(
|
99
|
+
"[bold yellow]Please use /restart for changes to take full effect.[/bold yellow]"
|
100
|
+
)
|
72
101
|
return
|
@@ -1,5 +1,6 @@
|
|
1
1
|
import os
|
2
2
|
|
3
|
+
|
3
4
|
def load_prompt(filename=None):
|
4
5
|
"""
|
5
6
|
Load the system prompt from a file. If filename is None, use the default prompt file.
|
@@ -7,9 +8,11 @@ def load_prompt(filename=None):
|
|
7
8
|
"""
|
8
9
|
if filename is None:
|
9
10
|
# Default prompt file path (can be customized)
|
10
|
-
filename = os.path.join(
|
11
|
+
filename = os.path.join(
|
12
|
+
os.path.dirname(__file__), "../agent/templates/system_prompt_template.j2"
|
13
|
+
)
|
11
14
|
filename = os.path.abspath(filename)
|
12
15
|
if not os.path.exists(filename):
|
13
16
|
raise FileNotFoundError(f"Prompt file not found: {filename}")
|
14
|
-
with open(filename,
|
17
|
+
with open(filename, "r", encoding="utf-8") as f:
|
15
18
|
return f.read()
|
@@ -3,65 +3,62 @@ import json
|
|
3
3
|
from datetime import datetime
|
4
4
|
|
5
5
|
|
6
|
-
def load_last_summary(path=
|
6
|
+
def load_last_summary(path=".janito/last_conversation.json"):
|
7
7
|
if not os.path.exists(path):
|
8
8
|
return None
|
9
|
-
with open(path,
|
9
|
+
with open(path, "r", encoding="utf-8") as f:
|
10
10
|
data = json.load(f)
|
11
11
|
return data
|
12
12
|
|
13
13
|
|
14
|
-
|
15
|
-
def load_last_conversation(path='.janito/last_conversation.json'):
|
14
|
+
def load_last_conversation(path=".janito/last_conversation.json"):
|
16
15
|
if not os.path.exists(path):
|
17
16
|
return [], [], None
|
18
|
-
with open(path,
|
17
|
+
with open(path, "r", encoding="utf-8") as f:
|
19
18
|
data = json.load(f)
|
20
|
-
messages = data.get(
|
21
|
-
prompts = data.get(
|
22
|
-
usage = data.get(
|
19
|
+
messages = data.get("messages", [])
|
20
|
+
prompts = data.get("prompts", [])
|
21
|
+
usage = data.get("last_usage_info")
|
23
22
|
return messages, prompts, usage
|
24
23
|
|
25
24
|
|
26
|
-
|
27
|
-
|
25
|
+
def save_conversation(
|
26
|
+
messages, prompts, usage_info=None, path=".janito/last_conversation.json"
|
27
|
+
):
|
28
28
|
os.makedirs(os.path.dirname(path), exist_ok=True)
|
29
|
-
data = {
|
30
|
-
|
31
|
-
'prompts': prompts,
|
32
|
-
'last_usage_info': usage_info
|
33
|
-
}
|
34
|
-
with open(path, 'w', encoding='utf-8') as f:
|
29
|
+
data = {"messages": messages, "prompts": prompts, "last_usage_info": usage_info}
|
30
|
+
with open(path, "w", encoding="utf-8") as f:
|
35
31
|
json.dump(data, f, indent=2)
|
36
32
|
|
37
33
|
|
38
34
|
def load_input_history():
|
39
|
-
history_dir = os.path.join(
|
35
|
+
history_dir = os.path.join(".janito", "input_history")
|
40
36
|
os.makedirs(history_dir, exist_ok=True)
|
41
|
-
today_str = datetime.now().strftime(
|
42
|
-
history_file = os.path.join(history_dir, f
|
37
|
+
today_str = datetime.now().strftime("%y%m%d")
|
38
|
+
history_file = os.path.join(history_dir, f"{today_str}.json")
|
43
39
|
try:
|
44
|
-
with open(history_file,
|
40
|
+
with open(history_file, "r", encoding="utf-8") as f:
|
45
41
|
return json.load(f)
|
46
42
|
except (FileNotFoundError, json.JSONDecodeError):
|
47
43
|
return []
|
48
44
|
|
49
45
|
|
50
46
|
def save_input_history(history_list):
|
51
|
-
history_dir = os.path.join(
|
47
|
+
history_dir = os.path.join(".janito", "input_history")
|
52
48
|
os.makedirs(history_dir, exist_ok=True)
|
53
|
-
today_str = datetime.now().strftime(
|
54
|
-
history_file = os.path.join(history_dir, f
|
55
|
-
with open(history_file,
|
49
|
+
today_str = datetime.now().strftime("%y%m%d")
|
50
|
+
history_file = os.path.join(history_dir, f"{today_str}.json")
|
51
|
+
with open(history_file, "w", encoding="utf-8") as f:
|
56
52
|
json.dump(history_list, f, indent=2)
|
57
53
|
|
58
|
-
|
54
|
+
|
55
|
+
def last_conversation_exists(path=".janito/last_conversation.json"):
|
59
56
|
if not os.path.exists(path):
|
60
57
|
return False
|
61
58
|
try:
|
62
|
-
with open(path,
|
59
|
+
with open(path, "r", encoding="utf-8") as f:
|
63
60
|
data = json.load(f)
|
64
|
-
messages = data.get(
|
61
|
+
messages = data.get("messages", [])
|
65
62
|
return bool(messages)
|
66
63
|
except Exception:
|
67
64
|
return False
|
janito/cli_chat_shell/ui.py
CHANGED
@@ -9,39 +9,58 @@ from .session_manager import last_conversation_exists
|
|
9
9
|
def print_summary(console, data, continue_session):
|
10
10
|
if not data:
|
11
11
|
return
|
12
|
-
msgs = data.get(
|
13
|
-
last_user = next(
|
14
|
-
|
15
|
-
|
16
|
-
|
12
|
+
msgs = data.get("messages", [])
|
13
|
+
last_user = next(
|
14
|
+
(m["content"] for m in reversed(msgs) if m.get("role") == "user"), None
|
15
|
+
)
|
16
|
+
last_assistant = next(
|
17
|
+
(m["content"] for m in reversed(msgs) if m.get("role") == "assistant"), None
|
18
|
+
)
|
19
|
+
usage = data.get("last_usage_info", {})
|
20
|
+
console.print("[bold cyan]Last saved conversation:[/bold cyan]")
|
17
21
|
console.print(f"Messages: {len(msgs)}")
|
18
22
|
if last_user:
|
19
|
-
console.print(
|
23
|
+
console.print(
|
24
|
+
f"Last user: [italic]{last_user[:100]}{'...' if len(last_user)>100 else ''}[/italic]"
|
25
|
+
)
|
20
26
|
if last_assistant:
|
21
|
-
console.print(
|
27
|
+
console.print(
|
28
|
+
f"Last assistant: [italic]{last_assistant[:100]}{'...' if len(last_assistant)>100 else ''}[/italic]"
|
29
|
+
)
|
22
30
|
if usage:
|
23
|
-
ptok = usage.get(
|
24
|
-
ctok = usage.get(
|
31
|
+
ptok = usage.get("prompt_tokens")
|
32
|
+
ctok = usage.get("completion_tokens")
|
25
33
|
tot = (ptok or 0) + (ctok or 0)
|
26
34
|
console.print(f"Tokens - Prompt: {ptok}, Completion: {ctok}, Total: {tot}")
|
27
35
|
# Only print /continue suggestion if a last conversation exists
|
28
36
|
if not continue_session and last_conversation_exists():
|
29
|
-
console.print(
|
37
|
+
console.print(
|
38
|
+
"[bold yellow]Type /continue to restore the last saved conversation.[/bold yellow]"
|
39
|
+
)
|
30
40
|
|
31
41
|
|
32
42
|
def print_welcome(console, version=None, continued=False):
|
33
43
|
version_str = f" (v{version})" if version else ""
|
34
|
-
vanilla_mode = runtime_config.get(
|
44
|
+
vanilla_mode = runtime_config.get("vanilla_mode", False)
|
35
45
|
if vanilla_mode:
|
36
|
-
console.print(
|
46
|
+
console.print(
|
47
|
+
f"[bold magenta]Welcome to Janito{version_str} in [white on magenta]VANILLA MODE[/white on magenta]! Tools, system prompt, and temperature are disabled unless overridden.[/bold magenta]\n[cyan]Quick action: Press F12 to continue. Double-check the suggested action first. 😊[/cyan]"
|
48
|
+
)
|
37
49
|
else:
|
38
|
-
console.print(
|
50
|
+
console.print(
|
51
|
+
f"[bold green]Welcome to Janito{version_str}! Entering chat mode. Type /exit to exit.[/bold green]\n[cyan]Quick action: Press F12 to continue. Double-check the suggested action first. 😊[/cyan]"
|
52
|
+
)
|
39
53
|
# Only print /continue suggestion if a last conversation exists
|
40
|
-
if not continued and last_conversation_exists():
|
41
|
-
console.print("[yellow]To resume your previous conversation, type /continue at any time.[/yellow]")
|
42
54
|
|
43
55
|
|
44
|
-
def get_toolbar_func(
|
56
|
+
def get_toolbar_func(
|
57
|
+
messages_ref,
|
58
|
+
last_usage_info_ref,
|
59
|
+
last_elapsed_ref,
|
60
|
+
model_name=None,
|
61
|
+
role_ref=None,
|
62
|
+
style_ref=None,
|
63
|
+
):
|
45
64
|
def format_tokens(n):
|
46
65
|
if n is None:
|
47
66
|
return "?"
|
@@ -52,12 +71,12 @@ def get_toolbar_func(messages_ref, last_usage_info_ref, last_elapsed_ref, model_
|
|
52
71
|
return str(n)
|
53
72
|
|
54
73
|
def get_toolbar():
|
55
|
-
left = f
|
74
|
+
left = f" Messages: <msg_count>{len(messages_ref())}</msg_count>"
|
56
75
|
usage = last_usage_info_ref()
|
57
76
|
last_elapsed = last_elapsed_ref()
|
58
77
|
if usage:
|
59
|
-
prompt_tokens = usage.get(
|
60
|
-
completion_tokens = usage.get(
|
78
|
+
prompt_tokens = usage.get("prompt_tokens")
|
79
|
+
completion_tokens = usage.get("completion_tokens")
|
61
80
|
total_tokens = (prompt_tokens or 0) + (completion_tokens or 0)
|
62
81
|
speed = None
|
63
82
|
if last_elapsed and last_elapsed > 0:
|
@@ -71,26 +90,34 @@ def get_toolbar_func(messages_ref, last_usage_info_ref, last_elapsed_ref, model_
|
|
71
90
|
left += f", speed=<speed>{speed:.1f}</speed> tokens/sec"
|
72
91
|
|
73
92
|
from prompt_toolkit.application import get_app
|
93
|
+
|
74
94
|
width = get_app().output.get_size().columns
|
75
95
|
model_part = f" Model: <model>{model_name}</model>" if model_name else ""
|
76
96
|
role_part = ""
|
77
|
-
vanilla_mode = runtime_config.get(
|
97
|
+
vanilla_mode = runtime_config.get("vanilla_mode", False)
|
78
98
|
if role_ref and not vanilla_mode:
|
79
99
|
role = role_ref()
|
80
100
|
if role:
|
81
101
|
role_part = f"Role: <b>{role}</b>"
|
102
|
+
style_part = ""
|
103
|
+
if style_ref:
|
104
|
+
style = style_ref()
|
105
|
+
if style:
|
106
|
+
style_part = f"Style: <b>{style}</b>"
|
82
107
|
first_line_parts = []
|
83
108
|
if model_part:
|
84
109
|
first_line_parts.append(model_part)
|
85
110
|
if role_part:
|
86
111
|
first_line_parts.append(role_part)
|
112
|
+
if style_part:
|
113
|
+
first_line_parts.append(style_part)
|
87
114
|
first_line = " | ".join(first_line_parts)
|
88
|
-
help_part = "<b>/help</b> for help | <b>F12</b>:
|
115
|
+
help_part = "<b>/help</b> for help | <b>F12</b>: proceed"
|
89
116
|
total_len = len(left) + len(help_part) + 3 # separators and spaces
|
90
117
|
if first_line:
|
91
118
|
total_len += len(first_line) + 3
|
92
119
|
if total_len < width:
|
93
|
-
padding =
|
120
|
+
padding = " " * (width - total_len)
|
94
121
|
second_line = f"{left}{padding} | {help_part}"
|
95
122
|
else:
|
96
123
|
second_line = f"{left} | {help_part}"
|
@@ -99,39 +126,47 @@ def get_toolbar_func(messages_ref, last_usage_info_ref, last_elapsed_ref, model_
|
|
99
126
|
else:
|
100
127
|
toolbar_text = second_line
|
101
128
|
return HTML(toolbar_text)
|
129
|
+
|
102
130
|
return get_toolbar
|
103
131
|
|
132
|
+
|
104
133
|
def get_prompt_session(get_toolbar_func, mem_history):
|
105
134
|
from prompt_toolkit.key_binding import KeyBindings
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
135
|
+
|
136
|
+
style = Style.from_dict(
|
137
|
+
{
|
138
|
+
"bottom-toolbar": "bg:#333333 #ffffff",
|
139
|
+
"b": "bold",
|
140
|
+
"prompt": "bold bg:#000080 #ffffff",
|
141
|
+
"model": "bold bg:#005f5f #ffffff", # distinct background/foreground
|
142
|
+
"msg_count": "bg:#333333 #ffff00 bold",
|
143
|
+
"tokens_in": "ansicyan bold",
|
144
|
+
"tokens_out": "ansigreen bold",
|
145
|
+
"tokens_total": "ansiyellow bold",
|
146
|
+
"speed": "ansimagenta bold",
|
147
|
+
"right": "bg:#005f5f #ffffff",
|
148
|
+
"input": "bg:#000080 #ffffff",
|
149
|
+
"": "bg:#000080 #ffffff",
|
150
|
+
}
|
151
|
+
)
|
120
152
|
kb = KeyBindings()
|
121
|
-
|
153
|
+
|
154
|
+
@kb.add("f12")
|
122
155
|
def _(event):
|
123
|
-
"""When F12 is pressed, send '
|
156
|
+
"""When F12 is pressed, send 'proceed' as input immediately."""
|
124
157
|
buf = event.app.current_buffer
|
125
|
-
buf.text =
|
158
|
+
buf.text = "proceed"
|
126
159
|
buf.validate_and_handle()
|
160
|
+
|
127
161
|
session = PromptSession(
|
128
162
|
multiline=False,
|
129
163
|
key_bindings=kb,
|
130
164
|
editing_mode=EditingMode.EMACS,
|
131
165
|
bottom_toolbar=get_toolbar_func,
|
132
166
|
style=style,
|
133
|
-
history=mem_history
|
167
|
+
history=mem_history,
|
134
168
|
)
|
135
169
|
return session
|
136
170
|
|
171
|
+
|
137
172
|
# ... rest of the file remains unchanged ...
|