tinet-agent-cli 0.1.0.dev37__py3-none-any.whl → 0.1.1.dev40__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.
taco/__init__.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.1.0.dev37"
1
+ __version__ = "0.1.1.dev40"
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import json
3
4
  from typing import Any
4
5
 
5
6
  import httpx
@@ -67,6 +68,11 @@ def validate_response(operation: AgentOperation, payload: Any) -> dict[str, Any]
67
68
  exit_code=4,
68
69
  request_id=request_id,
69
70
  path=operation.http["path"],
71
+ raw_response=json.dumps(
72
+ payload,
73
+ ensure_ascii=False,
74
+ separators=(",", ":"),
75
+ ),
70
76
  )
71
77
  business = dict(payload)
72
78
  business.pop("requestId", None)
taco/aikb/executors.py CHANGED
@@ -1,7 +1,9 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import json
3
4
  import math
4
5
  import os
6
+ import re
5
7
  from typing import Any
6
8
  from urllib.parse import urlsplit
7
9
 
@@ -17,6 +19,13 @@ from taco.core.http_headers import with_cli_user_agent
17
19
 
18
20
 
19
21
  DEFAULT_UPLOAD_TIMEOUT_SECONDS = 600.0
22
+ RAW_HTML_CONTENT_IGNORED_MESSAGE = (
23
+ "服务端 rawHtmlContent 为非标准 JSON 内容,CLI 已忽略该属性。"
24
+ )
25
+ _RAW_HTML_CONTENT_PATTERN = re.compile(
26
+ r'"rawHtmlContent"\s*:\s*.*?(?=,\s*"escapedHtmlContent"\s*:)',
27
+ re.DOTALL,
28
+ )
20
29
 
21
30
 
22
31
  def execute_http(
@@ -26,13 +35,53 @@ def execute_http(
26
35
  query: dict[str, Any],
27
36
  body: dict[str, Any],
28
37
  ) -> dict[str, Any]:
38
+ raw_html_content_ignored = False
39
+
40
+ def repair_signature_response(raw: str) -> Any:
41
+ nonlocal raw_html_content_ignored
42
+ payload = _parse_signature_response_without_raw_html(raw)
43
+ raw_html_content_ignored = True
44
+ return payload
45
+
29
46
  payload = client.request_json(
30
47
  operation.http["method"],
31
48
  operation.http["path"],
32
49
  query=query or None,
33
50
  json_body=body or None,
51
+ preserve_invalid_response=operation.id == "signature-for-upload",
52
+ invalid_json_parser=(
53
+ repair_signature_response
54
+ if operation.id == "signature-for-upload"
55
+ else None
56
+ ),
57
+ )
58
+ result = validate_response(operation, payload)
59
+ if raw_html_content_ignored:
60
+ result["message"] = RAW_HTML_CONTENT_IGNORED_MESSAGE
61
+ return result
62
+
63
+
64
+ def _parse_signature_response_without_raw_html(raw: str) -> dict[str, Any]:
65
+ matches = tuple(_RAW_HTML_CONTENT_PATTERN.finditer(raw))
66
+ if len(matches) != 1:
67
+ raise ValueError("响应中不存在唯一的非标准 rawHtmlContent 属性")
68
+ repaired = _RAW_HTML_CONTENT_PATTERN.sub(
69
+ '"rawHtmlContent":null',
70
+ raw,
71
+ count=1,
34
72
  )
35
- return validate_response(operation, payload)
73
+ payload = json.loads(repaired)
74
+ if not isinstance(payload, dict):
75
+ raise ValueError("响应不是 JSON 对象")
76
+ result = payload.get("result")
77
+ if (
78
+ not isinstance(result, dict)
79
+ or "rawHtmlContent" not in result
80
+ or result["rawHtmlContent"] is not None
81
+ ):
82
+ raise ValueError("非标准 rawHtmlContent 不在 result 中")
83
+ result.pop("rawHtmlContent")
84
+ return payload
36
85
 
37
86
 
38
87
  def execute_presigned_put(
taco/assets/__init__.py CHANGED
@@ -1 +1 @@
1
- """Packaged schemas, scenarios, examples, and Agent skills."""
1
+ """Packaged schemas, scenarios, examples, and API catalogs."""
@@ -47,12 +47,12 @@
47
47
  "inputs": {"description": "节点输入;JSON 内容返回对象,否则可能返回原始值。"},
48
48
  "process_data": {"description": "节点处理过程数据。"},
49
49
  "outputs": {"description": "节点输出;JSON 内容返回对象,否则可能返回原始值。"},
50
- "status": {"type": "string", "description": "节点执行状态。"},
50
+ "status": {"type": ["string", "null"], "description": "节点执行状态。"},
51
51
  "error": {"type": ["string", "null"], "description": "节点执行错误信息。"},
52
52
  "elapsed_time": {"type": ["number", "null"], "description": "节点执行耗时,单位秒。"},
53
53
  "execution_metadata": {"type": ["object", "null"], "description": "节点执行元数据。", "additionalProperties": true},
54
54
  "extras": {"type": ["object", "null"], "description": "节点额外信息。", "additionalProperties": true},
55
- "created_at": {"type": "integer", "description": "节点开始执行的 Unix 秒级时间戳。"},
55
+ "created_at": {"type": ["integer", "null"], "description": "节点开始执行的 Unix 秒级时间戳。"},
56
56
  "finished_at": {"type": ["integer", "null"], "description": "节点完成执行的 Unix 秒级时间戳。"}
57
57
  },
58
58
  "additionalProperties": true
taco/assets/manifest.json CHANGED
@@ -1,8 +1,7 @@
1
1
  {
2
2
  "format_version": 1,
3
- "cli_version": "0.1.0.dev37",
3
+ "cli_version": "0.1.1.dev40",
4
4
  "resource_groups": [
5
- "skills",
6
5
  "schemas",
7
6
  "scenarios",
8
7
  "examples",
taco/cli.py CHANGED
@@ -13,7 +13,10 @@ from taco.core.errors import TacoError
13
13
  from taco.core.output import failure, to_json_line
14
14
 
15
15
 
16
- app = typer.Typer(help="Tinet Agent CLI Orchestrator")
16
+ app = typer.Typer(
17
+ help="Tinet Agent CLI Orchestrator",
18
+ add_completion=False,
19
+ )
17
20
  app.add_typer(agent_app, name="agent")
18
21
  app.add_typer(aikb_app, name="aikb")
19
22
  app.add_typer(profile_app, name="profile")
@@ -63,7 +66,7 @@ def root(
63
66
  def main() -> None:
64
67
  try:
65
68
  result = app(
66
- args=_without_ignored_compat_args(sys.argv[1:]),
69
+ args=_normalize_cli_args(sys.argv[1:]),
67
70
  standalone_mode=False,
68
71
  prog_name="taco",
69
72
  )
@@ -101,11 +104,15 @@ def main() -> None:
101
104
  )
102
105
 
103
106
 
104
- def _without_ignored_compat_args(args: list[str]) -> list[str]:
105
- filtered: list[str] = []
107
+ def _normalize_cli_args(args: list[str]) -> list[str]:
108
+ global_args: list[str] = []
109
+ command_args: list[str] = []
106
110
  index = 0
107
111
  while index < len(args):
108
112
  argument = args[index]
113
+ if argument == "--":
114
+ command_args.extend(args[index:])
115
+ break
109
116
  if argument == "--from-zenava":
110
117
  index += 1
111
118
  if index < len(args) and args[index].lower() in {"true", "false"}:
@@ -114,9 +121,25 @@ def _without_ignored_compat_args(args: list[str]) -> list[str]:
114
121
  if argument.startswith("--from-zenava="):
115
122
  index += 1
116
123
  continue
117
- filtered.append(argument)
124
+ if argument in {"--json", "--no-input"}:
125
+ global_args.append(argument)
126
+ index += 1
127
+ continue
128
+ if argument == "--profile":
129
+ index += 1
130
+ if index < len(args) and not args[index].startswith("-"):
131
+ global_args.extend((argument, args[index]))
132
+ index += 1
133
+ else:
134
+ command_args.append(argument)
135
+ continue
136
+ if argument.startswith("--profile="):
137
+ global_args.append(argument)
138
+ index += 1
139
+ continue
140
+ command_args.append(argument)
118
141
  index += 1
119
- return filtered
142
+ return [*global_args, *command_args]
120
143
 
121
144
 
122
145
  def _exit_main_with_error(error: TacoError) -> None:
taco/commands/agent.py CHANGED
@@ -5,7 +5,7 @@ import os
5
5
  import tempfile
6
6
  from json import JSONDecodeError
7
7
  from pathlib import Path
8
- from typing import Any
8
+ from typing import Any, Literal
9
9
  from uuid import UUID, uuid4
10
10
 
11
11
  import typer
@@ -14,12 +14,11 @@ from taco.commands.app import app_app
14
14
  from taco.commands.category import category_app
15
15
  from taco.commands.chatflow import chatflow_app
16
16
  from taco.commands.credential import credential_app
17
- from taco.commands.discovery import example_app, scenario_app, schema_app, skills_app
17
+ from taco.commands.discovery import example_app, scenario_app, schema_app
18
18
  from taco.commands.model import model_app
19
19
  from taco.commands.tool import tool_app
20
20
  from taco.commands.workflow import workflow_app
21
- from taco.agent_openapi.catalog import load_catalog, operation_not_found
22
- from taco.agent_openapi.models import thaw
21
+ from taco.agent_openapi.catalog import load_catalog
23
22
  from taco.agent_openapi.parameters import parse_parameters
24
23
  from taco.agent_openapi.service import AgentOpenApiService
25
24
  from taco.core.context import get_cli_context
@@ -35,11 +34,15 @@ from taco.services.tool_service import ToolService
35
34
  from taco.services.workflow_tool_service import WorkflowToolService
36
35
 
37
36
 
38
- agent_app = typer.Typer(help="Agent 智能体管理")
37
+ agent_app = typer.Typer(help="智能体管理")
39
38
  agent_tool_app = typer.Typer(help="本地 Agent DSL 工具挂载")
40
- agent_operation_app = typer.Typer(help="发现 Agent 数据 OpenAPI operation")
39
+ conversation_app = typer.Typer(help="智能体会话记录管理")
40
+ conversation_message_app = typer.Typer(help="会话消息管理")
41
+ conversation_trace_app = typer.Typer(help="消息追踪管理")
41
42
  agent_app.add_typer(agent_tool_app, name="tool")
42
- agent_app.add_typer(agent_operation_app, name="operation")
43
+ agent_app.add_typer(conversation_app, name="conversation")
44
+ conversation_app.add_typer(conversation_message_app, name="message")
45
+ conversation_app.add_typer(conversation_trace_app, name="trace")
43
46
  agent_app.add_typer(app_app, name="app")
44
47
  agent_app.add_typer(category_app, name="category")
45
48
  agent_app.add_typer(model_app, name="model")
@@ -47,112 +50,121 @@ agent_app.add_typer(workflow_app, name="workflow")
47
50
  agent_app.add_typer(chatflow_app, name="chatflow")
48
51
  agent_app.add_typer(example_app, name="example")
49
52
  agent_app.add_typer(scenario_app, name="scenario")
50
- agent_app.add_typer(skills_app, name="skills")
51
53
  agent_app.add_typer(schema_app, name="schema")
52
54
  agent_tool_app.add_typer(tool_app, name="catalog")
53
55
  agent_tool_app.add_typer(credential_app, name="credential")
54
56
 
55
57
 
56
- @agent_operation_app.command("list", help="列出 Agent 数据 OpenAPI operation。")
57
- def list_agent_operations(
58
+ @conversation_app.command("list", help="查询智能体会话记录。")
59
+ def list_conversations(
58
60
  ctx: typer.Context,
59
- group: str | None = typer.Option(None, "--group", help="按分组筛选。"),
61
+ app_id: str = typer.Option(..., "--app-id", help="智能体应用 ID。"),
62
+ created_at_start: int = typer.Option(
63
+ ...,
64
+ "--created-at-start",
65
+ min=1,
66
+ help="创建开始时间,Unix 秒级时间戳。",
67
+ ),
68
+ created_at_end: int = typer.Option(
69
+ ...,
70
+ "--created-at-end",
71
+ min=1,
72
+ help="创建结束时间,Unix 秒级时间戳,跨度不超过 90 天。",
73
+ ),
74
+ conversation_id: str | None = typer.Option(
75
+ None,
76
+ "--conversation-id",
77
+ help="按指定会话 ID 查询。",
78
+ ),
79
+ invoke_from: Literal["PROD", "DEBUG"] | None = typer.Option(
80
+ None,
81
+ "--invoke-from",
82
+ help="会话来源:PROD 或 DEBUG。",
83
+ ),
84
+ is_transfer_human: bool | None = typer.Option(
85
+ None,
86
+ "--is-transfer-human/--no-transfer-human",
87
+ help="筛选是否发生过转人工。",
88
+ ),
89
+ limit: int = typer.Option(10, "--limit", min=1, max=100, help="查询数量。"),
90
+ cursor: str | None = typer.Option(None, "--cursor", help="下一页游标。"),
60
91
  ) -> None:
61
- operations = [
62
- _agent_operation_summary(item)
63
- for item in load_catalog().operations
64
- if group is None or item.group == group
65
- ]
66
- emit_success(ctx, operations, json_default=True)
67
-
68
-
69
- @agent_operation_app.command("search", help="按关键词搜索 Agent 数据 OpenAPI operation。")
70
- def search_agent_operations(ctx: typer.Context, query: str = typer.Argument(...)) -> None:
71
- needle = query.casefold()
72
- matches = []
73
- for operation in load_catalog().operations:
74
- haystack = " ".join(
75
- [operation.id, operation.title, operation.description, *operation.keywords]
76
- ).casefold()
77
- if needle in haystack:
78
- matches.append(_agent_operation_summary(operation))
79
- emit_success(ctx, matches[:10], json_default=True)
92
+ _call_named_agent_operation(
93
+ ctx,
94
+ "list-conversations",
95
+ body=_without_none(
96
+ {
97
+ "app_id": app_id,
98
+ "created_at_start": created_at_start,
99
+ "created_at_end": created_at_end,
100
+ "conversation_id": conversation_id,
101
+ "invoke_from": invoke_from,
102
+ "is_transfer_human": is_transfer_human,
103
+ "limit": limit,
104
+ "cursor": cursor,
105
+ }
106
+ ),
107
+ )
80
108
 
81
109
 
82
- @agent_operation_app.command("describe", help="查看 Agent 数据 OpenAPI operation 完整契约。")
83
- def describe_agent_operation(
110
+ @conversation_message_app.command("list", help="查询指定会话的消息记录。")
111
+ def list_conversation_messages(
84
112
  ctx: typer.Context,
85
- operation_id: str = typer.Argument(...),
113
+ app_id: str = typer.Option(..., "--app-id", help="智能体应用 ID。"),
114
+ conversation_id: str = typer.Option(..., "--conversation-id", help="会话 ID。"),
115
+ conversation_created_at: int = typer.Option(
116
+ ...,
117
+ "--conversation-created-at",
118
+ min=1,
119
+ help="会话创建时间,Unix 秒级时间戳。",
120
+ ),
121
+ limit: int = typer.Option(10, "--limit", min=1, max=100, help="查询数量。"),
122
+ cursor: str | None = typer.Option(None, "--cursor", help="下一页游标。"),
86
123
  ) -> None:
87
- operation = load_catalog().by_id.get(operation_id)
88
- if operation is None:
89
- exit_with_error(ctx, operation_not_found(operation_id), json_default=True)
90
- emit_success(
124
+ _call_named_agent_operation(
91
125
  ctx,
92
- {
93
- **_agent_operation_summary(operation),
94
- "description": operation.description,
95
- "keywords": list(operation.keywords),
96
- "http": thaw(operation.http),
97
- "response": thaw(operation.response),
98
- "risk": thaw(operation.risk),
99
- "execution": thaw(operation.execution),
100
- "examples": thaw(operation.examples),
101
- },
102
- json_default=True,
126
+ "list-conversation-messages",
127
+ body=_without_none(
128
+ {
129
+ "app_id": app_id,
130
+ "conversation_id": conversation_id,
131
+ "conversation_created_at": conversation_created_at,
132
+ "limit": limit,
133
+ "cursor": cursor,
134
+ }
135
+ ),
103
136
  )
104
137
 
105
138
 
106
- @agent_app.command("call", help="校验参数并执行 Agent 数据 OpenAPI operation。")
107
- def call_agent_operation(
139
+ @conversation_trace_app.command("list", help="查询指定消息的详细日志追踪。")
140
+ def list_message_traces(
108
141
  ctx: typer.Context,
109
- operation_id: str = typer.Argument(...),
110
- param: list[str] | None = typer.Option(
111
- None,
112
- "--param",
113
- help="语义参数 key=value,可重复。",
114
- ),
115
- params_file: Path | None = typer.Option(
116
- None,
117
- "--params-file",
118
- help="语义参数 JSON 文件。",
119
- ),
120
- body_file: Path | None = typer.Option(
121
- None,
122
- "--body-file",
123
- help="JSON body 文件。",
142
+ app_id: str = typer.Option(..., "--app-id", help="智能体应用 ID。"),
143
+ conversation_id: str = typer.Option(..., "--conversation-id", help="会话 ID。"),
144
+ conversation_created_at: int = typer.Option(
145
+ ...,
146
+ "--conversation-created-at",
147
+ min=1,
148
+ help="会话创建时间,Unix 秒级时间戳。",
124
149
  ),
150
+ message_id: str = typer.Option(..., "--message-id", help="消息 ID。"),
151
+ limit: int = typer.Option(10, "--limit", min=1, max=100, help="查询数量。"),
152
+ cursor: str | None = typer.Option(None, "--cursor", help="下一页游标。"),
125
153
  ) -> None:
126
- operation = load_catalog().by_id.get(operation_id)
127
- if operation is None:
128
- exit_with_error(ctx, operation_not_found(operation_id), json_default=True)
129
- try:
130
- query, body = parse_parameters(
131
- operation,
132
- params=param,
133
- params_file=params_file,
134
- body_file=body_file,
135
- )
136
- result = AgentOpenApiService().call(
137
- operation_id,
138
- query=query,
139
- body=body,
140
- profile=get_cli_context(ctx).profile,
141
- )
142
- except TacoError as error:
143
- exit_with_error(ctx, error, json_default=True)
144
- emit_success(ctx, result, json_default=True)
145
-
146
-
147
- def _agent_operation_summary(operation) -> dict[str, Any]:
148
- return {
149
- "id": operation.id,
150
- "title": operation.title,
151
- "group": operation.group,
152
- "method": operation.http["method"],
153
- "path": operation.http["path"],
154
- "risk": operation.risk["level"],
155
- }
154
+ _call_named_agent_operation(
155
+ ctx,
156
+ "list-message-traces",
157
+ body=_without_none(
158
+ {
159
+ "app_id": app_id,
160
+ "conversation_id": conversation_id,
161
+ "conversation_created_at": conversation_created_at,
162
+ "message_id": message_id,
163
+ "limit": limit,
164
+ "cursor": cursor,
165
+ }
166
+ ),
167
+ )
156
168
 
157
169
 
158
170
  @agent_app.command("create", help="创建 Agent 并把服务端默认配置写入本地 DSL。")
@@ -574,6 +586,48 @@ def publish_agent(
574
586
  emit_success(ctx, data, json_default=True)
575
587
 
576
588
 
589
+ def _call_named_agent_operation(
590
+ ctx: typer.Context,
591
+ operation_id: str,
592
+ *,
593
+ query: dict[str, Any] | None = None,
594
+ body: dict[str, Any] | None = None,
595
+ ) -> None:
596
+ operation = load_catalog().by_id[operation_id]
597
+ try:
598
+ validated_query, validated_body = _validated_agent_parameters(
599
+ operation,
600
+ query=query or {},
601
+ body=body or {},
602
+ )
603
+ result = AgentOpenApiService().call(
604
+ operation_id,
605
+ query=validated_query,
606
+ body=validated_body,
607
+ profile=get_cli_context(ctx).profile,
608
+ )
609
+ except TacoError as error:
610
+ exit_with_error(ctx, error, json_default=True)
611
+ emit_success(ctx, result, json_default=True)
612
+
613
+
614
+ def _validated_agent_parameters(
615
+ operation,
616
+ *,
617
+ query: dict[str, Any],
618
+ body: dict[str, Any],
619
+ ) -> tuple[dict[str, Any], dict[str, Any]]:
620
+ expressions = [
621
+ f"{key}={value if isinstance(value, str) else json.dumps(value)}"
622
+ for key, value in [*query.items(), *body.items()]
623
+ ]
624
+ return parse_parameters(operation, params=expressions)
625
+
626
+
627
+ def _without_none(values: dict[str, Any]) -> dict[str, Any]:
628
+ return {key: value for key, value in values.items() if value is not None}
629
+
630
+
577
631
  def _read_json_object(path: Path | None) -> dict[str, Any]:
578
632
  if path is None:
579
633
  return {}