pipecat-spatialreal 0.1.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.
Files changed (61) hide show
  1. pipecat_spatialreal-0.1.0/.github/workflows/ci.yml +102 -0
  2. pipecat_spatialreal-0.1.0/.github/workflows/compat.yml +130 -0
  3. pipecat_spatialreal-0.1.0/.github/workflows/protobuf-codegen.yml +202 -0
  4. pipecat_spatialreal-0.1.0/.github/workflows/release.yml +75 -0
  5. pipecat_spatialreal-0.1.0/.gitignore +19 -0
  6. pipecat_spatialreal-0.1.0/.python-version +1 -0
  7. pipecat_spatialreal-0.1.0/CHANGELOG.md +25 -0
  8. pipecat_spatialreal-0.1.0/LICENSE +202 -0
  9. pipecat_spatialreal-0.1.0/PKG-INFO +322 -0
  10. pipecat_spatialreal-0.1.0/README.md +283 -0
  11. pipecat_spatialreal-0.1.0/env.example +36 -0
  12. pipecat_spatialreal-0.1.0/examples/README.md +74 -0
  13. pipecat_spatialreal-0.1.0/examples/bot.py +413 -0
  14. pipecat_spatialreal-0.1.0/examples/client/README.md +73 -0
  15. pipecat_spatialreal-0.1.0/examples/client/index.html +199 -0
  16. pipecat_spatialreal-0.1.0/examples/probe/README.md +500 -0
  17. pipecat_spatialreal-0.1.0/examples/probe/capture.jsonl +551 -0
  18. pipecat_spatialreal-0.1.0/examples/probe/capture.wav +0 -0
  19. pipecat_spatialreal-0.1.0/examples/probe/index.html +999 -0
  20. pipecat_spatialreal-0.1.0/examples/probe/sync.js +1333 -0
  21. pipecat_spatialreal-0.1.0/pyproject.toml +108 -0
  22. pipecat_spatialreal-0.1.0/src/pipecat_spatialreal/__init__.py +6 -0
  23. pipecat_spatialreal-0.1.0/src/pipecat_spatialreal/driven.py +387 -0
  24. pipecat_spatialreal-0.1.0/src/pipecat_spatialreal/envelope.py +257 -0
  25. pipecat_spatialreal-0.1.0/src/pipecat_spatialreal/fingerprint.py +117 -0
  26. pipecat_spatialreal-0.1.0/src/pipecat_spatialreal/onset.py +118 -0
  27. pipecat_spatialreal-0.1.0/src/pipecat_spatialreal/processor.py +1379 -0
  28. pipecat_spatialreal-0.1.0/src/pipecat_spatialreal/proto/__init__.py +1 -0
  29. pipecat_spatialreal-0.1.0/src/pipecat_spatialreal/proto/generated/__init__.py +1 -0
  30. pipecat_spatialreal-0.1.0/src/pipecat_spatialreal/proto/generated/avatar_pb2.py +53 -0
  31. pipecat_spatialreal-0.1.0/src/pipecat_spatialreal/proto/generated/avatar_pb2.pyi +118 -0
  32. pipecat_spatialreal-0.1.0/src/pipecat_spatialreal/py.typed +0 -0
  33. pipecat_spatialreal-0.1.0/src/pipecat_spatialreal/version.py +3 -0
  34. pipecat_spatialreal-0.1.0/tests/client_page_check.mjs +366 -0
  35. pipecat_spatialreal-0.1.0/tests/conftest.py +289 -0
  36. pipecat_spatialreal-0.1.0/tests/example_loader.py +54 -0
  37. pipecat_spatialreal-0.1.0/tests/fake_driveningress.py +308 -0
  38. pipecat_spatialreal-0.1.0/tests/fixtures/sync/make_oracle_fixture.py +339 -0
  39. pipecat_spatialreal-0.1.0/tests/fixtures/sync/make_stall_fixture.py +387 -0
  40. pipecat_spatialreal-0.1.0/tests/fixtures/sync/oracle.json +1 -0
  41. pipecat_spatialreal-0.1.0/tests/fixtures/sync/stall.json +1 -0
  42. pipecat_spatialreal-0.1.0/tests/probe_check.mjs +1356 -0
  43. pipecat_spatialreal-0.1.0/tests/test_client_page.py +36 -0
  44. pipecat_spatialreal-0.1.0/tests/test_docstrings.py +256 -0
  45. pipecat_spatialreal-0.1.0/tests/test_driven.py +632 -0
  46. pipecat_spatialreal-0.1.0/tests/test_envelope.py +242 -0
  47. pipecat_spatialreal-0.1.0/tests/test_example_bot.py +336 -0
  48. pipecat_spatialreal-0.1.0/tests/test_example_token_route.py +405 -0
  49. pipecat_spatialreal-0.1.0/tests/test_fake_driveningress.py +223 -0
  50. pipecat_spatialreal-0.1.0/tests/test_fingerprint.py +196 -0
  51. pipecat_spatialreal-0.1.0/tests/test_fingerprint_oracle.py +243 -0
  52. pipecat_spatialreal-0.1.0/tests/test_onset.py +199 -0
  53. pipecat_spatialreal-0.1.0/tests/test_output_transport.py +160 -0
  54. pipecat_spatialreal-0.1.0/tests/test_package.py +9 -0
  55. pipecat_spatialreal-0.1.0/tests/test_play.py +30 -0
  56. pipecat_spatialreal-0.1.0/tests/test_probe_page.py +222 -0
  57. pipecat_spatialreal-0.1.0/tests/test_processor.py +3231 -0
  58. pipecat_spatialreal-0.1.0/tests/test_readme.py +22 -0
  59. pipecat_spatialreal-0.1.0/tests/test_rtvi_smoke.py +330 -0
  60. pipecat_spatialreal-0.1.0/tests/test_stall_rule.py +801 -0
  61. pipecat_spatialreal-0.1.0/tests/test_sync_replay.py +515 -0
@@ -0,0 +1,102 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ jobs:
13
+ test:
14
+ runs-on: ubuntu-latest
15
+ # A run takes about two minutes; a hung one is cut off here instead of at GitHub's six hours.
16
+ timeout-minutes: 15
17
+ strategy:
18
+ fail-fast: false
19
+ matrix:
20
+ # 3.11 is the floor (pipecat-ai >= 1.0 requires it); 3.12 is what the codegen workflow runs.
21
+ python-version: ["3.11", "3.12"]
22
+ steps:
23
+ - uses: actions/checkout@v5
24
+
25
+ - uses: astral-sh/setup-uv@v4
26
+ with:
27
+ python-version: ${{ matrix.python-version }}
28
+
29
+ # tests/probe_check.mjs runs the probe page's analysis under Node, so the test that calls it
30
+ # needs one; the runner image's own Node is not something to count on.
31
+ - uses: actions/setup-node@v7
32
+ with:
33
+ node-version: lts/*
34
+
35
+ # The example extra brings FastAPI and the Pipecat runner, which the token-route tests of
36
+ # examples/bot.py import: without it they are skipped.
37
+ - name: Install (dev and example extras)
38
+ run: uv sync --extra dev --extra example
39
+
40
+ - name: Lint
41
+ run: uv run ruff check .
42
+
43
+ - name: Format check
44
+ run: uv run ruff format --check .
45
+
46
+ - name: Test
47
+ run: uv run pytest
48
+
49
+ - name: Build
50
+ run: uv build
51
+
52
+ # The test job resolves the newest versions on every run. This one resolves the lowest that
53
+ # pyproject.toml declares for every direct dependency, so that the floors users can install
54
+ # (pipecat-ai, spatialreal and protobuf above all) stay tested once newer releases come out.
55
+ floors:
56
+ runs-on: ubuntu-latest
57
+ timeout-minutes: 15
58
+ env:
59
+ # For every uv command of the job: `uv run` locks and syncs again before it runs, and with
60
+ # the default resolution it would bring the newest versions back.
61
+ UV_RESOLUTION: lowest-direct
62
+ steps:
63
+ - uses: actions/checkout@v5
64
+
65
+ - uses: astral-sh/setup-uv@v4
66
+ with:
67
+ python-version: "3.11"
68
+
69
+ - uses: actions/setup-node@v7
70
+ with:
71
+ node-version: lts/*
72
+
73
+ - name: Install the lowest declared versions (dev and example extras)
74
+ run: uv sync --resolution lowest-direct --extra dev --extra example
75
+
76
+ # The resolver moves past a floor that cannot be installed (one that conflicts with another
77
+ # requirement, say) without a word. Fail instead, so that such a floor is raised on purpose.
78
+ - name: Check that every runtime floor is what got installed
79
+ run: |
80
+ uv run python - <<'EOF'
81
+ import importlib.metadata
82
+ import tomllib
83
+
84
+ from packaging.requirements import Requirement
85
+ from packaging.version import Version
86
+
87
+ with open("pyproject.toml", "rb") as file:
88
+ dependencies = tomllib.load(file)["project"]["dependencies"]
89
+ wrong = []
90
+ for line in dependencies:
91
+ requirement = Requirement(line)
92
+ floors = [spec.version for spec in requirement.specifier if spec.operator == ">="]
93
+ installed = importlib.metadata.version(requirement.name)
94
+ print(f"{requirement.name}: declared {requirement.specifier}, installed {installed}")
95
+ if floors and Version(installed) != Version(floors[0]):
96
+ wrong.append(requirement.name)
97
+ if wrong:
98
+ raise SystemExit(f"Not installed at the declared floor: {', '.join(wrong)}")
99
+ EOF
100
+
101
+ - name: Test
102
+ run: uv run pytest
@@ -0,0 +1,130 @@
1
+ name: Pipecat compatibility
2
+
3
+ # Once a week, runs the tests against the newest pipecat-ai that pyproject.toml allows, to learn
4
+ # early when a Pipecat release breaks this package.
5
+ #
6
+ # When the tests fail, the job fails and the run is the report: nothing is changed. When they pass
7
+ # on a release newer than the one the README says the package was tested with ("Tested with Pipecat
8
+ # vX.Y.Z"), a second job opens a pull request that moves that line.
9
+ #
10
+ # The two jobs are kept apart because the first runs freshly resolved third-party code (the newest
11
+ # Pipecat and whatever it pulls in). It holds a read-only token and nothing else; its checkout does
12
+ # not keep even that. The second holds the write token and runs no dependency code: it checks out
13
+ # the repository without keeping the token, edits one line of the README itself, and the pull
14
+ # request can carry README.md and nothing else.
15
+ #
16
+ # The pull request is opened with the workflow's own token, so GitHub starts no other workflow on
17
+ # it. The repository setting "Allow GitHub Actions to create and approve pull requests" must be on.
18
+ #
19
+ # workflow_dispatch only finds workflows that are on the default branch.
20
+
21
+ on:
22
+ schedule:
23
+ - cron: "23 6 * * 1" # Mondays, 06:23 UTC
24
+ workflow_dispatch:
25
+
26
+ permissions:
27
+ contents: read
28
+
29
+ jobs:
30
+ check:
31
+ runs-on: ubuntu-latest
32
+ # A run takes a few minutes; a hung one is cut off here instead of at GitHub's six hours.
33
+ timeout-minutes: 15
34
+ # The job's token, which setup-uv also receives by default (its github-token input), can only
35
+ # read.
36
+ permissions:
37
+ contents: read
38
+ outputs:
39
+ installed: ${{ steps.version.outputs.installed }}
40
+ bump: ${{ steps.version.outputs.bump }}
41
+ steps:
42
+ - uses: actions/checkout@v5
43
+ with:
44
+ persist-credentials: false
45
+
46
+ - uses: astral-sh/setup-uv@v4
47
+ with:
48
+ python-version: "3.12"
49
+
50
+ # tests/probe_check.mjs runs the probe page's analysis under Node.
51
+ - uses: actions/setup-node@v7
52
+ with:
53
+ node-version: lts/*
54
+
55
+ # Libraries resolve fresh here (uv.lock is not committed), and --upgrade-package makes the
56
+ # newest pipecat-ai win even when a lock file has been restored from somewhere. Resolving
57
+ # again, rather than upgrading the installed package alone, keeps the dependencies of the
58
+ # extras in step with the new release.
59
+ - name: Install the newest Pipecat that pyproject.toml allows
60
+ run: uv sync --extra dev --extra example --upgrade-package pipecat-ai
61
+
62
+ # The suite includes tests/test_rtvi_smoke.py, which sends the avatar messages through RTVI
63
+ # and Pipecat's eval transport.
64
+ - name: Test
65
+ run: uv run pytest -q
66
+
67
+ - name: Compare with the README
68
+ id: version
69
+ run: |
70
+ installed="$(uv run python -c 'import importlib.metadata as m; print(m.version("pipecat-ai"))')"
71
+ stated="$(grep -oE 'Tested with Pipecat v[0-9]+(\.[0-9]+)*' README.md | head -n 1 | sed 's/^.* v//')"
72
+ if [ -z "$stated" ]; then
73
+ echo "README.md has no 'Tested with Pipecat vX.Y.Z' line" >&2
74
+ exit 1
75
+ fi
76
+ echo "The README says v${stated}; the tests passed on v${installed}."
77
+ newest="$(printf '%s\n%s\n' "$stated" "$installed" | sort -V | tail -n 1)"
78
+ {
79
+ echo "installed=${installed}"
80
+ if [ "$installed" != "$stated" ] && [ "$newest" = "$installed" ]; then
81
+ echo "bump=true"
82
+ else
83
+ echo "bump=false"
84
+ fi
85
+ } >> "$GITHUB_OUTPUT"
86
+
87
+ pr:
88
+ needs: check
89
+ if: needs.check.outputs.bump == 'true'
90
+ runs-on: ubuntu-latest
91
+ timeout-minutes: 10
92
+ permissions:
93
+ contents: write
94
+ pull-requests: write
95
+ steps:
96
+ - uses: actions/checkout@v5
97
+ with:
98
+ persist-credentials: false
99
+
100
+ # The version comes from the job that ran third-party code, so it is checked before it is
101
+ # used anywhere, and it goes through env, not into the script text.
102
+ - name: Move the README to the tested version
103
+ env:
104
+ INSTALLED: ${{ needs.check.outputs.installed }}
105
+ run: |
106
+ if ! [[ "$INSTALLED" =~ ^[0-9]+(\.[0-9]+)*$ ]]; then
107
+ echo "Not a release version: '${INSTALLED}'" >&2
108
+ exit 1
109
+ fi
110
+ sed -E "s/Tested with Pipecat v[0-9]+(\.[0-9]+)*/Tested with Pipecat v${INSTALLED}/" README.md > README.md.new
111
+ mv README.md.new README.md
112
+ git diff --stat
113
+
114
+ - name: Open a pull request
115
+ uses: peter-evans/create-pull-request@v7
116
+ with:
117
+ token: ${{ github.token }}
118
+ # Only the README goes into the pull request, whatever else is in the working tree.
119
+ add-paths: README.md
120
+ branch: compat/pipecat-${{ needs.check.outputs.installed }}
121
+ delete-branch: true
122
+ commit-message: "docs: tested with Pipecat v${{ needs.check.outputs.installed }}"
123
+ title: "docs: tested with Pipecat v${{ needs.check.outputs.installed }}"
124
+ body: |
125
+ The weekly compatibility check ran the tests against pipecat-ai
126
+ ${{ needs.check.outputs.installed }} and they passed, so the README now says the
127
+ package was tested with it.
128
+
129
+ Nothing else changed. The check does not raise the `pipecat-ai` floor in
130
+ `pyproject.toml`.
@@ -0,0 +1,202 @@
1
+ name: Protobuf Codegen
2
+
3
+ # Regenerates src/pipecat_spatialreal/proto/generated/ and commits it once the gates pass.
4
+ #
5
+ # Trigger: shared-proto's tag CI dispatches this workflow; it can also be run by hand here.
6
+ # Pushes and PRs do not trigger it: the contract is released only when shared-proto is tagged.
7
+ # workflow_dispatch only finds workflows that are on the default branch.
8
+ #
9
+ # Auth: the org GitHub App `spatialreal-codegen` (secrets CODEGEN_APP_ID /
10
+ # CODEGEN_APP_PRIVATE_KEY), not a personal token. The App installation must include this repo and
11
+ # shared-proto.
12
+ #
13
+ # The App can reach every repository of its installation, so neither its key nor a token of its own
14
+ # is ever in a job that runs third-party code. A job is the unit that matters: GitHub gives a job
15
+ # every secret it names when the job starts. Three jobs, in order:
16
+ #
17
+ # contract reads the contract from shared-proto with a token that can only read that repository,
18
+ # and hands pipecat/v1/avatar.proto on. It runs GitHub's own actions and git, nothing
19
+ # else.
20
+ # generate installs the dev extra (freshly resolved third-party packages), generates the code and
21
+ # runs the gates. It names no secret, holds a read-only token and keeps none of it in
22
+ # the checkout, and hands the two generated files on.
23
+ # publish takes exactly those two files, and only then mints a token that can only write this
24
+ # repository, used by the push alone. It runs no dependency code.
25
+
26
+ on:
27
+ workflow_dispatch:
28
+ inputs:
29
+ shared_proto_ref:
30
+ description: shared-proto ref (tag / branch / sha)
31
+ type: string
32
+ required: false
33
+ default: main
34
+
35
+ permissions:
36
+ contents: read
37
+
38
+ jobs:
39
+ contract:
40
+ runs-on: ubuntu-latest
41
+ timeout-minutes: 10
42
+ outputs:
43
+ short_sha: ${{ steps.ref.outputs.short_sha }}
44
+ steps:
45
+ - name: Mint a token that can only read shared-proto
46
+ id: app-token
47
+ uses: actions/create-github-app-token@v3
48
+ with:
49
+ app-id: ${{ secrets.CODEGEN_APP_ID }}
50
+ private-key: ${{ secrets.CODEGEN_APP_PRIVATE_KEY }}
51
+ owner: ${{ github.repository_owner }}
52
+ repositories: shared-proto
53
+ permission-contents: read
54
+
55
+ - name: Checkout shared-proto
56
+ uses: actions/checkout@v5
57
+ with:
58
+ repository: SpatialReal-ai/shared-proto
59
+ ref: ${{ inputs.shared_proto_ref || 'main' }}
60
+ token: ${{ steps.app-token.outputs.token }}
61
+ persist-credentials: false
62
+ path: proto-src
63
+
64
+ - name: Record the commit
65
+ id: ref
66
+ run: echo "short_sha=$(git -C proto-src rev-parse --short HEAD)" >> "$GITHUB_OUTPUT"
67
+
68
+ # Only the file this package is generated from: the rest of shared-proto stays out of the job
69
+ # that runs third-party code. Should avatar.proto ever import another file, protoc names it
70
+ # and it is added here.
71
+ - uses: actions/upload-artifact@v7
72
+ with:
73
+ name: contract
74
+ path: proto-src/pipecat/v1/avatar.proto
75
+ if-no-files-found: error
76
+ retention-days: 1
77
+
78
+ generate:
79
+ needs: contract
80
+ runs-on: ubuntu-latest
81
+ # A run takes a few minutes; a hung one is cut off here instead of at GitHub's six hours.
82
+ timeout-minutes: 15
83
+ steps:
84
+ - uses: actions/checkout@v5
85
+ with:
86
+ persist-credentials: false
87
+
88
+ - uses: actions/download-artifact@v8
89
+ with:
90
+ name: contract
91
+ path: proto-src/pipecat/v1
92
+
93
+ # No pip cache: what a third-party package left in it would come back in later runs.
94
+ - uses: actions/setup-python@v5
95
+ with:
96
+ python-version: "3.12"
97
+
98
+ # tests/probe_check.mjs runs the probe page's analysis under Node.
99
+ - uses: actions/setup-node@v7
100
+ with:
101
+ node-version: lts/*
102
+
103
+ # grpcio-tools is pinned in the dev extra: it decides the gencode protobuf version, which
104
+ # must stay <= the `protobuf>=` floor in pyproject.toml, or an older runtime refuses to
105
+ # import the generated code. Bump the pin together with that floor.
106
+ - name: Install (dev extra)
107
+ run: python -m pip install -e ".[dev]"
108
+
109
+ - name: Generate
110
+ run: |
111
+ out="$(mktemp -d)"
112
+ gen=src/pipecat_spatialreal/proto/generated
113
+ python -m grpc_tools.protoc -I proto-src \
114
+ --python_out="$out" --pyi_out="$out" pipecat/v1/avatar.proto
115
+ rm -f "$gen/avatar_pb2.py" "$gen/avatar_pb2.pyi"
116
+ mv "$out/pipecat/v1/avatar_pb2.py" "$out/pipecat/v1/avatar_pb2.pyi" "$gen/"
117
+ rm -rf proto-src "$out"
118
+ git status --short "$gen"
119
+
120
+ # Gate: the package still imports (the generated module loads against the installed protobuf
121
+ # runtime) and the tests pass. A failure usually means plugin code relies on something the
122
+ # generated code no longer has — fix that first, then re-run.
123
+ - name: Gate — import smoke test
124
+ run: python -c "import pipecat_spatialreal"
125
+
126
+ - name: Gate — unit tests
127
+ run: python -m pytest tests -q
128
+
129
+ # The contract lives in shared-proto; this repo holds generated code only.
130
+ - name: Gate — generated code only
131
+ run: |
132
+ if git ls-files --others --cached --exclude-standard '*.proto' | grep .; then
133
+ echo "Only generated code is committed here." >&2
134
+ exit 1
135
+ fi
136
+
137
+ - uses: actions/upload-artifact@v7
138
+ with:
139
+ name: generated
140
+ path: |
141
+ src/pipecat_spatialreal/proto/generated/avatar_pb2.py
142
+ src/pipecat_spatialreal/proto/generated/avatar_pb2.pyi
143
+ if-no-files-found: error
144
+ retention-days: 1
145
+
146
+ publish:
147
+ needs: [contract, generate]
148
+ runs-on: ubuntu-latest
149
+ timeout-minutes: 10
150
+ steps:
151
+ - uses: actions/checkout@v5
152
+ with:
153
+ persist-credentials: false
154
+
155
+ - uses: actions/download-artifact@v8
156
+ with:
157
+ name: generated
158
+ path: ${{ runner.temp }}/generated
159
+
160
+ # The two generated files by name, whatever else the artifact might hold.
161
+ - name: Take the generated code
162
+ id: take
163
+ run: |
164
+ gen=src/pipecat_spatialreal/proto/generated
165
+ cp "$RUNNER_TEMP/generated/avatar_pb2.py" "$RUNNER_TEMP/generated/avatar_pb2.pyi" "$gen/"
166
+ git status --short
167
+ if git diff --quiet; then
168
+ echo "No changes."
169
+ echo "changed=false" >> "$GITHUB_OUTPUT"
170
+ else
171
+ echo "changed=true" >> "$GITHUB_OUTPUT"
172
+ fi
173
+
174
+ # Minted last, and with no owner given, for this repository alone.
175
+ - name: Mint a token that can only write this repository
176
+ if: steps.take.outputs.changed == 'true'
177
+ id: app-token
178
+ uses: actions/create-github-app-token@v3
179
+ with:
180
+ app-id: ${{ secrets.CODEGEN_APP_ID }}
181
+ private-key: ${{ secrets.CODEGEN_APP_PRIVATE_KEY }}
182
+ permission-contents: write
183
+
184
+ # The ref goes through env, not into the script text: a tag name is not trusted input. The
185
+ # token reaches git through a credential helper for this one push: it is never written to the
186
+ # git config and never appears on a command line.
187
+ - name: Commit and push
188
+ if: steps.take.outputs.changed == 'true'
189
+ env:
190
+ SHARED_PROTO_REF: ${{ inputs.shared_proto_ref || 'main' }}
191
+ SHORT_SHA: ${{ needs.contract.outputs.short_sha }}
192
+ PUSH_TOKEN: ${{ steps.app-token.outputs.token }}
193
+ run: |
194
+ git config user.name "github-actions[bot]"
195
+ git config user.email "github-actions[bot]@users.noreply.github.com"
196
+ git add src/pipecat_spatialreal/proto/generated/avatar_pb2.py \
197
+ src/pipecat_spatialreal/proto/generated/avatar_pb2.pyi
198
+ git commit -m "chore(proto): regenerate from shared-proto@${SHARED_PROTO_REF} (${SHORT_SHA})"
199
+ # shellcheck disable=SC2016 # the helper's shell expands $1 and $PUSH_TOKEN, not this one
200
+ git -c credential.helper= \
201
+ -c 'credential.helper=!f() { test "$1" = get && printf "username=x-access-token\npassword=%s\n" "$PUSH_TOKEN"; }; f' \
202
+ push
@@ -0,0 +1,75 @@
1
+ name: Release
2
+
3
+ # Publishing a GitHub Release triggers this; pushing the tag alone does not. `published` is the
4
+ # type that fires for a release made in the GitHub UI (`created` is skipped for a draft that is
5
+ # published later). Publishing uses PyPI trusted publishing (OIDC), so there is no API token:
6
+ # PyPI holds a publisher for SpatialReal-ai/pipecat-spatialreal, workflow `release.yml`,
7
+ # environment `pypi`. Those three must stay as they are, or the upload is refused.
8
+
9
+ on:
10
+ release:
11
+ types: [published]
12
+
13
+ permissions:
14
+ contents: read
15
+
16
+ jobs:
17
+ build:
18
+ runs-on: ubuntu-latest
19
+ timeout-minutes: 15
20
+ steps:
21
+ - uses: actions/checkout@v5
22
+
23
+ - uses: astral-sh/setup-uv@v4
24
+ with:
25
+ python-version: "3.11"
26
+
27
+ - name: Build sdist and wheel
28
+ run: uv build
29
+
30
+ # The release tag names the version PyPI will show forever; refuse a tag that does not
31
+ # match the version the package was built with (src/pipecat_spatialreal/version.py).
32
+ - name: Check the release tag against the built version
33
+ env:
34
+ TAG: ${{ github.event.release.tag_name }}
35
+ run: |
36
+ for whl in dist/*.whl; do
37
+ built=$(basename "$whl" | sed -E 's/^pipecat_spatialreal-([^-]+)-.*/\1/')
38
+ done
39
+ echo "tag=$TAG built=$built"
40
+ test "$TAG" = "v$built" || { echo "release tag $TAG does not match the built version $built"; exit 1; }
41
+
42
+ # The generated protobuf module is what a broken build drops silently, and the package
43
+ # imports it at load time. Install the built wheel, not the source tree, into a fresh
44
+ # environment and import it from outside the checkout.
45
+ - name: Smoke-test the built wheel in a fresh environment
46
+ run: |
47
+ uv venv "$RUNNER_TEMP/wheel-venv"
48
+ uv pip install --python "$RUNNER_TEMP/wheel-venv/bin/python" dist/*.whl
49
+ cd "$RUNNER_TEMP"
50
+ "$RUNNER_TEMP/wheel-venv/bin/python" -c "import pipecat_spatialreal, pipecat_spatialreal.proto.generated.avatar_pb2; print(pipecat_spatialreal.__file__)"
51
+
52
+ - uses: actions/upload-artifact@v4
53
+ with:
54
+ name: dist
55
+ path: dist/
56
+ if-no-files-found: error
57
+
58
+ publish:
59
+ needs: build
60
+ runs-on: ubuntu-latest
61
+ timeout-minutes: 10
62
+ environment: pypi
63
+ permissions:
64
+ # id-token for PyPI trusted publishing; contents because naming any permission drops the
65
+ # defaults for the rest.
66
+ id-token: write
67
+ contents: read
68
+ steps:
69
+ - uses: actions/download-artifact@v4
70
+ with:
71
+ name: dist
72
+ path: dist/
73
+
74
+ - name: Publish to PyPI
75
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,19 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.egg-info/
4
+ build/
5
+ dist/
6
+ .pytest_cache/
7
+ .ruff_cache/
8
+ .DS_Store
9
+ .env
10
+
11
+ # Libraries resolve fresh in CI (see ci.yml) instead of pinning through a lock file.
12
+ uv.lock
13
+
14
+ # The contract lives in shared-proto; this repo holds generated code only. The codegen
15
+ # workflow refuses any committed .proto, this keeps one from being added by accident.
16
+ *.proto
17
+
18
+ # hack/sync-sim/thrsearch_stall.py's data cache, if one is pointed into the repo.
19
+ hack/sync-sim/*.pkl
@@ -0,0 +1 @@
1
+ 3.12
@@ -0,0 +1,25 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project
6
+ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.1.0] - 2026-11-09
9
+
10
+ First release. Tested with Pipecat v1.15.0, the SpatialReal Python SDK 0.3.0 and the SpatialReal
11
+ web SDK 1.0.0-beta.6, on Daily and LiveKit.
12
+
13
+ ### Added
14
+
15
+ - `SpatialRealAvatarProcessor`: place it right after the TTS service. It streams the bot's speech
16
+ to SpatialReal for facial animation, sends the animation to the browser as RTVI server messages,
17
+ and releases the audio in step with it. The SpatialReal web SDK (1.0.0-beta.6 or newer) renders
18
+ the avatar in the browser, so the pipeline carries no video.
19
+ - Audio and animation stay in sync: the web SDK lines the animation up with the audio the browser
20
+ actually receives.
21
+ - Interruptions stop the avatar together with the voice. If SpatialReal cannot be reached, the bot
22
+ keeps talking without animation and the processor reconnects.
23
+ - Works with Pipecat's Daily and LiveKit transports.
24
+ - An example bot (`examples/bot.py`) and web page (`examples/client`). The bot gives the browser a
25
+ short-lived session token, so the API key stays on the server.