All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
- FHIR type interoperability (
client.fhir.*): The resolver now accepts any Coding-like input via duck typing, so pipelines that already parse FHIR withfhir.resourcesorfhirpycan pass those objects directly - no manual conversion.- New
coding=kwarg onFhir.resolve/AsyncFhir.resolve. Accepts a plain dict, theomophub.types.fhir.CodingTypedDict, or any object exposing.system/.code/.displayattributes (e.g.fhir.resources.Coding,fhirpycodings). Fhir.resolve_batch/AsyncFhir.resolve_batchnow accept a mixed list of dicts and Coding-like objects; each item is normalized to the wire format automatically.Fhir.resolve_codeable_concept/ async counterpart now accept either a list of Coding-likes (existing) or a full CodeableConcept-like object exposing.codingand.text(new); explicittext=kwarg still wins when both are passed.- Explicit
system/codekwargs override the corresponding fields on acoding=input - handy for last-mile overrides.
- New
- New FHIR type definitions in
omophub.types.fhir:CodingandCodeableConceptlightweight TypedDictsCodingLikeandCodeableConceptLikeruntime-checkableProtocols for structural matching against external libraries.
- FHIR client interop helpers (
omophub.fhir_interop): Thin helpers for configuring an external FHIR client library against the OMOPHub FHIR Terminology Service.get_fhir_server_url(version)returns the FHIR base URL for"r4"(default),"r4b","r5", or"r6".get_fhirpy_client(api_key, version)andget_async_fhirpy_client(api_key, version)returnfhirpyclients pre-wired with the right URL andAuthorization: Bearerheader.fhirpyis imported lazily, so it is never a required dependency; a helpfulImportErrorwith install instructions is raised only when you actually call the helper.- All three helpers are re-exported from the top-level
omophubnamespace.
OMOPHub.fhir_server_url/AsyncOMOPHub.fhir_server_url: Convenience read-only property returning the R4 FHIR endpoint for drop-in use with external FHIR clients (httpx,fhirpy,fhir.resources).- Optional extras in
pyproject.toml:pip install omophub[fhirpy]pulls infhirpy>=1.4.0.pip install omophub[fhir-resources]pulls infhir.resources>=7.0.0. Both are purely optional; duck typing means neither is required for core SDK use.
Fhir.resolve_batchsignature broadened fromcodings: list[dict[str, str | None]]tocodings: list[CodingInput]whereCodingInputis the union of dict,CodingTypedDict, andCodingLikeprotocol. Existing call sites keep working unchanged.Fhir.resolve_codeable_conceptsignature broadened fromcoding: list[dict[str, str]]tocoding: list[CodingInput] | CodeableConceptInput, accepting either the legacy list-of-codings shape or a full CodeableConcept-like object.
- 16 new unit tests covering
_extract_coding,_coding_to_dict, and duck-typed inputs acrossresolve,resolve_batch, andresolve_codeable_concepton the sync client. Explicit-kwargs-win precedence is covered. - New
tests/unit/test_fhir_interop.pywith 10 cases: URL builder for all four FHIR versions,fhirpylazy import with stubbed success and missing-module failure paths, and thefhir_server_urlproperty on both sync and async clients. - 5 new integration tests in
tests/integration/test_fhir.pyexercising the newcoding=kwarg, mixed dict + duck-typed batch inputs, CodeableConcept-like object resolution, and thefhir_server_urlproperty against the live API.
1.6.0 - 2026-04-10
- FHIR-to-OMOP Concept Resolver (
client.fhir): Translate FHIR coded values into OMOP standard concepts, CDM target tables, and optional Phoebe recommendations in a single API call.resolve(): Resolve a single FHIRCoding(system URI + code) or text-only input via semantic search fallback. Returns the standard concept, target CDM table, domain alignment check, and optional mapping quality signal.resolve_batch(): Batch-resolve up to 100 FHIR codings per request with inline per-item error reporting. Failed items do not fail the batch.resolve_codeable_concept(): Resolve a FHIRCodeableConceptwith multiple codings. Automatically picks the best match per OHDSI vocabulary preference (SNOMED > RxNorm > LOINC > CVX > ICD-10). Falls back to thetextfield via semantic search when no coding resolves.
- New TypedDict types for FHIR resolver:
FhirResolveResult,FhirResolution,FhirBatchResult,FhirBatchSummary,FhirCodeableConceptResult,ResolvedConcept,RecommendedConceptOutput. - Both sync (
OMOPHub) and async (AsyncOMOPHub) clients support FHIR resolver methods viaclient.fhir.*.
- Extracted shared response parsing (
_request.py): The duplicated JSON decode / error-handling / rate-limit-retry logic acrossRequest._parse_response,Request._parse_response_raw,AsyncRequest._parse_response, andAsyncRequest._parse_response_raw(4 copies of ~50 lines each) is now a single_parse_and_raise()module-level function. All four methods delegate to it, eliminating the risk of divergence bugs. - Fixed
paginate_asyncsignature (_pagination.py): The type hint now correctly declaresCallable[[int, int], Awaitable[tuple[...]]]instead ofCallable[[int, int], tuple[...]], and the runtimehasattr(__await__)duck-typing hack has been replaced with a cleanawait. AsyncSearch.semantic_iternow delegates topaginate_asyncinstead of manually reimplementing the pagination loop, matching the syncsemantic_iterwhich already usespaginate_sync.
- Python prerequisite in CONTRIBUTING.md corrected from
3.9+to3.10+(matchingpyproject.toml). __all__intypes/__init__.pysorted per RUF022.
1.5.1 - 2026-04-08
- Rate-limit handling: HTTP client now respects the
Retry-Afterheader on429 Too Many Requestsresponses and applies exponential backoff with jitter on retries. Previous versions retried only on502/503/504with a fixed2^attempt * 0.5sschedule and did not back off on429at all, so a client that hit the server's rate limit at high volume could burn through thousands of failed requests in a tight loop. The client now honorsRetry-After, uses exponential backoff with jitter, respects the configuredmax_retries, and caps backoff at 30 seconds. - Updated
examples/search_concepts.pyto reflect current API.
1.5.0 - 2026-03-26
- Bulk lexical search (
search.bulk_basic()): Execute up to 50 keyword searches in a single API call. Supports shared defaults for vocabulary, domain, and other filters. Each search is identified by a uniquesearch_idfor result matching. Maps toPOST /v1/search/bulk. - Bulk semantic search (
search.bulk_semantic()): Execute up to 25 natural-language searches using neural embeddings in a single call. Supports per-search similarity thresholds and shared defaults. Includes query enhancement data (abbreviation expansion, misspelling correction). Maps toPOST /v1/search/semantic-bulk. - New TypedDict types for bulk search:
BulkSearchInput,BulkSearchDefaults,BulkSearchResponse,BulkSearchResultItem,BulkSemanticSearchInput,BulkSemanticSearchDefaults,BulkSemanticSearchResponse,BulkSemanticSearchResultItem,QueryEnhancement. - Both sync (
OMOPHub) and async (AsyncOMOPHub) clients support bulk search methods.
- Updated
__all__exports to alphabetical order (ruff RUF022 compliance). BulkSearchInputandBulkSemanticSearchInputnow useRequired[str]forsearch_idandqueryfields for proper type checking.
1.4.1 - 2026-02-28
- User-Agent header now reports actual SDK version (e.g.,
OMOPHub-SDK-Python/1.4.1) instead of hardcoded0.1.0. Version is resolved at runtime viaimportlib.metadata.
1.4.0 - 2026-02-23
- Semantic search (
search.semantic(),search.semantic_iter()): Natural language concept search using neural embeddings. Search for clinical intent like "high blood sugar levels" to find diabetes-related concepts. Supports filtering by vocabulary, domain, standard concept, concept class, and minimum similarity threshold.semantic_iter()provides automatic pagination. - Similarity search (
search.similar()): Find concepts similar to a reference concept ID, concept name, or natural language query. Three algorithm options:'semantic'(neural embeddings),'lexical'(string matching), and'hybrid'(combined). Configurable similarity threshold with optional detailed scores and explanations.
1.3.1 - 2026-01-24
- Fixed
search.basic_iter()pagination bug that caused only the first page of results to be returned. The iterator now correctly fetches all pages when iterating through search results.
- Added
get_raw()method to internal request classes for retrieving full API responses with pagination metadata. - Expanded
search.basic_iter()method signature to explicitly list all filter parameters instead of using**kwargs.
1.3.0 - 2026-01-06
Parameter Renames (for API consistency):
search.autocomplete():max_suggestions→page_sizeconcepts.suggest():vocabulary→vocabulary_ids,domain→domain_ids,limit→page_sizeconcepts.related():relatedness_types→relationship_typesconcepts.relationships():relationship_type→relationship_idsrelationships.get():relationship_type→relationship_ids,target_vocabulary→vocabulary_idshierarchy.ancestors():vocabulary_id→vocabulary_ids,include_deprecated→include_invalidhierarchy.descendants():vocabulary_id→vocabulary_ids,include_deprecated→include_invalid
Simplified APIs (removed parameters):
vocabularies.get(): Removedinclude_stats,include_domains(usestats()method instead)vocabularies.domains(): Removed pagination parameters, now returns all domainsdomains.list(): Simplified to singleinclude_statsparameterdomains.concepts(): Removedconcept_class_ids, addedinclude_invalidmappings.get(): Simplified totarget_vocabulary,include_invalid,vocab_releaserelationships.types(): Removed all filtering parameters
Default Changes:
vocabularies.list(): Defaultpage_sizechanged from 100 to 20concepts.batch(): Defaultstandard_onlychanged fromFalsetoTrue
vocabularies.domain_stats(vocabulary_id, domain_id)- Get statistics for a specific domain within a vocabularyvocabularies.concept_classes()- Get all concept classeshierarchy.get(concept_id)- Get complete hierarchy (ancestors and descendants) in one callvocab_releaseparameter toconcepts.get(),concepts.get_by_code(),mappings.get(),mappings.map()include_hierarchyparameter toconcepts.get()andconcepts.get_by_code()- Pagination support to
concepts.suggest() domain_ids,standard_only,include_reverseparameters torelationships.get()
1.2.0 - 2025-12-09
include_synonymsandinclude_relationshipsparameters toconcepts.get_by_code()method for retrieving concept synonyms and relationships in a single request.
- User-Agent header updated to
OMOPHub-SDK-Python/{version}.
0.1.0 - 2025-12-01
- Initial release of the OMOPHub Python SDK
- Synchronous client (
OMOPHub) and asynchronous client (AsyncOMOPHub) - Full support for all OMOPHub API endpoints:
- Concepts: get, get_by_code, batch, suggest, related, relationships
- Search: basic, advanced, autocomplete
- Hierarchy: ancestors, descendants, paths
- Relationships: get, types
- Mappings: get, map, bulk
- Vocabularies: list, get, stats, domains, concepts
- Domains: list, get, concepts
- TypedDict type definitions for all API responses
- Automatic retry with exponential backoff
- Rate limit handling with Retry-After support
- Comprehensive exception hierarchy
- Auto-pagination iterators for search results
- Full type hints and PEP 561 compliance
- HTTP/2 support via httpx