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,212 @@
|
|
|
1
|
+
import fcntl
|
|
2
|
+
import json
|
|
3
|
+
import os
|
|
4
|
+
import threading
|
|
5
|
+
import time
|
|
6
|
+
from typing import List, Dict, Any, Optional
|
|
7
|
+
import uuid
|
|
8
|
+
from dolphin.core.logging.logger import get_logger
|
|
9
|
+
|
|
10
|
+
logger = get_logger("utils.cache_kv")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class CacheKV:
|
|
14
|
+
def __init__(
|
|
15
|
+
self, filePath: str, dumpInterval: int = 5, expireTimeByDay: float = 1
|
|
16
|
+
):
|
|
17
|
+
self.filePath = filePath
|
|
18
|
+
self.cache = {}
|
|
19
|
+
self.lock = threading.Lock()
|
|
20
|
+
self.dumpInterval = dumpInterval
|
|
21
|
+
self.lastDumpSec = 0
|
|
22
|
+
self.expireTimeByDay = expireTimeByDay
|
|
23
|
+
self.loadCache()
|
|
24
|
+
|
|
25
|
+
def loadCache(self):
|
|
26
|
+
if not os.path.exists(self.filePath):
|
|
27
|
+
return # No file, nothing to load
|
|
28
|
+
|
|
29
|
+
try:
|
|
30
|
+
with open(self.filePath, "r", encoding="utf-8") as f:
|
|
31
|
+
fd = f.fileno()
|
|
32
|
+
fcntl.flock(fd, fcntl.LOCK_SH) # Shared lock for reading
|
|
33
|
+
try:
|
|
34
|
+
loaded_cache = json.load(f)
|
|
35
|
+
for key, value in loaded_cache.items():
|
|
36
|
+
if self._cacheItemExpired(value):
|
|
37
|
+
continue
|
|
38
|
+
|
|
39
|
+
if (
|
|
40
|
+
isinstance(value, dict)
|
|
41
|
+
and "value" in value
|
|
42
|
+
and "timestamp" in value
|
|
43
|
+
):
|
|
44
|
+
self.cache[key] = value
|
|
45
|
+
else:
|
|
46
|
+
# Compatible with old formats
|
|
47
|
+
self.cache[key] = {"value": value, "timestamp": time.time()}
|
|
48
|
+
finally:
|
|
49
|
+
fcntl.flock(fd, fcntl.LOCK_UN)
|
|
50
|
+
except (json.JSONDecodeError, IOError) as e:
|
|
51
|
+
logger.error(
|
|
52
|
+
f"Error loading cache file {self.filePath}: {e} try to backup and reset cache"
|
|
53
|
+
)
|
|
54
|
+
backup_path = self.filePath + ".err"
|
|
55
|
+
self.cache = {}
|
|
56
|
+
try:
|
|
57
|
+
os.rename(self.filePath, backup_path)
|
|
58
|
+
except OSError as rename_err:
|
|
59
|
+
logger.error(f"Failed to backup cache file: {rename_err}")
|
|
60
|
+
|
|
61
|
+
def dumpCache(self):
|
|
62
|
+
curTime = time.time()
|
|
63
|
+
if curTime - self.lastDumpSec < self.dumpInterval:
|
|
64
|
+
return
|
|
65
|
+
|
|
66
|
+
with self.lock:
|
|
67
|
+
# Ensure the directory exists
|
|
68
|
+
os.makedirs(os.path.dirname(self.filePath), exist_ok=True)
|
|
69
|
+
# Clear expired cache
|
|
70
|
+
self.cache = {
|
|
71
|
+
key: value
|
|
72
|
+
for key, value in self.cache.items()
|
|
73
|
+
if not self._cacheItemExpired(value)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
# Use unique temporary filename (PID + UUID)
|
|
77
|
+
temp_path = f"{self.filePath}.tmp.{os.getpid()}.{uuid.uuid4().hex}"
|
|
78
|
+
try:
|
|
79
|
+
with open(temp_path, "w", encoding="utf-8") as f:
|
|
80
|
+
fd = f.fileno()
|
|
81
|
+
fcntl.flock(fd, fcntl.LOCK_EX) # Exclusive lock for writing
|
|
82
|
+
try:
|
|
83
|
+
json.dump(self.cache, f, ensure_ascii=False)
|
|
84
|
+
f.flush()
|
|
85
|
+
os.fsync(fd) # Ensure data is written to disk
|
|
86
|
+
finally:
|
|
87
|
+
fcntl.flock(fd, fcntl.LOCK_UN)
|
|
88
|
+
|
|
89
|
+
# Atomic rename
|
|
90
|
+
os.rename(temp_path, self.filePath)
|
|
91
|
+
self.lastDumpSec = curTime
|
|
92
|
+
except Exception as e:
|
|
93
|
+
logger.error(f"Error dumping cache to {self.filePath}: {e}")
|
|
94
|
+
if os.path.exists(temp_path):
|
|
95
|
+
try:
|
|
96
|
+
os.remove(temp_path)
|
|
97
|
+
except OSError as remove_err:
|
|
98
|
+
logger.error(
|
|
99
|
+
f"Failed to remove temp file {temp_path}: {remove_err}"
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
def _keyToStr(self, key: List[Dict]) -> str:
|
|
103
|
+
return json.dumps(key, sort_keys=True, ensure_ascii=False)
|
|
104
|
+
|
|
105
|
+
def get(self, key: List[Dict]) -> Optional[Any]:
|
|
106
|
+
keyStr = self._keyToStr(key)
|
|
107
|
+
with self.lock:
|
|
108
|
+
cached_item = self.cache.get(keyStr)
|
|
109
|
+
if cached_item:
|
|
110
|
+
if not self._cacheItemExpired(cached_item):
|
|
111
|
+
return cached_item["value"]
|
|
112
|
+
else:
|
|
113
|
+
del self.cache[keyStr]
|
|
114
|
+
return None
|
|
115
|
+
|
|
116
|
+
def set(self, key: List[Dict], value: Any):
|
|
117
|
+
keyStr = self._keyToStr(key)
|
|
118
|
+
with self.lock:
|
|
119
|
+
self.cache[keyStr] = {"value": value, "timestamp": time.time()}
|
|
120
|
+
self.dumpCache()
|
|
121
|
+
|
|
122
|
+
def remove(self, key: List[Dict]):
|
|
123
|
+
keyStr = self._keyToStr(key)
|
|
124
|
+
with self.lock:
|
|
125
|
+
if keyStr in self.cache:
|
|
126
|
+
del self.cache[keyStr]
|
|
127
|
+
self.dumpCache()
|
|
128
|
+
|
|
129
|
+
def _cacheItemExpired(self, cacheItem: dict) -> bool:
|
|
130
|
+
return time.time() - cacheItem["timestamp"] > self.expireTimeByDay * 86400
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
class CacheKVMgr:
|
|
134
|
+
FilePrefix = "cache_"
|
|
135
|
+
|
|
136
|
+
def __init__(
|
|
137
|
+
self,
|
|
138
|
+
cacheDir: str,
|
|
139
|
+
category: str,
|
|
140
|
+
dumpInterval: int = 5,
|
|
141
|
+
expireTimeByDay: float = 1,
|
|
142
|
+
):
|
|
143
|
+
self.prefix = f"{CacheKVMgr.FilePrefix}{category}_"
|
|
144
|
+
self.cacheDir = cacheDir
|
|
145
|
+
self.category = category
|
|
146
|
+
self.dumpInterval = dumpInterval
|
|
147
|
+
self.expireTimeByDay = expireTimeByDay
|
|
148
|
+
self.caches = {}
|
|
149
|
+
self.loadCaches()
|
|
150
|
+
|
|
151
|
+
def loadCaches(self):
|
|
152
|
+
if not os.path.exists(self.cacheDir):
|
|
153
|
+
os.makedirs(self.cacheDir)
|
|
154
|
+
|
|
155
|
+
for fileName in os.listdir(self.cacheDir):
|
|
156
|
+
if fileName.startswith(self.prefix) and fileName.endswith(".json"):
|
|
157
|
+
modelName = fileName[len(self.prefix) : -5]
|
|
158
|
+
filePath = os.path.join(self.cacheDir, fileName)
|
|
159
|
+
try:
|
|
160
|
+
self.caches[modelName] = CacheKV(
|
|
161
|
+
filePath, self.dumpInterval, self.expireTimeByDay
|
|
162
|
+
)
|
|
163
|
+
except Exception as e:
|
|
164
|
+
logger.error(f"Error initializing cache for {modelName}: {e}")
|
|
165
|
+
raise Exception(f"Error initializing cache for {modelName}: {e}")
|
|
166
|
+
|
|
167
|
+
def getCache(self, modelName: str) -> CacheKV:
|
|
168
|
+
return self.caches.get(modelName)
|
|
169
|
+
|
|
170
|
+
def getValue(self, modelName: str, key: List[Dict]) -> Optional[Any]:
|
|
171
|
+
cache = self.getCache(modelName)
|
|
172
|
+
if cache:
|
|
173
|
+
result = cache.get(key)
|
|
174
|
+
if result:
|
|
175
|
+
logger.debug(f"cache hit: {modelName} {key}")
|
|
176
|
+
return result
|
|
177
|
+
return None
|
|
178
|
+
|
|
179
|
+
def setValue(self, modelName: str, key: List[Dict], value: Any):
|
|
180
|
+
cache = self.getCache(modelName)
|
|
181
|
+
if not cache:
|
|
182
|
+
filePath = os.path.join(self.cacheDir, f"{self.prefix}{modelName}.json")
|
|
183
|
+
cache = CacheKV(filePath, self.dumpInterval, self.expireTimeByDay)
|
|
184
|
+
self.caches[modelName] = cache
|
|
185
|
+
cache.set(key, value)
|
|
186
|
+
|
|
187
|
+
def removeValue(self, modelName: str, key: List[Dict]):
|
|
188
|
+
cache = self.getCache(modelName)
|
|
189
|
+
if cache:
|
|
190
|
+
cache.remove(key)
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
class CacheKVCenter:
|
|
194
|
+
def __init__(self):
|
|
195
|
+
self.repos = {}
|
|
196
|
+
|
|
197
|
+
def getCacheMgr(
|
|
198
|
+
self,
|
|
199
|
+
cacheDir: str,
|
|
200
|
+
category: str,
|
|
201
|
+
dumpInterval: int = 5,
|
|
202
|
+
expireTimeByDay: float = 1,
|
|
203
|
+
) -> CacheKVMgr:
|
|
204
|
+
key = f"{cacheDir}_{category}"
|
|
205
|
+
if key not in self.repos:
|
|
206
|
+
self.repos[key] = CacheKVMgr(
|
|
207
|
+
cacheDir, category, dumpInterval, expireTimeByDay
|
|
208
|
+
)
|
|
209
|
+
return self.repos[key]
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
GlobalCacheKVCenter = CacheKVCenter()
|
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
from typing import Any, Dict, Iterator, List, Optional
|
|
2
|
+
import ast
|
|
3
|
+
import json
|
|
4
|
+
|
|
5
|
+
from dolphin.core.logging.logger import get_logger
|
|
6
|
+
|
|
7
|
+
logger = get_logger("utils.tools")
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def safe_json_loads(json_str: str, strict: bool = True) -> Dict[str, Any]:
|
|
11
|
+
"""A safe JSON parsing function that supports multiple parsing strategies.
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
json_str: The JSON string to be parsed.
|
|
15
|
+
strict: Whether to use strict mode.
|
|
16
|
+
|
|
17
|
+
Returns:
|
|
18
|
+
The parsed dictionary object.
|
|
19
|
+
|
|
20
|
+
Raises:
|
|
21
|
+
ValueError: When all parsing strategies fail.
|
|
22
|
+
"""
|
|
23
|
+
if not json_str or not json_str.strip():
|
|
24
|
+
return {}
|
|
25
|
+
|
|
26
|
+
json_str = json_str.strip()
|
|
27
|
+
|
|
28
|
+
# Strategy 1: Use ast.literal_eval for safer parsing
|
|
29
|
+
try:
|
|
30
|
+
value = ast.literal_eval(json_str)
|
|
31
|
+
if isinstance(value, dict) or isinstance(value, list):
|
|
32
|
+
return value
|
|
33
|
+
except Exception:
|
|
34
|
+
pass
|
|
35
|
+
|
|
36
|
+
# Strategy 2: Use standard json.loads with strict mode
|
|
37
|
+
try:
|
|
38
|
+
return json.loads(json_str, strict=strict)
|
|
39
|
+
except Exception:
|
|
40
|
+
pass
|
|
41
|
+
|
|
42
|
+
# Strategy 3: Replace single quotes with double quotes then parse
|
|
43
|
+
try:
|
|
44
|
+
normalized_content = json_str.replace("'", '"')
|
|
45
|
+
return json.loads(normalized_content, strict=strict)
|
|
46
|
+
except Exception:
|
|
47
|
+
pass
|
|
48
|
+
|
|
49
|
+
# Strategy 4: Try non-strict mode if we were in strict mode
|
|
50
|
+
if strict:
|
|
51
|
+
try:
|
|
52
|
+
return json.loads(json_str, strict=False)
|
|
53
|
+
except Exception:
|
|
54
|
+
pass
|
|
55
|
+
|
|
56
|
+
# Strategy 6: Advanced string cleaning
|
|
57
|
+
try:
|
|
58
|
+
# Clean up common formatting issues to improve fault tolerance
|
|
59
|
+
if json_str.startswith('{\\"'):
|
|
60
|
+
cleaned = json_str.replace('\\"', '"')
|
|
61
|
+
return json.loads(cleaned, strict=False)
|
|
62
|
+
except Exception:
|
|
63
|
+
pass
|
|
64
|
+
|
|
65
|
+
# Strategy 7: Replace newlines with spaces
|
|
66
|
+
try:
|
|
67
|
+
cleaned = json_str.replace("\n", " ")
|
|
68
|
+
return json.loads(cleaned, strict=False)
|
|
69
|
+
except Exception:
|
|
70
|
+
pass
|
|
71
|
+
|
|
72
|
+
# Strategy 8: Append missing closing brace if exactly one more open brace
|
|
73
|
+
try:
|
|
74
|
+
open_brace = json_str.count("{")
|
|
75
|
+
close_brace = json_str.count("}")
|
|
76
|
+
if open_brace == close_brace + 1:
|
|
77
|
+
appended = json_str + "}"
|
|
78
|
+
return json.loads(appended, strict=False)
|
|
79
|
+
except Exception:
|
|
80
|
+
pass
|
|
81
|
+
|
|
82
|
+
# Strategy 9: Handle malformed empty object like '{"}' -> treat as {}
|
|
83
|
+
try:
|
|
84
|
+
stripped = json_str.strip()
|
|
85
|
+
if stripped == '{"}':
|
|
86
|
+
return {}
|
|
87
|
+
except Exception:
|
|
88
|
+
pass
|
|
89
|
+
|
|
90
|
+
raise ValueError(f"Failed to parse JSON: {json_str}")
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def safe_jsonl_loads(jsonl_str: str) -> List[Dict[str, Any]]:
|
|
94
|
+
"""Safe JSONL parsing function
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
jsonl_str: JSONL string to be parsed
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
List of parsed dictionaries
|
|
101
|
+
|
|
102
|
+
Raises:
|
|
103
|
+
ValueError: If parsing fails
|
|
104
|
+
"""
|
|
105
|
+
jsonl_str = jsonl_str.strip()
|
|
106
|
+
if not jsonl_str:
|
|
107
|
+
return []
|
|
108
|
+
|
|
109
|
+
results = []
|
|
110
|
+
try:
|
|
111
|
+
results = json.loads(jsonl_str, strict=False)
|
|
112
|
+
except Exception:
|
|
113
|
+
try:
|
|
114
|
+
results = ast.literal_eval(jsonl_str)
|
|
115
|
+
if isinstance(results, list):
|
|
116
|
+
return results
|
|
117
|
+
except Exception:
|
|
118
|
+
pass
|
|
119
|
+
|
|
120
|
+
if results:
|
|
121
|
+
if isinstance(results, list):
|
|
122
|
+
return results
|
|
123
|
+
if isinstance(results, dict):
|
|
124
|
+
for key, value in results.items():
|
|
125
|
+
if isinstance(value, list):
|
|
126
|
+
return value
|
|
127
|
+
|
|
128
|
+
results = []
|
|
129
|
+
for line_num, line in enumerate(jsonl_str.split("\n"), 1):
|
|
130
|
+
line = line.strip()
|
|
131
|
+
if not line:
|
|
132
|
+
continue
|
|
133
|
+
|
|
134
|
+
try:
|
|
135
|
+
obj = safe_json_loads(line, strict=False)
|
|
136
|
+
results.append(obj)
|
|
137
|
+
except ValueError as e:
|
|
138
|
+
logger.error(f"Failed to parse JSONL line {line_num}: {e}")
|
|
139
|
+
logger.error(f"Line content: {line}")
|
|
140
|
+
raise ValueError(f"Invalid JSON format in JSONL line {line_num}: {e}")
|
|
141
|
+
|
|
142
|
+
return results
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def extract_json_from_response(response_str: str) -> str:
|
|
146
|
+
"""Extract JSON content from response string (handling code block markers)
|
|
147
|
+
|
|
148
|
+
Args:
|
|
149
|
+
response_str: Original response string
|
|
150
|
+
|
|
151
|
+
Returns:
|
|
152
|
+
Extracted JSON string
|
|
153
|
+
"""
|
|
154
|
+
response_str = response_str.strip()
|
|
155
|
+
|
|
156
|
+
# Process ```json code block
|
|
157
|
+
if "```json" in response_str:
|
|
158
|
+
start = response_str.find("```json") + 7
|
|
159
|
+
end = response_str.find("```", start)
|
|
160
|
+
if end != -1:
|
|
161
|
+
return response_str[start:end].strip()
|
|
162
|
+
|
|
163
|
+
# Process generic code blocks
|
|
164
|
+
elif response_str.startswith("```") and response_str.endswith("```"):
|
|
165
|
+
lines = response_str.split("\n")
|
|
166
|
+
if len(lines) > 2:
|
|
167
|
+
return "\n".join(lines[1:-1])
|
|
168
|
+
|
|
169
|
+
elif response_str.startswith("{"):
|
|
170
|
+
# Find the matching } considering nested braces
|
|
171
|
+
start = 0
|
|
172
|
+
count = 1
|
|
173
|
+
end = -1
|
|
174
|
+
for i in range(1, len(response_str)):
|
|
175
|
+
if response_str[i] == "{":
|
|
176
|
+
count += 1
|
|
177
|
+
elif response_str[i] == "}":
|
|
178
|
+
count -= 1
|
|
179
|
+
if count == 0:
|
|
180
|
+
end = i
|
|
181
|
+
break
|
|
182
|
+
if end != -1:
|
|
183
|
+
return response_str[start : end + 1].strip()
|
|
184
|
+
|
|
185
|
+
return response_str
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def extract_jsonl_from_response(response_str: str) -> str:
|
|
189
|
+
"""Extract JSONL content from response string (handling code block markers)
|
|
190
|
+
|
|
191
|
+
Args:
|
|
192
|
+
response_str: Original response string
|
|
193
|
+
|
|
194
|
+
Returns:
|
|
195
|
+
Extracted JSONL string
|
|
196
|
+
"""
|
|
197
|
+
response_str = response_str.strip()
|
|
198
|
+
|
|
199
|
+
# Handle ```jsonl or ```json code blocks
|
|
200
|
+
start_markers = ["```jsonl", "```json"]
|
|
201
|
+
for marker in start_markers:
|
|
202
|
+
if marker in response_str:
|
|
203
|
+
start = response_str.find(marker) + len(marker)
|
|
204
|
+
end = response_str.find("```", start)
|
|
205
|
+
if end != -1:
|
|
206
|
+
return response_str[start:end].strip()
|
|
207
|
+
break
|
|
208
|
+
|
|
209
|
+
# Process generic code blocks
|
|
210
|
+
if response_str.startswith("```") and response_str.endswith("```"):
|
|
211
|
+
lines = response_str.split("\n")
|
|
212
|
+
if len(lines) > 2:
|
|
213
|
+
return "\n".join(lines[1:-1])
|
|
214
|
+
|
|
215
|
+
elif response_str.startswith("["):
|
|
216
|
+
# Find the matching ] considering nested braces
|
|
217
|
+
start = 0
|
|
218
|
+
count = 1
|
|
219
|
+
end = -1
|
|
220
|
+
for i in range(1, len(response_str)):
|
|
221
|
+
if response_str[i] == "[":
|
|
222
|
+
count += 1
|
|
223
|
+
elif response_str[i] == "]":
|
|
224
|
+
count -= 1
|
|
225
|
+
if count == 0:
|
|
226
|
+
end = i
|
|
227
|
+
break
|
|
228
|
+
if end != -1:
|
|
229
|
+
return response_str[start : end + 1].strip()
|
|
230
|
+
|
|
231
|
+
return response_str
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def extract_json(content: str) -> Dict[str, Any]:
|
|
235
|
+
"""Extract JSON content from response string (handling code block markers)
|
|
236
|
+
|
|
237
|
+
Args:
|
|
238
|
+
content: Original response string
|
|
239
|
+
"""
|
|
240
|
+
content = extract_json_from_response(content)
|
|
241
|
+
result = safe_json_loads(content)
|
|
242
|
+
if isinstance(result, dict) or isinstance(result, list):
|
|
243
|
+
return result
|
|
244
|
+
else:
|
|
245
|
+
raise ValueError(f"Invalid JSON format: {content}")
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def extract_jsonl(content: str) -> List[Dict[str, Any]]:
|
|
249
|
+
"""Extract JSONL content from response string (handling code block markers)
|
|
250
|
+
|
|
251
|
+
Args:
|
|
252
|
+
content: Original response string
|
|
253
|
+
"""
|
|
254
|
+
content = extract_jsonl_from_response(content)
|
|
255
|
+
result = safe_jsonl_loads(content)
|
|
256
|
+
if isinstance(result, list):
|
|
257
|
+
return result
|
|
258
|
+
else:
|
|
259
|
+
raise ValueError(f"Invalid JSONL format: {content}")
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
class Tool:
|
|
263
|
+
# The unique name of the tool that clearly communicates its purpose
|
|
264
|
+
name: str
|
|
265
|
+
# Tool description, explaining the functions of the tool, facilitating the selection and invocation of LLM
|
|
266
|
+
description: str
|
|
267
|
+
# Tool type, eg: flow, component, api, etc
|
|
268
|
+
# type: str
|
|
269
|
+
# Input parameters of the tool
|
|
270
|
+
inputs: Dict
|
|
271
|
+
# Output format of the tool
|
|
272
|
+
outputs: Dict
|
|
273
|
+
|
|
274
|
+
result_process_strategy_cfg: list[Dict[str, str]] = None
|
|
275
|
+
|
|
276
|
+
def __init__(
|
|
277
|
+
self,
|
|
278
|
+
name: str,
|
|
279
|
+
description: str,
|
|
280
|
+
inputs: Dict,
|
|
281
|
+
outputs: Dict,
|
|
282
|
+
props: Optional[dict] = None,
|
|
283
|
+
result_process_strategy_cfg: list[Dict[str, str]] = None,
|
|
284
|
+
):
|
|
285
|
+
self.name = name
|
|
286
|
+
self.description = description
|
|
287
|
+
self.inputs = inputs
|
|
288
|
+
self.outputs = outputs
|
|
289
|
+
self.props = props
|
|
290
|
+
self.intervention = props.get("intervention", False)
|
|
291
|
+
self.result_process_strategy_cfg = result_process_strategy_cfg
|
|
292
|
+
|
|
293
|
+
def tool_info(self) -> dict:
|
|
294
|
+
"""The tool's information."""
|
|
295
|
+
return {
|
|
296
|
+
"name": self.name,
|
|
297
|
+
"description": self.description,
|
|
298
|
+
"inputs": self.inputs,
|
|
299
|
+
"outputs": self.outputs,
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
def run(self, **kwargs) -> Any:
|
|
303
|
+
"""
|
|
304
|
+
Run the tool
|
|
305
|
+
"""
|
|
306
|
+
raise NotImplementedError
|
|
307
|
+
|
|
308
|
+
async def arun(self, **kwargs) -> Any:
|
|
309
|
+
"""
|
|
310
|
+
Run the tool asynchronously.
|
|
311
|
+
"""
|
|
312
|
+
raise NotImplementedError
|
|
313
|
+
|
|
314
|
+
def run_stream(self, **kwargs) -> Iterator[Any]:
|
|
315
|
+
"""
|
|
316
|
+
Run the tool with streaming output.
|
|
317
|
+
"""
|
|
318
|
+
raise NotImplementedError
|
|
319
|
+
|
|
320
|
+
async def arun_stream(self, **kwargs) -> Iterator[Any]:
|
|
321
|
+
"""
|
|
322
|
+
Run the tool with streaming output asynchronously.
|
|
323
|
+
"""
|
|
324
|
+
raise NotImplementedError
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
class ToolInterrupt(Exception):
|
|
328
|
+
"""Exception raised when the tool is interrupted."""
|
|
329
|
+
|
|
330
|
+
def __init__(
|
|
331
|
+
self,
|
|
332
|
+
message="The tool was interrupted.",
|
|
333
|
+
tool_name: str = None,
|
|
334
|
+
tool_args: List[Dict] = None,
|
|
335
|
+
*args,
|
|
336
|
+
**kwargs,
|
|
337
|
+
):
|
|
338
|
+
super().__init__(message, *args, **kwargs)
|
|
339
|
+
self.tool_name = tool_name if tool_name else ""
|
|
340
|
+
self.tool_args = tool_args if tool_args else []
|
dolphin/lib/__init__.py
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
Dolphin Lib - 标准库和工具集(用户态)
|
|
4
|
+
|
|
5
|
+
职责:
|
|
6
|
+
- 内置 Skillkits(search、sql、memory、mcp 等)
|
|
7
|
+
- Ontology 管理系统
|
|
8
|
+
- VM 虚拟机(可选执行后端)
|
|
9
|
+
- Memory 内存管理(知识管理)
|
|
10
|
+
- 工具函数库
|
|
11
|
+
- 调试可视化工具
|
|
12
|
+
|
|
13
|
+
依赖规则:
|
|
14
|
+
- dolphin.lib → 依赖 dolphin.core
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from typing import TYPE_CHECKING
|
|
18
|
+
|
|
19
|
+
if TYPE_CHECKING:
|
|
20
|
+
# Skillkits
|
|
21
|
+
from dolphin.lib.skillkits import (
|
|
22
|
+
SearchSkillkit,
|
|
23
|
+
SQLSkillkit,
|
|
24
|
+
MemorySkillkit,
|
|
25
|
+
MCPSkillkit,
|
|
26
|
+
OntologySkillkit,
|
|
27
|
+
PlanActSkillkit,
|
|
28
|
+
CognitiveSkillkit,
|
|
29
|
+
VMSkillkit,
|
|
30
|
+
NoopSkillkit,
|
|
31
|
+
ResourceSkillkit,
|
|
32
|
+
SystemFunctionsSkillKit,
|
|
33
|
+
AgentSkillKit,
|
|
34
|
+
)
|
|
35
|
+
# Ontology
|
|
36
|
+
from dolphin.lib.ontology import (
|
|
37
|
+
Ontology,
|
|
38
|
+
OntologyManager,
|
|
39
|
+
OntologyContext,
|
|
40
|
+
)
|
|
41
|
+
# VM
|
|
42
|
+
from dolphin.lib.vm import (
|
|
43
|
+
VM,
|
|
44
|
+
PythonSessionManager,
|
|
45
|
+
)
|
|
46
|
+
# Memory
|
|
47
|
+
from dolphin.lib.memory import (
|
|
48
|
+
MemoryManager,
|
|
49
|
+
)
|
|
50
|
+
# Skill Results
|
|
51
|
+
from dolphin.lib.skill_results import (
|
|
52
|
+
CacheBackend,
|
|
53
|
+
ResultProcessor,
|
|
54
|
+
ResultReference,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
_module_lookup = {
|
|
58
|
+
# Skillkits
|
|
59
|
+
"SearchSkillkit": "dolphin.lib.skillkits",
|
|
60
|
+
"SQLSkillkit": "dolphin.lib.skillkits",
|
|
61
|
+
"MemorySkillkit": "dolphin.lib.skillkits",
|
|
62
|
+
"MCPSkillkit": "dolphin.lib.skillkits",
|
|
63
|
+
"OntologySkillkit": "dolphin.lib.skillkits",
|
|
64
|
+
"PlanActSkillkit": "dolphin.lib.skillkits",
|
|
65
|
+
"CognitiveSkillkit": "dolphin.lib.skillkits",
|
|
66
|
+
"VMSkillkit": "dolphin.lib.skillkits",
|
|
67
|
+
"NoopSkillkit": "dolphin.lib.skillkits",
|
|
68
|
+
"ResourceSkillkit": "dolphin.lib.skillkits",
|
|
69
|
+
"SystemFunctionsSkillKit": "dolphin.lib.skillkits",
|
|
70
|
+
"AgentSkillKit": "dolphin.lib.skillkits",
|
|
71
|
+
# Ontology
|
|
72
|
+
"Ontology": "dolphin.lib.ontology",
|
|
73
|
+
"OntologyManager": "dolphin.lib.ontology",
|
|
74
|
+
"OntologyContext": "dolphin.lib.ontology",
|
|
75
|
+
# VM
|
|
76
|
+
"VM": "dolphin.lib.vm",
|
|
77
|
+
"PythonSessionManager": "dolphin.lib.vm",
|
|
78
|
+
# Memory
|
|
79
|
+
"MemoryManager": "dolphin.lib.memory",
|
|
80
|
+
# Skill Results
|
|
81
|
+
"CacheBackend": "dolphin.lib.skill_results",
|
|
82
|
+
"ResultProcessor": "dolphin.lib.skill_results",
|
|
83
|
+
"ResultReference": "dolphin.lib.skill_results",
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
def __getattr__(name):
|
|
87
|
+
if name in _module_lookup:
|
|
88
|
+
import importlib
|
|
89
|
+
module = importlib.import_module(_module_lookup[name])
|
|
90
|
+
return getattr(module, name)
|
|
91
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
92
|
+
|
|
93
|
+
__all__ = list(_module_lookup.keys())
|