-
Notifications
You must be signed in to change notification settings - Fork 365
Expand file tree
/
Copy pathconfig.py
More file actions
221 lines (169 loc) · 7.1 KB
/
config.py
File metadata and controls
221 lines (169 loc) · 7.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
"""Configuration management module
Provides unified configuration loading and management functionality
"""
from pathlib import Path
from typing import Union, Optional
import yaml
from pydantic import BaseModel, Field
class RetryConfig(BaseModel):
"""Retry configuration"""
enabled: bool = True
max_retries: int = 3
initial_delay: float = 1.0
max_delay: float = 60.0
exponential_base: float = 2.0
class LLMConfig(BaseModel):
"""LLM configuration"""
api_key: str
api_base: str = "https://api.minimax.io"
model: str = "MiniMax-M2.5"
provider: str = "anthropic" # "anthropic" or "openai"
retry: RetryConfig = Field(default_factory=RetryConfig)
class AgentConfig(BaseModel):
"""Agent configuration"""
max_steps: int = 50
workspace_dir: str = "./workspace"
system_prompt_path: str = "system_prompt.md"
class MCPConfig(BaseModel):
"""MCP (Model Context Protocol) timeout configuration"""
connect_timeout: float = 10.0 # Connection timeout (seconds)
execute_timeout: float = 60.0 # Tool execution timeout (seconds)
sse_read_timeout: float = 120.0 # SSE read timeout (seconds)
class ToolsConfig(BaseModel):
"""Tools configuration"""
# Basic tools (file operations, bash)
enable_file_tools: bool = True
enable_bash: bool = True
enable_note: bool = True
# Skills
enable_skills: bool = True
skills_dir: str = "./skills"
# MCP tools
enable_mcp: bool = True
mcp_config_path: str = "mcp.json"
mcp: MCPConfig = Field(default_factory=MCPConfig)
class Config(BaseModel):
"""Main configuration class"""
llm: LLMConfig
agent: AgentConfig
tools: ToolsConfig
@classmethod
def load(cls) -> "Config":
"""Load configuration from the default search path."""
config_path = cls.get_default_config_path()
if not config_path.exists():
raise FileNotFoundError("Configuration file not found. Run scripts/setup-config.sh or place config.yaml in mini_agent/config/.")
return cls.from_yaml(config_path)
@classmethod
def from_yaml(cls, config_path: Union[str, Path]) -> "Config":
"""Load configuration from YAML file
Args:
config_path: Configuration file path
Returns:
Config instance
Raises:
FileNotFoundError: Configuration file does not exist
ValueError: Invalid configuration format or missing required fields
"""
config_path = Path(config_path)
if not config_path.exists():
raise FileNotFoundError(f"Configuration file does not exist: {config_path}")
with open(config_path, encoding="utf-8") as f:
data = yaml.safe_load(f)
if not data:
raise ValueError("Configuration file is empty")
# Parse LLM configuration
if "api_key" not in data:
raise ValueError("Configuration file missing required field: api_key")
if not data["api_key"] or data["api_key"] == "YOUR_API_KEY_HERE":
raise ValueError("Please configure a valid API Key")
# Parse retry configuration
retry_data = data.get("retry", {})
retry_config = RetryConfig(
enabled=retry_data.get("enabled", True),
max_retries=retry_data.get("max_retries", 3),
initial_delay=retry_data.get("initial_delay", 1.0),
max_delay=retry_data.get("max_delay", 60.0),
exponential_base=retry_data.get("exponential_base", 2.0),
)
llm_config = LLMConfig(
api_key=data["api_key"],
api_base=data.get("api_base", "https://api.minimax.io"),
model=data.get("model", "MiniMax-M2.5"),
provider=data.get("provider", "anthropic"),
retry=retry_config,
)
# Parse Agent configuration
agent_config = AgentConfig(
max_steps=data.get("max_steps", 50),
workspace_dir=data.get("workspace_dir", "./workspace"),
system_prompt_path=data.get("system_prompt_path", "system_prompt.md"),
)
# Parse tools configuration
tools_data = data.get("tools", {})
# Parse MCP configuration
mcp_data = tools_data.get("mcp", {})
mcp_config = MCPConfig(
connect_timeout=mcp_data.get("connect_timeout", 10.0),
execute_timeout=mcp_data.get("execute_timeout", 60.0),
sse_read_timeout=mcp_data.get("sse_read_timeout", 120.0),
)
tools_config = ToolsConfig(
enable_file_tools=tools_data.get("enable_file_tools", True),
enable_bash=tools_data.get("enable_bash", True),
enable_note=tools_data.get("enable_note", True),
enable_skills=tools_data.get("enable_skills", True),
skills_dir=tools_data.get("skills_dir", "./skills"),
enable_mcp=tools_data.get("enable_mcp", True),
mcp_config_path=tools_data.get("mcp_config_path", "mcp.json"),
mcp=mcp_config,
)
return cls(
llm=llm_config,
agent=agent_config,
tools=tools_config,
)
@staticmethod
def get_package_dir() -> Path:
"""Get the package installation directory
Returns:
Path to the mini_agent package directory
"""
# Get the directory where this config.py file is located
return Path(__file__).parent
@classmethod
def find_config_file(cls, filename: str) -> Optional[Path]:
"""Find configuration file with priority order
Search for config file in the following order of priority:
1) mini_agent/config/{filename} in current directory (development mode)
2) ~/.mini-agent/config/{filename} in user home directory
3) {package}/mini_agent/config/{filename} in package installation directory
Args:
filename: Configuration file name (e.g., "config.yaml", "mcp.json", "system_prompt.md")
Returns:
Path to found config file, or None if not found
"""
# Priority 1: Development mode - current directory's config/ subdirectory
dev_config = Path.cwd() / "mini_agent" / "config" / filename
if dev_config.exists():
return dev_config
# Priority 2: User config directory
user_config = Path.home() / ".mini-agent" / "config" / filename
if user_config.exists():
return user_config
# Priority 3: Package installation directory's config/ subdirectory
package_config = cls.get_package_dir() / "config" / filename
if package_config.exists():
return package_config
return None
@classmethod
def get_default_config_path(cls) -> Path:
"""Get the default config file path with priority search
Returns:
Path to config.yaml (prioritizes: dev config/ > user config/ > package config/)
"""
config_path = cls.find_config_file("config.yaml")
if config_path:
return config_path
# Fallback to package config directory for error message purposes
return cls.get_package_dir() / "config" / "config.yaml"