videosdkagent-cli 0.0.1__py2.py3-none-any.whl → 0.0.5__py2.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.
- videosdk_cli/auth.py +95 -30
- videosdk_cli/build.py +1387 -287
- videosdk_cli/main.py +69 -36
- videosdk_cli/projects.py +36 -19
- videosdk_cli/templates.py +87 -32
- videosdk_cli/utils/analytics.py +4 -3
- videosdk_cli/utils/api_client.py +26 -24
- videosdk_cli/utils/apis/deployment_client.py +247 -86
- videosdk_cli/utils/apis/videosdk_auth_api_client.py +5 -6
- videosdk_cli/utils/auth_api_client.py +2 -2
- videosdk_cli/utils/manager/agent_manager.py +275 -107
- videosdk_cli/utils/template_helper.py +102 -28
- videosdk_cli/utils/ui/progress_runner.py +15 -7
- videosdk_cli/utils/ui/theme.py +144 -0
- videosdk_cli/utils/videosdk_yaml_helper.py +173 -23
- {videosdkagent_cli-0.0.1.dist-info → videosdkagent_cli-0.0.5.dist-info}/METADATA +2 -2
- videosdkagent_cli-0.0.5.dist-info/RECORD +28 -0
- videosdk_cli/secret_set.py +0 -82
- videosdkagent_cli-0.0.1.dist-info/RECORD +0 -28
- {videosdkagent_cli-0.0.1.dist-info → videosdkagent_cli-0.0.5.dist-info}/WHEEL +0 -0
- {videosdkagent_cli-0.0.1.dist-info → videosdkagent_cli-0.0.5.dist-info}/entry_points.txt +0 -0
|
@@ -1,41 +1,75 @@
|
|
|
1
1
|
from videosdk_cli.utils.apis.videosdk_auth_api_client import VideoSDKAsyncClient
|
|
2
2
|
from typing import Optional
|
|
3
3
|
|
|
4
|
+
|
|
4
5
|
class DeploymentClient(VideoSDKAsyncClient):
|
|
5
6
|
def __init__(self):
|
|
6
7
|
super().__init__()
|
|
7
|
-
|
|
8
|
-
async def
|
|
9
|
-
|
|
8
|
+
|
|
9
|
+
async def create_version(
|
|
10
|
+
self,
|
|
11
|
+
agent_id: str,
|
|
12
|
+
deployment_id: str,
|
|
10
13
|
image_url: str,
|
|
11
14
|
min_replica: int = 1,
|
|
12
15
|
max_replica: int = 5,
|
|
13
16
|
profile: str = "cpu-small",
|
|
14
17
|
image_type: str = "public",
|
|
15
18
|
image_cr: str = "docker_hub",
|
|
19
|
+
name: Optional[str] = None,
|
|
20
|
+
version_tag: Optional[str] = None,
|
|
21
|
+
region: Optional[str] = None,
|
|
16
22
|
image_pull_secret: Optional[str] = None,
|
|
17
|
-
env_secret: Optional[str] = None
|
|
23
|
+
env_secret: Optional[str] = None,
|
|
24
|
+
build_id: Optional[str] = None,
|
|
18
25
|
):
|
|
26
|
+
"""
|
|
27
|
+
Create a new version for a deployment.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
agent_id: The agent ID
|
|
31
|
+
deployment_id: The deployment ID
|
|
32
|
+
image_url: Docker image URL
|
|
33
|
+
name: Name for this version
|
|
34
|
+
version_tag: Version tag (e.g., 'main/0.0.2')
|
|
35
|
+
min_replica: Minimum replicas
|
|
36
|
+
max_replica: Maximum replicas
|
|
37
|
+
profile: Compute profile
|
|
38
|
+
region: Deployment region
|
|
39
|
+
image_pull_secret: Secret for private registries
|
|
40
|
+
env_secret: Environment secret ID
|
|
41
|
+
build_id: Build log ID from build command
|
|
42
|
+
"""
|
|
19
43
|
payload = {
|
|
44
|
+
"name": name,
|
|
20
45
|
"agentId": agent_id,
|
|
46
|
+
"deploymentId": deployment_id,
|
|
21
47
|
"image": {
|
|
22
48
|
"imageType": image_type,
|
|
23
49
|
"imageCR": image_cr,
|
|
24
|
-
"imageUrl": image_url
|
|
50
|
+
"imageUrl": image_url,
|
|
25
51
|
},
|
|
26
52
|
"minReplica": min_replica,
|
|
27
53
|
"maxReplica": max_replica,
|
|
28
|
-
"profile": profile
|
|
54
|
+
"profile": profile,
|
|
55
|
+
"region": region,
|
|
29
56
|
}
|
|
57
|
+
if version_tag is not None:
|
|
58
|
+
payload["versionTag"] = version_tag
|
|
30
59
|
if image_pull_secret is not None:
|
|
31
|
-
payload.setdefault("image", {})["imagePullSecret"]= image_pull_secret
|
|
60
|
+
payload.setdefault("image", {})["imagePullSecret"] = image_pull_secret
|
|
32
61
|
if env_secret is not None:
|
|
33
62
|
payload["envSecret"] = env_secret
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
63
|
+
if build_id is not None:
|
|
64
|
+
payload["buildId"] = build_id
|
|
65
|
+
|
|
66
|
+
if env_secret is not None:
|
|
67
|
+
await self._request("PUT",f"/ai/v1/cloud/deployments/{deployment_id}/update",json={"envSecret":env_secret})
|
|
68
|
+
return await self._request("POST", "/ai/v1/cloud/versions", json=payload)
|
|
69
|
+
|
|
70
|
+
async def update_version(
|
|
71
|
+
self,
|
|
72
|
+
version_id: str,
|
|
39
73
|
image_url: Optional[str] = None,
|
|
40
74
|
min_replica: Optional[int] = None,
|
|
41
75
|
max_replica: Optional[int] = None,
|
|
@@ -43,22 +77,17 @@ class DeploymentClient(VideoSDKAsyncClient):
|
|
|
43
77
|
image_type: Optional[str] = None,
|
|
44
78
|
image_cr: Optional[str] = None,
|
|
45
79
|
image_pull_secret: Optional[str] = None,
|
|
46
|
-
env_secret: Optional[str] = None
|
|
80
|
+
env_secret: Optional[str] = None,
|
|
47
81
|
):
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
# "profile": profile
|
|
58
|
-
# }
|
|
59
|
-
payload = {
|
|
60
|
-
"agentId": agent_id
|
|
61
|
-
}
|
|
82
|
+
"""
|
|
83
|
+
Update an existing version.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
version_id: The version ID to update
|
|
87
|
+
agent_id: The agent ID
|
|
88
|
+
Other params: Optional fields to update
|
|
89
|
+
"""
|
|
90
|
+
payload = {}
|
|
62
91
|
|
|
63
92
|
if min_replica is not None:
|
|
64
93
|
payload["minReplica"] = min_replica
|
|
@@ -68,11 +97,10 @@ class DeploymentClient(VideoSDKAsyncClient):
|
|
|
68
97
|
|
|
69
98
|
if profile is not None:
|
|
70
99
|
payload["profile"] = profile
|
|
71
|
-
|
|
72
|
-
|
|
100
|
+
|
|
73
101
|
if env_secret is not None:
|
|
74
102
|
payload["envSecret"] = env_secret
|
|
75
|
-
|
|
103
|
+
|
|
76
104
|
image = {}
|
|
77
105
|
|
|
78
106
|
if image_url is not None:
|
|
@@ -89,20 +117,40 @@ class DeploymentClient(VideoSDKAsyncClient):
|
|
|
89
117
|
|
|
90
118
|
if image:
|
|
91
119
|
payload["image"] = image
|
|
92
|
-
return await self._request(
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
120
|
+
return await self._request(
|
|
121
|
+
"PUT", f"/ai/v1/cloud/versions/{version_id}", json=payload
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
async def deactivate_version(self, version_id: str, force: bool = False):
|
|
125
|
+
"""Deactivate a version."""
|
|
126
|
+
response = await self._request(
|
|
127
|
+
"PUT",
|
|
128
|
+
f"/ai/v1/cloud/versions/{version_id}/activate",
|
|
129
|
+
json={"activate": False, "force": force},
|
|
130
|
+
)
|
|
131
|
+
# print(response)
|
|
132
|
+
return response
|
|
133
|
+
|
|
134
|
+
async def activate_version(self, version_id: str):
|
|
135
|
+
"""Activate a version."""
|
|
136
|
+
return await self._request(
|
|
137
|
+
"PUT",
|
|
138
|
+
f"/ai/v1/cloud/versions/{version_id}/activate",
|
|
139
|
+
json={"activate": True},
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
async def agent_init(
|
|
143
|
+
self, name: Optional[str] = None, template: Optional[str] = None
|
|
144
|
+
):
|
|
101
145
|
"""
|
|
102
|
-
Initialize
|
|
146
|
+
Initialize a deployment (creates both agent and deployment).
|
|
103
147
|
|
|
104
|
-
|
|
105
|
-
|
|
148
|
+
Args:
|
|
149
|
+
name: Name for the deployment (optional, backend generates if not provided)
|
|
150
|
+
template: Template ID to use (e.g., 'Template01')
|
|
151
|
+
|
|
152
|
+
Returns:
|
|
153
|
+
Response containing agentId, deploymentId, name, template, status
|
|
106
154
|
"""
|
|
107
155
|
|
|
108
156
|
payload = {}
|
|
@@ -110,63 +158,176 @@ class DeploymentClient(VideoSDKAsyncClient):
|
|
|
110
158
|
if name:
|
|
111
159
|
payload["name"] = name
|
|
112
160
|
|
|
161
|
+
if template:
|
|
162
|
+
payload["template"] = template
|
|
163
|
+
|
|
113
164
|
return await self._request(
|
|
114
165
|
"POST",
|
|
115
|
-
"/ai/v1/cloud/
|
|
166
|
+
"/ai/v1/cloud/deployments",
|
|
116
167
|
json=payload if payload else None,
|
|
117
168
|
)
|
|
118
|
-
|
|
119
|
-
async def
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
async def secret_set(self,name:str,secrets:dict,type:Optional[str] = "NORMAL"):
|
|
135
|
-
payload = {
|
|
136
|
-
"name": name,
|
|
137
|
-
"keys": secrets,
|
|
138
|
-
"type": type
|
|
169
|
+
|
|
170
|
+
async def list_versions(
|
|
171
|
+
self,
|
|
172
|
+
agent_id: str,
|
|
173
|
+
deployment_id: str,
|
|
174
|
+
page: int = 1,
|
|
175
|
+
per_page: int = 10,
|
|
176
|
+
sort: int = -1,
|
|
177
|
+
):
|
|
178
|
+
"""List all versions for an agent."""
|
|
179
|
+
query_params = {
|
|
180
|
+
"agentId": agent_id,
|
|
181
|
+
"deploymentId": deployment_id,
|
|
182
|
+
"page": page,
|
|
183
|
+
"perPage": per_page,
|
|
184
|
+
"sort": sort,
|
|
139
185
|
}
|
|
186
|
+
|
|
187
|
+
query_string = "&".join([f"{k}={v}" for k, v in query_params.items()])
|
|
188
|
+
url = f"/ai/v1/cloud/versions?{query_string}"
|
|
189
|
+
|
|
190
|
+
return await self._request("GET", url)
|
|
191
|
+
|
|
192
|
+
async def agent_sessions_list(
|
|
193
|
+
self,
|
|
194
|
+
agent_id: str,
|
|
195
|
+
deployment_id: str,
|
|
196
|
+
version_id: Optional[str] = None,
|
|
197
|
+
room_id: Optional[str] = None,
|
|
198
|
+
session_id: Optional[str] = None,
|
|
199
|
+
page: int = 1,
|
|
200
|
+
per_page: int = 10,
|
|
201
|
+
sort: int = -1,
|
|
202
|
+
):
|
|
203
|
+
"""List sessions for an agent, optionally filtered by version."""
|
|
204
|
+
query_params = {
|
|
205
|
+
"page": page,
|
|
206
|
+
"perPage": per_page,
|
|
207
|
+
"sort": sort,
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if agent_id:
|
|
211
|
+
query_params["agentId"] = agent_id
|
|
212
|
+
if version_id:
|
|
213
|
+
query_params["versionId"] = version_id
|
|
214
|
+
if room_id:
|
|
215
|
+
query_params["roomId"] = room_id
|
|
216
|
+
if session_id:
|
|
217
|
+
query_params["sessionId"] = session_id
|
|
218
|
+
|
|
219
|
+
query_string = "&".join([f"{k}={v}" for k, v in query_params.items()])
|
|
220
|
+
url = f"/ai/v1/cloud/deployments/{deployment_id}/sessions?{query_string}"
|
|
221
|
+
|
|
222
|
+
return await self._request("GET", url)
|
|
223
|
+
|
|
224
|
+
async def describe_version(
|
|
225
|
+
self, deployment_id: str, version_id: Optional[str] = None
|
|
226
|
+
):
|
|
227
|
+
"""Get details of a specific version or the latest version."""
|
|
228
|
+
if version_id is None:
|
|
229
|
+
return await self._request(
|
|
230
|
+
"GET", f"/ai/v1/cloud/deployments/{deployment_id}/latest"
|
|
231
|
+
)
|
|
232
|
+
return await self._request("GET", f"/ai/v1/cloud/versions/{version_id}")
|
|
233
|
+
|
|
234
|
+
async def secret_set(
|
|
235
|
+
self, name: str, secrets: dict, type: Optional[str] = "NORMAL"
|
|
236
|
+
):
|
|
237
|
+
payload = {"name": name, "keys": secrets, "type": type}
|
|
140
238
|
return await self._request("POST", "/ai/v1/cloud/secrets", json=payload)
|
|
141
239
|
|
|
142
240
|
async def secret_list(self):
|
|
143
241
|
return await self._request("GET", "/ai/v1/cloud/secrets")
|
|
144
|
-
|
|
145
|
-
async def secret_remove(self,name:str):
|
|
242
|
+
|
|
243
|
+
async def secret_remove(self, name: str):
|
|
146
244
|
return await self._request("DELETE", f"/ai/v1/cloud/secrets/name/{name}")
|
|
147
|
-
|
|
148
|
-
async def secret_get(self,name:str):
|
|
245
|
+
|
|
246
|
+
async def secret_get(self, name: str):
|
|
149
247
|
return await self._request("GET", f"/ai/v1/cloud/secrets/name/{name}")
|
|
150
|
-
|
|
151
|
-
async def secret_add_key(self,name:str,secrets:dict):
|
|
152
|
-
print(secrets)
|
|
248
|
+
|
|
249
|
+
async def secret_add_key(self, name: str, secrets: dict):
|
|
250
|
+
# print(secrets)
|
|
153
251
|
payload = secrets
|
|
154
|
-
return await self._request(
|
|
252
|
+
return await self._request(
|
|
253
|
+
"PATCH", f"/ai/v1/cloud/secrets/keys/{name}", json=payload
|
|
254
|
+
)
|
|
155
255
|
|
|
156
|
-
async def secret_remove_key(self,name:str,keys:list[str]):
|
|
157
|
-
payload = {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
256
|
+
async def secret_remove_key(self, name: str, keys: list[str]):
|
|
257
|
+
payload = {"keys": keys}
|
|
258
|
+
return await self._request(
|
|
259
|
+
"DELETE", f"/ai/v1/cloud/secrets/keys/{name}", json=payload
|
|
260
|
+
)
|
|
162
261
|
|
|
163
|
-
async def agent_start(
|
|
262
|
+
async def agent_start(
|
|
263
|
+
self,
|
|
264
|
+
agent_id: str,
|
|
265
|
+
version_id: Optional[str] = None,
|
|
266
|
+
meeting_id: Optional[str] = None,
|
|
267
|
+
):
|
|
268
|
+
"""Start an agent session in a room."""
|
|
164
269
|
if meeting_id is None:
|
|
165
|
-
response = await self._request("POST",
|
|
270
|
+
response = await self._request("POST", "/v2/rooms")
|
|
166
271
|
meeting_id = response.get("roomId")
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
272
|
+
payload = {
|
|
273
|
+
"agentId": agent_id,
|
|
274
|
+
"meetingId": meeting_id,
|
|
275
|
+
}
|
|
276
|
+
if version_id:
|
|
277
|
+
payload["versionId"] = version_id
|
|
278
|
+
response = await self._request(
|
|
279
|
+
"POST",
|
|
280
|
+
"/v2/agent/dispatch",
|
|
281
|
+
json=payload,
|
|
282
|
+
)
|
|
283
|
+
# Return both the response and the meeting_id for downstream use
|
|
284
|
+
return {"response": response, "roomId": meeting_id}
|
|
285
|
+
|
|
286
|
+
async def agent_stop(self, meeting_id: str, session_id: str):
|
|
287
|
+
return await self._request(
|
|
288
|
+
"POST",
|
|
289
|
+
f"/v2/sessions/end",
|
|
290
|
+
json={"roomId": meeting_id, "sessionId": session_id},
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
async def agent_console_logs(
|
|
294
|
+
self,
|
|
295
|
+
deployment_id: str,
|
|
296
|
+
agent_id: str,
|
|
297
|
+
version_id: Optional[str] = None,
|
|
298
|
+
limit: Optional[int] = None,
|
|
299
|
+
):
|
|
300
|
+
"""Get console logs for an agent, optionally filtered by version."""
|
|
301
|
+
query_params = {}
|
|
302
|
+
if version_id:
|
|
303
|
+
query_params["versionId"] = version_id
|
|
304
|
+
if limit is not None:
|
|
305
|
+
query_params["perPage"] = limit
|
|
306
|
+
if agent_id:
|
|
307
|
+
query_params["agentId"] = agent_id
|
|
308
|
+
|
|
309
|
+
query_string = "&".join([f"{k}={v}" for k, v in query_params.items()])
|
|
310
|
+
url = f"/ai/v1/cloud/deployments/{deployment_id}/console-logs"
|
|
311
|
+
if query_string:
|
|
312
|
+
url = f"{url}?{query_string}"
|
|
313
|
+
|
|
314
|
+
return await self._request("GET", url)
|
|
315
|
+
|
|
316
|
+
async def get_build_log_presigned_url(self, deployment_id: str, tag: str):
|
|
317
|
+
"""
|
|
318
|
+
Get a presigned URL for uploading build logs.
|
|
319
|
+
|
|
320
|
+
Args:
|
|
321
|
+
deployment_id: The deployment ID
|
|
322
|
+
tag: The image tag (e.g., '1.0.0')
|
|
323
|
+
|
|
324
|
+
Returns:
|
|
325
|
+
Response containing buildId, presignedUrl, deploymentId, tag
|
|
326
|
+
"""
|
|
327
|
+
payload = {
|
|
328
|
+
"deploymentId": deployment_id,
|
|
329
|
+
"tag": tag,
|
|
330
|
+
}
|
|
331
|
+
return await self._request(
|
|
332
|
+
"POST", "/ai/v1/cloud/deployments/getPresignedUrl", json=payload
|
|
333
|
+
)
|
|
@@ -3,12 +3,13 @@ import aiohttp
|
|
|
3
3
|
from videosdk_cli.utils.config_manager import get_config_value
|
|
4
4
|
from videosdk_cli.utils.apis.error import AuthenticationError, APIRequestError
|
|
5
5
|
|
|
6
|
+
|
|
6
7
|
class VideoSDKAsyncClient:
|
|
7
8
|
def __init__(self):
|
|
9
|
+
from videosdk_cli.utils.template_helper import API_BASE_URL
|
|
10
|
+
|
|
8
11
|
self.auth_token = get_config_value("VIDEOSDK_AUTH_TOKEN") or ""
|
|
9
|
-
self.base_url =
|
|
10
|
-
"API_BASE_URL", "https://api.videosdk.live"
|
|
11
|
-
)
|
|
12
|
+
self.base_url = API_BASE_URL.rstrip("/")
|
|
12
13
|
|
|
13
14
|
self.headers = {
|
|
14
15
|
"Authorization": self.auth_token,
|
|
@@ -43,9 +44,7 @@ class VideoSDKAsyncClient:
|
|
|
43
44
|
|
|
44
45
|
if resp.status >= 400:
|
|
45
46
|
text = await resp.text()
|
|
46
|
-
raise APIRequestError(
|
|
47
|
-
f"Request failed [{resp.status}]: {text}"
|
|
48
|
-
)
|
|
47
|
+
raise APIRequestError(f"Request failed [{resp.status}]: {text}")
|
|
49
48
|
|
|
50
49
|
# auto-detect JSON vs text
|
|
51
50
|
content_type = resp.headers.get("Content-Type", "")
|
|
@@ -7,8 +7,8 @@ class AuthAPIClient:
|
|
|
7
7
|
Responsible ONLY for talking to your backend auth APIs.
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
|
-
def __init__(self,
|
|
11
|
-
self.base_url =
|
|
10
|
+
def __init__(self, api_base_url: str):
|
|
11
|
+
self.base_url = api_base_url.rstrip("/")
|
|
12
12
|
|
|
13
13
|
async def start_auth(self) -> dict:
|
|
14
14
|
"""
|