kweaver-dolphin 0.1.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.
- DolphinLanguageSDK/__init__.py +58 -0
- dolphin/__init__.py +62 -0
- dolphin/cli/__init__.py +20 -0
- dolphin/cli/args/__init__.py +9 -0
- dolphin/cli/args/parser.py +567 -0
- dolphin/cli/builtin_agents/__init__.py +22 -0
- dolphin/cli/commands/__init__.py +4 -0
- dolphin/cli/interrupt/__init__.py +8 -0
- dolphin/cli/interrupt/handler.py +205 -0
- dolphin/cli/interrupt/keyboard.py +82 -0
- dolphin/cli/main.py +49 -0
- dolphin/cli/multimodal/__init__.py +34 -0
- dolphin/cli/multimodal/clipboard.py +327 -0
- dolphin/cli/multimodal/handler.py +249 -0
- dolphin/cli/multimodal/image_processor.py +214 -0
- dolphin/cli/multimodal/input_parser.py +149 -0
- dolphin/cli/runner/__init__.py +8 -0
- dolphin/cli/runner/runner.py +989 -0
- dolphin/cli/ui/__init__.py +10 -0
- dolphin/cli/ui/console.py +2795 -0
- dolphin/cli/ui/input.py +340 -0
- dolphin/cli/ui/layout.py +425 -0
- dolphin/cli/ui/stream_renderer.py +302 -0
- dolphin/cli/utils/__init__.py +8 -0
- dolphin/cli/utils/helpers.py +135 -0
- dolphin/cli/utils/version.py +49 -0
- dolphin/core/__init__.py +107 -0
- dolphin/core/agent/__init__.py +10 -0
- dolphin/core/agent/agent_state.py +69 -0
- dolphin/core/agent/base_agent.py +970 -0
- dolphin/core/code_block/__init__.py +0 -0
- dolphin/core/code_block/agent_init_block.py +0 -0
- dolphin/core/code_block/assign_block.py +98 -0
- dolphin/core/code_block/basic_code_block.py +1865 -0
- dolphin/core/code_block/explore_block.py +1327 -0
- dolphin/core/code_block/explore_block_v2.py +712 -0
- dolphin/core/code_block/explore_strategy.py +672 -0
- dolphin/core/code_block/judge_block.py +220 -0
- dolphin/core/code_block/prompt_block.py +32 -0
- dolphin/core/code_block/skill_call_deduplicator.py +291 -0
- dolphin/core/code_block/tool_block.py +129 -0
- dolphin/core/common/__init__.py +17 -0
- dolphin/core/common/constants.py +176 -0
- dolphin/core/common/enums.py +1173 -0
- dolphin/core/common/exceptions.py +133 -0
- dolphin/core/common/multimodal.py +539 -0
- dolphin/core/common/object_type.py +165 -0
- dolphin/core/common/output_format.py +432 -0
- dolphin/core/common/types.py +36 -0
- dolphin/core/config/__init__.py +16 -0
- dolphin/core/config/global_config.py +1289 -0
- dolphin/core/config/ontology_config.py +133 -0
- dolphin/core/context/__init__.py +12 -0
- dolphin/core/context/context.py +1580 -0
- dolphin/core/context/context_manager.py +161 -0
- dolphin/core/context/var_output.py +82 -0
- dolphin/core/context/variable_pool.py +356 -0
- dolphin/core/context_engineer/__init__.py +41 -0
- dolphin/core/context_engineer/config/__init__.py +5 -0
- dolphin/core/context_engineer/config/settings.py +402 -0
- dolphin/core/context_engineer/core/__init__.py +7 -0
- dolphin/core/context_engineer/core/budget_manager.py +327 -0
- dolphin/core/context_engineer/core/context_assembler.py +583 -0
- dolphin/core/context_engineer/core/context_manager.py +637 -0
- dolphin/core/context_engineer/core/tokenizer_service.py +260 -0
- dolphin/core/context_engineer/example/incremental_example.py +267 -0
- dolphin/core/context_engineer/example/traditional_example.py +334 -0
- dolphin/core/context_engineer/services/__init__.py +5 -0
- dolphin/core/context_engineer/services/compressor.py +399 -0
- dolphin/core/context_engineer/utils/__init__.py +6 -0
- dolphin/core/context_engineer/utils/context_utils.py +441 -0
- dolphin/core/context_engineer/utils/message_formatter.py +270 -0
- dolphin/core/context_engineer/utils/token_utils.py +139 -0
- dolphin/core/coroutine/__init__.py +15 -0
- dolphin/core/coroutine/context_snapshot.py +154 -0
- dolphin/core/coroutine/context_snapshot_profile.py +922 -0
- dolphin/core/coroutine/context_snapshot_store.py +268 -0
- dolphin/core/coroutine/execution_frame.py +145 -0
- dolphin/core/coroutine/execution_state_registry.py +161 -0
- dolphin/core/coroutine/resume_handle.py +101 -0
- dolphin/core/coroutine/step_result.py +101 -0
- dolphin/core/executor/__init__.py +18 -0
- dolphin/core/executor/debug_controller.py +630 -0
- dolphin/core/executor/dolphin_executor.py +1063 -0
- dolphin/core/executor/executor.py +624 -0
- dolphin/core/flags/__init__.py +27 -0
- dolphin/core/flags/definitions.py +49 -0
- dolphin/core/flags/manager.py +113 -0
- dolphin/core/hook/__init__.py +95 -0
- dolphin/core/hook/expression_evaluator.py +499 -0
- dolphin/core/hook/hook_dispatcher.py +380 -0
- dolphin/core/hook/hook_types.py +248 -0
- dolphin/core/hook/isolated_variable_pool.py +284 -0
- dolphin/core/interfaces.py +53 -0
- dolphin/core/llm/__init__.py +0 -0
- dolphin/core/llm/llm.py +495 -0
- dolphin/core/llm/llm_call.py +100 -0
- dolphin/core/llm/llm_client.py +1285 -0
- dolphin/core/llm/message_sanitizer.py +120 -0
- dolphin/core/logging/__init__.py +20 -0
- dolphin/core/logging/logger.py +526 -0
- dolphin/core/message/__init__.py +8 -0
- dolphin/core/message/compressor.py +749 -0
- dolphin/core/parser/__init__.py +8 -0
- dolphin/core/parser/parser.py +405 -0
- dolphin/core/runtime/__init__.py +10 -0
- dolphin/core/runtime/runtime_graph.py +926 -0
- dolphin/core/runtime/runtime_instance.py +446 -0
- dolphin/core/skill/__init__.py +14 -0
- dolphin/core/skill/context_retention.py +157 -0
- dolphin/core/skill/skill_function.py +686 -0
- dolphin/core/skill/skill_matcher.py +282 -0
- dolphin/core/skill/skillkit.py +700 -0
- dolphin/core/skill/skillset.py +72 -0
- dolphin/core/trajectory/__init__.py +10 -0
- dolphin/core/trajectory/recorder.py +189 -0
- dolphin/core/trajectory/trajectory.py +522 -0
- dolphin/core/utils/__init__.py +9 -0
- dolphin/core/utils/cache_kv.py +212 -0
- dolphin/core/utils/tools.py +340 -0
- dolphin/lib/__init__.py +93 -0
- dolphin/lib/debug/__init__.py +8 -0
- dolphin/lib/debug/visualizer.py +409 -0
- dolphin/lib/memory/__init__.py +28 -0
- dolphin/lib/memory/async_processor.py +220 -0
- dolphin/lib/memory/llm_calls.py +195 -0
- dolphin/lib/memory/manager.py +78 -0
- dolphin/lib/memory/sandbox.py +46 -0
- dolphin/lib/memory/storage.py +245 -0
- dolphin/lib/memory/utils.py +51 -0
- dolphin/lib/ontology/__init__.py +12 -0
- dolphin/lib/ontology/basic/__init__.py +0 -0
- dolphin/lib/ontology/basic/base.py +102 -0
- dolphin/lib/ontology/basic/concept.py +130 -0
- dolphin/lib/ontology/basic/object.py +11 -0
- dolphin/lib/ontology/basic/relation.py +63 -0
- dolphin/lib/ontology/datasource/__init__.py +27 -0
- dolphin/lib/ontology/datasource/datasource.py +66 -0
- dolphin/lib/ontology/datasource/oracle_datasource.py +338 -0
- dolphin/lib/ontology/datasource/sql.py +845 -0
- dolphin/lib/ontology/mapping.py +177 -0
- dolphin/lib/ontology/ontology.py +733 -0
- dolphin/lib/ontology/ontology_context.py +16 -0
- dolphin/lib/ontology/ontology_manager.py +107 -0
- dolphin/lib/skill_results/__init__.py +31 -0
- dolphin/lib/skill_results/cache_backend.py +559 -0
- dolphin/lib/skill_results/result_processor.py +181 -0
- dolphin/lib/skill_results/result_reference.py +179 -0
- dolphin/lib/skill_results/skillkit_hook.py +324 -0
- dolphin/lib/skill_results/strategies.py +328 -0
- dolphin/lib/skill_results/strategy_registry.py +150 -0
- dolphin/lib/skillkits/__init__.py +44 -0
- dolphin/lib/skillkits/agent_skillkit.py +155 -0
- dolphin/lib/skillkits/cognitive_skillkit.py +82 -0
- dolphin/lib/skillkits/env_skillkit.py +250 -0
- dolphin/lib/skillkits/mcp_adapter.py +616 -0
- dolphin/lib/skillkits/mcp_skillkit.py +771 -0
- dolphin/lib/skillkits/memory_skillkit.py +650 -0
- dolphin/lib/skillkits/noop_skillkit.py +31 -0
- dolphin/lib/skillkits/ontology_skillkit.py +89 -0
- dolphin/lib/skillkits/plan_act_skillkit.py +452 -0
- dolphin/lib/skillkits/resource/__init__.py +52 -0
- dolphin/lib/skillkits/resource/models/__init__.py +6 -0
- dolphin/lib/skillkits/resource/models/skill_config.py +109 -0
- dolphin/lib/skillkits/resource/models/skill_meta.py +127 -0
- dolphin/lib/skillkits/resource/resource_skillkit.py +393 -0
- dolphin/lib/skillkits/resource/skill_cache.py +215 -0
- dolphin/lib/skillkits/resource/skill_loader.py +395 -0
- dolphin/lib/skillkits/resource/skill_validator.py +406 -0
- dolphin/lib/skillkits/resource_skillkit.py +11 -0
- dolphin/lib/skillkits/search_skillkit.py +163 -0
- dolphin/lib/skillkits/sql_skillkit.py +274 -0
- dolphin/lib/skillkits/system_skillkit.py +509 -0
- dolphin/lib/skillkits/vm_skillkit.py +65 -0
- dolphin/lib/utils/__init__.py +9 -0
- dolphin/lib/utils/data_process.py +207 -0
- dolphin/lib/utils/handle_progress.py +178 -0
- dolphin/lib/utils/security.py +139 -0
- dolphin/lib/utils/text_retrieval.py +462 -0
- dolphin/lib/vm/__init__.py +11 -0
- dolphin/lib/vm/env_executor.py +895 -0
- dolphin/lib/vm/python_session_manager.py +453 -0
- dolphin/lib/vm/vm.py +610 -0
- dolphin/sdk/__init__.py +60 -0
- dolphin/sdk/agent/__init__.py +12 -0
- dolphin/sdk/agent/agent_factory.py +236 -0
- dolphin/sdk/agent/dolphin_agent.py +1106 -0
- dolphin/sdk/api/__init__.py +4 -0
- dolphin/sdk/runtime/__init__.py +8 -0
- dolphin/sdk/runtime/env.py +363 -0
- dolphin/sdk/skill/__init__.py +10 -0
- dolphin/sdk/skill/global_skills.py +706 -0
- dolphin/sdk/skill/traditional_toolkit.py +260 -0
- kweaver_dolphin-0.1.0.dist-info/METADATA +521 -0
- kweaver_dolphin-0.1.0.dist-info/RECORD +199 -0
- kweaver_dolphin-0.1.0.dist-info/WHEEL +5 -0
- kweaver_dolphin-0.1.0.dist-info/entry_points.txt +27 -0
- kweaver_dolphin-0.1.0.dist-info/licenses/LICENSE.txt +201 -0
- kweaver_dolphin-0.1.0.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,1289 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
import json
|
|
3
|
+
import os
|
|
4
|
+
import re
|
|
5
|
+
from typing import Optional, Dict, Any, List
|
|
6
|
+
|
|
7
|
+
from dolphin.core.context_engineer.config.settings import ContextConfig
|
|
8
|
+
import yaml
|
|
9
|
+
|
|
10
|
+
from dolphin.core.common.enums import Messages
|
|
11
|
+
from dolphin.core.config.ontology_config import OntologyConfig
|
|
12
|
+
from dolphin.core.utils.cache_kv import GlobalCacheKVCenter
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _resolve_env_var(value: str) -> str:
|
|
16
|
+
"""Resolve environment variable references in string values.
|
|
17
|
+
|
|
18
|
+
Supports format: ${VAR_NAME}
|
|
19
|
+
If the environment variable is not found, raises ValueError (fail fast).
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
value: String value that may contain environment variable references
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
Resolved string with environment variables substituted
|
|
26
|
+
|
|
27
|
+
Raises:
|
|
28
|
+
ValueError: If referenced environment variable is not found
|
|
29
|
+
"""
|
|
30
|
+
if not isinstance(value, str):
|
|
31
|
+
return value
|
|
32
|
+
|
|
33
|
+
# If value doesn't contain ${}, return as-is
|
|
34
|
+
if '${' not in value:
|
|
35
|
+
return value
|
|
36
|
+
|
|
37
|
+
# Pattern to match ${VAR_NAME} format
|
|
38
|
+
pattern = r'\$\{([^}]+)\}'
|
|
39
|
+
|
|
40
|
+
def replace_env_var(match):
|
|
41
|
+
var_name = match.group(1)
|
|
42
|
+
env_value = os.getenv(var_name)
|
|
43
|
+
if env_value is None:
|
|
44
|
+
raise ValueError(
|
|
45
|
+
f"Environment variable '{var_name}' not found. "
|
|
46
|
+
f"Please set it before running the application."
|
|
47
|
+
)
|
|
48
|
+
return env_value
|
|
49
|
+
|
|
50
|
+
result = re.sub(pattern, replace_env_var, value)
|
|
51
|
+
return result
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class TypeAPI(Enum):
|
|
55
|
+
OPENAI = "openai"
|
|
56
|
+
AISHU_MODEL_FACTORY = "aishu_model_factory"
|
|
57
|
+
|
|
58
|
+
@staticmethod
|
|
59
|
+
def from_str(type_api_str: str) -> "TypeAPI":
|
|
60
|
+
if type_api_str == TypeAPI.AISHU_MODEL_FACTORY.value:
|
|
61
|
+
return TypeAPI.AISHU_MODEL_FACTORY
|
|
62
|
+
elif type_api_str == TypeAPI.OPENAI.value:
|
|
63
|
+
return TypeAPI.OPENAI
|
|
64
|
+
else:
|
|
65
|
+
raise ValueError(f"不支持的API类型: {type_api_str}")
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class CloudConfig:
|
|
69
|
+
def __init__(
|
|
70
|
+
self,
|
|
71
|
+
api: str = None,
|
|
72
|
+
api_key: str = None,
|
|
73
|
+
user_id: str = None,
|
|
74
|
+
headers: dict = None,
|
|
75
|
+
):
|
|
76
|
+
self.api = api
|
|
77
|
+
self.api_key = api_key
|
|
78
|
+
self.user_id = user_id
|
|
79
|
+
self.headers = headers or {}
|
|
80
|
+
|
|
81
|
+
@staticmethod
|
|
82
|
+
def from_dict(config_dict) -> "CloudConfig":
|
|
83
|
+
api = _resolve_env_var(config_dict.get("api")) if config_dict.get("api") else None
|
|
84
|
+
api_key = _resolve_env_var(config_dict.get("api_key")) if config_dict.get("api_key") else None
|
|
85
|
+
user_id = None
|
|
86
|
+
|
|
87
|
+
if "userid" in config_dict:
|
|
88
|
+
user_id = _resolve_env_var(config_dict.get("userid"))
|
|
89
|
+
headers = {"x-user-id": user_id}
|
|
90
|
+
else:
|
|
91
|
+
user_id = _resolve_env_var(
|
|
92
|
+
config_dict.get("headers", {}).get(
|
|
93
|
+
"x-user-id", "default-x-user-id"
|
|
94
|
+
)
|
|
95
|
+
)
|
|
96
|
+
headers = {"x-user-id": user_id}
|
|
97
|
+
|
|
98
|
+
if "security_token" in config_dict:
|
|
99
|
+
headers["security-token"] = _resolve_env_var(config_dict.get("security_token"))
|
|
100
|
+
|
|
101
|
+
# Merge the headers field in the configuration file
|
|
102
|
+
if "headers" in config_dict:
|
|
103
|
+
# Resolve environment variables in headers values
|
|
104
|
+
resolved_headers = {
|
|
105
|
+
k: _resolve_env_var(v) if isinstance(v, str) else v
|
|
106
|
+
for k, v in config_dict["headers"].items()
|
|
107
|
+
}
|
|
108
|
+
headers.update(resolved_headers)
|
|
109
|
+
|
|
110
|
+
headers["Authorization"] = f"Bearer {api_key}"
|
|
111
|
+
headers["Content-Type"] = "application/json"
|
|
112
|
+
return CloudConfig(api=api, api_key=api_key, user_id=user_id, headers=headers)
|
|
113
|
+
|
|
114
|
+
def to_dict(self) -> dict:
|
|
115
|
+
"""Convert to dictionary"""
|
|
116
|
+
return {
|
|
117
|
+
"api": self.api,
|
|
118
|
+
"api_key": self.api_key,
|
|
119
|
+
"user_id": self.user_id,
|
|
120
|
+
"headers": self.headers,
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class AllCloudsConfig:
|
|
125
|
+
def __init__(self, default_cloud: str, clouds: dict):
|
|
126
|
+
self.default_cloud = default_cloud
|
|
127
|
+
self.clouds = clouds
|
|
128
|
+
|
|
129
|
+
def get_cloud_config(self, cloud_name: Optional[str]) -> CloudConfig:
|
|
130
|
+
if cloud_name in self.clouds:
|
|
131
|
+
return self.clouds[cloud_name]
|
|
132
|
+
elif cloud_name is None:
|
|
133
|
+
return self.clouds[self.default_cloud]
|
|
134
|
+
else:
|
|
135
|
+
raise ValueError(f"cloud_name {cloud_name} not found in clouds")
|
|
136
|
+
|
|
137
|
+
@staticmethod
|
|
138
|
+
def from_dict(config_dict: dict) -> "AllCloudsConfig":
|
|
139
|
+
clouds = {}
|
|
140
|
+
default_cloud = config_dict.get("default")
|
|
141
|
+
for cloud_name, cloud_config in config_dict.items():
|
|
142
|
+
if cloud_name == "default":
|
|
143
|
+
continue
|
|
144
|
+
|
|
145
|
+
if cloud_name not in clouds:
|
|
146
|
+
clouds[cloud_name] = CloudConfig.from_dict(cloud_config)
|
|
147
|
+
else:
|
|
148
|
+
raise ValueError(f"cloud_name {cloud_name} already exists in clouds")
|
|
149
|
+
return AllCloudsConfig(default_cloud, clouds)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
class LLMConfig:
|
|
153
|
+
def __init__(
|
|
154
|
+
self,
|
|
155
|
+
name: str,
|
|
156
|
+
temperature: float,
|
|
157
|
+
top_p: float,
|
|
158
|
+
top_k: int,
|
|
159
|
+
frequency_penalty: int,
|
|
160
|
+
presence_penalty: int,
|
|
161
|
+
max_tokens: int,
|
|
162
|
+
icon: str,
|
|
163
|
+
model_name: str,
|
|
164
|
+
type_api: TypeAPI,
|
|
165
|
+
api: str,
|
|
166
|
+
userid: str,
|
|
167
|
+
headers: dict,
|
|
168
|
+
security_token: str,
|
|
169
|
+
id: str,
|
|
170
|
+
):
|
|
171
|
+
self.name = name
|
|
172
|
+
self.temperature = temperature
|
|
173
|
+
self.top_p = top_p
|
|
174
|
+
self.top_k = top_k
|
|
175
|
+
self.frequency_penalty = frequency_penalty
|
|
176
|
+
self.presence_penalty = presence_penalty
|
|
177
|
+
self.max_tokens = max_tokens
|
|
178
|
+
self.icon = icon
|
|
179
|
+
self.model_name = model_name
|
|
180
|
+
self.type_api = type_api
|
|
181
|
+
self.api = api
|
|
182
|
+
self.userid = userid
|
|
183
|
+
self.headers = headers or {}
|
|
184
|
+
self.security_token = security_token
|
|
185
|
+
self.id = id
|
|
186
|
+
|
|
187
|
+
@staticmethod
|
|
188
|
+
def from_dict(llm_name, config_dict) -> "LLMConfig":
|
|
189
|
+
temperature = config_dict.get("temperature", 0)
|
|
190
|
+
top_p = config_dict.get("top_p", 0.95)
|
|
191
|
+
top_k = config_dict.get("top_k", 1)
|
|
192
|
+
frequency_penalty = config_dict.get("frequency_penalty", 0)
|
|
193
|
+
presence_penalty = config_dict.get("presence_penalty", 0)
|
|
194
|
+
max_tokens = config_dict.get("max_tokens", 8192)
|
|
195
|
+
icon = config_dict.get("icon", "")
|
|
196
|
+
model_name = config_dict.get("model_name")
|
|
197
|
+
type_api = TypeAPI.from_str(
|
|
198
|
+
config_dict.get("type_api", TypeAPI.AISHU_MODEL_FACTORY.value)
|
|
199
|
+
)
|
|
200
|
+
api = _resolve_env_var(config_dict.get("api")) if config_dict.get("api") else None
|
|
201
|
+
userid = _resolve_env_var(config_dict.get("userid")) if config_dict.get("userid") else None
|
|
202
|
+
# Resolve environment variables in headers values
|
|
203
|
+
headers_raw = config_dict.get("headers", {})
|
|
204
|
+
headers = {
|
|
205
|
+
k: _resolve_env_var(v) if isinstance(v, str) else v
|
|
206
|
+
for k, v in headers_raw.items()
|
|
207
|
+
} if headers_raw else {}
|
|
208
|
+
security_token = _resolve_env_var(config_dict.get("security_token")) if config_dict.get("security_token") else None
|
|
209
|
+
id_value = config_dict.get("id")
|
|
210
|
+
return LLMConfig(
|
|
211
|
+
name=llm_name,
|
|
212
|
+
temperature=temperature,
|
|
213
|
+
top_p=top_p,
|
|
214
|
+
top_k=top_k,
|
|
215
|
+
frequency_penalty=frequency_penalty,
|
|
216
|
+
presence_penalty=presence_penalty,
|
|
217
|
+
max_tokens=max_tokens,
|
|
218
|
+
icon=icon,
|
|
219
|
+
model_name=model_name,
|
|
220
|
+
type_api=type_api,
|
|
221
|
+
api=api,
|
|
222
|
+
userid=userid,
|
|
223
|
+
headers=headers,
|
|
224
|
+
security_token=security_token,
|
|
225
|
+
id=id_value,
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
def to_dict(self) -> dict:
|
|
229
|
+
return {
|
|
230
|
+
"llm_name": self.name,
|
|
231
|
+
"model_name": self.model_name,
|
|
232
|
+
"type_api": self.type_api,
|
|
233
|
+
"api": self.api,
|
|
234
|
+
"userid": self.userid,
|
|
235
|
+
"headers": self.headers,
|
|
236
|
+
"security_token": self.security_token,
|
|
237
|
+
"id": self.id,
|
|
238
|
+
"icon": self.icon,
|
|
239
|
+
"temperature": self.temperature,
|
|
240
|
+
"max_tokens": self.max_tokens,
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
class LLMInstanceConfig:
|
|
245
|
+
def __init__(self, llm_name: str, cloud_config: CloudConfig, llm_config: LLMConfig):
|
|
246
|
+
self.llm_name = llm_name
|
|
247
|
+
self.cloud_config = cloud_config
|
|
248
|
+
self.llm_config = llm_config
|
|
249
|
+
|
|
250
|
+
# LLMConfig Attribute Accessor
|
|
251
|
+
@property
|
|
252
|
+
def name(self) -> str:
|
|
253
|
+
return self.llm_config.name
|
|
254
|
+
|
|
255
|
+
@property
|
|
256
|
+
def temperature(self) -> float:
|
|
257
|
+
return self.llm_config.temperature
|
|
258
|
+
|
|
259
|
+
@property
|
|
260
|
+
def top_p(self) -> float:
|
|
261
|
+
return self.llm_config.top_p
|
|
262
|
+
|
|
263
|
+
@property
|
|
264
|
+
def top_k(self) -> int:
|
|
265
|
+
return self.llm_config.top_k
|
|
266
|
+
|
|
267
|
+
@property
|
|
268
|
+
def frequency_penalty(self) -> int:
|
|
269
|
+
return self.llm_config.frequency_penalty
|
|
270
|
+
|
|
271
|
+
@property
|
|
272
|
+
def presence_penalty(self) -> int:
|
|
273
|
+
return self.llm_config.presence_penalty
|
|
274
|
+
|
|
275
|
+
@property
|
|
276
|
+
def max_tokens(self) -> int:
|
|
277
|
+
return self.llm_config.max_tokens
|
|
278
|
+
|
|
279
|
+
@property
|
|
280
|
+
def icon(self) -> str:
|
|
281
|
+
return self.llm_config.icon
|
|
282
|
+
|
|
283
|
+
@property
|
|
284
|
+
def model_name(self) -> str:
|
|
285
|
+
return self.llm_config.model_name
|
|
286
|
+
|
|
287
|
+
@property
|
|
288
|
+
def llm_api(self) -> str:
|
|
289
|
+
return self.llm_config.api
|
|
290
|
+
|
|
291
|
+
@property
|
|
292
|
+
def userid(self) -> str:
|
|
293
|
+
return self.llm_config.userid
|
|
294
|
+
|
|
295
|
+
@property
|
|
296
|
+
def llm_headers(self) -> dict:
|
|
297
|
+
return self.llm_config.headers
|
|
298
|
+
|
|
299
|
+
@property
|
|
300
|
+
def security_token(self) -> str:
|
|
301
|
+
return self.llm_config.security_token
|
|
302
|
+
|
|
303
|
+
@property
|
|
304
|
+
def id(self) -> str:
|
|
305
|
+
return self.llm_config.id
|
|
306
|
+
|
|
307
|
+
@property
|
|
308
|
+
def type_api(self) -> TypeAPI:
|
|
309
|
+
return self.llm_config.type_api
|
|
310
|
+
|
|
311
|
+
# CloudConfig Attribute Accessor
|
|
312
|
+
@property
|
|
313
|
+
def api(self) -> str:
|
|
314
|
+
return self.cloud_config.api
|
|
315
|
+
|
|
316
|
+
@property
|
|
317
|
+
def api_key(self) -> str:
|
|
318
|
+
return self.cloud_config.api_key
|
|
319
|
+
|
|
320
|
+
def set_api_key(self, api_key: str):
|
|
321
|
+
self.cloud_config.api_key = api_key
|
|
322
|
+
|
|
323
|
+
@property
|
|
324
|
+
def user_id(self) -> str:
|
|
325
|
+
return self.cloud_config.user_id
|
|
326
|
+
|
|
327
|
+
@property
|
|
328
|
+
def headers(self) -> dict:
|
|
329
|
+
return self.cloud_config.headers
|
|
330
|
+
|
|
331
|
+
# Convenient method:Prioritize using LLMConfig 的 api,如果没有则使用 CloudConfig 的 api
|
|
332
|
+
@property
|
|
333
|
+
def effective_api(self) -> str:
|
|
334
|
+
return self.llm_config.api if self.llm_config.api else self.cloud_config.api
|
|
335
|
+
|
|
336
|
+
@property
|
|
337
|
+
def effective_headers(self) -> dict:
|
|
338
|
+
# Merge CloudConfig and LLMConfig headers
|
|
339
|
+
combined_headers = {}
|
|
340
|
+
if self.cloud_config.headers:
|
|
341
|
+
combined_headers.update(self.cloud_config.headers)
|
|
342
|
+
if self.llm_config.headers:
|
|
343
|
+
combined_headers.update(self.llm_config.headers)
|
|
344
|
+
return combined_headers
|
|
345
|
+
|
|
346
|
+
@staticmethod
|
|
347
|
+
def from_dict(llm_name, all_clouds_config, config_dict) -> "LLMInstanceConfig":
|
|
348
|
+
cloud_name = config_dict.get("cloud", None)
|
|
349
|
+
if cloud_name:
|
|
350
|
+
cloud_config = all_clouds_config.get_cloud_config(cloud_name)
|
|
351
|
+
else:
|
|
352
|
+
cloud_config = CloudConfig.from_dict(config_dict)
|
|
353
|
+
|
|
354
|
+
llm_config = LLMConfig.from_dict(llm_name, config_dict)
|
|
355
|
+
return LLMInstanceConfig(
|
|
356
|
+
llm_name=llm_name, cloud_config=cloud_config, llm_config=llm_config
|
|
357
|
+
)
|
|
358
|
+
|
|
359
|
+
def to_dict(self) -> dict:
|
|
360
|
+
return {
|
|
361
|
+
"llm_name": self.llm_name,
|
|
362
|
+
"cloud_config": self.cloud_config.to_dict(),
|
|
363
|
+
"llm_config": self.llm_config.to_dict(),
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
class VMConnectionType(Enum):
|
|
368
|
+
SSH = 0
|
|
369
|
+
DOCKER = 1
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
class VMConfig:
|
|
373
|
+
def __init__(
|
|
374
|
+
self,
|
|
375
|
+
connectionType: VMConnectionType,
|
|
376
|
+
host: str,
|
|
377
|
+
port: int,
|
|
378
|
+
username: str,
|
|
379
|
+
encryptedPassword: str,
|
|
380
|
+
sshKeyPath: str = None,
|
|
381
|
+
timeout: int = 10,
|
|
382
|
+
retryCount: int = 3,
|
|
383
|
+
):
|
|
384
|
+
self.connectionType = connectionType
|
|
385
|
+
self.connection_type = connectionType # Add alias for backward compatibility
|
|
386
|
+
self.host = host
|
|
387
|
+
self.port = port
|
|
388
|
+
self.username = username
|
|
389
|
+
self.encryptedPassword = encryptedPassword
|
|
390
|
+
self.sshKeyPath = sshKeyPath
|
|
391
|
+
self.timeout = timeout
|
|
392
|
+
self.retryCount = retryCount
|
|
393
|
+
|
|
394
|
+
@staticmethod
|
|
395
|
+
def fromArgs(config: dict):
|
|
396
|
+
# Get basic configuration
|
|
397
|
+
connectionType = (
|
|
398
|
+
VMConnectionType.SSH
|
|
399
|
+
if config["connection_type"] == "ssh"
|
|
400
|
+
else VMConnectionType.DOCKER
|
|
401
|
+
)
|
|
402
|
+
host = config["host"]
|
|
403
|
+
port = config["port"]
|
|
404
|
+
username = config["username"]
|
|
405
|
+
encryptedPassword = config["encrypted_password"]
|
|
406
|
+
|
|
407
|
+
# Get optional configuration
|
|
408
|
+
sshKeyPath = config.get("ssh_key_path", None)
|
|
409
|
+
timeout = config.get("timeout", 10)
|
|
410
|
+
retryCount = config.get("retry_count", 3)
|
|
411
|
+
|
|
412
|
+
return VMConfig(
|
|
413
|
+
connectionType=connectionType,
|
|
414
|
+
host=host,
|
|
415
|
+
port=port,
|
|
416
|
+
username=username,
|
|
417
|
+
encryptedPassword=encryptedPassword,
|
|
418
|
+
sshKeyPath=sshKeyPath,
|
|
419
|
+
timeout=timeout,
|
|
420
|
+
retryCount=retryCount,
|
|
421
|
+
)
|
|
422
|
+
|
|
423
|
+
def validate(self) -> bool:
|
|
424
|
+
"""Validate whether the configuration is valid
|
|
425
|
+
|
|
426
|
+
Returns:
|
|
427
|
+
bool: Whether the configuration is valid
|
|
428
|
+
"""
|
|
429
|
+
# Validate basic parameters
|
|
430
|
+
if not self.host or not isinstance(self.host, str):
|
|
431
|
+
return False
|
|
432
|
+
|
|
433
|
+
if not isinstance(self.port, int) or self.port <= 0 or self.port > 65535:
|
|
434
|
+
return False
|
|
435
|
+
|
|
436
|
+
if not self.username or not isinstance(self.username, str):
|
|
437
|
+
return False
|
|
438
|
+
|
|
439
|
+
# At least one of password and SSH key must be provided
|
|
440
|
+
if not self.encryptedPassword and not self.sshKeyPath:
|
|
441
|
+
return False
|
|
442
|
+
|
|
443
|
+
# If an SSH key path is specified, check whether the file exists
|
|
444
|
+
if self.sshKeyPath and not os.path.exists(self.sshKeyPath):
|
|
445
|
+
return False
|
|
446
|
+
|
|
447
|
+
return True
|
|
448
|
+
|
|
449
|
+
def toDict(self) -> dict:
|
|
450
|
+
"""Convert configuration to dictionary
|
|
451
|
+
|
|
452
|
+
Returns:
|
|
453
|
+
dict: configuration dictionary
|
|
454
|
+
"""
|
|
455
|
+
return {
|
|
456
|
+
"connection_type": (
|
|
457
|
+
"ssh" if self.connectionType == VMConnectionType.SSH else "docker"
|
|
458
|
+
),
|
|
459
|
+
"host": self.host,
|
|
460
|
+
"port": self.port,
|
|
461
|
+
"username": self.username,
|
|
462
|
+
"encrypted_password": self.encryptedPassword,
|
|
463
|
+
"ssh_key_path": self.sshKeyPath,
|
|
464
|
+
"timeout": self.timeout,
|
|
465
|
+
"retry_count": self.retryCount,
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
def __str__(self) -> str:
|
|
469
|
+
"""Return the string representation of the configuration
|
|
470
|
+
|
|
471
|
+
Returns:
|
|
472
|
+
str: The string representation of the configuration
|
|
473
|
+
"""
|
|
474
|
+
return f"VMConfig(type={self.connectionType.name}, host={self.host}, port={self.port}, username={self.username})"
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
class ContextConstraints:
|
|
478
|
+
"""Context Constraint Parameters
|
|
479
|
+
|
|
480
|
+
Parameter Description:
|
|
481
|
+
- max_input_tokens: Maximum number of input tokens the model can accept, which is the model's input capacity limit
|
|
482
|
+
- reserve_output_tokens: Number of tokens reserved for model output, ensuring sufficient space to generate a response
|
|
483
|
+
- preserve_system: Whether to retain system messages during compression
|
|
484
|
+
|
|
485
|
+
Calculation Logic:
|
|
486
|
+
Available input tokens = max_input_tokens - reserve_output_tokens
|
|
487
|
+
|
|
488
|
+
Recommendation:
|
|
489
|
+
- max_input_tokens: Usually set to the model's maximum context length (e.g., 128K, 32K, etc.)
|
|
490
|
+
- reserve_output_tokens: Set to the expected maximum output length, typically the model's max_tokens configuration value
|
|
491
|
+
|
|
492
|
+
Automatic Adjustment:
|
|
493
|
+
When model_config is provided, the system automatically sets reserve_output_tokens to model_config.max_tokens
|
|
494
|
+
"""
|
|
495
|
+
|
|
496
|
+
def __init__(
|
|
497
|
+
self,
|
|
498
|
+
max_input_tokens: int = 64000, # Maximum input token count
|
|
499
|
+
reserve_output_tokens: int = 8192, # Number of tokens reserved for output
|
|
500
|
+
preserve_system: bool = True, # Whether to retain system messages
|
|
501
|
+
):
|
|
502
|
+
self.max_input_tokens = max_input_tokens
|
|
503
|
+
self.reserve_output_tokens = reserve_output_tokens
|
|
504
|
+
self.preserve_system = preserve_system
|
|
505
|
+
|
|
506
|
+
@staticmethod
|
|
507
|
+
def from_dict(config_dict: dict) -> "ContextConstraints":
|
|
508
|
+
"""Create constraints from dictionary"""
|
|
509
|
+
return ContextConstraints(
|
|
510
|
+
max_input_tokens=config_dict.get("max_input_tokens", 64000),
|
|
511
|
+
reserve_output_tokens=config_dict.get("reserve_output_tokens", 8192),
|
|
512
|
+
preserve_system=config_dict.get("preserve_system", True),
|
|
513
|
+
)
|
|
514
|
+
|
|
515
|
+
def to_dict(self) -> dict:
|
|
516
|
+
"""Convert to dictionary"""
|
|
517
|
+
return {
|
|
518
|
+
"max_input_tokens": self.max_input_tokens,
|
|
519
|
+
"reserve_output_tokens": self.reserve_output_tokens,
|
|
520
|
+
"preserve_system": self.preserve_system,
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
|
|
524
|
+
class MemoryConfig:
|
|
525
|
+
"""Configuration for the memory system."""
|
|
526
|
+
|
|
527
|
+
def __init__(
|
|
528
|
+
self,
|
|
529
|
+
enabled: bool = False,
|
|
530
|
+
storage_path: str = "data/memory/",
|
|
531
|
+
dialog_path: str = "data/dialog/",
|
|
532
|
+
default_top_k: int = 10,
|
|
533
|
+
):
|
|
534
|
+
self.enabled = enabled
|
|
535
|
+
self.storage_path = storage_path
|
|
536
|
+
self.dialog_path = dialog_path
|
|
537
|
+
self.default_top_k = default_top_k
|
|
538
|
+
|
|
539
|
+
@staticmethod
|
|
540
|
+
def from_dict(config_dict: dict) -> "MemoryConfig":
|
|
541
|
+
return MemoryConfig(
|
|
542
|
+
enabled=config_dict.get("enabled", False),
|
|
543
|
+
storage_path=config_dict.get("storage_path", "data/memory/"),
|
|
544
|
+
dialog_path=config_dict.get("dialog_path", "data/dialog/"),
|
|
545
|
+
default_top_k=config_dict.get("default_top_k", 10),
|
|
546
|
+
)
|
|
547
|
+
|
|
548
|
+
def to_dict(self) -> dict:
|
|
549
|
+
return {
|
|
550
|
+
"enabled": self.enabled,
|
|
551
|
+
"storage_path": self.storage_path,
|
|
552
|
+
"dialog_path": self.dialog_path,
|
|
553
|
+
"default_top_k": self.default_top_k,
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
|
|
557
|
+
class MCPServerConfig:
|
|
558
|
+
"""MCP Server Configuration"""
|
|
559
|
+
|
|
560
|
+
def __init__(
|
|
561
|
+
self,
|
|
562
|
+
name: str,
|
|
563
|
+
connection_mode: str = "stdio", # "stdio" or "http"
|
|
564
|
+
command: str = None, # stdio mode usage
|
|
565
|
+
args: List[str] = None, # stdio mode usage
|
|
566
|
+
url: str = None, # HTTP Mode Usage
|
|
567
|
+
env: Optional[Dict[str, str]] = None,
|
|
568
|
+
timeout: int = 30,
|
|
569
|
+
enabled: bool = False,
|
|
570
|
+
auth: Optional[Dict[str, str]] = None,
|
|
571
|
+
):
|
|
572
|
+
self.name = name
|
|
573
|
+
self.connection_mode = connection_mode
|
|
574
|
+
self.command = command # Start command, such as "npx"
|
|
575
|
+
self.args = args or [] # Parameter List
|
|
576
|
+
self.url = url # HTTP Server URL
|
|
577
|
+
self.env = env # Environment Variables
|
|
578
|
+
self.timeout = timeout
|
|
579
|
+
self.enabled = enabled
|
|
580
|
+
self.auth = auth # Authentication Information
|
|
581
|
+
|
|
582
|
+
# Validate configuration
|
|
583
|
+
if connection_mode == "stdio":
|
|
584
|
+
if not command:
|
|
585
|
+
raise ValueError(f"stdio mode requires 'command' for server {name}")
|
|
586
|
+
elif connection_mode == "http":
|
|
587
|
+
if not url:
|
|
588
|
+
raise ValueError(f"http mode requires 'url' for server {name}")
|
|
589
|
+
else:
|
|
590
|
+
raise ValueError(
|
|
591
|
+
f"Invalid connection_mode: {connection_mode}, must be 'stdio' or 'http'"
|
|
592
|
+
)
|
|
593
|
+
|
|
594
|
+
@staticmethod
|
|
595
|
+
def from_dict(config_dict: dict) -> "MCPServerConfig":
|
|
596
|
+
return MCPServerConfig(
|
|
597
|
+
name=config_dict["name"],
|
|
598
|
+
connection_mode=config_dict.get("connection_mode", "stdio"),
|
|
599
|
+
command=config_dict.get("command"),
|
|
600
|
+
args=config_dict.get("args", []),
|
|
601
|
+
url=config_dict.get("url"),
|
|
602
|
+
env=config_dict.get("env"),
|
|
603
|
+
timeout=config_dict.get("timeout", 30),
|
|
604
|
+
enabled=config_dict.get("enabled", True),
|
|
605
|
+
auth=config_dict.get("auth"),
|
|
606
|
+
)
|
|
607
|
+
|
|
608
|
+
def to_dict(self) -> dict:
|
|
609
|
+
"""Convert to dictionary"""
|
|
610
|
+
return {
|
|
611
|
+
"name": self.name,
|
|
612
|
+
"connection_mode": self.connection_mode,
|
|
613
|
+
"command": self.command,
|
|
614
|
+
"args": self.args,
|
|
615
|
+
"url": self.url,
|
|
616
|
+
"env": self.env,
|
|
617
|
+
"timeout": self.timeout,
|
|
618
|
+
"enabled": self.enabled,
|
|
619
|
+
"auth": self.auth,
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
|
|
623
|
+
class MCPConfig:
|
|
624
|
+
"""MCP Configuration"""
|
|
625
|
+
|
|
626
|
+
def __init__(self, enabled: bool = True, servers: List[MCPServerConfig] = None):
|
|
627
|
+
self.enabled = enabled
|
|
628
|
+
self.servers = servers or []
|
|
629
|
+
|
|
630
|
+
@staticmethod
|
|
631
|
+
def from_dict(config_dict: dict) -> "MCPConfig":
|
|
632
|
+
servers = []
|
|
633
|
+
for server_dict in config_dict.get("servers", []):
|
|
634
|
+
server = MCPServerConfig.from_dict(server_dict)
|
|
635
|
+
servers.append(server)
|
|
636
|
+
|
|
637
|
+
return MCPConfig(enabled=config_dict.get("enabled", True), servers=servers)
|
|
638
|
+
|
|
639
|
+
def to_dict(self) -> dict:
|
|
640
|
+
"""Convert to dictionary"""
|
|
641
|
+
return {
|
|
642
|
+
"enabled": self.enabled,
|
|
643
|
+
"servers": [server.to_dict() for server in self.servers],
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
|
|
647
|
+
class SkillConfig:
|
|
648
|
+
"""Skill Loading Configuration"""
|
|
649
|
+
|
|
650
|
+
def __init__(self, enabled_skills: List[str] = None):
|
|
651
|
+
"""Initialize skill configuration
|
|
652
|
+
|
|
653
|
+
Args:
|
|
654
|
+
enabled_skills: List of enabled skills, None means load all skills
|
|
655
|
+
Supports the following formats:
|
|
656
|
+
- "vm_skillkit": Load a specific skillkit
|
|
657
|
+
- "mcp": Load all MCP servers
|
|
658
|
+
- "mcp.filesystem": Load a specific MCP server
|
|
659
|
+
"""
|
|
660
|
+
self.enabled_skills = enabled_skills
|
|
661
|
+
|
|
662
|
+
@staticmethod
|
|
663
|
+
def _normalize_skill_name(name: str) -> str:
|
|
664
|
+
"""Normalize skill identifiers for backward compatibility.
|
|
665
|
+
|
|
666
|
+
Historically, config examples used names like "vm_skillkit" while the
|
|
667
|
+
entry-point based loader uses names like "vm". We accept both by
|
|
668
|
+
normalizing the "_skillkit" suffix for non-namespaced skill ids.
|
|
669
|
+
"""
|
|
670
|
+
if not isinstance(name, str):
|
|
671
|
+
return name
|
|
672
|
+
# Keep namespaced ids as-is (e.g. "mcp.playwright", "system_functions.grep")
|
|
673
|
+
if "." in name:
|
|
674
|
+
return name
|
|
675
|
+
if name.endswith("_skillkit"):
|
|
676
|
+
return name[: -len("_skillkit")]
|
|
677
|
+
return name
|
|
678
|
+
|
|
679
|
+
def should_load_skill(self, skill_name: str) -> bool:
|
|
680
|
+
"""Check whether a certain skill should be loaded
|
|
681
|
+
|
|
682
|
+
Args:
|
|
683
|
+
skill_name: Name of the skill
|
|
684
|
+
|
|
685
|
+
Returns:
|
|
686
|
+
bool: Whether it should be loaded
|
|
687
|
+
"""
|
|
688
|
+
if self.enabled_skills is None:
|
|
689
|
+
return True
|
|
690
|
+
|
|
691
|
+
# If it's an empty list, no skills will be loaded.
|
|
692
|
+
if len(self.enabled_skills) == 0:
|
|
693
|
+
return False
|
|
694
|
+
|
|
695
|
+
normalized_enabled = set()
|
|
696
|
+
for enabled in self.enabled_skills:
|
|
697
|
+
normalized_enabled.add(enabled)
|
|
698
|
+
normalized_enabled.add(self._normalize_skill_name(enabled))
|
|
699
|
+
|
|
700
|
+
normalized_skill_name = self._normalize_skill_name(skill_name)
|
|
701
|
+
|
|
702
|
+
# Check direct match
|
|
703
|
+
if skill_name in normalized_enabled or normalized_skill_name in normalized_enabled:
|
|
704
|
+
return True
|
|
705
|
+
|
|
706
|
+
# Check MCP mode matching
|
|
707
|
+
if skill_name.startswith("mcp."):
|
|
708
|
+
# If "mcp" is included, load all MCP servers
|
|
709
|
+
if "mcp" in normalized_enabled:
|
|
710
|
+
return True
|
|
711
|
+
# If a specific MCP server name is included, load that server
|
|
712
|
+
if skill_name in normalized_enabled:
|
|
713
|
+
return True
|
|
714
|
+
|
|
715
|
+
return False
|
|
716
|
+
|
|
717
|
+
def should_load_mcp_server(self, server_name: str) -> bool:
|
|
718
|
+
"""Check whether a certain MCP server should be loaded
|
|
719
|
+
|
|
720
|
+
Args:
|
|
721
|
+
server_name: Name of the MCP server
|
|
722
|
+
|
|
723
|
+
Returns:
|
|
724
|
+
bool: Whether it should be loaded
|
|
725
|
+
"""
|
|
726
|
+
if self.enabled_skills is None:
|
|
727
|
+
return True
|
|
728
|
+
|
|
729
|
+
# If "mcp" is included, load all MCP servers
|
|
730
|
+
if "mcp" in self.enabled_skills:
|
|
731
|
+
return True
|
|
732
|
+
|
|
733
|
+
# If a specific MCP server name is included, load that server.
|
|
734
|
+
if f"mcp.{server_name}" in self.enabled_skills:
|
|
735
|
+
return True
|
|
736
|
+
|
|
737
|
+
return False
|
|
738
|
+
|
|
739
|
+
@staticmethod
|
|
740
|
+
def from_dict(config_dict: dict) -> "SkillConfig":
|
|
741
|
+
"""Create configuration from dictionary"""
|
|
742
|
+
return SkillConfig(enabled_skills=config_dict.get("enabled_skills"))
|
|
743
|
+
|
|
744
|
+
def to_dict(self) -> dict:
|
|
745
|
+
"""Convert to dictionary"""
|
|
746
|
+
return {"enabled_skills": self.enabled_skills}
|
|
747
|
+
|
|
748
|
+
|
|
749
|
+
class RetrievalProcessingConfig:
|
|
750
|
+
"""Retrieval system processing configuration"""
|
|
751
|
+
|
|
752
|
+
def __init__(
|
|
753
|
+
self,
|
|
754
|
+
max_text_length: int = 512,
|
|
755
|
+
max_batch_size: int = 16,
|
|
756
|
+
max_documents_per_rerank: int = 100,
|
|
757
|
+
chunk_overlap: int = 50,
|
|
758
|
+
chunk_size: int = 512,
|
|
759
|
+
):
|
|
760
|
+
self.max_text_length = max_text_length # Maximum length (tokens) for a single text
|
|
761
|
+
self.max_batch_size = max_batch_size # embedding batch size
|
|
762
|
+
self.max_documents_per_rerank = max_documents_per_rerank # Number of documents processed at once by rerank
|
|
763
|
+
self.chunk_overlap = chunk_overlap # Document slice overlap length
|
|
764
|
+
self.chunk_size = chunk_size # Document chunk size
|
|
765
|
+
|
|
766
|
+
@staticmethod
|
|
767
|
+
def from_dict(config_dict: dict) -> "RetrievalProcessingConfig":
|
|
768
|
+
"""Create configuration from dictionary"""
|
|
769
|
+
return RetrievalProcessingConfig(
|
|
770
|
+
max_text_length=config_dict.get("max_text_length", 512),
|
|
771
|
+
max_batch_size=config_dict.get("max_batch_size", 16),
|
|
772
|
+
max_documents_per_rerank=config_dict.get("max_documents_per_rerank", 100),
|
|
773
|
+
chunk_overlap=config_dict.get("chunk_overlap", 50),
|
|
774
|
+
chunk_size=config_dict.get("chunk_size", 512),
|
|
775
|
+
)
|
|
776
|
+
|
|
777
|
+
def to_dict(self) -> dict:
|
|
778
|
+
"""Convert to dictionary"""
|
|
779
|
+
return {
|
|
780
|
+
"max_text_length": self.max_text_length,
|
|
781
|
+
"max_batch_size": self.max_batch_size,
|
|
782
|
+
"max_documents_per_rerank": self.max_documents_per_rerank,
|
|
783
|
+
"chunk_overlap": self.chunk_overlap,
|
|
784
|
+
"chunk_size": self.chunk_size,
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
|
|
788
|
+
class RetrievalModelConfig:
|
|
789
|
+
"""Retrieval model configuration, supports multiple cloud providers"""
|
|
790
|
+
|
|
791
|
+
def __init__(
|
|
792
|
+
self,
|
|
793
|
+
embedding_config: dict = None,
|
|
794
|
+
rerank_config: dict = None,
|
|
795
|
+
cloud: str = None,
|
|
796
|
+
index_path: str = None,
|
|
797
|
+
processing_config: RetrievalProcessingConfig = None,
|
|
798
|
+
):
|
|
799
|
+
self.embedding_config = embedding_config or {}
|
|
800
|
+
self.rerank_config = rerank_config or {}
|
|
801
|
+
self.cloud = cloud # Specify the cloud provider to use
|
|
802
|
+
self.index_path = index_path or "data/local_retrieval_index.pkl" # Index file path
|
|
803
|
+
# System Processing Configuration
|
|
804
|
+
self.processing_config = processing_config or RetrievalProcessingConfig()
|
|
805
|
+
|
|
806
|
+
@staticmethod
|
|
807
|
+
def from_dict(config_dict: dict) -> "RetrievalModelConfig":
|
|
808
|
+
"""Create configuration from dictionary"""
|
|
809
|
+
processing_config = None
|
|
810
|
+
if "processing_config" in config_dict:
|
|
811
|
+
processing_config = RetrievalProcessingConfig.from_dict(
|
|
812
|
+
config_dict["processing_config"]
|
|
813
|
+
)
|
|
814
|
+
|
|
815
|
+
return RetrievalModelConfig(
|
|
816
|
+
embedding_config=config_dict.get("embedding_config", {}),
|
|
817
|
+
rerank_config=config_dict.get("rerank_config", {}),
|
|
818
|
+
cloud=config_dict.get("cloud"),
|
|
819
|
+
index_path=config_dict.get("index_path"),
|
|
820
|
+
processing_config=processing_config,
|
|
821
|
+
)
|
|
822
|
+
|
|
823
|
+
def to_dict(self) -> dict:
|
|
824
|
+
"""Convert to dictionary"""
|
|
825
|
+
return {
|
|
826
|
+
"embedding_config": self.embedding_config,
|
|
827
|
+
"rerank_config": self.rerank_config,
|
|
828
|
+
"cloud": self.cloud,
|
|
829
|
+
"index_path": self.index_path,
|
|
830
|
+
"processing_config": self.processing_config.to_dict(),
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
|
|
834
|
+
class ContextEngineerConfig:
|
|
835
|
+
"""Context Engineer Configuration"""
|
|
836
|
+
|
|
837
|
+
def __init__(
|
|
838
|
+
self,
|
|
839
|
+
context_config: ContextConfig = None,
|
|
840
|
+
import_mem: bool = False,
|
|
841
|
+
default_strategy: str = "level",
|
|
842
|
+
constraints: ContextConstraints = None,
|
|
843
|
+
strategy_configs: Dict[str, Any] = None,
|
|
844
|
+
tokenizer_backend: str = "auto",
|
|
845
|
+
):
|
|
846
|
+
self.context_config = context_config
|
|
847
|
+
self.import_mem = import_mem
|
|
848
|
+
self.default_strategy = default_strategy
|
|
849
|
+
self.constraints = constraints or ContextConstraints()
|
|
850
|
+
self.strategy_configs = strategy_configs or {}
|
|
851
|
+
self.tokenizer_backend = tokenizer_backend
|
|
852
|
+
|
|
853
|
+
@staticmethod
|
|
854
|
+
def from_dict(config_dict: dict, base_dir: str = None) -> "ContextEngineerConfig":
|
|
855
|
+
"""Create configuration from dictionary
|
|
856
|
+
|
|
857
|
+
Args:
|
|
858
|
+
config_dict: Configuration dictionary
|
|
859
|
+
base_dir: Base directory for resolving relative paths in config_path
|
|
860
|
+
"""
|
|
861
|
+
context_config = None
|
|
862
|
+
config_path = config_dict.get("config_path")
|
|
863
|
+
if config_path:
|
|
864
|
+
# Resolve relative path relative to base_dir if provided
|
|
865
|
+
if not os.path.isabs(config_path) and base_dir:
|
|
866
|
+
# If config_path starts with base_dir name, remove the prefix to avoid duplication
|
|
867
|
+
base_dir_name = os.path.basename(base_dir)
|
|
868
|
+
if config_path.startswith(base_dir_name + os.sep):
|
|
869
|
+
config_path = config_path[len(base_dir_name + os.sep):]
|
|
870
|
+
config_path = os.path.join(base_dir, config_path)
|
|
871
|
+
context_config = ContextConfig.from_yaml(config_path)
|
|
872
|
+
|
|
873
|
+
constraints_dict = config_dict.get("constraints", {})
|
|
874
|
+
constraints = ContextConstraints.from_dict(constraints_dict)
|
|
875
|
+
return ContextEngineerConfig(
|
|
876
|
+
context_config=context_config,
|
|
877
|
+
import_mem=config_dict.get("import_mem", False),
|
|
878
|
+
default_strategy=config_dict.get("default_strategy", "level"),
|
|
879
|
+
constraints=constraints,
|
|
880
|
+
strategy_configs=config_dict.get("strategy_configs", {}),
|
|
881
|
+
tokenizer_backend=config_dict.get("tokenizer_backend", "auto"),
|
|
882
|
+
)
|
|
883
|
+
|
|
884
|
+
def to_dict(self) -> dict:
|
|
885
|
+
"""Convert to dictionary"""
|
|
886
|
+
data = {
|
|
887
|
+
"import_mem": self.import_mem,
|
|
888
|
+
"default_strategy": self.default_strategy,
|
|
889
|
+
"constraints": self.constraints.to_dict(),
|
|
890
|
+
"strategy_configs": self.strategy_configs,
|
|
891
|
+
"tokenizer_backend": self.tokenizer_backend,
|
|
892
|
+
}
|
|
893
|
+
# Compatible output extended fields
|
|
894
|
+
if hasattr(self, "config_path") and getattr(self, "config_path"):
|
|
895
|
+
data["config_path"] = getattr(self, "config_path")
|
|
896
|
+
return data
|
|
897
|
+
|
|
898
|
+
class GlobalConfig:
|
|
899
|
+
def __init__(
|
|
900
|
+
self,
|
|
901
|
+
default_llm: str = "",
|
|
902
|
+
llmInstanceConfigs: dict = {},
|
|
903
|
+
fast_llm: str = None,
|
|
904
|
+
all_clouds_config: AllCloudsConfig = None,
|
|
905
|
+
vm_config: VMConfig = None,
|
|
906
|
+
context_engineer_config: ContextEngineerConfig = None,
|
|
907
|
+
memory_config: MemoryConfig = None,
|
|
908
|
+
mcp_config: MCPConfig = None,
|
|
909
|
+
skill_config: SkillConfig = None,
|
|
910
|
+
resource_skills: Optional[Dict[str, Any]] = None,
|
|
911
|
+
ontology_config: OntologyConfig = None,
|
|
912
|
+
retrieval_model_config: RetrievalModelConfig = None,
|
|
913
|
+
base_dir: Optional[str] = None,
|
|
914
|
+
):
|
|
915
|
+
self.default_llm = default_llm
|
|
916
|
+
self.fast_llm = fast_llm if fast_llm else default_llm
|
|
917
|
+
self.all_clouds_config = all_clouds_config
|
|
918
|
+
self.llmInstanceConfigs = llmInstanceConfigs
|
|
919
|
+
self._vm_config = vm_config
|
|
920
|
+
self._context_engineer_config = (
|
|
921
|
+
context_engineer_config or ContextEngineerConfig()
|
|
922
|
+
)
|
|
923
|
+
self._memory_config = memory_config or MemoryConfig()
|
|
924
|
+
self._mcp_config = mcp_config or MCPConfig()
|
|
925
|
+
self._skill_config = skill_config or SkillConfig()
|
|
926
|
+
self._resource_skills = resource_skills
|
|
927
|
+
self._llm_cache = GlobalCacheKVCenter.getCacheMgr(
|
|
928
|
+
"data/cache/", category="llm", expireTimeByDay=7
|
|
929
|
+
)
|
|
930
|
+
self._ontology_config = ontology_config
|
|
931
|
+
self._retrieval_model_config = retrieval_model_config
|
|
932
|
+
self._base_dir = base_dir
|
|
933
|
+
|
|
934
|
+
@property
|
|
935
|
+
def base_dir(self) -> Optional[str]:
|
|
936
|
+
"""Get the base directory for resolving relative paths.
|
|
937
|
+
|
|
938
|
+
Returns:
|
|
939
|
+
The directory where the config file is located, or None if not set.
|
|
940
|
+
"""
|
|
941
|
+
return self._base_dir
|
|
942
|
+
|
|
943
|
+
@property
|
|
944
|
+
def vm_config(self) -> VMConfig:
|
|
945
|
+
return self._vm_config
|
|
946
|
+
|
|
947
|
+
@property
|
|
948
|
+
def context_engineer_config(self) -> ContextEngineerConfig:
|
|
949
|
+
"""[Deprecated alias] Old name, returns MessageCompressor configuration"""
|
|
950
|
+
return self._context_engineer_config
|
|
951
|
+
|
|
952
|
+
@property
|
|
953
|
+
def message_compressor_config(self) -> ContextEngineerConfig:
|
|
954
|
+
"""MessageCompressor New Naming Configuration Access Entry"""
|
|
955
|
+
return self._context_engineer_config
|
|
956
|
+
|
|
957
|
+
@property
|
|
958
|
+
def memory_config(self) -> MemoryConfig:
|
|
959
|
+
return self._memory_config
|
|
960
|
+
|
|
961
|
+
@property
|
|
962
|
+
def mcp_config(self) -> MCPConfig:
|
|
963
|
+
return self._mcp_config
|
|
964
|
+
|
|
965
|
+
@property
|
|
966
|
+
def skill_config(self) -> SkillConfig:
|
|
967
|
+
return self._skill_config
|
|
968
|
+
|
|
969
|
+
@property
|
|
970
|
+
def resource_skills(self) -> Optional[Dict[str, Any]]:
|
|
971
|
+
return self._resource_skills
|
|
972
|
+
|
|
973
|
+
@property
|
|
974
|
+
def ontology_config(self) -> OntologyConfig:
|
|
975
|
+
return self._ontology_config
|
|
976
|
+
|
|
977
|
+
@property
|
|
978
|
+
def retrieval_model_config(self) -> RetrievalModelConfig:
|
|
979
|
+
return self._retrieval_model_config
|
|
980
|
+
|
|
981
|
+
def get_model_config(self, llm_name: Optional[str]) -> LLMInstanceConfig:
|
|
982
|
+
if llm_name in self.llmInstanceConfigs:
|
|
983
|
+
return self.llmInstanceConfigs.get(llm_name, "")
|
|
984
|
+
elif not llm_name:
|
|
985
|
+
return self.llmInstanceConfigs.get(self.default_llm, {})
|
|
986
|
+
else:
|
|
987
|
+
available_models = list(self.llmInstanceConfigs.keys())
|
|
988
|
+
raise ValueError(
|
|
989
|
+
f"Model '{llm_name}' not found in configuration.\n"
|
|
990
|
+
f" Available models: {available_models}\n"
|
|
991
|
+
f" Default model: '{self.default_llm}'\n\n"
|
|
992
|
+
f" To fix this, either:\n"
|
|
993
|
+
f" 1. Add '{llm_name}' to the 'llms' section in your global.yaml, or\n"
|
|
994
|
+
f" 2. Remove the model parameter from your .dph file to use the default model, or\n"
|
|
995
|
+
f" 3. Use one of the available models: {available_models}"
|
|
996
|
+
)
|
|
997
|
+
|
|
998
|
+
def get_default_model_config(self) -> LLMInstanceConfig:
|
|
999
|
+
return self.get_model_config(self.default_llm)
|
|
1000
|
+
|
|
1001
|
+
def get_fast_model_config(self) -> LLMInstanceConfig:
|
|
1002
|
+
return self.get_model_config(self.fast_llm)
|
|
1003
|
+
|
|
1004
|
+
def set_llm_cache(self, llm: str, key: Messages, value: Any):
|
|
1005
|
+
self._llm_cache.setValue(llm, key=key.get_messages_as_dict(), value=value)
|
|
1006
|
+
|
|
1007
|
+
def get_llm_cache(self, llm: str, key: Messages):
|
|
1008
|
+
return self._llm_cache.getValue(llm, key=key.get_messages_as_dict())
|
|
1009
|
+
|
|
1010
|
+
def set_llm_cache_by_dict(self, llm: str, key: List[Dict[str, Any]], value: Any):
|
|
1011
|
+
"""Set LLM cache using a sanitized dict-list key."""
|
|
1012
|
+
self._llm_cache.setValue(llm, key=key, value=value)
|
|
1013
|
+
|
|
1014
|
+
def get_llm_cache_by_dict(self, llm: str, key: List[Dict[str, Any]]):
|
|
1015
|
+
"""Get LLM cache using a sanitized dict-list key."""
|
|
1016
|
+
return self._llm_cache.getValue(llm, key=key)
|
|
1017
|
+
|
|
1018
|
+
@staticmethod
|
|
1019
|
+
def from_dict(config_dict: dict, base_dir: str = None) -> "GlobalConfig":
|
|
1020
|
+
is_new_config_format = "llms" in config_dict and "default" in config_dict
|
|
1021
|
+
if is_new_config_format:
|
|
1022
|
+
default_llm = config_dict.get("default")
|
|
1023
|
+
fast_llm = config_dict.get("fast", None)
|
|
1024
|
+
|
|
1025
|
+
clouds = config_dict.get("clouds", None)
|
|
1026
|
+
all_clouds_config = AllCloudsConfig.from_dict(clouds) if clouds else None
|
|
1027
|
+
|
|
1028
|
+
llms = config_dict.get("llms")
|
|
1029
|
+
llmInstanceConfigs = {}
|
|
1030
|
+
for llm_name, llm_config in llms.items():
|
|
1031
|
+
llmInstanceConfigs[llm_name] = LLMInstanceConfig.from_dict(
|
|
1032
|
+
llm_name, all_clouds_config, llm_config
|
|
1033
|
+
)
|
|
1034
|
+
|
|
1035
|
+
if default_llm not in llmInstanceConfigs:
|
|
1036
|
+
raise ValueError(
|
|
1037
|
+
f"default_llm {default_llm} not found in llmInstanceConfigs"
|
|
1038
|
+
)
|
|
1039
|
+
|
|
1040
|
+
if fast_llm and fast_llm not in llmInstanceConfigs:
|
|
1041
|
+
raise ValueError(f"fast_llm {fast_llm} not found in llmInstanceConfigs")
|
|
1042
|
+
|
|
1043
|
+
vm = config_dict.get("vm", None)
|
|
1044
|
+
vm_config = VMConfig.fromArgs(vm) if vm else None
|
|
1045
|
+
|
|
1046
|
+
context_engineer = config_dict.get("context_engineer", None)
|
|
1047
|
+
context_engineer_config = (
|
|
1048
|
+
ContextEngineerConfig.from_dict(context_engineer, base_dir=base_dir)
|
|
1049
|
+
if context_engineer
|
|
1050
|
+
else None
|
|
1051
|
+
)
|
|
1052
|
+
|
|
1053
|
+
memory = config_dict.get("memory", None)
|
|
1054
|
+
memory_config = MemoryConfig.from_dict(memory) if memory else None
|
|
1055
|
+
|
|
1056
|
+
# Parse MCP configuration
|
|
1057
|
+
mcp = config_dict.get("mcp", None)
|
|
1058
|
+
mcp_config = MCPConfig.from_dict(mcp) if mcp else None
|
|
1059
|
+
|
|
1060
|
+
# Parse skill configuration
|
|
1061
|
+
skill = config_dict.get("skill", None)
|
|
1062
|
+
skill_config = SkillConfig.from_dict(skill) if skill else None
|
|
1063
|
+
|
|
1064
|
+
# ResourceSkillkit configuration (Claude Skill format support)
|
|
1065
|
+
resource_skills = config_dict.get("resource_skills", None)
|
|
1066
|
+
|
|
1067
|
+
ontology = config_dict.get("ontology", None)
|
|
1068
|
+
ontology_config = OntologyConfig.from_dict(ontology) if ontology else None
|
|
1069
|
+
|
|
1070
|
+
# Parse retrieval_model_config
|
|
1071
|
+
retrieval_model = config_dict.get("retrieval_model_config", None)
|
|
1072
|
+
retrieval_model_config = (
|
|
1073
|
+
RetrievalModelConfig.from_dict(retrieval_model)
|
|
1074
|
+
if retrieval_model
|
|
1075
|
+
else None
|
|
1076
|
+
)
|
|
1077
|
+
|
|
1078
|
+
return GlobalConfig(
|
|
1079
|
+
default_llm=default_llm,
|
|
1080
|
+
fast_llm=fast_llm,
|
|
1081
|
+
all_clouds_config=all_clouds_config,
|
|
1082
|
+
llmInstanceConfigs=llmInstanceConfigs,
|
|
1083
|
+
vm_config=vm_config,
|
|
1084
|
+
context_engineer_config=context_engineer_config,
|
|
1085
|
+
memory_config=memory_config,
|
|
1086
|
+
mcp_config=mcp_config,
|
|
1087
|
+
skill_config=skill_config,
|
|
1088
|
+
resource_skills=resource_skills,
|
|
1089
|
+
ontology_config=ontology_config,
|
|
1090
|
+
retrieval_model_config=retrieval_model_config,
|
|
1091
|
+
base_dir=base_dir,
|
|
1092
|
+
)
|
|
1093
|
+
else:
|
|
1094
|
+
model_name = config_dict.get("model_name")
|
|
1095
|
+
llm_instance_config = LLMInstanceConfig.from_dict(
|
|
1096
|
+
model_name, all_clouds_config=None, config_dict=config_dict
|
|
1097
|
+
)
|
|
1098
|
+
|
|
1099
|
+
# The old format also supports context_engineer, memory, and mcp configurations
|
|
1100
|
+
context_engineer = config_dict.get("context_engineer", None)
|
|
1101
|
+
context_engineer_config = (
|
|
1102
|
+
ContextEngineerConfig.from_dict(context_engineer, base_dir=base_dir)
|
|
1103
|
+
if context_engineer
|
|
1104
|
+
else None
|
|
1105
|
+
)
|
|
1106
|
+
|
|
1107
|
+
memory = config_dict.get("memory", None)
|
|
1108
|
+
memory_config = MemoryConfig.from_dict(memory) if memory else None
|
|
1109
|
+
|
|
1110
|
+
mcp = config_dict.get("mcp", None)
|
|
1111
|
+
mcp_config = MCPConfig.from_dict(mcp) if mcp else None
|
|
1112
|
+
|
|
1113
|
+
skill = config_dict.get("skill", None)
|
|
1114
|
+
skill_config = SkillConfig.from_dict(skill) if skill else None
|
|
1115
|
+
|
|
1116
|
+
resource_skills = config_dict.get("resource_skills", None)
|
|
1117
|
+
|
|
1118
|
+
ontology = config_dict.get("ontology", None)
|
|
1119
|
+
ontology_config = OntologyConfig.from_dict(ontology) if ontology else None
|
|
1120
|
+
|
|
1121
|
+
# Parse retrieval_model_config
|
|
1122
|
+
retrieval_model = config_dict.get("retrieval_model_config", None)
|
|
1123
|
+
retrieval_model_config = (
|
|
1124
|
+
RetrievalModelConfig.from_dict(retrieval_model)
|
|
1125
|
+
if retrieval_model
|
|
1126
|
+
else None
|
|
1127
|
+
)
|
|
1128
|
+
|
|
1129
|
+
llmInstanceConfigs = {model_name: llm_instance_config}
|
|
1130
|
+
if "name" in config_dict:
|
|
1131
|
+
llmInstanceConfigs[config_dict["name"]] = llm_instance_config
|
|
1132
|
+
return GlobalConfig(
|
|
1133
|
+
default_llm=model_name,
|
|
1134
|
+
llmInstanceConfigs=llmInstanceConfigs,
|
|
1135
|
+
context_engineer_config=context_engineer_config,
|
|
1136
|
+
memory_config=memory_config,
|
|
1137
|
+
mcp_config=mcp_config,
|
|
1138
|
+
skill_config=skill_config,
|
|
1139
|
+
resource_skills=resource_skills,
|
|
1140
|
+
ontology_config=ontology_config,
|
|
1141
|
+
retrieval_model_config=retrieval_model_config,
|
|
1142
|
+
)
|
|
1143
|
+
|
|
1144
|
+
@staticmethod
|
|
1145
|
+
def from_yaml(yaml_path: str) -> "GlobalConfig":
|
|
1146
|
+
with open(yaml_path, "r", encoding="utf-8") as file:
|
|
1147
|
+
config_dict = yaml.load(file, Loader=yaml.FullLoader)
|
|
1148
|
+
# Get the directory of the YAML file to resolve relative paths
|
|
1149
|
+
base_dir = os.path.dirname(os.path.abspath(yaml_path))
|
|
1150
|
+
return GlobalConfig.from_dict(config_dict, base_dir=base_dir)
|
|
1151
|
+
|
|
1152
|
+
@staticmethod
|
|
1153
|
+
def from_yaml_with_base(
|
|
1154
|
+
base_yaml: str, override_yaml: str = None
|
|
1155
|
+
) -> "GlobalConfig":
|
|
1156
|
+
"""Load from base configuration and override configuration, supporting configuration inheritance and merging.
|
|
1157
|
+
|
|
1158
|
+
Args:
|
|
1159
|
+
base_yaml: Path to the base configuration file (required)
|
|
1160
|
+
override_yaml: Path to the override configuration file (optional), will recursively override the base configuration
|
|
1161
|
+
|
|
1162
|
+
Returns:
|
|
1163
|
+
GlobalConfig: Merged configuration object
|
|
1164
|
+
|
|
1165
|
+
Examples:
|
|
1166
|
+
# Use only the base configuration
|
|
1167
|
+
config = GlobalConfig.from_yaml_with_base("config/global.yaml")
|
|
1168
|
+
|
|
1169
|
+
# Use base configuration + agent-specific configuration
|
|
1170
|
+
config = GlobalConfig.from_yaml_with_base(
|
|
1171
|
+
base_yaml="config/global.yaml",
|
|
1172
|
+
override_yaml="config/alice/agent.yaml"
|
|
1173
|
+
)
|
|
1174
|
+
"""
|
|
1175
|
+
# Load base configuration
|
|
1176
|
+
with open(base_yaml, "r", encoding="utf-8") as f:
|
|
1177
|
+
base_dict = yaml.load(f, Loader=yaml.FullLoader)
|
|
1178
|
+
|
|
1179
|
+
# If there is a configuration overlay, perform a deep merge.
|
|
1180
|
+
if override_yaml and os.path.exists(override_yaml):
|
|
1181
|
+
with open(override_yaml, "r", encoding="utf-8") as f:
|
|
1182
|
+
override_dict = yaml.load(f, Loader=yaml.FullLoader)
|
|
1183
|
+
# If the override_yaml file contains only comments or is empty, yaml.load will return None
|
|
1184
|
+
if override_dict:
|
|
1185
|
+
config_dict = GlobalConfig._deep_merge(base_dict, override_dict)
|
|
1186
|
+
else:
|
|
1187
|
+
config_dict = base_dict
|
|
1188
|
+
# Use override_yaml directory as base_dir for relative paths
|
|
1189
|
+
base_dir = os.path.dirname(os.path.abspath(override_yaml))
|
|
1190
|
+
else:
|
|
1191
|
+
config_dict = base_dict
|
|
1192
|
+
# Use base_yaml directory as base_dir for relative paths
|
|
1193
|
+
base_dir = os.path.dirname(os.path.abspath(base_yaml))
|
|
1194
|
+
|
|
1195
|
+
return GlobalConfig.from_dict(config_dict, base_dir=base_dir)
|
|
1196
|
+
|
|
1197
|
+
@staticmethod
|
|
1198
|
+
def _deep_merge(base: dict, override: dict) -> dict:
|
|
1199
|
+
"""Deeply merge two dictionaries, with values in override recursively overriding those in base.
|
|
1200
|
+
|
|
1201
|
+
Args:
|
|
1202
|
+
base: Base dictionary
|
|
1203
|
+
override: Override dictionary
|
|
1204
|
+
|
|
1205
|
+
Returns:
|
|
1206
|
+
dict: Merged dictionary (new dictionary, original dictionaries are not modified)
|
|
1207
|
+
|
|
1208
|
+
Note:
|
|
1209
|
+
- Dictionary types are merged recursively
|
|
1210
|
+
- Other types (including lists) are directly overwritten
|
|
1211
|
+
"""
|
|
1212
|
+
result = base.copy()
|
|
1213
|
+
for key, value in override.items():
|
|
1214
|
+
if (
|
|
1215
|
+
key in result
|
|
1216
|
+
and isinstance(result[key], dict)
|
|
1217
|
+
and isinstance(value, dict)
|
|
1218
|
+
):
|
|
1219
|
+
# Recursively merge dictionaries
|
|
1220
|
+
result[key] = GlobalConfig._deep_merge(result[key], value)
|
|
1221
|
+
else:
|
|
1222
|
+
# Direct override (including lists, strings, numbers, etc.)
|
|
1223
|
+
result[key] = value
|
|
1224
|
+
return result
|
|
1225
|
+
|
|
1226
|
+
def to_dict(self) -> dict:
|
|
1227
|
+
"""Convert to dictionary"""
|
|
1228
|
+
result = {
|
|
1229
|
+
"default": self.default_llm,
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
# Add fast_llm (if different from default_llm)
|
|
1233
|
+
if self.fast_llm and self.fast_llm != self.default_llm:
|
|
1234
|
+
result["fast"] = self.fast_llm
|
|
1235
|
+
|
|
1236
|
+
# Add clouds configuration
|
|
1237
|
+
if self.all_clouds_config:
|
|
1238
|
+
clouds = {"default": self.all_clouds_config.default_cloud}
|
|
1239
|
+
for cloud_name, cloud_config in self.all_clouds_config.clouds.items():
|
|
1240
|
+
clouds[cloud_name] = {
|
|
1241
|
+
"api": cloud_config.api,
|
|
1242
|
+
"api_key": cloud_config.api_key,
|
|
1243
|
+
}
|
|
1244
|
+
if cloud_config.user_id:
|
|
1245
|
+
clouds[cloud_name]["userid"] = cloud_config.user_id
|
|
1246
|
+
if cloud_config.headers:
|
|
1247
|
+
clouds[cloud_name]["headers"] = cloud_config.headers
|
|
1248
|
+
result["clouds"] = clouds
|
|
1249
|
+
|
|
1250
|
+
# Add llms configuration
|
|
1251
|
+
if self.llmInstanceConfigs:
|
|
1252
|
+
llms = {}
|
|
1253
|
+
for llm_name, llm_instance_config in self.llmInstanceConfigs.items():
|
|
1254
|
+
llm_dict = llm_instance_config.llm_config.to_dict()
|
|
1255
|
+
# If cloud configuration is used, add a cloud reference
|
|
1256
|
+
if self.all_clouds_config:
|
|
1257
|
+
for cloud_name, cloud_config in self.all_clouds_config.clouds.items():
|
|
1258
|
+
if cloud_config == llm_instance_config.cloud_config:
|
|
1259
|
+
llm_dict["cloud"] = cloud_name
|
|
1260
|
+
break
|
|
1261
|
+
llms[llm_name] = llm_dict
|
|
1262
|
+
result["llms"] = llms
|
|
1263
|
+
|
|
1264
|
+
# Add additional configuration
|
|
1265
|
+
if self._vm_config:
|
|
1266
|
+
result["vm"] = self._vm_config.toDict()
|
|
1267
|
+
|
|
1268
|
+
if self._context_engineer_config:
|
|
1269
|
+
result["context_engineer"] = self._context_engineer_config.to_dict()
|
|
1270
|
+
|
|
1271
|
+
if self._memory_config:
|
|
1272
|
+
result["memory"] = self._memory_config.to_dict()
|
|
1273
|
+
|
|
1274
|
+
if self._mcp_config:
|
|
1275
|
+
result["mcp"] = self._mcp_config.to_dict()
|
|
1276
|
+
|
|
1277
|
+
if self._skill_config:
|
|
1278
|
+
result["skill"] = self._skill_config.to_dict()
|
|
1279
|
+
|
|
1280
|
+
if self._resource_skills is not None:
|
|
1281
|
+
result["resource_skills"] = self._resource_skills
|
|
1282
|
+
|
|
1283
|
+
if self._ontology_config:
|
|
1284
|
+
result["ontology"] = self._ontology_config.to_dict()
|
|
1285
|
+
|
|
1286
|
+
if self._retrieval_model_config:
|
|
1287
|
+
result["retrieval_model_config"] = self._retrieval_model_config.to_dict()
|
|
1288
|
+
|
|
1289
|
+
return result
|