pipecatcloud 0.1.2__tar.gz → 0.1.3__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.
- {pipecatcloud-0.1.2 → pipecatcloud-0.1.3}/CHANGELOG.md +28 -2
- {pipecatcloud-0.1.2/src/pipecatcloud.egg-info → pipecatcloud-0.1.3}/PKG-INFO +1 -1
- {pipecatcloud-0.1.2 → pipecatcloud-0.1.3}/pyproject.toml +1 -1
- {pipecatcloud-0.1.2 → pipecatcloud-0.1.3}/src/pipecatcloud/__init__.py +13 -7
- pipecatcloud-0.1.3/src/pipecatcloud/__version__.py +16 -0
- {pipecatcloud-0.1.2 → pipecatcloud-0.1.3}/src/pipecatcloud/_utils/console_utils.py +4 -3
- {pipecatcloud-0.1.2 → pipecatcloud-0.1.3}/src/pipecatcloud/api.py +21 -5
- {pipecatcloud-0.1.2 → pipecatcloud-0.1.3}/src/pipecatcloud/cli/commands/agent.py +93 -20
- {pipecatcloud-0.1.2 → pipecatcloud-0.1.3}/src/pipecatcloud/cli/commands/deploy.py +44 -49
- {pipecatcloud-0.1.2 → pipecatcloud-0.1.3}/src/pipecatcloud/session.py +15 -0
- {pipecatcloud-0.1.2 → pipecatcloud-0.1.3/src/pipecatcloud.egg-info}/PKG-INFO +1 -1
- pipecatcloud-0.1.2/src/pipecatcloud/__version__.py +0 -12
- {pipecatcloud-0.1.2 → pipecatcloud-0.1.3}/.github/workflows/publish-pypi.yml +0 -0
- {pipecatcloud-0.1.2 → pipecatcloud-0.1.3}/.github/workflows/publish-test.yml +0 -0
- {pipecatcloud-0.1.2 → pipecatcloud-0.1.3}/.github/workflows/python-publish.yml +0 -0
- {pipecatcloud-0.1.2 → pipecatcloud-0.1.3}/.gitignore +0 -0
- {pipecatcloud-0.1.2 → pipecatcloud-0.1.3}/LICENSE +0 -0
- {pipecatcloud-0.1.2 → pipecatcloud-0.1.3}/README.md +0 -0
- {pipecatcloud-0.1.2 → pipecatcloud-0.1.3}/dev-requirements.txt +0 -0
- {pipecatcloud-0.1.2 → pipecatcloud-0.1.3}/pipecat-cloud.png +0 -0
- {pipecatcloud-0.1.2 → pipecatcloud-0.1.3}/setup.cfg +0 -0
- {pipecatcloud-0.1.2 → pipecatcloud-0.1.3}/src/pipecatcloud/__main__.py +0 -0
- {pipecatcloud-0.1.2 → pipecatcloud-0.1.3}/src/pipecatcloud/_utils/__init__.py +0 -0
- {pipecatcloud-0.1.2 → pipecatcloud-0.1.3}/src/pipecatcloud/_utils/async_utils.py +0 -0
- {pipecatcloud-0.1.2 → pipecatcloud-0.1.3}/src/pipecatcloud/_utils/auth_utils.py +0 -0
- {pipecatcloud-0.1.2 → pipecatcloud-0.1.3}/src/pipecatcloud/_utils/deploy_utils.py +0 -0
- {pipecatcloud-0.1.2 → pipecatcloud-0.1.3}/src/pipecatcloud/_utils/local_runner.py +0 -0
- {pipecatcloud-0.1.2 → pipecatcloud-0.1.3}/src/pipecatcloud/agent.py +0 -0
- {pipecatcloud-0.1.2 → pipecatcloud-0.1.3}/src/pipecatcloud/cli/__init__.py +0 -0
- {pipecatcloud-0.1.2 → pipecatcloud-0.1.3}/src/pipecatcloud/cli/api.py +0 -0
- {pipecatcloud-0.1.2 → pipecatcloud-0.1.3}/src/pipecatcloud/cli/commands/__init__.py +0 -0
- {pipecatcloud-0.1.2 → pipecatcloud-0.1.3}/src/pipecatcloud/cli/commands/auth.py +0 -0
- {pipecatcloud-0.1.2 → pipecatcloud-0.1.3}/src/pipecatcloud/cli/commands/init.py +0 -0
- {pipecatcloud-0.1.2 → pipecatcloud-0.1.3}/src/pipecatcloud/cli/commands/organizations.py +0 -0
- {pipecatcloud-0.1.2 → pipecatcloud-0.1.3}/src/pipecatcloud/cli/commands/run.py +0 -0
- {pipecatcloud-0.1.2 → pipecatcloud-0.1.3}/src/pipecatcloud/cli/commands/secrets.py +0 -0
- {pipecatcloud-0.1.2 → pipecatcloud-0.1.3}/src/pipecatcloud/cli/config.py +0 -0
- {pipecatcloud-0.1.2 → pipecatcloud-0.1.3}/src/pipecatcloud/cli/entry_point.py +0 -0
- {pipecatcloud-0.1.2 → pipecatcloud-0.1.3}/src/pipecatcloud/config.py +0 -0
- {pipecatcloud-0.1.2 → pipecatcloud-0.1.3}/src/pipecatcloud/exception.py +0 -0
- {pipecatcloud-0.1.2 → pipecatcloud-0.1.3}/src/pipecatcloud.egg-info/SOURCES.txt +0 -0
- {pipecatcloud-0.1.2 → pipecatcloud-0.1.3}/src/pipecatcloud.egg-info/dependency_links.txt +0 -0
- {pipecatcloud-0.1.2 → pipecatcloud-0.1.3}/src/pipecatcloud.egg-info/entry_points.txt +0 -0
- {pipecatcloud-0.1.2 → pipecatcloud-0.1.3}/src/pipecatcloud.egg-info/requires.txt +0 -0
- {pipecatcloud-0.1.2 → pipecatcloud-0.1.3}/src/pipecatcloud.egg-info/top_level.txt +0 -0
|
@@ -7,15 +7,41 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- `deploy` now correctly handles error states returned by the API
|
|
13
|
+
|
|
14
|
+
- `deploy` checks revision status vs. general service ready status (when updating)
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- `agent logs` now accepts a `--deployment_id` / `-d` argument for filtering
|
|
19
|
+
by specific deployment ID
|
|
20
|
+
|
|
21
|
+
- `agent start` now accepts `--daily-properties` / `-p` for customizing Daily
|
|
22
|
+
room settings when used with `--use-daily`.
|
|
23
|
+
|
|
24
|
+
- Added `daily_room_properties` to `SessionParams` in SDK for configuring Daily
|
|
25
|
+
rooms when creating sessions.
|
|
26
|
+
|
|
27
|
+
- Added an export for the `PipecatSessionArguments` class.
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
|
|
31
|
+
- Fix an issue where custom `data` resulted in an agent not starting.
|
|
32
|
+
|
|
33
|
+
- Fixed an issue where the link returned by `pcc agent start` was not clickable
|
|
34
|
+
in IDEs.
|
|
35
|
+
|
|
10
36
|
## [0.1.2] - 2025-03-12
|
|
11
37
|
|
|
12
38
|
### Added
|
|
13
39
|
|
|
14
|
-
- `agent.py` data classes for use in base images, providing guidance on params
|
|
40
|
+
- `agent.py` data classes for use in base images, providing guidance on params.
|
|
15
41
|
|
|
16
42
|
### Fixed
|
|
17
43
|
|
|
18
|
-
- Lookup issue when passing an image pull secret to the `deploy` command
|
|
44
|
+
- Lookup issue when passing an image pull secret to the `deploy` command.
|
|
19
45
|
|
|
20
46
|
### Changed
|
|
21
47
|
|
|
@@ -9,7 +9,12 @@ import sys
|
|
|
9
9
|
|
|
10
10
|
from loguru import logger
|
|
11
11
|
|
|
12
|
-
from pipecatcloud.agent import
|
|
12
|
+
from pipecatcloud.agent import (
|
|
13
|
+
DailySessionArguments,
|
|
14
|
+
PipecatSessionArguments,
|
|
15
|
+
SessionArguments,
|
|
16
|
+
WebSocketSessionArguments,
|
|
17
|
+
)
|
|
13
18
|
from pipecatcloud.exception import (
|
|
14
19
|
AgentNotHealthyError,
|
|
15
20
|
AgentStartError,
|
|
@@ -27,18 +32,19 @@ logger.add(sys.stderr, level=str(os.getenv("PCC_LOG_LEVEL", "INFO")))
|
|
|
27
32
|
|
|
28
33
|
__all__ = [
|
|
29
34
|
# Agent classes
|
|
30
|
-
"SessionArguments",
|
|
31
35
|
"DailySessionArguments",
|
|
36
|
+
"PipecatSessionArguments",
|
|
37
|
+
"SessionArguments",
|
|
32
38
|
"WebSocketSessionArguments",
|
|
33
39
|
# Session classes
|
|
34
40
|
"Session",
|
|
35
41
|
"SessionParams",
|
|
36
42
|
# Exception classes
|
|
37
|
-
"Error",
|
|
38
|
-
"ConfigFileError",
|
|
39
|
-
"AuthError",
|
|
40
|
-
"InvalidError",
|
|
41
|
-
"ConfigError",
|
|
42
43
|
"AgentNotHealthyError",
|
|
43
44
|
"AgentStartError",
|
|
45
|
+
"AuthError",
|
|
46
|
+
"ConfigError",
|
|
47
|
+
"ConfigFileError",
|
|
48
|
+
"Error",
|
|
49
|
+
"InvalidError",
|
|
44
50
|
]
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (c) 2025, Daily
|
|
3
|
+
#
|
|
4
|
+
# SPDX-License-Identifier: BSD 2-Clause License
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
try:
|
|
8
|
+
from importlib.metadata import version as get_version
|
|
9
|
+
version = get_version("pipecatcloud")
|
|
10
|
+
except ImportError:
|
|
11
|
+
# For Python < 3.8
|
|
12
|
+
try:
|
|
13
|
+
from importlib_metadata import version as get_version
|
|
14
|
+
version = get_version("pipecatcloud")
|
|
15
|
+
except ImportError:
|
|
16
|
+
version = "Unknown"
|
|
@@ -67,8 +67,7 @@ class PipecatConsole(Console):
|
|
|
67
67
|
title_align="left",
|
|
68
68
|
subtitle_align="left",
|
|
69
69
|
border_style="red",
|
|
70
|
-
)
|
|
71
|
-
)
|
|
70
|
+
))
|
|
72
71
|
|
|
73
72
|
def api_error(
|
|
74
73
|
self,
|
|
@@ -79,7 +78,9 @@ class PipecatConsole(Console):
|
|
|
79
78
|
DEFAULT_ERROR_MESSAGE = "Unknown error. Please contact support."
|
|
80
79
|
|
|
81
80
|
if isinstance(error_code, dict):
|
|
82
|
-
error_message = error_code.get(
|
|
81
|
+
error_message = error_code.get(
|
|
82
|
+
"error", None) or error_code.get(
|
|
83
|
+
"message", None) or DEFAULT_ERROR_MESSAGE
|
|
83
84
|
code = error_code.get("code")
|
|
84
85
|
else:
|
|
85
86
|
error_message = str(error_code) if error_code else DEFAULT_ERROR_MESSAGE
|
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
# SPDX-License-Identifier: BSD 2-Clause License
|
|
5
5
|
#
|
|
6
6
|
|
|
7
|
+
import json
|
|
7
8
|
from functools import wraps
|
|
8
|
-
from typing import Callable, List, Optional
|
|
9
|
+
from typing import Callable, List, Optional, Union
|
|
9
10
|
|
|
10
11
|
import aiohttp
|
|
11
12
|
from loguru import logger
|
|
@@ -368,13 +369,24 @@ class _API:
|
|
|
368
369
|
return self.create_api_method(self._agents)
|
|
369
370
|
|
|
370
371
|
async def _start_agent(
|
|
371
|
-
self,
|
|
372
|
+
self,
|
|
373
|
+
agent_name: str,
|
|
374
|
+
api_key: str,
|
|
375
|
+
use_daily: bool,
|
|
376
|
+
data: Optional[str] = None,
|
|
377
|
+
daily_properties: Optional[str] = None,
|
|
372
378
|
) -> dict | None:
|
|
373
379
|
url = f"{self.construct_api_url('start_path').format(service=agent_name)}"
|
|
374
380
|
|
|
375
381
|
payload: dict = {"createDailyRoom": use_daily}
|
|
382
|
+
|
|
383
|
+
# Add data to payload if provided
|
|
376
384
|
if data is not None:
|
|
377
|
-
payload["body"] = data
|
|
385
|
+
payload["body"] = json.loads(data)
|
|
386
|
+
|
|
387
|
+
# Add Daily room properties only if use_daily is True
|
|
388
|
+
if use_daily and daily_properties:
|
|
389
|
+
payload["dailyRoomProperties"] = json.loads(daily_properties)
|
|
378
390
|
|
|
379
391
|
return await self._base_request(
|
|
380
392
|
"POST", url, override_token=api_key, json=payload, not_found_is_empty=True
|
|
@@ -392,9 +404,13 @@ class _API:
|
|
|
392
404
|
def agent_delete(self):
|
|
393
405
|
return self.create_api_method(self._agent_delete)
|
|
394
406
|
|
|
395
|
-
async def _agent_logs(self, agent_name: str, org: str, limit: int = 100
|
|
407
|
+
async def _agent_logs(self, agent_name: str, org: str, limit: int = 100,
|
|
408
|
+
deployment_id: Optional[str] = None) -> dict | None:
|
|
396
409
|
url = f"{self.construct_api_url('services_logs_path').format(org=org, service=agent_name)}"
|
|
397
|
-
|
|
410
|
+
params: dict[str, Union[int, str]] = {"limit": limit}
|
|
411
|
+
if deployment_id:
|
|
412
|
+
params["deploymentId"] = deployment_id
|
|
413
|
+
return await self._base_request("GET", url, params=params)
|
|
398
414
|
|
|
399
415
|
@property
|
|
400
416
|
def agent_logs(self):
|
|
@@ -42,16 +42,19 @@ async def list(
|
|
|
42
42
|
):
|
|
43
43
|
org = organization or config.get("org")
|
|
44
44
|
|
|
45
|
-
with console.status(
|
|
45
|
+
with console.status(
|
|
46
|
+
f"[dim]Fetching agents for organization: [bold]'{org}'[/bold][/dim]", spinner="dots"
|
|
47
|
+
):
|
|
46
48
|
data, error = await API.agents(org=org)
|
|
47
49
|
|
|
48
50
|
if error:
|
|
49
|
-
typer.Exit()
|
|
51
|
+
return typer.Exit()
|
|
50
52
|
|
|
51
53
|
if not data or len(data) == 0:
|
|
52
54
|
console.error(
|
|
53
55
|
f"[red]No agents found for namespace / organization '{org}'[/red]\n\n"
|
|
54
|
-
f"[dim]Please deploy an agent first using[/dim] [bold cyan]{PIPECAT_CLI_NAME} deploy[/bold cyan]"
|
|
56
|
+
f"[dim]Please deploy an agent first using[/dim] [bold cyan]{PIPECAT_CLI_NAME} deploy[/bold cyan]"
|
|
57
|
+
)
|
|
55
58
|
return typer.Exit(1)
|
|
56
59
|
|
|
57
60
|
else:
|
|
@@ -92,6 +95,8 @@ async def status(
|
|
|
92
95
|
) as live:
|
|
93
96
|
data, error = await API.agent(agent_name=agent_name, org=org, live=live)
|
|
94
97
|
|
|
98
|
+
logger.debug(f"Agent status: {data}")
|
|
99
|
+
|
|
95
100
|
live.stop()
|
|
96
101
|
|
|
97
102
|
if error:
|
|
@@ -147,6 +152,25 @@ async def status(
|
|
|
147
152
|
border_style="dim",
|
|
148
153
|
)
|
|
149
154
|
|
|
155
|
+
# Error status
|
|
156
|
+
error_panel = None
|
|
157
|
+
errors = data.get("errors", [])
|
|
158
|
+
if errors and len(errors) > 0:
|
|
159
|
+
error_table = Table(show_header=False, show_lines=False, box=box.SIMPLE)
|
|
160
|
+
error_table.add_column("Code")
|
|
161
|
+
error_table.add_column("Message")
|
|
162
|
+
for error in errors:
|
|
163
|
+
error_table.add_row(
|
|
164
|
+
f"[bold red]{error['code']}[/bold red]",
|
|
165
|
+
f"[red]{error.get('message', None) or error.get('error', 'Unknown error')}[/red]",
|
|
166
|
+
)
|
|
167
|
+
error_panel = Panel(
|
|
168
|
+
error_table,
|
|
169
|
+
title="[bold red]Agent errors:[/bold red]",
|
|
170
|
+
title_align="left",
|
|
171
|
+
border_style="red",
|
|
172
|
+
)
|
|
173
|
+
|
|
150
174
|
color = "bold green" if data["ready"] else "bold yellow"
|
|
151
175
|
subtitle = (
|
|
152
176
|
f"[dim]Start a new active session with[/dim] [bold cyan]{PIPECAT_CLI_NAME} agent start {agent_name}[/bold cyan]"
|
|
@@ -163,6 +187,7 @@ async def status(
|
|
|
163
187
|
border_style="green" if data["ready"] else "yellow",
|
|
164
188
|
expand=False,
|
|
165
189
|
),
|
|
190
|
+
error_panel if error_panel else "",
|
|
166
191
|
),
|
|
167
192
|
title=f"Status for agent [bold]{agent_name}[/bold]",
|
|
168
193
|
title_align="left",
|
|
@@ -246,30 +271,51 @@ class LogLevelColors(str, Enum):
|
|
|
246
271
|
@synchronizer.create_blocking
|
|
247
272
|
@requires_login
|
|
248
273
|
async def logs(
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
274
|
+
agent_name: str,
|
|
275
|
+
organization: str = typer.Option(
|
|
276
|
+
None,
|
|
277
|
+
"--organization",
|
|
278
|
+
"-o",
|
|
279
|
+
help="Organization to get status of agent for"),
|
|
280
|
+
level: LogLevel = typer.Option(
|
|
281
|
+
None,
|
|
282
|
+
"--level",
|
|
283
|
+
"-l",
|
|
284
|
+
help="Level of logs to get"),
|
|
285
|
+
format: LogFormat = typer.Option(
|
|
286
|
+
LogFormat.TEXT,
|
|
287
|
+
"--format",
|
|
288
|
+
"-f",
|
|
289
|
+
help="Logs format"),
|
|
290
|
+
limit: int = typer.Option(
|
|
291
|
+
100,
|
|
292
|
+
"--limit",
|
|
293
|
+
"-n",
|
|
294
|
+
help="Number of logs to get"),
|
|
295
|
+
deployment_id: str = typer.Option(
|
|
296
|
+
None,
|
|
297
|
+
"--deployment",
|
|
298
|
+
"-d",
|
|
299
|
+
help="Filter logs by deployment ID"),
|
|
256
300
|
):
|
|
257
301
|
org = organization or config.get("org")
|
|
258
302
|
|
|
303
|
+
status_text = "agent" if not deployment_id else f"deployment ({deployment_id})"
|
|
304
|
+
|
|
259
305
|
with console.status(
|
|
260
|
-
f"[dim]Fetching logs for
|
|
306
|
+
f"[dim]Fetching logs for {status_text}: [bold]'{agent_name}'[/bold] with severity: [bold cyan]{level.value if level else 'ALL'}[/bold cyan][/dim]",
|
|
261
307
|
spinner="dots",
|
|
262
308
|
):
|
|
263
|
-
data, error = await API.agent_logs(agent_name=agent_name, org=org, limit=limit)
|
|
309
|
+
data, error = await API.agent_logs(agent_name=agent_name, org=org, limit=limit, deployment_id=deployment_id)
|
|
264
310
|
|
|
265
311
|
if not data or not data.get("logs"):
|
|
266
312
|
console.print("[dim]No logs found for agent[/dim]")
|
|
267
313
|
return typer.Exit(1)
|
|
268
314
|
|
|
269
|
-
for
|
|
270
|
-
log_data =
|
|
315
|
+
for log in data["logs"]:
|
|
316
|
+
log_data = log.get("log", "")
|
|
271
317
|
if log_data:
|
|
272
|
-
timestamp = format_timestamp(
|
|
318
|
+
timestamp = format_timestamp(log.get("timestamp", ""))
|
|
273
319
|
severity = LogLevel.INFO
|
|
274
320
|
for log_severity in LogLevel:
|
|
275
321
|
if log_severity.value in log_data.upper():
|
|
@@ -282,9 +328,9 @@ async def logs(
|
|
|
282
328
|
if format == LogFormat.TEXT:
|
|
283
329
|
color = getattr(LogLevelColors, severity, LogLevelColors.DEBUG).value
|
|
284
330
|
console.print(Text(timestamp, style="bold dim"), end=" ")
|
|
285
|
-
console.print(Text(
|
|
331
|
+
console.print(Text(log_data, style=color))
|
|
286
332
|
elif format == LogFormat.JSON:
|
|
287
|
-
line = {"timestamp": timestamp, "log":
|
|
333
|
+
line = {"timestamp": timestamp, "log": log_data}
|
|
288
334
|
console.print(Text(json.dumps(line, ensure_ascii=False), style="gray"))
|
|
289
335
|
|
|
290
336
|
|
|
@@ -427,6 +473,13 @@ async def start(
|
|
|
427
473
|
help="Create a Daily WebRTC session for the agent",
|
|
428
474
|
rich_help_panel="Start Configuration",
|
|
429
475
|
),
|
|
476
|
+
daily_properties: str = typer.Option(
|
|
477
|
+
None,
|
|
478
|
+
"--daily-properties",
|
|
479
|
+
"-p",
|
|
480
|
+
help="Daily room properties (stringified JSON)",
|
|
481
|
+
rich_help_panel="Start Configuration",
|
|
482
|
+
),
|
|
430
483
|
organization: str = typer.Option(
|
|
431
484
|
None,
|
|
432
485
|
"--organization",
|
|
@@ -449,22 +502,39 @@ async def start(
|
|
|
449
502
|
title="Public API Key Required",
|
|
450
503
|
title_align="left",
|
|
451
504
|
border_style="yellow",
|
|
452
|
-
)
|
|
505
|
+
)
|
|
506
|
+
)
|
|
453
507
|
|
|
454
508
|
return typer.Exit(1)
|
|
455
509
|
|
|
510
|
+
# Validate daily_properties JSON if provided
|
|
511
|
+
if use_daily and daily_properties:
|
|
512
|
+
try:
|
|
513
|
+
json.loads(daily_properties)
|
|
514
|
+
except json.JSONDecodeError as e:
|
|
515
|
+
console.error(f"Invalid JSON format for Daily room properties: {daily_properties}")
|
|
516
|
+
console.print(f"[dim]JSON error: {str(e)}[/dim]")
|
|
517
|
+
return typer.Exit(1)
|
|
518
|
+
|
|
456
519
|
# Confirm start request
|
|
457
520
|
if not force:
|
|
521
|
+
daily_props_display = daily_properties or "None"
|
|
522
|
+
# Truncate display of daily properties if too long
|
|
523
|
+
if daily_properties and len(daily_properties) > 80:
|
|
524
|
+
daily_props_display = daily_properties[:77] + "..."
|
|
525
|
+
|
|
458
526
|
console.print(
|
|
459
527
|
Panel(
|
|
460
528
|
f"Agent Name: {agent_name}\n"
|
|
461
529
|
f"Public API Key: {default_public_api_key_name} [dim]{default_public_api_key}[/dim]\n"
|
|
462
530
|
f"Use Daily: {use_daily}\n"
|
|
531
|
+
f"Daily Properties: {daily_props_display}\n"
|
|
463
532
|
f"Data: {data}",
|
|
464
533
|
title=f"[bold]Start Request for agent: {agent_name}[/bold]",
|
|
465
534
|
title_align="left",
|
|
466
535
|
border_style="yellow",
|
|
467
|
-
)
|
|
536
|
+
)
|
|
537
|
+
)
|
|
468
538
|
if not await questionary.confirm(
|
|
469
539
|
"Are you sure you want to start an active session for this agent?"
|
|
470
540
|
).ask_async():
|
|
@@ -494,10 +564,13 @@ async def start(
|
|
|
494
564
|
api_key=default_public_api_key,
|
|
495
565
|
use_daily=use_daily,
|
|
496
566
|
data=data,
|
|
567
|
+
daily_properties=daily_properties,
|
|
497
568
|
live=live,
|
|
498
569
|
)
|
|
499
570
|
|
|
500
571
|
if error:
|
|
572
|
+
live.stop()
|
|
573
|
+
# Error is displayed from start_agent create_api_method wrapper
|
|
501
574
|
return typer.Exit(1)
|
|
502
575
|
|
|
503
576
|
live.stop()
|
|
@@ -509,4 +582,4 @@ async def start(
|
|
|
509
582
|
if daily_room:
|
|
510
583
|
url = f"{daily_room}?t={daily_token}"
|
|
511
584
|
console.print("\nJoin your session by visiting the link below:")
|
|
512
|
-
console.print(f"[link=
|
|
585
|
+
console.print(f"[link={url}]{url}[/link]")
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import asyncio
|
|
8
8
|
|
|
9
9
|
import typer
|
|
10
|
+
from loguru import logger
|
|
10
11
|
from rich.console import Group
|
|
11
12
|
from rich.live import Live
|
|
12
13
|
from rich.panel import Panel
|
|
@@ -131,7 +132,6 @@ async def _deploy(params: DeployConfigParams, org, force: bool = False):
|
|
|
131
132
|
# 3. Poll status until healthy
|
|
132
133
|
"""
|
|
133
134
|
active_deployment_id = None
|
|
134
|
-
has_failed = False
|
|
135
135
|
is_ready = False
|
|
136
136
|
checks_performed = 0
|
|
137
137
|
|
|
@@ -145,23 +145,44 @@ async def _deploy(params: DeployConfigParams, org, force: bool = False):
|
|
|
145
145
|
) as status:
|
|
146
146
|
try:
|
|
147
147
|
while checks_performed < MAX_ALIVE_CHECKS:
|
|
148
|
+
logger.debug("Polling for deployment status")
|
|
149
|
+
|
|
148
150
|
# Get deployment status
|
|
149
151
|
agent_status, error = await API.agent(
|
|
150
152
|
agent_name=params.agent_name, org=org, live=None
|
|
151
153
|
)
|
|
152
154
|
|
|
155
|
+
logger.debug(f"Deployment status: {agent_status}")
|
|
156
|
+
|
|
157
|
+
# Look for any error messages in the agent status
|
|
158
|
+
# Exit out of the polling loop if we find an error
|
|
159
|
+
status_errors = agent_status.get("errors", [])
|
|
160
|
+
if status_errors and len(status_errors) > 0:
|
|
161
|
+
status.stop()
|
|
162
|
+
# Pluck the first error message
|
|
163
|
+
error_message = status_errors[0]
|
|
164
|
+
if "code" in error_message and "message" in error_message:
|
|
165
|
+
console.api_error(error_message, "Agent deployment failed")
|
|
166
|
+
else:
|
|
167
|
+
console.error(f"Deployment failed with an unknown error: {status_errors}")
|
|
168
|
+
return typer.Exit()
|
|
169
|
+
|
|
153
170
|
if error:
|
|
154
171
|
status.stop()
|
|
155
172
|
console.error("Error checking deployment status")
|
|
156
173
|
return typer.Exit()
|
|
157
174
|
|
|
158
|
-
# Update deployment ID if received
|
|
175
|
+
# Update deployment ID if received
|
|
159
176
|
if not active_deployment_id and agent_status.get("activeDeploymentId"):
|
|
160
177
|
active_deployment_id = agent_status["activeDeploymentId"]
|
|
161
178
|
deployment_status_message = f"[dim]Waiting for deployment to become ready (deployment ID: {active_deployment_id})...[/dim]"
|
|
179
|
+
status.update(deployment_status_message)
|
|
180
|
+
|
|
181
|
+
# If we have an active deployment ID, start tailing the log output
|
|
182
|
+
# @TODO - Implement this
|
|
162
183
|
|
|
163
184
|
# Check if deployment is ready
|
|
164
|
-
if agent_status.get("
|
|
185
|
+
if agent_status.get("activeDeploymentReady", False):
|
|
165
186
|
is_ready = True
|
|
166
187
|
break
|
|
167
188
|
|
|
@@ -176,43 +197,24 @@ async def _deploy(params: DeployConfigParams, org, force: bool = False):
|
|
|
176
197
|
)
|
|
177
198
|
return typer.Exit()
|
|
178
199
|
|
|
179
|
-
# Final result handling
|
|
180
|
-
if has_failed:
|
|
181
|
-
console.print(has_failed["message"])
|
|
182
|
-
console.error("Deployment failed with the above error")
|
|
183
|
-
return typer.Exit()
|
|
184
|
-
|
|
185
|
-
# check for org secret
|
|
186
|
-
default_public_api_key = config.get("default_public_key")
|
|
187
|
-
|
|
188
200
|
if is_ready:
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
return typer.Exit(1)
|
|
204
|
-
else:
|
|
205
|
-
console.success(
|
|
206
|
-
f"Agent deployment [bold]'{params.agent_name}'[/bold] is ready\n\n"
|
|
207
|
-
f"[dim]Start a session with your new agent by running:\n[/dim]"
|
|
208
|
-
f"[bold]`{PIPECAT_CLI_NAME} agent start {params.agent_name}`[/bold]",
|
|
209
|
-
title_extra=f"{'Update' if existing_agent else 'Deployment'} complete",
|
|
210
|
-
)
|
|
201
|
+
public_api_key = config.get("default_public_key")
|
|
202
|
+
extra_message = ""
|
|
203
|
+
if not public_api_key:
|
|
204
|
+
extra_message = "\n\n[yellow]Note: if you have not already created a public API key (required to start a session), you can do so by running:\n[/yellow]"
|
|
205
|
+
extra_message += f"[bold yellow]`{PIPECAT_CLI_NAME} organizations keys create`[/bold yellow]"
|
|
206
|
+
|
|
207
|
+
console.success(
|
|
208
|
+
f"Agent deployment [bold]'{params.agent_name}'[/bold] is ready\n\n"
|
|
209
|
+
f"[white]Start a session with your new agent by running:\n[/white]"
|
|
210
|
+
f"[bold]`{PIPECAT_CLI_NAME} agent start {params.agent_name}`[/bold]"
|
|
211
|
+
f"{extra_message}",
|
|
212
|
+
title_extra=f"{'Update' if existing_agent else 'Deployment'} complete",
|
|
213
|
+
)
|
|
211
214
|
else:
|
|
212
215
|
console.error(
|
|
213
216
|
f"Deployment did not enter ready state within {MAX_ALIVE_CHECKS * ALIVE_CHECK_SLEEP} seconds. "
|
|
214
|
-
f"Please check logs with `{PIPECAT_CLI_NAME} agent logs {params.agent_name}`"
|
|
215
|
-
)
|
|
217
|
+
f"Please check logs with `{PIPECAT_CLI_NAME} agent logs {params.agent_name}`")
|
|
216
218
|
|
|
217
219
|
return typer.Exit()
|
|
218
220
|
|
|
@@ -325,24 +327,17 @@ def create_deploy_command(app: typer.Typer):
|
|
|
325
327
|
(f"[bold white]Agent name:[/bold white] [green]{partial_config.agent_name}[/green]"),
|
|
326
328
|
(f"[bold white]Image:[/bold white] [green]{partial_config.image}[/green]"),
|
|
327
329
|
(f"[bold white]Organization:[/bold white] [green]{org}[/green]"),
|
|
328
|
-
(
|
|
329
|
-
|
|
330
|
-
),
|
|
331
|
-
(
|
|
332
|
-
f"[bold white]Image pull secret:[/bold white] {'[dim]None[/dim]' if not partial_config.image_credentials else '[green]' + partial_config.image_credentials + '[/green]'}"
|
|
333
|
-
),
|
|
330
|
+
(f"[bold white]Secret set:[/bold white] {'[dim]None[/dim]' if not partial_config.secret_set else '[green] '+ partial_config.secret_set + '[/green]'}"),
|
|
331
|
+
(f"[bold white]Image pull secret:[/bold white] {'[dim]None[/dim]' if not partial_config.image_credentials else '[green]' + partial_config.image_credentials + '[/green]'}"),
|
|
334
332
|
"\n[dim]Scaling configuration:[/dim]",
|
|
335
333
|
table,
|
|
336
|
-
*
|
|
337
|
-
|
|
338
|
-
if partial_config.scaling.min_instances
|
|
339
|
-
else [
|
|
334
|
+
*
|
|
335
|
+
(
|
|
336
|
+
[] if partial_config.scaling.min_instances else [
|
|
340
337
|
Text(
|
|
341
338
|
"Note: Deploying with 0 minimum instances may result in cold starts",
|
|
342
339
|
style="red",
|
|
343
|
-
)
|
|
344
|
-
]
|
|
345
|
-
),
|
|
340
|
+
)]),
|
|
346
341
|
)
|
|
347
342
|
|
|
348
343
|
console.print(
|
|
@@ -21,10 +21,14 @@ class SessionParams:
|
|
|
21
21
|
Args:
|
|
22
22
|
data: Optional dictionary of data to pass to the agent.
|
|
23
23
|
use_daily: If True, creates a Daily WebRTC room for the session.
|
|
24
|
+
daily_room_properties: Optional dictionary of properties to configure the Daily room.
|
|
25
|
+
Only used when use_daily=True. See Daily.co API documentation for available properties:
|
|
26
|
+
https://docs.daily.co/reference/rest-api/rooms/config
|
|
24
27
|
"""
|
|
25
28
|
|
|
26
29
|
data: Optional[Dict[str, Any]] = None
|
|
27
30
|
use_daily: Optional[bool] = False
|
|
31
|
+
daily_room_properties: Optional[Dict[str, Any]] = None
|
|
28
32
|
|
|
29
33
|
|
|
30
34
|
class Session:
|
|
@@ -90,12 +94,23 @@ class Session:
|
|
|
90
94
|
# If it's already a string or other type, use as is
|
|
91
95
|
data_param = self.params.data
|
|
92
96
|
|
|
97
|
+
# Only process daily_room_properties if use_daily is True
|
|
98
|
+
daily_properties_param = None
|
|
99
|
+
if self.params.use_daily and self.params.daily_room_properties:
|
|
100
|
+
# Convert dictionary to JSON string
|
|
101
|
+
if isinstance(self.params.daily_room_properties, dict):
|
|
102
|
+
daily_properties_param = json.dumps(self.params.daily_room_properties)
|
|
103
|
+
else:
|
|
104
|
+
# If it's already a string, use as is
|
|
105
|
+
daily_properties_param = self.params.daily_room_properties
|
|
106
|
+
|
|
93
107
|
# Call the method similar to how the CLI does it
|
|
94
108
|
result, error = await api.start_agent(
|
|
95
109
|
agent_name=self.agent_name,
|
|
96
110
|
api_key=self.api_key,
|
|
97
111
|
use_daily=bool(self.params.use_daily),
|
|
98
112
|
data=data_param,
|
|
113
|
+
daily_properties=daily_properties_param,
|
|
99
114
|
)
|
|
100
115
|
|
|
101
116
|
if error:
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
#
|
|
2
|
-
# Copyright (c) 2025, Daily
|
|
3
|
-
#
|
|
4
|
-
# SPDX-License-Identifier: BSD 2-Clause License
|
|
5
|
-
#
|
|
6
|
-
|
|
7
|
-
from pathlib import Path
|
|
8
|
-
|
|
9
|
-
import toml
|
|
10
|
-
|
|
11
|
-
pyproject_path = Path(__file__).parent.parent.parent / "pyproject.toml"
|
|
12
|
-
version = toml.load(pyproject_path)["project"]["version"]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|