feat(grpc-web): Pyodide/WASM grpc-web transport for the async client - #2142
Open
g-despot wants to merge 3 commits into
Open
feat(grpc-web): Pyodide/WASM grpc-web transport for the async client#2142g-despot wants to merge 3 commits into
g-despot wants to merge 3 commits into
Conversation
There was a problem hiding this comment.
Orca Security Scan Summary
| Status | Check | Issues by priority | |
|---|---|---|---|
| Secrets | View in Orca |
Lets the async client run inside Pyodide/WebAssembly, where grpcio has no wheel and sockets do not exist: gRPC goes over grpc-web (fetch) and REST over the browser's fetch, against Weaviate core's native /v1/grpc-web endpoint (1.38.3+). - packages/web: the weaviate-client-web companion distribution (pure-Python grpc shim, GrpcWebChannel, grpc-web framing, pyfetch/httpx senders, httpx-over-fetch transport) - weaviate/__init__.py: a bare `import weaviate` bootstraps the companion under Emscripten; a missing companion raises an install hint - setup.cfg: grpcio is skipped under Emscripten - connect/base.py: grpc_path_prefix on ConnectionParams (port collision allowed with a prefix), fail-fast check at client construction for a sync client or a missing shim, grpc-web.path_prefix channel option for the shim - connect/helpers.py: under Emscripten the async helpers pin gRPC to the REST endpoint under /v1/grpc-web (Con006 when a caller's gRPC endpoint is discarded); unchanged elsewhere - connect/v4.py: sync client rejected at construction under Emscripten; the gRPC ping error is passed through so WeaviateGRPCUnavailableError can name the real cause (grpc-web branch without firewall/port advice; 404 names server < 1.38.3 or a wrong prefix); fetch failures of the pypi version check are ignored - collections/batch/async_.py: batch.stream() fails fast under grpc-web, pointing to insert_many() - embedded.py: explicit error under Emscripten; proto/v1: grpcio version fallback when dist metadata is absent (Emscripten only), drift-pinned by proto_test - CI: grpc-web package tests (3.10-3.14) and a real-Pyodide e2e job under Node Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GUNU7QgDr9MmFZnjKY9zFN
g-despot
force-pushed
the
fix/token-refresh-lifecycle
branch
from
August 21, 2026 13:26
0b58414 to
21dc4b5
Compare
g-despot
force-pushed
the
feat/grpc-web-wasm
branch
from
August 21, 2026 13:26
6705ab1 to
cc13ad9
Compare
Comment/docstring wording only, no code changes: shorter sentences, plainer words (shim -> replacement, honour -> use, multiplexed -> shares, discarded -> ignored, REST listener -> REST endpoint), same meaning. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GUNU7QgDr9MmFZnjKY9zFN
Contributor
There was a problem hiding this comment.
Pull request overview
Adds Pyodide/WASM support to the async client through a companion grpc-web and browser-fetch transport.
Changes:
- Adds the
weaviate-client-webgrpc-web transport and shim. - Integrates automatic WASM routing, diagnostics, and unsupported-feature guards.
- Adds unit and Pyodide end-to-end CI coverage.
Reviewed changes
Copilot reviewed 32 out of 34 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
weaviate/warnings.py |
Adds endpoint-override warning. |
weaviate/proto/v1/__init__.py |
Adds Emscripten grpcio fallback. |
weaviate/exceptions.py |
Adds grpc-web diagnostics. |
weaviate/embedded.py |
Rejects embedded mode under WASM. |
weaviate/connect/v4.py |
Integrates grpc-web lifecycle and errors. |
weaviate/connect/helpers.py |
Routes async helpers through grpc-web. |
weaviate/connect/base.py |
Adds grpc-web connection parameters. |
weaviate/collections/batch/async_.py |
Rejects streaming batches over grpc-web. |
weaviate/__init__.py |
Bootstraps the companion package. |
test/test_wasm_compat.py |
Tests WASM-specific behavior. |
test/test_connection_params.py |
Tests grpc-web connection configuration. |
setup.cfg |
Excludes grpcio under Emscripten. |
pyrightconfig.json |
Type-checks the web package. |
proto_test/test_proto.py |
Tests grpcio fallback compatibility. |
packages/web/tests/test_transport.py |
Tests grpc-web transport behavior. |
packages/web/tests/test_single_import.py |
Tests automatic bootstrap. |
packages/web/tests/test_shim_install.py |
Tests shim installation. |
packages/web/tests/test_httpx_fetch.py |
Tests fetch-based REST transport. |
packages/web/tests/test_framing.py |
Tests grpc-web framing. |
packages/web/tests/conftest.py |
Configures package test imports. |
packages/web/src/weaviate_client_web/py.typed |
Marks the package as typed. |
packages/web/src/weaviate_client_web/_shim.py |
Implements the grpc API shim. |
packages/web/src/weaviate_client_web/_sender.py |
Implements HTTP senders. |
packages/web/src/weaviate_client_web/_httpx_fetch.py |
Implements fetch-backed HTTPX transport. |
packages/web/src/weaviate_client_web/_framing.py |
Implements grpc-web framing. |
packages/web/src/weaviate_client_web/_channel.py |
Implements the grpc-web channel. |
packages/web/src/weaviate_client_web/__init__.py |
Exposes and bootstraps the package. |
packages/web/README.md |
Documents usage and limitations. |
packages/web/pyproject.toml |
Defines companion package metadata. |
ci/pyodide-e2e/run.mjs |
Runs tests inside Pyodide. |
ci/pyodide-e2e/package.json |
Pins the Pyodide runtime. |
ci/pyodide-e2e/e2e.py |
Exercises WASM client workflows. |
.gitignore |
Ignores web-package build artifacts. |
.github/workflows/main.yaml |
Adds grpc-web and Pyodide CI jobs. |
Suppressed comments (1)
weaviate/connect/helpers.py:538
- This value comparison cannot tell an omitted argument from an explicitly supplied
grpc_port=50051(which the helper's own examples use). Under WASM that explicitly requested endpoint is still replaced by the REST port, but Con006 is suppressed, contrary to the documented promise that caller-supplied ports are warned about. Preserve argument presence with a sentinel default, then resolve the omitted value to 50051.
grpc_chosen_by_caller=grpc_port != _LOCAL_GRPC_PORT_DEFAULT,
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
…on006 - _encode_timeout/_abort_signal_ms: a huge finite timeout (e.g. 1e308) overflowed to infinity in the millisecond multiplication and raised OverflowError; both now compare against their cap before multiplying (no deadline / capped, as documented) - the "server too old / wrong grpc-web path" diagnosis now also requires the channel's synthetic "HTTP 404"/"HTTP 405" marker in the details, so a genuine UNIMPLEMENTED from a routed endpoint gets the generic message instead; the string contract is noted on both sides - Con006 prints grpc:// / grpcs:// schemes on both endpoints, so a mismatch in grpc_secure alone no longer shows two identical strings Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GUNU7QgDr9MmFZnjKY9zFN
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
Lets the async client run inside Pyodide/WebAssembly (marimo notebooks, browser, WASM workers), where
grpciohas no wheel and sockets don't exist. gRPC is re-routed over grpc-web (fetch), REST over the browser'sfetch— against Weaviate core's native/v1/grpc-webendpoint (default-on since 1.38.3).This is the trimmed successor of #2056 (kept open for reference): only what the WASM goal actually needs. The general fixes that were bundled there now ship separately:
fix/token-refresh-lifecycle): the async OIDC refresher must be an asyncio task because threads cannot start under Pyodide.Quickstart (Pyodide / marimo)
Under Emscripten all three async helpers (
use_async_with_local/_weaviate_cloud/_custom) pin gRPC to the REST endpoint under/v1/grpc-webthemselves — the same contract as the TS@weaviate/webclient'swebify(). There is nogrpc_path_prefixparameter on the helpers: pass gRPC arguments equal to the HTTP ones; anything else is discarded with aCon006warning. A grpc-web transcoder on a separate endpoint needs hand-builtConnectionParams(..., grpc_path_prefix=...). Off Emscripten nothing changes: the helpers build exactly the params they always did (pinned bytest_helper_params_off_emscripten_are_unchanged).Key pieces
packages/web/— companion distributionweaviate-client-web(unchanged from #2056):_shim.py(pure-Pythongrpcmodule shim),_channel.py(GrpcWebChannel: unary calls, metadata → headers, grpc-web status →AioRpcError,grpc-timeoutencoding, 404/405 diagnostics),_framing.py,_sender.py(pyfetch, plus an httpx sender for CPython testing),_httpx_fetch.py(REST over JSfetch), README.Base client (no-ops off Emscripten):
weaviate/__init__.py— platform-guarded bootstrap: a bareimport weaviatesoft-imports the companion; a missing companion raises an install hint.setup.cfg—grpcio ; sys_platform != "emscripten".connect/base.py—grpc_path_prefixonConnectionParams(port collision allowed with a prefix);_check_grpc_web_usableruns once, at client construction (sync client or missing shim fails fast);grpc-web.path_prefixchannel option for the shim.connect/helpers.py—_webify(): the async helpers route gRPC to the REST endpoint under Emscripten; Con006 when a caller's gRPC endpoint is discarded.connect/v4.py— sync client rejected at construction under Emscripten (a 3-line check in_ConnectionBase.__init__);_ping_grpcpasses the gRPC error through so the diagnostics can say what went wrong;_check_package_versionignoresOSError(fetch blocked by a page CSP).exceptions.py—WeaviateGRPCUnavailableErrorgains the gRPC code/details and a grpc-web branch (no firewall/port advice; a 404 names the two real causes: server < 1.38.3 or a wrong prefix).collections/batch/async_.py—batch.stream()fails fast under grpc-web, pointing toinsert_many()(bidi streaming is impossible over fetch).embedded.py— explicit error under Emscripten.proto/v1/__init__.py— grpcio version fallback when dist metadata is absent (Emscripten only), drift-pinned byproto_test.Dropped vs #2056 (and why)
_deadline/_Deadlinesplumbing acrossv4.pyand both batch modules: the web package already treats non-finite timeouts as "no deadline" in both transports (_encode_timeout,_abort_signal_ms); the one CPython effect (Thread.join(inf)in the sync batch-stream shutdown wait) is a separate one-line fix, not included here.ConnectionSync.__init__signature override (→ 3 lines in the base constructor) and the duplicate channel-level_check_grpc_web_usablecall.test_no_helper_takes_a_grpc_path_prefix, the Emscripten__get_timeouttest, the channel-level reject pair, the trivial platform-passes tests, …).CI / tests
grpc-web-tests(package suite, 3.10–3.14) andpyodide-e2e(real Pyodide under Node against core-native/v1/grpc-web, Weaviate 1.39.0) jobs; ruff / flake8 / pyright coverpackages/web;build-and-publishneeds both. Base-client tests:test/test_wasm_compat.py,test/test_connection_params.py,proto_test. Locally: 176 tests across those +packages/web/testspass; pyright clean for the whole project.Deferred
Publish/lockstep versioning for
weaviate-client-web; once on PyPI,weaviate-clientgainsweaviate-client-web ; sys_platform == "emscripten", collapsing the WASM install tomicropip.install("weaviate-client").🤖 Generated with Claude Code
https://claude.ai/code/session_01GUNU7QgDr9MmFZnjKY9zFN