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,274 @@
|
|
|
1
|
+
from typing import List, Optional
|
|
2
|
+
import re
|
|
3
|
+
from dolphin.core.skill.skill_function import SkillFunction
|
|
4
|
+
from dolphin.core.skill.skillkit import Skillkit
|
|
5
|
+
|
|
6
|
+
from dolphin.lib.ontology.ontology_context import OntologyContext
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class SQLSkillkit(Skillkit):
|
|
10
|
+
def __init__(self, ontologyContext: Optional[OntologyContext] = None):
|
|
11
|
+
super().__init__()
|
|
12
|
+
self.ontologyContext = ontologyContext
|
|
13
|
+
|
|
14
|
+
def getName(self) -> str:
|
|
15
|
+
return "sql_skillkit"
|
|
16
|
+
|
|
17
|
+
def setGlobalConfig(self, globalConfig):
|
|
18
|
+
super().setGlobalConfig(globalConfig)
|
|
19
|
+
if (
|
|
20
|
+
self.ontologyContext is None
|
|
21
|
+
and self.globalConfig.ontology_config is not None
|
|
22
|
+
):
|
|
23
|
+
self.ontologyContext = OntologyContext.loadOntologyContext(
|
|
24
|
+
self.globalConfig.ontology_config
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
def executeSQL(
|
|
28
|
+
self, datasource: str, sql: str, dialect: str = "mysql", **kwargs
|
|
29
|
+
) -> str:
|
|
30
|
+
"""Execute an SQL statement in the specified data source and return the result.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
datasource (str): Name or concept name of the data source
|
|
34
|
+
sql (str): A valid SQL statement starting with SELECT
|
|
35
|
+
dialect (str): SQL dialect, default is 'mysql', supports 'oracle', 'mysql', 'postgres', etc.
|
|
36
|
+
**kwargs: Other parameters
|
|
37
|
+
"""
|
|
38
|
+
# Extract valid SQL from input string using regex
|
|
39
|
+
cleanedSQL = self._extractSQL(sql)
|
|
40
|
+
if not cleanedSQL:
|
|
41
|
+
return "无法从输入中提取有效的SQL语句"
|
|
42
|
+
|
|
43
|
+
# Apply dialect-specific preprocessing
|
|
44
|
+
processedSQL = self._preprocessSQL(cleanedSQL, dialect)
|
|
45
|
+
|
|
46
|
+
if self.ontologyContext is None:
|
|
47
|
+
return "Ontology context is not initialized"
|
|
48
|
+
|
|
49
|
+
ontology = self.ontologyContext.getOntology()
|
|
50
|
+
if ontology is None:
|
|
51
|
+
return "Ontology is not available"
|
|
52
|
+
|
|
53
|
+
theDataSource = ontology.getDataSource(datasource)
|
|
54
|
+
if theDataSource is None:
|
|
55
|
+
return f"数据源或概念 {datasource} 不存在"
|
|
56
|
+
|
|
57
|
+
try:
|
|
58
|
+
result = theDataSource.executeQuery(processedSQL)
|
|
59
|
+
return str(result)
|
|
60
|
+
except Exception as e:
|
|
61
|
+
# Enhanced error handling with dialect-specific suggestions
|
|
62
|
+
return self._handleSQLError(e, processedSQL, dialect)
|
|
63
|
+
|
|
64
|
+
def _extractSQL(self, inputStr: str) -> str:
|
|
65
|
+
if not inputStr:
|
|
66
|
+
return ""
|
|
67
|
+
|
|
68
|
+
content = inputStr.strip()
|
|
69
|
+
|
|
70
|
+
if content.startswith("```") and content.endswith("```"):
|
|
71
|
+
content = content[3:-3].strip()
|
|
72
|
+
if content.lower().startswith("sql"):
|
|
73
|
+
content = content[3:].strip()
|
|
74
|
+
|
|
75
|
+
while True:
|
|
76
|
+
if (
|
|
77
|
+
(content.startswith('"') and content.endswith('"'))
|
|
78
|
+
or (content.startswith("'") and content.endswith("'"))
|
|
79
|
+
or (content.startswith("`") and content.endswith("`"))
|
|
80
|
+
):
|
|
81
|
+
content = content[1:-1].strip()
|
|
82
|
+
else:
|
|
83
|
+
break
|
|
84
|
+
|
|
85
|
+
lines = content.split("\n")
|
|
86
|
+
sql_lines = []
|
|
87
|
+
in_sql_block = False
|
|
88
|
+
sql_keywords = [
|
|
89
|
+
"SELECT",
|
|
90
|
+
"INSERT",
|
|
91
|
+
"UPDATE",
|
|
92
|
+
"DELETE",
|
|
93
|
+
"CREATE",
|
|
94
|
+
"DROP",
|
|
95
|
+
"ALTER",
|
|
96
|
+
"WITH",
|
|
97
|
+
]
|
|
98
|
+
|
|
99
|
+
for line in lines:
|
|
100
|
+
stripped_line = line.strip()
|
|
101
|
+
|
|
102
|
+
if not in_sql_block:
|
|
103
|
+
for keyword in sql_keywords:
|
|
104
|
+
if stripped_line.upper().startswith(keyword):
|
|
105
|
+
in_sql_block = True
|
|
106
|
+
break
|
|
107
|
+
|
|
108
|
+
if in_sql_block:
|
|
109
|
+
if not stripped_line:
|
|
110
|
+
break
|
|
111
|
+
sql_lines.append(line)
|
|
112
|
+
if stripped_line.endswith(";"):
|
|
113
|
+
break
|
|
114
|
+
|
|
115
|
+
if sql_lines:
|
|
116
|
+
return "\n".join(sql_lines).strip()
|
|
117
|
+
|
|
118
|
+
return content.strip()
|
|
119
|
+
|
|
120
|
+
def _extractSQLFromMixedContent(self, text: str) -> str:
|
|
121
|
+
return self._extractSQL(text) # Just reuse the main logic
|
|
122
|
+
|
|
123
|
+
def _normalizeWhitespace(self, sql: str) -> str:
|
|
124
|
+
return sql.strip()
|
|
125
|
+
|
|
126
|
+
def _validateSQLCompleteness(self, sql: str) -> bool:
|
|
127
|
+
if not sql:
|
|
128
|
+
return False
|
|
129
|
+
|
|
130
|
+
text = sql
|
|
131
|
+
|
|
132
|
+
# Remove string literals and comments to avoid interfering with structural validation
|
|
133
|
+
def _strip_literals_and_comments(s: str) -> str:
|
|
134
|
+
# Remove -- line comments
|
|
135
|
+
s = re.sub(r"--.*?$", "", s, flags=re.MULTILINE)
|
|
136
|
+
# Remove /* ... */ block comments
|
|
137
|
+
s = re.sub(r"/\*.*?\*/", "", s, flags=re.DOTALL)
|
|
138
|
+
# Remove the content within single and double quotation marks (preserve structure outside quotes)
|
|
139
|
+
s = re.sub(r"'(?:''|[^'])*'", "''", s)
|
|
140
|
+
s = re.sub(r'"(?:""|[^"])*"', '""', s)
|
|
141
|
+
return s
|
|
142
|
+
|
|
143
|
+
cleaned = _strip_literals_and_comments(text)
|
|
144
|
+
|
|
145
|
+
# Bracket Matching
|
|
146
|
+
open_parens = cleaned.count("(")
|
|
147
|
+
close_parens = cleaned.count(")")
|
|
148
|
+
if open_parens != close_parens:
|
|
149
|
+
return False
|
|
150
|
+
|
|
151
|
+
sql_upper = cleaned.upper().strip()
|
|
152
|
+
|
|
153
|
+
# Basic Structure Judgment
|
|
154
|
+
if sql_upper.startswith("SELECT"):
|
|
155
|
+
# Allow FROM across newlines/indentation by using word-boundary match
|
|
156
|
+
if not re.search(r"\bFROM\b", sql_upper):
|
|
157
|
+
return False
|
|
158
|
+
# Avoid false positives like identifiers ending with these keywords (e.g., SORT_ORDER)
|
|
159
|
+
if re.search(
|
|
160
|
+
r"\b(AND|OR|WHERE|FROM|JOIN|ON|GROUP|ORDER|HAVING|UNION|INTERSECT|EXCEPT)\s*$",
|
|
161
|
+
sql_upper,
|
|
162
|
+
):
|
|
163
|
+
return False
|
|
164
|
+
elif sql_upper.startswith("WITH"):
|
|
165
|
+
if "SELECT" not in sql_upper:
|
|
166
|
+
return False
|
|
167
|
+
|
|
168
|
+
if text.strip().endswith(","):
|
|
169
|
+
return False
|
|
170
|
+
|
|
171
|
+
# CASE/END Pairing (matched by word boundaries to avoid miscounting)
|
|
172
|
+
case_count = len(re.findall(r"\bCASE\b", cleaned, flags=re.IGNORECASE))
|
|
173
|
+
end_count = len(re.findall(r"\bEND\b", cleaned, flags=re.IGNORECASE))
|
|
174
|
+
if case_count != end_count:
|
|
175
|
+
return False
|
|
176
|
+
|
|
177
|
+
return True
|
|
178
|
+
|
|
179
|
+
def _extractSQLConservatively(self, text: str) -> str:
|
|
180
|
+
return self._extractSQL(text)
|
|
181
|
+
|
|
182
|
+
def _preprocessSQL(self, sql: str, dialect: str) -> str:
|
|
183
|
+
if dialect.lower() == "oracle":
|
|
184
|
+
return self._preprocessOracleSQL(sql)
|
|
185
|
+
elif dialect.lower() == "mysql":
|
|
186
|
+
return self._preprocessMySQLSQL(sql)
|
|
187
|
+
else:
|
|
188
|
+
return sql
|
|
189
|
+
|
|
190
|
+
def _preprocessOracleSQL(self, sql: str) -> str:
|
|
191
|
+
# Simplified replacements to avoid complex regex issues
|
|
192
|
+
# 1. Fix DATE() function -> TO_DATE(), but avoid replacing TO_DATE()
|
|
193
|
+
sql = re.sub(
|
|
194
|
+
r"(?<!TO_)DATE\s*\(\s*'(.*?)'\s*\)",
|
|
195
|
+
r"TO_DATE('\1', 'YYYY-MM-DD')",
|
|
196
|
+
sql,
|
|
197
|
+
flags=re.IGNORECASE,
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
# 2. Convert LIMIT -> FETCH FIRST
|
|
201
|
+
limit_match = re.search(r"LIMIT\s+(\d+)\s*$", sql, re.IGNORECASE)
|
|
202
|
+
if limit_match:
|
|
203
|
+
num = limit_match.group(1)
|
|
204
|
+
sql = re.sub(
|
|
205
|
+
r"LIMIT\s+\d+\s*$",
|
|
206
|
+
f"FETCH FIRST {num} ROWS ONLY",
|
|
207
|
+
sql,
|
|
208
|
+
flags=re.IGNORECASE,
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
# 3. Handle date strings -> TO_DATE(), but DO NOT alter ANSI DATE literals
|
|
212
|
+
# i.e., keep: DATE 'YYYY-MM-DD' as-is, and avoid nesting when already inside TO_DATE(
|
|
213
|
+
def _replace_plain_date_literals(text: str) -> str:
|
|
214
|
+
pattern = re.compile(r"'(\d{4}-\d{2}-\d{2})'")
|
|
215
|
+
|
|
216
|
+
def repl(m: re.Match) -> str:
|
|
217
|
+
start = m.start()
|
|
218
|
+
before = text[:start]
|
|
219
|
+
# Ignore if immediately preceded by DATE (ANSI literal)
|
|
220
|
+
if re.search(r"(?i)DATE\s*$", before.rstrip()[-10:]):
|
|
221
|
+
return m.group(0)
|
|
222
|
+
# Ignore if inside a TO_DATE( ... ) argument beginning
|
|
223
|
+
if re.search(r"(?i)TO_DATE\s*\($", before.rstrip()[-15:]):
|
|
224
|
+
return m.group(0)
|
|
225
|
+
value = m.group(1)
|
|
226
|
+
return f"TO_DATE('{value}', 'YYYY-MM-DD')"
|
|
227
|
+
|
|
228
|
+
return pattern.sub(repl, text)
|
|
229
|
+
|
|
230
|
+
sql = _replace_plain_date_literals(sql)
|
|
231
|
+
|
|
232
|
+
# 4. Ensure string literals use single quotes (preserve '=')
|
|
233
|
+
sql = re.sub(r'="([^"]*)"', r"='\1'", sql)
|
|
234
|
+
|
|
235
|
+
return sql
|
|
236
|
+
|
|
237
|
+
def _preprocessMySQLSQL(self, sql: str) -> str:
|
|
238
|
+
return sql
|
|
239
|
+
|
|
240
|
+
def _handleSQLError(self, error: Exception, sql: str, dialect: str) -> str:
|
|
241
|
+
error_str = str(error).upper()
|
|
242
|
+
if dialect.lower() == "oracle":
|
|
243
|
+
return self._handleOracleError(error_str, sql)
|
|
244
|
+
else:
|
|
245
|
+
return f"执行SQL语句failed: {error}"
|
|
246
|
+
|
|
247
|
+
def _handleOracleError(self, error_str: str, sql: str) -> str:
|
|
248
|
+
suggestions = []
|
|
249
|
+
if "ORA-00904" in error_str:
|
|
250
|
+
suggestions.append("字段不存在或无效。请检查字段名拼写和表别名。")
|
|
251
|
+
elif "ORA-00942" in error_str:
|
|
252
|
+
suggestions.append("表或视图不存在。请检查table name和用户权限。")
|
|
253
|
+
elif "ORA-01843" in error_str:
|
|
254
|
+
suggestions.append(
|
|
255
|
+
"日期格式无效。请使用 TO_DATE('YYYY-MM-DD', 'YYYY-MM-DD') 格式。"
|
|
256
|
+
)
|
|
257
|
+
elif "ORA-00933" in error_str:
|
|
258
|
+
suggestions.append("SQL命令未正确结束。请检查语法和括号匹配。")
|
|
259
|
+
elif "ORA-00936" in error_str:
|
|
260
|
+
suggestions.append("缺少表达式。请检查SELECT子句是否完整。")
|
|
261
|
+
|
|
262
|
+
if suggestions:
|
|
263
|
+
suggestion_text = "\n".join([f" - {s}" for s in suggestions])
|
|
264
|
+
return f"执行SQL语句failed: {error_str}\n\n建议修复方案:\n{suggestion_text}"
|
|
265
|
+
else:
|
|
266
|
+
return f"执行SQL语句failed: {error_str}"
|
|
267
|
+
|
|
268
|
+
def _createSkills(self) -> List[SkillFunction]:
|
|
269
|
+
return [
|
|
270
|
+
SkillFunction(self.executeSQL, block_as_parameter=("sql", "sql")),
|
|
271
|
+
]
|
|
272
|
+
|
|
273
|
+
def getTools(self) -> List[SkillFunction]:
|
|
274
|
+
return self.getSkills() # Uses base class getSkills() with caching
|