pipecatcloud 1.3.0__tar.gz → 1.5.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/.github/workflows/publish-pypi.yml +39 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/CHANGELOG.md +115 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/CLAUDE.md +3 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/PKG-INFO +6 -2
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/README.md +4 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/pyproject.toml +2 -2
- pipecatcloud-1.5.0/scripts/release-changelog.py +113 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/src/pipecatcloud/_utils/build_utils.py +11 -4
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/src/pipecatcloud/_utils/deploy_utils.py +2 -1
- pipecatcloud-1.5.0/src/pipecatcloud/_utils/log_utils.py +42 -0
- pipecatcloud-1.5.0/src/pipecatcloud/_utils/regions.py +99 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/src/pipecatcloud/api.py +212 -4
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/src/pipecatcloud/cli/commands/agent.py +6 -8
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/src/pipecatcloud/cli/commands/auth.py +4 -3
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/src/pipecatcloud/cli/commands/deploy.py +7 -10
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/src/pipecatcloud/cli/commands/github.py +142 -39
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/src/pipecatcloud/cli/commands/organizations.py +2 -1
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/src/pipecatcloud/cli/commands/secrets.py +244 -51
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/src/pipecatcloud/cli/entry_point.py +7 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/src/pipecatcloud/config.py +1 -2
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/src/pipecatcloud/constants.py +1 -1
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/src/pipecatcloud.egg-info/PKG-INFO +6 -2
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/src/pipecatcloud.egg-info/SOURCES.txt +6 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/src/pipecatcloud.egg-info/requires.txt +1 -1
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/src/pipecatcloud.egg-info/scm_file_list.json +6 -0
- pipecatcloud-1.5.0/src/pipecatcloud.egg-info/scm_version.json +8 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/tests/test_api_client.py +70 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/tests/test_github_integration.py +346 -6
- pipecatcloud-1.5.0/tests/test_log_redaction.py +472 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/tests/test_regions.py +0 -57
- pipecatcloud-1.5.0/tests/test_secrets_bulk_write.py +450 -0
- pipecatcloud-1.5.0/tests/test_secrets_keys.py +244 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/tests/test_secrets_reference.py +4 -8
- pipecatcloud-1.5.0/tests/test_secrets_retry.py +550 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/uv.lock +604 -479
- pipecatcloud-1.3.0/src/pipecatcloud/_utils/regions.py +0 -89
- pipecatcloud-1.3.0/src/pipecatcloud.egg-info/scm_version.json +0 -8
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/.claude/settings.json +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/.claude/skills/update-docs/SOURCE_DOC_MAPPING.md +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/.github/workflows/format.yml +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/.github/workflows/publish-test.yml +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/.github/workflows/python-compatibility.yml +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/.github/workflows/tests.yml +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/.github/workflows/update-docs.yml +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/.gitignore +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/.pre-commit-config.yaml +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/LICENSE +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/pipecat-cloud.png +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/pytest.ini +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/scripts/fix-ruff.sh +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/setup.cfg +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/src/pipecatcloud/__init__.py +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/src/pipecatcloud/__version__.py +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/src/pipecatcloud/_utils/__init__.py +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/src/pipecatcloud/_utils/async_utils.py +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/src/pipecatcloud/_utils/auth_utils.py +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/src/pipecatcloud/_utils/console_utils.py +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/src/pipecatcloud/_utils/github_utils.py +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/src/pipecatcloud/agent.py +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/src/pipecatcloud/cli/__init__.py +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/src/pipecatcloud/cli/api.py +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/src/pipecatcloud/cli/commands/__init__.py +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/src/pipecatcloud/cli/commands/agent_profiles.py +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/src/pipecatcloud/cli/commands/build.py +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/src/pipecatcloud/cli/commands/docker.py +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/src/pipecatcloud/cli/commands/regions.py +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/src/pipecatcloud/cli/commands/registry_keys.py +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/src/pipecatcloud/cli/commands/spend_limit.py +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/src/pipecatcloud/cli/config.py +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/src/pipecatcloud/exception.py +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/src/pipecatcloud/py.typed +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/src/pipecatcloud/session.py +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/src/pipecatcloud/smallwebrtc/session_manager.py +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/src/pipecatcloud.egg-info/dependency_links.txt +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/src/pipecatcloud.egg-info/entry_points.txt +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/src/pipecatcloud.egg-info/top_level.txt +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/tests/__init__.py +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/tests/conftest.py +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/tests/test_agent_args.py +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/tests/test_agent_profiles_and_resources.py +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/tests/test_agent_sessions.py +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/tests/test_agent_status_output.py +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/tests/test_agent_stop.py +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/tests/test_auth.py +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/tests/test_build_id_display.py +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/tests/test_build_utils.py +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/tests/test_config_file.py +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/tests/test_deploy_architecture.py +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/tests/test_deploy_image_pull_secret.py +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/tests/test_deploy_poll.py +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/tests/test_deploy_referenced_secret_set.py +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/tests/test_deployment_status.py +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/tests/test_docker_commands.py +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/tests/test_exit_codes.py +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/tests/test_json_output.py +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/tests/test_krisp_viva.py +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/tests/test_max_session_duration.py +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/tests/test_organizations.py +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/tests/test_output_modes.py +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/tests/test_pat_auth.py +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/tests/test_regions_lifecycle.py +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/tests/test_secrets_set_referenced.py +0 -0
- {pipecatcloud-1.3.0 → pipecatcloud-1.5.0}/tests/test_spend_limit.py +0 -0
|
@@ -78,3 +78,42 @@ jobs:
|
|
|
78
78
|
verbose: true
|
|
79
79
|
print-hash: true
|
|
80
80
|
repository-url: https://test.pypi.org/legacy/
|
|
81
|
+
|
|
82
|
+
# Runs once the package is installable from PyPI, so the release notification
|
|
83
|
+
# never points at a version nobody can install yet. Notes come from the
|
|
84
|
+
# CHANGELOG.md section at the tag: the changelog is the single source of
|
|
85
|
+
# release notes, reformatted for GitHub by scripts/release-changelog.py. A tag
|
|
86
|
+
# whose version has no changelog section fails the run.
|
|
87
|
+
github-release:
|
|
88
|
+
name: 'Create GitHub Release'
|
|
89
|
+
runs-on: ubuntu-latest
|
|
90
|
+
needs: [publish-to-pypi]
|
|
91
|
+
permissions:
|
|
92
|
+
contents: write
|
|
93
|
+
steps:
|
|
94
|
+
- name: Checkout repo
|
|
95
|
+
uses: actions/checkout@v4
|
|
96
|
+
with:
|
|
97
|
+
ref: ${{ github.event.inputs.gitref }}
|
|
98
|
+
|
|
99
|
+
- name: Set up Python
|
|
100
|
+
uses: actions/setup-python@v5
|
|
101
|
+
with:
|
|
102
|
+
python-version: '3.12'
|
|
103
|
+
|
|
104
|
+
- name: Generate release notes
|
|
105
|
+
env:
|
|
106
|
+
GITREF: ${{ github.event.inputs.gitref }}
|
|
107
|
+
run: |
|
|
108
|
+
python scripts/release-changelog.py --no-heading "${GITREF#v}" > release-notes.md
|
|
109
|
+
|
|
110
|
+
- name: Publish release
|
|
111
|
+
env:
|
|
112
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
113
|
+
GITREF: ${{ github.event.inputs.gitref }}
|
|
114
|
+
run: |
|
|
115
|
+
if gh release view "$GITREF" >/dev/null 2>&1; then
|
|
116
|
+
gh release edit "$GITREF" --notes-file release-notes.md
|
|
117
|
+
else
|
|
118
|
+
gh release create "$GITREF" --title "$GITREF" --notes-file release-notes.md
|
|
119
|
+
fi
|
|
@@ -5,6 +5,121 @@ 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
|
+
## [1.5.0] - 2026-11-05
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- `secrets keys --region <region>` shows your organization's secret keys in a
|
|
13
|
+
region: each key's fingerprint, state, created and retired times, and how
|
|
14
|
+
many secrets it holds, as text or with `--output json`. The fingerprint is
|
|
15
|
+
the one a security notice names and, in a self-hosted region, the one your
|
|
16
|
+
cluster shows for the key. It never shows key material. Against a server
|
|
17
|
+
that does not offer this yet, it says so and exits 1.
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- `secrets set` with several values now writes them in one request, which the
|
|
22
|
+
server applies whole or not at all, so a large set takes one write in place
|
|
23
|
+
of one per key. Against a server that does not take this yet, including one
|
|
24
|
+
part way through an update, it writes one key at a time as before, and still
|
|
25
|
+
says how far it got if one fails.
|
|
26
|
+
- `secrets set`, `secrets unset` and `secrets image-pull-secret` now wait and
|
|
27
|
+
retry when the API answers a write with a 503 and a `Retry-After` of up to
|
|
28
|
+
30 seconds, as a region can on its first secrets write. They retry up to 3 times, say they
|
|
29
|
+
are waiting, and fail as before if the 503 persists. Other errors, and a 503
|
|
30
|
+
without a usable `Retry-After`, are not retried. A `Retry-After` above 30
|
|
31
|
+
seconds is not waited out; the error says how long the region asked for.
|
|
32
|
+
A write that ends in a 503 may still have reached the region, unless the
|
|
33
|
+
error says nothing was stored. Running the same command again settles it.
|
|
34
|
+
|
|
35
|
+
### Fixed
|
|
36
|
+
|
|
37
|
+
- `--region` is now checked against the regions of the organization given
|
|
38
|
+
with `--organization`, not only the organization in your config file. This
|
|
39
|
+
affects `secrets set`, `secrets list`, `secrets image-pull-secret`,
|
|
40
|
+
`secrets reference`, `agent list` and `deploy`. A region that belongs only
|
|
41
|
+
to the other organization, such as a self-hosted region, was refused as
|
|
42
|
+
invalid.
|
|
43
|
+
- `secrets set` no longer reports success when a write gets no answer from the
|
|
44
|
+
server, as when the connection drops. It says so and exits 1.
|
|
45
|
+
|
|
46
|
+
### Security
|
|
47
|
+
|
|
48
|
+
- Logs no longer contain secret values or other request data. With debug
|
|
49
|
+
logging on (`PIPECAT_CLI_LOG_LEVEL=DEBUG` for the CLI, `PCC_LOG_LEVEL=DEBUG`
|
|
50
|
+
for the Python package), `secrets set` wrote the values it was given to
|
|
51
|
+
stderr, and every API request wrote its body and query there, including
|
|
52
|
+
image pull credentials and session data. Requests now log their method,
|
|
53
|
+
path and how much they carry. Tokens were not in these lines. If you ran
|
|
54
|
+
with debug logging on and the output was kept, for example in CI logs,
|
|
55
|
+
change the values it contains.
|
|
56
|
+
- An unexpected error in the CLI could print its local variables in the
|
|
57
|
+
traceback, including secret values and your token. Tracebacks now show code
|
|
58
|
+
only. To see them as before, set `TYPER_STANDARD_TRACEBACK` to an empty value.
|
|
59
|
+
- A failed build context upload now logs its status and S3 error code, not
|
|
60
|
+
S3's error document, which can repeat signed upload details. This line is
|
|
61
|
+
logged at the default level.
|
|
62
|
+
- `secrets set` no longer shows the first characters of each value when it asks
|
|
63
|
+
you to confirm. It shows a fixed mask, the same for any value. Without
|
|
64
|
+
`--skip` and without a terminal, as in CI, it printed those characters and
|
|
65
|
+
then failed; it now fails first. If such output was kept, change the values
|
|
66
|
+
it contains.
|
|
67
|
+
- The locked versions of `virtualenv`, `packaging`, `distlib`, `Pillow` and
|
|
68
|
+
`setuptools` are upgraded for security fixes. These are the versions in
|
|
69
|
+
`uv.lock`, used to develop and test this package. What it requires when you
|
|
70
|
+
install it has not changed, so upgrade your own environment if it holds an
|
|
71
|
+
older one.
|
|
72
|
+
|
|
73
|
+
## [1.4.0] - 2026-10-22
|
|
74
|
+
|
|
75
|
+
### Added
|
|
76
|
+
|
|
77
|
+
- `pipecat cloud github connect --existing` links a GitHub App installation
|
|
78
|
+
that is already on GitHub to your Pipecat Cloud organization. Use it for
|
|
79
|
+
an installation you installed or had an owner approve, while still a
|
|
80
|
+
member of the GitHub organization it is on, or one on your personal
|
|
81
|
+
account or a GitHub organization you own. That covers an install request
|
|
82
|
+
an owner approved and an install made from GitHub, neither of which linked
|
|
83
|
+
before. You authorize the App on GitHub, then confirm the installation to
|
|
84
|
+
link on the Pipecat Cloud dashboard, and the CLI waits up to 14 minutes
|
|
85
|
+
for it. `github status` and the `github connect` timeout message point to
|
|
86
|
+
it.
|
|
87
|
+
|
|
88
|
+
### Changed
|
|
89
|
+
|
|
90
|
+
- Connecting an organization to GitHub now finishes on the Pipecat Cloud
|
|
91
|
+
dashboard, where you may need to sign in as the same Pipecat Cloud user the
|
|
92
|
+
CLI is logged in as. GitHub also asks you to authorize the App after you
|
|
93
|
+
install it. This happens on the server side, so it applies to every CLI
|
|
94
|
+
version. `pipecat cloud github connect` now says so, also with `--json`
|
|
95
|
+
(on stderr), and waits up to 26 minutes for the connection instead of 15,
|
|
96
|
+
to cover both steps. Older versions stop waiting after 15 minutes; the
|
|
97
|
+
connection still completes, and `pipecat cloud github status` shows it.
|
|
98
|
+
- `pipecat cloud github connect` stops waiting as soon as Pipecat Cloud
|
|
99
|
+
refuses the CLI's sign-in or its access to the organization, instead of
|
|
100
|
+
waiting until it times out.
|
|
101
|
+
- The `aiohttp` minimum version is now `3.14.4`, the first release with fixes
|
|
102
|
+
for several security advisories. The SDK makes its API requests with
|
|
103
|
+
`aiohttp`, and `pipecat cloud auth login` uses it for the local server that
|
|
104
|
+
receives the sign-in callback.
|
|
105
|
+
|
|
106
|
+
### Removed
|
|
107
|
+
|
|
108
|
+
- The `login_path` and `login_status_path` settings and their
|
|
109
|
+
`PIPECAT_LOGIN_PATH` and `PIPECAT_LOGIN_STATUS_PATH` overrides. They
|
|
110
|
+
pointed at the device-code sign-in that 0.4.1 replaced with OAuth and
|
|
111
|
+
PKCE, and nothing has read them since, so setting them had no effect.
|
|
112
|
+
Removing them is not a breaking change: the only difference is that they
|
|
113
|
+
no longer appear in `--show-cli-config` output. Pipecat Cloud is retiring
|
|
114
|
+
that sign-in, so a CLI older than 0.4.1 has to be upgraded to log in.
|
|
115
|
+
`pipecat cloud auth login` is unchanged.
|
|
116
|
+
|
|
117
|
+
### Fixed
|
|
118
|
+
|
|
119
|
+
- `pipecat cloud github connect` says it could not reach Pipecat Cloud when
|
|
120
|
+
no usable response comes back, such as on a dropped connection, instead
|
|
121
|
+
of saying the API returned no URL.
|
|
122
|
+
|
|
8
123
|
## [1.3.0] - 2026-10-01
|
|
9
124
|
|
|
10
125
|
### Added
|
|
@@ -103,6 +103,7 @@ pcc-deploy.toml Example deployment config consumed by `pipecat cloud de
|
|
|
103
103
|
- Auth uses OAuth2 with PKCE (RFC 7636). OAuth endpoints are discovered from the API server, not hardcoded. The callback server tries ports 8400-8404.
|
|
104
104
|
- The session argument types in `agent.py` subclass pipecat-ai's runner types and need pipecat-ai (>= 1.0.0); there are no fallback definitions. `MOQSessionArguments` needs 1.12.0 (`MOQRunnerArguments.relay_url`), so it is imported and defined under its own guard: on older pipecat-ai the module still imports and only that name is missing. It stays out of `__all__` for the same reason.
|
|
105
105
|
- Krisp VIVA audio filter names are `Literal["tel", "pro"]` in `constants.py`. These must match the server-side ConfigMap, so coordinate changes with the backend.
|
|
106
|
+
- Never log a request or response body, a header, or an exception's text: they can hold secret values and tokens. Use `_utils/log_utils.py`. `tests/test_log_redaction.py` fails on a log call that reads a name or attribute it knows can hold one, however it is formatted, or that uses `logger.opt`, `logger.exception` or `logger.catch`. Only `describe_*` and `len` calls are exempt.
|
|
106
107
|
|
|
107
108
|
## Release flow
|
|
108
109
|
|
|
@@ -110,6 +111,8 @@ pcc-deploy.toml Example deployment config consumed by `pipecat cloud de
|
|
|
110
111
|
2. Tag the commit with `v<MAJOR>.<MINOR>.<PATCH>`.
|
|
111
112
|
3. Trigger `publish-pypi.yml` manually with that tag. `setuptools_scm` picks up the version from the tag.
|
|
112
113
|
|
|
114
|
+
After publishing to PyPI, the workflow creates (or updates) the GitHub Release for the tag. Its notes are the tag's `CHANGELOG.md` section, reformatted by `scripts/release-changelog.py`, so fix release-note wording in the changelog rather than on GitHub: a re-run overwrites edits made there. Preview the notes locally with `python scripts/release-changelog.py --no-heading 1.3.0`.
|
|
115
|
+
|
|
113
116
|
## Writing style for generated content in this repo
|
|
114
117
|
|
|
115
118
|
- No emdashes.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pipecatcloud
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.5.0
|
|
4
4
|
Summary: Cloud hosting for Pipecat AI applications
|
|
5
5
|
License: BSD 2-Clause License
|
|
6
6
|
Project-URL: Website, https://docs.pipecat.ai/pipecat-cloud
|
|
@@ -8,7 +8,7 @@ Keywords: pipecat,pipecat-ai,voice,ai,llm
|
|
|
8
8
|
Requires-Python: >=3.11
|
|
9
9
|
Description-Content-Type: text/markdown
|
|
10
10
|
License-File: LICENSE
|
|
11
|
-
Requires-Dist: aiohttp<4.0.0,>=3.
|
|
11
|
+
Requires-Dist: aiohttp<4.0.0,>=3.14.4
|
|
12
12
|
Requires-Dist: fastapi<1.0.0,>=0.115.6
|
|
13
13
|
Requires-Dist: loguru~=0.7.3
|
|
14
14
|
Requires-Dist: python-dotenv<2.0.0,>=1.0.1
|
|
@@ -70,6 +70,10 @@ To use Pipecat Cloud programmatically (without the CLI), install the package dir
|
|
|
70
70
|
|
|
71
71
|
4. Build and deploy your agent `pipecat cloud deploy`
|
|
72
72
|
|
|
73
|
+
### Seeing your secret keys
|
|
74
|
+
|
|
75
|
+
`pipecat cloud secrets keys --region <region>` shows your organization's secret keys in a region: each key's fingerprint, state, created and retired times, and how many secrets it holds. The fingerprint is the one a security notice names and, in a self-hosted region, the one your cluster shows for the key, so you can compare them. Add `--output json` before `secrets` for a single JSON document. Key material is never shown.
|
|
76
|
+
|
|
73
77
|
### Usage in Python scripts
|
|
74
78
|
|
|
75
79
|
If want to programmatically start an agent within a Python script, you can use the `pipecatcloud.session` module.
|
|
@@ -47,6 +47,10 @@ To use Pipecat Cloud programmatically (without the CLI), install the package dir
|
|
|
47
47
|
|
|
48
48
|
4. Build and deploy your agent `pipecat cloud deploy`
|
|
49
49
|
|
|
50
|
+
### Seeing your secret keys
|
|
51
|
+
|
|
52
|
+
`pipecat cloud secrets keys --region <region>` shows your organization's secret keys in a region: each key's fingerprint, state, created and retired times, and how many secrets it holds. The fingerprint is the one a security notice names and, in a self-hosted region, the one your cluster shows for the key, so you can compare them. Add `--output json` before `secrets` for a single JSON document. Key material is never shown.
|
|
53
|
+
|
|
50
54
|
### Usage in Python scripts
|
|
51
55
|
|
|
52
56
|
If want to programmatically start an agent within a Python script, you can use the `pipecatcloud.session` module.
|
|
@@ -11,7 +11,7 @@ license = { text = "BSD 2-Clause License" }
|
|
|
11
11
|
readme = "README.md"
|
|
12
12
|
keywords = ["pipecat", "pipecat-ai", "voice", "ai", "llm"]
|
|
13
13
|
dependencies = [
|
|
14
|
-
"aiohttp>=3.
|
|
14
|
+
"aiohttp>=3.14.4,<4.0.0",
|
|
15
15
|
"fastapi>=0.115.6,<1.0.0",
|
|
16
16
|
"loguru~=0.7.3",
|
|
17
17
|
"python-dotenv>=1.0.1,<2.0.0",
|
|
@@ -41,7 +41,7 @@ dev = [
|
|
|
41
41
|
"pytest>=9.0.0,<10",
|
|
42
42
|
"pytest-asyncio>=1.0.0,<2",
|
|
43
43
|
"ruff>=0.12.11,<1",
|
|
44
|
-
"setuptools>=
|
|
44
|
+
"setuptools>=84.1.0,<85",
|
|
45
45
|
"setuptools_scm~=8.3.1",
|
|
46
46
|
# Imported by the tests to compare pipecat-ai versions.
|
|
47
47
|
"packaging>=25.0",
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Generate release notes from CHANGELOG.md for use in GitHub Releases (or anywhere).
|
|
3
|
+
|
|
4
|
+
Usage::
|
|
5
|
+
|
|
6
|
+
python scripts/release-changelog.py VERSION
|
|
7
|
+
python scripts/release-changelog.py 1.1.0
|
|
8
|
+
python scripts/release-changelog.py --file path/to/CHANGELOG.md 1.1.0
|
|
9
|
+
python scripts/release-changelog.py --no-heading 1.1.0
|
|
10
|
+
|
|
11
|
+
Extracts the requested ``## [VERSION]`` section (heading and ``### …``
|
|
12
|
+
subheadings included) and prints it to stdout. The only transformation
|
|
13
|
+
applied is collapsing each entry paragraph onto a single line, so it is
|
|
14
|
+
suitable for pasting into release notes that don't need 80-column wrapping.
|
|
15
|
+
``(PR [...])`` references stay on their own two-space-indented continuation
|
|
16
|
+
line. The input file is never modified.
|
|
17
|
+
|
|
18
|
+
``--no-heading`` drops the ``## [VERSION]`` line, leaving the body to start
|
|
19
|
+
at the first ``### …`` subheading. GitHub Releases carry the version in the
|
|
20
|
+
release title, so the heading would be redundant there.
|
|
21
|
+
|
|
22
|
+
Every paragraph is unfilled. Indentation is preserved — each logical line
|
|
23
|
+
(bullets, sub-bullets, and the ``(PR [...])`` continuation) keeps its
|
|
24
|
+
leading whitespace; only the wrapped continuation lines that follow them
|
|
25
|
+
get joined back. Code-block paragraphs (triple-backtick fences, or deeply
|
|
26
|
+
indented blocks with no list markers around) are passed through untouched.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
from __future__ import annotations
|
|
30
|
+
|
|
31
|
+
import argparse
|
|
32
|
+
import re
|
|
33
|
+
import sys
|
|
34
|
+
from pathlib import Path
|
|
35
|
+
|
|
36
|
+
DEFAULT_CHANGELOG = Path(__file__).resolve().parent.parent / "CHANGELOG.md"
|
|
37
|
+
|
|
38
|
+
PARAGRAPH_SPLIT_RE = re.compile(r"(\n[ \t]*\n+)")
|
|
39
|
+
CONTINUATION_RE = re.compile(r"\n(?![ \t]*[-*+] )[ \t]+")
|
|
40
|
+
PR_RE = re.compile(r" \(PR \[")
|
|
41
|
+
CODE_FENCE_RE = re.compile(r"^\s*```")
|
|
42
|
+
DEEP_INDENT_RE = re.compile(r"^ {4,}")
|
|
43
|
+
LIST_MARKER_RE = re.compile(r"^[ \t]*[-*+] ")
|
|
44
|
+
SECTION_HEADING_RE = re.compile(r"^## \[", re.MULTILINE)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def is_code_block_paragraph(paragraph: str) -> bool:
|
|
48
|
+
lines = paragraph.splitlines()
|
|
49
|
+
if any(CODE_FENCE_RE.match(line) for line in lines):
|
|
50
|
+
return True
|
|
51
|
+
# In a list paragraph, 4-space indent is a sub-bullet continuation, not
|
|
52
|
+
# code. Only treat deep-indented lines as code when no bullets are around
|
|
53
|
+
# to claim them.
|
|
54
|
+
if any(LIST_MARKER_RE.match(line) for line in lines):
|
|
55
|
+
return False
|
|
56
|
+
return any(DEEP_INDENT_RE.match(line) for line in lines)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def unfill_entry(paragraph: str) -> str:
|
|
60
|
+
joined = CONTINUATION_RE.sub(" ", paragraph)
|
|
61
|
+
return PR_RE.sub("\n (PR [", joined, count=1)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def process(text: str) -> str:
|
|
65
|
+
parts = PARAGRAPH_SPLIT_RE.split(text)
|
|
66
|
+
for i in range(0, len(parts), 2):
|
|
67
|
+
para = parts[i]
|
|
68
|
+
if para and not is_code_block_paragraph(para):
|
|
69
|
+
parts[i] = unfill_entry(para)
|
|
70
|
+
return "".join(parts)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def extract_section(text: str, version: str) -> str:
|
|
74
|
+
head_re = re.compile(rf"^## \[{re.escape(version)}\]", re.MULTILINE)
|
|
75
|
+
m = head_re.search(text)
|
|
76
|
+
if not m:
|
|
77
|
+
sys.exit(f"error: version {version!r} not found")
|
|
78
|
+
nxt = SECTION_HEADING_RE.search(text, m.end())
|
|
79
|
+
end = nxt.start() if nxt else len(text)
|
|
80
|
+
return text[m.start() : end]
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def strip_heading(section: str) -> str:
|
|
84
|
+
_, _, body = section.partition("\n")
|
|
85
|
+
return body.lstrip("\n")
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def main() -> None:
|
|
89
|
+
summary = (__doc__ or "").splitlines()[0]
|
|
90
|
+
parser = argparse.ArgumentParser(description=summary)
|
|
91
|
+
parser.add_argument("version", help="Version to extract (e.g. 1.1.0).")
|
|
92
|
+
parser.add_argument(
|
|
93
|
+
"--file",
|
|
94
|
+
type=Path,
|
|
95
|
+
default=DEFAULT_CHANGELOG,
|
|
96
|
+
help=f"Path to the changelog (default: {DEFAULT_CHANGELOG}).",
|
|
97
|
+
)
|
|
98
|
+
parser.add_argument(
|
|
99
|
+
"--no-heading",
|
|
100
|
+
action="store_true",
|
|
101
|
+
help="Omit the '## [VERSION]' heading line.",
|
|
102
|
+
)
|
|
103
|
+
args = parser.parse_args()
|
|
104
|
+
|
|
105
|
+
text = args.file.read_text()
|
|
106
|
+
section = extract_section(text, args.version)
|
|
107
|
+
if args.no_heading:
|
|
108
|
+
section = strip_heading(section)
|
|
109
|
+
sys.stdout.write(process(section))
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
if __name__ == "__main__":
|
|
113
|
+
main()
|
|
@@ -17,6 +17,7 @@ import gzip
|
|
|
17
17
|
import hashlib
|
|
18
18
|
import io
|
|
19
19
|
import os
|
|
20
|
+
import re
|
|
20
21
|
import tarfile
|
|
21
22
|
import time
|
|
22
23
|
from collections.abc import Callable
|
|
@@ -26,6 +27,8 @@ from pathlib import Path
|
|
|
26
27
|
import aiohttp
|
|
27
28
|
from loguru import logger
|
|
28
29
|
|
|
30
|
+
from pipecatcloud._utils.log_utils import describe_exception
|
|
31
|
+
|
|
29
32
|
# Default patterns to exclude from build context
|
|
30
33
|
DEFAULT_EXCLUSIONS: set[str] = {
|
|
31
34
|
# Version control
|
|
@@ -189,7 +192,7 @@ def load_dockerignore(context_dir: Path) -> set[str] | None:
|
|
|
189
192
|
if normalized:
|
|
190
193
|
patterns.add(normalized)
|
|
191
194
|
except Exception as e:
|
|
192
|
-
logger.warning(f"Failed to read .dockerignore: {e}")
|
|
195
|
+
logger.warning(f"Failed to read .dockerignore: {describe_exception(e)}")
|
|
193
196
|
return None
|
|
194
197
|
|
|
195
198
|
return patterns
|
|
@@ -375,11 +378,15 @@ async def upload_to_s3(
|
|
|
375
378
|
logger.debug(f"Upload successful: {response.status}")
|
|
376
379
|
return True
|
|
377
380
|
else:
|
|
378
|
-
|
|
379
|
-
|
|
381
|
+
# S3's error document can echo the signed policy and the
|
|
382
|
+
# credential it was given, so only its code is logged.
|
|
383
|
+
code = re.search(r"<Code>(\w+)</Code>", await response.text())
|
|
384
|
+
logger.error(
|
|
385
|
+
f"Upload failed: {response.status}{f' ({code[1]})' if code else ''}"
|
|
386
|
+
)
|
|
380
387
|
return False
|
|
381
388
|
except Exception as e:
|
|
382
|
-
logger.error(f"Upload error: {e}")
|
|
389
|
+
logger.error(f"Upload error: {describe_exception(e)}")
|
|
383
390
|
return False
|
|
384
391
|
|
|
385
392
|
|
|
@@ -17,6 +17,7 @@ import typer
|
|
|
17
17
|
from attr import dataclass, field
|
|
18
18
|
from loguru import logger
|
|
19
19
|
|
|
20
|
+
from pipecatcloud._utils.log_utils import describe_exception
|
|
20
21
|
from pipecatcloud.cli import PIPECAT_DEPLOY_CONFIG_PATH
|
|
21
22
|
from pipecatcloud.constants import KRISP_VIVA_MODELS, KrispVivaAudioFilter
|
|
22
23
|
from pipecatcloud.exception import ConfigFileError
|
|
@@ -607,7 +608,7 @@ def load_deploy_config_file() -> DeployConfigParams | None:
|
|
|
607
608
|
return validated_config
|
|
608
609
|
|
|
609
610
|
except Exception as e:
|
|
610
|
-
logger.debug(e)
|
|
611
|
+
logger.debug(describe_exception(e))
|
|
611
612
|
raise ConfigFileError(str(e))
|
|
612
613
|
|
|
613
614
|
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (c) 2025, Daily
|
|
3
|
+
#
|
|
4
|
+
# SPDX-License-Identifier: BSD 2-Clause License
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
What a log line may say about a request, a response or a failure.
|
|
9
|
+
|
|
10
|
+
Request and response bodies can hold secret values and credentials, under any name
|
|
11
|
+
and at any depth, and an exception's text can quote them. So a log line says how
|
|
12
|
+
much is there and what kind of thing it is, and never quotes any of it. Headers are
|
|
13
|
+
not logged at all: they carry the token.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def describe_url(url: str) -> str:
|
|
18
|
+
"""A URL without its query string, whose values are not for a log."""
|
|
19
|
+
return url.split("?", 1)[0]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def describe_body(body: object) -> str:
|
|
23
|
+
"""How much a request or response body holds, and never what."""
|
|
24
|
+
if body is None:
|
|
25
|
+
return "none"
|
|
26
|
+
if isinstance(body, dict | list | tuple):
|
|
27
|
+
return f"{type(body).__name__} of {len(body)}"
|
|
28
|
+
return type(body).__name__
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def describe_exception(exc: BaseException) -> str:
|
|
32
|
+
"""An exception's type, and its HTTP status if it has one, without its text."""
|
|
33
|
+
status = getattr(exc, "status", None)
|
|
34
|
+
return (
|
|
35
|
+
f"{type(exc).__name__} (status {status})" if isinstance(status, int) else type(exc).__name__
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def describe_api_error(error: object) -> str:
|
|
40
|
+
"""The code of an error body from the API, without its text."""
|
|
41
|
+
code = error.get("code") if isinstance(error, dict) else None
|
|
42
|
+
return f"code {code}" if code else "no code"
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (c) 2025, Daily
|
|
3
|
+
#
|
|
4
|
+
# SPDX-License-Identifier: BSD 2-Clause License
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
Region utilities for Pipecat Cloud CLI.
|
|
9
|
+
|
|
10
|
+
Provides functions to fetch and cache available regions from the API.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from pipecatcloud.cli.api import API
|
|
14
|
+
from pipecatcloud.cli.config import config
|
|
15
|
+
|
|
16
|
+
# Module-level cache of region lists, keyed by organization. Each organization
|
|
17
|
+
# has its own list (a self-hosted region is visible only to its organization),
|
|
18
|
+
# so one list must never answer for another.
|
|
19
|
+
_regions_cache: dict[str, list[dict[str, str]]] | None = None
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
async def get_regions(org: str | None = None) -> list[dict[str, str]]:
|
|
23
|
+
"""
|
|
24
|
+
Fetch available regions for an organization from the API with caching.
|
|
25
|
+
|
|
26
|
+
The list is cached per organization, so the API is called once per
|
|
27
|
+
organization per CLI session.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
org: Organization to list regions for. Defaults to the organization in
|
|
31
|
+
the config file.
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
List of region objects with 'code' and 'display_name' fields, or an
|
|
35
|
+
empty list when the list cannot be fetched.
|
|
36
|
+
Example: [
|
|
37
|
+
{"code": "us-west", "display_name": "US West"},
|
|
38
|
+
{"code": "eu-central", "display_name": "EU Central"}
|
|
39
|
+
]
|
|
40
|
+
"""
|
|
41
|
+
global _regions_cache
|
|
42
|
+
|
|
43
|
+
org = org or config.get("org")
|
|
44
|
+
if _regions_cache is None:
|
|
45
|
+
_regions_cache = {}
|
|
46
|
+
|
|
47
|
+
if org not in _regions_cache:
|
|
48
|
+
# API returns (data, error) tuple
|
|
49
|
+
data, error = await API.regions(org=org)
|
|
50
|
+
if error or not data:
|
|
51
|
+
return []
|
|
52
|
+
_regions_cache[org] = data
|
|
53
|
+
|
|
54
|
+
return _regions_cache[org]
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
async def region_error(region: str, org: str | None = None) -> str | None:
|
|
58
|
+
"""
|
|
59
|
+
Check a region code against the regions of the organization in use.
|
|
60
|
+
|
|
61
|
+
Fetches the list once. Returns None when the region is valid, otherwise a
|
|
62
|
+
message for the user. When the list cannot be fetched, the message says so
|
|
63
|
+
rather than calling the region invalid. The API client prints its own error
|
|
64
|
+
for the failed call before this message is returned (in json mode that error
|
|
65
|
+
goes to stdout as the error object), so the caller shows both: why the call
|
|
66
|
+
failed, then that the command stopped.
|
|
67
|
+
"""
|
|
68
|
+
org = org or config.get("org")
|
|
69
|
+
regions = await get_regions(org)
|
|
70
|
+
if not regions:
|
|
71
|
+
return (
|
|
72
|
+
f"Could not list the regions of organization '{org}', so region "
|
|
73
|
+
f"'{region}' could not be checked. Not continuing."
|
|
74
|
+
)
|
|
75
|
+
codes = [r["code"] for r in regions]
|
|
76
|
+
if region in codes:
|
|
77
|
+
return None
|
|
78
|
+
return f"Invalid region '{region}'. Valid regions are: {', '.join(codes)}"
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def validate_architecture_for_region(
|
|
82
|
+
architecture: str, region: str, regions: list[dict]
|
|
83
|
+
) -> str | None:
|
|
84
|
+
"""Pre-validate an architecture choice against a region's capability
|
|
85
|
+
(PCC-1105). Returns an error message, or None when the choice is valid
|
|
86
|
+
or cannot be checked locally.
|
|
87
|
+
|
|
88
|
+
The regions payload carries ``supported_architectures`` only on newer
|
|
89
|
+
APIs — when absent, defer to the server's own validation (its 400 names
|
|
90
|
+
the supported list).
|
|
91
|
+
"""
|
|
92
|
+
entry = next((r for r in regions if r.get("code") == region), None)
|
|
93
|
+
supported = (entry or {}).get("supported_architectures")
|
|
94
|
+
if not supported or architecture in supported:
|
|
95
|
+
return None
|
|
96
|
+
return (
|
|
97
|
+
f"Architecture '{architecture}' is not supported in region '{region}' "
|
|
98
|
+
f"(supported: {', '.join(supported)})"
|
|
99
|
+
)
|