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
@@ -1,8 +1,9 @@
1
1
  """
2
- MUST BE IMPLEMENTED:
3
- - check that all params found in the signature have documentation in the docstring which is provided in the parameter schema doc
4
- - the Return must be documented and integrated in the sechema description
5
- - backward compatibility is not required
2
+ Generates OpenAI-compatible function schemas from Python callables and class docstrings.
3
+
4
+ - Ensures all parameters are documented and type-annotated.
5
+ - Integrates return documentation into the schema description.
6
+ - Supports Google, NumPy, and relaxed docstring formats.
6
7
  """
7
8
 
8
9
  import inspect
@@ -20,135 +21,167 @@ PYTHON_TYPE_TO_JSON = {
20
21
  }
21
22
 
22
23
 
23
- def _type_to_json_schema(annotation):
24
- if hasattr(annotation, "__origin__"):
25
- if annotation.__origin__ is list or annotation.__origin__ is typing.List:
26
- return {
27
- "type": "array",
28
- "items": _type_to_json_schema(annotation.__args__[0]),
29
- }
30
- if annotation.__origin__ is dict or annotation.__origin__ is typing.Dict:
31
- return {"type": "object"}
32
- return {"type": PYTHON_TYPE_TO_JSON.get(annotation, "string")}
33
-
34
-
35
- def _parse_docstring(docstring: str):
24
+ class OpenAISchemaGenerator:
36
25
  """
37
- Parses a docstring to extract summary, parameter descriptions, and return description.
38
- Accepts Google, NumPy, and relaxed formats.
39
- Returns: summary, {param: description}, return_description
26
+ Generates OpenAI-compatible function schemas from Python callables and class docstrings.
27
+
28
+ - Ensures all parameters are documented and type-annotated.
29
+ - Integrates return documentation into the schema description.
30
+ - Supports Google, NumPy, and relaxed docstring formats.
40
31
  """
41
- if not docstring:
42
- return "", {}, ""
43
- lines = docstring.strip().split("\n")
44
- summary = lines[0].strip()
45
- param_descs = {}
46
- return_desc = ""
47
- in_params = False
48
- in_returns = False
49
- param_section_headers = ("args", "arguments", "params", "parameters")
50
- for line in lines[1:]:
51
- stripped_line = line.strip()
52
- if any(
53
- stripped_line.lower().startswith(h + ":") or stripped_line.lower() == h
54
- for h in param_section_headers
55
- ):
56
- in_params = True
57
- in_returns = False
58
- continue
59
- if (
60
- stripped_line.lower().startswith("returns:")
61
- or stripped_line.lower() == "returns"
62
- ):
63
- in_returns = True
64
- in_params = False
65
- continue
66
- if in_params:
67
- # Accept: name: desc, name (type): desc, name - desc, name desc
68
- m = re.match(
69
- r"([a-zA-Z_][a-zA-Z0-9_]*)\s*(?:\(([^)]+)\))?\s*[:\-]?\s*(.+)",
70
- stripped_line,
71
- )
72
- if m:
73
- param, _, desc = m.groups()
74
- param_descs[param] = desc.strip()
75
- elif stripped_line and stripped_line[0] != "-":
76
- # Continuation of previous param
77
- if param_descs:
78
- last = list(param_descs)[-1]
79
- param_descs[last] += " " + stripped_line
80
- elif in_returns:
81
- if stripped_line:
82
- return_desc += (" " if return_desc else "") + stripped_line
83
- return summary, param_descs, return_desc
84
32
 
33
+ def __init__(self):
34
+ pass
85
35
 
86
- def generate_openai_function_schema(func, tool_name: str, tool_class=None):
87
- """
88
- Generates an OpenAI-compatible function schema for a callable.
89
- Raises ValueError if the return type is not explicitly str or if any parameter is missing a type hint.
90
- """
91
- sig = inspect.signature(func)
92
- # Enforce explicit str return type
93
- if sig.return_annotation is inspect._empty or sig.return_annotation is not str:
94
- raise ValueError(
95
- f"Tool '{tool_name}' must have an explicit return type of 'str'. Found: {sig.return_annotation}"
96
- )
97
- # Enforce type hints for all parameters (except self)
98
- missing_type_hints = [
99
- name
100
- for name, param in sig.parameters.items()
101
- if name != "self" and param.annotation is inspect._empty
102
- ]
103
- if missing_type_hints:
104
- raise ValueError(
105
- f"Tool '{tool_name}' is missing type hints for parameter(s): {', '.join(missing_type_hints)}.\n"
106
- f"All parameters must have explicit type hints for schema generation."
107
- )
108
- # Only use the class docstring for schema generation
109
- class_doc = tool_class.__doc__.strip() if tool_class and tool_class.__doc__ else ""
110
- summary, param_descs, return_desc = _parse_docstring(class_doc)
111
- description = summary
112
- if return_desc:
113
- description += f"\n\nReturns: {return_desc}"
114
- # Check that all parameters in the signature have documentation
115
- undocumented = [
116
- name
117
- for name, param in sig.parameters.items()
118
- if name != "self" and name not in param_descs
119
- ]
120
- if undocumented:
121
- raise ValueError(
122
- f"Tool '{tool_name}' is missing docstring documentation for parameter(s): {', '.join(undocumented)}.\n"
123
- f"Parameter documentation must be provided in the Tool class docstring, not the method docstring."
36
+ def type_to_json_schema(self, annotation):
37
+ if hasattr(annotation, "__origin__"):
38
+ if annotation.__origin__ is list or annotation.__origin__ is typing.List:
39
+ return {
40
+ "type": "array",
41
+ "items": self.type_to_json_schema(annotation.__args__[0]),
42
+ }
43
+ if annotation.__origin__ is dict or annotation.__origin__ is typing.Dict:
44
+ return {"type": "object"}
45
+ return {"type": PYTHON_TYPE_TO_JSON.get(annotation, "string")}
46
+
47
+ def parse_param_section(self, lines, param_section_headers):
48
+ param_descs = {}
49
+ in_params = False
50
+ for line in lines:
51
+ stripped_line = line.strip()
52
+ if any(
53
+ stripped_line.lower().startswith(h + ":") or stripped_line.lower() == h
54
+ for h in param_section_headers
55
+ ):
56
+ in_params = True
57
+ continue
58
+ if in_params:
59
+ m = re.match(
60
+ r"([a-zA-Z_][a-zA-Z0-9_]*)\s*(?:\(([^)]+)\))?\s*[:\-]?\s*(.+)",
61
+ stripped_line,
62
+ )
63
+ if m:
64
+ param, _, desc = m.groups()
65
+ param_descs[param] = desc.strip()
66
+ elif stripped_line and stripped_line[0] != "-":
67
+ if param_descs:
68
+ last = list(param_descs)[-1]
69
+ param_descs[last] += " " + stripped_line
70
+ if (
71
+ stripped_line.lower().startswith("returns:")
72
+ or stripped_line.lower() == "returns"
73
+ ):
74
+ break
75
+ return param_descs
76
+
77
+ def parse_return_section(self, lines):
78
+ in_returns = False
79
+ return_desc = ""
80
+ for line in lines:
81
+ stripped_line = line.strip()
82
+ if (
83
+ stripped_line.lower().startswith("returns:")
84
+ or stripped_line.lower() == "returns"
85
+ ):
86
+ in_returns = True
87
+ continue
88
+ if in_returns:
89
+ if stripped_line:
90
+ return_desc += (" " if return_desc else "") + stripped_line
91
+ return return_desc
92
+
93
+ def parse_docstring(self, docstring: str):
94
+ """
95
+ Parses a docstring to extract summary, parameter descriptions, and return description.
96
+ Accepts Google, NumPy, and relaxed formats.
97
+ Returns: summary, {param: description}, return_description
98
+ """
99
+ if not docstring:
100
+ return "", {}, ""
101
+ lines = docstring.strip().split("\n")
102
+ summary = lines[0].strip()
103
+ param_section_headers = ("args", "arguments", "params", "parameters")
104
+ param_descs = self.parse_param_section(lines[1:], param_section_headers)
105
+ return_desc = self.parse_return_section(lines[1:])
106
+ return summary, param_descs, return_desc
107
+
108
+ def generate_schema(self, tool_class):
109
+ """
110
+ Generates an OpenAI-compatible function schema for a tool class.
111
+ The tool class must have _tool_run_method and _tool_name attributes set by the tool registration decorator.
112
+ Raises ValueError if the return type is not explicitly str or if any parameter is missing a type hint.
113
+ """
114
+ if not hasattr(tool_class, "_tool_run_method") or not hasattr(
115
+ tool_class, "_tool_name"
116
+ ):
117
+ raise ValueError(
118
+ "Tool class must have _tool_run_method and _tool_name attributes (set by @register_tool)."
119
+ )
120
+ func = tool_class._tool_run_method
121
+ tool_name = tool_class._tool_name
122
+ sig = inspect.signature(func)
123
+ # Enforce explicit str return type
124
+ if sig.return_annotation is inspect._empty or sig.return_annotation is not str:
125
+ raise ValueError(
126
+ f"Tool '{tool_name}' must have an explicit return type of 'str'. Found: {sig.return_annotation}"
127
+ )
128
+ # Enforce type hints for all parameters (except self)
129
+ missing_type_hints = [
130
+ name
131
+ for name, param in sig.parameters.items()
132
+ if name != "self" and param.annotation is inspect._empty
133
+ ]
134
+ if missing_type_hints:
135
+ raise ValueError(
136
+ f"Tool '{tool_name}' is missing type hints for parameter(s): {', '.join(missing_type_hints)}.\n"
137
+ f"All parameters must have explicit type hints for schema generation."
138
+ )
139
+ # Only use the class docstring for schema generation
140
+ class_doc = (
141
+ tool_class.__doc__.strip() if tool_class and tool_class.__doc__ else ""
124
142
  )
125
- properties = OrderedDict()
126
- required = []
127
- # Inject tool_call_reason as the first required parameter, unless --ntt is set
128
- from janito.agent.runtime_config import runtime_config
143
+ summary, param_descs, return_desc = self.parse_docstring(class_doc)
144
+ description = summary
145
+ if return_desc:
146
+ description += f"\n\nReturns: {return_desc}"
147
+ # Check that all parameters in the signature have documentation
148
+ undocumented = [
149
+ name
150
+ for name, param in sig.parameters.items()
151
+ if name != "self" and name not in param_descs
152
+ ]
153
+ if undocumented:
154
+ raise ValueError(
155
+ f"Tool '{tool_name}' is missing docstring documentation for parameter(s): {', '.join(undocumented)}.\n"
156
+ f"Parameter documentation must be provided in the Tool class docstring, not the method docstring."
157
+ )
158
+ properties = OrderedDict()
159
+ required = []
160
+ # Inject tool_call_reason as the first required parameter, unless --ntt is set
161
+ from janito.agent.runtime_config import runtime_config
129
162
 
130
- if not runtime_config.get("no_tools_tracking", False):
131
- properties["tool_call_reason"] = {
132
- "type": "string",
133
- "description": "The reason or context for why this tool is being called. This is required for traceability.",
163
+ if not runtime_config.get("no_tools_tracking", False):
164
+ properties["tool_call_reason"] = {
165
+ "type": "string",
166
+ "description": "The reason or context for why this tool is being called. This is required for traceability.",
167
+ }
168
+ required.append("tool_call_reason")
169
+ for name, param in sig.parameters.items():
170
+ if name == "self":
171
+ continue
172
+ annotation = param.annotation
173
+ pdesc = param_descs.get(name, "")
174
+ schema = self.type_to_json_schema(annotation)
175
+ schema["description"] = pdesc
176
+ properties[name] = schema
177
+ if param.default == inspect._empty:
178
+ required.append(name)
179
+ return {
180
+ "name": tool_name,
181
+ "description": description,
182
+ "parameters": {
183
+ "type": "object",
184
+ "properties": properties,
185
+ "required": required,
186
+ },
134
187
  }
135
- required.append("tool_call_reason")
136
- for name, param in sig.parameters.items():
137
- if name == "self":
138
- continue
139
- annotation = param.annotation
140
- pdesc = param_descs.get(name, "")
141
- schema = _type_to_json_schema(annotation)
142
- schema["description"] = pdesc
143
- properties[name] = schema
144
- if param.default == inspect._empty:
145
- required.append(name)
146
- return {
147
- "name": tool_name,
148
- "description": description,
149
- "parameters": {
150
- "type": "object",
151
- "properties": properties,
152
- "required": required,
153
- },
154
- }
@@ -1,90 +1,147 @@
1
+ import os
1
2
  import platform
3
+ import subprocess
2
4
  import sys
3
5
 
4
6
 
5
- def detect_shell():
6
- import os
7
- import subprocess
8
-
9
- shell_info = None
10
-
11
- # 1. Detect shell (prefer Git Bash if detected)
12
- if os.environ.get("MSYSTEM"):
13
- shell_info = f"Git Bash ({os.environ.get('MSYSTEM')})"
14
- # 2. Detect WSL (before PowerShell)
15
- elif os.environ.get("WSL_DISTRO_NAME"):
16
- shell = os.environ.get("SHELL")
17
- shell_name = shell.split("/")[-1] if shell else "unknown"
18
- distro = os.environ.get("WSL_DISTRO_NAME")
19
- shell_info = f"{shell_name} (WSL: {distro})"
20
- else:
21
- # 3. Try to detect PowerShell by running $host.Name
7
+ class PlatformDiscovery:
8
+ """
9
+ Provides utilities for detecting the current shell, platform, and Python version.
10
+ Uses the system's environment and subprocess modules internally.
11
+ """
12
+
13
+ def __init__(self):
14
+ """
15
+ Initialize the PlatformDiscovery instance.
16
+ Uses os.environ and subprocess by default.
17
+ """
18
+ self.os_environ = os.environ
19
+ self.subprocess_mod = subprocess
20
+
21
+ def _detect_git_bash(self):
22
+ if self.os_environ.get("MSYSTEM"):
23
+ return f"Git Bash ({self.os_environ.get('MSYSTEM')})"
24
+ return None
25
+
26
+ def _detect_wsl(self):
27
+ if self.os_environ.get("WSL_DISTRO_NAME"):
28
+ shell = self.os_environ.get("SHELL")
29
+ shell_name = shell.split("/")[-1] if shell else "unknown"
30
+ distro = self.os_environ.get("WSL_DISTRO_NAME")
31
+ return f"{shell_name} (WSL: {distro})"
32
+ return None
33
+
34
+ def _detect_powershell(self):
22
35
  try:
23
- result = subprocess.run(
36
+ result = self.subprocess_mod.run(
24
37
  ["powershell.exe", "-NoProfile", "-Command", "$host.Name"],
25
38
  capture_output=True,
26
39
  text=True,
27
40
  timeout=2,
28
41
  )
29
42
  if result.returncode == 0 and "ConsoleHost" in result.stdout:
30
- shell_info = "PowerShell"
31
- else:
32
- shell_info = None
43
+ return "PowerShell"
33
44
  except Exception:
34
- shell_info = None
35
-
36
- # 4. If not PowerShell, check SHELL
37
- if not shell_info:
38
- shell = os.environ.get("SHELL")
39
- if shell:
40
- shell_info = shell
45
+ pass
46
+ return None
47
+
48
+ def _detect_shell_env(self):
49
+ shell = self.os_environ.get("SHELL")
50
+ if shell:
51
+ return shell
52
+ return None
53
+
54
+ def _detect_comspec(self):
55
+ comspec = self.os_environ.get("COMSPEC")
56
+ if comspec:
57
+ if "powershell" in comspec.lower():
58
+ return "PowerShell"
59
+ elif "cmd" in comspec.lower():
60
+ return "cmd.exe"
41
61
  else:
42
- # 5. If not, check COMSPEC for PowerShell or cmd.exe
43
- comspec = os.environ.get("COMSPEC")
44
- if comspec:
45
- if "powershell" in comspec.lower():
46
- shell_info = "PowerShell"
47
- elif "cmd" in comspec.lower():
48
- shell_info = "cmd.exe"
49
- else:
50
- shell_info = "Unknown shell"
51
- else:
52
- shell_info = "Unknown shell"
53
-
54
- # 6. Always append TERM and TERM_PROGRAM if present
55
- term_env = os.environ.get("TERM")
56
- if term_env:
57
- shell_info += f" [TERM={term_env}]"
58
-
59
- term_program = os.environ.get("TERM_PROGRAM")
60
- if term_program:
61
- shell_info += f" [TERM_PROGRAM={term_program}]"
62
-
63
- return shell_info
64
-
65
-
66
- def get_platform_name():
67
- sys_platform = platform.system().lower()
68
- if sys_platform.startswith("win"):
69
- return "windows"
70
- elif sys_platform.startswith("linux"):
71
- return "linux"
72
- elif sys_platform.startswith("darwin"):
73
- return "darwin"
74
- return sys_platform
75
-
76
-
77
- def get_python_version():
78
- return platform.python_version()
79
-
80
-
81
- def is_windows():
82
- return sys.platform.startswith("win")
83
-
84
-
85
- def is_linux():
86
- return sys.platform.startswith("linux")
87
-
88
-
89
- def is_mac():
90
- return sys.platform.startswith("darwin")
62
+ return "Unknown shell"
63
+ return "Unknown shell"
64
+
65
+ def _append_term_info(self, shell_info):
66
+ term_env = self.os_environ.get("TERM")
67
+ if term_env:
68
+ shell_info += f" [TERM={term_env}]"
69
+ term_program = self.os_environ.get("TERM_PROGRAM")
70
+ if term_program:
71
+ shell_info += f" [TERM_PROGRAM={term_program}]"
72
+ return shell_info
73
+
74
+ def detect_shell(self) -> str:
75
+ """
76
+ Detects the current shell environment and returns a descriptive string,
77
+ including terminal information if available.
78
+
79
+ Note:
80
+ This method may invoke subprocesses to execute shell commands
81
+ (e.g., to detect PowerShell), which could have side effects or
82
+ performance implications.
83
+
84
+ Returns:
85
+ str: Description of the detected shell and terminal info.
86
+ """
87
+ shell_info = (
88
+ self._detect_git_bash()
89
+ or self._detect_wsl()
90
+ or self._detect_powershell()
91
+ or self._detect_shell_env()
92
+ or self._detect_comspec()
93
+ )
94
+ shell_info = self._append_term_info(shell_info)
95
+ return shell_info
96
+
97
+ def get_platform_name(self) -> str:
98
+ """
99
+ Returns the normalized platform name.
100
+
101
+ Returns:
102
+ str: One of 'windows', 'linux', 'darwin', or the raw platform string.
103
+ """
104
+ sys_platform = platform.system().lower()
105
+ if sys_platform.startswith("win"):
106
+ return "windows"
107
+ elif sys_platform.startswith("linux"):
108
+ return "linux"
109
+ elif sys_platform.startswith("darwin"):
110
+ return "darwin"
111
+ return sys_platform
112
+
113
+ def get_python_version(self) -> str:
114
+ """
115
+ Returns the current Python version as a string.
116
+
117
+ Returns:
118
+ str: Python version (e.g., '3.12.0').
119
+ """
120
+ return platform.python_version()
121
+
122
+ def is_windows(self) -> bool:
123
+ """
124
+ Checks if the current platform is Windows.
125
+
126
+ Returns:
127
+ bool: True if running on Windows, False otherwise.
128
+ """
129
+ return sys.platform.startswith("win")
130
+
131
+ def is_linux(self) -> bool:
132
+ """
133
+ Checks if the current platform is Linux.
134
+
135
+ Returns:
136
+ bool: True if running on Linux, False otherwise.
137
+ """
138
+ return sys.platform.startswith("linux")
139
+
140
+ def is_mac(self) -> bool:
141
+ """
142
+ Checks if the current platform is macOS.
143
+
144
+ Returns:
145
+ bool: True if running on macOS, False otherwise.
146
+ """
147
+ return sys.platform.startswith("darwin")
@@ -1,8 +1,7 @@
1
1
  from openai import OpenAI
2
2
  from jinja2 import Environment, FileSystemLoader, select_autoescape
3
3
  from pathlib import Path
4
- from janito.agent.platform_discovery import get_platform_name, get_python_version
5
- from janito.agent.platform_discovery import detect_shell
4
+ from janito.agent.platform_discovery import PlatformDiscovery
6
5
 
7
6
 
8
7
  class AgentProfileManager:
@@ -21,9 +20,10 @@ class AgentProfileManager:
21
20
  main_template_name = "system_prompt_template_base_pt.txt.j2"
22
21
  else:
23
22
  main_template_name = "system_prompt_template_base.txt.j2"
24
- platform_name = get_platform_name()
25
- python_version = get_python_version()
26
- shell_info = detect_shell()
23
+ pd = PlatformDiscovery()
24
+ platform_name = pd.get_platform_name()
25
+ python_version = pd.get_python_version()
26
+ shell_info = pd.detect_shell()
27
27
 
28
28
  context = {
29
29
  "role": self.role,