data-ops-cli 0.4.5__tar.gz → 0.5.1__tar.gz
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.
- {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/PKG-INFO +1 -1
- {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/data_ops_cli.egg-info/PKG-INFO +1 -1
- {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/data_ops_cli.egg-info/SOURCES.txt +1 -0
- {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/dataops_cli/__init__.py +1 -1
- {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/dataops_cli/cli/factory.py +2 -1
- data_ops_cli-0.5.1/src/dataops_cli/cli/kb.py +248 -0
- {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/dataops_cli/cli/ontology.py +94 -0
- {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/dataops_cli/cli/skill.py +69 -22
- {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/dataops_cli/cli/workflow.py +10 -6
- {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/README.md +0 -0
- {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/pyproject.toml +0 -0
- {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/setup.cfg +0 -0
- {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/data_ops_cli.egg-info/dependency_links.txt +0 -0
- {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/data_ops_cli.egg-info/entry_points.txt +0 -0
- {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/data_ops_cli.egg-info/requires.txt +0 -0
- {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/data_ops_cli.egg-info/top_level.txt +0 -0
- {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/dataops_cli/_compat.py +0 -0
- {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/dataops_cli/cli/__init__.py +0 -0
- {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/dataops_cli/cli/alarm.py +0 -0
- {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/dataops_cli/cli/api.py +0 -0
- {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/dataops_cli/cli/auth.py +0 -0
- {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/dataops_cli/cli/data_table.py +0 -0
- {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/dataops_cli/cli/dataset.py +0 -0
- {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/dataops_cli/cli/datasource.py +0 -0
- {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/dataops_cli/cli/dict_.py +0 -0
- {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/dataops_cli/cli/hazard.py +0 -0
- {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/dataops_cli/cli/metrics.py +0 -0
- {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/dataops_cli/cli/ref.py +0 -0
- {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/dataops_cli/cli/report.py +0 -0
- {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/dataops_cli/cli/table.py +0 -0
- {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/dataops_cli/cli/tag.py +0 -0
- {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/dataops_cli/cli/utils.py +0 -0
- {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/dataops_cli/cli/widget.py +0 -0
- {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/dataops_cli/exceptions.py +0 -0
- {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/dataops_cli/runner.py +0 -0
- {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/dataops_cli/schems.py +0 -0
- {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/dataops_cli/sdk.py +0 -0
- {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/dataops_cli/session.py +0 -0
- {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/dataops_cli/utils.py +0 -0
- {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/tests/test_table_relation.py +0 -0
|
@@ -53,6 +53,7 @@ def create_app():
|
|
|
53
53
|
from dataops_cli.cli.datasource import datasource_typer
|
|
54
54
|
from dataops_cli.cli.dict_ import dict_typer
|
|
55
55
|
from dataops_cli.cli.hazard import hazard_typer
|
|
56
|
+
from dataops_cli.cli.kb import kb_typer
|
|
56
57
|
from dataops_cli.cli.metrics import metrics_typer
|
|
57
58
|
from dataops_cli.cli.ontology import ontology_typer
|
|
58
59
|
from dataops_cli.cli.ref import ref_typer
|
|
@@ -84,7 +85,7 @@ def create_app():
|
|
|
84
85
|
app.add_typer(widget_typer)
|
|
85
86
|
app.add_typer(dict_typer)
|
|
86
87
|
app.add_typer(hazard_typer)
|
|
87
|
-
|
|
88
|
+
app.add_typer(kb_typer)
|
|
88
89
|
app.add_typer(tag_typer)
|
|
89
90
|
|
|
90
91
|
app.registered_callback = TyperInfo(callback=main)
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
"""Knowledge Base(知识库)管理命令组.
|
|
2
|
+
|
|
3
|
+
- 服务层函数 (service_*):纯 Python 接口,返回原始数据
|
|
4
|
+
- CLI 命令:基于服务层的薄包装,负责格式化输出
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Annotated
|
|
11
|
+
|
|
12
|
+
import typer
|
|
13
|
+
from dataops_cli.cli.utils import print_list
|
|
14
|
+
from dataops_sdk.exceptions import DataOpsError
|
|
15
|
+
from dataops_sdk.schemas.wiki import KbType
|
|
16
|
+
from dataops_sdk.services.wiki import (
|
|
17
|
+
service_get_knowledge_base,
|
|
18
|
+
service_list_knowledge_bases,
|
|
19
|
+
service_list_knowledge_documents,
|
|
20
|
+
service_search_knowledge,
|
|
21
|
+
service_search_wiki_pages,
|
|
22
|
+
service_upload_knowledge_file,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
kb_typer = typer.Typer(
|
|
26
|
+
name="kb",
|
|
27
|
+
help="知识库管理",
|
|
28
|
+
no_args_is_help=True,
|
|
29
|
+
)
|
|
30
|
+
doc_typer = typer.Typer(name="doc", help="知识库文档", no_args_is_help=True)
|
|
31
|
+
page_typer = typer.Typer(name="page", help="Wiki 页面", no_args_is_help=True)
|
|
32
|
+
kb_typer.add_typer(doc_typer)
|
|
33
|
+
kb_typer.add_typer(page_typer)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@kb_typer.command("list")
|
|
37
|
+
def kb_list_cmd(
|
|
38
|
+
kb_type: Annotated[
|
|
39
|
+
KbType | None,
|
|
40
|
+
typer.Option("--type", "-t", help="知识库类型:document / faq / wiki"),
|
|
41
|
+
] = None,
|
|
42
|
+
page: Annotated[int, typer.Option("--page", help="页码", min=1)] = 1,
|
|
43
|
+
page_size: Annotated[int, typer.Option("--page-size", help="每页条数", min=1)] = 20,
|
|
44
|
+
) -> None:
|
|
45
|
+
"""列出知识库。
|
|
46
|
+
|
|
47
|
+
Examples:
|
|
48
|
+
>>> data-ops-cli kb list
|
|
49
|
+
>>> data-ops-cli kb list --type wiki
|
|
50
|
+
"""
|
|
51
|
+
try:
|
|
52
|
+
result = service_list_knowledge_bases(page=page, page_size=page_size, kb_type=kb_type)
|
|
53
|
+
except DataOpsError as e:
|
|
54
|
+
typer.echo(f"错误: {e.message}")
|
|
55
|
+
raise typer.Exit(code=1) from None
|
|
56
|
+
|
|
57
|
+
if not result.items:
|
|
58
|
+
print("未查询到知识库")
|
|
59
|
+
return
|
|
60
|
+
|
|
61
|
+
print_list(
|
|
62
|
+
("id", "name", "type", "knowledge_count"),
|
|
63
|
+
[
|
|
64
|
+
[
|
|
65
|
+
it.id,
|
|
66
|
+
it.name,
|
|
67
|
+
it.type or "",
|
|
68
|
+
it.knowledge_count if it.knowledge_count is not None else "",
|
|
69
|
+
]
|
|
70
|
+
for it in result.items
|
|
71
|
+
],
|
|
72
|
+
)
|
|
73
|
+
print(f"total={result.total} page={result.page} page_size={result.page_size}")
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
@kb_typer.command("detail", no_args_is_help=True)
|
|
77
|
+
def kb_detail_cmd(
|
|
78
|
+
kb_id: Annotated[str, typer.Argument(help="知识库 ID")],
|
|
79
|
+
) -> None:
|
|
80
|
+
"""查看知识库详情。
|
|
81
|
+
|
|
82
|
+
Examples:
|
|
83
|
+
>>> data-ops-cli kb detail <kb_id>
|
|
84
|
+
"""
|
|
85
|
+
try:
|
|
86
|
+
kb = service_get_knowledge_base(kb_id)
|
|
87
|
+
except DataOpsError as e:
|
|
88
|
+
typer.echo(f"错误: {e.message}")
|
|
89
|
+
raise typer.Exit(code=1) from None
|
|
90
|
+
|
|
91
|
+
print_list(
|
|
92
|
+
("field", "value"),
|
|
93
|
+
[
|
|
94
|
+
["id", kb.id],
|
|
95
|
+
["name", kb.name],
|
|
96
|
+
["type", kb.type or ""],
|
|
97
|
+
["description", kb.description or ""],
|
|
98
|
+
["knowledge_count", kb.knowledge_count if kb.knowledge_count is not None else ""],
|
|
99
|
+
["chunk_count", kb.chunk_count if kb.chunk_count is not None else ""],
|
|
100
|
+
],
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
@doc_typer.command("list", no_args_is_help=True)
|
|
105
|
+
def doc_list_cmd(
|
|
106
|
+
kb_id: Annotated[str, typer.Argument(help="知识库 ID")],
|
|
107
|
+
keyword: Annotated[
|
|
108
|
+
str | None,
|
|
109
|
+
typer.Option("--keyword", "-k", help="关键词"),
|
|
110
|
+
] = None,
|
|
111
|
+
page: Annotated[int, typer.Option("--page", help="页码", min=1)] = 1,
|
|
112
|
+
page_size: Annotated[int, typer.Option("--page-size", help="每页条数", min=1)] = 20,
|
|
113
|
+
) -> None:
|
|
114
|
+
"""列出知识库文档。
|
|
115
|
+
|
|
116
|
+
Examples:
|
|
117
|
+
>>> data-ops-cli kb doc list <kb_id>
|
|
118
|
+
>>> data-ops-cli kb doc list <kb_id> --keyword "故障处理"
|
|
119
|
+
"""
|
|
120
|
+
try:
|
|
121
|
+
result = service_list_knowledge_documents(
|
|
122
|
+
kb_id, page=page, page_size=page_size, keyword=keyword
|
|
123
|
+
)
|
|
124
|
+
except DataOpsError as e:
|
|
125
|
+
typer.echo(f"错误: {e.message}")
|
|
126
|
+
raise typer.Exit(code=1) from None
|
|
127
|
+
|
|
128
|
+
if not result.items:
|
|
129
|
+
print("未查询到文档")
|
|
130
|
+
return
|
|
131
|
+
|
|
132
|
+
print_list(
|
|
133
|
+
("id", "title", "file_name", "parse_status"),
|
|
134
|
+
[
|
|
135
|
+
[
|
|
136
|
+
it.id,
|
|
137
|
+
it.title or "",
|
|
138
|
+
it.file_name or "",
|
|
139
|
+
it.parse_status or "",
|
|
140
|
+
]
|
|
141
|
+
for it in result.items
|
|
142
|
+
],
|
|
143
|
+
)
|
|
144
|
+
print(f"total={result.total} page={result.page} page_size={result.page_size}")
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
@doc_typer.command("upload", no_args_is_help=True)
|
|
148
|
+
def doc_upload_cmd(
|
|
149
|
+
kb_id: Annotated[str, typer.Argument(help="知识目录 CUID")],
|
|
150
|
+
file: Annotated[Path, typer.Argument(help="本地文件路径", exists=True, readable=True)],
|
|
151
|
+
name: Annotated[
|
|
152
|
+
str | None,
|
|
153
|
+
typer.Option("--name", help="覆盖上传文件名"),
|
|
154
|
+
] = None,
|
|
155
|
+
security_level: Annotated[
|
|
156
|
+
str,
|
|
157
|
+
typer.Option(
|
|
158
|
+
"--security-level",
|
|
159
|
+
help="文档密级:PUBLIC / INTERNAL / CONFIDENTIAL / SECRET",
|
|
160
|
+
),
|
|
161
|
+
] = "INTERNAL",
|
|
162
|
+
) -> None:
|
|
163
|
+
"""上传文件到知识库。
|
|
164
|
+
|
|
165
|
+
Examples:
|
|
166
|
+
>>> data-ops-cli kb doc upload <kb_id> ./manual.pdf
|
|
167
|
+
"""
|
|
168
|
+
try:
|
|
169
|
+
doc = service_upload_knowledge_file(
|
|
170
|
+
kb_id, file, file_name=name, security_level=security_level
|
|
171
|
+
)
|
|
172
|
+
except (DataOpsError, FileNotFoundError) as e:
|
|
173
|
+
msg = getattr(e, "message", None) or str(e)
|
|
174
|
+
typer.echo(f"错误: {msg}")
|
|
175
|
+
raise typer.Exit(code=1) from None
|
|
176
|
+
|
|
177
|
+
print(f"上传成功: id={doc.id} title={doc.title or ''} file_name={doc.file_name or ''}")
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
@kb_typer.command("search", no_args_is_help=True)
|
|
181
|
+
def search_cmd(
|
|
182
|
+
keyword: Annotated[str, typer.Argument(help="检索关键词")],
|
|
183
|
+
limit: Annotated[int, typer.Option("--limit", help="返回条数", min=1)] = 20,
|
|
184
|
+
offset: Annotated[int, typer.Option("--offset", help="偏移量", min=0)] = 0,
|
|
185
|
+
) -> None:
|
|
186
|
+
"""全局关键词检索知识内容。
|
|
187
|
+
|
|
188
|
+
Examples:
|
|
189
|
+
>>> data-ops-cli kb search "网络割接"
|
|
190
|
+
"""
|
|
191
|
+
try:
|
|
192
|
+
result = service_search_knowledge(keyword, limit=limit, offset=offset)
|
|
193
|
+
except DataOpsError as e:
|
|
194
|
+
typer.echo(f"错误: {e.message}")
|
|
195
|
+
raise typer.Exit(code=1) from None
|
|
196
|
+
|
|
197
|
+
if not result.items:
|
|
198
|
+
print("未检索到内容")
|
|
199
|
+
return
|
|
200
|
+
|
|
201
|
+
print_list(
|
|
202
|
+
("title", "kb_name", "score", "content"),
|
|
203
|
+
[
|
|
204
|
+
[
|
|
205
|
+
it.title or it.knowledge_title or "",
|
|
206
|
+
it.kb_name or "",
|
|
207
|
+
f"{it.score:.4f}" if it.score is not None else "",
|
|
208
|
+
(it.content or "")[:80],
|
|
209
|
+
]
|
|
210
|
+
for it in result.items
|
|
211
|
+
],
|
|
212
|
+
)
|
|
213
|
+
print(f"total={result.total}")
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
@page_typer.command("search", no_args_is_help=True)
|
|
217
|
+
def page_search_cmd(
|
|
218
|
+
kb_id: Annotated[str, typer.Argument(help="Wiki 资源 CUID(非目录 CUID)")],
|
|
219
|
+
query: Annotated[str, typer.Argument(help="检索词")],
|
|
220
|
+
limit: Annotated[int, typer.Option("--limit", help="返回条数", min=1)] = 20,
|
|
221
|
+
) -> None:
|
|
222
|
+
"""检索知识库 Wiki 页面。
|
|
223
|
+
|
|
224
|
+
Examples:
|
|
225
|
+
>>> data-ops-cli kb page search <kb_id> "运维手册"
|
|
226
|
+
"""
|
|
227
|
+
try:
|
|
228
|
+
pages = service_search_wiki_pages(kb_id, query, limit=limit)
|
|
229
|
+
except DataOpsError as e:
|
|
230
|
+
typer.echo(f"错误: {e.message}")
|
|
231
|
+
raise typer.Exit(code=1) from None
|
|
232
|
+
|
|
233
|
+
if not pages:
|
|
234
|
+
print("未检索到 Wiki 页面")
|
|
235
|
+
return
|
|
236
|
+
|
|
237
|
+
print_list(
|
|
238
|
+
("slug", "title", "page_type", "summary"),
|
|
239
|
+
[
|
|
240
|
+
[
|
|
241
|
+
it.slug,
|
|
242
|
+
it.title or "",
|
|
243
|
+
it.page_type or "",
|
|
244
|
+
(it.summary or "")[:60],
|
|
245
|
+
]
|
|
246
|
+
for it in pages
|
|
247
|
+
],
|
|
248
|
+
)
|
|
@@ -11,22 +11,30 @@ from typing import Annotated, Any
|
|
|
11
11
|
|
|
12
12
|
import typer
|
|
13
13
|
from dataops_sdk.exceptions import DataOpsError
|
|
14
|
+
from dataops_sdk.schemas.ontology import BUILTIN_SKILL_KINDS, OntologyBuiltinSkillKind
|
|
14
15
|
from dataops_sdk.services.ontology import (
|
|
16
|
+
get_scenario_skill_code,
|
|
15
17
|
service_get_ontology_by_code,
|
|
16
18
|
service_get_ontology_by_name,
|
|
17
19
|
service_list_ontologies,
|
|
20
|
+
service_list_ontology_apis,
|
|
21
|
+
service_list_ontology_metrics,
|
|
22
|
+
service_reset_ontology_builtin_skill,
|
|
18
23
|
service_tree_ontology,
|
|
19
24
|
service_update_ontology_basic,
|
|
20
25
|
service_update_ontology_constraints,
|
|
21
26
|
service_update_ontology_notes,
|
|
22
27
|
service_update_ontology_relations,
|
|
28
|
+
service_update_ontology_scenarios,
|
|
23
29
|
)
|
|
24
30
|
|
|
25
31
|
from dataops_cli.cli.utils import print_group_tree, print_list
|
|
26
32
|
|
|
27
33
|
ontology_typer = typer.Typer(name="ontology", help="本体管理", no_args_is_help=True)
|
|
28
34
|
update_typer = typer.Typer(name="update", help="更新本体配置", no_args_is_help=True)
|
|
35
|
+
builtin_skill_typer = typer.Typer(name="builtin-skill", help="本体内置技能", no_args_is_help=True)
|
|
29
36
|
ontology_typer.add_typer(update_typer)
|
|
37
|
+
ontology_typer.add_typer(builtin_skill_typer)
|
|
30
38
|
|
|
31
39
|
|
|
32
40
|
def _read_json_payload(
|
|
@@ -133,9 +141,18 @@ def detail_ontology(
|
|
|
133
141
|
|
|
134
142
|
data = meta.data
|
|
135
143
|
print(f"本体: {meta.name} ({meta.title})")
|
|
144
|
+
print(f"编码: {meta.code}")
|
|
136
145
|
print(f"备注: {meta.remark or '-'}")
|
|
137
146
|
print(f"多维表格: {data.data_table or '-'}")
|
|
147
|
+
if data.skills:
|
|
148
|
+
print("内置技能 name 映射:")
|
|
149
|
+
for kind in BUILTIN_SKILL_KINDS:
|
|
150
|
+
if kind in data.skills:
|
|
151
|
+
print(f" {kind}: {data.skills[kind]}")
|
|
138
152
|
print(f"场景数: {len(data.scenarios)}")
|
|
153
|
+
for scenario in data.scenarios:
|
|
154
|
+
skill_code = get_scenario_skill_code(scenario)
|
|
155
|
+
print(f" - {scenario.id}: {scenario.name or '-'} → skill {skill_code or '-'}")
|
|
139
156
|
print(f"约束数: {len(data.constraints)}")
|
|
140
157
|
print(f"关系数: {len(data.relations)}")
|
|
141
158
|
if data.notes:
|
|
@@ -289,3 +306,80 @@ def update_relations(
|
|
|
289
306
|
raise typer.Exit(code=1) from None
|
|
290
307
|
assert result.data is not None
|
|
291
308
|
typer.echo(f"本体 {result.name} 关联关系已更新(共 {len(result.data.relations)} 条)")
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
@update_typer.command("scenarios", no_args_is_help=True)
|
|
312
|
+
def update_scenarios(
|
|
313
|
+
name: Annotated[str, typer.Argument(help="本体编码或业务名")],
|
|
314
|
+
file: Annotated[
|
|
315
|
+
Path | None,
|
|
316
|
+
typer.Option("--file", "-f", help="场景 JSON 文件路径(数组)"),
|
|
317
|
+
] = None,
|
|
318
|
+
content: Annotated[
|
|
319
|
+
str | None,
|
|
320
|
+
typer.Option("--content", "-c", help="场景 JSON 内容(数组)"),
|
|
321
|
+
] = None,
|
|
322
|
+
) -> None:
|
|
323
|
+
"""全量替换本体场景列表(内置场景 id 不可删除)。"""
|
|
324
|
+
payload = _read_json_payload(file, content)
|
|
325
|
+
if not isinstance(payload, list):
|
|
326
|
+
typer.echo("错误: 场景内容必须是 JSON 数组", err=True)
|
|
327
|
+
raise typer.Exit(code=1)
|
|
328
|
+
try:
|
|
329
|
+
result = service_update_ontology_scenarios(name, payload)
|
|
330
|
+
except DataOpsError as e:
|
|
331
|
+
typer.echo(f"错误: {e.message}", err=True)
|
|
332
|
+
raise typer.Exit(code=1) from None
|
|
333
|
+
assert result.data is not None
|
|
334
|
+
typer.echo(f"本体 {result.name} 场景已更新(共 {len(result.data.scenarios)} 条)")
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
@ontology_typer.command("metrics", no_args_is_help=True)
|
|
338
|
+
def list_metrics(
|
|
339
|
+
code: Annotated[str, typer.Argument(help="本体编码")],
|
|
340
|
+
) -> None:
|
|
341
|
+
"""查询本体关联的指标列表。"""
|
|
342
|
+
try:
|
|
343
|
+
items = service_list_ontology_metrics(code)
|
|
344
|
+
except DataOpsError as e:
|
|
345
|
+
typer.echo(f"错误: {e.message}", err=True)
|
|
346
|
+
raise typer.Exit(code=1) from None
|
|
347
|
+
print(json.dumps(items, ensure_ascii=False, indent=2))
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
@ontology_typer.command("apis", no_args_is_help=True)
|
|
351
|
+
def list_apis(
|
|
352
|
+
code: Annotated[str, typer.Argument(help="本体编码")],
|
|
353
|
+
) -> None:
|
|
354
|
+
"""查询本体关联的接口列表。"""
|
|
355
|
+
try:
|
|
356
|
+
items = service_list_ontology_apis(code)
|
|
357
|
+
except DataOpsError as e:
|
|
358
|
+
typer.echo(f"错误: {e.message}", err=True)
|
|
359
|
+
raise typer.Exit(code=1) from None
|
|
360
|
+
print(json.dumps(items, ensure_ascii=False, indent=2))
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
@builtin_skill_typer.command("reset", no_args_is_help=True)
|
|
364
|
+
def reset_builtin_skill(
|
|
365
|
+
name_or_code: Annotated[str, typer.Argument(help="本体编码或业务名")],
|
|
366
|
+
kind: Annotated[
|
|
367
|
+
OntologyBuiltinSkillKind,
|
|
368
|
+
typer.Argument(help="内置技能类型: query/create/delete/update"),
|
|
369
|
+
],
|
|
370
|
+
) -> None:
|
|
371
|
+
"""重置本体内置技能并回填场景绑定。"""
|
|
372
|
+
try:
|
|
373
|
+
meta = service_get_ontology_by_name(name_or_code)
|
|
374
|
+
except DataOpsError:
|
|
375
|
+
try:
|
|
376
|
+
meta = service_get_ontology_by_code(name_or_code)
|
|
377
|
+
except DataOpsError as e:
|
|
378
|
+
typer.echo(f"错误: {e.message}", err=True)
|
|
379
|
+
raise typer.Exit(code=1) from None
|
|
380
|
+
try:
|
|
381
|
+
result = service_reset_ontology_builtin_skill(meta.code, kind)
|
|
382
|
+
except DataOpsError as e:
|
|
383
|
+
typer.echo(f"错误: {e.message}", err=True)
|
|
384
|
+
raise typer.Exit(code=1) from None
|
|
385
|
+
print(json.dumps(result, ensure_ascii=False, indent=2))
|
|
@@ -11,10 +11,8 @@ from pathlib import Path
|
|
|
11
11
|
from typing import Annotated
|
|
12
12
|
|
|
13
13
|
import typer
|
|
14
|
-
from dataops_cli.cli.utils import paginate_with_line_numbers, print_list
|
|
15
14
|
from dataops_sdk.exceptions import DataOpsError
|
|
16
15
|
from dataops_sdk.schemas import MetaSkillFileSubType, MetaSkillSubType, MetaType
|
|
17
|
-
from dataops_sdk.schemas.enums import GroupNodeRefType
|
|
18
16
|
from dataops_sdk.services import (
|
|
19
17
|
SkillPackValidationError,
|
|
20
18
|
service_delete_skill,
|
|
@@ -25,6 +23,9 @@ from dataops_sdk.services import (
|
|
|
25
23
|
service_skill_call_by_name,
|
|
26
24
|
service_skill_delete_sub_file,
|
|
27
25
|
service_skill_download,
|
|
26
|
+
service_skill_list_ontologies_by_codes,
|
|
27
|
+
service_skill_list_ontology_options,
|
|
28
|
+
service_skill_list_skill_codes_by_ontology,
|
|
28
29
|
service_skill_update,
|
|
29
30
|
service_skill_upload,
|
|
30
31
|
service_skill_upload_sub_file,
|
|
@@ -33,6 +34,8 @@ from dataops_sdk.tiptap import TableStyle, TipTapTool
|
|
|
33
34
|
from pydantic_core import from_json
|
|
34
35
|
from rich.console import Console
|
|
35
36
|
|
|
37
|
+
from dataops_cli.cli.utils import paginate_with_line_numbers, print_list
|
|
38
|
+
|
|
36
39
|
# ---------------------------------------------------------------------------
|
|
37
40
|
# CLI 命令
|
|
38
41
|
# ---------------------------------------------------------------------------
|
|
@@ -47,10 +50,70 @@ skill_file_typer = typer.Typer(
|
|
|
47
50
|
help="技能文件管理",
|
|
48
51
|
no_args_is_help=True,
|
|
49
52
|
)
|
|
53
|
+
skill_ontology_typer = typer.Typer(
|
|
54
|
+
name="ontology",
|
|
55
|
+
help="技能与本体归属",
|
|
56
|
+
no_args_is_help=True,
|
|
57
|
+
)
|
|
50
58
|
skill_typer.add_typer(skill_file_typer)
|
|
59
|
+
skill_typer.add_typer(skill_ontology_typer)
|
|
51
60
|
MAKRDOWN_TABLE_STYLE = TableStyle.MARKDOWN
|
|
52
61
|
|
|
53
62
|
|
|
63
|
+
@skill_ontology_typer.command("options")
|
|
64
|
+
def ontology_options_cmd() -> None:
|
|
65
|
+
"""列出已绑定技能的本体(用于技能中心筛选)。"""
|
|
66
|
+
try:
|
|
67
|
+
items = service_skill_list_ontology_options()
|
|
68
|
+
except DataOpsError as e:
|
|
69
|
+
typer.echo(f"错误: {e.message}")
|
|
70
|
+
raise typer.Exit(code=1) from None
|
|
71
|
+
if not items:
|
|
72
|
+
print("未查询到本体选项")
|
|
73
|
+
return
|
|
74
|
+
print_list(
|
|
75
|
+
("code", "title"),
|
|
76
|
+
[[it.code, it.title] for it in items],
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
@skill_ontology_typer.command("by-codes", no_args_is_help=True)
|
|
81
|
+
def ontology_by_codes_cmd(
|
|
82
|
+
codes: Annotated[
|
|
83
|
+
str,
|
|
84
|
+
typer.Option("--codes", "-c", help="技能 code 列表,逗号分隔"),
|
|
85
|
+
],
|
|
86
|
+
) -> None:
|
|
87
|
+
"""批量查询技能归属的本体。"""
|
|
88
|
+
code_list = [c.strip() for c in codes.split(",") if c.strip()]
|
|
89
|
+
if not code_list:
|
|
90
|
+
typer.echo("错误: --codes 不能为空")
|
|
91
|
+
raise typer.Exit(code=1)
|
|
92
|
+
try:
|
|
93
|
+
mapping = service_skill_list_ontologies_by_codes(code_list)
|
|
94
|
+
except DataOpsError as e:
|
|
95
|
+
typer.echo(f"错误: {e.message}")
|
|
96
|
+
raise typer.Exit(code=1) from None
|
|
97
|
+
payload = {key: [vo.model_dump() for vo in val] for key, val in mapping.items()}
|
|
98
|
+
print(json.dumps(payload, ensure_ascii=False, indent=2))
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@skill_ontology_typer.command("skill-codes", no_args_is_help=True)
|
|
102
|
+
def ontology_skill_codes_cmd(
|
|
103
|
+
ontology_code: Annotated[
|
|
104
|
+
str,
|
|
105
|
+
typer.Option("--ontology-code", help="本体编码"),
|
|
106
|
+
],
|
|
107
|
+
) -> None:
|
|
108
|
+
"""按本体查询绑定的技能 code 列表。"""
|
|
109
|
+
try:
|
|
110
|
+
codes = service_skill_list_skill_codes_by_ontology(ontology_code)
|
|
111
|
+
except DataOpsError as e:
|
|
112
|
+
typer.echo(f"错误: {e.message}")
|
|
113
|
+
raise typer.Exit(code=1) from None
|
|
114
|
+
print(json.dumps(codes, ensure_ascii=False, indent=2))
|
|
115
|
+
|
|
116
|
+
|
|
54
117
|
@skill_typer.command("list")
|
|
55
118
|
def list_cmd(
|
|
56
119
|
sub_types: Annotated[
|
|
@@ -125,41 +188,27 @@ def detail_cmd(
|
|
|
125
188
|
|
|
126
189
|
@skill_typer.command("upload", no_args_is_help=True)
|
|
127
190
|
def upload_cmd(
|
|
128
|
-
space_code: Annotated[
|
|
129
|
-
str, typer.Argument(help="空间编码 (通过 ``report sapce list`` 查看空间编码)")
|
|
130
|
-
],
|
|
131
191
|
file: Annotated[Path, typer.Argument(help="技能 ZIP 文件路径")],
|
|
132
192
|
dtype: Annotated[
|
|
133
193
|
MetaSkillSubType,
|
|
134
194
|
typer.Option("--type", help="技能子类型"),
|
|
135
195
|
],
|
|
136
|
-
ref_node_id: Annotated[
|
|
137
|
-
str | None, typer.Option("--ref-node-id", help="引用节点ID (通过 `report sapce tree` 查看)")
|
|
138
|
-
] = None,
|
|
139
|
-
ref_type: Annotated[
|
|
140
|
-
GroupNodeRefType,
|
|
141
|
-
typer.Option("--ref-type", help="引用节点类型 (放置在引用节点前、后还是内)"),
|
|
142
|
-
] = GroupNodeRefType.AFTER,
|
|
143
196
|
title: Annotated[str | None, typer.Option("--title", help="技能简体中文名称")] = None,
|
|
144
197
|
) -> None:
|
|
145
|
-
"""从 ZIP
|
|
198
|
+
"""从 ZIP 包导入技能到技能中心。
|
|
146
199
|
|
|
147
200
|
Examples:
|
|
148
|
-
|
|
149
|
-
>>> data-ops-cli skill import {space-code} my-skill.zip --type api --ref-node-id {node-id} --ref-type inner
|
|
201
|
+
>>> data-ops-cli skill upload my-skill.zip --type llm
|
|
150
202
|
"""
|
|
151
203
|
if not file.exists():
|
|
152
204
|
typer.echo(f"错误: 文件不存在 {file}")
|
|
153
205
|
raise typer.Exit(code=2)
|
|
154
206
|
|
|
155
207
|
try:
|
|
156
|
-
|
|
208
|
+
service_skill_upload(
|
|
157
209
|
file_path=str(file),
|
|
158
210
|
sub_type=dtype,
|
|
159
211
|
title=title,
|
|
160
|
-
group_name=space_code,
|
|
161
|
-
ref_node_id=ref_node_id,
|
|
162
|
-
ref_type=ref_type,
|
|
163
212
|
)
|
|
164
213
|
except DataOpsError as e:
|
|
165
214
|
typer.echo(f"错误: {e.message}")
|
|
@@ -218,9 +267,7 @@ def download_cmd(
|
|
|
218
267
|
output_path: str | None = None
|
|
219
268
|
if output is not None:
|
|
220
269
|
# Typer Path 会吞掉末尾分隔符;无 .zip 后缀时视为目录
|
|
221
|
-
output_path = (
|
|
222
|
-
str(output) if output.suffix.lower() == ".zip" else f"{output}/"
|
|
223
|
-
)
|
|
270
|
+
output_path = str(output) if output.suffix.lower() == ".zip" else f"{output}/"
|
|
224
271
|
|
|
225
272
|
try:
|
|
226
273
|
zip_path = service_skill_download(name=name, output_path=output_path)
|
|
@@ -160,6 +160,7 @@ def get_workflow_template(
|
|
|
160
160
|
@workflow_typer.command("run", no_args_is_help=True)
|
|
161
161
|
def run_workflow(
|
|
162
162
|
code: Annotated[str, typer.Argument(help="工作流编码")],
|
|
163
|
+
start_node_id: Annotated[str, typer.Argument(help="启动节点 ID")],
|
|
163
164
|
params: Annotated[
|
|
164
165
|
str | None,
|
|
165
166
|
typer.Option(help='启动参数 JSON 字符串,例如 \'{"key":"value"}\''),
|
|
@@ -172,14 +173,15 @@ def run_workflow(
|
|
|
172
173
|
|
|
173
174
|
Args:
|
|
174
175
|
code: 工作流编码。
|
|
176
|
+
start_node_id: 启动节点 ID(手动触发一般为 manually-trigger)。
|
|
175
177
|
params: 可选启动参数 JSON 字符串。
|
|
176
178
|
|
|
177
179
|
Examples:
|
|
178
180
|
无参启动
|
|
179
|
-
>>> data-ops-cli workflow run wf_code_001
|
|
181
|
+
>>> data-ops-cli workflow run wf_code_001 manually-trigger
|
|
180
182
|
|
|
181
183
|
带参数启动
|
|
182
|
-
>>> data-ops-cli workflow run wf_code_001 --params '{"date":"2026-07-16"}'
|
|
184
|
+
>>> data-ops-cli workflow run wf_code_001 manually-trigger --params '{"date":"2026-07-16"}'
|
|
183
185
|
"""
|
|
184
186
|
try:
|
|
185
187
|
params_dict = json.loads(params) if params else None
|
|
@@ -188,7 +190,7 @@ def run_workflow(
|
|
|
188
190
|
raise typer.Exit(code=1) from None
|
|
189
191
|
|
|
190
192
|
try:
|
|
191
|
-
result = service_start_workflow(code, params_dict)
|
|
193
|
+
result = service_start_workflow(code, start_node_id, params_dict)
|
|
192
194
|
except DataOpsError as e:
|
|
193
195
|
typer.echo(f"错误: {e.message}")
|
|
194
196
|
raise typer.Exit(code=1) from None
|
|
@@ -199,6 +201,7 @@ def run_workflow(
|
|
|
199
201
|
@workflow_typer.command("run-async", no_args_is_help=True)
|
|
200
202
|
def run_workflow_async(
|
|
201
203
|
code: Annotated[str, typer.Argument(help="工作流编码")],
|
|
204
|
+
start_node_id: Annotated[str, typer.Argument(help="启动节点 ID")],
|
|
202
205
|
params: Annotated[
|
|
203
206
|
str | None,
|
|
204
207
|
typer.Option(help='启动参数 JSON 字符串,例如 \'{"key":"value"}\''),
|
|
@@ -211,14 +214,15 @@ def run_workflow_async(
|
|
|
211
214
|
|
|
212
215
|
Args:
|
|
213
216
|
code: 工作流编码。
|
|
217
|
+
start_node_id: 启动节点 ID(手动触发一般为 manually-trigger)。
|
|
214
218
|
params: 可选启动参数 JSON 字符串。
|
|
215
219
|
|
|
216
220
|
Examples:
|
|
217
221
|
无参异步启动
|
|
218
|
-
>>> data-ops-cli workflow run-async wf_code_001
|
|
222
|
+
>>> data-ops-cli workflow run-async wf_code_001 manually-trigger
|
|
219
223
|
|
|
220
224
|
带参数异步启动
|
|
221
|
-
>>> data-ops-cli workflow run-async wf_code_001 --params '{"date":"2026-07-16"}'
|
|
225
|
+
>>> data-ops-cli workflow run-async wf_code_001 manually-trigger --params '{"date":"2026-07-16"}'
|
|
222
226
|
"""
|
|
223
227
|
try:
|
|
224
228
|
params_dict = json.loads(params) if params else None
|
|
@@ -227,7 +231,7 @@ def run_workflow_async(
|
|
|
227
231
|
raise typer.Exit(code=1) from None
|
|
228
232
|
|
|
229
233
|
try:
|
|
230
|
-
result = service_start_workflow_async(code, params_dict)
|
|
234
|
+
result = service_start_workflow_async(code, start_node_id, params_dict)
|
|
231
235
|
except DataOpsError as e:
|
|
232
236
|
typer.echo(f"错误: {e.message}")
|
|
233
237
|
raise typer.Exit(code=1) from None
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|