deepflow-voicespeed-cli 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3 @@
1
+ """VoiceSpeed OpenAPI command line client."""
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1,181 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import json
5
+ import os
6
+ import sys
7
+ from typing import Any, Sequence
8
+
9
+ from .client import VoiceSpeedApiError, VoiceSpeedClient, VoiceSpeedRequestError
10
+
11
+ DEFAULT_BASE_URL = "http://localhost:5002"
12
+
13
+
14
+ class CliError(Exception):
15
+ pass
16
+
17
+
18
+ def build_parser() -> argparse.ArgumentParser:
19
+ parser = argparse.ArgumentParser(prog="voicespeed")
20
+ parser.add_argument(
21
+ "--base-url",
22
+ default=os.environ.get("VOICESPEED_BASE_URL", DEFAULT_BASE_URL),
23
+ help="VoiceSpeed server base URL. Defaults to VOICESPEED_BASE_URL or localhost.",
24
+ )
25
+ parser.add_argument(
26
+ "--api-key",
27
+ default=os.environ.get("VOICESPEED_API_KEY"),
28
+ help="VoiceSpeed OpenAPI key. Defaults to VOICESPEED_API_KEY.",
29
+ )
30
+
31
+ subparsers = parser.add_subparsers(dest="resource", required=True)
32
+ _add_agent_commands(subparsers)
33
+ _add_conversation_commands(subparsers)
34
+ return parser
35
+
36
+
37
+ def _add_agent_commands(subparsers: argparse._SubParsersAction) -> None:
38
+ agents = subparsers.add_parser("agents", help="Manage OpenAPI agents")
39
+ actions = agents.add_subparsers(dest="action", required=True)
40
+
41
+ list_parser = actions.add_parser("list", help="List agents")
42
+ list_parser.add_argument("--status", choices=["enabled", "disabled"])
43
+ list_parser.add_argument("--page", type=int, default=1)
44
+ list_parser.add_argument("--per-page", type=int, default=20)
45
+ list_parser.set_defaults(handler=_handle_agents_list)
46
+
47
+ create_parser = actions.add_parser("create", help="Create an agent")
48
+ create_parser.add_argument("--name", required=True)
49
+ create_parser.add_argument("--description")
50
+ create_parser.set_defaults(handler=_handle_agents_create)
51
+
52
+ update_parser = actions.add_parser("update", help="Update an agent")
53
+ update_parser.add_argument("agent_id", type=int)
54
+ update_parser.add_argument("--name")
55
+ update_parser.add_argument("--description")
56
+ update_parser.set_defaults(handler=_handle_agents_update)
57
+
58
+ disable_parser = actions.add_parser("disable", help="Disable an agent")
59
+ disable_parser.add_argument("agent_id", type=int)
60
+ disable_parser.set_defaults(handler=_handle_agents_disable)
61
+
62
+ enable_parser = actions.add_parser("enable", help="Enable an agent")
63
+ enable_parser.add_argument("agent_id", type=int)
64
+ enable_parser.set_defaults(handler=_handle_agents_enable)
65
+
66
+ settings_parser = actions.add_parser(
67
+ "basic-settings",
68
+ help="Get agent basic engine settings",
69
+ )
70
+ settings_parser.add_argument("agent_id", type=int)
71
+ settings_parser.set_defaults(handler=_handle_agents_basic_settings)
72
+
73
+
74
+ def _add_conversation_commands(subparsers: argparse._SubParsersAction) -> None:
75
+ conversation = subparsers.add_parser("conversation", help="Run OpenAPI chat")
76
+ actions = conversation.add_subparsers(dest="action", required=True)
77
+
78
+ run_parser = actions.add_parser("run", help="Run one conversation turn")
79
+ run_parser.add_argument("agent_id", type=int)
80
+ run_parser.add_argument("--message", required=True)
81
+ run_parser.add_argument("--mode", choices=["text2text", "text2voice"], default="text2text")
82
+ run_parser.set_defaults(handler=_handle_conversation_run)
83
+
84
+
85
+ def main(argv: Sequence[str] | None = None) -> int:
86
+ parser = build_parser()
87
+ args = parser.parse_args(argv)
88
+
89
+ try:
90
+ client = _build_client(args)
91
+ result = args.handler(client, args)
92
+ _print_json(result)
93
+ return 0
94
+ except CliError as exc:
95
+ print(str(exc), file=sys.stderr)
96
+ return 1
97
+ except VoiceSpeedApiError as exc:
98
+ print(f"HTTP {exc.status_code}: {exc.body}", file=sys.stderr)
99
+ return 1
100
+ except VoiceSpeedRequestError as exc:
101
+ print(f"Request failed: {exc}", file=sys.stderr)
102
+ return 1
103
+
104
+
105
+ def _build_client(args: argparse.Namespace) -> VoiceSpeedClient:
106
+ if not args.api_key:
107
+ raise CliError("Missing API key. Use --api-key or VOICESPEED_API_KEY.")
108
+ return VoiceSpeedClient(base_url=args.base_url, api_key=args.api_key)
109
+
110
+
111
+ def _print_json(value: Any) -> None:
112
+ if value is None:
113
+ return
114
+ print(json.dumps(value, ensure_ascii=False, indent=2))
115
+
116
+
117
+ def _handle_agents_list(
118
+ client: VoiceSpeedClient,
119
+ args: argparse.Namespace,
120
+ ) -> Any:
121
+ return client.list_agents(
122
+ status=args.status,
123
+ page=args.page,
124
+ per_page=args.per_page,
125
+ )
126
+
127
+
128
+ def _handle_agents_create(
129
+ client: VoiceSpeedClient,
130
+ args: argparse.Namespace,
131
+ ) -> Any:
132
+ return client.create_agent(name=args.name, description=args.description)
133
+
134
+
135
+ def _handle_agents_update(
136
+ client: VoiceSpeedClient,
137
+ args: argparse.Namespace,
138
+ ) -> Any:
139
+ if args.name is None and args.description is None:
140
+ raise CliError("At least one of --name or --description is required.")
141
+ return client.update_agent(
142
+ args.agent_id,
143
+ name=args.name,
144
+ description=args.description,
145
+ )
146
+
147
+
148
+ def _handle_agents_disable(
149
+ client: VoiceSpeedClient,
150
+ args: argparse.Namespace,
151
+ ) -> Any:
152
+ return client.set_agent_enabled(args.agent_id, enabled=False)
153
+
154
+
155
+ def _handle_agents_enable(
156
+ client: VoiceSpeedClient,
157
+ args: argparse.Namespace,
158
+ ) -> Any:
159
+ return client.set_agent_enabled(args.agent_id, enabled=True)
160
+
161
+
162
+ def _handle_agents_basic_settings(
163
+ client: VoiceSpeedClient,
164
+ args: argparse.Namespace,
165
+ ) -> Any:
166
+ return client.get_basic_settings(args.agent_id)
167
+
168
+
169
+ def _handle_conversation_run(
170
+ client: VoiceSpeedClient,
171
+ args: argparse.Namespace,
172
+ ) -> Any:
173
+ return client.run_conversation(
174
+ args.agent_id,
175
+ message=args.message,
176
+ mode=args.mode,
177
+ )
178
+
179
+
180
+ if __name__ == "__main__":
181
+ raise SystemExit(main())
@@ -0,0 +1,142 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from dataclasses import dataclass
5
+ from typing import Any
6
+ from urllib.error import HTTPError, URLError
7
+ from urllib.parse import urlencode
8
+ from urllib.request import Request, urlopen
9
+
10
+
11
+ class VoiceSpeedClientError(Exception):
12
+ """Base error raised by the VoiceSpeed client."""
13
+
14
+
15
+ class VoiceSpeedApiError(VoiceSpeedClientError):
16
+ def __init__(self, status_code: int, body: str) -> None:
17
+ self.status_code = status_code
18
+ self.body = body
19
+ super().__init__(f"HTTP {status_code}: {body}")
20
+
21
+
22
+ class VoiceSpeedRequestError(VoiceSpeedClientError):
23
+ pass
24
+
25
+
26
+ @dataclass
27
+ class VoiceSpeedClient:
28
+ base_url: str
29
+ api_key: str
30
+ timeout: float = 30.0
31
+
32
+ def __post_init__(self) -> None:
33
+ self.base_url = self.base_url.rstrip("/")
34
+ self.api_key = self.api_key.strip()
35
+ if not self.api_key:
36
+ raise ValueError("Missing API key")
37
+
38
+ def request(
39
+ self,
40
+ method: str,
41
+ path: str,
42
+ *,
43
+ query: dict[str, Any] | None = None,
44
+ payload: dict[str, Any] | None = None,
45
+ ) -> Any:
46
+ url = f"{self.base_url}/api/v1/openapi{path}"
47
+ clean_query = {
48
+ key: value for key, value in (query or {}).items() if value is not None
49
+ }
50
+ if clean_query:
51
+ url = f"{url}?{urlencode(clean_query)}"
52
+
53
+ body = None
54
+ headers = {
55
+ "Accept": "application/json",
56
+ "Authorization": f"Bearer {self.api_key}",
57
+ }
58
+ if payload is not None:
59
+ body = json.dumps(payload).encode("utf-8")
60
+ headers["Content-Type"] = "application/json"
61
+
62
+ req = Request(url, data=body, headers=headers, method=method.upper())
63
+ try:
64
+ with urlopen(req, timeout=self.timeout) as resp:
65
+ return self._parse_response(resp.read())
66
+ except HTTPError as exc:
67
+ raise VoiceSpeedApiError(exc.code, self._read_error_body(exc)) from exc
68
+ except URLError as exc:
69
+ raise VoiceSpeedRequestError(str(exc.reason)) from exc
70
+ except OSError as exc:
71
+ raise VoiceSpeedRequestError(str(exc)) from exc
72
+
73
+ def list_agents(
74
+ self,
75
+ *,
76
+ status: str | None = None,
77
+ page: int = 1,
78
+ per_page: int = 20,
79
+ ) -> Any:
80
+ return self.request(
81
+ "GET",
82
+ "/agents",
83
+ query={"status": status, "page": page, "per_page": per_page},
84
+ )
85
+
86
+ def create_agent(self, *, name: str, description: str | None = None) -> Any:
87
+ return self.request(
88
+ "POST",
89
+ "/agents",
90
+ payload={"name": name, "description": description},
91
+ )
92
+
93
+ def update_agent(
94
+ self,
95
+ agent_id: int,
96
+ *,
97
+ name: str | None = None,
98
+ description: str | None = None,
99
+ ) -> Any:
100
+ payload = {
101
+ key: value
102
+ for key, value in {"name": name, "description": description}.items()
103
+ if value is not None
104
+ }
105
+ return self.request("PATCH", f"/agents/{agent_id}", payload=payload)
106
+
107
+ def set_agent_enabled(self, agent_id: int, *, enabled: bool) -> Any:
108
+ action = "enable" if enabled else "disable"
109
+ return self.request("POST", f"/agents/{agent_id}/{action}")
110
+
111
+ def get_basic_settings(self, agent_id: int) -> Any:
112
+ return self.request("GET", f"/agents/{agent_id}/basic-settings")
113
+
114
+ def run_conversation(
115
+ self,
116
+ agent_id: int,
117
+ *,
118
+ message: str,
119
+ mode: str = "text2text",
120
+ ) -> Any:
121
+ return self.request(
122
+ "POST",
123
+ f"/agents/{agent_id}/conversation",
124
+ payload={"message": message, "mode": mode},
125
+ )
126
+
127
+ @staticmethod
128
+ def _parse_response(data: bytes) -> Any:
129
+ if not data:
130
+ return None
131
+ text = data.decode("utf-8")
132
+ try:
133
+ return json.loads(text)
134
+ except json.JSONDecodeError:
135
+ return text
136
+
137
+ @staticmethod
138
+ def _read_error_body(exc: HTTPError) -> str:
139
+ body = exc.read()
140
+ if not body:
141
+ return exc.reason
142
+ return body.decode("utf-8", errors="replace")
@@ -0,0 +1,42 @@
1
+ Metadata-Version: 2.4
2
+ Name: deepflow-voicespeed-cli
3
+ Version: 0.1.0
4
+ Summary: Command line client for VoiceSpeed OpenAPI
5
+ Home-page: https://github.com/DeepFlowAI/voicespeed
6
+ License: Proprietary
7
+ Keywords: voicespeed,deepflow,cli,openapi
8
+ Requires-Python: >=3.10
9
+ Description-Content-Type: text/markdown
10
+
11
+ # deepflow-voicespeed-cli
12
+
13
+ Command line client for VoiceSpeed OpenAPI.
14
+
15
+ ## Install
16
+
17
+ ```bash
18
+ pip install deepflow-voicespeed-cli
19
+ ```
20
+
21
+ ## Configure
22
+
23
+ ```bash
24
+ export VOICESPEED_BASE_URL=http://localhost:5002
25
+ export VOICESPEED_API_KEY=YOUR_API_KEY
26
+ ```
27
+
28
+ Both values can also be passed with `--base-url` and `--api-key`.
29
+
30
+ ## Usage
31
+
32
+ ```bash
33
+ voicespeed agents list --status enabled
34
+ voicespeed agents create --name "Support" --description "Demo agent"
35
+ voicespeed agents update 1 --name "Support Pro"
36
+ voicespeed agents disable 1
37
+ voicespeed agents enable 1
38
+ voicespeed agents basic-settings 1
39
+ voicespeed conversation run 1 --message "Hello" --mode text2text
40
+ ```
41
+
42
+ All successful commands print formatted JSON.
@@ -0,0 +1,8 @@
1
+ deepflow_voicespeed_cli/__init__.py,sha256=FTA-PNbav1HDBMe-WNKrW53T1L5FK_jQ_XXcYHfZhco,69
2
+ deepflow_voicespeed_cli/cli.py,sha256=K3Kiy6Qecr7svbVBLMTNXqMHH3pOHHfTjtXQUIwv-wU,5698
3
+ deepflow_voicespeed_cli/client.py,sha256=03UFuwQa6mAsZjIH_w0NZp21XSzA6_2xnQqAm2anikA,4191
4
+ deepflow_voicespeed_cli-0.1.0.dist-info/METADATA,sha256=1T_z-WEpT_C67VNsJKwJxawifwJ9xBz1fNHx52144S4,995
5
+ deepflow_voicespeed_cli-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
6
+ deepflow_voicespeed_cli-0.1.0.dist-info/entry_points.txt,sha256=865sc9SU_pNJhazfQNnO_Ch5xD7ZZVRCP7vLJQEmi-k,64
7
+ deepflow_voicespeed_cli-0.1.0.dist-info/top_level.txt,sha256=kUuQDQ_5K-JlkryPqCDbKmBXB1jJ0V6ogRVHVsNDE2c,24
8
+ deepflow_voicespeed_cli-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ voicespeed = deepflow_voicespeed_cli.cli:main
@@ -0,0 +1 @@
1
+ deepflow_voicespeed_cli