pipecatcloud 0.2.4__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 (53) hide show
  1. pipecatcloud-0.2.4/.github/workflows/publish-test.yml → pipecatcloud-0.2.6/.github/workflows/publish-pypi.yml +30 -25
  2. pipecatcloud-0.2.6/.github/workflows/publish-test.yml +51 -0
  3. {pipecatcloud-0.2.4 → pipecatcloud-0.2.6}/CHANGELOG.md +30 -0
  4. {pipecatcloud-0.2.4/src/pipecatcloud.egg-info → pipecatcloud-0.2.6}/PKG-INFO +1 -1
  5. {pipecatcloud-0.2.4 → pipecatcloud-0.2.6}/pyproject.toml +5 -1
  6. {pipecatcloud-0.2.4 → pipecatcloud-0.2.6}/src/pipecatcloud/__init__.py +4 -0
  7. {pipecatcloud-0.2.4 → pipecatcloud-0.2.6}/src/pipecatcloud/_utils/deploy_utils.py +6 -0
  8. {pipecatcloud-0.2.4 → pipecatcloud-0.2.6}/src/pipecatcloud/agent.py +26 -0
  9. {pipecatcloud-0.2.4 → pipecatcloud-0.2.6}/src/pipecatcloud/api.py +2 -0
  10. {pipecatcloud-0.2.4 → pipecatcloud-0.2.6}/src/pipecatcloud/cli/commands/agent.py +87 -60
  11. {pipecatcloud-0.2.4 → pipecatcloud-0.2.6}/src/pipecatcloud/cli/commands/deploy.py +17 -0
  12. pipecatcloud-0.2.6/src/pipecatcloud/smallwebrtc/session_manager.py +87 -0
  13. {pipecatcloud-0.2.4 → pipecatcloud-0.2.6/src/pipecatcloud.egg-info}/PKG-INFO +1 -1
  14. {pipecatcloud-0.2.4 → pipecatcloud-0.2.6}/src/pipecatcloud.egg-info/SOURCES.txt +3 -2
  15. pipecatcloud-0.2.6/tests/test_managed_keys.py +420 -0
  16. pipecatcloud-0.2.4/.github/workflows/publish-pypi.yml +0 -84
  17. pipecatcloud-0.2.4/.github/workflows/python-publish.yml +0 -40
  18. {pipecatcloud-0.2.4 → pipecatcloud-0.2.6}/.github/workflows/tests.yml +0 -0
  19. {pipecatcloud-0.2.4 → pipecatcloud-0.2.6}/.gitignore +0 -0
  20. {pipecatcloud-0.2.4 → pipecatcloud-0.2.6}/LICENSE +0 -0
  21. {pipecatcloud-0.2.4 → pipecatcloud-0.2.6}/README.md +0 -0
  22. {pipecatcloud-0.2.4 → pipecatcloud-0.2.6}/pipecat-cloud.png +0 -0
  23. {pipecatcloud-0.2.4 → pipecatcloud-0.2.6}/pytest.ini +0 -0
  24. {pipecatcloud-0.2.4 → pipecatcloud-0.2.6}/setup.cfg +0 -0
  25. {pipecatcloud-0.2.4 → pipecatcloud-0.2.6}/src/pipecatcloud/__main__.py +0 -0
  26. {pipecatcloud-0.2.4 → pipecatcloud-0.2.6}/src/pipecatcloud/__version__.py +0 -0
  27. {pipecatcloud-0.2.4 → pipecatcloud-0.2.6}/src/pipecatcloud/_utils/__init__.py +0 -0
  28. {pipecatcloud-0.2.4 → pipecatcloud-0.2.6}/src/pipecatcloud/_utils/async_utils.py +0 -0
  29. {pipecatcloud-0.2.4 → pipecatcloud-0.2.6}/src/pipecatcloud/_utils/auth_utils.py +0 -0
  30. {pipecatcloud-0.2.4 → pipecatcloud-0.2.6}/src/pipecatcloud/_utils/console_utils.py +0 -0
  31. {pipecatcloud-0.2.4 → pipecatcloud-0.2.6}/src/pipecatcloud/_utils/local_runner.py +0 -0
  32. {pipecatcloud-0.2.4 → pipecatcloud-0.2.6}/src/pipecatcloud/cli/__init__.py +0 -0
  33. {pipecatcloud-0.2.4 → pipecatcloud-0.2.6}/src/pipecatcloud/cli/api.py +0 -0
  34. {pipecatcloud-0.2.4 → pipecatcloud-0.2.6}/src/pipecatcloud/cli/commands/__init__.py +0 -0
  35. {pipecatcloud-0.2.4 → pipecatcloud-0.2.6}/src/pipecatcloud/cli/commands/auth.py +0 -0
  36. {pipecatcloud-0.2.4 → pipecatcloud-0.2.6}/src/pipecatcloud/cli/commands/docker.py +0 -0
  37. {pipecatcloud-0.2.4 → pipecatcloud-0.2.6}/src/pipecatcloud/cli/commands/init.py +0 -0
  38. {pipecatcloud-0.2.4 → pipecatcloud-0.2.6}/src/pipecatcloud/cli/commands/organizations.py +0 -0
  39. {pipecatcloud-0.2.4 → pipecatcloud-0.2.6}/src/pipecatcloud/cli/commands/run.py +0 -0
  40. {pipecatcloud-0.2.4 → pipecatcloud-0.2.6}/src/pipecatcloud/cli/commands/secrets.py +0 -0
  41. {pipecatcloud-0.2.4 → pipecatcloud-0.2.6}/src/pipecatcloud/cli/config.py +0 -0
  42. {pipecatcloud-0.2.4 → pipecatcloud-0.2.6}/src/pipecatcloud/cli/entry_point.py +0 -0
  43. {pipecatcloud-0.2.4 → pipecatcloud-0.2.6}/src/pipecatcloud/config.py +0 -0
  44. {pipecatcloud-0.2.4 → pipecatcloud-0.2.6}/src/pipecatcloud/exception.py +0 -0
  45. {pipecatcloud-0.2.4 → pipecatcloud-0.2.6}/src/pipecatcloud/py.typed +0 -0
  46. {pipecatcloud-0.2.4 → pipecatcloud-0.2.6}/src/pipecatcloud/session.py +0 -0
  47. {pipecatcloud-0.2.4 → pipecatcloud-0.2.6}/src/pipecatcloud.egg-info/dependency_links.txt +0 -0
  48. {pipecatcloud-0.2.4 → pipecatcloud-0.2.6}/src/pipecatcloud.egg-info/entry_points.txt +0 -0
  49. {pipecatcloud-0.2.4 → pipecatcloud-0.2.6}/src/pipecatcloud.egg-info/requires.txt +0 -0
  50. {pipecatcloud-0.2.4 → pipecatcloud-0.2.6}/src/pipecatcloud.egg-info/top_level.txt +0 -0
  51. {pipecatcloud-0.2.4 → pipecatcloud-0.2.6}/tests/__init__.py +0 -0
  52. {pipecatcloud-0.2.4 → pipecatcloud-0.2.6}/tests/test_agent_sessions.py +0 -0
  53. {pipecatcloud-0.2.4 → pipecatcloud-0.2.6}/tests/test_docker_commands.py +0 -0
@@ -1,61 +1,67 @@
1
- name: Publish to TEST PyPI
1
+ name: Publish to PyPI
2
2
 
3
3
  on:
4
4
  workflow_dispatch:
5
5
  inputs:
6
- version:
7
- description: 'Version to publish (must match pyproject.toml, e.g. 0.0.4)'
8
- required: true
6
+ gitref:
9
7
  type: string
8
+ description: 'what git tag to build (e.g. v0.0.74)'
9
+ required: true
10
10
 
11
11
  jobs:
12
12
  build:
13
13
  name: 'Build and upload wheels'
14
14
  runs-on: ubuntu-latest
15
- permissions:
16
- contents: write
17
15
  steps:
18
- - name: Checkout code
16
+ - name: Checkout repo
19
17
  uses: actions/checkout@v4
20
18
  with:
21
- fetch-tags: true
22
- fetch-depth: 100
19
+ ref: ${{ github.event.inputs.gitref }}
23
20
 
24
21
  - name: Install uv
25
22
  uses: astral-sh/setup-uv@v3
26
23
  with:
27
24
  version: 'latest'
28
-
29
25
  - name: Set up Python
30
26
  run: uv python install 3.12
31
-
32
- - name: Verify version matches
33
- run: |
34
- PROJECT_VERSION=$(uv run python -c "import tomllib; print(tomllib.load(open('pyproject.toml', 'rb'))['project']['version'])")
35
- if [ "$PROJECT_VERSION" != "${{ github.event.inputs.version }}" ]; then
36
- echo "Error: Input version (${{ github.event.inputs.version }}) does not match pyproject.toml version ($PROJECT_VERSION)"
37
- exit 1
38
- fi
39
-
40
27
  - name: Install development dependencies
41
28
  run: uv sync --group dev
42
-
43
29
  - name: Build project
44
30
  run: uv build
45
-
46
31
  - name: Upload wheels
47
32
  uses: actions/upload-artifact@v4
48
33
  with:
49
34
  name: wheels
50
35
  path: ./dist
51
36
 
37
+ publish-to-pypi:
38
+ name: 'Publish to PyPI'
39
+ runs-on: ubuntu-latest
40
+ needs: [build]
41
+ environment:
42
+ name: pypi
43
+ url: https://pypi.org/p/pipecatcloud
44
+ permissions:
45
+ id-token: write
46
+ steps:
47
+ - name: Download wheels
48
+ uses: actions/download-artifact@v4
49
+ with:
50
+ name: wheels
51
+ path: ./dist
52
+ - name: Publish to PyPI
53
+ uses: pypa/gh-action-pypi-publish@release/v1
54
+ with:
55
+ verbose: true
56
+ print-hash: true
57
+
52
58
  publish-to-test-pypi:
53
59
  name: 'Publish to Test PyPI'
54
60
  runs-on: ubuntu-latest
55
61
  needs: [build]
56
62
  environment:
57
63
  name: testpypi
58
- url: https://test.pypi.org/project/pipecat-cloud
64
+ url: https://test.pypi.org/p/pipecatcloud
59
65
  permissions:
60
66
  id-token: write
61
67
  steps:
@@ -64,10 +70,9 @@ jobs:
64
70
  with:
65
71
  name: wheels
66
72
  path: ./dist
67
-
68
- - name: Publish to TestPyPI
73
+ - name: Publish to Test PyPI
69
74
  uses: pypa/gh-action-pypi-publish@release/v1
70
75
  with:
71
- repository-url: https://test.pypi.org/legacy/
72
76
  verbose: true
73
77
  print-hash: true
78
+ repository-url: https://test.pypi.org/legacy/
@@ -0,0 +1,51 @@
1
+ name: Publish to TEST PyPI
2
+
3
+ on: workflow_dispatch
4
+
5
+ jobs:
6
+ build:
7
+ name: 'Build and upload wheels'
8
+ runs-on: ubuntu-latest
9
+ steps:
10
+ - name: Checkout repo
11
+ uses: actions/checkout@v4
12
+ with:
13
+ fetch-tags: true
14
+ fetch-depth: 100
15
+ - name: Install uv
16
+ uses: astral-sh/setup-uv@v3
17
+ with:
18
+ version: 'latest'
19
+ - name: Set up Python
20
+ run: uv python install 3.12
21
+ - name: Install development dependencies
22
+ run: uv sync --group dev
23
+ - name: Build project
24
+ run: uv build
25
+ - name: Upload wheels
26
+ uses: actions/upload-artifact@v4
27
+ with:
28
+ name: wheels
29
+ path: ./dist
30
+
31
+ publish-to-test-pypi:
32
+ name: 'Publish to Test PyPI'
33
+ runs-on: ubuntu-latest
34
+ needs: [build]
35
+ environment:
36
+ name: testpypi
37
+ url: https://test.pypi.org/p/pipecatcloud
38
+ permissions:
39
+ id-token: write
40
+ steps:
41
+ - name: Download wheels
42
+ uses: actions/download-artifact@v4
43
+ with:
44
+ name: wheels
45
+ path: ./dist
46
+ - name: Publish to Test PyPI
47
+ uses: pypa/gh-action-pypi-publish@release/v1
48
+ with:
49
+ verbose: true
50
+ print-hash: true
51
+ repository-url: https://test.pypi.org/legacy/
@@ -5,6 +5,36 @@ 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
+
21
+ ## [0.2.5] - 2025-10-02
22
+
23
+ ### Added
24
+
25
+ - Added a `body` parameter to the fallback type, `WebsocketRunnerArguments`.
26
+
27
+ - Added `SmallWebRTCSessionManager`, a Session Manager to simplify bot creation
28
+ in Pipecat Cloud using `SmallWebRTCTransport`.
29
+ - Added a `SmallWebRTCSessionArguments` dataclass.
30
+
31
+ - `deploy` command now accepts a `--enable-managed-keys` flag which enables
32
+ properly configured agents to use Daily's API keys for supported upstream
33
+ services. This can also be enabled via `pcc-deploy.toml` by adding the
34
+ `enable_managed_keys` attribute and a boolean value.
35
+
36
+ - `agent status` now reflects the state of the managed keys feature.
37
+
8
38
  ## [0.2.4] - 2025-08-26
9
39
 
10
40
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pipecatcloud
3
- Version: 0.2.4
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/
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "pipecatcloud"
7
- version = "0.2.4"
7
+ dynamic = ["version"]
8
8
  requires-python = ">=3.10"
9
9
  description = "Cloud hosting for Pipecat AI applications"
10
10
  license = { text = "BSD 2-Clause License" }
@@ -58,3 +58,7 @@ line-length = 100
58
58
 
59
59
  [project.urls]
60
60
  Website = "https://docs.pipecat.daily.co/"
61
+
62
+ [tool.setuptools_scm]
63
+ local_scheme = "no-local-version"
64
+ fallback_version = "0.0.0-dev"
@@ -13,6 +13,7 @@ from pipecatcloud.agent import (
13
13
  DailySessionArguments,
14
14
  PipecatSessionArguments,
15
15
  SessionArguments,
16
+ SmallWebRTCRunnerArguments,
16
17
  WebSocketSessionArguments,
17
18
  )
18
19
  from pipecatcloud.exception import (
@@ -25,6 +26,7 @@ from pipecatcloud.exception import (
25
26
  InvalidError,
26
27
  )
27
28
  from pipecatcloud.session import Session, SessionParams
29
+ from pipecatcloud.smallwebrtc.session_manager import SmallWebRTCSessionManager
28
30
 
29
31
  logger.remove()
30
32
  logger.add(sys.stderr, level=str(os.getenv("PCC_LOG_LEVEL", "INFO")))
@@ -35,10 +37,12 @@ __all__ = [
35
37
  "DailySessionArguments",
36
38
  "PipecatSessionArguments",
37
39
  "SessionArguments",
40
+ "SmallWebRTCRunnerArguments",
38
41
  "WebSocketSessionArguments",
39
42
  # Session classes
40
43
  "Session",
41
44
  "SessionParams",
45
+ "SmallWebRTCSessionManager",
42
46
  # Exception classes
43
47
  "AgentNotHealthyError",
44
48
  "AgentStartError",
@@ -64,7 +64,9 @@ class DeployConfigParams:
64
64
  secret_set: Optional[str] = None
65
65
  scaling: ScalingParams = ScalingParams()
66
66
  enable_krisp: bool = False
67
+ enable_managed_keys: bool = False
67
68
  docker_config: dict = field(factory=dict)
69
+ agent_profile: Optional[str] = None
68
70
 
69
71
  def __attrs_post_init__(self):
70
72
  if self.image is not None and ":" not in self.image:
@@ -78,7 +80,9 @@ class DeployConfigParams:
78
80
  "secret_set": self.secret_set,
79
81
  "scaling": self.scaling.to_dict() if self.scaling else None,
80
82
  "enable_krisp": self.enable_krisp,
83
+ "enable_managed_keys": self.enable_managed_keys,
81
84
  "docker_config": self.docker_config,
85
+ "agent_profile": self.agent_profile,
82
86
  }
83
87
 
84
88
 
@@ -117,7 +121,9 @@ def load_deploy_config_file() -> Optional[DeployConfigParams]:
117
121
  "secret_set",
118
122
  "scaling",
119
123
  "enable_krisp",
124
+ "enable_managed_keys",
120
125
  "docker",
126
+ "agent_profile",
121
127
  }
122
128
  unexpected_keys = set(config_data.keys()) - expected_keys
123
129
  if unexpected_keys:
@@ -16,6 +16,7 @@ try:
16
16
  DailyRunnerArguments,
17
17
  RunnerArguments,
18
18
  WebSocketRunnerArguments,
19
+ SmallWebRTCRunnerArguments,
19
20
  )
20
21
 
21
22
  _PIPECAT_RUNNER_TYPES_AVAILABLE = True
@@ -62,6 +63,17 @@ except ImportError:
62
63
  """
63
64
 
64
65
  websocket: WebSocket
66
+ body: Any
67
+
68
+ @dataclass
69
+ class SmallWebRTCRunnerArguments(RunnerArguments):
70
+ """Fallback Small WebRTC transport session arguments for the runner.
71
+
72
+ Parameters:
73
+ webrtc_connection: Pre-configured WebRTC peer connection
74
+ """
75
+
76
+ webrtc_connection: Any
65
77
 
66
78
 
67
79
  def _warn_standalone_usage():
@@ -131,3 +143,17 @@ class WebSocketSessionArguments(WebSocketRunnerArguments, SessionArguments):
131
143
  def __post_init__(self):
132
144
  WebSocketRunnerArguments.__post_init__(self)
133
145
  _warn_standalone_usage()
146
+
147
+ @dataclass
148
+ class SmallWebRTCSessionArguments(SmallWebRTCRunnerArguments, SessionArguments):
149
+ """SmallWebRTCTransport based agent session arguments.
150
+
151
+ Inherits from SmallWebRTCRunnerArguments for compatibility with pipecat-ai runner.
152
+ When pipecat-ai is not installed, uses a fallback implementation (deprecated).
153
+
154
+ For best compatibility, install: pip install pipecatcloud[pipecat]
155
+ """
156
+
157
+ def __post_init__(self):
158
+ SmallWebRTCRunnerArguments.__post_init__(self)
159
+ _warn_standalone_usage()
@@ -321,6 +321,8 @@ class _API:
321
321
  "maxAgents": deploy_config.scaling.max_agents,
322
322
  },
323
323
  "enableKrisp": deploy_config.enable_krisp,
324
+ "enableIntegratedKeysProxy": deploy_config.enable_managed_keys, # API expects this field name
325
+ "agentProfile": deploy_config.agent_profile,
324
326
  }
325
327
 
326
328
  # Remove None values recursively
@@ -5,7 +5,6 @@
5
5
  #
6
6
 
7
7
  import json
8
- import statistics
9
8
  from enum import Enum
10
9
 
11
10
  import aiohttp
@@ -22,11 +21,16 @@ from rich.text import Text
22
21
 
23
22
  from pipecatcloud._utils.async_utils import synchronizer
24
23
  from pipecatcloud._utils.auth_utils import requires_login
25
- 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
26
31
  from pipecatcloud.cli import PIPECAT_CLI_NAME
27
32
  from pipecatcloud.cli.api import API
28
33
  from pipecatcloud.cli.config import config
29
- from pipecatcloud._utils.deploy_utils import with_deploy_config, DeployConfigParams
30
34
 
31
35
  agent_cli = typer.Typer(name="agent", help="Agent management", no_args_is_help=True)
32
36
 
@@ -55,7 +59,8 @@ async def list(
55
59
  if not data or len(data) == 0:
56
60
  console.error(
57
61
  f"[red]No agents found for namespace / organization '{org}'[/red]\n\n"
58
- 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
+ )
59
64
  return typer.Exit(1)
60
65
 
61
66
  else:
@@ -120,6 +125,15 @@ async def status(
120
125
  "[bold]Image:[/bold]",
121
126
  str(data.get("deployment", {}).get("manifest", {}).get("spec", {}).get("image", "N/A")),
122
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
+
123
137
  deployment_table.add_row(
124
138
  "[bold]Active Deployment ID:[/bold]",
125
139
  str(data.get("activeDeploymentId", "N/A")),
@@ -133,16 +147,34 @@ async def status(
133
147
  str(data.get("updatedAt", "N/A")),
134
148
  )
135
149
 
150
+ # Check for Managed Keys status
151
+ # API returns integratedKeysProxy but we display as "Managed Keys"
152
+ integrated_keys = (
153
+ data.get("deployment", {})
154
+ .get("manifest", {})
155
+ .get("spec", {})
156
+ .get("integratedKeysProxy", {})
157
+ )
158
+ if isinstance(integrated_keys, dict):
159
+ integrated_keys_enabled = integrated_keys.get("enabled", False)
160
+ else:
161
+ integrated_keys_enabled = bool(integrated_keys)
162
+
163
+ deployment_table.add_row(
164
+ "[bold]Managed Keys:[/bold]",
165
+ "[green]Enabled[/green]" if integrated_keys_enabled else "[dim]Disabled[/dim]",
166
+ )
167
+
136
168
  # Autoscaling info
137
169
  autoscaling_data = data.get("autoScaling", None)
138
170
  if autoscaling_data:
139
171
  scaling_renderables = [
140
172
  Panel(
141
- f"[bold]Minimum Agents[/bold]\n{autoscaling_data.get('minAgents', 0)}",
173
+ f"[bold]Minimum Agents[/bold]\n{autoscaling_data.get('minReplicas', 0)}",
142
174
  expand=True,
143
175
  ),
144
176
  Panel(
145
- f"[bold]Maximum Agents[/bold]\n{autoscaling_data.get('maxAgents', 0)}",
177
+ f"[bold]Maximum Agents[/bold]\n{autoscaling_data.get('maxReplicas', 0)}",
146
178
  expand=True,
147
179
  ),
148
180
  ]
@@ -205,9 +237,7 @@ async def status(
205
237
  async def sessions(
206
238
  deploy_config=typer.Option(None, hidden=True),
207
239
  agent_name: str = typer.Argument(
208
- None,
209
- help="Name of the agent to list sessions for e.g. 'my-agent'",
210
- show_default=False
240
+ None, help="Name of the agent to list sessions for e.g. 'my-agent'", show_default=False
211
241
  ),
212
242
  session_id: str = typer.Option(
213
243
  None,
@@ -220,7 +250,7 @@ async def sessions(
220
250
  ),
221
251
  ):
222
252
  org = organization or config.get("org")
223
-
253
+
224
254
  # Get agent name from argument or deploy config
225
255
  if not agent_name:
226
256
  if deploy_config and deploy_config.agent_name:
@@ -252,15 +282,17 @@ async def sessions(
252
282
  for session in completed_sessions:
253
283
  try:
254
284
  from datetime import datetime
255
- created_at = datetime.fromisoformat(session["createdAt"].replace('Z', '+00:00'))
256
- 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"))
257
288
  duration_seconds = (ended_at - created_at).total_seconds()
258
289
  durations.append(duration_seconds)
259
290
  except BaseException:
260
291
  continue
261
292
 
262
- bot_start_times = [s["botStartSeconds"]
263
- 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
+ ]
264
296
  bot_start_metrics = calculate_percentiles(bot_start_times)
265
297
  duration_metrics = calculate_percentiles(durations)
266
298
  cold_starts_count = sum(1 for s in sessions_list if s.get("coldStart") is True)
@@ -305,8 +337,9 @@ async def sessions(
305
337
  if session_id and session["sessionId"] != session_id:
306
338
  continue
307
339
 
308
- session_duration = format_duration(
309
- 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
+ )
310
343
  status = session.get("completionStatus", "")
311
344
  if session["endedAt"]:
312
345
  if status == "500":
@@ -321,14 +354,18 @@ async def sessions(
321
354
 
322
355
  row_data = [
323
356
  session["sessionId"],
324
- format_timestamp(
325
- session["createdAt"]),
326
- format_timestamp(
327
- 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]",
328
359
  session_duration,
329
360
  status_display,
330
- f"{session['botStartSeconds']}s" if session["botStartSeconds"] is not None else "[dim]N/A[/dim]",
331
- "[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]",
332
369
  ]
333
370
 
334
371
  if is_cold_start:
@@ -338,10 +375,11 @@ async def sessions(
338
375
 
339
376
  console.success(
340
377
  Group(
341
- Columns(
342
- metric_renderables,
343
- equal=True) if metric_renderables and not session_id else "",
344
- table),
378
+ Columns(metric_renderables, equal=True)
379
+ if metric_renderables and not session_id
380
+ else "",
381
+ table,
382
+ ),
345
383
  title=f"Session data for agent {agent_name} [dim]({org})[/dim]",
346
384
  )
347
385
 
@@ -378,32 +416,16 @@ class LogLevelColors(str, Enum):
378
416
  @synchronizer.create_blocking
379
417
  @requires_login
380
418
  async def logs(
381
- agent_name: str,
382
- organization: str = typer.Option(
383
- None,
384
- "--organization",
385
- "-o",
386
- help="Organization to get status of agent for"),
387
- level: LogLevel = typer.Option(
388
- None,
389
- "--level",
390
- "-l",
391
- help="Level of logs to get"),
392
- format: LogFormat = typer.Option(
393
- LogFormat.TEXT,
394
- "--format",
395
- "-f",
396
- help="Logs format"),
397
- limit: int = typer.Option(
398
- 100,
399
- "--limit",
400
- "-n",
401
- help="Number of logs to get"),
402
- deployment_id: str = typer.Option(
403
- None,
404
- "--deployment",
405
- "-d",
406
- 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
+ ),
407
429
  ):
408
430
  org = organization or config.get("org")
409
431
 
@@ -413,7 +435,9 @@ async def logs(
413
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]",
414
436
  spinner="dots",
415
437
  ):
416
- 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
+ )
417
441
 
418
442
  if not data or not data.get("logs"):
419
443
  console.print("[dim]No logs found for agent[/dim]")
@@ -527,12 +551,13 @@ async def deployments(
527
551
  table.add_column("Updated At")
528
552
 
529
553
  for deployment in data["deployments"]:
554
+ spec = deployment.get("manifest", {}).get("spec", {})
530
555
  table.add_row(
531
- deployment["id"],
532
- deployment["manifest"]["spec"]["dailyNodeType"],
533
- deployment["manifest"]["spec"]["image"],
534
- deployment["createdAt"],
535
- 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"),
536
561
  )
537
562
 
538
563
  console.print(
@@ -624,7 +649,8 @@ async def start(
624
649
  title="Public API Key Required",
625
650
  title_align="left",
626
651
  border_style="yellow",
627
- ))
652
+ )
653
+ )
628
654
 
629
655
  return typer.Exit(1)
630
656
 
@@ -654,7 +680,8 @@ async def start(
654
680
  title=f"[bold]Start Request for agent: {agent_name}[/bold]",
655
681
  title_align="left",
656
682
  border_style="yellow",
657
- ))
683
+ )
684
+ )
658
685
  if not await questionary.confirm(
659
686
  "Are you sure you want to start an active session for this agent?"
660
687
  ).ask_async():
@@ -279,6 +279,19 @@ def create_deploy_command(app: typer.Typer):
279
279
  help="Enable Krisp integration for this deployment",
280
280
  rich_help_panel="Deployment Configuration",
281
281
  ),
282
+ managed_keys: bool = typer.Option(
283
+ False,
284
+ "--enable-managed-keys",
285
+ help="Enable Managed Keys for this deployment",
286
+ rich_help_panel="Deployment Configuration",
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
+ ),
282
295
  skip_confirm: bool = typer.Option(
283
296
  False,
284
297
  "--force",
@@ -345,6 +358,8 @@ def create_deploy_command(app: typer.Typer):
345
358
  else partial_config.scaling.max_agents,
346
359
  )
347
360
  partial_config.enable_krisp = krisp or partial_config.enable_krisp
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
348
363
 
349
364
  # Assert agent name and image are provided
350
365
  if not partial_config.agent_name:
@@ -381,7 +396,9 @@ def create_deploy_command(app: typer.Typer):
381
396
  (f"[bold white]Organization:[/bold white] [green]{org}[/green]"),
382
397
  (f"[bold white]Secret set:[/bold white] {'[dim]None[/dim]' if not partial_config.secret_set else '[green] '+ partial_config.secret_set + '[/green]'}"),
383
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]'}"),
384
400
  (f"[bold white]Krisp:[/bold white] {'[dim]Disabled[/dim]' if not partial_config.enable_krisp else '[green]Enabled[/green]'}"),
401
+ (f"[bold white]Managed Keys:[/bold white] {'[dim]Disabled[/dim]' if not partial_config.enable_managed_keys else '[green]Enabled[/green]'}"),
385
402
  "\n[dim]Scaling configuration:[/dim]",
386
403
  table,
387
404
  *