-
Notifications
You must be signed in to change notification settings - Fork 0
fix(sdk): rename wave -> wave_sdk (stdlib shadow), fix quickstart routes, add fresh-install CI #39
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,78 @@ | ||
| name: smoke install | ||
|
|
||
| # Regression guard for the fresh-install class of bug: builds the wheel from | ||
| # this checkout, installs it (no `-e`, no repo on sys.path) into a throwaway | ||
| # venv, and proves the installed package imports and reaches the live WAVE | ||
| # gateway with the README's own quickstart. Running `pytest` from the repo | ||
| # checkout does NOT catch this class of bug (the checkout dir is first on | ||
| # sys.path and hides an import collision that only appears once the package | ||
| # is actually installed and run from elsewhere) — this workflow is the one | ||
| # gate that runs it the way a real `pip install wave-sdk` user does. | ||
| on: | ||
| pull_request: | ||
| push: | ||
| branches: [main] | ||
| workflow_dispatch: | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| concurrency: | ||
| group: smoke-install-${{ github.ref }} | ||
| cancel-in-progress: true | ||
|
|
||
| jobs: | ||
| smoke: | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 10 | ||
| strategy: | ||
| fail-fast: false | ||
| matrix: | ||
| python-version: ["3.9", "3.12", "3.13"] | ||
| steps: | ||
| - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 | ||
| with: | ||
| persist-credentials: false | ||
|
|
||
| - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 | ||
| with: | ||
| python-version: ${{ matrix.python-version }} | ||
|
|
||
| - name: Build wheel | ||
| run: | | ||
| python -m pip install --upgrade pip | ||
| pip install build | ||
| python -m build --wheel | ||
|
|
||
| - name: Create fresh venv (no repo on sys.path) | ||
| run: python -m venv "$RUNNER_TEMP/smoke" | ||
|
|
||
| - name: Install the built wheel | ||
| run: | | ||
| WHEEL=$(ls dist/*.whl) | ||
| "$RUNNER_TEMP/smoke/bin/pip" install --upgrade pip | ||
| "$RUNNER_TEMP/smoke/bin/pip" install "$WHEEL" | ||
|
|
||
| - name: Import check (installed wheel, run away from the repo) | ||
| working-directory: ${{ runner.temp }}/smoke | ||
| run: | | ||
| bin/python -c " | ||
| import wave_sdk | ||
| print('wave_sdk', wave_sdk.__version__, 'imported from', wave_sdk.__file__) | ||
| from wave_sdk import Wave | ||
| print('Wave facade OK,', len([n for n in dir(wave_sdk) if n.endswith('API')]), 'API classes') | ||
| " | ||
|
|
||
| - name: Copy quickstart into the smoke venv's working directory | ||
| run: cp scripts/smoke_quickstart.py "$RUNNER_TEMP/smoke/smoke_quickstart.py" | ||
|
|
||
| - name: README quickstart (live gateway, real credentials) | ||
| working-directory: ${{ runner.temp }}/smoke | ||
| env: | ||
| WAVE_GATEWAY_API_KEY: ${{ secrets.WAVE_GATEWAY_API_KEY }} | ||
| run: | | ||
| if [ -z "$WAVE_GATEWAY_API_KEY" ]; then | ||
| echo "skipped: WAVE_GATEWAY_API_KEY absent (fork or unset)" | ||
| exit 0 | ||
| fi | ||
| bin/python smoke_quickstart.py |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -1,6 +1,7 @@ | ||||||
| # WAVE SDK for Python | ||||||
|
|
||||||
| Official Python SDK for the WAVE API by WAVE Inc. | ||||||
| Media infrastructure for the agentic internet. Official Python SDK for WAVE, by | ||||||
| WAVE Online, LLC. | ||||||
|
|
||||||
| ## Installation | ||||||
|
|
||||||
|
|
@@ -11,32 +12,19 @@ pip install wave-sdk | |||||
| ## Quick start | ||||||
|
|
||||||
| ```python | ||||||
| from wave import Wave | ||||||
|
|
||||||
| wave = Wave(api_key="your-api-key", organization_id="org_123") | ||||||
|
|
||||||
| # Create and start a live stream | ||||||
| stream = wave.pipeline.create(title="My Stream", protocol="webrtc") | ||||||
| wave.pipeline.start(stream.id) | ||||||
| health = wave.pipeline.get_health(stream.id) | ||||||
| print(f"Viewers: {health['viewer_count']}") | ||||||
|
|
||||||
| # Create a virtual camera from NDI | ||||||
| device = wave.prism.create_device( | ||||||
| name="PTZ Camera 1", | ||||||
| type="camera", | ||||||
| source_protocol="ndi", | ||||||
| source_endpoint="NDI-CAM-1", | ||||||
| node_id="node_abc", | ||||||
| ptz_enabled=True, | ||||||
| ) | ||||||
|
|
||||||
| # Get analytics | ||||||
| viewers = wave.pulse.get_viewer_analytics(time_range="24h") | ||||||
|
|
||||||
| # Send a transcript email (mail:write) and read the usage ledger (meter:read) | ||||||
| wave.mail.transcript_email(to="alice@example.com", transcript="...") | ||||||
| ledger = wave.meter.ledger(channel="mail") | ||||||
| from wave_sdk import Wave | ||||||
|
|
||||||
| client = Wave(api_key="your-api-key", organization_id="org_123") | ||||||
|
Comment on lines
+15
to
+17
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Complete the namespace rename in the API catalog. The quick start now uses 🤖 Prompt for AI Agents |
||||||
|
|
||||||
| # Search your organization's indexed media | ||||||
| results = client.search.search(query="product launch") | ||||||
|
|
||||||
| # List your org's published pricing tiers (requires the pricing:read scope) | ||||||
| manifests = client.pricing.list_manifests() | ||||||
|
|
||||||
| # Transcribe a recording and auto-generate captions for it | ||||||
| transcription = client.transcribe.create(source_url="https://example.com/clip.mp4") | ||||||
| captions = client.captions.generate(media_id=transcription.id, media_type="video") | ||||||
| ``` | ||||||
|
|
||||||
| ## All 42 APIs | ||||||
|
|
@@ -116,10 +104,10 @@ ledger = wave.meter.ledger(channel="mail") | |||||
| ## Error handling | ||||||
|
|
||||||
| ```python | ||||||
| from wave import WaveError, RateLimitError | ||||||
| from wave_sdk import WaveError, RateLimitError | ||||||
|
|
||||||
| try: | ||||||
| wave.pipeline.get("invalid-id") | ||||||
| client.clips.get("invalid-id") | ||||||
| except RateLimitError as e: | ||||||
| print(f"Rate limited. Retry after {e.retry_after}s") | ||||||
| except WaveError as e: | ||||||
|
|
@@ -134,4 +122,4 @@ except WaveError as e: | |||||
|
|
||||||
| ## License | ||||||
|
|
||||||
| MIT - WAVE Inc. | ||||||
| MIT - WAVE Online, LLC | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win Align the README license with
Proposed fix-MIT - WAVE Online, LLC
+Apache License 2.0 - WAVE Online, LLC📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| """CI fresh-install smoke: proves the INSTALLED WHEEL imports and reaches the | ||
| live WAVE gateway (https://api.wave.online), using the first two calls from | ||
| README.md's quickstart (search + pricing) — the ones that resolve to routes | ||
| confirmed live in the public OpenAPI spec and that respond deterministically | ||
| (200, or an auth/scope error) without depending on a real, fetchable media | ||
| URL. Never mocked: this is a real HTTP round trip against production. | ||
|
|
||
| Exit 0 when the SDK reaches the gateway, whether or not the call is fully | ||
| authorized (a 402 Payment Required or a 403 SCOPE_INSUFFICIENT both prove the | ||
| request landed on a real, authenticating route). Exit 1 on anything that | ||
| indicates the *installed package itself* is broken (ImportError, or any | ||
| response that is not a recognized "reached the gateway" shape). | ||
|
|
||
| Invoked by .github/workflows/smoke-install.yml against a wheel built from | ||
| this checkout, installed into a throwaway venv with no repo source on | ||
| sys.path — the class of bug this guards against (the SDK's own top-level | ||
| package shadowing Python's stdlib `wave` module) is invisible to `pytest` | ||
| run from the repo checkout, because the checkout directory being first on | ||
| sys.path masks the collision. Only an install-from-wheel-elsewhere run like | ||
| this one, or a real end user's environment, sees it. | ||
| """ | ||
| from __future__ import annotations | ||
|
|
||
| import os | ||
| import sys | ||
|
|
||
|
|
||
| def main() -> int: | ||
| api_key = os.environ.get("WAVE_GATEWAY_API_KEY") | ||
| if not api_key: | ||
| print("skipped: WAVE_GATEWAY_API_KEY absent (fork or unset)") | ||
| return 0 | ||
|
|
||
| # Import happens after the env-var short-circuit so a fork PR (no secret) | ||
| # still exercises the import path, which is the cheapest and most common | ||
| # way this class of bug shows up. | ||
| from wave_sdk import Wave, WaveError | ||
|
|
||
| client = Wave(api_key=api_key, organization_id="org_123") | ||
|
|
||
| reached_gateway = False | ||
|
|
||
| try: | ||
| results = client.search.search(query="product launch") | ||
| print(f"OK: search.search() -> {len(results.get('results', []))} results") | ||
| reached_gateway = True | ||
| except WaveError as e: | ||
| if e.status_code in (402, 403): | ||
| print(f"OK (reached gateway, gated): search.search() -> {e.status_code} {e.code}") | ||
| reached_gateway = True | ||
| else: | ||
| print(f"FAIL: search.search() -> {e.status_code} {e.code}: {e.message}", file=sys.stderr) | ||
|
|
||
| try: | ||
| client.pricing.list_manifests() | ||
| print("OK: pricing.list_manifests() -> 200") | ||
| reached_gateway = True | ||
| except WaveError as e: | ||
| if e.status_code in (402, 403): | ||
| print(f"OK (reached gateway, gated): pricing.list_manifests() -> {e.status_code} {e.code}") | ||
| reached_gateway = True | ||
| else: | ||
| print(f"FAIL: pricing.list_manifests() -> {e.status_code} {e.code}: {e.message}", file=sys.stderr) | ||
|
|
||
| if not reached_gateway: | ||
| print("FAIL: neither quickstart call reached the gateway", file=sys.stderr) | ||
| return 1 | ||
|
|
||
| print("QUICKSTART OK") | ||
| return 0 | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| sys.exit(main()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Keep pending release notes under
## [Unreleased].Until version 2.1.0 is published to PyPI, move its user-facing changes under
## [Unreleased]. The current dated entry makes pending changes appear released and violates the repository changelog guideline.🤖 Prompt for AI Agents