janito 1.9.0__py3-none-any.whl → 1.10.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 (81) hide show
  1. janito/__init__.py +1 -1
  2. janito/agent/api_exceptions.py +4 -0
  3. janito/agent/config.py +1 -1
  4. janito/agent/config_defaults.py +2 -26
  5. janito/agent/conversation.py +163 -122
  6. janito/agent/conversation_api.py +149 -159
  7. janito/agent/{conversation_history.py → llm_conversation_history.py} +18 -1
  8. janito/agent/openai_client.py +38 -23
  9. janito/agent/openai_schema_generator.py +162 -129
  10. janito/agent/platform_discovery.py +134 -77
  11. janito/agent/profile_manager.py +5 -5
  12. janito/agent/rich_message_handler.py +80 -31
  13. janito/agent/templates/profiles/system_prompt_template_base.txt.j2 +5 -4
  14. janito/agent/test_openai_schema_generator.py +93 -0
  15. janito/agent/tool_base.py +7 -2
  16. janito/agent/tool_executor.py +54 -49
  17. janito/agent/tool_registry.py +5 -2
  18. janito/agent/tool_use_tracker.py +26 -5
  19. janito/agent/tools/__init__.py +6 -3
  20. janito/agent/tools/create_directory.py +3 -1
  21. janito/agent/tools/create_file.py +7 -1
  22. janito/agent/tools/fetch_url.py +40 -3
  23. janito/agent/tools/find_files.py +3 -1
  24. janito/agent/tools/get_file_outline/core.py +6 -7
  25. janito/agent/tools/get_file_outline/search_outline.py +3 -1
  26. janito/agent/tools/get_lines.py +7 -2
  27. janito/agent/tools/move_file.py +3 -1
  28. janito/agent/tools/present_choices.py +3 -1
  29. janito/agent/tools/python_command_runner.py +150 -0
  30. janito/agent/tools/python_file_runner.py +148 -0
  31. janito/agent/tools/python_stdin_runner.py +154 -0
  32. janito/agent/tools/remove_directory.py +3 -1
  33. janito/agent/tools/remove_file.py +5 -1
  34. janito/agent/tools/replace_file.py +12 -2
  35. janito/agent/tools/replace_text_in_file.py +4 -2
  36. janito/agent/tools/run_bash_command.py +30 -69
  37. janito/agent/tools/run_powershell_command.py +134 -105
  38. janito/agent/tools/search_text.py +172 -122
  39. janito/agent/tools/validate_file_syntax/core.py +3 -1
  40. janito/agent/tools_utils/action_type.py +7 -0
  41. janito/agent/tools_utils/dir_walk_utils.py +3 -2
  42. janito/agent/tools_utils/formatting.py +47 -21
  43. janito/agent/tools_utils/gitignore_utils.py +66 -40
  44. janito/agent/tools_utils/test_gitignore_utils.py +46 -0
  45. janito/cli/_print_config.py +63 -61
  46. janito/cli/arg_parser.py +13 -12
  47. janito/cli/cli_main.py +137 -147
  48. janito/cli/main.py +152 -174
  49. janito/cli/one_shot.py +40 -26
  50. janito/i18n/__init__.py +1 -1
  51. janito/rich_utils.py +46 -8
  52. janito/shell/commands/__init__.py +2 -4
  53. janito/shell/commands/conversation_restart.py +3 -1
  54. janito/shell/commands/edit.py +3 -0
  55. janito/shell/commands/history_view.py +3 -3
  56. janito/shell/commands/lang.py +3 -0
  57. janito/shell/commands/livelogs.py +5 -3
  58. janito/shell/commands/prompt.py +6 -0
  59. janito/shell/commands/session.py +3 -0
  60. janito/shell/commands/session_control.py +3 -0
  61. janito/shell/commands/termweb_log.py +8 -0
  62. janito/shell/commands/tools.py +3 -0
  63. janito/shell/commands/track.py +36 -0
  64. janito/shell/commands/utility.py +13 -18
  65. janito/shell/commands/verbose.py +3 -4
  66. janito/shell/input_history.py +62 -0
  67. janito/shell/main.py +117 -181
  68. janito/shell/session/manager.py +0 -21
  69. janito/shell/ui/interactive.py +0 -2
  70. janito/termweb/static/editor.css +0 -4
  71. janito/tests/test_rich_utils.py +44 -0
  72. janito/web/app.py +0 -75
  73. {janito-1.9.0.dist-info → janito-1.10.0.dist-info}/METADATA +61 -42
  74. {janito-1.9.0.dist-info → janito-1.10.0.dist-info}/RECORD +78 -71
  75. {janito-1.9.0.dist-info → janito-1.10.0.dist-info}/WHEEL +1 -1
  76. janito/agent/providers.py +0 -77
  77. janito/agent/tools/run_python_command.py +0 -161
  78. janito/shell/commands/sum.py +0 -49
  79. {janito-1.9.0.dist-info → janito-1.10.0.dist-info}/entry_points.txt +0 -0
  80. {janito-1.9.0.dist-info → janito-1.10.0.dist-info}/licenses/LICENSE +0 -0
  81. {janito-1.9.0.dist-info → janito-1.10.0.dist-info}/top_level.txt +0 -0
janito/agent/providers.py DELETED
@@ -1,77 +0,0 @@
1
- """Providers module: defines LLM provider interfaces and implementations."""
2
-
3
- from abc import ABC, abstractmethod
4
-
5
-
6
- class Provider(ABC):
7
- """Abstract base class for LLM providers."""
8
-
9
- def __init__(self, config: dict):
10
- self.config = config
11
-
12
- @abstractmethod
13
- def create_client(self):
14
- """Instantiate and return the provider-specific client."""
15
- pass
16
-
17
- @abstractmethod
18
- def get_default_model(self) -> str:
19
- """Return the default model for this provider."""
20
- pass
21
-
22
-
23
- class OpenAIProvider(Provider):
24
- def create_client(self):
25
- from openai import OpenAI
26
-
27
- return OpenAI(
28
- base_url=self.config.get("base_url", "https://api.openai.com/v1"),
29
- api_key=self.config["api_key"],
30
- )
31
-
32
- def get_default_model(self) -> str:
33
- return self.config.get("default_model", "gpt-3.5-turbo")
34
-
35
-
36
- class AzureAIProvider(Provider):
37
- def create_client(self):
38
- from openai import AzureOpenAI
39
-
40
- return AzureOpenAI(
41
- api_key=self.config["api_key"],
42
- azure_endpoint=self.config["base_url"],
43
- api_version=self.config.get("api_version", "2023-05-15"),
44
- )
45
-
46
- def get_default_model(self) -> str:
47
- return self.config.get("default_model", "gpt-35-turbo")
48
-
49
-
50
- class OpenrouterAIProvider(Provider):
51
- def create_client(self):
52
- from openai import OpenAI
53
-
54
- return OpenAI(
55
- base_url=self.config.get("base_url", "https://openrouter.ai/api/v1"),
56
- api_key=self.config["api_key"],
57
- )
58
-
59
- def get_default_model(self) -> str:
60
- return self.config.get("default_model", "openrouter/cognitive")
61
-
62
-
63
- class FireworksAIProvider(Provider):
64
- def create_client(self):
65
- from openai import OpenAI
66
-
67
- return OpenAI(
68
- base_url=self.config.get(
69
- "base_url", "https://api.fireworks.ai/inference/v1"
70
- ),
71
- api_key=self.config["api_key"],
72
- )
73
-
74
- def get_default_model(self) -> str:
75
- return self.config.get(
76
- "default_model", "accounts/fireworks/models/firefunction-v1"
77
- )
@@ -1,161 +0,0 @@
1
- import subprocess
2
- import tempfile
3
- import sys
4
- import os
5
- from janito.agent.tool_base import ToolBase
6
- from janito.agent.tool_registry import register_tool
7
- from janito.i18n import tr
8
-
9
-
10
- @register_tool(name="run_python_command")
11
- class RunPythonCommandTool(ToolBase):
12
- """
13
- Tool to execute Python code in a subprocess and capture output.
14
- Args:
15
- code (str): The Python code to execute.
16
- timeout (int, optional): Timeout in seconds for the command. Defaults to 60.
17
- require_confirmation (bool, optional): If True, require user confirmation before running. Defaults to False.
18
- interactive (bool, optional): If True, warns that the command may require user interaction. Defaults to False.
19
- Returns:
20
- str: File paths and line counts for stdout and stderr, or direct output if small enough.
21
- """
22
-
23
- def run(
24
- self,
25
- code: str,
26
- timeout: int = 60,
27
- require_confirmation: bool = False,
28
- interactive: bool = False,
29
- ) -> str:
30
- if not code.strip():
31
- self.report_warning(tr("ℹ️ Empty code provided."))
32
- return tr("Warning: Empty code provided. Operation skipped.")
33
- self.report_info(tr("🐍 Running Python code: ...\n{code}\n", code=code))
34
- if interactive:
35
- self.report_warning(
36
- tr(
37
- "⚠️ Warning: This code might be interactive, require user input, and might hang."
38
- )
39
- )
40
- sys.stdout.flush()
41
- if require_confirmation:
42
- confirmed = self.confirm_action(
43
- tr("Do you want to execute this Python code?")
44
- )
45
- if not confirmed:
46
- self.report_warning(tr("⚠️ Execution cancelled by user."))
47
- return tr("Execution cancelled by user.")
48
- try:
49
- with (
50
- tempfile.NamedTemporaryFile(
51
- mode="w+",
52
- suffix=".py",
53
- prefix="run_python_",
54
- delete=False,
55
- encoding="utf-8",
56
- ) as code_file,
57
- tempfile.NamedTemporaryFile(
58
- mode="w+",
59
- prefix="run_python_stdout_",
60
- delete=False,
61
- encoding="utf-8",
62
- ) as stdout_file,
63
- tempfile.NamedTemporaryFile(
64
- mode="w+",
65
- prefix="run_python_stderr_",
66
- delete=False,
67
- encoding="utf-8",
68
- ) as stderr_file,
69
- ):
70
- code_file.write(code)
71
- code_file.flush()
72
- env = os.environ.copy()
73
- env["PYTHONIOENCODING"] = "utf-8"
74
- process = subprocess.Popen(
75
- [sys.executable, code_file.name],
76
- stdout=stdout_file,
77
- stderr=stderr_file,
78
- text=True,
79
- env=env,
80
- )
81
- try:
82
- return_code = process.wait(timeout=timeout)
83
- except subprocess.TimeoutExpired:
84
- process.kill()
85
- self.report_error(
86
- tr(" ❌ Timed out after {timeout} seconds.", timeout=timeout)
87
- )
88
- return tr(
89
- "Code timed out after {timeout} seconds.", timeout=timeout
90
- )
91
- stdout_file.flush()
92
- stderr_file.flush()
93
- with open(
94
- stdout_file.name, "r", encoding="utf-8", errors="replace"
95
- ) as out_f:
96
- out_f.seek(0)
97
- for line in out_f:
98
- self.report_stdout(line)
99
- with open(
100
- stderr_file.name, "r", encoding="utf-8", errors="replace"
101
- ) as err_f:
102
- err_f.seek(0)
103
- for line in err_f:
104
- self.report_stderr(line)
105
- with open(
106
- stdout_file.name, "r", encoding="utf-8", errors="replace"
107
- ) as out_f:
108
- stdout_lines = sum(1 for _ in out_f)
109
- with open(
110
- stderr_file.name, "r", encoding="utf-8", errors="replace"
111
- ) as err_f:
112
- stderr_lines = sum(1 for _ in err_f)
113
- self.report_success(
114
- tr(" ✅ return code {return_code}", return_code=return_code)
115
- )
116
- warning_msg = ""
117
- if interactive:
118
- warning_msg = tr(
119
- "⚠️ Warning: This code might be interactive, require user input, and might hang.\n"
120
- )
121
- with open(
122
- stdout_file.name, "r", encoding="utf-8", errors="replace"
123
- ) as out_f:
124
- stdout_content = out_f.read()
125
- with open(
126
- stderr_file.name, "r", encoding="utf-8", errors="replace"
127
- ) as err_f:
128
- stderr_content = err_f.read()
129
- max_lines = 100
130
- if stdout_lines <= max_lines and stderr_lines <= max_lines:
131
- result = warning_msg + tr(
132
- "Return code: {return_code}\n--- STDOUT ---\n{stdout_content}",
133
- return_code=return_code,
134
- stdout_content=stdout_content,
135
- )
136
- if stderr_content.strip():
137
- result += tr(
138
- "\n--- STDERR ---\n{stderr_content}",
139
- stderr_content=stderr_content,
140
- )
141
- return result
142
- else:
143
- result = warning_msg + tr(
144
- "stdout_file: {stdout_file} (lines: {stdout_lines})\n",
145
- stdout_file=stdout_file.name,
146
- stdout_lines=stdout_lines,
147
- )
148
- if stderr_lines > 0 and stderr_content.strip():
149
- result += tr(
150
- "stderr_file: {stderr_file} (lines: {stderr_lines})\n",
151
- stderr_file=stderr_file.name,
152
- stderr_lines=stderr_lines,
153
- )
154
- result += tr(
155
- "returncode: {return_code}\nUse the get_lines tool to inspect the contents of these files when needed.",
156
- return_code=return_code,
157
- )
158
- return result
159
- except Exception as e:
160
- self.report_error(tr(" ❌ Error: {error}", error=e))
161
- return tr("Error running code: {error}", error=e)
@@ -1,49 +0,0 @@
1
- def handle_sum(console, shell_state=None, **kwargs):
2
- """
3
- Summarize the current chat history and replace it with a summary message.
4
- """
5
- agent = kwargs.get("agent")
6
- if agent is None:
7
- console.print("[bold red]Agent not provided to /sum command.[/bold red]")
8
- return
9
-
10
- history = shell_state.conversation_history.get_messages()
11
- if not history or len(history) < 2:
12
- console.print(
13
- "[bold yellow]Not enough conversation to summarize.[/bold yellow]"
14
- )
15
- return
16
-
17
- # Find the system message if present
18
- system_msg = next((m for m in history if m.get("role") == "system"), None)
19
-
20
- # Prepare summary prompt
21
- summary_prompt = {
22
- "role": "user",
23
- "content": "Summarize the following conversation in a concise paragraph for context. Only output the summary, do not include any tool calls or formatting.",
24
- }
25
- # Exclude system messages for the summary context
26
- convo_for_summary = [m for m in history if m.get("role") != "system"]
27
- summary_messages = [summary_prompt] + convo_for_summary
28
-
29
- try:
30
- summary_response = agent.chat(summary_messages, spinner=True, max_tokens=256)
31
- summary_text = (
32
- summary_response["content"]
33
- if isinstance(summary_response, dict)
34
- else str(summary_response)
35
- )
36
- except Exception as e:
37
- console.print(f"[bold red]Error during summarization: {e}[/bold red]")
38
- return
39
-
40
- # Rebuild conversation history
41
- new_history = []
42
- if system_msg:
43
- new_history.append(system_msg)
44
- new_history.append({"role": "assistant", "content": summary_text})
45
- shell_state.conversation_history.set_messages(new_history)
46
-
47
- console.print(
48
- "[bold green]Conversation summarized and history replaced with summary.[/bold green]"
49
- )