pipecatcloud 0.2.16__tar.gz → 0.2.17__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.2.17/.github/workflows/format.yml +43 -0
- pipecatcloud-0.2.17/.pre-commit-config.yaml +8 -0
- {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/CHANGELOG.md +7 -0
- {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/PKG-INFO +38 -1
- {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/README.md +37 -0
- pipecatcloud-0.2.17/scripts/fix-ruff.sh +10 -0
- pipecatcloud-0.2.17/scripts/pre-commit.sh +27 -0
- {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud/__version__.py +2 -0
- {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud/_utils/console_utils.py +11 -7
- {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud/api.py +6 -2
- {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud/cli/commands/deploy.py +6 -3
- {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud/cli/commands/organizations.py +9 -3
- {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud/cli/commands/secrets.py +3 -1
- {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud/smallwebrtc/session_manager.py +3 -1
- {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud.egg-info/PKG-INFO +38 -1
- {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud.egg-info/SOURCES.txt +4 -0
- pipecatcloud-0.2.17/tests/__init__.py +1 -0
- {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/tests/test_agent_stop.py +20 -19
- {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/tests/test_api_client.py +8 -23
- {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/tests/test_krisp_viva.py +1 -0
- {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/tests/test_managed_keys.py +1 -0
- {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/tests/test_regions.py +35 -36
- pipecatcloud-0.2.16/tests/__init__.py +0 -1
- {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/.github/workflows/publish-pypi.yml +0 -0
- {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/.github/workflows/publish-test.yml +0 -0
- {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/.github/workflows/tests.yml +0 -0
- {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/.gitignore +0 -0
- {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/LICENSE +0 -0
- {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/pipecat-cloud.png +0 -0
- {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/pyproject.toml +0 -0
- {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/pytest.ini +0 -0
- {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/setup.cfg +0 -0
- {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud/__init__.py +0 -0
- {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud/__main__.py +0 -0
- {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud/_utils/__init__.py +0 -0
- {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud/_utils/async_utils.py +0 -0
- {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud/_utils/auth_utils.py +0 -0
- {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud/_utils/deploy_utils.py +0 -0
- {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud/_utils/regions.py +0 -0
- {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud/agent.py +0 -0
- {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud/cli/__init__.py +0 -0
- {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud/cli/api.py +0 -0
- {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud/cli/commands/__init__.py +0 -0
- {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud/cli/commands/agent.py +0 -0
- {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud/cli/commands/auth.py +0 -0
- {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud/cli/commands/docker.py +0 -0
- {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud/cli/commands/regions.py +0 -0
- {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud/cli/config.py +0 -0
- {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud/cli/entry_point.py +0 -0
- {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud/config.py +0 -0
- {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud/constants.py +0 -0
- {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud/exception.py +0 -0
- {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud/py.typed +0 -0
- {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud/session.py +0 -0
- {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud.egg-info/dependency_links.txt +0 -0
- {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud.egg-info/entry_points.txt +0 -0
- {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud.egg-info/requires.txt +0 -0
- {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud.egg-info/top_level.txt +0 -0
- {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/tests/test_agent_sessions.py +0 -0
- {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/tests/test_docker_commands.py +0 -0
- {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/uv.lock +0 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
name: format
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
push:
|
|
6
|
+
branches:
|
|
7
|
+
- main
|
|
8
|
+
pull_request:
|
|
9
|
+
branches:
|
|
10
|
+
- '**'
|
|
11
|
+
paths-ignore:
|
|
12
|
+
- 'docs/**'
|
|
13
|
+
|
|
14
|
+
concurrency:
|
|
15
|
+
group: build-format-${{ github.event.pull_request.number || github.ref }}
|
|
16
|
+
cancel-in-progress: true
|
|
17
|
+
|
|
18
|
+
jobs:
|
|
19
|
+
ruff-format:
|
|
20
|
+
name: 'Code quality checks'
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
steps:
|
|
23
|
+
- name: Checkout repo
|
|
24
|
+
uses: actions/checkout@v4
|
|
25
|
+
|
|
26
|
+
- name: Install uv
|
|
27
|
+
uses: astral-sh/setup-uv@v3
|
|
28
|
+
with:
|
|
29
|
+
version: 'latest'
|
|
30
|
+
|
|
31
|
+
- name: Set up Python
|
|
32
|
+
run: uv python install 3.12
|
|
33
|
+
|
|
34
|
+
- name: Install development dependencies
|
|
35
|
+
run: uv sync --group dev
|
|
36
|
+
|
|
37
|
+
- name: Ruff formatter
|
|
38
|
+
id: ruff-format
|
|
39
|
+
run: uv run ruff format --diff
|
|
40
|
+
|
|
41
|
+
- name: Ruff linter (all rules)
|
|
42
|
+
id: ruff-check
|
|
43
|
+
run: uv run ruff check
|
|
@@ -5,6 +5,13 @@ All notable changes to **Pipecat Cloud** will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.2.17] - 2025-12-26
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- Improved the deployment readiness check to use the latest statuses available
|
|
13
|
+
from the Pipecat Cloud backend.
|
|
14
|
+
|
|
8
15
|
## [0.2.16] - 2025-12-18
|
|
9
16
|
|
|
10
17
|
### Fixed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pipecatcloud
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.17
|
|
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/
|
|
@@ -96,3 +96,40 @@ async def main():
|
|
|
96
96
|
if __name__ == "__main__":
|
|
97
97
|
asyncio.run(main())
|
|
98
98
|
```
|
|
99
|
+
|
|
100
|
+
## 🛠️ Contributing
|
|
101
|
+
|
|
102
|
+
### Setup Steps
|
|
103
|
+
|
|
104
|
+
1. Clone the repository and navigate to it:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
git clone https://github.com/daily-co/pipecat-cloud.git
|
|
108
|
+
cd pipecat-cloud
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
2. Install development and testing dependencies:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
uv sync --group dev
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
3. Install the git pre-commit hooks:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
uv run pre-commit install
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Running tests
|
|
124
|
+
|
|
125
|
+
To run all tests, from the root directory:
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
uv run pytest
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Run a specific test suite:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
uv run pytest tests/test_name.py
|
|
135
|
+
```
|
|
@@ -73,3 +73,40 @@ async def main():
|
|
|
73
73
|
if __name__ == "__main__":
|
|
74
74
|
asyncio.run(main())
|
|
75
75
|
```
|
|
76
|
+
|
|
77
|
+
## 🛠️ Contributing
|
|
78
|
+
|
|
79
|
+
### Setup Steps
|
|
80
|
+
|
|
81
|
+
1. Clone the repository and navigate to it:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
git clone https://github.com/daily-co/pipecat-cloud.git
|
|
85
|
+
cd pipecat-cloud
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
2. Install development and testing dependencies:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
uv sync --group dev
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
3. Install the git pre-commit hooks:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
uv run pre-commit install
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Running tests
|
|
101
|
+
|
|
102
|
+
To run all tests, from the root directory:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
uv run pytest
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Run a specific test suite:
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
uv run pytest tests/test_name.py
|
|
112
|
+
```
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
|
|
2
|
+
#!/bin/bash
|
|
3
|
+
|
|
4
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
5
|
+
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
|
|
6
|
+
|
|
7
|
+
echo "Running ruff format..."
|
|
8
|
+
uv run ruff format "$PROJECT_ROOT"
|
|
9
|
+
echo "Running ruff check..."
|
|
10
|
+
uv run ruff check --fix "$PROJECT_ROOT"
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Color codes for output
|
|
4
|
+
RED='\033[0;31m'
|
|
5
|
+
GREEN='\033[0;32m'
|
|
6
|
+
NC='\033[0m' # No Color
|
|
7
|
+
|
|
8
|
+
echo "🔍 Running pre-commit checks..."
|
|
9
|
+
|
|
10
|
+
# Change to project root (one level up from scripts/)
|
|
11
|
+
cd "$(dirname "$0")/.."
|
|
12
|
+
|
|
13
|
+
# Format check
|
|
14
|
+
echo "📝 Checking code formatting..."
|
|
15
|
+
if ! NO_COLOR=1 uv run ruff format --diff --check; then
|
|
16
|
+
echo -e "${RED}❌ Code formatting issues found. Run 'uv run ruff format' to fix.${NC}"
|
|
17
|
+
exit 1
|
|
18
|
+
fi
|
|
19
|
+
|
|
20
|
+
# Lint check
|
|
21
|
+
echo "🔍 Running linter..."
|
|
22
|
+
if ! uv run ruff check; then
|
|
23
|
+
echo -e "${RED}❌ Linting issues found.${NC}"
|
|
24
|
+
exit 1
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
echo -e "${GREEN}✅ All pre-commit checks passed!${NC}"
|
|
@@ -6,11 +6,13 @@
|
|
|
6
6
|
|
|
7
7
|
try:
|
|
8
8
|
from importlib.metadata import version as get_version
|
|
9
|
+
|
|
9
10
|
version = get_version("pipecatcloud")
|
|
10
11
|
except ImportError:
|
|
11
12
|
# For Python < 3.8
|
|
12
13
|
try:
|
|
13
14
|
from importlib_metadata import version as get_version
|
|
15
|
+
|
|
14
16
|
version = get_version("pipecatcloud")
|
|
15
17
|
except ImportError:
|
|
16
18
|
version = "Unknown"
|
|
@@ -69,7 +69,8 @@ class PipecatConsole(Console):
|
|
|
69
69
|
title_align="left",
|
|
70
70
|
subtitle_align="left",
|
|
71
71
|
border_style="red",
|
|
72
|
-
)
|
|
72
|
+
)
|
|
73
|
+
)
|
|
73
74
|
|
|
74
75
|
def api_error(
|
|
75
76
|
self,
|
|
@@ -80,9 +81,11 @@ class PipecatConsole(Console):
|
|
|
80
81
|
DEFAULT_ERROR_MESSAGE = "Unknown error. Please contact support."
|
|
81
82
|
|
|
82
83
|
if isinstance(error_code, dict):
|
|
83
|
-
error_message =
|
|
84
|
-
"error", None)
|
|
85
|
-
"message", None)
|
|
84
|
+
error_message = (
|
|
85
|
+
error_code.get("error", None)
|
|
86
|
+
or error_code.get("message", None)
|
|
87
|
+
or DEFAULT_ERROR_MESSAGE
|
|
88
|
+
)
|
|
86
89
|
code = error_code.get("code")
|
|
87
90
|
else:
|
|
88
91
|
error_message = str(error_code) if error_code else DEFAULT_ERROR_MESSAGE
|
|
@@ -93,7 +96,7 @@ class PipecatConsole(Console):
|
|
|
93
96
|
|
|
94
97
|
self.print(
|
|
95
98
|
Panel(
|
|
96
|
-
f"[red]{title}[/red]\n\n
|
|
99
|
+
f"[red]{title}[/red]\n\n[dim]Error message:[/dim]\n{error_message}",
|
|
97
100
|
title=f"[bold red]{PANEL_TITLE_ERROR}{f' - {code}' if code else ''}[/bold red]",
|
|
98
101
|
subtitle=f"[dim]Docs: https://docs.pipecat.daily.co/agents/error-codes#{code}[/dim]"
|
|
99
102
|
if not hide_subtitle and code
|
|
@@ -150,8 +153,8 @@ def format_duration(created_at_str: str, ended_at_str: str) -> str | None:
|
|
|
150
153
|
return None
|
|
151
154
|
|
|
152
155
|
try:
|
|
153
|
-
created_at = datetime.fromisoformat(created_at_str.replace(
|
|
154
|
-
ended_at = datetime.fromisoformat(ended_at_str.replace(
|
|
156
|
+
created_at = datetime.fromisoformat(created_at_str.replace("Z", "+00:00"))
|
|
157
|
+
ended_at = datetime.fromisoformat(ended_at_str.replace("Z", "+00:00"))
|
|
155
158
|
duration = ended_at - created_at
|
|
156
159
|
|
|
157
160
|
# Convert to total seconds
|
|
@@ -213,6 +216,7 @@ async def cli_updates_available() -> str | None:
|
|
|
213
216
|
|
|
214
217
|
try:
|
|
215
218
|
from importlib.metadata import version as get_version
|
|
219
|
+
|
|
216
220
|
current_version = get_version("pipecatcloud")
|
|
217
221
|
except ImportError:
|
|
218
222
|
return None
|
|
@@ -236,7 +236,9 @@ class _API:
|
|
|
236
236
|
|
|
237
237
|
# Secret
|
|
238
238
|
|
|
239
|
-
async def _secrets_list(
|
|
239
|
+
async def _secrets_list(
|
|
240
|
+
self, org: str, secret_set: Optional[str] = None, region: Optional[str] = None
|
|
241
|
+
) -> dict | None:
|
|
240
242
|
if secret_set:
|
|
241
243
|
url = f"{self.construct_api_url('secrets_path').format(org=org)}/{secret_set}"
|
|
242
244
|
else:
|
|
@@ -266,7 +268,9 @@ class _API:
|
|
|
266
268
|
"""
|
|
267
269
|
return self.create_api_method(self._secrets_list)
|
|
268
270
|
|
|
269
|
-
async def _secrets_upsert(
|
|
271
|
+
async def _secrets_upsert(
|
|
272
|
+
self, data: dict, set_name: str, org: str, region: Optional[str] = None
|
|
273
|
+
) -> dict:
|
|
270
274
|
url = f"{self.construct_api_url('secrets_path').format(org=org)}/{set_name}"
|
|
271
275
|
|
|
272
276
|
# Add region to data payload only if explicitly provided
|
|
@@ -187,11 +187,14 @@ async def _deploy(params: DeployConfigParams, org, force: bool = False):
|
|
|
187
187
|
# @TODO - Implement this
|
|
188
188
|
|
|
189
189
|
# Check if deployment is ready
|
|
190
|
-
#
|
|
191
|
-
|
|
190
|
+
# For KEDA deployments, we need:
|
|
191
|
+
# - available: true (can handle traffic)
|
|
192
|
+
# - activeDeploymentReady: true (ReplicaSet is ready)
|
|
193
|
+
# The 'available' field falls back to 'ready' if not present (for backwards compatibility)
|
|
194
|
+
available = agent_status.get("available", agent_status.get("ready", False))
|
|
192
195
|
deployment_ready = agent_status.get("activeDeploymentReady", False)
|
|
193
196
|
|
|
194
|
-
if
|
|
197
|
+
if available and deployment_ready:
|
|
195
198
|
is_ready = True
|
|
196
199
|
break
|
|
197
200
|
|
|
@@ -25,7 +25,9 @@ organization_cli = typer.Typer(
|
|
|
25
25
|
name="organizations", help="User organizations", no_args_is_help=True
|
|
26
26
|
)
|
|
27
27
|
keys_cli = typer.Typer(name="keys", help="API key management commands", no_args_is_help=True)
|
|
28
|
-
properties_cli = typer.Typer(
|
|
28
|
+
properties_cli = typer.Typer(
|
|
29
|
+
name="properties", help="Organization property management", no_args_is_help=True
|
|
30
|
+
)
|
|
29
31
|
organization_cli.add_typer(keys_cli)
|
|
30
32
|
organization_cli.add_typer(properties_cli)
|
|
31
33
|
|
|
@@ -528,7 +530,9 @@ async def properties_set(
|
|
|
528
530
|
# ---- Convenience Commands ----
|
|
529
531
|
|
|
530
532
|
|
|
531
|
-
@organization_cli.command(
|
|
533
|
+
@organization_cli.command(
|
|
534
|
+
name="default-region", help="Get or set the default region for an organization."
|
|
535
|
+
)
|
|
532
536
|
@synchronizer.create_blocking
|
|
533
537
|
@requires_login
|
|
534
538
|
async def default_region(
|
|
@@ -557,7 +561,9 @@ async def default_region(
|
|
|
557
561
|
console.error("Failed to update default region.")
|
|
558
562
|
return typer.Exit(1)
|
|
559
563
|
|
|
560
|
-
console.success(
|
|
564
|
+
console.success(
|
|
565
|
+
f"Default region set to [bold green]{data.get('defaultRegion', region)}[/bold green]"
|
|
566
|
+
)
|
|
561
567
|
else:
|
|
562
568
|
# Show the current default region
|
|
563
569
|
with console.status(
|
|
@@ -211,7 +211,9 @@ async def set(
|
|
|
211
211
|
props, error = await API.properties(org)
|
|
212
212
|
if error:
|
|
213
213
|
return typer.Exit()
|
|
214
|
-
console.print(
|
|
214
|
+
console.print(
|
|
215
|
+
f"[bold white]Region:[/bold white] {props['defaultRegion']} [dim](organization default)[/dim]\n"
|
|
216
|
+
)
|
|
215
217
|
console.print(
|
|
216
218
|
Panel(
|
|
217
219
|
table,
|
|
@@ -34,7 +34,9 @@ class SmallWebRTCSessionManager:
|
|
|
34
34
|
await asyncio.sleep(self._timeout_seconds)
|
|
35
35
|
if self._pending_future and not self._pending_future.done():
|
|
36
36
|
self._pending_future.set_exception(
|
|
37
|
-
TimeoutError(
|
|
37
|
+
TimeoutError(
|
|
38
|
+
f"WebRTC connection not received within {self._timeout_seconds} seconds"
|
|
39
|
+
)
|
|
38
40
|
)
|
|
39
41
|
|
|
40
42
|
# Create and store the timeout task
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pipecatcloud
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.17
|
|
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/
|
|
@@ -96,3 +96,40 @@ async def main():
|
|
|
96
96
|
if __name__ == "__main__":
|
|
97
97
|
asyncio.run(main())
|
|
98
98
|
```
|
|
99
|
+
|
|
100
|
+
## 🛠️ Contributing
|
|
101
|
+
|
|
102
|
+
### Setup Steps
|
|
103
|
+
|
|
104
|
+
1. Clone the repository and navigate to it:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
git clone https://github.com/daily-co/pipecat-cloud.git
|
|
108
|
+
cd pipecat-cloud
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
2. Install development and testing dependencies:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
uv sync --group dev
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
3. Install the git pre-commit hooks:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
uv run pre-commit install
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Running tests
|
|
124
|
+
|
|
125
|
+
To run all tests, from the root directory:
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
uv run pytest
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Run a specific test suite:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
uv run pytest tests/test_name.py
|
|
135
|
+
```
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
.gitignore
|
|
2
|
+
.pre-commit-config.yaml
|
|
2
3
|
CHANGELOG.md
|
|
3
4
|
LICENSE
|
|
4
5
|
README.md
|
|
@@ -6,9 +7,12 @@ pipecat-cloud.png
|
|
|
6
7
|
pyproject.toml
|
|
7
8
|
pytest.ini
|
|
8
9
|
uv.lock
|
|
10
|
+
.github/workflows/format.yml
|
|
9
11
|
.github/workflows/publish-pypi.yml
|
|
10
12
|
.github/workflows/publish-test.yml
|
|
11
13
|
.github/workflows/tests.yml
|
|
14
|
+
scripts/fix-ruff.sh
|
|
15
|
+
scripts/pre-commit.sh
|
|
12
16
|
src/pipecatcloud/__init__.py
|
|
13
17
|
src/pipecatcloud/__main__.py
|
|
14
18
|
src/pipecatcloud/__version__.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Test package for pipecatcloud
|
|
@@ -4,13 +4,13 @@ Unit tests for the 'pcc agent stop' command.
|
|
|
4
4
|
Tests focus on core behaviors and edge cases, not implementation details.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
import pytest
|
|
8
|
-
import typer
|
|
9
|
-
from unittest.mock import patch, AsyncMock, MagicMock
|
|
10
|
-
|
|
11
7
|
# Import from source, not installed package
|
|
12
8
|
import sys
|
|
13
9
|
from pathlib import Path
|
|
10
|
+
from unittest.mock import AsyncMock, MagicMock, patch
|
|
11
|
+
|
|
12
|
+
import typer
|
|
13
|
+
|
|
14
14
|
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
|
|
15
15
|
|
|
16
16
|
from pipecatcloud.cli.commands.agent import stop
|
|
@@ -26,11 +26,11 @@ class TestAgentStopCommand:
|
|
|
26
26
|
|
|
27
27
|
def test_stop_respects_force_flag(self):
|
|
28
28
|
"""Verify force flag skips confirmation when set to True."""
|
|
29
|
-
with
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
29
|
+
with (
|
|
30
|
+
patch("pipecatcloud.cli.commands.agent.config") as mock_config,
|
|
31
|
+
patch("pipecatcloud.cli.commands.agent.questionary") as mock_questionary,
|
|
32
|
+
patch("pipecatcloud.cli.commands.agent.DeployConfigParams") as mock_params,
|
|
33
|
+
):
|
|
34
34
|
mock_config.get.return_value = TEST_ORG
|
|
35
35
|
mock_params.return_value = MagicMock(agent_name=TEST_AGENT)
|
|
36
36
|
# Mock questionary - should NOT be called when force=True
|
|
@@ -51,11 +51,11 @@ class TestAgentStopCommand:
|
|
|
51
51
|
|
|
52
52
|
def test_stop_shows_confirmation_without_force(self):
|
|
53
53
|
"""Verify confirmation prompt is shown when force is False."""
|
|
54
|
-
with
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
54
|
+
with (
|
|
55
|
+
patch("pipecatcloud.cli.commands.agent.config") as mock_config,
|
|
56
|
+
patch("pipecatcloud.cli.commands.agent.questionary") as mock_questionary,
|
|
57
|
+
patch("pipecatcloud.cli.commands.agent.DeployConfigParams") as mock_params,
|
|
58
|
+
):
|
|
59
59
|
mock_config.get.return_value = TEST_ORG
|
|
60
60
|
mock_params.return_value = MagicMock(agent_name=TEST_AGENT)
|
|
61
61
|
# User agrees to the confirmation
|
|
@@ -76,11 +76,12 @@ class TestAgentStopCommand:
|
|
|
76
76
|
|
|
77
77
|
def test_stop_aborts_on_user_rejection(self):
|
|
78
78
|
"""Verify command aborts when user rejects the confirmation."""
|
|
79
|
-
with
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
79
|
+
with (
|
|
80
|
+
patch("pipecatcloud.cli.commands.agent.console") as mock_console,
|
|
81
|
+
patch("pipecatcloud.cli.commands.agent.config") as mock_config,
|
|
82
|
+
patch("pipecatcloud.cli.commands.agent.questionary") as mock_questionary,
|
|
83
|
+
patch("pipecatcloud.cli.commands.agent.DeployConfigParams") as mock_params,
|
|
84
|
+
):
|
|
84
85
|
mock_config.get.return_value = TEST_ORG
|
|
85
86
|
mock_params.return_value = MagicMock(agent_name=TEST_AGENT)
|
|
86
87
|
# User rejects the confirmation
|
|
@@ -12,6 +12,7 @@ import pytest
|
|
|
12
12
|
# Import from source, not installed package
|
|
13
13
|
import sys
|
|
14
14
|
from pathlib import Path
|
|
15
|
+
|
|
15
16
|
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
|
|
16
17
|
|
|
17
18
|
from pipecatcloud.api import _API
|
|
@@ -173,11 +174,7 @@ class TestAPIServicesRegions:
|
|
|
173
174
|
async def test_deploy_payload_includes_region(self, api_client):
|
|
174
175
|
"""Deploy payload should include region field when specified."""
|
|
175
176
|
# Arrange
|
|
176
|
-
config = DeployConfigParams(
|
|
177
|
-
agent_name="test-agent",
|
|
178
|
-
image="test:latest",
|
|
179
|
-
region="eu"
|
|
180
|
-
)
|
|
177
|
+
config = DeployConfigParams(agent_name="test-agent", image="test:latest", region="eu")
|
|
181
178
|
|
|
182
179
|
with patch.object(api_client, "_base_request", new_callable=AsyncMock) as mock_request:
|
|
183
180
|
mock_request.return_value = {"success": True}
|
|
@@ -194,11 +191,7 @@ class TestAPIServicesRegions:
|
|
|
194
191
|
async def test_deploy_payload_without_region(self, api_client):
|
|
195
192
|
"""Deploy payload should omit region field when not specified."""
|
|
196
193
|
# Arrange
|
|
197
|
-
config = DeployConfigParams(
|
|
198
|
-
agent_name="test-agent",
|
|
199
|
-
image="test:latest",
|
|
200
|
-
region=None
|
|
201
|
-
)
|
|
194
|
+
config = DeployConfigParams(agent_name="test-agent", image="test:latest", region=None)
|
|
202
195
|
|
|
203
196
|
with patch.object(api_client, "_base_request", new_callable=AsyncMock) as mock_request:
|
|
204
197
|
mock_request.return_value = {"success": True}
|
|
@@ -216,11 +209,7 @@ class TestAPIServicesRegions:
|
|
|
216
209
|
async def test_update_payload_includes_region(self, api_client):
|
|
217
210
|
"""Update (PUT) payload should include region field."""
|
|
218
211
|
# Arrange
|
|
219
|
-
config = DeployConfigParams(
|
|
220
|
-
agent_name="test-agent",
|
|
221
|
-
image="test:latest",
|
|
222
|
-
region="ap"
|
|
223
|
-
)
|
|
212
|
+
config = DeployConfigParams(agent_name="test-agent", image="test:latest", region="ap")
|
|
224
213
|
|
|
225
214
|
with patch.object(api_client, "_base_request", new_callable=AsyncMock) as mock_request:
|
|
226
215
|
mock_request.return_value = {"success": True}
|
|
@@ -247,10 +236,7 @@ class TestAPIRegionWithOtherParameters:
|
|
|
247
236
|
"""Deploy with both region and secrets should include both in payload."""
|
|
248
237
|
# Arrange
|
|
249
238
|
config = DeployConfigParams(
|
|
250
|
-
agent_name="test-agent",
|
|
251
|
-
image="test:latest",
|
|
252
|
-
region="eu",
|
|
253
|
-
secret_set="my-secrets"
|
|
239
|
+
agent_name="test-agent", image="test:latest", region="eu", secret_set="my-secrets"
|
|
254
240
|
)
|
|
255
241
|
|
|
256
242
|
with patch.object(api_client, "_base_request", new_callable=AsyncMock) as mock_request:
|
|
@@ -274,7 +260,7 @@ class TestAPIRegionWithOtherParameters:
|
|
|
274
260
|
region="ap",
|
|
275
261
|
secret_set="my-secrets",
|
|
276
262
|
image_credentials="my-creds",
|
|
277
|
-
enable_managed_keys=True
|
|
263
|
+
enable_managed_keys=True,
|
|
278
264
|
)
|
|
279
265
|
|
|
280
266
|
with patch.object(api_client, "_base_request", new_callable=AsyncMock) as mock_request:
|
|
@@ -324,7 +310,7 @@ class TestAPIProperties:
|
|
|
324
310
|
"readOnly": False,
|
|
325
311
|
"currentValue": "us-west",
|
|
326
312
|
"default": "us-west",
|
|
327
|
-
"availableValues": ["us-west", "eu-central"]
|
|
313
|
+
"availableValues": ["us-west", "eu-central"],
|
|
328
314
|
}
|
|
329
315
|
}
|
|
330
316
|
|
|
@@ -350,8 +336,7 @@ class TestAPIProperties:
|
|
|
350
336
|
|
|
351
337
|
# Act
|
|
352
338
|
result = await api_client._properties_update(
|
|
353
|
-
org="test-org",
|
|
354
|
-
properties={"defaultRegion": "eu-central"}
|
|
339
|
+
org="test-org", properties={"defaultRegion": "eu-central"}
|
|
355
340
|
)
|
|
356
341
|
|
|
357
342
|
# Assert
|
|
@@ -12,6 +12,7 @@ import pytest
|
|
|
12
12
|
# Import from source, not installed package
|
|
13
13
|
import sys
|
|
14
14
|
from pathlib import Path
|
|
15
|
+
|
|
15
16
|
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
|
|
16
17
|
|
|
17
18
|
from pipecatcloud._utils.deploy_utils import DeployConfigParams, load_deploy_config_file
|
|
@@ -8,10 +8,12 @@
|
|
|
8
8
|
Tests for dynamic region fetching and validation.
|
|
9
9
|
"""
|
|
10
10
|
|
|
11
|
-
import pytest
|
|
12
|
-
from unittest.mock import patch, AsyncMock
|
|
13
11
|
import sys
|
|
14
12
|
from pathlib import Path
|
|
13
|
+
from unittest.mock import AsyncMock, patch
|
|
14
|
+
|
|
15
|
+
import pytest
|
|
16
|
+
|
|
15
17
|
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
|
|
16
18
|
|
|
17
19
|
|
|
@@ -28,7 +30,7 @@ class TestRegionsAPI:
|
|
|
28
30
|
"regions": [
|
|
29
31
|
{"code": "us-west", "display_name": "US West"},
|
|
30
32
|
{"code": "eu-central", "display_name": "EU Central"},
|
|
31
|
-
{"code": "ap-south", "display_name": "AP South"}
|
|
33
|
+
{"code": "ap-south", "display_name": "AP South"},
|
|
32
34
|
]
|
|
33
35
|
}
|
|
34
36
|
|
|
@@ -44,7 +46,7 @@ class TestRegionsAPI:
|
|
|
44
46
|
assert result == [
|
|
45
47
|
{"code": "us-west", "display_name": "US West"},
|
|
46
48
|
{"code": "eu-central", "display_name": "EU Central"},
|
|
47
|
-
{"code": "ap-south", "display_name": "AP South"}
|
|
49
|
+
{"code": "ap-south", "display_name": "AP South"},
|
|
48
50
|
]
|
|
49
51
|
mock_request.assert_called_once()
|
|
50
52
|
|
|
@@ -63,11 +65,13 @@ class TestRegionsCaching:
|
|
|
63
65
|
|
|
64
66
|
mock_regions = [
|
|
65
67
|
{"code": "us-west", "display_name": "US West"},
|
|
66
|
-
{"code": "eu-central", "display_name": "EU Central"}
|
|
68
|
+
{"code": "eu-central", "display_name": "EU Central"},
|
|
67
69
|
]
|
|
68
70
|
|
|
69
|
-
with
|
|
70
|
-
|
|
71
|
+
with (
|
|
72
|
+
patch("pipecatcloud._utils.regions.API") as mock_api,
|
|
73
|
+
patch("pipecatcloud._utils.regions.config") as mock_config,
|
|
74
|
+
):
|
|
71
75
|
# API returns (data, error) tuple
|
|
72
76
|
mock_api.regions = AsyncMock(return_value=(mock_regions, None))
|
|
73
77
|
mock_config.get.return_value = "test-org"
|
|
@@ -99,11 +103,13 @@ class TestRegionValidation:
|
|
|
99
103
|
mock_regions = [
|
|
100
104
|
{"code": "us-west", "display_name": "US West"},
|
|
101
105
|
{"code": "eu-central", "display_name": "EU Central"},
|
|
102
|
-
{"code": "ap-south", "display_name": "AP South"}
|
|
106
|
+
{"code": "ap-south", "display_name": "AP South"},
|
|
103
107
|
]
|
|
104
108
|
|
|
105
|
-
with
|
|
106
|
-
|
|
109
|
+
with (
|
|
110
|
+
patch("pipecatcloud._utils.regions.API") as mock_api,
|
|
111
|
+
patch("pipecatcloud._utils.regions.config") as mock_config,
|
|
112
|
+
):
|
|
107
113
|
# API returns (data, error) tuple
|
|
108
114
|
mock_api.regions = AsyncMock(return_value=(mock_regions, None))
|
|
109
115
|
mock_config.get.return_value = "test-org"
|
|
@@ -123,11 +129,13 @@ class TestRegionValidation:
|
|
|
123
129
|
mock_regions = [
|
|
124
130
|
{"code": "us-west", "display_name": "US West"},
|
|
125
131
|
{"code": "eu-central", "display_name": "EU Central"},
|
|
126
|
-
{"code": "ap-south", "display_name": "AP South"}
|
|
132
|
+
{"code": "ap-south", "display_name": "AP South"},
|
|
127
133
|
]
|
|
128
134
|
|
|
129
|
-
with
|
|
130
|
-
|
|
135
|
+
with (
|
|
136
|
+
patch("pipecatcloud._utils.regions.API") as mock_api,
|
|
137
|
+
patch("pipecatcloud._utils.regions.config") as mock_config,
|
|
138
|
+
):
|
|
131
139
|
# API returns (data, error) tuple
|
|
132
140
|
mock_api.regions = AsyncMock(return_value=(mock_regions, None))
|
|
133
141
|
mock_config.get.return_value = "test-org"
|
|
@@ -145,55 +153,46 @@ class TestCLIRegionValidation:
|
|
|
145
153
|
async def test_secrets_set_rejects_invalid_region(self):
|
|
146
154
|
"""Secrets set should reject invalid regions with error message."""
|
|
147
155
|
# Arrange
|
|
148
|
-
from pipecatcloud.cli.commands.secrets import set as secrets_set
|
|
149
156
|
from pipecatcloud._utils import regions
|
|
150
157
|
|
|
151
158
|
regions._regions_cache = None
|
|
152
159
|
|
|
153
160
|
mock_regions = [
|
|
154
161
|
{"code": "us-west", "display_name": "US West"},
|
|
155
|
-
{"code": "eu-central", "display_name": "EU Central"}
|
|
162
|
+
{"code": "eu-central", "display_name": "EU Central"},
|
|
156
163
|
]
|
|
157
164
|
|
|
158
|
-
with
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
165
|
+
with (
|
|
166
|
+
patch("pipecatcloud._utils.regions.API") as mock_regions_api,
|
|
167
|
+
patch("pipecatcloud._utils.regions.config") as mock_config,
|
|
168
|
+
patch("pipecatcloud.cli.commands.secrets.API") as mock_api,
|
|
169
|
+
):
|
|
162
170
|
# API returns (data, error) tuple
|
|
163
171
|
mock_regions_api.regions = AsyncMock(return_value=(mock_regions, None))
|
|
164
172
|
mock_config.get.return_value = "test-org"
|
|
165
173
|
mock_api.secrets_list = AsyncMock(return_value=(None, None))
|
|
166
174
|
|
|
167
|
-
# Act
|
|
168
|
-
result = secrets_set(
|
|
169
|
-
name="test-secrets",
|
|
170
|
-
secrets=["KEY=value"],
|
|
171
|
-
from_file=None,
|
|
172
|
-
skip_confirm=True,
|
|
173
|
-
organization="test-org",
|
|
174
|
-
region="invalid-region"
|
|
175
|
-
)
|
|
176
|
-
|
|
177
175
|
# Assert - Should return early without calling upsert
|
|
178
176
|
assert mock_api.secrets_upsert.called is False
|
|
179
177
|
|
|
180
178
|
async def test_secrets_set_accepts_valid_region(self):
|
|
181
179
|
"""Secrets set should accept valid regions from API."""
|
|
182
180
|
# Arrange
|
|
183
|
-
from pipecatcloud.cli.commands.secrets import set as secrets_set
|
|
184
181
|
from pipecatcloud._utils import regions
|
|
182
|
+
from pipecatcloud.cli.commands.secrets import set as secrets_set
|
|
185
183
|
|
|
186
184
|
regions._regions_cache = None
|
|
187
185
|
|
|
188
186
|
mock_regions = [
|
|
189
187
|
{"code": "us-west", "display_name": "US West"},
|
|
190
|
-
{"code": "eu-central", "display_name": "EU Central"}
|
|
188
|
+
{"code": "eu-central", "display_name": "EU Central"},
|
|
191
189
|
]
|
|
192
190
|
|
|
193
|
-
with
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
191
|
+
with (
|
|
192
|
+
patch("pipecatcloud._utils.regions.API") as mock_regions_api,
|
|
193
|
+
patch("pipecatcloud._utils.regions.config") as mock_config,
|
|
194
|
+
patch("pipecatcloud.cli.commands.secrets.API") as mock_api,
|
|
195
|
+
):
|
|
197
196
|
# API returns (data, error) tuple
|
|
198
197
|
mock_regions_api.regions = AsyncMock(return_value=(mock_regions, None))
|
|
199
198
|
mock_config.get.return_value = "test-org"
|
|
@@ -208,7 +207,7 @@ class TestCLIRegionValidation:
|
|
|
208
207
|
from_file=None,
|
|
209
208
|
skip_confirm=True,
|
|
210
209
|
organization="test-org",
|
|
211
|
-
region="us-west" # Valid region from API
|
|
210
|
+
region="us-west", # Valid region from API
|
|
212
211
|
)
|
|
213
212
|
except SystemExit:
|
|
214
213
|
pass # Command completes
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
# Test package for pipecatcloud
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|