pipecatcloud 0.2.17__tar.gz → 0.2.19__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 → pipecatcloud-0.2.19}/CHANGELOG.md +22 -0
  2. {pipecatcloud-0.2.17 → pipecatcloud-0.2.19}/PKG-INFO +2 -2
  3. {pipecatcloud-0.2.17 → pipecatcloud-0.2.19}/pyproject.toml +1 -1
  4. {pipecatcloud-0.2.17 → pipecatcloud-0.2.19}/src/pipecatcloud/api.py +14 -0
  5. {pipecatcloud-0.2.17 → pipecatcloud-0.2.19}/src/pipecatcloud/cli/commands/agent.py +130 -0
  6. {pipecatcloud-0.2.17 → pipecatcloud-0.2.19}/src/pipecatcloud/cli/commands/auth.py +25 -24
  7. {pipecatcloud-0.2.17 → pipecatcloud-0.2.19}/src/pipecatcloud.egg-info/PKG-INFO +2 -2
  8. {pipecatcloud-0.2.17 → pipecatcloud-0.2.19}/src/pipecatcloud.egg-info/requires.txt +1 -1
  9. pipecatcloud-0.2.19/uv.lock +2520 -0
  10. pipecatcloud-0.2.17/uv.lock +0 -2212
  11. {pipecatcloud-0.2.17 → pipecatcloud-0.2.19}/.github/workflows/format.yml +0 -0
  12. {pipecatcloud-0.2.17 → pipecatcloud-0.2.19}/.github/workflows/publish-pypi.yml +0 -0
  13. {pipecatcloud-0.2.17 → pipecatcloud-0.2.19}/.github/workflows/publish-test.yml +0 -0
  14. {pipecatcloud-0.2.17 → pipecatcloud-0.2.19}/.github/workflows/tests.yml +0 -0
  15. {pipecatcloud-0.2.17 → pipecatcloud-0.2.19}/.gitignore +0 -0
  16. {pipecatcloud-0.2.17 → pipecatcloud-0.2.19}/.pre-commit-config.yaml +0 -0
  17. {pipecatcloud-0.2.17 → pipecatcloud-0.2.19}/LICENSE +0 -0
  18. {pipecatcloud-0.2.17 → pipecatcloud-0.2.19}/README.md +0 -0
  19. {pipecatcloud-0.2.17 → pipecatcloud-0.2.19}/pipecat-cloud.png +0 -0
  20. {pipecatcloud-0.2.17 → pipecatcloud-0.2.19}/pytest.ini +0 -0
  21. {pipecatcloud-0.2.17 → pipecatcloud-0.2.19}/scripts/fix-ruff.sh +0 -0
  22. {pipecatcloud-0.2.17 → pipecatcloud-0.2.19}/scripts/pre-commit.sh +0 -0
  23. {pipecatcloud-0.2.17 → pipecatcloud-0.2.19}/setup.cfg +0 -0
  24. {pipecatcloud-0.2.17 → pipecatcloud-0.2.19}/src/pipecatcloud/__init__.py +0 -0
  25. {pipecatcloud-0.2.17 → pipecatcloud-0.2.19}/src/pipecatcloud/__main__.py +0 -0
  26. {pipecatcloud-0.2.17 → pipecatcloud-0.2.19}/src/pipecatcloud/__version__.py +0 -0
  27. {pipecatcloud-0.2.17 → pipecatcloud-0.2.19}/src/pipecatcloud/_utils/__init__.py +0 -0
  28. {pipecatcloud-0.2.17 → pipecatcloud-0.2.19}/src/pipecatcloud/_utils/async_utils.py +0 -0
  29. {pipecatcloud-0.2.17 → pipecatcloud-0.2.19}/src/pipecatcloud/_utils/auth_utils.py +0 -0
  30. {pipecatcloud-0.2.17 → pipecatcloud-0.2.19}/src/pipecatcloud/_utils/console_utils.py +0 -0
  31. {pipecatcloud-0.2.17 → pipecatcloud-0.2.19}/src/pipecatcloud/_utils/deploy_utils.py +0 -0
  32. {pipecatcloud-0.2.17 → pipecatcloud-0.2.19}/src/pipecatcloud/_utils/regions.py +0 -0
  33. {pipecatcloud-0.2.17 → pipecatcloud-0.2.19}/src/pipecatcloud/agent.py +0 -0
  34. {pipecatcloud-0.2.17 → pipecatcloud-0.2.19}/src/pipecatcloud/cli/__init__.py +0 -0
  35. {pipecatcloud-0.2.17 → pipecatcloud-0.2.19}/src/pipecatcloud/cli/api.py +0 -0
  36. {pipecatcloud-0.2.17 → pipecatcloud-0.2.19}/src/pipecatcloud/cli/commands/__init__.py +0 -0
  37. {pipecatcloud-0.2.17 → pipecatcloud-0.2.19}/src/pipecatcloud/cli/commands/deploy.py +0 -0
  38. {pipecatcloud-0.2.17 → pipecatcloud-0.2.19}/src/pipecatcloud/cli/commands/docker.py +0 -0
  39. {pipecatcloud-0.2.17 → pipecatcloud-0.2.19}/src/pipecatcloud/cli/commands/organizations.py +0 -0
  40. {pipecatcloud-0.2.17 → pipecatcloud-0.2.19}/src/pipecatcloud/cli/commands/regions.py +0 -0
  41. {pipecatcloud-0.2.17 → pipecatcloud-0.2.19}/src/pipecatcloud/cli/commands/secrets.py +0 -0
  42. {pipecatcloud-0.2.17 → pipecatcloud-0.2.19}/src/pipecatcloud/cli/config.py +0 -0
  43. {pipecatcloud-0.2.17 → pipecatcloud-0.2.19}/src/pipecatcloud/cli/entry_point.py +0 -0
  44. {pipecatcloud-0.2.17 → pipecatcloud-0.2.19}/src/pipecatcloud/config.py +0 -0
  45. {pipecatcloud-0.2.17 → pipecatcloud-0.2.19}/src/pipecatcloud/constants.py +0 -0
  46. {pipecatcloud-0.2.17 → pipecatcloud-0.2.19}/src/pipecatcloud/exception.py +0 -0
  47. {pipecatcloud-0.2.17 → pipecatcloud-0.2.19}/src/pipecatcloud/py.typed +0 -0
  48. {pipecatcloud-0.2.17 → pipecatcloud-0.2.19}/src/pipecatcloud/session.py +0 -0
  49. {pipecatcloud-0.2.17 → pipecatcloud-0.2.19}/src/pipecatcloud/smallwebrtc/session_manager.py +0 -0
  50. {pipecatcloud-0.2.17 → pipecatcloud-0.2.19}/src/pipecatcloud.egg-info/SOURCES.txt +0 -0
  51. {pipecatcloud-0.2.17 → pipecatcloud-0.2.19}/src/pipecatcloud.egg-info/dependency_links.txt +0 -0
  52. {pipecatcloud-0.2.17 → pipecatcloud-0.2.19}/src/pipecatcloud.egg-info/entry_points.txt +0 -0
  53. {pipecatcloud-0.2.17 → pipecatcloud-0.2.19}/src/pipecatcloud.egg-info/top_level.txt +0 -0
  54. {pipecatcloud-0.2.17 → pipecatcloud-0.2.19}/tests/__init__.py +0 -0
  55. {pipecatcloud-0.2.17 → pipecatcloud-0.2.19}/tests/test_agent_sessions.py +0 -0
  56. {pipecatcloud-0.2.17 → pipecatcloud-0.2.19}/tests/test_agent_stop.py +0 -0
  57. {pipecatcloud-0.2.17 → pipecatcloud-0.2.19}/tests/test_api_client.py +0 -0
  58. {pipecatcloud-0.2.17 → pipecatcloud-0.2.19}/tests/test_docker_commands.py +0 -0
  59. {pipecatcloud-0.2.17 → pipecatcloud-0.2.19}/tests/test_krisp_viva.py +0 -0
  60. {pipecatcloud-0.2.17 → pipecatcloud-0.2.19}/tests/test_managed_keys.py +0 -0
  61. {pipecatcloud-0.2.17 → pipecatcloud-0.2.19}/tests/test_regions.py +0 -0
@@ -5,6 +5,28 @@ 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.19] - 2026-01-27
9
+
10
+ ### Added
11
+
12
+ - Added detailed session view with resource metrics. Use `pcc agent sessions <agent> --id <session-id>`
13
+ to see CPU and memory usage with sparkline visualizations and percentile summaries.
14
+
15
+ ## [0.2.18] - 2026-01-06
16
+
17
+ ### Added
18
+
19
+ - Return the session ID when starting an agent session.
20
+
21
+ ### Changed
22
+
23
+ - Improved `auth login` UX to display authentication code and URL before opening
24
+ browser. Users now press Enter to open browser or 'q' to quit, ensuring the
25
+ authentication code is visible before browser takes focus.
26
+
27
+ - Relaxed `synchronicity` version to ">=0.11,<1.0.0" for better compatibilty
28
+ with other libraries.
29
+
8
30
  ## [0.2.17] - 2025-12-26
9
31
 
10
32
  ### Changed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pipecatcloud
3
- Version: 0.2.17
3
+ Version: 0.2.19
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/
@@ -14,7 +14,7 @@ Requires-Dist: loguru~=0.7.3
14
14
  Requires-Dist: python-dotenv<2.0.0,>=1.0.1
15
15
  Requires-Dist: uvicorn<1.0.0,>=0.32.0
16
16
  Requires-Dist: typer<1,>=0.20.0
17
- Requires-Dist: synchronicity~=0.7.6
17
+ Requires-Dist: synchronicity<1.0.0,>=0.11
18
18
  Requires-Dist: toml~=0.10.2
19
19
  Requires-Dist: questionary~=2.1.0
20
20
  Provides-Extra: pipecat
@@ -17,7 +17,7 @@ dependencies = [
17
17
  "python-dotenv>=1.0.1,<2.0.0",
18
18
  "uvicorn>=0.32.0,<1.0.0",
19
19
  "typer>=0.20.0,<1",
20
- "synchronicity~=0.7.6",
20
+ "synchronicity>=0.11,<1.0.0",
21
21
  "toml~=0.10.2",
22
22
  "questionary~=2.1.0"
23
23
  ]
@@ -475,6 +475,20 @@ class _API:
475
475
  def agent_sessions(self):
476
476
  return self.create_api_method(self._agent_sessions)
477
477
 
478
+ async def _agent_session(self, agent_name: str, session_id: str, org: str) -> dict | None:
479
+ url = f"{self.construct_api_url('services_sessions_path').format(org=org, service=agent_name)}/{session_id}"
480
+ return await self._base_request("GET", url, not_found_is_empty=True)
481
+
482
+ @property
483
+ def agent_session(self):
484
+ """Get details for a specific session including resource metrics.
485
+ Args:
486
+ agent_name: Name of the agent
487
+ session_id: ID of the session
488
+ org: Organization ID
489
+ """
490
+ return self.create_api_method(self._agent_session)
491
+
478
492
  async def _agent_session_terminate(
479
493
  self, agent_name: str, session_id: str, org: str
480
494
  ) -> dict | None:
@@ -38,6 +38,46 @@ from pipecatcloud.constants import Region
38
38
  agent_cli = typer.Typer(name="agent", help="Agent management", no_args_is_help=True)
39
39
 
40
40
 
41
+ def sparkline(values: list[int | float], max_width: int = 50) -> str:
42
+ """Generate Unicode sparkline from values, downsampling if needed."""
43
+ if not values:
44
+ return ""
45
+
46
+ # Downsample if too many values
47
+ if len(values) > max_width:
48
+ bucket_size = len(values) / max_width
49
+ downsampled = []
50
+ for i in range(max_width):
51
+ start = int(i * bucket_size)
52
+ end = int((i + 1) * bucket_size)
53
+ bucket = values[start:end]
54
+ downsampled.append(sum(bucket) / len(bucket) if bucket else 0)
55
+ values = downsampled
56
+
57
+ blocks = "▁▂▃▄▅▆▇█"
58
+ lo, hi = min(values), max(values)
59
+ if hi == lo:
60
+ return blocks[4] * len(values) # flat line
61
+ scale = (hi - lo) / 7
62
+ return "".join(blocks[min(7, int((v - lo) / scale))] for v in values)
63
+
64
+
65
+ def format_bytes(b: int) -> str:
66
+ """Format bytes as human-readable string."""
67
+ if b >= 1024 * 1024 * 1024:
68
+ return f"{b / (1024 * 1024 * 1024):.1f}GB"
69
+ if b >= 1024 * 1024:
70
+ return f"{b / (1024 * 1024):.0f}MB"
71
+ if b >= 1024:
72
+ return f"{b / 1024:.0f}KB"
73
+ return f"{b}B"
74
+
75
+
76
+ def format_cpu(millicores: int) -> str:
77
+ """Format CPU millicores as human-readable string."""
78
+ return f"{millicores / 1000:.2f} cores"
79
+
80
+
41
81
  # ----- Agent Commands -----
42
82
 
43
83
 
@@ -292,6 +332,90 @@ async def sessions(
292
332
  console.error("No target agent name provided")
293
333
  return typer.Exit(1)
294
334
 
335
+ # If session_id is specified, fetch single session with detailed metrics
336
+ if session_id:
337
+ with Live(
338
+ console.status(f"[dim]Looking up session '{session_id}'[/dim]", spinner="dots")
339
+ ) as live:
340
+ data, error = await API.agent_session(
341
+ agent_name=agent_name, session_id=session_id, org=org, live=live
342
+ )
343
+ live.stop()
344
+
345
+ if error:
346
+ return typer.Exit()
347
+
348
+ if not data:
349
+ console.error(f"Session '{session_id}' not found")
350
+ return typer.Exit()
351
+
352
+ # Display detailed session view
353
+ session_duration = format_duration(data.get("createdAt"), data.get("endedAt")) or "N/A"
354
+ status = data.get("completionStatus", "")
355
+ if data.get("endedAt"):
356
+ status_display = "[red]Error (500)[/red]" if status == "500" else "Complete"
357
+ else:
358
+ status_display = "[yellow]Active[/yellow]"
359
+
360
+ # Build session info panel
361
+ info_lines = [
362
+ f"[bold]Session ID:[/bold] {data['sessionId']}",
363
+ f"[bold]Status:[/bold] {status_display}"
364
+ + (f" ({status})" if status and status != "500" else ""),
365
+ f"[bold]Duration:[/bold] {session_duration}",
366
+ f"[bold]Created:[/bold] {format_timestamp(data.get('createdAt'))}",
367
+ f"[bold]Ended:[/bold] {format_timestamp(data.get('endedAt')) if data.get('endedAt') else '[dim]N/A[/dim]'}",
368
+ f"[bold]Bot Start:[/bold] {data.get('botStartSeconds')}s"
369
+ if data.get("botStartSeconds") is not None
370
+ else "[bold]Bot Start:[/bold] [dim]N/A[/dim]",
371
+ f"[bold]Cold Start:[/bold] {'[red]Yes[/red]' if data.get('coldStart') else 'No'}",
372
+ ]
373
+
374
+ # Add resource metrics if available
375
+ metrics = data.get("resourceMetrics")
376
+ if metrics:
377
+ timeseries = metrics.get("timeseries", [])
378
+ sample_count = metrics.get("sampleCount", 0)
379
+
380
+ # Calculate duration from timeseries
381
+ if len(timeseries) >= 2:
382
+ ts_duration = timeseries[-1].get("t", 0) - timeseries[0].get("t", 0)
383
+ ts_duration_str = f"{ts_duration}s"
384
+ else:
385
+ ts_duration_str = "N/A"
386
+
387
+ info_lines.append("")
388
+ info_lines.append(
389
+ f"[bold]Resource Metrics[/bold] ({sample_count} samples over {ts_duration_str}):"
390
+ )
391
+
392
+ # CPU sparkline and percentiles
393
+ cpu_values = [s.get("c", 0) for s in timeseries]
394
+ cpu_spark = sparkline(cpu_values) if cpu_values else ""
395
+ cpu_p50 = metrics.get("cpuMillicoresP50", 0)
396
+ cpu_p99 = metrics.get("cpuMillicoresP99", 0)
397
+ info_lines.append(
398
+ f" CPU: {cpu_spark} p50: {format_cpu(cpu_p50)} p99: {format_cpu(cpu_p99)}"
399
+ )
400
+
401
+ # Memory sparkline and percentiles
402
+ mem_values = [s.get("m", 0) for s in timeseries]
403
+ mem_spark = sparkline(mem_values) if mem_values else ""
404
+ mem_p50 = int(metrics.get("memoryBytesP50", 0))
405
+ mem_p99 = int(metrics.get("memoryBytesP99", 0))
406
+ info_lines.append(
407
+ f" Memory: {mem_spark} p50: {format_bytes(mem_p50)} p99: {format_bytes(mem_p99)}"
408
+ )
409
+
410
+ console.success(
411
+ Panel(
412
+ "\n".join(info_lines),
413
+ title=f"Session details for agent [bold]{agent_name}[/bold] [dim]({org})[/dim]",
414
+ title_align="left",
415
+ ),
416
+ )
417
+ return
418
+
295
419
  with Live(
296
420
  console.status(f"[dim]Looking up agent with name '{agent_name}'[/dim]", spinner="dots")
297
421
  ) as live:
@@ -765,6 +889,7 @@ async def start(
765
889
  live.stop()
766
890
 
767
891
  console.success(f"Agent '{agent_name}' started successfully")
892
+
768
893
  if use_daily and isinstance(data, dict):
769
894
  daily_room = data.get("dailyRoom")
770
895
  daily_token = data.get("dailyToken")
@@ -773,6 +898,11 @@ async def start(
773
898
  console.print("\nJoin your session by visiting the link below:")
774
899
  console.print(f"[link={url}]{url}[/link]")
775
900
 
901
+ if isinstance(data, dict):
902
+ session_id = data.get("sessionId")
903
+ if session_id:
904
+ console.print(f"\nSession ID: {session_id}")
905
+
776
906
 
777
907
  @agent_cli.command(name="stop", help="Stop an active agent session")
778
908
  @synchronizer.create_blocking
@@ -6,8 +6,8 @@
6
6
 
7
7
  import asyncio
8
8
  import itertools
9
- import webbrowser
10
9
  import os
10
+ import webbrowser
11
11
  from contextlib import asynccontextmanager
12
12
  from typing import AsyncGenerator, Optional, Tuple
13
13
 
@@ -160,9 +160,30 @@ async def login(
160
160
  )
161
161
  return
162
162
 
163
- # Display the code if it was provided
164
- if code:
165
- console.print(f"[bold]Authentication code:[/bold] [cyan]{code}[/cyan]\n")
163
+ # Display the authentication URL and code
164
+ console.print(
165
+ Panel(
166
+ "[bold]To authenticate with Pipecat Cloud:[/bold]\n\n"
167
+ "Visit this URL:\n"
168
+ f"[blue][link={web_url}]{web_url}[/link][/blue]\n\n"
169
+ "Then enter this code:\n"
170
+ f"[cyan bold]{code}[/cyan bold]",
171
+ title="[bold]Authentication Required[/bold]",
172
+ border_style="blue",
173
+ )
174
+ )
175
+
176
+ # Prompt user to open the browser (unless in headless mode)
177
+ if not headless:
178
+ response = typer.prompt(
179
+ "\nPress Enter to open the browser (or 'q' to quit)",
180
+ default="",
181
+ show_default=False,
182
+ )
183
+ if response.lower() == "q":
184
+ console.print("[yellow]Authentication cancelled[/yellow]")
185
+ return typer.Exit()
186
+ _open_url(web_url)
166
187
 
167
188
  with Live(
168
189
  console.status(
@@ -170,26 +191,6 @@ async def login(
170
191
  ),
171
192
  transient=True,
172
193
  ) as live:
173
- # Open the web url in the browser
174
- if not headless and _open_url(web_url):
175
- live.update(
176
- console.status(
177
- Panel(
178
- "The web browser should have opened for you to authenticate with Pipecat Cloud.\n"
179
- "If it didn't, please copy this URL into your web browser manually:\n\n"
180
- f"[blue][link={web_url}]{web_url}[/link][/blue]\n",
181
- )
182
- )
183
- )
184
- else:
185
- # For headless use-cases, just print the URL
186
- live.stop()
187
- console.print("[dim]Unable to launch web browser[/dim]")
188
- console.print("[bold]Please visit the following URL to confirm login[/bold]\n")
189
- console.print(web_url)
190
-
191
- live.start()
192
-
193
194
  for attempt in itertools.count():
194
195
  result = await auth_flow.finish()
195
196
  if result is not None:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pipecatcloud
3
- Version: 0.2.17
3
+ Version: 0.2.19
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/
@@ -14,7 +14,7 @@ Requires-Dist: loguru~=0.7.3
14
14
  Requires-Dist: python-dotenv<2.0.0,>=1.0.1
15
15
  Requires-Dist: uvicorn<1.0.0,>=0.32.0
16
16
  Requires-Dist: typer<1,>=0.20.0
17
- Requires-Dist: synchronicity~=0.7.6
17
+ Requires-Dist: synchronicity<1.0.0,>=0.11
18
18
  Requires-Dist: toml~=0.10.2
19
19
  Requires-Dist: questionary~=2.1.0
20
20
  Provides-Extra: pipecat
@@ -4,7 +4,7 @@ loguru~=0.7.3
4
4
  python-dotenv<2.0.0,>=1.0.1
5
5
  uvicorn<1.0.0,>=0.32.0
6
6
  typer<1,>=0.20.0
7
- synchronicity~=0.7.6
7
+ synchronicity<1.0.0,>=0.11
8
8
  toml~=0.10.2
9
9
  questionary~=2.1.0
10
10