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.
Files changed (61) hide show
  1. pipecatcloud-0.2.17/.github/workflows/format.yml +43 -0
  2. pipecatcloud-0.2.17/.pre-commit-config.yaml +8 -0
  3. {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/CHANGELOG.md +7 -0
  4. {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/PKG-INFO +38 -1
  5. {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/README.md +37 -0
  6. pipecatcloud-0.2.17/scripts/fix-ruff.sh +10 -0
  7. pipecatcloud-0.2.17/scripts/pre-commit.sh +27 -0
  8. {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud/__version__.py +2 -0
  9. {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud/_utils/console_utils.py +11 -7
  10. {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud/api.py +6 -2
  11. {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud/cli/commands/deploy.py +6 -3
  12. {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud/cli/commands/organizations.py +9 -3
  13. {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud/cli/commands/secrets.py +3 -1
  14. {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud/smallwebrtc/session_manager.py +3 -1
  15. {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud.egg-info/PKG-INFO +38 -1
  16. {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud.egg-info/SOURCES.txt +4 -0
  17. pipecatcloud-0.2.17/tests/__init__.py +1 -0
  18. {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/tests/test_agent_stop.py +20 -19
  19. {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/tests/test_api_client.py +8 -23
  20. {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/tests/test_krisp_viva.py +1 -0
  21. {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/tests/test_managed_keys.py +1 -0
  22. {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/tests/test_regions.py +35 -36
  23. pipecatcloud-0.2.16/tests/__init__.py +0 -1
  24. {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/.github/workflows/publish-pypi.yml +0 -0
  25. {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/.github/workflows/publish-test.yml +0 -0
  26. {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/.github/workflows/tests.yml +0 -0
  27. {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/.gitignore +0 -0
  28. {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/LICENSE +0 -0
  29. {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/pipecat-cloud.png +0 -0
  30. {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/pyproject.toml +0 -0
  31. {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/pytest.ini +0 -0
  32. {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/setup.cfg +0 -0
  33. {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud/__init__.py +0 -0
  34. {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud/__main__.py +0 -0
  35. {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud/_utils/__init__.py +0 -0
  36. {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud/_utils/async_utils.py +0 -0
  37. {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud/_utils/auth_utils.py +0 -0
  38. {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud/_utils/deploy_utils.py +0 -0
  39. {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud/_utils/regions.py +0 -0
  40. {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud/agent.py +0 -0
  41. {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud/cli/__init__.py +0 -0
  42. {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud/cli/api.py +0 -0
  43. {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud/cli/commands/__init__.py +0 -0
  44. {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud/cli/commands/agent.py +0 -0
  45. {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud/cli/commands/auth.py +0 -0
  46. {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud/cli/commands/docker.py +0 -0
  47. {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud/cli/commands/regions.py +0 -0
  48. {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud/cli/config.py +0 -0
  49. {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud/cli/entry_point.py +0 -0
  50. {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud/config.py +0 -0
  51. {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud/constants.py +0 -0
  52. {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud/exception.py +0 -0
  53. {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud/py.typed +0 -0
  54. {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud/session.py +0 -0
  55. {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud.egg-info/dependency_links.txt +0 -0
  56. {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud.egg-info/entry_points.txt +0 -0
  57. {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud.egg-info/requires.txt +0 -0
  58. {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/src/pipecatcloud.egg-info/top_level.txt +0 -0
  59. {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/tests/test_agent_sessions.py +0 -0
  60. {pipecatcloud-0.2.16 → pipecatcloud-0.2.17}/tests/test_docker_commands.py +0 -0
  61. {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
@@ -0,0 +1,8 @@
1
+ repos:
2
+ - repo: https://github.com/astral-sh/ruff-pre-commit
3
+ rev: v0.12.1
4
+ hooks:
5
+ - id: ruff
6
+ language_version: python3
7
+ args: [--fix]
8
+ - id: ruff-format
@@ -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.16
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 = error_code.get(
84
- "error", None) or error_code.get(
85
- "message", None) or DEFAULT_ERROR_MESSAGE
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" f"[dim]Error message:[/dim]\n{error_message}",
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('Z', '+00:00'))
154
- ended_at = datetime.fromisoformat(ended_at_str.replace('Z', '+00:00'))
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(self, org: str, secret_set: Optional[str] = None, region: Optional[str] = None) -> dict | None:
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(self, data: dict, set_name: str, org: str, region: Optional[str] = None) -> dict:
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
- # Both the service AND the active deployment must be ready
191
- service_ready = agent_status.get("ready", False)
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 service_ready and deployment_ready:
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(name="properties", help="Organization property management", no_args_is_help=True)
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(name="default-region", help="Get or set the default region for an organization.")
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(f"Default region set to [bold green]{data.get('defaultRegion', region)}[/bold green]")
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(f"[bold white]Region:[/bold white] {props['defaultRegion']} [dim](organization default)[/dim]\n")
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(f"WebRTC connection not received within {self._timeout_seconds} seconds")
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.16
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 patch('pipecatcloud.cli.commands.agent.console') as mock_console, \
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
-
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 patch('pipecatcloud.cli.commands.agent.console') as mock_console, \
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
-
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 patch('pipecatcloud.cli.commands.agent.console') as mock_console, \
80
- patch('pipecatcloud.cli.commands.agent.config') as mock_config, \
81
- patch('pipecatcloud.cli.commands.agent.questionary') as mock_questionary, \
82
- patch('pipecatcloud.cli.commands.agent.DeployConfigParams') as mock_params:
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 (
@@ -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 patch("pipecatcloud._utils.regions.API") as mock_api, \
70
- patch("pipecatcloud._utils.regions.config") as mock_config:
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 patch("pipecatcloud._utils.regions.API") as mock_api, \
106
- patch("pipecatcloud._utils.regions.config") as mock_config:
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 patch("pipecatcloud._utils.regions.API") as mock_api, \
130
- patch("pipecatcloud._utils.regions.config") as mock_config:
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 patch("pipecatcloud._utils.regions.API") as mock_regions_api, \
159
- patch("pipecatcloud._utils.regions.config") as mock_config, \
160
- patch("pipecatcloud.cli.commands.secrets.API") as mock_api:
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 patch("pipecatcloud._utils.regions.API") as mock_regions_api, \
194
- patch("pipecatcloud._utils.regions.config") as mock_config, \
195
- patch("pipecatcloud.cli.commands.secrets.API") as mock_api:
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