pipecatcloud 0.1.6__tar.gz → 0.1.8__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.6 → pipecatcloud-0.1.8}/CHANGELOG.md +27 -8
- {pipecatcloud-0.1.6/src/pipecatcloud.egg-info → pipecatcloud-0.1.8}/PKG-INFO +2 -2
- {pipecatcloud-0.1.6 → pipecatcloud-0.1.8}/pyproject.toml +5 -2
- {pipecatcloud-0.1.6 → pipecatcloud-0.1.8}/src/pipecatcloud/_utils/console_utils.py +94 -0
- {pipecatcloud-0.1.6 → pipecatcloud-0.1.8}/src/pipecatcloud/api.py +8 -0
- {pipecatcloud-0.1.6 → pipecatcloud-0.1.8}/src/pipecatcloud/cli/commands/agent.py +112 -18
- {pipecatcloud-0.1.6 → pipecatcloud-0.1.8}/src/pipecatcloud/cli/commands/deploy.py +29 -7
- {pipecatcloud-0.1.6 → pipecatcloud-0.1.8}/src/pipecatcloud/config.py +1 -0
- pipecatcloud-0.1.8/src/pipecatcloud/py.typed +0 -0
- {pipecatcloud-0.1.6 → pipecatcloud-0.1.8/src/pipecatcloud.egg-info}/PKG-INFO +2 -2
- {pipecatcloud-0.1.6 → pipecatcloud-0.1.8}/src/pipecatcloud.egg-info/SOURCES.txt +1 -0
- {pipecatcloud-0.1.6 → pipecatcloud-0.1.8}/src/pipecatcloud.egg-info/requires.txt +1 -1
- {pipecatcloud-0.1.6 → pipecatcloud-0.1.8}/.github/workflows/publish-pypi.yml +0 -0
- {pipecatcloud-0.1.6 → pipecatcloud-0.1.8}/.github/workflows/publish-test.yml +0 -0
- {pipecatcloud-0.1.6 → pipecatcloud-0.1.8}/.github/workflows/python-publish.yml +0 -0
- {pipecatcloud-0.1.6 → pipecatcloud-0.1.8}/.gitignore +0 -0
- {pipecatcloud-0.1.6 → pipecatcloud-0.1.8}/LICENSE +0 -0
- {pipecatcloud-0.1.6 → pipecatcloud-0.1.8}/README.md +0 -0
- {pipecatcloud-0.1.6 → pipecatcloud-0.1.8}/dev-requirements.txt +0 -0
- {pipecatcloud-0.1.6 → pipecatcloud-0.1.8}/pipecat-cloud.png +0 -0
- {pipecatcloud-0.1.6 → pipecatcloud-0.1.8}/setup.cfg +0 -0
- {pipecatcloud-0.1.6 → pipecatcloud-0.1.8}/src/pipecatcloud/__init__.py +0 -0
- {pipecatcloud-0.1.6 → pipecatcloud-0.1.8}/src/pipecatcloud/__main__.py +0 -0
- {pipecatcloud-0.1.6 → pipecatcloud-0.1.8}/src/pipecatcloud/__version__.py +0 -0
- {pipecatcloud-0.1.6 → pipecatcloud-0.1.8}/src/pipecatcloud/_utils/__init__.py +0 -0
- {pipecatcloud-0.1.6 → pipecatcloud-0.1.8}/src/pipecatcloud/_utils/async_utils.py +0 -0
- {pipecatcloud-0.1.6 → pipecatcloud-0.1.8}/src/pipecatcloud/_utils/auth_utils.py +0 -0
- {pipecatcloud-0.1.6 → pipecatcloud-0.1.8}/src/pipecatcloud/_utils/deploy_utils.py +0 -0
- {pipecatcloud-0.1.6 → pipecatcloud-0.1.8}/src/pipecatcloud/_utils/local_runner.py +0 -0
- {pipecatcloud-0.1.6 → pipecatcloud-0.1.8}/src/pipecatcloud/agent.py +0 -0
- {pipecatcloud-0.1.6 → pipecatcloud-0.1.8}/src/pipecatcloud/cli/__init__.py +0 -0
- {pipecatcloud-0.1.6 → pipecatcloud-0.1.8}/src/pipecatcloud/cli/api.py +0 -0
- {pipecatcloud-0.1.6 → pipecatcloud-0.1.8}/src/pipecatcloud/cli/commands/__init__.py +0 -0
- {pipecatcloud-0.1.6 → pipecatcloud-0.1.8}/src/pipecatcloud/cli/commands/auth.py +0 -0
- {pipecatcloud-0.1.6 → pipecatcloud-0.1.8}/src/pipecatcloud/cli/commands/init.py +0 -0
- {pipecatcloud-0.1.6 → pipecatcloud-0.1.8}/src/pipecatcloud/cli/commands/organizations.py +0 -0
- {pipecatcloud-0.1.6 → pipecatcloud-0.1.8}/src/pipecatcloud/cli/commands/run.py +0 -0
- {pipecatcloud-0.1.6 → pipecatcloud-0.1.8}/src/pipecatcloud/cli/commands/secrets.py +0 -0
- {pipecatcloud-0.1.6 → pipecatcloud-0.1.8}/src/pipecatcloud/cli/config.py +0 -0
- {pipecatcloud-0.1.6 → pipecatcloud-0.1.8}/src/pipecatcloud/cli/entry_point.py +0 -0
- {pipecatcloud-0.1.6 → pipecatcloud-0.1.8}/src/pipecatcloud/exception.py +0 -0
- {pipecatcloud-0.1.6 → pipecatcloud-0.1.8}/src/pipecatcloud/session.py +0 -0
- {pipecatcloud-0.1.6 → pipecatcloud-0.1.8}/src/pipecatcloud.egg-info/dependency_links.txt +0 -0
- {pipecatcloud-0.1.6 → pipecatcloud-0.1.8}/src/pipecatcloud.egg-info/entry_points.txt +0 -0
- {pipecatcloud-0.1.6 → pipecatcloud-0.1.8}/src/pipecatcloud.egg-info/top_level.txt +0 -0
|
@@ -7,28 +7,47 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.1.8] - 2025-07-09
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- `deploy` command now shows a warning when image pull credentials are not provided.
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- Add py.typed marker for static type checking support.
|
|
19
|
+
|
|
20
|
+
## [0.1.7] - 2025-06-12
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
|
|
24
|
+
- `agent sessions` command lists session history and various statistics (avg. start times, cold starts etc.)
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
|
|
28
|
+
- Bumped `typer` dependency to `0.15` to fix errors when using `--help` flag.
|
|
29
|
+
|
|
10
30
|
## [0.1.6] - 2025-04-25
|
|
11
31
|
|
|
12
|
-
|
|
32
|
+
### Changed
|
|
13
33
|
|
|
14
34
|
- `min-instances` and `max-instances` has been changed to reflect API terminology changes.
|
|
15
35
|
|
|
16
36
|
## [0.1.5] - 2025-04-09
|
|
17
37
|
|
|
18
|
-
|
|
38
|
+
### Added
|
|
19
39
|
|
|
20
40
|
- `deploy` command now accepts a `--enable-krisp / -krisp` which enables Krisp integration for your pipeline.
|
|
21
41
|
|
|
22
|
-
|
|
42
|
+
### Changed
|
|
23
43
|
|
|
24
|
-
- `start` command now takes agent name from `pcc-deploy.toml` where applicable
|
|
44
|
+
- `start` command now takes agent name from `pcc-deploy.toml` where applicable
|
|
25
45
|
- API error handling now checks `response.ok` and properly checks for error codes for all non-ok responses
|
|
26
46
|
|
|
27
|
-
|
|
47
|
+
### Fixed
|
|
28
48
|
|
|
29
49
|
- REST and cURL requests now render errors correctly
|
|
30
50
|
|
|
31
|
-
|
|
32
51
|
## [0.1.4] - 2025-03-28
|
|
33
52
|
|
|
34
53
|
### Changed
|
|
@@ -53,8 +72,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
53
72
|
|
|
54
73
|
### Added
|
|
55
74
|
|
|
56
|
-
- `agent logs` now accepts a `--deployment_id` / `-d` argument for filtering
|
|
57
|
-
by specific deployment ID
|
|
75
|
+
- `agent logs` now accepts a `--deployment_id` / `-d` argument for filtering
|
|
76
|
+
by specific deployment ID
|
|
58
77
|
|
|
59
78
|
- `agent start` now accepts `--daily-properties` / `-p` for customizing Daily
|
|
60
79
|
room settings when used with `--use-daily`.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pipecatcloud
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.8
|
|
4
4
|
Summary: Cloud hosting for Pipecat AI applications
|
|
5
5
|
License: BSD 2-Clause License
|
|
6
6
|
Project-URL: Website, https://docs.pipecat.daily.co/
|
|
@@ -13,7 +13,7 @@ Requires-Dist: fastapi~=0.115.6
|
|
|
13
13
|
Requires-Dist: loguru~=0.7.3
|
|
14
14
|
Requires-Dist: python-dotenv~=1.0.1
|
|
15
15
|
Requires-Dist: uvicorn~=0.32.0
|
|
16
|
-
Requires-Dist: typer~=0.
|
|
16
|
+
Requires-Dist: typer~=0.15.3
|
|
17
17
|
Requires-Dist: synchronicity~=0.7.6
|
|
18
18
|
Requires-Dist: toml~=0.10.2
|
|
19
19
|
Requires-Dist: questionary~=2.1.0
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pipecatcloud"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.8"
|
|
8
8
|
requires-python = ">=3.10"
|
|
9
9
|
description = "Cloud hosting for Pipecat AI applications"
|
|
10
10
|
license = { text = "BSD 2-Clause License" }
|
|
@@ -16,7 +16,7 @@ dependencies = [
|
|
|
16
16
|
"loguru~=0.7.3",
|
|
17
17
|
"python-dotenv~=1.0.1",
|
|
18
18
|
"uvicorn~=0.32.0",
|
|
19
|
-
"typer~=0.
|
|
19
|
+
"typer~=0.15.3",
|
|
20
20
|
"synchronicity~=0.7.6",
|
|
21
21
|
"toml~=0.10.2",
|
|
22
22
|
"questionary~=2.1.0"
|
|
@@ -33,6 +33,9 @@ package-dir = {"" = "src"}
|
|
|
33
33
|
where = ["src"]
|
|
34
34
|
include = ["pipecatcloud*"]
|
|
35
35
|
|
|
36
|
+
[tool.setuptools.package-data]
|
|
37
|
+
pipecatcloud = ["py.typed"]
|
|
38
|
+
|
|
36
39
|
[tool.ruff]
|
|
37
40
|
exclude = ["*_pb2.py"]
|
|
38
41
|
line-length = 100
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
# SPDX-License-Identifier: BSD 2-Clause License
|
|
5
5
|
#
|
|
6
6
|
|
|
7
|
+
import statistics
|
|
8
|
+
from datetime import datetime
|
|
7
9
|
from typing import Optional, Union
|
|
8
10
|
|
|
9
11
|
from rich.console import Console
|
|
@@ -140,3 +142,95 @@ def format_timestamp(timestamp: str) -> str:
|
|
|
140
142
|
|
|
141
143
|
# Return original if parsing fails
|
|
142
144
|
return timestamp
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def format_duration(created_at_str: str, ended_at_str: str) -> str | None:
|
|
148
|
+
"""Calculate and format session duration as HH:MM:SS"""
|
|
149
|
+
if not ended_at_str:
|
|
150
|
+
return None
|
|
151
|
+
|
|
152
|
+
try:
|
|
153
|
+
created_at = datetime.fromisoformat(created_at_str.replace('Z', '+00:00'))
|
|
154
|
+
ended_at = datetime.fromisoformat(ended_at_str.replace('Z', '+00:00'))
|
|
155
|
+
duration = ended_at - created_at
|
|
156
|
+
|
|
157
|
+
# Convert to total seconds
|
|
158
|
+
total_seconds = int(duration.total_seconds())
|
|
159
|
+
|
|
160
|
+
# Calculate hours, minutes, seconds
|
|
161
|
+
hours = total_seconds // 3600
|
|
162
|
+
minutes = (total_seconds % 3600) // 60
|
|
163
|
+
seconds = total_seconds % 60
|
|
164
|
+
|
|
165
|
+
return f"{hours:02d}:{minutes:02d}:{seconds:02d}"
|
|
166
|
+
except Exception:
|
|
167
|
+
return None
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def calculate_percentiles(data: list[float]) -> tuple[float, float, float] | None:
|
|
171
|
+
"""
|
|
172
|
+
Calculate average, 5th percentile, and 95th percentile for a list of numeric data.
|
|
173
|
+
|
|
174
|
+
Args:
|
|
175
|
+
data: List of float values to analyze
|
|
176
|
+
|
|
177
|
+
Returns:
|
|
178
|
+
Tuple of (average, p5, p95) if data is provided, None if empty list.
|
|
179
|
+
For single values, p5 and p95 will equal the single value.
|
|
180
|
+
"""
|
|
181
|
+
if not data:
|
|
182
|
+
return None
|
|
183
|
+
|
|
184
|
+
avg = statistics.mean(data)
|
|
185
|
+
|
|
186
|
+
if len(data) == 1:
|
|
187
|
+
return avg, data[0], data[0]
|
|
188
|
+
|
|
189
|
+
sorted_data = sorted(data)
|
|
190
|
+
|
|
191
|
+
def percentile(data_list, p):
|
|
192
|
+
if not data_list:
|
|
193
|
+
return 0
|
|
194
|
+
k = (len(data_list) - 1) * p / 100
|
|
195
|
+
f = int(k)
|
|
196
|
+
c = k - f
|
|
197
|
+
if f + 1 < len(data_list):
|
|
198
|
+
return data_list[f] * (1 - c) + data_list[f + 1] * c
|
|
199
|
+
else:
|
|
200
|
+
return data_list[f]
|
|
201
|
+
|
|
202
|
+
p5 = percentile(sorted_data, 5)
|
|
203
|
+
p95 = percentile(sorted_data, 95)
|
|
204
|
+
|
|
205
|
+
return avg, p5, p95
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
async def cli_updates_available() -> str | None:
|
|
209
|
+
"""
|
|
210
|
+
Check if there are updates available for the CLI from PyPI.
|
|
211
|
+
"""
|
|
212
|
+
import aiohttp
|
|
213
|
+
|
|
214
|
+
try:
|
|
215
|
+
from importlib.metadata import version as get_version
|
|
216
|
+
current_version = get_version("pipecatcloud")
|
|
217
|
+
except ImportError:
|
|
218
|
+
return None
|
|
219
|
+
|
|
220
|
+
pypi_url = "https://pypi.org/pypi/pipecatcloud/json"
|
|
221
|
+
|
|
222
|
+
try:
|
|
223
|
+
async with aiohttp.ClientSession() as session:
|
|
224
|
+
response = await session.get(pypi_url)
|
|
225
|
+
if not response.ok:
|
|
226
|
+
return None
|
|
227
|
+
|
|
228
|
+
data = await response.json()
|
|
229
|
+
latest_version = data["info"]["version"]
|
|
230
|
+
|
|
231
|
+
if latest_version > current_version:
|
|
232
|
+
return latest_version
|
|
233
|
+
return None
|
|
234
|
+
|
|
235
|
+
except Exception:
|
|
236
|
+
return None
|
|
@@ -428,3 +428,11 @@ class _API:
|
|
|
428
428
|
@property
|
|
429
429
|
def agent_logs(self):
|
|
430
430
|
return self.create_api_method(self._agent_logs)
|
|
431
|
+
|
|
432
|
+
async def _agent_sessions(self, agent_name: str, org: str) -> dict | None:
|
|
433
|
+
url = f"{self.construct_api_url('services_sessions_path').format(org=org, service=agent_name)}"
|
|
434
|
+
return await self._base_request("GET", url) or {}
|
|
435
|
+
|
|
436
|
+
@property
|
|
437
|
+
def agent_sessions(self):
|
|
438
|
+
return self.create_api_method(self._agent_sessions)
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
#
|
|
6
6
|
|
|
7
7
|
import json
|
|
8
|
+
import statistics
|
|
8
9
|
from enum import Enum
|
|
9
10
|
|
|
10
11
|
import aiohttp
|
|
@@ -21,7 +22,7 @@ from rich.text import Text
|
|
|
21
22
|
|
|
22
23
|
from pipecatcloud._utils.async_utils import synchronizer
|
|
23
24
|
from pipecatcloud._utils.auth_utils import requires_login
|
|
24
|
-
from pipecatcloud._utils.console_utils import console, format_timestamp
|
|
25
|
+
from pipecatcloud._utils.console_utils import console, format_duration, format_timestamp, calculate_percentiles
|
|
25
26
|
from pipecatcloud.cli import PIPECAT_CLI_NAME
|
|
26
27
|
from pipecatcloud.cli.api import API
|
|
27
28
|
from pipecatcloud.cli.config import config
|
|
@@ -200,18 +201,31 @@ async def status(
|
|
|
200
201
|
@agent_cli.command(name="sessions", help="List active sessions for an agent")
|
|
201
202
|
@synchronizer.create_blocking
|
|
202
203
|
@requires_login
|
|
204
|
+
@with_deploy_config
|
|
203
205
|
async def sessions(
|
|
204
|
-
|
|
206
|
+
deploy_config=typer.Option(None, hidden=True),
|
|
207
|
+
agent_name: str = typer.Argument(
|
|
208
|
+
None,
|
|
209
|
+
help="Name of the agent to list sessions for e.g. 'my-agent'",
|
|
210
|
+
show_default=False
|
|
211
|
+
),
|
|
212
|
+
session_id: str = typer.Option(
|
|
213
|
+
None,
|
|
214
|
+
"--id",
|
|
215
|
+
"-i",
|
|
216
|
+
help="Session ID to filter by",
|
|
217
|
+
),
|
|
205
218
|
organization: str = typer.Option(
|
|
206
219
|
None, "--organization", "-o", help="Organization to list sessions for"
|
|
207
220
|
),
|
|
208
221
|
):
|
|
209
222
|
org = organization or config.get("org")
|
|
223
|
+
agent_name = agent_name or deploy_config.agent_name
|
|
210
224
|
|
|
211
225
|
with Live(
|
|
212
226
|
console.status(f"[dim]Looking up agent with name '{agent_name}'[/dim]", spinner="dots")
|
|
213
227
|
) as live:
|
|
214
|
-
data, error = await API.
|
|
228
|
+
data, error = await API.agent_sessions(agent_name=agent_name, org=org, live=live)
|
|
215
229
|
|
|
216
230
|
live.stop()
|
|
217
231
|
|
|
@@ -219,24 +233,104 @@ async def sessions(
|
|
|
219
233
|
return typer.Exit()
|
|
220
234
|
|
|
221
235
|
if not data:
|
|
222
|
-
console.error(f"No
|
|
236
|
+
console.error(f"No session data found for agent with name '{agent_name}'")
|
|
223
237
|
return typer.Exit()
|
|
224
238
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
)
|
|
239
|
+
sessions_list = data.get("sessions", [])
|
|
240
|
+
total_sessions = len(sessions_list)
|
|
228
241
|
|
|
229
|
-
if
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
242
|
+
completed_sessions = [s for s in sessions_list if s.get("endedAt")]
|
|
243
|
+
|
|
244
|
+
durations = []
|
|
245
|
+
for session in completed_sessions:
|
|
246
|
+
try:
|
|
247
|
+
from datetime import datetime
|
|
248
|
+
created_at = datetime.fromisoformat(session["createdAt"].replace('Z', '+00:00'))
|
|
249
|
+
ended_at = datetime.fromisoformat(session["endedAt"].replace('Z', '+00:00'))
|
|
250
|
+
duration_seconds = (ended_at - created_at).total_seconds()
|
|
251
|
+
durations.append(duration_seconds)
|
|
252
|
+
except BaseException:
|
|
253
|
+
continue
|
|
254
|
+
|
|
255
|
+
bot_start_times = [s["botStartSeconds"]
|
|
256
|
+
for s in sessions_list if s.get("botStartSeconds") is not None]
|
|
257
|
+
bot_start_metrics = calculate_percentiles(bot_start_times)
|
|
258
|
+
duration_metrics = calculate_percentiles(durations)
|
|
259
|
+
cold_starts_count = sum(1 for s in sessions_list if s.get("coldStart") is True)
|
|
260
|
+
cold_start_percent = cold_starts_count / total_sessions * 100
|
|
261
|
+
|
|
262
|
+
if duration_metrics and bot_start_metrics and total_sessions > 0:
|
|
263
|
+
metric_renderables = [
|
|
264
|
+
Panel(
|
|
265
|
+
f"[bold]Total Sessions:[/bold]\n{total_sessions}\n ",
|
|
266
|
+
expand=True,
|
|
267
|
+
),
|
|
268
|
+
Panel(
|
|
269
|
+
f"[bold]Average Duration:[/bold]\n{duration_metrics[0]:.1f}s\n[dim](p5: {duration_metrics[1]:.1f}s, p95: {duration_metrics[2]:.1f}s)[/dim]",
|
|
270
|
+
expand=True,
|
|
271
|
+
),
|
|
272
|
+
Panel(
|
|
273
|
+
f"[bold]Bot Start Time:[/bold]\n{bot_start_metrics[0]:.1f}s\n[dim](p5: {bot_start_metrics[1]:.1f}s, p95: {bot_start_metrics[2]:.1f}s)[/dim]",
|
|
274
|
+
expand=True,
|
|
275
|
+
),
|
|
276
|
+
Panel(
|
|
277
|
+
f"[bold]Cold Starts:[/bold]\n{cold_starts_count}/{total_sessions}\n[dim]({cold_start_percent:.1f}%)[/dim]",
|
|
278
|
+
expand=True,
|
|
279
|
+
),
|
|
280
|
+
]
|
|
281
|
+
|
|
282
|
+
table = Table(show_header=True, show_lines=True, border_style="dim", box=box.SIMPLE)
|
|
283
|
+
table.add_column("Session ID")
|
|
284
|
+
table.add_column("Created At")
|
|
285
|
+
table.add_column("Ended At")
|
|
286
|
+
table.add_column("Duration")
|
|
287
|
+
table.add_column("Status")
|
|
288
|
+
table.add_column("Bot Start Time")
|
|
289
|
+
table.add_column("Cold Start")
|
|
290
|
+
|
|
291
|
+
for session in data.get("sessions", []):
|
|
292
|
+
if session_id and session["sessionId"] != session_id:
|
|
293
|
+
continue
|
|
294
|
+
|
|
295
|
+
session_duration = format_duration(
|
|
296
|
+
session["createdAt"], session["endedAt"]) or "[dim]N/A[/dim]"
|
|
297
|
+
status = session.get("completionStatus", "")
|
|
298
|
+
if session["endedAt"]:
|
|
299
|
+
if status == "500":
|
|
300
|
+
status_display = "[red]Error (500)[/red]"
|
|
301
|
+
else:
|
|
302
|
+
status_display = "Complete"
|
|
303
|
+
else:
|
|
304
|
+
status_display = "[yellow]Active[/yellow]"
|
|
305
|
+
|
|
306
|
+
is_cold_start = session["coldStart"] is True
|
|
307
|
+
row_style = "on red" if is_cold_start else ""
|
|
308
|
+
|
|
309
|
+
row_data = [
|
|
310
|
+
session["sessionId"],
|
|
311
|
+
format_timestamp(
|
|
312
|
+
session["createdAt"]),
|
|
313
|
+
format_timestamp(
|
|
314
|
+
session["endedAt"]) if session["endedAt"] else "[dim]N/A[/dim]",
|
|
315
|
+
session_duration,
|
|
316
|
+
status_display,
|
|
317
|
+
f"{session['botStartSeconds']}s" if session["botStartSeconds"] is not None else "[dim]N/A[/dim]",
|
|
318
|
+
"[red]Yes[/red]" if session["coldStart"] is True else "No" if session["coldStart"] is False else "[dim]N/A[/dim]",
|
|
319
|
+
]
|
|
320
|
+
|
|
321
|
+
if is_cold_start:
|
|
322
|
+
row_data = [f"[{row_style}]{cell}[/]" for cell in row_data]
|
|
323
|
+
|
|
324
|
+
table.add_row(*row_data)
|
|
325
|
+
|
|
326
|
+
console.success(
|
|
327
|
+
Group(
|
|
328
|
+
Columns(
|
|
329
|
+
metric_renderables,
|
|
330
|
+
equal=True) if metric_renderables and not session_id else "",
|
|
331
|
+
table),
|
|
332
|
+
title=f"Session data for agent {agent_name} [dim]({org})[/dim]",
|
|
333
|
+
)
|
|
240
334
|
|
|
241
335
|
|
|
242
336
|
@agent_cli.command(name="scale", help="Modify agent runtime configuration")
|
|
@@ -232,6 +232,14 @@ def create_deploy_command(app: typer.Typer):
|
|
|
232
232
|
image: str = typer.Argument(
|
|
233
233
|
None, help="Docker image location e.g. 'my-image:latest'", show_default=False
|
|
234
234
|
),
|
|
235
|
+
credentials: str = typer.Option(
|
|
236
|
+
None,
|
|
237
|
+
"--credentials",
|
|
238
|
+
"-c",
|
|
239
|
+
help="Image pull secret to use for deployment",
|
|
240
|
+
rich_help_panel="Deployment Configuration",
|
|
241
|
+
show_default=False,
|
|
242
|
+
),
|
|
235
243
|
min_agents: int = typer.Option(
|
|
236
244
|
None,
|
|
237
245
|
"--min-agents",
|
|
@@ -263,13 +271,7 @@ def create_deploy_command(app: typer.Typer):
|
|
|
263
271
|
help="Organization to deploy to",
|
|
264
272
|
rich_help_panel="Deployment Configuration",
|
|
265
273
|
),
|
|
266
|
-
|
|
267
|
-
None,
|
|
268
|
-
"--credentials",
|
|
269
|
-
"-c",
|
|
270
|
-
help="Image pull secret to use for deployment",
|
|
271
|
-
rich_help_panel="Deployment Configuration",
|
|
272
|
-
),
|
|
274
|
+
|
|
273
275
|
krisp: bool = typer.Option(
|
|
274
276
|
False,
|
|
275
277
|
"--enable-krisp",
|
|
@@ -282,6 +284,7 @@ def create_deploy_command(app: typer.Typer):
|
|
|
282
284
|
"--force",
|
|
283
285
|
"-f",
|
|
284
286
|
help="Force deployment / skip confirmation",
|
|
287
|
+
rich_help_panel="Additional Options",
|
|
285
288
|
),
|
|
286
289
|
# @deprecated
|
|
287
290
|
min_instances: int = typer.Option(
|
|
@@ -345,6 +348,25 @@ def create_deploy_command(app: typer.Typer):
|
|
|
345
348
|
console.error("Image / repository URL is required")
|
|
346
349
|
return typer.Exit()
|
|
347
350
|
|
|
351
|
+
# Show a warning if no credentials are provided
|
|
352
|
+
if not partial_config.image_credentials and not skip_confirm:
|
|
353
|
+
console.print(
|
|
354
|
+
Panel(
|
|
355
|
+
"[yellow]You are attempting to deploy an agent image without an image pull secret."
|
|
356
|
+
"\n\nUse [bold][--credentials][/bold] to provide a image pull secret."
|
|
357
|
+
"\n\nPlease be aware that future versions of the Pipecat Cloud CLI will require credentials for all deployments (unless forced manually).",
|
|
358
|
+
title="[bold yellow]Warning: Deploying without credentials[/bold yellow]",
|
|
359
|
+
title_align="left",
|
|
360
|
+
subtitle_align="left",
|
|
361
|
+
subtitle="[dim]Learn more: https://docs.pipecat.daily.co/agents/secrets#image-pull-secrets[/dim]",
|
|
362
|
+
border_style="yellow",
|
|
363
|
+
))
|
|
364
|
+
if not typer.confirm(
|
|
365
|
+
"Are you sure you want to deploy without an image pull secret?",
|
|
366
|
+
default=False,
|
|
367
|
+
):
|
|
368
|
+
return typer.Exit()
|
|
369
|
+
|
|
348
370
|
# Create and display table
|
|
349
371
|
table = Table(show_header=False, border_style="dim", show_edge=True, show_lines=True)
|
|
350
372
|
table.add_column("Property", style="cyan")
|
|
@@ -30,6 +30,7 @@ _SETTINGS = {
|
|
|
30
30
|
"services_path": _Setting("/v1/organizations/{org}/services"),
|
|
31
31
|
"services_logs_path": _Setting("/v1/organizations/{org}/services/{service}/logs"),
|
|
32
32
|
"services_deployments_path": _Setting("/v1/organizations/{org}/services/{service}/deployments"),
|
|
33
|
+
"services_sessions_path": _Setting("/v1/organizations/{org}/services/{service}/sessions"),
|
|
33
34
|
"start_path": _Setting("/v1/public/{service}/start"),
|
|
34
35
|
"api_keys_path": _Setting("/v1/organizations/{org}/apiKeys"),
|
|
35
36
|
"secrets_path": _Setting("/v1/organizations/{org}/secrets"),
|
|
File without changes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pipecatcloud
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.8
|
|
4
4
|
Summary: Cloud hosting for Pipecat AI applications
|
|
5
5
|
License: BSD 2-Clause License
|
|
6
6
|
Project-URL: Website, https://docs.pipecat.daily.co/
|
|
@@ -13,7 +13,7 @@ Requires-Dist: fastapi~=0.115.6
|
|
|
13
13
|
Requires-Dist: loguru~=0.7.3
|
|
14
14
|
Requires-Dist: python-dotenv~=1.0.1
|
|
15
15
|
Requires-Dist: uvicorn~=0.32.0
|
|
16
|
-
Requires-Dist: typer~=0.
|
|
16
|
+
Requires-Dist: typer~=0.15.3
|
|
17
17
|
Requires-Dist: synchronicity~=0.7.6
|
|
18
18
|
Requires-Dist: toml~=0.10.2
|
|
19
19
|
Requires-Dist: questionary~=2.1.0
|
|
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
|