Deploy Examples #28
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Deploy Examples | |
| on: | |
| # Manual runs deploy from the branch picked in the Actions "Use workflow | |
| # from" selector β no input needed. | |
| workflow_dispatch: | |
| # Called by publish.yml after a release to deploy the published commit. | |
| workflow_call: | |
| inputs: | |
| ref: | |
| description: "Commit/branch/tag to deploy. Defaults to the triggering ref." | |
| type: string | |
| required: false | |
| permissions: | |
| contents: read | |
| jobs: | |
| deploy: | |
| name: Deploy ${{ matrix.example }} | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| example: [healthcare, survey, frontdesk, drive-thru, inference, avatar] | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| with: | |
| ref: ${{ inputs.ref || github.ref }} | |
| # Fetch Git-LFS assets (e.g. examples/drive-thru/bg_noise.mp3) so the | |
| # real binaries β not pointer files β get uploaded as the build context | |
| # and copied into the image via `COPY . .`. This checkout authenticates | |
| # with GITHUB_TOKEN, so its LFS fetch is allowed (unlike pip's anonymous | |
| # git clone). Without this the agent crashes at runtime decoding a | |
| # 131-byte pointer as audio. | |
| lfs: true | |
| - name: Install LiveKit CLI | |
| run: | | |
| curl -sSL https://get.livekit.io/cli | bash | |
| lk --version | |
| - name: Add LiveKit Cloud project | |
| env: | |
| LIVEKIT_URL: ${{ secrets.LIVEKIT_EXAMPLES_URL }} | |
| LIVEKIT_API_KEY: ${{ secrets.LIVEKIT_EXAMPLES_API_KEY }} | |
| LIVEKIT_API_SECRET: ${{ secrets.LIVEKIT_EXAMPLES_API_SECRET }} | |
| run: | | |
| lk project add examples \ | |
| --url "$LIVEKIT_URL" \ | |
| --api-key "$LIVEKIT_API_KEY" \ | |
| --api-secret "$LIVEKIT_API_SECRET" \ | |
| --default | |
| - name: Regenerate livekit.toml from playground.yaml | |
| run: | | |
| python3 -m pip install --quiet pyyaml | |
| python3 <<'PY' | |
| import yaml | |
| from pathlib import Path | |
| data = yaml.safe_load(Path("examples/playground.yaml").read_text()) | |
| subdomain = data["project"]["subdomain"] | |
| slug = "${{ matrix.example }}" | |
| entry = data["examples"][slug] | |
| toml = ( | |
| "[project]\n" | |
| f' subdomain = "{subdomain}"\n' | |
| "\n" | |
| "[agent]\n" | |
| f' id = "{entry["agent_id"]}"\n' | |
| ) | |
| Path(f"examples/{slug}/livekit.toml").write_text(toml) | |
| print(f"wrote examples/{slug}/livekit.toml") | |
| PY | |
| - name: Pin livekit-* requirements to the deployed ref | |
| working-directory: examples/${{ matrix.example }} | |
| env: | |
| # Build the agent against the code at the ref we're deploying, | |
| # not the latest release on PyPI. Without this, a branch deploy | |
| # would silently run against the published livekit-agents instead | |
| # of the branch's own SDK changes. Mirror the checkout ref above, | |
| # using ref_name so it's a plain branch/tag pip can resolve. | |
| DEPLOY_REF: ${{ inputs.ref || github.ref_name }} | |
| run: | | |
| python3 <<'PY' | |
| import os, re | |
| from pathlib import Path | |
| REF = os.environ["DEPLOY_REF"] | |
| REPO = "git+https://github.com/livekit/agents.git" | |
| REPO_ROOT = Path(os.environ["GITHUB_WORKSPACE"]) | |
| # The package name (and optional [extras]) at the start of a | |
| # requirement, e.g. "livekit-agents[evals]>=1.5.7". | |
| REQUIREMENT = re.compile(r"^(?P<name>[A-Za-z0-9._-]+)(?P<extras>\[.*\])?") | |
| def monorepo_path(name): | |
| """Path of `name` within this repo, or None if it lives elsewhere. | |
| We only pin packages that actually exist in the checkout. The | |
| directory basename is the distribution name for every package | |
| here (livekit-agents at the root, everything else under | |
| livekit-plugins/ β including the irregular ones like | |
| livekit-blingfire). Anything not found (livekit rtc, | |
| livekit-local-inference, β¦) keeps its PyPI pin. | |
| """ | |
| for rel in (name, f"livekit-plugins/{name}"): | |
| if (REPO_ROOT / rel).is_dir(): | |
| return rel | |
| return None | |
| def pin_to_ref(line): | |
| """Repoint an in-repo requirement at the deployed git ref. | |
| External requirements, comments and blanks pass through | |
| untouched (the regex doesn't match a leading '#' or ''). | |
| """ | |
| match = REQUIREMENT.match(line.strip()) | |
| path = monorepo_path(match["name"]) if match else None | |
| if path is None: | |
| return line | |
| return f"{match['name']}{match['extras'] or ''} @ {REPO}@{REF}#subdirectory={path}" | |
| requirements = Path("requirements.txt") | |
| pinned = [pin_to_ref(line) for line in requirements.read_text().splitlines()] | |
| requirements.write_text("\n".join(pinned) + "\n") | |
| print(f"--- requirements.txt pinned to {REF} ---") | |
| print(requirements.read_text()) | |
| PY | |
| - name: Build secrets file | |
| working-directory: examples/${{ matrix.example }} | |
| env: | |
| OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} | |
| LEMONSLICE_API_KEY: ${{ secrets.LEMONSLICE_API_KEY }} | |
| run: | | |
| : > .env.deploy | |
| # Register the agent under a deterministic name so the playground | |
| # can dispatch to it explicitly. Matches the slug in playground.yaml. | |
| echo "LIVEKIT_AGENT_NAME=${{ matrix.example }}" >> .env.deploy | |
| case "${{ matrix.example }}" in | |
| healthcare) | |
| keys="OPENAI_API_KEY" | |
| ;; | |
| avatar) | |
| keys="LEMONSLICE_API_KEY" | |
| ;; | |
| *) | |
| keys="" | |
| ;; | |
| esac | |
| for k in $keys; do | |
| val="${!k}" | |
| if [ -n "$val" ]; then | |
| echo "${k}=${val}" >> .env.deploy | |
| fi | |
| done | |
| - name: Deploy ${{ matrix.example }} | |
| working-directory: examples/${{ matrix.example }} | |
| run: | | |
| args=() | |
| if [ -s .env.deploy ]; then | |
| args+=(--secrets-file .env.deploy) | |
| fi | |
| lk agent deploy "${args[@]}" . |