pipecatcloud 0.2.5__tar.gz → 0.2.6__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 (51) hide show
  1. {pipecatcloud-0.2.5 → pipecatcloud-0.2.6}/CHANGELOG.md +13 -0
  2. {pipecatcloud-0.2.5/src/pipecatcloud.egg-info → pipecatcloud-0.2.6}/PKG-INFO +1 -1
  3. {pipecatcloud-0.2.5 → pipecatcloud-0.2.6}/src/pipecatcloud/_utils/deploy_utils.py +3 -0
  4. {pipecatcloud-0.2.5 → pipecatcloud-0.2.6}/src/pipecatcloud/api.py +1 -0
  5. {pipecatcloud-0.2.5 → pipecatcloud-0.2.6}/src/pipecatcloud/cli/commands/agent.py +76 -61
  6. {pipecatcloud-0.2.5 → pipecatcloud-0.2.6}/src/pipecatcloud/cli/commands/deploy.py +9 -0
  7. {pipecatcloud-0.2.5 → pipecatcloud-0.2.6/src/pipecatcloud.egg-info}/PKG-INFO +1 -1
  8. {pipecatcloud-0.2.5 → pipecatcloud-0.2.6}/tests/test_managed_keys.py +99 -116
  9. {pipecatcloud-0.2.5 → pipecatcloud-0.2.6}/.github/workflows/publish-pypi.yml +0 -0
  10. {pipecatcloud-0.2.5 → pipecatcloud-0.2.6}/.github/workflows/publish-test.yml +0 -0
  11. {pipecatcloud-0.2.5 → pipecatcloud-0.2.6}/.github/workflows/tests.yml +0 -0
  12. {pipecatcloud-0.2.5 → pipecatcloud-0.2.6}/.gitignore +0 -0
  13. {pipecatcloud-0.2.5 → pipecatcloud-0.2.6}/LICENSE +0 -0
  14. {pipecatcloud-0.2.5 → pipecatcloud-0.2.6}/README.md +0 -0
  15. {pipecatcloud-0.2.5 → pipecatcloud-0.2.6}/pipecat-cloud.png +0 -0
  16. {pipecatcloud-0.2.5 → pipecatcloud-0.2.6}/pyproject.toml +0 -0
  17. {pipecatcloud-0.2.5 → pipecatcloud-0.2.6}/pytest.ini +0 -0
  18. {pipecatcloud-0.2.5 → pipecatcloud-0.2.6}/setup.cfg +0 -0
  19. {pipecatcloud-0.2.5 → pipecatcloud-0.2.6}/src/pipecatcloud/__init__.py +0 -0
  20. {pipecatcloud-0.2.5 → pipecatcloud-0.2.6}/src/pipecatcloud/__main__.py +0 -0
  21. {pipecatcloud-0.2.5 → pipecatcloud-0.2.6}/src/pipecatcloud/__version__.py +0 -0
  22. {pipecatcloud-0.2.5 → pipecatcloud-0.2.6}/src/pipecatcloud/_utils/__init__.py +0 -0
  23. {pipecatcloud-0.2.5 → pipecatcloud-0.2.6}/src/pipecatcloud/_utils/async_utils.py +0 -0
  24. {pipecatcloud-0.2.5 → pipecatcloud-0.2.6}/src/pipecatcloud/_utils/auth_utils.py +0 -0
  25. {pipecatcloud-0.2.5 → pipecatcloud-0.2.6}/src/pipecatcloud/_utils/console_utils.py +0 -0
  26. {pipecatcloud-0.2.5 → pipecatcloud-0.2.6}/src/pipecatcloud/_utils/local_runner.py +0 -0
  27. {pipecatcloud-0.2.5 → pipecatcloud-0.2.6}/src/pipecatcloud/agent.py +0 -0
  28. {pipecatcloud-0.2.5 → pipecatcloud-0.2.6}/src/pipecatcloud/cli/__init__.py +0 -0
  29. {pipecatcloud-0.2.5 → pipecatcloud-0.2.6}/src/pipecatcloud/cli/api.py +0 -0
  30. {pipecatcloud-0.2.5 → pipecatcloud-0.2.6}/src/pipecatcloud/cli/commands/__init__.py +0 -0
  31. {pipecatcloud-0.2.5 → pipecatcloud-0.2.6}/src/pipecatcloud/cli/commands/auth.py +0 -0
  32. {pipecatcloud-0.2.5 → pipecatcloud-0.2.6}/src/pipecatcloud/cli/commands/docker.py +0 -0
  33. {pipecatcloud-0.2.5 → pipecatcloud-0.2.6}/src/pipecatcloud/cli/commands/init.py +0 -0
  34. {pipecatcloud-0.2.5 → pipecatcloud-0.2.6}/src/pipecatcloud/cli/commands/organizations.py +0 -0
  35. {pipecatcloud-0.2.5 → pipecatcloud-0.2.6}/src/pipecatcloud/cli/commands/run.py +0 -0
  36. {pipecatcloud-0.2.5 → pipecatcloud-0.2.6}/src/pipecatcloud/cli/commands/secrets.py +0 -0
  37. {pipecatcloud-0.2.5 → pipecatcloud-0.2.6}/src/pipecatcloud/cli/config.py +0 -0
  38. {pipecatcloud-0.2.5 → pipecatcloud-0.2.6}/src/pipecatcloud/cli/entry_point.py +0 -0
  39. {pipecatcloud-0.2.5 → pipecatcloud-0.2.6}/src/pipecatcloud/config.py +0 -0
  40. {pipecatcloud-0.2.5 → pipecatcloud-0.2.6}/src/pipecatcloud/exception.py +0 -0
  41. {pipecatcloud-0.2.5 → pipecatcloud-0.2.6}/src/pipecatcloud/py.typed +0 -0
  42. {pipecatcloud-0.2.5 → pipecatcloud-0.2.6}/src/pipecatcloud/session.py +0 -0
  43. {pipecatcloud-0.2.5 → pipecatcloud-0.2.6}/src/pipecatcloud/smallwebrtc/session_manager.py +0 -0
  44. {pipecatcloud-0.2.5 → pipecatcloud-0.2.6}/src/pipecatcloud.egg-info/SOURCES.txt +0 -0
  45. {pipecatcloud-0.2.5 → pipecatcloud-0.2.6}/src/pipecatcloud.egg-info/dependency_links.txt +0 -0
  46. {pipecatcloud-0.2.5 → pipecatcloud-0.2.6}/src/pipecatcloud.egg-info/entry_points.txt +0 -0
  47. {pipecatcloud-0.2.5 → pipecatcloud-0.2.6}/src/pipecatcloud.egg-info/requires.txt +0 -0
  48. {pipecatcloud-0.2.5 → pipecatcloud-0.2.6}/src/pipecatcloud.egg-info/top_level.txt +0 -0
  49. {pipecatcloud-0.2.5 → pipecatcloud-0.2.6}/tests/__init__.py +0 -0
  50. {pipecatcloud-0.2.5 → pipecatcloud-0.2.6}/tests/test_agent_sessions.py +0 -0
  51. {pipecatcloud-0.2.5 → pipecatcloud-0.2.6}/tests/test_docker_commands.py +0 -0
@@ -5,6 +5,19 @@ 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.6] - 2025-10-09
9
+
10
+ ### Added
11
+
12
+ - Added an optional `--profile` option to the `deploy` command which selects between
13
+ `agent-1x`, with 0.5 vCPUs / 1 GB RAM, and `agent-2x`, with 1 vCPU / 2 GB RAM. The
14
+ default profile is `agent-1x`.
15
+
16
+ ### Fixed
17
+
18
+ - Fixed an issue where the `agent status` CLI command would return incorrect
19
+ values for Min and Max Agents.
20
+
8
21
  ## [0.2.5] - 2025-10-02
9
22
 
10
23
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pipecatcloud
3
- Version: 0.2.5
3
+ Version: 0.2.6
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/
@@ -66,6 +66,7 @@ class DeployConfigParams:
66
66
  enable_krisp: bool = False
67
67
  enable_managed_keys: bool = False
68
68
  docker_config: dict = field(factory=dict)
69
+ agent_profile: Optional[str] = None
69
70
 
70
71
  def __attrs_post_init__(self):
71
72
  if self.image is not None and ":" not in self.image:
@@ -81,6 +82,7 @@ class DeployConfigParams:
81
82
  "enable_krisp": self.enable_krisp,
82
83
  "enable_managed_keys": self.enable_managed_keys,
83
84
  "docker_config": self.docker_config,
85
+ "agent_profile": self.agent_profile,
84
86
  }
85
87
 
86
88
 
@@ -121,6 +123,7 @@ def load_deploy_config_file() -> Optional[DeployConfigParams]:
121
123
  "enable_krisp",
122
124
  "enable_managed_keys",
123
125
  "docker",
126
+ "agent_profile",
124
127
  }
125
128
  unexpected_keys = set(config_data.keys()) - expected_keys
126
129
  if unexpected_keys:
@@ -322,6 +322,7 @@ class _API:
322
322
  },
323
323
  "enableKrisp": deploy_config.enable_krisp,
324
324
  "enableIntegratedKeysProxy": deploy_config.enable_managed_keys, # API expects this field name
325
+ "agentProfile": deploy_config.agent_profile,
325
326
  }
326
327
 
327
328
  # Remove None values recursively
@@ -21,11 +21,16 @@ from rich.text import Text
21
21
 
22
22
  from pipecatcloud._utils.async_utils import synchronizer
23
23
  from pipecatcloud._utils.auth_utils import requires_login
24
- from pipecatcloud._utils.console_utils import console, format_duration, format_timestamp, calculate_percentiles
24
+ from pipecatcloud._utils.console_utils import (
25
+ calculate_percentiles,
26
+ console,
27
+ format_duration,
28
+ format_timestamp,
29
+ )
30
+ from pipecatcloud._utils.deploy_utils import DeployConfigParams, with_deploy_config
25
31
  from pipecatcloud.cli import PIPECAT_CLI_NAME
26
32
  from pipecatcloud.cli.api import API
27
33
  from pipecatcloud.cli.config import config
28
- from pipecatcloud._utils.deploy_utils import with_deploy_config, DeployConfigParams
29
34
 
30
35
  agent_cli = typer.Typer(name="agent", help="Agent management", no_args_is_help=True)
31
36
 
@@ -54,7 +59,8 @@ async def list(
54
59
  if not data or len(data) == 0:
55
60
  console.error(
56
61
  f"[red]No agents found for namespace / organization '{org}'[/red]\n\n"
57
- f"[dim]Please deploy an agent first using[/dim] [bold cyan]{PIPECAT_CLI_NAME} deploy[/bold cyan]")
62
+ f"[dim]Please deploy an agent first using[/dim] [bold cyan]{PIPECAT_CLI_NAME} deploy[/bold cyan]"
63
+ )
58
64
  return typer.Exit(1)
59
65
 
60
66
  else:
@@ -119,6 +125,15 @@ async def status(
119
125
  "[bold]Image:[/bold]",
120
126
  str(data.get("deployment", {}).get("manifest", {}).get("spec", {}).get("image", "N/A")),
121
127
  )
128
+
129
+ # Display agent profile if available
130
+ agent_profile = data.get("agentProfile")
131
+ if agent_profile:
132
+ deployment_table.add_row(
133
+ "[bold]Agent Profile:[/bold]",
134
+ str(agent_profile),
135
+ )
136
+
122
137
  deployment_table.add_row(
123
138
  "[bold]Active Deployment ID:[/bold]",
124
139
  str(data.get("activeDeploymentId", "N/A")),
@@ -131,10 +146,15 @@ async def status(
131
146
  "[bold]Updated At:[/bold]",
132
147
  str(data.get("updatedAt", "N/A")),
133
148
  )
134
-
149
+
135
150
  # Check for Managed Keys status
136
151
  # API returns integratedKeysProxy but we display as "Managed Keys"
137
- integrated_keys = data.get("deployment", {}).get("manifest", {}).get("spec", {}).get("integratedKeysProxy", {})
152
+ integrated_keys = (
153
+ data.get("deployment", {})
154
+ .get("manifest", {})
155
+ .get("spec", {})
156
+ .get("integratedKeysProxy", {})
157
+ )
138
158
  if isinstance(integrated_keys, dict):
139
159
  integrated_keys_enabled = integrated_keys.get("enabled", False)
140
160
  else:
@@ -150,11 +170,11 @@ async def status(
150
170
  if autoscaling_data:
151
171
  scaling_renderables = [
152
172
  Panel(
153
- f"[bold]Minimum Agents[/bold]\n{autoscaling_data.get('minAgents', 0)}",
173
+ f"[bold]Minimum Agents[/bold]\n{autoscaling_data.get('minReplicas', 0)}",
154
174
  expand=True,
155
175
  ),
156
176
  Panel(
157
- f"[bold]Maximum Agents[/bold]\n{autoscaling_data.get('maxAgents', 0)}",
177
+ f"[bold]Maximum Agents[/bold]\n{autoscaling_data.get('maxReplicas', 0)}",
158
178
  expand=True,
159
179
  ),
160
180
  ]
@@ -217,9 +237,7 @@ async def status(
217
237
  async def sessions(
218
238
  deploy_config=typer.Option(None, hidden=True),
219
239
  agent_name: str = typer.Argument(
220
- None,
221
- help="Name of the agent to list sessions for e.g. 'my-agent'",
222
- show_default=False
240
+ None, help="Name of the agent to list sessions for e.g. 'my-agent'", show_default=False
223
241
  ),
224
242
  session_id: str = typer.Option(
225
243
  None,
@@ -232,7 +250,7 @@ async def sessions(
232
250
  ),
233
251
  ):
234
252
  org = organization or config.get("org")
235
-
253
+
236
254
  # Get agent name from argument or deploy config
237
255
  if not agent_name:
238
256
  if deploy_config and deploy_config.agent_name:
@@ -264,15 +282,17 @@ async def sessions(
264
282
  for session in completed_sessions:
265
283
  try:
266
284
  from datetime import datetime
267
- created_at = datetime.fromisoformat(session["createdAt"].replace('Z', '+00:00'))
268
- ended_at = datetime.fromisoformat(session["endedAt"].replace('Z', '+00:00'))
285
+
286
+ created_at = datetime.fromisoformat(session["createdAt"].replace("Z", "+00:00"))
287
+ ended_at = datetime.fromisoformat(session["endedAt"].replace("Z", "+00:00"))
269
288
  duration_seconds = (ended_at - created_at).total_seconds()
270
289
  durations.append(duration_seconds)
271
290
  except BaseException:
272
291
  continue
273
292
 
274
- bot_start_times = [s["botStartSeconds"]
275
- for s in sessions_list if s.get("botStartSeconds") is not None]
293
+ bot_start_times = [
294
+ s["botStartSeconds"] for s in sessions_list if s.get("botStartSeconds") is not None
295
+ ]
276
296
  bot_start_metrics = calculate_percentiles(bot_start_times)
277
297
  duration_metrics = calculate_percentiles(durations)
278
298
  cold_starts_count = sum(1 for s in sessions_list if s.get("coldStart") is True)
@@ -317,8 +337,9 @@ async def sessions(
317
337
  if session_id and session["sessionId"] != session_id:
318
338
  continue
319
339
 
320
- session_duration = format_duration(
321
- session["createdAt"], session["endedAt"]) or "[dim]N/A[/dim]"
340
+ session_duration = (
341
+ format_duration(session["createdAt"], session["endedAt"]) or "[dim]N/A[/dim]"
342
+ )
322
343
  status = session.get("completionStatus", "")
323
344
  if session["endedAt"]:
324
345
  if status == "500":
@@ -333,14 +354,18 @@ async def sessions(
333
354
 
334
355
  row_data = [
335
356
  session["sessionId"],
336
- format_timestamp(
337
- session["createdAt"]),
338
- format_timestamp(
339
- session["endedAt"]) if session["endedAt"] else "[dim]N/A[/dim]",
357
+ format_timestamp(session["createdAt"]),
358
+ format_timestamp(session["endedAt"]) if session["endedAt"] else "[dim]N/A[/dim]",
340
359
  session_duration,
341
360
  status_display,
342
- f"{session['botStartSeconds']}s" if session["botStartSeconds"] is not None else "[dim]N/A[/dim]",
343
- "[red]Yes[/red]" if session["coldStart"] is True else "No" if session["coldStart"] is False else "[dim]N/A[/dim]",
361
+ f"{session['botStartSeconds']}s"
362
+ if session["botStartSeconds"] is not None
363
+ else "[dim]N/A[/dim]",
364
+ "[red]Yes[/red]"
365
+ if session["coldStart"] is True
366
+ else "No"
367
+ if session["coldStart"] is False
368
+ else "[dim]N/A[/dim]",
344
369
  ]
345
370
 
346
371
  if is_cold_start:
@@ -350,10 +375,11 @@ async def sessions(
350
375
 
351
376
  console.success(
352
377
  Group(
353
- Columns(
354
- metric_renderables,
355
- equal=True) if metric_renderables and not session_id else "",
356
- table),
378
+ Columns(metric_renderables, equal=True)
379
+ if metric_renderables and not session_id
380
+ else "",
381
+ table,
382
+ ),
357
383
  title=f"Session data for agent {agent_name} [dim]({org})[/dim]",
358
384
  )
359
385
 
@@ -390,32 +416,16 @@ class LogLevelColors(str, Enum):
390
416
  @synchronizer.create_blocking
391
417
  @requires_login
392
418
  async def logs(
393
- agent_name: str,
394
- organization: str = typer.Option(
395
- None,
396
- "--organization",
397
- "-o",
398
- help="Organization to get status of agent for"),
399
- level: LogLevel = typer.Option(
400
- None,
401
- "--level",
402
- "-l",
403
- help="Level of logs to get"),
404
- format: LogFormat = typer.Option(
405
- LogFormat.TEXT,
406
- "--format",
407
- "-f",
408
- help="Logs format"),
409
- limit: int = typer.Option(
410
- 100,
411
- "--limit",
412
- "-n",
413
- help="Number of logs to get"),
414
- deployment_id: str = typer.Option(
415
- None,
416
- "--deployment",
417
- "-d",
418
- help="Filter logs by deployment ID"),
419
+ agent_name: str,
420
+ organization: str = typer.Option(
421
+ None, "--organization", "-o", help="Organization to get status of agent for"
422
+ ),
423
+ level: LogLevel = typer.Option(None, "--level", "-l", help="Level of logs to get"),
424
+ format: LogFormat = typer.Option(LogFormat.TEXT, "--format", "-f", help="Logs format"),
425
+ limit: int = typer.Option(100, "--limit", "-n", help="Number of logs to get"),
426
+ deployment_id: str = typer.Option(
427
+ None, "--deployment", "-d", help="Filter logs by deployment ID"
428
+ ),
419
429
  ):
420
430
  org = organization or config.get("org")
421
431
 
@@ -425,7 +435,9 @@ async def logs(
425
435
  f"[dim]Fetching logs for {status_text}: [bold]'{agent_name}'[/bold] with severity: [bold cyan]{level.value if level else 'ALL'}[/bold cyan][/dim]",
426
436
  spinner="dots",
427
437
  ):
428
- data, error = await API.agent_logs(agent_name=agent_name, org=org, limit=limit, deployment_id=deployment_id)
438
+ data, error = await API.agent_logs(
439
+ agent_name=agent_name, org=org, limit=limit, deployment_id=deployment_id
440
+ )
429
441
 
430
442
  if not data or not data.get("logs"):
431
443
  console.print("[dim]No logs found for agent[/dim]")
@@ -539,12 +551,13 @@ async def deployments(
539
551
  table.add_column("Updated At")
540
552
 
541
553
  for deployment in data["deployments"]:
554
+ spec = deployment.get("manifest", {}).get("spec", {})
542
555
  table.add_row(
543
- deployment["id"],
544
- deployment["manifest"]["spec"]["dailyNodeType"],
545
- deployment["manifest"]["spec"]["image"],
546
- deployment["createdAt"],
547
- deployment["updatedAt"],
556
+ deployment.get("id", "N/A"),
557
+ spec.get("dailyNodeType", "N/A"),
558
+ spec.get("image", "N/A"),
559
+ deployment.get("createdAt", "N/A"),
560
+ deployment.get("updatedAt", "N/A"),
548
561
  )
549
562
 
550
563
  console.print(
@@ -636,7 +649,8 @@ async def start(
636
649
  title="Public API Key Required",
637
650
  title_align="left",
638
651
  border_style="yellow",
639
- ))
652
+ )
653
+ )
640
654
 
641
655
  return typer.Exit(1)
642
656
 
@@ -666,7 +680,8 @@ async def start(
666
680
  title=f"[bold]Start Request for agent: {agent_name}[/bold]",
667
681
  title_align="left",
668
682
  border_style="yellow",
669
- ))
683
+ )
684
+ )
670
685
  if not await questionary.confirm(
671
686
  "Are you sure you want to start an active session for this agent?"
672
687
  ).ask_async():
@@ -285,6 +285,13 @@ def create_deploy_command(app: typer.Typer):
285
285
  help="Enable Managed Keys for this deployment",
286
286
  rich_help_panel="Deployment Configuration",
287
287
  ),
288
+ profile: str = typer.Option(
289
+ None,
290
+ "--profile",
291
+ "-p",
292
+ help="Agent profile to use for deployment",
293
+ rich_help_panel="Deployment Configuration",
294
+ ),
288
295
  skip_confirm: bool = typer.Option(
289
296
  False,
290
297
  "--force",
@@ -352,6 +359,7 @@ def create_deploy_command(app: typer.Typer):
352
359
  )
353
360
  partial_config.enable_krisp = krisp or partial_config.enable_krisp
354
361
  partial_config.enable_managed_keys = managed_keys or partial_config.enable_managed_keys
362
+ partial_config.agent_profile = profile or partial_config.agent_profile
355
363
 
356
364
  # Assert agent name and image are provided
357
365
  if not partial_config.agent_name:
@@ -388,6 +396,7 @@ def create_deploy_command(app: typer.Typer):
388
396
  (f"[bold white]Organization:[/bold white] [green]{org}[/green]"),
389
397
  (f"[bold white]Secret set:[/bold white] {'[dim]None[/dim]' if not partial_config.secret_set else '[green] '+ partial_config.secret_set + '[/green]'}"),
390
398
  (f"[bold white]Image pull secret:[/bold white] {'[dim]None[/dim]' if not partial_config.image_credentials else '[green]' + partial_config.image_credentials + '[/green]'}"),
399
+ (f"[bold white]Agent profile:[/bold white] {'[dim]None[/dim]' if not partial_config.agent_profile else '[green]' + partial_config.agent_profile + '[/green]'}"),
391
400
  (f"[bold white]Krisp:[/bold white] {'[dim]Disabled[/dim]' if not partial_config.enable_krisp else '[green]Enabled[/green]'}"),
392
401
  (f"[bold white]Managed Keys:[/bold white] {'[dim]Disabled[/dim]' if not partial_config.enable_managed_keys else '[green]Enabled[/green]'}"),
393
402
  "\n[dim]Scaling configuration:[/dim]",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pipecatcloud
3
- Version: 0.2.5
3
+ Version: 0.2.6
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/
@@ -5,17 +5,12 @@ Tests follow AAA pattern and focus on behaviors/outcomes rather than implementat
5
5
  Covers data model, TOML parsing, CLI commands, and API integration.
6
6
  """
7
7
 
8
- import json
9
- import tempfile
10
- from pathlib import Path
11
- from unittest.mock import AsyncMock, MagicMock, patch
8
+ from unittest.mock import AsyncMock, patch
12
9
 
13
10
  import pytest
14
- import toml
15
11
 
16
12
  from src.pipecatcloud._utils.deploy_utils import DeployConfigParams, load_deploy_config_file
17
13
  from src.pipecatcloud.api import _API
18
- from src.pipecatcloud.exception import ConfigFileError
19
14
 
20
15
 
21
16
  class TestDeployConfigDataModel:
@@ -25,7 +20,7 @@ class TestDeployConfigDataModel:
25
20
  """When not specified, managed keys should default to disabled."""
26
21
  # Arrange & Act
27
22
  config = DeployConfigParams()
28
-
23
+
29
24
  # Assert
30
25
  assert config.enable_managed_keys is False
31
26
 
@@ -33,21 +28,18 @@ class TestDeployConfigDataModel:
33
28
  """Integrated keys can be explicitly enabled."""
34
29
  # Arrange & Act
35
30
  config = DeployConfigParams(enable_managed_keys=True)
36
-
31
+
37
32
  # Assert
38
33
  assert config.enable_managed_keys is True
39
34
 
40
35
  def test_keys_included_in_dict_representation(self):
41
36
  """Dictionary representation should include managed keys setting."""
42
37
  # Arrange
43
- config = DeployConfigParams(
44
- agent_name="test-agent",
45
- enable_managed_keys=True
46
- )
47
-
38
+ config = DeployConfigParams(agent_name="test-agent", enable_managed_keys=True)
39
+
48
40
  # Act
49
41
  result = config.to_dict()
50
-
42
+
51
43
  # Assert
52
44
  assert "enable_managed_keys" in result
53
45
  assert result["enable_managed_keys"] is True
@@ -59,12 +51,12 @@ class TestDeployConfigDataModel:
59
51
  agent_name="test-agent",
60
52
  image="test:latest",
61
53
  enable_krisp=True,
62
- enable_managed_keys=True
54
+ enable_managed_keys=True,
63
55
  )
64
-
56
+
65
57
  # Act
66
58
  result = config.to_dict()
67
-
59
+
68
60
  # Assert
69
61
  assert result["agent_name"] == "test-agent"
70
62
  assert result["image"] == "test:latest"
@@ -90,11 +82,11 @@ class TestTOMLConfiguration:
90
82
  enable_managed_keys = true
91
83
  """
92
84
  temp_config_file.write_text(config_content)
93
-
85
+
94
86
  # Act
95
87
  with patch("pipecatcloud.cli.config.deploy_config_path", str(temp_config_file)):
96
88
  config = load_deploy_config_file()
97
-
89
+
98
90
  # Assert
99
91
  assert config is not None
100
92
  assert config.enable_managed_keys is True
@@ -108,11 +100,11 @@ class TestTOMLConfiguration:
108
100
  enable_managed_keys = false
109
101
  """
110
102
  temp_config_file.write_text(config_content)
111
-
103
+
112
104
  # Act
113
105
  with patch("pipecatcloud.cli.config.deploy_config_path", str(temp_config_file)):
114
106
  config = load_deploy_config_file()
115
-
107
+
116
108
  # Assert
117
109
  assert config is not None
118
110
  assert config.enable_managed_keys is False
@@ -125,11 +117,11 @@ class TestTOMLConfiguration:
125
117
  image = "test:latest"
126
118
  """
127
119
  temp_config_file.write_text(config_content)
128
-
120
+
129
121
  # Act
130
122
  with patch("pipecatcloud.cli.config.deploy_config_path", str(temp_config_file)):
131
123
  config = load_deploy_config_file()
132
-
124
+
133
125
  # Assert
134
126
  assert config is not None
135
127
  assert config.enable_managed_keys is False
@@ -149,11 +141,11 @@ class TestTOMLConfiguration:
149
141
  max_agents = 10
150
142
  """
151
143
  temp_config_file.write_text(config_content)
152
-
144
+
153
145
  # Act
154
146
  with patch("pipecatcloud.cli.config.deploy_config_path", str(temp_config_file)):
155
147
  config = load_deploy_config_file()
156
-
148
+
157
149
  # Assert
158
150
  assert config.agent_name == "test-agent"
159
151
  assert config.image == "test:latest"
@@ -164,7 +156,6 @@ class TestTOMLConfiguration:
164
156
  assert config.scaling.max_agents == 10
165
157
 
166
158
 
167
-
168
159
  class TestAPIIntegration:
169
160
  """Test API client integration with managed keys."""
170
161
 
@@ -178,17 +169,15 @@ class TestAPIIntegration:
178
169
  """Deploy payload should include enableIntegratedKeys field."""
179
170
  # Arrange
180
171
  config = DeployConfigParams(
181
- agent_name="test-agent",
182
- image="test:latest",
183
- enable_managed_keys=True
172
+ agent_name="test-agent", image="test:latest", enable_managed_keys=True
184
173
  )
185
-
174
+
186
175
  with patch.object(api_client, "_base_request", new_callable=AsyncMock) as mock_request:
187
176
  mock_request.return_value = {"success": True}
188
-
177
+
189
178
  # Act
190
179
  await api_client._deploy(config, "test-org", update=False)
191
-
180
+
192
181
  # Assert
193
182
  mock_request.assert_called_once()
194
183
  payload = mock_request.call_args[1]["json"]
@@ -200,17 +189,15 @@ class TestAPIIntegration:
200
189
  """Update payload should include enableIntegratedKeys field."""
201
190
  # Arrange
202
191
  config = DeployConfigParams(
203
- agent_name="test-agent",
204
- image="test:latest",
205
- enable_managed_keys=True
192
+ agent_name="test-agent", image="test:latest", enable_managed_keys=True
206
193
  )
207
-
194
+
208
195
  with patch.object(api_client, "_base_request", new_callable=AsyncMock) as mock_request:
209
196
  mock_request.return_value = {"success": True}
210
-
197
+
211
198
  # Act
212
199
  await api_client._deploy(config, "test-org", update=True)
213
-
200
+
214
201
  # Assert
215
202
  mock_request.assert_called_once()
216
203
  payload = mock_request.call_args[1]["json"]
@@ -222,17 +209,15 @@ class TestAPIIntegration:
222
209
  """When managed keys is disabled, API should send false value."""
223
210
  # Arrange
224
211
  config = DeployConfigParams(
225
- agent_name="test-agent",
226
- image="test:latest",
227
- enable_managed_keys=False
212
+ agent_name="test-agent", image="test:latest", enable_managed_keys=False
228
213
  )
229
-
214
+
230
215
  with patch.object(api_client, "_base_request", new_callable=AsyncMock) as mock_request:
231
216
  mock_request.return_value = {"success": True}
232
-
217
+
233
218
  # Act
234
219
  await api_client._deploy(config, "test-org", update=False)
235
-
220
+
236
221
  # Assert
237
222
  mock_request.assert_called_once()
238
223
  payload = mock_request.call_args[1]["json"]
@@ -253,24 +238,26 @@ class TestAgentStatusDisplay:
253
238
  """Agent status should display when managed keys is enabled."""
254
239
  # Arrange
255
240
  from src.pipecatcloud.cli.commands.agent import status
256
-
257
- mock_api.agent = AsyncMock(return_value=({
258
- "ready": True,
259
- "deployment": {
260
- "manifest": {
261
- "spec": {
262
- "integratedKeysProxy": {
263
- "enabled": True
264
- }
265
- }
266
- }
267
- },
268
- "autoScaling": {"minAgents": 0, "maxAgents": 1} # Avoid undefined variable bug
269
- }, None))
270
-
241
+
242
+ mock_api.agent = AsyncMock(
243
+ return_value=(
244
+ {
245
+ "ready": True,
246
+ "deployment": {
247
+ "manifest": {"spec": {"integratedKeysProxy": {"enabled": True}}}
248
+ },
249
+ "autoScaling": {
250
+ "minReplicas": 0,
251
+ "maxReplicas": 1,
252
+ }, # Avoid undefined variable bug
253
+ },
254
+ None,
255
+ )
256
+ )
257
+
271
258
  # Act
272
259
  status(agent_name="test-agent", organization="test-org")
273
-
260
+
274
261
  # Assert
275
262
  captured = capsys.readouterr()
276
263
  assert "Managed Keys" in captured.out
@@ -280,20 +267,24 @@ class TestAgentStatusDisplay:
280
267
  """Agent status should display when managed keys is disabled."""
281
268
  # Arrange
282
269
  from src.pipecatcloud.cli.commands.agent import status
283
-
284
- mock_api.agent = AsyncMock(return_value=({
285
- "ready": True,
286
- "deployment": {
287
- "manifest": {
288
- "spec": {}
289
- }
290
- },
291
- "autoScaling": {"minAgents": 0, "maxAgents": 1} # Avoid undefined variable bug
292
- }, None))
293
-
270
+
271
+ mock_api.agent = AsyncMock(
272
+ return_value=(
273
+ {
274
+ "ready": True,
275
+ "deployment": {"manifest": {"spec": {}}},
276
+ "autoScaling": {
277
+ "minReplicas": 0,
278
+ "maxReplicas": 1,
279
+ }, # Avoid undefined variable bug
280
+ },
281
+ None,
282
+ )
283
+ )
284
+
294
285
  # Act
295
286
  status(agent_name="test-agent", organization="test-org")
296
-
287
+
297
288
  # Assert
298
289
  captured = capsys.readouterr()
299
290
  assert "Managed Keys" in captured.out
@@ -303,22 +294,24 @@ class TestAgentStatusDisplay:
303
294
  """Agent status should handle boolean value for backward compatibility."""
304
295
  # Arrange
305
296
  from src.pipecatcloud.cli.commands.agent import status
306
-
307
- mock_api.agent = AsyncMock(return_value=({
308
- "ready": True,
309
- "deployment": {
310
- "manifest": {
311
- "spec": {
312
- "integratedKeysProxy": True
313
- }
314
- }
315
- },
316
- "autoScaling": {"minAgents": 0, "maxAgents": 1} # Avoid undefined variable bug
317
- }, None))
318
-
297
+
298
+ mock_api.agent = AsyncMock(
299
+ return_value=(
300
+ {
301
+ "ready": True,
302
+ "deployment": {"manifest": {"spec": {"integratedKeysProxy": True}}},
303
+ "autoScaling": {
304
+ "minReplicas": 0,
305
+ "maxReplicas": 1,
306
+ }, # Avoid undefined variable bug
307
+ },
308
+ None,
309
+ )
310
+ )
311
+
319
312
  # Act
320
313
  status(agent_name="test-agent", organization="test-org")
321
-
314
+
322
315
  # Assert
323
316
  captured = capsys.readouterr()
324
317
  assert "Managed Keys" in captured.out
@@ -331,15 +324,11 @@ class TestBackwardCompatibility:
331
324
  def test_existing_config_without_keys_proxy_works(self):
332
325
  """Existing configs without managed keys field should work unchanged."""
333
326
  # Arrange
334
- config = DeployConfigParams(
335
- agent_name="test-agent",
336
- image="test:latest",
337
- enable_krisp=True
338
- )
339
-
327
+ config = DeployConfigParams(agent_name="test-agent", image="test:latest", enable_krisp=True)
328
+
340
329
  # Act
341
330
  result = config.to_dict()
342
-
331
+
343
332
  # Assert
344
333
  assert result["enable_krisp"] is True
345
334
  assert result["enable_managed_keys"] is False
@@ -347,24 +336,20 @@ class TestBackwardCompatibility:
347
336
  def test_api_response_without_keys_proxy_doesnt_crash(self):
348
337
  """Agent status should handle API responses without managed keys field."""
349
338
  # Arrange
350
- data = {
351
- "ready": True,
352
- "deployment": {
353
- "manifest": {
354
- "spec": {
355
- "image": "test:latest"
356
- }
357
- }
358
- }
359
- }
360
-
339
+ data = {"ready": True, "deployment": {"manifest": {"spec": {"image": "test:latest"}}}}
340
+
361
341
  # Act - simulate the logic from agent.py
362
- integrated_keys = data.get("deployment", {}).get("manifest", {}).get("spec", {}).get("integratedKeysProxy", {})
342
+ integrated_keys = (
343
+ data.get("deployment", {})
344
+ .get("manifest", {})
345
+ .get("spec", {})
346
+ .get("integratedKeysProxy", {})
347
+ )
363
348
  if isinstance(integrated_keys, dict):
364
349
  keys_proxy_enabled = integrated_keys.get("enabled", False)
365
350
  else:
366
351
  keys_proxy_enabled = bool(integrated_keys)
367
-
352
+
368
353
  # Assert
369
354
  assert keys_proxy_enabled is False
370
355
 
@@ -389,7 +374,7 @@ class TestBackwardCompatibility:
389
374
  # Arrange & Act
390
375
  with patch("pipecatcloud.cli.config.deploy_config_path", str(temp_legacy_config)):
391
376
  config = load_deploy_config_file()
392
-
377
+
393
378
  # Assert
394
379
  assert config is not None
395
380
  assert config.agent_name == "legacy-agent"
@@ -407,14 +392,12 @@ class TestErrorHandling:
407
392
  # Arrange
408
393
  api = _API(token="test-token", is_cli=True)
409
394
  config = DeployConfigParams(
410
- agent_name="test-agent",
411
- image="test:latest",
412
- enable_managed_keys=True
395
+ agent_name="test-agent", image="test:latest", enable_managed_keys=True
413
396
  )
414
-
397
+
415
398
  with patch.object(api, "_base_request", new_callable=AsyncMock) as mock_request:
416
399
  mock_request.side_effect = Exception("Network error")
417
-
400
+
418
401
  # Act & Assert
419
402
  with pytest.raises(Exception, match="Network error"):
420
403
  await api._deploy(config, "test-org", update=False)
@@ -426,12 +409,12 @@ class TestErrorHandling:
426
409
  agent_name="test-agent",
427
410
  image="test:latest",
428
411
  enable_krisp=True,
429
- enable_managed_keys=True
412
+ enable_managed_keys=True,
430
413
  )
431
-
414
+
432
415
  # Act
433
416
  result = config.to_dict()
434
-
417
+
435
418
  # Assert
436
419
  assert result["enable_krisp"] is True
437
- assert result["enable_managed_keys"] is True
420
+ assert result["enable_managed_keys"] is True
File without changes
File without changes
File without changes
File without changes
File without changes