janito 0.14.0__py3-none-any.whl → 0.15.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- janito/__init__.py +1 -1
- janito/cli/agent/initialization.py +4 -8
- janito/cli/agent/query.py +29 -25
- janito/cli/app.py +17 -21
- janito/cli/commands/config.py +25 -237
- janito/cli/commands/profile.py +92 -71
- janito/cli/commands/workspace.py +30 -30
- janito/config/README.md +104 -0
- janito/config/__init__.py +16 -0
- janito/config/cli/__init__.py +28 -0
- janito/config/cli/commands.py +397 -0
- janito/config/cli/validators.py +77 -0
- janito/config/core/__init__.py +23 -0
- janito/config/core/file_operations.py +90 -0
- janito/config/core/properties.py +316 -0
- janito/config/core/singleton.py +282 -0
- janito/config/profiles/__init__.py +8 -0
- janito/config/profiles/definitions.py +38 -0
- janito/config/profiles/manager.py +80 -0
- janito/data/instructions_template.txt +6 -3
- janito/tools/bash/bash.py +80 -7
- janito/tools/bash/unix_persistent_bash.py +32 -1
- janito/tools/bash/win_persistent_bash.py +34 -1
- janito/tools/move_file.py +1 -1
- janito/tools/str_replace_editor/handlers/view.py +14 -8
- {janito-0.14.0.dist-info → janito-0.15.0.dist-info}/METADATA +107 -22
- {janito-0.14.0.dist-info → janito-0.15.0.dist-info}/RECORD +31 -20
- janito/config.py +0 -375
- {janito-0.14.0.dist-info → janito-0.15.0.dist-info}/WHEEL +0 -0
- {janito-0.14.0.dist-info → janito-0.15.0.dist-info}/entry_points.txt +0 -0
- {janito-0.14.0.dist-info → janito-0.15.0.dist-info}/licenses/LICENSE +0 -0
janito/cli/commands/workspace.py
CHANGED
@@ -1,31 +1,31 @@
|
|
1
|
-
"""
|
2
|
-
Workspace management functions for Janito CLI.
|
3
|
-
"""
|
4
|
-
import sys
|
5
|
-
from typing import Optional
|
6
|
-
from rich.console import Console
|
7
|
-
|
8
|
-
from janito.config import
|
9
|
-
|
10
|
-
console = Console()
|
11
|
-
|
12
|
-
def handle_workspace(workspace: Optional[str]) -> bool:
|
13
|
-
"""
|
14
|
-
Handle the --workspace parameter.
|
15
|
-
|
16
|
-
Args:
|
17
|
-
workspace: Workspace directory path
|
18
|
-
|
19
|
-
Returns:
|
20
|
-
bool: True if the program should exit after this operation
|
21
|
-
"""
|
22
|
-
if workspace:
|
23
|
-
try:
|
24
|
-
console.print(f"[bold]📂 Setting workspace directory to: {workspace}[/bold]")
|
25
|
-
|
26
|
-
console.print(f"[bold green]✅ Workspace directory set to: {
|
27
|
-
except ValueError as e:
|
28
|
-
console.print(f"[bold red]Error:[/bold red] {str(e)}")
|
29
|
-
sys.exit(1)
|
30
|
-
|
1
|
+
"""
|
2
|
+
Workspace management functions for Janito CLI.
|
3
|
+
"""
|
4
|
+
import sys
|
5
|
+
from typing import Optional
|
6
|
+
from rich.console import Console
|
7
|
+
|
8
|
+
from janito.config import Config
|
9
|
+
|
10
|
+
console = Console()
|
11
|
+
|
12
|
+
def handle_workspace(workspace: Optional[str]) -> bool:
|
13
|
+
"""
|
14
|
+
Handle the --workspace parameter.
|
15
|
+
|
16
|
+
Args:
|
17
|
+
workspace: Workspace directory path
|
18
|
+
|
19
|
+
Returns:
|
20
|
+
bool: True if the program should exit after this operation
|
21
|
+
"""
|
22
|
+
if workspace:
|
23
|
+
try:
|
24
|
+
console.print(f"[bold]📂 Setting workspace directory to: {workspace}[/bold]")
|
25
|
+
Config().workspace_dir = workspace
|
26
|
+
console.print(f"[bold green]✅ Workspace directory set to: {Config().workspace_dir}[/bold green]")
|
27
|
+
except ValueError as e:
|
28
|
+
console.print(f"[bold red]Error:[/bold red] {str(e)}")
|
29
|
+
sys.exit(1)
|
30
|
+
|
31
31
|
return False
|
janito/config/README.md
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
# Janito Configuration System
|
2
|
+
|
3
|
+
This directory contains the configuration system for Janito. The configuration system is designed to be modular, extensible, and easy to use.
|
4
|
+
|
5
|
+
## Directory Structure
|
6
|
+
|
7
|
+
```
|
8
|
+
janito/config/
|
9
|
+
├── __init__.py # Re-exports and backward compatibility
|
10
|
+
├── README.md # This file
|
11
|
+
├── core/ # Core configuration functionality
|
12
|
+
│ ├── __init__.py # Re-exports core components
|
13
|
+
│ ├── singleton.py # Singleton implementation
|
14
|
+
│ ├── properties.py # Property getters and setters
|
15
|
+
│ └── file_operations.py # File I/O operations
|
16
|
+
├── profiles/ # Profile management
|
17
|
+
│ ├── __init__.py # Re-exports profile components
|
18
|
+
│ ├── definitions.py # Profile definitions
|
19
|
+
│ └── manager.py # Profile management functions
|
20
|
+
└── cli/ # CLI integration
|
21
|
+
├── __init__.py # Re-exports CLI components
|
22
|
+
├── commands.py # Command handling functions
|
23
|
+
└── validators.py # Input validation functions
|
24
|
+
```
|
25
|
+
|
26
|
+
## Core Components
|
27
|
+
|
28
|
+
The core configuration functionality is implemented in the `core` directory:
|
29
|
+
|
30
|
+
- `singleton.py`: Implements the `Config` class as a singleton to ensure only one instance exists
|
31
|
+
- `properties.py`: Contains property getters and setters for the `Config` class
|
32
|
+
- `file_operations.py`: Handles file I/O operations for loading and saving configuration files
|
33
|
+
|
34
|
+
## Profiles
|
35
|
+
|
36
|
+
The `profiles` directory contains functionality related to parameter profiles:
|
37
|
+
|
38
|
+
- `definitions.py`: Defines predefined parameter profiles (precise, balanced, conversational, creative, technical)
|
39
|
+
- `manager.py`: Provides functions for managing profiles, including getting available profiles and creating custom profiles
|
40
|
+
|
41
|
+
## CLI Integration
|
42
|
+
|
43
|
+
The `cli` directory contains functionality related to CLI integration:
|
44
|
+
|
45
|
+
- `commands.py`: Implements command handling functions for configuration-related CLI commands
|
46
|
+
- `validators.py`: Provides validation functions for configuration inputs
|
47
|
+
|
48
|
+
## Usage
|
49
|
+
|
50
|
+
### Basic Usage
|
51
|
+
|
52
|
+
```python
|
53
|
+
from janito.config import Config
|
54
|
+
|
55
|
+
# Get the singleton instance
|
56
|
+
config = Config()
|
57
|
+
|
58
|
+
# Access configuration properties
|
59
|
+
workspace_dir = config.workspace_dir
|
60
|
+
temperature = config.temperature
|
61
|
+
role = config.role
|
62
|
+
|
63
|
+
# Set configuration properties
|
64
|
+
config.temperature = 0.7 # Set runtime value only
|
65
|
+
config.temperature = (0.7, "local") # Set in local config
|
66
|
+
config.temperature = (0.7, "global") # Set in global config
|
67
|
+
```
|
68
|
+
|
69
|
+
### Working with Profiles
|
70
|
+
|
71
|
+
```python
|
72
|
+
from janito.config import Config, get_available_profiles, get_profile
|
73
|
+
|
74
|
+
# Get available profiles
|
75
|
+
profiles = get_available_profiles()
|
76
|
+
for name, data in profiles.items():
|
77
|
+
print(f"{name}: {data['description']}")
|
78
|
+
|
79
|
+
# Get a specific profile
|
80
|
+
technical_profile = get_profile("technical")
|
81
|
+
print(f"Temperature: {technical_profile['temperature']}")
|
82
|
+
|
83
|
+
# Set a profile
|
84
|
+
config = Config()
|
85
|
+
config.set_profile("creative", "local")
|
86
|
+
```
|
87
|
+
|
88
|
+
### Configuration Files
|
89
|
+
|
90
|
+
The configuration system uses two configuration files:
|
91
|
+
|
92
|
+
- Global configuration file: `~/.janito/config.json`
|
93
|
+
- Local configuration file: `.janito/config.json` (in the current workspace directory)
|
94
|
+
|
95
|
+
Local configuration overrides global configuration when both are present.
|
96
|
+
|
97
|
+
## Extending the Configuration System
|
98
|
+
|
99
|
+
To add a new configuration property:
|
100
|
+
|
101
|
+
1. Add a property getter and setter in `core/properties.py`
|
102
|
+
2. Update the `_apply_config` method in `core/singleton.py` to handle the new property
|
103
|
+
3. Add validation in `cli/validators.py` if needed
|
104
|
+
4. Update the command handling in `cli/commands.py` to support the new property
|
@@ -0,0 +1,16 @@
|
|
1
|
+
"""
|
2
|
+
Configuration module for Janito.
|
3
|
+
Provides a singleton Config class to access configuration values.
|
4
|
+
Supports both local and global configuration with merging functionality.
|
5
|
+
"""
|
6
|
+
from .core.singleton import Config
|
7
|
+
from .profiles.definitions import PROFILES
|
8
|
+
from .profiles.manager import get_available_profiles, get_profile
|
9
|
+
|
10
|
+
# Convenience function to get the config instance
|
11
|
+
def get_config() -> Config:
|
12
|
+
"""Get the singleton Config instance."""
|
13
|
+
return Config()
|
14
|
+
|
15
|
+
# Re-export the Config class for backward compatibility
|
16
|
+
__all__ = ["Config", "PROFILES", "get_config", "get_available_profiles", "get_profile"]
|
@@ -0,0 +1,28 @@
|
|
1
|
+
"""
|
2
|
+
CLI integration for Janito configuration.
|
3
|
+
Provides command handling and validation for configuration-related CLI commands.
|
4
|
+
"""
|
5
|
+
from .commands import (
|
6
|
+
handle_reset_config,
|
7
|
+
handle_reset_local_config,
|
8
|
+
handle_reset_global_config,
|
9
|
+
handle_show_config,
|
10
|
+
handle_set_api_key,
|
11
|
+
handle_set_local_config,
|
12
|
+
handle_set_global_config,
|
13
|
+
handle_config_commands
|
14
|
+
)
|
15
|
+
from .validators import validate_temperature, validate_boolean_value
|
16
|
+
|
17
|
+
__all__ = [
|
18
|
+
"handle_reset_config",
|
19
|
+
"handle_reset_local_config",
|
20
|
+
"handle_reset_global_config",
|
21
|
+
"handle_show_config",
|
22
|
+
"handle_set_api_key",
|
23
|
+
"handle_set_local_config",
|
24
|
+
"handle_set_global_config",
|
25
|
+
"handle_config_commands",
|
26
|
+
"validate_temperature",
|
27
|
+
"validate_boolean_value"
|
28
|
+
]
|
@@ -0,0 +1,397 @@
|
|
1
|
+
"""
|
2
|
+
Command handling functions for configuration-related CLI commands.
|
3
|
+
"""
|
4
|
+
import sys
|
5
|
+
import os
|
6
|
+
from pathlib import Path
|
7
|
+
from typing import Optional, Dict, Any
|
8
|
+
import typer
|
9
|
+
from rich.console import Console
|
10
|
+
|
11
|
+
from ..core.singleton import Config
|
12
|
+
from .validators import validate_temperature, validate_boolean_value, validate_config_key_value
|
13
|
+
|
14
|
+
console = Console()
|
15
|
+
|
16
|
+
def handle_reset_config(reset_config: bool, ctx: typer.Context, query: Optional[str]) -> bool:
|
17
|
+
"""
|
18
|
+
Handle the --reset-config parameter (deprecated, kept for backward compatibility).
|
19
|
+
This function now does nothing as --reset-config has been replaced by --reset-local-config and --reset-global-config.
|
20
|
+
|
21
|
+
Args:
|
22
|
+
reset_config: Whether to reset the configuration (ignored)
|
23
|
+
ctx: Typer context
|
24
|
+
query: Query string
|
25
|
+
|
26
|
+
Returns:
|
27
|
+
bool: Always returns False
|
28
|
+
"""
|
29
|
+
# This function is kept for backward compatibility but does nothing
|
30
|
+
# Users should use --reset-local-config or --reset-global-config instead
|
31
|
+
return False
|
32
|
+
|
33
|
+
def handle_reset_local_config(reset_local_config: bool, ctx: typer.Context, query: Optional[str]) -> bool:
|
34
|
+
"""
|
35
|
+
Handle the --reset-local-config parameter.
|
36
|
+
This removes the local configuration file (.janito/config.json) in the current workspace.
|
37
|
+
|
38
|
+
Args:
|
39
|
+
reset_local_config: Whether to reset the local configuration
|
40
|
+
ctx: Typer context
|
41
|
+
query: Query string
|
42
|
+
|
43
|
+
Returns:
|
44
|
+
bool: True if the program should exit after this operation
|
45
|
+
"""
|
46
|
+
if reset_local_config:
|
47
|
+
try:
|
48
|
+
config_path = Path(Config().workspace_dir) / ".janito" / "config.json"
|
49
|
+
if Config().reset_local_config():
|
50
|
+
console.print(f"[bold green]✅ Local configuration reset[/bold green]")
|
51
|
+
else:
|
52
|
+
console.print(f"[bold yellow]⚠️ No local configuration found[/bold yellow]")
|
53
|
+
except Exception as e:
|
54
|
+
console.print(f"[bold red]Error removing configuration file:[/bold red] {str(e)}")
|
55
|
+
|
56
|
+
# Exit after resetting config if no other operation is requested
|
57
|
+
return ctx.invoked_subcommand is None and not query
|
58
|
+
|
59
|
+
return False
|
60
|
+
|
61
|
+
def handle_reset_global_config(reset_global_config: bool, ctx: typer.Context, query: Optional[str]) -> bool:
|
62
|
+
"""
|
63
|
+
Handle the --reset-global-config parameter.
|
64
|
+
This removes the global configuration file (~/.janito/config.json) in the user's home directory.
|
65
|
+
|
66
|
+
Args:
|
67
|
+
reset_global_config: Whether to reset the global configuration
|
68
|
+
ctx: Typer context
|
69
|
+
query: Query string
|
70
|
+
|
71
|
+
Returns:
|
72
|
+
bool: True if the program should exit after this operation
|
73
|
+
"""
|
74
|
+
if reset_global_config:
|
75
|
+
try:
|
76
|
+
config_path = Path.home() / ".janito" / "config.json"
|
77
|
+
if Config().reset_global_config():
|
78
|
+
console.print(f"[bold green]✅ Global configuration reset[/bold green]")
|
79
|
+
else:
|
80
|
+
console.print(f"[bold yellow]⚠️ No global configuration found[/bold yellow]")
|
81
|
+
except Exception as e:
|
82
|
+
console.print(f"[bold red]Error removing configuration file:[/bold red] {str(e)}")
|
83
|
+
|
84
|
+
# Exit after resetting config if no other operation is requested
|
85
|
+
return ctx.invoked_subcommand is None and not query
|
86
|
+
|
87
|
+
return False
|
88
|
+
|
89
|
+
def handle_show_config(show_config: bool, ctx: typer.Context, query: Optional[str]) -> bool:
|
90
|
+
"""
|
91
|
+
Handle the --show-config parameter.
|
92
|
+
|
93
|
+
Args:
|
94
|
+
show_config: Whether to show the configuration
|
95
|
+
ctx: Typer context
|
96
|
+
query: Query string
|
97
|
+
|
98
|
+
Returns:
|
99
|
+
bool: True if the program should exit after this operation
|
100
|
+
"""
|
101
|
+
if show_config:
|
102
|
+
config = Config()
|
103
|
+
console.print("[bold blue]⚙️ Current Configuration:[/bold blue]")
|
104
|
+
|
105
|
+
# Show configuration file paths
|
106
|
+
local_config_path = Path(config.workspace_dir) / ".janito" / "config.json"
|
107
|
+
global_config_path = Path.home() / ".janito" / "config.json"
|
108
|
+
console.print(f"[bold]📁 Local Configuration File:[/bold] {local_config_path}")
|
109
|
+
console.print(f"[bold]🏠 Global Configuration File:[/bold] {global_config_path}")
|
110
|
+
|
111
|
+
# Show API key status
|
112
|
+
api_key_global = Config().get_api_key()
|
113
|
+
if api_key_global:
|
114
|
+
console.print(f"[bold]🔑 API Key:[/bold] [green]Set in global config[/green]")
|
115
|
+
else:
|
116
|
+
console.print(f"[bold]🔑 API Key:[/bold] [red]Not set[/red]")
|
117
|
+
|
118
|
+
# Show merged configuration (effective settings)
|
119
|
+
console.print("\n[bold blue]🔄 Merged Configuration (Effective Settings):[/bold blue]")
|
120
|
+
console.print(f"[bold]🔊 Verbose Mode:[/bold] {'Enabled' if config.verbose else 'Disabled'}")
|
121
|
+
console.print(f"[bold]❓ Ask Mode:[/bold] {'Enabled' if config.ask_mode else 'Disabled'} [dim](runtime-only setting)[/dim]")
|
122
|
+
console.print(f"[bold]📊 Show Usage Report:[/bold] {'Enabled' if config.show_usage_report else 'Disabled'}")
|
123
|
+
console.print(f"[bold]👤 Role:[/bold] [bold white on blue]{config.role}[/bold white on blue]")
|
124
|
+
console.print(f"[bold]🌡️ Temperature:[/bold] {config.temperature}")
|
125
|
+
|
126
|
+
# Show profile information if one is set
|
127
|
+
if config.profile:
|
128
|
+
profile_data = config.get_available_profiles()[config.profile]
|
129
|
+
console.print(f"[bold]📋 Active Profile:[/bold] {config.profile} - {profile_data['description']}")
|
130
|
+
|
131
|
+
# Show local configuration
|
132
|
+
local_config = config.get_local_config()
|
133
|
+
if local_config:
|
134
|
+
console.print("\n[bold blue]📁 Local Configuration:[/bold blue]")
|
135
|
+
for key, value in local_config.items():
|
136
|
+
# Don't show API key or runtime-only settings like ask_mode
|
137
|
+
if key != "api_key" and key != "ask_mode":
|
138
|
+
console.print(f"[bold]🔹 {key}:[/bold] {value}")
|
139
|
+
else:
|
140
|
+
console.print("\n[bold blue]📁 Local Configuration:[/bold blue] [dim]Empty[/dim]")
|
141
|
+
|
142
|
+
# Show global configuration
|
143
|
+
global_config = config.get_global_config()
|
144
|
+
if global_config:
|
145
|
+
console.print("\n[bold blue]🏠 Global Configuration:[/bold blue]")
|
146
|
+
for key, value in global_config.items():
|
147
|
+
# Don't show API key or runtime-only settings like ask_mode
|
148
|
+
if key != "api_key" and key != "ask_mode":
|
149
|
+
console.print(f"[bold]🔹 {key}:[/bold] {value}")
|
150
|
+
else:
|
151
|
+
console.print("\n[bold blue]🏠 Global Configuration:[/bold blue] [dim]Empty[/dim]")
|
152
|
+
|
153
|
+
# Show available profiles
|
154
|
+
profiles = config.get_available_profiles()
|
155
|
+
if profiles:
|
156
|
+
console.print("\n[bold blue]📋 Available Parameter Profiles:[/bold blue]")
|
157
|
+
for name, data in profiles.items():
|
158
|
+
console.print(f"[bold]🔹 {name}[/bold] - {data['description']}")
|
159
|
+
|
160
|
+
# Exit if this was the only operation requested
|
161
|
+
return ctx.invoked_subcommand is None and not query
|
162
|
+
|
163
|
+
return False
|
164
|
+
|
165
|
+
def handle_set_api_key(set_api_key: Optional[str], ctx: typer.Context, query: Optional[str]) -> bool:
|
166
|
+
"""
|
167
|
+
Handle the --set-api-key parameter.
|
168
|
+
|
169
|
+
Args:
|
170
|
+
set_api_key: API key
|
171
|
+
ctx: Typer context
|
172
|
+
query: Query string
|
173
|
+
|
174
|
+
Returns:
|
175
|
+
bool: True if the program should exit after this operation
|
176
|
+
"""
|
177
|
+
if set_api_key is not None:
|
178
|
+
try:
|
179
|
+
Config().set_api_key(set_api_key)
|
180
|
+
console.print(f"[bold green]✅ API key saved[/bold green]")
|
181
|
+
|
182
|
+
# Exit after setting API key if no other operation is requested
|
183
|
+
return ctx.invoked_subcommand is None and not query
|
184
|
+
except Exception as e:
|
185
|
+
console.print(f"[bold red]Error:[/bold red] {str(e)}")
|
186
|
+
sys.exit(1)
|
187
|
+
|
188
|
+
return False
|
189
|
+
|
190
|
+
def _handle_config_setting(key: str, value: str, config_type: str = "local") -> bool:
|
191
|
+
"""
|
192
|
+
Handle setting a configuration value.
|
193
|
+
|
194
|
+
Args:
|
195
|
+
key: Configuration key
|
196
|
+
value: Configuration value
|
197
|
+
config_type: Type of configuration to update ("local" or "global")
|
198
|
+
|
199
|
+
Returns:
|
200
|
+
bool: True if the operation was successful
|
201
|
+
"""
|
202
|
+
try:
|
203
|
+
if key == "profile":
|
204
|
+
try:
|
205
|
+
Config().set_profile(value, config_type)
|
206
|
+
profile_data = Config().get_available_profiles()[value.lower()]
|
207
|
+
console.print(f"[bold green]✅ Profile set to '{value.lower()}'[/bold green]")
|
208
|
+
return True
|
209
|
+
except ValueError as e:
|
210
|
+
console.print(f"[bold red]Error:[/bold red] {str(e)}")
|
211
|
+
return False
|
212
|
+
elif key == "temperature":
|
213
|
+
is_valid, result = validate_temperature(value)
|
214
|
+
if not is_valid:
|
215
|
+
console.print(f"[bold red]Error:[/bold red] {result}")
|
216
|
+
return False
|
217
|
+
|
218
|
+
if config_type == "local":
|
219
|
+
Config().temperature = result, "local"
|
220
|
+
else:
|
221
|
+
Config().temperature = result, "global"
|
222
|
+
console.print(f"[bold green]✅ Temperature set to {result}[/bold green]")
|
223
|
+
return True
|
224
|
+
# top_k and top_p are now only accessible through profiles
|
225
|
+
elif key == "role":
|
226
|
+
if config_type == "local":
|
227
|
+
Config().role = value, "local"
|
228
|
+
else:
|
229
|
+
Config().role = value, "global"
|
230
|
+
console.print(f"[bold green]✅ Role set to '{value}'[/bold green]")
|
231
|
+
return True
|
232
|
+
elif key == "ask_mode":
|
233
|
+
is_valid, result = validate_boolean_value(value)
|
234
|
+
if not is_valid:
|
235
|
+
console.print(f"[bold red]Error:[/bold red] {result}")
|
236
|
+
return False
|
237
|
+
|
238
|
+
# ask_mode is a runtime-only setting, inform the user
|
239
|
+
console.print(f"[bold yellow]⚠️ Ask mode is a runtime-only setting and cannot be stored in configuration.[/bold yellow]")
|
240
|
+
console.print(f"[bold yellow]Use the --ask flag when running the command instead.[/bold yellow]")
|
241
|
+
return True
|
242
|
+
elif key == "show_usage_report":
|
243
|
+
is_valid, result = validate_boolean_value(value)
|
244
|
+
if not is_valid:
|
245
|
+
console.print(f"[bold red]Error:[/bold red] {result}")
|
246
|
+
return False
|
247
|
+
|
248
|
+
if config_type == "local":
|
249
|
+
Config().show_usage_report = result, "local"
|
250
|
+
else:
|
251
|
+
Config().show_usage_report = result, "global"
|
252
|
+
console.print(f"[bold green]✅ Show usage report set to {result}[/bold green]")
|
253
|
+
return True
|
254
|
+
else:
|
255
|
+
# For other keys, set them directly in the configuration
|
256
|
+
if config_type == "local":
|
257
|
+
Config().set_local_config(key, value)
|
258
|
+
else:
|
259
|
+
Config().set_global_config(key, value)
|
260
|
+
console.print(f"[bold green]✅ {key} set to '{value}'[/bold green]")
|
261
|
+
return True
|
262
|
+
except Exception as e:
|
263
|
+
console.print(f"[bold red]Error:[/bold red] {str(e)}")
|
264
|
+
return False
|
265
|
+
|
266
|
+
|
267
|
+
def handle_set_local_config(config_str: Optional[str], ctx: typer.Context, query: Optional[str]) -> bool:
|
268
|
+
"""
|
269
|
+
Handle the --set-local-config parameter.
|
270
|
+
|
271
|
+
Args:
|
272
|
+
config_str: Configuration string in format 'key=value'
|
273
|
+
ctx: Typer context
|
274
|
+
query: Query string
|
275
|
+
|
276
|
+
Returns:
|
277
|
+
bool: True if the program should exit after this operation
|
278
|
+
"""
|
279
|
+
if config_str is not None:
|
280
|
+
is_valid, result = validate_config_key_value(config_str)
|
281
|
+
if not is_valid:
|
282
|
+
console.print(f"[bold red]Error:[/bold red] {result}")
|
283
|
+
return ctx.invoked_subcommand is None and not query
|
284
|
+
|
285
|
+
key, value = result
|
286
|
+
_handle_config_setting(key, value, "local")
|
287
|
+
|
288
|
+
# Exit after applying config changes if no other operation is requested
|
289
|
+
return ctx.invoked_subcommand is None and not query
|
290
|
+
|
291
|
+
return False
|
292
|
+
|
293
|
+
def handle_set_global_config(config_str: Optional[str], ctx: typer.Context, query: Optional[str]) -> bool:
|
294
|
+
"""
|
295
|
+
Handle the --set-global-config parameter.
|
296
|
+
|
297
|
+
Args:
|
298
|
+
config_str: Configuration string in format 'key=value'
|
299
|
+
ctx: Typer context
|
300
|
+
query: Query string
|
301
|
+
|
302
|
+
Returns:
|
303
|
+
bool: True if the program should exit after this operation
|
304
|
+
"""
|
305
|
+
if config_str is not None:
|
306
|
+
is_valid, result = validate_config_key_value(config_str)
|
307
|
+
if not is_valid:
|
308
|
+
console.print(f"[bold red]Error:[/bold red] {result}")
|
309
|
+
return ctx.invoked_subcommand is None and not query
|
310
|
+
|
311
|
+
key, value = result
|
312
|
+
_handle_config_setting(key, value, "global")
|
313
|
+
|
314
|
+
# Exit after applying config changes if no other operation is requested
|
315
|
+
return ctx.invoked_subcommand is None and not query
|
316
|
+
|
317
|
+
return False
|
318
|
+
|
319
|
+
def handle_config_commands(
|
320
|
+
ctx: typer.Context,
|
321
|
+
reset_config: bool,
|
322
|
+
reset_local_config: bool = False,
|
323
|
+
reset_global_config: bool = False,
|
324
|
+
workspace: Optional[str] = None,
|
325
|
+
show_config: bool = False,
|
326
|
+
profile: Optional[str] = None,
|
327
|
+
role: Optional[str] = None,
|
328
|
+
set_api_key: Optional[str] = None,
|
329
|
+
set_local_config: Optional[str] = None,
|
330
|
+
set_global_config: Optional[str] = None,
|
331
|
+
query: Optional[str] = None,
|
332
|
+
continue_flag: Optional[str] = None,
|
333
|
+
history_flag: bool = False,
|
334
|
+
history_count: Optional[int] = None
|
335
|
+
) -> bool:
|
336
|
+
"""
|
337
|
+
Handle all configuration-related commands.
|
338
|
+
|
339
|
+
Args:
|
340
|
+
ctx: Typer context
|
341
|
+
reset_config: Deprecated parameter kept for backward compatibility
|
342
|
+
reset_local_config: Whether to reset the local configuration
|
343
|
+
reset_global_config: Whether to reset the global configuration
|
344
|
+
workspace: Workspace directory path
|
345
|
+
show_config: Whether to show the configuration
|
346
|
+
profile: Profile name
|
347
|
+
role: Role name
|
348
|
+
set_api_key: API key
|
349
|
+
set_local_config: Configuration string in format 'key=value' for local config
|
350
|
+
set_global_config: Configuration string in format 'key=value' for global config
|
351
|
+
query: Query string
|
352
|
+
continue_flag: Optional string that can be empty (flag only) or contain a chat ID
|
353
|
+
history_flag: Whether to show conversation history (--history flag)
|
354
|
+
history_count: Number of history entries to display (value after --history)
|
355
|
+
|
356
|
+
Returns:
|
357
|
+
bool: True if the program should exit after these operations
|
358
|
+
"""
|
359
|
+
# Import these here to avoid circular imports
|
360
|
+
from janito.cli.commands.workspace import handle_workspace
|
361
|
+
from janito.cli.commands.profile import handle_profile, handle_role
|
362
|
+
from janito.cli.commands.history import handle_history
|
363
|
+
|
364
|
+
# Handle each command and check if we should exit after it
|
365
|
+
if handle_reset_config(reset_config, ctx, query):
|
366
|
+
return True
|
367
|
+
|
368
|
+
if handle_reset_local_config(reset_local_config, ctx, query):
|
369
|
+
return True
|
370
|
+
|
371
|
+
if handle_reset_global_config(reset_global_config, ctx, query):
|
372
|
+
return True
|
373
|
+
|
374
|
+
handle_workspace(workspace)
|
375
|
+
|
376
|
+
if handle_show_config(show_config, ctx, query):
|
377
|
+
return True
|
378
|
+
|
379
|
+
if handle_profile(profile, ctx, query):
|
380
|
+
return True
|
381
|
+
|
382
|
+
if handle_role(role, ctx, query):
|
383
|
+
return True
|
384
|
+
|
385
|
+
if handle_set_api_key(set_api_key, ctx, query):
|
386
|
+
return True
|
387
|
+
|
388
|
+
if handle_set_local_config(set_local_config, ctx, query):
|
389
|
+
return True
|
390
|
+
|
391
|
+
if handle_set_global_config(set_global_config, ctx, query):
|
392
|
+
return True
|
393
|
+
|
394
|
+
if handle_history(history_flag, history_count, ctx, query):
|
395
|
+
return True
|
396
|
+
|
397
|
+
return False
|
@@ -0,0 +1,77 @@
|
|
1
|
+
"""
|
2
|
+
Validation functions for configuration-related CLI commands.
|
3
|
+
"""
|
4
|
+
from typing import Tuple, Any, Union, Optional
|
5
|
+
|
6
|
+
def validate_temperature(value: str) -> Tuple[bool, Union[float, str]]:
|
7
|
+
"""
|
8
|
+
Validate a temperature value from a string input.
|
9
|
+
|
10
|
+
Args:
|
11
|
+
value: String representation of a temperature value
|
12
|
+
|
13
|
+
Returns:
|
14
|
+
Tuple of (is_valid, result)
|
15
|
+
If valid, result is the float value
|
16
|
+
If invalid, result is an error message
|
17
|
+
"""
|
18
|
+
try:
|
19
|
+
temp_value = float(value)
|
20
|
+
if temp_value < 0.0 or temp_value > 1.0:
|
21
|
+
return False, "Temperature must be between 0.0 and 1.0"
|
22
|
+
return True, temp_value
|
23
|
+
except ValueError:
|
24
|
+
return False, f"Invalid temperature value: {value}. Must be a float between 0.0 and 1.0."
|
25
|
+
|
26
|
+
def validate_boolean_value(value: str) -> Tuple[bool, Union[bool, str]]:
|
27
|
+
"""
|
28
|
+
Validate a boolean value from a string input.
|
29
|
+
|
30
|
+
Args:
|
31
|
+
value: String representation of a boolean value
|
32
|
+
|
33
|
+
Returns:
|
34
|
+
Tuple of (is_valid, result)
|
35
|
+
If valid, result is the boolean value
|
36
|
+
If invalid, result is an error message
|
37
|
+
"""
|
38
|
+
try:
|
39
|
+
lower_value = value.lower()
|
40
|
+
if lower_value in ["true", "yes", "1", "on", "y"]:
|
41
|
+
return True, True
|
42
|
+
elif lower_value in ["false", "no", "0", "off", "n"]:
|
43
|
+
return True, False
|
44
|
+
else:
|
45
|
+
return False, f"Invalid boolean value: {value}. Use 'true', 'false', 'yes', 'no', '1', '0', 'on', or 'off'."
|
46
|
+
except Exception:
|
47
|
+
return False, f"Invalid boolean value: {value}. Use 'true', 'false', 'yes', 'no', '1', '0', 'on', or 'off'."
|
48
|
+
|
49
|
+
def validate_config_key_value(config_str: str) -> Tuple[bool, Union[Tuple[str, str], str]]:
|
50
|
+
"""
|
51
|
+
Validate a configuration key-value pair from a string input.
|
52
|
+
|
53
|
+
Args:
|
54
|
+
config_str: String in the format 'key=value'
|
55
|
+
|
56
|
+
Returns:
|
57
|
+
Tuple of (is_valid, result)
|
58
|
+
If valid, result is a tuple of (key, value)
|
59
|
+
If invalid, result is an error message
|
60
|
+
"""
|
61
|
+
try:
|
62
|
+
# Parse the config string
|
63
|
+
config_parts = config_str.split("=", 1)
|
64
|
+
if len(config_parts) != 2:
|
65
|
+
return False, "Invalid configuration format. Use 'key=value' format."
|
66
|
+
|
67
|
+
key = config_parts[0].strip()
|
68
|
+
value = config_parts[1].strip()
|
69
|
+
|
70
|
+
# Remove quotes if present
|
71
|
+
if (value.startswith("'") and value.endswith("'")) or \
|
72
|
+
(value.startswith('"') and value.endswith('"')):
|
73
|
+
value = value[1:-1]
|
74
|
+
|
75
|
+
return True, (key, value)
|
76
|
+
except Exception as e:
|
77
|
+
return False, f"Invalid configuration format: {str(e)}"
|