OpenMed ships a schema-first gRPC service for polyglot integrations that need
typed contracts and server-streaming de-identification. The gRPC service uses
the same ServiceRuntime and warm-pool model loader as the REST service, so
preloaded models, resident-model limits, and keep-alive behavior stay
consistent across transports.
Install the service extra to get the gRPC runtime dependencies:
uv pip install -e ".[service]"Developers regenerating protobuf stubs need dev dependencies:
uv sync --extra dev
make grpc-protoThe OpenMed service contract is committed at
openmed/service/proto/openmed.proto. The client-only KServe V2 wire subset
used by the remote inference backend is committed at
openmed/service/proto/kserve_v2.proto. Generated Python modules for both are
committed under openmed/service/proto/generated/.
The OpenMedService exposes:
Analyze: unary equivalent ofPOST /analyzeExtract: unary equivalent ofPOST /pii/extractDeidentify: unary equivalent ofPOST /pii/deidentifyStreamDeidentify: unary request with server-streamed redacted fragments
Response messages include typed entity records and the canonical OpenMedSpan
message shape for callers that consume schema-versioned span contracts.
from openmed.service.grpc_server import serve
server = serve("127.0.0.1:50051")
server.wait_for_termination()import grpc
from openmed.service.proto.generated import openmed_pb2, openmed_pb2_grpc
channel = grpc.insecure_channel("127.0.0.1:50051")
stub = openmed_pb2_grpc.OpenMedServiceStub(channel)
response = stub.Deidentify(
openmed_pb2.DeidentifyRequest(
text="Patient Maria Garcia can be reached at maria@example.test.",
method="mask",
lang="en",
)
)
print(response.deidentified_text)
for entity in response.pii_entities:
print(entity.label, entity.start, entity.end)StreamDeidentify accepts either explicit chunks or a single request.text
with an optional chunk_size. The stream yields redacted text fragments as they
become safe to emit and ends with a final event that carries aggregate stream
metadata and final spans.
request = openmed_pb2.DeidentifyStreamRequest(
request=openmed_pb2.DeidentifyRequest(
text="Long synthetic clinical note...",
method="mask",
),
chunk_size=4096,
max_buffer=8192,
)
for event in stub.StreamDeidentify(request):
if event.redacted_text:
print(event.redacted_text, end="")Run this before committing proto changes:
make grpc-proto
make grpc-proto-checkThe unit test suite also runs the same drift check, so CI fails when
openmed.proto and the committed generated stubs disagree.