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.
Files changed (85) hide show
  1. janito/__init__.py +1 -1
  2. janito/__main__.py +0 -1
  3. janito/agent/config.py +11 -10
  4. janito/agent/config_defaults.py +3 -2
  5. janito/agent/conversation.py +93 -119
  6. janito/agent/conversation_api.py +98 -0
  7. janito/agent/conversation_exceptions.py +12 -0
  8. janito/agent/conversation_tool_calls.py +22 -0
  9. janito/agent/conversation_ui.py +17 -0
  10. janito/agent/message_handler.py +8 -9
  11. janito/agent/{agent.py → openai_client.py} +48 -16
  12. janito/agent/openai_schema_generator.py +53 -37
  13. janito/agent/profile_manager.py +172 -0
  14. janito/agent/queued_message_handler.py +13 -14
  15. janito/agent/rich_live.py +32 -0
  16. janito/agent/rich_message_handler.py +64 -0
  17. janito/agent/runtime_config.py +6 -1
  18. janito/agent/{tools/tool_base.py → tool_base.py} +15 -8
  19. janito/agent/tool_registry.py +118 -132
  20. janito/agent/tools/__init__.py +41 -2
  21. janito/agent/tools/ask_user.py +43 -33
  22. janito/agent/tools/create_directory.py +18 -16
  23. janito/agent/tools/create_file.py +31 -36
  24. janito/agent/tools/fetch_url.py +23 -19
  25. janito/agent/tools/find_files.py +40 -36
  26. janito/agent/tools/get_file_outline.py +100 -22
  27. janito/agent/tools/get_lines.py +40 -32
  28. janito/agent/tools/gitignore_utils.py +9 -6
  29. janito/agent/tools/move_file.py +22 -13
  30. janito/agent/tools/py_compile_file.py +40 -0
  31. janito/agent/tools/remove_directory.py +34 -24
  32. janito/agent/tools/remove_file.py +22 -20
  33. janito/agent/tools/replace_file.py +51 -0
  34. janito/agent/tools/replace_text_in_file.py +69 -42
  35. janito/agent/tools/rich_live.py +9 -2
  36. janito/agent/tools/run_bash_command.py +155 -107
  37. janito/agent/tools/run_python_command.py +139 -0
  38. janito/agent/tools/search_files.py +51 -34
  39. janito/agent/tools/tools_utils.py +4 -2
  40. janito/agent/tools/utils.py +6 -2
  41. janito/cli/_print_config.py +42 -16
  42. janito/cli/_utils.py +1 -0
  43. janito/cli/arg_parser.py +182 -29
  44. janito/cli/config_commands.py +54 -22
  45. janito/cli/logging_setup.py +9 -3
  46. janito/cli/main.py +11 -10
  47. janito/cli/runner/__init__.py +2 -0
  48. janito/cli/runner/cli_main.py +148 -0
  49. janito/cli/runner/config.py +33 -0
  50. janito/cli/runner/formatting.py +12 -0
  51. janito/cli/runner/scan.py +44 -0
  52. janito/cli_chat_shell/__init__.py +0 -1
  53. janito/cli_chat_shell/chat_loop.py +71 -92
  54. janito/cli_chat_shell/chat_state.py +38 -0
  55. janito/cli_chat_shell/chat_ui.py +43 -0
  56. janito/cli_chat_shell/commands/__init__.py +45 -0
  57. janito/cli_chat_shell/commands/config.py +22 -0
  58. janito/cli_chat_shell/commands/history_reset.py +29 -0
  59. janito/cli_chat_shell/commands/session.py +48 -0
  60. janito/cli_chat_shell/commands/session_control.py +12 -0
  61. janito/cli_chat_shell/commands/system.py +73 -0
  62. janito/cli_chat_shell/commands/utility.py +29 -0
  63. janito/cli_chat_shell/config_shell.py +39 -10
  64. janito/cli_chat_shell/load_prompt.py +5 -2
  65. janito/cli_chat_shell/session_manager.py +24 -27
  66. janito/cli_chat_shell/ui.py +75 -40
  67. janito/rich_utils.py +15 -2
  68. janito/web/__main__.py +10 -2
  69. janito/web/app.py +88 -52
  70. {janito-1.5.2.dist-info → janito-1.6.0.dist-info}/METADATA +76 -11
  71. janito-1.6.0.dist-info/RECORD +81 -0
  72. {janito-1.5.2.dist-info → janito-1.6.0.dist-info}/WHEEL +1 -1
  73. janito/agent/rich_tool_handler.py +0 -43
  74. janito/agent/templates/system_instructions.j2 +0 -38
  75. janito/agent/tool_auto_imports.py +0 -5
  76. janito/agent/tools/append_text_to_file.py +0 -41
  77. janito/agent/tools/py_compile.py +0 -39
  78. janito/agent/tools/python_exec.py +0 -83
  79. janito/cli/runner.py +0 -137
  80. janito/cli_chat_shell/commands.py +0 -204
  81. janito/render_prompt.py +0 -13
  82. janito-1.5.2.dist-info/RECORD +0 -66
  83. {janito-1.5.2.dist-info → janito-1.6.0.dist-info}/entry_points.txt +0 -0
  84. {janito-1.5.2.dist-info → janito-1.6.0.dist-info}/licenses/LICENSE +0 -0
  85. {janito-1.5.2.dist-info → janito-1.6.0.dist-info}/top_level.txt +0 -0
@@ -7,7 +7,9 @@ def setup_verbose_logging(args):
7
7
  httpx_logger = logging.getLogger("httpx")
8
8
  httpx_logger.setLevel(logging.DEBUG)
9
9
  handler = logging.StreamHandler()
10
- handler.setFormatter(logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s"))
10
+ handler.setFormatter(
11
+ logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
12
+ )
11
13
  httpx_logger.addHandler(handler)
12
14
 
13
15
  if args.verbose_http_raw:
@@ -16,12 +18,16 @@ def setup_verbose_logging(args):
16
18
  httpcore_logger = logging.getLogger("httpcore")
17
19
  httpcore_logger.setLevel(logging.DEBUG)
18
20
  handler_core = logging.StreamHandler()
19
- handler_core.setFormatter(logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s"))
21
+ handler_core.setFormatter(
22
+ logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
23
+ )
20
24
  httpcore_logger.addHandler(handler_core)
21
25
 
22
26
  # Re-add handler to httpx logger in case
23
27
  httpx_logger = logging.getLogger("httpx")
24
28
  httpx_logger.setLevel(logging.DEBUG)
25
29
  handler = logging.StreamHandler()
26
- handler.setFormatter(logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s"))
30
+ handler.setFormatter(
31
+ logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
32
+ )
27
33
  httpx_logger.addHandler(handler)
janito/cli/main.py CHANGED
@@ -1,29 +1,29 @@
1
1
  """Main CLI entry point for Janito."""
2
2
 
3
- import janito.agent.tool_auto_imports
4
3
  from janito.cli.arg_parser import create_parser
5
4
  from janito.cli.config_commands import handle_config_commands
6
5
  from janito.cli.logging_setup import setup_verbose_logging
7
6
  from janito.cli.runner import run_cli
8
7
 
8
+ # Ensure all tools are registered at startup
9
+ import janito.agent.tools # noqa: F401
10
+
9
11
 
10
12
  def main():
11
- """Entry point for the Janito CLI.
13
+ """Unified entry point for the Janito CLI and web server."""
14
+ import sys
12
15
 
13
- Parses command-line arguments, handles config commands, sets up logging,
14
- and launches either the CLI chat shell or the web server.
15
- """
16
- # Ensure configs are loaded once at CLI startup
17
16
  from janito.agent.config import local_config, global_config
17
+
18
18
  local_config.load()
19
19
  global_config.load()
20
20
 
21
21
  parser = create_parser()
22
22
  args = parser.parse_args()
23
23
 
24
- from janito.agent.config import CONFIG_OPTIONS # Kept here: avoids circular import at module level
24
+ from janito.agent.config import CONFIG_OPTIONS
25
25
  from janito.agent.config_defaults import CONFIG_DEFAULTS
26
- import sys
26
+
27
27
  if getattr(args, "help_config", False):
28
28
  print("Available configuration options:\n")
29
29
  for key, desc in CONFIG_OPTIONS.items():
@@ -33,8 +33,9 @@ def main():
33
33
 
34
34
  handle_config_commands(args)
35
35
  setup_verbose_logging(args)
36
- if getattr(args, 'web', False):
36
+ if getattr(args, "web", False):
37
37
  import subprocess # Only needed if launching web
38
- subprocess.run(['python', '-m', 'janito.web'])
38
+
39
+ subprocess.run([sys.executable, "-m", "janito.web"])
39
40
  else:
40
41
  run_cli(args)
@@ -0,0 +1,2 @@
1
+ from .cli_main import run_cli
2
+ from .formatting import format_tokens
@@ -0,0 +1,148 @@
1
+ import sys
2
+ import os
3
+ from rich.console import Console
4
+ from janito.agent.profile_manager import AgentProfileManager
5
+ from janito.agent.runtime_config import unified_config, runtime_config
6
+ from janito.agent.config import get_api_key
7
+ from janito import __version__
8
+ from .formatting import format_tokens
9
+ from .scan import scan_project
10
+ from .config import get_system_prompt_template
11
+ from janito.agent.conversation_exceptions import (
12
+ MaxRoundsExceededError,
13
+ EmptyResponseError,
14
+ ProviderError,
15
+ )
16
+
17
+ def run_cli(args):
18
+ if args.version:
19
+ print(f"janito version {__version__}")
20
+ sys.exit(0)
21
+
22
+ # --scan: auto-detect tech/skills and save to .janito/tech.txt
23
+ if getattr(args, "scan", False):
24
+ scan_project()
25
+ sys.exit(0)
26
+
27
+ # Check for .janito/tech.txt and print a tip if missing
28
+ tech_txt_path = os.path.join(".janito", "tech.txt")
29
+ if not os.path.exists(tech_txt_path):
30
+ print("⚠️ No tech.txt found in .janito.")
31
+ print(
32
+ "💡 Tip: Run with --scan first to auto-detect project tech/skills and improve results."
33
+ )
34
+
35
+ role = args.role or unified_config.get("role", "software engineer")
36
+
37
+ # Ensure runtime_config is updated so chat shell sees the role
38
+ if args.role:
39
+ runtime_config.set("role", args.role)
40
+
41
+ # Set runtime_config['model'] if --model is provided (highest priority, session only)
42
+ if getattr(args, "model", None):
43
+ runtime_config.set("model", args.model)
44
+
45
+ # Set runtime_config['max_tools'] if --max-tools is provided
46
+ if getattr(args, "max_tools", None) is not None:
47
+ runtime_config.set("max_tools", args.max_tools)
48
+
49
+ # Set trust-tools mode if enabled
50
+ if getattr(args, "trust_tools", False):
51
+ runtime_config.set("trust_tools", True)
52
+
53
+ # Get system prompt template (instructions/config logic)
54
+ system_prompt_template = get_system_prompt_template(args, role)
55
+
56
+ if args.show_system:
57
+ api_key = get_api_key()
58
+ model = unified_config.get("model")
59
+ print("Model:", model)
60
+ print("Parameters: {}")
61
+ print(
62
+ "System Prompt Template:",
63
+ system_prompt_template or "(default system prompt template not provided)",
64
+ )
65
+ sys.exit(0)
66
+
67
+ api_key = get_api_key()
68
+ model = unified_config.get("model")
69
+ base_url = unified_config.get("base_url", "https://openrouter.ai/api/v1")
70
+ azure_openai_api_version = unified_config.get(
71
+ "azure_openai_api_version", "2023-05-15"
72
+ )
73
+ # Handle vanilla mode
74
+ vanilla_mode = getattr(args, "vanilla", False)
75
+ if vanilla_mode:
76
+ runtime_config.set("vanilla_mode", True)
77
+ system_prompt_template = None
78
+ runtime_config.set("system_prompt_template", None)
79
+ if args.temperature is None:
80
+ runtime_config.set("temperature", None)
81
+ else:
82
+ runtime_config.set("vanilla_mode", False)
83
+
84
+ interaction_style = getattr(args, "style", None) or unified_config.get(
85
+ "interaction_style", "default"
86
+ )
87
+
88
+ if not getattr(args, "prompt", None):
89
+ interaction_mode = "chat"
90
+ else:
91
+ interaction_mode = "prompt"
92
+
93
+ profile_manager = AgentProfileManager(
94
+ api_key=api_key,
95
+ model=model,
96
+ role=role,
97
+ interaction_style=interaction_style,
98
+ interaction_mode=interaction_mode,
99
+ verbose_tools=args.verbose_tools,
100
+ base_url=base_url,
101
+ azure_openai_api_version=azure_openai_api_version,
102
+ use_azure_openai=unified_config.get("use_azure_openai", False),
103
+ )
104
+ profile_manager.refresh_prompt()
105
+
106
+ if args.max_tokens is not None:
107
+ runtime_config.set("max_tokens", args.max_tokens)
108
+
109
+ if not getattr(args, "prompt", None):
110
+ from janito.cli_chat_shell.chat_loop import start_chat_shell
111
+
112
+ start_chat_shell(
113
+ profile_manager, continue_session=getattr(args, "continue_session", False)
114
+ )
115
+ sys.exit(0)
116
+
117
+ prompt = args.prompt
118
+ console = Console()
119
+ from janito.agent.rich_message_handler import RichMessageHandler
120
+
121
+ message_handler = RichMessageHandler()
122
+ messages = []
123
+ if profile_manager.system_prompt_template:
124
+ messages.append(
125
+ {"role": "system", "content": profile_manager.system_prompt_template}
126
+ )
127
+ messages.append({"role": "user", "content": prompt})
128
+ try:
129
+ try:
130
+ max_rounds = runtime_config.get("max_rounds", 50)
131
+ profile_manager.agent.handle_conversation(
132
+ messages,
133
+ message_handler=message_handler,
134
+ spinner=True,
135
+ max_rounds=max_rounds,
136
+ verbose_response=getattr(args, "verbose_response", False),
137
+ verbose_events=getattr(args, "verbose_events", False),
138
+ stream=getattr(args, "stream", False),
139
+ verbose_stream=getattr(args, "verbose_stream", False),
140
+ )
141
+ except MaxRoundsExceededError:
142
+ console.print("[red]Max conversation rounds exceeded.[/red]")
143
+ except ProviderError as e:
144
+ console.print(f"[red]Provider error:[/red] {e}")
145
+ except EmptyResponseError as e:
146
+ console.print(f"[red]Error:[/red] {e}")
147
+ except KeyboardInterrupt:
148
+ console.print("[yellow]Interrupted by user.[/yellow]")
@@ -0,0 +1,33 @@
1
+ import os
2
+ from janito.agent.profile_manager import AgentProfileManager
3
+ from janito.agent.runtime_config import unified_config, runtime_config
4
+ from janito.agent.config import get_api_key
5
+
6
+ def get_system_prompt_template(args, role):
7
+ system_prompt_template = None
8
+ if getattr(args, "system_prompt_template_file", None):
9
+ with open(args.system_prompt_template_file, "r", encoding="utf-8") as f:
10
+ system_prompt_template = f.read()
11
+ runtime_config.set(
12
+ "system_prompt_template_file", args.system_prompt_template_file
13
+ )
14
+ else:
15
+ system_prompt_template = getattr(
16
+ args, "system_prompt_template", None
17
+ ) or unified_config.get("system_prompt_template")
18
+ if getattr(args, "system_prompt_template", None):
19
+ runtime_config.set("system_prompt_template", system_prompt_template)
20
+ if system_prompt_template is None:
21
+ profile_manager = AgentProfileManager(
22
+ api_key=get_api_key(),
23
+ model=unified_config.get("model"),
24
+ role=role,
25
+ interaction_style=unified_config.get("interaction_style", "default"),
26
+ interaction_mode=unified_config.get("interaction_mode", "prompt"),
27
+ verbose_tools=unified_config.get("verbose_tools", False),
28
+ base_url=unified_config.get("base_url", None),
29
+ azure_openai_api_version=unified_config.get("azure_openai_api_version", None),
30
+ use_azure_openai=unified_config.get("use_azure_openai", False),
31
+ )
32
+ system_prompt_template = profile_manager.render_prompt()
33
+ return system_prompt_template
@@ -0,0 +1,12 @@
1
+ def format_tokens(n):
2
+ if n is None:
3
+ return "?"
4
+ try:
5
+ n = int(n)
6
+ except (TypeError, ValueError):
7
+ return str(n)
8
+ if n >= 1_000_000:
9
+ return f"{n/1_000_000:.1f}m"
10
+ if n >= 1_000:
11
+ return f"{n/1_000:.1f}k"
12
+ return str(n)
@@ -0,0 +1,44 @@
1
+ import os
2
+ from janito.agent.openai_client import Agent
3
+ from janito.agent.runtime_config import unified_config
4
+ from janito.agent.config import get_api_key
5
+
6
+ def scan_project():
7
+ prompt_path = os.path.abspath(os.path.join(
8
+ os.path.dirname(__file__),
9
+ "..", "..", "agent", "templates", "detect_tech_prompt.j2"
10
+ ))
11
+ with open(prompt_path, "r", encoding="utf-8") as f:
12
+ detect_prompt = f.read()
13
+ api_key = get_api_key()
14
+ model = unified_config.get("model")
15
+ base_url = unified_config.get("base_url", "https://openrouter.ai/api/v1")
16
+ azure_openai_api_version = unified_config.get("azure_openai_api_version", "2023-05-15")
17
+ use_azure_openai = unified_config.get("use_azure_openai", False)
18
+ agent = Agent(
19
+ api_key=api_key,
20
+ model=model,
21
+ system_prompt_template=detect_prompt,
22
+ verbose_tools=True,
23
+ base_url=base_url,
24
+ azure_openai_api_version=azure_openai_api_version,
25
+ use_azure_openai=use_azure_openai,
26
+ )
27
+ from janito.agent.rich_message_handler import RichMessageHandler
28
+ message_handler = RichMessageHandler()
29
+ messages = [{"role": "system", "content": detect_prompt}]
30
+ print("🔍 Scanning project for relevant tech/skills...")
31
+ result = agent.chat(
32
+ messages,
33
+ message_handler=message_handler,
34
+ spinner=True,
35
+ max_rounds=10,
36
+ verbose_response=False,
37
+ verbose_events=False,
38
+ stream=False,
39
+ )
40
+ os.makedirs(".janito", exist_ok=True)
41
+ tech_txt = os.path.join(".janito", "tech.txt")
42
+ with open(tech_txt, "w", encoding="utf-8") as f:
43
+ f.write(result["content"].strip() + "\n")
44
+ print(f"✅ Tech/skills detected and saved to {tech_txt}")
@@ -1 +0,0 @@
1
- from .chat_loop import start_chat_shell
@@ -1,93 +1,47 @@
1
- from janito.agent.rich_tool_handler import MessageHandler
2
- from prompt_toolkit.history import InMemoryHistory
3
- from .session_manager import load_last_conversation, load_input_history
4
- from .ui import print_welcome, get_toolbar_func, get_prompt_session
5
- from janito import __version__
1
+ from janito.agent.rich_message_handler import RichMessageHandler
2
+ from .chat_state import load_chat_state, save_chat_state
3
+ from .chat_ui import setup_prompt_session, print_welcome_message
6
4
  from .commands import handle_command
7
- from janito.agent.config import effective_config
8
- from janito.agent.runtime_config import runtime_config
9
- from janito.agent.conversation import EmptyResponseError, ProviderError
5
+ from janito.agent.conversation_exceptions import EmptyResponseError, ProviderError
10
6
 
11
7
 
12
- def start_chat_shell(agent, continue_session=False, max_rounds=50):
13
- message_handler = MessageHandler()
8
+ def start_chat_shell(profile_manager, continue_session=False, max_rounds=50):
9
+ agent = profile_manager.agent
10
+ message_handler = RichMessageHandler()
14
11
  console = message_handler.console
15
12
 
16
- # Load input history
17
- history_list = load_input_history()
18
- mem_history = InMemoryHistory()
19
- for item in history_list:
20
- mem_history.append_string(item)
21
-
22
- # Initialize chat state variables
23
- messages = []
24
- last_usage_info = None
25
- last_elapsed = None
26
-
27
- state = {
28
- 'messages': messages,
29
- 'mem_history': mem_history,
30
- 'history_list': history_list,
31
- 'last_usage_info': last_usage_info,
32
- 'last_elapsed': last_elapsed,
33
- }
34
-
35
- # Restore conversation if requested
36
- if continue_session:
37
- msgs, prompts, usage = load_last_conversation()
38
- messages = msgs
39
- last_usage_info = usage
40
- mem_history = InMemoryHistory()
41
- for item in prompts:
42
- mem_history.append_string(item)
43
- # update state dict with restored data
44
-
45
- state['messages'] = messages
46
- state['last_usage_info'] = last_usage_info
47
- state['mem_history'] = mem_history
48
- message_handler.handle_message({'type': 'success', 'message': 'Restored last saved conversation.'})
13
+ # Load state
14
+ state = load_chat_state(continue_session)
15
+ messages = state["messages"]
16
+ mem_history = state["mem_history"]
17
+ last_usage_info = state["last_usage_info"]
18
+ last_elapsed = state["last_elapsed"]
49
19
 
50
20
  # Add system prompt if needed
51
- if agent.system_prompt and not any(m.get('role') == 'system' for m in messages):
52
- messages.insert(0, {"role": "system", "content": agent.system_prompt})
53
-
54
- print_welcome(console, version=__version__, continued=continue_session)
55
-
56
- # Toolbar references
57
- def get_messages():
58
- return messages
59
-
60
- def get_usage():
61
- return last_usage_info
62
-
63
- def get_elapsed():
64
- return last_elapsed
21
+ if profile_manager.system_prompt_template and not any(
22
+ m.get("role") == "system" for m in messages
23
+ ):
24
+ messages.insert(0, {"role": "system", "content": agent.system_prompt_template})
65
25
 
66
- # Try to get model name from agent
67
- model_name = getattr(agent, 'model', None)
26
+ print_welcome_message(console, continued=continue_session)
68
27
 
69
- session = get_prompt_session(
70
- get_toolbar_func(
71
- get_messages, get_usage, get_elapsed, model_name=model_name,
72
- role_ref=lambda: ("*using custom system prompt*" if (runtime_config.get('system_prompt') or runtime_config.get('system_prompt_file')) else (runtime_config.get('role') or effective_config.get('role')))
73
- ),
74
- mem_history
28
+ session = setup_prompt_session(
29
+ messages, last_usage_info, last_elapsed, mem_history, profile_manager, agent
75
30
  )
76
31
 
77
-
78
- # Main chat loop
79
32
  while True:
80
- # max_rounds is now available for use in the chat loop
81
-
82
33
  try:
83
- if state.get('paste_mode'):
84
- console.print('')
85
- user_input = session.prompt('Multiline> ', multiline=True)
34
+ if state.get("paste_mode"):
35
+ console.print("")
36
+ user_input = session.prompt("Multiline> ", multiline=True)
86
37
  was_paste_mode = True
87
- state['paste_mode'] = False
38
+ state["paste_mode"] = False
88
39
  else:
89
40
  from prompt_toolkit.formatted_text import HTML
90
- user_input = session.prompt(HTML('<prompt>💬 </prompt>'), multiline=False)
41
+
42
+ user_input = session.prompt(
43
+ HTML("<prompt>💬 </prompt>"), multiline=False
44
+ )
91
45
  was_paste_mode = False
92
46
  except EOFError:
93
47
  console.print("\n[bold red]Exiting...[/bold red]")
@@ -95,19 +49,39 @@ def start_chat_shell(agent, continue_session=False, max_rounds=50):
95
49
  except KeyboardInterrupt:
96
50
  console.print() # Move to next line
97
51
  try:
98
- confirm = input("Do you really want to exit? (y/n): ").strip().lower()
52
+ confirm = (
53
+ session.prompt(
54
+ HTML("<prompt>Do you really want to exit? (y/n): </prompt>")
55
+ )
56
+ .strip()
57
+ .lower()
58
+ )
99
59
  except KeyboardInterrupt:
100
- message_handler.handle_message({'type': 'error', 'message': 'Exiting...'})
60
+ message_handler.handle_message(
61
+ {"type": "error", "message": "Exiting..."}
62
+ )
101
63
  break
102
- if confirm == 'y':
103
- message_handler.handle_message({'type': 'error', 'message': 'Exiting...'})
64
+ if confirm == "y":
65
+ message_handler.handle_message(
66
+ {"type": "error", "message": "Exiting..."}
67
+ )
104
68
  break
105
69
  else:
106
70
  continue
107
71
 
108
- if not was_paste_mode and user_input.strip().startswith('/'):
109
- result = handle_command(user_input.strip(), console, agent=agent, messages=messages, mem_history=mem_history, state=state)
110
- if result == 'exit':
72
+ cmd_input = user_input.strip().lower()
73
+ if not was_paste_mode and (cmd_input.startswith("/") or cmd_input == "exit"):
74
+ # Treat both '/exit' and 'exit' as commands
75
+ result = handle_command(
76
+ user_input.strip(),
77
+ console,
78
+ profile_manager=profile_manager,
79
+ agent=agent,
80
+ messages=messages,
81
+ mem_history=mem_history,
82
+ state=state,
83
+ )
84
+ if result == "exit":
111
85
  break
112
86
  continue
113
87
 
@@ -117,29 +91,34 @@ def start_chat_shell(agent, continue_session=False, max_rounds=50):
117
91
  mem_history.append_string(user_input)
118
92
  messages.append({"role": "user", "content": user_input})
119
93
 
120
- start_time = None
121
94
  import time
122
- start_time = time.time()
123
95
 
96
+ start_time = time.time()
124
97
 
125
98
  try:
126
- response = agent.chat(messages, spinner=True, max_rounds=max_rounds, message_handler=message_handler)
99
+ response = profile_manager.agent.handle_conversation(
100
+ messages,
101
+ max_rounds=max_rounds,
102
+ message_handler=message_handler,
103
+ spinner=True,
104
+ )
127
105
  except KeyboardInterrupt:
128
- message_handler.handle_message({'type': 'info', 'message': 'Request interrupted. Returning to prompt.'})
106
+ message_handler.handle_message(
107
+ {"type": "info", "message": "Request interrupted. Returning to prompt."}
108
+ )
129
109
  continue
130
110
  except ProviderError as e:
131
- message_handler.handle_message({'type': 'error', 'message': f'Provider error: {e}'})
111
+ message_handler.handle_message(
112
+ {"type": "error", "message": f"Provider error: {e}"}
113
+ )
132
114
  continue
133
115
  except EmptyResponseError as e:
134
- message_handler.handle_message({'type': 'error', 'message': f'Error: {e}'})
116
+ message_handler.handle_message({"type": "error", "message": f"Error: {e}"})
135
117
  continue
136
118
  last_elapsed = time.time() - start_time
137
119
 
138
- usage = response.get('usage')
120
+ usage = response.get("usage")
139
121
  last_usage_info = usage
140
122
 
141
123
  # Save conversation and input history
142
- from .session_manager import save_conversation, save_input_history
143
- prompts = [h for h in mem_history.get_strings()]
144
- save_conversation(messages, prompts, last_usage_info)
145
- save_input_history(prompts)
124
+ save_chat_state(messages, mem_history, last_usage_info)
@@ -0,0 +1,38 @@
1
+ from .session_manager import (
2
+ load_last_conversation,
3
+ load_input_history,
4
+ save_conversation,
5
+ save_input_history,
6
+ )
7
+ from prompt_toolkit.history import InMemoryHistory
8
+
9
+
10
+ def load_chat_state(continue_session: bool):
11
+ messages = []
12
+ last_usage_info = None
13
+ last_elapsed = None
14
+ history_list = load_input_history()
15
+ mem_history = InMemoryHistory()
16
+ for item in history_list:
17
+ mem_history.append_string(item)
18
+ if continue_session:
19
+ msgs, prompts, usage = load_last_conversation()
20
+ messages = msgs
21
+ last_usage_info = usage
22
+ mem_history = InMemoryHistory()
23
+ for item in prompts:
24
+ mem_history.append_string(item)
25
+ state = {
26
+ "messages": messages,
27
+ "mem_history": mem_history,
28
+ "history_list": history_list,
29
+ "last_usage_info": last_usage_info,
30
+ "last_elapsed": last_elapsed,
31
+ }
32
+ return state
33
+
34
+
35
+ def save_chat_state(messages, mem_history, last_usage_info):
36
+ prompts = [h for h in mem_history.get_strings()]
37
+ save_conversation(messages, prompts, last_usage_info)
38
+ save_input_history(prompts)
@@ -0,0 +1,43 @@
1
+ from .ui import print_welcome, get_toolbar_func, get_prompt_session
2
+ from janito import __version__
3
+ from janito.agent.config import effective_config
4
+ from janito.agent.runtime_config import runtime_config
5
+
6
+
7
+ def setup_prompt_session(
8
+ messages, last_usage_info, last_elapsed, mem_history, profile_manager, agent
9
+ ):
10
+ model_name = getattr(agent, "model", None)
11
+
12
+ def get_messages():
13
+ return messages
14
+
15
+ def get_usage():
16
+ return last_usage_info
17
+
18
+ def get_elapsed():
19
+ return last_elapsed
20
+
21
+ session = get_prompt_session(
22
+ get_toolbar_func(
23
+ get_messages,
24
+ get_usage,
25
+ get_elapsed,
26
+ model_name=model_name,
27
+ role_ref=lambda: (
28
+ "*using custom system prompt*"
29
+ if (
30
+ runtime_config.get("system_prompt_template")
31
+ or runtime_config.get("system_prompt_template_file")
32
+ )
33
+ else (runtime_config.get("role") or effective_config.get("role"))
34
+ ),
35
+ style_ref=lambda: getattr(profile_manager, "interaction_style", "default"),
36
+ ),
37
+ mem_history,
38
+ )
39
+ return session
40
+
41
+
42
+ def print_welcome_message(console, continued):
43
+ print_welcome(console, version=__version__, continued=continued)
@@ -0,0 +1,45 @@
1
+ from .session import handle_continue, handle_history
2
+ from .system import handle_system, handle_role, handle_style
3
+ from .session_control import handle_exit, handle_restart
4
+ from .utility import handle_help, handle_clear, handle_multi
5
+ from .config import handle_reload
6
+ from .history_reset import handle_reset
7
+ from ..config_shell import handle_config_shell
8
+ from janito.agent.runtime_config import runtime_config
9
+
10
+ COMMAND_HANDLERS = {
11
+ "/history": handle_history,
12
+ "/continue": handle_continue,
13
+ "/exit": handle_exit,
14
+ "exit": handle_exit,
15
+ "/restart": handle_restart,
16
+ "/help": handle_help,
17
+ "/multi": handle_multi,
18
+ "/system": handle_system,
19
+ }
20
+
21
+ if not runtime_config.get("vanilla_mode", False):
22
+ COMMAND_HANDLERS["/role"] = handle_role
23
+
24
+ COMMAND_HANDLERS.update(
25
+ {
26
+ "/clear": handle_clear,
27
+ "/reset": handle_reset,
28
+ "/config": handle_config_shell,
29
+ "/reload": handle_reload,
30
+ "/style": handle_style,
31
+ }
32
+ )
33
+
34
+
35
+ def handle_command(command, console, **kwargs):
36
+ parts = command.strip().split()
37
+ cmd = parts[0]
38
+ args = parts[1:]
39
+ handler = COMMAND_HANDLERS.get(cmd)
40
+ if handler:
41
+ return handler(console, *args, **kwargs)
42
+ console.print(
43
+ f"[bold red]Invalid command: {cmd}. Type /help for a list of commands.[/bold red]"
44
+ )
45
+ return None