janito 1.2.0__tar.gz → 1.3.0__tar.gz

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 (74) hide show
  1. janito-1.3.0/PKG-INFO +142 -0
  2. janito-1.3.0/README.md +117 -0
  3. janito-1.3.0/janito/__init__.py +1 -0
  4. {janito-1.2.0 → janito-1.3.0}/janito/agent/agent.py +27 -10
  5. {janito-1.2.0 → janito-1.3.0}/janito/agent/config.py +37 -9
  6. janito-1.3.0/janito/agent/config_utils.py +9 -0
  7. {janito-1.2.0 → janito-1.3.0}/janito/agent/conversation.py +19 -3
  8. {janito-1.2.0 → janito-1.3.0}/janito/agent/runtime_config.py +1 -0
  9. janito-1.3.0/janito/agent/tool_handler.py +229 -0
  10. janito-1.3.0/janito/agent/tools/__init__.py +12 -0
  11. {janito-1.2.0 → janito-1.3.0}/janito/agent/tools/ask_user.py +2 -1
  12. janito-1.3.0/janito/agent/tools/fetch_url.py +40 -0
  13. janito-1.3.0/janito/agent/tools/file_ops.py +72 -0
  14. janito-1.3.0/janito/agent/tools/find_files.py +58 -0
  15. janito-1.3.0/janito/agent/tools/get_lines.py +58 -0
  16. janito-1.3.0/janito/agent/tools/py_compile.py +26 -0
  17. janito-1.3.0/janito/agent/tools/python_exec.py +47 -0
  18. janito-1.3.0/janito/agent/tools/remove_directory.py +38 -0
  19. janito-1.3.0/janito/agent/tools/replace_text_in_file.py +67 -0
  20. janito-1.3.0/janito/agent/tools/run_bash_command.py +134 -0
  21. janito-1.3.0/janito/agent/tools/search_files.py +52 -0
  22. {janito-1.2.0 → janito-1.3.0}/janito/cli/_print_config.py +12 -12
  23. {janito-1.2.0 → janito-1.3.0}/janito/cli/arg_parser.py +6 -1
  24. {janito-1.2.0 → janito-1.3.0}/janito/cli/config_commands.py +56 -8
  25. {janito-1.2.0 → janito-1.3.0}/janito/cli/runner.py +21 -9
  26. {janito-1.2.0 → janito-1.3.0}/janito/cli_chat_shell/chat_loop.py +5 -3
  27. {janito-1.2.0 → janito-1.3.0}/janito/cli_chat_shell/commands.py +34 -37
  28. {janito-1.2.0 → janito-1.3.0}/janito/cli_chat_shell/config_shell.py +1 -1
  29. {janito-1.2.0 → janito-1.3.0}/janito/cli_chat_shell/load_prompt.py +1 -1
  30. {janito-1.2.0 → janito-1.3.0}/janito/cli_chat_shell/session_manager.py +11 -15
  31. {janito-1.2.0 → janito-1.3.0}/janito/cli_chat_shell/ui.py +17 -8
  32. {janito-1.2.0 → janito-1.3.0}/janito/render_prompt.py +3 -1
  33. janito-1.3.0/janito/web/app.py +189 -0
  34. janito-1.3.0/janito.egg-info/PKG-INFO +142 -0
  35. {janito-1.2.0 → janito-1.3.0}/janito.egg-info/SOURCES.txt +9 -9
  36. {janito-1.2.0 → janito-1.3.0}/janito.egg-info/requires.txt +6 -3
  37. {janito-1.2.0 → janito-1.3.0}/pyproject.toml +10 -7
  38. janito-1.2.0/PKG-INFO +0 -85
  39. janito-1.2.0/README.md +0 -63
  40. janito-1.2.0/janito/__init__.py +0 -1
  41. janito-1.2.0/janito/agent/tool_handler.py +0 -127
  42. janito-1.2.0/janito/agent/tools/__init__.py +0 -11
  43. janito-1.2.0/janito/agent/tools/bash_exec.py +0 -58
  44. janito-1.2.0/janito/agent/tools/create_directory.py +0 -19
  45. janito-1.2.0/janito/agent/tools/create_file.py +0 -43
  46. janito-1.2.0/janito/agent/tools/fetch_url.py +0 -48
  47. janito-1.2.0/janito/agent/tools/file_str_replace.py +0 -48
  48. janito-1.2.0/janito/agent/tools/find_files.py +0 -37
  49. janito-1.2.0/janito/agent/tools/move_file.py +0 -37
  50. janito-1.2.0/janito/agent/tools/remove_file.py +0 -19
  51. janito-1.2.0/janito/agent/tools/search_text.py +0 -41
  52. janito-1.2.0/janito/agent/tools/view_file.py +0 -34
  53. janito-1.2.0/janito/templates/system_instructions.j2 +0 -38
  54. janito-1.2.0/janito/web/app.py +0 -132
  55. janito-1.2.0/janito.egg-info/PKG-INFO +0 -85
  56. {janito-1.2.0 → janito-1.3.0}/LICENSE +0 -0
  57. {janito-1.2.0 → janito-1.3.0}/janito/__main__.py +0 -0
  58. {janito-1.2.0 → janito-1.3.0}/janito/agent/__init__.py +0 -0
  59. {janito-1.2.0 → janito-1.3.0}/janito/agent/config_defaults.py +0 -0
  60. {janito-1.2.0 → janito-1.3.0}/janito/agent/queued_tool_handler.py +0 -0
  61. {janito-1.2.0 → janito-1.3.0}/janito/agent/tools/gitignore_utils.py +0 -0
  62. {janito-1.2.0 → janito-1.3.0}/janito/agent/tools/rich_live.py +0 -0
  63. {janito-1.2.0 → janito-1.3.0}/janito/agent/tools/rich_utils.py +0 -0
  64. {janito-1.2.0 → janito-1.3.0}/janito/cli/__init__.py +0 -0
  65. {janito-1.2.0 → janito-1.3.0}/janito/cli/_utils.py +0 -0
  66. {janito-1.2.0 → janito-1.3.0}/janito/cli/logging_setup.py +0 -0
  67. {janito-1.2.0 → janito-1.3.0}/janito/cli/main.py +0 -0
  68. {janito-1.2.0 → janito-1.3.0}/janito/cli_chat_shell/__init__.py +0 -0
  69. {janito-1.2.0 → janito-1.3.0}/janito/web/__init__.py +0 -0
  70. {janito-1.2.0 → janito-1.3.0}/janito/web/__main__.py +0 -0
  71. {janito-1.2.0 → janito-1.3.0}/janito.egg-info/dependency_links.txt +0 -0
  72. {janito-1.2.0 → janito-1.3.0}/janito.egg-info/entry_points.txt +0 -0
  73. {janito-1.2.0 → janito-1.3.0}/janito.egg-info/top_level.txt +0 -0
  74. {janito-1.2.0 → janito-1.3.0}/setup.cfg +0 -0
janito-1.3.0/PKG-INFO ADDED
@@ -0,0 +1,142 @@
1
+ Metadata-Version: 2.4
2
+ Name: janito
3
+ Version: 1.3.0
4
+ Summary: A Natural Programming Language Agent,
5
+ Author-email: João Pinto <joao.pinto@gmail.com>
6
+ License: MIT
7
+ Project-URL: homepage, https://github.com/joaompinto/janito
8
+ Project-URL: repository, https://github.com/joaompinto/janito
9
+ Keywords: agent,framework,tools,automation
10
+ Classifier: Programming Language :: Python :: 3.10
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Operating System :: OS Independent
13
+ Requires-Python: >=3.10
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+ Requires-Dist: beautifulsoup4
17
+ Requires-Dist: flask
18
+ Requires-Dist: jinja2
19
+ Requires-Dist: openai
20
+ Requires-Dist: pathspec
21
+ Requires-Dist: prompt_toolkit
22
+ Requires-Dist: requests
23
+ Requires-Dist: rich
24
+ Dynamic: license-file
25
+
26
+ # 🚀 Janito: Agent
27
+
28
+ Janito is an AI-powered assistant for the command line and web that interprets natural language instructions to edit code, manage files, and analyze projects using patterns and tools designed by experienced software engineers. It prioritizes transparency, interactive clarification, and precise, reviewable changes.
29
+
30
+ ---
31
+
32
+ ## ⚡ Quick Start
33
+
34
+ Run a one-off prompt:
35
+ ```bash
36
+ python -m janito "Refactor the data processing module to improve readability."
37
+ ```
38
+
39
+ Or start the interactive chat shell:
40
+ ```bash
41
+ python -m janito
42
+ ```
43
+
44
+ Launch the web UI:
45
+ ```bash
46
+ python -m janito.web
47
+ ```
48
+
49
+ ---
50
+
51
+ ## ✨ Key Features
52
+ - 📝 **Code Editing via Natural Language:** Modify, create, or delete code files simply by describing the changes.
53
+ - 📁 **File & Directory Management:** Navigate, create, move, or remove files and folders.
54
+ - 🧠 **Context-Aware:** Understands your project structure for precise edits.
55
+ - 💬 **Interactive User Prompts:** Asks for clarification when needed.
56
+ - 🧩 **Extensible Tooling:** Built-in tools for file operations, shell commands, directory listing, Python file validation, text replacement, code execution, and more. Recent tools include:
57
+ - `find_files`: Search for files matching a pattern in directories.
58
+ - `get_lines`: Retrieve specific lines from files for efficient context.
59
+ - `py_compile_file`: Validate Python files for syntax correctness.
60
+ - `replace_text_in_file`: Replace exact text fragments in files.
61
+ - `search_files`: Search for text patterns across files.
62
+ - `python_exec`: Execute Python code and capture output.
63
+ - And more, see `janito/agent/tools/` for the full list.
64
+ - 🌐 **Web Interface (In Development):** Upcoming simple web UI for streaming responses and tool progress.
65
+
66
+ ---
67
+
68
+
69
+ ## 📦 Installation
70
+
71
+ ### Requirements
72
+ - Python 3.10+
73
+
74
+ ...
75
+
76
+ ### Configuration & CLI Options
77
+
78
+ Below are the supported configuration parameters and CLI flags. Some options can be set via config files, CLI flags, or both. Use `python -m janito --help` for a full list, or `python -m janito --help-config` to see all config keys and their descriptions.
79
+
80
+ | Key / Flag | Description | How to set | Default |
81
+ |---------------------------|---------------------------------------------------------------------------------------------|-----------------------------------------------------------------|--------------------------------------------|
82
+ | `api_key` | API key for OpenAI-compatible service | `--set-api-key`, config file | _None_ (required) |
83
+ | `model` | Model name to use for this session | `--model` (session only), `--set-local-config model=...`, or `--set-global-config` | `openai/gpt-4.1` |
84
+ | `base_url` | API base URL (OpenAI-compatible endpoint) | `--set-local-config base_url=...` or `--set-global-config` | `https://openrouter.ai/api/v1` |
85
+ | `role` | Role description for the system prompt | `--role` or config | "software engineer" |
86
+ | `system_prompt` | Override the entire system prompt as a raw string | `--system-prompt` or config | _Default prompt_ |
87
+ | `system_file` | Use a plain text file as the system prompt (takes precedence over `system_prompt`) | `--system-file` (CLI only) | _None_ |
88
+ | `temperature` | Sampling temperature (float, e.g., 0.0 - 2.0) | `--temperature` or config | 0.2 |
89
+ | `max_tokens` | Maximum tokens for model response | `--max-tokens` or config | 200000 |
90
+ | `max_rounds` | Maximum number of agent rounds per prompt/session | `--max-rounds` or config | 50 |
91
+ | `max_tools` | Maximum number of tool calls allowed within a chat session | `--max-tools` or config | _None_ (unlimited) |
92
+ | `no_tools` | Disable tool use (no tools passed to agent) | `-n`, `--no-tools` (CLI only) | False |
93
+ | `trust` | Trust mode: suppresses run_bash_command output, only shows output file locations | `--trust` (CLI only) | False |
94
+ | `template` / `template.*` | Template context dictionary for prompt rendering (nested or flat keys) | Config only | _None_ |
95
+
96
+ Other config-related CLI flags:
97
+
98
+ - `--set-local-config key=val` Set a local config value
99
+ - `--set-global-config key=val` Set a global config value
100
+ - `--run-config key=val` Set a runtime (in-memory only) config value (can be repeated)
101
+ - `--show-config` Show effective configuration and exit
102
+ - `--config-reset-local` Remove the local config file
103
+ - `--config-reset-global` Remove the global config file
104
+ - `--set-api-key KEY` Set and save the API key globally
105
+ - `--help-config` Show all configuration options and exit
106
+
107
+ Session & shell options:
108
+
109
+ - `--continue-session` Continue from the last saved conversation
110
+ - `--web` Launch the Janito web server instead of CLI
111
+
112
+ Verbose/debugging flags:
113
+
114
+ - `--verbose-http` Enable verbose HTTP logging
115
+ - `--verbose-http-raw` Enable raw HTTP wire-level logging
116
+ - `--verbose-response` Pretty print the full response object
117
+ - `--verbose-tools` Print tool call parameters and results
118
+ - `--show-system` Show model, parameters, system prompt, and tool definitions, then exit
119
+ - `--version` Show program's version number and exit
120
+
121
+
122
+
123
+ ---
124
+
125
+ ## 🧩 System Prompt & Role
126
+
127
+ Janito operates using a system prompt template that defines its behavior, communication style, and capabilities. By default, Janito assumes the role of a "software engineer"—this means its responses and actions are tailored to the expectations and best practices of professional software engineering.
128
+
129
+ - **Role:** You can customize the agent's role (e.g., "data scientist", "DevOps engineer") using the `--role` flag or config. The default is `software engineer`.
130
+ - **System Prompt Template:** The system prompt is rendered from a Jinja2 template (see `janito/agent/templates/system_instructions.j2` (now located directly under the agent directory)). This template governs how the agent interprets instructions, interacts with files, and communicates with users.
131
+ - **Customization:** Advanced users can override the system prompt with the `--system-prompt` flag (raw string), or point to a custom file using `--system-file`.
132
+
133
+ The default template ensures the agent:
134
+ - Prioritizes safe, reviewable, and minimal changes
135
+ - Asks for clarification when instructions are ambiguous
136
+ - Provides concise plans before taking action
137
+ - Documents any changes made
138
+
139
+ For more details or to customize the prompt, see the template file at `janito/agent/templates/system_instructions.j2`.
140
+
141
+ ---
142
+
janito-1.3.0/README.md ADDED
@@ -0,0 +1,117 @@
1
+ # 🚀 Janito: Agent
2
+
3
+ Janito is an AI-powered assistant for the command line and web that interprets natural language instructions to edit code, manage files, and analyze projects using patterns and tools designed by experienced software engineers. It prioritizes transparency, interactive clarification, and precise, reviewable changes.
4
+
5
+ ---
6
+
7
+ ## ⚡ Quick Start
8
+
9
+ Run a one-off prompt:
10
+ ```bash
11
+ python -m janito "Refactor the data processing module to improve readability."
12
+ ```
13
+
14
+ Or start the interactive chat shell:
15
+ ```bash
16
+ python -m janito
17
+ ```
18
+
19
+ Launch the web UI:
20
+ ```bash
21
+ python -m janito.web
22
+ ```
23
+
24
+ ---
25
+
26
+ ## ✨ Key Features
27
+ - 📝 **Code Editing via Natural Language:** Modify, create, or delete code files simply by describing the changes.
28
+ - 📁 **File & Directory Management:** Navigate, create, move, or remove files and folders.
29
+ - 🧠 **Context-Aware:** Understands your project structure for precise edits.
30
+ - 💬 **Interactive User Prompts:** Asks for clarification when needed.
31
+ - 🧩 **Extensible Tooling:** Built-in tools for file operations, shell commands, directory listing, Python file validation, text replacement, code execution, and more. Recent tools include:
32
+ - `find_files`: Search for files matching a pattern in directories.
33
+ - `get_lines`: Retrieve specific lines from files for efficient context.
34
+ - `py_compile_file`: Validate Python files for syntax correctness.
35
+ - `replace_text_in_file`: Replace exact text fragments in files.
36
+ - `search_files`: Search for text patterns across files.
37
+ - `python_exec`: Execute Python code and capture output.
38
+ - And more, see `janito/agent/tools/` for the full list.
39
+ - 🌐 **Web Interface (In Development):** Upcoming simple web UI for streaming responses and tool progress.
40
+
41
+ ---
42
+
43
+
44
+ ## 📦 Installation
45
+
46
+ ### Requirements
47
+ - Python 3.10+
48
+
49
+ ...
50
+
51
+ ### Configuration & CLI Options
52
+
53
+ Below are the supported configuration parameters and CLI flags. Some options can be set via config files, CLI flags, or both. Use `python -m janito --help` for a full list, or `python -m janito --help-config` to see all config keys and their descriptions.
54
+
55
+ | Key / Flag | Description | How to set | Default |
56
+ |---------------------------|---------------------------------------------------------------------------------------------|-----------------------------------------------------------------|--------------------------------------------|
57
+ | `api_key` | API key for OpenAI-compatible service | `--set-api-key`, config file | _None_ (required) |
58
+ | `model` | Model name to use for this session | `--model` (session only), `--set-local-config model=...`, or `--set-global-config` | `openai/gpt-4.1` |
59
+ | `base_url` | API base URL (OpenAI-compatible endpoint) | `--set-local-config base_url=...` or `--set-global-config` | `https://openrouter.ai/api/v1` |
60
+ | `role` | Role description for the system prompt | `--role` or config | "software engineer" |
61
+ | `system_prompt` | Override the entire system prompt as a raw string | `--system-prompt` or config | _Default prompt_ |
62
+ | `system_file` | Use a plain text file as the system prompt (takes precedence over `system_prompt`) | `--system-file` (CLI only) | _None_ |
63
+ | `temperature` | Sampling temperature (float, e.g., 0.0 - 2.0) | `--temperature` or config | 0.2 |
64
+ | `max_tokens` | Maximum tokens for model response | `--max-tokens` or config | 200000 |
65
+ | `max_rounds` | Maximum number of agent rounds per prompt/session | `--max-rounds` or config | 50 |
66
+ | `max_tools` | Maximum number of tool calls allowed within a chat session | `--max-tools` or config | _None_ (unlimited) |
67
+ | `no_tools` | Disable tool use (no tools passed to agent) | `-n`, `--no-tools` (CLI only) | False |
68
+ | `trust` | Trust mode: suppresses run_bash_command output, only shows output file locations | `--trust` (CLI only) | False |
69
+ | `template` / `template.*` | Template context dictionary for prompt rendering (nested or flat keys) | Config only | _None_ |
70
+
71
+ Other config-related CLI flags:
72
+
73
+ - `--set-local-config key=val` Set a local config value
74
+ - `--set-global-config key=val` Set a global config value
75
+ - `--run-config key=val` Set a runtime (in-memory only) config value (can be repeated)
76
+ - `--show-config` Show effective configuration and exit
77
+ - `--config-reset-local` Remove the local config file
78
+ - `--config-reset-global` Remove the global config file
79
+ - `--set-api-key KEY` Set and save the API key globally
80
+ - `--help-config` Show all configuration options and exit
81
+
82
+ Session & shell options:
83
+
84
+ - `--continue-session` Continue from the last saved conversation
85
+ - `--web` Launch the Janito web server instead of CLI
86
+
87
+ Verbose/debugging flags:
88
+
89
+ - `--verbose-http` Enable verbose HTTP logging
90
+ - `--verbose-http-raw` Enable raw HTTP wire-level logging
91
+ - `--verbose-response` Pretty print the full response object
92
+ - `--verbose-tools` Print tool call parameters and results
93
+ - `--show-system` Show model, parameters, system prompt, and tool definitions, then exit
94
+ - `--version` Show program's version number and exit
95
+
96
+
97
+
98
+ ---
99
+
100
+ ## 🧩 System Prompt & Role
101
+
102
+ Janito operates using a system prompt template that defines its behavior, communication style, and capabilities. By default, Janito assumes the role of a "software engineer"—this means its responses and actions are tailored to the expectations and best practices of professional software engineering.
103
+
104
+ - **Role:** You can customize the agent's role (e.g., "data scientist", "DevOps engineer") using the `--role` flag or config. The default is `software engineer`.
105
+ - **System Prompt Template:** The system prompt is rendered from a Jinja2 template (see `janito/agent/templates/system_instructions.j2` (now located directly under the agent directory)). This template governs how the agent interprets instructions, interacts with files, and communicates with users.
106
+ - **Customization:** Advanced users can override the system prompt with the `--system-prompt` flag (raw string), or point to a custom file using `--system-file`.
107
+
108
+ The default template ensures the agent:
109
+ - Prioritizes safe, reviewable, and minimal changes
110
+ - Asks for clarification when instructions are ambiguous
111
+ - Provides concise plans before taking action
112
+ - Documents any changes made
113
+
114
+ For more details or to customize the prompt, see the template file at `janito/agent/templates/system_instructions.j2`.
115
+
116
+ ---
117
+
@@ -0,0 +1 @@
1
+ __version__ = "1.3.0"
@@ -7,7 +7,7 @@ from janito.agent.conversation import ConversationHandler
7
7
  from janito.agent.tool_handler import ToolHandler
8
8
 
9
9
  class Agent:
10
- """LLM Agent capable of handling conversations and tool calls."""
10
+ """Agent capable of handling conversations and tool calls."""
11
11
 
12
12
  REFERER = "www.janito.dev"
13
13
  TITLE = "Janito"
@@ -19,10 +19,22 @@ class Agent:
19
19
  system_prompt: str | None = None,
20
20
  verbose_tools: bool = False,
21
21
  tool_handler = None,
22
- base_url: str = "https://openrouter.ai/api/v1"
22
+ base_url: str = "https://openrouter.ai/api/v1",
23
+ azure_openai_api_version: str = "2023-05-15",
24
+ use_azure_openai: bool = False
23
25
  ):
24
26
  """
25
- Initialize the Agent.
27
+ Initialize Agent,
28
+
29
+ Args:
30
+ api_key: API key for OpenAI-compatible service.
31
+ model: Model name to use.
32
+ system_prompt: Optional system prompt override.
33
+ verbose_tools: Enable verbose tool call logging.
34
+ tool_handler: Optional custom ToolHandler instance.
35
+ base_url: API base URL.
36
+ azure_openai_api_version: Azure OpenAI API version (default: "2023-05-15").
37
+ use_azure_openai: Whether to use Azure OpenAI client (default: False).
26
38
 
27
39
  Args:
28
40
  api_key: API key for OpenAI-compatible service.
@@ -35,12 +47,12 @@ class Agent:
35
47
  self.api_key = api_key
36
48
  self.model = model
37
49
  self.system_prompt = system_prompt
38
- if os.environ.get("USE_AZURE_OPENAI"):
50
+ if use_azure_openai:
39
51
  from openai import AzureOpenAI
40
52
  self.client = AzureOpenAI(
41
53
  api_key=api_key,
42
- azure_endpoint=os.environ.get("AZURE_OPENAI_ENDPOINT"),
43
- api_version=os.environ.get("AZURE_OPENAI_API_VERSION", "2023-05-15"),
54
+ azure_endpoint=base_url,
55
+ api_version=azure_openai_api_version,
44
56
  )
45
57
  else:
46
58
  self.client = OpenAI(
@@ -60,7 +72,11 @@ class Agent:
60
72
  self.client, self.model, self.tool_handler
61
73
  )
62
74
 
63
- def chat(self, messages, on_content=None, on_tool_progress=None, verbose_response=False, spinner=False, max_tokens=None):
75
+ @property
76
+ def usage_history(self):
77
+ return self.conversation_handler.usage_history
78
+
79
+ def chat(self, messages, on_content=None, on_tool_progress=None, verbose_response=False, spinner=False, max_tokens=None, max_rounds=50):
64
80
  import time
65
81
  from janito.agent.conversation import ProviderError
66
82
 
@@ -69,11 +85,12 @@ class Agent:
69
85
  try:
70
86
  return self.conversation_handler.handle_conversation(
71
87
  messages,
72
- max_tokens=max_tokens,
88
+ max_rounds=max_rounds,
73
89
  on_content=on_content,
74
90
  on_tool_progress=on_tool_progress,
75
91
  verbose_response=verbose_response,
76
- spinner=spinner
92
+ spinner=spinner,
93
+ max_tokens=max_tokens
77
94
  )
78
95
  except ProviderError as e:
79
96
  error_data = getattr(e, 'error_data', {}) or {}
@@ -92,5 +109,5 @@ class Agent:
92
109
  else:
93
110
  print("Max retries reached. Raising error.")
94
111
  raise
95
- except Exception:
112
+
96
113
  raise
@@ -40,14 +40,12 @@ class FileConfig(BaseConfig):
40
40
 
41
41
  def load(self):
42
42
  if self.path.exists():
43
- try:
44
- with open(self.path, 'r') as f:
45
- self._data = json.load(f)
46
- # Remove keys with value None (null in JSON)
47
- self._data = {k: v for k, v in self._data.items() if v is not None}
48
- except Exception as e:
49
- print(f"Warning: Failed to load config file {self.path}: {e}")
50
- self._data = {}
43
+ with open(self.path, 'r') as f:
44
+ self._data = json.load(f)
45
+ # Remove keys with value None (null in JSON)
46
+ self._data = {k: v for k, v in self._data.items() if v is not None}
47
+
48
+
51
49
  else:
52
50
  self._data = {}
53
51
 
@@ -57,6 +55,7 @@ class FileConfig(BaseConfig):
57
55
  json.dump(self._data, f, indent=2)
58
56
 
59
57
 
58
+
60
59
  CONFIG_OPTIONS = {
61
60
  "api_key": "API key for OpenAI-compatible service (required)",
62
61
  "model": "Model name to use (e.g., 'openai/gpt-4.1')",
@@ -64,9 +63,38 @@ CONFIG_OPTIONS = {
64
63
  "role": "Role description for the system prompt (e.g., 'software engineer')",
65
64
  "system_prompt": "Override the entire system prompt text",
66
65
  "temperature": "Sampling temperature (float, e.g., 0.0 - 2.0)",
67
- "max_tokens": "Maximum tokens for model response (int)"
66
+ "max_tokens": "Maximum tokens for model response (int)",
67
+ # Accept template.* keys as valid config keys (for CLI validation, etc.)
68
+ "template": "Template context dictionary for prompt rendering (nested)",
69
+ # Note: template.* keys are validated dynamically, not statically here
68
70
  }
69
71
 
72
+ class BaseConfig:
73
+ def __init__(self):
74
+ self._data = {}
75
+
76
+ def get(self, key, default=None):
77
+ return self._data.get(key, default)
78
+
79
+ def set(self, key, value):
80
+ self._data[key] = value
81
+
82
+ def all(self):
83
+ return self._data
84
+
85
+
86
+ """
87
+ Returns a dictionary suitable for passing as Jinja2 template variables.
88
+ Merges the nested 'template' dict (if present) and all flat 'template.*' keys.
89
+ Flat keys override nested dict keys if there is a conflict.
90
+ """
91
+ template_vars = dict(self._data.get("template", {}))
92
+ for k, v in self._data.items():
93
+ if k.startswith("template.") and k != "template":
94
+ template_vars[k[9:]] = v
95
+ return template_vars
96
+
97
+
70
98
  # Import defaults for reference
71
99
  from .config_defaults import CONFIG_DEFAULTS
72
100
 
@@ -0,0 +1,9 @@
1
+ def merge_configs(*configs):
2
+ """
3
+ Merge multiple config-like objects (with .all()) into one dict.
4
+ Later configs override earlier ones.
5
+ """
6
+ merged = {}
7
+ for cfg in configs:
8
+ merged.update(cfg.all())
9
+ return merged
@@ -16,8 +16,12 @@ class ConversationHandler:
16
16
  self.client = client
17
17
  self.model = model
18
18
  self.tool_handler = tool_handler
19
+ self.usage_history = []
19
20
 
20
21
  def handle_conversation(self, messages, max_rounds=50, on_content=None, on_tool_progress=None, verbose_response=False, spinner=False, max_tokens=None):
22
+ from janito.agent.runtime_config import runtime_config
23
+ max_tools = runtime_config.get('max_tools', None)
24
+ tool_calls_made = 0
21
25
  if not messages:
22
26
  raise ValueError("No prompt provided in messages")
23
27
 
@@ -95,16 +99,28 @@ class ConversationHandler:
95
99
 
96
100
  # If no tool calls, return the assistant's message and usage info
97
101
  if not choice.message.tool_calls:
102
+ # Store usage info in usage_history, linked to the next assistant message index
103
+ assistant_idx = len([m for m in messages if m.get('role') == 'assistant'])
104
+ self.usage_history.append({"assistant_index": assistant_idx, "usage": usage_info})
98
105
  return {
99
- "content": choice.message.content,
100
- "usage": usage_info
101
- }
106
+ "content": choice.message.content,
107
+ "usage": usage_info,
108
+ "usage_history": self.usage_history
109
+ }
102
110
 
111
+ from janito.agent.runtime_config import runtime_config
103
112
  tool_responses = []
113
+ # Sequential tool execution (default, only mode)
104
114
  for tool_call in choice.message.tool_calls:
115
+ if max_tools is not None and tool_calls_made >= max_tools:
116
+ raise MaxRoundsExceededError(f"Maximum number of tool calls ({max_tools}) reached in this chat session.")
105
117
  result = self.tool_handler.handle_tool_call(tool_call, on_progress=on_tool_progress)
106
118
  tool_responses.append({"tool_call_id": tool_call.id, "content": result})
119
+ tool_calls_made += 1
107
120
 
121
+ # Store usage info in usage_history, linked to the next assistant message index
122
+ assistant_idx = len([m for m in messages if m.get('role') == 'assistant'])
123
+ self.usage_history.append({"assistant_index": assistant_idx, "usage": usage_info})
108
124
  messages.append({"role": "assistant", "content": choice.message.content, "tool_calls": [tc.to_dict() for tc in choice.message.tool_calls]})
109
125
 
110
126
  for tr in tool_responses:
@@ -16,6 +16,7 @@ class UnifiedConfig:
16
16
  self.runtime_cfg = runtime_cfg
17
17
  self.effective_cfg = effective_cfg
18
18
 
19
+
19
20
  def get(self, key, default=None):
20
21
  val = self.runtime_cfg.get(key)
21
22
  if val is not None: