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.
Files changed (40) hide show
  1. {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/PKG-INFO +1 -1
  2. {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/data_ops_cli.egg-info/PKG-INFO +1 -1
  3. {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/data_ops_cli.egg-info/SOURCES.txt +1 -0
  4. {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/dataops_cli/__init__.py +1 -1
  5. {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/dataops_cli/cli/factory.py +2 -1
  6. data_ops_cli-0.5.1/src/dataops_cli/cli/kb.py +248 -0
  7. {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/dataops_cli/cli/ontology.py +94 -0
  8. {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/dataops_cli/cli/skill.py +69 -22
  9. {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/dataops_cli/cli/workflow.py +10 -6
  10. {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/README.md +0 -0
  11. {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/pyproject.toml +0 -0
  12. {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/setup.cfg +0 -0
  13. {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/data_ops_cli.egg-info/dependency_links.txt +0 -0
  14. {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/data_ops_cli.egg-info/entry_points.txt +0 -0
  15. {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/data_ops_cli.egg-info/requires.txt +0 -0
  16. {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/data_ops_cli.egg-info/top_level.txt +0 -0
  17. {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/dataops_cli/_compat.py +0 -0
  18. {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/dataops_cli/cli/__init__.py +0 -0
  19. {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/dataops_cli/cli/alarm.py +0 -0
  20. {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/dataops_cli/cli/api.py +0 -0
  21. {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/dataops_cli/cli/auth.py +0 -0
  22. {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/dataops_cli/cli/data_table.py +0 -0
  23. {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/dataops_cli/cli/dataset.py +0 -0
  24. {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/dataops_cli/cli/datasource.py +0 -0
  25. {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/dataops_cli/cli/dict_.py +0 -0
  26. {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/dataops_cli/cli/hazard.py +0 -0
  27. {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/dataops_cli/cli/metrics.py +0 -0
  28. {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/dataops_cli/cli/ref.py +0 -0
  29. {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/dataops_cli/cli/report.py +0 -0
  30. {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/dataops_cli/cli/table.py +0 -0
  31. {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/dataops_cli/cli/tag.py +0 -0
  32. {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/dataops_cli/cli/utils.py +0 -0
  33. {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/dataops_cli/cli/widget.py +0 -0
  34. {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/dataops_cli/exceptions.py +0 -0
  35. {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/dataops_cli/runner.py +0 -0
  36. {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/dataops_cli/schems.py +0 -0
  37. {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/dataops_cli/sdk.py +0 -0
  38. {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/dataops_cli/session.py +0 -0
  39. {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/src/dataops_cli/utils.py +0 -0
  40. {data_ops_cli-0.4.5 → data_ops_cli-0.5.1}/tests/test_table_relation.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: data-ops-cli
3
- Version: 0.4.5
3
+ Version: 0.5.1
4
4
  Summary: Command-line wrapper around dataops_sdk for the DataOps platform.
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: data-ops-cli
3
- Version: 0.4.5
3
+ Version: 0.5.1
4
4
  Summary: Command-line wrapper around dataops_sdk for the DataOps platform.
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -24,6 +24,7 @@ src/dataops_cli/cli/datasource.py
24
24
  src/dataops_cli/cli/dict_.py
25
25
  src/dataops_cli/cli/factory.py
26
26
  src/dataops_cli/cli/hazard.py
27
+ src/dataops_cli/cli/kb.py
27
28
  src/dataops_cli/cli/metrics.py
28
29
  src/dataops_cli/cli/ontology.py
29
30
  src/dataops_cli/cli/ref.py
@@ -14,4 +14,4 @@ groups command modules under :mod:`dataops_cli.cli` and re-exports the package
14
14
  removed in ``dataops_cli`` 1.0.
15
15
  """
16
16
 
17
- __version__ = "0.4.5"
17
+ __version__ = "0.5.1"
@@ -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
- # app.add_typer(kb_typer) # TODO: kb CLI 尚未实现
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
- 把技能上传到指定空间的执行Node里面
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
- result = service_skill_upload(
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