deepflow-openagent-cli 0.1.1__tar.gz → 0.1.4__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.
- {deepflow_openagent_cli-0.1.1 → deepflow_openagent_cli-0.1.4}/PKG-INFO +9 -1
- {deepflow_openagent_cli-0.1.1 → deepflow_openagent_cli-0.1.4}/README.md +8 -0
- {deepflow_openagent_cli-0.1.1 → deepflow_openagent_cli-0.1.4}/pyproject.toml +1 -1
- {deepflow_openagent_cli-0.1.1 → deepflow_openagent_cli-0.1.4}/src/deepflow_openagent_cli/__init__.py +1 -1
- {deepflow_openagent_cli-0.1.1 → deepflow_openagent_cli-0.1.4}/src/deepflow_openagent_cli/cli.py +136 -16
- {deepflow_openagent_cli-0.1.1 → deepflow_openagent_cli-0.1.4}/src/deepflow_openagent_cli.egg-info/PKG-INFO +9 -1
- deepflow_openagent_cli-0.1.4/tests/test_cli.py +354 -0
- deepflow_openagent_cli-0.1.1/tests/test_cli.py +0 -165
- {deepflow_openagent_cli-0.1.1 → deepflow_openagent_cli-0.1.4}/setup.cfg +0 -0
- {deepflow_openagent_cli-0.1.1 → deepflow_openagent_cli-0.1.4}/src/deepflow_openagent_cli/__main__.py +0 -0
- {deepflow_openagent_cli-0.1.1 → deepflow_openagent_cli-0.1.4}/src/deepflow_openagent_cli.egg-info/SOURCES.txt +0 -0
- {deepflow_openagent_cli-0.1.1 → deepflow_openagent_cli-0.1.4}/src/deepflow_openagent_cli.egg-info/dependency_links.txt +0 -0
- {deepflow_openagent_cli-0.1.1 → deepflow_openagent_cli-0.1.4}/src/deepflow_openagent_cli.egg-info/entry_points.txt +0 -0
- {deepflow_openagent_cli-0.1.1 → deepflow_openagent_cli-0.1.4}/src/deepflow_openagent_cli.egg-info/requires.txt +0 -0
- {deepflow_openagent_cli-0.1.1 → deepflow_openagent_cli-0.1.4}/src/deepflow_openagent_cli.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: deepflow-openagent-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: Command-line client for OpenAgent OpenAPI (agents, conversations, SSE chat, tools, channels, knowledge bases).
|
|
5
5
|
Author: DeepFlow AI
|
|
6
6
|
Project-URL: Repository, https://github.com/DeepFlowAI/OpenAgent
|
|
@@ -67,6 +67,14 @@ deepflow_openagent_cli knowledge list --page 1 --per-page 10
|
|
|
67
67
|
deepflow_openagent_cli knowledge documents 1
|
|
68
68
|
deepflow_openagent_cli knowledge query-document 6670 --slice-id 512208 --line 1720
|
|
69
69
|
deepflow_openagent_cli chat 1 --message "Hello"
|
|
70
|
+
deepflow_openagent_cli steps 1 42
|
|
71
|
+
deepflow_openagent_cli steps get 1 42 100
|
|
72
|
+
deepflow_openagent_cli steps feedback 1 42 100 --rating like --comment "Helpful answer"
|
|
73
|
+
|
|
74
|
+
# Python code tool (create → update config → debug execute)
|
|
75
|
+
deepflow_openagent_cli tools create 1 --name order_query --tool-type python_code
|
|
76
|
+
deepflow_openagent_cli tools update 1 2 --json '{"config":{"code":"import openagent\nopenagent.set_output(openagent.args)","parameters_json":"{\"type\":\"object\",\"properties\":{\"order_id\":{\"type\":\"string\"}},\"required\":[\"order_id\"]}"}}'
|
|
77
|
+
deepflow_openagent_cli tools execute 1 2 --json '{"brief":"查订单","order_id":"A1"}'
|
|
70
78
|
```
|
|
71
79
|
|
|
72
80
|
## Development (build wheel/sdist locally)
|
|
@@ -46,6 +46,14 @@ deepflow_openagent_cli knowledge list --page 1 --per-page 10
|
|
|
46
46
|
deepflow_openagent_cli knowledge documents 1
|
|
47
47
|
deepflow_openagent_cli knowledge query-document 6670 --slice-id 512208 --line 1720
|
|
48
48
|
deepflow_openagent_cli chat 1 --message "Hello"
|
|
49
|
+
deepflow_openagent_cli steps 1 42
|
|
50
|
+
deepflow_openagent_cli steps get 1 42 100
|
|
51
|
+
deepflow_openagent_cli steps feedback 1 42 100 --rating like --comment "Helpful answer"
|
|
52
|
+
|
|
53
|
+
# Python code tool (create → update config → debug execute)
|
|
54
|
+
deepflow_openagent_cli tools create 1 --name order_query --tool-type python_code
|
|
55
|
+
deepflow_openagent_cli tools update 1 2 --json '{"config":{"code":"import openagent\nopenagent.set_output(openagent.args)","parameters_json":"{\"type\":\"object\",\"properties\":{\"order_id\":{\"type\":\"string\"}},\"required\":[\"order_id\"]}"}}'
|
|
56
|
+
deepflow_openagent_cli tools execute 1 2 --json '{"brief":"查订单","order_id":"A1"}'
|
|
49
57
|
```
|
|
50
58
|
|
|
51
59
|
## Development (build wheel/sdist locally)
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "deepflow-openagent-cli"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.4"
|
|
8
8
|
description = "Command-line client for OpenAgent OpenAPI (agents, conversations, SSE chat, tools, channels, knowledge bases)."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
{deepflow_openagent_cli-0.1.1 → deepflow_openagent_cli-0.1.4}/src/deepflow_openagent_cli/cli.py
RENAMED
|
@@ -241,6 +241,40 @@ ENDPOINTS: tuple[EndpointDoc, ...] = (
|
|
|
241
241
|
errors=["401 missing or invalid API key", "404 conversation not found"],
|
|
242
242
|
example="deepflow_openagent_cli steps 1 42",
|
|
243
243
|
),
|
|
244
|
+
EndpointDoc(
|
|
245
|
+
key="steps.get",
|
|
246
|
+
method="GET",
|
|
247
|
+
path="/api/v1/agents/{agent_id}/conversations/{conversation_id}/steps/{step_id}",
|
|
248
|
+
scope="chat",
|
|
249
|
+
summary="Get full execution step detail, including LLM request/response payload fields.",
|
|
250
|
+
path_params=[
|
|
251
|
+
_param("agent_id", "integer", True, "Agent ID."),
|
|
252
|
+
_param("conversation_id", "integer", True, "Conversation ID."),
|
|
253
|
+
_param("step_id", "integer", True, "Step ID."),
|
|
254
|
+
],
|
|
255
|
+
response="StepDetailResponse with request_messages, request_tools, request_params, response fields, and child tool calls.",
|
|
256
|
+
errors=["401 missing or invalid API key", "404 step not found"],
|
|
257
|
+
example="deepflow_openagent_cli steps get 1 42 100",
|
|
258
|
+
),
|
|
259
|
+
EndpointDoc(
|
|
260
|
+
key="steps.feedback",
|
|
261
|
+
method="POST",
|
|
262
|
+
path="/api/v1/agents/{agent_id}/conversations/{conversation_id}/steps/{step_id}/feedback",
|
|
263
|
+
scope="chat",
|
|
264
|
+
summary="Submit or overwrite like/dislike feedback for one completed assistant_message step.",
|
|
265
|
+
path_params=[
|
|
266
|
+
_param("agent_id", "integer", True, "Agent ID."),
|
|
267
|
+
_param("conversation_id", "integer", True, "Conversation ID."),
|
|
268
|
+
_param("step_id", "integer", True, "Assistant message Step ID."),
|
|
269
|
+
],
|
|
270
|
+
body_fields=[
|
|
271
|
+
_param("rating", "string", True, "Feedback rating: like or dislike."),
|
|
272
|
+
_param("comment", "string", False, "Optional feedback comment, max 500 characters."),
|
|
273
|
+
],
|
|
274
|
+
response="StepFeedbackResponse with step_id, feedback_rating, feedback_comment, and feedback_updated_at.",
|
|
275
|
+
errors=["400 invalid step state", "401 missing or invalid API key", "403 scope missing", "404 step not found", "422 validation error"],
|
|
276
|
+
example="deepflow_openagent_cli steps feedback 1 42 100 --rating like --comment 'Helpful answer'",
|
|
277
|
+
),
|
|
244
278
|
EndpointDoc(
|
|
245
279
|
key="agents.list",
|
|
246
280
|
method="GET",
|
|
@@ -316,7 +350,7 @@ ENDPOINTS: tuple[EndpointDoc, ...] = (
|
|
|
316
350
|
summary="Partially update system_prompt, model, selected_tool_ids, context, or pre_recall.",
|
|
317
351
|
path_params=[_param("agent_id", "integer", True, "Agent ID.")],
|
|
318
352
|
body_fields=[
|
|
319
|
-
_param("system_prompt", "string", False, "System prompt, max
|
|
353
|
+
_param("system_prompt", "string", False, "System prompt, max 30000 chars."),
|
|
320
354
|
_param("model", "object", False, "Model config."),
|
|
321
355
|
_param("selected_tool_ids", "array[integer]", False, "Enabled tool IDs."),
|
|
322
356
|
_param("context", "object", False, "Context policy."),
|
|
@@ -345,14 +379,24 @@ ENDPOINTS: tuple[EndpointDoc, ...] = (
|
|
|
345
379
|
summary="Create a custom tool for an agent.",
|
|
346
380
|
path_params=[_param("agent_id", "integer", True, "Agent ID.")],
|
|
347
381
|
body_fields=[
|
|
348
|
-
_param("name", "string", True, "Tool name, 1-128 chars
|
|
382
|
+
_param("name", "string", True, "Tool name, 1-128 chars, pattern ^[a-zA-Z0-9_-]+$."),
|
|
349
383
|
_param("description", "string", False, "Tool description."),
|
|
350
384
|
_param("tool_type", "string", True, "search, doc_query, doc_grep, notebook, tool_response_fetch, or python_code."),
|
|
351
|
-
_param(
|
|
385
|
+
_param(
|
|
386
|
+
"config",
|
|
387
|
+
"object",
|
|
388
|
+
False,
|
|
389
|
+
"Tool config. python_code uses code (Python source) and parameters_json (JSON Schema text string).",
|
|
390
|
+
),
|
|
352
391
|
],
|
|
353
392
|
response="AgentToolResponse.",
|
|
354
|
-
errors=["401 missing or invalid API key", "403 scope missing", "404 agent not found", "422 validation error"],
|
|
355
|
-
example=
|
|
393
|
+
errors=["401 missing or invalid API key", "403 scope missing", "404 agent not found", "400/422 validation error"],
|
|
394
|
+
example=(
|
|
395
|
+
"deepflow_openagent_cli tools create 1 --name order_query --tool-type python_code "
|
|
396
|
+
"--json '{\"config\":{\"code\":\"import openagent\\nopenagent.set_output(openagent.args)\","
|
|
397
|
+
"\"parameters_json\":\"{\\\"type\\\":\\\"object\\\",\\\"properties\\\":{\\\"order_id\\\":{\\\"type\\\":\\\"string\\\"}},"
|
|
398
|
+
"\\\"required\\\":[\\\"order_id\\\"]}\"}}'"
|
|
399
|
+
),
|
|
356
400
|
),
|
|
357
401
|
EndpointDoc(
|
|
358
402
|
key="tools.get",
|
|
@@ -379,13 +423,22 @@ ENDPOINTS: tuple[EndpointDoc, ...] = (
|
|
|
379
423
|
_param("tool_id", "integer", True, "Tool ID."),
|
|
380
424
|
],
|
|
381
425
|
body_fields=[
|
|
382
|
-
_param("name", "string", False, "Tool name
|
|
426
|
+
_param("name", "string", False, "Tool name, pattern ^[a-zA-Z0-9_-]+$."),
|
|
383
427
|
_param("description", "string", False, "Tool description."),
|
|
384
|
-
_param(
|
|
428
|
+
_param(
|
|
429
|
+
"config",
|
|
430
|
+
"object",
|
|
431
|
+
False,
|
|
432
|
+
"Tool config. python_code: {code, parameters_json}; search/doc_*: {kb_ids}; human_handoff: {service_hours_id, route_fields}.",
|
|
433
|
+
),
|
|
385
434
|
],
|
|
386
435
|
response="AgentToolResponse.",
|
|
387
|
-
errors=["401 missing or invalid API key", "403 scope missing", "404 tool not found", "422 validation error"],
|
|
388
|
-
example=
|
|
436
|
+
errors=["401 missing or invalid API key", "403 scope missing", "404 tool not found", "400/422 validation error"],
|
|
437
|
+
example=(
|
|
438
|
+
"deepflow_openagent_cli tools update 1 2 --json "
|
|
439
|
+
"'{\"config\":{\"code\":\"import openagent\\nopenagent.set_output(\\\"ok\\\")\","
|
|
440
|
+
"\"parameters_json\":\"{\\\"type\\\":\\\"object\\\",\\\"properties\\\":{}}\"}}'"
|
|
441
|
+
),
|
|
389
442
|
),
|
|
390
443
|
EndpointDoc(
|
|
391
444
|
key="tools.toggle",
|
|
@@ -427,10 +480,12 @@ ENDPOINTS: tuple[EndpointDoc, ...] = (
|
|
|
427
480
|
_param("tool_id", "integer", True, "Tool ID."),
|
|
428
481
|
],
|
|
429
482
|
query_params=[_param("conversation_id", "integer", False, "Conversation ID for tool context.")],
|
|
430
|
-
body_fields=[
|
|
431
|
-
|
|
483
|
+
body_fields=[
|
|
484
|
+
_param("*", "object", False, "Tool-specific arguments. python_code example: brief + business params from parameters_json."),
|
|
485
|
+
],
|
|
486
|
+
response="ToolExecuteResponse with tool_name, tool_type, arguments, result, duration_ms.",
|
|
432
487
|
errors=["401 missing or invalid API key", "403 scope missing", "404 tool not found", "422 validation error"],
|
|
433
|
-
example="deepflow_openagent_cli tools execute 1 2 --json '{\"
|
|
488
|
+
example="deepflow_openagent_cli tools execute 1 2 --json '{\"brief\":\"查订单\",\"order_id\":\"A1\"}'",
|
|
434
489
|
),
|
|
435
490
|
EndpointDoc(
|
|
436
491
|
key="channels.list",
|
|
@@ -785,10 +840,39 @@ def handle_chat(args: argparse.Namespace) -> int:
|
|
|
785
840
|
|
|
786
841
|
|
|
787
842
|
def handle_steps(args: argparse.Namespace) -> int:
|
|
843
|
+
action, values = parse_steps_args(args.steps_args)
|
|
844
|
+
if action == "get":
|
|
845
|
+
agent_id, conversation_id, step_id = values
|
|
846
|
+
return run_request(
|
|
847
|
+
args,
|
|
848
|
+
"steps.get",
|
|
849
|
+
{
|
|
850
|
+
"agent_id": agent_id,
|
|
851
|
+
"conversation_id": conversation_id,
|
|
852
|
+
"step_id": step_id,
|
|
853
|
+
},
|
|
854
|
+
)
|
|
855
|
+
if action == "feedback":
|
|
856
|
+
agent_id, conversation_id, step_id = values
|
|
857
|
+
body = merge_payload(args, {"rating": args.rating, "comment": args.comment})
|
|
858
|
+
if "rating" not in body:
|
|
859
|
+
raise CliError("Missing feedback rating. Pass --rating like|dislike or include rating in --json.")
|
|
860
|
+
return run_request(
|
|
861
|
+
args,
|
|
862
|
+
"steps.feedback",
|
|
863
|
+
{
|
|
864
|
+
"agent_id": agent_id,
|
|
865
|
+
"conversation_id": conversation_id,
|
|
866
|
+
"step_id": step_id,
|
|
867
|
+
},
|
|
868
|
+
body=body,
|
|
869
|
+
)
|
|
870
|
+
|
|
871
|
+
agent_id, conversation_id = values
|
|
788
872
|
return run_request(
|
|
789
873
|
args,
|
|
790
874
|
"steps.list",
|
|
791
|
-
{"agent_id":
|
|
875
|
+
{"agent_id": agent_id, "conversation_id": conversation_id},
|
|
792
876
|
)
|
|
793
877
|
|
|
794
878
|
|
|
@@ -967,9 +1051,20 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
967
1051
|
add_json_args(chat)
|
|
968
1052
|
chat.set_defaults(handler=handle_chat)
|
|
969
1053
|
|
|
970
|
-
steps = subparsers.add_parser(
|
|
971
|
-
|
|
972
|
-
|
|
1054
|
+
steps = subparsers.add_parser(
|
|
1055
|
+
"steps",
|
|
1056
|
+
help="List, inspect, or submit feedback for conversation execution steps.",
|
|
1057
|
+
description=(
|
|
1058
|
+
"List conversation execution steps, get full step detail, or submit feedback. "
|
|
1059
|
+
"Forms: steps <agent_id> <conversation_id>; "
|
|
1060
|
+
"steps get <agent_id> <conversation_id> <step_id>; "
|
|
1061
|
+
"steps feedback <agent_id> <conversation_id> <step_id> --rating like."
|
|
1062
|
+
),
|
|
1063
|
+
)
|
|
1064
|
+
steps.add_argument("steps_args", nargs="+", metavar="ARGS")
|
|
1065
|
+
steps.add_argument("--rating", choices=["like", "dislike"])
|
|
1066
|
+
steps.add_argument("--comment")
|
|
1067
|
+
add_json_args(steps)
|
|
973
1068
|
steps.set_defaults(handler=handle_steps)
|
|
974
1069
|
|
|
975
1070
|
agents = subparsers.add_parser("agents", help="Agent endpoints.")
|
|
@@ -1063,6 +1158,31 @@ def parse_bool(value: str) -> bool:
|
|
|
1063
1158
|
raise argparse.ArgumentTypeError("Expected boolean: true/false")
|
|
1064
1159
|
|
|
1065
1160
|
|
|
1161
|
+
def parse_steps_args(values: list[str]) -> tuple[str, tuple[int, ...]]:
|
|
1162
|
+
action = "list"
|
|
1163
|
+
raw_values = values
|
|
1164
|
+
if values[0] in {"list", "get", "detail", "feedback"}:
|
|
1165
|
+
action = "get" if values[0] == "detail" else values[0]
|
|
1166
|
+
raw_values = values[1:]
|
|
1167
|
+
elif len(values) == 3:
|
|
1168
|
+
action = "get"
|
|
1169
|
+
|
|
1170
|
+
expected = 3 if action in {"get", "feedback"} else 2
|
|
1171
|
+
if len(raw_values) != expected:
|
|
1172
|
+
raise CliError(
|
|
1173
|
+
"Usage: steps <agent_id> <conversation_id> or "
|
|
1174
|
+
"steps get <agent_id> <conversation_id> <step_id> or "
|
|
1175
|
+
"steps feedback <agent_id> <conversation_id> <step_id> --rating like|dislike."
|
|
1176
|
+
)
|
|
1177
|
+
|
|
1178
|
+
try:
|
|
1179
|
+
parsed = tuple(int(value) for value in raw_values)
|
|
1180
|
+
except ValueError as exc:
|
|
1181
|
+
raise CliError("steps arguments must be integers.") from exc
|
|
1182
|
+
|
|
1183
|
+
return action, parsed
|
|
1184
|
+
|
|
1185
|
+
|
|
1066
1186
|
def main(argv: list[str] | None = None) -> int:
|
|
1067
1187
|
parser = build_parser()
|
|
1068
1188
|
args = parser.parse_args(argv)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: deepflow-openagent-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: Command-line client for OpenAgent OpenAPI (agents, conversations, SSE chat, tools, channels, knowledge bases).
|
|
5
5
|
Author: DeepFlow AI
|
|
6
6
|
Project-URL: Repository, https://github.com/DeepFlowAI/OpenAgent
|
|
@@ -67,6 +67,14 @@ deepflow_openagent_cli knowledge list --page 1 --per-page 10
|
|
|
67
67
|
deepflow_openagent_cli knowledge documents 1
|
|
68
68
|
deepflow_openagent_cli knowledge query-document 6670 --slice-id 512208 --line 1720
|
|
69
69
|
deepflow_openagent_cli chat 1 --message "Hello"
|
|
70
|
+
deepflow_openagent_cli steps 1 42
|
|
71
|
+
deepflow_openagent_cli steps get 1 42 100
|
|
72
|
+
deepflow_openagent_cli steps feedback 1 42 100 --rating like --comment "Helpful answer"
|
|
73
|
+
|
|
74
|
+
# Python code tool (create → update config → debug execute)
|
|
75
|
+
deepflow_openagent_cli tools create 1 --name order_query --tool-type python_code
|
|
76
|
+
deepflow_openagent_cli tools update 1 2 --json '{"config":{"code":"import openagent\nopenagent.set_output(openagent.args)","parameters_json":"{\"type\":\"object\",\"properties\":{\"order_id\":{\"type\":\"string\"}},\"required\":[\"order_id\"]}"}}'
|
|
77
|
+
deepflow_openagent_cli tools execute 1 2 --json '{"brief":"查订单","order_id":"A1"}'
|
|
70
78
|
```
|
|
71
79
|
|
|
72
80
|
## Development (build wheel/sdist locally)
|
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
import tomllib
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from deepflow_openagent_cli import __version__
|
|
8
|
+
from deepflow_openagent_cli.cli import (
|
|
9
|
+
CliError,
|
|
10
|
+
ENDPOINTS,
|
|
11
|
+
OpenApiClient,
|
|
12
|
+
build_parser,
|
|
13
|
+
client_from_args,
|
|
14
|
+
get_endpoint,
|
|
15
|
+
load_json_payload,
|
|
16
|
+
main,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def test_version_matches_release() -> None:
|
|
21
|
+
pyproject = Path(__file__).resolve().parents[1] / "pyproject.toml"
|
|
22
|
+
metadata = tomllib.loads(pyproject.read_text(encoding="utf-8"))
|
|
23
|
+
|
|
24
|
+
assert __version__ == metadata["project"]["version"]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_catalog_has_stable_unique_keys() -> None:
|
|
28
|
+
keys = [endpoint.key for endpoint in ENDPOINTS]
|
|
29
|
+
|
|
30
|
+
assert len(keys) == len(set(keys))
|
|
31
|
+
assert "knowledge.list" in keys
|
|
32
|
+
assert "knowledge.documents" in keys
|
|
33
|
+
assert "documents.query" in keys
|
|
34
|
+
assert "chat.stream" in keys
|
|
35
|
+
assert "steps.get" in keys
|
|
36
|
+
assert "channels.secret_key" in keys
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def test_get_endpoint_unknown_key_raises_clear_error() -> None:
|
|
40
|
+
with pytest.raises(CliError, match="Unknown endpoint"):
|
|
41
|
+
get_endpoint("missing.endpoint")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def test_load_json_payload_rejects_non_object() -> None:
|
|
45
|
+
with pytest.raises(CliError, match="must be an object"):
|
|
46
|
+
load_json_payload("[1, 2, 3]", None)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def test_build_url_formats_path_and_query() -> None:
|
|
50
|
+
client = OpenApiClient("http://localhost:5001/", "sk-test")
|
|
51
|
+
endpoint = get_endpoint("knowledge.documents")
|
|
52
|
+
|
|
53
|
+
url = client._build_url(
|
|
54
|
+
endpoint,
|
|
55
|
+
{"kb_id": 1},
|
|
56
|
+
{"page": 2, "per_page": 20, "source": None},
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
assert url == "http://localhost:5001/api/v1/knowledge-bases/1/documents?page=2&per_page=20"
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def test_client_from_args_reads_openagent_env(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
63
|
+
monkeypatch.setenv("OPENAGENT_HOST", "https://api.example.com")
|
|
64
|
+
monkeypatch.setenv("OPENAGENT_API_KEY", "sk-openagent")
|
|
65
|
+
args = build_parser().parse_args(["docs", "list"])
|
|
66
|
+
|
|
67
|
+
client = client_from_args(args)
|
|
68
|
+
|
|
69
|
+
assert client.host == "https://api.example.com"
|
|
70
|
+
assert client.api_key == "sk-openagent"
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def test_client_from_args_reads_legacy_newagent_env(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
74
|
+
monkeypatch.delenv("OPENAGENT_HOST", raising=False)
|
|
75
|
+
monkeypatch.delenv("OPENAGENT_API_KEY", raising=False)
|
|
76
|
+
monkeypatch.setenv("NEWAGENT_HOST", "https://legacy.example.com")
|
|
77
|
+
monkeypatch.setenv("NEWAGENT_API_KEY", "sk-legacy")
|
|
78
|
+
args = build_parser().parse_args(["docs", "list"])
|
|
79
|
+
|
|
80
|
+
client = client_from_args(args)
|
|
81
|
+
|
|
82
|
+
assert client.host == "https://legacy.example.com"
|
|
83
|
+
assert client.api_key == "sk-legacy"
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def test_docs_show_json_outputs_endpoint_detail(capsys: pytest.CaptureFixture[str]) -> None:
|
|
87
|
+
exit_code = main(["docs", "show", "documents.query", "--json"])
|
|
88
|
+
|
|
89
|
+
captured = capsys.readouterr()
|
|
90
|
+
assert exit_code == 0
|
|
91
|
+
payload = json.loads(captured.out)
|
|
92
|
+
assert payload["method"] == "POST"
|
|
93
|
+
assert payload["scope"] == "chat"
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def test_query_document_command_posts_body(
|
|
97
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
98
|
+
capsys: pytest.CaptureFixture[str],
|
|
99
|
+
) -> None:
|
|
100
|
+
calls = []
|
|
101
|
+
|
|
102
|
+
def fake_request(self, endpoint_key, path_params=None, query=None, body=None, *, stream=False):
|
|
103
|
+
calls.append((endpoint_key, path_params, query, body, stream))
|
|
104
|
+
return b'{"ok": true}'
|
|
105
|
+
|
|
106
|
+
monkeypatch.setattr(OpenApiClient, "request", fake_request)
|
|
107
|
+
|
|
108
|
+
exit_code = main(
|
|
109
|
+
[
|
|
110
|
+
"--host",
|
|
111
|
+
"http://localhost:5001",
|
|
112
|
+
"--api-key",
|
|
113
|
+
"sk-test",
|
|
114
|
+
"knowledge",
|
|
115
|
+
"query-document",
|
|
116
|
+
"6670",
|
|
117
|
+
"--slice-id",
|
|
118
|
+
"512208",
|
|
119
|
+
"--line",
|
|
120
|
+
"1720",
|
|
121
|
+
]
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
captured = capsys.readouterr()
|
|
125
|
+
assert exit_code == 0
|
|
126
|
+
assert json.loads(captured.out) == {"ok": True}
|
|
127
|
+
assert calls == [
|
|
128
|
+
(
|
|
129
|
+
"documents.query",
|
|
130
|
+
None,
|
|
131
|
+
None,
|
|
132
|
+
{"doc_id": 6670, "slice_id": 512208, "line": 1720},
|
|
133
|
+
False,
|
|
134
|
+
)
|
|
135
|
+
]
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def test_steps_list_keeps_legacy_short_form(
|
|
139
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
140
|
+
capsys: pytest.CaptureFixture[str],
|
|
141
|
+
) -> None:
|
|
142
|
+
calls = []
|
|
143
|
+
|
|
144
|
+
def fake_request(self, endpoint_key, path_params=None, query=None, body=None, *, stream=False):
|
|
145
|
+
calls.append((endpoint_key, path_params, query, body, stream))
|
|
146
|
+
return b'{"ok": true}'
|
|
147
|
+
|
|
148
|
+
monkeypatch.setattr(OpenApiClient, "request", fake_request)
|
|
149
|
+
|
|
150
|
+
exit_code = main(
|
|
151
|
+
[
|
|
152
|
+
"--host",
|
|
153
|
+
"http://localhost:5001",
|
|
154
|
+
"--api-key",
|
|
155
|
+
"sk-test",
|
|
156
|
+
"steps",
|
|
157
|
+
"1",
|
|
158
|
+
"42",
|
|
159
|
+
]
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
captured = capsys.readouterr()
|
|
163
|
+
assert exit_code == 0
|
|
164
|
+
assert json.loads(captured.out) == {"ok": True}
|
|
165
|
+
assert calls == [
|
|
166
|
+
(
|
|
167
|
+
"steps.list",
|
|
168
|
+
{"agent_id": 1, "conversation_id": 42},
|
|
169
|
+
None,
|
|
170
|
+
None,
|
|
171
|
+
False,
|
|
172
|
+
)
|
|
173
|
+
]
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def test_steps_get_fetches_full_step_detail(
|
|
177
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
178
|
+
capsys: pytest.CaptureFixture[str],
|
|
179
|
+
) -> None:
|
|
180
|
+
calls = []
|
|
181
|
+
|
|
182
|
+
def fake_request(self, endpoint_key, path_params=None, query=None, body=None, *, stream=False):
|
|
183
|
+
calls.append((endpoint_key, path_params, query, body, stream))
|
|
184
|
+
return b'{"ok": true}'
|
|
185
|
+
|
|
186
|
+
monkeypatch.setattr(OpenApiClient, "request", fake_request)
|
|
187
|
+
|
|
188
|
+
exit_code = main(
|
|
189
|
+
[
|
|
190
|
+
"--host",
|
|
191
|
+
"http://localhost:5001",
|
|
192
|
+
"--api-key",
|
|
193
|
+
"sk-test",
|
|
194
|
+
"steps",
|
|
195
|
+
"get",
|
|
196
|
+
"1",
|
|
197
|
+
"42",
|
|
198
|
+
"100",
|
|
199
|
+
]
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
captured = capsys.readouterr()
|
|
203
|
+
assert exit_code == 0
|
|
204
|
+
assert json.loads(captured.out) == {"ok": True}
|
|
205
|
+
assert calls == [
|
|
206
|
+
(
|
|
207
|
+
"steps.get",
|
|
208
|
+
{"agent_id": 1, "conversation_id": 42, "step_id": 100},
|
|
209
|
+
None,
|
|
210
|
+
None,
|
|
211
|
+
False,
|
|
212
|
+
)
|
|
213
|
+
]
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def test_tools_create_accepts_doc_grep(
|
|
217
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
218
|
+
capsys: pytest.CaptureFixture[str],
|
|
219
|
+
) -> None:
|
|
220
|
+
calls = []
|
|
221
|
+
|
|
222
|
+
def fake_request(self, endpoint_key, path_params=None, query=None, body=None, *, stream=False):
|
|
223
|
+
calls.append((endpoint_key, path_params, query, body, stream))
|
|
224
|
+
return b'{"ok": true}'
|
|
225
|
+
|
|
226
|
+
monkeypatch.setattr(OpenApiClient, "request", fake_request)
|
|
227
|
+
|
|
228
|
+
exit_code = main(
|
|
229
|
+
[
|
|
230
|
+
"--host",
|
|
231
|
+
"http://localhost:5001",
|
|
232
|
+
"--api-key",
|
|
233
|
+
"sk-test",
|
|
234
|
+
"tools",
|
|
235
|
+
"create",
|
|
236
|
+
"1",
|
|
237
|
+
"--name",
|
|
238
|
+
"Doc Grep",
|
|
239
|
+
"--tool-type",
|
|
240
|
+
"doc_grep",
|
|
241
|
+
]
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
captured = capsys.readouterr()
|
|
245
|
+
assert exit_code == 0
|
|
246
|
+
assert json.loads(captured.out) == {"ok": True}
|
|
247
|
+
assert calls[0][0] == "tools.create"
|
|
248
|
+
assert calls[0][1] == {"agent_id": 1}
|
|
249
|
+
assert calls[0][3]["tool_type"] == "doc_grep"
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def test_tools_create_accepts_python_code(
|
|
253
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
254
|
+
capsys: pytest.CaptureFixture[str],
|
|
255
|
+
) -> None:
|
|
256
|
+
calls = []
|
|
257
|
+
|
|
258
|
+
def fake_request(self, endpoint_key, path_params=None, query=None, body=None, *, stream=False):
|
|
259
|
+
calls.append((endpoint_key, path_params, query, body, stream))
|
|
260
|
+
return b'{"ok": true}'
|
|
261
|
+
|
|
262
|
+
monkeypatch.setattr(OpenApiClient, "request", fake_request)
|
|
263
|
+
|
|
264
|
+
config = {
|
|
265
|
+
"code": "import openagent\nopenagent.set_output(openagent.args)",
|
|
266
|
+
"parameters_json": '{"type":"object","properties":{"order_id":{"type":"string"}},"required":["order_id"]}',
|
|
267
|
+
}
|
|
268
|
+
exit_code = main(
|
|
269
|
+
[
|
|
270
|
+
"--host",
|
|
271
|
+
"http://localhost:5001",
|
|
272
|
+
"--api-key",
|
|
273
|
+
"sk-test",
|
|
274
|
+
"tools",
|
|
275
|
+
"create",
|
|
276
|
+
"1",
|
|
277
|
+
"--name",
|
|
278
|
+
"order_query",
|
|
279
|
+
"--tool-type",
|
|
280
|
+
"python_code",
|
|
281
|
+
"--json",
|
|
282
|
+
json.dumps({"config": config}),
|
|
283
|
+
]
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
captured = capsys.readouterr()
|
|
287
|
+
assert exit_code == 0
|
|
288
|
+
assert json.loads(captured.out) == {"ok": True}
|
|
289
|
+
assert calls[0][0] == "tools.create"
|
|
290
|
+
assert calls[0][1] == {"agent_id": 1}
|
|
291
|
+
assert calls[0][3]["tool_type"] == "python_code"
|
|
292
|
+
assert calls[0][3]["name"] == "order_query"
|
|
293
|
+
assert calls[0][3]["config"] == config
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def test_tools_update_and_execute_python_code(
|
|
297
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
298
|
+
capsys: pytest.CaptureFixture[str],
|
|
299
|
+
) -> None:
|
|
300
|
+
calls = []
|
|
301
|
+
|
|
302
|
+
def fake_request(self, endpoint_key, path_params=None, query=None, body=None, *, stream=False):
|
|
303
|
+
calls.append((endpoint_key, path_params, query, body, stream))
|
|
304
|
+
return b'{"ok": true}'
|
|
305
|
+
|
|
306
|
+
monkeypatch.setattr(OpenApiClient, "request", fake_request)
|
|
307
|
+
|
|
308
|
+
update_body = {
|
|
309
|
+
"config": {
|
|
310
|
+
"code": "import openagent\nopenagent.set_output('ok')",
|
|
311
|
+
"parameters_json": '{"type":"object","properties":{}}',
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
exit_code = main(
|
|
315
|
+
[
|
|
316
|
+
"--host",
|
|
317
|
+
"http://localhost:5001",
|
|
318
|
+
"--api-key",
|
|
319
|
+
"sk-test",
|
|
320
|
+
"tools",
|
|
321
|
+
"update",
|
|
322
|
+
"1",
|
|
323
|
+
"2",
|
|
324
|
+
"--json",
|
|
325
|
+
json.dumps(update_body),
|
|
326
|
+
]
|
|
327
|
+
)
|
|
328
|
+
captured = capsys.readouterr()
|
|
329
|
+
assert exit_code == 0
|
|
330
|
+
assert json.loads(captured.out) == {"ok": True}
|
|
331
|
+
assert calls[0][0] == "tools.update"
|
|
332
|
+
assert calls[0][1] == {"agent_id": 1, "tool_id": 2}
|
|
333
|
+
assert calls[0][3] == update_body
|
|
334
|
+
|
|
335
|
+
exit_code = main(
|
|
336
|
+
[
|
|
337
|
+
"--host",
|
|
338
|
+
"http://localhost:5001",
|
|
339
|
+
"--api-key",
|
|
340
|
+
"sk-test",
|
|
341
|
+
"tools",
|
|
342
|
+
"execute",
|
|
343
|
+
"1",
|
|
344
|
+
"2",
|
|
345
|
+
"--json",
|
|
346
|
+
'{"brief":"查订单","order_id":"A1"}',
|
|
347
|
+
]
|
|
348
|
+
)
|
|
349
|
+
captured = capsys.readouterr()
|
|
350
|
+
assert exit_code == 0
|
|
351
|
+
assert json.loads(captured.out) == {"ok": True}
|
|
352
|
+
assert calls[1][0] == "tools.execute"
|
|
353
|
+
assert calls[1][1] == {"agent_id": 1, "tool_id": 2}
|
|
354
|
+
assert calls[1][3] == {"brief": "查订单", "order_id": "A1"}
|
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
|
|
3
|
-
import pytest
|
|
4
|
-
|
|
5
|
-
from deepflow_openagent_cli import __version__
|
|
6
|
-
from deepflow_openagent_cli.cli import (
|
|
7
|
-
CliError,
|
|
8
|
-
ENDPOINTS,
|
|
9
|
-
OpenApiClient,
|
|
10
|
-
build_parser,
|
|
11
|
-
client_from_args,
|
|
12
|
-
get_endpoint,
|
|
13
|
-
load_json_payload,
|
|
14
|
-
main,
|
|
15
|
-
)
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def test_version_matches_release() -> None:
|
|
19
|
-
assert __version__ == "0.1.1"
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
def test_catalog_has_stable_unique_keys() -> None:
|
|
23
|
-
keys = [endpoint.key for endpoint in ENDPOINTS]
|
|
24
|
-
|
|
25
|
-
assert len(keys) == len(set(keys))
|
|
26
|
-
assert "knowledge.list" in keys
|
|
27
|
-
assert "knowledge.documents" in keys
|
|
28
|
-
assert "documents.query" in keys
|
|
29
|
-
assert "chat.stream" in keys
|
|
30
|
-
assert "channels.secret_key" in keys
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
def test_get_endpoint_unknown_key_raises_clear_error() -> None:
|
|
34
|
-
with pytest.raises(CliError, match="Unknown endpoint"):
|
|
35
|
-
get_endpoint("missing.endpoint")
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
def test_load_json_payload_rejects_non_object() -> None:
|
|
39
|
-
with pytest.raises(CliError, match="must be an object"):
|
|
40
|
-
load_json_payload("[1, 2, 3]", None)
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
def test_build_url_formats_path_and_query() -> None:
|
|
44
|
-
client = OpenApiClient("http://localhost:5001/", "sk-test")
|
|
45
|
-
endpoint = get_endpoint("knowledge.documents")
|
|
46
|
-
|
|
47
|
-
url = client._build_url(
|
|
48
|
-
endpoint,
|
|
49
|
-
{"kb_id": 1},
|
|
50
|
-
{"page": 2, "per_page": 20, "source": None},
|
|
51
|
-
)
|
|
52
|
-
|
|
53
|
-
assert url == "http://localhost:5001/api/v1/knowledge-bases/1/documents?page=2&per_page=20"
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
def test_client_from_args_reads_openagent_env(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
57
|
-
monkeypatch.setenv("OPENAGENT_HOST", "https://api.example.com")
|
|
58
|
-
monkeypatch.setenv("OPENAGENT_API_KEY", "sk-openagent")
|
|
59
|
-
args = build_parser().parse_args(["docs", "list"])
|
|
60
|
-
|
|
61
|
-
client = client_from_args(args)
|
|
62
|
-
|
|
63
|
-
assert client.host == "https://api.example.com"
|
|
64
|
-
assert client.api_key == "sk-openagent"
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
def test_client_from_args_reads_legacy_newagent_env(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
68
|
-
monkeypatch.delenv("OPENAGENT_HOST", raising=False)
|
|
69
|
-
monkeypatch.delenv("OPENAGENT_API_KEY", raising=False)
|
|
70
|
-
monkeypatch.setenv("NEWAGENT_HOST", "https://legacy.example.com")
|
|
71
|
-
monkeypatch.setenv("NEWAGENT_API_KEY", "sk-legacy")
|
|
72
|
-
args = build_parser().parse_args(["docs", "list"])
|
|
73
|
-
|
|
74
|
-
client = client_from_args(args)
|
|
75
|
-
|
|
76
|
-
assert client.host == "https://legacy.example.com"
|
|
77
|
-
assert client.api_key == "sk-legacy"
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
def test_docs_show_json_outputs_endpoint_detail(capsys: pytest.CaptureFixture[str]) -> None:
|
|
81
|
-
exit_code = main(["docs", "show", "documents.query", "--json"])
|
|
82
|
-
|
|
83
|
-
captured = capsys.readouterr()
|
|
84
|
-
assert exit_code == 0
|
|
85
|
-
payload = json.loads(captured.out)
|
|
86
|
-
assert payload["method"] == "POST"
|
|
87
|
-
assert payload["scope"] == "chat"
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
def test_query_document_command_posts_body(
|
|
91
|
-
monkeypatch: pytest.MonkeyPatch,
|
|
92
|
-
capsys: pytest.CaptureFixture[str],
|
|
93
|
-
) -> None:
|
|
94
|
-
calls = []
|
|
95
|
-
|
|
96
|
-
def fake_request(self, endpoint_key, path_params=None, query=None, body=None, *, stream=False):
|
|
97
|
-
calls.append((endpoint_key, path_params, query, body, stream))
|
|
98
|
-
return b'{"ok": true}'
|
|
99
|
-
|
|
100
|
-
monkeypatch.setattr(OpenApiClient, "request", fake_request)
|
|
101
|
-
|
|
102
|
-
exit_code = main(
|
|
103
|
-
[
|
|
104
|
-
"--host",
|
|
105
|
-
"http://localhost:5001",
|
|
106
|
-
"--api-key",
|
|
107
|
-
"sk-test",
|
|
108
|
-
"knowledge",
|
|
109
|
-
"query-document",
|
|
110
|
-
"6670",
|
|
111
|
-
"--slice-id",
|
|
112
|
-
"512208",
|
|
113
|
-
"--line",
|
|
114
|
-
"1720",
|
|
115
|
-
]
|
|
116
|
-
)
|
|
117
|
-
|
|
118
|
-
captured = capsys.readouterr()
|
|
119
|
-
assert exit_code == 0
|
|
120
|
-
assert json.loads(captured.out) == {"ok": True}
|
|
121
|
-
assert calls == [
|
|
122
|
-
(
|
|
123
|
-
"documents.query",
|
|
124
|
-
None,
|
|
125
|
-
None,
|
|
126
|
-
{"doc_id": 6670, "slice_id": 512208, "line": 1720},
|
|
127
|
-
False,
|
|
128
|
-
)
|
|
129
|
-
]
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
def test_tools_create_accepts_doc_grep(
|
|
133
|
-
monkeypatch: pytest.MonkeyPatch,
|
|
134
|
-
capsys: pytest.CaptureFixture[str],
|
|
135
|
-
) -> None:
|
|
136
|
-
calls = []
|
|
137
|
-
|
|
138
|
-
def fake_request(self, endpoint_key, path_params=None, query=None, body=None, *, stream=False):
|
|
139
|
-
calls.append((endpoint_key, path_params, query, body, stream))
|
|
140
|
-
return b'{"ok": true}'
|
|
141
|
-
|
|
142
|
-
monkeypatch.setattr(OpenApiClient, "request", fake_request)
|
|
143
|
-
|
|
144
|
-
exit_code = main(
|
|
145
|
-
[
|
|
146
|
-
"--host",
|
|
147
|
-
"http://localhost:5001",
|
|
148
|
-
"--api-key",
|
|
149
|
-
"sk-test",
|
|
150
|
-
"tools",
|
|
151
|
-
"create",
|
|
152
|
-
"1",
|
|
153
|
-
"--name",
|
|
154
|
-
"Doc Grep",
|
|
155
|
-
"--tool-type",
|
|
156
|
-
"doc_grep",
|
|
157
|
-
]
|
|
158
|
-
)
|
|
159
|
-
|
|
160
|
-
captured = capsys.readouterr()
|
|
161
|
-
assert exit_code == 0
|
|
162
|
-
assert json.loads(captured.out) == {"ok": True}
|
|
163
|
-
assert calls[0][0] == "tools.create"
|
|
164
|
-
assert calls[0][1] == {"agent_id": 1}
|
|
165
|
-
assert calls[0][3]["tool_type"] == "doc_grep"
|
|
File without changes
|
{deepflow_openagent_cli-0.1.1 → deepflow_openagent_cli-0.1.4}/src/deepflow_openagent_cli/__main__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|