From df25378706f10b9348f01b65cf11813def2c86f5 Mon Sep 17 00:00:00 2001 From: "fern-api[bot]" <115122769+fern-api[bot]@users.noreply.github.com> Date: Wed, 7 Oct 2026 20:55:03 +0000 Subject: [PATCH 1/5] [fern-generated] Update SDK Generated by Fern CLI Version: unknown Generators: - fernapi/fern-python-sdk: 5.31.1 --- .fern/metadata.json | 10 +- poetry.lock | 1625 +++++++++-------- pyproject.toml | 7 +- reference.md | 711 ++++++-- src/phenoml/agent/chat/client.py | 2 +- src/phenoml/agent/chat/raw_client.py | 4 + src/phenoml/client.py | 30 +- src/phenoml/construe/__init__.py | 3 - src/phenoml/construe/code_systems/client.py | 4 +- .../construe/code_systems/raw_client.py | 4 +- src/phenoml/construe/codes/client.py | 22 +- src/phenoml/construe/codes/raw_client.py | 75 +- src/phenoml/construe/errors/__init__.py | 3 - src/phenoml/core/client_wrapper.py | 4 +- src/phenoml/core/http_client.py | 101 +- src/phenoml/core/http_response.py | 4 + src/phenoml/core/jsonable_encoder.py | 13 + src/phenoml/core/oauth_token_provider.py | 8 +- src/phenoml/core/pydantic_utilities.py | 134 +- src/phenoml/fhir2omop/__init__.py | 28 +- src/phenoml/fhir2omop/client.py | 730 +++++++- src/phenoml/fhir2omop/errors/__init__.py | 4 +- .../errors/service_unavailable_error.py | 10 - src/phenoml/fhir2omop/raw_client.py | 741 ++++++-- src/phenoml/fhir2omop/types/__init__.py | 24 + src/phenoml/fhir2omop/types/coding.py | 21 + .../types/condition_occurrence_row.py | 13 +- .../fhir2omop/types/create_omop_response.py | 43 +- src/phenoml/fhir2omop/types/death_row.py | 6 +- .../fhir2omop/types/drug_exposure_row.py | 48 +- src/phenoml/fhir2omop/types/mapping_entry.py | 40 +- .../types/mapping_entry_mapping_status.py | 7 + .../fhir2omop/types/measurement_row.py | 6 +- .../fhir2omop/types/observation_period_row.py | 12 +- .../fhir2omop/types/observation_row.py | 6 +- src/phenoml/fhir2omop/types/omop_tables.py | 6 +- src/phenoml/fhir2omop/types/person_row.py | 76 +- .../types/procedure_occurrence_row.py | 12 +- .../types/provider_role_care_site.py | 25 + .../types/provider_role_codeable_concept.py | 21 + .../fhir2omop/types/provider_role_context.py | 48 + .../provider_role_practitioner_identifier.py | 24 + src/phenoml/fhir2omop/types/provider_row.py | 29 +- .../fhir2omop/types/reference_diagnostic.py | 33 + .../types/reference_diagnostic_outcome.py | 7 + src/phenoml/fhir2omop/types/summary.py | 26 +- .../fhir2omop/types/visit_occurrence_row.py | 12 +- src/phenoml/implementation_guides/__init__.py | 23 +- .../implementation_guides/errors/__init__.py | 11 +- .../errors/conflict_error.py} | 4 +- .../implementation_guides/client.py | 224 ++- .../implementation_guides/raw_client.py | 352 +++- .../implementation_guides/types/__init__.py | 12 +- .../types/fhir_implementation_guide.py | 38 + .../types/implementation_guide_summary.py | 12 +- .../implementation_guide_version_detail.py | 29 + src/phenoml/lang2fhir/__init__.py | 12 + src/phenoml/lang2fhir/client.py | 77 +- src/phenoml/lang2fhir/raw_client.py | 121 +- src/phenoml/lang2fhir/types/__init__.py | 12 + .../types/create_request_resource.py | 3 + .../lang2fhir/types/patient_reference.py | 2 +- .../lang2fhir/types/primary_patient.py | 42 + .../lang2fhir/types/primary_patient_gender.py | 5 + .../lang2fhir/types/primary_patient_name.py | 31 + .../lang2fhir/types/resource_review.py | 2 +- .../types/resource_review_finding.py | 7 +- .../types/resource_review_flagged.py | 2 +- .../types/resource_review_remediated.py | 42 + .../lang2fhir/types/resource_review_result.py | 10 +- src/phenoml/lang2fhir_batch/client.py | 208 +-- src/phenoml/lang2fhir_batch/raw_client.py | 230 +-- .../lang2fhir_batch/types/batch_error.py | 10 +- .../types/batch_item_status.py | 5 +- .../lang2fhir_batch/types/batch_job.py | 4 +- src/phenoml/profiles/profiles/raw_client.py | 22 + src/phenoml/profiles/types/profile_summary.py | 2 +- src/phenoml/profiles/versions/raw_client.py | 22 + tests/utils/test_http_client.py | 112 +- tests/wire/test_fhir2Omop.py | 1 + ...plementationGuides_implementationGuides.py | 29 + tests/wire/test_lang2Fhir.py | 4 +- wiremock/wiremock-mappings.json | 632 ++++++- 83 files changed, 5417 insertions(+), 1759 deletions(-) delete mode 100644 src/phenoml/fhir2omop/errors/service_unavailable_error.py create mode 100644 src/phenoml/fhir2omop/types/coding.py create mode 100644 src/phenoml/fhir2omop/types/mapping_entry_mapping_status.py create mode 100644 src/phenoml/fhir2omop/types/provider_role_care_site.py create mode 100644 src/phenoml/fhir2omop/types/provider_role_codeable_concept.py create mode 100644 src/phenoml/fhir2omop/types/provider_role_context.py create mode 100644 src/phenoml/fhir2omop/types/provider_role_practitioner_identifier.py create mode 100644 src/phenoml/fhir2omop/types/reference_diagnostic.py create mode 100644 src/phenoml/fhir2omop/types/reference_diagnostic_outcome.py rename src/phenoml/{construe/errors/bad_gateway_error.py => implementation_guides/errors/conflict_error.py} (69%) create mode 100644 src/phenoml/implementation_guides/types/fhir_implementation_guide.py create mode 100644 src/phenoml/implementation_guides/types/implementation_guide_version_detail.py create mode 100644 src/phenoml/lang2fhir/types/primary_patient.py create mode 100644 src/phenoml/lang2fhir/types/primary_patient_gender.py create mode 100644 src/phenoml/lang2fhir/types/primary_patient_name.py create mode 100644 src/phenoml/lang2fhir/types/resource_review_remediated.py diff --git a/.fern/metadata.json b/.fern/metadata.json index 508188d7..edf440b8 100644 --- a/.fern/metadata.json +++ b/.fern/metadata.json @@ -1,17 +1,17 @@ { - "cliVersion": "5.118.0", + "cliVersion": "5.147.9", "generatorName": "fernapi/fern-python-sdk", - "generatorVersion": "5.18.1", + "generatorVersion": "5.31.1", "generatorConfig": { "client_class_name": "PhenomlClient", "wire_tests": { "enabled": true } }, - "originGitCommit": "26224ef37e22adb507f28c21e8991d4a27822bde", + "originGitCommit": "e955fe1d5d69dfe7f09607612a03a87058b280fa", "originGitCommitIsDirty": true, "invokedBy": "ci", "requestedVersion": "AUTO", "ciProvider": "unknown", - "sdkVersion": "17.0.0" -} + "sdkVersion": "0.0.0.dev0" +} \ No newline at end of file diff --git a/poetry.lock b/poetry.lock index 3656b36f..b7d61fc8 100644 --- a/poetry.lock +++ b/poetry.lock @@ -15,132 +15,174 @@ files = [ [[package]] name = "aiohttp" -version = "3.14.3" +version = "3.14.4" description = "Async http client/server framework (asyncio)" optional = true python-versions = ">=3.10" groups = ["main"] markers = "extra == \"aiohttp\"" files = [ - {file = "aiohttp-3.14.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:eb0495d778817619273c108784292be161a924b9f5ae5cbbc70a2caa6838250b"}, - {file = "aiohttp-3.14.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c3c200cf9757edd785051dc699c7ecbec22110dbfcb3fefc7a9f9695eda8ea7a"}, - {file = "aiohttp-3.14.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd51ebf9d3a00c074df4ede271023f4d2dba289bcc740b88191872716014e3c5"}, - {file = "aiohttp-3.14.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:134ac5ddcf61c6fad984b9a5727d83492ada43d63471db20fb73042c13fca62f"}, - {file = "aiohttp-3.14.3-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:70c987b27534f9ae1a723f47ae921571d616da21d3208282bf4c52af5164ac43"}, - {file = "aiohttp-3.14.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1b59533861b70a2185c8f4f350f791f39d64358ef6944ce71c5240c9ec0982c9"}, - {file = "aiohttp-3.14.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1c5281acc88b92396f88c7e1e2748f8466689df22b80170e4f51efa712fb47a8"}, - {file = "aiohttp-3.14.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:48d67b87db6279c044760787eb01f6413032c2e6f3ba1cafaa492b1c8e578479"}, - {file = "aiohttp-3.14.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f53bcd52f585e1ac3e590d61434eb61f9a88c38df041b4ea126d97144344a77b"}, - {file = "aiohttp-3.14.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0fdea2281997af69da84c77ffa6f5938a0285f21fb3887c249d67419ca865b3d"}, - {file = "aiohttp-3.14.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:cda5fd5c95ad7a125a2e8464acc78b98b94c475a3780d6aa0aa157c93f470f4d"}, - {file = "aiohttp-3.14.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:6debfa7312ff9d4c124dc71d72e9a0a4b9e0879e48ba6fcb42bef5c3300289e2"}, - {file = "aiohttp-3.14.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:f4e05329faa0ea1a404b37de4f034fd2c2defcca06a68dc6745e4e56c88e8a48"}, - {file = "aiohttp-3.14.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:a3a8296e7ab5c295f53f1041487cb088e1480775aafbf7fe545d93b770a0f96f"}, - {file = "aiohttp-3.14.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5373dc80ad1aa2fb9ad95c83f24eef418bbda3a61375f128e5b0192e4f3f9b32"}, - {file = "aiohttp-3.14.3-cp310-cp310-win32.whl", hash = "sha256:a3e22975f905b89a55a488c2a08f2fdb2186175349e917d48985cc468a3d4c6e"}, - {file = "aiohttp-3.14.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdd0e2834dce1a26c1bbe26464861e16bbe217042cbff619247c11594472518c"}, - {file = "aiohttp-3.14.3-cp310-cp310-win_arm64.whl", hash = "sha256:eac645b09bcfdf73df7536331f0678c1086ea250981118ddb5199e17ccef72bb"}, - {file = "aiohttp-3.14.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e568e14940c09955aa51f4e645b6daa18a581c5dcfcd73744dcc86a856e3ced3"}, - {file = "aiohttp-3.14.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:54cfcdee2770dac994417cbb0ee1f3eb0e7cb6b30c79bf44f2c02ff79ec5124a"}, - {file = "aiohttp-3.14.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:21c016079415ed3fd676963e9793700a566d85dbbd6bfc564b9b2d209147dcc8"}, - {file = "aiohttp-3.14.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d6088ec9894113802bddb3c09e974929aed2c7b3a8c456219b8aab4481f1a239"}, - {file = "aiohttp-3.14.3-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:16ea7e24c309fb7c0bbd505d149abe4fe4dccfb8db911db7dbec0921bc889a6f"}, - {file = "aiohttp-3.14.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:56f355e79f71aef2a85c80305cc915f894b170dba76de5fe84f6351939b83c06"}, - {file = "aiohttp-3.14.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:18c441d0a8fca6de8d1f546849b9f0ab20d435993e2c5b59562b2fae6be2f929"}, - {file = "aiohttp-3.14.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:53e7b4ce82b54a8bcc71b3b67a5cbd177ca1d7f592cbc92cd38b7349f73482db"}, - {file = "aiohttp-3.14.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f55119f7bf25f49ed210f6096090715da24f2943c62102448915fde3c62877ce"}, - {file = "aiohttp-3.14.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9aa6e61fdf20105c4144e755bd586008ff450791d67b1c8146fdc15959c4d51c"}, - {file = "aiohttp-3.14.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:ccd4893707b3e2a13e39c90d43cf80edf2e4d0457935bcc103bf2346214c3f15"}, - {file = "aiohttp-3.14.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:b2466434105a4e03113c36ec775cc2ebe6676b62eae326fa670bb607ef788c1c"}, - {file = "aiohttp-3.14.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:ba59d59aba08ac02fc03b0c8983ccd5ee39a199d0552ce9e6d2b4845b34d59ae"}, - {file = "aiohttp-3.14.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:ed099d105449c4f9e84f24af203cd131349d4761d8813fa7e02c32e7128cd910"}, - {file = "aiohttp-3.14.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:152516815ef926786a0b6ae2b8f1fd2e0c71582dee0b435636865316fd4891b7"}, - {file = "aiohttp-3.14.3-cp311-cp311-win32.whl", hash = "sha256:a4af35c443e0b1a1bd6a8af3f3485d7fda15c142751a00f3ff8090f0b93346fa"}, - {file = "aiohttp-3.14.3-cp311-cp311-win_amd64.whl", hash = "sha256:e1e74298bab6ee0d6e749ed4fd1901c7e604bdda32c03d787a2cc71c46d0433d"}, - {file = "aiohttp-3.14.3-cp311-cp311-win_arm64.whl", hash = "sha256:03cd2bde3d7f085b64e549c985f4bb928cad7e8ecf5323bfca320db548d81b39"}, - {file = "aiohttp-3.14.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:39aded8c7f3b935b54aab1d8d73c70ec0ee2d3ec3b943e0e86611bc150ba47f5"}, - {file = "aiohttp-3.14.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5bcb6ff3fdab1258a192679ff1a05d44f59626430aa05cd1a9d2447423599228"}, - {file = "aiohttp-3.14.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:617105e2c3018ee38d0c8ce5ee3c84f621a6d8b9f723202aacaff28449ca91ee"}, - {file = "aiohttp-3.14.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f631fe87a6f30df5fbe6d79640b25e4cffb38c31c7fb6f10871517b84b0f8c1a"}, - {file = "aiohttp-3.14.3-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a94dbaae5ae27bd849c93570669bff91e0510f33a80805738e3de72a7be0447b"}, - {file = "aiohttp-3.14.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8f2f1c4c032c7cedd7d8da6f54c97b70266c6570c3108d3fdffee7188bb70529"}, - {file = "aiohttp-3.14.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ea05e1f97ceea523942d9b2a7d7c0359d781d683d6b043f5943a602b14da4787"}, - {file = "aiohttp-3.14.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:543906c127fb1d929b95076db19b83fa2d46751006ff1e23b093aa5ac4d8db42"}, - {file = "aiohttp-3.14.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0a5ff2dfbb9ce645fa5b8ef3e02c6c0b9cc3f6030ff863d0c51fffc50cb5541b"}, - {file = "aiohttp-3.14.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:041badb8f84396357c4d3ad26de6afd7a32b112f43d3c63045c0c8278cfd2043"}, - {file = "aiohttp-3.14.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:530125ee1163c4219af35dc3aa1206e541e7b31b6efc1a3f93b70a136f65d427"}, - {file = "aiohttp-3.14.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c8653fd547c93a61aadc612007790f5555cdd18946fa48cf45e26d8ea4ea473d"}, - {file = "aiohttp-3.14.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:89176250f686cb9853c0fb7ead90e639e915b84a6f43eedc2a4e7ec21f1037f0"}, - {file = "aiohttp-3.14.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3a26434dafe408229ff3403458ca58de24fb51936504decac49ce6755f77e59d"}, - {file = "aiohttp-3.14.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d1558173930a5a8d3069cee5c92fc91c87c4dbcb099debbb3622053717145a19"}, - {file = "aiohttp-3.14.3-cp312-cp312-win32.whl", hash = "sha256:16100ad3ab8d649fdfbee87602d9d2dcdca9df0b9eda8a1b5fdc0d41f96da559"}, - {file = "aiohttp-3.14.3-cp312-cp312-win_amd64.whl", hash = "sha256:33a2d7c28d33797a2e99923dffa63f83d908a19b6bf26cfe80fa790aa5e1a75a"}, - {file = "aiohttp-3.14.3-cp312-cp312-win_arm64.whl", hash = "sha256:362a3fd481769cac1a824514bcd86fda51c65e8fe6e051099e008fddde6db17c"}, - {file = "aiohttp-3.14.3-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:2e9878ae68e4a5f1c0abe4dd497dbc3d51946f5837b56759e2a02e78fa90ef86"}, - {file = "aiohttp-3.14.3-cp313-cp313-android_21_x86_64.whl", hash = "sha256:f3d2669fe7dec7fc359ecdb5984b29b50d85d5d00f8c1cb61de4f4a24ee42627"}, - {file = "aiohttp-3.14.3-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:cc7cb243a68167172f48c1fd43cee91ec4b1d40cefd190edd43369d1a6bc9c82"}, - {file = "aiohttp-3.14.3-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:78253b573e6ffab5028924fc98bc281aae05445969982a10864bc360dea2016c"}, - {file = "aiohttp-3.14.3-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:7041d52c3a7fa20c9e8c182b534704abb19502c8bdcbde7ab23bfda6f642394f"}, - {file = "aiohttp-3.14.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ac74facc01463f138b0da5580329cfcc82818dea5656e83ddcd11268fc12ff80"}, - {file = "aiohttp-3.14.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d6218d92e450824e9b4881f44e8c09f1853b490f9a64130801024a4793b1b3b0"}, - {file = "aiohttp-3.14.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:11fb37ef075669eee52ab1928fbf6e1741fada40409fa309ebde9607a962aebf"}, - {file = "aiohttp-3.14.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:55bdcc472aafe2de4a253045cc128007a64f1e0264fb675791e132ea5edaa3bd"}, - {file = "aiohttp-3.14.3-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c39846c3aad97a8530c89d7a3869a8f8e9e3762c6ac0504481e5c80948f7e807"}, - {file = "aiohttp-3.14.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5895ef58c4620afe02fa16044f023dc4dafec08158f9d08874a46a7dbc0341b8"}, - {file = "aiohttp-3.14.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fa9467a8113aa69d3d7c55a70ef0b7c636010a40993f3df9d9d0d73b3eb7ef24"}, - {file = "aiohttp-3.14.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d7d2deec16eeedf55f2c7cf75b521ea3856a5177e123844f8fd0f114ce252cb5"}, - {file = "aiohttp-3.14.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:dd54d0e8717de95939766febac482ac0474d8ac3b048115f9f2b1d23a16e7db4"}, - {file = "aiohttp-3.14.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:df82f3787c940c94986b34222d59c9e38843fba85139f36e85255a82ad5355a9"}, - {file = "aiohttp-3.14.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:42a67efc36300d052fb4508a53e8b6901b9284b599ae63945c377569c5fcc1e1"}, - {file = "aiohttp-3.14.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7a75aa63cbf9b21cfaf60dc2657e19df2c2867d91707d653fee171ffeedd1371"}, - {file = "aiohttp-3.14.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:e92eb8acc45eb6a9f4935071a77edf5b85cc6f8dfad5cd99e97653c26593cdde"}, - {file = "aiohttp-3.14.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b014a6ed7cf912e787149fdc529166d3ceabac23f26efeea3158c9aba2354e7e"}, - {file = "aiohttp-3.14.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3d4f72af88ac2474bb5bca640030320e3d38a0163a1d7533500e87be458eef71"}, - {file = "aiohttp-3.14.3-cp313-cp313-win32.whl", hash = "sha256:5f08ec777f35ee70720233b8b9811d3bb5d728137f30ac91b7457709c3261ac0"}, - {file = "aiohttp-3.14.3-cp313-cp313-win_amd64.whl", hash = "sha256:dff9461ec275f22135650d5ba4b4931a11f3958df7dfbb8db630000d4dee0883"}, - {file = "aiohttp-3.14.3-cp313-cp313-win_arm64.whl", hash = "sha256:ddcac3c6b382e81f1dd0499199d4136b877beb4cb5ef770bbbfba56c4b8f55d2"}, - {file = "aiohttp-3.14.3-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:49f7325beb0f85ef4aef5f48f490269575f83e6e2acad00a1d80b807eb027062"}, - {file = "aiohttp-3.14.3-cp314-cp314-android_24_x86_64.whl", hash = "sha256:e3be98a7c30b8c25d573dafba7171d66dfb05ee6a9070fc46535464ff97700a6"}, - {file = "aiohttp-3.14.3-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:614c61d478b83953e261d02bb2df750f17227cd33ef8002945bf5aebbde21919"}, - {file = "aiohttp-3.14.3-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:1caa7b0d05f3e3a36f87788c59e970a7ee1cefcfcbb924a9f138c4a6551c9cb7"}, - {file = "aiohttp-3.14.3-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:dfa68deb2a443bdaa3ea5297b0699c1464f08aef3812b486d1348eee61b07dc0"}, - {file = "aiohttp-3.14.3-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:e72ee89e28d907a18f46959b4eb0bb06701cc7f8cf4366e00029e2ccfaaf5924"}, - {file = "aiohttp-3.14.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:ad4c8b7488d745d2ca4838ebd8ae5ba9b56341d30b1da43640e4ce87f9f49646"}, - {file = "aiohttp-3.14.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:db332af25642007330fca8be5c4d194caf2bea7a7fc84415aff3497af5dfee6b"}, - {file = "aiohttp-3.14.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:25bd2708db6bdf6a6630dd37bdcdfcb47c4434d22ac69c64665b802910140b30"}, - {file = "aiohttp-3.14.3-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:cef89a58e628c4efcac3275c2d68083f82426dcdc89c1492a6f654f9f7ea6ab9"}, - {file = "aiohttp-3.14.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c23ec8ee9d5ab2f5421f9c7fffce208435607af27fd46d4a44e031954352838f"}, - {file = "aiohttp-3.14.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e2667f0bbe7eb6c74eae5e9691441ad186e5845ca3cff63230fc09c4e7514f5d"}, - {file = "aiohttp-3.14.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18cb43369747b2ae007bd2655fb8e63a099c2ff1d207962943636dac989b3147"}, - {file = "aiohttp-3.14.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d77640cc618c1d99fc4f8589c0f24a730adfa54eb1e57ef7bf0c8dfb78da898c"}, - {file = "aiohttp-3.14.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:53e5179d8abb5710f8e83ba207c41c8d1261fcffd4616500e15ca2b7a33be10a"}, - {file = "aiohttp-3.14.3-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:cd817772b2fcf2b8c0905795318485f9ec16eae60b29feb7f4c77085311637f0"}, - {file = "aiohttp-3.14.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:4e3ac92d90e92773b2362d506068e9a948192bd553e743c5b2429e28527c8661"}, - {file = "aiohttp-3.14.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:3f42e9b78301f11c8f861746175d8b9c1ccef713fcad9eab396e2f6db8ed4a22"}, - {file = "aiohttp-3.14.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:9d9edccfe496b476db5f398d97b865e9a6752bcf8aec4eef8390ce20fb64bb41"}, - {file = "aiohttp-3.14.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:1c5ec8fb1bcc31a8466f74aaf26c345d5c386fa4bd08a3f0eb9c7a4a3fe8b5bf"}, - {file = "aiohttp-3.14.3-cp314-cp314-win32.whl", hash = "sha256:38901a84da3ce22249f6e860bf8f90d141bcab7da090cc398f8bb58c0e44b7da"}, - {file = "aiohttp-3.14.3-cp314-cp314-win_amd64.whl", hash = "sha256:8b3b60de05f3dcb6f6a00f818bb2ec781cee4de0645f59ccaf99b1d1823b6100"}, - {file = "aiohttp-3.14.3-cp314-cp314-win_arm64.whl", hash = "sha256:1576145bdceeb92382d899751e12743a3a5b8e460a841e3e50543859e54864dc"}, - {file = "aiohttp-3.14.3-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:8800c996b01c2772a783e3e46f3e1abd5823029adca0df54231960de9bfefa5b"}, - {file = "aiohttp-3.14.3-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:ebe8e504f058fe91223351cecd2d9d6946c9d241bb0250d898ffbdf584cc72b0"}, - {file = "aiohttp-3.14.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:30402d03a7c0ff52bce290b57e564e9079fd9d0cb545c8aba73f86a103162d2e"}, - {file = "aiohttp-3.14.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9fc7b5bfec6573f3ae844f457fdde5adeb713f8b8e4a81ad64fc207b49383716"}, - {file = "aiohttp-3.14.3-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8a5fd34f7f7410d1730d5c2ba873cacb2eed3fede366feb268a70ba22581ed8f"}, - {file = "aiohttp-3.14.3-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:270d3dace9ca2f10f0da5d8ebe519b7a310fc6112ed916e32df5866df0888553"}, - {file = "aiohttp-3.14.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3ae5b3a59436d089b5395d910121a390feed4d00578eb95a0fd1a329fe963100"}, - {file = "aiohttp-3.14.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2498f0fe69ead802f9675beca44a7c21c62fdaa4ec5145ea1c3ad6edbee29f85"}, - {file = "aiohttp-3.14.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a0dc483c00da8b673abbb367eb6f8d8f4bcec30eb58529ea13cb42e7fd2dfa33"}, - {file = "aiohttp-3.14.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c7d3a97c678d34fc5b59da671ee9cd630096ddc643e7b5a30d54a2a6f3574d3f"}, - {file = "aiohttp-3.14.3-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:f8fb78a83c9e5f741ca3a68cfb455c1f5bb83b4e7249a3848b3cd78d0a8563b0"}, - {file = "aiohttp-3.14.3-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:74ab5b6a9fb13e873e5a90946588baecaf488745e1db1a4a5c433f971f035098"}, - {file = "aiohttp-3.14.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:bd52f811e65f6fb634b1047159657c98f52b407f8efec907bcfc09da9a4c0a25"}, - {file = "aiohttp-3.14.3-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:f0f177d1b195b9e06376cfd7d308d8a1b920909a609d03ac82a8c73bbb16d3b9"}, - {file = "aiohttp-3.14.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:498c6c623134f8e09a3c4e60bcd607a0b4590dd7dbf08dd40851b27cbb520ccb"}, - {file = "aiohttp-3.14.3-cp314-cp314t-win32.whl", hash = "sha256:b304db572b4368edd8dda8a2274f73156fe15558fca4a917cb8a09fc47af5963"}, - {file = "aiohttp-3.14.3-cp314-cp314t-win_amd64.whl", hash = "sha256:b20032766aedf6261c7a566585a40867d092ac03a0d81592d5370ef9b054f99b"}, - {file = "aiohttp-3.14.3-cp314-cp314t-win_arm64.whl", hash = "sha256:2e1161602f45a54de2ce0905243a95f58cb42dcd378402f3697f5e0b21e9d2e7"}, - {file = "aiohttp-3.14.3.tar.gz", hash = "sha256:9491196535a88924a60afd5b5f434b5b203b6cc616250878dbdb223a8f7844bc"}, + {file = "aiohttp-3.14.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:dea3b3653c1d0d6babd8ce09cb9e5a7776864315d9cfb1c569f926389874a261"}, + {file = "aiohttp-3.14.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3099a389e5b5900fc79c6f613015ff3f8fa06a519744e6001cc68a31ba418cac"}, + {file = "aiohttp-3.14.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:70247156fc76e92919f9aacb820556cb30289a54784299a31714d030bb604d84"}, + {file = "aiohttp-3.14.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fdf2d2a6ad402797422949f4a200a60d43e3a021695c8a7804aa1d5bbef22652"}, + {file = "aiohttp-3.14.4-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ba835fad8d4b5eaea0d34f9461cf499737a720a5f8bc6768d2f6d98a1d3ba8b5"}, + {file = "aiohttp-3.14.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0d4415ca5c9c8b24bc2f5733f870d4eab4929e680203559ba60131dd1a27852a"}, + {file = "aiohttp-3.14.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f6757e02c2c2b0d7985f57be87cf4a7c5b35708f849e1feaea3f77656ddb9dff"}, + {file = "aiohttp-3.14.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2a950bd8418ef54eccf5f9f517f08e37c1adab4bc50a470a0422ee1cc1032e29"}, + {file = "aiohttp-3.14.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:34cf6f2fe4fc1d7183e8cc0d8c48bbdda828a740bc372d8278126149714d343c"}, + {file = "aiohttp-3.14.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:bc0fbe4f79c3b156d754dd062718ffe404c3b31ca878d9d2b88ecdbc72ef8369"}, + {file = "aiohttp-3.14.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:d3d50a5a07a2b31a4369b86dd4a3acf3c2577c45091dbd5e884b198dec56480b"}, + {file = "aiohttp-3.14.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:de15c46a63f46da881d219e44a04778c40114c756e5575b5ea38143b20eb3713"}, + {file = "aiohttp-3.14.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:caa4c0fc9b25e8c679baac68337e9c00deb6baf6601fe6f07e8511469cc1ceba"}, + {file = "aiohttp-3.14.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:3647f8c090632c70abb1e80826e4ee24faea3f895f0283b60e91a0b17aea5e34"}, + {file = "aiohttp-3.14.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3f3e8030169843fb652a60a3d32c206e03f260198aa398985a48b41cd7e42f0a"}, + {file = "aiohttp-3.14.4-cp310-cp310-win32.whl", hash = "sha256:a1205cbdddfca1428129bb6d23e0b292b09e681df951fa828145d83a63ffc0ca"}, + {file = "aiohttp-3.14.4-cp310-cp310-win_amd64.whl", hash = "sha256:e90980e0454a043f81b435309a089ac5deab883779557eb58d004f33990f61e4"}, + {file = "aiohttp-3.14.4-cp310-cp310-win_arm64.whl", hash = "sha256:e63eecf9a4b670a24055d5c0486128af16f83b02d9817abd685d3238940f8752"}, + {file = "aiohttp-3.14.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:eb0093ca0817019d539f0d27bdf98e823944e05bb7efd9d2337a45b92e4d1b06"}, + {file = "aiohttp-3.14.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f482dc9c8309801b4259dd218609d3dae0a7bcdedeac56d90500eabaa0c23465"}, + {file = "aiohttp-3.14.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:aa800bb7c1d00e166493931f2ddacb982db913292503dc2f4884db1f2bc5f105"}, + {file = "aiohttp-3.14.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d90e4dae71b26597f6d1fd12af7d092ae67551edf0e524b73e0687214b79ee33"}, + {file = "aiohttp-3.14.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5b681054b8c3b86aa6a0384b6ad7f5cb78dccbabab1f4a3a6793f98ad1378959"}, + {file = "aiohttp-3.14.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6dfe80b41bcf8d80d2125fb99a171adeb86db2fcb35c1a68e0226acba0ba7f03"}, + {file = "aiohttp-3.14.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:71e4a59c6a8c5a6ae8b0b696c32635913e8f526cc01448762eaf5a3c59df8a4f"}, + {file = "aiohttp-3.14.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f887c3b7fb3058fc1ccd917f52078540c18d938d12018cb9edd64b6811d20c57"}, + {file = "aiohttp-3.14.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0da4525e9ba145a11617d2cd7e44fe1acaf471f9c72a0cad28e72879c1020fbb"}, + {file = "aiohttp-3.14.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f7e8d3c5caa44fdbee3eba1aa1417aa58c74c95d4089fe3c451911117fcbb2f3"}, + {file = "aiohttp-3.14.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:09092cf906c18c824b6b16881da5a89cc9cfa9b15344e496bcf880e9045bee33"}, + {file = "aiohttp-3.14.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:dfd144837e92264878bba6d3a7ffa8206668b21e87748740afd45d193e48c87e"}, + {file = "aiohttp-3.14.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:9413cffe4e0d654b9b524f9c99dde8cbdb37e0ed9a2c4edd2e63adbac6b374e8"}, + {file = "aiohttp-3.14.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:f50d719f97ba488dfb8e306cf8c8cc172ff0683ec0af53917b288229aa555ba5"}, + {file = "aiohttp-3.14.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:68ed4824d96b7afec1f3a8d7fba190fe282718041c5772e20298003e1e3e5d66"}, + {file = "aiohttp-3.14.4-cp311-cp311-win32.whl", hash = "sha256:d7a41d1427136828e6b11d3f3fd194d4357d5205a5b610be7ae07b1ba52ab974"}, + {file = "aiohttp-3.14.4-cp311-cp311-win_amd64.whl", hash = "sha256:2efbdb87e79d596325c4eefaef0f4495d701145e882351310ebc538e854dc7a3"}, + {file = "aiohttp-3.14.4-cp311-cp311-win_arm64.whl", hash = "sha256:ac6e6f90d9360e460c873f945f11dc8698db0a71fec6612823f1aaaab2c11faa"}, + {file = "aiohttp-3.14.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:55b0b335982b0db7117dff7f6a8e3aad43c4105f9e392ca04f9fc8d958a95cce"}, + {file = "aiohttp-3.14.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2d0d8e435582b0d53009dac99e26a3d2be932ed7c9dbfcfc70f3fce1cf943bd5"}, + {file = "aiohttp-3.14.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bf734016932d68e1324cfb638cac7a0866a83933a33badf15e836a7e24efb8c0"}, + {file = "aiohttp-3.14.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:322e3d741b3133bcbb981a41d4f9b8ac3545c18de49c662c2b1f4e1c770b98ad"}, + {file = "aiohttp-3.14.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:15a3bb5f90a4e515071f3617a45dfe26560beb834a6b6e108cdf4bc9e9719ec3"}, + {file = "aiohttp-3.14.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6c044e52df1a466af82cad3518c92ea80cc4ddcfea86966ff083bb34bab29bf4"}, + {file = "aiohttp-3.14.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d613d7d51bf06fe9a5e3aab86ce06d835bb0ceb1a31ea9e8766df4a403ae62a6"}, + {file = "aiohttp-3.14.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c5e93ab6e6330dce40f5b43996f1fc5f2543296d263a5e4b59ff09e73870b7d8"}, + {file = "aiohttp-3.14.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:20134dc8e68c67e7478124d516449428684c0545b2d3faa575691c8bcbd56678"}, + {file = "aiohttp-3.14.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f0daef5ef012369dddc391c61f604111424aa9a96313a69fb899018b09d1b01e"}, + {file = "aiohttp-3.14.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:19290a108de0718b73bc69b86e788975c6a59855c671ebe2a98a07a6f968328f"}, + {file = "aiohttp-3.14.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1494877625baeffab669f4a25a09ec4eea721b49e35363081a2a8e4231fe141f"}, + {file = "aiohttp-3.14.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:a779ce4bf099ce4ad2cdfed62f1ca536d435f53237c54ff40172fddada24d1b5"}, + {file = "aiohttp-3.14.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:0b4cec9fca876e2d4f4c6cb32b177390132d04618867689f43fefabf3f5c3980"}, + {file = "aiohttp-3.14.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:217707313e23aedde9bea67cf8365c7b138b549ae179121ef3f476cf67e3173a"}, + {file = "aiohttp-3.14.4-cp312-cp312-win32.whl", hash = "sha256:18a9fb9a3f6e6e63f4d27f9adeef313a2d3c69a84d31170a844623824e722534"}, + {file = "aiohttp-3.14.4-cp312-cp312-win_amd64.whl", hash = "sha256:69dcb02d33dfe415d5ee342cc63b7d320efcaf4b761701c7bd7bfb91c715481a"}, + {file = "aiohttp-3.14.4-cp312-cp312-win_arm64.whl", hash = "sha256:d36b0263e7c2fbf1750b9f35d4e7e48b0442d8c45e4a89e3e74168821fb014bb"}, + {file = "aiohttp-3.14.4-cp313-cp313-android_24_arm64_v8a.whl", hash = "sha256:264b5a7568306590c44ae4a0237ad1715e35d447ea44ebf324e4c8fd9f78f67a"}, + {file = "aiohttp-3.14.4-cp313-cp313-android_24_x86_64.whl", hash = "sha256:17e5d7c7775d8dd8e894c7ef6ebfa26509de36868a9a6c7c4fcfaf6a1bf43d60"}, + {file = "aiohttp-3.14.4-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:e91dc8fe9cbd052d16f7f1267e285cf8bbac4d2194bd5741f05ef201c77ed09e"}, + {file = "aiohttp-3.14.4-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:8df6157d31703972aec9e3b93963f039ff06318301e30b007c3e941ff8a7ac42"}, + {file = "aiohttp-3.14.4-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:220912b351549dc736c59d104dc443fa615753102bb778a4e98f86e4d94bff6a"}, + {file = "aiohttp-3.14.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2efa42b39bb3f524d5eca3760639b584afdd1143a6b047fda29910ffb75e4993"}, + {file = "aiohttp-3.14.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7e7356059c8262d2c1fd95242e2b7b70fbc6eee3135bf470049cbec3e968b5b0"}, + {file = "aiohttp-3.14.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:fac247d0cc956d732df1211ac2d8a9999ec8dc1a92a90401de9d52e4ead546f7"}, + {file = "aiohttp-3.14.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e2eb0f8b03b4f2bd154ed8cf7c1a119a1ad04808c258bf58f90138c83b8c6d5"}, + {file = "aiohttp-3.14.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7fbe827d27e0e369fd7bb88ceeaa5b46ea006e73e015b9dd77e3723a53162293"}, + {file = "aiohttp-3.14.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a70387146d03af9f047c629b71aa27d9594b3b31ec4c9c91e1a73d0d8bf38e0e"}, + {file = "aiohttp-3.14.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0d0934926fc65744e2fdd44ce68b2d79d5ee608a1e23f0596b35dd696519bb7c"}, + {file = "aiohttp-3.14.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ff4b366510c733974adb7a4102c094e4a6308e6f369315a4cf14d2d0a619cf82"}, + {file = "aiohttp-3.14.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:85fac7c99e0ac3dcbd6ae1c33774e0603bfc732ddb967a6fce4a731103c1888e"}, + {file = "aiohttp-3.14.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ef1e9c3b4a2300023a5dc865889ce532cfaf6798ba4ef78fbbefe738a9d23efa"}, + {file = "aiohttp-3.14.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:97e65916588be0f952b3307aaa8460c0771fa98dbea2dd27dabc9f5d84d53c3b"}, + {file = "aiohttp-3.14.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:840775cb39a9424f9edf142133a1192263ac5bd79117ad2a16602cef6a0c1e9d"}, + {file = "aiohttp-3.14.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:54f9256430ca040d58affbd551513de13a5cff7d065204fc52b9a1b0af09a049"}, + {file = "aiohttp-3.14.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:6112d5931bf12c776188cab81b82bbb17d86e2e1381f2ade6edc43e6b240f0cb"}, + {file = "aiohttp-3.14.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:facc6df39047c4732cbd885bb62e40df20885fcd709fc399e11f81b45ce0fff9"}, + {file = "aiohttp-3.14.4-cp313-cp313-win32.whl", hash = "sha256:7968634fa3a967a2bc0b9aeba1d5a1955501dcd7d0de44b96f7ebfb662797735"}, + {file = "aiohttp-3.14.4-cp313-cp313-win_amd64.whl", hash = "sha256:c0a894b0265d139cd4c2c8bd4643822cc289a4a5331e21e810881bd000ffb2dc"}, + {file = "aiohttp-3.14.4-cp313-cp313-win_arm64.whl", hash = "sha256:da16c3037178e72589d91de59536d271dffc92c2be2b47c38a45538cba54fd29"}, + {file = "aiohttp-3.14.4-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:749bfc43bb1df71bbe6ed215044d5412d786698fc5771d2037436ca49622f3c9"}, + {file = "aiohttp-3.14.4-cp314-cp314-android_24_x86_64.whl", hash = "sha256:c113e4b489d711f606ca278cd8dc89179746244ea841aa0638b1b134600afad0"}, + {file = "aiohttp-3.14.4-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:50969522bc7022026a2c7968e67da5c789f6b716b988edac55dc630390efd5df"}, + {file = "aiohttp-3.14.4-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:cbf92cd7a1c800cee244558dd5630abb45c1c1093ef7b0d546a99f526ff7a9eb"}, + {file = "aiohttp-3.14.4-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:fc76be08ff407fba717a769aff04ca1b4bf9ecfdcb5caadef84a563fdbbd146d"}, + {file = "aiohttp-3.14.4-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:9b416f995b68a921069e6ec61fa07f2ac3ff033d92e97a69f673af89f5bb954d"}, + {file = "aiohttp-3.14.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2c9585101cfca10e74a07cc0ef2bdc6d90616e6750926b20456f429d0608f740"}, + {file = "aiohttp-3.14.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:3c4dcf611b095e7983421f49d01ffbed7ed574891533d606ea115fc23e7c4e90"}, + {file = "aiohttp-3.14.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f8f61a9b5fa56c58e3441f9dc64137f786232d40fb89127190d0e0f84e1e5865"}, + {file = "aiohttp-3.14.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d43e538dfc85b705fd832620d050970ef5613b3869a7169b44a554b9dab9eee9"}, + {file = "aiohttp-3.14.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4135c4bb4b7682a7633fadc7170ea969d843f83aa7224e70111a20675105c985"}, + {file = "aiohttp-3.14.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9055324f156a94fdf6dc9a825ae60f9a2947b88f591ea81ee80826633de62ae7"}, + {file = "aiohttp-3.14.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fb22c8e4b60385a9b414c70c6e8b2d0f4b23ff7fc4bb572ceb58297b228169f1"}, + {file = "aiohttp-3.14.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3b6945559a260112742ed58cde2165488d2fc51aeea34afbaa99d9371b43e1b5"}, + {file = "aiohttp-3.14.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8f4078a90b174f15efb2c8725e8720c40dbd3b2cf6350dbc085c964a938d9de7"}, + {file = "aiohttp-3.14.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:f433c5654e32f72de3ef074d715eb5c7604eb6d28a5c2f3c9e5056f20fe6b306"}, + {file = "aiohttp-3.14.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:871035fe79c636b6bc126ed78df37d72b830dfc751612cb4b1040360edf2719f"}, + {file = "aiohttp-3.14.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2617132524f66338f6c1d218cd16081576aefc40df4f2bcba338a99fbf67fec1"}, + {file = "aiohttp-3.14.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d8bc326133272deee8ef337581550450e4bb735c0520be893b950a1f5baaf41a"}, + {file = "aiohttp-3.14.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:3ec57223e261b7fa9979c12dbce009ca53439be76cac3af27f860d3e4b1d6f7a"}, + {file = "aiohttp-3.14.4-cp314-cp314-win32.whl", hash = "sha256:7242043e71fc449a19a47d92f4e9fd1e0ae8d3f88488982381f422bf6d651218"}, + {file = "aiohttp-3.14.4-cp314-cp314-win_amd64.whl", hash = "sha256:019587c7b3a44917dd61bcb312fd5c034f341dc9851b83e7b41b65e08b68ca0f"}, + {file = "aiohttp-3.14.4-cp314-cp314-win_arm64.whl", hash = "sha256:60dc71d38db0988f37e78f6ad4a010a2f6355cfac3fff5f130cf5ef2231f3f0d"}, + {file = "aiohttp-3.14.4-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:674f53ab9d2794e2dd767619a3384bf1006f8d075e43c19d449a645f8ff641c2"}, + {file = "aiohttp-3.14.4-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:6d243221f73e5ac546c2ed9255100423e4bc092b08b944f23498e8cf3096c26c"}, + {file = "aiohttp-3.14.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0daa016eb74e7888ed5f9ceec6eea3158999c1c4a115ef5cbe88baa8d924f94c"}, + {file = "aiohttp-3.14.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70c031a622aa20add05ebd234b90adaf6bf51c1c88cc305ec760721af83308c3"}, + {file = "aiohttp-3.14.4-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7df363fa7c38945a896e12c69af4a1a56cbd4152791d9e41fd0bac396b72de38"}, + {file = "aiohttp-3.14.4-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:02a508ba5ad91c4d730523584c0adc65882274bba5e37c33bc2f475e37577256"}, + {file = "aiohttp-3.14.4-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0cb3edece6fe6a944eaf97d803b88f606da6bc1600b1894c2969e78ffdc1fc29"}, + {file = "aiohttp-3.14.4-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:21ce1223b461bcc5fb389a7204001578f1c0b85c2da60a84d915ebb021294245"}, + {file = "aiohttp-3.14.4-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:35d5432f0dc1c8b71307e9112461b52f336da225e344283eec4779222c0a1dff"}, + {file = "aiohttp-3.14.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d6ec364493057c118d08292f7cd60865965c209167d8657b45643a26aef77495"}, + {file = "aiohttp-3.14.4-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:70492983b7aed9e61bda82fe1ce80bcba9199610886443133783a288638923c0"}, + {file = "aiohttp-3.14.4-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:a77776673331577147e9d25cd7833a72e8891b47556336cff06ba1569e488b99"}, + {file = "aiohttp-3.14.4-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:73799000dd6fd5247094fd8e6d3e1eb871e41aab5cc35dff9d9f654cd230bc24"}, + {file = "aiohttp-3.14.4-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:27b370fb532e707e8c96d04fa0c96301fb7dba2d50aaca57f6c456cdfffec5bd"}, + {file = "aiohttp-3.14.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:884e0d1c707b6217fee3080b9b4029eee7551af788ee761010df19be158414b7"}, + {file = "aiohttp-3.14.4-cp314-cp314t-win32.whl", hash = "sha256:f2e3d34edb151d3e27f93a8338e2da33e2c7261cd4c879ce5f5fe280cc190ea5"}, + {file = "aiohttp-3.14.4-cp314-cp314t-win_amd64.whl", hash = "sha256:88cf889e51092537fe44adfbff96c2a22d6479d6f92b97b3d413fbfd0a4decf4"}, + {file = "aiohttp-3.14.4-cp314-cp314t-win_arm64.whl", hash = "sha256:ca450c6d42fda7c0bfaf0e200f32f77c5f2c960a33be1e14d197c84588b7f5f6"}, + {file = "aiohttp-3.14.4-cp315-cp315-android_24_arm64_v8a.whl", hash = "sha256:3b10799df72f66c30935bc661cb89e5ac2918debfc575e350e226fbaa846cbb7"}, + {file = "aiohttp-3.14.4-cp315-cp315-android_24_x86_64.whl", hash = "sha256:7ffd7f9dd39caccc6b2f68ec0339435ecad2b20f6ea2e2b3de91bbe464ac7137"}, + {file = "aiohttp-3.14.4-cp315-cp315-ios_13_0_arm64_iphoneos.whl", hash = "sha256:a0a7a227353f7693f73878ad14cd2623cafadd656ababcd20e2adfb7225172ea"}, + {file = "aiohttp-3.14.4-cp315-cp315-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:1c43b6af8e4708e8b3812a9ee4f79895207e12d555b7f6d70deb838e0011e12d"}, + {file = "aiohttp-3.14.4-cp315-cp315-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:97416b2b997e5e9b817e322b2f0a89b76c54cde22471f51ac17ac15e10901ba0"}, + {file = "aiohttp-3.14.4-cp315-cp315-macosx_10_15_universal2.whl", hash = "sha256:ee2a5aa31e507c17841d592364f2148393b17f83bc2766e9211e1eb31ea72a70"}, + {file = "aiohttp-3.14.4-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:5d5d9843c34866e986dd24eef713452609e304550c471320ce7b54b3cfb1391e"}, + {file = "aiohttp-3.14.4-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:efcbf93e31b52c21665b6ebc489eb6fdb0dfd86b592edc677b5cffdfa7b11677"}, + {file = "aiohttp-3.14.4-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2f1a79977794592dcf6485ed964ec2448db527a1bc0c72ec89663658dad3a739"}, + {file = "aiohttp-3.14.4-cp315-cp315-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:515e7ac6d27509dc3ba2c35fb88970b00b9e7b07ee7cc947c8733dcd54ef5def"}, + {file = "aiohttp-3.14.4-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:185154ffa3b54765b5de96f464f41cfa476f8815a03854ae5e0fe4d48fb06e9c"}, + {file = "aiohttp-3.14.4-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6aece59f77959628a22cd2ffe48b102cda9611d680e090f204385b3998a1190f"}, + {file = "aiohttp-3.14.4-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d7205aa6589f1ad1306c6994ff2e1ab5efabacf938f9ffd7682fb225c197b06a"}, + {file = "aiohttp-3.14.4-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f38c7b3ce60d55ac0f2a9fd2edc194789d043fefea15cfb7dcb40b7d5f561146"}, + {file = "aiohttp-3.14.4-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:b0a1274ee4ee6203c15a341cce87f9f3df8151d5496047b3608bb62bd969cffc"}, + {file = "aiohttp-3.14.4-cp315-cp315-musllinux_1_2_armv7l.whl", hash = "sha256:69ce2407951516f413adf3132b88451eababd710ce631f83c81a9603a3640253"}, + {file = "aiohttp-3.14.4-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:b84d3d057c60eb8e0c2b457a1203e868d82709d3f0c272a4975dfd88c2414939"}, + {file = "aiohttp-3.14.4-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:4ecc84f69c9e217984f6877e832e4786a74567432c0bf2b7512919fbdfe18ade"}, + {file = "aiohttp-3.14.4-cp315-cp315-musllinux_1_2_s390x.whl", hash = "sha256:878f2d7950238b2b0a8cbb88054444a125f0cfbc77f54c811aca0483132a9be3"}, + {file = "aiohttp-3.14.4-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:6ff9d3a0e6935f1cb0a6685f9f4b3f0d3e406456d7a434a03cf309b8cd5d7a2e"}, + {file = "aiohttp-3.14.4-cp315-cp315-win32.whl", hash = "sha256:e07dfd7cd360f20be26dc2487ad4c7f21b0b39fdd593e9ebf37d2424ad52defb"}, + {file = "aiohttp-3.14.4-cp315-cp315-win_amd64.whl", hash = "sha256:921fc4f1ad549091bfc39cb5e93925b04f307f83517756a3111843851130efce"}, + {file = "aiohttp-3.14.4-cp315-cp315-win_arm64.whl", hash = "sha256:5be8af106f96fd625f6c264fa9faaaa9e0104089684d9015d2fa5b6eba6407c7"}, + {file = "aiohttp-3.14.4-cp315-cp315t-macosx_10_15_universal2.whl", hash = "sha256:e69727ec04a4608200ff32ec29478299df7657e2eb78c416ae338d0ca6662596"}, + {file = "aiohttp-3.14.4-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:f8b658dd3ef2ebd7708b311ed96ff7cb5edaca001a30018a091504ef492bcb1c"}, + {file = "aiohttp-3.14.4-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:3749e018123df1205161a73ba96d336e17e9cf6b947295e53a43571de420e7b1"}, + {file = "aiohttp-3.14.4-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8f28a3c4fca436b2e594090e7cd22558f5e93a30ca18516c436de982da6fc25a"}, + {file = "aiohttp-3.14.4-cp315-cp315t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e099aae21246992a1d84303ff262262d054c474e46cace33b0d5a69810c71877"}, + {file = "aiohttp-3.14.4-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:fafb521891e646d9cf5da89f01c75e7159d08be7254a024149630a698be28251"}, + {file = "aiohttp-3.14.4-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1d385db2cd154bce0b3dd451cf2a52bec91ebb7be2c3a5d06b14b4391631d807"}, + {file = "aiohttp-3.14.4-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ae8a244889d8a53549851bf595b9f382f64acca57e8762da1ecb26ba786828f7"}, + {file = "aiohttp-3.14.4-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:681dde68ff8d8d5e7d5abcad4feb45bfb457a218310c919825e21fc36cac3d0b"}, + {file = "aiohttp-3.14.4-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:b91a6441fcaed88ca7bca67726b189332df3ceb6ac0dfc98dcb9654bb723288c"}, + {file = "aiohttp-3.14.4-cp315-cp315t-musllinux_1_2_armv7l.whl", hash = "sha256:f15dbbac49bc15eaff95c0b8166525c4050996af578c7be141e145f40ee2cf24"}, + {file = "aiohttp-3.14.4-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:7abae043e87692d1c2adda1fab501bb0612d28c4a23eb082005c1e684f792dac"}, + {file = "aiohttp-3.14.4-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:21f4624fb1051fc1aa10d57a636a80e829f7d0417462d6f1ce380b08fe1a904c"}, + {file = "aiohttp-3.14.4-cp315-cp315t-musllinux_1_2_s390x.whl", hash = "sha256:2e8dc3a0fdaf9d12b954d3d06cc4c355d91b17fb6c671715b593624ca4c379f8"}, + {file = "aiohttp-3.14.4-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:1410b6f6e3d52ea7c02c950496522ce6e85d5543bf154bc80a6141e505251cfa"}, + {file = "aiohttp-3.14.4-cp315-cp315t-win32.whl", hash = "sha256:a65189a89f3e621f7c27a968b5131bbca29126e2b8517dc72b205747a39d4c96"}, + {file = "aiohttp-3.14.4-cp315-cp315t-win_amd64.whl", hash = "sha256:2b2c95f5f769eec92b552969db1f79ea0282058ce5131ed0f2fa540b4af3f14e"}, + {file = "aiohttp-3.14.4-cp315-cp315t-win_arm64.whl", hash = "sha256:54209fff79346ee1ef0d5cbf92fa80a5bb37396406bb4d932e5107f9c29b2d3f"}, + {file = "aiohttp-3.14.4-py3-none-any.whl", hash = "sha256:5c6758ba62aea282c537179cfc8474a90f2b5f7b089cc5ff66d8920d86a9bfdd"}, + {file = "aiohttp-3.14.4.tar.gz", hash = "sha256:831fc5bd39ec2517851e348f613ddb5447a47cf4b71cb09845af7ad7ed45d8f9"}, ] [package.dependencies] @@ -152,7 +194,7 @@ frozenlist = ">=1.1.1" multidict = ">=4.5,<7.0" propcache = ">=0.2.0" typing_extensions = {version = ">=4.4", markers = "python_version < \"3.13\""} -yarl = ">=1.17.0,<2.0" +yarl = ">=1.25.1,<2.0" [package.extras] speedups = ["Brotli (>=1.2) ; platform_python_implementation == \"CPython\" and sys_platform != \"android\" and sys_platform != \"ios\"", "aiodns (>=3.3.0) ; sys_platform != \"android\" and sys_platform != \"ios\"", "backports.zstd ; platform_python_implementation == \"CPython\" and python_version < \"3.14\" and sys_platform != \"android\" and sys_platform != \"ios\"", "brotlicffi (>=1.2) ; platform_python_implementation != \"CPython\""] @@ -259,184 +301,184 @@ files = [ [[package]] name = "charset-normalizer" -version = "3.5.1" +version = "3.5.2" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7" groups = ["dev"] files = [ - {file = "charset_normalizer-3.5.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d1ee1e296209fdce05b81b663250eefa02213a2da7b41bf26f7829b8ba3545aa"}, - {file = "charset_normalizer-3.5.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e9fbdce1e47394b09bc9f26ab117dfc8d6491977a11d86f592bb42c779db2fda"}, - {file = "charset_normalizer-3.5.1-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:00668ebb0609751758682eb0b5857e7c35b9f00e84dfdef062e103244ec94d45"}, - {file = "charset_normalizer-3.5.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ba2f37ee79e6338845261a3c5b1784e5d1acdff2c0785b284f1b633033d136ab"}, - {file = "charset_normalizer-3.5.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ce854f5f478050ade5a238731c4ca985a7d3b3cb53ff600a9b5c3b689b5f0a7a"}, - {file = "charset_normalizer-3.5.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:96eefc178f8636b9c760c5829345307fd81cfae9ab1e80997dbddeb0f54ee9a3"}, - {file = "charset_normalizer-3.5.1-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:366ec70f5547c640d3ce1985722490f23faf4eb5216a7eeba78277490e78dacb"}, - {file = "charset_normalizer-3.5.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:950f23cb393f85543777b0433f082cddd25b51ab398eac7971146495679efe5f"}, - {file = "charset_normalizer-3.5.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:c1dcc36dcb96abc02236e182d17e0f71430152a6c2c7447421da2d2dc144edea"}, - {file = "charset_normalizer-3.5.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:07ffd07412fc5d5e84cd8952acf9ff7e4ed7a708e69d1bada19d8ba91711353f"}, - {file = "charset_normalizer-3.5.1-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:f5542f9b941279d82d41eb0aa9f98eba36fe4df5c7086c651df7944935b37182"}, - {file = "charset_normalizer-3.5.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:a545775cfe815855ea32d7c27731d79da358ef2055b4a25830231b1622dd18aa"}, - {file = "charset_normalizer-3.5.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:494b70049a4d69aec6e8137c13af4cf8db8c9f9820a1392ac293b0dd2987a818"}, - {file = "charset_normalizer-3.5.1-cp310-cp310-win32.whl", hash = "sha256:94fbf1c0c6cc0d3d5e50f9a9313a8cdca90dd696d34b381cd1704f8c9e939f20"}, - {file = "charset_normalizer-3.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:be47f99644b208bff7766314013f9acf57b056b04191d570d68ad14022cf5b1d"}, - {file = "charset_normalizer-3.5.1-cp310-cp310-win_arm64.whl", hash = "sha256:a6d095662e73e74f0a49988e0593373e243e3a52e27bfeea0a859e88acf4a0f5"}, - {file = "charset_normalizer-3.5.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:eda059b6bc8bc0812d626fd91a7ce01bf583df0a61296eff390fd94141a34e30"}, - {file = "charset_normalizer-3.5.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aa2bb0b37202dca27175591f761108b5d34096ade1191ffe4808bdf6b1571488"}, - {file = "charset_normalizer-3.5.1-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0b2b1b3fa5670c127b246df1d0c059defd41f689a868a3b9d79df9b1cac42d22"}, - {file = "charset_normalizer-3.5.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6e5e4d73d588ca5ed09df1b7dcd1b203d1df3c542e3f50d126c947d432b10731"}, - {file = "charset_normalizer-3.5.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b54e7e13267d49ffbfe68e25b3cbd774dab38fa37238f71265e91b36146eb21c"}, - {file = "charset_normalizer-3.5.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c7b742bf31c88566b4bb6335a7f393bb322e580b6bb98df7bd0c25e6e3519ce8"}, - {file = "charset_normalizer-3.5.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6ba32c4d2abf1d2fe7cf27d280f4cca5664233b0f885549c7761719eb977f486"}, - {file = "charset_normalizer-3.5.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0722590aabf9dc6a6c0343d523c05458fa2b5047dbe6302fd526bb570600753f"}, - {file = "charset_normalizer-3.5.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:aa1099b956fb795e686d073568f6dc002a0bb89765ea6d5b055dd7d9bf1b116c"}, - {file = "charset_normalizer-3.5.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:bd6c173f04743d483881bffa1478d5a4624475b8cd1d2194956a75548e191c18"}, - {file = "charset_normalizer-3.5.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:f298e218441525d3794428b4c8b8fb8662c6d3ea79925d4807ee6b9a96a3bca5"}, - {file = "charset_normalizer-3.5.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:6e2912d4babbc65196ac13c2f53468dc57fb8b9c25ef913e8c59ddf7c6dc0e1b"}, - {file = "charset_normalizer-3.5.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3d27167433c0d5f18dc850f07d0b3816221984fecdc405d6c157a6f0b8f8e9e6"}, - {file = "charset_normalizer-3.5.1-cp311-cp311-win32.whl", hash = "sha256:ac00177c4831ffa650f8609e4bdddd5fe09c03b1c0c47acece7e6ea20421598b"}, - {file = "charset_normalizer-3.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:f9b1e28d0e8dbfa858abdba91d6b547beaf2df1a59bec6da6faae7b96a4991a9"}, - {file = "charset_normalizer-3.5.1-cp311-cp311-win_arm64.whl", hash = "sha256:ae31a1a1db2ee6cc2942fccaf695c934bc7f3db9f2133a3fef1f367cf1a4ab10"}, - {file = "charset_normalizer-3.5.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:5b6d1386bf0096d26d3a863dc0a487a5b4eb9aa93cf5ba69683d29dde6b9d60f"}, - {file = "charset_normalizer-3.5.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4582c27e8c889d64811987b5967fbd3ae0c823fe1fd933b543d55ac20bb475fa"}, - {file = "charset_normalizer-3.5.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:1d1c7a53a6c2103925cdd6d7229f8c567379f211c869793df679f2e9f738c369"}, - {file = "charset_normalizer-3.5.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e6621fb2a4988d6e53eedc455e5903e2679f3967b8acb3d639f1b63c14a2e893"}, - {file = "charset_normalizer-3.5.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7c0c10730342b0c9b35dd1d619beb8214e520bd96a1f870f452680b238aab3e0"}, - {file = "charset_normalizer-3.5.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b9af956078716df40d985fb0dfeb2c2120c5ca92ba4ff4b388acfd01cdc14d08"}, - {file = "charset_normalizer-3.5.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f9f8405c2c758532c74fed975dbee57be1f31a6e865c031870c79a6ed3212ada"}, - {file = "charset_normalizer-3.5.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:96fef3e886d6a9874b14f27fc193fbdc69d5d8035783d86aa4e1cea594e695f9"}, - {file = "charset_normalizer-3.5.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5d8531a6569d025f68e2321e7638fb7978f23db58e5f69f56913837aae03816e"}, - {file = "charset_normalizer-3.5.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:aae2ee51122d3ae968a3837d97dc24a0aeebb0dea23694422cd172bd30017cd6"}, - {file = "charset_normalizer-3.5.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:7235dc28fc6dd9d832ac7c7bce95367dedb85929f17368a0c2bee1e080b9acbf"}, - {file = "charset_normalizer-3.5.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:4abdc5f9ad448c1ecbfae2974b820535d6bc6e7eef63babbab3d81cf46968c71"}, - {file = "charset_normalizer-3.5.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ba501e667c17d8411f98e67a022d9604ef179aff0e459b7e292c796837c13573"}, - {file = "charset_normalizer-3.5.1-cp312-cp312-win32.whl", hash = "sha256:cfa1c0cc3a8f9f53f1243a5a99ac36fd003880199383b37672e86ddda9cb07e2"}, - {file = "charset_normalizer-3.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:3617ac3cfd8b9888f145ad89dd6e692285834b0201c6074a5eeaad3fd4d668c2"}, - {file = "charset_normalizer-3.5.1-cp312-cp312-win_arm64.whl", hash = "sha256:88e85ab89cb822c1e635f51d6d32e488f94e002e70e2f492bdb8b945543f345a"}, - {file = "charset_normalizer-3.5.1-cp313-cp313-android_24_arm64_v8a.whl", hash = "sha256:4f298bdadb8f0b9e5672877f647d1be9373ef5320c9e2f049795e26cad28b6a9"}, - {file = "charset_normalizer-3.5.1-cp313-cp313-android_24_x86_64.whl", hash = "sha256:88ca277405c2d3b71c4e1c2ee0e7966e807bcba86a69d11e19ba199d18ae4491"}, - {file = "charset_normalizer-3.5.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:9362dd90aa7dab48c0054a21187791ccf05473f7dba5d92b8033ae62164675e7"}, - {file = "charset_normalizer-3.5.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:977cdbd483a9cff38179bea4fd754289a6f2195c7abd414aba85410b3e66cc5e"}, - {file = "charset_normalizer-3.5.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e90251c0c7bdd54a100a0dce3c07b7e637278c93af29dbf78ebb89a58c4bac7d"}, - {file = "charset_normalizer-3.5.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:94d78ecec2605a8d0398b0f365d5f12a63248438516f5dac536a5eff7337df4a"}, - {file = "charset_normalizer-3.5.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d59b75732e9b6f27388e10c14b0259cc5f2e48c78627d185e6a177b58ad3cffe"}, - {file = "charset_normalizer-3.5.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0d929fc574b4d6fd9e7c0f5c2ede8716a41911923aa7fa5fce38e0818aa4a1ac"}, - {file = "charset_normalizer-3.5.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:394fea06235c8543390050ed5f529187074b029fb027213f6c46ac11ab5d950e"}, - {file = "charset_normalizer-3.5.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62b55f6722735a6c472f88361cde6640608773d9443cebdbb51abf436a1fcdd3"}, - {file = "charset_normalizer-3.5.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fa48b1b63d639f9483e0633e092f5851e2348c352f1f9bb6c8182f87884ef876"}, - {file = "charset_normalizer-3.5.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c71fb0d56c920c269cd3e2e3fe7c610e3f1fdb21a6ce60efa6430ff63676cea6"}, - {file = "charset_normalizer-3.5.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:485a0d363cafefcd2538a73c7c838daa2035f09b2c9f9b5e3133f80c6aeb84c2"}, - {file = "charset_normalizer-3.5.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5c0ea61a470e070686aa30892fed79e297d2c8d0ab46b8bcdf027d38c51da591"}, - {file = "charset_normalizer-3.5.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:90b7481fb62fbe172c558bc6fd1c4c98d82004a54a7551f20e11ac9bf0b8708c"}, - {file = "charset_normalizer-3.5.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:35fe081843b35aad20ffeccec3eeffbe637b15d14f3fb22cc1b59cd8ec17e93c"}, - {file = "charset_normalizer-3.5.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fd0350afdc3aabd5576f60ea109228bd5538139713c7b094c5cd27c73a98bc6f"}, - {file = "charset_normalizer-3.5.1-cp313-cp313-pyemscripten_2025_0_wasm32.whl", hash = "sha256:9d9a0dc7cbe9bec24c3f767c9122c41fe5a1bc43f47cd099d00d393e09769de4"}, - {file = "charset_normalizer-3.5.1-cp313-cp313-win32.whl", hash = "sha256:d63600d620ad0064c3a748b950ac5ea38a80190e5498532efefa4b7b3f1da1f3"}, - {file = "charset_normalizer-3.5.1-cp313-cp313-win_amd64.whl", hash = "sha256:aea996a6aba25260827c9ea511d1addfde2da9eb686ac961838509086188b7e6"}, - {file = "charset_normalizer-3.5.1-cp313-cp313-win_arm64.whl", hash = "sha256:fd0a274c0e5f9a21565cd9d3dd749b61f96b7aa1e20a93aa1ba4029518f2e5c0"}, - {file = "charset_normalizer-3.5.1-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:774d157f112367ff4abd29019f38f023c24e00e56edc7829c20e358a5a913ad8"}, - {file = "charset_normalizer-3.5.1-cp314-cp314-android_24_x86_64.whl", hash = "sha256:26422d45fd13551cf564c58932f7d72b4f58b93b0fcf18c35ba6be12b46bb102"}, - {file = "charset_normalizer-3.5.1-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:09a7bba9f739468c8e78c36a75c33768e53cb1959fc638f510454c14683f00d5"}, - {file = "charset_normalizer-3.5.1-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:4c9548dc78002099910abaebc0a72ac58b7d30931869e0351c09b507dff4ece3"}, - {file = "charset_normalizer-3.5.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:c428c6c31eb5f4277d7f8eccaf767fbd548ddd5ce3c8b4f4cbbfab3d96b5904c"}, - {file = "charset_normalizer-3.5.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2f06b7eae9dbe77fe1d644ca244dad508de8d302870a43f3c559b521270938a0"}, - {file = "charset_normalizer-3.5.1-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6b7430cf5728e68f6c462254009a6ef4086e1bea43cf2f57aa9c55fb4f50ff96"}, - {file = "charset_normalizer-3.5.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ab743e9bc90c1f73552ec33e10e3331315acd2c397b36065b591b0181de533cc"}, - {file = "charset_normalizer-3.5.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f6f7deae3feb4edfa2efaf7c574fe88cbf055038a6abdb40188e4fff66d5699f"}, - {file = "charset_normalizer-3.5.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15f024313246a4ed976c60f440bb8d257815513a681d212ff74fd46f7d715a90"}, - {file = "charset_normalizer-3.5.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:823f82903d189af463d7df250ef1f7f696f3cee08cc8d91deb565e8d425f6506"}, - {file = "charset_normalizer-3.5.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:01e93745f7f219b703b60ba7afead36cfc4242782be5af484673fc500df12da5"}, - {file = "charset_normalizer-3.5.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:329fc3ccb63ad22d867d84c2adea759a64079a37ba4a343433b02c7a2816871e"}, - {file = "charset_normalizer-3.5.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:bb57753e36e4855b8ca375069482250a6246372331a3e4f3407eaebb007443f5"}, - {file = "charset_normalizer-3.5.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:fce8cbd4997efeb450bd298b54f755dcdff18d496f7a5ddbb4867c6d7c88fdc3"}, - {file = "charset_normalizer-3.5.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:6c9cdde8becb25a7fde49924511aa2644d6f8081cc8df8e9452724303348d8e3"}, - {file = "charset_normalizer-3.5.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9ac4444d8d4fd4c4bd08bf451ed3167aa9e7ec6cdb41b648794f1d1103652e36"}, - {file = "charset_normalizer-3.5.1-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:f03ac127268b43ef4fe9e6ab6794a6794b49485a0cc0c1db79876d2f33f75bc7"}, - {file = "charset_normalizer-3.5.1-cp314-cp314-win32.whl", hash = "sha256:1f5883d77fd409a261abb5dc8ccbe335720d798b1de4abb3b1d47ccbbc76b53b"}, - {file = "charset_normalizer-3.5.1-cp314-cp314-win_amd64.whl", hash = "sha256:c658c50ac0c98cd755a2dd50b7977d3bca7df401dcc47fbdfa87db53ef7d4e8b"}, - {file = "charset_normalizer-3.5.1-cp314-cp314-win_arm64.whl", hash = "sha256:4bea7f8ebe90bbd7f0e4a2de42ca6924ba23e3e76418c408ff82f1d46fabd687"}, - {file = "charset_normalizer-3.5.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:fbc597639158fd7c14d55e808718848319540f51b0e6746e3eefa59723a4a348"}, - {file = "charset_normalizer-3.5.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e71c909f353863b2b89c83de2ebed71ea6d0df8a6ef65a128193c5e650766bef"}, - {file = "charset_normalizer-3.5.1-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7ac76cf9afd34929d76eb7fcb63be476a4853d8a96f0dcf2d0db68a0cbdf9885"}, - {file = "charset_normalizer-3.5.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a3a370082ce34d0612f421e15fe011c53bb1feff21a26d06ad4fb244dab5a375"}, - {file = "charset_normalizer-3.5.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:256dd4d85d9e4dc595e2bc983c980e73f62ddeb3165c58b4c3dfe78c5c8548c1"}, - {file = "charset_normalizer-3.5.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:58d4aa13a59c969dbfdf9e6a9560e242cbfd9e8a8f50c2747714df1a423adf65"}, - {file = "charset_normalizer-3.5.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0c6dfb5ca6723eeed15aa8e564a014d69fcb8812f94eef11fe3631e0508199f5"}, - {file = "charset_normalizer-3.5.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c010f5581d9c612804cc59fcf7b524b707fbcb72828551237ab545bb5c7034af"}, - {file = "charset_normalizer-3.5.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:52ec005752a56ae79547a05c0139ca2501a0c866390b6115008456b9f0e7cde1"}, - {file = "charset_normalizer-3.5.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:2bced4061f000f7187254a02ad3433ae17eaf991747ceea2f478422590a5bba9"}, - {file = "charset_normalizer-3.5.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:9eea3ab2597a5e65fe65296e2d6a84570845a6b55532d90333d740d48bbc850a"}, - {file = "charset_normalizer-3.5.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:496846868fea80e479324862fa877f02411f2fd0f83b79ccee2607aa68b2a032"}, - {file = "charset_normalizer-3.5.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:85d5855daafc240cc045c026d7a15fd198a09b0fc8ff6f5ecbb5297b509cb11e"}, - {file = "charset_normalizer-3.5.1-cp314-cp314t-win32.whl", hash = "sha256:58d3e12c88e0950bca850ae1f7c256055c097639c2edb9eb123af9807d8b15e4"}, - {file = "charset_normalizer-3.5.1-cp314-cp314t-win_amd64.whl", hash = "sha256:acaf604462bf330b0d07e7a07c1d6e4adac79e5fb13e9c5140590542cafacc00"}, - {file = "charset_normalizer-3.5.1-cp314-cp314t-win_arm64.whl", hash = "sha256:fdb8a068947befafba9952162645dc2fecaeb400e64584829ed5e9b2fbe21a7f"}, - {file = "charset_normalizer-3.5.1-cp315-cp315-macosx_10_15_universal2.whl", hash = "sha256:9085f87b0e38a2b92b8923059b4e8789fe40d9279712d15dcc670048d77079af"}, - {file = "charset_normalizer-3.5.1-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2679de311c7946dde5d3b6f44941844133ff5c7cb86099c0061ab1e8901c20a8"}, - {file = "charset_normalizer-3.5.1-cp315-cp315-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:baf3775a2635e5a11fbd5e4e64ee69c7e86875d224a5c72aca4c141064589a90"}, - {file = "charset_normalizer-3.5.1-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8ac8c94b6539074e0f40899301273ac8402b9b3e01c7b7ba269ff30340aaaf20"}, - {file = "charset_normalizer-3.5.1-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8fe532b3c966d1fb794e0698e4589d0444017ae77fc0b31edea13c0e35bcc449"}, - {file = "charset_normalizer-3.5.1-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5c84bec0ab5ae0c64bfe73a7d2adcb5ce73b467523fc27fd6a28ab2aa6cbe35a"}, - {file = "charset_normalizer-3.5.1-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:854066be00447fa8de2ccbbe893e2ffc4b123ef16d897af794c1e18bd4a714b0"}, - {file = "charset_normalizer-3.5.1-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:21b82d8082f6f5e7f456ef0bd16323d08de1266efbfeb476e64b2a91d1471a4e"}, - {file = "charset_normalizer-3.5.1-cp315-cp315-musllinux_1_2_armv7l.whl", hash = "sha256:838648accb3a7fd9803fd45c87bce8509648eb0c11bc34e216141300977244f2"}, - {file = "charset_normalizer-3.5.1-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:195ce897c6153c0700078142cf8efe3e6454ca4cf4357499e4078dfd83396626"}, - {file = "charset_normalizer-3.5.1-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:978eab16f55b4ab2c2a745be9a0a840bf8f09a7f227d9c76eb30214d078865a5"}, - {file = "charset_normalizer-3.5.1-cp315-cp315-musllinux_1_2_s390x.whl", hash = "sha256:cc0329df4caaceb950d2f580b5ac716a377f7059624a0bafaeaf8a218c6ed774"}, - {file = "charset_normalizer-3.5.1-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:687c9ca3035544b113bea2055e180af96fb63c0c476e22a9180f51925186e7b7"}, - {file = "charset_normalizer-3.5.1-cp315-cp315-win32.whl", hash = "sha256:706bfd38730a5ac7a365793269a00f4e988178cec121391f4248d84ad8c972e9"}, - {file = "charset_normalizer-3.5.1-cp315-cp315-win_amd64.whl", hash = "sha256:92caef967d287a407085d61176fce4012b1dd62daed4eb6d5ceb26d3d2538712"}, - {file = "charset_normalizer-3.5.1-cp315-cp315-win_arm64.whl", hash = "sha256:5fc45d653ea8c9a20479167e11d4a0f8cb2fa3470737ab6f9c827532313187b7"}, - {file = "charset_normalizer-3.5.1-cp315-cp315t-macosx_10_15_universal2.whl", hash = "sha256:59171c6e45bf07d0d5cab3b0bf81d945035530f6873398b3b531c31184d46663"}, - {file = "charset_normalizer-3.5.1-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9dbdd9205662134957cf0c324f639bdc5031c0ca056e2369e238db75187c0f11"}, - {file = "charset_normalizer-3.5.1-cp315-cp315t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e4b018dc5a0eee4676e38fe84a47a427816c590b93b55d9025274ec4d6ffc2dc"}, - {file = "charset_normalizer-3.5.1-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ced3fdd71aaa83ce593746c2edb42b7a59cb4c19c8b5c407781c72e493aae55a"}, - {file = "charset_normalizer-3.5.1-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:19a3dd5aa73cef1c99687c4fc57db016a9c17104ae1185da88ba566a5d3bebe4"}, - {file = "charset_normalizer-3.5.1-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cc5d36d96478aa9c60654bd932525bf32964c62a7281eafdf16d85003a8d6004"}, - {file = "charset_normalizer-3.5.1-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:04368edf83514385ffc3e1cfd4546e595f4f1272dd23ba437a93a9cc3741d47b"}, - {file = "charset_normalizer-3.5.1-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:9b5db6052055d34d41230fb78d7c439c23dc536a9896f6cb039e8dd92cfc1263"}, - {file = "charset_normalizer-3.5.1-cp315-cp315t-musllinux_1_2_armv7l.whl", hash = "sha256:252d099029bcbea642f2a06c4ed5046bdf8b5a8150b64afa5e027e88b106e5ee"}, - {file = "charset_normalizer-3.5.1-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:6199d5606e2bbf2b096cf64d03f8b6790c91081d5ac866b8e7bb6422738cc60c"}, - {file = "charset_normalizer-3.5.1-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:77efcff2b23071c349402ac1066667a3d011f62398d81408c9b88ad991747c9e"}, - {file = "charset_normalizer-3.5.1-cp315-cp315t-musllinux_1_2_s390x.whl", hash = "sha256:a5cbd90ecf0fc62e64726917ad083b73001f0563657a87ec3c0b504e277dc90d"}, - {file = "charset_normalizer-3.5.1-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:4d26f14f041e83dd8edfd61f4cd4fa7285d31798b5bf1f28e70c367ba6c41d61"}, - {file = "charset_normalizer-3.5.1-cp315-cp315t-win32.whl", hash = "sha256:ac13b004224fb341e1e25a1ed5e19d32f57cdb2a403e01f003b46f051a550f6f"}, - {file = "charset_normalizer-3.5.1-cp315-cp315t-win_amd64.whl", hash = "sha256:35aea775dc2bd5f54cd84a1cd2696cc3207c479cb9cf0bd346f0d343e4300ddb"}, - {file = "charset_normalizer-3.5.1-cp315-cp315t-win_arm64.whl", hash = "sha256:fb78f6e7fcd8ad785d28cd577168bc1aaee827b25bb8755638f694794ea98f0a"}, - {file = "charset_normalizer-3.5.1-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:41876ee62a3dddf48ff1121ad8f0798032aa03f2fd35f21f34a4cab14f18d8d2"}, - {file = "charset_normalizer-3.5.1-cp37-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a6dac12ff6b846103483683f60c5f8fee205121adc58ffd87e90a90a3af69e99"}, - {file = "charset_normalizer-3.5.1-cp37-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cee5dd7c6fb5dd52a0fe2a740f9bc6e3593f5f8b1788bde49de02086f30182b2"}, - {file = "charset_normalizer-3.5.1-cp37-abi3-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:343fb4f2821043bd87095f7b08a1a181febc8e36ac64212143bbfd0a0e1bc235"}, - {file = "charset_normalizer-3.5.1-cp37-abi3-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ae4a097991662cd4fff0ddc74e0fe7874f82e00042fa0ea00855645ed0c79598"}, - {file = "charset_normalizer-3.5.1-cp37-abi3-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4b599739b93b2cbeded49645ae3c8d1405c29ddfbceac1545c87a3f9580a9e96"}, - {file = "charset_normalizer-3.5.1-cp37-abi3-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b39b69b347e5e47a3b5b8cfc005c68c1ba347474e3960236c4944a8ecd174962"}, - {file = "charset_normalizer-3.5.1-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:a2028475ba855475b8b4d3cfeb4994269c967aea8b9892dfba907f4263a863a3"}, - {file = "charset_normalizer-3.5.1-cp37-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:36047af20e17097c3bb9476c2b7655f2f7aa51322c0ba58c07695bedf755a950"}, - {file = "charset_normalizer-3.5.1-cp37-abi3-musllinux_1_2_ppc64le.whl", hash = "sha256:4c4fb141a727957c93edfe5c32a26ceb6b5f6461d67146e2d39f51e16170bea8"}, - {file = "charset_normalizer-3.5.1-cp37-abi3-musllinux_1_2_riscv64.whl", hash = "sha256:2f293479cce755c75f1697e87c409b7ae4c555c7dfecb6e988ad13abba943031"}, - {file = "charset_normalizer-3.5.1-cp37-abi3-musllinux_1_2_s390x.whl", hash = "sha256:3588e376b3ea2eea84976f67273d679f229e24c66dce7b82ae45aef04ff6e072"}, - {file = "charset_normalizer-3.5.1-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:e199fb99720074809a7720f1c0b4d919eea8b87e88713e0f8f602f7bef543d9d"}, - {file = "charset_normalizer-3.5.1-cp37-abi3-win32.whl", hash = "sha256:dd732602a7009217f658d5863d12d79d373a4de0eebc111094bcdd3bb8e0a6cc"}, - {file = "charset_normalizer-3.5.1-cp37-abi3-win_amd64.whl", hash = "sha256:70055ff39b97c99e7ae40ea3e393fb62aa2e44dbd9b29f8d14f42fb0025c3959"}, - {file = "charset_normalizer-3.5.1-cp37-abi3-win_arm64.whl", hash = "sha256:87e4f41d375c0b9be2fb5251aee4b8a689169e134535aed81bf085c3b647451e"}, - {file = "charset_normalizer-3.5.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:85de3134b5379856e323ba37c19c9256d39425f7b76a63af52b09fb4664c2e8f"}, - {file = "charset_normalizer-3.5.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3e5e1224c0a6a90e05843e07adfec669edebec17801c67072f51e59561d63c0b"}, - {file = "charset_normalizer-3.5.1-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5e2d0e146dcb57034f8b97dc58d2d512cb90aba253960ce449f695fec6a82c6f"}, - {file = "charset_normalizer-3.5.1-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e06efa066f7dbadbc84ebc126a97c452a6451dfcf589d89d788484949e1cf795"}, - {file = "charset_normalizer-3.5.1-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:994e883d17c559cdfd38c84003c8b27d25424a1077272a17e7cd27bfe0bf57b2"}, - {file = "charset_normalizer-3.5.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:789b8982559ae28dad2356519f841655756cdcd96616410590ae0b17454ee64f"}, - {file = "charset_normalizer-3.5.1-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a951ad59cad9145664a730d3036b40b844e74d2d3683da40111463cd3a83845d"}, - {file = "charset_normalizer-3.5.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:55261ac0d2941c42f196dd576f543d87a8ee03cd6f5e30dfb4d807b2e3b9121a"}, - {file = "charset_normalizer-3.5.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:5ca0555312ae2fe82715cada7fac375530c2f3349e1eaa1bcb33d0283ac79a18"}, - {file = "charset_normalizer-3.5.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:2e9cf9253119d8e5d111f05d71626786fd3d6193817316eab1ca088cdb8593cf"}, - {file = "charset_normalizer-3.5.1-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:433c5a81eade63b47e522303bad236f59dba55ea6951746f5558355eeed8c75d"}, - {file = "charset_normalizer-3.5.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:13e3afe97712e8887cd516e960c63f0b93122971e5b5e4b2622fe7701771e838"}, - {file = "charset_normalizer-3.5.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb12fb2ba69ffa05f8695f61c69e591dc4b4a12ac3757ac8af8adb259bf56d17"}, - {file = "charset_normalizer-3.5.1-cp39-cp39-win32.whl", hash = "sha256:56490c595a28b1bb27dfc583e816152a9767721ef58b2c03b13f954d2f707420"}, - {file = "charset_normalizer-3.5.1-cp39-cp39-win_amd64.whl", hash = "sha256:012a22b88a77ca2e59b98ac5889b0deb604147666032f45e6d6e217634d2550d"}, - {file = "charset_normalizer-3.5.1-cp39-cp39-win_arm64.whl", hash = "sha256:29880d17a8eb0b5cfdfd8944b468322928059aa35f1f5fa8ff22b149ec0b42f8"}, - {file = "charset_normalizer-3.5.1-py3-none-any.whl", hash = "sha256:6df0ec430f9a831772c23ca5a224cba36517a58a84bb32c32bb59a9fa67c47f6"}, - {file = "charset_normalizer-3.5.1.tar.gz", hash = "sha256:6117b84ea48435e5356dc737f5121485c30920ba43375fa7b434fd753df0eac3"}, + {file = "charset_normalizer-3.5.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:195c26fb65950f8fce54e26349852b7bdd7c5f120aeefbcc440b8a20faaed4a3"}, + {file = "charset_normalizer-3.5.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9373ad13ef0d2c0fb761e04e55bfdee5a08b52cef2c882c8fbe9935b1517152e"}, + {file = "charset_normalizer-3.5.2-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ddf19c062bea7a0cc80f519243d2c01dd091be0cf952a0750d4ad576709559f5"}, + {file = "charset_normalizer-3.5.2-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3d14b50de6bf4d0edf857a9386836846f982b8f524e188e2e68b96d702bcf4aa"}, + {file = "charset_normalizer-3.5.2-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:28a15fdad492a99b6eccfaaed66ef3f74050680545ea61ec8b2f4c538f1f1320"}, + {file = "charset_normalizer-3.5.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8a893cc101149f80a653f82062ebc95b34525a2614382e1da5458fe7c6997249"}, + {file = "charset_normalizer-3.5.2-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:619799369eeef6366ed3e8755a5670f4f2f0fb6b30a0fd7264dc0fdc2357058e"}, + {file = "charset_normalizer-3.5.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:447441e76ec720b15e64418d32e092297340387053047c7c694f579efb0ee1d9"}, + {file = "charset_normalizer-3.5.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:62588a277bfb59def052abd940703fa35107152bf479781a878617d60faf8fb5"}, + {file = "charset_normalizer-3.5.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:44bd4fbb29dfbeba60e7d2bd000c59e4b21ddb3cc53912b14048d37092706d7c"}, + {file = "charset_normalizer-3.5.2-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:30fcd120b732aa79317f08dee04d7de0847822e4cf7ee0e9f445bb958832252c"}, + {file = "charset_normalizer-3.5.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:50e3adfb96fc189eb27b1cf62d3b598b89b4bb0420d93a3d3e42e137409011be"}, + {file = "charset_normalizer-3.5.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b736353c0a625bbd5fcec108576e2385db3496f4f771f785ff32e108d3c3bc45"}, + {file = "charset_normalizer-3.5.2-cp310-cp310-win32.whl", hash = "sha256:f5833ad231be5eb6553de524a70f48d71b2c8563101750531e0b80184e175cd4"}, + {file = "charset_normalizer-3.5.2-cp310-cp310-win_amd64.whl", hash = "sha256:1461ac396c4fdb983a675f20aa555624f0ee18ac83d832b9244ffff3d8055275"}, + {file = "charset_normalizer-3.5.2-cp310-cp310-win_arm64.whl", hash = "sha256:c6708715abcf3c73b99508253e961a9967f02fe536532834149574eda6de0d1c"}, + {file = "charset_normalizer-3.5.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3d21b8b13c7592db2ac5e544a6d83187b995257472b0c9e8351b6d507ae37ed6"}, + {file = "charset_normalizer-3.5.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d760fe2a4d7c3b226cb9026d6a842868d52a7901bd98420e1baf14e80da85cf5"}, + {file = "charset_normalizer-3.5.2-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c9790464842f85f437dbbb54417eda1e0e6bfc52dd8d22d6fd1c994b73b2dc74"}, + {file = "charset_normalizer-3.5.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4685902cf26edf013ed7a3da0f426ebba7a00ebb9541386d835afbf002c11cab"}, + {file = "charset_normalizer-3.5.2-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4495c5002a7b28557e7e222e77e0b661183e432b7d6d2e788101e3f240e05b8c"}, + {file = "charset_normalizer-3.5.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:211d5a3eb6af8f513b8d4ca19a8c1b7accab1b5f0d3175f9826b03c1a920dc1f"}, + {file = "charset_normalizer-3.5.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ef4fcbf3327382cd4c9f540babd61248208af7b93eec4de397b4d5f58a09e288"}, + {file = "charset_normalizer-3.5.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bd16aabe4a02a297c23417aa17ac6299dbd8c49f673bcd645b4929b11f5a4400"}, + {file = "charset_normalizer-3.5.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:fb9e68df06293761f9fe66ade60a9bc6d0f5e42b8acf2939a9158af86ab0e5bd"}, + {file = "charset_normalizer-3.5.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:59f63901b0031c3136cf64704dcb21de0bbae62ce2c9529bc39d27665463de37"}, + {file = "charset_normalizer-3.5.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:304d5463e65a35d7bb0850550e0780395395f6fcf452f04db7d5ca7cecc425ac"}, + {file = "charset_normalizer-3.5.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:9cf9b1a857e25c4baceeb3624e92a56df3668f398c4acba74e174d81fb4d1d3a"}, + {file = "charset_normalizer-3.5.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:114e4d0c92d618409ed82a99e22b5c5e768fe995f2973f78265f4524f49d4640"}, + {file = "charset_normalizer-3.5.2-cp311-cp311-win32.whl", hash = "sha256:2625388c6c754520c37abaf3b41eb34d1cc4a373f457898f08606c8e362b891d"}, + {file = "charset_normalizer-3.5.2-cp311-cp311-win_amd64.whl", hash = "sha256:87e50a3e7cb90af586b6c5faf23e302a970415ac73bd7bd90a515a04b427ef96"}, + {file = "charset_normalizer-3.5.2-cp311-cp311-win_arm64.whl", hash = "sha256:254eb48b9fa5ee9898a3c445825a1f340fe53712a098904b39b0bddba8ea3cb1"}, + {file = "charset_normalizer-3.5.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ed2a239c0ea213acc1908150a3037257083c7c083128f1a4cec2ec4b97dca491"}, + {file = "charset_normalizer-3.5.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b91363207bd9dc966a691e959bb47f64b30f7ac4b072be9968b366982f7db77c"}, + {file = "charset_normalizer-3.5.2-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:38a873987f3be698494da8b2e3085e29da02da7b633dce73e79c699a113d7bf0"}, + {file = "charset_normalizer-3.5.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:355ad8011081dec5412240c087a9a0c9d4d5039f3ed11a3f13e18c2b29b56c51"}, + {file = "charset_normalizer-3.5.2-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ee21e28f0430bd6dc9086c6e525d5e818a44a5ad19720c8a0ef766792f3eb5e5"}, + {file = "charset_normalizer-3.5.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3d31298449090ab8d47b7b1b2a555ff73cac7ed438a08b7ac160980c7ebed649"}, + {file = "charset_normalizer-3.5.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5cde776b7cc66e4f6c99612cea4aa7269aa65863f7a15841b2c264f103822f4e"}, + {file = "charset_normalizer-3.5.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ae4f5fea5b8b8ccff88238cc8569303e5ee95efae67fa62922a311397a71f346"}, + {file = "charset_normalizer-3.5.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:f7d486c83842422badd511868fd8a9a20e9407ace71564b6af47ce7e60a336c1"}, + {file = "charset_normalizer-3.5.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:11a4d68a6ecda3292cb1e50239e111543ba5d709bb62a6b4ea1afcfa729d8875"}, + {file = "charset_normalizer-3.5.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d6734d2ef8a50fbf8445c139477da401f50d62a0606bf00e20ec6d87773fefb1"}, + {file = "charset_normalizer-3.5.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a815775b6c38d4e0ff7bcffbeba67feded90202bb6a226b8dd35f1c855217413"}, + {file = "charset_normalizer-3.5.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:23851fb4e1b85ed3f6c2a27b777cdfe2e19fb5b38429a8faf38c7542b7665869"}, + {file = "charset_normalizer-3.5.2-cp312-cp312-win32.whl", hash = "sha256:db19d07e2e0129e974a0e65d0064fc222a446cd5122c2fd4184d2af9fc734a9e"}, + {file = "charset_normalizer-3.5.2-cp312-cp312-win_amd64.whl", hash = "sha256:780fbe7cab297b81dad9fb8dc5eb003c0468ffb0d9e5f65068c53a34661a96bc"}, + {file = "charset_normalizer-3.5.2-cp312-cp312-win_arm64.whl", hash = "sha256:e2af3aad578aa6bd1384bcf4750fc285e5a9de53f40b7d41e5a0bf748edeb2b3"}, + {file = "charset_normalizer-3.5.2-cp313-cp313-android_24_arm64_v8a.whl", hash = "sha256:ed905975ab14056a2e5eb1c376cb2e1ebc5396baf84163939c518556fccde9f5"}, + {file = "charset_normalizer-3.5.2-cp313-cp313-android_24_x86_64.whl", hash = "sha256:a66c3bc5ab1f0ff2164fc9965ddd611ff0802173f4b9d24554c563f6ab7e1d6e"}, + {file = "charset_normalizer-3.5.2-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:d2374b62878abb00cd8309b32af6c0b715cd02dec0ca74ef12e5069bdc64144a"}, + {file = "charset_normalizer-3.5.2-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:d376bbd28b3a8999db1a103b3b388aee6f1ddeb3e51bc2172993efdcd86e064d"}, + {file = "charset_normalizer-3.5.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6045373d5a89a5ec71afde535db987ca28e76dfa276c2d4c818265b375d4b055"}, + {file = "charset_normalizer-3.5.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:849df64e889b2e17230d58410a03dba311a65b163508fd33679b2b737d4b7858"}, + {file = "charset_normalizer-3.5.2-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:15c44f7edfd477b06f517a5cc317fc1707edb9de2c865f43d4b6513907473234"}, + {file = "charset_normalizer-3.5.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a89012d6d5476ee112d20d998570ed58df2260a852afb1758809cd6900411d21"}, + {file = "charset_normalizer-3.5.2-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0c951d5e6dd9c2ff60609476752bee49da4206adde960ebc247766937f72e718"}, + {file = "charset_normalizer-3.5.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7218e8f32b0956cfcd048fd42d9d5779809745ca1d86113ca56f66e7ae1549c4"}, + {file = "charset_normalizer-3.5.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a19a731138fc27d5682277d3b9df22855cea1239bce7fcec5f78f42ef2d1f3c3"}, + {file = "charset_normalizer-3.5.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:62603db9a7caa0802eaa28c1c46fecd7b3a263a774069c24c3c28c302448721c"}, + {file = "charset_normalizer-3.5.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:b6856554c4f44d79fc2307d5768854310a8f0096e501c75637542c82292b0429"}, + {file = "charset_normalizer-3.5.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:1bc0baf5ef96b6ede57d47f4b8fe4d9d84019c3bfcbeb20a41edc6a6ee341f1f"}, + {file = "charset_normalizer-3.5.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:56bc200a365efb37383b7852e4cc5898d3b2da5987289b543956cf8cad71018a"}, + {file = "charset_normalizer-3.5.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:2c9ad19a6cfcd5ea5c0d41161d22f9df1dcc277e9bef2751391334546a314c00"}, + {file = "charset_normalizer-3.5.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e243bd13217235fc7290c621941c3f5cc8b66e4872495be821d7436ba2fb838d"}, + {file = "charset_normalizer-3.5.2-cp313-cp313-pyemscripten_2025_0_wasm32.whl", hash = "sha256:a090bb2c68df85450502e3e20d665e3a5af9c65a84d6508ed477badd49166fd3"}, + {file = "charset_normalizer-3.5.2-cp313-cp313-win32.whl", hash = "sha256:2b7b3bbfb4fe8ef40600792d762fbaa9057559f9d3fad209525b7a22b99e91fd"}, + {file = "charset_normalizer-3.5.2-cp313-cp313-win_amd64.whl", hash = "sha256:78456a747de8dc58360ffa581f30a002baf5aa28cb262536545e91f113ed7639"}, + {file = "charset_normalizer-3.5.2-cp313-cp313-win_arm64.whl", hash = "sha256:11912e4bb14baae7c5d8791aa55ba0a3a03ec6729073307b0f57270abaa713d3"}, + {file = "charset_normalizer-3.5.2-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:1afb975bd5d68d5ce9f6b6d44fdf2f7e34b895a35e95708a7a91b20a3b51d187"}, + {file = "charset_normalizer-3.5.2-cp314-cp314-android_24_x86_64.whl", hash = "sha256:bbbfc8e28816f19d7c0f1816664980c0a9875d01b27cdf8eedddb639d9e108ad"}, + {file = "charset_normalizer-3.5.2-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:7967d08cf06dee78443b874f98c98036f624f3a4e73e11f9f64f5be4d25393cf"}, + {file = "charset_normalizer-3.5.2-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:4c2b5031f63e331e3839b40aed2dd6f191e9c07edbde303e7876846ea1946995"}, + {file = "charset_normalizer-3.5.2-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:fcff63213e8e6e47770541a4607175404f47cbb3ebea7b6058cc82d524a0e424"}, + {file = "charset_normalizer-3.5.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8d86d6fc60743dc916eb79e2eb1ec4818e21e427731543af40a3021851174a13"}, + {file = "charset_normalizer-3.5.2-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7a881931aa470808df94a8c380eed2bbbc76cd9dc622310f99665658c821eb6d"}, + {file = "charset_normalizer-3.5.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8024d00c3faf3fc0c16e07a69f4405e8eac7cc0ab15f65fe6cf43827c4cf72b4"}, + {file = "charset_normalizer-3.5.2-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4d48f2d08b9de5864e2c8744d4461b862fb149a18274abc8b698c45975573438"}, + {file = "charset_normalizer-3.5.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34276fd796040bf0993ab33a369aa572e6979c7aab225a88893667ad8eac8f7a"}, + {file = "charset_normalizer-3.5.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0521c5665880b33d603717defa76c094048900010897909952397feb3039da56"}, + {file = "charset_normalizer-3.5.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:eff0ac9dbe711a4aee69bf04a83896aa9b85f19641264053a9f6d48573abb7dd"}, + {file = "charset_normalizer-3.5.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:1503bccbeb36d5527790c3930327704c39af22de3112f1b1666a9f3ce15ee204"}, + {file = "charset_normalizer-3.5.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:52aa6992700996af31f375de0c6bacd402b0097fe40b53c426b9f51a90ebabc7"}, + {file = "charset_normalizer-3.5.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e09a3942ecbdee5cce73ea9d42da82b81b72ac1bf031ce069b93b5adf4eac8cd"}, + {file = "charset_normalizer-3.5.2-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:c7c9ab723cde841fefb34efbad91e87f00a674b1fe1cd0784fde742bf2c154dc"}, + {file = "charset_normalizer-3.5.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ddc7dacc8ece3a182e7f15cb862d1fd616b46d076cb1ae9dd232b2c38b655874"}, + {file = "charset_normalizer-3.5.2-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:ee43c17b173d46a3212baa6ead3ae258eeabdae48c263a01ccf0218c366dd655"}, + {file = "charset_normalizer-3.5.2-cp314-cp314-win32.whl", hash = "sha256:4f87960d57feabfb618e4e0af6e7371645fa26a277860739d6e5d6e0012c92f0"}, + {file = "charset_normalizer-3.5.2-cp314-cp314-win_amd64.whl", hash = "sha256:e4e81e09c1578b8df602e3db08b0b3ea0a6947ad612f52bf8dc5ea8d47691f0c"}, + {file = "charset_normalizer-3.5.2-cp314-cp314-win_arm64.whl", hash = "sha256:80d02b6f04e92601a081dd97b23d3128033098bff5d35d392ddcc0476ea11253"}, + {file = "charset_normalizer-3.5.2-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:dca9ab98072a5a54ebacebdc45f53e645336b320c667410b061be1ca588ae709"}, + {file = "charset_normalizer-3.5.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f0aa869112ef88429ae17820d99c3dd9504c9e9c671d3c246f3d7442cb051084"}, + {file = "charset_normalizer-3.5.2-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c0afc6800ba57ccc350374c5bd6150419915d95ce93cdbab2d783d75eaf30ecb"}, + {file = "charset_normalizer-3.5.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7dcd882da75ef9adf94903b1e3b9419e8aa8fb4c7396822b834b9ef7fb96954f"}, + {file = "charset_normalizer-3.5.2-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2e06a3a98f916dd41d27f3105e02e7a40181c98c94b9158733d03a6f80506c09"}, + {file = "charset_normalizer-3.5.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6bd128f206a7752ae1f2ab6c61bf8a24ba28913a10df8b14c2637b973ff97a80"}, + {file = "charset_normalizer-3.5.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c8f3d67aeaf55f017982b73683f0e7342ba2f6635a78f69ce89ebb26aa411e5c"}, + {file = "charset_normalizer-3.5.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:fe9753dfee015c570d73df76f899f18444d41388bffcde097deba51c4fadbb9f"}, + {file = "charset_normalizer-3.5.2-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:92888bb3187c5ba50500b00b3b310c9f2c651709d28036077680cb5255450a03"}, + {file = "charset_normalizer-3.5.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:d008d90a7f2471519aef0c90dfbe73b3e6e4d5e66ac48e19154c17e89e98b604"}, + {file = "charset_normalizer-3.5.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:31f3930700408d211f13378ccbe1c40845d8da54bd0681fac3a9b5aae81c7aa8"}, + {file = "charset_normalizer-3.5.2-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:2a925889534b3748302dae5dead07cc13480de1dac3aea80a941b729b471ef93"}, + {file = "charset_normalizer-3.5.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f5ec61164adcec446f8969a3358ec3f9b26bbda3b9213e5586d219afa8df2915"}, + {file = "charset_normalizer-3.5.2-cp314-cp314t-win32.whl", hash = "sha256:598a11a2c7ebaa5334bf698bf29568c9c390abac6a154d8170fedecd1cea38c5"}, + {file = "charset_normalizer-3.5.2-cp314-cp314t-win_amd64.whl", hash = "sha256:7fdde2c9fd9e3eca40631e024664cf2584272cc8f96308cbe5fdfc930f51d8bc"}, + {file = "charset_normalizer-3.5.2-cp314-cp314t-win_arm64.whl", hash = "sha256:d1befeed746d247c81127bb14de9dc3d30edb6e5976d34f83f86ed262b1d9105"}, + {file = "charset_normalizer-3.5.2-cp315-cp315-macosx_10_15_universal2.whl", hash = "sha256:87475fabc8d9996fd9c27debb395e642e8c838d78a00b6e932227a0e06b81e26"}, + {file = "charset_normalizer-3.5.2-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9409a8bf35cf78353942504b24a57de3d75b708997a1e4bd8db71ac8633ce364"}, + {file = "charset_normalizer-3.5.2-cp315-cp315-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:498dc3188ca05a68231ac3fdbfc7f57eb67e1343c30e0fea17f8218c1599b253"}, + {file = "charset_normalizer-3.5.2-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e242bb1c5e76e97dfa9e7f209a71e93a01d7f19ffdd5cfbb2e2d55b4f08f8ab0"}, + {file = "charset_normalizer-3.5.2-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:def79fa35ef0cef8d2accec024f4fdc7ead3012ff02f5215c783f39f03ef8cfc"}, + {file = "charset_normalizer-3.5.2-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3df041de8887954562c9b261cba85ca0e9ded74048daf125f45edcfaa4832229"}, + {file = "charset_normalizer-3.5.2-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:04851f73ae72b8413dddadb16a49dfee95263553741fd42d546f7d66907e6be5"}, + {file = "charset_normalizer-3.5.2-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:183b88127acdb4fabe59d951ab424faf1af7b63cdbb5f776186c1ea2ffcaed98"}, + {file = "charset_normalizer-3.5.2-cp315-cp315-musllinux_1_2_armv7l.whl", hash = "sha256:16fa0eccf81304b79c5cd87f9271c3b85dd9dd99245e4422ae9c0dd45e0f99d3"}, + {file = "charset_normalizer-3.5.2-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:7441d755b7ab94f8d4eb3e43ec05482d760842fd263d003a99102d742cd835e2"}, + {file = "charset_normalizer-3.5.2-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:ca403d7e4798f525fdfc78e258820419cbbd0f0ecbab9de7840e3c017cf6b8cf"}, + {file = "charset_normalizer-3.5.2-cp315-cp315-musllinux_1_2_s390x.whl", hash = "sha256:df29a0a7107f7011e77f4eebdddec4c7331e24d787a0b21a46d63bdf7445da95"}, + {file = "charset_normalizer-3.5.2-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:f3c96f633825733f735c5a9cf21d21a257d8e1edf0b1cee0a064b9c424ca0f7d"}, + {file = "charset_normalizer-3.5.2-cp315-cp315-win32.whl", hash = "sha256:281cb91036248400f4cc957495cccd44c275c2e0c5854f7e45ac5cf7dc193847"}, + {file = "charset_normalizer-3.5.2-cp315-cp315-win_amd64.whl", hash = "sha256:89b53f3cda69831909888e0494f4fa0bcd3537e3e138dabeb620bd6ad946bae8"}, + {file = "charset_normalizer-3.5.2-cp315-cp315-win_arm64.whl", hash = "sha256:6be488a102b8cf28d0391d8c4ba7748938ae28b78ad901f8585520fca33ead1a"}, + {file = "charset_normalizer-3.5.2-cp315-cp315t-macosx_10_15_universal2.whl", hash = "sha256:915563965d418f986e7e145accc592eae9e1a1be3566ff98a05d7a9ec42a76e1"}, + {file = "charset_normalizer-3.5.2-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:65cd72beeeca9d3aaea1201e5923859f308f952f9c71de93f06063c79f0f7a3b"}, + {file = "charset_normalizer-3.5.2-cp315-cp315t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b7fd005a73d9e657273b7a10dc71a9e03c8fb9ee6999798d6918ce095b81ac7f"}, + {file = "charset_normalizer-3.5.2-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e54da4baf05720032d527874d40b65fa4d7e5c6c6a43d0c3adbeffcaf275a2b3"}, + {file = "charset_normalizer-3.5.2-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:124fbf1a8ff966d87ae05bb8bd45a71f966055ed8bba320d0c7cf450bc5f4d0e"}, + {file = "charset_normalizer-3.5.2-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:28b4f0d66fb834ff90f28209ac7bce77868c45d8c93e26f906709d9b7c2e1af9"}, + {file = "charset_normalizer-3.5.2-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:58ca3755ee7ff7f59b57789ec9833c9de9ea275405cdd240eda1f193112e398a"}, + {file = "charset_normalizer-3.5.2-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:443eae2bf318abeaf6f15d785138f71fd6de770e99a92158b8b814265e079115"}, + {file = "charset_normalizer-3.5.2-cp315-cp315t-musllinux_1_2_armv7l.whl", hash = "sha256:58f361dcbab699cf8f42db3f47c8e7fd1036f138c23a5d08de9fde5f425a730c"}, + {file = "charset_normalizer-3.5.2-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:1b4cbc7c3491ccb4aa17fcd8165649d01cf39f76de1696da8631b5f71b85401d"}, + {file = "charset_normalizer-3.5.2-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:ba0b1d2620edf869789c3879223f52bf2afc5d31b3cb47cc57b3a12c05e2aa9d"}, + {file = "charset_normalizer-3.5.2-cp315-cp315t-musllinux_1_2_s390x.whl", hash = "sha256:5e2b6b57e9733d39f0c9fd3185efa6b8e29652c4cd8fe94180272cf6ed9a78c4"}, + {file = "charset_normalizer-3.5.2-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:51cf45226a9b588d0d2b4880c62d686934b63ab0bd79ca23ab0e9762eb27441b"}, + {file = "charset_normalizer-3.5.2-cp315-cp315t-win32.whl", hash = "sha256:5fb29fb8cd1a46c27a1bf9613ad5ec2599310d46b4025d9556404a6b6a292800"}, + {file = "charset_normalizer-3.5.2-cp315-cp315t-win_amd64.whl", hash = "sha256:a192e2c40070d92c3ccf777e3a5c4ff515573cd2bb7ed0c537fdadbbec5bbf21"}, + {file = "charset_normalizer-3.5.2-cp315-cp315t-win_arm64.whl", hash = "sha256:749e97e1b32313717a565abbe321bc2190bc8b35f1a67e4cdbc7c56c8d8ffe58"}, + {file = "charset_normalizer-3.5.2-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:4275811936e2f06feff5e598fb42a1b7ae852da8e39605211892b56b81a34efd"}, + {file = "charset_normalizer-3.5.2-cp37-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1c50fe28bbc2ced33386f298650d91218076c05420e6cbd790b913adc41659e7"}, + {file = "charset_normalizer-3.5.2-cp37-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d19fbd981a488e22cd04883659ca6b08f50b5974f9fd7c95655ef6a043e5893f"}, + {file = "charset_normalizer-3.5.2-cp37-abi3-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0fed1d06615f022ee3b13caf5e8b180cfea32bb2c5aded8a9d44277afc040f93"}, + {file = "charset_normalizer-3.5.2-cp37-abi3-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:838dcc90063569a0448120554591a1d6c4a4ffe11babf048908793154ab86ade"}, + {file = "charset_normalizer-3.5.2-cp37-abi3-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2ce45c6627b22c47e390bc91a41c3d13032192e699fa0bea96e9671b373d69b0"}, + {file = "charset_normalizer-3.5.2-cp37-abi3-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0774bf9bf620249fee3e0b8b9fd3065de213be30f3aa94ce2494b3b638949e26"}, + {file = "charset_normalizer-3.5.2-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1db38f4c5496827c1a501846d64d14c3b80c7e6714e406cd7dc36a9899fa1011"}, + {file = "charset_normalizer-3.5.2-cp37-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:304d8e4d493af723536393eee0c689eb7813f4a474c8b479dee63f1fdd98f621"}, + {file = "charset_normalizer-3.5.2-cp37-abi3-musllinux_1_2_ppc64le.whl", hash = "sha256:9b7f416ff0978e2f2249330527f0ad6fa02f4932e6199692d3b52da2048c19e4"}, + {file = "charset_normalizer-3.5.2-cp37-abi3-musllinux_1_2_riscv64.whl", hash = "sha256:01077390b03f7988f11d700a2194e69b119741a86b1a638b1db88891e3eced8e"}, + {file = "charset_normalizer-3.5.2-cp37-abi3-musllinux_1_2_s390x.whl", hash = "sha256:7e841fb9010836c992c9f12fcbd43a831de93a5f726fc1ccd8ca1d0268c5014c"}, + {file = "charset_normalizer-3.5.2-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:9cae88599c7219005d879f98e5ed53341e9a122af585e1091200358a3003d2a0"}, + {file = "charset_normalizer-3.5.2-cp37-abi3-win32.whl", hash = "sha256:01b0c0d2262a9e28e8484a278c7e1b5d650e3ac8cf2683d2967e25899f208bdf"}, + {file = "charset_normalizer-3.5.2-cp37-abi3-win_amd64.whl", hash = "sha256:9f56f72050826f63dcee7a7f55b0a77168cb3bfc553fd405e7f8f9ece75a4036"}, + {file = "charset_normalizer-3.5.2-cp37-abi3-win_arm64.whl", hash = "sha256:40ab6bffa02ae10a0581e6c198be7d2d8ca5c2a0c64e4ed3465d766df457573e"}, + {file = "charset_normalizer-3.5.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:75a3ceed0724d625d64b86ca20aba182e4df462e04c2414fc941c0f523f06aac"}, + {file = "charset_normalizer-3.5.2-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0891b9d3903c5571c03771ca669a4b0ec5618ca722a5c957d3d29cd4e5062848"}, + {file = "charset_normalizer-3.5.2-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:fc14a032f813bf5fe624d991960ea83e9715adc27e4c1830a2361eb1d02ac341"}, + {file = "charset_normalizer-3.5.2-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8b2bfab86aa71ae13aa41a6a26aab338e0db2b8bc75434b05aea89e011ff35a4"}, + {file = "charset_normalizer-3.5.2-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9bde855991b7e362c146535e3136a50bfaffc0487d38b33ca7e5edefc6e23849"}, + {file = "charset_normalizer-3.5.2-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:55ea99acb17b9325618de155a0cd6a2e8f5d10be008113e1d433bbb58db543b2"}, + {file = "charset_normalizer-3.5.2-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:68eb192d85ab8e5f6ec69c2bc6ac0179fbf04a5ac1569d12fbef74883fe102d0"}, + {file = "charset_normalizer-3.5.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d913de495d90407cd859d263bee2e5d1a4ed3eb6573c04e70d9ec619a7cbed7f"}, + {file = "charset_normalizer-3.5.2-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:3ddacd27458c45bdacd6bd6db644bfb730efbf9e830310186e3045c9c5be8fb2"}, + {file = "charset_normalizer-3.5.2-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:588461c2e8384d309bd63e5826019b6977bc66d629b99ac8737bb795d7b2cb5a"}, + {file = "charset_normalizer-3.5.2-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:e80e6c2f55656b4824d72065abb4ddd6a525c74bd78a0aab5d9fc2cf4fb5af50"}, + {file = "charset_normalizer-3.5.2-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:d4a7319f304a774bed22115bc891618e45f85065ab44ea6acd07d274e750519a"}, + {file = "charset_normalizer-3.5.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:fd1fbe0f116b6e55da77aca2c6ddcddcfac2186cbf78bdebf40fc156efca389d"}, + {file = "charset_normalizer-3.5.2-cp39-cp39-win32.whl", hash = "sha256:93223adc95033dd47133a46ccfc316a0139176fd79085762e27202ec56018f03"}, + {file = "charset_normalizer-3.5.2-cp39-cp39-win_amd64.whl", hash = "sha256:15bb4005af6320d259dc7593ca84a38d7fe06a421dbcf7b910ae23979101e787"}, + {file = "charset_normalizer-3.5.2-cp39-cp39-win_arm64.whl", hash = "sha256:2cc961b171b3f3440f410489ab3573e86aea8736134ebbb40ea1338b7f0831bc"}, + {file = "charset_normalizer-3.5.2-py3-none-any.whl", hash = "sha256:b6b751274acb69d77b3323d6b7dbaa3c7fdfc1eb829b7eb61d262f32e1af9685"}, + {file = "charset_normalizer-3.5.2.tar.gz", hash = "sha256:39de2a259fc954455c57274dc94c79d5842774e1247a016aff30bc0efed0f4ef"}, ] [[package]] @@ -688,15 +730,15 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "httpx-aiohttp" -version = "0.1.8" +version = "0.1.12" description = "Aiohttp transport for HTTPX" optional = true python-versions = ">=3.8" groups = ["main"] markers = "extra == \"aiohttp\"" files = [ - {file = "httpx_aiohttp-0.1.8-py3-none-any.whl", hash = "sha256:b7bd958d1331f3759a38a0ba22ad29832cb63ca69498c17735228055bf78fa7e"}, - {file = "httpx_aiohttp-0.1.8.tar.gz", hash = "sha256:756c5e74cdb568c3248ba63fe82bfe8bbe64b928728720f7eaac64b3cf46f308"}, + {file = "httpx_aiohttp-0.1.12-py3-none-any.whl", hash = "sha256:5b0eac39a7f360fa7867a60bcb46bb1024eada9c01cbfecdb54dc1edb3fb7141"}, + {file = "httpx_aiohttp-0.1.12.tar.gz", hash = "sha256:81feec51fd82c0ecfa0e9aaf1b1a6c2591260d5e2bcbeb7eb0277a78e610df2c"}, ] [package.dependencies] @@ -705,14 +747,14 @@ httpx = ">=0.27.0" [[package]] name = "idna" -version = "3.19" +version = "3.20" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.9" groups = ["main", "dev"] files = [ - {file = "idna-3.19-py3-none-any.whl", hash = "sha256:815e7be7a7806d54abb586dc943addc79e8b2ee16915059658cbeff4b1b43bf4"}, - {file = "idna-3.19.tar.gz", hash = "sha256:5e0811a4383b21dc5838069f801c4fb62113b7447663d2530d2bd6e77b49bf15"}, + {file = "idna-3.20-py3-none-any.whl", hash = "sha256:ab7ae7122974553370f0bdb919e1a960b2cd1bc1ef0276416d896db81c14582c"}, + {file = "idna-3.20.tar.gz", hash = "sha256:a7db850025b95ded1eae8a46181a1a6c56c92c96f0e2b005d9ff8dc0210cab44"}, ] [package.extras] @@ -720,196 +762,196 @@ all = ["coverage (>=7.10.0)", "hypothesis (>=6.141.1)", "mypy (>=1.11.2)", "pyte [[package]] name = "iniconfig" -version = "2.3.0" +version = "2.3.1" description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12"}, - {file = "iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730"}, + {file = "iniconfig-2.3.1-py3-none-any.whl", hash = "sha256:9121e2c1fdb355232495be3194c8dfe87ccc2d5dee45947b78e68f499790d7a7"}, + {file = "iniconfig-2.3.1.tar.gz", hash = "sha256:67f4b9c50da0dedf52af349e7749a80a9057a5031199791b906c3bb3ae878960"}, ] [[package]] name = "multidict" -version = "6.8.0" +version = "6.9.1" description = "multidict implementation" optional = true python-versions = ">=3.10" groups = ["main"] markers = "extra == \"aiohttp\"" files = [ - {file = "multidict-6.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c0fe437a6d2f36aac2b49517057776575b5bf359df314cca20d230a6e139c089"}, - {file = "multidict-6.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f3a0a31189acf6703307397c6139ddabd734c20c5ef92649fc93e473df6615a3"}, - {file = "multidict-6.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c9c98d2f0126ba84cb45601eed97ff67ff767e19ae6eb3c31b02827b54d700e5"}, - {file = "multidict-6.8.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7bc7003991ebd368a20d05228137a37b3d3066751f3ea1e4f7b8efe8e752f2f5"}, - {file = "multidict-6.8.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c2c5fd0fd39574ccd58e1a52565b341aff522c5c836f1b3eb7605c371e61f52c"}, - {file = "multidict-6.8.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b1cd4d66ce894a45482e1ac2837c31d0bd447df35065e542b60055aa2d00404b"}, - {file = "multidict-6.8.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7a2573d0fd34f361a4a14e54d8cda3a91ac4e55fbf0d719698024f3b09c5b147"}, - {file = "multidict-6.8.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:edda19aff836ec515caafc09ea53d2ab144a041f09ee9a7cefcbd3ae4e976256"}, - {file = "multidict-6.8.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e418ec99574ca24365ca96546af285c2b021a1a072478a79f0e3cc3b08837154"}, - {file = "multidict-6.8.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:11d71490bf4bbff1141b14b93af419ad68c56b60bea9277fcb3f94dcca4796eb"}, - {file = "multidict-6.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e8e1e895e23818d343e4ae7dd95a0a556fdeaf8b471acf1c0a39b93c6f54d478"}, - {file = "multidict-6.8.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:15db8e6cab5f4cc9241bc56e69fdf3452cf49c10ee3c7977c742e68a275b3786"}, - {file = "multidict-6.8.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:8dc2d9c3a924ed14166e63650b2cf9f59e7821743bdd50b23802bd97ca09bde5"}, - {file = "multidict-6.8.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f2fa3d3b1c933d4bcb8fd2018700d5e7235c52f2ab8c88d22286965c5c0f00f8"}, - {file = "multidict-6.8.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d6dc7804c50fabd28644d4d18a4b20aad3681b3e64f3acd3182b330ca73f7a32"}, - {file = "multidict-6.8.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:0eca15d627e942ce186a935061f1568cc46c02e97c419c8da802df2be9f917d8"}, - {file = "multidict-6.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d7e5ba0a0153e35fbce9c51df530c8b4cb0c3012b46a04ff9a048441a269c2ed"}, - {file = "multidict-6.8.0-cp310-cp310-win32.whl", hash = "sha256:b7e62b8fc7bd6cad007b9f2e0ad9c8d4854c06350d5f51e1a439dd18b510ecac"}, - {file = "multidict-6.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:dc911ae6152e455b16a2a1a626aa6cd612fa01efb9d0a4ab3f5cf328b911483d"}, - {file = "multidict-6.8.0-cp310-cp310-win_arm64.whl", hash = "sha256:6300d5176647145ba1e22991c924fb29743e54b4d7b8bc85a0d3ec0e55e189cb"}, - {file = "multidict-6.8.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1b8986d4313dcee7c932837d16a535f1840b827bac1ea7c5c4c80751d0423794"}, - {file = "multidict-6.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2622fe114c0bd66ca5c461859357587f5a5e35ee5ff49fc5643d1bc78dbb41c6"}, - {file = "multidict-6.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:26a7aafc992e78872e2c8c1f7248c0e01139cf9020a7781b0c064fa566832712"}, - {file = "multidict-6.8.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:05c2e90c5289c5f7436ba2c25812a5fbdaa1c1bc11c8d8d3bbf64f5cd7c633dd"}, - {file = "multidict-6.8.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7941ef106ca1f2c62314a13c7ed913bcf49641f3efdc12864d588e17870920ac"}, - {file = "multidict-6.8.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a5a7ee1217949ddd43c6b7bcf70d5c22193bb50e8c695386de5905325e93ce9f"}, - {file = "multidict-6.8.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6b62b7e0025aa48dec11e125e655d1157985a5fdcec04b1ad500101ad072b891"}, - {file = "multidict-6.8.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ea880d441be7c510106bc56064be39266d948aef94ad4955e8784690019a5d9f"}, - {file = "multidict-6.8.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ab83fdd8cf307353edba9c427c17a3a021c2522d690f5633dd9f72d28b48ccca"}, - {file = "multidict-6.8.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3d1f48582686a0a3b81e9b43234766cc96697df72081af3f48107bd3f34d34e5"}, - {file = "multidict-6.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:563661919f603374c40cf45ffcd25535c12b8954203569a2ab1cee5265871cf4"}, - {file = "multidict-6.8.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:2ba9933e8f35fe4a70f540b837254c4055da82dc3a9e500a8f95e61498083a15"}, - {file = "multidict-6.8.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:68d40b2bace413f3231f5729d3fcfb1837fd31c4907e241b5d43211bfd76f3c2"}, - {file = "multidict-6.8.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a9e246f67ac038568b854ed7c5578e4c6af1f742359901a8fcc3603ff1358df6"}, - {file = "multidict-6.8.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:29b6e7bc4442a56cf8e0dc1cabf3fdc77cd533568d6829fc76a1effd2ce332ec"}, - {file = "multidict-6.8.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:82780eb8bf59e8fb25dd081fde6e058805045d6374a7f2f877effc826ca4434b"}, - {file = "multidict-6.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:2196ba6df392c3574acadd14ef87550f3611349c8618564de324b806a7a31cee"}, - {file = "multidict-6.8.0-cp311-cp311-win32.whl", hash = "sha256:b8b7aa75146266fd3e2a2437cf69ae188688c04ab8665b163d4257b46c1e0c83"}, - {file = "multidict-6.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:b03ca066b47b18b205cc080dca6f76cbd159f8cdd33a02a0700164c13b37e463"}, - {file = "multidict-6.8.0-cp311-cp311-win_arm64.whl", hash = "sha256:54af1266710cb0f305127ae0b970aff8d208057f8a29cd6e1db99b0114947035"}, - {file = "multidict-6.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:22a310ad37672a261e55a8b5e28d0ae08cfb68abb1f46418ccd19835c3b8e836"}, - {file = "multidict-6.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bb8c7da8c861391f7ae48e3593762be2dabe405109e01aec520fbe1a6d15d14b"}, - {file = "multidict-6.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:10456943903744ae1249728161c96bd9d2f7eb5ee17fcc2ffda2dc32e1bb36c7"}, - {file = "multidict-6.8.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:658f5a1895b804423d97b22d06fc0d0b171c7c01dcc3aa9c8faf0c0e26a249a5"}, - {file = "multidict-6.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:90c10b22860dbd09982d0b8993b66231a861bea2993d4a817ff35273f6ea285a"}, - {file = "multidict-6.8.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:088b04a66b3c1fce6fe4d771ec184a0426262d0b86709c908477b4ac7965df40"}, - {file = "multidict-6.8.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9caef53b20a105c0d66518a34be2f71b2783de8d091767575ef86f6ea422236d"}, - {file = "multidict-6.8.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a5e1583c14775580da05641240ce0d93f36ce3ddef3d5083a827468b0bcfe874"}, - {file = "multidict-6.8.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:003a3bddb32915c3f67096ea41d24e53edf710edb65a1f5d0c70ab40b0e4d20b"}, - {file = "multidict-6.8.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:35977263d9bf506dbc65349f63b3b8c91606d4abc110990945e3b94bc671319c"}, - {file = "multidict-6.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e9dc7b4ff6ef184504b49ef9a4113d49a646653b2ce89f5f48c1f57cdf6ba081"}, - {file = "multidict-6.8.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:887f9a975996032c686719eb7b3e1e7942fab5079c2b778bbd9afe9a9d78244f"}, - {file = "multidict-6.8.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:f3071e6515cc63714d014da8f738ae9fa3997c476203f3cd46de380c2376ed7b"}, - {file = "multidict-6.8.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c5f3a2af441670d80ce5fdf13b6c1b421fc1fc7fc5182d58ac7486738bb2b742"}, - {file = "multidict-6.8.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:31e8901637e20ccb3cf8f8848b5d0f7a00462bf5b34f7cf3dcbb2753b18e8b39"}, - {file = "multidict-6.8.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:13967dca8b2f33230a1427b52438326bb1c9101a1df22a3309ed3fcbbb3c96f0"}, - {file = "multidict-6.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad474c11d851b6fc97cb625e4822bc0cbd567fc07dc2602e28faec5a36b42bbb"}, - {file = "multidict-6.8.0-cp312-cp312-win32.whl", hash = "sha256:7bb0dad75068fee80fcb60f88569722c199d8656a16706702dc6e3b786819c90"}, - {file = "multidict-6.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:7d26dc8f070c0ec5579e987fa615ffd6883086106eefdff9e10d160fc5630630"}, - {file = "multidict-6.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:e6ec7d37841609a691b96a10b4fde386c7cd93ebbb939f59c9f23325ee788395"}, - {file = "multidict-6.8.0-cp313-cp313-android_24_x86_64.whl", hash = "sha256:ec0a4d066356054d569a66e0a94691a2058b680be5e710298f61db11a3c4609f"}, - {file = "multidict-6.8.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:714597cb5d5e15a8a449d2ae23c45b486a9e8fa33c462c7a33d7f35b65d92943"}, - {file = "multidict-6.8.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:e0db3a4d1e264e225037a6023888972c25206a96e016021a5bea41c9a939f2a9"}, - {file = "multidict-6.8.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:27747162712e85c84598d364425dbf1714ff335bdb6ba3171c4e5081196e8916"}, - {file = "multidict-6.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:847d6082ae694dc95e548acb201bc100e1cfa96513bc71fdcb86f709dad6c435"}, - {file = "multidict-6.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:88a6df88567680504ae28bfa7a1f2f64243d91e79a40b2c92ef42efc531e23da"}, - {file = "multidict-6.8.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:560b211fc3bd4a1e1c6de44f6d38113bf5b410dfc89a4c0d2a3c0edbf1a0dfb8"}, - {file = "multidict-6.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:202436df907c15adbb94360296c425ea53cf8968a5d2cff9b5b9790ae1972b33"}, - {file = "multidict-6.8.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c46a08bf070d6849fed483e9d9833f9d06aecb8382ed985be0b38508b3ae958e"}, - {file = "multidict-6.8.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2cd560498ae8e1bcc955643c1d78eb8e338226d07a983c656ea8c4443d3eec0f"}, - {file = "multidict-6.8.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:758233648ac47b07c575224c4eadd73c8929c3b4c31e2afcfea935fde1cda735"}, - {file = "multidict-6.8.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:122adc7c46ac1e31ecfc7f81b2530533dccafdba70f5d741649f87e336c63384"}, - {file = "multidict-6.8.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8125e60f3c70e323ac07dd8b3635f7b3bbc5c3a9ac04ae5988f668ff7ae28a18"}, - {file = "multidict-6.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:83ff054b04915be5c15680da6c6012474a2cc2bf534129a0e8c6a99f17ba7238"}, - {file = "multidict-6.8.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:930c6058047410e3edff445f5a6e4457f2e089042dede00e2d18ce06f3ceae2e"}, - {file = "multidict-6.8.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:13e26f59f0eecfc5f67c663ad550ffdaf62c0f657547cde387f6c86af1c9449e"}, - {file = "multidict-6.8.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:fd789a294d8e098528be29b2669b83005ce569339f8cef167fc0274c3115c34c"}, - {file = "multidict-6.8.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:3126f2a96704505aa4e92a72d6e8a5d7f29d40a987ced8bf69e29d71dfc71fbc"}, - {file = "multidict-6.8.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:23c9ee89967b6a9b4048acb3b93b660ed714ce9c8bf3bbe652959bc120dc02dc"}, - {file = "multidict-6.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7a62e302fc8cd6aa8972207e7e951d1fdee7c1dda18568305041d19f0e2c00f5"}, - {file = "multidict-6.8.0-cp313-cp313-win32.whl", hash = "sha256:093167d22a8c95af30f597b8a5686f20a14512989942d4be804d119899caca20"}, - {file = "multidict-6.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:f25b61a708bd276e8cbb6afcbbf1b8e793a3be70ba0a842d0b8692020f83b706"}, - {file = "multidict-6.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:bb36381e1f9f9d06eba2f10bdd438e5d20c07d5b55e1a3eee30b9f44cbf52316"}, - {file = "multidict-6.8.0-cp314-cp314-android_24_x86_64.whl", hash = "sha256:f8b09b25e0f4dc2ea9e2adbb1cc3ba11a94d6fa3dd978ae659c8743052e1afbc"}, - {file = "multidict-6.8.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:1f57c414be82490bc0e0305fdb834186229b2d9b6a35fa0afd1eb1a772d125ab"}, - {file = "multidict-6.8.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:00be37bde741bf60871082cd347a093218c44886e99231b7516671c70f2c280d"}, - {file = "multidict-6.8.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:e37b744849fb631bb52e3dadde35ffeee365a6c41cf71257b5b7acc9cd83fd38"}, - {file = "multidict-6.8.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:c2b2a96cf1dd99fe7867be4c013314225f4d5786e6685906e29932d42aca6f11"}, - {file = "multidict-6.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:bea7df027015856ba5d0a88e3b4777ff8cb5c66b58fc108050fe79d4dd9d4d2d"}, - {file = "multidict-6.8.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d3da668e903c934ed0b587ecacfed6901f6ae6384a6e975887592b61845e78bc"}, - {file = "multidict-6.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:64eaeda36ee8d88f9e8616a587a8c66a663283cf6e0dcf013c1ddd8c758e4aef"}, - {file = "multidict-6.8.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ac746cb365bac1c462da9e3e6ab8904a8efe2217a56b0b2e3d9480f41d2b2602"}, - {file = "multidict-6.8.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:18f0e06360c3e451a3ab800355773c8d125a758238d780c800b0ee5e90ee903c"}, - {file = "multidict-6.8.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:69708fecaa88bcb2341397b49fc95057a835b02a3670c551b37f95dd79e64e3a"}, - {file = "multidict-6.8.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9606f583e7acaf61e7b3f56074e14037b9af7cb194590edfc0114b3ae5931ff7"}, - {file = "multidict-6.8.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1f66fe6a021173d0d47968491791966b9f3e6d61115f2491744aa0c07a6e67af"}, - {file = "multidict-6.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:be007d1aee2cbd530347dcafedb400891a3b5f1bd7135f95cf5d5b330b5219ee"}, - {file = "multidict-6.8.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:8457aff3c12a89a8e1c4674de5c777857fbc429f40fe117a3d29538547cbc364"}, - {file = "multidict-6.8.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:95c27b4f3f04320fc44e338573f40c5c956b504a7fcf081a157fd0b02579311c"}, - {file = "multidict-6.8.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:d244cf6b52b5ba1c34c3832f4652a668ebb36d95949b96eed9a1c54d916a90dd"}, - {file = "multidict-6.8.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:5bbbb696c8024475b1877d14ce20d5f1cc05b8f6d786cea0fe3aa7fedc02e891"}, - {file = "multidict-6.8.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:cbd86f9787c5e2f5fd27d8b21458222f107347c6731c4e93dde68f554b466a2d"}, - {file = "multidict-6.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2f8a4b0b4d639d525928c7f30de527bfdf9ead6e44a5e8cb9c50aced5e4590cb"}, - {file = "multidict-6.8.0-cp314-cp314-win32.whl", hash = "sha256:8890c89d662560e51c55ac1304d6f919b23942abe9ae1127cb1de9aa6132fa52"}, - {file = "multidict-6.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:45cc39ba50fb0754a4359b90f8229ae08598fe2266abe3521b4e5a9ba916534a"}, - {file = "multidict-6.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:d0264f8d5cb0a803f650a6a8572dfa0cd1e099a2234c588dc8fb220b415b865f"}, - {file = "multidict-6.8.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:1969971900b0871530f9b62280dcc2d75688e74d2a69262bc01faf2b96c78f04"}, - {file = "multidict-6.8.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:8180b635290a75af8478f1b3e9810135381ae24833293fe77b85c1c21ff842ab"}, - {file = "multidict-6.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:4261863fc8b5ab1b815ede94e592e94c6af5b04616014929057e61859e7382a9"}, - {file = "multidict-6.8.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0b143d53590e89f43153d81d505a8448d4d57354354385aef8a51d67ffefa27e"}, - {file = "multidict-6.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:da1c112c5784ccd9d32cd90be6739fee32644e874eff6ae8f0497cba3e352e58"}, - {file = "multidict-6.8.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f7eefd0233a7c33ca980a5cfef26f1e9b5e2137839e752a99963696729f12d91"}, - {file = "multidict-6.8.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:348bb85e2038b40c007383616d73f734869063772372519549ebd7da1723d1a4"}, - {file = "multidict-6.8.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:095f62ea4e7a3be2f6c567ab695ce10e950f2adb905c1bec82281593e0b2d2ad"}, - {file = "multidict-6.8.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:be569fff1d85cd29391c431c5641c8772acb75bbdc61e60a8e82fceb9023d385"}, - {file = "multidict-6.8.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3533a03e4e789baf6a286e7b0b1b6da3f3d7c3eab569686ee29ee1d8b52e2cb4"}, - {file = "multidict-6.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1bdb9b8fba5a9aef673ec90db3f55b1ce743f2fbdea4d37dc04d14ccdfc153ff"}, - {file = "multidict-6.8.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:f8d7b66c9e09c0bb0add2b5895e646b62a0849e71155066f215523de6b95cbe6"}, - {file = "multidict-6.8.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:563d6500ca80dac7bba6f48a78e0ffd87e21a7d4d24642c6503a2ddccd70c110"}, - {file = "multidict-6.8.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:346ac52e56bcda320c0dcdfdd081947ed7cada33afea4e2284bef7b0733bff9b"}, - {file = "multidict-6.8.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:4ee953a5ebaeed38dc21cc032ed17a9d9782802e00042200497ab4b01b0bf7c0"}, - {file = "multidict-6.8.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:2f79cc3e8039a8cf5c77e0811b0807953fd52d0863b9b76970b20d696dc64a78"}, - {file = "multidict-6.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:43a4b56555bbcf8af161e7c7682bd93eec10f068c95844511864c018c8e5e13b"}, - {file = "multidict-6.8.0-cp314-cp314t-win32.whl", hash = "sha256:48ea524a25a1cd5972cf293bc95713918cba0bcd6fa9b992d906c857c546abe2"}, - {file = "multidict-6.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d0be2b832435001bc623ca7f1499ca1a853d4f082fb61221a80ce71132f50b26"}, - {file = "multidict-6.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:62b8e291a4f7edbf7cde7a43d831d893ba443a1b627498b53581943b0e348feb"}, - {file = "multidict-6.8.0-cp315-cp315-android_24_x86_64.whl", hash = "sha256:e192018b732f7b168e6604cbdf40fa8e05c996693b9eb445a0d8a73f4b77c5d3"}, - {file = "multidict-6.8.0-cp315-cp315-ios_13_0_arm64_iphoneos.whl", hash = "sha256:b25426f9f6ed402835617c8f23609a47045f91ecff365eb6734817e039a8ed25"}, - {file = "multidict-6.8.0-cp315-cp315-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:fa6c2880709c84457de104385b704fc28860f27e442ad13966fc4af8e714fe9c"}, - {file = "multidict-6.8.0-cp315-cp315-macosx_10_15_universal2.whl", hash = "sha256:eabb03dc3e4ed6333ecd1cc9826ec80e7a98b5506deeb832d7260c8e44166d23"}, - {file = "multidict-6.8.0-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:59e539c4eb4d3a53b0e630a6ba2b2f2824732b5e73f90e30a280f12fde157b15"}, - {file = "multidict-6.8.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:835d5a90b11d1f5f8200ff3cc8316bded76eebebc92436398947a27657e645e7"}, - {file = "multidict-6.8.0-cp315-cp315-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d2d236b8a44ae91536a12ebcb996bdb31cf27425f36b4d05c87f2ba2716050ba"}, - {file = "multidict-6.8.0-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bb9a60b7faa5d37c426fa91cf4d6738182a1f2755b9fab7c9c64cd466c4ce51e"}, - {file = "multidict-6.8.0-cp315-cp315-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0ef606c15cac6c90279acf34120784b6f36662cbf382defd3955cd8f1115336b"}, - {file = "multidict-6.8.0-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:68186a2d4051c8ffd17be33553bea2ec9bbc8ef860fe2980a221d96126296f31"}, - {file = "multidict-6.8.0-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2cc66abb85e2108c9ff8a1c0d20fa260bf690bbb33caef4ff3ecb2c2cbdfff5d"}, - {file = "multidict-6.8.0-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:69b3e519a132bb943b0daae15fc8c2168706b17f826481d32a32a5e784b129e3"}, - {file = "multidict-6.8.0-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e41226ecf607f062fe34a2f4cf64ad3a89e3a0180dc800b463b6b14c06dd10dc"}, - {file = "multidict-6.8.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:610c7637bc36b90f39e6c66f710f93d57018f83d53e1e187caaa218c6892b95f"}, - {file = "multidict-6.8.0-cp315-cp315-musllinux_1_2_armv7l.whl", hash = "sha256:65c85c79f5a2c04fbbc18f006c014674dc5fdf270cb978d8862c82c6f694e60c"}, - {file = "multidict-6.8.0-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:628ff11e6720f90acd0c305dfa3339f04a783a20de8cda6ac333ba46447261e8"}, - {file = "multidict-6.8.0-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:0935971bffd0b479fc90c4811ca787703e93fcb6afea939a375dfc80285ab368"}, - {file = "multidict-6.8.0-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:9442b14eec262a1f74369bbd07e75bc5155105164649a4b9fbc1ebc7b8fb0b14"}, - {file = "multidict-6.8.0-cp315-cp315-musllinux_1_2_s390x.whl", hash = "sha256:397599503b718f0137f26d3f6532d6955069cd2e5917c47ef581495bc2529ff8"}, - {file = "multidict-6.8.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:9e37024b41d7a7e7e9cce14b248d54707c21c2a2ea30a47b71bdcefcafec00f2"}, - {file = "multidict-6.8.0-cp315-cp315-win32.whl", hash = "sha256:071da134651b04a8507dfb331ac0988f376337c2aea59486bf20989fb5b5a64e"}, - {file = "multidict-6.8.0-cp315-cp315-win_amd64.whl", hash = "sha256:3bafff8598f0528017ddc74194e5451d5c22d046c98935f8f86247b0f286e4f8"}, - {file = "multidict-6.8.0-cp315-cp315-win_arm64.whl", hash = "sha256:e886ef8c9879105fe4fc99417447b3a5f35d1131412ce839470bd2089fe2043f"}, - {file = "multidict-6.8.0-cp315-cp315t-macosx_10_15_universal2.whl", hash = "sha256:883284137e25318ed9735b742ae46341a864888fae28e8b6314c4f84da080f08"}, - {file = "multidict-6.8.0-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:ca52b9ec80851366197577154c862c4c4c7036ca76ae94cef5cb59c5cfeab944"}, - {file = "multidict-6.8.0-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:91fa75d0a693832106d98f66c849f034f21c828d14437f1fb97d3784aab89e84"}, - {file = "multidict-6.8.0-cp315-cp315t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:307c1acd812fe897e7fbe10c6758822e8c04be4e7c60a9f54901cdf8b5ab8bc3"}, - {file = "multidict-6.8.0-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5caf684986a2490628f059a99dd107b566a2d34cf947f8eb8387e0500a1f90c5"}, - {file = "multidict-6.8.0-cp315-cp315t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:77745725125d01fd613b6db043362aa7c6bfbfdb23d45dbfc3d92bf58160af62"}, - {file = "multidict-6.8.0-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8daafaa0b2eb43f76898ced78b1e0fb91b38c4fa50da516c18067f2a2d578c20"}, - {file = "multidict-6.8.0-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c68e0c0649d17c2d0339e3674e86a4aeba4a7e6b21c1e394cf947a95433b31d0"}, - {file = "multidict-6.8.0-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d8a5ac357ac283490a8d1899b0383355fd1f8634b14ba0d59e4c0dd97db85556"}, - {file = "multidict-6.8.0-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:46029e6e27a3ec0dc55b53f58df82d10f04c5e111f78248279b530bedad2c30a"}, - {file = "multidict-6.8.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:8d1046b5427dcafe6e8a0e07527dd74f1ee694006160162f53f3a17f15aad3b4"}, - {file = "multidict-6.8.0-cp315-cp315t-musllinux_1_2_armv7l.whl", hash = "sha256:f1f4a220db6ed7c8fd16b6d644ffd1f082651693204daf3275e049fadc849e39"}, - {file = "multidict-6.8.0-cp315-cp315t-musllinux_1_2_i686.whl", hash = "sha256:029897732a9c798737457e382bf84e8c64237eff224a90aea2639f4413c45e4e"}, - {file = "multidict-6.8.0-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:29be9fd289e9ab8f480996ea2f686e1654b80242033843cb11691688329423f1"}, - {file = "multidict-6.8.0-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:29631224698de1e42abc8fa7658d830e0aed0029785144b5832b695da5adef2f"}, - {file = "multidict-6.8.0-cp315-cp315t-musllinux_1_2_s390x.whl", hash = "sha256:962f18c59a000f30b084ea2e6b8001521bb315efd4e5f10acf9fb36f366b7882"}, - {file = "multidict-6.8.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:c60e50bc5b07faac92fd3a20fa21cc8cf3e3f7204d2867b206c73293ebc19101"}, - {file = "multidict-6.8.0-cp315-cp315t-win32.whl", hash = "sha256:fc5460940f50dff00731b4132366840ba9685286ea88ea104b661899084f3fea"}, - {file = "multidict-6.8.0-cp315-cp315t-win_amd64.whl", hash = "sha256:b367c342327717d644db4c0ddb37ceb655c84822215ea0773a3a36911b74b71d"}, - {file = "multidict-6.8.0-cp315-cp315t-win_arm64.whl", hash = "sha256:0c1c4debad7337627b86837abdf0237ca3cb3d7e17de7eab0177c263878546d4"}, - {file = "multidict-6.8.0-py3-none-any.whl", hash = "sha256:75daa15ca16d6285eb2e104b2f05ee6f8d9836c68da3ce5c85f615a0450eed0e"}, - {file = "multidict-6.8.0.tar.gz", hash = "sha256:5cd4637ce76312ba1e05eb9c5193fec231f64fee0944e135fa1e951242355b37"}, + {file = "multidict-6.9.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:aef74e9beabbd6c4aafc091dabff86d046ccf013ce1e4396c0fbb01b4cad9de8"}, + {file = "multidict-6.9.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1ca5ebe6d454f1e5496cf052386a559f1080bf2de75bf327ebca0a6003b79f19"}, + {file = "multidict-6.9.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02f6d0c4b70f783305e73f9944d8efe6be1022550f0974ba0ae9d8893c0350fa"}, + {file = "multidict-6.9.1-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:8050e75af7e4c6e2d5260b84eeedb618f3e452e66473432e085b5d1b81429299"}, + {file = "multidict-6.9.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4736d350371337825cac1793c9f7c40701c32a03912548c2a7608e51679cbb96"}, + {file = "multidict-6.9.1-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:02fe09dc197b8ae7e355371e51e5dce2f39060cd5a8badf94904527e23a3f188"}, + {file = "multidict-6.9.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ad4528cdce058b684f75fad1faf4a6a6c992fe2f08376370ca66e4ce5916a84a"}, + {file = "multidict-6.9.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8885e3808aedbd6725b921fb67dacaae0678933561ddd47c2b01315198c70e2e"}, + {file = "multidict-6.9.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d3b6e6840421c83ccb60398e333b44f910b0907bb409597685ab2eedd1e22eab"}, + {file = "multidict-6.9.1-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c9648ed33dc8179e4ec04bbc73bd7f0038e1e81217a69467f61f02a78bf07e88"}, + {file = "multidict-6.9.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d9d6544790ba50438a9c1a903c3c4afb1ec8a7832db5518549275b40ef0dd4b1"}, + {file = "multidict-6.9.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:1c9f3c25df6c9d3bbae4f6fd3f514b1c5c740a2110f56ccf36069c85417e289c"}, + {file = "multidict-6.9.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:cf22b43f35b7dbb9f71e8ee2041b5c00029cdfc28ede3a2f31cf8906f9a6c126"}, + {file = "multidict-6.9.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8ed78ccad4c7b421804f5d524b7740946a7ef75d89dc0dac52e9d7c24c472410"}, + {file = "multidict-6.9.1-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:e3b1aa25f01238886a6baed9e13b9a9240ed344346c79ae280ae65e8603b21d5"}, + {file = "multidict-6.9.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:bc94a68ea5e18f8e85dc6b522bcb53093f692c8eb62b4837ac047da73956cbe4"}, + {file = "multidict-6.9.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:73533644e1f69ea1164d56cc6505f564c6c44072b646b66d67df2d31fed0348c"}, + {file = "multidict-6.9.1-cp310-cp310-win32.whl", hash = "sha256:66987aa68b0f7c2a1cc5f388ca962b8ed92b10f79de38d6d0d8c716154f519d9"}, + {file = "multidict-6.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:a32b78c1e52ebd8e247bb68300b90b233300d8816faa008ed0713bc539fb6af0"}, + {file = "multidict-6.9.1-cp310-cp310-win_arm64.whl", hash = "sha256:ab64ace1a68682d191d9bedd9d4c939406ad86b1d9f628180410644249ad46c2"}, + {file = "multidict-6.9.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:910d4260512660484c0dc1588a316fbb35a40c081c36fc51d1225351af17cfe4"}, + {file = "multidict-6.9.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:33fa55b990f81c2927e01399ace0d18926c69d69baa8cdaa819424132fb97987"}, + {file = "multidict-6.9.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:369b5aa01b241cd3fea6890bdbb11a1425d87bf1515831500d518f4223e9d72c"}, + {file = "multidict-6.9.1-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:803f8b575a71b1b299d677c28db0653459c79b5308874efec813f17b7457c7f1"}, + {file = "multidict-6.9.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b8a65621b98984a62e59403009591b8a5a7736273aefe1cab64cfb85b365cc07"}, + {file = "multidict-6.9.1-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:13849a1d4f54c3809ae721e9e83ab28f5ea602f33660cb84eb6ef261eac706c1"}, + {file = "multidict-6.9.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:dd9a137a4a9becda3094f3831cd026380f75f6855e051eefe4c73ade524f1cc3"}, + {file = "multidict-6.9.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9ae9614c317c50836689ce2dfde07c05fe0b16378562e2221746c3913ede3c80"}, + {file = "multidict-6.9.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8f1e362c9352b50ed120f001046fdbb80810c9d56580f4c3fc13bbe30823387"}, + {file = "multidict-6.9.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0dd655518f136febd96c05131a76a863e32fc2a1d7acd4e3c959e3ceb77d8345"}, + {file = "multidict-6.9.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2c5d675da8f1cb5650271c8ad5e95c0a3e5a183c105e72d953b12877b1c8d0fd"}, + {file = "multidict-6.9.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:658f90f49cf5af2441cad0a2b801c3ef520471989a1ec55bcb25b255b2ca8d2f"}, + {file = "multidict-6.9.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:43124fe172ada86d03ac3c8dc8179091341f6724d5e5d5b160e1587e4cd3761b"}, + {file = "multidict-6.9.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:b2f0adc22a4eb31e545221d93fc73a0f6a8cc2379d0f4309f71d1d17ba938b82"}, + {file = "multidict-6.9.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7fc59e9ba821b220944ccfe0f89c9dc4745f6d097569992356eb03869f21e953"}, + {file = "multidict-6.9.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:87cc632c88ee5dc80e12681047839304d98ee5c9a708d686505767001c9b8b9a"}, + {file = "multidict-6.9.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b828cf64d62dc09ac183f03c1aeedd164ade96a2ce4934109452edf29de1dd13"}, + {file = "multidict-6.9.1-cp311-cp311-win32.whl", hash = "sha256:2c1aeb92eea59d824f004341b26d5e4b47a8a441cf9726769b9a90abf9d0e08f"}, + {file = "multidict-6.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:5f89dad732280e7a10b74d40b91364f88e13c3f2c08c2ef83a8cd42f7a61af2e"}, + {file = "multidict-6.9.1-cp311-cp311-win_arm64.whl", hash = "sha256:5800368526647146978389dfaa46da3356291e9f0fff9a4ef12e8c2bef964a0d"}, + {file = "multidict-6.9.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:29138fef49828542e859828107e42e50d0e587c513b7eb4b2d92bade2b0860fe"}, + {file = "multidict-6.9.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:19e31815d41cefc365489e591d105d2baceb2f65aa75d29471fbdbda8651e006"}, + {file = "multidict-6.9.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6ed30be8918e18c8bed0a2e8b70639ecf02feb61ed00ca2e41cfcb2a50fa3f42"}, + {file = "multidict-6.9.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:637f4ae36264bd7b8d9a60193acddc1d735ad52e8ed53a19931ea6d921fea8e5"}, + {file = "multidict-6.9.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:35fc236507fb1b3138f0af5ecd5f94ed752d4d6d826248eae425f86204013eea"}, + {file = "multidict-6.9.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e58952f04772f59f11c6e007471449809a30165188669bca8fdb19dde40a8f24"}, + {file = "multidict-6.9.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d35a4f1c63f07fbb8c8f9946dea98b21eddf6c57421585f71d91864be3ba2a24"}, + {file = "multidict-6.9.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b5f8771aaaed7f80e84a4e471d2f29ab6721e4595075e54d03ae1ed951b2000a"}, + {file = "multidict-6.9.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:976fd7689d69ec78d67d31d38d396d8adb562f7e8368279f76aed4aa451fa06d"}, + {file = "multidict-6.9.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:95052e8777a86bae87c0bd0b5ab22d809e3d1d02bf69e3e66ddda5ba75a05805"}, + {file = "multidict-6.9.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1a8adfcaf96f587ab138476eaddef95f29b8a2a8a9afbfea8d2fd62180995d02"}, + {file = "multidict-6.9.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:1a53de2772cfb74559df2eb4456ec4eeb908435ec55a84b69370d9d745d62aa8"}, + {file = "multidict-6.9.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f2ce963299d42fa3f22a90adc0fdf174792ffef5ff4c7ffb68260548fb05580"}, + {file = "multidict-6.9.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5b30ddf7234e611ca877575b62840e6af5977f92f1f9d532eedbb05a44ff8004"}, + {file = "multidict-6.9.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:63ada7ee2e9345695f9e9bc4c65d72222253f07b1ac94fd0e37555cc6f3c7f60"}, + {file = "multidict-6.9.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3c95601ed98fad3f6e2f8fe809c3b526b0fab31ef525e00a155e227f3d17f58a"}, + {file = "multidict-6.9.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c148e8b596000dd3e4bfe206e70f3e666be18d72032e0012555f2373c52e35d6"}, + {file = "multidict-6.9.1-cp312-cp312-win32.whl", hash = "sha256:f9dad513626a33670f17cddc6078e30e311f444c957e8dbfc5b2b4603c8b4edb"}, + {file = "multidict-6.9.1-cp312-cp312-win_amd64.whl", hash = "sha256:a16a1dc8529f9e734a41c3b856f3eae7ebacdc061dde3f8a844e0c7889c97203"}, + {file = "multidict-6.9.1-cp312-cp312-win_arm64.whl", hash = "sha256:361f7206cf341ba94fb015688f5c8b480f8e63bd58a4c14a48aeca7851a241cc"}, + {file = "multidict-6.9.1-cp313-cp313-android_24_x86_64.whl", hash = "sha256:d7bf9e43282d69561618e8a0ea33368d532ebef42f15c096f427090521dd74f3"}, + {file = "multidict-6.9.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:03d47df72f084f757c1cb771188d5f4e3a805e4abc4d67e32509272343ae9382"}, + {file = "multidict-6.9.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:6bc94fe17c3c56e5418f79515b786b101845f70609b0d19d0c1ba13448e5633a"}, + {file = "multidict-6.9.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8f2973bbd2bebd9d2e0cd6394c1292a1a19ccd56bdcbe1e174059f1a39be5b40"}, + {file = "multidict-6.9.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:de7738b8c0bb74c4cc16bbd7fb49fc2bcf6430dba11b3432cd52768ae40933e8"}, + {file = "multidict-6.9.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e5ccad4b7bac125722f48d6f862bed3b514d8526deea06316bb72f152cd30a7c"}, + {file = "multidict-6.9.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a49ff5cdb33654cb7d6a3c377aa2a83ddefaa1db31eb10bcf3c180aa84f9af8a"}, + {file = "multidict-6.9.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b22ff30006a2f28f8bff878fb93413cbe3a4d1fd517c28081d848c90e9cfd2c8"}, + {file = "multidict-6.9.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3adf06c66041aa21eeb8a71e82379b74773298c8e6d3d839b151aae441a99b94"}, + {file = "multidict-6.9.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:963a8d8f97057082679523d0fd4c53a38f86bc58cabe4556faef682ae53fa2fa"}, + {file = "multidict-6.9.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b66ccc5c2cdd26e74fa5d4c29ffae424cc6148bf93ce574821783fb3b6d452c5"}, + {file = "multidict-6.9.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7fd79c521f6290c69125fa2b85fa65d9e657e6a8ffaf722dc881b926bef4aa5c"}, + {file = "multidict-6.9.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1d804e4caf5d5da37d6dac1325da5629ebef1e27a294c2b568b295814aa36c7b"}, + {file = "multidict-6.9.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bb0d664505f4b112f384cffeee82e91e3f6448d8e574989479db4439b68cba05"}, + {file = "multidict-6.9.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:9e14d17773b1b3c758ff153659a1824608a0cb562c45f484b5ed8a433428444a"}, + {file = "multidict-6.9.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:083735b7f395894e43adb278d5dae901448883a835ff8f1977e285fefdb10418"}, + {file = "multidict-6.9.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b65121091567847a8cb520d364ab22ba90e00d3cc55fa9eb34bb439f0684bcd1"}, + {file = "multidict-6.9.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:ea999ae6e80e66ad5eea287860951b033d0104ca34d6d87c7b5125ebe0e12721"}, + {file = "multidict-6.9.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:095d900c242e00fbe5f321ee072e7278b4153e78c5ce9c1efde167d62c1e4771"}, + {file = "multidict-6.9.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6441cc837aea58be7d9baef1b2383eb8311ab9303f500f99ac90b584cd78bb14"}, + {file = "multidict-6.9.1-cp313-cp313-win32.whl", hash = "sha256:9c4880d017555d70dea367dd49271830842d48e3891c2da97da7ce8c4abcee40"}, + {file = "multidict-6.9.1-cp313-cp313-win_amd64.whl", hash = "sha256:ac51cd64bae51c462ea58ad2492c9b8209667a4ef60c45c4a304518b67598d5d"}, + {file = "multidict-6.9.1-cp313-cp313-win_arm64.whl", hash = "sha256:37a9ebe00c698279213d56e6c64e1962ab1e092918270649b397cac3dc196ca4"}, + {file = "multidict-6.9.1-cp314-cp314-android_24_x86_64.whl", hash = "sha256:fc0dcb22fa9aeabfe3fa4e0430099acff985ec5d77a851382f76cc6146e780e5"}, + {file = "multidict-6.9.1-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:024123f0ab402ab33828e24eb80fa8f25167d0d3783ba5f287e39ed741e6abf9"}, + {file = "multidict-6.9.1-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:af1b5a92315048c3e36bbebfa7d4760a9c3e910bc4166f11b20d77d20d6bcfca"}, + {file = "multidict-6.9.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:b2483da477932ad1983d1d33c18bc3771c6fb00cfbaaed70a875fd547ef8e840"}, + {file = "multidict-6.9.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:db4d697b18b6ef5528b1f36bfa25072cd2a421869f5963bc0e92c8a34b9e2800"}, + {file = "multidict-6.9.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:854fd2f1bc6e8a56b89910b5cd7261a8b40f13ebb31572da985ce59c7da0886d"}, + {file = "multidict-6.9.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:03731f6fc036180700c9dc2308205a48e5ca6f3ff03087739ab746f294022201"}, + {file = "multidict-6.9.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:989261c5f1735a165f2e4e87cf6d5f17ab734fa18f9ad0383d5adfcdaefce701"}, + {file = "multidict-6.9.1-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b4c9e5d05b126b267ac048a89a0e2d9b48b1b648add62d4872906304a8590610"}, + {file = "multidict-6.9.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9d0a21cf76153de8f2d96a877991d6bc59b9ab5180b949e50db73cc193b4a694"}, + {file = "multidict-6.9.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e7386aa18d98d6b8af44b92173654ec469237fda35f8e8523e43b581a86f476a"}, + {file = "multidict-6.9.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b69651732c64afb691e50cdc3387cae305e0eeff8804fe3e3ce203876494932a"}, + {file = "multidict-6.9.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1fee9a16d88a1c4865610de31ef5c666671020d7a81d1f510eaf3c97d00ebeba"}, + {file = "multidict-6.9.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7814bbee202acd3bd240204c17d8b87a4c48c81064fd8674dbe527d94d5a4290"}, + {file = "multidict-6.9.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:95f273bea318a194f656527ee2ed19494327bc500b8e87d9358e3222579aa28d"}, + {file = "multidict-6.9.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:73bcafa21a78d0776b3ee7cd2a63c66f968eb7db8e8d594e32d7329950f6e828"}, + {file = "multidict-6.9.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:1a938761c77e0e6edb0c93d02f4e988d44a69e5195e5a3b893e5553311347132"}, + {file = "multidict-6.9.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:37245ca4105386194dd1d292a6f2aae09bfe1bd7ac6f9ec25093e3cf8e9b143b"}, + {file = "multidict-6.9.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f0700527dd5bfa8b7204b08330542f4f388899d3c14d885d8a368992e0eb562d"}, + {file = "multidict-6.9.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6b7e54fd883671d1a8810704851c044b7173287c552b9f5c3d9e0eb9f00ae194"}, + {file = "multidict-6.9.1-cp314-cp314-win32.whl", hash = "sha256:e81ae656b9935ac4528a71f96bb7a14d949778ed1897c573d3e7ebb9187f8841"}, + {file = "multidict-6.9.1-cp314-cp314-win_amd64.whl", hash = "sha256:acddcac38adc8342ba48aba98896faa7928854bebb62542362138655b5367ee3"}, + {file = "multidict-6.9.1-cp314-cp314-win_arm64.whl", hash = "sha256:32217133dddc58c927805cf6c0731d8144584176b768042ee886d51e71860bc9"}, + {file = "multidict-6.9.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:0b2fb8c349d1103863750b5d8cb5ace766917f4b35f3d883c8f778853eaa9f76"}, + {file = "multidict-6.9.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:56d834b74c993a7d7cb2b8ab33a0d55c3e0d4a3d2f2da2808a4ad3d79189711b"}, + {file = "multidict-6.9.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:783ba7d845d79ce976afd9c1e91a4e5714671defa198ee789e8b23316083a485"}, + {file = "multidict-6.9.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:4d718fa1b5f0d0dd75e86fbbc5b0c93ea3a5d65216c85c61cd5d7cdddfe08455"}, + {file = "multidict-6.9.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a313bad717dde740959d50850c315b75fd4eb0c5e6b4dc8535db0f1c369be125"}, + {file = "multidict-6.9.1-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ef01d29fca550ab871fd99154f82c6472aacf8e7def272dfb07b460123850390"}, + {file = "multidict-6.9.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0f3bd290711c6e9486173a6ee7cd4e7f00c3971c7908c1ec1b6e5437c5e4c6f9"}, + {file = "multidict-6.9.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:81cdc537e0a42e3c0170752fcadbe450246d5c3b4b7231d6eb9672456605ac94"}, + {file = "multidict-6.9.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8b193bf7a443c97d81c47052f60c486071a4bdef4a573fa2514f920089414d45"}, + {file = "multidict-6.9.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7a0c98f6a636adf0d7edd60c61589eaf52d149239af754d0bfeb0effedba53a8"}, + {file = "multidict-6.9.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:6e6b7e3a1520c39a2772f414cd9ddf5995f77e4e823dfa1522af383addd465a3"}, + {file = "multidict-6.9.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:ca65cced0d67a9039e93bcd98a369920e499bf98296844ff56ead01c9085321f"}, + {file = "multidict-6.9.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:372c063f37480f62c1ae32dc3a1a0a5942b180c883f99789075a8a8ec3c4e709"}, + {file = "multidict-6.9.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:827c92145b3b976b39430129c89d213b250dacbe5fce678e9a03940e6e848983"}, + {file = "multidict-6.9.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:9b0124b9c17e9890f0819b2e7a5f65ec9a2f5aabd6c8e7ad090c1675cf70dee6"}, + {file = "multidict-6.9.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:23136f5a564654eb61061ec6d5620a4c1ea32c8f552b65e9982a12b72bff601b"}, + {file = "multidict-6.9.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f2524ec55b3e65cbe235a8b3c36e2af3b635be02ed05e20c94e70c5c943c009e"}, + {file = "multidict-6.9.1-cp314-cp314t-win32.whl", hash = "sha256:35ba0263bae5dd3ad5aad767cc9afc01a8598c7dae30f1b3b2de98b1b32c28bd"}, + {file = "multidict-6.9.1-cp314-cp314t-win_amd64.whl", hash = "sha256:7c8d5882ba25ac0282258be435d8a05aa0cbcacfce15799154a338f847f159b9"}, + {file = "multidict-6.9.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ac348379cf4de5538a0a213be1532d289aa801ec5d267c5909446b9ea2f8e2c3"}, + {file = "multidict-6.9.1-cp315-cp315-android_24_x86_64.whl", hash = "sha256:f04551dce5a7db8c9659f2e4245494c182d0663b83661803e08d46bfcae5eda1"}, + {file = "multidict-6.9.1-cp315-cp315-ios_13_0_arm64_iphoneos.whl", hash = "sha256:c0c88085affd35c33e124e36930c5ad96aff9294ce195eaa0fd9cec962b64a82"}, + {file = "multidict-6.9.1-cp315-cp315-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:a8b75dd3d3638d9a19f23e84af4ffab3b8940422c0df2da2a77005ef5aa3d7ea"}, + {file = "multidict-6.9.1-cp315-cp315-macosx_10_15_universal2.whl", hash = "sha256:006c4478de0a1876f4834e14255776286f09b9846b505fe63f67f9d173a9487c"}, + {file = "multidict-6.9.1-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:2784090c30a586d5b45197bd9c32f87fb927216cde302f6cfd76d76577e90f08"}, + {file = "multidict-6.9.1-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:7c708566da8014b120a64b1eb6d200c6c0c8cb36296383723cdb6fc82038270b"}, + {file = "multidict-6.9.1-cp315-cp315-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:042fb0196047e786936a730bd302de83143950da45f2c16078da8f35e1cf7919"}, + {file = "multidict-6.9.1-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a2212a0c842c723d919ea4a22a9296cb6b244b386e4e6ea92adfc7fbf3095519"}, + {file = "multidict-6.9.1-cp315-cp315-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:fd882aa29bf402b62bf1fd7c19fd5df4b6528cf468a908864b39368572b662a9"}, + {file = "multidict-6.9.1-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c57541034d12b215ab0a2bfa371d1a8a198da18176d0426c27105b9161a6862d"}, + {file = "multidict-6.9.1-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d73fed4e37158ff00cd271871170b79e40138db51e625fd352fa17c6acb34f67"}, + {file = "multidict-6.9.1-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:79da2491348b30810728050b4a8ec0416f85884125c2fd44655b3d01150d9c3e"}, + {file = "multidict-6.9.1-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b4da208a63434d21a3df64d29758e650fc4aa8cb05848554b76949c296539cca"}, + {file = "multidict-6.9.1-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:877ca17fdcfdf5c397493a71e5ff97a87bb181417fe717fdadc77c08c09301ac"}, + {file = "multidict-6.9.1-cp315-cp315-musllinux_1_2_armv7l.whl", hash = "sha256:40aec5299e1ed71fbb988389059da381c3c1a60e0c649acceb2a35d9b128848e"}, + {file = "multidict-6.9.1-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:4a409ccc42aefec904038695d5d7fd6d8f3af2721b7b6401d55135ec7d6d298e"}, + {file = "multidict-6.9.1-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:0a5769559e3312dd96731fbe15b4abb6033368ac1cad5a98dadd21946a4c7d6c"}, + {file = "multidict-6.9.1-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:03fac50ddfd8302175b77863a015eccfae76767cda5506eef86df559ba861e1f"}, + {file = "multidict-6.9.1-cp315-cp315-musllinux_1_2_s390x.whl", hash = "sha256:5ecace251ccfa705bf3d7d35c5032cf750f5c629809740405697bce5c118c4a4"}, + {file = "multidict-6.9.1-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:ecc89dbd4155b2f8a47f4bbd89242a35ed15e8e0ec581cab5ac65fa38407329d"}, + {file = "multidict-6.9.1-cp315-cp315-win32.whl", hash = "sha256:a3ffe881246d28a862f1985824f484cb7361f44d6b99c4c620436ef56462f38d"}, + {file = "multidict-6.9.1-cp315-cp315-win_amd64.whl", hash = "sha256:59c123d0e948d760a5f930f316cfefa07e8d632ab84327c0693ee6a88171154f"}, + {file = "multidict-6.9.1-cp315-cp315-win_arm64.whl", hash = "sha256:31199204b3ced121ff5407a2c342326a5d27e3870abbf94bd80dbd2451b7bc8f"}, + {file = "multidict-6.9.1-cp315-cp315t-macosx_10_15_universal2.whl", hash = "sha256:8879510a76940670517ea1cb589978da44b86e286ec3e50d664ef330817afce7"}, + {file = "multidict-6.9.1-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:aec65b53a07f580606593f877eefbb29a45939bfc0d3fe6e6d9f42b41b749f68"}, + {file = "multidict-6.9.1-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:14c56f73e78faa1f68bbb826197cd5871994e70e841b8590829c35912ece5c64"}, + {file = "multidict-6.9.1-cp315-cp315t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:38c9986f9ce50c459b10de216a05f4bd7ed5ac63887d56e500a52bb464b861ce"}, + {file = "multidict-6.9.1-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:15db6a102cbaf1949cf028ecf080aac76d20bcd29ad4e092574db6c6b7af78a5"}, + {file = "multidict-6.9.1-cp315-cp315t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:bd82c4977196681a499bb6ca9e462afbc5c91c1c15b6a991dfbd72733fea4dd2"}, + {file = "multidict-6.9.1-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bb58ba73a3f96f9a3e46b1fab69929d7edbbddb3133ee74b5c3c54074a53c4f1"}, + {file = "multidict-6.9.1-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7c6eecfab7ce4cd9487ff8ba936fe38cdfd68c04faf3d5c710363c6a8e695659"}, + {file = "multidict-6.9.1-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c62e71e6289d78c0108d8aeb495f8bd3cad4bc1632fedddc9297ddf287ecc20"}, + {file = "multidict-6.9.1-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:539c2cd5fed0947c135cd7eabaaac55f48300dfa1de0f3ca4edb5efa6606f471"}, + {file = "multidict-6.9.1-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:3100d169ceb7bc8f05f89a6db11d1b21f119975fc26f29dc45a472e3569f0879"}, + {file = "multidict-6.9.1-cp315-cp315t-musllinux_1_2_armv7l.whl", hash = "sha256:696477ad71385c4795e3b8e4cf10b0d2c28c2a1ca6a955e031cb1e62993e9ee3"}, + {file = "multidict-6.9.1-cp315-cp315t-musllinux_1_2_i686.whl", hash = "sha256:f5844e7befc707367807586f550fa97e23dcfef0728f02b98c7bc498a961a5df"}, + {file = "multidict-6.9.1-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:abc7c2e4b47bfe6a9aea434d3fdebb9597ee636e914e92ba352f2068b9142f4c"}, + {file = "multidict-6.9.1-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:7ab379f95caee071a37cbd8be86d4c65accc651d391f9f97748fef006f38769c"}, + {file = "multidict-6.9.1-cp315-cp315t-musllinux_1_2_s390x.whl", hash = "sha256:557a4e1708df428ebe6c3081c83a273d275dad2446ce0d81fc648ac71afdb18d"}, + {file = "multidict-6.9.1-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:475d04d5192eba487a3e2f935976340baa24529046e9c1c9c7a3b7bf80445ae1"}, + {file = "multidict-6.9.1-cp315-cp315t-win32.whl", hash = "sha256:10083a8a0f4e1b26b599889e90b9802504ce5d3f7722f925bbb7ca47dd22a7c1"}, + {file = "multidict-6.9.1-cp315-cp315t-win_amd64.whl", hash = "sha256:e96ca64383efa107262ee3949f047af5ee4f1845ba09463466c04d35a83bd3ec"}, + {file = "multidict-6.9.1-cp315-cp315t-win_arm64.whl", hash = "sha256:501ed8b02a5990c67a91c732843609d43a6be1f7576fcdfc867331239f37fbd3"}, + {file = "multidict-6.9.1-py3-none-any.whl", hash = "sha256:7bf6478188f4e47bf5686e8a33da4ae28bf43b1b2528d9ee144d28492bfac60b"}, + {file = "multidict-6.9.1.tar.gz", hash = "sha256:0f06e60fa190aa7abd0914c2a766736fdc8e9f34878c4346338534b73d1b20e2"}, ] [package.dependencies] @@ -1011,134 +1053,151 @@ testing = ["coverage", "pytest", "pytest-benchmark"] [[package]] name = "propcache" -version = "0.5.2" +version = "0.5.4" description = "Accelerated property cache" optional = true python-versions = ">=3.10" groups = ["main"] markers = "extra == \"aiohttp\"" files = [ - {file = "propcache-0.5.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d5a81be28596d6559f6131ef33e10200de6e17643b3c74ce03f9eb103be6ae8b"}, - {file = "propcache-0.5.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:29cbaac5ea0212663e6845e04b5e188d5a6ae6dd919810ac835bf1d3b42c3f4c"}, - {file = "propcache-0.5.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6bf3be92233808fcd338eba0fb4d0b59ec5772af4f4ecfcec450d1bfc0f8b5eb"}, - {file = "propcache-0.5.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2f8ea531c794b9d6274acd4e8d2c2ebcac590a4361d27482edd3010b79f1325e"}, - {file = "propcache-0.5.2-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:decfca4c79dd53ebab484b00cc4b6717d8c369f86e74aa4ca395a64ac651495e"}, - {file = "propcache-0.5.2-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4621064bbf28fa77ff64dd5d94367c04684c67d3a5bf1dff25f0cd0d98a38f3b"}, - {file = "propcache-0.5.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b96db7141a592cbc968daf1feea83a118e6ab378af4abbc72b248c895414c22d"}, - {file = "propcache-0.5.2-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1ca071adabaab6e9219924bbe00af821f1ee7de113a9eca1cdc292de3d120f4d"}, - {file = "propcache-0.5.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e4294d04a94dcab1b3bccd8b66d962dcad411a1d19414b2a41d1445f1de32ad0"}, - {file = "propcache-0.5.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:a0e399a2eccb91ed18721f86aa85757727400b6865c89e88934781deb9c8498b"}, - {file = "propcache-0.5.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:823581fd5cb08b12a48bfa11fe962a7916766b6170c17b028fbdf762b85eb9bf"}, - {file = "propcache-0.5.2-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:949c91d1a990cf3b2e8188dfcfb25005e0b834a06c63fa4ef9f360878ce21ecf"}, - {file = "propcache-0.5.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:cc1177027eda740fdb152706bd215a3f124e3eea15afc39f2cb9fe351b50619e"}, - {file = "propcache-0.5.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b05d643f944a8c3c4bd86d65ffd87bf3264b617f87791940302bc474d2ff5274"}, - {file = "propcache-0.5.2-cp310-cp310-win32.whl", hash = "sha256:8114f28879e0904748e831c3a7774261bd9e75f49be089f389a76f959dcd13fe"}, - {file = "propcache-0.5.2-cp310-cp310-win_amd64.whl", hash = "sha256:5fcb98e7598b1ee0addab320d90f65b530297a867dbfe9de52ea838077e16e3d"}, - {file = "propcache-0.5.2-cp310-cp310-win_arm64.whl", hash = "sha256:04dc2390d9edbbaef7461f33322555976ffddf0b650a038649d026358714e6c5"}, - {file = "propcache-0.5.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:74b70780220e2dd89175ca24b81b68b67c83db499ae611e7f2313cb329801c78"}, - {file = "propcache-0.5.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a4840ab0ae0216d952f4b53dc6d0b992bfc2bedbfe360bdd9b548bc184c08959"}, - {file = "propcache-0.5.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c6844ba6364fb12f403928a82cfd295ab103a2b315c77c747b2dbe4a41894ea7"}, - {file = "propcache-0.5.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2293949b855ce597f2826452d17c2d545fb5622379c4ea6fdf525e9b8e8a2511"}, - {file = "propcache-0.5.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0fd59b5af35f74da48d905dcbad55449ba13be91823cb05a9bd590bbf5b61660"}, - {file = "propcache-0.5.2-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29f9309a2e42b0d273be006fdb4be2d6c39a47f6f57d8fb1cf9f81481df81b66"}, - {file = "propcache-0.5.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5aaa2b923c1944ac8febd6609cb373540a5563e7cbcb0fd770f75dace2eb817b"}, - {file = "propcache-0.5.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:66ea454f095ddf5b6b14f56c064c0941c4788be11e18d2464cf643bf7203ff67"}, - {file = "propcache-0.5.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:95f1e3f4760d404b13c9976c0229b2b49a3c8e2c62a9ce92efdd2b11ada75e3f"}, - {file = "propcache-0.5.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:85341b12b9d55bad0bded24cac341bb34289469e03a11f3f583ea1cc1db0326c"}, - {file = "propcache-0.5.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:26a4dca084132874e639895c3135dfad5eb20bae209f62d1aeb31b03e601c3c0"}, - {file = "propcache-0.5.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:3b199b9b2b3d6a7edf3183ba8a9a137a22b97f7df525feb5ae1eccf026d2a9c6"}, - {file = "propcache-0.5.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e59bc9e66329185b93dab73f210f1a37f81cb40f321501db8017c9aea15dba27"}, - {file = "propcache-0.5.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:552ffadf6ad409844bc5919c42a0a83d88314cedddaea0e41e80a8b8fffe881f"}, - {file = "propcache-0.5.2-cp311-cp311-win32.whl", hash = "sha256:cd416c1de191973c52ff1a12a57446bfc7642797b282d7caf2162d7d1b8aa9a0"}, - {file = "propcache-0.5.2-cp311-cp311-win_amd64.whl", hash = "sha256:44e488ef40dbb452700b2b1f8188934121f6648f52c295055662d2191959ff82"}, - {file = "propcache-0.5.2-cp311-cp311-win_arm64.whl", hash = "sha256:54adaa85a22078d1e306304a40984dc5be99d599bf3dc0a24dc98f7daeab89ab"}, - {file = "propcache-0.5.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:806719138ecd720339a12410fb9614ac9b2b2d3a5fdf8235d56981c36f4039ba"}, - {file = "propcache-0.5.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:db2b80ea58eab4f86b2beec3cc8b39e8ff9276ac20e96b7cce43c8ae84cd6b5a"}, - {file = "propcache-0.5.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e5cbfac9f61484f7e9f3597775500cd3ebe8274e9b050c38f9525c77c97520bf"}, - {file = "propcache-0.5.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5dbc581d2814337da56222fab8dc5f161cd798a434e49bac27930aaef798e144"}, - {file = "propcache-0.5.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:857187f381f88c8e2fa2fe56ab94879d011b883d5a2ee5a1b60a8cd2a06846d9"}, - {file = "propcache-0.5.2-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:178b4a2cdaac1818e2bf1c5a99b94383fa73ea5382e032a48dec07dc5668dc42"}, - {file = "propcache-0.5.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6f328175a2cde1f0ff2c4ed8ce968b9dcfb55f3a7153f39e2957ed994da13476"}, - {file = "propcache-0.5.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5671d09a36b06d0fd4a3da0fccbcae360e9b1570924171a15e9e0997f0249fba"}, - {file = "propcache-0.5.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:80168e2ebe4d3ec6599d10ad8f520304ae1cad9b6c5a95372aef1b66b7bfb53a"}, - {file = "propcache-0.5.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:45f11346f884bc47444f6e6647131055844134c3175b629f84952e2b5cd62b64"}, - {file = "propcache-0.5.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8e778ebd44ef4f66ed60a0416b06b489687db264a9c0b3620362f26489492913"}, - {file = "propcache-0.5.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:c0cb9ed24c8964e172768d455a38254c2dd8a552905729ce006cad3d3dda59b1"}, - {file = "propcache-0.5.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:1d1ad32d9d4355e2be65574fd0bfd3677e7066b009cd5b9b2dee8aa6a6393b33"}, - {file = "propcache-0.5.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c80f4ba3e8f00189165999a742ee526ebeccedf6c3f7beb0c7df821e9772435a"}, - {file = "propcache-0.5.2-cp312-cp312-win32.whl", hash = "sha256:8c7972d8f193740d9175f0998ab38717e6cd322d5935c5b0fef8c0d323fd9031"}, - {file = "propcache-0.5.2-cp312-cp312-win_amd64.whl", hash = "sha256:d9ee8826a7d47863a08ac44e1a5f611a462eefc3a194b492da242128bec75b42"}, - {file = "propcache-0.5.2-cp312-cp312-win_arm64.whl", hash = "sha256:2800a4a8ead6b28cccd1ec54b59346f0def7922ee1c7598e8499c733cfbb7c84"}, - {file = "propcache-0.5.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:099aaf4b4d1a02265b92a977edf00b5c4f63b3b17ac6de39b0d637c9cac0188a"}, - {file = "propcache-0.5.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:68ce1c44c7a813a7f71ea04315a8c7b330b63db99d059a797a4651bb6f69f117"}, - {file = "propcache-0.5.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:fc299c129490f55f254cd90be0deca4764e36e9a7c08b4aa588479a3bbed3098"}, - {file = "propcache-0.5.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a6ae2198be502c10f09b2516e7b5d019816924bc3183a43ce792a7bd6625e6f4"}, - {file = "propcache-0.5.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6041d31504dc1779d700e1edcfb08eea334b357620b06681a4eabb57a74e574e"}, - {file = "propcache-0.5.2-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f7eabc04151c78a9f4d5bbb5f1faf571e4defeb4b585e0fe95b60ff2dbe4d3d7"}, - {file = "propcache-0.5.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4db0ba63d693afd40d249bd93f842b5f144f8fcbb83de05660373bcf30517b1d"}, - {file = "propcache-0.5.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1dbcf7675229b35d31abb6547d8ebc8c27a830ac3f9a794edff6254873ec7c0a"}, - {file = "propcache-0.5.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d310c013aad2c72f1c3f2f8dd3279d460a858c551f97aeb8c63e4693cca7b4d2"}, - {file = "propcache-0.5.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:06187263ddad280d05b4d8a8b3bb7d164cbebd469236544a42e6d9b28ac6a4fa"}, - {file = "propcache-0.5.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3115559b8effafd63b142ea5ed53d63a16ea6469cbc63dce4ee194b42db5d853"}, - {file = "propcache-0.5.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c60462af8e6dc30c35407c7237ea908d777b22862bbee27bc4699c0d8bcdc45a"}, - {file = "propcache-0.5.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:40314bca9ac559716fe374094fc81c11dcc34b64fd6c585360f5775690505704"}, - {file = "propcache-0.5.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:cfa21e036ce1e1db2be04ba3b85d2df1bb1702fa01932d984c5464c665228ff4"}, - {file = "propcache-0.5.2-cp313-cp313-win32.whl", hash = "sha256:f156a3529f38063b6dbaf356e15602a7f95f8055b1295a438433a6386f10463d"}, - {file = "propcache-0.5.2-cp313-cp313-win_amd64.whl", hash = "sha256:dfed59d0a5aeb01e242e66ff0300bc4a265a7c05f612d30016f0b60b1017d757"}, - {file = "propcache-0.5.2-cp313-cp313-win_arm64.whl", hash = "sha256:ba338430e87ceb9c8f0cf754de38a9860560261e56c00376debd628698a7364f"}, - {file = "propcache-0.5.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:a592f5f3da71c8691c788c13cb6734b6d17663d2e1cb8caddf0673d01ef8847d"}, - {file = "propcache-0.5.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:6a997d0489e9668a384fcfd5061b857aa5361de73191cac204d04b889cfbbafa"}, - {file = "propcache-0.5.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:10734b5484ea113152ee25a91dccedf81631791805d2c9ccb054958e51842c94"}, - {file = "propcache-0.5.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cafca7e56c12bb02ae16d283742bef25a61122e9dab2b5b3f2ccbe589ce32164"}, - {file = "propcache-0.5.2-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f064f8d2b59177878b7615df1735cd8fe3462ed6be8c7b217d17a276489c2b7f"}, - {file = "propcache-0.5.2-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f78abfa8dfc32376fd1aacf597b2f2fbbe0ea751419aee718af5d4f82537ef8c"}, - {file = "propcache-0.5.2-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f7467da8a9822bf1a55336f877340c5bcbd3c482afc43a99771169f74a26dedc"}, - {file = "propcache-0.5.2-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a6ddc6ac9e25de626c1f129c1b467d7ecd33ce2237d3fd0c4e429feef0a7ee1f"}, - {file = "propcache-0.5.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2f22cbbac9e26a8e864c0985ff1268d5d939d53d9d9411a9824279097e03a2cb"}, - {file = "propcache-0.5.2-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:fc76378c62a0f04d0cd82fbb1a2cd2d7e28fcb40d5873f28a6c44e388aaa2751"}, - {file = "propcache-0.5.2-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:acd2c8edba48e31e58a363b8cf4e5c7db3b04b3f9e371f601df30d9b0d244836"}, - {file = "propcache-0.5.2-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:452b5065457eb9991ec5eb38ff41d6cd4c991c9ac7c531c4d5849ae473a9a13f"}, - {file = "propcache-0.5.2-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:3430bb2bfe1331885c427745a751e774ee679fd4344f80b97bf879815fe8fa55"}, - {file = "propcache-0.5.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:cef6cea3922890dd6c9654971001fa797b526c16ab5e1e46c05fd6f877be7568"}, - {file = "propcache-0.5.2-cp313-cp313t-win32.whl", hash = "sha256:72d61e16dd78228b58c5d47be830ff3da7e5f139abdf0aef9d86cde1c5cf2191"}, - {file = "propcache-0.5.2-cp313-cp313t-win_amd64.whl", hash = "sha256:0958834041a0166d343b8d2cedcd8bcbaeb4fdbe0cf08320c5379f143c3be6e7"}, - {file = "propcache-0.5.2-cp313-cp313t-win_arm64.whl", hash = "sha256:6de8bd93ddde9b992cf2b2e0d796d501a19026b5b9fd87356d7d0779531a8d96"}, - {file = "propcache-0.5.2-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:46088abff4cba581dea21ae0467a480526cb25aa5f3c269e909f800328bc3999"}, - {file = "propcache-0.5.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fc88b26f08d634f7bc819a7852e5214f5802641ab8d9fd5326892292eee1993e"}, - {file = "propcache-0.5.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:97797ebb098e670a2f92dd66f32897e30d7615b14e7f59711de23e30a9072539"}, - {file = "propcache-0.5.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba57fffe4ac99c5d30076161b5866336d97600769bad35cc68f7774b15298a4e"}, - {file = "propcache-0.5.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:583c19759d9eec1e5b69e2fbef36a7d9c326041be9746cb822d335c8cedc2979"}, - {file = "propcache-0.5.2-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d0326e2e5e1f3163fa306c834e48e8d490e5fae607a097a40c0648109b47ba80"}, - {file = "propcache-0.5.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e00820e192c8dbebcafb383ebbf99030895f09905e7a0eb2e0340a0bcc2bc825"}, - {file = "propcache-0.5.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c66afea89b1e43725731d2004732a046fe6fe955d51f952c3e95a7314a284a39"}, - {file = "propcache-0.5.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d4dc37dec6c6cdad0b57881a5658fd14fbf53e333b1a86cf86559f190e1d9ec4"}, - {file = "propcache-0.5.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:5570dbcc97571c15f68068e529c92715a12f8d54030e272d264b377e22bd17a5"}, - {file = "propcache-0.5.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:f814362777a9f841adddb200ecdf8f5cb1e5a3c4b7a86378edbd6ccb26edd702"}, - {file = "propcache-0.5.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:196913dea116aeb5a2ba95af4ddcb7ea85559ae07d8eee8751688310d09168c3"}, - {file = "propcache-0.5.2-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:6e7b8719005dd1175be4ab1cd25e9b98659a5e0347331506ec6760d2773a7fb5"}, - {file = "propcache-0.5.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:51f96d685ab16e88cab128cd37a52c5da540809c8b879fa047731bfcb4ad35a4"}, - {file = "propcache-0.5.2-cp314-cp314-win32.whl", hash = "sha256:cc6fc3cc62e8501d3ed62894425040d2728ecddb1ed072737a5c70bd537aa9f0"}, - {file = "propcache-0.5.2-cp314-cp314-win_amd64.whl", hash = "sha256:81e3a30b0bb60caa22033dd0f8a3618d1d67356212514f62c57db75cb0ef410c"}, - {file = "propcache-0.5.2-cp314-cp314-win_arm64.whl", hash = "sha256:0d2c9bf8528f135dbb805ce027567e09164f7efa51a2be07458a2c0420f292d0"}, - {file = "propcache-0.5.2-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:4bc8ff1feffc6a61c7002ffe84634c41b822e104990ae009f44a0834430070bb"}, - {file = "propcache-0.5.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:79aa3ff0a9b566633b642fa9caf7e21ed1c13d6feca718187873f199e1514078"}, - {file = "propcache-0.5.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1b31822f4474c4036bae62de9402710051d431a606d6a0f907fec79935a071aa"}, - {file = "propcache-0.5.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:13fef48778b5a2a756523fdb781326b028ca75e32858b04f2cdd19f394564917"}, - {file = "propcache-0.5.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8b73ab70f1a3351fbc71f663b3e645af6dd0329100c353081cf69c37433fc6fe"}, - {file = "propcache-0.5.2-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5538d2c13d93e4698af7e092b57bc7298fd35d1d58e656ae18f23ee0d0378e03"}, - {file = "propcache-0.5.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cd645f03898405cabe694fb8bc35241e3a9c332ec85627584fe3de201452b335"}, - {file = "propcache-0.5.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a473b3440261e0c60706e732b2ed2f517857344fc21bf48fdfe211e2d98eb285"}, - {file = "propcache-0.5.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7afa37062e6650640e932e4cc9297d81f9f42d9944029cc386b8247dea4da837"}, - {file = "propcache-0.5.2-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:8a90efd5777e996e42d568db9ac740b944d691e565cbfd31b2f7832f9184b2b8"}, - {file = "propcache-0.5.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:f19bb891234d72535764d703bfed1153cc34f4214d5bd7150aee1eec9e8f4366"}, - {file = "propcache-0.5.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:32775082acd2d807ee3db715c7770d38767b817870acfa08c29e057f3c4d5b56"}, - {file = "propcache-0.5.2-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:9282fb1a3bccd038da9f768b927b24a0c753e466c086b7c4f3c6982851eefb2d"}, - {file = "propcache-0.5.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cc49723e2f60d6b32a0f0b08a3fd6d13203c07f1cd9566cfce0f12a917c967a2"}, - {file = "propcache-0.5.2-cp314-cp314t-win32.whl", hash = "sha256:2d7aa89ebca5acc98cba9d1472d976e394782f587bad6661003602a619fd1821"}, - {file = "propcache-0.5.2-cp314-cp314t-win_amd64.whl", hash = "sha256:d447bb0b3054be5818458fbb171208b1d9ff11eba14e18ca18b90cbb45767370"}, - {file = "propcache-0.5.2-cp314-cp314t-win_arm64.whl", hash = "sha256:fe67a3d11cd9b4efabfa45c3d00ffba2b26811442a73a581a94b67c2b5faccf6"}, - {file = "propcache-0.5.2-py3-none-any.whl", hash = "sha256:be1ddfcbb376e3de5d2e2db1d58d6d67463e6b4f9f040c000de8e300295465fe"}, - {file = "propcache-0.5.2.tar.gz", hash = "sha256:01c4fc7480cd0598bb4b57022df55b9ca296da7fc5a8760bd8451a7e63a7d427"}, + {file = "propcache-0.5.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b77c313314524ca9c38fbd70f73515d04597ac58c40c939bc0e71eeb4abff680"}, + {file = "propcache-0.5.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8f911c395cef73c510bac566da9507bb6a43e7763d0c79138dc60ee53f11207e"}, + {file = "propcache-0.5.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d83b12902eb8bce151259c86c03ba746600b2d994543de46e370cecf96c452f2"}, + {file = "propcache-0.5.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c9281e922c072158c91974d4589f1dbe0fee6d467f284c28e463f9f5a4d933f4"}, + {file = "propcache-0.5.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9f3551b8a35c1df3e7ea4d2d86edee15f0dde1bddd434a71744048683544d0ef"}, + {file = "propcache-0.5.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ec6a85f424afa8d23e0d9a094e5dbb6eda01da91c92b9183cd433768247ffc97"}, + {file = "propcache-0.5.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f574e460d1c8a08384a016fdb09ccf3543433263ed6b2f97104f979e64ea57c2"}, + {file = "propcache-0.5.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d8e017eeb7482bed34cdb0d61cf2bcfc88d104bbab296a17cd16a6af8aabc70e"}, + {file = "propcache-0.5.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f273dcf7149a50527c4fd1f55cfe9eac0f60753f5af544b4c9352578e20c0874"}, + {file = "propcache-0.5.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:fc2461ecc45f17893f8207e73b46ea8ba93e33630e51cf4af3fbc21d47462b1a"}, + {file = "propcache-0.5.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:279655a16973f1ee2bd2fe79973137681642fd9ae0d89215bba263726eb0dc3a"}, + {file = "propcache-0.5.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:e9f165403b81fea7e89c932d89046a1e3d9a3a60e8d7ef2f249dccdcb0982bf5"}, + {file = "propcache-0.5.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:1783582065a1f07f9d9ee1e992e13f15d7dc8fb1eb3a7476d43eb3f2e69d26bb"}, + {file = "propcache-0.5.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3d605bb239b796e82a81c6709548b2bd460ab73b4590cb0c83de8a2dd9694d0f"}, + {file = "propcache-0.5.4-cp310-cp310-win32.whl", hash = "sha256:141fdbd73748db0cf7636035030aaac383d2efde8f34e7bc24594cc776d225b8"}, + {file = "propcache-0.5.4-cp310-cp310-win_amd64.whl", hash = "sha256:146f48a9e4812611a7581003b1a39de56c34967046310c4171a68ef908c9a745"}, + {file = "propcache-0.5.4-cp310-cp310-win_arm64.whl", hash = "sha256:6c7599df2b57ebeea8de011b5f2f7b85de95e76037d43d34b95e328430275487"}, + {file = "propcache-0.5.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:897d1ddf6716e8f47200f7aad9a0efa6cc7586df66c6defa572f9eab379c078e"}, + {file = "propcache-0.5.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9cbfff4423eef4cc6cafc021469641a2b835f610b2647a6c5281903e21b8670d"}, + {file = "propcache-0.5.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3fc24f209c1b7f7f688b66b98293954f5504279760999b58920ee12dd8471c1d"}, + {file = "propcache-0.5.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:62530ca89187827e4a4fe733f971abe81a7542eeea48ff61995f19b64d7199c8"}, + {file = "propcache-0.5.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:56fc3f7599528db40b1efa0889a620116e2704144495273d66066e8164e45838"}, + {file = "propcache-0.5.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4f2d880ff60f45898f4acfa152aac8d04e3ee627d90ff4003491bf92239d5757"}, + {file = "propcache-0.5.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6e9368e87a3efc285e559131092c5db643eb8e56de4ee42064d5baec22ef2bb5"}, + {file = "propcache-0.5.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:004e685b315646c410771836e72a44f143bbe624f29653a42687815069a303d5"}, + {file = "propcache-0.5.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:594eb4c6ec35e7179b058481f4e9f02521b56de16fa577c4b85c76fb1bf8a9f8"}, + {file = "propcache-0.5.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:2dba2f02d2d5c09ef8a0e6c1a42aeaa451f4be9898cb00b04fe98717da2eb23b"}, + {file = "propcache-0.5.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c3ef2818d63bc86071e9d2989ae75a1bc32b8f7059cfd9f5abbbee70c32e2ed6"}, + {file = "propcache-0.5.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:dd2ac8f5b643454c2cc6b6118b13da16e88f4a6434fc3ba61aca384029f04f36"}, + {file = "propcache-0.5.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:4054acf80d40456a0537f2913b349718649d8d6458a14ab7f48d0ce28c30869d"}, + {file = "propcache-0.5.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:40e94adb1e7d39ff28a8bd8d8b8fbd1df6b9f40976dbe379134f1ce058e532dd"}, + {file = "propcache-0.5.4-cp311-cp311-win32.whl", hash = "sha256:9f86f7259efe2c951f43e57d471c9b41daa5bfc7db9f67189059cf1ae6d77fd9"}, + {file = "propcache-0.5.4-cp311-cp311-win_amd64.whl", hash = "sha256:e904d4d01f36bd6e197590be1533c44e06058771e0746dd073a8ebb3ef880858"}, + {file = "propcache-0.5.4-cp311-cp311-win_arm64.whl", hash = "sha256:d42a9a856a4a6e2f6c10f1318c07e7daa498d6593abe745c71dae4521a26ca39"}, + {file = "propcache-0.5.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b28f41fa3b8c6900457f858ec5b03998f3a6d535fbc1bb2edec5961ea05ec429"}, + {file = "propcache-0.5.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:dcbf346a318a5e30063f547630b02bb787ce2f45b6368d5da143660b6a3835d8"}, + {file = "propcache-0.5.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:87a3caecf8095e48dc72f84bfa42e23a848cf410cc9cc13031fba4869b706a21"}, + {file = "propcache-0.5.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:60a64cbccaa11b7760ce705a14ada17ba459e7ca9f23ba587eb013821032d7ef"}, + {file = "propcache-0.5.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a74bfa37147cc08fb29df10bd9c16f40fa7f860cd3a6d2fff853323a94f6e17f"}, + {file = "propcache-0.5.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a4d7a54719b67338a305dca2ce6aafe366817df94ddfd4b5514374356f5ca546"}, + {file = "propcache-0.5.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2814ecd8e818f487bee4b0f921bc4d1c176cc5fc71ac0f072d0fa67eda4ac14b"}, + {file = "propcache-0.5.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6af4693716bfb03f1752ef1b30faa593db2c01d5272e9b8564a1549452a979ab"}, + {file = "propcache-0.5.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4fbc1a15dc8cd1689508758d626b372b1f09d28d9577667feaf9e6bfcd8efcbc"}, + {file = "propcache-0.5.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:cdee8205a44d0be91bbac4c41b95d86641b72dfc7aef1279400e4fda3f26a937"}, + {file = "propcache-0.5.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:9a2a8a50a93dee0268a860a07fa3b4bd968f8ce4dbd794957da772f395368526"}, + {file = "propcache-0.5.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:7ffafcbfc7b549ab940047e505c831eabac5e67de53e1bc174adbc5285c55944"}, + {file = "propcache-0.5.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:d1f5a500bfcbb2c0ab85e98a0dcd70f5899d34efe365a0187700369a79603031"}, + {file = "propcache-0.5.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8a235f73d6e020855dc29dff012d920c02ee0feab8d73a24185a7569f4be1161"}, + {file = "propcache-0.5.4-cp312-cp312-win32.whl", hash = "sha256:b3083bfe87f95c756e610bd8025f26cbd1cd4aaa03a422f2d65efb7a97cd53d8"}, + {file = "propcache-0.5.4-cp312-cp312-win_amd64.whl", hash = "sha256:98914de2c4d7f0f9f4a8c6ea4bf05841f4175796941e3ef7d47eb718f22311fb"}, + {file = "propcache-0.5.4-cp312-cp312-win_arm64.whl", hash = "sha256:8876b39961e33d912afe3c1bee18ee564fdad0206f873cc15d522756b7f50737"}, + {file = "propcache-0.5.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:36c0d9db44b523ef93d03341b1c42d69ff01d673c053d1b1c6c3a363bcaa39ba"}, + {file = "propcache-0.5.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1d52a05dc417279f7e5c7618c5dfbbc29923aaf9bc0a5c1802ddcebf54c61a0"}, + {file = "propcache-0.5.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:44149f46500a0a41b95b4d99c2e586a77319539730607b9892974a092788b111"}, + {file = "propcache-0.5.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dbab5f5ff6897c81f355d079010cdae85b02e5a0b518b5251523b8ad8ae9ac3c"}, + {file = "propcache-0.5.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c3e98c55bde2bcf7db3c70d1aed7ae9aa8aebbf19a250c66645cde44cdb8b867"}, + {file = "propcache-0.5.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:db3ae52ccc150dbc84704e9d642743897f3e1c54742ff34cacb661e52e3818a9"}, + {file = "propcache-0.5.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f85915e00dcb1cd9f2f890ead064ed40a27df06f0db65be427b29482ae357572"}, + {file = "propcache-0.5.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c2ba30a89035b57b73e00475de948521602f543d79ce01db10b04b36c4c76fc8"}, + {file = "propcache-0.5.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ae58f361bd5dae942717c65d3413b478c70aea9c462599e7b9adad3731db3894"}, + {file = "propcache-0.5.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:96f7c5c15656040ddcbc51e56dc59b58aa25999d743c126abd425b9766ab43e9"}, + {file = "propcache-0.5.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7cc528e760a8af06f2b13e9b9f362cd90c7c718ea61228a96dbd31ba16ed7f47"}, + {file = "propcache-0.5.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:425f8cc86ab5018b4b8d4a23bc8e74d964bd3d757c3702e301aa79be76c53f6c"}, + {file = "propcache-0.5.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:a5793c7698a53f56f4a1889a4737c7eeb1b7ad0842fa6b1abca22913ff79c8c1"}, + {file = "propcache-0.5.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c02c0e570c5c7e077b0181a9f3cdb7d4c3617d1cda6b5c95bd5d34022923d82c"}, + {file = "propcache-0.5.4-cp313-cp313-win32.whl", hash = "sha256:3e413d7a4a9b4866b7a761d6060d434b64d23cd35122eda3b026a0bbe8196b25"}, + {file = "propcache-0.5.4-cp313-cp313-win_amd64.whl", hash = "sha256:0c889f6fa84957bc7e8b4eab71fd16a0455068d5045e3aa40c733071d2b2fd77"}, + {file = "propcache-0.5.4-cp313-cp313-win_arm64.whl", hash = "sha256:69fc35c0779522da366c563e5faf203ffc1f8ff0021d5b1337fa4efa5be73177"}, + {file = "propcache-0.5.4-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:e6720ba44ad7e72174314d0e1fb0172494cff5c73a3a8a2159c3d2402ff15565"}, + {file = "propcache-0.5.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:4cfe0a92ae30151869e67a4b5f5e105e4e03ad30b3f38e5211b5bf77d0881993"}, + {file = "propcache-0.5.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d759d05634f1b038fb625a66662a8c85e5a8fec912da381b5149ddac107482b"}, + {file = "propcache-0.5.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:251c63dd46a0659bb875cb254dc4c1e79ee91a847c737cd62373295afc2235dc"}, + {file = "propcache-0.5.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7a8d5ff04eb1f85698a78d20c62a14676e7b960dcafde09a388d60ad377d355d"}, + {file = "propcache-0.5.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7b9100a93b372418d8688f3f2a3e5b45c64d70ca4d6176e121aca1e3bfc1e32f"}, + {file = "propcache-0.5.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cc07876cfb079b6f6f36d21ce75784ad6c2c6b563eeac0ed26c2fa2669b85df9"}, + {file = "propcache-0.5.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0951315a6b3142ee2167404d707743f0157c110091342b1aa0accac5cf0e4acf"}, + {file = "propcache-0.5.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:bee7d3aed13d56f54e681df38c3a23031bc9e3863f687d9d598825c9146acd7d"}, + {file = "propcache-0.5.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:4e985382be6d15da8d0c2710a6fa7b9070fc9ecdeefb7f580e88373984ec8be3"}, + {file = "propcache-0.5.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:9e9ab13760aa8b6d0881ae7cb04fd891d8d490cd2554ea8e79bb278399169bcc"}, + {file = "propcache-0.5.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:1b2f3bec4261a94019575481c726c29850f72e27907773c75b1de421e20e9f9d"}, + {file = "propcache-0.5.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:720cf832eb2d0b0dfee129cb3335a26f6ce3cc45ee1187e8f0731758caa16792"}, + {file = "propcache-0.5.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9fb0a5be8d9aa213150e8d8148a42aca4984b285bcad1e69587dc4298edd929b"}, + {file = "propcache-0.5.4-cp314-cp314-win32.whl", hash = "sha256:30cc1cebaf9aef49db06357a50398323ae04d70460c0491837d026ab7d6452ea"}, + {file = "propcache-0.5.4-cp314-cp314-win_amd64.whl", hash = "sha256:0a095db8e15a6020db149ecbed6461939fe74f6acaa3ae8b702a1fe8c38cd983"}, + {file = "propcache-0.5.4-cp314-cp314-win_arm64.whl", hash = "sha256:45488d1a5f9ab5bd90aaa1ca20f50fe1922b8ffad71a2009d2adf41355897aac"}, + {file = "propcache-0.5.4-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:53eaa697c4d0422ff4cb714d00231b43352064d97b944033b30c1d57cc506ec0"}, + {file = "propcache-0.5.4-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:886b59c4d28ca97dd23b025fdfc50a0356be934efbbbca89ad26230067f86fe5"}, + {file = "propcache-0.5.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3fa15757fea1dfcd5b7745cad9f4638929605531bd4018ab2adff7955f1a403d"}, + {file = "propcache-0.5.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6f0093ac3e9daada202c2082439d414a625c57184727a46e112a3fb2a81cb788"}, + {file = "propcache-0.5.4-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3cd3a7edb6b95b9b33998135ebfa18d709da82290fb8f27c858970b5a12c8b56"}, + {file = "propcache-0.5.4-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c174bfd1c48a1b51a3078e95586dde718374bac79719ab3541ec9e74aec40574"}, + {file = "propcache-0.5.4-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a219f0ac59817a9114dd2aa57c13180f993e819ba658c7ddab4b66ed1ee0d370"}, + {file = "propcache-0.5.4-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:17a7400cec0256f0a71ae71f9da398f9894c956ff6668a1c9d317b3367316320"}, + {file = "propcache-0.5.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:978f28401afbc76cdc3df9e1717b4229a06b626a1dcc75db4e1f2beb3884c3e9"}, + {file = "propcache-0.5.4-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:4a1f4f5ffa55dce6307631f3cb2948e117e665966ea512e0d502b16c24f567e7"}, + {file = "propcache-0.5.4-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:213bb68d9ced5cf2bf717b1071bf2b09b4b04c426256f9fe6d054c60318424c4"}, + {file = "propcache-0.5.4-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:286867fb156488c251a3721766e380ac4495e4fd6b51aaa1403d89ce7f4359d9"}, + {file = "propcache-0.5.4-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:445ee3bfb46e85838387fb3c536a73cc0b994dc192b004e40e170adc54aa2a7e"}, + {file = "propcache-0.5.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:48cb48c5346a97de792254af77715aa2529c2a1ebc5f586aa0aae44a02f1fe57"}, + {file = "propcache-0.5.4-cp314-cp314t-win32.whl", hash = "sha256:03b229037d25b801e7af53fd52b9fc49d9439b036fca1e087e02780631adfa97"}, + {file = "propcache-0.5.4-cp314-cp314t-win_amd64.whl", hash = "sha256:8a1fc236528c457cd739c88abe823da851b7ab645d72792f88658114cc340c12"}, + {file = "propcache-0.5.4-cp314-cp314t-win_arm64.whl", hash = "sha256:135036c5cfc93864affb0f9af9a27e5d7a71cb7bd745e7b6dbfc2d56cc30e827"}, + {file = "propcache-0.5.4-cp315-cp315-macosx_10_15_universal2.whl", hash = "sha256:45bf2e730ab8905d0527fe05a86500f406e64305c34cc81ebe64b4617cab9760"}, + {file = "propcache-0.5.4-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:31eb43ba2edc704ab2ec27815315dd8a19def0fb16215be4cfe8d32fe78ffd51"}, + {file = "propcache-0.5.4-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:174507f82d3594622acb1dd2dafecf2d899d6d506335494e7107767bf05f3aae"}, + {file = "propcache-0.5.4-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:50e337653721d20ead710da33bf44487fbe8a0db8782714b60306481e9f95b51"}, + {file = "propcache-0.5.4-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0d21d0d2c82bbfeb1677a9711f38df968f9837576102bb4add1bd449d28d88f1"}, + {file = "propcache-0.5.4-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ccf4f7a79e26bb7efb06ecd50c177833b71df05cbc748701372325e6bcc17f6f"}, + {file = "propcache-0.5.4-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:23278f808cd81d5ada7184a76606b925fb3389c60e1077b2cd7da7b1fcf0553c"}, + {file = "propcache-0.5.4-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e738ab81179510ce79b2eac9a6ecf47feffd9e76d1c72e403005dddb6e36c06c"}, + {file = "propcache-0.5.4-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:a419ee85e654927baabda3929c03c0cc1112bf472ff0dfd6142f4e3a81ca4162"}, + {file = "propcache-0.5.4-cp315-cp315-musllinux_1_2_armv7l.whl", hash = "sha256:b61805357d966680acf68b3b6d49772631ed9df44ebece10ff1460e117a7da8a"}, + {file = "propcache-0.5.4-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:58134228927cee6c047d626c08e60a81be604a20578a12ce752cc5c9a84d4826"}, + {file = "propcache-0.5.4-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:350b272b2279f4135a64fc0c304a5d08e28a137c9573442c606152446638a831"}, + {file = "propcache-0.5.4-cp315-cp315-musllinux_1_2_s390x.whl", hash = "sha256:45bebbe252550fec975ba3b62bc6f931643cfd3b5464ef47619cf3fef154e01c"}, + {file = "propcache-0.5.4-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:ada748108a43d29b7c328ba7db3755327cd94f028bcc1a7ee3f0addcfacd9c38"}, + {file = "propcache-0.5.4-cp315-cp315-win32.whl", hash = "sha256:ee19113bce2f3acd46432050688b70f61acd6857d75abb9ec96341b7e9ced123"}, + {file = "propcache-0.5.4-cp315-cp315-win_amd64.whl", hash = "sha256:ceb3e879afac028f93d272c957814695dc5569e4904262dbee92f6c41bd5e4a3"}, + {file = "propcache-0.5.4-cp315-cp315-win_arm64.whl", hash = "sha256:c83acbce9f2b5e3f5f5eda9e53d2001fed22fcdfef81274a9e02d8fd53b70a30"}, + {file = "propcache-0.5.4-cp315-cp315t-macosx_10_15_universal2.whl", hash = "sha256:a5e8ef588c109725dc713ba69aadcac00a1ef90c2ce9c0a8c7075128f569f47f"}, + {file = "propcache-0.5.4-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:4d86476a935c88963d9b8e1a9a0d38188790e9622169bfbafa173046846709d3"}, + {file = "propcache-0.5.4-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:f5470694918830da62fac9e69133b53d23b736d7070e587b27a4a2be37e08e68"}, + {file = "propcache-0.5.4-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10ef33a68a61ce317e095fd2e202a592ea92392b90944a78c993f0d9a73ab06c"}, + {file = "propcache-0.5.4-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5cacf3c9efd09df409dc33654dd077e1c245ba8fb747b0f0236ef41b7c49b589"}, + {file = "propcache-0.5.4-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:770e8209d018175fc0063936fa9583b6d27e88c5ad31543f3383d66080efdd62"}, + {file = "propcache-0.5.4-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:03969626faf0783a592dfa17e28eac06018bd0b44dafae6943d53b92421a7f72"}, + {file = "propcache-0.5.4-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ef3b928d9c984322b5c44e6964d8dbc653da87d2d8ee1647fa6da43072e650a9"}, + {file = "propcache-0.5.4-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:7177c43eddf10a0893c4fec52ebb408fdcd7f7d63962caace9180d8f81b14ece"}, + {file = "propcache-0.5.4-cp315-cp315t-musllinux_1_2_armv7l.whl", hash = "sha256:420162a77f94eb1cf5ef7893f500016dabd548e73de956785a1dd899cc73006a"}, + {file = "propcache-0.5.4-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:3eb2e820e8e2101407da93f17c57cbb7d225461955fc60105daaba14cd421ee2"}, + {file = "propcache-0.5.4-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:13e52b6e0bde97dee98ab66552dbff2931649c96f1ac432eac299fe689ec373b"}, + {file = "propcache-0.5.4-cp315-cp315t-musllinux_1_2_s390x.whl", hash = "sha256:12682126712ddc19b70ff819debbd279e58adf1f0c8f8f8138c18ade2044b284"}, + {file = "propcache-0.5.4-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:3af0c8642b2da4815d86e631232ac8286e17644fad907c19508aa8e7cb4ba8ad"}, + {file = "propcache-0.5.4-cp315-cp315t-win32.whl", hash = "sha256:1df8d8561b21465c5dd56110a01caf897e026d065b4b84e98a488209094272ec"}, + {file = "propcache-0.5.4-cp315-cp315t-win_amd64.whl", hash = "sha256:02c0a34f16889cf800f10f0247a564d8ce6eeab6ffcd7c87198f769067eb8432"}, + {file = "propcache-0.5.4-cp315-cp315t-win_arm64.whl", hash = "sha256:dc4242ca653c9b30ab51c5f8193323e7bc0928f897ee9103201e59a43abcb72e"}, + {file = "propcache-0.5.4-py3-none-any.whl", hash = "sha256:62c60aec739ed00124573cce1178138fd690c7676352d67a37328c1cf51d7468"}, + {file = "propcache-0.5.4.tar.gz", hash = "sha256:ff6b113f50bc066a698db5d944d2c6dc7507168dd3341e255a8892fd0715a558"}, ] [[package]] @@ -1456,60 +1515,78 @@ files = [ [[package]] name = "tomli" -version = "2.4.1" +version = "2.5.0" description = "A lil' TOML parser" optional = false python-versions = ">=3.8" groups = ["dev"] markers = "python_version == \"3.10\"" files = [ - {file = "tomli-2.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30"}, - {file = "tomli-2.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a"}, - {file = "tomli-2.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96481a5786729fd470164b47cdb3e0e58062a496f455ee41b4403be77cb5a076"}, - {file = "tomli-2.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9"}, - {file = "tomli-2.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47149d5bd38761ac8be13a84864bf0b7b70bc051806bc3669ab1cbc56216b23c"}, - {file = "tomli-2.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ec9bfaf3ad2df51ace80688143a6a4ebc09a248f6ff781a9945e51937008fcbc"}, - {file = "tomli-2.4.1-cp311-cp311-win32.whl", hash = "sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049"}, - {file = "tomli-2.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:5ee18d9ebdb417e384b58fe414e8d6af9f4e7a0ae761519fb50f721de398dd4e"}, - {file = "tomli-2.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:c2541745709bad0264b7d4705ad453b76ccd191e64aa6f0fc66b69a293a45ece"}, - {file = "tomli-2.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c742f741d58a28940ce01d58f0ab2ea3ced8b12402f162f4d534dfe18ba1cd6a"}, - {file = "tomli-2.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7f86fd587c4ed9dd76f318225e7d9b29cfc5a9d43de44e5754db8d1128487085"}, - {file = "tomli-2.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9"}, - {file = "tomli-2.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5"}, - {file = "tomli-2.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5e262d41726bc187e69af7825504c933b6794dc3fbd5945e41a79bb14c31f585"}, - {file = "tomli-2.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5cb41aa38891e073ee49d55fbc7839cfdb2bc0e600add13874d048c94aadddd1"}, - {file = "tomli-2.4.1-cp312-cp312-win32.whl", hash = "sha256:da25dc3563bff5965356133435b757a795a17b17d01dbc0f42fb32447ddfd917"}, - {file = "tomli-2.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:52c8ef851d9a240f11a88c003eacb03c31fc1c9c4ec64a99a0f922b93874fda9"}, - {file = "tomli-2.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:f758f1b9299d059cc3f6546ae2af89670cb1c4d48ea29c3cacc4fe7de3058257"}, - {file = "tomli-2.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54"}, - {file = "tomli-2.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a"}, - {file = "tomli-2.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897"}, - {file = "tomli-2.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f"}, - {file = "tomli-2.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d"}, - {file = "tomli-2.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5"}, - {file = "tomli-2.4.1-cp313-cp313-win32.whl", hash = "sha256:2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd"}, - {file = "tomli-2.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36"}, - {file = "tomli-2.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd"}, - {file = "tomli-2.4.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fd0409a3653af6c147209d267a0e4243f0ae46b011aa978b1080359fddc9b6cf"}, - {file = "tomli-2.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a120733b01c45e9a0c34aeef92bf0cf1d56cfe81ed9d47d562f9ed591a9828ac"}, - {file = "tomli-2.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:559db847dc486944896521f68d8190be1c9e719fced785720d2216fe7022b662"}, - {file = "tomli-2.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01f520d4f53ef97964a240a035ec2a869fe1a37dde002b57ebc4417a27ccd853"}, - {file = "tomli-2.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7f94b27a62cfad8496c8d2513e1a222dd446f095fca8987fceef261225538a15"}, - {file = "tomli-2.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ede3e6487c5ef5d28634ba3f31f989030ad6af71edfb0055cbbd14189ff240ba"}, - {file = "tomli-2.4.1-cp314-cp314-win32.whl", hash = "sha256:3d48a93ee1c9b79c04bb38772ee1b64dcf18ff43085896ea460ca8dec96f35f6"}, - {file = "tomli-2.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:88dceee75c2c63af144e456745e10101eb67361050196b0b6af5d717254dddf7"}, - {file = "tomli-2.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:b8c198f8c1805dc42708689ed6864951fd2494f924149d3e4bce7710f8eb5232"}, - {file = "tomli-2.4.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:d4d8fe59808a54658fcc0160ecfb1b30f9089906c50b23bcb4c69eddc19ec2b4"}, - {file = "tomli-2.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7008df2e7655c495dd12d2a4ad038ff878d4ca4b81fccaf82b714e07eae4402c"}, - {file = "tomli-2.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1d8591993e228b0c930c4bb0db464bdad97b3289fb981255d6c9a41aedc84b2d"}, - {file = "tomli-2.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:734e20b57ba95624ecf1841e72b53f6e186355e216e5412de414e3c51e5e3c41"}, - {file = "tomli-2.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8a650c2dbafa08d42e51ba0b62740dae4ecb9338eefa093aa5c78ceb546fcd5c"}, - {file = "tomli-2.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:504aa796fe0569bb43171066009ead363de03675276d2d121ac1a4572397870f"}, - {file = "tomli-2.4.1-cp314-cp314t-win32.whl", hash = "sha256:b1d22e6e9387bf4739fbe23bfa80e93f6b0373a7f1b96c6227c32bef95a4d7a8"}, - {file = "tomli-2.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:2c1c351919aca02858f740c6d33adea0c5deea37f9ecca1cc1ef9e884a619d26"}, - {file = "tomli-2.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:eab21f45c7f66c13f2a9e0e1535309cee140182a9cdae1e041d02e47291e8396"}, - {file = "tomli-2.4.1-py3-none-any.whl", hash = "sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe"}, - {file = "tomli-2.4.1.tar.gz", hash = "sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f"}, + {file = "tomli-2.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c4dc1c1781f2f716de763d1e9a7b34c6a894e167e291c7c5d16c72f7a9538545"}, + {file = "tomli-2.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:eff8babca5a7999bc137acbc7482a8b7e17ffca5075ab41f5d770ab408c7bfef"}, + {file = "tomli-2.5.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:86665cee9c4835b7a7f1e8ec2c719b5258d4dc782887aded5a8ae7352a96843b"}, + {file = "tomli-2.5.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d7e369fd63331746182360977b1892bfc215476a30d61612d732425311639f56"}, + {file = "tomli-2.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7ad1ea345759240d6463efa0ed1c704402752e49aa21476620738d74d72d8aa1"}, + {file = "tomli-2.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:96243987194634bd411066ce40c952e108f86af04db533ecd8ac3ff2a85b1885"}, + {file = "tomli-2.5.0-cp311-cp311-win32.whl", hash = "sha256:610b27d99f28ec5f191c7064a48f3ddb179a1fe6ca73d571483ae859f57b605e"}, + {file = "tomli-2.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:c804ae44fe7b4bab5da295e4f980a1ff04670bca9d23fe0a4e887e08ebd741a8"}, + {file = "tomli-2.5.0-cp311-cp311-win_arm64.whl", hash = "sha256:cfac177ebd6236003846ea339981f71457cb6eb748f23381eb257e45092e3980"}, + {file = "tomli-2.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:1f4a40d03fb9f63424f0979855bdeaf44dd7696b8d59501822c10ed30ba532df"}, + {file = "tomli-2.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9ebf8d19b17bd0daeb7b7dec81a946a439b753942fd0210d6e96c532249eea6b"}, + {file = "tomli-2.5.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf0b5e8e0f68ebb494356e577c06c139161efd8d3b9050f93b39b7c26cc54ff0"}, + {file = "tomli-2.5.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6cf74416bdc94ae458b14e37286c1073081850ac8459a00d0c5efef5d44294c6"}, + {file = "tomli-2.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:61ea1ebe1e55a34ea8199cc8dbff398d35027b82271c8ac4802fd3a1fd5b1bcc"}, + {file = "tomli-2.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ed53f7e89bb04f6d9e8e7799112360b0c4d5cbff067de0814c98c37c39b920f7"}, + {file = "tomli-2.5.0-cp312-cp312-win32.whl", hash = "sha256:e7ad033e27a516a233bea839cdb77b80146facb3b4f40bf02cd0cac165cdd5c2"}, + {file = "tomli-2.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:bd05de8c1698f8413dd7d869492693a0bf2211543b787ac78cd5e7536af1a6d7"}, + {file = "tomli-2.5.0-cp312-cp312-win_arm64.whl", hash = "sha256:069435bd5480429b98c5e5afb02ab21c219b6f0064680671c6dc0d46817346ea"}, + {file = "tomli-2.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:943276cf269e0071948d9ff697159c1735e623c1151d88abb09b74659ef0cbea"}, + {file = "tomli-2.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:463b16086865b97facd8d0b3fb4cb7c544e3f58d2a69dc3113d6db9653fdb043"}, + {file = "tomli-2.5.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1245a6638fc4bb0a60af38a7d45413db34a13842027c77597c712c998c62fdf0"}, + {file = "tomli-2.5.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5d8bac3d603c97e6854424e5b2b5b741bdbde387e09f162fb0446812b4a8362b"}, + {file = "tomli-2.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:21e4cae4114aba25aa0d4f85cdf486d290fb35c0954d7bba536248da64d43066"}, + {file = "tomli-2.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:bbaefc84548d754be821bba7c4141c4787dda182f9e77f2f87b71213529efa7b"}, + {file = "tomli-2.5.0-cp313-cp313-win32.whl", hash = "sha256:abdbf6313b8d9efe157edeb7ab6eae4de064b1300ad31abf73755154b30abe68"}, + {file = "tomli-2.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:fd4dc129784e0c5335bd4e61dfcc4487499a013419e655cf2da1d091b7e0efdc"}, + {file = "tomli-2.5.0-cp313-cp313-win_arm64.whl", hash = "sha256:69491c143d2fe063046e0301e62a810bed338fa4d1ce0fd870c27dc1e09b0d84"}, + {file = "tomli-2.5.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:d3182ee2d887e507bd67319a0a61105d1dd33facc111329559a233b772c1a105"}, + {file = "tomli-2.5.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:521345fd1f19d45b8df87657aaa38b6f2ca3800059fadf428e7ebf479a383646"}, + {file = "tomli-2.5.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e95c7614e705bfe2b04b27aa124adec59752d15813df37e2156747cab3a006b"}, + {file = "tomli-2.5.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7ac2027d37c3afbdf4bdd377f2676f6f1d2122a5be1f1137b49dced590b37e75"}, + {file = "tomli-2.5.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c414be4ed9d3cac80c42e348fa5a956117d1a48227f48026e31f59cb4a7671eb"}, + {file = "tomli-2.5.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9b03d7dc168353b4132965bde20feceabaa470e570c6f59660dfae59b1f9eeb3"}, + {file = "tomli-2.5.0-cp314-cp314-win32.whl", hash = "sha256:6f041843c4d3a37245c0c056fd955b186bf8b1fb85690cbe40b81230891dc34b"}, + {file = "tomli-2.5.0-cp314-cp314-win_amd64.whl", hash = "sha256:f4b653094e18f9031102d3a1da5c729c8f222d85225b18037dac621695e46e1a"}, + {file = "tomli-2.5.0-cp314-cp314-win_arm64.whl", hash = "sha256:3f89d10c1ff6a38d992c27fc8a4816af71a909e08a40ec66934240b1e74347c3"}, + {file = "tomli-2.5.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:e9e15b4a6c7dd6b85b5fbab29488a73f1f70de516942308daa266bf0e0aeb0d4"}, + {file = "tomli-2.5.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e12bbcd32897272fb05929110362ae9ff4c1b9bb26bd9e971e71dcd3275b4c3d"}, + {file = "tomli-2.5.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:20aa36de8f2cf87237143bc1fa1aae8d6612c09118f4da21c6a684db5dd1f6f9"}, + {file = "tomli-2.5.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22185fad8a1e622f064e78008018a0dd3323550dcb479cb7a1d296888d74024f"}, + {file = "tomli-2.5.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:984012f71908165449a951de2050d52f276bfe3aa5d5f570f63ddad814370374"}, + {file = "tomli-2.5.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f79203b3965b4000e91808aaa7c040206093f2b8bf86f455982f2274c9ccf442"}, + {file = "tomli-2.5.0-cp314-cp314t-win32.whl", hash = "sha256:91294a9fb94a75542f6e46e4a2ae709bd8d9b51134098cae5cf3bea5478b6d03"}, + {file = "tomli-2.5.0-cp314-cp314t-win_amd64.whl", hash = "sha256:f15e3e0b835a6d68b10c86bf80a3149780498d6911c93c3ffd1861d19f9200f1"}, + {file = "tomli-2.5.0-cp314-cp314t-win_arm64.whl", hash = "sha256:6664b7ae7af7294256c53960a6103077f4914cec8ff98479c352f622c6f6b2f0"}, + {file = "tomli-2.5.0-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:a525685c2f97da40762b8695eb7aa0af4c8344ca1905c73e4e29cb04d34607dc"}, + {file = "tomli-2.5.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:9dbb18c1cfb2f6517942fc9314437f66aa06d94436ffb1f06102ef3572f35276"}, + {file = "tomli-2.5.0-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:752e8b1aa6a4367ef8bf6a1a1e005540f7ed055ba36d7193796812ca5404eb52"}, + {file = "tomli-2.5.0-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c47300f9bf791808f77d82747691c4bb09cb14bdf3060cca99b42cdc4361d5a7"}, + {file = "tomli-2.5.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:19b0dd8749f4ea2f112c5fcfb3c5248390c899d7e2e173f1d91abee1fa0ff391"}, + {file = "tomli-2.5.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:57b1c3b01fab802e2899bc3d168dca320e14165e2fd9fd584760fb4ca5826859"}, + {file = "tomli-2.5.0-cp315-cp315-win32.whl", hash = "sha256:667e521b37a6c5ccaa044202c235b530f90177ffe2cd4a64ecc213c7dd535feb"}, + {file = "tomli-2.5.0-cp315-cp315-win_amd64.whl", hash = "sha256:d747252933c8a65ef6bd8da0fbb7ce28a90eb6119d8cd00772cd528aa07b68d5"}, + {file = "tomli-2.5.0-cp315-cp315-win_arm64.whl", hash = "sha256:75dbcde8751b0a960aa3de173aa5e894d590755c6d7758b7e774c06f1dc3cbdd"}, + {file = "tomli-2.5.0-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:2419c2a189551987b59d80e63ec355671283336f41c6b9b89462df679c7d0c57"}, + {file = "tomli-2.5.0-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:0dc598040da8d42cf20f0be588ed7004f46db12a0ac6c32e03a59dccedaaadcd"}, + {file = "tomli-2.5.0-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49096930c8d886c9bbdab62d2d0d17ce823ddeea522309a190b36245d5b49e01"}, + {file = "tomli-2.5.0-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8ade5023067f99fe72b88accd30d0ea05a158e9e32a11f124e731ea9695313f"}, + {file = "tomli-2.5.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:b69564772b5c8f22ea5f498dff08cfa825045b4d4c4400529000bdf818aa3b2a"}, + {file = "tomli-2.5.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:8ff3a2ca028c7eee0c777f9a092038d0a594a9fa04e215f929a22c329e2cb142"}, + {file = "tomli-2.5.0-cp315-cp315t-win32.whl", hash = "sha256:62fc1bc8eb03e3a9cadfca713d65614ed8e09d974a283295ffe3a831976b4dc5"}, + {file = "tomli-2.5.0-cp315-cp315t-win_amd64.whl", hash = "sha256:f3fcbc57b1791fa6cbe5d8434179d51de12be1a4811469529f47f6e7487a2571"}, + {file = "tomli-2.5.0-cp315-cp315t-win_arm64.whl", hash = "sha256:d2ba24db8a9376921b5e87b4762b9adb0f3f1deaea68f2b8b0bb2c11efb9c3e7"}, + {file = "tomli-2.5.0-py3-none-any.whl", hash = "sha256:32a7b79ac57a2e83670ce329ccf675798bc5a2094783a63676866b70503f2e2b"}, + {file = "tomli-2.5.0.tar.gz", hash = "sha256:264507556cd8b8c8e7c6ee037cdf443a463f03f4c958e57195e3d369711b8ff6"}, ] [[package]] @@ -1526,14 +1603,14 @@ files = [ [[package]] name = "types-requests" -version = "2.33.0.20260906" +version = "2.33.0.20261006" description = "Typing stubs for requests" optional = false python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "types_requests-2.33.0.20260906-py3-none-any.whl", hash = "sha256:9f53622652bd921ead7a54d665be1b8d518165c8b51cc9d68d944cd6b6bfa8fb"}, - {file = "types_requests-2.33.0.20260906.tar.gz", hash = "sha256:76ab8a0fb736744a0c3deee7aa57b2927e301f078d9e61f5391b3e92002416b9"}, + {file = "types_requests-2.33.0.20261006-py3-none-any.whl", hash = "sha256:26cc8146505cab33cda9737991929e4144c559bebe05078ccc6998f27c4ca2c1"}, + {file = "types_requests-2.33.0.20261006.tar.gz", hash = "sha256:0652999e9306aea345f40732d58fa49a7f6cade6a0d74d92119c5c8d82eddaf0"}, ] [package.dependencies] @@ -1568,14 +1645,14 @@ typing-extensions = ">=4.15.0" [[package]] name = "urllib3" -version = "2.7.0" +version = "2.8.0" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897"}, - {file = "urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c"}, + {file = "urllib3-2.8.0-py3-none-any.whl", hash = "sha256:0cf3cae568d36aa9576b28dfb35f11328f1cb974ca7647d9475ebb86c75ac6e3"}, + {file = "urllib3-2.8.0.tar.gz", hash = "sha256:63bf2ead4c879426ebf22ef2a781eeb4aa3b4ae798a0435506f8687fd5bb9b63"}, ] [package.extras] @@ -1586,117 +1663,151 @@ zstd = ["backports-zstd (>=1.0.0) ; python_version < \"3.14\""] [[package]] name = "yarl" -version = "1.24.5" +version = "1.25.1" description = "Yet another URL library" optional = true python-versions = ">=3.10" groups = ["main"] markers = "extra == \"aiohttp\"" files = [ - {file = "yarl-1.24.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:88f50c94e21a0a7f14042c015b0eba1881af78562e7bf007e0033e624da59750"}, - {file = "yarl-1.24.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6efbccc3d7f75d5b03105172a8dc86d82ba4da86817952529dd93185f4a88be2"}, - {file = "yarl-1.24.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0ebfaffe1a16cb72141c8e09f18cc76856dbe58639f393a4f2b26e474b96b871"}, - {file = "yarl-1.24.5-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8ac73abdc7ab75610f95a8fd994c6457e87752b02a63987e188f937a1fc180f0"}, - {file = "yarl-1.24.5-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4d97a951a81039050e45f04e96689b58b8243fa5e62aa14fe67cb6075300885e"}, - {file = "yarl-1.24.5-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:fe7b7bb170daccbba19ad33012d2b15f1e7942296fd4d45fc1b79013da8cc0f2"}, - {file = "yarl-1.24.5-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:89a1bbb58e0e3f7a283653d854b1e95d65e5cfd4af224dac5f02629ec1a3e621"}, - {file = "yarl-1.24.5-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7fa5e51397466ea7e98de493fa2ff1b8193cfef8a7b0f9b4842f92d342df0dba"}, - {file = "yarl-1.24.5-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4103b77b8a8225e413107d2349b65eb3c1c52627b5cc5c3c4c1c6a798b218950"}, - {file = "yarl-1.24.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f9f3e9c8a9ecffa57bef8fb4fa19e5fa4d2d8307cf6bac5b1fca5e5860f4ba00"}, - {file = "yarl-1.24.5-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:c0ebc836c47a6477e182169c6a476fc691d12b518894bf7dd2572f0d59f1c7ed"}, - {file = "yarl-1.24.5-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:96d30286dd02679e32a39aa8f0b7498fc847fcda46cfc09df5513e82ce252440"}, - {file = "yarl-1.24.5-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:fd8c81f346b58f45818d09ea11db69a8d5fd34a224b79871f6d44f12cd7977b1"}, - {file = "yarl-1.24.5-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:5c55256dee8f4b27bfbf636c8363383c7c8db7890c7cba5217d7bd5f5f21dab6"}, - {file = "yarl-1.24.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9f4d8cf085a4c6a40fb97ea0f46938a8df43c85d31f9d45e2a8867ea9293790d"}, - {file = "yarl-1.24.5-cp310-cp310-win_amd64.whl", hash = "sha256:240cbec09667c1fed4c6cd0060b9ec57332427d7441289a2ed8875dc9fb2b224"}, - {file = "yarl-1.24.5-cp310-cp310-win_arm64.whl", hash = "sha256:8a6987eaad834cb32dd57d9d582225f0054a5d1af706ccfbbdba735af4927e13"}, - {file = "yarl-1.24.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2c1fe720934a16ea8e7146175cba2126f87f54912c8c5435e7f7c7a51ef808d3"}, - {file = "yarl-1.24.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c687ed078e145f5fd53a14854beff320e1d2ab76df03e2009c98f39a0f68f39a"}, - {file = "yarl-1.24.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:709f1efed56c4a145793c046cd4939f9959bcd818979a787b77d8e09c57a0840"}, - {file = "yarl-1.24.5-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:874019bd513008b009f58657134e5d0c5e030b3559bd0553976837adf52fe966"}, - {file = "yarl-1.24.5-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a4582acf7ef76482f6f511ebaf1946dae7f2e85ec4728b81a678c01df63bd723"}, - {file = "yarl-1.24.5-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2cabe6546e41dabe439999a23fcb5246e0c3b595b4315b96ef755252be90caeb"}, - {file = "yarl-1.24.5-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:17f57620f5475b3c69109376cc87e42a7af5db13c9398e4292772a706ff10780"}, - {file = "yarl-1.24.5-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:570fec8fbd22b032733625f03f10b7ff023bc399213db15e72a7acaef28c2f4e"}, - {file = "yarl-1.24.5-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5fede79c6f73ff2c3ef822864cb1ada23196e62756df53bc6231d351a49516a2"}, - {file = "yarl-1.24.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8ccf9aca873b767977c73df497a85dbedee4ee086ae9ae49dc461333b9b79f58"}, - {file = "yarl-1.24.5-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:ad5d8201d310b031e6cd839d9bac2d4e5a01533ce5d3d5b50b7de1ef3af1de61"}, - {file = "yarl-1.24.5-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:841f0852f48fefea3b12c9dfec00704dfa3aef5215d0e3ce564bb3d7cd8d57c6"}, - {file = "yarl-1.24.5-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:9baafc71b04f8f4bb0703b21d6fc9f0c30b346c636a532ff16ec8491a5ea4b1f"}, - {file = "yarl-1.24.5-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:d897129df1a22b12aeed2c2c98df0785a2e8e6e0bde87b389491d0025c187077"}, - {file = "yarl-1.24.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:dd625535328fd9882374356269227670189adfcc6a2d90284f323c05862eecbd"}, - {file = "yarl-1.24.5-cp311-cp311-win_amd64.whl", hash = "sha256:f4239bbec5a3577ddb49e4b50aeb32d8e5792098262ae2f63723f916a29b1a25"}, - {file = "yarl-1.24.5-cp311-cp311-win_arm64.whl", hash = "sha256:3ac6aff147deb9c09461b2d4bbdf6256831198f5d8a23f5d37138213090b6d8a"}, - {file = "yarl-1.24.5-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d693396e5aea78db03decd60aec9ece16c9b40ba00a587f089615ff4e718a81d"}, - {file = "yarl-1.24.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3363fcc96e665878946ad7a106b9a13eac0541766a690ef287c0232ac768b6ec"}, - {file = "yarl-1.24.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9d399bdcfb4a0f659b9b3788bbc89babe63d9a6a65aacdf4d4e7065ff2e6316c"}, - {file = "yarl-1.24.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:90333fd89b43c0d08ac85f3f1447593fc2c66de18c3d6378d7125ea118dc7a54"}, - {file = "yarl-1.24.5-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:665b0a2c463cc9423dd647e0bfd9f4ccc9b50f768c55304d5e9f80b177c1de12"}, - {file = "yarl-1.24.5-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e006d3a974c4ee19512e5f058abedb6eef36a5e553c14812bdeba1758d812e6d"}, - {file = "yarl-1.24.5-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e7d42c531243450ef0d4d9c172e7ed6ef052640f195629065041b5add4e058d1"}, - {file = "yarl-1.24.5-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f08c7513ecef5aad65687bfdf6bc601ae9fccd04a42904501f8f7141abad9eb9"}, - {file = "yarl-1.24.5-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6c95b17fe34ed802f17e205112e6e10db92275c34fee290aa9bdc55a9c724027"}, - {file = "yarl-1.24.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:56b149b22de33b23b0c6077ab9518c6dcb538ad462e1830e68d06591ccf6e38b"}, - {file = "yarl-1.24.5-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:a8fe66b8f300da93798025a785a5b90b42f3810dc2b72283ff84a41aaaebc293"}, - {file = "yarl-1.24.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:377fe3732edbaf78ee74efdf2c9f49f6e99f20e7f9d2649fda3eb4badd77d76e"}, - {file = "yarl-1.24.5-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:e8ffa78582120024f476a611d7befc123cee59e47e8309d470cf667d806e613b"}, - {file = "yarl-1.24.5-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:daba5e594f06114e37db186efd2dd916609071e59daca901a0a2e71f02b142ce"}, - {file = "yarl-1.24.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:65be18ec59496c13908f02a2472751d9ef840b4f3fb5726f129306bf6a2a7bba"}, - {file = "yarl-1.24.5-cp312-cp312-win_amd64.whl", hash = "sha256:a929d878fec099030c292803b31e5d5540a7b6a31e6a3cc76cb4685fc2a2f51b"}, - {file = "yarl-1.24.5-cp312-cp312-win_arm64.whl", hash = "sha256:7ce27823052e2013b597e0c738b13e7e36b8ccb9400df8959417b052ab0fd92c"}, - {file = "yarl-1.24.5-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:79af890482fc94648e8cde4c68620378f7fef60932710fa17a66abc039244da2"}, - {file = "yarl-1.24.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:46c2f213e23a04b93a392942d782eb9e413e6ef6bf7c8c53884e599a5c174dcb"}, - {file = "yarl-1.24.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92ab3e11448f2ff7bf53c5a26eff0edc086898ec8b21fb154b85839ce1d88075"}, - {file = "yarl-1.24.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ebb0ec7f17803063d5aeb982f3b1bd2b2f4e4fae6751226cbd6ba1fcfe9e63ff"}, - {file = "yarl-1.24.5-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:82632daed195dcc8ea664e8556dc9bdbd671960fb3776bd92806ce05792c2448"}, - {file = "yarl-1.24.5-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:53e549287ef628fecba270045c9701b0c564563a9b0577d24a4ec75b8ab8040f"}, - {file = "yarl-1.24.5-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fcd3b77e2f17bbe4ca56ec7bcb07992647d19d0b9c05d84886dcd6f9eb810afd"}, - {file = "yarl-1.24.5-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d46b86567dd4e248c6c159fcbcdcce01e0a5c8a7cd2334a0fff759d0fa075b16"}, - {file = "yarl-1.24.5-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7f72c74aa99359e27a2ee8d6613fefa28b5f76a983c083074dfc2aaa4ab46213"}, - {file = "yarl-1.24.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3f45789ce415a7ec0820dc4f82925f9b5f7732070be1dec1f5f23ec381435a24"}, - {file = "yarl-1.24.5-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:6e73e7fe93f17a7b191f52ec9da9dd8c06a8fe735a1ecbd13b97d1c723bff385"}, - {file = "yarl-1.24.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4a36f9becdd4c5c52a20c3e9484128b070b1dcfc8944c006f3a528295a359a9c"}, - {file = "yarl-1.24.5-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:7bcbe0fcf850eae67b6b01749815a4f7161c560a844c769ad7b48fcd99f791c4"}, - {file = "yarl-1.24.5-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:24e861e9630e0daddcb9191fb187f60f034e17a4426f8101279f0c475cd74144"}, - {file = "yarl-1.24.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9335a099ad87287c37fe5d1a982ff392fa5efe5d14b40a730b1ec1d6a41382b4"}, - {file = "yarl-1.24.5-cp313-cp313-win_amd64.whl", hash = "sha256:2dbe06fc16bc91502bca713704022182e5729861ae00277c3a23354b40929740"}, - {file = "yarl-1.24.5-cp313-cp313-win_arm64.whl", hash = "sha256:6b8536851f9f65e7f00c7a1d49ba7f2be0ffe2c11555367fc9f50d9f842410a1"}, - {file = "yarl-1.24.5-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:2729fcfc4f6a596fb0c50f32090400aa9367774ac296a00387e65098c0befa76"}, - {file = "yarl-1.24.5-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:ff330d3c30db4eb6b01d79e29d2d0b407a7ecad39cfd9ec993ece57396a2ec0d"}, - {file = "yarl-1.24.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:e42d75862735da90e7fc5a7b23db0c976f737113a54b3c9777a9b665e9cbff75"}, - {file = "yarl-1.24.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a3732e66413163e72508da9eff9ce9d2846fde51fae45d3605393d3e6cd303e9"}, - {file = "yarl-1.24.5-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5b8ee53be440a0cffc991a27be3057e0530122548dbe7c0892df08822fce5ede"}, - {file = "yarl-1.24.5-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:af3aefa655adb5869491fa907e652290386800ae99cc50095cba71e2c6aefdca"}, - {file = "yarl-1.24.5-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2120b96872df4a117cde97d270bac96aea7cc52205d305cf4611df694a487027"}, - {file = "yarl-1.24.5-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:66410eb6345d467151934b49bfa70fb32f5b35a6140baa40ad97d6436abea2e9"}, - {file = "yarl-1.24.5-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4af7b7e1be0a69bee8210735fe6dcfc38879adfac6d62e789d53ba432d1ffa41"}, - {file = "yarl-1.24.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fa139875ff98ab97da323cfadfaff08900d1ad42f1b5087b0b812a55c5a06373"}, - {file = "yarl-1.24.5-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:0055afc45e864b92729ac7600e2d102c17bef060647e74bca75fa84d66b9ff36"}, - {file = "yarl-1.24.5-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:f0e466ed7511fe9d459a819edbc6c2585c0b6eabde9fa8a8947552468a7a6ef0"}, - {file = "yarl-1.24.5-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:f141474e85b7e54998ec5180530a7cda99ab29e282fa50e0756d89981a9b43c5"}, - {file = "yarl-1.24.5-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:e2935f8c39e3b03e83519292d78f075189978f3f4adc15a78144c7c8e2a1cba5"}, - {file = "yarl-1.24.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9d1216a7f6f77836617dba35687c5b78a4170afc3c3f18fc788f785ba26565c4"}, - {file = "yarl-1.24.5-cp314-cp314-win_amd64.whl", hash = "sha256:5ba4f78df2bcc19f764a4b26a8a4f5049c110090ad5825993aacb052bf8003ad"}, - {file = "yarl-1.24.5-cp314-cp314-win_arm64.whl", hash = "sha256:9e4e16c73d717c5cf27626c524d0a2e261ad20e46932b2670f64ad5dde23e26f"}, - {file = "yarl-1.24.5-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:e1ae548a9d901adca07899a4147a7c826bbcc06239d3ce9a59f57886a28a4c88"}, - {file = "yarl-1.24.5-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:ff405d91509d88e8d44129cd87b18d70acd1f0c1aeabd7bc3c46792b1fe2acba"}, - {file = "yarl-1.24.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:47e98aab9d8d82ff682e7b0b5dded33bf138a32b817fcf7fa3b27b2d7c412928"}, - {file = "yarl-1.24.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f0a658a6d3fafee5c6f63c58f3e785c8c43c93fbc02bf9f2b6663f8185e0971f"}, - {file = "yarl-1.24.5-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4377407001ca3c057773f44d8ddd6358fa5f691407c1ba92210bd3cf8d9e4c95"}, - {file = "yarl-1.24.5-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7c0494a31a1ac5461a226e7947a9c9b78c44e1dc7185164fa7e9651557a5d9bc"}, - {file = "yarl-1.24.5-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a7cff474ab7cd149765bb784cf6d78b32e18e20473fb7bda860bce98ab58e9da"}, - {file = "yarl-1.24.5-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cbb833ccacdb5519eff9b8b71ee618cc2801c878e77e288775d77c3a2ced858a"}, - {file = "yarl-1.24.5-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:82f75e05912e84b7a0fe57075d9c59de3cb352b928330f2eb69b2e1f54c3e1f0"}, - {file = "yarl-1.24.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:16a2f5010280020e90f5330257e6944bc33e73593b136cc5a241e6c1dc292498"}, - {file = "yarl-1.24.5-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:ffcd54362564dc1a30fb74d8b8a6e5a6b11ebd5e27266adc3b7427a21a6c9104"}, - {file = "yarl-1.24.5-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0465ec8cedc2349b97a6b595ace64084a50c6e839eca40aa0626f38b8350e331"}, - {file = "yarl-1.24.5-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:4db9aecb141cb7a5447171b57aa1ed3a8fee06af40b992ffc31206c0b0121550"}, - {file = "yarl-1.24.5-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:f540c013589084679a6c7fac07096b10159737918174f5dfc5e11bf5bca4dfe6"}, - {file = "yarl-1.24.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a61834fb15d81322d872eaafd333838ae7c9cea84067f232656f75965933d047"}, - {file = "yarl-1.24.5-cp314-cp314t-win_amd64.whl", hash = "sha256:5c88e5815a49d289e599f3513aa7fde0bc2092ff188f99c940f007f90f53d104"}, - {file = "yarl-1.24.5-cp314-cp314t-win_arm64.whl", hash = "sha256:cf139c02f5f23ef6532040a30ff662c00a318c952334f211046b8e60b7f17688"}, - {file = "yarl-1.24.5-py3-none-any.whl", hash = "sha256:a33700d13d9b7d84fd10947b09ff69fb9a792e519c8cb9764a3ca70baa6c23a7"}, - {file = "yarl-1.24.5.tar.gz", hash = "sha256:e81b83143bee16329c23db3c1b2d82b29892fcbcb849186d2f6e98a5abe9a57f"}, + {file = "yarl-1.25.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:142c06c4d6a35ee3ec5da08499805e879cb3ca7c1fbfbecb0140fe72403818d6"}, + {file = "yarl-1.25.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:24ce942011a61953e7d313438038f4d32ff21387b775f58a957f7a07dd55ef95"}, + {file = "yarl-1.25.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e23c82b63cd7652fc24d33ed6cc17099d607aa3b4fc4ddc75e95062f3d82df4"}, + {file = "yarl-1.25.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8ee202350cf57abf0e9502a41601841019c25d3db7ff52d980aaf31446254059"}, + {file = "yarl-1.25.1-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5df89f769cc8ff94c3d7e7603386fba309d25ce5240132d26c15baa8d0e96c4c"}, + {file = "yarl-1.25.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:83e9f4a25085bd4b7214701a0794ff1f50fc633ffb8bdfebf07abdd81c2db126"}, + {file = "yarl-1.25.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e636b64d24fd9c38053c5e389a1174c66361fa49dcfd220f4dd35b4abde7cb89"}, + {file = "yarl-1.25.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e5637ca8d0bd7fb72648a6c7934af4baaccb697657f7438c9d264fc2abb8b0b1"}, + {file = "yarl-1.25.1-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:683e362b8ba453080f7489c66f4ea794e751c35b72e7eab3575ef784c2fbc7fb"}, + {file = "yarl-1.25.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:df23df54b5114a17c2d0ef192433e2e5a9f0c5178c32375e90b7cfc965f349d0"}, + {file = "yarl-1.25.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:f53dcd26694f148f738edc052b5a69234833e739f10f4c3287bdfd8ec0f7b326"}, + {file = "yarl-1.25.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:b8075fe90bc08e40b8b8a1874fab42ee4c7b56af05c5886e9cc841397f916908"}, + {file = "yarl-1.25.1-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:a8c2b841478068440d8b733005d13a5ef535b9928cbc05f17182d410f32ba449"}, + {file = "yarl-1.25.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:ca32926d7d77bcc8838425c4c95e040a3ace1cb7dfdae599013458dcda2607ca"}, + {file = "yarl-1.25.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:192a866877a49993949ef1975864ad8728bea28ee810f6abe1a0729c2b500426"}, + {file = "yarl-1.25.1-cp310-cp310-win_amd64.whl", hash = "sha256:3f4d48a6112712973e676bd792121fee470e432d749177162d9949d5c9460a1b"}, + {file = "yarl-1.25.1-cp310-cp310-win_arm64.whl", hash = "sha256:48796ea00a303961507dc6c8437c4b325a6fc3f95f7c36c71b91ea9a8150963c"}, + {file = "yarl-1.25.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9d693bf4bf534e9ba3ae2780cfd577f5135629f7b5ac653490859d0b77864865"}, + {file = "yarl-1.25.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ab2054c5531af2a9ba7b69b8ec91e4f884420e83a8c5e579b013084cb57e5e5d"}, + {file = "yarl-1.25.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:564fdc7085d2245ab84f88882fdb1d6ac0723124bff6ded35bfb1c00f812630d"}, + {file = "yarl-1.25.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:acae6b45d1ace09b6ba3876da43b88366ef368f73b988c7f57e14231753d4420"}, + {file = "yarl-1.25.1-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:1fb2a01ba8cd9c5d2c5dc1ec35e0fc951d04b4f037541d4ac090c993ce58b3d7"}, + {file = "yarl-1.25.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e92b6bcc741b86d67606c40d3cb9c7cc8e6c737f81e31f4a94efc204456c92e3"}, + {file = "yarl-1.25.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:72c34ac7ad4314c19362d5ce27626dcc8429bd30bbf8c179f4234078851f9492"}, + {file = "yarl-1.25.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d5add7b4ca7afeea91d52e4d4e4db3b1fe9885b71f07054560d8c4296b7441a2"}, + {file = "yarl-1.25.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:def538065f9e4d4cf1ae164bd59aba00dfa84f03923e0de4c3788f252d6bcd17"}, + {file = "yarl-1.25.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0a191bfdb30a79b98e5d175d75285f9fcb78bf0e46ba5efda042e1c72071a0de"}, + {file = "yarl-1.25.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:71f42c5b9a948c113bbdebfa544598321431d064ff959d32e99b1feb61d68345"}, + {file = "yarl-1.25.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:72849d892954be4d09e569b8b831ac39ce58417fedc767d4308a0fe542018a40"}, + {file = "yarl-1.25.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:efb01a106f971cb3752856bca2318bbdf7f01bd8823779c461586cbe5ffd5258"}, + {file = "yarl-1.25.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:a1daf47cd95a7c3a63456336bc5aaa8c86dd3a47d07ed3d0e76132ae4666a5a1"}, + {file = "yarl-1.25.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9489e6abf47ba37f332075a91444c7cfedb03e6ce99fbb2f116bfe1ce810da3b"}, + {file = "yarl-1.25.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7306dee25b8a0e737363f347362b875094b4dc4e367311470656ae420fdbf8e"}, + {file = "yarl-1.25.1-cp311-cp311-win_arm64.whl", hash = "sha256:abb1384477f5901d436b5d2e5465954de46ea6098f59163d243660b5c4461d35"}, + {file = "yarl-1.25.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:94d7aa6debf92a1dd14cb5280b083a764169a13cfb23a452111160274ed989f4"}, + {file = "yarl-1.25.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:83d4a37e4b95da4d8bda930d6d35b75b4cdadbacbb4980cae290ea3100b5d51d"}, + {file = "yarl-1.25.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e029648f9c951db30e98a7d7ec90835db88ec4b32820efe2a9bdc2287e032eb6"}, + {file = "yarl-1.25.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4d781294bb815ecb5ea57ff6bbf8038e0a31a95fdf3e1788f66e0dc100d64b58"}, + {file = "yarl-1.25.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e12c538e00e7c1b286a07061046b90e8124e6a9793efae2c70db6a4aad07faad"}, + {file = "yarl-1.25.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7e4de3ac4adbad3d0bc7c6f4360a7dbff5de2f15e3b723be3198074e17fd9c40"}, + {file = "yarl-1.25.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:419f392a1da624877975709e3864dfe833af6cc7671b39318086d456e288380c"}, + {file = "yarl-1.25.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c6f117789d22dce188e5754e8bc65b7e6ebf8cb73963b9fa761f672a5883769d"}, + {file = "yarl-1.25.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:80e47012e730da131c9f059c80936783f9659aae22dc31c03c0595590d11ed54"}, + {file = "yarl-1.25.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e80f557716fd765439577131e526b8942ffc2c07bdbc5e39fa62f660ba1e963f"}, + {file = "yarl-1.25.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:f61964f235a43738bfac50da46fc4254943a7eea3051aeb0b6fc7c992c29fadc"}, + {file = "yarl-1.25.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:e546fe1d4a93ebc2910f0d768baff19faa09843ab3f2036a67ed6e69fae4419d"}, + {file = "yarl-1.25.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:cce0727fd5ac04d372fa9bbfde9febc2bcf209aadfcf0468e45dec72719895d1"}, + {file = "yarl-1.25.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af4ea5b37403ef4e30f3927eaed540db942bde01d8d3ff083527c0704d1c9c68"}, + {file = "yarl-1.25.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:68782fdb4027b8d1eee25ec35e9a6db05e863b899eb0310b3a33b6c3fef55707"}, + {file = "yarl-1.25.1-cp312-cp312-win_amd64.whl", hash = "sha256:7d575b54cb3863ef9bc290ea4b009999d55dc237326131e4853cf33e888fee03"}, + {file = "yarl-1.25.1-cp312-cp312-win_arm64.whl", hash = "sha256:bc3ac7bf569f6b64dad04dd7808c7872dae8a97df657856eac05e9b7e3614a85"}, + {file = "yarl-1.25.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:25868beca8b6765f8f7d0e11fe6dd7c66dd4b0793b9500286d20cc92352126a5"}, + {file = "yarl-1.25.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:10b2fd95332f0d716d5eee3c9fb2ce8eada19082de7fee83d32e37992fd75c26"}, + {file = "yarl-1.25.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0f12afda4eea8c8994a76d4df1875c765194f5fbe8a9d197929ea303caee29ec"}, + {file = "yarl-1.25.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:14b79a30a93a3ce2e8832603fd0ab780ada281b0ba5110b519a634f2d7d7d1fc"}, + {file = "yarl-1.25.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4bd6340d20ae2c7ca719b87b426e808e90743b676d05d4c26c4fb5ca71f41184"}, + {file = "yarl-1.25.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:126a2533570c554719ca40a1288fdee1700b6bc82e7131aa69fa85252d92e651"}, + {file = "yarl-1.25.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a3faadac7d812ddac258feb57b9846b60c1b437c4f4b9ad42595c6f6fe4390df"}, + {file = "yarl-1.25.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:be80550d9bfe83d9b62398a37081a90434e6df2d978ec345c3d2820de6beddab"}, + {file = "yarl-1.25.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e07595c7d6f4db270ceede356a1bd1c07a34f1c26f958d1ed0cd7b48e0d2bba3"}, + {file = "yarl-1.25.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:eb96ed1ae6c7d072d60840c0434aef07a2df611812810807fbc54263a6053e9a"}, + {file = "yarl-1.25.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:3feb99222553a8cbedfa52c2f59dd84c3f50d5b582c728d522caf8d72769a54b"}, + {file = "yarl-1.25.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:a2ed0ba415ccdf08f14bf544cb78346d0f76086707ffee24921a2c84dbf1305a"}, + {file = "yarl-1.25.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:2b49375d22299b0a834c2bca72f39aaecc270d96fb24c30424899676f487b22a"}, + {file = "yarl-1.25.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:ef74070ac553c59eb4f04258722066d6c6135b7baa03b2e9f2da65c096e96d98"}, + {file = "yarl-1.25.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0a66db89ea473abeac4b70523cafd94db3772380e565f9d28af7a179b7af71fa"}, + {file = "yarl-1.25.1-cp313-cp313-win_amd64.whl", hash = "sha256:1f51020b2eb8a003c84925638ec63c21a750a4bddd3a22ec8eac6a742dadf1b9"}, + {file = "yarl-1.25.1-cp313-cp313-win_arm64.whl", hash = "sha256:b10dd0557ba422715b5206b3743192135a6022acca8baec51aa127d0a75db8fe"}, + {file = "yarl-1.25.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:a9ca696eb02e5c02a8afd872ada510eba9b7fe6e68b9572c2e9a9b1941e31e2e"}, + {file = "yarl-1.25.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a5877f2255aab518ebe528289037699201d5dc5f045f2396cb30aa02db22f57f"}, + {file = "yarl-1.25.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7a5c3115595995779ee21f2567035793911c3802a43c74f3fbb0314929ec67ac"}, + {file = "yarl-1.25.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:77e5099b99b37f3cf79c246998ca9f7313a78054cd1809ec46bc1afad47e1c4c"}, + {file = "yarl-1.25.1-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6efaf45df6a849cef613a03a94c845647456662f85438c886bb67a9c027c8c2c"}, + {file = "yarl-1.25.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d5f90e44653c4e0f78501ed9bb7d3fce835a8d62b7c6ed0cb16557534087e743"}, + {file = "yarl-1.25.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:632da579b2d879f6bad20f2cfa35ded1efe2f4f77f8abb26a6234a5b236acd2f"}, + {file = "yarl-1.25.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:30eec96e8a91bd588ce897c9543f6d5d8d34b28fbcba28a4dedf20ebeae9fe57"}, + {file = "yarl-1.25.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:12b6bc4906e11f5e1a1cdcb12296e7afbd366c783cc8073403cd2fb74334e453"}, + {file = "yarl-1.25.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9d6ed3d17bccce4c05343e1ca8da13bc5c02c812a4e7282ddd05e8769322d3fc"}, + {file = "yarl-1.25.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:f38a70074041d3b7e138e452799f5174198bae5bd5ab2000917badf403908c5f"}, + {file = "yarl-1.25.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:4ca89e4e21854ed27ec753297dde84b16c9f8e53b14a4866fb44457d643c19f8"}, + {file = "yarl-1.25.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:1ab7618921a93767387a4b83776f751588f5b5ae9bb5bc96620e2e2e00bca868"}, + {file = "yarl-1.25.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:0ae12ff2b805fa02c4dab838005caef735e39986322698c48588d3beacb65c62"}, + {file = "yarl-1.25.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:90c30ed53546da833c700115c0064c22120d1b1560f474699fd31f22dd668233"}, + {file = "yarl-1.25.1-cp314-cp314-win_amd64.whl", hash = "sha256:acfa7e22aa6c6e7a5996a41d275bfa01efa7ea56ab890590280e9063e2cf5c1b"}, + {file = "yarl-1.25.1-cp314-cp314-win_arm64.whl", hash = "sha256:8e7d98cdbb6d71e726f7d525952867096053d1f290dd4e3c50d7d313a136f414"}, + {file = "yarl-1.25.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:d21f0fa80a02d05299207eeaafef345d812ace96d5306e4ef265e1d419a615fa"}, + {file = "yarl-1.25.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:17c9877a89fb6e2bca6f9087eb24cd7fb434653946ef5075e470d23d49b52287"}, + {file = "yarl-1.25.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:29273edf1530e397bd07cb784db1fbe0d2590b77569f2e24679a9c0a2d763b94"}, + {file = "yarl-1.25.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b7abffdf37af1cec6a2ad69b827aa84320db5894791bc8ed932dc93fb274b7e9"}, + {file = "yarl-1.25.1-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2239a02249d9326655419e0168a28ca9008938eaab31dc29fc875c217927a6c0"}, + {file = "yarl-1.25.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:664ec6a520b74a1df2810666eb67695fcb77fa663e6ea0a25aaf2e529cb24dfa"}, + {file = "yarl-1.25.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4f1c91f5a5980a937ff8e238e98e6897e1ad74a4b1e2c0d68c73b5ffbb3f5c0b"}, + {file = "yarl-1.25.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7c88edaec8c349ad4c5ad4c486a3defcc4b80ceb2f074436ffa0a87caf5e76a6"}, + {file = "yarl-1.25.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:35dcbea443fafb3eece757ad4e514560ddeb6c34cfae1582c620d7b293d7feee"}, + {file = "yarl-1.25.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:882569ff613758cac762a457a5d72d6e211b28d4bcfea89d1d71ea942b02eac0"}, + {file = "yarl-1.25.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:d0f1489233a254bb3643d2f05de7d59019254d81daeca6b9162fe9edef57e0c7"}, + {file = "yarl-1.25.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:f41753a76f4f63927d03a0d8ba8f5ce0f2083bec29a8cfaccc55371b1564b96b"}, + {file = "yarl-1.25.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:8fb0eb4955adf0579001581f2f71a126e8781ba61bcd120f127b0401163c6c2d"}, + {file = "yarl-1.25.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:a1e32763e641a1566507d90a8d3b19bfc3cc04a9d4e5ae3e32189874ed4b58a3"}, + {file = "yarl-1.25.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:65b5b2066651b7432d389e9799d979c703bcc6ef44266bb8153ef54e91e4aab3"}, + {file = "yarl-1.25.1-cp314-cp314t-win_amd64.whl", hash = "sha256:734f6e5400352ac4254456003d462866c684703570929cff7a7bde015d0cb371"}, + {file = "yarl-1.25.1-cp314-cp314t-win_arm64.whl", hash = "sha256:287e99ff5aa4dc1c7630bfc683ded6f106d756c99dec432a2d7f197a784f51c6"}, + {file = "yarl-1.25.1-cp315-cp315-macosx_10_15_universal2.whl", hash = "sha256:9b1bdaae98bc016825dd3c9d8ee1832f829b3341f9cc6ebd1a1b0a7fef7367cc"}, + {file = "yarl-1.25.1-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:e7011b8fb8c4054bf0c12e5edc6cd83778b0028e99ce59b18586ed036f92cfdc"}, + {file = "yarl-1.25.1-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:f074e8d4aa0a5798920ddb6de3d08b228c614ff3724c3e8bd7577f4bafea867b"}, + {file = "yarl-1.25.1-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7d42e7e3ca399555578b4d617e3a6ecf13371b3743a115995fa010c7bf341459"}, + {file = "yarl-1.25.1-cp315-cp315-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:aa4ed3dd308548f9e707d9caaf005d2d7f8c1e7868f858dfeb47fe76e16b391d"}, + {file = "yarl-1.25.1-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:42a66563d8cc056ee32e6191e05097a7b2b3bc302e0bc3133daf8710eb18bd26"}, + {file = "yarl-1.25.1-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:98d370568f393215d605304cdb77b3d5539bd192c75b623c7304c42c8d6d8273"}, + {file = "yarl-1.25.1-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:23bf5b403c879a54964e0feac7285688e04bb220074878d737d331522da0a5bf"}, + {file = "yarl-1.25.1-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d45673badd08456d0340e9364eddafe1c53a9d2896424294de4d7dd71ad3ee57"}, + {file = "yarl-1.25.1-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:0136d640dfa9b0523853e411430a99f8a91eca85774c6420285a33b755bc6de3"}, + {file = "yarl-1.25.1-cp315-cp315-musllinux_1_2_armv7l.whl", hash = "sha256:59ba3a6e1aa8cfe5adf4bd270fd965db21955401b7ca6f1696010c55ed4daec2"}, + {file = "yarl-1.25.1-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:87796fedc3ba97ec14fab55acb48584276e6c1e4c1e89c422bda62c838e754a9"}, + {file = "yarl-1.25.1-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:bd0912757081f89b107d6c00b2ff8a194401b0b87eadcf4481de2b865a8fd44f"}, + {file = "yarl-1.25.1-cp315-cp315-musllinux_1_2_s390x.whl", hash = "sha256:b51c159a9794633f5e0db7ecec7b2b6e3734eca1f5d17dc989ff3552a43ff78b"}, + {file = "yarl-1.25.1-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:319e070a01db9920fb63761843f96a104c8e2b9427266731810dc1e22595b17c"}, + {file = "yarl-1.25.1-cp315-cp315-win_amd64.whl", hash = "sha256:a2059a2d891bd156bc5184e7ab7a56e78a84dfcfdeac8c501b552533ad1c36ee"}, + {file = "yarl-1.25.1-cp315-cp315-win_arm64.whl", hash = "sha256:a78b50b4f7918a3de71105d5c0b93bbc57bb8339a4d03a9dfd449f9068e76f3d"}, + {file = "yarl-1.25.1-cp315-cp315t-macosx_10_15_universal2.whl", hash = "sha256:b5402a340723fa7da00b5cff987ddab61276be6d11251ea71ae02bcac54890d8"}, + {file = "yarl-1.25.1-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:eda19ea5ee88742f47a2340816e6f2d40b53bed3ab5b69794769f36af9f35bb4"}, + {file = "yarl-1.25.1-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:75baa6cf9b6d1c52f3e111a130e202fd8cf0a5b3a066c3f73d615e885092e4ec"}, + {file = "yarl-1.25.1-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dbcef5a9119ef653653132cccaf999b30a0af6f33bb0a4ba80bec30056868487"}, + {file = "yarl-1.25.1-cp315-cp315t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7efc9f082dfed77c316edffa9deb52888e1bc6789171887cc1f68e06d65465c8"}, + {file = "yarl-1.25.1-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:fe01645169a2112aa1d4ebc3e4c5f029c5c8f97adfc32e5d37c993b39a994d75"}, + {file = "yarl-1.25.1-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1c557dfd5e3db046053a0bdc72261ade790ebe8e2c7a41b36b0ca1f14cb95f3"}, + {file = "yarl-1.25.1-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8ce4d6ccafb33d39bd78444612d14938ead674c25702ded2ee9c54a47735d225"}, + {file = "yarl-1.25.1-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:80a063f8297fc796296f00f100be520f209b23dc98f93ce8eba6ee7122598209"}, + {file = "yarl-1.25.1-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:7cb414a73e21a7ab58254926073f2930cb22f5b4314ea4260a687e2b3fd4dce3"}, + {file = "yarl-1.25.1-cp315-cp315t-musllinux_1_2_armv7l.whl", hash = "sha256:85a18376073f8a39aa07be34f9fc77e2869aa72c55c441efdd2cf79a0407504d"}, + {file = "yarl-1.25.1-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:77716e245c90f058466a05e6a465bb8600f767a8f4b18b4d40f3aff958e5f73c"}, + {file = "yarl-1.25.1-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:1e80dcf1446e1b080b1932b0d103c464a04112f5bc31f0f983ad418172063cde"}, + {file = "yarl-1.25.1-cp315-cp315t-musllinux_1_2_s390x.whl", hash = "sha256:bdc8d8b8c22e9e43ac68316b5e6cf083dec537f4ec213cb4aa967b583bc3fa64"}, + {file = "yarl-1.25.1-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:dfbf531053a0935f2e871bcd4753f90313688772ff8c017f5ea402e315a78c1f"}, + {file = "yarl-1.25.1-cp315-cp315t-win_amd64.whl", hash = "sha256:b13b88747769537f3d32e89e3a735da10c0a9e35d7322928c701b5f93d3afffd"}, + {file = "yarl-1.25.1-cp315-cp315t-win_arm64.whl", hash = "sha256:783dd1467083f4d3f7722ad6a313f24c173e7571372738fcb7a6e6d1ba48df25"}, + {file = "yarl-1.25.1-py3-none-any.whl", hash = "sha256:681c758b0490f9e96b78e5fa8e8dc6e648e9185bb6eaebe73183c33ea0c445f3"}, + {file = "yarl-1.25.1.tar.gz", hash = "sha256:03dd38de09bc213e9a8b29761eec33ee1d5318dac0e49d8af36e4d27830e23a7"}, ] [package.dependencies] @@ -1710,4 +1821,4 @@ aiohttp = ["aiohttp", "httpx-aiohttp"] [metadata] lock-version = "2.1" python-versions = "^3.10" -content-hash = "65ef513ef69a32344fa2133df094babe6493482ea7586d0e19edbcb2f4bbe7c8" +content-hash = "d6f20007544f9ff1b04d92f47b351e1eda2f66672b4c62350d35c919bc2ae9f3" diff --git a/pyproject.toml b/pyproject.toml index 17a6e916..1dc545d0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ dynamic = ["version"] [tool.poetry] name = "phenoml" -version = "17.0.0" +version = "0.0.0.dev0" description = "" readme = "README.md" authors = [] @@ -31,9 +31,6 @@ classifiers = [ packages = [ { include = "phenoml", from = "src"} ] -include = [ - { path = "src/phenoml/openapi/openapi.json", format = ["sdist", "wheel"] } -] [tool.poetry.urls] Repository = 'https://github.com/phenoml/phenoml-python-sdk' @@ -42,7 +39,7 @@ Repository = 'https://github.com/phenoml/phenoml-python-sdk' python = "^3.10" aiohttp = { version = ">=3.14.1,<4", optional = true, python = ">=3.10"} httpx = ">=0.21.2" -httpx-aiohttp = { version = "0.1.8", optional = true, python = ">=3.10"} +httpx-aiohttp = { version = "^0.1.8", optional = true, python = ">=3.10"} pydantic = ">= 1.9.2" pydantic-core = ">=2.18.2,<3.0.0" typing_extensions = ">= 4.0.0" diff --git a/reference.md b/reference.md index 99385a63..ea984f73 100644 --- a/reference.md +++ b/reference.md @@ -1770,7 +1770,7 @@ When false (default), uploading a duplicate returns 409 Conflict.
-Returns the terminology server's catalog of available code systems, including both built-in standard terminologies and custom uploaded systems. +Returns the catalog of available code systems, including both built-in standard terminologies and custom uploaded systems.
@@ -2315,10 +2315,9 @@ client.construe.codes.extract(
-**Alpha:** phenocr is an alpha feature. The API contract — request -parameters and response shape — may change as its internals evolve, and -results may vary between releases. Do not depend on it for production -workloads yet. +**Alpha:** phenocr is an alpha feature. Request parameters, response +shape, and results may change between releases. Do not depend on it for +production workloads yet. Extracts medical codes from natural language clinical text using phenocr. @@ -2509,7 +2508,7 @@ client.construe.codes.crosswalk(
-Returns a paginated list of all codes in the specified code system from the terminology server. +Returns a paginated list of all codes in the specified code system. Usage of CPT is subject to AMA requirements: see PhenoML Terms of Service.
@@ -2612,7 +2611,7 @@ client.construe.codes.list(
-Looks up a specific code in the terminology server and returns its details. +Looks up a specific code and returns its details. Usage of CPT is subject to AMA requirements: see PhenoML Terms of Service.
@@ -3719,61 +3718,328 @@ Multiple FHIR provider integrations can be provided as comma-separated values.
-Maps a FHIR R4 resource or Bundle into OMOP Common Data Model v5.4 rows -(person, visit_occurrence, condition_occurrence, drug_exposure, -procedure_occurrence, measurement, observation). +Maps a FHIR R4 resource or Bundle into OMOP Common Data Model v5.4 rows, +grouped by destination table in `tables`. -Resource support is intentionally limited to the OMOP tables returned by -this endpoint: -- `Patient` -> `person` +Set `vocab_version` to select the OMOP vocabulary release used for coded +concept resolution. If omitted or empty, the API uses its default +release. The response's `vocab_version`, when present, identifies the +release used. Specify a release explicitly when reproducibility matters. + +Standards basis: [FHIR R4 (v4.0.1)](https://hl7.org/fhir/R4/) defines +the accepted source elements and [OMOP CDM +v5.4](https://ohdsi.github.io/CommonDataModel/cdm54.html) defines the +output columns. The published [Vulcan FHIR-to-OMOP IG +v1.0.0](https://hl7.org/fhir/uv/omop/) is an informative FHIR R5 +baseline; this endpoint documents and implements the equivalent R4 +source elements, rather than accepting R5-only fields. + +This response is a mapping result, not a complete CDM load pipeline. +When a source cannot supply a field that CDM v5.4 requires, the row is +still returned with that field unset; the value is not inferred. Common +cases are `year_of_birth` without a usable `birthDate`, +`drug_exposure_end_date` without an explicit end or single-event timing, +and a required event date (such as `condition_start_date`, +`procedure_date`, or `death_date`) whose source has no timing with at +least day precision. Apply your own policy to such rows before loading +them into a strictly conformant CDM instance. + +Current resource coverage: +- `Patient` -> `person`; `deceased[x]` can also produce `death`, the + first address can produce `location`, and more than one supplied race + produces `observation` race rows (see Patient demographics below) +- `observation_period` -> one request-local derived row per person, + spanning the populated dates of that person's visit, clinical, and + death rows; this is not enrollment or capture-completeness evidence +- `Location` -> `location` and `care_site` +- `Organization` -> `care_site`; its first address can produce `location` +- `HealthcareService` -> `care_site` +- `Practitioner` and `PractitionerRole` -> `provider` - `Encounter` -> `visit_occurrence` - `Condition` -> `condition_occurrence` - `Procedure` -> `procedure_occurrence` - `MedicationRequest`, `MedicationStatement`, and `MedicationAdministration` -> `drug_exposure` - `Immunization` -> `drug_exposure` -- `Observation` with a numeric `valueQuantity`, `valueInteger`, or - numeric-looking `valueString` (for example `"<2"`) -> `measurement` -- non-numeric `Observation` -> `observation` +- `Observation` -> `measurement` or `observation`. For coded + Observations, the resolved OMOP concept domain selects the table; value + form only breaks ties. For text-only Observations, numeric values route + to `measurement` and nonnumeric values to `observation`. - `AllergyIntolerance` -> `observation` -`Medication` is supported only as reference data for medication -resources; it is not emitted as its own row because OMOP CDM has no -Medication table. Other reference/admin resources such as `Practitioner`, -`Organization`, `Location`, `Coverage`, and `Claim`, and clinical -workflow/document resources such as `DiagnosticReport`, `ServiceRequest`, -`CarePlan`, `DocumentReference`, `Composition`, `Specimen`, and -`DeviceUseStatement`, are currently accepted in a Bundle but are not -shaped into OMOP rows. Unsupported resource types are ignored rather than -listed under `dropped`; `dropped` is reserved for supported resource types -that were missing the subject/patient, code, or medication reference data -needed to produce a valid row. - -Each resource's primary clinical coding is resolved to a standard OMOP -`concept_id`. Alongside the OMOP rows grouped by table (`tables`), the -response carries `mappings` (how each source coding resolved, linked back -to the row it produced), `dropped` (resources that could not be shaped -into a row), `vocab_version` (the OMOP vocabulary release codes were -resolved against), and a small `summary` of the resolution outcomes. +`Medication` is reference data for medication resources; it does not +create its own row because OMOP CDM has no Medication table. Administrative +linkages (provider, care site, and location) are best-effort and limited to +references supplied in the request. `Patient.managingOrganization` is a +record custodian, not a care-delivery site. Provider specialty is not +mapped. Recorded `Practitioner.gender` is distinct from Person +demographics: `male` and `female` resolve to validated OMOP Gender +concepts in `provider.gender_concept_id`; `other`, `unknown`, and absent +gender remain unmapped. `Address.country` is resolved to `location.country_concept_id`, +and CMS Place of Service codings in `Location.type` are resolved to +`care_site.place_of_service_concept_id`. +A `PractitionerRole` that identifies one supplied `Practitioner` aliases +that canonical provider: by a top-level structural reference, a +parent-contained `#id` reference, or an exact `identifier.system` and +`identifier.value` match against a top-level Practitioner. No remote +identifier lookup is performed. When `Reference.type` is present it must +be `Practitioner`; duplicate contained IDs and identifier matches are +ambiguous. An explicit reference that is unresolved, ambiguous, or +unsupported retains a role-fallback provider row and is returned in +`diagnostics`. `provider_role_contexts` preserves role-specific +specialty and care-site context that a canonical OMOP provider row cannot +represent together. + +Patient demographics: +- Sex at birth, race, and ethnicity are read from these US Core + extensions, with their US Core 6.1.0 structures and value sets, on + any Patient (US Core profile conformance is not required). Sex at + birth falls back to Patient `gender`. Other extensions, including US + Core sex and gender identity, are ignored. + - Birth sex: + `http://hl7.org/fhir/us/core/StructureDefinition/us-core-birthsex` + (`valueCode` from `http://hl7.org/fhir/us/core/ValueSet/birthsex`) + - Race: + `http://hl7.org/fhir/us/core/StructureDefinition/us-core-race` + (`ombCategory` from + `http://hl7.org/fhir/us/core/ValueSet/omb-race-category`) + - Ethnicity: + `http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity` + (`ombCategory` from + `http://hl7.org/fhir/us/core/ValueSet/omb-ethnicity-category`) +- `gender_concept_id` is sex at birth. A supplied birth sex always + decides it: `M` and `F` are resolved; `UNK`, `ASKU`, `OTH`, a code + outside the value set, conflicting values, and a birth sex without + `valueCode` leave it `0`, and Patient `gender` is not used. +- Without a birth sex, Patient `gender` `male` or `female` is resolved + under the OMOP convention that the supplied gender represents sex at + birth; `other` and `unknown` keep concept `0`. +- `gender_source_value` is the chosen source code (`F` for birth sex, + `female` for Patient `gender`). +- Each race category is resolved separately, and null flavors (`UNK`, + `ASKU`) are ignored. One distinct standard race sets + `race_concept_id`. More than one sets it to `1546847` (More than one + race) and adds one `observation` row per race, with + `observation_concept_id` `4013886` (Race), the race in + `value_as_concept_id`, `observation_type_concept_id` `32817`, the + category code in `value_source_value`, and no + `observation_source_value` or `observation_date`. A loading pipeline + that requires `observation_date` must apply its own date policy. +- The single non-null ethnicity category is resolved, and null flavors + are ignored; more than one distinct category leaves + `ethnicity_concept_id` `0`. Ethnicity is not derived from race, and + no demographic is inferred from names, addresses, or other + extensions. +- `race_source_value` and `ethnicity_source_value` list every supplied + category and detailed code in source order, joined with `|`, or the + extension text when no code is supplied. Detailed codes and text are + not resolved. +- Every supplied birth sex, gender, and OMB category code has a + `mappings` entry whose `note` names its source and outcome. When a + birth sex is supplied, Patient `gender` is reported unselected with + the note `FHIR administrative gender; not used, birth sex supplied`. + Conflicting values and a birth sex without `valueCode` are also + returned in `diagnostics` with path `extension:birthsex` or + `extension:ethnicity`. `summary` counts each demographic field once, + as described under `Summary`. +- Differences from the reference conventions: Vulcan's example gender + ConceptMap maps `other` and `unknown` to concepts, which stay `0` + here; more than one race follows the OHDSI THEMIS convention, also + used by Vulcan, rather than the CDM 5.4 note that mixed races use + `0`; and Vulcan's suggested `observation` rows for multiple + ethnicities are not produced. + +`DiagnosticReport`, `ServiceRequest`, `CarePlan`, `DocumentReference`, +`Composition`, `Specimen`, `DeviceUseStatement`, `Coverage`, `Claim`, and +other unsupported resource types are accepted in a Bundle but ignored: they +create no row and no `dropped` entry. `dropped` is reserved for supported +row-producing resources that cannot safely produce a positive OMOP row: +required subject/patient, clinical code/text, or medication data may be +unusable, or the resource may explicitly negate or fail the documented +clinical-event eligibility policy. A single-Patient Bundle uses the sole +Patient only when `subject`/`patient` is absent. An explicit subject/patient +reference that is unresolved, ambiguous, or unsupported drops the clinical +resource in every request scope. + +Eligibility distinguishes clinical validity, performed/taken evidence, and +administrative workflow state. Checks run before terminology resolution; +entered-in-error and explicitly non-performed events never produce a positive +clinical-event row, and an unrecognized required status fails closed. Condition +requires absent or confirmed canonical-HL7 `verificationStatus`; clinical course is not a +diagnosis-role mapping. Procedures accept completed or stopped events. +Medication requests are prescription evidence only: `doNotPerform`, drafts, +cancellations, and non-authorizing intents are dropped. Medication statements +accept active, completed, stopped, or on-hold reported use; administrations +accept completed, in-progress, on-hold, or stopped events. An on-hold +administration also needs an `effectiveDateTime` or `effectivePeriod.start` +that supplies start evidence; a valid partial date remains an undated row. +An on-hold administration is started evidence that is temporarily paused and +expected to continue. Immunizations require completed +status. Observations require final, +amended, or corrected status; registered, +preliminary, cancelled, entered-in-error, unknown, and missing statuses are +dropped. AllergyIntolerance accepts absent, unconfirmed, or confirmed canonical-HL7 +`verificationStatus` as a reported allergy, but drops refuted, +entered-in-error, and unreadable supplied verification statuses. Encounter +accepts arrived, triaged, in-progress, onleave, or finished status, but drops +planned, cancelled, entered-in-error, unknown, and missing statuses. An eligible +encounter without `Period.end` remains a partial visit; no end date is invented. + +Coded Observation routing is selected from the resolved OMOP concept +domain. Numeric and nonnumeric `value[x]` forms establish the preferred +target only when the code is valid for both tables. A text-only +Observation has no resolver target, so numeric values route to +`measurement` and nonnumeric values to `observation`. Numeric values +populate `value_as_number` in the selected row; other non-coded values +populate `value_as_string` for an `observation` or `value_source_value` +for a `measurement`. Coded `valueCodeableConcept` values are resolved +against the selected row's `value_as_concept_id` and use the selected +bare code in `value_source_value`, leaving an `observation`'s +`value_as_string` empty; unmapped or target-invalid coded values remain +`0`. +Other unsupported `value[x]` forms and Observation components do not +populate separate converted values. A +numeric comparator (`<`, `<=`, `>`, `>=`) is represented only by a +measurement's `operator_concept_id`; units remain source text and have +`unit_concept_id` of `0`. + +A standard OMOP `concept_id` is selected for each eligible primary clinical coding +after considering all of the resource's supplied codings. An unambiguous +coded medication route is resolved independently to +`drug_exposure.route_concept_id`. Alongside the OMOP rows grouped by +table (`tables`), the response carries `mappings` (an entry for every +supported source coding from a resource that shaped a row, linked back to +that row; some, such +as demographic null flavors, are reported without being resolved), +`provider_role_contexts` (source role details linked to provider rows), +`dropped` (resources that could not be shaped into a row), +`diagnostics` (explicit references that could not safely create a link, +and conflicting or unsupported Patient demographic extensions), +`vocab_version` (the OMOP vocabulary release codes were resolved +against), and a small `summary` of the resolution outcomes. A `concept_id` of `0` is reported, not omitted (OMOP "no matching concept" semantics): it covers both a coding with no standard match (`UNMAPPED`) and an unverified suggestion for a text-only resource -(`UNCHECKED`). Only the primary clinical coding is resolved, so -`gender`/`race`/`ethnicity`/`visit`/`value`/`unit` `concept_id`s are -always `0`; the one populated non-resolved concept is measurement -`operator_concept_id`, set from a value comparator (`<`, `<=`, `>`, `>=`) -rather than the resolver. Each `*_source_value` carries the verbatim FHIR -coding (`system#code`), and `*_type_concept_id` is set to `32817` (EHR). +(`UNCHECKED`). Visit and unit concept fields currently remain `0`; +Person demographics and Provider recorded gender follow the policies +above. Coded Observation values may populate `value_as_concept_id`. +Concepts set by a fixed convention rather than terminology resolution +are measurement `operator_concept_id`, set from a value comparator (`<`, +`<=`, `>`, `>=`), and the multiple-race concepts described above. Clinical +`*_source_value` fields contain the selected FHIR code (or source text +for text-only resources). +The corresponding selected `mappings` entry preserves the coding system +and full source-coding provenance. +Known OID-form coding systems are accepted as either FHIR OID URNs (for +example, `urn:oid:2.16.840.1.113883.6.1` for LOINC) or bare OIDs, and +are normalized to their canonical system URLs before terminology +resolution. `mappings[].source_system` reports that canonical URL, so the +OID and URL forms produce the same mapping. An unknown OID is not +rewritten and may be `UNMAPPED`. +Other `*_source_value` fields preserve row-specific raw source values, +such as resource identifiers, names, units, or status codes. +`MedicationRequest` uses `32838` (EHR prescription) for +`drug_type_concept_id`; other current resources use `32817` (EHR). This +is a coarse provenance policy: it does not infer patient-reported, +medication-history, or other more-specific type concepts from FHIR +status fields. + +Dates and datetimes: +- A `*_date` is the calendar date (`YYYY-MM-DD`) of a source value with + at least day precision. A `*_datetime` is set only when that value + has a time of day, as local time without a UTC offset + (`YYYY-MM-DDTHH:MM:SS`, with fractional seconds to microseconds when + supplied): `2024-01-15T23:30:00-05:00` becomes `2024-01-15T23:30:00`. + A datetime without a timezone is read as local time. +- A partial date (`2024` or `2024-03`) leaves both columns unset. It + still counts as that source's value, so later sources in the list + below are not used. +- Free-text timing (such as `onsetString`), `Age` and `Range` forms, and + values that are not dates are ignored, so a later source in the list + is used if there is one. +- Other than the sources below and the same-day ends of single-event + medication records, no date is imputed: partial dates are not + completed, and a row is not dated from another resource such as its + Encounter. +- Valid date values never cause a request to be rejected, and the response + does not report which date elements were unusable. An on-hold + MedicationAdministration is an eligibility exception: it needs a supplied + effective start as evidence that administration began. + +Timing sources, in priority order where several are listed: +- `Patient`: `birthDate` sets `year_of_birth`, `month_of_birth`, and + `day_of_birth` from the parts it supplies; `birth_datetime` is not + set. `deceasedDateTime` sets the `death` dates. +- `Practitioner`: `birthDate` sets the provider's `year_of_birth`. +- `Encounter`: `period.start` and `period.end`. +- `Condition`: start from `onsetDateTime` or `onsetPeriod.start`, then + `recordedDate` (when the condition was recorded, not when it began); + end from `abatementDateTime` or `abatementPeriod.end`. +- `Procedure`: start from `performedDateTime` or + `performedPeriod.start`; end from `performedPeriod.end` only. +- `Observation`: `effectiveDateTime`, `effectivePeriod.start`, or + `effectiveInstant`. +- `AllergyIntolerance`: `recordedDate`, then `onsetDateTime` or + `onsetPeriod.start`. +- `MedicationStatement`: start from `effectiveDateTime` or + `effectivePeriod.start`; end and `verbatim_end_date` from + `effectivePeriod.end`. `dateAsserted` records when the statement was + made and is not used. +- `MedicationAdministration`: an `effectiveDateTime` is a single event + that sets both start and end; an `effectivePeriod` sets the start + and, when present, the end and `verbatim_end_date`. +- `Immunization`: `occurrenceDateTime` is a single event that sets both + start and end; `expirationDate` is not used. +- `MedicationRequest`: `authoredOn`, the order date, sets the start; it + is not evidence of administration. No end is set, and the validity + period is not used as an exposure duration. +- Differences from the Vulcan maps: `birth_datetime` is not set from + `birthDate`, Condition also reads `onsetPeriod.start`, and + AllergyIntolerance falls back to its onset when `recordedDate` is + missing. + +Medication details: +- For `MedicationRequest`, `dispenseRequest.numberOfRepeatsAllowed` sets + `refills` and a whole-day `expectedSupplyDuration` sets `days_supply`. +- All non-empty dosage text is preserved in `sig`. +- Coded dosage routes and `Immunization.route` are target-validated in + the OMOP Route domain. Conflicting routes are left unset; route + codings shared by every dosage instruction identify the same route. +- `Immunization.lotNumber` is preserved in `lot_number`. Medication codes are resolved whether they appear inline (`medicationCodeableConcept`) or via a `medicationReference` to a contained, relative (`Type/id`), or bundle-entry (`urn:uuid`) `Medication` resource. +A reference that resolves to another resource type is dropped even when it +supplies display text; an unresolved or display-only reference may use +its display as text-only medication input. Resources that cannot be shaped into a row — a medication with no usable -code, resolvable reference, or display, or any clinical resource whose -subject/patient reference cannot be tied to a person — are reported under -`dropped` rather than emitted as blank rows. The -bundle must contain at least one Patient resource. +code, resolvable reference, or display; any clinical resource whose +subject/patient reference cannot be tied to a person; or an event excluded +by eligibility — are reported under `dropped` rather than emitted as blank +rows. The Bundle must contain at least one Patient resource. + +Structural references resolve only to top-level resources supplied in the +request, by `Type/id` or an exactly matching Bundle `fullUrl` (including +`urn:uuid`). Contained references are supported for medication code lookup +and `PractitionerRole.practitioner` enrichment; the latter also supports +exact request-local identifier matching without a remote lookup. Every +nonzero structural foreign key targets a row in the same response. Missing +optional links remain unset without a diagnostic; an explicit optional +reference that is unresolved, ambiguous, conflicting with the row's +person, or unsupported remains unset and is returned in `diagnostics` +with its source path and outcome. + +All row IDs start at `1` for each request and are not stable or global. +For clinical conversion rows whose resource supplies an `id`, `mappings` +associates each row with that source FHIR resource ID. A `person` row +retains the Patient ID or its first identifier value in +`person_source_value`, when present; other reference and derived rows do +not uniformly carry a FHIR resource ID. Input resources without those +source identifiers cannot be correlated across responses from the +returned rows alone. Consumers combining responses need to establish +their own stable keys and remap every primary and foreign key together.
@@ -3801,7 +4067,7 @@ client.fhir2omop.create( fhir_resources={ "resourceType": "Bundle", "type": "collection", - "entry": [{"resource": {"resourceType": "Patient", "id": "patient-1", "gender": "female", "birthDate": "1985-07-22"}}, {"resource": {"resourceType": "Condition", "id": "condition-1", "subject": {"reference": "Patient/patient-1"}, "code": {"coding": [{"system": "http://snomed.info/sct", "code": "44054006", "display": "Type 2 diabetes mellitus"}]}, "onsetDateTime": "2024-01-15"}}, {"resource": {"resourceType": "MedicationRequest", "id": "medreq-1", "status": "active", "subject": {"reference": "Patient/patient-1"}, "medicationReference": {"reference": "#med0"}, "authoredOn": "2024-01-16", "contained": [{"resourceType": "Medication", "id": "med0", "code": {"coding": [{"system": "http://www.nlm.nih.gov/research/umls/rxnorm", "code": "860975", "display": "metformin hydrochloride 500 MG"}]}}]}}] + "entry": [{"resource": {"resourceType": "Patient", "id": "patient-1", "gender": "female", "birthDate": "1985-07-22"}}, {"resource": {"resourceType": "Condition", "id": "condition-1", "subject": {"reference": "Patient/patient-1"}, "code": {"coding": [{"system": "http://snomed.info/sct", "code": "44054006", "display": "Type 2 diabetes mellitus"}]}, "onsetDateTime": "2024-01-15"}}, {"resource": {"resourceType": "MedicationRequest", "id": "medreq-1", "status": "active", "intent": "order", "subject": {"reference": "Patient/patient-1"}, "medicationReference": {"reference": "#med0"}, "authoredOn": "2024-01-16", "contained": [{"resourceType": "Medication", "id": "med0", "code": {"coding": [{"system": "http://www.nlm.nih.gov/research/umls/rxnorm", "code": "860975", "display": "metformin hydrochloride 500 MG"}]}}]}}] }, ) @@ -3823,11 +4089,25 @@ client.fhir2omop.create( FHIR resources (single resource or Bundle). Must contain at least one Patient resource. Supported row-producing resources are Patient, -Encounter, Condition, Procedure, MedicationRequest, +Location, Organization, HealthcareService, Practitioner, +PractitionerRole, Encounter, Condition, Procedure, MedicationRequest, MedicationStatement, MedicationAdministration, Immunization, Observation, and AllergyIntolerance. Standalone Medication resources are consumed by medication references rather than mapped to their own -table. Other resource types are accepted but ignored. +table. Unsupported resource types are accepted in a Bundle but ignored. + + +
+ +
+
+ +**vocab_version:** `typing.Optional[str]` + +OMOP vocabulary release to use for coded concept resolution. If +omitted or empty, the API uses its default release. Specify a +release explicitly when reproducibility matters. The response's +`vocab_version`, when present, identifies the release used.
@@ -4689,11 +4969,8 @@ client.implementation_guides.implementation_guides.update(
-Deletes the stored metadata for an implementation guide — its -profile_context and timestamps. Member profiles keep their -implementation_guide assignment, so a guide still referenced by at least -one profile continues to appear in listings, just without context or -timestamps. +Deletes the guide's metadata and all its canonical package versions. +Custom profiles and their implementation-guide assignments are preserved.
@@ -4751,6 +5028,184 @@ client.implementation_guides.implementation_guides.delete( + + + + +
client.implementation_guides.implementation_guides.create_version(...) -> ImplementationGuideVersionDetail +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Publishes an exact package beneath this guide family. Each guide family +supports one exact package version. Publishing another version returns +`409 Conflict`. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from phenoml import PhenomlClient +from phenoml.environment import PhenomlClientEnvironment +from phenoml.implementation_guides import FhirImplementationGuide + +client = PhenomlClient( + client_id="", + client_secret="", + environment=PhenomlClientEnvironment.DEFAULT, +) + +client.implementation_guides.implementation_guides.create_version( + name="name", + implementation_guide=FhirImplementationGuide( + resource_type="ImplementationGuide", + url="url", + version="version", + ), + profile_refs=[ + "profile_refs" + ], +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**name:** `str` + +
+
+ +
+
+ +**implementation_guide:** `FhirImplementationGuide` + +
+
+ +
+
+ +**profile_refs:** `typing.List[str]` — Exact canonical `url|version` references to builtin or custom profiles. A package can contain at most 250 references. + +
+
+ +
+
+ +**profile_context:** `typing.Optional[str]` — Natural-language profile-selection context for this package. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.implementation_guides.implementation_guides.get_version(...) -> ImplementationGuideVersionDetail +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from phenoml import PhenomlClient +from phenoml.environment import PhenomlClientEnvironment + +client = PhenomlClient( + client_id="", + client_secret="", + environment=PhenomlClientEnvironment.DEFAULT, +) + +client.implementation_guides.implementation_guides.get_version( + name="name", + version="1.0.0", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**name:** `str` + +
+
+ +
+
+ +**version:** `str` — The authored ImplementationGuide.version. It may contain letters, numbers, and the punctuation characters `.`, `_`, `~`, `+`, and `-`; it cannot be exactly `.` or `..`. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ +
@@ -4822,7 +5277,7 @@ client.lang2fhir.create(
-**resource:** `CreateRequestResource` — Type of FHIR resource to create. Use 'auto' for automatic resource type detection, or specify a supported US Core profile. Recommended to use the supported US Core Profiles for validated results but you can also use any custom profile you've uploaded (if you're a develop or launch customer) +**resource:** `CreateRequestResource` — Type of FHIR resource to create. Use 'auto' for automatic resource type detection, or specify a supported profile. The default profile set includes US Core profiles and selected base R4 resources; you can also use any custom profile you've uploaded (if you're a develop or launch customer).
@@ -4933,7 +5388,7 @@ client.lang2fhir.create_multi(
-**patient_reference:** `typing.Optional[PatientReference]` +**primary_patient:** `typing.Optional[PrimaryPatient]`
@@ -4941,7 +5396,7 @@ client.lang2fhir.create_multi(
-**implementation_guide:** `typing.Optional[str]` — Custom Implementation Guide name. When specified, profiles from this IG are included alongside US Core profiles during resource detection. US Core is always the base layer; custom IG profiles are additive. +**patient_reference:** `typing.Optional[PatientReference]` — Deprecated compatibility alias for primary_patient.identifier. Cannot be combined with primary_patient.
@@ -4949,7 +5404,15 @@ client.lang2fhir.create_multi(
-**detection_effort:** `typing.Optional[CreateMultiRequestDetectionEffort]` — Detection effort. 'standard' runs detection once, 'deep' runs detection multiple times for higher recall. +**implementation_guide:** `typing.Optional[str]` — Custom Implementation Guide name. When specified, profiles from this IG are included alongside the default profiles during resource detection. Default profiles are always the base layer; custom IG profiles are additive. + +
+
+ +
+
+ +**detection_effort:** `typing.Optional[CreateMultiRequestDetectionEffort]` — Deprecated; use the default 'standard' value. This field will be removed in a future release. 'standard' runs detection once; 'deep' runs detection multiple times for higher recall.
@@ -5179,7 +5642,7 @@ client.lang2fhir.upload_profile(
-Extracts text from a document (PDF or image) and converts it into a structured FHIR resource. +Extracts text from a PDF, image, RTF, or XML/C-CDA document and converts it into a structured FHIR resource. **Patient identifier handling.** When generating a `patient` (or `patient-canvas`) resource, US Core requires `Patient.identifier` (a business identifier such as an MRN). When the source text contains an identifier, it is extracted with an appropriate URI system. When the source text does not contain a detectable identifier, a synthetic one is generated with `system: "urn:phenoml:lang2fhir-generated-id"` and a UUID `value` so the resource remains FHIR-valid and US Core conformant. Callers who need a tenant-specific namespace should rewrite the synthetic system after extraction.
@@ -5208,7 +5671,7 @@ client = PhenomlClient( client.lang2fhir.document( version="R4", resource="questionnaire", - content="JVBERi0xLjQKJeLjz9MK...(base64-encoded PDF or image bytes)", + content="JVBERi0xLjQKJeLjz9MK...(base64-encoded document bytes)", ) ``` @@ -5244,8 +5707,11 @@ client.lang2fhir.document( **content:** `str` Base64 encoded file content. -Supported file types: PDF (application/pdf), PNG (image/png), JPEG (image/jpeg), TIFF (image/tiff). +Supported file types: PDF (application/pdf), PNG (image/png), JPEG (image/jpeg), TIFF (image/tiff), RTF (application/rtf), XML/C-CDA (text/xml). +TIFF, RTF, and XML/C-CDA uploads are available on dedicated instances only. File type is auto-detected from content magic bytes. +The decoded file must not exceed 20 MiB. RTF and XML/C-CDA documents whose extracted text exceeds 1 MiB are rejected. +Generic XML must include an XML declaration; C-CDA documents rooted at `ClinicalDocument` may omit it.
@@ -5285,7 +5751,7 @@ File type is auto-detected from content magic bytes.
-Extracts text from a document (PDF or image) and converts it into multiple FHIR resources, +Extracts text from a PDF, image, RTF, or XML/C-CDA document and converts it into multiple FHIR resources, returned as a transaction Bundle. Combines document text extraction with multi-resource detection. Automatically detects Patient, Condition, MedicationRequest, Observation, and other resource types. Resources are linked with proper references (e.g., Conditions reference the Patient). @@ -5319,7 +5785,7 @@ client = PhenomlClient( client.lang2fhir.document_multi( version="R4", - content="JVBERi0xLjQKJeLjz9MK...(base64-encoded PDF or image bytes)", + content="JVBERi0xLjQKJeLjz9MK...(base64-encoded document bytes)", provider="medplum", config=DocumentConfig( split_classifications=[ @@ -5362,8 +5828,11 @@ client.lang2fhir.document_multi( **content:** `str` Base64 encoded file content. -Supported file types: PDF (application/pdf), PNG (image/png), JPEG (image/jpeg), TIFF (image/tiff). +Supported file types: PDF (application/pdf), PNG (image/png), JPEG (image/jpeg), TIFF (image/tiff), RTF (application/rtf), XML/C-CDA (text/xml). +TIFF, RTF, and XML/C-CDA uploads are available on dedicated instances only. File type is auto-detected from content magic bytes. +The decoded file must not exceed 20 MiB. RTF and XML/C-CDA documents whose extracted text exceeds 1 MiB are rejected. +Generic XML must include an XML declaration; C-CDA documents rooted at `ClinicalDocument` may omit it.
@@ -5379,7 +5848,7 @@ File type is auto-detected from content magic bytes.
-**patient_reference:** `typing.Optional[PatientReference]` +**primary_patient:** `typing.Optional[PrimaryPatient]`
@@ -5387,7 +5856,7 @@ File type is auto-detected from content magic bytes.
-**implementation_guide:** `typing.Optional[str]` — Custom Implementation Guide name. When specified, profiles from this IG are included alongside US Core profiles during resource detection. US Core is always the base layer; custom IG profiles are additive. +**patient_reference:** `typing.Optional[PatientReference]` — Deprecated compatibility alias for primary_patient.identifier. Cannot be combined with primary_patient.
@@ -5395,7 +5864,15 @@ File type is auto-detected from content magic bytes.
-**detection_effort:** `typing.Optional[DocumentMultiRequestDetectionEffort]` — Detection effort. 'standard' runs detection once, 'deep' runs detection multiple times for higher recall. +**implementation_guide:** `typing.Optional[str]` — Custom Implementation Guide name. When specified, profiles from this IG are included alongside the default profiles during resource detection. Default profiles are always the base layer; custom IG profiles are additive. + +
+
+ +
+
+ +**detection_effort:** `typing.Optional[DocumentMultiRequestDetectionEffort]` — Deprecated; use the default 'standard' value. This field will be removed in a future release. 'standard' runs detection once; 'deep' runs detection multiple times for higher recall.
@@ -5530,20 +6007,11 @@ client.lang2fhir_batch.list(
-Opens an empty batch job. Items arrive on later upload calls and the set -is sealed at finalize. +Opens an empty job; upload items, then finalize it to start processing. -Supplying `request_id` makes the create idempotent on that token: a -retried submit whose response was lost returns the original job rather -than opening a second one. This dedupe is scoped to the calling -credential. A `request_id` whose job was canceled or failed before it -finalized is released for a fresh replay; once a job is finalized, its -`request_id` keeps resolving to it even after cancellation. - -An instance may hold at most 4 active (pending or processing) jobs at -once; a create past that limit returns `409`. The limit is instance-wide -— jobs are shared across the instance's credentials — so another -credential's jobs count against it. +`request_id` makes creation idempotent: a retry returns the original +job. A token is released when its job is canceled or fails before +finalization; otherwise it continues to resolve to that job.
@@ -5587,8 +6055,9 @@ client.lang2fhir_batch.create( **request_id:** `typing.Optional[str]` -Optional client idempotency token. A retried create with the same -token returns the original job instead of opening a second one. +Optional client idempotency token (at most 256 UTF-8 bytes). A +retried create with the same token returns the original job instead +of opening a second one. @@ -5620,43 +6089,22 @@ token returns the original job instead of opening a second one.
-Stores one item of a job from a multipart upload. A batch's items arrive -one per request. The item carries **either** a `document` extraction -(whose input file rides as raw bytes in the `file` part) **or** a -`create` extraction (JSON only, no file). - -The upload enforces these rules: -- Set **exactly one** of `document` or `create`. Setting both, or - neither, is a `400`. -- When `document` is set, `file` is **required** — it supplies the - document's binary content (PDF or image). -- When `create` is set, `file` is **forbidden** — a create item carries - no file. -- `document` and `create` must each be a JSON **object**. +Stores one multipart item. Set **either** `document` with a raw `file`, +or JSON-only `create`. -Only the item's structure is checked here: the fields inside `document` -or `create` are not validated at upload. A body that is well-formed JSON -but not a valid request for its endpoint is still accepted with `202` -and fails later during processing, recorded as an item `error`. A -wrong-typed field the endpoint cannot decode fails as `invalid_input`; a -body that decodes but the pipeline rejects (for example, a missing -required field) fails as `processing_failed`. +Set exactly one JSON object. `document` requires `file`; `create` +forbids it. Violations return `400`. -Supplying `request_id` makes the upload idempotent on that token. A -re-upload under the same token overwrites the same item rather than -adding a second, so a client that lost an upload's response can safely -re-send it. The response's `deduplicated` is `true` only when the -re-uploaded payload matches the one already stored; a same-token upload -with a changed payload overwrites in place and returns `false`. +Upload validates only the envelope. Endpoint request validation happens +during processing: decoding failures are `invalid_input`; other pipeline +failures are `processing_failed`. -Set a `request_id` on **every** upload: re-sending under the same token -is the only way to repair a lost or incomplete upload, including the one -a finalize `409` reports. Without one, a re-send adds a new item instead -of replacing the missing one, and the job cannot be finalized. +Use `request_id` for every upload so retries replace the same item. +`deduplicated` is true only for an unchanged payload; a changed payload +overwrites the item and returns false. -Uploads are rejected once the job has been finalized (`409`), once it -holds its 500-item limit (`409`), or when the item is too large (`413` — -see the raw-file limit in the API description). +Uploads return `409` after finalization or at the item limit, and `413` +when the item is too large.
@@ -5712,11 +6160,12 @@ client.lang2fhir_batch.upload_item( The JSON body of `POST /lang2fhir/document/multi`, **without** its base64 `content` field — the uploaded `file` supplies the content. Accepts that endpoint's fields (`version`, `provider`, -`patient_reference`, `implementation_guide`, `detection_effort`, +`primary_patient`, `patient_reference` (deprecated), `implementation_guide`, `detection_effort` (deprecated), `validation_method`, `config`). This is the **multi**-resource body: it has no single-`resource` field, and the item's result is a `DocumentMultiResponse` (a Bundle of resources). Mutually -exclusive with `create`; requires `file`. +exclusive with `create`; requires `file`. Do not combine +`primary_patient` with `patient_reference`. @@ -5728,12 +6177,13 @@ exclusive with `create`; requires `file`. The JSON body of `POST /lang2fhir/create/multi`. Accepts that endpoint's fields (`text`, `version`, `provider`, -`patient_reference`, `implementation_guide`, `detection_effort`, +`primary_patient`, `patient_reference` (deprecated), `implementation_guide`, `detection_effort` (deprecated), `validation_method`, `resource_review`). This is the **multi**-resource body: it has no single-`resource` field, and the item's result is a `CreateMultiResponse` (a Bundle of resources). Mutually exclusive with `document`; must **not** be -accompanied by a `file`. +accompanied by a `file`. Do not combine `primary_patient` with +`patient_reference`. @@ -5743,7 +6193,13 @@ accompanied by a `file`. **file:** `typing.Optional[core.File]` -The document's binary content (PDF, PNG, JPEG, or TIFF). +The document's file content (PDF, PNG, JPEG, TIFF, RTF, or +XML/C-CDA). The document pipeline accepts files up to 20 MiB; +an upload that passes the storage cap but exceeds this limit +fails during processing. RTF and XML/C-CDA documents whose +extracted text exceeds 1 MiB also fail during processing. +Generic XML must include an XML declaration; C-CDA documents +rooted at `ClinicalDocument` may omit it. Required with `document`; forbidden with `create`. @@ -5754,10 +6210,11 @@ Required with `document`; forbidden with `create`. **request_id:** `typing.Optional[str]` -Optional idempotency token (max 256 bytes). Re-uploading under -the same token overwrites the same item instead of adding a -new one. The token is scoped to this job; the same token in -another job is independent and creates a separate item. +Optional idempotency token (at most 256 UTF-8 bytes). +Re-uploading under the same token overwrites the same item +instead of adding a new one. The token is scoped to this job; +the same token in another job is independent and creates a +separate item. @@ -5767,9 +6224,9 @@ another job is independent and creates a separate item. **id:** `typing.Optional[str]` -Optional caller-supplied correlation label (max 512 bytes), -echoed back on status and result listings so you can match the -server's item_id to your own record. +Optional caller-supplied correlation label (at most 512 UTF-8 +bytes), echoed back on status and result listings so you can +match the server's item_id to your own record. @@ -5880,8 +6337,8 @@ client.lang2fhir_batch.finalize(
-Drives a job to the terminal `canceled` state on request, freeing its -active-job slot immediately. Takes no request body. +Drives a job to the terminal `canceled` state on request. Takes no +request body. Cancel does not delete the job: the job record and any results already produced are preserved for the normal retention window, the same as a diff --git a/src/phenoml/agent/chat/client.py b/src/phenoml/agent/chat/client.py index 9939bf60..6cce68f7 100644 --- a/src/phenoml/agent/chat/client.py +++ b/src/phenoml/agent/chat/client.py @@ -418,7 +418,7 @@ async def stream( async def main() -> None: - response = await client.agent.chat.stream( + response = client.agent.chat.stream( phenoml_on_behalf_of="Patient/550e8400-e29b-41d4-a716-446655440000", phenoml_fhir_provider="550e8400-e29b-41d4-a716-446655440000:eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c...", message="What is the patient's current condition?", diff --git a/src/phenoml/agent/chat/raw_client.py b/src/phenoml/agent/chat/raw_client.py index b163d97b..221e6573 100644 --- a/src/phenoml/agent/chat/raw_client.py +++ b/src/phenoml/agent/chat/raw_client.py @@ -277,6 +277,8 @@ def _iter(): for _sse in _event_source.iter_sse(): if _sse.data == None: return + if len(_sse.data) == 0: + continue try: yield typing.cast( AgentChatStreamEvent, @@ -766,6 +768,8 @@ async def _iter(): async for _sse in _event_source.aiter_sse(): if _sse.data == None: return + if len(_sse.data) == 0: + continue try: yield typing.cast( AgentChatStreamEvent, diff --git a/src/phenoml/client.py b/src/phenoml/client.py index 87cfa7e9..8073193d 100644 --- a/src/phenoml/client.py +++ b/src/phenoml/client.py @@ -63,8 +63,8 @@ class PhenomlClient: base_url : typing.Optional[str] The base url to use for requests from the client. - token : typing.Callable[[], str] - Authenticate by providing a callable that returns a pre-generated bearer token. In this mode, OAuth client credentials are not required. + token : typing.Union[str, typing.Callable[[], str]] + Authenticate by providing a pre-generated bearer token, or a callable that returns one. In this mode, OAuth client credentials are not required. timeout : typing.Optional[float] The timeout to be used, in seconds, for requests. By default the timeout is 60 seconds, unless a custom httpx client is used, in which case this default is not enforced. @@ -130,7 +130,7 @@ def __init__( follow_redirects: typing.Optional[bool] = True, httpx_client: typing.Optional[httpx.Client] = None, logging: typing.Optional[typing.Union[LogConfig, Logger]] = None, - token: typing.Callable[[], str], + token: typing.Union[str, typing.Callable[[], str]], ): ... def __init__( self, @@ -141,7 +141,7 @@ def __init__( headers: typing.Optional[typing.Dict[str, str]] = None, client_id: typing.Optional[str] = os.getenv("PHENOML_CLIENT_ID"), client_secret: typing.Optional[str] = os.getenv("PHENOML_CLIENT_SECRET"), - token: typing.Optional[typing.Callable[[], str]] = None, + token: typing.Optional[typing.Union[str, typing.Callable[[], str]]] = None, _token_getter_override: typing.Optional[typing.Callable[[], str]] = None, timeout: typing.Optional[float] = None, max_retries: typing.Optional[int] = None, @@ -155,7 +155,12 @@ def __init__( _defaulted_max_retries = max_retries if max_retries is not None else 2 if instance_url is not None: _instance_url = instance_url if instance_url is not None else "experiment.app.pheno.ml" - base_url = "https://{instanceUrl}".format(instanceUrl=_instance_url) + _environment_url_templates = { + PhenomlClientEnvironment.DEFAULT: "https://{instanceUrl}", + } + _url_template = _environment_url_templates.get(environment, "https://{instanceUrl}") + if base_url is None: + base_url = _url_template.format(instanceUrl=_instance_url) if token is not None: self._client_wrapper = SyncClientWrapper( base_url=_get_base_url(base_url=base_url, environment=environment), @@ -398,8 +403,8 @@ class AsyncPhenomlClient: base_url : typing.Optional[str] The base url to use for requests from the client. - token : typing.Callable[[], str] - Authenticate by providing a callable that returns a pre-generated bearer token. In this mode, OAuth client credentials are not required. + token : typing.Union[str, typing.Callable[[], str]] + Authenticate by providing a pre-generated bearer token, or a callable that returns one. In this mode, OAuth client credentials are not required. timeout : typing.Optional[float] The timeout to be used, in seconds, for requests. By default the timeout is 60 seconds, unless a custom httpx client is used, in which case this default is not enforced. @@ -465,7 +470,7 @@ def __init__( follow_redirects: typing.Optional[bool] = True, httpx_client: typing.Optional[httpx.AsyncClient] = None, logging: typing.Optional[typing.Union[LogConfig, Logger]] = None, - token: typing.Callable[[], str], + token: typing.Union[str, typing.Callable[[], str]], ): ... def __init__( self, @@ -476,7 +481,7 @@ def __init__( headers: typing.Optional[typing.Dict[str, str]] = None, client_id: typing.Optional[str] = os.getenv("PHENOML_CLIENT_ID"), client_secret: typing.Optional[str] = os.getenv("PHENOML_CLIENT_SECRET"), - token: typing.Optional[typing.Callable[[], str]] = None, + token: typing.Optional[typing.Union[str, typing.Callable[[], str]]] = None, _token_getter_override: typing.Optional[typing.Callable[[], str]] = None, timeout: typing.Optional[float] = None, max_retries: typing.Optional[int] = None, @@ -490,7 +495,12 @@ def __init__( _defaulted_max_retries = max_retries if max_retries is not None else 2 if instance_url is not None: _instance_url = instance_url if instance_url is not None else "experiment.app.pheno.ml" - base_url = "https://{instanceUrl}".format(instanceUrl=_instance_url) + _environment_url_templates = { + PhenomlClientEnvironment.DEFAULT: "https://{instanceUrl}", + } + _url_template = _environment_url_templates.get(environment, "https://{instanceUrl}") + if base_url is None: + base_url = _url_template.format(instanceUrl=_instance_url) if token is not None: self._client_wrapper = AsyncClientWrapper( base_url=_get_base_url(base_url=base_url, environment=environment), diff --git a/src/phenoml/construe/__init__.py b/src/phenoml/construe/__init__.py index e7054dee..cf742bc3 100644 --- a/src/phenoml/construe/__init__.py +++ b/src/phenoml/construe/__init__.py @@ -37,7 +37,6 @@ TextSearchResult, ) from .errors import ( - BadGatewayError, BadRequestError, ConflictError, ContentTooLargeError, @@ -53,7 +52,6 @@ from . import code_systems, codes from .code_systems import UploadRequestFormat, UploadResponse _dynamic_imports: typing.Dict[str, str] = { - "BadGatewayError": ".errors", "BadRequestError": ".errors", "Citation": ".types", "CodeCategory": ".types", @@ -122,7 +120,6 @@ def __dir__(): __all__ = [ - "BadGatewayError", "BadRequestError", "Citation", "CodeCategory", diff --git a/src/phenoml/construe/code_systems/client.py b/src/phenoml/construe/code_systems/client.py index 0fc31876..1e815d7a 100644 --- a/src/phenoml/construe/code_systems/client.py +++ b/src/phenoml/construe/code_systems/client.py @@ -143,7 +143,7 @@ def upload( def list(self, *, request_options: typing.Optional[RequestOptions] = None) -> ListCodeSystemsResponse: """ - Returns the terminology server's catalog of available code systems, including both built-in standard terminologies and custom uploaded systems. + Returns the catalog of available code systems, including both built-in standard terminologies and custom uploaded systems. Parameters ---------- @@ -432,7 +432,7 @@ async def main() -> None: async def list(self, *, request_options: typing.Optional[RequestOptions] = None) -> ListCodeSystemsResponse: """ - Returns the terminology server's catalog of available code systems, including both built-in standard terminologies and custom uploaded systems. + Returns the catalog of available code systems, including both built-in standard terminologies and custom uploaded systems. Parameters ---------- diff --git a/src/phenoml/construe/code_systems/raw_client.py b/src/phenoml/construe/code_systems/raw_client.py index 3b756e7d..7d1f600f 100644 --- a/src/phenoml/construe/code_systems/raw_client.py +++ b/src/phenoml/construe/code_systems/raw_client.py @@ -214,7 +214,7 @@ def upload( def list(self, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[ListCodeSystemsResponse]: """ - Returns the terminology server's catalog of available code systems, including both built-in standard terminologies and custom uploaded systems. + Returns the catalog of available code systems, including both built-in standard terminologies and custom uploaded systems. Parameters ---------- @@ -796,7 +796,7 @@ async def list( self, *, request_options: typing.Optional[RequestOptions] = None ) -> AsyncHttpResponse[ListCodeSystemsResponse]: """ - Returns the terminology server's catalog of available code systems, including both built-in standard terminologies and custom uploaded systems. + Returns the catalog of available code systems, including both built-in standard terminologies and custom uploaded systems. Parameters ---------- diff --git a/src/phenoml/construe/codes/client.py b/src/phenoml/construe/codes/client.py index 40ff7f9a..8b8a32c9 100644 --- a/src/phenoml/construe/codes/client.py +++ b/src/phenoml/construe/codes/client.py @@ -91,10 +91,9 @@ def phenocr( self, *, text: str, system: PhenocrExtractRequestSystem, request_options: typing.Optional[RequestOptions] = None ) -> ExtractCodesResult: """ - **Alpha:** phenocr is an alpha feature. The API contract — request - parameters and response shape — may change as its internals evolve, and - results may vary between releases. Do not depend on it for production - workloads yet. + **Alpha:** phenocr is an alpha feature. Request parameters, response + shape, and results may change between releases. Do not depend on it for + production workloads yet. Extracts medical codes from natural language clinical text using phenocr. @@ -200,7 +199,7 @@ def list( request_options: typing.Optional[RequestOptions] = None, ) -> ListCodesResponse: """ - Returns a paginated list of all codes in the specified code system from the terminology server. + Returns a paginated list of all codes in the specified code system. Usage of CPT is subject to AMA requirements: see PhenoML Terms of Service. @@ -255,7 +254,7 @@ def lookup( request_options: typing.Optional[RequestOptions] = None, ) -> GetCodeResponse: """ - Looks up a specific code in the terminology server and returns its details. + Looks up a specific code and returns its details. Usage of CPT is subject to AMA requirements: see PhenoML Terms of Service. @@ -528,10 +527,9 @@ async def phenocr( self, *, text: str, system: PhenocrExtractRequestSystem, request_options: typing.Optional[RequestOptions] = None ) -> ExtractCodesResult: """ - **Alpha:** phenocr is an alpha feature. The API contract — request - parameters and response shape — may change as its internals evolve, and - results may vary between releases. Do not depend on it for production - workloads yet. + **Alpha:** phenocr is an alpha feature. Request parameters, response + shape, and results may change between releases. Do not depend on it for + production workloads yet. Extracts medical codes from natural language clinical text using phenocr. @@ -653,7 +651,7 @@ async def list( request_options: typing.Optional[RequestOptions] = None, ) -> ListCodesResponse: """ - Returns a paginated list of all codes in the specified code system from the terminology server. + Returns a paginated list of all codes in the specified code system. Usage of CPT is subject to AMA requirements: see PhenoML Terms of Service. @@ -716,7 +714,7 @@ async def lookup( request_options: typing.Optional[RequestOptions] = None, ) -> GetCodeResponse: """ - Looks up a specific code in the terminology server and returns its details. + Looks up a specific code and returns its details. Usage of CPT is subject to AMA requirements: see PhenoML Terms of Service. diff --git a/src/phenoml/construe/codes/raw_client.py b/src/phenoml/construe/codes/raw_client.py index 2a2109cb..644933ca 100644 --- a/src/phenoml/construe/codes/raw_client.py +++ b/src/phenoml/construe/codes/raw_client.py @@ -11,7 +11,6 @@ from ...core.pydantic_utilities import parse_obj_as from ...core.request_options import RequestOptions from ...core.serialization import convert_and_respect_annotation_metadata -from ..errors.bad_gateway_error import BadGatewayError from ..errors.bad_request_error import BadRequestError from ..errors.content_too_large_error import ContentTooLargeError from ..errors.gateway_timeout_error import GatewayTimeoutError @@ -179,10 +178,9 @@ def phenocr( self, *, text: str, system: PhenocrExtractRequestSystem, request_options: typing.Optional[RequestOptions] = None ) -> HttpResponse[ExtractCodesResult]: """ - **Alpha:** phenocr is an alpha feature. The API contract — request - parameters and response shape — may change as its internals evolve, and - results may vary between releases. Do not depend on it for production - workloads yet. + **Alpha:** phenocr is an alpha feature. Request parameters, response + shape, and results may change between releases. Do not depend on it for + production workloads yet. Extracts medical codes from natural language clinical text using phenocr. @@ -396,30 +394,8 @@ def crosswalk( ), ), ) - if _response.status_code == 501: - raise NotImplementedError( - headers=dict(_response.headers), - body=typing.cast( - typing.Any, - parse_obj_as( - type_=typing.Any, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 502: - raise BadGatewayError( - headers=dict(_response.headers), - body=typing.cast( - typing.Any, - parse_obj_as( - type_=typing.Any, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 503: - raise ServiceUnavailableError( + if _response.status_code == 500: + raise InternalServerError( headers=dict(_response.headers), body=typing.cast( typing.Any, @@ -448,7 +424,7 @@ def list( request_options: typing.Optional[RequestOptions] = None, ) -> HttpResponse[ListCodesResponse]: """ - Returns a paginated list of all codes in the specified code system from the terminology server. + Returns a paginated list of all codes in the specified code system. Usage of CPT is subject to AMA requirements: see PhenoML Terms of Service. @@ -556,7 +532,7 @@ def lookup( request_options: typing.Optional[RequestOptions] = None, ) -> HttpResponse[GetCodeResponse]: """ - Looks up a specific code in the terminology server and returns its details. + Looks up a specific code and returns its details. Usage of CPT is subject to AMA requirements: see PhenoML Terms of Service. @@ -1062,10 +1038,9 @@ async def phenocr( self, *, text: str, system: PhenocrExtractRequestSystem, request_options: typing.Optional[RequestOptions] = None ) -> AsyncHttpResponse[ExtractCodesResult]: """ - **Alpha:** phenocr is an alpha feature. The API contract — request - parameters and response shape — may change as its internals evolve, and - results may vary between releases. Do not depend on it for production - workloads yet. + **Alpha:** phenocr is an alpha feature. Request parameters, response + shape, and results may change between releases. Do not depend on it for + production workloads yet. Extracts medical codes from natural language clinical text using phenocr. @@ -1279,30 +1254,8 @@ async def crosswalk( ), ), ) - if _response.status_code == 501: - raise NotImplementedError( - headers=dict(_response.headers), - body=typing.cast( - typing.Any, - parse_obj_as( - type_=typing.Any, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 502: - raise BadGatewayError( - headers=dict(_response.headers), - body=typing.cast( - typing.Any, - parse_obj_as( - type_=typing.Any, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 503: - raise ServiceUnavailableError( + if _response.status_code == 500: + raise InternalServerError( headers=dict(_response.headers), body=typing.cast( typing.Any, @@ -1331,7 +1284,7 @@ async def list( request_options: typing.Optional[RequestOptions] = None, ) -> AsyncHttpResponse[ListCodesResponse]: """ - Returns a paginated list of all codes in the specified code system from the terminology server. + Returns a paginated list of all codes in the specified code system. Usage of CPT is subject to AMA requirements: see PhenoML Terms of Service. @@ -1439,7 +1392,7 @@ async def lookup( request_options: typing.Optional[RequestOptions] = None, ) -> AsyncHttpResponse[GetCodeResponse]: """ - Looks up a specific code in the terminology server and returns its details. + Looks up a specific code and returns its details. Usage of CPT is subject to AMA requirements: see PhenoML Terms of Service. diff --git a/src/phenoml/construe/errors/__init__.py b/src/phenoml/construe/errors/__init__.py index 08824c7b..2bbaf2cf 100644 --- a/src/phenoml/construe/errors/__init__.py +++ b/src/phenoml/construe/errors/__init__.py @@ -6,7 +6,6 @@ from importlib import import_module if typing.TYPE_CHECKING: - from .bad_gateway_error import BadGatewayError from .bad_request_error import BadRequestError from .conflict_error import ConflictError from .content_too_large_error import ContentTooLargeError @@ -19,7 +18,6 @@ from .service_unavailable_error import ServiceUnavailableError from .unauthorized_error import UnauthorizedError _dynamic_imports: typing.Dict[str, str] = { - "BadGatewayError": ".bad_gateway_error", "BadRequestError": ".bad_request_error", "ConflictError": ".conflict_error", "ContentTooLargeError": ".content_too_large_error", @@ -56,7 +54,6 @@ def __dir__(): __all__ = [ - "BadGatewayError", "BadRequestError", "ConflictError", "ContentTooLargeError", diff --git a/src/phenoml/core/client_wrapper.py b/src/phenoml/core/client_wrapper.py index dc25c0d5..c03a2430 100644 --- a/src/phenoml/core/client_wrapper.py +++ b/src/phenoml/core/client_wrapper.py @@ -33,12 +33,12 @@ def get_headers(self) -> typing.Dict[str, str]: import platform headers: typing.Dict[str, str] = { - "User-Agent": "phenoml/17.0.0", + "User-Agent": "phenoml/0.0.0-fern-placeholder", "X-Fern-Language": "Python", "X-Fern-Runtime": f"python/{platform.python_version()}", "X-Fern-Platform": f"{platform.system().lower()}/{platform.release()}", "X-Fern-SDK-Name": "phenoml", - "X-Fern-SDK-Version": "17.0.0", + "X-Fern-SDK-Version": "0.0.0.dev0", **(self.get_custom_headers() or {}), } token = self._get_token() diff --git a/src/phenoml/core/http_client.py b/src/phenoml/core/http_client.py index 124dce25..3e5a7865 100644 --- a/src/phenoml/core/http_client.py +++ b/src/phenoml/core/http_client.py @@ -3,6 +3,7 @@ import asyncio import email.utils import re +import socket import time import typing from contextlib import asynccontextmanager, contextmanager @@ -23,6 +24,39 @@ JITTER_FACTOR = 0.2 # 20% random jitter +def get_keepalive_socket_options( + idle: int = 60, + intvl: int = 30, + cnt: int = 5, +) -> typing.List[typing.Tuple[int, int, int]]: + """ + Build TCP keepalive socket options for the current platform. + + Keepalive probes keep otherwise-idle connections alive so that long, + non-streaming requests survive idle-connection reaping by a firewall, + load balancer, or NAT. The available socket constants are OS-dependent, + so each option is guarded and only emitted when the platform defines it: + + - ``SO_KEEPALIVE`` is portable (Linux/macOS/Windows). + - The idle-before-first-probe knob is ``TCP_KEEPIDLE`` on Linux and modern + Windows, but ``TCP_KEEPALIVE`` on macOS. + - ``TCP_KEEPINTVL`` / ``TCP_KEEPCNT`` exist on Linux/macOS/modern Windows. + + Passing these tuples to ``httpx.HTTPTransport(socket_options=...)`` / + ``httpx.AsyncHTTPTransport(socket_options=...)`` applies them to every + connection the transport opens. + """ + opts: typing.List[typing.Tuple[int, int, int]] = [(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)] + idle_const = getattr(socket, "TCP_KEEPIDLE", None) or getattr(socket, "TCP_KEEPALIVE", None) + if idle_const: + opts.append((socket.IPPROTO_TCP, idle_const, idle)) + if hasattr(socket, "TCP_KEEPINTVL"): + opts.append((socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, intvl)) + if hasattr(socket, "TCP_KEEPCNT"): + opts.append((socket.IPPROTO_TCP, socket.TCP_KEEPCNT, cnt)) + return opts + + def _parse_retry_after(response_headers: httpx.Headers) -> typing.Optional[float]: """ This function parses the `Retry-After` header in a HTTP response and returns the number of seconds to wait. @@ -238,7 +272,16 @@ def get_request_body( data: typing.Optional[typing.Any], request_options: typing.Optional[RequestOptions], omit: typing.Optional[typing.Any], + optional_body: bool = False, ) -> typing.Tuple[typing.Optional[typing.Any], typing.Optional[typing.Any]]: + # A whole body left at the sentinel was never passed by the caller, so it is absent + # rather than empty: the request carries no content and no `Content-Type`. + if omit is not None: + if json is omit: + json = None + if data is omit: + data = None + json_body = None data_body = None if data is not None: @@ -254,14 +297,36 @@ def get_request_body( # Only collapse empty dict to None when the body was not explicitly provided # and there are no additional body parameters. This preserves explicit empty # bodies (e.g., when an endpoint has a request body type but all fields are optional). - if json_body == {} and json is None and not has_additional_body_parameters: + # `optional_body` marks an endpoint whose body the API does not require, where a body + # that ends up empty means the caller passed none of its properties, so the request is + # sent with no content and no `Content-Type`. + if json_body == {} and (json is None or optional_body) and not has_additional_body_parameters: json_body = None - if data_body == {} and data is None and not has_additional_body_parameters: + if data_body == {} and (data is None or optional_body) and not has_additional_body_parameters: data_body = None return json_body, data_body +def drop_content_type_without_body( + headers: typing.Dict[str, typing.Any], + *, + json_body: typing.Optional[typing.Any], + data_body: typing.Optional[typing.Any], + optional_body: bool, +) -> typing.Dict[str, typing.Any]: + """Strip ``Content-Type`` from a request that carries no body. + + ``get_request_body`` drops the body of an ``optional_body`` endpoint when the caller + supplied none of it, but the endpoint still passes the content type it would have used. + A request that sends nothing must not advertise a media type, so a server that branches + on the header sees a bodyless call for what it is. + """ + if not optional_body or json_body is not None or data_body is not None: + return headers + return {key: value for key, value in headers.items() if key.lower() != "content-type"} + + class HttpClient: def __init__( self, @@ -309,6 +374,7 @@ def request( request_options: typing.Optional[RequestOptions] = None, retries: int = 0, omit: typing.Optional[typing.Any] = None, + optional_body: bool = False, force_multipart: typing.Optional[bool] = None, ) -> httpx.Response: base_url = self.get_base_url(base_url) @@ -321,7 +387,9 @@ def request( ) timeout = _timeout if _timeout is not None else httpx.USE_CLIENT_DEFAULT - json_body, data_body = get_request_body(json=json, data=data, request_options=request_options, omit=omit) + json_body, data_body = get_request_body( + json=json, data=data, request_options=request_options, omit=omit, optional_body=optional_body + ) request_files: typing.Optional[RequestFiles] = ( convert_file_dict_to_httpx_tuples(remove_omit_from_dict(remove_none_from_dict(files), omit)) @@ -364,6 +432,9 @@ def request( } ) ) + _request_headers = drop_content_type_without_body( + _request_headers, json_body=json_body, data_body=data_body, optional_body=optional_body + ) if self.logger.is_debug(): self.logger.debug( @@ -472,6 +543,7 @@ def stream( request_options: typing.Optional[RequestOptions] = None, retries: int = 0, omit: typing.Optional[typing.Any] = None, + optional_body: bool = False, force_multipart: typing.Optional[bool] = None, ) -> typing.Iterator[httpx.Response]: base_url = self.get_base_url(base_url) @@ -493,7 +565,9 @@ def stream( if (request_files is None or len(request_files) == 0) and force_multipart: request_files = FORCE_MULTIPART - json_body, data_body = get_request_body(json=json, data=data, request_options=request_options, omit=omit) + json_body, data_body = get_request_body( + json=json, data=data, request_options=request_options, omit=omit, optional_body=optional_body + ) data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) @@ -527,6 +601,9 @@ def stream( } ) ) + _request_headers = drop_content_type_without_body( + _request_headers, json_body=json_body, data_body=data_body, optional_body=optional_body + ) if self.logger.is_debug(): self.logger.debug( @@ -604,6 +681,7 @@ async def request( request_options: typing.Optional[RequestOptions] = None, retries: int = 0, omit: typing.Optional[typing.Any] = None, + optional_body: bool = False, force_multipart: typing.Optional[bool] = None, ) -> httpx.Response: base_url = self.get_base_url(base_url) @@ -625,7 +703,9 @@ async def request( if (request_files is None or len(request_files) == 0) and force_multipart: request_files = FORCE_MULTIPART - json_body, data_body = get_request_body(json=json, data=data, request_options=request_options, omit=omit) + json_body, data_body = get_request_body( + json=json, data=data, request_options=request_options, omit=omit, optional_body=optional_body + ) data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) @@ -662,6 +742,9 @@ async def request( } ) ) + _request_headers = drop_content_type_without_body( + _request_headers, json_body=json_body, data_body=data_body, optional_body=optional_body + ) if self.logger.is_debug(): self.logger.debug( @@ -770,6 +853,7 @@ async def stream( request_options: typing.Optional[RequestOptions] = None, retries: int = 0, omit: typing.Optional[typing.Any] = None, + optional_body: bool = False, force_multipart: typing.Optional[bool] = None, ) -> typing.AsyncIterator[httpx.Response]: base_url = self.get_base_url(base_url) @@ -791,7 +875,9 @@ async def stream( if (request_files is None or len(request_files) == 0) and force_multipart: request_files = FORCE_MULTIPART - json_body, data_body = get_request_body(json=json, data=data, request_options=request_options, omit=omit) + json_body, data_body = get_request_body( + json=json, data=data, request_options=request_options, omit=omit, optional_body=optional_body + ) data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) @@ -828,6 +914,9 @@ async def stream( } ) ) + _request_headers = drop_content_type_without_body( + _request_headers, json_body=json_body, data_body=data_body, optional_body=optional_body + ) if self.logger.is_debug(): self.logger.debug( diff --git a/src/phenoml/core/http_response.py b/src/phenoml/core/http_response.py index 00bb1096..9aa1e188 100644 --- a/src/phenoml/core/http_response.py +++ b/src/phenoml/core/http_response.py @@ -24,6 +24,10 @@ def headers(self) -> Dict[str, str]: def status_code(self) -> int: return self._response.status_code + @property + def response(self) -> httpx.Response: + return self._response + class HttpResponse(Generic[T], BaseHttpResponse): """HTTP response wrapper that exposes response headers and data.""" diff --git a/src/phenoml/core/jsonable_encoder.py b/src/phenoml/core/jsonable_encoder.py index 5b0902eb..f638cc9a 100644 --- a/src/phenoml/core/jsonable_encoder.py +++ b/src/phenoml/core/jsonable_encoder.py @@ -15,6 +15,7 @@ from pathlib import PurePath from types import GeneratorType from typing import Any, Callable, Dict, List, Optional, Set, Union +from urllib.parse import quote import pydantic from .datetime_utils import serialize_datetime @@ -118,3 +119,15 @@ def encode_path_param(obj: Any) -> str: if isinstance(obj, bool): return "true" if obj else "false" return str(jsonable_encoder(obj)) + + +def quote_path_param(obj: Any) -> str: + """Encode a value for use in a URL path segment, percent-encoding it. + + Same as encode_path_param, except the result is percent-encoded so + that a value containing "/" or ".." cannot change which endpoint + the request resolves to. + """ + if isinstance(obj, bool): + return "true" if obj else "false" + return quote(str(jsonable_encoder(obj)), safe="") diff --git a/src/phenoml/core/oauth_token_provider.py b/src/phenoml/core/oauth_token_provider.py index 0360d170..370bb869 100644 --- a/src/phenoml/core/oauth_token_provider.py +++ b/src/phenoml/core/oauth_token_provider.py @@ -31,7 +31,9 @@ def get_token(self) -> str: return self._refresh() def _refresh(self) -> str: - token_response = self._auth_client.get_token(client_id=self._client_id, client_secret=self._client_secret) + token_response = self._auth_client.get_token( + client_id=self._client_id, client_secret=self._client_secret, grant_type="client_credentials" + ) self._access_token = token_response.access_token self._expires_at = self._get_expires_at( expires_in_seconds=token_response.expires_in, buffer_in_minutes=self.BUFFER_IN_MINUTES @@ -62,7 +64,9 @@ async def get_token(self) -> str: return await self._refresh() async def _refresh(self) -> str: - token_response = await self._auth_client.get_token(client_id=self._client_id, client_secret=self._client_secret) + token_response = await self._auth_client.get_token( + client_id=self._client_id, client_secret=self._client_secret, grant_type="client_credentials" + ) self._access_token = token_response.access_token self._expires_at = self._get_expires_at( expires_in_seconds=token_response.expires_in, buffer_in_minutes=self.BUFFER_IN_MINUTES diff --git a/src/phenoml/core/pydantic_utilities.py b/src/phenoml/core/pydantic_utilities.py index 6587f5e1..70816b99 100644 --- a/src/phenoml/core/pydantic_utilities.py +++ b/src/phenoml/core/pydantic_utilities.py @@ -5,6 +5,7 @@ import inspect import json import logging +import weakref from collections import defaultdict from dataclasses import asdict from typing import ( @@ -184,6 +185,58 @@ def _get_type_adapter(type_: Type[Any]) -> Any: return adapter +_field_alias_cache: "weakref.WeakKeyDictionary[type, Tuple[Dict[str, str], Tuple[str, ...]]]" = ( + weakref.WeakKeyDictionary() +) + + +def _get_field_aliases(model: type) -> Tuple[Dict[str, str], Tuple[str, ...]]: + """ + Map of field name to Pydantic alias for the fields whose alias differs from their name, together with the + keys that are ambiguous (an alias of one field and the name of another). Computed once per model class. + """ + cached = _field_alias_cache.get(model) + if cached is None: + fields: Mapping[str, Any] = ( + getattr(model, "model_fields", {}) if IS_PYDANTIC_V2 else getattr(model, "__fields__", {}) + ) + name_to_alias: Dict[str, str] = {} + for name, field in fields.items(): + alias = getattr(field, "alias", None) + if alias is not None and alias != name: + name_to_alias[name] = alias + cached = (name_to_alias, tuple(alias for alias in name_to_alias.values() if alias in fields)) + _field_alias_cache[model] = cached + return cached + + +def _coerce_keys_to_aliases(model: type, data: Any) -> Any: + """ + Accept Python field names in input by rewriting them to their Pydantic aliases, + while avoiding silent collisions when a key could refer to multiple fields. + """ + if not isinstance(data, Mapping): + return data + + name_to_alias, ambiguous_keys = _get_field_aliases(model) + for key in ambiguous_keys: + if key in data and name_to_alias.get(key, key) not in data: + raise ValueError( + f"Ambiguous input key '{key}': it is both a field name and an alias. " + "Provide the explicit alias key to disambiguate." + ) + + if not name_to_alias or not any(name in data for name in name_to_alias): + return data if isinstance(data, dict) else dict(data) + + rewritten: Dict[str, Any] = dict(data) + for name, alias in name_to_alias.items(): + if name in data and alias not in rewritten: + rewritten[alias] = rewritten.pop(name) + + return rewritten + + def parse_obj_as(type_: Type[T], object_: Any) -> T: # convert_and_respect_annotation_metadata is required for TypedDict aliasing. # @@ -193,20 +246,7 @@ def parse_obj_as(type_: Type[T], object_: Any) -> T: # - If the model encodes aliasing only via FieldMetadata annotations, then we MUST pre-dealias because Pydantic # will not recognize those aliases during validation. if inspect.isclass(type_) and issubclass(type_, pydantic.BaseModel): - has_pydantic_aliases = False - if IS_PYDANTIC_V2: - for field_name, field_info in getattr(type_, "model_fields", {}).items(): # type: ignore[attr-defined] - alias = getattr(field_info, "alias", None) - if alias is not None and alias != field_name: - has_pydantic_aliases = True - break - else: - for field in getattr(type_, "__fields__", {}).values(): - alias = getattr(field, "alias", None) - name = getattr(field, "name", None) - if alias is not None and name is not None and alias != name: - has_pydantic_aliases = True - break + has_pydantic_aliases = bool(_get_field_aliases(type_)[0]) dealiased_object = ( object_ @@ -239,39 +279,7 @@ class UniversalBaseModel(pydantic.BaseModel): @pydantic.model_validator(mode="before") # type: ignore[attr-defined] @classmethod def _coerce_field_names_to_aliases(cls, data: Any) -> Any: - """ - Accept Python field names in input by rewriting them to their Pydantic aliases, - while avoiding silent collisions when a key could refer to multiple fields. - """ - if not isinstance(data, Mapping): - return data - - fields = getattr(cls, "model_fields", {}) # type: ignore[attr-defined] - name_to_alias: Dict[str, str] = {} - alias_to_name: Dict[str, str] = {} - - for name, field_info in fields.items(): - alias = getattr(field_info, "alias", None) or name - name_to_alias[name] = alias - if alias != name: - alias_to_name[alias] = name - - # Detect ambiguous keys: a key that is an alias for one field and a name for another. - ambiguous_keys = set(alias_to_name.keys()).intersection(set(name_to_alias.keys())) - for key in ambiguous_keys: - if key in data and name_to_alias[key] not in data: - raise ValueError( - f"Ambiguous input key '{key}': it is both a field name and an alias. " - "Provide the explicit alias key to disambiguate." - ) - - original_keys = set(data.keys()) - rewritten: Dict[str, Any] = dict(data) - for name, alias in name_to_alias.items(): - if alias != name and name in original_keys and alias not in rewritten: - rewritten[alias] = rewritten.pop(name) - - return rewritten + return _coerce_keys_to_aliases(cls, data) @pydantic.model_serializer(mode="plain", when_used="json") # type: ignore[attr-defined] def serialize_model(self) -> Any: # type: ignore[name-defined] @@ -287,37 +295,7 @@ class Config: @pydantic.root_validator(pre=True) def _coerce_field_names_to_aliases(cls, values: Any) -> Any: - """ - Pydantic v1 equivalent of _coerce_field_names_to_aliases. - """ - if not isinstance(values, Mapping): - return values - - fields = getattr(cls, "__fields__", {}) - name_to_alias: Dict[str, str] = {} - alias_to_name: Dict[str, str] = {} - - for name, field in fields.items(): - alias = getattr(field, "alias", None) or name - name_to_alias[name] = alias - if alias != name: - alias_to_name[alias] = name - - ambiguous_keys = set(alias_to_name.keys()).intersection(set(name_to_alias.keys())) - for key in ambiguous_keys: - if key in values and name_to_alias[key] not in values: - raise ValueError( - f"Ambiguous input key '{key}': it is both a field name and an alias. " - "Provide the explicit alias key to disambiguate." - ) - - original_keys = set(values.keys()) - rewritten: Dict[str, Any] = dict(values) - for name, alias in name_to_alias.items(): - if alias != name and name in original_keys and alias not in rewritten: - rewritten[alias] = rewritten.pop(name) - - return rewritten + return _coerce_keys_to_aliases(cls, values) # type: ignore[arg-type] @classmethod def model_construct(cls: Type["Model"], _fields_set: Optional[Set[str]] = None, **values: Any) -> "Model": diff --git a/src/phenoml/fhir2omop/__init__.py b/src/phenoml/fhir2omop/__init__.py index 8906ff16..b14ccd5b 100644 --- a/src/phenoml/fhir2omop/__init__.py +++ b/src/phenoml/fhir2omop/__init__.py @@ -8,6 +8,7 @@ if typing.TYPE_CHECKING: from .types import ( CareSiteRow, + Coding, ConditionOccurrenceRow, CreateOmopResponse, DeathRow, @@ -15,20 +16,28 @@ DrugExposureRow, LocationRow, MappingEntry, + MappingEntryMappingStatus, MeasurementRow, ObservationPeriodRow, ObservationRow, OmopTables, PersonRow, ProcedureOccurrenceRow, + ProviderRoleCareSite, + ProviderRoleCodeableConcept, + ProviderRoleContext, + ProviderRolePractitionerIdentifier, ProviderRow, + ReferenceDiagnostic, + ReferenceDiagnosticOutcome, Summary, VisitOccurrenceRow, ) - from .errors import BadRequestError, InternalServerError, ServiceUnavailableError, UnauthorizedError + from .errors import BadRequestError, InternalServerError, UnauthorizedError _dynamic_imports: typing.Dict[str, str] = { "BadRequestError": ".errors", "CareSiteRow": ".types", + "Coding": ".types", "ConditionOccurrenceRow": ".types", "CreateOmopResponse": ".types", "DeathRow": ".types", @@ -37,14 +46,20 @@ "InternalServerError": ".errors", "LocationRow": ".types", "MappingEntry": ".types", + "MappingEntryMappingStatus": ".types", "MeasurementRow": ".types", "ObservationPeriodRow": ".types", "ObservationRow": ".types", "OmopTables": ".types", "PersonRow": ".types", "ProcedureOccurrenceRow": ".types", + "ProviderRoleCareSite": ".types", + "ProviderRoleCodeableConcept": ".types", + "ProviderRoleContext": ".types", + "ProviderRolePractitionerIdentifier": ".types", "ProviderRow": ".types", - "ServiceUnavailableError": ".errors", + "ReferenceDiagnostic": ".types", + "ReferenceDiagnosticOutcome": ".types", "Summary": ".types", "UnauthorizedError": ".errors", "VisitOccurrenceRow": ".types", @@ -75,6 +90,7 @@ def __dir__(): __all__ = [ "BadRequestError", "CareSiteRow", + "Coding", "ConditionOccurrenceRow", "CreateOmopResponse", "DeathRow", @@ -83,14 +99,20 @@ def __dir__(): "InternalServerError", "LocationRow", "MappingEntry", + "MappingEntryMappingStatus", "MeasurementRow", "ObservationPeriodRow", "ObservationRow", "OmopTables", "PersonRow", "ProcedureOccurrenceRow", + "ProviderRoleCareSite", + "ProviderRoleCodeableConcept", + "ProviderRoleContext", + "ProviderRolePractitionerIdentifier", "ProviderRow", - "ServiceUnavailableError", + "ReferenceDiagnostic", + "ReferenceDiagnosticOutcome", "Summary", "UnauthorizedError", "VisitOccurrenceRow", diff --git a/src/phenoml/fhir2omop/client.py b/src/phenoml/fhir2omop/client.py index ac8e8814..81f502c4 100644 --- a/src/phenoml/fhir2omop/client.py +++ b/src/phenoml/fhir2omop/client.py @@ -27,75 +27,353 @@ def with_raw_response(self) -> RawFhir2OmopClient: return self._raw_client def create( - self, *, fhir_resources: typing.Dict[str, typing.Any], request_options: typing.Optional[RequestOptions] = None + self, + *, + fhir_resources: typing.Dict[str, typing.Any], + vocab_version: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, ) -> CreateOmopResponse: """ - Maps a FHIR R4 resource or Bundle into OMOP Common Data Model v5.4 rows - (person, visit_occurrence, condition_occurrence, drug_exposure, - procedure_occurrence, measurement, observation). - - Resource support is intentionally limited to the OMOP tables returned by - this endpoint: - - `Patient` -> `person` + Maps a FHIR R4 resource or Bundle into OMOP Common Data Model v5.4 rows, + grouped by destination table in `tables`. + + Set `vocab_version` to select the OMOP vocabulary release used for coded + concept resolution. If omitted or empty, the API uses its default + release. The response's `vocab_version`, when present, identifies the + release used. Specify a release explicitly when reproducibility matters. + + Standards basis: [FHIR R4 (v4.0.1)](https://hl7.org/fhir/R4/) defines + the accepted source elements and [OMOP CDM + v5.4](https://ohdsi.github.io/CommonDataModel/cdm54.html) defines the + output columns. The published [Vulcan FHIR-to-OMOP IG + v1.0.0](https://hl7.org/fhir/uv/omop/) is an informative FHIR R5 + baseline; this endpoint documents and implements the equivalent R4 + source elements, rather than accepting R5-only fields. + + This response is a mapping result, not a complete CDM load pipeline. + When a source cannot supply a field that CDM v5.4 requires, the row is + still returned with that field unset; the value is not inferred. Common + cases are `year_of_birth` without a usable `birthDate`, + `drug_exposure_end_date` without an explicit end or single-event timing, + and a required event date (such as `condition_start_date`, + `procedure_date`, or `death_date`) whose source has no timing with at + least day precision. Apply your own policy to such rows before loading + them into a strictly conformant CDM instance. + + Current resource coverage: + - `Patient` -> `person`; `deceased[x]` can also produce `death`, the + first address can produce `location`, and more than one supplied race + produces `observation` race rows (see Patient demographics below) + - `observation_period` -> one request-local derived row per person, + spanning the populated dates of that person's visit, clinical, and + death rows; this is not enrollment or capture-completeness evidence + - `Location` -> `location` and `care_site` + - `Organization` -> `care_site`; its first address can produce `location` + - `HealthcareService` -> `care_site` + - `Practitioner` and `PractitionerRole` -> `provider` - `Encounter` -> `visit_occurrence` - `Condition` -> `condition_occurrence` - `Procedure` -> `procedure_occurrence` - `MedicationRequest`, `MedicationStatement`, and `MedicationAdministration` -> `drug_exposure` - `Immunization` -> `drug_exposure` - - `Observation` with a numeric `valueQuantity`, `valueInteger`, or - numeric-looking `valueString` (for example `"<2"`) -> `measurement` - - non-numeric `Observation` -> `observation` + - `Observation` -> `measurement` or `observation`. For coded + Observations, the resolved OMOP concept domain selects the table; value + form only breaks ties. For text-only Observations, numeric values route + to `measurement` and nonnumeric values to `observation`. - `AllergyIntolerance` -> `observation` - `Medication` is supported only as reference data for medication - resources; it is not emitted as its own row because OMOP CDM has no - Medication table. Other reference/admin resources such as `Practitioner`, - `Organization`, `Location`, `Coverage`, and `Claim`, and clinical - workflow/document resources such as `DiagnosticReport`, `ServiceRequest`, - `CarePlan`, `DocumentReference`, `Composition`, `Specimen`, and - `DeviceUseStatement`, are currently accepted in a Bundle but are not - shaped into OMOP rows. Unsupported resource types are ignored rather than - listed under `dropped`; `dropped` is reserved for supported resource types - that were missing the subject/patient, code, or medication reference data - needed to produce a valid row. - - Each resource's primary clinical coding is resolved to a standard OMOP - `concept_id`. Alongside the OMOP rows grouped by table (`tables`), the - response carries `mappings` (how each source coding resolved, linked back - to the row it produced), `dropped` (resources that could not be shaped - into a row), `vocab_version` (the OMOP vocabulary release codes were - resolved against), and a small `summary` of the resolution outcomes. + `Medication` is reference data for medication resources; it does not + create its own row because OMOP CDM has no Medication table. Administrative + linkages (provider, care site, and location) are best-effort and limited to + references supplied in the request. `Patient.managingOrganization` is a + record custodian, not a care-delivery site. Provider specialty is not + mapped. Recorded `Practitioner.gender` is distinct from Person + demographics: `male` and `female` resolve to validated OMOP Gender + concepts in `provider.gender_concept_id`; `other`, `unknown`, and absent + gender remain unmapped. `Address.country` is resolved to `location.country_concept_id`, + and CMS Place of Service codings in `Location.type` are resolved to + `care_site.place_of_service_concept_id`. + A `PractitionerRole` that identifies one supplied `Practitioner` aliases + that canonical provider: by a top-level structural reference, a + parent-contained `#id` reference, or an exact `identifier.system` and + `identifier.value` match against a top-level Practitioner. No remote + identifier lookup is performed. When `Reference.type` is present it must + be `Practitioner`; duplicate contained IDs and identifier matches are + ambiguous. An explicit reference that is unresolved, ambiguous, or + unsupported retains a role-fallback provider row and is returned in + `diagnostics`. `provider_role_contexts` preserves role-specific + specialty and care-site context that a canonical OMOP provider row cannot + represent together. + + Patient demographics: + - Sex at birth, race, and ethnicity are read from these US Core + extensions, with their US Core 6.1.0 structures and value sets, on + any Patient (US Core profile conformance is not required). Sex at + birth falls back to Patient `gender`. Other extensions, including US + Core sex and gender identity, are ignored. + - Birth sex: + `http://hl7.org/fhir/us/core/StructureDefinition/us-core-birthsex` + (`valueCode` from `http://hl7.org/fhir/us/core/ValueSet/birthsex`) + - Race: + `http://hl7.org/fhir/us/core/StructureDefinition/us-core-race` + (`ombCategory` from + `http://hl7.org/fhir/us/core/ValueSet/omb-race-category`) + - Ethnicity: + `http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity` + (`ombCategory` from + `http://hl7.org/fhir/us/core/ValueSet/omb-ethnicity-category`) + - `gender_concept_id` is sex at birth. A supplied birth sex always + decides it: `M` and `F` are resolved; `UNK`, `ASKU`, `OTH`, a code + outside the value set, conflicting values, and a birth sex without + `valueCode` leave it `0`, and Patient `gender` is not used. + - Without a birth sex, Patient `gender` `male` or `female` is resolved + under the OMOP convention that the supplied gender represents sex at + birth; `other` and `unknown` keep concept `0`. + - `gender_source_value` is the chosen source code (`F` for birth sex, + `female` for Patient `gender`). + - Each race category is resolved separately, and null flavors (`UNK`, + `ASKU`) are ignored. One distinct standard race sets + `race_concept_id`. More than one sets it to `1546847` (More than one + race) and adds one `observation` row per race, with + `observation_concept_id` `4013886` (Race), the race in + `value_as_concept_id`, `observation_type_concept_id` `32817`, the + category code in `value_source_value`, and no + `observation_source_value` or `observation_date`. A loading pipeline + that requires `observation_date` must apply its own date policy. + - The single non-null ethnicity category is resolved, and null flavors + are ignored; more than one distinct category leaves + `ethnicity_concept_id` `0`. Ethnicity is not derived from race, and + no demographic is inferred from names, addresses, or other + extensions. + - `race_source_value` and `ethnicity_source_value` list every supplied + category and detailed code in source order, joined with `|`, or the + extension text when no code is supplied. Detailed codes and text are + not resolved. + - Every supplied birth sex, gender, and OMB category code has a + `mappings` entry whose `note` names its source and outcome. When a + birth sex is supplied, Patient `gender` is reported unselected with + the note `FHIR administrative gender; not used, birth sex supplied`. + Conflicting values and a birth sex without `valueCode` are also + returned in `diagnostics` with path `extension:birthsex` or + `extension:ethnicity`. `summary` counts each demographic field once, + as described under `Summary`. + - Differences from the reference conventions: Vulcan's example gender + ConceptMap maps `other` and `unknown` to concepts, which stay `0` + here; more than one race follows the OHDSI THEMIS convention, also + used by Vulcan, rather than the CDM 5.4 note that mixed races use + `0`; and Vulcan's suggested `observation` rows for multiple + ethnicities are not produced. + + `DiagnosticReport`, `ServiceRequest`, `CarePlan`, `DocumentReference`, + `Composition`, `Specimen`, `DeviceUseStatement`, `Coverage`, `Claim`, and + other unsupported resource types are accepted in a Bundle but ignored: they + create no row and no `dropped` entry. `dropped` is reserved for supported + row-producing resources that cannot safely produce a positive OMOP row: + required subject/patient, clinical code/text, or medication data may be + unusable, or the resource may explicitly negate or fail the documented + clinical-event eligibility policy. A single-Patient Bundle uses the sole + Patient only when `subject`/`patient` is absent. An explicit subject/patient + reference that is unresolved, ambiguous, or unsupported drops the clinical + resource in every request scope. + + Eligibility distinguishes clinical validity, performed/taken evidence, and + administrative workflow state. Checks run before terminology resolution; + entered-in-error and explicitly non-performed events never produce a positive + clinical-event row, and an unrecognized required status fails closed. Condition + requires absent or confirmed canonical-HL7 `verificationStatus`; clinical course is not a + diagnosis-role mapping. Procedures accept completed or stopped events. + Medication requests are prescription evidence only: `doNotPerform`, drafts, + cancellations, and non-authorizing intents are dropped. Medication statements + accept active, completed, stopped, or on-hold reported use; administrations + accept completed, in-progress, on-hold, or stopped events. An on-hold + administration also needs an `effectiveDateTime` or `effectivePeriod.start` + that supplies start evidence; a valid partial date remains an undated row. + An on-hold administration is started evidence that is temporarily paused and + expected to continue. Immunizations require completed + status. Observations require final, + amended, or corrected status; registered, + preliminary, cancelled, entered-in-error, unknown, and missing statuses are + dropped. AllergyIntolerance accepts absent, unconfirmed, or confirmed canonical-HL7 + `verificationStatus` as a reported allergy, but drops refuted, + entered-in-error, and unreadable supplied verification statuses. Encounter + accepts arrived, triaged, in-progress, onleave, or finished status, but drops + planned, cancelled, entered-in-error, unknown, and missing statuses. An eligible + encounter without `Period.end` remains a partial visit; no end date is invented. + + Coded Observation routing is selected from the resolved OMOP concept + domain. Numeric and nonnumeric `value[x]` forms establish the preferred + target only when the code is valid for both tables. A text-only + Observation has no resolver target, so numeric values route to + `measurement` and nonnumeric values to `observation`. Numeric values + populate `value_as_number` in the selected row; other non-coded values + populate `value_as_string` for an `observation` or `value_source_value` + for a `measurement`. Coded `valueCodeableConcept` values are resolved + against the selected row's `value_as_concept_id` and use the selected + bare code in `value_source_value`, leaving an `observation`'s + `value_as_string` empty; unmapped or target-invalid coded values remain + `0`. + Other unsupported `value[x]` forms and Observation components do not + populate separate converted values. A + numeric comparator (`<`, `<=`, `>`, `>=`) is represented only by a + measurement's `operator_concept_id`; units remain source text and have + `unit_concept_id` of `0`. + + A standard OMOP `concept_id` is selected for each eligible primary clinical coding + after considering all of the resource's supplied codings. An unambiguous + coded medication route is resolved independently to + `drug_exposure.route_concept_id`. Alongside the OMOP rows grouped by + table (`tables`), the response carries `mappings` (an entry for every + supported source coding from a resource that shaped a row, linked back to + that row; some, such + as demographic null flavors, are reported without being resolved), + `provider_role_contexts` (source role details linked to provider rows), + `dropped` (resources that could not be shaped into a row), + `diagnostics` (explicit references that could not safely create a link, + and conflicting or unsupported Patient demographic extensions), + `vocab_version` (the OMOP vocabulary release codes were resolved + against), and a small `summary` of the resolution outcomes. A `concept_id` of `0` is reported, not omitted (OMOP "no matching concept" semantics): it covers both a coding with no standard match (`UNMAPPED`) and an unverified suggestion for a text-only resource - (`UNCHECKED`). Only the primary clinical coding is resolved, so - `gender`/`race`/`ethnicity`/`visit`/`value`/`unit` `concept_id`s are - always `0`; the one populated non-resolved concept is measurement - `operator_concept_id`, set from a value comparator (`<`, `<=`, `>`, `>=`) - rather than the resolver. Each `*_source_value` carries the verbatim FHIR - coding (`system#code`), and `*_type_concept_id` is set to `32817` (EHR). + (`UNCHECKED`). Visit and unit concept fields currently remain `0`; + Person demographics and Provider recorded gender follow the policies + above. Coded Observation values may populate `value_as_concept_id`. + Concepts set by a fixed convention rather than terminology resolution + are measurement `operator_concept_id`, set from a value comparator (`<`, + `<=`, `>`, `>=`), and the multiple-race concepts described above. Clinical + `*_source_value` fields contain the selected FHIR code (or source text + for text-only resources). + The corresponding selected `mappings` entry preserves the coding system + and full source-coding provenance. + Known OID-form coding systems are accepted as either FHIR OID URNs (for + example, `urn:oid:2.16.840.1.113883.6.1` for LOINC) or bare OIDs, and + are normalized to their canonical system URLs before terminology + resolution. `mappings[].source_system` reports that canonical URL, so the + OID and URL forms produce the same mapping. An unknown OID is not + rewritten and may be `UNMAPPED`. + Other `*_source_value` fields preserve row-specific raw source values, + such as resource identifiers, names, units, or status codes. + `MedicationRequest` uses `32838` (EHR prescription) for + `drug_type_concept_id`; other current resources use `32817` (EHR). This + is a coarse provenance policy: it does not infer patient-reported, + medication-history, or other more-specific type concepts from FHIR + status fields. + + Dates and datetimes: + - A `*_date` is the calendar date (`YYYY-MM-DD`) of a source value with + at least day precision. A `*_datetime` is set only when that value + has a time of day, as local time without a UTC offset + (`YYYY-MM-DDTHH:MM:SS`, with fractional seconds to microseconds when + supplied): `2024-01-15T23:30:00-05:00` becomes `2024-01-15T23:30:00`. + A datetime without a timezone is read as local time. + - A partial date (`2024` or `2024-03`) leaves both columns unset. It + still counts as that source's value, so later sources in the list + below are not used. + - Free-text timing (such as `onsetString`), `Age` and `Range` forms, and + values that are not dates are ignored, so a later source in the list + is used if there is one. + - Other than the sources below and the same-day ends of single-event + medication records, no date is imputed: partial dates are not + completed, and a row is not dated from another resource such as its + Encounter. + - Valid date values never cause a request to be rejected, and the response + does not report which date elements were unusable. An on-hold + MedicationAdministration is an eligibility exception: it needs a supplied + effective start as evidence that administration began. + + Timing sources, in priority order where several are listed: + - `Patient`: `birthDate` sets `year_of_birth`, `month_of_birth`, and + `day_of_birth` from the parts it supplies; `birth_datetime` is not + set. `deceasedDateTime` sets the `death` dates. + - `Practitioner`: `birthDate` sets the provider's `year_of_birth`. + - `Encounter`: `period.start` and `period.end`. + - `Condition`: start from `onsetDateTime` or `onsetPeriod.start`, then + `recordedDate` (when the condition was recorded, not when it began); + end from `abatementDateTime` or `abatementPeriod.end`. + - `Procedure`: start from `performedDateTime` or + `performedPeriod.start`; end from `performedPeriod.end` only. + - `Observation`: `effectiveDateTime`, `effectivePeriod.start`, or + `effectiveInstant`. + - `AllergyIntolerance`: `recordedDate`, then `onsetDateTime` or + `onsetPeriod.start`. + - `MedicationStatement`: start from `effectiveDateTime` or + `effectivePeriod.start`; end and `verbatim_end_date` from + `effectivePeriod.end`. `dateAsserted` records when the statement was + made and is not used. + - `MedicationAdministration`: an `effectiveDateTime` is a single event + that sets both start and end; an `effectivePeriod` sets the start + and, when present, the end and `verbatim_end_date`. + - `Immunization`: `occurrenceDateTime` is a single event that sets both + start and end; `expirationDate` is not used. + - `MedicationRequest`: `authoredOn`, the order date, sets the start; it + is not evidence of administration. No end is set, and the validity + period is not used as an exposure duration. + - Differences from the Vulcan maps: `birth_datetime` is not set from + `birthDate`, Condition also reads `onsetPeriod.start`, and + AllergyIntolerance falls back to its onset when `recordedDate` is + missing. + + Medication details: + - For `MedicationRequest`, `dispenseRequest.numberOfRepeatsAllowed` sets + `refills` and a whole-day `expectedSupplyDuration` sets `days_supply`. + - All non-empty dosage text is preserved in `sig`. + - Coded dosage routes and `Immunization.route` are target-validated in + the OMOP Route domain. Conflicting routes are left unset; route + codings shared by every dosage instruction identify the same route. + - `Immunization.lotNumber` is preserved in `lot_number`. Medication codes are resolved whether they appear inline (`medicationCodeableConcept`) or via a `medicationReference` to a contained, relative (`Type/id`), or bundle-entry (`urn:uuid`) `Medication` resource. + A reference that resolves to another resource type is dropped even when it + supplies display text; an unresolved or display-only reference may use + its display as text-only medication input. Resources that cannot be shaped into a row — a medication with no usable - code, resolvable reference, or display, or any clinical resource whose - subject/patient reference cannot be tied to a person — are reported under - `dropped` rather than emitted as blank rows. The - bundle must contain at least one Patient resource. + code, resolvable reference, or display; any clinical resource whose + subject/patient reference cannot be tied to a person; or an event excluded + by eligibility — are reported under `dropped` rather than emitted as blank + rows. The Bundle must contain at least one Patient resource. + + Structural references resolve only to top-level resources supplied in the + request, by `Type/id` or an exactly matching Bundle `fullUrl` (including + `urn:uuid`). Contained references are supported for medication code lookup + and `PractitionerRole.practitioner` enrichment; the latter also supports + exact request-local identifier matching without a remote lookup. Every + nonzero structural foreign key targets a row in the same response. Missing + optional links remain unset without a diagnostic; an explicit optional + reference that is unresolved, ambiguous, conflicting with the row's + person, or unsupported remains unset and is returned in `diagnostics` + with its source path and outcome. + + All row IDs start at `1` for each request and are not stable or global. + For clinical conversion rows whose resource supplies an `id`, `mappings` + associates each row with that source FHIR resource ID. A `person` row + retains the Patient ID or its first identifier value in + `person_source_value`, when present; other reference and derived rows do + not uniformly carry a FHIR resource ID. Input resources without those + source identifiers cannot be correlated across responses from the + returned rows alone. Consumers combining responses need to establish + their own stable keys and remap every primary and foreign key together. Parameters ---------- fhir_resources : typing.Dict[str, typing.Any] FHIR resources (single resource or Bundle). Must contain at least one Patient resource. Supported row-producing resources are Patient, - Encounter, Condition, Procedure, MedicationRequest, + Location, Organization, HealthcareService, Practitioner, + PractitionerRole, Encounter, Condition, Procedure, MedicationRequest, MedicationStatement, MedicationAdministration, Immunization, Observation, and AllergyIntolerance. Standalone Medication resources are consumed by medication references rather than mapped to their own - table. Other resource types are accepted but ignored. + table. Unsupported resource types are accepted in a Bundle but ignored. + + vocab_version : typing.Optional[str] + OMOP vocabulary release to use for coded concept resolution. If + omitted or empty, the API uses its default release. Specify a + release explicitly when reproducibility matters. The response's + `vocab_version`, when present, identifies the release used. request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -148,6 +426,7 @@ def create( "resourceType": "MedicationRequest", "id": "medreq-1", "status": "active", + "intent": "order", "subject": {"reference": "Patient/patient-1"}, "medicationReference": {"reference": "#med0"}, "authoredOn": "2024-01-16", @@ -172,7 +451,9 @@ def create( }, ) """ - _response = self._raw_client.create(fhir_resources=fhir_resources, request_options=request_options) + _response = self._raw_client.create( + fhir_resources=fhir_resources, vocab_version=vocab_version, request_options=request_options + ) return _response.data @@ -192,75 +473,353 @@ def with_raw_response(self) -> AsyncRawFhir2OmopClient: return self._raw_client async def create( - self, *, fhir_resources: typing.Dict[str, typing.Any], request_options: typing.Optional[RequestOptions] = None + self, + *, + fhir_resources: typing.Dict[str, typing.Any], + vocab_version: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, ) -> CreateOmopResponse: """ - Maps a FHIR R4 resource or Bundle into OMOP Common Data Model v5.4 rows - (person, visit_occurrence, condition_occurrence, drug_exposure, - procedure_occurrence, measurement, observation). - - Resource support is intentionally limited to the OMOP tables returned by - this endpoint: - - `Patient` -> `person` + Maps a FHIR R4 resource or Bundle into OMOP Common Data Model v5.4 rows, + grouped by destination table in `tables`. + + Set `vocab_version` to select the OMOP vocabulary release used for coded + concept resolution. If omitted or empty, the API uses its default + release. The response's `vocab_version`, when present, identifies the + release used. Specify a release explicitly when reproducibility matters. + + Standards basis: [FHIR R4 (v4.0.1)](https://hl7.org/fhir/R4/) defines + the accepted source elements and [OMOP CDM + v5.4](https://ohdsi.github.io/CommonDataModel/cdm54.html) defines the + output columns. The published [Vulcan FHIR-to-OMOP IG + v1.0.0](https://hl7.org/fhir/uv/omop/) is an informative FHIR R5 + baseline; this endpoint documents and implements the equivalent R4 + source elements, rather than accepting R5-only fields. + + This response is a mapping result, not a complete CDM load pipeline. + When a source cannot supply a field that CDM v5.4 requires, the row is + still returned with that field unset; the value is not inferred. Common + cases are `year_of_birth` without a usable `birthDate`, + `drug_exposure_end_date` without an explicit end or single-event timing, + and a required event date (such as `condition_start_date`, + `procedure_date`, or `death_date`) whose source has no timing with at + least day precision. Apply your own policy to such rows before loading + them into a strictly conformant CDM instance. + + Current resource coverage: + - `Patient` -> `person`; `deceased[x]` can also produce `death`, the + first address can produce `location`, and more than one supplied race + produces `observation` race rows (see Patient demographics below) + - `observation_period` -> one request-local derived row per person, + spanning the populated dates of that person's visit, clinical, and + death rows; this is not enrollment or capture-completeness evidence + - `Location` -> `location` and `care_site` + - `Organization` -> `care_site`; its first address can produce `location` + - `HealthcareService` -> `care_site` + - `Practitioner` and `PractitionerRole` -> `provider` - `Encounter` -> `visit_occurrence` - `Condition` -> `condition_occurrence` - `Procedure` -> `procedure_occurrence` - `MedicationRequest`, `MedicationStatement`, and `MedicationAdministration` -> `drug_exposure` - `Immunization` -> `drug_exposure` - - `Observation` with a numeric `valueQuantity`, `valueInteger`, or - numeric-looking `valueString` (for example `"<2"`) -> `measurement` - - non-numeric `Observation` -> `observation` + - `Observation` -> `measurement` or `observation`. For coded + Observations, the resolved OMOP concept domain selects the table; value + form only breaks ties. For text-only Observations, numeric values route + to `measurement` and nonnumeric values to `observation`. - `AllergyIntolerance` -> `observation` - `Medication` is supported only as reference data for medication - resources; it is not emitted as its own row because OMOP CDM has no - Medication table. Other reference/admin resources such as `Practitioner`, - `Organization`, `Location`, `Coverage`, and `Claim`, and clinical - workflow/document resources such as `DiagnosticReport`, `ServiceRequest`, - `CarePlan`, `DocumentReference`, `Composition`, `Specimen`, and - `DeviceUseStatement`, are currently accepted in a Bundle but are not - shaped into OMOP rows. Unsupported resource types are ignored rather than - listed under `dropped`; `dropped` is reserved for supported resource types - that were missing the subject/patient, code, or medication reference data - needed to produce a valid row. - - Each resource's primary clinical coding is resolved to a standard OMOP - `concept_id`. Alongside the OMOP rows grouped by table (`tables`), the - response carries `mappings` (how each source coding resolved, linked back - to the row it produced), `dropped` (resources that could not be shaped - into a row), `vocab_version` (the OMOP vocabulary release codes were - resolved against), and a small `summary` of the resolution outcomes. + `Medication` is reference data for medication resources; it does not + create its own row because OMOP CDM has no Medication table. Administrative + linkages (provider, care site, and location) are best-effort and limited to + references supplied in the request. `Patient.managingOrganization` is a + record custodian, not a care-delivery site. Provider specialty is not + mapped. Recorded `Practitioner.gender` is distinct from Person + demographics: `male` and `female` resolve to validated OMOP Gender + concepts in `provider.gender_concept_id`; `other`, `unknown`, and absent + gender remain unmapped. `Address.country` is resolved to `location.country_concept_id`, + and CMS Place of Service codings in `Location.type` are resolved to + `care_site.place_of_service_concept_id`. + A `PractitionerRole` that identifies one supplied `Practitioner` aliases + that canonical provider: by a top-level structural reference, a + parent-contained `#id` reference, or an exact `identifier.system` and + `identifier.value` match against a top-level Practitioner. No remote + identifier lookup is performed. When `Reference.type` is present it must + be `Practitioner`; duplicate contained IDs and identifier matches are + ambiguous. An explicit reference that is unresolved, ambiguous, or + unsupported retains a role-fallback provider row and is returned in + `diagnostics`. `provider_role_contexts` preserves role-specific + specialty and care-site context that a canonical OMOP provider row cannot + represent together. + + Patient demographics: + - Sex at birth, race, and ethnicity are read from these US Core + extensions, with their US Core 6.1.0 structures and value sets, on + any Patient (US Core profile conformance is not required). Sex at + birth falls back to Patient `gender`. Other extensions, including US + Core sex and gender identity, are ignored. + - Birth sex: + `http://hl7.org/fhir/us/core/StructureDefinition/us-core-birthsex` + (`valueCode` from `http://hl7.org/fhir/us/core/ValueSet/birthsex`) + - Race: + `http://hl7.org/fhir/us/core/StructureDefinition/us-core-race` + (`ombCategory` from + `http://hl7.org/fhir/us/core/ValueSet/omb-race-category`) + - Ethnicity: + `http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity` + (`ombCategory` from + `http://hl7.org/fhir/us/core/ValueSet/omb-ethnicity-category`) + - `gender_concept_id` is sex at birth. A supplied birth sex always + decides it: `M` and `F` are resolved; `UNK`, `ASKU`, `OTH`, a code + outside the value set, conflicting values, and a birth sex without + `valueCode` leave it `0`, and Patient `gender` is not used. + - Without a birth sex, Patient `gender` `male` or `female` is resolved + under the OMOP convention that the supplied gender represents sex at + birth; `other` and `unknown` keep concept `0`. + - `gender_source_value` is the chosen source code (`F` for birth sex, + `female` for Patient `gender`). + - Each race category is resolved separately, and null flavors (`UNK`, + `ASKU`) are ignored. One distinct standard race sets + `race_concept_id`. More than one sets it to `1546847` (More than one + race) and adds one `observation` row per race, with + `observation_concept_id` `4013886` (Race), the race in + `value_as_concept_id`, `observation_type_concept_id` `32817`, the + category code in `value_source_value`, and no + `observation_source_value` or `observation_date`. A loading pipeline + that requires `observation_date` must apply its own date policy. + - The single non-null ethnicity category is resolved, and null flavors + are ignored; more than one distinct category leaves + `ethnicity_concept_id` `0`. Ethnicity is not derived from race, and + no demographic is inferred from names, addresses, or other + extensions. + - `race_source_value` and `ethnicity_source_value` list every supplied + category and detailed code in source order, joined with `|`, or the + extension text when no code is supplied. Detailed codes and text are + not resolved. + - Every supplied birth sex, gender, and OMB category code has a + `mappings` entry whose `note` names its source and outcome. When a + birth sex is supplied, Patient `gender` is reported unselected with + the note `FHIR administrative gender; not used, birth sex supplied`. + Conflicting values and a birth sex without `valueCode` are also + returned in `diagnostics` with path `extension:birthsex` or + `extension:ethnicity`. `summary` counts each demographic field once, + as described under `Summary`. + - Differences from the reference conventions: Vulcan's example gender + ConceptMap maps `other` and `unknown` to concepts, which stay `0` + here; more than one race follows the OHDSI THEMIS convention, also + used by Vulcan, rather than the CDM 5.4 note that mixed races use + `0`; and Vulcan's suggested `observation` rows for multiple + ethnicities are not produced. + + `DiagnosticReport`, `ServiceRequest`, `CarePlan`, `DocumentReference`, + `Composition`, `Specimen`, `DeviceUseStatement`, `Coverage`, `Claim`, and + other unsupported resource types are accepted in a Bundle but ignored: they + create no row and no `dropped` entry. `dropped` is reserved for supported + row-producing resources that cannot safely produce a positive OMOP row: + required subject/patient, clinical code/text, or medication data may be + unusable, or the resource may explicitly negate or fail the documented + clinical-event eligibility policy. A single-Patient Bundle uses the sole + Patient only when `subject`/`patient` is absent. An explicit subject/patient + reference that is unresolved, ambiguous, or unsupported drops the clinical + resource in every request scope. + + Eligibility distinguishes clinical validity, performed/taken evidence, and + administrative workflow state. Checks run before terminology resolution; + entered-in-error and explicitly non-performed events never produce a positive + clinical-event row, and an unrecognized required status fails closed. Condition + requires absent or confirmed canonical-HL7 `verificationStatus`; clinical course is not a + diagnosis-role mapping. Procedures accept completed or stopped events. + Medication requests are prescription evidence only: `doNotPerform`, drafts, + cancellations, and non-authorizing intents are dropped. Medication statements + accept active, completed, stopped, or on-hold reported use; administrations + accept completed, in-progress, on-hold, or stopped events. An on-hold + administration also needs an `effectiveDateTime` or `effectivePeriod.start` + that supplies start evidence; a valid partial date remains an undated row. + An on-hold administration is started evidence that is temporarily paused and + expected to continue. Immunizations require completed + status. Observations require final, + amended, or corrected status; registered, + preliminary, cancelled, entered-in-error, unknown, and missing statuses are + dropped. AllergyIntolerance accepts absent, unconfirmed, or confirmed canonical-HL7 + `verificationStatus` as a reported allergy, but drops refuted, + entered-in-error, and unreadable supplied verification statuses. Encounter + accepts arrived, triaged, in-progress, onleave, or finished status, but drops + planned, cancelled, entered-in-error, unknown, and missing statuses. An eligible + encounter without `Period.end` remains a partial visit; no end date is invented. + + Coded Observation routing is selected from the resolved OMOP concept + domain. Numeric and nonnumeric `value[x]` forms establish the preferred + target only when the code is valid for both tables. A text-only + Observation has no resolver target, so numeric values route to + `measurement` and nonnumeric values to `observation`. Numeric values + populate `value_as_number` in the selected row; other non-coded values + populate `value_as_string` for an `observation` or `value_source_value` + for a `measurement`. Coded `valueCodeableConcept` values are resolved + against the selected row's `value_as_concept_id` and use the selected + bare code in `value_source_value`, leaving an `observation`'s + `value_as_string` empty; unmapped or target-invalid coded values remain + `0`. + Other unsupported `value[x]` forms and Observation components do not + populate separate converted values. A + numeric comparator (`<`, `<=`, `>`, `>=`) is represented only by a + measurement's `operator_concept_id`; units remain source text and have + `unit_concept_id` of `0`. + + A standard OMOP `concept_id` is selected for each eligible primary clinical coding + after considering all of the resource's supplied codings. An unambiguous + coded medication route is resolved independently to + `drug_exposure.route_concept_id`. Alongside the OMOP rows grouped by + table (`tables`), the response carries `mappings` (an entry for every + supported source coding from a resource that shaped a row, linked back to + that row; some, such + as demographic null flavors, are reported without being resolved), + `provider_role_contexts` (source role details linked to provider rows), + `dropped` (resources that could not be shaped into a row), + `diagnostics` (explicit references that could not safely create a link, + and conflicting or unsupported Patient demographic extensions), + `vocab_version` (the OMOP vocabulary release codes were resolved + against), and a small `summary` of the resolution outcomes. A `concept_id` of `0` is reported, not omitted (OMOP "no matching concept" semantics): it covers both a coding with no standard match (`UNMAPPED`) and an unverified suggestion for a text-only resource - (`UNCHECKED`). Only the primary clinical coding is resolved, so - `gender`/`race`/`ethnicity`/`visit`/`value`/`unit` `concept_id`s are - always `0`; the one populated non-resolved concept is measurement - `operator_concept_id`, set from a value comparator (`<`, `<=`, `>`, `>=`) - rather than the resolver. Each `*_source_value` carries the verbatim FHIR - coding (`system#code`), and `*_type_concept_id` is set to `32817` (EHR). + (`UNCHECKED`). Visit and unit concept fields currently remain `0`; + Person demographics and Provider recorded gender follow the policies + above. Coded Observation values may populate `value_as_concept_id`. + Concepts set by a fixed convention rather than terminology resolution + are measurement `operator_concept_id`, set from a value comparator (`<`, + `<=`, `>`, `>=`), and the multiple-race concepts described above. Clinical + `*_source_value` fields contain the selected FHIR code (or source text + for text-only resources). + The corresponding selected `mappings` entry preserves the coding system + and full source-coding provenance. + Known OID-form coding systems are accepted as either FHIR OID URNs (for + example, `urn:oid:2.16.840.1.113883.6.1` for LOINC) or bare OIDs, and + are normalized to their canonical system URLs before terminology + resolution. `mappings[].source_system` reports that canonical URL, so the + OID and URL forms produce the same mapping. An unknown OID is not + rewritten and may be `UNMAPPED`. + Other `*_source_value` fields preserve row-specific raw source values, + such as resource identifiers, names, units, or status codes. + `MedicationRequest` uses `32838` (EHR prescription) for + `drug_type_concept_id`; other current resources use `32817` (EHR). This + is a coarse provenance policy: it does not infer patient-reported, + medication-history, or other more-specific type concepts from FHIR + status fields. + + Dates and datetimes: + - A `*_date` is the calendar date (`YYYY-MM-DD`) of a source value with + at least day precision. A `*_datetime` is set only when that value + has a time of day, as local time without a UTC offset + (`YYYY-MM-DDTHH:MM:SS`, with fractional seconds to microseconds when + supplied): `2024-01-15T23:30:00-05:00` becomes `2024-01-15T23:30:00`. + A datetime without a timezone is read as local time. + - A partial date (`2024` or `2024-03`) leaves both columns unset. It + still counts as that source's value, so later sources in the list + below are not used. + - Free-text timing (such as `onsetString`), `Age` and `Range` forms, and + values that are not dates are ignored, so a later source in the list + is used if there is one. + - Other than the sources below and the same-day ends of single-event + medication records, no date is imputed: partial dates are not + completed, and a row is not dated from another resource such as its + Encounter. + - Valid date values never cause a request to be rejected, and the response + does not report which date elements were unusable. An on-hold + MedicationAdministration is an eligibility exception: it needs a supplied + effective start as evidence that administration began. + + Timing sources, in priority order where several are listed: + - `Patient`: `birthDate` sets `year_of_birth`, `month_of_birth`, and + `day_of_birth` from the parts it supplies; `birth_datetime` is not + set. `deceasedDateTime` sets the `death` dates. + - `Practitioner`: `birthDate` sets the provider's `year_of_birth`. + - `Encounter`: `period.start` and `period.end`. + - `Condition`: start from `onsetDateTime` or `onsetPeriod.start`, then + `recordedDate` (when the condition was recorded, not when it began); + end from `abatementDateTime` or `abatementPeriod.end`. + - `Procedure`: start from `performedDateTime` or + `performedPeriod.start`; end from `performedPeriod.end` only. + - `Observation`: `effectiveDateTime`, `effectivePeriod.start`, or + `effectiveInstant`. + - `AllergyIntolerance`: `recordedDate`, then `onsetDateTime` or + `onsetPeriod.start`. + - `MedicationStatement`: start from `effectiveDateTime` or + `effectivePeriod.start`; end and `verbatim_end_date` from + `effectivePeriod.end`. `dateAsserted` records when the statement was + made and is not used. + - `MedicationAdministration`: an `effectiveDateTime` is a single event + that sets both start and end; an `effectivePeriod` sets the start + and, when present, the end and `verbatim_end_date`. + - `Immunization`: `occurrenceDateTime` is a single event that sets both + start and end; `expirationDate` is not used. + - `MedicationRequest`: `authoredOn`, the order date, sets the start; it + is not evidence of administration. No end is set, and the validity + period is not used as an exposure duration. + - Differences from the Vulcan maps: `birth_datetime` is not set from + `birthDate`, Condition also reads `onsetPeriod.start`, and + AllergyIntolerance falls back to its onset when `recordedDate` is + missing. + + Medication details: + - For `MedicationRequest`, `dispenseRequest.numberOfRepeatsAllowed` sets + `refills` and a whole-day `expectedSupplyDuration` sets `days_supply`. + - All non-empty dosage text is preserved in `sig`. + - Coded dosage routes and `Immunization.route` are target-validated in + the OMOP Route domain. Conflicting routes are left unset; route + codings shared by every dosage instruction identify the same route. + - `Immunization.lotNumber` is preserved in `lot_number`. Medication codes are resolved whether they appear inline (`medicationCodeableConcept`) or via a `medicationReference` to a contained, relative (`Type/id`), or bundle-entry (`urn:uuid`) `Medication` resource. + A reference that resolves to another resource type is dropped even when it + supplies display text; an unresolved or display-only reference may use + its display as text-only medication input. Resources that cannot be shaped into a row — a medication with no usable - code, resolvable reference, or display, or any clinical resource whose - subject/patient reference cannot be tied to a person — are reported under - `dropped` rather than emitted as blank rows. The - bundle must contain at least one Patient resource. + code, resolvable reference, or display; any clinical resource whose + subject/patient reference cannot be tied to a person; or an event excluded + by eligibility — are reported under `dropped` rather than emitted as blank + rows. The Bundle must contain at least one Patient resource. + + Structural references resolve only to top-level resources supplied in the + request, by `Type/id` or an exactly matching Bundle `fullUrl` (including + `urn:uuid`). Contained references are supported for medication code lookup + and `PractitionerRole.practitioner` enrichment; the latter also supports + exact request-local identifier matching without a remote lookup. Every + nonzero structural foreign key targets a row in the same response. Missing + optional links remain unset without a diagnostic; an explicit optional + reference that is unresolved, ambiguous, conflicting with the row's + person, or unsupported remains unset and is returned in `diagnostics` + with its source path and outcome. + + All row IDs start at `1` for each request and are not stable or global. + For clinical conversion rows whose resource supplies an `id`, `mappings` + associates each row with that source FHIR resource ID. A `person` row + retains the Patient ID or its first identifier value in + `person_source_value`, when present; other reference and derived rows do + not uniformly carry a FHIR resource ID. Input resources without those + source identifiers cannot be correlated across responses from the + returned rows alone. Consumers combining responses need to establish + their own stable keys and remap every primary and foreign key together. Parameters ---------- fhir_resources : typing.Dict[str, typing.Any] FHIR resources (single resource or Bundle). Must contain at least one Patient resource. Supported row-producing resources are Patient, - Encounter, Condition, Procedure, MedicationRequest, + Location, Organization, HealthcareService, Practitioner, + PractitionerRole, Encounter, Condition, Procedure, MedicationRequest, MedicationStatement, MedicationAdministration, Immunization, Observation, and AllergyIntolerance. Standalone Medication resources are consumed by medication references rather than mapped to their own - table. Other resource types are accepted but ignored. + table. Unsupported resource types are accepted in a Bundle but ignored. + + vocab_version : typing.Optional[str] + OMOP vocabulary release to use for coded concept resolution. If + omitted or empty, the API uses its default release. Specify a + release explicitly when reproducibility matters. The response's + `vocab_version`, when present, identifies the release used. request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -318,6 +877,7 @@ async def main() -> None: "resourceType": "MedicationRequest", "id": "medreq-1", "status": "active", + "intent": "order", "subject": {"reference": "Patient/patient-1"}, "medicationReference": {"reference": "#med0"}, "authoredOn": "2024-01-16", @@ -345,5 +905,7 @@ async def main() -> None: asyncio.run(main()) """ - _response = await self._raw_client.create(fhir_resources=fhir_resources, request_options=request_options) + _response = await self._raw_client.create( + fhir_resources=fhir_resources, vocab_version=vocab_version, request_options=request_options + ) return _response.data diff --git a/src/phenoml/fhir2omop/errors/__init__.py b/src/phenoml/fhir2omop/errors/__init__.py index 8e6eb58e..3d0326c1 100644 --- a/src/phenoml/fhir2omop/errors/__init__.py +++ b/src/phenoml/fhir2omop/errors/__init__.py @@ -8,12 +8,10 @@ if typing.TYPE_CHECKING: from .bad_request_error import BadRequestError from .internal_server_error import InternalServerError - from .service_unavailable_error import ServiceUnavailableError from .unauthorized_error import UnauthorizedError _dynamic_imports: typing.Dict[str, str] = { "BadRequestError": ".bad_request_error", "InternalServerError": ".internal_server_error", - "ServiceUnavailableError": ".service_unavailable_error", "UnauthorizedError": ".unauthorized_error", } @@ -39,4 +37,4 @@ def __dir__(): return sorted(lazy_attrs) -__all__ = ["BadRequestError", "InternalServerError", "ServiceUnavailableError", "UnauthorizedError"] +__all__ = ["BadRequestError", "InternalServerError", "UnauthorizedError"] diff --git a/src/phenoml/fhir2omop/errors/service_unavailable_error.py b/src/phenoml/fhir2omop/errors/service_unavailable_error.py deleted file mode 100644 index 1e7c99e9..00000000 --- a/src/phenoml/fhir2omop/errors/service_unavailable_error.py +++ /dev/null @@ -1,10 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -from ...core.api_error import ApiError - - -class ServiceUnavailableError(ApiError): - def __init__(self, body: typing.Any, headers: typing.Optional[typing.Dict[str, str]] = None): - super().__init__(status_code=503, headers=headers, body=body) diff --git a/src/phenoml/fhir2omop/raw_client.py b/src/phenoml/fhir2omop/raw_client.py index 51990acf..9c766eee 100644 --- a/src/phenoml/fhir2omop/raw_client.py +++ b/src/phenoml/fhir2omop/raw_client.py @@ -11,7 +11,6 @@ from ..core.request_options import RequestOptions from .errors.bad_request_error import BadRequestError from .errors.internal_server_error import InternalServerError -from .errors.service_unavailable_error import ServiceUnavailableError from .errors.unauthorized_error import UnauthorizedError from .types.create_omop_response import CreateOmopResponse from pydantic import ValidationError @@ -25,75 +24,353 @@ def __init__(self, *, client_wrapper: SyncClientWrapper): self._client_wrapper = client_wrapper def create( - self, *, fhir_resources: typing.Dict[str, typing.Any], request_options: typing.Optional[RequestOptions] = None + self, + *, + fhir_resources: typing.Dict[str, typing.Any], + vocab_version: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, ) -> HttpResponse[CreateOmopResponse]: """ - Maps a FHIR R4 resource or Bundle into OMOP Common Data Model v5.4 rows - (person, visit_occurrence, condition_occurrence, drug_exposure, - procedure_occurrence, measurement, observation). + Maps a FHIR R4 resource or Bundle into OMOP Common Data Model v5.4 rows, + grouped by destination table in `tables`. - Resource support is intentionally limited to the OMOP tables returned by - this endpoint: - - `Patient` -> `person` + Set `vocab_version` to select the OMOP vocabulary release used for coded + concept resolution. If omitted or empty, the API uses its default + release. The response's `vocab_version`, when present, identifies the + release used. Specify a release explicitly when reproducibility matters. + + Standards basis: [FHIR R4 (v4.0.1)](https://hl7.org/fhir/R4/) defines + the accepted source elements and [OMOP CDM + v5.4](https://ohdsi.github.io/CommonDataModel/cdm54.html) defines the + output columns. The published [Vulcan FHIR-to-OMOP IG + v1.0.0](https://hl7.org/fhir/uv/omop/) is an informative FHIR R5 + baseline; this endpoint documents and implements the equivalent R4 + source elements, rather than accepting R5-only fields. + + This response is a mapping result, not a complete CDM load pipeline. + When a source cannot supply a field that CDM v5.4 requires, the row is + still returned with that field unset; the value is not inferred. Common + cases are `year_of_birth` without a usable `birthDate`, + `drug_exposure_end_date` without an explicit end or single-event timing, + and a required event date (such as `condition_start_date`, + `procedure_date`, or `death_date`) whose source has no timing with at + least day precision. Apply your own policy to such rows before loading + them into a strictly conformant CDM instance. + + Current resource coverage: + - `Patient` -> `person`; `deceased[x]` can also produce `death`, the + first address can produce `location`, and more than one supplied race + produces `observation` race rows (see Patient demographics below) + - `observation_period` -> one request-local derived row per person, + spanning the populated dates of that person's visit, clinical, and + death rows; this is not enrollment or capture-completeness evidence + - `Location` -> `location` and `care_site` + - `Organization` -> `care_site`; its first address can produce `location` + - `HealthcareService` -> `care_site` + - `Practitioner` and `PractitionerRole` -> `provider` - `Encounter` -> `visit_occurrence` - `Condition` -> `condition_occurrence` - `Procedure` -> `procedure_occurrence` - `MedicationRequest`, `MedicationStatement`, and `MedicationAdministration` -> `drug_exposure` - `Immunization` -> `drug_exposure` - - `Observation` with a numeric `valueQuantity`, `valueInteger`, or - numeric-looking `valueString` (for example `"<2"`) -> `measurement` - - non-numeric `Observation` -> `observation` + - `Observation` -> `measurement` or `observation`. For coded + Observations, the resolved OMOP concept domain selects the table; value + form only breaks ties. For text-only Observations, numeric values route + to `measurement` and nonnumeric values to `observation`. - `AllergyIntolerance` -> `observation` - `Medication` is supported only as reference data for medication - resources; it is not emitted as its own row because OMOP CDM has no - Medication table. Other reference/admin resources such as `Practitioner`, - `Organization`, `Location`, `Coverage`, and `Claim`, and clinical - workflow/document resources such as `DiagnosticReport`, `ServiceRequest`, - `CarePlan`, `DocumentReference`, `Composition`, `Specimen`, and - `DeviceUseStatement`, are currently accepted in a Bundle but are not - shaped into OMOP rows. Unsupported resource types are ignored rather than - listed under `dropped`; `dropped` is reserved for supported resource types - that were missing the subject/patient, code, or medication reference data - needed to produce a valid row. - - Each resource's primary clinical coding is resolved to a standard OMOP - `concept_id`. Alongside the OMOP rows grouped by table (`tables`), the - response carries `mappings` (how each source coding resolved, linked back - to the row it produced), `dropped` (resources that could not be shaped - into a row), `vocab_version` (the OMOP vocabulary release codes were - resolved against), and a small `summary` of the resolution outcomes. + `Medication` is reference data for medication resources; it does not + create its own row because OMOP CDM has no Medication table. Administrative + linkages (provider, care site, and location) are best-effort and limited to + references supplied in the request. `Patient.managingOrganization` is a + record custodian, not a care-delivery site. Provider specialty is not + mapped. Recorded `Practitioner.gender` is distinct from Person + demographics: `male` and `female` resolve to validated OMOP Gender + concepts in `provider.gender_concept_id`; `other`, `unknown`, and absent + gender remain unmapped. `Address.country` is resolved to `location.country_concept_id`, + and CMS Place of Service codings in `Location.type` are resolved to + `care_site.place_of_service_concept_id`. + A `PractitionerRole` that identifies one supplied `Practitioner` aliases + that canonical provider: by a top-level structural reference, a + parent-contained `#id` reference, or an exact `identifier.system` and + `identifier.value` match against a top-level Practitioner. No remote + identifier lookup is performed. When `Reference.type` is present it must + be `Practitioner`; duplicate contained IDs and identifier matches are + ambiguous. An explicit reference that is unresolved, ambiguous, or + unsupported retains a role-fallback provider row and is returned in + `diagnostics`. `provider_role_contexts` preserves role-specific + specialty and care-site context that a canonical OMOP provider row cannot + represent together. + + Patient demographics: + - Sex at birth, race, and ethnicity are read from these US Core + extensions, with their US Core 6.1.0 structures and value sets, on + any Patient (US Core profile conformance is not required). Sex at + birth falls back to Patient `gender`. Other extensions, including US + Core sex and gender identity, are ignored. + - Birth sex: + `http://hl7.org/fhir/us/core/StructureDefinition/us-core-birthsex` + (`valueCode` from `http://hl7.org/fhir/us/core/ValueSet/birthsex`) + - Race: + `http://hl7.org/fhir/us/core/StructureDefinition/us-core-race` + (`ombCategory` from + `http://hl7.org/fhir/us/core/ValueSet/omb-race-category`) + - Ethnicity: + `http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity` + (`ombCategory` from + `http://hl7.org/fhir/us/core/ValueSet/omb-ethnicity-category`) + - `gender_concept_id` is sex at birth. A supplied birth sex always + decides it: `M` and `F` are resolved; `UNK`, `ASKU`, `OTH`, a code + outside the value set, conflicting values, and a birth sex without + `valueCode` leave it `0`, and Patient `gender` is not used. + - Without a birth sex, Patient `gender` `male` or `female` is resolved + under the OMOP convention that the supplied gender represents sex at + birth; `other` and `unknown` keep concept `0`. + - `gender_source_value` is the chosen source code (`F` for birth sex, + `female` for Patient `gender`). + - Each race category is resolved separately, and null flavors (`UNK`, + `ASKU`) are ignored. One distinct standard race sets + `race_concept_id`. More than one sets it to `1546847` (More than one + race) and adds one `observation` row per race, with + `observation_concept_id` `4013886` (Race), the race in + `value_as_concept_id`, `observation_type_concept_id` `32817`, the + category code in `value_source_value`, and no + `observation_source_value` or `observation_date`. A loading pipeline + that requires `observation_date` must apply its own date policy. + - The single non-null ethnicity category is resolved, and null flavors + are ignored; more than one distinct category leaves + `ethnicity_concept_id` `0`. Ethnicity is not derived from race, and + no demographic is inferred from names, addresses, or other + extensions. + - `race_source_value` and `ethnicity_source_value` list every supplied + category and detailed code in source order, joined with `|`, or the + extension text when no code is supplied. Detailed codes and text are + not resolved. + - Every supplied birth sex, gender, and OMB category code has a + `mappings` entry whose `note` names its source and outcome. When a + birth sex is supplied, Patient `gender` is reported unselected with + the note `FHIR administrative gender; not used, birth sex supplied`. + Conflicting values and a birth sex without `valueCode` are also + returned in `diagnostics` with path `extension:birthsex` or + `extension:ethnicity`. `summary` counts each demographic field once, + as described under `Summary`. + - Differences from the reference conventions: Vulcan's example gender + ConceptMap maps `other` and `unknown` to concepts, which stay `0` + here; more than one race follows the OHDSI THEMIS convention, also + used by Vulcan, rather than the CDM 5.4 note that mixed races use + `0`; and Vulcan's suggested `observation` rows for multiple + ethnicities are not produced. + + `DiagnosticReport`, `ServiceRequest`, `CarePlan`, `DocumentReference`, + `Composition`, `Specimen`, `DeviceUseStatement`, `Coverage`, `Claim`, and + other unsupported resource types are accepted in a Bundle but ignored: they + create no row and no `dropped` entry. `dropped` is reserved for supported + row-producing resources that cannot safely produce a positive OMOP row: + required subject/patient, clinical code/text, or medication data may be + unusable, or the resource may explicitly negate or fail the documented + clinical-event eligibility policy. A single-Patient Bundle uses the sole + Patient only when `subject`/`patient` is absent. An explicit subject/patient + reference that is unresolved, ambiguous, or unsupported drops the clinical + resource in every request scope. + + Eligibility distinguishes clinical validity, performed/taken evidence, and + administrative workflow state. Checks run before terminology resolution; + entered-in-error and explicitly non-performed events never produce a positive + clinical-event row, and an unrecognized required status fails closed. Condition + requires absent or confirmed canonical-HL7 `verificationStatus`; clinical course is not a + diagnosis-role mapping. Procedures accept completed or stopped events. + Medication requests are prescription evidence only: `doNotPerform`, drafts, + cancellations, and non-authorizing intents are dropped. Medication statements + accept active, completed, stopped, or on-hold reported use; administrations + accept completed, in-progress, on-hold, or stopped events. An on-hold + administration also needs an `effectiveDateTime` or `effectivePeriod.start` + that supplies start evidence; a valid partial date remains an undated row. + An on-hold administration is started evidence that is temporarily paused and + expected to continue. Immunizations require completed + status. Observations require final, + amended, or corrected status; registered, + preliminary, cancelled, entered-in-error, unknown, and missing statuses are + dropped. AllergyIntolerance accepts absent, unconfirmed, or confirmed canonical-HL7 + `verificationStatus` as a reported allergy, but drops refuted, + entered-in-error, and unreadable supplied verification statuses. Encounter + accepts arrived, triaged, in-progress, onleave, or finished status, but drops + planned, cancelled, entered-in-error, unknown, and missing statuses. An eligible + encounter without `Period.end` remains a partial visit; no end date is invented. + + Coded Observation routing is selected from the resolved OMOP concept + domain. Numeric and nonnumeric `value[x]` forms establish the preferred + target only when the code is valid for both tables. A text-only + Observation has no resolver target, so numeric values route to + `measurement` and nonnumeric values to `observation`. Numeric values + populate `value_as_number` in the selected row; other non-coded values + populate `value_as_string` for an `observation` or `value_source_value` + for a `measurement`. Coded `valueCodeableConcept` values are resolved + against the selected row's `value_as_concept_id` and use the selected + bare code in `value_source_value`, leaving an `observation`'s + `value_as_string` empty; unmapped or target-invalid coded values remain + `0`. + Other unsupported `value[x]` forms and Observation components do not + populate separate converted values. A + numeric comparator (`<`, `<=`, `>`, `>=`) is represented only by a + measurement's `operator_concept_id`; units remain source text and have + `unit_concept_id` of `0`. + + A standard OMOP `concept_id` is selected for each eligible primary clinical coding + after considering all of the resource's supplied codings. An unambiguous + coded medication route is resolved independently to + `drug_exposure.route_concept_id`. Alongside the OMOP rows grouped by + table (`tables`), the response carries `mappings` (an entry for every + supported source coding from a resource that shaped a row, linked back to + that row; some, such + as demographic null flavors, are reported without being resolved), + `provider_role_contexts` (source role details linked to provider rows), + `dropped` (resources that could not be shaped into a row), + `diagnostics` (explicit references that could not safely create a link, + and conflicting or unsupported Patient demographic extensions), + `vocab_version` (the OMOP vocabulary release codes were resolved + against), and a small `summary` of the resolution outcomes. A `concept_id` of `0` is reported, not omitted (OMOP "no matching concept" semantics): it covers both a coding with no standard match (`UNMAPPED`) and an unverified suggestion for a text-only resource - (`UNCHECKED`). Only the primary clinical coding is resolved, so - `gender`/`race`/`ethnicity`/`visit`/`value`/`unit` `concept_id`s are - always `0`; the one populated non-resolved concept is measurement - `operator_concept_id`, set from a value comparator (`<`, `<=`, `>`, `>=`) - rather than the resolver. Each `*_source_value` carries the verbatim FHIR - coding (`system#code`), and `*_type_concept_id` is set to `32817` (EHR). + (`UNCHECKED`). Visit and unit concept fields currently remain `0`; + Person demographics and Provider recorded gender follow the policies + above. Coded Observation values may populate `value_as_concept_id`. + Concepts set by a fixed convention rather than terminology resolution + are measurement `operator_concept_id`, set from a value comparator (`<`, + `<=`, `>`, `>=`), and the multiple-race concepts described above. Clinical + `*_source_value` fields contain the selected FHIR code (or source text + for text-only resources). + The corresponding selected `mappings` entry preserves the coding system + and full source-coding provenance. + Known OID-form coding systems are accepted as either FHIR OID URNs (for + example, `urn:oid:2.16.840.1.113883.6.1` for LOINC) or bare OIDs, and + are normalized to their canonical system URLs before terminology + resolution. `mappings[].source_system` reports that canonical URL, so the + OID and URL forms produce the same mapping. An unknown OID is not + rewritten and may be `UNMAPPED`. + Other `*_source_value` fields preserve row-specific raw source values, + such as resource identifiers, names, units, or status codes. + `MedicationRequest` uses `32838` (EHR prescription) for + `drug_type_concept_id`; other current resources use `32817` (EHR). This + is a coarse provenance policy: it does not infer patient-reported, + medication-history, or other more-specific type concepts from FHIR + status fields. + + Dates and datetimes: + - A `*_date` is the calendar date (`YYYY-MM-DD`) of a source value with + at least day precision. A `*_datetime` is set only when that value + has a time of day, as local time without a UTC offset + (`YYYY-MM-DDTHH:MM:SS`, with fractional seconds to microseconds when + supplied): `2024-01-15T23:30:00-05:00` becomes `2024-01-15T23:30:00`. + A datetime without a timezone is read as local time. + - A partial date (`2024` or `2024-03`) leaves both columns unset. It + still counts as that source's value, so later sources in the list + below are not used. + - Free-text timing (such as `onsetString`), `Age` and `Range` forms, and + values that are not dates are ignored, so a later source in the list + is used if there is one. + - Other than the sources below and the same-day ends of single-event + medication records, no date is imputed: partial dates are not + completed, and a row is not dated from another resource such as its + Encounter. + - Valid date values never cause a request to be rejected, and the response + does not report which date elements were unusable. An on-hold + MedicationAdministration is an eligibility exception: it needs a supplied + effective start as evidence that administration began. + + Timing sources, in priority order where several are listed: + - `Patient`: `birthDate` sets `year_of_birth`, `month_of_birth`, and + `day_of_birth` from the parts it supplies; `birth_datetime` is not + set. `deceasedDateTime` sets the `death` dates. + - `Practitioner`: `birthDate` sets the provider's `year_of_birth`. + - `Encounter`: `period.start` and `period.end`. + - `Condition`: start from `onsetDateTime` or `onsetPeriod.start`, then + `recordedDate` (when the condition was recorded, not when it began); + end from `abatementDateTime` or `abatementPeriod.end`. + - `Procedure`: start from `performedDateTime` or + `performedPeriod.start`; end from `performedPeriod.end` only. + - `Observation`: `effectiveDateTime`, `effectivePeriod.start`, or + `effectiveInstant`. + - `AllergyIntolerance`: `recordedDate`, then `onsetDateTime` or + `onsetPeriod.start`. + - `MedicationStatement`: start from `effectiveDateTime` or + `effectivePeriod.start`; end and `verbatim_end_date` from + `effectivePeriod.end`. `dateAsserted` records when the statement was + made and is not used. + - `MedicationAdministration`: an `effectiveDateTime` is a single event + that sets both start and end; an `effectivePeriod` sets the start + and, when present, the end and `verbatim_end_date`. + - `Immunization`: `occurrenceDateTime` is a single event that sets both + start and end; `expirationDate` is not used. + - `MedicationRequest`: `authoredOn`, the order date, sets the start; it + is not evidence of administration. No end is set, and the validity + period is not used as an exposure duration. + - Differences from the Vulcan maps: `birth_datetime` is not set from + `birthDate`, Condition also reads `onsetPeriod.start`, and + AllergyIntolerance falls back to its onset when `recordedDate` is + missing. + + Medication details: + - For `MedicationRequest`, `dispenseRequest.numberOfRepeatsAllowed` sets + `refills` and a whole-day `expectedSupplyDuration` sets `days_supply`. + - All non-empty dosage text is preserved in `sig`. + - Coded dosage routes and `Immunization.route` are target-validated in + the OMOP Route domain. Conflicting routes are left unset; route + codings shared by every dosage instruction identify the same route. + - `Immunization.lotNumber` is preserved in `lot_number`. Medication codes are resolved whether they appear inline (`medicationCodeableConcept`) or via a `medicationReference` to a contained, relative (`Type/id`), or bundle-entry (`urn:uuid`) `Medication` resource. + A reference that resolves to another resource type is dropped even when it + supplies display text; an unresolved or display-only reference may use + its display as text-only medication input. Resources that cannot be shaped into a row — a medication with no usable - code, resolvable reference, or display, or any clinical resource whose - subject/patient reference cannot be tied to a person — are reported under - `dropped` rather than emitted as blank rows. The - bundle must contain at least one Patient resource. + code, resolvable reference, or display; any clinical resource whose + subject/patient reference cannot be tied to a person; or an event excluded + by eligibility — are reported under `dropped` rather than emitted as blank + rows. The Bundle must contain at least one Patient resource. + + Structural references resolve only to top-level resources supplied in the + request, by `Type/id` or an exactly matching Bundle `fullUrl` (including + `urn:uuid`). Contained references are supported for medication code lookup + and `PractitionerRole.practitioner` enrichment; the latter also supports + exact request-local identifier matching without a remote lookup. Every + nonzero structural foreign key targets a row in the same response. Missing + optional links remain unset without a diagnostic; an explicit optional + reference that is unresolved, ambiguous, conflicting with the row's + person, or unsupported remains unset and is returned in `diagnostics` + with its source path and outcome. + + All row IDs start at `1` for each request and are not stable or global. + For clinical conversion rows whose resource supplies an `id`, `mappings` + associates each row with that source FHIR resource ID. A `person` row + retains the Patient ID or its first identifier value in + `person_source_value`, when present; other reference and derived rows do + not uniformly carry a FHIR resource ID. Input resources without those + source identifiers cannot be correlated across responses from the + returned rows alone. Consumers combining responses need to establish + their own stable keys and remap every primary and foreign key together. Parameters ---------- fhir_resources : typing.Dict[str, typing.Any] FHIR resources (single resource or Bundle). Must contain at least one Patient resource. Supported row-producing resources are Patient, - Encounter, Condition, Procedure, MedicationRequest, + Location, Organization, HealthcareService, Practitioner, + PractitionerRole, Encounter, Condition, Procedure, MedicationRequest, MedicationStatement, MedicationAdministration, Immunization, Observation, and AllergyIntolerance. Standalone Medication resources are consumed by medication references rather than mapped to their own - table. Other resource types are accepted but ignored. + table. Unsupported resource types are accepted in a Bundle but ignored. + + vocab_version : typing.Optional[str] + OMOP vocabulary release to use for coded concept resolution. If + omitted or empty, the API uses its default release. Specify a + release explicitly when reproducibility matters. The response's + `vocab_version`, when present, identifies the release used. request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -107,6 +384,7 @@ def create( "fhir2omop/create", method="POST", json={ + "vocab_version": vocab_version, "fhir_resources": fhir_resources, }, headers={ @@ -158,17 +436,6 @@ def create( ), ), ) - if _response.status_code == 503: - raise ServiceUnavailableError( - headers=dict(_response.headers), - body=typing.cast( - typing.Any, - parse_obj_as( - type_=typing.Any, # type: ignore - object_=_response.json(), - ), - ), - ) _response_json = _response.json() except JSONDecodeError: raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) @@ -184,75 +451,353 @@ def __init__(self, *, client_wrapper: AsyncClientWrapper): self._client_wrapper = client_wrapper async def create( - self, *, fhir_resources: typing.Dict[str, typing.Any], request_options: typing.Optional[RequestOptions] = None + self, + *, + fhir_resources: typing.Dict[str, typing.Any], + vocab_version: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, ) -> AsyncHttpResponse[CreateOmopResponse]: """ - Maps a FHIR R4 resource or Bundle into OMOP Common Data Model v5.4 rows - (person, visit_occurrence, condition_occurrence, drug_exposure, - procedure_occurrence, measurement, observation). + Maps a FHIR R4 resource or Bundle into OMOP Common Data Model v5.4 rows, + grouped by destination table in `tables`. - Resource support is intentionally limited to the OMOP tables returned by - this endpoint: - - `Patient` -> `person` + Set `vocab_version` to select the OMOP vocabulary release used for coded + concept resolution. If omitted or empty, the API uses its default + release. The response's `vocab_version`, when present, identifies the + release used. Specify a release explicitly when reproducibility matters. + + Standards basis: [FHIR R4 (v4.0.1)](https://hl7.org/fhir/R4/) defines + the accepted source elements and [OMOP CDM + v5.4](https://ohdsi.github.io/CommonDataModel/cdm54.html) defines the + output columns. The published [Vulcan FHIR-to-OMOP IG + v1.0.0](https://hl7.org/fhir/uv/omop/) is an informative FHIR R5 + baseline; this endpoint documents and implements the equivalent R4 + source elements, rather than accepting R5-only fields. + + This response is a mapping result, not a complete CDM load pipeline. + When a source cannot supply a field that CDM v5.4 requires, the row is + still returned with that field unset; the value is not inferred. Common + cases are `year_of_birth` without a usable `birthDate`, + `drug_exposure_end_date` without an explicit end or single-event timing, + and a required event date (such as `condition_start_date`, + `procedure_date`, or `death_date`) whose source has no timing with at + least day precision. Apply your own policy to such rows before loading + them into a strictly conformant CDM instance. + + Current resource coverage: + - `Patient` -> `person`; `deceased[x]` can also produce `death`, the + first address can produce `location`, and more than one supplied race + produces `observation` race rows (see Patient demographics below) + - `observation_period` -> one request-local derived row per person, + spanning the populated dates of that person's visit, clinical, and + death rows; this is not enrollment or capture-completeness evidence + - `Location` -> `location` and `care_site` + - `Organization` -> `care_site`; its first address can produce `location` + - `HealthcareService` -> `care_site` + - `Practitioner` and `PractitionerRole` -> `provider` - `Encounter` -> `visit_occurrence` - `Condition` -> `condition_occurrence` - `Procedure` -> `procedure_occurrence` - `MedicationRequest`, `MedicationStatement`, and `MedicationAdministration` -> `drug_exposure` - `Immunization` -> `drug_exposure` - - `Observation` with a numeric `valueQuantity`, `valueInteger`, or - numeric-looking `valueString` (for example `"<2"`) -> `measurement` - - non-numeric `Observation` -> `observation` + - `Observation` -> `measurement` or `observation`. For coded + Observations, the resolved OMOP concept domain selects the table; value + form only breaks ties. For text-only Observations, numeric values route + to `measurement` and nonnumeric values to `observation`. - `AllergyIntolerance` -> `observation` - `Medication` is supported only as reference data for medication - resources; it is not emitted as its own row because OMOP CDM has no - Medication table. Other reference/admin resources such as `Practitioner`, - `Organization`, `Location`, `Coverage`, and `Claim`, and clinical - workflow/document resources such as `DiagnosticReport`, `ServiceRequest`, - `CarePlan`, `DocumentReference`, `Composition`, `Specimen`, and - `DeviceUseStatement`, are currently accepted in a Bundle but are not - shaped into OMOP rows. Unsupported resource types are ignored rather than - listed under `dropped`; `dropped` is reserved for supported resource types - that were missing the subject/patient, code, or medication reference data - needed to produce a valid row. - - Each resource's primary clinical coding is resolved to a standard OMOP - `concept_id`. Alongside the OMOP rows grouped by table (`tables`), the - response carries `mappings` (how each source coding resolved, linked back - to the row it produced), `dropped` (resources that could not be shaped - into a row), `vocab_version` (the OMOP vocabulary release codes were - resolved against), and a small `summary` of the resolution outcomes. + `Medication` is reference data for medication resources; it does not + create its own row because OMOP CDM has no Medication table. Administrative + linkages (provider, care site, and location) are best-effort and limited to + references supplied in the request. `Patient.managingOrganization` is a + record custodian, not a care-delivery site. Provider specialty is not + mapped. Recorded `Practitioner.gender` is distinct from Person + demographics: `male` and `female` resolve to validated OMOP Gender + concepts in `provider.gender_concept_id`; `other`, `unknown`, and absent + gender remain unmapped. `Address.country` is resolved to `location.country_concept_id`, + and CMS Place of Service codings in `Location.type` are resolved to + `care_site.place_of_service_concept_id`. + A `PractitionerRole` that identifies one supplied `Practitioner` aliases + that canonical provider: by a top-level structural reference, a + parent-contained `#id` reference, or an exact `identifier.system` and + `identifier.value` match against a top-level Practitioner. No remote + identifier lookup is performed. When `Reference.type` is present it must + be `Practitioner`; duplicate contained IDs and identifier matches are + ambiguous. An explicit reference that is unresolved, ambiguous, or + unsupported retains a role-fallback provider row and is returned in + `diagnostics`. `provider_role_contexts` preserves role-specific + specialty and care-site context that a canonical OMOP provider row cannot + represent together. + + Patient demographics: + - Sex at birth, race, and ethnicity are read from these US Core + extensions, with their US Core 6.1.0 structures and value sets, on + any Patient (US Core profile conformance is not required). Sex at + birth falls back to Patient `gender`. Other extensions, including US + Core sex and gender identity, are ignored. + - Birth sex: + `http://hl7.org/fhir/us/core/StructureDefinition/us-core-birthsex` + (`valueCode` from `http://hl7.org/fhir/us/core/ValueSet/birthsex`) + - Race: + `http://hl7.org/fhir/us/core/StructureDefinition/us-core-race` + (`ombCategory` from + `http://hl7.org/fhir/us/core/ValueSet/omb-race-category`) + - Ethnicity: + `http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity` + (`ombCategory` from + `http://hl7.org/fhir/us/core/ValueSet/omb-ethnicity-category`) + - `gender_concept_id` is sex at birth. A supplied birth sex always + decides it: `M` and `F` are resolved; `UNK`, `ASKU`, `OTH`, a code + outside the value set, conflicting values, and a birth sex without + `valueCode` leave it `0`, and Patient `gender` is not used. + - Without a birth sex, Patient `gender` `male` or `female` is resolved + under the OMOP convention that the supplied gender represents sex at + birth; `other` and `unknown` keep concept `0`. + - `gender_source_value` is the chosen source code (`F` for birth sex, + `female` for Patient `gender`). + - Each race category is resolved separately, and null flavors (`UNK`, + `ASKU`) are ignored. One distinct standard race sets + `race_concept_id`. More than one sets it to `1546847` (More than one + race) and adds one `observation` row per race, with + `observation_concept_id` `4013886` (Race), the race in + `value_as_concept_id`, `observation_type_concept_id` `32817`, the + category code in `value_source_value`, and no + `observation_source_value` or `observation_date`. A loading pipeline + that requires `observation_date` must apply its own date policy. + - The single non-null ethnicity category is resolved, and null flavors + are ignored; more than one distinct category leaves + `ethnicity_concept_id` `0`. Ethnicity is not derived from race, and + no demographic is inferred from names, addresses, or other + extensions. + - `race_source_value` and `ethnicity_source_value` list every supplied + category and detailed code in source order, joined with `|`, or the + extension text when no code is supplied. Detailed codes and text are + not resolved. + - Every supplied birth sex, gender, and OMB category code has a + `mappings` entry whose `note` names its source and outcome. When a + birth sex is supplied, Patient `gender` is reported unselected with + the note `FHIR administrative gender; not used, birth sex supplied`. + Conflicting values and a birth sex without `valueCode` are also + returned in `diagnostics` with path `extension:birthsex` or + `extension:ethnicity`. `summary` counts each demographic field once, + as described under `Summary`. + - Differences from the reference conventions: Vulcan's example gender + ConceptMap maps `other` and `unknown` to concepts, which stay `0` + here; more than one race follows the OHDSI THEMIS convention, also + used by Vulcan, rather than the CDM 5.4 note that mixed races use + `0`; and Vulcan's suggested `observation` rows for multiple + ethnicities are not produced. + + `DiagnosticReport`, `ServiceRequest`, `CarePlan`, `DocumentReference`, + `Composition`, `Specimen`, `DeviceUseStatement`, `Coverage`, `Claim`, and + other unsupported resource types are accepted in a Bundle but ignored: they + create no row and no `dropped` entry. `dropped` is reserved for supported + row-producing resources that cannot safely produce a positive OMOP row: + required subject/patient, clinical code/text, or medication data may be + unusable, or the resource may explicitly negate or fail the documented + clinical-event eligibility policy. A single-Patient Bundle uses the sole + Patient only when `subject`/`patient` is absent. An explicit subject/patient + reference that is unresolved, ambiguous, or unsupported drops the clinical + resource in every request scope. + + Eligibility distinguishes clinical validity, performed/taken evidence, and + administrative workflow state. Checks run before terminology resolution; + entered-in-error and explicitly non-performed events never produce a positive + clinical-event row, and an unrecognized required status fails closed. Condition + requires absent or confirmed canonical-HL7 `verificationStatus`; clinical course is not a + diagnosis-role mapping. Procedures accept completed or stopped events. + Medication requests are prescription evidence only: `doNotPerform`, drafts, + cancellations, and non-authorizing intents are dropped. Medication statements + accept active, completed, stopped, or on-hold reported use; administrations + accept completed, in-progress, on-hold, or stopped events. An on-hold + administration also needs an `effectiveDateTime` or `effectivePeriod.start` + that supplies start evidence; a valid partial date remains an undated row. + An on-hold administration is started evidence that is temporarily paused and + expected to continue. Immunizations require completed + status. Observations require final, + amended, or corrected status; registered, + preliminary, cancelled, entered-in-error, unknown, and missing statuses are + dropped. AllergyIntolerance accepts absent, unconfirmed, or confirmed canonical-HL7 + `verificationStatus` as a reported allergy, but drops refuted, + entered-in-error, and unreadable supplied verification statuses. Encounter + accepts arrived, triaged, in-progress, onleave, or finished status, but drops + planned, cancelled, entered-in-error, unknown, and missing statuses. An eligible + encounter without `Period.end` remains a partial visit; no end date is invented. + + Coded Observation routing is selected from the resolved OMOP concept + domain. Numeric and nonnumeric `value[x]` forms establish the preferred + target only when the code is valid for both tables. A text-only + Observation has no resolver target, so numeric values route to + `measurement` and nonnumeric values to `observation`. Numeric values + populate `value_as_number` in the selected row; other non-coded values + populate `value_as_string` for an `observation` or `value_source_value` + for a `measurement`. Coded `valueCodeableConcept` values are resolved + against the selected row's `value_as_concept_id` and use the selected + bare code in `value_source_value`, leaving an `observation`'s + `value_as_string` empty; unmapped or target-invalid coded values remain + `0`. + Other unsupported `value[x]` forms and Observation components do not + populate separate converted values. A + numeric comparator (`<`, `<=`, `>`, `>=`) is represented only by a + measurement's `operator_concept_id`; units remain source text and have + `unit_concept_id` of `0`. + + A standard OMOP `concept_id` is selected for each eligible primary clinical coding + after considering all of the resource's supplied codings. An unambiguous + coded medication route is resolved independently to + `drug_exposure.route_concept_id`. Alongside the OMOP rows grouped by + table (`tables`), the response carries `mappings` (an entry for every + supported source coding from a resource that shaped a row, linked back to + that row; some, such + as demographic null flavors, are reported without being resolved), + `provider_role_contexts` (source role details linked to provider rows), + `dropped` (resources that could not be shaped into a row), + `diagnostics` (explicit references that could not safely create a link, + and conflicting or unsupported Patient demographic extensions), + `vocab_version` (the OMOP vocabulary release codes were resolved + against), and a small `summary` of the resolution outcomes. A `concept_id` of `0` is reported, not omitted (OMOP "no matching concept" semantics): it covers both a coding with no standard match (`UNMAPPED`) and an unverified suggestion for a text-only resource - (`UNCHECKED`). Only the primary clinical coding is resolved, so - `gender`/`race`/`ethnicity`/`visit`/`value`/`unit` `concept_id`s are - always `0`; the one populated non-resolved concept is measurement - `operator_concept_id`, set from a value comparator (`<`, `<=`, `>`, `>=`) - rather than the resolver. Each `*_source_value` carries the verbatim FHIR - coding (`system#code`), and `*_type_concept_id` is set to `32817` (EHR). + (`UNCHECKED`). Visit and unit concept fields currently remain `0`; + Person demographics and Provider recorded gender follow the policies + above. Coded Observation values may populate `value_as_concept_id`. + Concepts set by a fixed convention rather than terminology resolution + are measurement `operator_concept_id`, set from a value comparator (`<`, + `<=`, `>`, `>=`), and the multiple-race concepts described above. Clinical + `*_source_value` fields contain the selected FHIR code (or source text + for text-only resources). + The corresponding selected `mappings` entry preserves the coding system + and full source-coding provenance. + Known OID-form coding systems are accepted as either FHIR OID URNs (for + example, `urn:oid:2.16.840.1.113883.6.1` for LOINC) or bare OIDs, and + are normalized to their canonical system URLs before terminology + resolution. `mappings[].source_system` reports that canonical URL, so the + OID and URL forms produce the same mapping. An unknown OID is not + rewritten and may be `UNMAPPED`. + Other `*_source_value` fields preserve row-specific raw source values, + such as resource identifiers, names, units, or status codes. + `MedicationRequest` uses `32838` (EHR prescription) for + `drug_type_concept_id`; other current resources use `32817` (EHR). This + is a coarse provenance policy: it does not infer patient-reported, + medication-history, or other more-specific type concepts from FHIR + status fields. + + Dates and datetimes: + - A `*_date` is the calendar date (`YYYY-MM-DD`) of a source value with + at least day precision. A `*_datetime` is set only when that value + has a time of day, as local time without a UTC offset + (`YYYY-MM-DDTHH:MM:SS`, with fractional seconds to microseconds when + supplied): `2024-01-15T23:30:00-05:00` becomes `2024-01-15T23:30:00`. + A datetime without a timezone is read as local time. + - A partial date (`2024` or `2024-03`) leaves both columns unset. It + still counts as that source's value, so later sources in the list + below are not used. + - Free-text timing (such as `onsetString`), `Age` and `Range` forms, and + values that are not dates are ignored, so a later source in the list + is used if there is one. + - Other than the sources below and the same-day ends of single-event + medication records, no date is imputed: partial dates are not + completed, and a row is not dated from another resource such as its + Encounter. + - Valid date values never cause a request to be rejected, and the response + does not report which date elements were unusable. An on-hold + MedicationAdministration is an eligibility exception: it needs a supplied + effective start as evidence that administration began. + + Timing sources, in priority order where several are listed: + - `Patient`: `birthDate` sets `year_of_birth`, `month_of_birth`, and + `day_of_birth` from the parts it supplies; `birth_datetime` is not + set. `deceasedDateTime` sets the `death` dates. + - `Practitioner`: `birthDate` sets the provider's `year_of_birth`. + - `Encounter`: `period.start` and `period.end`. + - `Condition`: start from `onsetDateTime` or `onsetPeriod.start`, then + `recordedDate` (when the condition was recorded, not when it began); + end from `abatementDateTime` or `abatementPeriod.end`. + - `Procedure`: start from `performedDateTime` or + `performedPeriod.start`; end from `performedPeriod.end` only. + - `Observation`: `effectiveDateTime`, `effectivePeriod.start`, or + `effectiveInstant`. + - `AllergyIntolerance`: `recordedDate`, then `onsetDateTime` or + `onsetPeriod.start`. + - `MedicationStatement`: start from `effectiveDateTime` or + `effectivePeriod.start`; end and `verbatim_end_date` from + `effectivePeriod.end`. `dateAsserted` records when the statement was + made and is not used. + - `MedicationAdministration`: an `effectiveDateTime` is a single event + that sets both start and end; an `effectivePeriod` sets the start + and, when present, the end and `verbatim_end_date`. + - `Immunization`: `occurrenceDateTime` is a single event that sets both + start and end; `expirationDate` is not used. + - `MedicationRequest`: `authoredOn`, the order date, sets the start; it + is not evidence of administration. No end is set, and the validity + period is not used as an exposure duration. + - Differences from the Vulcan maps: `birth_datetime` is not set from + `birthDate`, Condition also reads `onsetPeriod.start`, and + AllergyIntolerance falls back to its onset when `recordedDate` is + missing. + + Medication details: + - For `MedicationRequest`, `dispenseRequest.numberOfRepeatsAllowed` sets + `refills` and a whole-day `expectedSupplyDuration` sets `days_supply`. + - All non-empty dosage text is preserved in `sig`. + - Coded dosage routes and `Immunization.route` are target-validated in + the OMOP Route domain. Conflicting routes are left unset; route + codings shared by every dosage instruction identify the same route. + - `Immunization.lotNumber` is preserved in `lot_number`. Medication codes are resolved whether they appear inline (`medicationCodeableConcept`) or via a `medicationReference` to a contained, relative (`Type/id`), or bundle-entry (`urn:uuid`) `Medication` resource. + A reference that resolves to another resource type is dropped even when it + supplies display text; an unresolved or display-only reference may use + its display as text-only medication input. Resources that cannot be shaped into a row — a medication with no usable - code, resolvable reference, or display, or any clinical resource whose - subject/patient reference cannot be tied to a person — are reported under - `dropped` rather than emitted as blank rows. The - bundle must contain at least one Patient resource. + code, resolvable reference, or display; any clinical resource whose + subject/patient reference cannot be tied to a person; or an event excluded + by eligibility — are reported under `dropped` rather than emitted as blank + rows. The Bundle must contain at least one Patient resource. + + Structural references resolve only to top-level resources supplied in the + request, by `Type/id` or an exactly matching Bundle `fullUrl` (including + `urn:uuid`). Contained references are supported for medication code lookup + and `PractitionerRole.practitioner` enrichment; the latter also supports + exact request-local identifier matching without a remote lookup. Every + nonzero structural foreign key targets a row in the same response. Missing + optional links remain unset without a diagnostic; an explicit optional + reference that is unresolved, ambiguous, conflicting with the row's + person, or unsupported remains unset and is returned in `diagnostics` + with its source path and outcome. + + All row IDs start at `1` for each request and are not stable or global. + For clinical conversion rows whose resource supplies an `id`, `mappings` + associates each row with that source FHIR resource ID. A `person` row + retains the Patient ID or its first identifier value in + `person_source_value`, when present; other reference and derived rows do + not uniformly carry a FHIR resource ID. Input resources without those + source identifiers cannot be correlated across responses from the + returned rows alone. Consumers combining responses need to establish + their own stable keys and remap every primary and foreign key together. Parameters ---------- fhir_resources : typing.Dict[str, typing.Any] FHIR resources (single resource or Bundle). Must contain at least one Patient resource. Supported row-producing resources are Patient, - Encounter, Condition, Procedure, MedicationRequest, + Location, Organization, HealthcareService, Practitioner, + PractitionerRole, Encounter, Condition, Procedure, MedicationRequest, MedicationStatement, MedicationAdministration, Immunization, Observation, and AllergyIntolerance. Standalone Medication resources are consumed by medication references rather than mapped to their own - table. Other resource types are accepted but ignored. + table. Unsupported resource types are accepted in a Bundle but ignored. + + vocab_version : typing.Optional[str] + OMOP vocabulary release to use for coded concept resolution. If + omitted or empty, the API uses its default release. Specify a + release explicitly when reproducibility matters. The response's + `vocab_version`, when present, identifies the release used. request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -266,6 +811,7 @@ async def create( "fhir2omop/create", method="POST", json={ + "vocab_version": vocab_version, "fhir_resources": fhir_resources, }, headers={ @@ -317,17 +863,6 @@ async def create( ), ), ) - if _response.status_code == 503: - raise ServiceUnavailableError( - headers=dict(_response.headers), - body=typing.cast( - typing.Any, - parse_obj_as( - type_=typing.Any, # type: ignore - object_=_response.json(), - ), - ), - ) _response_json = _response.json() except JSONDecodeError: raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) diff --git a/src/phenoml/fhir2omop/types/__init__.py b/src/phenoml/fhir2omop/types/__init__.py index 669a67ab..c8f78f0f 100644 --- a/src/phenoml/fhir2omop/types/__init__.py +++ b/src/phenoml/fhir2omop/types/__init__.py @@ -7,6 +7,7 @@ if typing.TYPE_CHECKING: from .care_site_row import CareSiteRow + from .coding import Coding from .condition_occurrence_row import ConditionOccurrenceRow from .create_omop_response import CreateOmopResponse from .death_row import DeathRow @@ -14,17 +15,25 @@ from .drug_exposure_row import DrugExposureRow from .location_row import LocationRow from .mapping_entry import MappingEntry + from .mapping_entry_mapping_status import MappingEntryMappingStatus from .measurement_row import MeasurementRow from .observation_period_row import ObservationPeriodRow from .observation_row import ObservationRow from .omop_tables import OmopTables from .person_row import PersonRow from .procedure_occurrence_row import ProcedureOccurrenceRow + from .provider_role_care_site import ProviderRoleCareSite + from .provider_role_codeable_concept import ProviderRoleCodeableConcept + from .provider_role_context import ProviderRoleContext + from .provider_role_practitioner_identifier import ProviderRolePractitionerIdentifier from .provider_row import ProviderRow + from .reference_diagnostic import ReferenceDiagnostic + from .reference_diagnostic_outcome import ReferenceDiagnosticOutcome from .summary import Summary from .visit_occurrence_row import VisitOccurrenceRow _dynamic_imports: typing.Dict[str, str] = { "CareSiteRow": ".care_site_row", + "Coding": ".coding", "ConditionOccurrenceRow": ".condition_occurrence_row", "CreateOmopResponse": ".create_omop_response", "DeathRow": ".death_row", @@ -32,13 +41,20 @@ "DrugExposureRow": ".drug_exposure_row", "LocationRow": ".location_row", "MappingEntry": ".mapping_entry", + "MappingEntryMappingStatus": ".mapping_entry_mapping_status", "MeasurementRow": ".measurement_row", "ObservationPeriodRow": ".observation_period_row", "ObservationRow": ".observation_row", "OmopTables": ".omop_tables", "PersonRow": ".person_row", "ProcedureOccurrenceRow": ".procedure_occurrence_row", + "ProviderRoleCareSite": ".provider_role_care_site", + "ProviderRoleCodeableConcept": ".provider_role_codeable_concept", + "ProviderRoleContext": ".provider_role_context", + "ProviderRolePractitionerIdentifier": ".provider_role_practitioner_identifier", "ProviderRow": ".provider_row", + "ReferenceDiagnostic": ".reference_diagnostic", + "ReferenceDiagnosticOutcome": ".reference_diagnostic_outcome", "Summary": ".summary", "VisitOccurrenceRow": ".visit_occurrence_row", } @@ -67,6 +83,7 @@ def __dir__(): __all__ = [ "CareSiteRow", + "Coding", "ConditionOccurrenceRow", "CreateOmopResponse", "DeathRow", @@ -74,13 +91,20 @@ def __dir__(): "DrugExposureRow", "LocationRow", "MappingEntry", + "MappingEntryMappingStatus", "MeasurementRow", "ObservationPeriodRow", "ObservationRow", "OmopTables", "PersonRow", "ProcedureOccurrenceRow", + "ProviderRoleCareSite", + "ProviderRoleCodeableConcept", + "ProviderRoleContext", + "ProviderRolePractitionerIdentifier", "ProviderRow", + "ReferenceDiagnostic", + "ReferenceDiagnosticOutcome", "Summary", "VisitOccurrenceRow", ] diff --git a/src/phenoml/fhir2omop/types/coding.py b/src/phenoml/fhir2omop/types/coding.py new file mode 100644 index 00000000..f4685a51 --- /dev/null +++ b/src/phenoml/fhir2omop/types/coding.py @@ -0,0 +1,21 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel + + +class Coding(UniversalBaseModel): + system: typing.Optional[str] = None + code: typing.Optional[str] = None + display: typing.Optional[str] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/phenoml/fhir2omop/types/condition_occurrence_row.py b/src/phenoml/fhir2omop/types/condition_occurrence_row.py index 0e7d8295..56cfa531 100644 --- a/src/phenoml/fhir2omop/types/condition_occurrence_row.py +++ b/src/phenoml/fhir2omop/types/condition_occurrence_row.py @@ -10,9 +10,18 @@ class ConditionOccurrenceRow(UniversalBaseModel): condition_occurrence_id: typing.Optional[int] = None person_id: typing.Optional[int] = None condition_concept_id: typing.Optional[int] = None - condition_start_date: typing.Optional[str] = None + condition_start_date: typing.Optional[str] = pydantic.Field(default=None) + """ + Date from Condition.onsetDateTime or onsetPeriod.start, otherwise Condition.recordedDate. + """ + condition_start_datetime: typing.Optional[str] = None - condition_end_date: typing.Optional[str] = None + condition_end_date: typing.Optional[str] = pydantic.Field(default=None) + """ + Date from Condition.abatementDateTime or abatementPeriod.end. + """ + + condition_end_datetime: typing.Optional[str] = None condition_type_concept_id: typing.Optional[int] = None visit_occurrence_id: typing.Optional[int] = None provider_id: typing.Optional[int] = None diff --git a/src/phenoml/fhir2omop/types/create_omop_response.py b/src/phenoml/fhir2omop/types/create_omop_response.py index 122c5a95..1c3120a1 100644 --- a/src/phenoml/fhir2omop/types/create_omop_response.py +++ b/src/phenoml/fhir2omop/types/create_omop_response.py @@ -7,6 +7,8 @@ from .dropped_resource import DroppedResource from .mapping_entry import MappingEntry from .omop_tables import OmopTables +from .provider_role_context import ProviderRoleContext +from .reference_diagnostic import ReferenceDiagnostic from .summary import Summary @@ -16,22 +18,49 @@ class CreateOmopResponse(UniversalBaseModel): tables: typing.Optional[OmopTables] = None mappings: typing.Optional[typing.List[MappingEntry]] = pydantic.Field(default=None) """ - One entry per source coding (or one entry for a text-only resource with no coding), describing how it resolved and linking back to the row it produced. + One entry per supported source coding from a resource that shaped a row (or one entry for a text-only primary resource with no coding), describing how it resolved and linking back to the row it produced. A coded route or Observation valueCodeableConcept is a separate entry linked to its medication, vaccine, or observation row. A Patient demographic code links to its person row, or to its `observation` race row when the person has more than one race. + """ + + provider_role_contexts: typing.Optional[typing.List[ProviderRoleContext]] = pydantic.Field(default=None) + """ + Additive FHIR provenance for every supplied PractitionerRole. Each + context identifies the canonical or role-fallback provider row and + preserves source role facts that OMOP's singular provider columns + cannot represent together. """ dropped: typing.Optional[typing.List[DroppedResource]] = pydantic.Field(default=None) """ Supported resource instances that could not be shaped into an OMOP - row because required subject/patient, code, or medication reference - data was missing. Unsupported resource types are ignored and do not - appear here. + row because the subject/patient, clinical code or text, or medication + data was missing or unusable, including an explicit subject/patient + reference that was unresolved, ambiguous, or unsupported, or because + their clinical-event eligibility status excluded them. A resource that + lacks only a date or another CDM-required field is returned as a row + instead. Unsupported resource types are ignored and do not appear here. + Eligibility exclusions use stable, resource-specific `reason` codes; + other shaping failures retain an explanatory reason string. + """ + + diagnostics: typing.Optional[typing.List[ReferenceDiagnostic]] = pydantic.Field(default=None) + """ + Explanations for explicit references that could not safely produce + an OMOP link or canonicalize a `PractitionerRole` provider identity, or explicit + subject/patient references that caused a clinical row to be dropped. + Missing optional references are normal and do not produce a diagnostic. + References resolve only against resources supplied in this request. + Outcomes distinguish unresolved, ambiguous, conflicting, and unsupported + references. Patient demographic extensions that conflict, or a birth + sex without `valueCode`, are also reported here; their `path` is + `extension:birthsex` or `extension:ethnicity` and they have no + `reference`. """ vocab_version: typing.Optional[str] = pydantic.Field(default=None) """ - The OMOP vocabulary release the clinical codes were resolved against - (e.g. "v20240229"), for reproducibility. Present when at least one - coded concept was resolved. + The OMOP vocabulary release used for coded concept resolution + (for example, "v20260227"), for reproducibility. Omitted when no + vocabulary resolution was performed. """ summary: typing.Optional[Summary] = None diff --git a/src/phenoml/fhir2omop/types/death_row.py b/src/phenoml/fhir2omop/types/death_row.py index 0afd6cef..e0808e69 100644 --- a/src/phenoml/fhir2omop/types/death_row.py +++ b/src/phenoml/fhir2omop/types/death_row.py @@ -8,7 +8,11 @@ class DeathRow(UniversalBaseModel): person_id: typing.Optional[int] = None - death_date: typing.Optional[str] = None + death_date: typing.Optional[str] = pydantic.Field(default=None) + """ + Date from Patient.deceasedDateTime; unset for a boolean-only or partial value. + """ + death_datetime: typing.Optional[str] = None death_type_concept_id: typing.Optional[int] = None cause_concept_id: typing.Optional[int] = None diff --git a/src/phenoml/fhir2omop/types/drug_exposure_row.py b/src/phenoml/fhir2omop/types/drug_exposure_row.py index 48803cb4..b5455151 100644 --- a/src/phenoml/fhir2omop/types/drug_exposure_row.py +++ b/src/phenoml/fhir2omop/types/drug_exposure_row.py @@ -10,16 +10,58 @@ class DrugExposureRow(UniversalBaseModel): drug_exposure_id: typing.Optional[int] = None person_id: typing.Optional[int] = None drug_concept_id: typing.Optional[int] = None - drug_exposure_start_date: typing.Optional[str] = None + drug_exposure_start_date: typing.Optional[str] = pydantic.Field(default=None) + """ + Date from the resource's timing source, such as effective[x], occurrenceDateTime, or MedicationRequest.authoredOn. + """ + drug_exposure_start_datetime: typing.Optional[str] = None - drug_exposure_end_date: typing.Optional[str] = None + drug_exposure_end_date: typing.Optional[str] = pydantic.Field(default=None) + """ + Date from an explicit FHIR Period end, or the same day as the start for a single-event administration or immunization. Unset when the source supplies neither. + """ + + drug_exposure_end_datetime: typing.Optional[str] = None + verbatim_end_date: typing.Optional[str] = pydantic.Field(default=None) + """ + Date from an explicit FHIR Period.end only; inferred same-day ends are not verbatim source values. + """ + drug_type_concept_id: typing.Optional[int] = None stop_reason: typing.Optional[str] = None - sig: typing.Optional[str] = None + refills: typing.Optional[int] = pydantic.Field(default=None) + """ + Direct MedicationRequest.dispenseRequest.numberOfRepeatsAllowed value, when supplied. + """ + + days_supply: typing.Optional[int] = pydantic.Field(default=None) + """ + Direct positive whole-day MedicationRequest.dispenseRequest.expectedSupplyDuration; no dose or quantity conversion is applied. + """ + + sig: typing.Optional[str] = pydantic.Field(default=None) + """ + Newline-joined non-empty FHIR Dosage.text instructions in source order. + """ + + route_concept_id: typing.Optional[int] = pydantic.Field(default=None) + """ + Target-valid OMOP Route concept for an unambiguous coded FHIR route; `0` for an unmapped coded route, omitted for absent, text-only, or conflicting routes. + """ + + lot_number: typing.Optional[str] = pydantic.Field(default=None) + """ + Direct FHIR R4 Immunization.lotNumber value. + """ + visit_occurrence_id: typing.Optional[int] = None provider_id: typing.Optional[int] = None drug_source_value: typing.Optional[str] = None drug_source_concept_id: typing.Optional[int] = None + route_source_value: typing.Optional[str] = pydantic.Field(default=None) + """ + Selected source coding or text for an unambiguous FHIR route. + """ if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 diff --git a/src/phenoml/fhir2omop/types/mapping_entry.py b/src/phenoml/fhir2omop/types/mapping_entry.py index e19b7e0c..18330be3 100644 --- a/src/phenoml/fhir2omop/types/mapping_entry.py +++ b/src/phenoml/fhir2omop/types/mapping_entry.py @@ -4,11 +4,12 @@ import pydantic from ...core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .mapping_entry_mapping_status import MappingEntryMappingStatus class MappingEntry(UniversalBaseModel): """ - How one source coding (or a text-only resource's free text) resolved to an OMOP standard concept. + How one source coding (or a text-only primary resource's free text) resolved to an OMOP standard concept. `omop_table`, `omop_field`, and `omop_id` link it to the row and concept field it produced. A coded medication route is a separate entry linked to the same drug_exposure row as its medication or vaccine coding. `selected` identifies the coding rendered in the associated row's `*_source_value`; it is false for alternate codings and text-only rows. """ resource_type: typing.Optional[str] = None @@ -21,6 +22,11 @@ class MappingEntry(UniversalBaseModel): per coding, all sharing this id. """ + omop_field: typing.Optional[str] = pydantic.Field(default=None) + """ + The OMOP concept-ID field populated from this source coding, such as `condition_concept_id`, `route_concept_id`, or `race_concept_id`. + """ + source_system: typing.Optional[str] = None source_code: typing.Optional[str] = None source_name: typing.Optional[str] = None @@ -34,16 +40,38 @@ class MappingEntry(UniversalBaseModel): """ target_name: typing.Optional[str] = None - mapping_status: typing.Optional[str] = pydantic.Field(default=None) + mapping_status: typing.Optional[MappingEntryMappingStatus] = pydantic.Field(default=None) """ ALREADY_STANDARD (source coding is already a standard OMOP concept), MAPPED (source coding was mapped to a standard concept), UNCHECKED (a - standard code was suggested — e.g. for a text-only resource — but not - verified against the OMOP vocabulary, so `concept_id` stays `0`), or - UNMAPPED (no standard concept found). + standard code was suggested for a text-only resource but not verified + against the OMOP vocabulary, so `concept_id` stays `0`), or UNMAPPED + (no standard concept found). + """ + + selected: bool = pydantic.Field() + """ + Whether this source coding was selected for the linked row's `*_source_value` field. Always present; false for alternate codings and text-only rows. For a Patient demographic, it marks the code that determined the PERSON field or race `observation` row, even when that code has no concept; it is false for race and ethnicity null flavors, conflicting values, a Patient `gender` overridden by birth sex, and race categories that did not determine the field. """ - note: typing.Optional[str] = None + note: typing.Optional[str] = pydantic.Field(default=None) + """ + Additional context for the entry. A coded route is noted as + `FHIR route`. Patient demographic entries name their source and, when + not applied, why: + - `US Core birth sex`; `US Core birth sex; null flavor`; + `US Core birth sex; outside value set`; + `US Core birth sex; conflicting values` + - `FHIR administrative gender; assumed sex at birth`; + `FHIR administrative gender; not a sex-at-birth value`; + `FHIR administrative gender; not used, birth sex supplied` + - `US Core race OMB category`; + `US Core race OMB category; more than one race` (linked to its + `observation` race row); `US Core race; null flavor` + - `US Core ethnicity OMB category`; + `US Core ethnicity OMB category; conflicting categories`; + `US Core ethnicity; null flavor` + """ if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 diff --git a/src/phenoml/fhir2omop/types/mapping_entry_mapping_status.py b/src/phenoml/fhir2omop/types/mapping_entry_mapping_status.py new file mode 100644 index 00000000..655e0c44 --- /dev/null +++ b/src/phenoml/fhir2omop/types/mapping_entry_mapping_status.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +MappingEntryMappingStatus = typing.Union[ + typing.Literal["ALREADY_STANDARD", "MAPPED", "UNCHECKED", "UNMAPPED"], typing.Any +] diff --git a/src/phenoml/fhir2omop/types/measurement_row.py b/src/phenoml/fhir2omop/types/measurement_row.py index 169fc173..92fb137e 100644 --- a/src/phenoml/fhir2omop/types/measurement_row.py +++ b/src/phenoml/fhir2omop/types/measurement_row.py @@ -10,7 +10,11 @@ class MeasurementRow(UniversalBaseModel): measurement_id: typing.Optional[int] = None person_id: typing.Optional[int] = None measurement_concept_id: typing.Optional[int] = None - measurement_date: typing.Optional[str] = None + measurement_date: typing.Optional[str] = pydantic.Field(default=None) + """ + Date from Observation.effectiveDateTime, effectivePeriod.start, or effectiveInstant. + """ + measurement_datetime: typing.Optional[str] = None measurement_type_concept_id: typing.Optional[int] = None value_as_number: typing.Optional[float] = None diff --git a/src/phenoml/fhir2omop/types/observation_period_row.py b/src/phenoml/fhir2omop/types/observation_period_row.py index 8dc956f2..44c1a47a 100644 --- a/src/phenoml/fhir2omop/types/observation_period_row.py +++ b/src/phenoml/fhir2omop/types/observation_period_row.py @@ -9,8 +9,16 @@ class ObservationPeriodRow(UniversalBaseModel): observation_period_id: typing.Optional[int] = None person_id: typing.Optional[int] = None - observation_period_start_date: typing.Optional[str] = None - observation_period_end_date: typing.Optional[str] = None + observation_period_start_date: typing.Optional[str] = pydantic.Field(default=None) + """ + Earliest populated date among the person's visit, clinical, and death rows in this request; not enrollment evidence. + """ + + observation_period_end_date: typing.Optional[str] = pydantic.Field(default=None) + """ + Latest populated date, including end dates, among the person's visit, clinical, and death rows in this request; not enrollment evidence. + """ + period_type_concept_id: typing.Optional[int] = None if IS_PYDANTIC_V2: diff --git a/src/phenoml/fhir2omop/types/observation_row.py b/src/phenoml/fhir2omop/types/observation_row.py index 8a1732fb..ed55dc5f 100644 --- a/src/phenoml/fhir2omop/types/observation_row.py +++ b/src/phenoml/fhir2omop/types/observation_row.py @@ -10,7 +10,11 @@ class ObservationRow(UniversalBaseModel): observation_id: typing.Optional[int] = None person_id: typing.Optional[int] = None observation_concept_id: typing.Optional[int] = None - observation_date: typing.Optional[str] = None + observation_date: typing.Optional[str] = pydantic.Field(default=None) + """ + For an Observation, date from effectiveDateTime, effectivePeriod.start, or effectiveInstant. For an AllergyIntolerance, date from recordedDate, otherwise onsetDateTime or onsetPeriod.start. + """ + observation_datetime: typing.Optional[str] = None observation_type_concept_id: typing.Optional[int] = None value_as_number: typing.Optional[float] = None diff --git a/src/phenoml/fhir2omop/types/omop_tables.py b/src/phenoml/fhir2omop/types/omop_tables.py index 10f0cba1..e16e66cb 100644 --- a/src/phenoml/fhir2omop/types/omop_tables.py +++ b/src/phenoml/fhir2omop/types/omop_tables.py @@ -20,7 +20,11 @@ class OmopTables(UniversalBaseModel): """ - OMOP CDM v5.4 rows grouped by destination table. + OMOP CDM v5.4 rows grouped by destination table. IDs are sequential and + scoped to one response; they are not stable keys across requests. + Fields with no value are unset (omitted from the row), except concept + IDs reported as `0`. Each `*_datetime` comes from the same source as its + `*_date` and is set only when that source has a time of day. """ location: typing.Optional[typing.List[LocationRow]] = None diff --git a/src/phenoml/fhir2omop/types/person_row.py b/src/phenoml/fhir2omop/types/person_row.py index 39c31ee5..968dcafa 100644 --- a/src/phenoml/fhir2omop/types/person_row.py +++ b/src/phenoml/fhir2omop/types/person_row.py @@ -8,18 +8,74 @@ class PersonRow(UniversalBaseModel): person_id: typing.Optional[int] = None - gender_concept_id: typing.Optional[int] = None - year_of_birth: typing.Optional[int] = None - month_of_birth: typing.Optional[int] = None - day_of_birth: typing.Optional[int] = None - birth_datetime: typing.Optional[str] = None - race_concept_id: typing.Optional[int] = None - ethnicity_concept_id: typing.Optional[int] = None + gender_concept_id: typing.Optional[int] = pydantic.Field(default=None) + """ + Standard OMOP Gender concept for sex at birth, from US Core birth sex when supplied, otherwise from Patient `gender` `male` or `female`. `0` for absent, unknown, other, unsupported, or conflicting values. + """ + + year_of_birth: typing.Optional[int] = pydantic.Field(default=None) + """ + Year from Patient.birthDate. + """ + + month_of_birth: typing.Optional[int] = pydantic.Field(default=None) + """ + Month from Patient.birthDate, when it supplies one. + """ + + day_of_birth: typing.Optional[int] = pydantic.Field(default=None) + """ + Day from Patient.birthDate, when it supplies one. + """ + + birth_datetime: typing.Optional[str] = pydantic.Field(default=None) + """ + Not set; Patient.birthDate has no time of day. + """ + + race_concept_id: typing.Optional[int] = pydantic.Field(default=None) + """ + Standard OMOP Race concept from a US Core race OMB category; `1546847` (More than one race) when more than one distinct race resolves, with each race in an `observation` row. `0` when no category resolves. + """ + + ethnicity_concept_id: typing.Optional[int] = pydantic.Field(default=None) + """ + Standard OMOP Ethnicity concept from the US Core ethnicity OMB category. `0` when absent, unresolved, or conflicting; never derived from race. + """ + location_id: typing.Optional[int] = None + provider_id: typing.Optional[int] = None + care_site_id: typing.Optional[int] = None person_source_value: typing.Optional[str] = None - gender_source_value: typing.Optional[str] = None - race_source_value: typing.Optional[str] = None - ethnicity_source_value: typing.Optional[str] = None + gender_source_value: typing.Optional[str] = pydantic.Field(default=None) + """ + The selected sex-at-birth source code: the US Core birth sex `valueCode`, or Patient `gender` when no birth sex is supplied. Conflicting birth sex values are joined with `|`; empty when the birth sex has no `valueCode`. + """ + + gender_source_concept_id: typing.Optional[int] = pydantic.Field(default=None) + """ + OMOP source concept of the selected sex-at-birth code, when that code is itself an OMOP source concept; `0` otherwise, as for FHIR administrative gender codes. + """ + + race_source_value: typing.Optional[str] = pydantic.Field(default=None) + """ + Every supplied US Core race category and detailed code, joined with `|` in source order, or the extension text when no code is supplied. + """ + + race_source_concept_id: typing.Optional[int] = pydantic.Field(default=None) + """ + OMOP source concept of a single resolved race code, when that code is itself an OMOP source concept; `0` otherwise, including when more than one race resolves. + """ + + ethnicity_source_value: typing.Optional[str] = pydantic.Field(default=None) + """ + Every supplied US Core ethnicity category and detailed code, joined with `|` in source order, or the extension text when no code is supplied. + """ + + ethnicity_source_concept_id: typing.Optional[int] = pydantic.Field(default=None) + """ + OMOP source concept of the resolved ethnicity code, when that code is itself an OMOP source concept; `0` otherwise. + """ if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 diff --git a/src/phenoml/fhir2omop/types/procedure_occurrence_row.py b/src/phenoml/fhir2omop/types/procedure_occurrence_row.py index ace41677..31a8dff8 100644 --- a/src/phenoml/fhir2omop/types/procedure_occurrence_row.py +++ b/src/phenoml/fhir2omop/types/procedure_occurrence_row.py @@ -10,8 +10,18 @@ class ProcedureOccurrenceRow(UniversalBaseModel): procedure_occurrence_id: typing.Optional[int] = None person_id: typing.Optional[int] = None procedure_concept_id: typing.Optional[int] = None - procedure_date: typing.Optional[str] = None + procedure_date: typing.Optional[str] = pydantic.Field(default=None) + """ + Date from Procedure.performedDateTime or performedPeriod.start. + """ + procedure_datetime: typing.Optional[str] = None + procedure_end_date: typing.Optional[str] = pydantic.Field(default=None) + """ + Date from Procedure.performedPeriod.end. + """ + + procedure_end_datetime: typing.Optional[str] = None procedure_type_concept_id: typing.Optional[int] = None visit_occurrence_id: typing.Optional[int] = None provider_id: typing.Optional[int] = None diff --git a/src/phenoml/fhir2omop/types/provider_role_care_site.py b/src/phenoml/fhir2omop/types/provider_role_care_site.py new file mode 100644 index 00000000..89cd0ea0 --- /dev/null +++ b/src/phenoml/fhir2omop/types/provider_role_care_site.py @@ -0,0 +1,25 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel + + +class ProviderRoleCareSite(UniversalBaseModel): + path: str = pydantic.Field() + """ + The FHIR element path on the PractitionerRole. + """ + + reference: str + care_site_id: typing.Optional[int] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/phenoml/fhir2omop/types/provider_role_codeable_concept.py b/src/phenoml/fhir2omop/types/provider_role_codeable_concept.py new file mode 100644 index 00000000..247ba960 --- /dev/null +++ b/src/phenoml/fhir2omop/types/provider_role_codeable_concept.py @@ -0,0 +1,21 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .coding import Coding + + +class ProviderRoleCodeableConcept(UniversalBaseModel): + source_value: typing.Optional[str] = None + codings: typing.Optional[typing.List[Coding]] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/phenoml/fhir2omop/types/provider_role_context.py b/src/phenoml/fhir2omop/types/provider_role_context.py new file mode 100644 index 00000000..fdda53d5 --- /dev/null +++ b/src/phenoml/fhir2omop/types/provider_role_context.py @@ -0,0 +1,48 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .provider_role_care_site import ProviderRoleCareSite +from .provider_role_codeable_concept import ProviderRoleCodeableConcept +from .provider_role_practitioner_identifier import ProviderRolePractitionerIdentifier + + +class ProviderRoleContext(UniversalBaseModel): + """ + Source FHIR PractitionerRole context linked to one response-local provider row. + """ + + provider_id: int + role_source_value: typing.Optional[str] = pydantic.Field(default=None) + """ + The PractitionerRole source identity: its FHIR id, identifier value, + name, or fullUrl. When none is available, it is `PractitionerRole`. + """ + + practitioner_reference: typing.Optional[str] = pydantic.Field(default=None) + """ + The role's supplied practitioner reference, when present. + """ + + practitioner_identifier: typing.Optional[ProviderRolePractitionerIdentifier] = None + role_codes: typing.Optional[typing.List[ProviderRoleCodeableConcept]] = None + specialties: typing.Optional[typing.List[ProviderRoleCodeableConcept]] = None + care_sites: typing.Optional[typing.List[ProviderRoleCareSite]] = pydantic.Field(default=None) + """ + Every organization, healthcareService, or location reference supplied by the role. + """ + + active: typing.Optional[bool] = None + period_start: typing.Optional[str] = None + period_end: typing.Optional[str] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/phenoml/fhir2omop/types/provider_role_practitioner_identifier.py b/src/phenoml/fhir2omop/types/provider_role_practitioner_identifier.py new file mode 100644 index 00000000..e319e4fa --- /dev/null +++ b/src/phenoml/fhir2omop/types/provider_role_practitioner_identifier.py @@ -0,0 +1,24 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel + + +class ProviderRolePractitionerIdentifier(UniversalBaseModel): + """ + The logical identifier supplied on a PractitionerRole's practitioner reference. + """ + + system: typing.Optional[str] = None + value: str + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/phenoml/fhir2omop/types/provider_row.py b/src/phenoml/fhir2omop/types/provider_row.py index 4ba47455..58f1240f 100644 --- a/src/phenoml/fhir2omop/types/provider_row.py +++ b/src/phenoml/fhir2omop/types/provider_row.py @@ -13,13 +13,32 @@ class ProviderRow(UniversalBaseModel): dea: typing.Optional[str] = None specialty_concept_id: typing.Optional[int] = None care_site_id: typing.Optional[int] = None - year_of_birth: typing.Optional[int] = None - gender_concept_id: typing.Optional[int] = None - provider_source_value: typing.Optional[str] = None + year_of_birth: typing.Optional[int] = pydantic.Field(default=None) + """ + Year from Practitioner.birthDate. + """ + + gender_concept_id: typing.Optional[int] = pydantic.Field(default=None) + """ + For recorded `Practitioner.gender`, `male` and `female` resolve to validated OMOP Gender concepts. `other`, `unknown`, and absent values remain `0`. + """ + + provider_source_value: typing.Optional[str] = pydantic.Field(default=None) + """ + The source practitioner identity. A Practitioner contained by a PractitionerRole is scoped as `PractitionerRole/#` so identical local contained IDs do not collide; an id-less parent uses an explicitly marked response-local role ordinal such as `@role-index:1`. + """ + specialty_source_value: typing.Optional[str] = None specialty_source_concept_id: typing.Optional[int] = None - gender_source_value: typing.Optional[str] = None - gender_source_concept_id: typing.Optional[int] = None + gender_source_value: typing.Optional[str] = pydantic.Field(default=None) + """ + The recorded FHIR administrative-gender value for this Provider. + """ + + gender_source_concept_id: typing.Optional[int] = pydantic.Field(default=None) + """ + Remains `0` for FHIR administrative-gender enum-policy results. + """ if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 diff --git a/src/phenoml/fhir2omop/types/reference_diagnostic.py b/src/phenoml/fhir2omop/types/reference_diagnostic.py new file mode 100644 index 00000000..29def8ca --- /dev/null +++ b/src/phenoml/fhir2omop/types/reference_diagnostic.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .reference_diagnostic_outcome import ReferenceDiagnosticOutcome + + +class ReferenceDiagnostic(UniversalBaseModel): + resource_type: typing.Optional[str] = None + resource_id: typing.Optional[str] = None + path: typing.Optional[str] = pydantic.Field(default=None) + """ + FHIR element path on the source resource. + """ + + reference: typing.Optional[str] = pydantic.Field(default=None) + """ + The supplied Reference.reference value, when present. + """ + + outcome: typing.Optional[ReferenceDiagnosticOutcome] = None + reason: typing.Optional[str] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/phenoml/fhir2omop/types/reference_diagnostic_outcome.py b/src/phenoml/fhir2omop/types/reference_diagnostic_outcome.py new file mode 100644 index 00000000..a4e7b84b --- /dev/null +++ b/src/phenoml/fhir2omop/types/reference_diagnostic_outcome.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ReferenceDiagnosticOutcome = typing.Union[ + typing.Literal["UNRESOLVED", "AMBIGUOUS", "CONFLICTING", "UNSUPPORTED"], typing.Any +] diff --git a/src/phenoml/fhir2omop/types/summary.py b/src/phenoml/fhir2omop/types/summary.py index 6885bf8d..5f1bd9ac 100644 --- a/src/phenoml/fhir2omop/types/summary.py +++ b/src/phenoml/fhir2omop/types/summary.py @@ -8,31 +8,39 @@ class Summary(UniversalBaseModel): """ - The request's data-quality headline: how the coded concepts split across - resolution outcomes, and the share that was not already in a target - standard vocabulary. Each coded resource is counted once (per resolved - concept), even when it carried several codings — unlike `mappings`, which - has one entry per coding. + The request's data-quality headline: how row-producing coded concepts, + selected routes, and Patient demographic fields split, and the share + that was not already in a target standard vocabulary. Each is counted + once even when it carried several codings — unlike `mappings`, which has + one entry per coding. For example, a medication code and its selected + coded route on the same `drug_exposure` row are separate outcomes; + independently checked report-only alternate route codings do not alter + `summary`. A Patient demographic counts once per PERSON field, or per + race `observation` row, that received a non-null value; conflicting + values count once as unmapped. Null flavors, Patient `gender` `other` or + `unknown`, a Patient `gender` overridden by birth sex, and race + categories that did not determine the field are reported in `mappings` + only. """ codes_already_standard: typing.Optional[int] = pydantic.Field(default=None) """ - Coded concepts already a standard OMOP concept (ALREADY_STANDARD). + Resolution outcomes already a standard OMOP concept (ALREADY_STANDARD). """ codes_normalized: typing.Optional[int] = pydantic.Field(default=None) """ - Coded concepts mapped or suggested to a standard concept (MAPPED or UNCHECKED). + Resolution outcomes mapped or suggested to a standard concept (MAPPED or UNCHECKED). """ codes_unmapped: typing.Optional[int] = pydantic.Field(default=None) """ - Coded concepts with no standard concept found (UNMAPPED). + Resolution outcomes with no standard concept found (UNMAPPED). """ off_vocab_rate: typing.Optional[float] = pydantic.Field(default=None) """ - Share of coded concepts not already standard ((normalized + unmapped) / total). + Share of resolution outcomes not already standard ((normalized + unmapped) / total). """ if IS_PYDANTIC_V2: diff --git a/src/phenoml/fhir2omop/types/visit_occurrence_row.py b/src/phenoml/fhir2omop/types/visit_occurrence_row.py index c7b6a1ac..0897933e 100644 --- a/src/phenoml/fhir2omop/types/visit_occurrence_row.py +++ b/src/phenoml/fhir2omop/types/visit_occurrence_row.py @@ -10,9 +10,17 @@ class VisitOccurrenceRow(UniversalBaseModel): visit_occurrence_id: typing.Optional[int] = None person_id: typing.Optional[int] = None visit_concept_id: typing.Optional[int] = None - visit_start_date: typing.Optional[str] = None + visit_start_date: typing.Optional[str] = pydantic.Field(default=None) + """ + Date from Encounter.period.start. + """ + visit_start_datetime: typing.Optional[str] = None - visit_end_date: typing.Optional[str] = None + visit_end_date: typing.Optional[str] = pydantic.Field(default=None) + """ + Date from Encounter.period.end. + """ + visit_end_datetime: typing.Optional[str] = None visit_type_concept_id: typing.Optional[int] = None provider_id: typing.Optional[int] = None diff --git a/src/phenoml/implementation_guides/__init__.py b/src/phenoml/implementation_guides/__init__.py index 2d192958..8b08ecc7 100644 --- a/src/phenoml/implementation_guides/__init__.py +++ b/src/phenoml/implementation_guides/__init__.py @@ -6,15 +6,31 @@ from importlib import import_module if typing.TYPE_CHECKING: - from .types import ImplementationGuideDetail, ImplementationGuideListResponse, ImplementationGuideSummary - from .errors import BadRequestError, ForbiddenError, InternalServerError, NotFoundError, UnauthorizedError + from .types import ( + FhirImplementationGuide, + ImplementationGuideDetail, + ImplementationGuideListResponse, + ImplementationGuideSummary, + ImplementationGuideVersionDetail, + ) + from .errors import ( + BadRequestError, + ConflictError, + ForbiddenError, + InternalServerError, + NotFoundError, + UnauthorizedError, + ) from . import implementation_guides _dynamic_imports: typing.Dict[str, str] = { "BadRequestError": ".errors", + "ConflictError": ".errors", + "FhirImplementationGuide": ".types", "ForbiddenError": ".errors", "ImplementationGuideDetail": ".types", "ImplementationGuideListResponse": ".types", "ImplementationGuideSummary": ".types", + "ImplementationGuideVersionDetail": ".types", "InternalServerError": ".errors", "NotFoundError": ".errors", "UnauthorizedError": ".errors", @@ -45,10 +61,13 @@ def __dir__(): __all__ = [ "BadRequestError", + "ConflictError", + "FhirImplementationGuide", "ForbiddenError", "ImplementationGuideDetail", "ImplementationGuideListResponse", "ImplementationGuideSummary", + "ImplementationGuideVersionDetail", "InternalServerError", "NotFoundError", "UnauthorizedError", diff --git a/src/phenoml/implementation_guides/errors/__init__.py b/src/phenoml/implementation_guides/errors/__init__.py index 306a114a..29aa057d 100644 --- a/src/phenoml/implementation_guides/errors/__init__.py +++ b/src/phenoml/implementation_guides/errors/__init__.py @@ -7,12 +7,14 @@ if typing.TYPE_CHECKING: from .bad_request_error import BadRequestError + from .conflict_error import ConflictError from .forbidden_error import ForbiddenError from .internal_server_error import InternalServerError from .not_found_error import NotFoundError from .unauthorized_error import UnauthorizedError _dynamic_imports: typing.Dict[str, str] = { "BadRequestError": ".bad_request_error", + "ConflictError": ".conflict_error", "ForbiddenError": ".forbidden_error", "InternalServerError": ".internal_server_error", "NotFoundError": ".not_found_error", @@ -41,4 +43,11 @@ def __dir__(): return sorted(lazy_attrs) -__all__ = ["BadRequestError", "ForbiddenError", "InternalServerError", "NotFoundError", "UnauthorizedError"] +__all__ = [ + "BadRequestError", + "ConflictError", + "ForbiddenError", + "InternalServerError", + "NotFoundError", + "UnauthorizedError", +] diff --git a/src/phenoml/construe/errors/bad_gateway_error.py b/src/phenoml/implementation_guides/errors/conflict_error.py similarity index 69% rename from src/phenoml/construe/errors/bad_gateway_error.py rename to src/phenoml/implementation_guides/errors/conflict_error.py index dc20174a..abe1a9f5 100644 --- a/src/phenoml/construe/errors/bad_gateway_error.py +++ b/src/phenoml/implementation_guides/errors/conflict_error.py @@ -5,6 +5,6 @@ from ...core.api_error import ApiError -class BadGatewayError(ApiError): +class ConflictError(ApiError): def __init__(self, body: typing.Any, headers: typing.Optional[typing.Dict[str, str]] = None): - super().__init__(status_code=502, headers=headers, body=body) + super().__init__(status_code=409, headers=headers, body=body) diff --git a/src/phenoml/implementation_guides/implementation_guides/client.py b/src/phenoml/implementation_guides/implementation_guides/client.py index 6cc33749..d9a74c14 100644 --- a/src/phenoml/implementation_guides/implementation_guides/client.py +++ b/src/phenoml/implementation_guides/implementation_guides/client.py @@ -4,9 +4,11 @@ from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ...core.request_options import RequestOptions +from ..types.fhir_implementation_guide import FhirImplementationGuide from ..types.implementation_guide_detail import ImplementationGuideDetail from ..types.implementation_guide_list_response import ImplementationGuideListResponse from ..types.implementation_guide_summary import ImplementationGuideSummary +from ..types.implementation_guide_version_detail import ImplementationGuideVersionDetail from .raw_client import AsyncRawImplementationGuidesClient, RawImplementationGuidesClient # this is used as the default value for optional parameters @@ -136,11 +138,8 @@ def update( def delete(self, name: str, *, request_options: typing.Optional[RequestOptions] = None) -> None: """ - Deletes the stored metadata for an implementation guide — its - profile_context and timestamps. Member profiles keep their - implementation_guide assignment, so a guide still referenced by at least - one profile continues to appear in listings, just without context or - timestamps. + Deletes the guide's metadata and all its canonical package versions. + Custom profiles and their implementation-guide assignments are preserved. Parameters ---------- @@ -169,6 +168,102 @@ def delete(self, name: str, *, request_options: typing.Optional[RequestOptions] _response = self._raw_client.delete(name, request_options=request_options) return _response.data + def create_version( + self, + name: str, + *, + implementation_guide: FhirImplementationGuide, + profile_refs: typing.Sequence[str], + profile_context: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> ImplementationGuideVersionDetail: + """ + Publishes an exact package beneath this guide family. Each guide family + supports one exact package version. Publishing another version returns + `409 Conflict`. + + Parameters + ---------- + name : str + + implementation_guide : FhirImplementationGuide + + profile_refs : typing.Sequence[str] + Exact canonical `url|version` references to builtin or custom profiles. A package can contain at most 250 references. + + profile_context : typing.Optional[str] + Natural-language profile-selection context for this package. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ImplementationGuideVersionDetail + Canonical package published + + Examples + -------- + from phenoml import PhenomlClient + from phenoml.implementation_guides import FhirImplementationGuide + + client = PhenomlClient( + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", + ) + client.implementation_guides.implementation_guides.create_version( + name="name", + implementation_guide=FhirImplementationGuide( + url="url", + version="version", + ), + profile_refs=["profile_refs"], + ) + """ + _response = self._raw_client.create_version( + name, + implementation_guide=implementation_guide, + profile_refs=profile_refs, + profile_context=profile_context, + request_options=request_options, + ) + return _response.data + + def get_version( + self, name: str, version: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> ImplementationGuideVersionDetail: + """ + Parameters + ---------- + name : str + + version : str + The authored ImplementationGuide.version. It may contain letters, numbers, and the punctuation characters `.`, `_`, `~`, `+`, and `-`; it cannot be exactly `.` or `..`. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ImplementationGuideVersionDetail + Exact canonical package + + Examples + -------- + from phenoml import PhenomlClient + + client = PhenomlClient( + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", + ) + client.implementation_guides.implementation_guides.get_version( + name="name", + version="1.0.0", + ) + """ + _response = self._raw_client.get_version(name, version, request_options=request_options) + return _response.data + class AsyncImplementationGuidesClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -321,11 +416,8 @@ async def main() -> None: async def delete(self, name: str, *, request_options: typing.Optional[RequestOptions] = None) -> None: """ - Deletes the stored metadata for an implementation guide — its - profile_context and timestamps. Member profiles keep their - implementation_guide assignment, so a guide still referenced by at least - one profile continues to appear in listings, just without context or - timestamps. + Deletes the guide's metadata and all its canonical package versions. + Custom profiles and their implementation-guide assignments are preserved. Parameters ---------- @@ -361,3 +453,115 @@ async def main() -> None: """ _response = await self._raw_client.delete(name, request_options=request_options) return _response.data + + async def create_version( + self, + name: str, + *, + implementation_guide: FhirImplementationGuide, + profile_refs: typing.Sequence[str], + profile_context: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> ImplementationGuideVersionDetail: + """ + Publishes an exact package beneath this guide family. Each guide family + supports one exact package version. Publishing another version returns + `409 Conflict`. + + Parameters + ---------- + name : str + + implementation_guide : FhirImplementationGuide + + profile_refs : typing.Sequence[str] + Exact canonical `url|version` references to builtin or custom profiles. A package can contain at most 250 references. + + profile_context : typing.Optional[str] + Natural-language profile-selection context for this package. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ImplementationGuideVersionDetail + Canonical package published + + Examples + -------- + import asyncio + + from phenoml import AsyncPhenomlClient + from phenoml.implementation_guides import FhirImplementationGuide + + client = AsyncPhenomlClient( + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", + ) + + + async def main() -> None: + await client.implementation_guides.implementation_guides.create_version( + name="name", + implementation_guide=FhirImplementationGuide( + url="url", + version="version", + ), + profile_refs=["profile_refs"], + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create_version( + name, + implementation_guide=implementation_guide, + profile_refs=profile_refs, + profile_context=profile_context, + request_options=request_options, + ) + return _response.data + + async def get_version( + self, name: str, version: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> ImplementationGuideVersionDetail: + """ + Parameters + ---------- + name : str + + version : str + The authored ImplementationGuide.version. It may contain letters, numbers, and the punctuation characters `.`, `_`, `~`, `+`, and `-`; it cannot be exactly `.` or `..`. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ImplementationGuideVersionDetail + Exact canonical package + + Examples + -------- + import asyncio + + from phenoml import AsyncPhenomlClient + + client = AsyncPhenomlClient( + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", + ) + + + async def main() -> None: + await client.implementation_guides.implementation_guides.get_version( + name="name", + version="1.0.0", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.get_version(name, version, request_options=request_options) + return _response.data diff --git a/src/phenoml/implementation_guides/implementation_guides/raw_client.py b/src/phenoml/implementation_guides/implementation_guides/raw_client.py index 8700e845..9f6c6f30 100644 --- a/src/phenoml/implementation_guides/implementation_guides/raw_client.py +++ b/src/phenoml/implementation_guides/implementation_guides/raw_client.py @@ -10,14 +10,18 @@ from ...core.parse_error import ParsingError from ...core.pydantic_utilities import parse_obj_as from ...core.request_options import RequestOptions +from ...core.serialization import convert_and_respect_annotation_metadata from ..errors.bad_request_error import BadRequestError +from ..errors.conflict_error import ConflictError from ..errors.forbidden_error import ForbiddenError from ..errors.internal_server_error import InternalServerError from ..errors.not_found_error import NotFoundError from ..errors.unauthorized_error import UnauthorizedError +from ..types.fhir_implementation_guide import FhirImplementationGuide from ..types.implementation_guide_detail import ImplementationGuideDetail from ..types.implementation_guide_list_response import ImplementationGuideListResponse from ..types.implementation_guide_summary import ImplementationGuideSummary +from ..types.implementation_guide_version_detail import ImplementationGuideVersionDetail from pydantic import ValidationError # this is used as the default value for optional parameters @@ -308,11 +312,8 @@ def update( def delete(self, name: str, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[None]: """ - Deletes the stored metadata for an implementation guide — its - profile_context and timestamps. Member profiles keep their - implementation_guide assignment, so a guide still referenced by at least - one profile continues to appear in listings, just without context or - timestamps. + Deletes the guide's metadata and all its canonical package versions. + Custom profiles and their implementation-guide assignments are preserved. Parameters ---------- @@ -398,6 +399,173 @@ def delete(self, name: str, *, request_options: typing.Optional[RequestOptions] ) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + def create_version( + self, + name: str, + *, + implementation_guide: FhirImplementationGuide, + profile_refs: typing.Sequence[str], + profile_context: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ImplementationGuideVersionDetail]: + """ + Publishes an exact package beneath this guide family. Each guide family + supports one exact package version. Publishing another version returns + `409 Conflict`. + + Parameters + ---------- + name : str + + implementation_guide : FhirImplementationGuide + + profile_refs : typing.Sequence[str] + Exact canonical `url|version` references to builtin or custom profiles. A package can contain at most 250 references. + + profile_context : typing.Optional[str] + Natural-language profile-selection context for this package. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ImplementationGuideVersionDetail] + Canonical package published + """ + _response = self._client_wrapper.httpx_client.request( + f"fhir/implementation-guides/{encode_path_param(name)}/versions", + method="POST", + json={ + "implementation_guide": convert_and_respect_annotation_metadata( + object_=implementation_guide, annotation=FhirImplementationGuide, direction="write" + ), + "profile_refs": profile_refs, + "profile_context": profile_context, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ImplementationGuideVersionDetail, + parse_obj_as( + type_=ImplementationGuideVersionDetail, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 409: + raise ConflictError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + def get_version( + self, name: str, version: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[ImplementationGuideVersionDetail]: + """ + Parameters + ---------- + name : str + + version : str + The authored ImplementationGuide.version. It may contain letters, numbers, and the punctuation characters `.`, `_`, `~`, `+`, and `-`; it cannot be exactly `.` or `..`. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ImplementationGuideVersionDetail] + Exact canonical package + """ + _response = self._client_wrapper.httpx_client.request( + f"fhir/implementation-guides/{encode_path_param(name)}/versions/{encode_path_param(version)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ImplementationGuideVersionDetail, + parse_obj_as( + type_=ImplementationGuideVersionDetail, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + class AsyncRawImplementationGuidesClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -685,11 +853,8 @@ async def delete( self, name: str, *, request_options: typing.Optional[RequestOptions] = None ) -> AsyncHttpResponse[None]: """ - Deletes the stored metadata for an implementation guide — its - profile_context and timestamps. Member profiles keep their - implementation_guide assignment, so a guide still referenced by at least - one profile continues to appear in listings, just without context or - timestamps. + Deletes the guide's metadata and all its canonical package versions. + Custom profiles and their implementation-guide assignments are preserved. Parameters ---------- @@ -774,3 +939,170 @@ async def delete( status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e ) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + async def create_version( + self, + name: str, + *, + implementation_guide: FhirImplementationGuide, + profile_refs: typing.Sequence[str], + profile_context: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ImplementationGuideVersionDetail]: + """ + Publishes an exact package beneath this guide family. Each guide family + supports one exact package version. Publishing another version returns + `409 Conflict`. + + Parameters + ---------- + name : str + + implementation_guide : FhirImplementationGuide + + profile_refs : typing.Sequence[str] + Exact canonical `url|version` references to builtin or custom profiles. A package can contain at most 250 references. + + profile_context : typing.Optional[str] + Natural-language profile-selection context for this package. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ImplementationGuideVersionDetail] + Canonical package published + """ + _response = await self._client_wrapper.httpx_client.request( + f"fhir/implementation-guides/{encode_path_param(name)}/versions", + method="POST", + json={ + "implementation_guide": convert_and_respect_annotation_metadata( + object_=implementation_guide, annotation=FhirImplementationGuide, direction="write" + ), + "profile_refs": profile_refs, + "profile_context": profile_context, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ImplementationGuideVersionDetail, + parse_obj_as( + type_=ImplementationGuideVersionDetail, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 409: + raise ConflictError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + async def get_version( + self, name: str, version: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[ImplementationGuideVersionDetail]: + """ + Parameters + ---------- + name : str + + version : str + The authored ImplementationGuide.version. It may contain letters, numbers, and the punctuation characters `.`, `_`, `~`, `+`, and `-`; it cannot be exactly `.` or `..`. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ImplementationGuideVersionDetail] + Exact canonical package + """ + _response = await self._client_wrapper.httpx_client.request( + f"fhir/implementation-guides/{encode_path_param(name)}/versions/{encode_path_param(version)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ImplementationGuideVersionDetail, + parse_obj_as( + type_=ImplementationGuideVersionDetail, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/src/phenoml/implementation_guides/types/__init__.py b/src/phenoml/implementation_guides/types/__init__.py index 2b67df5f..6c2a89fd 100644 --- a/src/phenoml/implementation_guides/types/__init__.py +++ b/src/phenoml/implementation_guides/types/__init__.py @@ -6,13 +6,17 @@ from importlib import import_module if typing.TYPE_CHECKING: + from .fhir_implementation_guide import FhirImplementationGuide from .implementation_guide_detail import ImplementationGuideDetail from .implementation_guide_list_response import ImplementationGuideListResponse from .implementation_guide_summary import ImplementationGuideSummary + from .implementation_guide_version_detail import ImplementationGuideVersionDetail _dynamic_imports: typing.Dict[str, str] = { + "FhirImplementationGuide": ".fhir_implementation_guide", "ImplementationGuideDetail": ".implementation_guide_detail", "ImplementationGuideListResponse": ".implementation_guide_list_response", "ImplementationGuideSummary": ".implementation_guide_summary", + "ImplementationGuideVersionDetail": ".implementation_guide_version_detail", } @@ -37,4 +41,10 @@ def __dir__(): return sorted(lazy_attrs) -__all__ = ["ImplementationGuideDetail", "ImplementationGuideListResponse", "ImplementationGuideSummary"] +__all__ = [ + "FhirImplementationGuide", + "ImplementationGuideDetail", + "ImplementationGuideListResponse", + "ImplementationGuideSummary", + "ImplementationGuideVersionDetail", +] diff --git a/src/phenoml/implementation_guides/types/fhir_implementation_guide.py b/src/phenoml/implementation_guides/types/fhir_implementation_guide.py new file mode 100644 index 00000000..949b9df0 --- /dev/null +++ b/src/phenoml/implementation_guides/types/fhir_implementation_guide.py @@ -0,0 +1,38 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from ...core.serialization import FieldMetadata + + +class FhirImplementationGuide(UniversalBaseModel): + """ + A complete authored FHIR ImplementationGuide JSON resource. + """ + + resource_type: typing_extensions.Annotated[ + typing.Literal["ImplementationGuide"], FieldMetadata(alias="resourceType"), pydantic.Field(alias="resourceType") + ] = "ImplementationGuide" + id: typing.Optional[str] = None + url: str + version: str + name: typing.Optional[str] = None + status: typing.Optional[str] = None + package_id: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="packageId"), pydantic.Field(alias="packageId") + ] = None + fhir_version: typing_extensions.Annotated[ + typing.Optional[typing.List[str]], FieldMetadata(alias="fhirVersion"), pydantic.Field(alias="fhirVersion") + ] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/phenoml/implementation_guides/types/implementation_guide_summary.py b/src/phenoml/implementation_guides/types/implementation_guide_summary.py index bd3364d4..d2f32779 100644 --- a/src/phenoml/implementation_guides/types/implementation_guide_summary.py +++ b/src/phenoml/implementation_guides/types/implementation_guide_summary.py @@ -9,7 +9,7 @@ class ImplementationGuideSummary(UniversalBaseModel): """ - Metadata for an implementation guide. This is an instance-local grouping record, not a complete FHIR ImplementationGuide resource. + Metadata for an implementation guide. Canonical fields are present only for published canonical packages; guides without a published package omit them. """ name: typing.Optional[str] = pydantic.Field(default=None) @@ -27,6 +27,16 @@ class ImplementationGuideSummary(UniversalBaseModel): The number of custom profiles in this implementation guide. """ + canonical_url: typing.Optional[str] = pydantic.Field(default=None) + """ + Canonical FHIR ImplementationGuide URL, when the family has an exact package. + """ + + version_count: typing.Optional[int] = pydantic.Field(default=None) + """ + Number of retained exact package versions. + """ + created_at: typing.Optional[dt.datetime] = pydantic.Field(default=None) """ Present only for guides that have stored metadata (a profile_context has been set). Omitted for guides that exist solely because a profile references them. diff --git a/src/phenoml/implementation_guides/types/implementation_guide_version_detail.py b/src/phenoml/implementation_guides/types/implementation_guide_version_detail.py new file mode 100644 index 00000000..a5c2e660 --- /dev/null +++ b/src/phenoml/implementation_guides/types/implementation_guide_version_detail.py @@ -0,0 +1,29 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .fhir_implementation_guide import FhirImplementationGuide + + +class ImplementationGuideVersionDetail(UniversalBaseModel): + name: str + url: str + version: str + profile_context: str + profiles: typing.List[str] + profile_refs: typing.List[str] + implementation_guide: FhirImplementationGuide + created_at: dt.datetime + updated_at: dt.datetime + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/phenoml/lang2fhir/__init__.py b/src/phenoml/lang2fhir/__init__.py index 8b0af5df..96bde8ef 100644 --- a/src/phenoml/lang2fhir/__init__.py +++ b/src/phenoml/lang2fhir/__init__.py @@ -28,11 +28,15 @@ PageClassification, PageFilter, PatientReference, + PrimaryPatient, + PrimaryPatientGender, + PrimaryPatientName, ProfileUploadRequest, ResourceReview, ResourceReviewFinding, ResourceReviewFindingFieldKind, ResourceReviewFlagged, + ResourceReviewRemediated, ResourceReviewResult, ResourceReviewTarget, ResourceReviewTargetFieldsItem, @@ -80,11 +84,15 @@ "PageClassification": ".types", "PageFilter": ".types", "PatientReference": ".types", + "PrimaryPatient": ".types", + "PrimaryPatientGender": ".types", + "PrimaryPatientName": ".types", "ProfileUploadRequest": ".types", "ResourceReview": ".types", "ResourceReviewFinding": ".types", "ResourceReviewFindingFieldKind": ".types", "ResourceReviewFlagged": ".types", + "ResourceReviewRemediated": ".types", "ResourceReviewResult": ".types", "ResourceReviewTarget": ".types", "ResourceReviewTargetFieldsItem": ".types", @@ -147,11 +155,15 @@ def __dir__(): "PageClassification", "PageFilter", "PatientReference", + "PrimaryPatient", + "PrimaryPatientGender", + "PrimaryPatientName", "ProfileUploadRequest", "ResourceReview", "ResourceReviewFinding", "ResourceReviewFindingFieldKind", "ResourceReviewFlagged", + "ResourceReviewRemediated", "ResourceReviewResult", "ResourceReviewTarget", "ResourceReviewTargetFieldsItem", diff --git a/src/phenoml/lang2fhir/client.py b/src/phenoml/lang2fhir/client.py index c4a0ad59..42fecf3d 100644 --- a/src/phenoml/lang2fhir/client.py +++ b/src/phenoml/lang2fhir/client.py @@ -15,6 +15,7 @@ from .types.document_multi_response import DocumentMultiResponse from .types.fhir_resource import FhirResource from .types.patient_reference import PatientReference +from .types.primary_patient import PrimaryPatient from .types.resource_review import ResourceReview from .types.search_response import SearchResponse from .types.upload_profile_response import UploadProfileResponse @@ -57,7 +58,7 @@ def create( FHIR version to use resource : CreateRequestResource - Type of FHIR resource to create. Use 'auto' for automatic resource type detection, or specify a supported US Core profile. Recommended to use the supported US Core Profiles for validated results but you can also use any custom profile you've uploaded (if you're a develop or launch customer) + Type of FHIR resource to create. Use 'auto' for automatic resource type detection, or specify a supported profile. The default profile set includes US Core profiles and selected base R4 resources; you can also use any custom profile you've uploaded (if you're a develop or launch customer). text : str Natural language text to convert @@ -95,6 +96,7 @@ def create_multi( text: str, version: typing.Optional[str] = OMIT, provider: typing.Optional[str] = OMIT, + primary_patient: typing.Optional[PrimaryPatient] = OMIT, patient_reference: typing.Optional[PatientReference] = OMIT, implementation_guide: typing.Optional[str] = OMIT, detection_effort: typing.Optional[CreateMultiRequestDetectionEffort] = OMIT, @@ -120,13 +122,16 @@ def create_multi( provider : typing.Optional[str] Optional FHIR provider name for provider-specific profiles + primary_patient : typing.Optional[PrimaryPatient] + patient_reference : typing.Optional[PatientReference] + Deprecated compatibility alias for primary_patient.identifier. Cannot be combined with primary_patient. implementation_guide : typing.Optional[str] - Custom Implementation Guide name. When specified, profiles from this IG are included alongside US Core profiles during resource detection. US Core is always the base layer; custom IG profiles are additive. + Custom Implementation Guide name. When specified, profiles from this IG are included alongside the default profiles during resource detection. Default profiles are always the base layer; custom IG profiles are additive. detection_effort : typing.Optional[CreateMultiRequestDetectionEffort] - Detection effort. 'standard' runs detection once, 'deep' runs detection multiple times for higher recall. + Deprecated; use the default 'standard' value. This field will be removed in a future release. 'standard' runs detection once; 'deep' runs detection multiple times for higher recall. validation_method : typing.Optional[CreateMultiRequestValidationMethod] FHIR validation method to apply to the generated bundle. 'none' skips validation (default). 'check' runs the bundle through a FHIR structure validator and includes the results in the response. 'fix' runs validation and attempts to auto-correct errors using an LLM (up to 3 validation passes). The response includes results from each pass. Warning: 'fix' can significantly increase latency due to multiple LLM and validation round-trips. @@ -158,6 +163,7 @@ def create_multi( text=text, version=version, provider=provider, + primary_patient=primary_patient, patient_reference=patient_reference, implementation_guide=implementation_guide, detection_effort=detection_effort, @@ -291,7 +297,7 @@ def document( request_options: typing.Optional[RequestOptions] = None, ) -> FhirResource: """ - Extracts text from a document (PDF or image) and converts it into a structured FHIR resource. + Extracts text from a PDF, image, RTF, or XML/C-CDA document and converts it into a structured FHIR resource. **Patient identifier handling.** When generating a `patient` (or `patient-canvas`) resource, US Core requires `Patient.identifier` (a business identifier such as an MRN). When the source text contains an identifier, it is extracted with an appropriate URI system. When the source text does not contain a detectable identifier, a synthetic one is generated with `system: "urn:phenoml:lang2fhir-generated-id"` and a UUID `value` so the resource remains FHIR-valid and US Core conformant. Callers who need a tenant-specific namespace should rewrite the synthetic system after extraction. @@ -305,8 +311,11 @@ def document( content : str Base64 encoded file content. - Supported file types: PDF (application/pdf), PNG (image/png), JPEG (image/jpeg), TIFF (image/tiff). + Supported file types: PDF (application/pdf), PNG (image/png), JPEG (image/jpeg), TIFF (image/tiff), RTF (application/rtf), XML/C-CDA (text/xml). + TIFF, RTF, and XML/C-CDA uploads are available on dedicated instances only. File type is auto-detected from content magic bytes. + The decoded file must not exceed 20 MiB. RTF and XML/C-CDA documents whose extracted text exceeds 1 MiB are rejected. + Generic XML must include an XML declaration; C-CDA documents rooted at `ClinicalDocument` may omit it. config : typing.Optional[DocumentConfig] @@ -329,7 +338,7 @@ def document( client.lang2fhir.document( version="R4", resource="questionnaire", - content="JVBERi0xLjQKJeLjz9MK...(base64-encoded PDF or image bytes)", + content="JVBERi0xLjQKJeLjz9MK...(base64-encoded document bytes)", ) """ _response = self._raw_client.document( @@ -343,6 +352,7 @@ def document_multi( version: str, content: str, provider: typing.Optional[str] = OMIT, + primary_patient: typing.Optional[PrimaryPatient] = OMIT, patient_reference: typing.Optional[PatientReference] = OMIT, implementation_guide: typing.Optional[str] = OMIT, detection_effort: typing.Optional[DocumentMultiRequestDetectionEffort] = OMIT, @@ -351,7 +361,7 @@ def document_multi( request_options: typing.Optional[RequestOptions] = None, ) -> DocumentMultiResponse: """ - Extracts text from a document (PDF or image) and converts it into multiple FHIR resources, + Extracts text from a PDF, image, RTF, or XML/C-CDA document and converts it into multiple FHIR resources, returned as a transaction Bundle. Combines document text extraction with multi-resource detection. Automatically detects Patient, Condition, MedicationRequest, Observation, and other resource types. Resources are linked with proper references (e.g., Conditions reference the Patient). @@ -367,19 +377,25 @@ def document_multi( content : str Base64 encoded file content. - Supported file types: PDF (application/pdf), PNG (image/png), JPEG (image/jpeg), TIFF (image/tiff). + Supported file types: PDF (application/pdf), PNG (image/png), JPEG (image/jpeg), TIFF (image/tiff), RTF (application/rtf), XML/C-CDA (text/xml). + TIFF, RTF, and XML/C-CDA uploads are available on dedicated instances only. File type is auto-detected from content magic bytes. + The decoded file must not exceed 20 MiB. RTF and XML/C-CDA documents whose extracted text exceeds 1 MiB are rejected. + Generic XML must include an XML declaration; C-CDA documents rooted at `ClinicalDocument` may omit it. provider : typing.Optional[str] Optional FHIR provider name for provider-specific profiles + primary_patient : typing.Optional[PrimaryPatient] + patient_reference : typing.Optional[PatientReference] + Deprecated compatibility alias for primary_patient.identifier. Cannot be combined with primary_patient. implementation_guide : typing.Optional[str] - Custom Implementation Guide name. When specified, profiles from this IG are included alongside US Core profiles during resource detection. US Core is always the base layer; custom IG profiles are additive. + Custom Implementation Guide name. When specified, profiles from this IG are included alongside the default profiles during resource detection. Default profiles are always the base layer; custom IG profiles are additive. detection_effort : typing.Optional[DocumentMultiRequestDetectionEffort] - Detection effort. 'standard' runs detection once, 'deep' runs detection multiple times for higher recall. + Deprecated; use the default 'standard' value. This field will be removed in a future release. 'standard' runs detection once; 'deep' runs detection multiple times for higher recall. validation_method : typing.Optional[DocumentMultiRequestValidationMethod] FHIR validation method to apply to the generated bundle. 'none' skips validation (default). 'check' runs the bundle through a FHIR structure validator and includes the results in the response. 'fix' runs validation and attempts to auto-correct errors using an LLM (up to 3 validation passes). The response includes results from each pass. Warning: 'fix' can significantly increase latency due to multiple LLM and validation round-trips. @@ -405,7 +421,7 @@ def document_multi( ) client.lang2fhir.document_multi( version="R4", - content="JVBERi0xLjQKJeLjz9MK...(base64-encoded PDF or image bytes)", + content="JVBERi0xLjQKJeLjz9MK...(base64-encoded document bytes)", provider="medplum", config=DocumentConfig( split_classifications=[ @@ -427,6 +443,7 @@ def document_multi( version=version, content=content, provider=provider, + primary_patient=primary_patient, patient_reference=patient_reference, implementation_guide=implementation_guide, detection_effort=detection_effort, @@ -471,7 +488,7 @@ async def create( FHIR version to use resource : CreateRequestResource - Type of FHIR resource to create. Use 'auto' for automatic resource type detection, or specify a supported US Core profile. Recommended to use the supported US Core Profiles for validated results but you can also use any custom profile you've uploaded (if you're a develop or launch customer) + Type of FHIR resource to create. Use 'auto' for automatic resource type detection, or specify a supported profile. The default profile set includes US Core profiles and selected base R4 resources; you can also use any custom profile you've uploaded (if you're a develop or launch customer). text : str Natural language text to convert @@ -517,6 +534,7 @@ async def create_multi( text: str, version: typing.Optional[str] = OMIT, provider: typing.Optional[str] = OMIT, + primary_patient: typing.Optional[PrimaryPatient] = OMIT, patient_reference: typing.Optional[PatientReference] = OMIT, implementation_guide: typing.Optional[str] = OMIT, detection_effort: typing.Optional[CreateMultiRequestDetectionEffort] = OMIT, @@ -542,13 +560,16 @@ async def create_multi( provider : typing.Optional[str] Optional FHIR provider name for provider-specific profiles + primary_patient : typing.Optional[PrimaryPatient] + patient_reference : typing.Optional[PatientReference] + Deprecated compatibility alias for primary_patient.identifier. Cannot be combined with primary_patient. implementation_guide : typing.Optional[str] - Custom Implementation Guide name. When specified, profiles from this IG are included alongside US Core profiles during resource detection. US Core is always the base layer; custom IG profiles are additive. + Custom Implementation Guide name. When specified, profiles from this IG are included alongside the default profiles during resource detection. Default profiles are always the base layer; custom IG profiles are additive. detection_effort : typing.Optional[CreateMultiRequestDetectionEffort] - Detection effort. 'standard' runs detection once, 'deep' runs detection multiple times for higher recall. + Deprecated; use the default 'standard' value. This field will be removed in a future release. 'standard' runs detection once; 'deep' runs detection multiple times for higher recall. validation_method : typing.Optional[CreateMultiRequestValidationMethod] FHIR validation method to apply to the generated bundle. 'none' skips validation (default). 'check' runs the bundle through a FHIR structure validator and includes the results in the response. 'fix' runs validation and attempts to auto-correct errors using an LLM (up to 3 validation passes). The response includes results from each pass. Warning: 'fix' can significantly increase latency due to multiple LLM and validation round-trips. @@ -588,6 +609,7 @@ async def main() -> None: text=text, version=version, provider=provider, + primary_patient=primary_patient, patient_reference=patient_reference, implementation_guide=implementation_guide, detection_effort=detection_effort, @@ -737,7 +759,7 @@ async def document( request_options: typing.Optional[RequestOptions] = None, ) -> FhirResource: """ - Extracts text from a document (PDF or image) and converts it into a structured FHIR resource. + Extracts text from a PDF, image, RTF, or XML/C-CDA document and converts it into a structured FHIR resource. **Patient identifier handling.** When generating a `patient` (or `patient-canvas`) resource, US Core requires `Patient.identifier` (a business identifier such as an MRN). When the source text contains an identifier, it is extracted with an appropriate URI system. When the source text does not contain a detectable identifier, a synthetic one is generated with `system: "urn:phenoml:lang2fhir-generated-id"` and a UUID `value` so the resource remains FHIR-valid and US Core conformant. Callers who need a tenant-specific namespace should rewrite the synthetic system after extraction. @@ -751,8 +773,11 @@ async def document( content : str Base64 encoded file content. - Supported file types: PDF (application/pdf), PNG (image/png), JPEG (image/jpeg), TIFF (image/tiff). + Supported file types: PDF (application/pdf), PNG (image/png), JPEG (image/jpeg), TIFF (image/tiff), RTF (application/rtf), XML/C-CDA (text/xml). + TIFF, RTF, and XML/C-CDA uploads are available on dedicated instances only. File type is auto-detected from content magic bytes. + The decoded file must not exceed 20 MiB. RTF and XML/C-CDA documents whose extracted text exceeds 1 MiB are rejected. + Generic XML must include an XML declaration; C-CDA documents rooted at `ClinicalDocument` may omit it. config : typing.Optional[DocumentConfig] @@ -780,7 +805,7 @@ async def main() -> None: await client.lang2fhir.document( version="R4", resource="questionnaire", - content="JVBERi0xLjQKJeLjz9MK...(base64-encoded PDF or image bytes)", + content="JVBERi0xLjQKJeLjz9MK...(base64-encoded document bytes)", ) @@ -797,6 +822,7 @@ async def document_multi( version: str, content: str, provider: typing.Optional[str] = OMIT, + primary_patient: typing.Optional[PrimaryPatient] = OMIT, patient_reference: typing.Optional[PatientReference] = OMIT, implementation_guide: typing.Optional[str] = OMIT, detection_effort: typing.Optional[DocumentMultiRequestDetectionEffort] = OMIT, @@ -805,7 +831,7 @@ async def document_multi( request_options: typing.Optional[RequestOptions] = None, ) -> DocumentMultiResponse: """ - Extracts text from a document (PDF or image) and converts it into multiple FHIR resources, + Extracts text from a PDF, image, RTF, or XML/C-CDA document and converts it into multiple FHIR resources, returned as a transaction Bundle. Combines document text extraction with multi-resource detection. Automatically detects Patient, Condition, MedicationRequest, Observation, and other resource types. Resources are linked with proper references (e.g., Conditions reference the Patient). @@ -821,19 +847,25 @@ async def document_multi( content : str Base64 encoded file content. - Supported file types: PDF (application/pdf), PNG (image/png), JPEG (image/jpeg), TIFF (image/tiff). + Supported file types: PDF (application/pdf), PNG (image/png), JPEG (image/jpeg), TIFF (image/tiff), RTF (application/rtf), XML/C-CDA (text/xml). + TIFF, RTF, and XML/C-CDA uploads are available on dedicated instances only. File type is auto-detected from content magic bytes. + The decoded file must not exceed 20 MiB. RTF and XML/C-CDA documents whose extracted text exceeds 1 MiB are rejected. + Generic XML must include an XML declaration; C-CDA documents rooted at `ClinicalDocument` may omit it. provider : typing.Optional[str] Optional FHIR provider name for provider-specific profiles + primary_patient : typing.Optional[PrimaryPatient] + patient_reference : typing.Optional[PatientReference] + Deprecated compatibility alias for primary_patient.identifier. Cannot be combined with primary_patient. implementation_guide : typing.Optional[str] - Custom Implementation Guide name. When specified, profiles from this IG are included alongside US Core profiles during resource detection. US Core is always the base layer; custom IG profiles are additive. + Custom Implementation Guide name. When specified, profiles from this IG are included alongside the default profiles during resource detection. Default profiles are always the base layer; custom IG profiles are additive. detection_effort : typing.Optional[DocumentMultiRequestDetectionEffort] - Detection effort. 'standard' runs detection once, 'deep' runs detection multiple times for higher recall. + Deprecated; use the default 'standard' value. This field will be removed in a future release. 'standard' runs detection once; 'deep' runs detection multiple times for higher recall. validation_method : typing.Optional[DocumentMultiRequestValidationMethod] FHIR validation method to apply to the generated bundle. 'none' skips validation (default). 'check' runs the bundle through a FHIR structure validator and includes the results in the response. 'fix' runs validation and attempts to auto-correct errors using an LLM (up to 3 validation passes). The response includes results from each pass. Warning: 'fix' can significantly increase latency due to multiple LLM and validation round-trips. @@ -864,7 +896,7 @@ async def document_multi( async def main() -> None: await client.lang2fhir.document_multi( version="R4", - content="JVBERi0xLjQKJeLjz9MK...(base64-encoded PDF or image bytes)", + content="JVBERi0xLjQKJeLjz9MK...(base64-encoded document bytes)", provider="medplum", config=DocumentConfig( split_classifications=[ @@ -889,6 +921,7 @@ async def main() -> None: version=version, content=content, provider=provider, + primary_patient=primary_patient, patient_reference=patient_reference, implementation_guide=implementation_guide, detection_effort=detection_effort, diff --git a/src/phenoml/lang2fhir/raw_client.py b/src/phenoml/lang2fhir/raw_client.py index 78485897..c5b30b43 100644 --- a/src/phenoml/lang2fhir/raw_client.py +++ b/src/phenoml/lang2fhir/raw_client.py @@ -28,6 +28,7 @@ from .types.document_multi_response import DocumentMultiResponse from .types.fhir_resource import FhirResource from .types.patient_reference import PatientReference +from .types.primary_patient import PrimaryPatient from .types.resource_review import ResourceReview from .types.search_response import SearchResponse from .types.upload_profile_response import UploadProfileResponse @@ -60,7 +61,7 @@ def create( FHIR version to use resource : CreateRequestResource - Type of FHIR resource to create. Use 'auto' for automatic resource type detection, or specify a supported US Core profile. Recommended to use the supported US Core Profiles for validated results but you can also use any custom profile you've uploaded (if you're a develop or launch customer) + Type of FHIR resource to create. Use 'auto' for automatic resource type detection, or specify a supported profile. The default profile set includes US Core profiles and selected base R4 resources; you can also use any custom profile you've uploaded (if you're a develop or launch customer). text : str Natural language text to convert @@ -167,6 +168,7 @@ def create_multi( text: str, version: typing.Optional[str] = OMIT, provider: typing.Optional[str] = OMIT, + primary_patient: typing.Optional[PrimaryPatient] = OMIT, patient_reference: typing.Optional[PatientReference] = OMIT, implementation_guide: typing.Optional[str] = OMIT, detection_effort: typing.Optional[CreateMultiRequestDetectionEffort] = OMIT, @@ -192,13 +194,16 @@ def create_multi( provider : typing.Optional[str] Optional FHIR provider name for provider-specific profiles + primary_patient : typing.Optional[PrimaryPatient] + patient_reference : typing.Optional[PatientReference] + Deprecated compatibility alias for primary_patient.identifier. Cannot be combined with primary_patient. implementation_guide : typing.Optional[str] - Custom Implementation Guide name. When specified, profiles from this IG are included alongside US Core profiles during resource detection. US Core is always the base layer; custom IG profiles are additive. + Custom Implementation Guide name. When specified, profiles from this IG are included alongside the default profiles during resource detection. Default profiles are always the base layer; custom IG profiles are additive. detection_effort : typing.Optional[CreateMultiRequestDetectionEffort] - Detection effort. 'standard' runs detection once, 'deep' runs detection multiple times for higher recall. + Deprecated; use the default 'standard' value. This field will be removed in a future release. 'standard' runs detection once; 'deep' runs detection multiple times for higher recall. validation_method : typing.Optional[CreateMultiRequestValidationMethod] FHIR validation method to apply to the generated bundle. 'none' skips validation (default). 'check' runs the bundle through a FHIR structure validator and includes the results in the response. 'fix' runs validation and attempts to auto-correct errors using an LLM (up to 3 validation passes). The response includes results from each pass. Warning: 'fix' can significantly increase latency due to multiple LLM and validation round-trips. @@ -220,6 +225,9 @@ def create_multi( "text": text, "version": version, "provider": provider, + "primary_patient": convert_and_respect_annotation_metadata( + object_=primary_patient, annotation=PrimaryPatient, direction="write" + ), "patient_reference": convert_and_respect_annotation_metadata( object_=patient_reference, annotation=PatientReference, direction="write" ), @@ -537,7 +545,7 @@ def document( request_options: typing.Optional[RequestOptions] = None, ) -> HttpResponse[FhirResource]: """ - Extracts text from a document (PDF or image) and converts it into a structured FHIR resource. + Extracts text from a PDF, image, RTF, or XML/C-CDA document and converts it into a structured FHIR resource. **Patient identifier handling.** When generating a `patient` (or `patient-canvas`) resource, US Core requires `Patient.identifier` (a business identifier such as an MRN). When the source text contains an identifier, it is extracted with an appropriate URI system. When the source text does not contain a detectable identifier, a synthetic one is generated with `system: "urn:phenoml:lang2fhir-generated-id"` and a UUID `value` so the resource remains FHIR-valid and US Core conformant. Callers who need a tenant-specific namespace should rewrite the synthetic system after extraction. @@ -551,8 +559,11 @@ def document( content : str Base64 encoded file content. - Supported file types: PDF (application/pdf), PNG (image/png), JPEG (image/jpeg), TIFF (image/tiff). + Supported file types: PDF (application/pdf), PNG (image/png), JPEG (image/jpeg), TIFF (image/tiff), RTF (application/rtf), XML/C-CDA (text/xml). + TIFF, RTF, and XML/C-CDA uploads are available on dedicated instances only. File type is auto-detected from content magic bytes. + The decoded file must not exceed 20 MiB. RTF and XML/C-CDA documents whose extracted text exceeds 1 MiB are rejected. + Generic XML must include an XML declaration; C-CDA documents rooted at `ClinicalDocument` may omit it. config : typing.Optional[DocumentConfig] @@ -613,6 +624,17 @@ def document( ), ), ) + if _response.status_code == 403: + raise ForbiddenError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) if _response.status_code == 404: raise NotFoundError( headers=dict(_response.headers), @@ -683,6 +705,7 @@ def document_multi( version: str, content: str, provider: typing.Optional[str] = OMIT, + primary_patient: typing.Optional[PrimaryPatient] = OMIT, patient_reference: typing.Optional[PatientReference] = OMIT, implementation_guide: typing.Optional[str] = OMIT, detection_effort: typing.Optional[DocumentMultiRequestDetectionEffort] = OMIT, @@ -691,7 +714,7 @@ def document_multi( request_options: typing.Optional[RequestOptions] = None, ) -> HttpResponse[DocumentMultiResponse]: """ - Extracts text from a document (PDF or image) and converts it into multiple FHIR resources, + Extracts text from a PDF, image, RTF, or XML/C-CDA document and converts it into multiple FHIR resources, returned as a transaction Bundle. Combines document text extraction with multi-resource detection. Automatically detects Patient, Condition, MedicationRequest, Observation, and other resource types. Resources are linked with proper references (e.g., Conditions reference the Patient). @@ -707,19 +730,25 @@ def document_multi( content : str Base64 encoded file content. - Supported file types: PDF (application/pdf), PNG (image/png), JPEG (image/jpeg), TIFF (image/tiff). + Supported file types: PDF (application/pdf), PNG (image/png), JPEG (image/jpeg), TIFF (image/tiff), RTF (application/rtf), XML/C-CDA (text/xml). + TIFF, RTF, and XML/C-CDA uploads are available on dedicated instances only. File type is auto-detected from content magic bytes. + The decoded file must not exceed 20 MiB. RTF and XML/C-CDA documents whose extracted text exceeds 1 MiB are rejected. + Generic XML must include an XML declaration; C-CDA documents rooted at `ClinicalDocument` may omit it. provider : typing.Optional[str] Optional FHIR provider name for provider-specific profiles + primary_patient : typing.Optional[PrimaryPatient] + patient_reference : typing.Optional[PatientReference] + Deprecated compatibility alias for primary_patient.identifier. Cannot be combined with primary_patient. implementation_guide : typing.Optional[str] - Custom Implementation Guide name. When specified, profiles from this IG are included alongside US Core profiles during resource detection. US Core is always the base layer; custom IG profiles are additive. + Custom Implementation Guide name. When specified, profiles from this IG are included alongside the default profiles during resource detection. Default profiles are always the base layer; custom IG profiles are additive. detection_effort : typing.Optional[DocumentMultiRequestDetectionEffort] - Detection effort. 'standard' runs detection once, 'deep' runs detection multiple times for higher recall. + Deprecated; use the default 'standard' value. This field will be removed in a future release. 'standard' runs detection once; 'deep' runs detection multiple times for higher recall. validation_method : typing.Optional[DocumentMultiRequestValidationMethod] FHIR validation method to apply to the generated bundle. 'none' skips validation (default). 'check' runs the bundle through a FHIR structure validator and includes the results in the response. 'fix' runs validation and attempts to auto-correct errors using an LLM (up to 3 validation passes). The response includes results from each pass. Warning: 'fix' can significantly increase latency due to multiple LLM and validation round-trips. @@ -741,6 +770,9 @@ def document_multi( "version": version, "content": content, "provider": provider, + "primary_patient": convert_and_respect_annotation_metadata( + object_=primary_patient, annotation=PrimaryPatient, direction="write" + ), "patient_reference": convert_and_respect_annotation_metadata( object_=patient_reference, annotation=PatientReference, direction="write" ), @@ -789,6 +821,17 @@ def document_multi( ), ), ) + if _response.status_code == 403: + raise ForbiddenError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) if _response.status_code == 404: raise NotFoundError( headers=dict(_response.headers), @@ -877,7 +920,7 @@ async def create( FHIR version to use resource : CreateRequestResource - Type of FHIR resource to create. Use 'auto' for automatic resource type detection, or specify a supported US Core profile. Recommended to use the supported US Core Profiles for validated results but you can also use any custom profile you've uploaded (if you're a develop or launch customer) + Type of FHIR resource to create. Use 'auto' for automatic resource type detection, or specify a supported profile. The default profile set includes US Core profiles and selected base R4 resources; you can also use any custom profile you've uploaded (if you're a develop or launch customer). text : str Natural language text to convert @@ -984,6 +1027,7 @@ async def create_multi( text: str, version: typing.Optional[str] = OMIT, provider: typing.Optional[str] = OMIT, + primary_patient: typing.Optional[PrimaryPatient] = OMIT, patient_reference: typing.Optional[PatientReference] = OMIT, implementation_guide: typing.Optional[str] = OMIT, detection_effort: typing.Optional[CreateMultiRequestDetectionEffort] = OMIT, @@ -1009,13 +1053,16 @@ async def create_multi( provider : typing.Optional[str] Optional FHIR provider name for provider-specific profiles + primary_patient : typing.Optional[PrimaryPatient] + patient_reference : typing.Optional[PatientReference] + Deprecated compatibility alias for primary_patient.identifier. Cannot be combined with primary_patient. implementation_guide : typing.Optional[str] - Custom Implementation Guide name. When specified, profiles from this IG are included alongside US Core profiles during resource detection. US Core is always the base layer; custom IG profiles are additive. + Custom Implementation Guide name. When specified, profiles from this IG are included alongside the default profiles during resource detection. Default profiles are always the base layer; custom IG profiles are additive. detection_effort : typing.Optional[CreateMultiRequestDetectionEffort] - Detection effort. 'standard' runs detection once, 'deep' runs detection multiple times for higher recall. + Deprecated; use the default 'standard' value. This field will be removed in a future release. 'standard' runs detection once; 'deep' runs detection multiple times for higher recall. validation_method : typing.Optional[CreateMultiRequestValidationMethod] FHIR validation method to apply to the generated bundle. 'none' skips validation (default). 'check' runs the bundle through a FHIR structure validator and includes the results in the response. 'fix' runs validation and attempts to auto-correct errors using an LLM (up to 3 validation passes). The response includes results from each pass. Warning: 'fix' can significantly increase latency due to multiple LLM and validation round-trips. @@ -1037,6 +1084,9 @@ async def create_multi( "text": text, "version": version, "provider": provider, + "primary_patient": convert_and_respect_annotation_metadata( + object_=primary_patient, annotation=PrimaryPatient, direction="write" + ), "patient_reference": convert_and_respect_annotation_metadata( object_=patient_reference, annotation=PatientReference, direction="write" ), @@ -1354,7 +1404,7 @@ async def document( request_options: typing.Optional[RequestOptions] = None, ) -> AsyncHttpResponse[FhirResource]: """ - Extracts text from a document (PDF or image) and converts it into a structured FHIR resource. + Extracts text from a PDF, image, RTF, or XML/C-CDA document and converts it into a structured FHIR resource. **Patient identifier handling.** When generating a `patient` (or `patient-canvas`) resource, US Core requires `Patient.identifier` (a business identifier such as an MRN). When the source text contains an identifier, it is extracted with an appropriate URI system. When the source text does not contain a detectable identifier, a synthetic one is generated with `system: "urn:phenoml:lang2fhir-generated-id"` and a UUID `value` so the resource remains FHIR-valid and US Core conformant. Callers who need a tenant-specific namespace should rewrite the synthetic system after extraction. @@ -1368,8 +1418,11 @@ async def document( content : str Base64 encoded file content. - Supported file types: PDF (application/pdf), PNG (image/png), JPEG (image/jpeg), TIFF (image/tiff). + Supported file types: PDF (application/pdf), PNG (image/png), JPEG (image/jpeg), TIFF (image/tiff), RTF (application/rtf), XML/C-CDA (text/xml). + TIFF, RTF, and XML/C-CDA uploads are available on dedicated instances only. File type is auto-detected from content magic bytes. + The decoded file must not exceed 20 MiB. RTF and XML/C-CDA documents whose extracted text exceeds 1 MiB are rejected. + Generic XML must include an XML declaration; C-CDA documents rooted at `ClinicalDocument` may omit it. config : typing.Optional[DocumentConfig] @@ -1430,6 +1483,17 @@ async def document( ), ), ) + if _response.status_code == 403: + raise ForbiddenError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) if _response.status_code == 404: raise NotFoundError( headers=dict(_response.headers), @@ -1500,6 +1564,7 @@ async def document_multi( version: str, content: str, provider: typing.Optional[str] = OMIT, + primary_patient: typing.Optional[PrimaryPatient] = OMIT, patient_reference: typing.Optional[PatientReference] = OMIT, implementation_guide: typing.Optional[str] = OMIT, detection_effort: typing.Optional[DocumentMultiRequestDetectionEffort] = OMIT, @@ -1508,7 +1573,7 @@ async def document_multi( request_options: typing.Optional[RequestOptions] = None, ) -> AsyncHttpResponse[DocumentMultiResponse]: """ - Extracts text from a document (PDF or image) and converts it into multiple FHIR resources, + Extracts text from a PDF, image, RTF, or XML/C-CDA document and converts it into multiple FHIR resources, returned as a transaction Bundle. Combines document text extraction with multi-resource detection. Automatically detects Patient, Condition, MedicationRequest, Observation, and other resource types. Resources are linked with proper references (e.g., Conditions reference the Patient). @@ -1524,19 +1589,25 @@ async def document_multi( content : str Base64 encoded file content. - Supported file types: PDF (application/pdf), PNG (image/png), JPEG (image/jpeg), TIFF (image/tiff). + Supported file types: PDF (application/pdf), PNG (image/png), JPEG (image/jpeg), TIFF (image/tiff), RTF (application/rtf), XML/C-CDA (text/xml). + TIFF, RTF, and XML/C-CDA uploads are available on dedicated instances only. File type is auto-detected from content magic bytes. + The decoded file must not exceed 20 MiB. RTF and XML/C-CDA documents whose extracted text exceeds 1 MiB are rejected. + Generic XML must include an XML declaration; C-CDA documents rooted at `ClinicalDocument` may omit it. provider : typing.Optional[str] Optional FHIR provider name for provider-specific profiles + primary_patient : typing.Optional[PrimaryPatient] + patient_reference : typing.Optional[PatientReference] + Deprecated compatibility alias for primary_patient.identifier. Cannot be combined with primary_patient. implementation_guide : typing.Optional[str] - Custom Implementation Guide name. When specified, profiles from this IG are included alongside US Core profiles during resource detection. US Core is always the base layer; custom IG profiles are additive. + Custom Implementation Guide name. When specified, profiles from this IG are included alongside the default profiles during resource detection. Default profiles are always the base layer; custom IG profiles are additive. detection_effort : typing.Optional[DocumentMultiRequestDetectionEffort] - Detection effort. 'standard' runs detection once, 'deep' runs detection multiple times for higher recall. + Deprecated; use the default 'standard' value. This field will be removed in a future release. 'standard' runs detection once; 'deep' runs detection multiple times for higher recall. validation_method : typing.Optional[DocumentMultiRequestValidationMethod] FHIR validation method to apply to the generated bundle. 'none' skips validation (default). 'check' runs the bundle through a FHIR structure validator and includes the results in the response. 'fix' runs validation and attempts to auto-correct errors using an LLM (up to 3 validation passes). The response includes results from each pass. Warning: 'fix' can significantly increase latency due to multiple LLM and validation round-trips. @@ -1558,6 +1629,9 @@ async def document_multi( "version": version, "content": content, "provider": provider, + "primary_patient": convert_and_respect_annotation_metadata( + object_=primary_patient, annotation=PrimaryPatient, direction="write" + ), "patient_reference": convert_and_respect_annotation_metadata( object_=patient_reference, annotation=PatientReference, direction="write" ), @@ -1606,6 +1680,17 @@ async def document_multi( ), ), ) + if _response.status_code == 403: + raise ForbiddenError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) if _response.status_code == 404: raise NotFoundError( headers=dict(_response.headers), diff --git a/src/phenoml/lang2fhir/types/__init__.py b/src/phenoml/lang2fhir/types/__init__.py index 12303154..cb35942d 100644 --- a/src/phenoml/lang2fhir/types/__init__.py +++ b/src/phenoml/lang2fhir/types/__init__.py @@ -31,11 +31,15 @@ from .page_classification import PageClassification from .page_filter import PageFilter from .patient_reference import PatientReference + from .primary_patient import PrimaryPatient + from .primary_patient_gender import PrimaryPatientGender + from .primary_patient_name import PrimaryPatientName from .profile_upload_request import ProfileUploadRequest from .resource_review import ResourceReview from .resource_review_finding import ResourceReviewFinding from .resource_review_finding_field_kind import ResourceReviewFindingFieldKind from .resource_review_flagged import ResourceReviewFlagged + from .resource_review_remediated import ResourceReviewRemediated from .resource_review_result import ResourceReviewResult from .resource_review_target import ResourceReviewTarget from .resource_review_target_fields_item import ResourceReviewTargetFieldsItem @@ -66,11 +70,15 @@ "PageClassification": ".page_classification", "PageFilter": ".page_filter", "PatientReference": ".patient_reference", + "PrimaryPatient": ".primary_patient", + "PrimaryPatientGender": ".primary_patient_gender", + "PrimaryPatientName": ".primary_patient_name", "ProfileUploadRequest": ".profile_upload_request", "ResourceReview": ".resource_review", "ResourceReviewFinding": ".resource_review_finding", "ResourceReviewFindingFieldKind": ".resource_review_finding_field_kind", "ResourceReviewFlagged": ".resource_review_flagged", + "ResourceReviewRemediated": ".resource_review_remediated", "ResourceReviewResult": ".resource_review_result", "ResourceReviewTarget": ".resource_review_target", "ResourceReviewTargetFieldsItem": ".resource_review_target_fields_item", @@ -125,11 +133,15 @@ def __dir__(): "PageClassification", "PageFilter", "PatientReference", + "PrimaryPatient", + "PrimaryPatientGender", + "PrimaryPatientName", "ProfileUploadRequest", "ResourceReview", "ResourceReviewFinding", "ResourceReviewFindingFieldKind", "ResourceReviewFlagged", + "ResourceReviewRemediated", "ResourceReviewResult", "ResourceReviewTarget", "ResourceReviewTargetFieldsItem", diff --git a/src/phenoml/lang2fhir/types/create_request_resource.py b/src/phenoml/lang2fhir/types/create_request_resource.py index 7cf0d87d..db4879bd 100644 --- a/src/phenoml/lang2fhir/types/create_request_resource.py +++ b/src/phenoml/lang2fhir/types/create_request_resource.py @@ -7,7 +7,10 @@ "auto", "appointment", "condition-encounter-diagnosis", + "familymemberhistory", + "medicationadministration", "medicationrequest", + "medicationstatement", "careplan", "condition-problems-health-concerns", "coverage", diff --git a/src/phenoml/lang2fhir/types/patient_reference.py b/src/phenoml/lang2fhir/types/patient_reference.py index 3eec8ed3..a0d3b35f 100644 --- a/src/phenoml/lang2fhir/types/patient_reference.py +++ b/src/phenoml/lang2fhir/types/patient_reference.py @@ -8,7 +8,7 @@ class PatientReference(UniversalBaseModel): """ - Optional reference to an existing Patient, by business identifier. If a Patient resource is extracted, this identifier is added to that Patient's identifier list (existing identifiers are kept). If no Patient is extracted, generated clinical resources are linked to this patient as a logical reference (subject.identifier) instead of the request failing, and no placeholder Patient is created. Supply the patient-level identifier (not an order or specimen identifier). + Business identifier for the document's primary patient. When Lang2FHIR identifies that Patient in generated output, it adds this identifier to the Patient's identifier list (preserving existing identifiers). If no Patient is generated, Lang2FHIR uses it in logical references on generated clinical resources. Supply the patient-level identifier (not an order or specimen identifier). """ system: str = pydantic.Field() diff --git a/src/phenoml/lang2fhir/types/primary_patient.py b/src/phenoml/lang2fhir/types/primary_patient.py new file mode 100644 index 00000000..67d45c99 --- /dev/null +++ b/src/phenoml/lang2fhir/types/primary_patient.py @@ -0,0 +1,42 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from ...core.serialization import FieldMetadata +from .patient_reference import PatientReference +from .primary_patient_gender import PrimaryPatientGender +from .primary_patient_name import PrimaryPatientName + + +class PrimaryPatient(UniversalBaseModel): + """ + Partial context for the patient the document is primarily about. This is not a complete FHIR Patient resource. Lang2FHIR uses the available context to identify a generated primary Patient reliably. An identifier supplied here is added to that Patient; when no Patient is generated, it is used in logical references on generated clinical resources. + """ + + identifier: typing.Optional[PatientReference] = None + name: typing.Optional[PrimaryPatientName] = None + birth_date: typing_extensions.Annotated[ + typing.Optional[str], + FieldMetadata(alias="birthDate"), + pydantic.Field(alias="birthDate", description="Complete date of birth in YYYY-MM-DD format."), + ] = None + """ + Complete date of birth in YYYY-MM-DD format. + """ + + gender: typing.Optional[PrimaryPatientGender] = pydantic.Field(default=None) + """ + Administrative gender. This corroborates another match but does not identify a patient alone. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/phenoml/lang2fhir/types/primary_patient_gender.py b/src/phenoml/lang2fhir/types/primary_patient_gender.py new file mode 100644 index 00000000..534f6268 --- /dev/null +++ b/src/phenoml/lang2fhir/types/primary_patient_gender.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +PrimaryPatientGender = typing.Union[typing.Literal["male", "female", "other", "unknown"], typing.Any] diff --git a/src/phenoml/lang2fhir/types/primary_patient_name.py b/src/phenoml/lang2fhir/types/primary_patient_name.py new file mode 100644 index 00000000..5f6f39f9 --- /dev/null +++ b/src/phenoml/lang2fhir/types/primary_patient_name.py @@ -0,0 +1,31 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel + + +class PrimaryPatientName(UniversalBaseModel): + """ + The known portions of the primary patient's name. Provide a non-empty family name or at least one non-empty given name. + """ + + family: typing.Optional[str] = pydantic.Field(default=None) + """ + Family name. + """ + + given: typing.Optional[typing.List[str]] = pydantic.Field(default=None) + """ + Given names. Matching succeeds when a generated name has a supplied given name. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/phenoml/lang2fhir/types/resource_review.py b/src/phenoml/lang2fhir/types/resource_review.py index 02f825d9..6a62b332 100644 --- a/src/phenoml/lang2fhir/types/resource_review.py +++ b/src/phenoml/lang2fhir/types/resource_review.py @@ -9,7 +9,7 @@ class ResourceReview(UniversalBaseModel): """ - Opt-in, report-only faithfulness audit (honored by /lang2fhir/create/multi and /lang2fhir/document/multi). For each selected resource type an LLM checks whether selected dates and clinical code concepts are actually supported by the full source document. Resources with an unsupported field are pulled out of the returned bundle and reported under resource_review in the response. + Opt-in faithfulness audit (honored by /lang2fhir/create/multi and /lang2fhir/document/multi). For each selected resource type an LLM checks whether selected dates and clinical code concepts are actually supported by the full source document. An unsupported individual coding is removed when another coding remains in its concept. Resources with an unsupported structural field, a profile-required coding, or no coding remaining in an affected concept, are pulled out of the returned bundle and reported under resource_review in the response. """ targets: typing.List[ResourceReviewTarget] = pydantic.Field() diff --git a/src/phenoml/lang2fhir/types/resource_review_finding.py b/src/phenoml/lang2fhir/types/resource_review_finding.py index 57031498..c78e567c 100644 --- a/src/phenoml/lang2fhir/types/resource_review_finding.py +++ b/src/phenoml/lang2fhir/types/resource_review_finding.py @@ -27,7 +27,12 @@ class ResourceReviewFinding(UniversalBaseModel): value: typing.Optional[str] = None supported: typing.Optional[bool] = pydantic.Field(default=None) """ - Always false for a flagged finding. + False when the reviewer found the field unsupported. Do not treat this field as a verdict when unaudited is true. + """ + + unaudited: typing.Optional[bool] = pydantic.Field(default=None) + """ + True when the reviewer did not return a verdict for this field; the resource was quarantined without treating the finding as evidence that the value is unsupported. """ rationale: typing.Optional[str] = pydantic.Field(default=None) diff --git a/src/phenoml/lang2fhir/types/resource_review_flagged.py b/src/phenoml/lang2fhir/types/resource_review_flagged.py index 01b845dc..a3f08aff 100644 --- a/src/phenoml/lang2fhir/types/resource_review_flagged.py +++ b/src/phenoml/lang2fhir/types/resource_review_flagged.py @@ -29,7 +29,7 @@ class ResourceReviewFlagged(UniversalBaseModel): findings: typing.Optional[typing.List[ResourceReviewFinding]] = pydantic.Field(default=None) """ - The unsupported fields that caused the resource to be flagged. + The findings that caused the resource to be quarantined. """ if IS_PYDANTIC_V2: diff --git a/src/phenoml/lang2fhir/types/resource_review_remediated.py b/src/phenoml/lang2fhir/types/resource_review_remediated.py new file mode 100644 index 00000000..0db49993 --- /dev/null +++ b/src/phenoml/lang2fhir/types/resource_review_remediated.py @@ -0,0 +1,42 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from ...core.serialization import FieldMetadata +from .resource_review_finding import ResourceReviewFinding + + +class ResourceReviewRemediated(UniversalBaseModel): + temp_id: typing_extensions.Annotated[ + typing.Optional[str], + FieldMetadata(alias="tempId"), + pydantic.Field(alias="tempId", description="The urn:uuid of the remediated resource (its bundle fullUrl)."), + ] = None + """ + The urn:uuid of the remediated resource (its bundle fullUrl). + """ + + resource_type: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="resourceType"), pydantic.Field(alias="resourceType") + ] = None + action: typing.Optional[typing.Literal["removed_codings"]] = pydantic.Field(default=None) + """ + The safe change applied to the resource in the returned bundle. + """ + + findings: typing.Optional[typing.List[ResourceReviewFinding]] = pydantic.Field(default=None) + """ + Findings for fields in the pre-remediation resource that caused this action. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/phenoml/lang2fhir/types/resource_review_result.py b/src/phenoml/lang2fhir/types/resource_review_result.py index fa91ed59..16ae1161 100644 --- a/src/phenoml/lang2fhir/types/resource_review_result.py +++ b/src/phenoml/lang2fhir/types/resource_review_result.py @@ -5,16 +5,22 @@ import pydantic from ...core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel from .resource_review_flagged import ResourceReviewFlagged +from .resource_review_remediated import ResourceReviewRemediated class ResourceReviewResult(UniversalBaseModel): """ - Present when resource_review was requested and at least one resource was flagged. + Present when resource_review was requested and at least one resource was quarantined or safely remediated. The returned bundle is authoritative and contains the post-review representation of every retained resource. """ flagged: typing.Optional[typing.List[ResourceReviewFlagged]] = pydantic.Field(default=None) """ - Resources pulled from the bundle because a reviewed field was not supported by the source. + Resources pulled from the bundle because an unsupported finding could not be safely repaired. + """ + + remediated: typing.Optional[typing.List[ResourceReviewRemediated]] = pydantic.Field(default=None) + """ + Resources retained in the bundle after unsupported codings were safely removed. """ if IS_PYDANTIC_V2: diff --git a/src/phenoml/lang2fhir_batch/client.py b/src/phenoml/lang2fhir_batch/client.py index d6417555..e68bb8eb 100644 --- a/src/phenoml/lang2fhir_batch/client.py +++ b/src/phenoml/lang2fhir_batch/client.py @@ -80,26 +80,18 @@ def create( self, *, request_id: typing.Optional[str] = OMIT, request_options: typing.Optional[RequestOptions] = None ) -> BatchJob: """ - Opens an empty batch job. Items arrive on later upload calls and the set - is sealed at finalize. + Opens an empty job; upload items, then finalize it to start processing. - Supplying `request_id` makes the create idempotent on that token: a - retried submit whose response was lost returns the original job rather - than opening a second one. This dedupe is scoped to the calling - credential. A `request_id` whose job was canceled or failed before it - finalized is released for a fresh replay; once a job is finalized, its - `request_id` keeps resolving to it even after cancellation. - - An instance may hold at most 4 active (pending or processing) jobs at - once; a create past that limit returns `409`. The limit is instance-wide - — jobs are shared across the instance's credentials — so another - credential's jobs count against it. + `request_id` makes creation idempotent: a retry returns the original + job. A token is released when its job is canceled or fails before + finalization; otherwise it continues to resolve to that job. Parameters ---------- request_id : typing.Optional[str] - Optional client idempotency token. A retried create with the same - token returns the original job instead of opening a second one. + Optional client idempotency token (at most 256 UTF-8 bytes). A + retried create with the same token returns the original job instead + of opening a second one. request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -136,43 +128,22 @@ def upload_item( request_options: typing.Optional[RequestOptions] = None, ) -> UploadItemResponse: """ - Stores one item of a job from a multipart upload. A batch's items arrive - one per request. The item carries **either** a `document` extraction - (whose input file rides as raw bytes in the `file` part) **or** a - `create` extraction (JSON only, no file). - - The upload enforces these rules: - - Set **exactly one** of `document` or `create`. Setting both, or - neither, is a `400`. - - When `document` is set, `file` is **required** — it supplies the - document's binary content (PDF or image). - - When `create` is set, `file` is **forbidden** — a create item carries - no file. - - `document` and `create` must each be a JSON **object**. - - Only the item's structure is checked here: the fields inside `document` - or `create` are not validated at upload. A body that is well-formed JSON - but not a valid request for its endpoint is still accepted with `202` - and fails later during processing, recorded as an item `error`. A - wrong-typed field the endpoint cannot decode fails as `invalid_input`; a - body that decodes but the pipeline rejects (for example, a missing - required field) fails as `processing_failed`. - - Supplying `request_id` makes the upload idempotent on that token. A - re-upload under the same token overwrites the same item rather than - adding a second, so a client that lost an upload's response can safely - re-send it. The response's `deduplicated` is `true` only when the - re-uploaded payload matches the one already stored; a same-token upload - with a changed payload overwrites in place and returns `false`. - - Set a `request_id` on **every** upload: re-sending under the same token - is the only way to repair a lost or incomplete upload, including the one - a finalize `409` reports. Without one, a re-send adds a new item instead - of replacing the missing one, and the job cannot be finalized. - - Uploads are rejected once the job has been finalized (`409`), once it - holds its 500-item limit (`409`), or when the item is too large (`413` — - see the raw-file limit in the API description). + Stores one multipart item. Set **either** `document` with a raw `file`, + or JSON-only `create`. + + Set exactly one JSON object. `document` requires `file`; `create` + forbids it. Violations return `400`. + + Upload validates only the envelope. Endpoint request validation happens + during processing: decoding failures are `invalid_input`; other pipeline + failures are `processing_failed`. + + Use `request_id` for every upload so retries replace the same item. + `deduplicated` is true only for an unchanged payload; a changed payload + overwrites the item and returns false. + + Uploads return `409` after finalization or at the item limit, and `413` + when the item is too large. Parameters ---------- @@ -182,35 +153,38 @@ def upload_item( The JSON body of `POST /lang2fhir/document/multi`, **without** its base64 `content` field — the uploaded `file` supplies the content. Accepts that endpoint's fields (`version`, `provider`, - `patient_reference`, `implementation_guide`, `detection_effort`, + `primary_patient`, `patient_reference` (deprecated), `implementation_guide`, `detection_effort` (deprecated), `validation_method`, `config`). This is the **multi**-resource body: it has no single-`resource` field, and the item's result is a `DocumentMultiResponse` (a Bundle of resources). Mutually - exclusive with `create`; requires `file`. + exclusive with `create`; requires `file`. Do not combine + `primary_patient` with `patient_reference`. create : typing.Optional[typing.Dict[str, typing.Any]] The JSON body of `POST /lang2fhir/create/multi`. Accepts that endpoint's fields (`text`, `version`, `provider`, - `patient_reference`, `implementation_guide`, `detection_effort`, + `primary_patient`, `patient_reference` (deprecated), `implementation_guide`, `detection_effort` (deprecated), `validation_method`, `resource_review`). This is the **multi**-resource body: it has no single-`resource` field, and the item's result is a `CreateMultiResponse` (a Bundle of resources). Mutually exclusive with `document`; must **not** be - accompanied by a `file`. + accompanied by a `file`. Do not combine `primary_patient` with + `patient_reference`. file : typing.Optional[core.File] See core.File for more documentation request_id : typing.Optional[str] - Optional idempotency token (max 256 bytes). Re-uploading under - the same token overwrites the same item instead of adding a - new one. The token is scoped to this job; the same token in - another job is independent and creates a separate item. + Optional idempotency token (at most 256 UTF-8 bytes). + Re-uploading under the same token overwrites the same item + instead of adding a new one. The token is scoped to this job; + the same token in another job is independent and creates a + separate item. id : typing.Optional[str] - Optional caller-supplied correlation label (max 512 bytes), - echoed back on status and result listings so you can match the - server's item_id to your own record. + Optional caller-supplied correlation label (at most 512 UTF-8 + bytes), echoed back on status and result listings so you can + match the server's item_id to your own record. request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -281,8 +255,8 @@ def finalize(self, job_id: str, *, request_options: typing.Optional[RequestOptio def cancel(self, job_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> BatchJob: """ - Drives a job to the terminal `canceled` state on request, freeing its - active-job slot immediately. Takes no request body. + Drives a job to the terminal `canceled` state on request. Takes no + request body. Cancel does not delete the job: the job record and any results already produced are preserved for the normal retention window, the same as a @@ -544,26 +518,18 @@ async def create( self, *, request_id: typing.Optional[str] = OMIT, request_options: typing.Optional[RequestOptions] = None ) -> BatchJob: """ - Opens an empty batch job. Items arrive on later upload calls and the set - is sealed at finalize. + Opens an empty job; upload items, then finalize it to start processing. - Supplying `request_id` makes the create idempotent on that token: a - retried submit whose response was lost returns the original job rather - than opening a second one. This dedupe is scoped to the calling - credential. A `request_id` whose job was canceled or failed before it - finalized is released for a fresh replay; once a job is finalized, its - `request_id` keeps resolving to it even after cancellation. - - An instance may hold at most 4 active (pending or processing) jobs at - once; a create past that limit returns `409`. The limit is instance-wide - — jobs are shared across the instance's credentials — so another - credential's jobs count against it. + `request_id` makes creation idempotent: a retry returns the original + job. A token is released when its job is canceled or fails before + finalization; otherwise it continues to resolve to that job. Parameters ---------- request_id : typing.Optional[str] - Optional client idempotency token. A retried create with the same - token returns the original job instead of opening a second one. + Optional client idempotency token (at most 256 UTF-8 bytes). A + retried create with the same token returns the original job instead + of opening a second one. request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -608,43 +574,22 @@ async def upload_item( request_options: typing.Optional[RequestOptions] = None, ) -> UploadItemResponse: """ - Stores one item of a job from a multipart upload. A batch's items arrive - one per request. The item carries **either** a `document` extraction - (whose input file rides as raw bytes in the `file` part) **or** a - `create` extraction (JSON only, no file). - - The upload enforces these rules: - - Set **exactly one** of `document` or `create`. Setting both, or - neither, is a `400`. - - When `document` is set, `file` is **required** — it supplies the - document's binary content (PDF or image). - - When `create` is set, `file` is **forbidden** — a create item carries - no file. - - `document` and `create` must each be a JSON **object**. - - Only the item's structure is checked here: the fields inside `document` - or `create` are not validated at upload. A body that is well-formed JSON - but not a valid request for its endpoint is still accepted with `202` - and fails later during processing, recorded as an item `error`. A - wrong-typed field the endpoint cannot decode fails as `invalid_input`; a - body that decodes but the pipeline rejects (for example, a missing - required field) fails as `processing_failed`. - - Supplying `request_id` makes the upload idempotent on that token. A - re-upload under the same token overwrites the same item rather than - adding a second, so a client that lost an upload's response can safely - re-send it. The response's `deduplicated` is `true` only when the - re-uploaded payload matches the one already stored; a same-token upload - with a changed payload overwrites in place and returns `false`. - - Set a `request_id` on **every** upload: re-sending under the same token - is the only way to repair a lost or incomplete upload, including the one - a finalize `409` reports. Without one, a re-send adds a new item instead - of replacing the missing one, and the job cannot be finalized. - - Uploads are rejected once the job has been finalized (`409`), once it - holds its 500-item limit (`409`), or when the item is too large (`413` — - see the raw-file limit in the API description). + Stores one multipart item. Set **either** `document` with a raw `file`, + or JSON-only `create`. + + Set exactly one JSON object. `document` requires `file`; `create` + forbids it. Violations return `400`. + + Upload validates only the envelope. Endpoint request validation happens + during processing: decoding failures are `invalid_input`; other pipeline + failures are `processing_failed`. + + Use `request_id` for every upload so retries replace the same item. + `deduplicated` is true only for an unchanged payload; a changed payload + overwrites the item and returns false. + + Uploads return `409` after finalization or at the item limit, and `413` + when the item is too large. Parameters ---------- @@ -654,35 +599,38 @@ async def upload_item( The JSON body of `POST /lang2fhir/document/multi`, **without** its base64 `content` field — the uploaded `file` supplies the content. Accepts that endpoint's fields (`version`, `provider`, - `patient_reference`, `implementation_guide`, `detection_effort`, + `primary_patient`, `patient_reference` (deprecated), `implementation_guide`, `detection_effort` (deprecated), `validation_method`, `config`). This is the **multi**-resource body: it has no single-`resource` field, and the item's result is a `DocumentMultiResponse` (a Bundle of resources). Mutually - exclusive with `create`; requires `file`. + exclusive with `create`; requires `file`. Do not combine + `primary_patient` with `patient_reference`. create : typing.Optional[typing.Dict[str, typing.Any]] The JSON body of `POST /lang2fhir/create/multi`. Accepts that endpoint's fields (`text`, `version`, `provider`, - `patient_reference`, `implementation_guide`, `detection_effort`, + `primary_patient`, `patient_reference` (deprecated), `implementation_guide`, `detection_effort` (deprecated), `validation_method`, `resource_review`). This is the **multi**-resource body: it has no single-`resource` field, and the item's result is a `CreateMultiResponse` (a Bundle of resources). Mutually exclusive with `document`; must **not** be - accompanied by a `file`. + accompanied by a `file`. Do not combine `primary_patient` with + `patient_reference`. file : typing.Optional[core.File] See core.File for more documentation request_id : typing.Optional[str] - Optional idempotency token (max 256 bytes). Re-uploading under - the same token overwrites the same item instead of adding a - new one. The token is scoped to this job; the same token in - another job is independent and creates a separate item. + Optional idempotency token (at most 256 UTF-8 bytes). + Re-uploading under the same token overwrites the same item + instead of adding a new one. The token is scoped to this job; + the same token in another job is independent and creates a + separate item. id : typing.Optional[str] - Optional caller-supplied correlation label (max 512 bytes), - echoed back on status and result listings so you can match the - server's item_id to your own record. + Optional caller-supplied correlation label (at most 512 UTF-8 + bytes), echoed back on status and result listings so you can + match the server's item_id to your own record. request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -769,8 +717,8 @@ async def main() -> None: async def cancel(self, job_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> BatchJob: """ - Drives a job to the terminal `canceled` state on request, freeing its - active-job slot immediately. Takes no request body. + Drives a job to the terminal `canceled` state on request. Takes no + request body. Cancel does not delete the job: the job record and any results already produced are preserved for the normal retention window, the same as a diff --git a/src/phenoml/lang2fhir_batch/raw_client.py b/src/phenoml/lang2fhir_batch/raw_client.py index 8873fac6..ffd31fe7 100644 --- a/src/phenoml/lang2fhir_batch/raw_client.py +++ b/src/phenoml/lang2fhir_batch/raw_client.py @@ -151,26 +151,18 @@ def create( self, *, request_id: typing.Optional[str] = OMIT, request_options: typing.Optional[RequestOptions] = None ) -> HttpResponse[BatchJob]: """ - Opens an empty batch job. Items arrive on later upload calls and the set - is sealed at finalize. + Opens an empty job; upload items, then finalize it to start processing. - Supplying `request_id` makes the create idempotent on that token: a - retried submit whose response was lost returns the original job rather - than opening a second one. This dedupe is scoped to the calling - credential. A `request_id` whose job was canceled or failed before it - finalized is released for a fresh replay; once a job is finalized, its - `request_id` keeps resolving to it even after cancellation. - - An instance may hold at most 4 active (pending or processing) jobs at - once; a create past that limit returns `409`. The limit is instance-wide - — jobs are shared across the instance's credentials — so another - credential's jobs count against it. + `request_id` makes creation idempotent: a retry returns the original + job. A token is released when its job is canceled or fails before + finalization; otherwise it continues to resolve to that job. Parameters ---------- request_id : typing.Optional[str] - Optional client idempotency token. A retried create with the same - token returns the original job instead of opening a second one. + Optional client idempotency token (at most 256 UTF-8 bytes). A + retried create with the same token returns the original job instead + of opening a second one. request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -224,17 +216,6 @@ def create( ), ), ) - if _response.status_code == 409: - raise ConflictError( - headers=dict(_response.headers), - body=typing.cast( - typing.Any, - parse_obj_as( - type_=typing.Any, # type: ignore - object_=_response.json(), - ), - ), - ) if _response.status_code == 499: raise ClientClosedRequestError( headers=dict(_response.headers), @@ -289,43 +270,22 @@ def upload_item( request_options: typing.Optional[RequestOptions] = None, ) -> HttpResponse[UploadItemResponse]: """ - Stores one item of a job from a multipart upload. A batch's items arrive - one per request. The item carries **either** a `document` extraction - (whose input file rides as raw bytes in the `file` part) **or** a - `create` extraction (JSON only, no file). - - The upload enforces these rules: - - Set **exactly one** of `document` or `create`. Setting both, or - neither, is a `400`. - - When `document` is set, `file` is **required** — it supplies the - document's binary content (PDF or image). - - When `create` is set, `file` is **forbidden** — a create item carries - no file. - - `document` and `create` must each be a JSON **object**. - - Only the item's structure is checked here: the fields inside `document` - or `create` are not validated at upload. A body that is well-formed JSON - but not a valid request for its endpoint is still accepted with `202` - and fails later during processing, recorded as an item `error`. A - wrong-typed field the endpoint cannot decode fails as `invalid_input`; a - body that decodes but the pipeline rejects (for example, a missing - required field) fails as `processing_failed`. - - Supplying `request_id` makes the upload idempotent on that token. A - re-upload under the same token overwrites the same item rather than - adding a second, so a client that lost an upload's response can safely - re-send it. The response's `deduplicated` is `true` only when the - re-uploaded payload matches the one already stored; a same-token upload - with a changed payload overwrites in place and returns `false`. - - Set a `request_id` on **every** upload: re-sending under the same token - is the only way to repair a lost or incomplete upload, including the one - a finalize `409` reports. Without one, a re-send adds a new item instead - of replacing the missing one, and the job cannot be finalized. - - Uploads are rejected once the job has been finalized (`409`), once it - holds its 500-item limit (`409`), or when the item is too large (`413` — - see the raw-file limit in the API description). + Stores one multipart item. Set **either** `document` with a raw `file`, + or JSON-only `create`. + + Set exactly one JSON object. `document` requires `file`; `create` + forbids it. Violations return `400`. + + Upload validates only the envelope. Endpoint request validation happens + during processing: decoding failures are `invalid_input`; other pipeline + failures are `processing_failed`. + + Use `request_id` for every upload so retries replace the same item. + `deduplicated` is true only for an unchanged payload; a changed payload + overwrites the item and returns false. + + Uploads return `409` after finalization or at the item limit, and `413` + when the item is too large. Parameters ---------- @@ -335,35 +295,38 @@ def upload_item( The JSON body of `POST /lang2fhir/document/multi`, **without** its base64 `content` field — the uploaded `file` supplies the content. Accepts that endpoint's fields (`version`, `provider`, - `patient_reference`, `implementation_guide`, `detection_effort`, + `primary_patient`, `patient_reference` (deprecated), `implementation_guide`, `detection_effort` (deprecated), `validation_method`, `config`). This is the **multi**-resource body: it has no single-`resource` field, and the item's result is a `DocumentMultiResponse` (a Bundle of resources). Mutually - exclusive with `create`; requires `file`. + exclusive with `create`; requires `file`. Do not combine + `primary_patient` with `patient_reference`. create : typing.Optional[typing.Dict[str, typing.Any]] The JSON body of `POST /lang2fhir/create/multi`. Accepts that endpoint's fields (`text`, `version`, `provider`, - `patient_reference`, `implementation_guide`, `detection_effort`, + `primary_patient`, `patient_reference` (deprecated), `implementation_guide`, `detection_effort` (deprecated), `validation_method`, `resource_review`). This is the **multi**-resource body: it has no single-`resource` field, and the item's result is a `CreateMultiResponse` (a Bundle of resources). Mutually exclusive with `document`; must **not** be - accompanied by a `file`. + accompanied by a `file`. Do not combine `primary_patient` with + `patient_reference`. file : typing.Optional[core.File] See core.File for more documentation request_id : typing.Optional[str] - Optional idempotency token (max 256 bytes). Re-uploading under - the same token overwrites the same item instead of adding a - new one. The token is scoped to this job; the same token in - another job is independent and creates a separate item. + Optional idempotency token (at most 256 UTF-8 bytes). + Re-uploading under the same token overwrites the same item + instead of adding a new one. The token is scoped to this job; + the same token in another job is independent and creates a + separate item. id : typing.Optional[str] - Optional caller-supplied correlation label (max 512 bytes), - echoed back on status and result listings so you can match the - server's item_id to your own record. + Optional caller-supplied correlation label (at most 512 UTF-8 + bytes), echoed back on status and result listings so you can + match the server's item_id to your own record. request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -630,8 +593,8 @@ def finalize( def cancel(self, job_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[BatchJob]: """ - Drives a job to the terminal `canceled` state on request, freeing its - active-job slot immediately. Takes no request body. + Drives a job to the terminal `canceled` state on request. Takes no + request body. Cancel does not delete the job: the job record and any results already produced are preserved for the normal retention window, the same as a @@ -1255,26 +1218,18 @@ async def create( self, *, request_id: typing.Optional[str] = OMIT, request_options: typing.Optional[RequestOptions] = None ) -> AsyncHttpResponse[BatchJob]: """ - Opens an empty batch job. Items arrive on later upload calls and the set - is sealed at finalize. + Opens an empty job; upload items, then finalize it to start processing. - Supplying `request_id` makes the create idempotent on that token: a - retried submit whose response was lost returns the original job rather - than opening a second one. This dedupe is scoped to the calling - credential. A `request_id` whose job was canceled or failed before it - finalized is released for a fresh replay; once a job is finalized, its - `request_id` keeps resolving to it even after cancellation. - - An instance may hold at most 4 active (pending or processing) jobs at - once; a create past that limit returns `409`. The limit is instance-wide - — jobs are shared across the instance's credentials — so another - credential's jobs count against it. + `request_id` makes creation idempotent: a retry returns the original + job. A token is released when its job is canceled or fails before + finalization; otherwise it continues to resolve to that job. Parameters ---------- request_id : typing.Optional[str] - Optional client idempotency token. A retried create with the same - token returns the original job instead of opening a second one. + Optional client idempotency token (at most 256 UTF-8 bytes). A + retried create with the same token returns the original job instead + of opening a second one. request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -1328,17 +1283,6 @@ async def create( ), ), ) - if _response.status_code == 409: - raise ConflictError( - headers=dict(_response.headers), - body=typing.cast( - typing.Any, - parse_obj_as( - type_=typing.Any, # type: ignore - object_=_response.json(), - ), - ), - ) if _response.status_code == 499: raise ClientClosedRequestError( headers=dict(_response.headers), @@ -1393,43 +1337,22 @@ async def upload_item( request_options: typing.Optional[RequestOptions] = None, ) -> AsyncHttpResponse[UploadItemResponse]: """ - Stores one item of a job from a multipart upload. A batch's items arrive - one per request. The item carries **either** a `document` extraction - (whose input file rides as raw bytes in the `file` part) **or** a - `create` extraction (JSON only, no file). - - The upload enforces these rules: - - Set **exactly one** of `document` or `create`. Setting both, or - neither, is a `400`. - - When `document` is set, `file` is **required** — it supplies the - document's binary content (PDF or image). - - When `create` is set, `file` is **forbidden** — a create item carries - no file. - - `document` and `create` must each be a JSON **object**. - - Only the item's structure is checked here: the fields inside `document` - or `create` are not validated at upload. A body that is well-formed JSON - but not a valid request for its endpoint is still accepted with `202` - and fails later during processing, recorded as an item `error`. A - wrong-typed field the endpoint cannot decode fails as `invalid_input`; a - body that decodes but the pipeline rejects (for example, a missing - required field) fails as `processing_failed`. - - Supplying `request_id` makes the upload idempotent on that token. A - re-upload under the same token overwrites the same item rather than - adding a second, so a client that lost an upload's response can safely - re-send it. The response's `deduplicated` is `true` only when the - re-uploaded payload matches the one already stored; a same-token upload - with a changed payload overwrites in place and returns `false`. - - Set a `request_id` on **every** upload: re-sending under the same token - is the only way to repair a lost or incomplete upload, including the one - a finalize `409` reports. Without one, a re-send adds a new item instead - of replacing the missing one, and the job cannot be finalized. - - Uploads are rejected once the job has been finalized (`409`), once it - holds its 500-item limit (`409`), or when the item is too large (`413` — - see the raw-file limit in the API description). + Stores one multipart item. Set **either** `document` with a raw `file`, + or JSON-only `create`. + + Set exactly one JSON object. `document` requires `file`; `create` + forbids it. Violations return `400`. + + Upload validates only the envelope. Endpoint request validation happens + during processing: decoding failures are `invalid_input`; other pipeline + failures are `processing_failed`. + + Use `request_id` for every upload so retries replace the same item. + `deduplicated` is true only for an unchanged payload; a changed payload + overwrites the item and returns false. + + Uploads return `409` after finalization or at the item limit, and `413` + when the item is too large. Parameters ---------- @@ -1439,35 +1362,38 @@ async def upload_item( The JSON body of `POST /lang2fhir/document/multi`, **without** its base64 `content` field — the uploaded `file` supplies the content. Accepts that endpoint's fields (`version`, `provider`, - `patient_reference`, `implementation_guide`, `detection_effort`, + `primary_patient`, `patient_reference` (deprecated), `implementation_guide`, `detection_effort` (deprecated), `validation_method`, `config`). This is the **multi**-resource body: it has no single-`resource` field, and the item's result is a `DocumentMultiResponse` (a Bundle of resources). Mutually - exclusive with `create`; requires `file`. + exclusive with `create`; requires `file`. Do not combine + `primary_patient` with `patient_reference`. create : typing.Optional[typing.Dict[str, typing.Any]] The JSON body of `POST /lang2fhir/create/multi`. Accepts that endpoint's fields (`text`, `version`, `provider`, - `patient_reference`, `implementation_guide`, `detection_effort`, + `primary_patient`, `patient_reference` (deprecated), `implementation_guide`, `detection_effort` (deprecated), `validation_method`, `resource_review`). This is the **multi**-resource body: it has no single-`resource` field, and the item's result is a `CreateMultiResponse` (a Bundle of resources). Mutually exclusive with `document`; must **not** be - accompanied by a `file`. + accompanied by a `file`. Do not combine `primary_patient` with + `patient_reference`. file : typing.Optional[core.File] See core.File for more documentation request_id : typing.Optional[str] - Optional idempotency token (max 256 bytes). Re-uploading under - the same token overwrites the same item instead of adding a - new one. The token is scoped to this job; the same token in - another job is independent and creates a separate item. + Optional idempotency token (at most 256 UTF-8 bytes). + Re-uploading under the same token overwrites the same item + instead of adding a new one. The token is scoped to this job; + the same token in another job is independent and creates a + separate item. id : typing.Optional[str] - Optional caller-supplied correlation label (max 512 bytes), - echoed back on status and result listings so you can match the - server's item_id to your own record. + Optional caller-supplied correlation label (at most 512 UTF-8 + bytes), echoed back on status and result listings so you can + match the server's item_id to your own record. request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -1736,8 +1662,8 @@ async def cancel( self, job_id: str, *, request_options: typing.Optional[RequestOptions] = None ) -> AsyncHttpResponse[BatchJob]: """ - Drives a job to the terminal `canceled` state on request, freeing its - active-job slot immediately. Takes no request body. + Drives a job to the terminal `canceled` state on request. Takes no + request body. Cancel does not delete the job: the job record and any results already produced are preserved for the normal retention window, the same as a diff --git a/src/phenoml/lang2fhir_batch/types/batch_error.py b/src/phenoml/lang2fhir_batch/types/batch_error.py index 29efa459..396e45ae 100644 --- a/src/phenoml/lang2fhir_batch/types/batch_error.py +++ b/src/phenoml/lang2fhir_batch/types/batch_error.py @@ -15,12 +15,10 @@ class BatchError(UniversalBaseModel): """ Short stable token to branch on. Item-level kinds: `invalid_input` (the stored body was not a valid create/document request), - `processing_failed` (the conversion failed), `budget_exceeded` (the - item ran past its time budget — 600s for a document item, 450s for a - create item), `result_too_large` (the result exceeded the storage - cap), `input_unavailable` (the input could not be read), and - `retries_exhausted` / `attempts_exhausted` (buried after too many - failed attempts). + `processing_failed` (the conversion failed), `result_too_large` (the + result exceeded the storage cap), `input_unavailable` (the input + could not be read), and `retries_exhausted` / `attempts_exhausted` + (the item could not complete after repeated interruptions). Job-level kinds: `timeout` (the job did not finish within 36 hours of creation). """ diff --git a/src/phenoml/lang2fhir_batch/types/batch_item_status.py b/src/phenoml/lang2fhir_batch/types/batch_item_status.py index e2da4e99..84e5f98f 100644 --- a/src/phenoml/lang2fhir_batch/types/batch_item_status.py +++ b/src/phenoml/lang2fhir_batch/types/batch_item_status.py @@ -31,9 +31,8 @@ class BatchItemStatus(UniversalBaseModel): attempts: int = pydantic.Field() """ - Number of processing attempts so far. An item runs up to 3 attempts, - and only an interruption — a worker preemption or a recovered internal - error — is retried; a conversion error fails the item with no retry. + Number of processing attempts started so far. The service may retry + interrupted work; a conversion error fails the item without retry. """ detect_retries: typing.Optional[int] = pydantic.Field(default=None) diff --git a/src/phenoml/lang2fhir_batch/types/batch_job.py b/src/phenoml/lang2fhir_batch/types/batch_job.py index a574933c..91bd46bd 100644 --- a/src/phenoml/lang2fhir_batch/types/batch_job.py +++ b/src/phenoml/lang2fhir_batch/types/batch_job.py @@ -31,8 +31,8 @@ class BatchJob(UniversalBaseModel): failure (the job could not run at all), distinct from individual item failures, which never fail the job. `canceled` is a caller-requested cancellation via `POST /lang2fhir/batch/{job_id}/cancel`; like the - other terminal states it frees the job's active-job slot and keeps any - results already produced readable for the retention window. + other terminal states it keeps any results already produced readable + for the retention window. """ finalized: bool = pydantic.Field() diff --git a/src/phenoml/profiles/profiles/raw_client.py b/src/phenoml/profiles/profiles/raw_client.py index 907ac1da..54ef7fc1 100644 --- a/src/phenoml/profiles/profiles/raw_client.py +++ b/src/phenoml/profiles/profiles/raw_client.py @@ -527,6 +527,17 @@ def delete(self, id: str, *, request_options: typing.Optional[RequestOptions] = ), ), ) + if _response.status_code == 409: + raise ConflictError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) if _response.status_code == 500: raise InternalServerError( headers=dict(_response.headers), @@ -1051,6 +1062,17 @@ async def delete( ), ), ) + if _response.status_code == 409: + raise ConflictError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) if _response.status_code == 500: raise InternalServerError( headers=dict(_response.headers), diff --git a/src/phenoml/profiles/types/profile_summary.py b/src/phenoml/profiles/types/profile_summary.py index b9687dbb..7b0be351 100644 --- a/src/phenoml/profiles/types/profile_summary.py +++ b/src/phenoml/profiles/types/profile_summary.py @@ -20,7 +20,7 @@ class ProfileSummary(UniversalBaseModel): source: ProfileSummarySource = pydantic.Field() """ - The profile's origin. Profile management responses currently return custom (uploaded) profiles, so this is always "custom" today. + The profile's origin. Profile management responses return custom (uploaded) profiles, so this value is always "custom". """ resource_type: str = pydantic.Field() diff --git a/src/phenoml/profiles/versions/raw_client.py b/src/phenoml/profiles/versions/raw_client.py index 0a1e0c12..7f77e6ee 100644 --- a/src/phenoml/profiles/versions/raw_client.py +++ b/src/phenoml/profiles/versions/raw_client.py @@ -429,6 +429,17 @@ def delete( ), ), ) + if _response.status_code == 409: + raise ConflictError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) if _response.status_code == 500: raise InternalServerError( headers=dict(_response.headers), @@ -853,6 +864,17 @@ async def delete( ), ), ) + if _response.status_code == 409: + raise ConflictError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) if _response.status_code == 500: raise InternalServerError( headers=dict(_response.headers), diff --git a/tests/utils/test_http_client.py b/tests/utils/test_http_client.py index 732c2f97..4cf2992c 100644 --- a/tests/utils/test_http_client.py +++ b/tests/utils/test_http_client.py @@ -11,6 +11,7 @@ HttpClient, _build_url, _should_retry, + drop_content_type_without_body, get_request_body, remove_none_from_dict, ) @@ -121,6 +122,67 @@ def test_explicit_empty_json_body_is_preserved() -> None: assert data_body2 == {} +def test_omitted_body_sends_no_content() -> None: + """A body left at the sentinel was never passed, so nothing is sent. + + This is how an endpoint whose body may be omitted stays bodyless, as opposed to + sending an empty ``{}``. An explicit ``None`` still reaches the wire as ``null``. + """ + omit = cast(Any, ...) + unrelated_request_options: RequestOptions = {"max_retries": 3} + + json_body, data_body = get_request_body(json=omit, data=None, request_options=unrelated_request_options, omit=omit) + assert json_body is None + assert data_body is None + + json_body2, data_body2 = get_request_body( + json=None, data=omit, request_options=unrelated_request_options, omit=omit + ) + assert json_body2 is None + assert data_body2 is None + + # an explicitly passed body is untouched + json_body3, _ = get_request_body(json={"hello": "world"}, data=None, request_options=None, omit=omit) + assert json_body3 == {"hello": "world"} + + +def test_optional_body_sends_no_content_when_every_property_is_omitted() -> None: + """An endpoint that inlines an omittable body sends nothing once every property is omitted. + + The body reaches the client as a dict of sentinels rather than as a single argument, so + ``optional_body`` is what tells the client that an empty result means "no body at all". + """ + omit = cast(Any, ...) + + json_body, data_body = get_request_body( + json={"amount": omit, "source": omit}, data=None, request_options=None, omit=omit, optional_body=True + ) + assert json_body is None + assert data_body is None + + # a body the API requires still goes out as `{}` + required_json_body, _ = get_request_body( + json={"amount": omit, "source": omit}, data=None, request_options=None, omit=omit + ) + assert required_json_body == {} + + # a property the caller did pass keeps the body + populated_json_body, _ = get_request_body( + json={"amount": 1, "source": omit}, data=None, request_options=None, omit=omit, optional_body=True + ) + assert populated_json_body == {"amount": 1} + + # additional body parameters keep the body, since the caller asked for them + with_additional_body_parameters, _ = get_request_body( + json={"amount": omit}, + data=None, + request_options={"additional_body_parameters": {"custom": "value"}}, + omit=omit, + optional_body=True, + ) + assert with_additional_body_parameters == {"custom": "value"} + + def test_json_body_preserves_none_values() -> None: """Test that JSON bodies preserve None values (they become JSON null).""" json_body, data_body = get_request_body( @@ -672,28 +734,20 @@ def _make_response(status_code: int) -> httpx.Response: return httpx.Response(status_code=status_code, content=b"") -@pytest.mark.parametrize( - "status_code", - [408, 409, 429, 500, 501, 502, 503, 504, 599], -) +RETRYABLE_STATUS_CODES = [408, 409, 429, 500, 501, 502, 503, 504, 599] +NON_RETRYABLE_STATUS_CODES = [200, 201, 301, 400, 401, 403, 404] + + +@pytest.mark.parametrize("status_code", RETRYABLE_STATUS_CODES) def test_should_retry_retryable_status_codes(status_code: int) -> None: - """Legacy mode: retries on 408, 409, 429, and all >= 500.""" assert _should_retry(_make_response(status_code)) is True -@pytest.mark.parametrize( - "status_code", - [200, 201, 301, 400, 401, 403, 404], -) +@pytest.mark.parametrize("status_code", NON_RETRYABLE_STATUS_CODES) def test_should_not_retry_non_retryable_status_codes(status_code: int) -> None: assert _should_retry(_make_response(status_code)) is False -def test_should_retry_599_upper_boundary() -> None: - """Legacy mode retries on >= 500, which includes 599.""" - assert _should_retry(_make_response(599)) is True - - # --------------------------------------------------------------------------- # RequestOptions timeout resolution tests (timeout / deprecated timeout_in_seconds) # --------------------------------------------------------------------------- @@ -759,3 +813,33 @@ async def test_async_request_options_timeout_takes_precedence() -> None: ) await http_client.request(path="/test", method="GET", request_options={"timeout": 30, "timeout_in_seconds": 45}) assert dummy_client.last_request_kwargs["timeout"] == 30 + + +def test_drop_content_type_without_body_omits_header_for_bodyless_optional_call() -> None: + """An optional-body endpoint the caller left empty must not advertise a media type. + + `get_request_body` drops the body, but the endpoint still hands over the content type it + would have used, so a server that branches on the header would see a JSON request carrying + nothing at all. + """ + headers = {"content-type": "application/json", "authorization": "Bearer x"} + + assert drop_content_type_without_body(headers, json_body=None, data_body=None, optional_body=True) == { + "authorization": "Bearer x" + } + + +def test_drop_content_type_without_body_keeps_header_when_a_body_is_sent() -> None: + headers = {"content-type": "application/json"} + + assert ( + drop_content_type_without_body(headers, json_body={"amount": 60}, data_body=None, optional_body=True) == headers + ) + assert drop_content_type_without_body(headers, json_body=None, data_body="raw", optional_body=True) == headers + + +def test_drop_content_type_without_body_leaves_required_body_endpoints_alone() -> None: + """Without the opt-in, an endpoint keeps the header it has always sent.""" + headers = {"Content-Type": "application/json"} + + assert drop_content_type_without_body(headers, json_body=None, data_body=None, optional_body=False) == headers diff --git a/tests/wire/test_fhir2Omop.py b/tests/wire/test_fhir2Omop.py index dc7f0910..010128ef 100644 --- a/tests/wire/test_fhir2Omop.py +++ b/tests/wire/test_fhir2Omop.py @@ -40,6 +40,7 @@ def test_fhir2Omop_create() -> None: "resourceType": "MedicationRequest", "id": "medreq-1", "status": "active", + "intent": "order", "subject": {"reference": "Patient/patient-1"}, "medicationReference": {"reference": "#med0"}, "authoredOn": "2024-01-16", diff --git a/tests/wire/test_implementationGuides_implementationGuides.py b/tests/wire/test_implementationGuides_implementationGuides.py index f65a2297..f0cef92e 100644 --- a/tests/wire/test_implementationGuides_implementationGuides.py +++ b/tests/wire/test_implementationGuides_implementationGuides.py @@ -1,5 +1,7 @@ from .conftest import get_client, verify_request_count +from phenoml.implementation_guides import FhirImplementationGuide + def test_implementationGuides_implementationGuides_list_() -> None: """Test list endpoint with WireMock""" @@ -37,3 +39,30 @@ def test_implementationGuides_implementationGuides_delete() -> None: name="acme-cardiology", ) verify_request_count(test_id, "DELETE", "/fhir/implementation-guides/acme-cardiology", None, 1) + + +def test_implementationGuides_implementationGuides_create_version() -> None: + """Test createVersion endpoint with WireMock""" + test_id = "implementation_guides.implementation_guides.create_version.0" + client = get_client(test_id) + client.implementation_guides.implementation_guides.create_version( + name="name", + implementation_guide=FhirImplementationGuide( + resource_type="ImplementationGuide", + url="url", + version="version", + ), + profile_refs=["profile_refs"], + ) + verify_request_count(test_id, "POST", "/fhir/implementation-guides/name/versions", None, 1) + + +def test_implementationGuides_implementationGuides_get_version() -> None: + """Test getVersion endpoint with WireMock""" + test_id = "implementation_guides.implementation_guides.get_version.0" + client = get_client(test_id) + client.implementation_guides.implementation_guides.get_version( + name="name", + version="1.0.0", + ) + verify_request_count(test_id, "GET", "/fhir/implementation-guides/name/versions/1.0.0", None, 1) diff --git a/tests/wire/test_lang2Fhir.py b/tests/wire/test_lang2Fhir.py index 8095b877..5e23cb84 100644 --- a/tests/wire/test_lang2Fhir.py +++ b/tests/wire/test_lang2Fhir.py @@ -55,7 +55,7 @@ def test_lang2Fhir_document() -> None: client.lang2fhir.document( version="R4", resource="questionnaire", - content="JVBERi0xLjQKJeLjz9MK...(base64-encoded PDF or image bytes)", + content="JVBERi0xLjQKJeLjz9MK...(base64-encoded document bytes)", ) verify_request_count(test_id, "POST", "/lang2fhir/document", None, 1) @@ -66,7 +66,7 @@ def test_lang2Fhir_document_multi() -> None: client = get_client(test_id) client.lang2fhir.document_multi( version="R4", - content="JVBERi0xLjQKJeLjz9MK...(base64-encoded PDF or image bytes)", + content="JVBERi0xLjQKJeLjz9MK...(base64-encoded document bytes)", provider="medplum", config=DocumentConfig( split_classifications=[ diff --git a/wiremock/wiremock-mappings.json b/wiremock/wiremock-mappings.json index 9d3fc774..58277694 100644 --- a/wiremock/wiremock-mappings.json +++ b/wiremock/wiremock-mappings.json @@ -5,7 +5,12 @@ "name": "Create a new agent - default", "request": { "urlPathTemplate": "/agent/create", - "method": "POST" + "method": "POST", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + } }, "response": { "status": 201, @@ -32,6 +37,11 @@ "request": { "urlPathTemplate": "/agent/list", "method": "GET", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "queryParameters": { "tags": { "equalTo": "tags" @@ -64,6 +74,11 @@ "request": { "urlPathTemplate": "/agent/{id}", "method": "GET", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "pathParameters": { "id": { "equalTo": "id" @@ -95,6 +110,11 @@ "request": { "urlPathTemplate": "/agent/{id}", "method": "PUT", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "pathParameters": { "id": { "equalTo": "id" @@ -126,6 +146,11 @@ "request": { "urlPathTemplate": "/agent/{id}", "method": "DELETE", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "pathParameters": { "id": { "equalTo": "id" @@ -157,6 +182,11 @@ "request": { "urlPathTemplate": "/agent/{id}", "method": "PATCH", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "pathParameters": { "id": { "equalTo": "id" @@ -187,7 +217,12 @@ "name": "Chat with agent - Query Patient Condition", "request": { "urlPathTemplate": "/agent/chat", - "method": "POST" + "method": "POST", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + } }, "response": { "status": 200, @@ -213,7 +248,12 @@ "name": "Chat with agent (streaming) - default", "request": { "urlPathTemplate": "/agent/stream-chat", - "method": "POST" + "method": "POST", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + } }, "response": { "status": 200, @@ -240,6 +280,11 @@ "request": { "urlPathTemplate": "/agent/chat/messages", "method": "GET", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "queryParameters": { "chat_session_id": { "equalTo": "chat_session_id" @@ -280,7 +325,12 @@ "name": "Create agent prompt - default", "request": { "urlPathTemplate": "/agent/prompts", - "method": "POST" + "method": "POST", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + } }, "response": { "status": 201, @@ -306,7 +356,12 @@ "name": "List agent prompts - default", "request": { "urlPathTemplate": "/agent/prompts/list", - "method": "GET" + "method": "GET", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + } }, "response": { "status": 200, @@ -334,6 +389,11 @@ "request": { "urlPathTemplate": "/agent/prompts/{id}", "method": "GET", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "pathParameters": { "id": { "equalTo": "id" @@ -365,6 +425,11 @@ "request": { "urlPathTemplate": "/agent/prompts/{id}", "method": "PUT", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "pathParameters": { "id": { "equalTo": "id" @@ -396,6 +461,11 @@ "request": { "urlPathTemplate": "/agent/prompts/{id}", "method": "DELETE", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "pathParameters": { "id": { "equalTo": "id" @@ -427,6 +497,11 @@ "request": { "urlPathTemplate": "/agent/prompts/{id}", "method": "PATCH", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "pathParameters": { "id": { "equalTo": "id" @@ -483,7 +558,12 @@ "name": "Analyze text for patient cohort criteria - default", "request": { "urlPathTemplate": "/cohort", - "method": "POST" + "method": "POST", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + } }, "response": { "status": 200, @@ -509,7 +589,12 @@ "name": "Upload custom code system - JSON codes (preferred)", "request": { "urlPathTemplate": "/construe/upload", - "method": "POST" + "method": "POST", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + } }, "response": { "status": 202, @@ -535,7 +620,12 @@ "name": "List available code systems - default", "request": { "urlPathTemplate": "/construe/codes/systems", - "method": "GET" + "method": "GET", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + } }, "response": { "status": 200, @@ -563,6 +653,11 @@ "request": { "urlPathTemplate": "/construe/codes/systems/{codesystem}", "method": "GET", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "pathParameters": { "codesystem": { "equalTo": "ICD-10-CM" @@ -599,6 +694,11 @@ "request": { "urlPathTemplate": "/construe/codes/systems/{codesystem}", "method": "DELETE", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "pathParameters": { "codesystem": { "equalTo": "CUSTOM_CODES" @@ -635,6 +735,11 @@ "request": { "urlPathTemplate": "/construe/codes/systems/{codesystem}/export", "method": "GET", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "pathParameters": { "codesystem": { "equalTo": "CUSTOM_CODES" @@ -670,7 +775,12 @@ "name": "Submit feedback on extraction results - default", "request": { "urlPathTemplate": "/construe/feedback", - "method": "POST" + "method": "POST", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + } }, "response": { "status": 201, @@ -696,7 +806,12 @@ "name": "Extract medical codes from text - Basic Extraction", "request": { "urlPathTemplate": "/construe/extract", - "method": "POST" + "method": "POST", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + } }, "response": { "status": 200, @@ -722,7 +837,12 @@ "name": "[Alpha] Extract medical codes with phenocr - HPO Phenotype Extraction", "request": { "urlPathTemplate": "/construe/phenocr", - "method": "POST" + "method": "POST", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + } }, "response": { "status": 200, @@ -748,7 +868,12 @@ "name": "Crosswalk a code to target code systems - ICD-10-CM to HPO", "request": { "urlPathTemplate": "/construe/codes/crosswalk", - "method": "POST" + "method": "POST", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + } }, "response": { "status": 200, @@ -775,6 +900,11 @@ "request": { "urlPathTemplate": "/construe/codes/{codesystem}", "method": "GET", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "pathParameters": { "codesystem": { "equalTo": "ICD-10-CM" @@ -817,6 +947,11 @@ "request": { "urlPathTemplate": "/construe/codes/{codesystem}/{codeID}", "method": "GET", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "pathParameters": { "codesystem": { "equalTo": "ICD-10-CM" @@ -856,6 +991,11 @@ "request": { "urlPathTemplate": "/construe/codes/{codesystem}/search/semantic", "method": "GET", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "pathParameters": { "codesystem": { "equalTo": "ICD-10-CM" @@ -898,6 +1038,11 @@ "request": { "urlPathTemplate": "/construe/codes/{codesystem}/search/text", "method": "GET", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "pathParameters": { "codesystem": { "equalTo": "ICD-10-CM" @@ -940,6 +1085,11 @@ "request": { "urlPathTemplate": "/fhir-provider/{fhir_provider_id}/fhir/{fhir_path}", "method": "GET", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "pathParameters": { "fhir_provider_id": { "equalTo": "550e8400-e29b-41d4-a716-446655440000" @@ -974,6 +1124,11 @@ "request": { "urlPathTemplate": "/fhir-provider/{fhir_provider_id}/fhir/{fhir_path}", "method": "POST", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "pathParameters": { "fhir_provider_id": { "equalTo": "550e8400-e29b-41d4-a716-446655440000" @@ -1008,6 +1163,11 @@ "request": { "urlPathTemplate": "/fhir-provider/{fhir_provider_id}/fhir/{fhir_path}", "method": "PUT", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "pathParameters": { "fhir_provider_id": { "equalTo": "550e8400-e29b-41d4-a716-446655440000" @@ -1042,6 +1202,11 @@ "request": { "urlPathTemplate": "/fhir-provider/{fhir_provider_id}/fhir/{fhir_path}", "method": "DELETE", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "pathParameters": { "fhir_provider_id": { "equalTo": "550e8400-e29b-41d4-a716-446655440000" @@ -1076,6 +1241,11 @@ "request": { "urlPathTemplate": "/fhir-provider/{fhir_provider_id}/fhir/{fhir_path}", "method": "PATCH", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "pathParameters": { "fhir_provider_id": { "equalTo": "550e8400-e29b-41d4-a716-446655440000" @@ -1110,6 +1280,11 @@ "request": { "urlPathTemplate": "/fhir-provider/{fhir_provider_id}/fhir", "method": "POST", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "pathParameters": { "fhir_provider_id": { "equalTo": "550e8400-e29b-41d4-a716-446655440000" @@ -1136,20 +1311,25 @@ } }, { - "id": "a7d8dc82-74e1-4559-913a-04a444d02bc1", - "name": "Map FHIR resources to OMOP CDM v5.4 - Mapping result", + "id": "320c1df7-9ebd-4cd0-91a6-4aa888e82c78", + "name": "Map FHIR resources to OMOP CDM v5.4 - Bundle with a referenced medication", "request": { "urlPathTemplate": "/fhir2omop/create", - "method": "POST" + "method": "POST", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + } }, "response": { "status": 200, - "body": "{\n \"success\": true,\n \"message\": \"FHIR resources mapped to OMOP CDM v5.4\",\n \"tables\": {\n \"location\": [\n {}\n ],\n \"care_site\": [\n {}\n ],\n \"provider\": [\n {}\n ],\n \"person\": [\n {\n \"person_id\": 1,\n \"gender_concept_id\": 0,\n \"year_of_birth\": 1985,\n \"month_of_birth\": 7,\n \"day_of_birth\": 22,\n \"birth_datetime\": \"1985-07-22\",\n \"race_concept_id\": 0,\n \"ethnicity_concept_id\": 0,\n \"person_source_value\": \"patient-1\",\n \"gender_source_value\": \"female\"\n }\n ],\n \"death\": [\n {}\n ],\n \"observation_period\": [\n {}\n ],\n \"visit_occurrence\": [\n {}\n ],\n \"condition_occurrence\": [\n {\n \"condition_occurrence_id\": 1,\n \"person_id\": 1,\n \"condition_concept_id\": 201826,\n \"condition_start_date\": \"2024-01-15\",\n \"condition_start_datetime\": \"2024-01-15\",\n \"condition_type_concept_id\": 32817,\n \"condition_source_value\": \"http://snomed.info/sct#44054006\",\n \"condition_source_concept_id\": 201826\n }\n ],\n \"drug_exposure\": [\n {\n \"drug_exposure_id\": 1,\n \"person_id\": 1,\n \"drug_concept_id\": 40163924,\n \"drug_exposure_start_date\": \"2024-01-16\",\n \"drug_exposure_start_datetime\": \"2024-01-16\",\n \"drug_type_concept_id\": 32817,\n \"drug_source_value\": \"http://www.nlm.nih.gov/research/umls/rxnorm#860975\",\n \"drug_source_concept_id\": 40163924\n }\n ],\n \"procedure_occurrence\": [\n {}\n ],\n \"measurement\": [\n {}\n ],\n \"observation\": [\n {}\n ]\n },\n \"mappings\": [\n {\n \"resource_type\": \"Condition\",\n \"resource_id\": \"condition-1\",\n \"omop_table\": \"condition_occurrence\",\n \"omop_id\": 1,\n \"source_system\": \"http://snomed.info/sct\",\n \"source_code\": \"44054006\",\n \"source_name\": \"Type 2 diabetes mellitus\",\n \"target_vocabulary\": \"SNOMED\",\n \"target_code\": \"44054006\",\n \"target_name\": \"Type 2 diabetes mellitus\",\n \"mapping_status\": \"ALREADY_STANDARD\",\n \"note\": \"note\"\n },\n {\n \"resource_type\": \"MedicationRequest\",\n \"resource_id\": \"medreq-1\",\n \"omop_table\": \"drug_exposure\",\n \"omop_id\": 1,\n \"source_system\": \"http://www.nlm.nih.gov/research/umls/rxnorm\",\n \"source_code\": \"860975\",\n \"source_name\": \"metformin hydrochloride 500 MG\",\n \"target_vocabulary\": \"RXNORM\",\n \"target_code\": \"860975\",\n \"target_name\": \"metformin hydrochloride 500 MG\",\n \"mapping_status\": \"ALREADY_STANDARD\",\n \"note\": \"note\"\n }\n ],\n \"dropped\": [\n {\n \"resource_type\": \"resource_type\",\n \"resource_id\": \"resource_id\",\n \"reason\": \"reason\"\n }\n ],\n \"vocab_version\": \"v20240229\",\n \"summary\": {\n \"codes_already_standard\": 2,\n \"codes_normalized\": 0,\n \"codes_unmapped\": 0,\n \"off_vocab_rate\": 0\n }\n}", + "body": "{\n \"success\": true,\n \"message\": \"FHIR resources mapped to OMOP CDM v5.4\",\n \"tables\": {\n \"location\": [\n {}\n ],\n \"care_site\": [\n {}\n ],\n \"provider\": [\n {}\n ],\n \"person\": [\n {\n \"person_id\": 1,\n \"gender_concept_id\": 8532,\n \"year_of_birth\": 1985,\n \"month_of_birth\": 7,\n \"day_of_birth\": 22,\n \"race_concept_id\": 0,\n \"ethnicity_concept_id\": 0,\n \"person_source_value\": \"patient-1\",\n \"gender_source_value\": \"female\",\n \"gender_source_concept_id\": 0,\n \"race_source_concept_id\": 0,\n \"ethnicity_source_concept_id\": 0\n }\n ],\n \"death\": [\n {}\n ],\n \"observation_period\": [\n {\n \"observation_period_id\": 1,\n \"person_id\": 1,\n \"observation_period_start_date\": \"2024-01-15\",\n \"observation_period_end_date\": \"2024-01-16\",\n \"period_type_concept_id\": 32817\n }\n ],\n \"visit_occurrence\": [\n {}\n ],\n \"condition_occurrence\": [\n {\n \"condition_occurrence_id\": 1,\n \"person_id\": 1,\n \"condition_concept_id\": 201826,\n \"condition_start_date\": \"2024-01-15\",\n \"condition_type_concept_id\": 32817,\n \"condition_source_value\": \"44054006\",\n \"condition_source_concept_id\": 201826\n }\n ],\n \"drug_exposure\": [\n {\n \"drug_exposure_id\": 1,\n \"person_id\": 1,\n \"drug_concept_id\": 40163924,\n \"drug_exposure_start_date\": \"2024-01-16\",\n \"drug_type_concept_id\": 32838,\n \"drug_source_value\": \"860975\",\n \"drug_source_concept_id\": 40163924\n }\n ],\n \"procedure_occurrence\": [\n {}\n ],\n \"measurement\": [\n {}\n ],\n \"observation\": [\n {}\n ]\n },\n \"mappings\": [\n {\n \"resource_type\": \"Patient\",\n \"resource_id\": \"patient-1\",\n \"omop_table\": \"person\",\n \"omop_id\": 1,\n \"omop_field\": \"gender_concept_id\",\n \"source_system\": \"http://hl7.org/fhir/administrative-gender\",\n \"source_code\": \"female\",\n \"source_name\": \"female\",\n \"target_vocabulary\": \"Gender\",\n \"target_code\": \"F\",\n \"target_name\": \"FEMALE\",\n \"mapping_status\": \"MAPPED\",\n \"selected\": true,\n \"note\": \"FHIR administrative gender; assumed sex at birth\"\n },\n {\n \"resource_type\": \"Condition\",\n \"resource_id\": \"condition-1\",\n \"omop_table\": \"condition_occurrence\",\n \"omop_id\": 1,\n \"omop_field\": \"condition_concept_id\",\n \"source_system\": \"http://snomed.info/sct\",\n \"source_code\": \"44054006\",\n \"source_name\": \"Type 2 diabetes mellitus\",\n \"target_vocabulary\": \"SNOMED\",\n \"target_code\": \"44054006\",\n \"target_name\": \"Type 2 diabetes mellitus\",\n \"mapping_status\": \"ALREADY_STANDARD\",\n \"selected\": true,\n \"note\": \"note\"\n },\n {\n \"resource_type\": \"MedicationRequest\",\n \"resource_id\": \"medreq-1\",\n \"omop_table\": \"drug_exposure\",\n \"omop_id\": 1,\n \"omop_field\": \"drug_concept_id\",\n \"source_system\": \"http://www.nlm.nih.gov/research/umls/rxnorm\",\n \"source_code\": \"860975\",\n \"source_name\": \"metformin hydrochloride 500 MG\",\n \"target_vocabulary\": \"RXNORM\",\n \"target_code\": \"860975\",\n \"target_name\": \"metformin hydrochloride 500 MG\",\n \"mapping_status\": \"ALREADY_STANDARD\",\n \"selected\": true,\n \"note\": \"note\"\n }\n ],\n \"provider_role_contexts\": [\n {\n \"provider_id\": 1000000,\n \"role_source_value\": \"role_source_value\",\n \"practitioner_reference\": \"practitioner_reference\",\n \"practitioner_identifier\": {\n \"value\": \"value\"\n },\n \"role_codes\": [\n {}\n ],\n \"specialties\": [\n {}\n ],\n \"care_sites\": [\n {\n \"path\": \"path\",\n \"reference\": \"reference\"\n }\n ],\n \"active\": true,\n \"period_start\": \"period_start\",\n \"period_end\": \"period_end\"\n }\n ],\n \"dropped\": [\n {\n \"resource_type\": \"resource_type\",\n \"resource_id\": \"resource_id\",\n \"reason\": \"reason\"\n }\n ],\n \"diagnostics\": [\n {\n \"resource_type\": \"resource_type\",\n \"resource_id\": \"resource_id\",\n \"path\": \"path\",\n \"reference\": \"reference\",\n \"outcome\": \"UNRESOLVED\",\n \"reason\": \"reason\"\n }\n ],\n \"vocab_version\": \"v20260227\",\n \"summary\": {\n \"codes_already_standard\": 2,\n \"codes_normalized\": 1,\n \"codes_unmapped\": 0,\n \"off_vocab_rate\": 0.3333333333333333\n }\n}", "headers": { "Content-Type": "application/json" } }, - "uuid": "a7d8dc82-74e1-4559-913a-04a444d02bc1", + "uuid": "320c1df7-9ebd-4cd0-91a6-4aa888e82c78", "persistent": true, "priority": 3, "metadata": { @@ -1166,7 +1346,12 @@ "name": "Create FHIR provider - default", "request": { "urlPathTemplate": "/fhir-provider", - "method": "POST" + "method": "POST", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + } }, "response": { "status": 201, @@ -1192,7 +1377,12 @@ "name": "List FHIR providers - default", "request": { "urlPathTemplate": "/fhir-provider/list", - "method": "GET" + "method": "GET", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + } }, "response": { "status": 200, @@ -1220,6 +1410,11 @@ "request": { "urlPathTemplate": "/fhir-provider/{fhir_provider_id}", "method": "GET", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "pathParameters": { "fhir_provider_id": { "equalTo": "fhir_provider_id" @@ -1251,6 +1446,11 @@ "request": { "urlPathTemplate": "/fhir-provider/{fhir_provider_id}", "method": "DELETE", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "pathParameters": { "fhir_provider_id": { "equalTo": "fhir_provider_id" @@ -1282,6 +1482,11 @@ "request": { "urlPathTemplate": "/fhir-provider/{fhir_provider_id}/add-auth-config", "method": "PATCH", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "pathParameters": { "fhir_provider_id": { "equalTo": "1716d214-de93-43a4-aa6b-a878d864e2ad" @@ -1313,6 +1518,11 @@ "request": { "urlPathTemplate": "/fhir-provider/{fhir_provider_id}/set-active-auth-config", "method": "PATCH", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "pathParameters": { "fhir_provider_id": { "equalTo": "1716d214-de93-43a4-aa6b-a878d864e2ad" @@ -1344,6 +1554,11 @@ "request": { "urlPathTemplate": "/fhir-provider/{fhir_provider_id}/remove-auth-config", "method": "PATCH", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "pathParameters": { "fhir_provider_id": { "equalTo": "1716d214-de93-43a4-aa6b-a878d864e2ad" @@ -1374,11 +1589,16 @@ "name": "List implementation guides - default", "request": { "urlPathTemplate": "/fhir/implementation-guides", - "method": "GET" + "method": "GET", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + } }, "response": { "status": 200, - "body": "{\n \"implementation_guides\": [\n {\n \"name\": \"acme-cardiology\",\n \"profile_context\": \"When the text mentions phenotypic features, prefer the hpo-observation profile over Condition.\",\n \"profile_count\": 3,\n \"created_at\": \"2024-01-15T09:30:00Z\",\n \"updated_at\": \"2024-01-15T09:30:00Z\"\n }\n ]\n}", + "body": "{\n \"implementation_guides\": [\n {\n \"name\": \"acme-cardiology\",\n \"profile_context\": \"When the text mentions phenotypic features, prefer the hpo-observation profile over Condition.\",\n \"profile_count\": 3,\n \"canonical_url\": \"canonical_url\",\n \"version_count\": 1,\n \"created_at\": \"2024-01-15T09:30:00Z\",\n \"updated_at\": \"2024-01-15T09:30:00Z\"\n }\n ]\n}", "headers": { "Content-Type": "application/json" } @@ -1402,6 +1622,11 @@ "request": { "urlPathTemplate": "/fhir/implementation-guides/{name}", "method": "GET", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "pathParameters": { "name": { "equalTo": "acme-cardiology" @@ -1410,7 +1635,7 @@ }, "response": { "status": 200, - "body": "{\n \"name\": \"acme-cardiology\",\n \"profile_context\": \"When the text mentions phenotypic features, prefer the hpo-observation profile over Condition.\",\n \"profile_count\": 3,\n \"created_at\": \"2024-01-15T09:30:00Z\",\n \"updated_at\": \"2024-01-15T09:30:00Z\",\n \"profiles\": [\n \"custom-patient\",\n \"acme-vital-signs\"\n ]\n}", + "body": "{\n \"name\": \"acme-cardiology\",\n \"profile_context\": \"When the text mentions phenotypic features, prefer the hpo-observation profile over Condition.\",\n \"profile_count\": 3,\n \"canonical_url\": \"canonical_url\",\n \"version_count\": 1,\n \"created_at\": \"2024-01-15T09:30:00Z\",\n \"updated_at\": \"2024-01-15T09:30:00Z\",\n \"profiles\": [\n \"custom-patient\",\n \"acme-vital-signs\"\n ]\n}", "headers": { "Content-Type": "application/json" } @@ -1433,6 +1658,11 @@ "request": { "urlPathTemplate": "/fhir/implementation-guides/{name}", "method": "PUT", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "pathParameters": { "name": { "equalTo": "acme-cardiology" @@ -1441,7 +1671,7 @@ }, "response": { "status": 200, - "body": "{\n \"name\": \"acme-cardiology\",\n \"profile_context\": \"When the text mentions phenotypic features, prefer the hpo-observation profile over Condition.\",\n \"profile_count\": 3,\n \"created_at\": \"2024-01-15T09:30:00Z\",\n \"updated_at\": \"2024-01-15T09:30:00Z\"\n}", + "body": "{\n \"name\": \"acme-cardiology\",\n \"profile_context\": \"When the text mentions phenotypic features, prefer the hpo-observation profile over Condition.\",\n \"profile_count\": 3,\n \"canonical_url\": \"canonical_url\",\n \"version_count\": 1,\n \"created_at\": \"2024-01-15T09:30:00Z\",\n \"updated_at\": \"2024-01-15T09:30:00Z\"\n}", "headers": { "Content-Type": "application/json" } @@ -1459,11 +1689,16 @@ } }, { - "id": "44652922-4793-4275-840e-4dc1b1bde786", - "name": "Delete an implementation guide's metadata - default", + "id": "2678a6e7-10b0-4621-ad5c-67980574a8f8", + "name": "Delete an implementation guide family - default", "request": { "urlPathTemplate": "/fhir/implementation-guides/{name}", "method": "DELETE", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "pathParameters": { "name": { "equalTo": "acme-cardiology" @@ -1477,7 +1712,82 @@ "Content-Type": "application/json" } }, - "uuid": "44652922-4793-4275-840e-4dc1b1bde786", + "uuid": "2678a6e7-10b0-4621-ad5c-67980574a8f8", + "persistent": true, + "priority": 3, + "metadata": { + "mocklab": { + "created": { + "at": "2020-01-01T00:00:00.000Z", + "via": "SYSTEM" + } + } + } + }, + { + "id": "a7a30315-de87-4485-a1e4-15eeea48b530", + "name": "Publish an exact custom-profile package - default", + "request": { + "urlPathTemplate": "/fhir/implementation-guides/{name}/versions", + "method": "POST", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, + "pathParameters": { + "name": { + "equalTo": "name" + } + } + }, + "response": { + "status": 201, + "body": "{\n \"name\": \"name\",\n \"url\": \"url\",\n \"version\": \"version\",\n \"profile_context\": \"profile_context\",\n \"profiles\": [\n \"profiles\"\n ],\n \"profile_refs\": [\n \"profile_refs\"\n ],\n \"implementation_guide\": {\n \"resourceType\": \"ImplementationGuide\",\n \"id\": \"id\",\n \"url\": \"url\",\n \"version\": \"version\",\n \"name\": \"name\",\n \"status\": \"status\",\n \"packageId\": \"packageId\",\n \"fhirVersion\": [\n \"fhirVersion\"\n ]\n },\n \"created_at\": \"2024-01-15T09:30:00Z\",\n \"updated_at\": \"2024-01-15T09:30:00Z\"\n}", + "headers": { + "Content-Type": "application/json" + } + }, + "uuid": "a7a30315-de87-4485-a1e4-15eeea48b530", + "persistent": true, + "priority": 3, + "metadata": { + "mocklab": { + "created": { + "at": "2020-01-01T00:00:00.000Z", + "via": "SYSTEM" + } + } + } + }, + { + "id": "bfd69c60-e799-438e-a780-2c4c1fd1a9a8", + "name": "Get an exact custom-profile package - default", + "request": { + "urlPathTemplate": "/fhir/implementation-guides/{name}/versions/{version}", + "method": "GET", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, + "pathParameters": { + "name": { + "equalTo": "name" + }, + "version": { + "equalTo": "1.0.0" + } + } + }, + "response": { + "status": 200, + "body": "{\n \"name\": \"name\",\n \"url\": \"url\",\n \"version\": \"version\",\n \"profile_context\": \"profile_context\",\n \"profiles\": [\n \"profiles\"\n ],\n \"profile_refs\": [\n \"profile_refs\"\n ],\n \"implementation_guide\": {\n \"resourceType\": \"ImplementationGuide\",\n \"id\": \"id\",\n \"url\": \"url\",\n \"version\": \"version\",\n \"name\": \"name\",\n \"status\": \"status\",\n \"packageId\": \"packageId\",\n \"fhirVersion\": [\n \"fhirVersion\"\n ]\n },\n \"created_at\": \"2024-01-15T09:30:00Z\",\n \"updated_at\": \"2024-01-15T09:30:00Z\"\n}", + "headers": { + "Content-Type": "application/json" + } + }, + "uuid": "bfd69c60-e799-438e-a780-2c4c1fd1a9a8", "persistent": true, "priority": 3, "metadata": { @@ -1494,7 +1804,12 @@ "name": "Create FHIR resource from text - Condition Resource", "request": { "urlPathTemplate": "/lang2fhir/create", - "method": "POST" + "method": "POST", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + } }, "response": { "status": 200, @@ -1520,11 +1835,16 @@ "name": "Extract multiple FHIR resources from text - Full Patient Record", "request": { "urlPathTemplate": "/lang2fhir/create/multi", - "method": "POST" + "method": "POST", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + } }, "response": { "status": 200, - "body": "{\n \"success\": true,\n \"message\": \"Successfully extracted 3 resources\",\n \"bundle\": {\n \"resourceType\": \"Bundle\",\n \"type\": \"transaction\",\n \"entry\": [\n {\n \"fullUrl\": \"urn:uuid:patient-001\",\n \"resource\": {\n \"resourceType\": \"Patient\",\n \"name\": [\n {\n \"given\": [\n \"John\"\n ],\n \"family\": \"Smith\"\n }\n ],\n \"gender\": \"male\"\n },\n \"request\": {\n \"method\": \"POST\",\n \"url\": \"Patient\"\n }\n },\n {\n \"fullUrl\": \"urn:uuid:condition-001\",\n \"resource\": {\n \"resourceType\": \"Condition\",\n \"code\": {\n \"text\": \"Type 2 Diabetes\"\n }\n },\n \"request\": {\n \"method\": \"POST\",\n \"url\": \"Condition\"\n }\n },\n {\n \"fullUrl\": \"urn:uuid:medication-001\",\n \"resource\": {\n \"resourceType\": \"MedicationRequest\",\n \"medicationCodeableConcept\": {\n \"text\": \"Metformin 500mg\"\n }\n },\n \"request\": {\n \"method\": \"POST\",\n \"url\": \"MedicationRequest\"\n }\n }\n ]\n },\n \"resources\": [\n {\n \"tempId\": \"urn:uuid:a842c4bc-f6cb-4555-9741-ac3aec4ef0b8\",\n \"resourceType\": \"Patient\",\n \"description\": \"John Smith (DOB 1980-05-12) was diagnosed with Type 2 Diabetes during office visit on 2025-03-01 with Dr. Chen\",\n \"originalText\": \"diagnosed with Type 2 Diabetes\",\n \"group\": \"clinical\",\n \"sourcePages\": [\n 2\n ]\n }\n ],\n \"validation\": {\n \"passes\": [\n {}\n ],\n \"fixed\": true,\n \"attempts\": 1,\n \"summary\": \"summary\"\n },\n \"resource_review\": {\n \"flagged\": [\n {\n \"tempId\": \"urn:uuid:a842c4bc-f6cb-4555-9741-ac3aec4ef0b8\",\n \"resourceType\": \"Condition\",\n \"findings\": [\n {\n \"fieldPath\": \"onsetDateTime\",\n \"value\": \"2024-01-15\",\n \"supported\": false,\n \"rationale\": \"Date is the visit date, not when the condition began.\"\n }\n ]\n }\n ]\n }\n}", + "body": "{\n \"success\": true,\n \"message\": \"Successfully extracted 3 resources\",\n \"bundle\": {\n \"resourceType\": \"Bundle\",\n \"type\": \"transaction\",\n \"entry\": [\n {\n \"fullUrl\": \"urn:uuid:patient-001\",\n \"resource\": {\n \"resourceType\": \"Patient\",\n \"name\": [\n {\n \"given\": [\n \"John\"\n ],\n \"family\": \"Smith\"\n }\n ],\n \"gender\": \"male\"\n },\n \"request\": {\n \"method\": \"POST\",\n \"url\": \"Patient\"\n }\n },\n {\n \"fullUrl\": \"urn:uuid:condition-001\",\n \"resource\": {\n \"resourceType\": \"Condition\",\n \"code\": {\n \"text\": \"Type 2 Diabetes\"\n }\n },\n \"request\": {\n \"method\": \"POST\",\n \"url\": \"Condition\"\n }\n },\n {\n \"fullUrl\": \"urn:uuid:medication-001\",\n \"resource\": {\n \"resourceType\": \"MedicationRequest\",\n \"medicationCodeableConcept\": {\n \"text\": \"Metformin 500mg\"\n }\n },\n \"request\": {\n \"method\": \"POST\",\n \"url\": \"MedicationRequest\"\n }\n }\n ]\n },\n \"resources\": [\n {\n \"tempId\": \"urn:uuid:a842c4bc-f6cb-4555-9741-ac3aec4ef0b8\",\n \"resourceType\": \"Patient\",\n \"description\": \"John Smith (DOB 1980-05-12) was diagnosed with Type 2 Diabetes during office visit on 2025-03-01 with Dr. Chen\",\n \"originalText\": \"diagnosed with Type 2 Diabetes\",\n \"group\": \"clinical\",\n \"sourcePages\": [\n 2\n ]\n }\n ],\n \"validation\": {\n \"passes\": [\n {}\n ],\n \"fixed\": true,\n \"attempts\": 1,\n \"summary\": \"summary\"\n },\n \"resource_review\": {\n \"flagged\": [\n {\n \"tempId\": \"urn:uuid:a842c4bc-f6cb-4555-9741-ac3aec4ef0b8\",\n \"resourceType\": \"Condition\",\n \"findings\": [\n {\n \"fieldPath\": \"onsetDateTime\",\n \"value\": \"2024-01-15\",\n \"supported\": false,\n \"unaudited\": true,\n \"rationale\": \"Date is the visit date, not when the condition began.\"\n }\n ]\n }\n ],\n \"remediated\": [\n {\n \"tempId\": \"urn:uuid:a842c4bc-f6cb-4555-9741-ac3aec4ef0b8\",\n \"resourceType\": \"Condition\",\n \"findings\": [\n {\n \"fieldPath\": \"onsetDateTime\",\n \"value\": \"2024-01-15\",\n \"supported\": false,\n \"unaudited\": true,\n \"rationale\": \"Date is the visit date, not when the condition began.\"\n }\n ]\n }\n ]\n }\n}", "headers": { "Content-Type": "application/json" } @@ -1546,7 +1866,12 @@ "name": "Generate FHIR search parameters from text - Search Appointments", "request": { "urlPathTemplate": "/lang2fhir/search", - "method": "POST" + "method": "POST", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + } }, "response": { "status": 200, @@ -1572,7 +1897,12 @@ "name": "Upload custom FHIR profile (deprecated) - default", "request": { "urlPathTemplate": "/lang2fhir/profile/upload", - "method": "POST" + "method": "POST", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + } }, "response": { "status": 201, @@ -1598,7 +1928,12 @@ "name": "Convert document to FHIR resource - default", "request": { "urlPathTemplate": "/lang2fhir/document", - "method": "POST" + "method": "POST", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + } }, "response": { "status": 200, @@ -1624,11 +1959,16 @@ "name": "Extract multiple FHIR resources from a document - default", "request": { "urlPathTemplate": "/lang2fhir/document/multi", - "method": "POST" + "method": "POST", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + } }, "response": { "status": 200, - "body": "{\n \"success\": true,\n \"message\": \"Successfully extracted 3 resources\",\n \"bundle\": {\n \"resourceType\": \"Bundle\",\n \"type\": \"transaction\",\n \"entry\": [\n {\n \"fullUrl\": \"urn:uuid:patient-001\",\n \"resource\": {\n \"resourceType\": \"Patient\",\n \"meta\": {\n \"tag\": [\n {\n \"system\": \"https://phenoml.com/fhir/split-classification\",\n \"code\": \"clinical\"\n }\n ]\n },\n \"name\": [\n {\n \"given\": [\n \"John\"\n ],\n \"family\": \"Doe\"\n }\n ],\n \"gender\": \"male\",\n \"birthDate\": \"1979-03-15\"\n },\n \"request\": {\n \"method\": \"POST\",\n \"url\": \"Patient\"\n }\n },\n {\n \"fullUrl\": \"urn:uuid:condition-001\",\n \"resource\": {\n \"resourceType\": \"Condition\",\n \"meta\": {\n \"tag\": [\n {\n \"system\": \"https://phenoml.com/fhir/split-classification\",\n \"code\": \"clinical\"\n }\n ]\n },\n \"code\": {\n \"text\": \"Type 2 Diabetes Mellitus\"\n },\n \"subject\": {\n \"reference\": \"urn:uuid:patient-001\"\n }\n },\n \"request\": {\n \"method\": \"POST\",\n \"url\": \"Condition\"\n }\n },\n {\n \"fullUrl\": \"urn:uuid:medication-001\",\n \"resource\": {\n \"resourceType\": \"MedicationRequest\",\n \"meta\": {\n \"tag\": [\n {\n \"system\": \"https://phenoml.com/fhir/split-classification\",\n \"code\": \"clinical\"\n }\n ]\n },\n \"medicationCodeableConcept\": {\n \"text\": \"Metformin 500mg\"\n },\n \"subject\": {\n \"reference\": \"urn:uuid:patient-001\"\n }\n },\n \"request\": {\n \"method\": \"POST\",\n \"url\": \"MedicationRequest\"\n }\n }\n ]\n },\n \"resources\": [\n {\n \"tempId\": \"urn:uuid:patient-001\",\n \"resourceType\": \"Patient\",\n \"description\": \"John Doe, born 1979-03-15\",\n \"originalText\": \"John Doe, DOB 1979-03-15\",\n \"group\": \"clinical\",\n \"sourcePages\": [\n 1\n ]\n },\n {\n \"tempId\": \"urn:uuid:condition-001\",\n \"resourceType\": \"Condition\",\n \"description\": \"Type 2 Diabetes Mellitus diagnosis\",\n \"originalText\": \"diagnosed with Type 2 Diabetes\",\n \"group\": \"clinical\",\n \"sourcePages\": [\n 1\n ]\n },\n {\n \"tempId\": \"urn:uuid:medication-001\",\n \"resourceType\": \"MedicationRequest\",\n \"description\": \"Metformin 500mg prescription\",\n \"originalText\": \"Prescribed Metformin 500mg\",\n \"group\": \"clinical\",\n \"sourcePages\": [\n 2\n ]\n }\n ],\n \"validation\": {\n \"passes\": [\n {}\n ],\n \"fixed\": true,\n \"attempts\": 1,\n \"summary\": \"summary\"\n },\n \"resource_review\": {\n \"flagged\": [\n {\n \"tempId\": \"urn:uuid:a842c4bc-f6cb-4555-9741-ac3aec4ef0b8\",\n \"resourceType\": \"Condition\",\n \"findings\": [\n {\n \"fieldPath\": \"onsetDateTime\",\n \"value\": \"2024-01-15\",\n \"supported\": false,\n \"rationale\": \"Date is the visit date, not when the condition began.\"\n }\n ]\n }\n ]\n },\n \"page_classifications\": [\n {\n \"page_number\": 1,\n \"include\": true,\n \"classification_id\": \"clinical\",\n \"reason\": \"clinical demographics and diagnosis\"\n },\n {\n \"page_number\": 2,\n \"include\": true,\n \"classification_id\": \"clinical\",\n \"reason\": \"clinical medication details\"\n },\n {\n \"page_number\": 3,\n \"include\": false,\n \"classification_id\": \"admin\",\n \"reason\": \"administrative cover sheet\"\n }\n ]\n}", + "body": "{\n \"success\": true,\n \"message\": \"Successfully extracted 3 resources\",\n \"bundle\": {\n \"resourceType\": \"Bundle\",\n \"type\": \"transaction\",\n \"entry\": [\n {\n \"fullUrl\": \"urn:uuid:patient-001\",\n \"resource\": {\n \"resourceType\": \"Patient\",\n \"meta\": {\n \"tag\": [\n {\n \"system\": \"https://phenoml.com/fhir/split-classification\",\n \"code\": \"clinical\"\n }\n ]\n },\n \"name\": [\n {\n \"given\": [\n \"John\"\n ],\n \"family\": \"Doe\"\n }\n ],\n \"gender\": \"male\",\n \"birthDate\": \"1979-03-15\"\n },\n \"request\": {\n \"method\": \"POST\",\n \"url\": \"Patient\"\n }\n },\n {\n \"fullUrl\": \"urn:uuid:condition-001\",\n \"resource\": {\n \"resourceType\": \"Condition\",\n \"meta\": {\n \"tag\": [\n {\n \"system\": \"https://phenoml.com/fhir/split-classification\",\n \"code\": \"clinical\"\n }\n ]\n },\n \"code\": {\n \"text\": \"Type 2 Diabetes Mellitus\"\n },\n \"subject\": {\n \"reference\": \"urn:uuid:patient-001\"\n }\n },\n \"request\": {\n \"method\": \"POST\",\n \"url\": \"Condition\"\n }\n },\n {\n \"fullUrl\": \"urn:uuid:medication-001\",\n \"resource\": {\n \"resourceType\": \"MedicationRequest\",\n \"meta\": {\n \"tag\": [\n {\n \"system\": \"https://phenoml.com/fhir/split-classification\",\n \"code\": \"clinical\"\n }\n ]\n },\n \"medicationCodeableConcept\": {\n \"text\": \"Metformin 500mg\"\n },\n \"subject\": {\n \"reference\": \"urn:uuid:patient-001\"\n }\n },\n \"request\": {\n \"method\": \"POST\",\n \"url\": \"MedicationRequest\"\n }\n }\n ]\n },\n \"resources\": [\n {\n \"tempId\": \"urn:uuid:patient-001\",\n \"resourceType\": \"Patient\",\n \"description\": \"John Doe, born 1979-03-15\",\n \"originalText\": \"John Doe, DOB 1979-03-15\",\n \"group\": \"clinical\",\n \"sourcePages\": [\n 1\n ]\n },\n {\n \"tempId\": \"urn:uuid:condition-001\",\n \"resourceType\": \"Condition\",\n \"description\": \"Type 2 Diabetes Mellitus diagnosis\",\n \"originalText\": \"diagnosed with Type 2 Diabetes\",\n \"group\": \"clinical\",\n \"sourcePages\": [\n 1\n ]\n },\n {\n \"tempId\": \"urn:uuid:medication-001\",\n \"resourceType\": \"MedicationRequest\",\n \"description\": \"Metformin 500mg prescription\",\n \"originalText\": \"Prescribed Metformin 500mg\",\n \"group\": \"clinical\",\n \"sourcePages\": [\n 2\n ]\n }\n ],\n \"validation\": {\n \"passes\": [\n {}\n ],\n \"fixed\": true,\n \"attempts\": 1,\n \"summary\": \"summary\"\n },\n \"resource_review\": {\n \"flagged\": [\n {\n \"tempId\": \"urn:uuid:a842c4bc-f6cb-4555-9741-ac3aec4ef0b8\",\n \"resourceType\": \"Condition\",\n \"findings\": [\n {\n \"fieldPath\": \"onsetDateTime\",\n \"value\": \"2024-01-15\",\n \"supported\": false,\n \"unaudited\": true,\n \"rationale\": \"Date is the visit date, not when the condition began.\"\n }\n ]\n }\n ],\n \"remediated\": [\n {\n \"tempId\": \"urn:uuid:a842c4bc-f6cb-4555-9741-ac3aec4ef0b8\",\n \"resourceType\": \"Condition\",\n \"findings\": [\n {\n \"fieldPath\": \"onsetDateTime\",\n \"value\": \"2024-01-15\",\n \"supported\": false,\n \"unaudited\": true,\n \"rationale\": \"Date is the visit date, not when the condition began.\"\n }\n ]\n }\n ]\n },\n \"page_classifications\": [\n {\n \"page_number\": 1,\n \"include\": true,\n \"classification_id\": \"clinical\",\n \"reason\": \"clinical demographics and diagnosis\"\n },\n {\n \"page_number\": 2,\n \"include\": true,\n \"classification_id\": \"clinical\",\n \"reason\": \"clinical medication details\"\n },\n {\n \"page_number\": 3,\n \"include\": false,\n \"classification_id\": \"admin\",\n \"reason\": \"administrative cover sheet\"\n }\n ]\n}", "headers": { "Content-Type": "application/json" } @@ -1651,6 +1991,11 @@ "request": { "urlPathTemplate": "/lang2fhir/batch", "method": "GET", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "queryParameters": { "cursor": { "equalTo": "cursor" @@ -1685,7 +2030,12 @@ "name": "Create a batch job - default", "request": { "urlPathTemplate": "/lang2fhir/batch", - "method": "POST" + "method": "POST", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + } }, "response": { "status": 202, @@ -1712,6 +2062,11 @@ "request": { "urlPathTemplate": "/lang2fhir/batch/{job_id}/items", "method": "POST", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "pathParameters": { "job_id": { "equalTo": "job_id" @@ -1743,6 +2098,11 @@ "request": { "urlPathTemplate": "/lang2fhir/batch/{job_id}/finalize", "method": "POST", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "pathParameters": { "job_id": { "equalTo": "job_id" @@ -1774,6 +2134,11 @@ "request": { "urlPathTemplate": "/lang2fhir/batch/{job_id}/cancel", "method": "POST", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "pathParameters": { "job_id": { "equalTo": "job_id" @@ -1805,6 +2170,11 @@ "request": { "urlPathTemplate": "/lang2fhir/batch/{job_id}", "method": "GET", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "pathParameters": { "job_id": { "equalTo": "job_id" @@ -1844,6 +2214,11 @@ "request": { "urlPathTemplate": "/lang2fhir/batch/{job_id}/results", "method": "GET", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "pathParameters": { "job_id": { "equalTo": "job_id" @@ -1883,6 +2258,11 @@ "request": { "urlPathTemplate": "/lang2fhir/batch/{job_id}/results/{item_id}", "method": "GET", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "pathParameters": { "job_id": { "equalTo": "job_id" @@ -1917,6 +2297,11 @@ "request": { "urlPathTemplate": "/fhir/profiles", "method": "GET", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "queryParameters": { "url": { "equalTo": "http://phenoml.com/fhir/StructureDefinition/custom-patient|1.0.0" @@ -1948,7 +2333,12 @@ "name": "Upload a custom FHIR profile - default", "request": { "urlPathTemplate": "/fhir/profiles", - "method": "POST" + "method": "POST", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + } }, "response": { "status": 201, @@ -1975,6 +2365,11 @@ "request": { "urlPathTemplate": "/fhir/profiles/{id}", "method": "GET", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "pathParameters": { "id": { "equalTo": "custom-patient" @@ -2006,6 +2401,11 @@ "request": { "urlPathTemplate": "/fhir/profiles/{id}", "method": "PUT", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "pathParameters": { "id": { "equalTo": "custom-patient" @@ -2037,6 +2437,11 @@ "request": { "urlPathTemplate": "/fhir/profiles/{id}", "method": "DELETE", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "pathParameters": { "id": { "equalTo": "custom-patient" @@ -2068,6 +2473,11 @@ "request": { "urlPathTemplate": "/fhir/profiles/{id}/versions", "method": "GET", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "pathParameters": { "id": { "equalTo": "custom-patient" @@ -2099,6 +2509,11 @@ "request": { "urlPathTemplate": "/fhir/profiles/{id}/versions", "method": "POST", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "pathParameters": { "id": { "equalTo": "custom-patient" @@ -2130,6 +2545,11 @@ "request": { "urlPathTemplate": "/fhir/profiles/{id}/versions/{version}", "method": "GET", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "pathParameters": { "id": { "equalTo": "custom-patient" @@ -2164,6 +2584,11 @@ "request": { "urlPathTemplate": "/fhir/profiles/{id}/versions/{version}", "method": "DELETE", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "pathParameters": { "id": { "equalTo": "custom-patient" @@ -2197,7 +2622,12 @@ "name": "List all summary templates - default", "request": { "urlPathTemplate": "/fhir2summary/templates", - "method": "GET" + "method": "GET", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + } }, "response": { "status": 200, @@ -2224,7 +2654,12 @@ "name": "Create a new summary template - Discharge Summary Template", "request": { "urlPathTemplate": "/fhir2summary/template", - "method": "POST" + "method": "POST", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + } }, "response": { "status": 200, @@ -2251,6 +2686,11 @@ "request": { "urlPathTemplate": "/fhir2summary/template/{id}", "method": "GET", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "pathParameters": { "id": { "equalTo": "id" @@ -2282,6 +2722,11 @@ "request": { "urlPathTemplate": "/fhir2summary/template/{id}", "method": "PUT", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "pathParameters": { "id": { "equalTo": "id" @@ -2313,6 +2758,11 @@ "request": { "urlPathTemplate": "/fhir2summary/template/{id}", "method": "DELETE", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "pathParameters": { "id": { "equalTo": "id" @@ -2343,7 +2793,12 @@ "name": "Generate a summary from FHIR resources - Narrative Summary", "request": { "urlPathTemplate": "/fhir2summary/create", - "method": "POST" + "method": "POST", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + } }, "response": { "status": 200, @@ -2369,7 +2824,12 @@ "name": "Create FHIR resource from text and store it - default", "request": { "urlPathTemplate": "/tools/lang2fhir-and-create", - "method": "POST" + "method": "POST", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + } }, "response": { "status": 200, @@ -2395,7 +2855,12 @@ "name": "Extract and store multiple FHIR resources - default", "request": { "urlPathTemplate": "/tools/lang2fhir-and-create-multi", - "method": "POST" + "method": "POST", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + } }, "response": { "status": 200, @@ -2421,7 +2886,12 @@ "name": "Search FHIR resources from natural language - default", "request": { "urlPathTemplate": "/tools/lang2fhir-and-search", - "method": "POST" + "method": "POST", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + } }, "response": { "status": 200, @@ -2447,7 +2917,12 @@ "name": "Analyze patient cohorts - default", "request": { "urlPathTemplate": "/tools/cohort", - "method": "POST" + "method": "POST", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + } }, "response": { "status": 200, @@ -2473,7 +2948,12 @@ "name": "Create MCP server - default", "request": { "urlPathTemplate": "/tools/mcp-server/create", - "method": "POST" + "method": "POST", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + } }, "response": { "status": 200, @@ -2499,7 +2979,12 @@ "name": "List MCP servers - default", "request": { "urlPathTemplate": "/tools/mcp-server/list", - "method": "GET" + "method": "GET", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + } }, "response": { "status": 200, @@ -2527,6 +3012,11 @@ "request": { "urlPathTemplate": "/tools/mcp-server/{mcp_server_id}", "method": "GET", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "pathParameters": { "mcp_server_id": { "equalTo": "mcp_server_id" @@ -2558,6 +3048,11 @@ "request": { "urlPathTemplate": "/tools/mcp-server/{mcp_server_id}", "method": "DELETE", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "pathParameters": { "mcp_server_id": { "equalTo": "mcp_server_id" @@ -2589,6 +3084,11 @@ "request": { "urlPathTemplate": "/tools/mcp-server/{mcp_server_id}/list", "method": "GET", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "pathParameters": { "mcp_server_id": { "equalTo": "mcp_server_id" @@ -2620,6 +3120,11 @@ "request": { "urlPathTemplate": "/tools/mcp-server/tool/{mcp_server_tool_id}", "method": "GET", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "pathParameters": { "mcp_server_tool_id": { "equalTo": "mcp_server_tool_id" @@ -2651,6 +3156,11 @@ "request": { "urlPathTemplate": "/tools/mcp-server/tool/{mcp_server_tool_id}", "method": "DELETE", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "pathParameters": { "mcp_server_tool_id": { "equalTo": "mcp_server_tool_id" @@ -2682,6 +3192,11 @@ "request": { "urlPathTemplate": "/workflows", "method": "GET", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "queryParameters": { "verbose": { "equalTo": "true" @@ -2714,6 +3229,11 @@ "request": { "urlPathTemplate": "/workflows", "method": "POST", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "queryParameters": { "verbose": { "equalTo": "true" @@ -2745,6 +3265,11 @@ "request": { "urlPathTemplate": "/workflows/{id}", "method": "GET", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "pathParameters": { "id": { "equalTo": "id" @@ -2781,6 +3306,11 @@ "request": { "urlPathTemplate": "/workflows/{id}", "method": "PUT", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "pathParameters": { "id": { "equalTo": "id" @@ -2817,6 +3347,11 @@ "request": { "urlPathTemplate": "/workflows/{id}", "method": "DELETE", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "pathParameters": { "id": { "equalTo": "id" @@ -2848,6 +3383,11 @@ "request": { "urlPathTemplate": "/workflows/{id}/execute", "method": "POST", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, "pathParameters": { "id": { "equalTo": "7a8b9c0d-1234-5678-abcd-ef9876543210" @@ -2875,6 +3415,6 @@ } ], "meta": { - "total": 94 + "total": 96 } } \ No newline at end of file From 649e14d03d953c4feb41109c46f5079d7ecb749c Mon Sep 17 00:00:00 2001 From: "fern-api[bot]" <115122769+fern-api[bot]@users.noreply.github.com> Date: Wed, 7 Oct 2026 20:55:04 +0000 Subject: [PATCH 2/5] [fern-autoversion] SDK regeneration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🌿 Generated with Fern --- .fern/metadata.json | 2 +- pyproject.toml | 2 +- src/phenoml/core/client_wrapper.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.fern/metadata.json b/.fern/metadata.json index edf440b8..8d6bb06b 100644 --- a/.fern/metadata.json +++ b/.fern/metadata.json @@ -13,5 +13,5 @@ "invokedBy": "ci", "requestedVersion": "AUTO", "ciProvider": "unknown", - "sdkVersion": "0.0.0.dev0" + "sdkVersion": "17.0.1" } \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 1dc545d0..61c14674 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ dynamic = ["version"] [tool.poetry] name = "phenoml" -version = "0.0.0.dev0" +version = "17.0.1" description = "" readme = "README.md" authors = [] diff --git a/src/phenoml/core/client_wrapper.py b/src/phenoml/core/client_wrapper.py index c03a2430..cc097093 100644 --- a/src/phenoml/core/client_wrapper.py +++ b/src/phenoml/core/client_wrapper.py @@ -38,7 +38,7 @@ def get_headers(self) -> typing.Dict[str, str]: "X-Fern-Runtime": f"python/{platform.python_version()}", "X-Fern-Platform": f"{platform.system().lower()}/{platform.release()}", "X-Fern-SDK-Name": "phenoml", - "X-Fern-SDK-Version": "0.0.0.dev0", + "X-Fern-SDK-Version": "17.0.1", **(self.get_custom_headers() or {}), } token = self._get_token() From a0d40ba67fc8eccf3609a36124b6e0389f3333a6 Mon Sep 17 00:00:00 2001 From: "fern-api[bot]" <115122769+fern-api[bot]@users.noreply.github.com> Date: Wed, 7 Oct 2026 20:55:04 +0000 Subject: [PATCH 3/5] [fern-replay] Applied customizations Patches applied (1): - patch-6516695e: Release 15.0.2: restore bundled openapi.json packaging (#169) --- .fern/replay.lock | 27 +++++++++++++++++++++------ pyproject.toml | 3 +++ 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/.fern/replay.lock b/.fern/replay.lock index 25dc6cb6..a2016fe0 100644 --- a/.fern/replay.lock +++ b/.fern/replay.lock @@ -120,21 +120,36 @@ generations: cli_version: unknown generator_versions: fernapi/fern-python-sdk: 5.18.1 -current_generation: 3ac2feef8a9595d7dea2a99b024322ae61959133 + - commit_sha: ec40d483d65b520271769b3abee6f47c767cd2c7 + tree_hash: 8972f8ec6812c8570335857e9d265068efd30a28 + timestamp: 2026-10-07T20:53:50.379Z + cli_version: unknown + generator_versions: + fernapi/fern-python-sdk: 5.31.1 +current_generation: ec40d483d65b520271769b3abee6f47c767cd2c7 patches: - id: patch-6516695e - content_hash: sha256:5caeff601ccd2db4bda36068ff6c1ac7dba46f1c049cccc367a7db553613acad + content_hash: sha256:1d979cf90aad13d61c31dd2bace465f9b4427b9282da800066490b1b716224bd original_commit: 6516695ecaba47ae4bcc8119acca86a1113adeeb original_message: "Release 15.0.2: restore bundled openapi.json packaging (#169)" original_author: Gavin Sharp - base_generation: 3ac2feef8a9595d7dea2a99b024322ae61959133 + base_generation: ec40d483d65b520271769b3abee6f47c767cd2c7 files: - pyproject.toml patch_content: | diff --git a/pyproject.toml b/pyproject.toml - index cea8b20..f3f1f73 100644 + index 1dc545d..23c4eae 100644 --- a/pyproject.toml +++ b/pyproject.toml + @@ -4,7 +4,7 @@ dynamic = ["version"] + + [tool.poetry] + name = "phenoml" + -version = "0.0.0.dev0" + +version = "17.0.1" + description = "" + readme = "README.md" + authors = [] @@ -31,6 +31,9 @@ classifiers = [ packages = [ { include = "phenoml", from = "src"} @@ -153,7 +168,7 @@ patches: [tool.poetry] name = "phenoml" - version = "17.0.0" + version = "17.0.1" description = "" readme = "README.md" authors = [] @@ -191,7 +206,7 @@ patches: python = "^3.10" aiohttp = { version = ">=3.14.1,<4", optional = true, python = ">=3.10"} httpx = ">=0.21.2" - httpx-aiohttp = { version = "0.1.8", optional = true, python = ">=3.10"} + httpx-aiohttp = { version = "^0.1.8", optional = true, python = ">=3.10"} pydantic = ">= 1.9.2" pydantic-core = ">=2.18.2,<3.0.0" typing_extensions = ">= 4.0.0" diff --git a/pyproject.toml b/pyproject.toml index 61c14674..23c4eae1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,6 +31,9 @@ classifiers = [ packages = [ { include = "phenoml", from = "src"} ] +include = [ + { path = "src/phenoml/openapi/openapi.json", format = ["sdist", "wheel"] } +] [tool.poetry.urls] Repository = 'https://github.com/phenoml/phenoml-python-sdk' From d8e17854701a8660d265248a49636447ce74b1b5 Mon Sep 17 00:00:00 2001 From: "fern-api[bot]" <115122769+fern-api[bot]@users.noreply.github.com> Date: Wed, 7 Oct 2026 20:55:05 +0000 Subject: [PATCH 4/5] SDK regeneration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🌿 Generated with Fern --- changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/changelog.md b/changelog.md index 2a9ecbb0..26ca4e27 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,5 @@ +## [17.0.1] - 2026-10-07 + ## [17.0.0] - 2026-09-09 ### Breaking Changes - **`ProfileSummary`** — `id`, `source`, `resource_type`, `url`, `version`, `fhir_version`, `implementation_guide`, `created_at`, and `updated_at` are now required; remove `None` guards for these fields. From 2ca85e27c773a2637aa9eb240033264a99208859 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 7 Oct 2026 20:55:19 +0000 Subject: [PATCH 5/5] chore: sync OpenAPI spec + code-examples for e955fe1d5d69dfe7f09607612a03a87058b280fa [skip ci] --- code-examples.json | 156 ++++- src/phenoml/openapi/openapi.json | 1008 +++++++++++++++++++++++++----- 2 files changed, 989 insertions(+), 175 deletions(-) diff --git a/code-examples.json b/code-examples.json index 79472cca..6c1cc5d1 100644 --- a/code-examples.json +++ b/code-examples.json @@ -2,8 +2,8 @@ "metadata": { "language": "python", "packageName": "phenoml", - "sdkVersion": "17.0.0", - "specCommit": "26224ef37e22adb507f28c21e8991d4a27822bde", + "sdkVersion": "17.0.1", + "specCommit": "e955fe1d5d69dfe7f09607612a03a87058b280fa", "generatorName": "fernapi/fern-python-sdk" }, "renderRules": { @@ -1873,6 +1873,7 @@ "resourceType": "MedicationRequest", "id": "medreq-1", "status": "active", + "intent": "order", "subject": { "reference": "Patient/patient-1" }, @@ -1909,15 +1910,26 @@ "person": [ { "person_id": 1, - "gender_concept_id": 0, + "gender_concept_id": 8532, "year_of_birth": 1985, "month_of_birth": 7, "day_of_birth": 22, - "birth_datetime": "1985-07-22", "race_concept_id": 0, "ethnicity_concept_id": 0, "person_source_value": "patient-1", - "gender_source_value": "female" + "gender_source_value": "female", + "gender_source_concept_id": 0, + "race_source_concept_id": 0, + "ethnicity_source_concept_id": 0 + } + ], + "observation_period": [ + { + "observation_period_id": 1, + "person_id": 1, + "observation_period_start_date": "2024-01-15", + "observation_period_end_date": "2024-01-16", + "period_type_concept_id": 32817 } ], "condition_occurrence": [ @@ -1926,9 +1938,8 @@ "person_id": 1, "condition_concept_id": 201826, "condition_start_date": "2024-01-15", - "condition_start_datetime": "2024-01-15", "condition_type_concept_id": 32817, - "condition_source_value": "http://snomed.info/sct#44054006", + "condition_source_value": "44054006", "condition_source_concept_id": 201826 } ], @@ -1938,18 +1949,34 @@ "person_id": 1, "drug_concept_id": 40163924, "drug_exposure_start_date": "2024-01-16", - "drug_exposure_start_datetime": "2024-01-16", - "drug_type_concept_id": 32817, - "drug_source_value": "http://www.nlm.nih.gov/research/umls/rxnorm#860975", + "drug_type_concept_id": 32838, + "drug_source_value": "860975", "drug_source_concept_id": 40163924 } ] }, "mappings": [ + { + "resource_type": "Patient", + "resource_id": "patient-1", + "omop_table": "person", + "omop_field": "gender_concept_id", + "omop_id": 1, + "source_system": "http://hl7.org/fhir/administrative-gender", + "source_code": "female", + "source_name": "female", + "target_vocabulary": "Gender", + "target_code": "F", + "target_name": "FEMALE", + "mapping_status": "MAPPED", + "selected": true, + "note": "FHIR administrative gender; assumed sex at birth" + }, { "resource_type": "Condition", "resource_id": "condition-1", "omop_table": "condition_occurrence", + "omop_field": "condition_concept_id", "omop_id": 1, "source_system": "http://snomed.info/sct", "source_code": "44054006", @@ -1957,12 +1984,14 @@ "target_vocabulary": "SNOMED", "target_code": "44054006", "target_name": "Type 2 diabetes mellitus", - "mapping_status": "ALREADY_STANDARD" + "mapping_status": "ALREADY_STANDARD", + "selected": true }, { "resource_type": "MedicationRequest", "resource_id": "medreq-1", "omop_table": "drug_exposure", + "omop_field": "drug_concept_id", "omop_id": 1, "source_system": "http://www.nlm.nih.gov/research/umls/rxnorm", "source_code": "860975", @@ -1970,15 +1999,16 @@ "target_vocabulary": "RXNORM", "target_code": "860975", "target_name": "metformin hydrochloride 500 MG", - "mapping_status": "ALREADY_STANDARD" + "mapping_status": "ALREADY_STANDARD", + "selected": true } ], - "vocab_version": "v20240229", + "vocab_version": "v20260227", "summary": { "codes_already_standard": 2, - "codes_normalized": 0, + "codes_normalized": 1, "codes_unmapped": 0, - "off_vocab_rate": 0 + "off_vocab_rate": 0.3333333333333333 } } }, @@ -1993,6 +2023,12 @@ "fieldTemplate": "fhir_resources={{value}}", "kind": "object", "required": true + }, + { + "jsonKey": "vocab_version", + "fieldTemplate": "vocab_version={{value}}", + "kind": "string", + "required": false } ] } @@ -3008,7 +3044,10 @@ "auto", "appointment", "condition-encounter-diagnosis", + "familymemberhistory", + "medicationadministration", "medicationrequest", + "medicationstatement", "careplan", "condition-problems-health-concerns", "coverage", @@ -3124,6 +3163,12 @@ "kind": "string", "required": false }, + { + "jsonKey": "primary_patient", + "fieldTemplate": "primary_patient={{value}}", + "kind": "object", + "required": false + }, { "jsonKey": "patient_reference", "fieldTemplate": "patient_reference={{value}}", @@ -3250,7 +3295,7 @@ "body": { "version": "R4", "resource": "questionnaire", - "content": "JVBERi0xLjQKJeLjz9MK...(base64-encoded PDF or image bytes)" + "content": "JVBERi0xLjQKJeLjz9MK...(base64-encoded document bytes)" } }, "response": { @@ -3312,7 +3357,7 @@ "request": { "body": { "version": "R4", - "content": "JVBERi0xLjQKJeLjz9MK...(base64-encoded PDF or image bytes)", + "content": "JVBERi0xLjQKJeLjz9MK...(base64-encoded document bytes)", "provider": "medplum", "config": { "split_classifications": [ @@ -3494,6 +3539,12 @@ "kind": "string", "required": false }, + { + "jsonKey": "primary_patient", + "fieldTemplate": "primary_patient={{value}}", + "kind": "object", + "required": false + }, { "jsonKey": "patient_reference", "fieldTemplate": "patient_reference={{value}}", @@ -3957,6 +4008,77 @@ ] } }, + "POST /fhir/implementation-guides/{name}/versions": { + "httpMethod": "POST", + "httpPath": "/fhir/implementation-guides/{name}/versions", + "request": { + "body": null + }, + "response": { + "body": null + }, + "render": { + "callTemplate": "client.implementation_guides.implementation_guides.create_version(name={{name}}, {{__body__}})", + "params": [ + { + "name": "name", + "kind": "string" + } + ], + "body": { + "fieldSeparator": ", ", + "fields": [ + { + "jsonKey": "implementation_guide", + "fieldTemplate": "implementation_guide={{value}}", + "kind": "object", + "required": true + }, + { + "jsonKey": "profile_refs", + "fieldTemplate": "profile_refs={{value}}", + "kind": "list", + "required": true, + "items": { + "jsonKey": "", + "fieldTemplate": "{{value}}", + "kind": "string", + "required": true + } + }, + { + "jsonKey": "profile_context", + "fieldTemplate": "profile_context={{value}}", + "kind": "string", + "required": false + } + ] + } + } + }, + "GET /fhir/implementation-guides/{name}/versions/{version}": { + "httpMethod": "GET", + "httpPath": "/fhir/implementation-guides/{name}/versions/{version}", + "request": { + "body": null + }, + "response": { + "body": null + }, + "render": { + "callTemplate": "client.implementation_guides.implementation_guides.get_version(name={{name}}, version={{version}})", + "params": [ + { + "name": "name", + "kind": "string" + }, + { + "name": "version", + "kind": "string" + } + ] + } + }, "GET /fhir/profiles": { "httpMethod": "GET", "httpPath": "/fhir/profiles", diff --git a/src/phenoml/openapi/openapi.json b/src/phenoml/openapi/openapi.json index 0a3b0ed0..3229fdb3 100644 --- a/src/phenoml/openapi/openapi.json +++ b/src/phenoml/openapi/openapi.json @@ -2,7 +2,7 @@ "openapi": "3.0.3", "info": { "title": "Phenoml API", - "version": "26224ef37e22adb507f28c21e8991d4a27822bde" + "version": "e955fe1d5d69dfe7f09607612a03a87058b280fa" }, "x-services": [ { @@ -2040,7 +2040,7 @@ ], "operationId": "construe_phenocr", "summary": "[Alpha] Extract medical codes with phenocr", - "description": "**Alpha:** phenocr is an alpha feature. The API contract \u2014 request\nparameters and response shape \u2014 may change as its internals evolve, and\nresults may vary between releases. Do not depend on it for production\nworkloads yet.\n\nExtracts medical codes from natural language clinical text using phenocr.\n\nSupported code systems: HPO, ICD-10-CM, RXNORM, and SNOMED_CT_US. The\ncode system name and version are both required.\n", + "description": "**Alpha:** phenocr is an alpha feature. Request parameters, response\nshape, and results may change between releases. Do not depend on it for\nproduction workloads yet.\n\nExtracts medical codes from natural language clinical text using phenocr.\n\nSupported code systems: HPO, ICD-10-CM, RXNORM, and SNOMED_CT_US. The\ncode system name and version are both required.\n", "requestBody": { "required": true, "content": { @@ -2109,7 +2109,7 @@ ], "operationId": "construe_list", "summary": "List available code systems", - "description": "Returns the terminology server's catalog of available code systems, including both built-in standard terminologies and custom uploaded systems.\n", + "description": "Returns the catalog of available code systems, including both built-in standard terminologies and custom uploaded systems.\n", "responses": { "200": { "description": "List of available code systems", @@ -2543,14 +2543,8 @@ "413": { "description": "Request is too large" }, - "501": { - "description": "Code crosswalk is not configured" - }, - "502": { - "description": "Code crosswalk returned an upstream error" - }, - "503": { - "description": "Code crosswalk is temporarily unavailable" + "500": { + "description": "Server error" } }, "security": [ @@ -2568,7 +2562,7 @@ ], "operationId": "construe_listCodes", "summary": "List codes in a code system", - "description": "Returns a paginated list of all codes in the specified code system from the terminology server.\n\nUsage of CPT is subject to AMA requirements: see PhenoML Terms of Service.\n", + "description": "Returns a paginated list of all codes in the specified code system.\n\nUsage of CPT is subject to AMA requirements: see PhenoML Terms of Service.\n", "parameters": [ { "name": "codesystem", @@ -2747,7 +2741,7 @@ ], "operationId": "construe_lookup", "summary": "Get a specific code", - "description": "Looks up a specific code in the terminology server and returns its details.\n\nUsage of CPT is subject to AMA requirements: see PhenoML Terms of Service.\n", + "description": "Looks up a specific code and returns its details.\n\nUsage of CPT is subject to AMA requirements: see PhenoML Terms of Service.\n", "parameters": [ { "name": "codesystem", @@ -3274,7 +3268,7 @@ "post": { "operationId": "fhir2omop_create", "summary": "Map FHIR resources to OMOP CDM v5.4", - "description": "Maps a FHIR R4 resource or Bundle into OMOP Common Data Model v5.4 rows\n(person, visit_occurrence, condition_occurrence, drug_exposure,\nprocedure_occurrence, measurement, observation).\n\nResource support is intentionally limited to the OMOP tables returned by\nthis endpoint:\n- `Patient` -> `person`\n- `Encounter` -> `visit_occurrence`\n- `Condition` -> `condition_occurrence`\n- `Procedure` -> `procedure_occurrence`\n- `MedicationRequest`, `MedicationStatement`, and\n `MedicationAdministration` -> `drug_exposure`\n- `Immunization` -> `drug_exposure`\n- `Observation` with a numeric `valueQuantity`, `valueInteger`, or\n numeric-looking `valueString` (for example `\"<2\"`) -> `measurement`\n- non-numeric `Observation` -> `observation`\n- `AllergyIntolerance` -> `observation`\n\n`Medication` is supported only as reference data for medication\nresources; it is not emitted as its own row because OMOP CDM has no\nMedication table. Other reference/admin resources such as `Practitioner`,\n`Organization`, `Location`, `Coverage`, and `Claim`, and clinical\nworkflow/document resources such as `DiagnosticReport`, `ServiceRequest`,\n`CarePlan`, `DocumentReference`, `Composition`, `Specimen`, and\n`DeviceUseStatement`, are currently accepted in a Bundle but are not\nshaped into OMOP rows. Unsupported resource types are ignored rather than\nlisted under `dropped`; `dropped` is reserved for supported resource types\nthat were missing the subject/patient, code, or medication reference data\nneeded to produce a valid row.\n\nEach resource's primary clinical coding is resolved to a standard OMOP\n`concept_id`. Alongside the OMOP rows grouped by table (`tables`), the\nresponse carries `mappings` (how each source coding resolved, linked back\nto the row it produced), `dropped` (resources that could not be shaped\ninto a row), `vocab_version` (the OMOP vocabulary release codes were\nresolved against), and a small `summary` of the resolution outcomes.\n\nA `concept_id` of `0` is reported, not omitted (OMOP \"no matching\nconcept\" semantics): it covers both a coding with no standard match\n(`UNMAPPED`) and an unverified suggestion for a text-only resource\n(`UNCHECKED`). Only the primary clinical coding is resolved, so\n`gender`/`race`/`ethnicity`/`visit`/`value`/`unit` `concept_id`s are\nalways `0`; the one populated non-resolved concept is measurement\n`operator_concept_id`, set from a value comparator (`<`, `<=`, `>`, `>=`)\nrather than the resolver. Each `*_source_value` carries the verbatim FHIR\ncoding (`system#code`), and `*_type_concept_id` is set to `32817` (EHR).\n\nMedication codes are resolved whether they appear inline\n(`medicationCodeableConcept`) or via a `medicationReference` to a contained,\nrelative (`Type/id`), or bundle-entry (`urn:uuid`) `Medication` resource.\nResources that cannot be shaped into a row \u2014 a medication with no usable\ncode, resolvable reference, or display, or any clinical resource whose\nsubject/patient reference cannot be tied to a person \u2014 are reported under\n`dropped` rather than emitted as blank rows. The\nbundle must contain at least one Patient resource.\n", + "description": "Maps a FHIR R4 resource or Bundle into OMOP Common Data Model v5.4 rows,\ngrouped by destination table in `tables`.\n\nSet `vocab_version` to select the OMOP vocabulary release used for coded\nconcept resolution. If omitted or empty, the API uses its default\nrelease. The response's `vocab_version`, when present, identifies the\nrelease used. Specify a release explicitly when reproducibility matters.\n\nStandards basis: [FHIR R4 (v4.0.1)](https://hl7.org/fhir/R4/) defines\nthe accepted source elements and [OMOP CDM\nv5.4](https://ohdsi.github.io/CommonDataModel/cdm54.html) defines the\noutput columns. The published [Vulcan FHIR-to-OMOP IG\nv1.0.0](https://hl7.org/fhir/uv/omop/) is an informative FHIR R5\nbaseline; this endpoint documents and implements the equivalent R4\nsource elements, rather than accepting R5-only fields.\n\nThis response is a mapping result, not a complete CDM load pipeline.\nWhen a source cannot supply a field that CDM v5.4 requires, the row is\nstill returned with that field unset; the value is not inferred. Common\ncases are `year_of_birth` without a usable `birthDate`,\n`drug_exposure_end_date` without an explicit end or single-event timing,\nand a required event date (such as `condition_start_date`,\n`procedure_date`, or `death_date`) whose source has no timing with at\nleast day precision. Apply your own policy to such rows before loading\nthem into a strictly conformant CDM instance.\n\nCurrent resource coverage:\n- `Patient` -> `person`; `deceased[x]` can also produce `death`, the\n first address can produce `location`, and more than one supplied race\n produces `observation` race rows (see Patient demographics below)\n- `observation_period` -> one request-local derived row per person,\n spanning the populated dates of that person's visit, clinical, and\n death rows; this is not enrollment or capture-completeness evidence\n- `Location` -> `location` and `care_site`\n- `Organization` -> `care_site`; its first address can produce `location`\n- `HealthcareService` -> `care_site`\n- `Practitioner` and `PractitionerRole` -> `provider`\n- `Encounter` -> `visit_occurrence`\n- `Condition` -> `condition_occurrence`\n- `Procedure` -> `procedure_occurrence`\n- `MedicationRequest`, `MedicationStatement`, and\n `MedicationAdministration` -> `drug_exposure`\n- `Immunization` -> `drug_exposure`\n- `Observation` -> `measurement` or `observation`. For coded\n Observations, the resolved OMOP concept domain selects the table; value\n form only breaks ties. For text-only Observations, numeric values route\n to `measurement` and nonnumeric values to `observation`.\n- `AllergyIntolerance` -> `observation`\n\n`Medication` is reference data for medication resources; it does not\ncreate its own row because OMOP CDM has no Medication table. Administrative\nlinkages (provider, care site, and location) are best-effort and limited to\nreferences supplied in the request. `Patient.managingOrganization` is a\nrecord custodian, not a care-delivery site. Provider specialty is not\nmapped. Recorded `Practitioner.gender` is distinct from Person\ndemographics: `male` and `female` resolve to validated OMOP Gender\nconcepts in `provider.gender_concept_id`; `other`, `unknown`, and absent\ngender remain unmapped. `Address.country` is resolved to `location.country_concept_id`,\nand CMS Place of Service codings in `Location.type` are resolved to\n`care_site.place_of_service_concept_id`.\nA `PractitionerRole` that identifies one supplied `Practitioner` aliases\nthat canonical provider: by a top-level structural reference, a\nparent-contained `#id` reference, or an exact `identifier.system` and\n`identifier.value` match against a top-level Practitioner. No remote\nidentifier lookup is performed. When `Reference.type` is present it must\nbe `Practitioner`; duplicate contained IDs and identifier matches are\nambiguous. An explicit reference that is unresolved, ambiguous, or\nunsupported retains a role-fallback provider row and is returned in\n`diagnostics`. `provider_role_contexts` preserves role-specific\nspecialty and care-site context that a canonical OMOP provider row cannot\nrepresent together.\n\nPatient demographics:\n- Sex at birth, race, and ethnicity are read from these US Core\n extensions, with their US Core 6.1.0 structures and value sets, on\n any Patient (US Core profile conformance is not required). Sex at\n birth falls back to Patient `gender`. Other extensions, including US\n Core sex and gender identity, are ignored.\n - Birth sex:\n `http://hl7.org/fhir/us/core/StructureDefinition/us-core-birthsex`\n (`valueCode` from `http://hl7.org/fhir/us/core/ValueSet/birthsex`)\n - Race:\n `http://hl7.org/fhir/us/core/StructureDefinition/us-core-race`\n (`ombCategory` from\n `http://hl7.org/fhir/us/core/ValueSet/omb-race-category`)\n - Ethnicity:\n `http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity`\n (`ombCategory` from\n `http://hl7.org/fhir/us/core/ValueSet/omb-ethnicity-category`)\n- `gender_concept_id` is sex at birth. A supplied birth sex always\n decides it: `M` and `F` are resolved; `UNK`, `ASKU`, `OTH`, a code\n outside the value set, conflicting values, and a birth sex without\n `valueCode` leave it `0`, and Patient `gender` is not used.\n- Without a birth sex, Patient `gender` `male` or `female` is resolved\n under the OMOP convention that the supplied gender represents sex at\n birth; `other` and `unknown` keep concept `0`.\n- `gender_source_value` is the chosen source code (`F` for birth sex,\n `female` for Patient `gender`).\n- Each race category is resolved separately, and null flavors (`UNK`,\n `ASKU`) are ignored. One distinct standard race sets\n `race_concept_id`. More than one sets it to `1546847` (More than one\n race) and adds one `observation` row per race, with\n `observation_concept_id` `4013886` (Race), the race in\n `value_as_concept_id`, `observation_type_concept_id` `32817`, the\n category code in `value_source_value`, and no\n `observation_source_value` or `observation_date`. A loading pipeline\n that requires `observation_date` must apply its own date policy.\n- The single non-null ethnicity category is resolved, and null flavors\n are ignored; more than one distinct category leaves\n `ethnicity_concept_id` `0`. Ethnicity is not derived from race, and\n no demographic is inferred from names, addresses, or other\n extensions.\n- `race_source_value` and `ethnicity_source_value` list every supplied\n category and detailed code in source order, joined with `|`, or the\n extension text when no code is supplied. Detailed codes and text are\n not resolved.\n- Every supplied birth sex, gender, and OMB category code has a\n `mappings` entry whose `note` names its source and outcome. When a\n birth sex is supplied, Patient `gender` is reported unselected with\n the note `FHIR administrative gender; not used, birth sex supplied`.\n Conflicting values and a birth sex without `valueCode` are also\n returned in `diagnostics` with path `extension:birthsex` or\n `extension:ethnicity`. `summary` counts each demographic field once,\n as described under `Summary`.\n- Differences from the reference conventions: Vulcan's example gender\n ConceptMap maps `other` and `unknown` to concepts, which stay `0`\n here; more than one race follows the OHDSI THEMIS convention, also\n used by Vulcan, rather than the CDM 5.4 note that mixed races use\n `0`; and Vulcan's suggested `observation` rows for multiple\n ethnicities are not produced.\n\n`DiagnosticReport`, `ServiceRequest`, `CarePlan`, `DocumentReference`,\n`Composition`, `Specimen`, `DeviceUseStatement`, `Coverage`, `Claim`, and\nother unsupported resource types are accepted in a Bundle but ignored: they\ncreate no row and no `dropped` entry. `dropped` is reserved for supported\nrow-producing resources that cannot safely produce a positive OMOP row:\nrequired subject/patient, clinical code/text, or medication data may be\nunusable, or the resource may explicitly negate or fail the documented\nclinical-event eligibility policy. A single-Patient Bundle uses the sole\nPatient only when `subject`/`patient` is absent. An explicit subject/patient\nreference that is unresolved, ambiguous, or unsupported drops the clinical\nresource in every request scope.\n\nEligibility distinguishes clinical validity, performed/taken evidence, and\nadministrative workflow state. Checks run before terminology resolution;\nentered-in-error and explicitly non-performed events never produce a positive\nclinical-event row, and an unrecognized required status fails closed. Condition\nrequires absent or confirmed canonical-HL7 `verificationStatus`; clinical course is not a\ndiagnosis-role mapping. Procedures accept completed or stopped events.\nMedication requests are prescription evidence only: `doNotPerform`, drafts,\ncancellations, and non-authorizing intents are dropped. Medication statements\naccept active, completed, stopped, or on-hold reported use; administrations\naccept completed, in-progress, on-hold, or stopped events. An on-hold\nadministration also needs an `effectiveDateTime` or `effectivePeriod.start`\nthat supplies start evidence; a valid partial date remains an undated row.\nAn on-hold administration is started evidence that is temporarily paused and\nexpected to continue. Immunizations require completed\nstatus. Observations require final,\namended, or corrected status; registered,\npreliminary, cancelled, entered-in-error, unknown, and missing statuses are\ndropped. AllergyIntolerance accepts absent, unconfirmed, or confirmed canonical-HL7\n`verificationStatus` as a reported allergy, but drops refuted,\nentered-in-error, and unreadable supplied verification statuses. Encounter\naccepts arrived, triaged, in-progress, onleave, or finished status, but drops\nplanned, cancelled, entered-in-error, unknown, and missing statuses. An eligible\nencounter without `Period.end` remains a partial visit; no end date is invented.\n\nCoded Observation routing is selected from the resolved OMOP concept\ndomain. Numeric and nonnumeric `value[x]` forms establish the preferred\ntarget only when the code is valid for both tables. A text-only\nObservation has no resolver target, so numeric values route to\n`measurement` and nonnumeric values to `observation`. Numeric values\npopulate `value_as_number` in the selected row; other non-coded values\npopulate `value_as_string` for an `observation` or `value_source_value`\nfor a `measurement`. Coded `valueCodeableConcept` values are resolved\nagainst the selected row's `value_as_concept_id` and use the selected\nbare code in `value_source_value`, leaving an `observation`'s\n`value_as_string` empty; unmapped or target-invalid coded values remain\n`0`.\nOther unsupported `value[x]` forms and Observation components do not\npopulate separate converted values. A\nnumeric comparator (`<`, `<=`, `>`, `>=`) is represented only by a\nmeasurement's `operator_concept_id`; units remain source text and have\n`unit_concept_id` of `0`.\n\nA standard OMOP `concept_id` is selected for each eligible primary clinical coding\nafter considering all of the resource's supplied codings. An unambiguous\ncoded medication route is resolved independently to\n`drug_exposure.route_concept_id`. Alongside the OMOP rows grouped by\ntable (`tables`), the response carries `mappings` (an entry for every\nsupported source coding from a resource that shaped a row, linked back to\nthat row; some, such\nas demographic null flavors, are reported without being resolved),\n`provider_role_contexts` (source role details linked to provider rows),\n`dropped` (resources that could not be shaped into a row),\n`diagnostics` (explicit references that could not safely create a link,\nand conflicting or unsupported Patient demographic extensions),\n`vocab_version` (the OMOP vocabulary release codes were resolved\nagainst), and a small `summary` of the resolution outcomes.\n\nA `concept_id` of `0` is reported, not omitted (OMOP \"no matching\nconcept\" semantics): it covers both a coding with no standard match\n(`UNMAPPED`) and an unverified suggestion for a text-only resource\n(`UNCHECKED`). Visit and unit concept fields currently remain `0`;\nPerson demographics and Provider recorded gender follow the policies\nabove. Coded Observation values may populate `value_as_concept_id`.\nConcepts set by a fixed convention rather than terminology resolution\nare measurement `operator_concept_id`, set from a value comparator (`<`,\n`<=`, `>`, `>=`), and the multiple-race concepts described above. Clinical\n`*_source_value` fields contain the selected FHIR code (or source text\nfor text-only resources).\nThe corresponding selected `mappings` entry preserves the coding system\nand full source-coding provenance.\nKnown OID-form coding systems are accepted as either FHIR OID URNs (for\nexample, `urn:oid:2.16.840.1.113883.6.1` for LOINC) or bare OIDs, and\nare normalized to their canonical system URLs before terminology\nresolution. `mappings[].source_system` reports that canonical URL, so the\nOID and URL forms produce the same mapping. An unknown OID is not\nrewritten and may be `UNMAPPED`.\nOther `*_source_value` fields preserve row-specific raw source values,\nsuch as resource identifiers, names, units, or status codes.\n`MedicationRequest` uses `32838` (EHR prescription) for\n`drug_type_concept_id`; other current resources use `32817` (EHR). This\nis a coarse provenance policy: it does not infer patient-reported,\nmedication-history, or other more-specific type concepts from FHIR\nstatus fields.\n\nDates and datetimes:\n- A `*_date` is the calendar date (`YYYY-MM-DD`) of a source value with\n at least day precision. A `*_datetime` is set only when that value\n has a time of day, as local time without a UTC offset\n (`YYYY-MM-DDTHH:MM:SS`, with fractional seconds to microseconds when\n supplied): `2024-01-15T23:30:00-05:00` becomes `2024-01-15T23:30:00`.\n A datetime without a timezone is read as local time.\n- A partial date (`2024` or `2024-03`) leaves both columns unset. It\n still counts as that source's value, so later sources in the list\n below are not used.\n- Free-text timing (such as `onsetString`), `Age` and `Range` forms, and\n values that are not dates are ignored, so a later source in the list\n is used if there is one.\n- Other than the sources below and the same-day ends of single-event\n medication records, no date is imputed: partial dates are not\n completed, and a row is not dated from another resource such as its\n Encounter.\n- Valid date values never cause a request to be rejected, and the response\n does not report which date elements were unusable. An on-hold\n MedicationAdministration is an eligibility exception: it needs a supplied\n effective start as evidence that administration began.\n\nTiming sources, in priority order where several are listed:\n- `Patient`: `birthDate` sets `year_of_birth`, `month_of_birth`, and\n `day_of_birth` from the parts it supplies; `birth_datetime` is not\n set. `deceasedDateTime` sets the `death` dates.\n- `Practitioner`: `birthDate` sets the provider's `year_of_birth`.\n- `Encounter`: `period.start` and `period.end`.\n- `Condition`: start from `onsetDateTime` or `onsetPeriod.start`, then\n `recordedDate` (when the condition was recorded, not when it began);\n end from `abatementDateTime` or `abatementPeriod.end`.\n- `Procedure`: start from `performedDateTime` or\n `performedPeriod.start`; end from `performedPeriod.end` only.\n- `Observation`: `effectiveDateTime`, `effectivePeriod.start`, or\n `effectiveInstant`.\n- `AllergyIntolerance`: `recordedDate`, then `onsetDateTime` or\n `onsetPeriod.start`.\n- `MedicationStatement`: start from `effectiveDateTime` or\n `effectivePeriod.start`; end and `verbatim_end_date` from\n `effectivePeriod.end`. `dateAsserted` records when the statement was\n made and is not used.\n- `MedicationAdministration`: an `effectiveDateTime` is a single event\n that sets both start and end; an `effectivePeriod` sets the start\n and, when present, the end and `verbatim_end_date`.\n- `Immunization`: `occurrenceDateTime` is a single event that sets both\n start and end; `expirationDate` is not used.\n- `MedicationRequest`: `authoredOn`, the order date, sets the start; it\n is not evidence of administration. No end is set, and the validity\n period is not used as an exposure duration.\n- Differences from the Vulcan maps: `birth_datetime` is not set from\n `birthDate`, Condition also reads `onsetPeriod.start`, and\n AllergyIntolerance falls back to its onset when `recordedDate` is\n missing.\n\nMedication details:\n- For `MedicationRequest`, `dispenseRequest.numberOfRepeatsAllowed` sets\n `refills` and a whole-day `expectedSupplyDuration` sets `days_supply`.\n- All non-empty dosage text is preserved in `sig`.\n- Coded dosage routes and `Immunization.route` are target-validated in\n the OMOP Route domain. Conflicting routes are left unset; route\n codings shared by every dosage instruction identify the same route.\n- `Immunization.lotNumber` is preserved in `lot_number`.\n\nMedication codes are resolved whether they appear inline\n(`medicationCodeableConcept`) or via a `medicationReference` to a contained,\nrelative (`Type/id`), or bundle-entry (`urn:uuid`) `Medication` resource.\nA reference that resolves to another resource type is dropped even when it\nsupplies display text; an unresolved or display-only reference may use\nits display as text-only medication input.\nResources that cannot be shaped into a row \u2014 a medication with no usable\ncode, resolvable reference, or display; any clinical resource whose\nsubject/patient reference cannot be tied to a person; or an event excluded\nby eligibility \u2014 are reported under `dropped` rather than emitted as blank\nrows. The Bundle must contain at least one Patient resource.\n\nStructural references resolve only to top-level resources supplied in the\nrequest, by `Type/id` or an exactly matching Bundle `fullUrl` (including\n`urn:uuid`). Contained references are supported for medication code lookup\nand `PractitionerRole.practitioner` enrichment; the latter also supports\nexact request-local identifier matching without a remote lookup. Every\nnonzero structural foreign key targets a row in the same response. Missing\noptional links remain unset without a diagnostic; an explicit optional\nreference that is unresolved, ambiguous, conflicting with the row's\nperson, or unsupported remains unset and is returned in `diagnostics`\nwith its source path and outcome.\n\nAll row IDs start at `1` for each request and are not stable or global.\nFor clinical conversion rows whose resource supplies an `id`, `mappings`\nassociates each row with that source FHIR resource ID. A `person` row\nretains the Patient ID or its first identifier value in\n`person_source_value`, when present; other reference and derived rows do\nnot uniformly carry a FHIR resource ID. Input resources without those\nsource identifiers cannot be correlated across responses from the\nreturned rows alone. Consumers combining responses need to establish\ntheir own stable keys and remap every primary and foreign key together.\n", "requestBody": { "required": true, "content": { @@ -3323,6 +3317,7 @@ "resourceType": "MedicationRequest", "id": "medreq-1", "status": "active", + "intent": "order", "subject": { "reference": "Patient/patient-1" }, @@ -3350,6 +3345,46 @@ ] } } + }, + "oid_form_coding": { + "summary": "OID-form LOINC coding", + "description": "Known FHIR OID URNs are accepted for `Coding.system`. This\nLOINC OID is resolved as `http://loinc.org`; responses use\nthat canonical URL in source fields and mappings.\n", + "value": { + "fhir_resources": { + "resourceType": "Bundle", + "type": "collection", + "entry": [ + { + "resource": { + "resourceType": "Patient", + "id": "patient-1" + } + }, + { + "resource": { + "resourceType": "Observation", + "id": "hemoglobin-1", + "subject": { + "reference": "Patient/patient-1" + }, + "code": { + "coding": [ + { + "system": "urn:oid:2.16.840.1.113883.6.1", + "code": "718-7", + "display": "Hemoglobin" + } + ] + }, + "valueQuantity": { + "value": 13.5, + "unit": "g/dL" + } + } + } + ] + } + } } } } @@ -3366,7 +3401,7 @@ "examples": { "mapping_result": { "summary": "Mapping result", - "description": "The example bundle mapped to OMOP. Both source codes are already\nstandard, so each clinical row carries its own OMOP `concept_id`\nwith `ALREADY_STANDARD` status and a `target_code` equal to the\nsource code. Illustrative `concept_id` values.\n", + "description": "The example bundle mapped to OMOP. Both clinical source codes\nare already standard, so each clinical row carries its own OMOP\n`concept_id` with `ALREADY_STANDARD` status and a `target_code`\nequal to the source code. The Patient has no birth sex, so its\nadministrative gender is mapped as sex at birth. The concept IDs\nand vocabulary version illustrate the response shape; production\nvalues depend on the vocabulary release used for the request.\n", "value": { "success": true, "message": "FHIR resources mapped to OMOP CDM v5.4", @@ -3374,15 +3409,26 @@ "person": [ { "person_id": 1, - "gender_concept_id": 0, + "gender_concept_id": 8532, "year_of_birth": 1985, "month_of_birth": 7, "day_of_birth": 22, - "birth_datetime": "1985-07-22", "race_concept_id": 0, "ethnicity_concept_id": 0, "person_source_value": "patient-1", - "gender_source_value": "female" + "gender_source_value": "female", + "gender_source_concept_id": 0, + "race_source_concept_id": 0, + "ethnicity_source_concept_id": 0 + } + ], + "observation_period": [ + { + "observation_period_id": 1, + "person_id": 1, + "observation_period_start_date": "2024-01-15", + "observation_period_end_date": "2024-01-16", + "period_type_concept_id": 32817 } ], "condition_occurrence": [ @@ -3391,9 +3437,8 @@ "person_id": 1, "condition_concept_id": 201826, "condition_start_date": "2024-01-15", - "condition_start_datetime": "2024-01-15", "condition_type_concept_id": 32817, - "condition_source_value": "http://snomed.info/sct#44054006", + "condition_source_value": "44054006", "condition_source_concept_id": 201826 } ], @@ -3403,18 +3448,34 @@ "person_id": 1, "drug_concept_id": 40163924, "drug_exposure_start_date": "2024-01-16", - "drug_exposure_start_datetime": "2024-01-16", - "drug_type_concept_id": 32817, - "drug_source_value": "http://www.nlm.nih.gov/research/umls/rxnorm#860975", + "drug_type_concept_id": 32838, + "drug_source_value": "860975", "drug_source_concept_id": 40163924 } ] }, "mappings": [ + { + "resource_type": "Patient", + "resource_id": "patient-1", + "omop_table": "person", + "omop_field": "gender_concept_id", + "omop_id": 1, + "source_system": "http://hl7.org/fhir/administrative-gender", + "source_code": "female", + "source_name": "female", + "target_vocabulary": "Gender", + "target_code": "F", + "target_name": "FEMALE", + "mapping_status": "MAPPED", + "selected": true, + "note": "FHIR administrative gender; assumed sex at birth" + }, { "resource_type": "Condition", "resource_id": "condition-1", "omop_table": "condition_occurrence", + "omop_field": "condition_concept_id", "omop_id": 1, "source_system": "http://snomed.info/sct", "source_code": "44054006", @@ -3422,12 +3483,14 @@ "target_vocabulary": "SNOMED", "target_code": "44054006", "target_name": "Type 2 diabetes mellitus", - "mapping_status": "ALREADY_STANDARD" + "mapping_status": "ALREADY_STANDARD", + "selected": true }, { "resource_type": "MedicationRequest", "resource_id": "medreq-1", "omop_table": "drug_exposure", + "omop_field": "drug_concept_id", "omop_id": 1, "source_system": "http://www.nlm.nih.gov/research/umls/rxnorm", "source_code": "860975", @@ -3435,15 +3498,16 @@ "target_vocabulary": "RXNORM", "target_code": "860975", "target_name": "metformin hydrochloride 500 MG", - "mapping_status": "ALREADY_STANDARD" + "mapping_status": "ALREADY_STANDARD", + "selected": true } ], - "vocab_version": "v20240229", + "vocab_version": "v20260227", "summary": { "codes_already_standard": 2, - "codes_normalized": 0, + "codes_normalized": 1, "codes_unmapped": 0, - "off_vocab_rate": 0 + "off_vocab_rate": 0.3333333333333333 } } } @@ -3452,7 +3516,7 @@ } }, "400": { - "description": "Bad request - invalid input. Possible reasons:\n- Missing or malformed request body (no `fhir_resources`)\n- Invalid FHIR input (non-Bundle/non-resource payload)\n- No Patient resource found (OMOP requires at least one person)\n", + "description": "Bad request - invalid input. Possible reasons:\n- Missing or malformed request body (no `fhir_resources`)\n- Invalid FHIR input (non-Bundle/non-resource payload)\n- No Patient resource found (OMOP requires at least one person)\n- A coded field is too large to process with the selected vocabulary release\n- Unsupported `vocab_version`\n", "content": { "application/json": { "schema": { @@ -3466,16 +3530,6 @@ }, "500": { "description": "Internal server error" - }, - "503": { - "description": "Service unavailable due to a server configuration error. No rows are\nreturned.\n", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/fhir2omop_CreateOmopResponse" - } - } - } } }, "security": [ @@ -4586,7 +4640,7 @@ "post": { "operationId": "batch_create", "summary": "Create a batch job", - "description": "Opens an empty batch job. Items arrive on later upload calls and the set\nis sealed at finalize.\n\nSupplying `request_id` makes the create idempotent on that token: a\nretried submit whose response was lost returns the original job rather\nthan opening a second one. This dedupe is scoped to the calling\ncredential. A `request_id` whose job was canceled or failed before it\nfinalized is released for a fresh replay; once a job is finalized, its\n`request_id` keeps resolving to it even after cancellation.\n\nAn instance may hold at most 4 active (pending or processing) jobs at\nonce; a create past that limit returns `409`. The limit is instance-wide\n\u2014 jobs are shared across the instance's credentials \u2014 so another\ncredential's jobs count against it.\n", + "description": "Opens an empty job; upload items, then finalize it to start processing.\n\n`request_id` makes creation idempotent: a retry returns the original\njob. A token is released when its job is canceled or fails before\nfinalization; otherwise it continues to resolve to that job.\n", "requestBody": { "required": true, "content": { @@ -4617,9 +4671,6 @@ "401": { "description": "Unauthorized" }, - "409": { - "description": "The instance is at its 4-job active-batch limit; wait for one to finish" - }, "499": { "description": "Client closed request before response was ready" }, @@ -4702,7 +4753,7 @@ "post": { "operationId": "batch_uploadItem", "summary": "Upload one batch item", - "description": "Stores one item of a job from a multipart upload. A batch's items arrive\none per request. The item carries **either** a `document` extraction\n(whose input file rides as raw bytes in the `file` part) **or** a\n`create` extraction (JSON only, no file).\n\nThe upload enforces these rules:\n- Set **exactly one** of `document` or `create`. Setting both, or\n neither, is a `400`.\n- When `document` is set, `file` is **required** \u2014 it supplies the\n document's binary content (PDF or image).\n- When `create` is set, `file` is **forbidden** \u2014 a create item carries\n no file.\n- `document` and `create` must each be a JSON **object**.\n\nOnly the item's structure is checked here: the fields inside `document`\nor `create` are not validated at upload. A body that is well-formed JSON\nbut not a valid request for its endpoint is still accepted with `202`\nand fails later during processing, recorded as an item `error`. A\nwrong-typed field the endpoint cannot decode fails as `invalid_input`; a\nbody that decodes but the pipeline rejects (for example, a missing\nrequired field) fails as `processing_failed`.\n\nSupplying `request_id` makes the upload idempotent on that token. A\nre-upload under the same token overwrites the same item rather than\nadding a second, so a client that lost an upload's response can safely\nre-send it. The response's `deduplicated` is `true` only when the\nre-uploaded payload matches the one already stored; a same-token upload\nwith a changed payload overwrites in place and returns `false`.\n\nSet a `request_id` on **every** upload: re-sending under the same token\nis the only way to repair a lost or incomplete upload, including the one\na finalize `409` reports. Without one, a re-send adds a new item instead\nof replacing the missing one, and the job cannot be finalized.\n\nUploads are rejected once the job has been finalized (`409`), once it\nholds its 500-item limit (`409`), or when the item is too large (`413` \u2014\nsee the raw-file limit in the API description).\n", + "description": "Stores one multipart item. Set **either** `document` with a raw `file`,\nor JSON-only `create`.\n\nSet exactly one JSON object. `document` requires `file`; `create`\nforbids it. Violations return `400`.\n\nUpload validates only the envelope. Endpoint request validation happens\nduring processing: decoding failures are `invalid_input`; other pipeline\nfailures are `processing_failed`.\n\nUse `request_id` for every upload so retries replace the same item.\n`deduplicated` is true only for an unchanged payload; a changed payload\noverwrites the item and returns false.\n\nUploads return `409` after finalization or at the item limit, and `413`\nwhen the item is too large.\n", "parameters": [ { "name": "job_id", @@ -4722,26 +4773,24 @@ "properties": { "document": { "type": "object", - "description": "The JSON body of `POST /lang2fhir/document/multi`, **without**\nits base64 `content` field \u2014 the uploaded `file` supplies the\ncontent. Accepts that endpoint's fields (`version`, `provider`,\n`patient_reference`, `implementation_guide`, `detection_effort`,\n`validation_method`, `config`). This is the **multi**-resource\nbody: it has no single-`resource` field, and the item's result\nis a `DocumentMultiResponse` (a Bundle of resources). Mutually\nexclusive with `create`; requires `file`.\n" + "description": "The JSON body of `POST /lang2fhir/document/multi`, **without**\nits base64 `content` field \u2014 the uploaded `file` supplies the\ncontent. Accepts that endpoint's fields (`version`, `provider`,\n`primary_patient`, `patient_reference` (deprecated), `implementation_guide`, `detection_effort` (deprecated),\n`validation_method`, `config`). This is the **multi**-resource\nbody: it has no single-`resource` field, and the item's result\nis a `DocumentMultiResponse` (a Bundle of resources). Mutually\nexclusive with `create`; requires `file`. Do not combine\n`primary_patient` with `patient_reference`.\n" }, "create": { "type": "object", - "description": "The JSON body of `POST /lang2fhir/create/multi`. Accepts that\nendpoint's fields (`text`, `version`, `provider`,\n`patient_reference`, `implementation_guide`, `detection_effort`,\n`validation_method`, `resource_review`). This is the\n**multi**-resource body: it has no single-`resource` field, and\nthe item's result is a `CreateMultiResponse` (a Bundle of\nresources). Mutually exclusive with `document`; must **not** be\naccompanied by a `file`.\n" + "description": "The JSON body of `POST /lang2fhir/create/multi`. Accepts that\nendpoint's fields (`text`, `version`, `provider`,\n`primary_patient`, `patient_reference` (deprecated), `implementation_guide`, `detection_effort` (deprecated),\n`validation_method`, `resource_review`). This is the\n**multi**-resource body: it has no single-`resource` field, and\nthe item's result is a `CreateMultiResponse` (a Bundle of\nresources). Mutually exclusive with `document`; must **not** be\naccompanied by a `file`. Do not combine `primary_patient` with\n`patient_reference`.\n" }, "file": { "type": "string", "format": "binary", - "description": "The document's binary content (PDF, PNG, JPEG, or TIFF).\nRequired with `document`; forbidden with `create`.\n" + "description": "The document's file content (PDF, PNG, JPEG, TIFF, RTF, or\nXML/C-CDA). The document pipeline accepts files up to 20 MiB;\nan upload that passes the storage cap but exceeds this limit\nfails during processing. RTF and XML/C-CDA documents whose\nextracted text exceeds 1 MiB also fail during processing.\nGeneric XML must include an XML declaration; C-CDA documents\nrooted at `ClinicalDocument` may omit it.\nRequired with `document`; forbidden with `create`.\n" }, "request_id": { "type": "string", - "maxLength": 256, - "description": "Optional idempotency token (max 256 bytes). Re-uploading under\nthe same token overwrites the same item instead of adding a\nnew one. The token is scoped to this job; the same token in\nanother job is independent and creates a separate item.\n" + "description": "Optional idempotency token (at most 256 UTF-8 bytes).\nRe-uploading under the same token overwrites the same item\ninstead of adding a new one. The token is scoped to this job;\nthe same token in another job is independent and creates a\nseparate item.\n" }, "id": { "type": "string", - "maxLength": 512, - "description": "Optional caller-supplied correlation label (max 512 bytes),\nechoed back on status and result listings so you can match the\nserver's item_id to your own record.\n" + "description": "Optional caller-supplied correlation label (at most 512 UTF-8\nbytes), echoed back on status and result listings so you can\nmatch the server's item_id to your own record.\n" } } }, @@ -4777,7 +4826,7 @@ "description": "Batch job not found" }, "409": { - "description": "The job is finalized (no longer accepting items) or holds its 500-item limit" + "description": "The job is finalized or at its item limit" }, "413": { "description": "The upload is too large. Both size trips return 413: the request body\nexceeding the 32 MiB upload cap, and the item's stored payload\nexceeding the 28 MiB per-item limit (~21 MiB of raw file once\nbase64-encoded).\n" @@ -4860,7 +4909,7 @@ "post": { "operationId": "batch_cancel", "summary": "Cancel a batch job", - "description": "Drives a job to the terminal `canceled` state on request, freeing its\nactive-job slot immediately. Takes no request body.\n\nCancel does not delete the job: the job record and any results already\nproduced are preserved for the normal retention window, the same as a\n`completed` or `failed` job. Items stop being processed and keep the state\nthey held at cancellation, so a canceled job's `counts` may show\nunfinished items that never resolve.\n\nCancel is idempotent: canceling an already-`canceled` job returns `200`\nwith the job. Canceling a job that has already `completed` or `failed` is\na `409`.\n", + "description": "Drives a job to the terminal `canceled` state on request. Takes no\nrequest body.\n\nCancel does not delete the job: the job record and any results already\nproduced are preserved for the normal retention window, the same as a\n`completed` or `failed` job. Items stop being processed and keep the state\nthey held at cancellation, so a canceled job's `counts` may show\nunfinished items that never resolve.\n\nCancel is idempotent: canceling an already-`canceled` job returns `200`\nwith the job. Canceling a job that has already `completed` or `failed` is\na `409`.\n", "parameters": [ { "name": "job_id", @@ -5605,7 +5654,7 @@ "post": { "operationId": "lang2fhir_document", "summary": "Convert document to FHIR resource", - "description": "Extracts text from a document (PDF or image) and converts it into a structured FHIR resource.\n\n**Patient identifier handling.** When generating a `patient` (or `patient-canvas`) resource, US Core requires `Patient.identifier` (a business identifier such as an MRN). When the source text contains an identifier, it is extracted with an appropriate URI system. When the source text does not contain a detectable identifier, a synthetic one is generated with `system: \"urn:phenoml:lang2fhir-generated-id\"` and a UUID `value` so the resource remains FHIR-valid and US Core conformant. Callers who need a tenant-specific namespace should rewrite the synthetic system after extraction.\n", + "description": "Extracts text from a PDF, image, RTF, or XML/C-CDA document and converts it into a structured FHIR resource.\n\n**Patient identifier handling.** When generating a `patient` (or `patient-canvas`) resource, US Core requires `Patient.identifier` (a business identifier such as an MRN). When the source text contains an identifier, it is extracted with an appropriate URI system. When the source text does not contain a detectable identifier, a synthetic one is generated with `system: \"urn:phenoml:lang2fhir-generated-id\"` and a UUID `value` so the resource remains FHIR-valid and US Core conformant. Callers who need a tenant-specific namespace should rewrite the synthetic system after extraction.\n", "requestBody": { "required": true, "content": { @@ -5616,7 +5665,7 @@ "example": { "version": "R4", "resource": "questionnaire", - "content": "JVBERi0xLjQKJeLjz9MK...(base64-encoded PDF or image bytes)" + "content": "JVBERi0xLjQKJeLjz9MK...(base64-encoded document bytes)" } } } @@ -5655,6 +5704,9 @@ "401": { "description": "Unauthorized" }, + "403": { + "description": "Forbidden - TIFF, RTF, and XML/C-CDA uploads are only available on dedicated instances" + }, "404": { "description": "Profile not found" }, @@ -5683,7 +5735,7 @@ "post": { "operationId": "lang2fhir_documentMulti", "summary": "Extract multiple FHIR resources from a document", - "description": "Extracts text from a document (PDF or image) and converts it into multiple FHIR resources,\nreturned as a transaction Bundle. Combines document text extraction with multi-resource detection.\nAutomatically detects Patient, Condition, MedicationRequest, Observation, and other resource types.\nResources are linked with proper references (e.g., Conditions reference the Patient).\n\n**Patient identifier handling.** US Core requires `Patient.identifier` (a business identifier such as an MRN). When the source text contains an identifier, it is extracted with an appropriate URI system. When the source text does not contain a detectable identifier, a synthetic one is generated with `system: \"urn:phenoml:lang2fhir-generated-id\"` and a UUID `value` so the bundle remains FHIR-valid and US Core conformant. Callers who need a tenant-specific namespace should rewrite the synthetic system after extraction.\n\n**Split classifications (optional).** `config.split_classifications` is a caller-defined list, not a fixed taxonomy. Choose each classification `id` and write a natural-language `description` for the per-page classifier. For each page, the classifier assigns the best-matching classification or leaves the page ungrouped. Classifications with `operation: \"group\"` keep matching pages and label resources extracted from those pages; classifications with `operation: \"drop\"` remove matching pages before extraction. The `clinical` and `admin` ids in the example are illustrative, not a fixed set.\n", + "description": "Extracts text from a PDF, image, RTF, or XML/C-CDA document and converts it into multiple FHIR resources,\nreturned as a transaction Bundle. Combines document text extraction with multi-resource detection.\nAutomatically detects Patient, Condition, MedicationRequest, Observation, and other resource types.\nResources are linked with proper references (e.g., Conditions reference the Patient).\n\n**Patient identifier handling.** US Core requires `Patient.identifier` (a business identifier such as an MRN). When the source text contains an identifier, it is extracted with an appropriate URI system. When the source text does not contain a detectable identifier, a synthetic one is generated with `system: \"urn:phenoml:lang2fhir-generated-id\"` and a UUID `value` so the bundle remains FHIR-valid and US Core conformant. Callers who need a tenant-specific namespace should rewrite the synthetic system after extraction.\n\n**Split classifications (optional).** `config.split_classifications` is a caller-defined list, not a fixed taxonomy. Choose each classification `id` and write a natural-language `description` for the per-page classifier. For each page, the classifier assigns the best-matching classification or leaves the page ungrouped. Classifications with `operation: \"group\"` keep matching pages and label resources extracted from those pages; classifications with `operation: \"drop\"` remove matching pages before extraction. The `clinical` and `admin` ids in the example are illustrative, not a fixed set.\n", "requestBody": { "required": true, "content": { @@ -5693,7 +5745,7 @@ }, "example": { "version": "R4", - "content": "JVBERi0xLjQKJeLjz9MK...(base64-encoded PDF or image bytes)", + "content": "JVBERi0xLjQKJeLjz9MK...(base64-encoded document bytes)", "provider": "medplum", "config": { "split_classifications": [ @@ -5868,6 +5920,9 @@ "401": { "description": "Unauthorized" }, + "403": { + "description": "Forbidden - TIFF, RTF, and XML/C-CDA uploads are only available on dedicated instances" + }, "404": { "description": "Profile not found" }, @@ -6574,11 +6629,11 @@ "tags": [ "FHIR Artifacts / Implementation Guides" ], - "summary": "Delete an implementation guide's metadata", - "description": "Deletes the stored metadata for an implementation guide \u2014 its\nprofile_context and timestamps. Member profiles keep their\nimplementation_guide assignment, so a guide still referenced by at least\none profile continues to appear in listings, just without context or\ntimestamps.\n", + "summary": "Delete an implementation guide family", + "description": "Deletes the guide's metadata and all its canonical package versions.\nCustom profiles and their implementation-guide assignments are preserved.\n", "responses": { "204": { - "description": "Implementation guide metadata successfully deleted" + "description": "Implementation guide family successfully deleted" }, "400": { "description": "The name is reserved or malformed" @@ -6590,7 +6645,7 @@ "description": "Forbidden - custom profiles are only available on dedicated instances" }, "404": { - "description": "No implementation guide metadata exists for this name" + "description": "No implementation guide family exists for this name" }, "500": { "description": "Server error" @@ -6604,6 +6659,117 @@ "x-service": "fhir_artifacts" } }, + "/fhir/implementation-guides/{name}/versions": { + "parameters": [ + { + "name": "name", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "post": { + "operationId": "implementation-guides_createVersion", + "tags": [ + "FHIR Artifacts / Implementation Guides" + ], + "summary": "Publish an exact custom-profile package", + "description": "Publishes an exact package beneath this guide family. Each guide family\nsupports one exact package version. Publishing another version returns\n`409 Conflict`.\n", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/implementation-guides_CreateCanonicalImplementationGuideRequest" + } + } + } + }, + "responses": { + "201": { + "description": "Canonical package published", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/implementation-guides_ImplementationGuideVersionDetail" + } + } + } + }, + "400": { + "description": "Invalid ImplementationGuide, profile reference, version, or context" + }, + "404": { + "description": "An exact profile reference was not found" + }, + "409": { + "description": "The guide family already has a package version, or the canonical URL belongs to another guide family\n" + } + }, + "security": [ + { + "bearerAuth": [] + } + ], + "x-service": "fhir_artifacts" + } + }, + "/fhir/implementation-guides/{name}/versions/{version}": { + "parameters": [ + { + "name": "name", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "version", + "in": "path", + "required": true, + "description": "The authored ImplementationGuide.version. It may contain letters, numbers, and the punctuation characters `.`, `_`, `~`, `+`, and `-`; it cannot be exactly `.` or `..`.\n", + "schema": { + "type": "string", + "pattern": "^[A-Za-z0-9._~+-]+$" + }, + "example": "1.0.0" + } + ], + "get": { + "operationId": "implementation-guides_getVersion", + "tags": [ + "FHIR Artifacts / Implementation Guides" + ], + "summary": "Get an exact custom-profile package", + "responses": { + "200": { + "description": "Exact canonical package", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/implementation-guides_ImplementationGuideVersionDetail" + } + } + } + }, + "400": { + "description": "Invalid guide name or version" + }, + "404": { + "description": "Package version not found" + } + }, + "security": [ + { + "bearerAuth": [] + } + ], + "x-service": "fhir_artifacts" + } + }, "/fhir/profiles": { "get": { "operationId": "profiles_list", @@ -6832,6 +6998,9 @@ "404": { "description": "Profile not found" }, + "409": { + "description": "Profile is pinned by an exact implementation guide package" + }, "500": { "description": "Server error" } @@ -7050,6 +7219,9 @@ "404": { "description": "Profile or version not found" }, + "409": { + "description": "Profile version is pinned by an exact implementation guide package" + }, "500": { "description": "Server error" } @@ -10275,10 +10447,14 @@ "fhir_resources" ], "properties": { + "vocab_version": { + "type": "string", + "description": "OMOP vocabulary release to use for coded concept resolution. If\nomitted or empty, the API uses its default release. Specify a\nrelease explicitly when reproducibility matters. The response's\n`vocab_version`, when present, identifies the release used.\n" + }, "fhir_resources": { "type": "object", "additionalProperties": true, - "description": "FHIR resources (single resource or Bundle). Must contain at least one\nPatient resource. Supported row-producing resources are Patient,\nEncounter, Condition, Procedure, MedicationRequest,\nMedicationStatement, MedicationAdministration, Immunization,\nObservation, and AllergyIntolerance. Standalone Medication resources\nare consumed by medication references rather than mapped to their own\ntable. Other resource types are accepted but ignored.\n" + "description": "FHIR resources (single resource or Bundle). Must contain at least one\nPatient resource. Supported row-producing resources are Patient,\nLocation, Organization, HealthcareService, Practitioner,\nPractitionerRole, Encounter, Condition, Procedure, MedicationRequest,\nMedicationStatement, MedicationAdministration, Immunization,\nObservation, and AllergyIntolerance. Standalone Medication resources\nare consumed by medication references rather than mapped to their own\ntable. Unsupported resource types are accepted in a Bundle but ignored.\n" } } }, @@ -10296,21 +10472,35 @@ }, "mappings": { "type": "array", - "description": "One entry per source coding (or one entry for a text-only resource with no coding), describing how it resolved and linking back to the row it produced.", + "description": "One entry per supported source coding from a resource that shaped a row (or one entry for a text-only primary resource with no coding), describing how it resolved and linking back to the row it produced. A coded route or Observation valueCodeableConcept is a separate entry linked to its medication, vaccine, or observation row. A Patient demographic code links to its person row, or to its `observation` race row when the person has more than one race.", "items": { "$ref": "#/components/schemas/fhir2omop_MappingEntry" } }, + "provider_role_contexts": { + "type": "array", + "description": "Additive FHIR provenance for every supplied PractitionerRole. Each\ncontext identifies the canonical or role-fallback provider row and\npreserves source role facts that OMOP's singular provider columns\ncannot represent together.\n", + "items": { + "$ref": "#/components/schemas/fhir2omop_ProviderRoleContext" + } + }, "dropped": { "type": "array", - "description": "Supported resource instances that could not be shaped into an OMOP\nrow because required subject/patient, code, or medication reference\ndata was missing. Unsupported resource types are ignored and do not\nappear here.\n", + "description": "Supported resource instances that could not be shaped into an OMOP\nrow because the subject/patient, clinical code or text, or medication\ndata was missing or unusable, including an explicit subject/patient\nreference that was unresolved, ambiguous, or unsupported, or because\ntheir clinical-event eligibility status excluded them. A resource that\nlacks only a date or another CDM-required field is returned as a row\ninstead. Unsupported resource types are ignored and do not appear here.\nEligibility exclusions use stable, resource-specific `reason` codes;\nother shaping failures retain an explanatory reason string.\n", "items": { "$ref": "#/components/schemas/fhir2omop_DroppedResource" } }, + "diagnostics": { + "type": "array", + "description": "Explanations for explicit references that could not safely produce\nan OMOP link or canonicalize a `PractitionerRole` provider identity, or explicit\nsubject/patient references that caused a clinical row to be dropped.\nMissing optional references are normal and do not produce a diagnostic.\nReferences resolve only against resources supplied in this request.\nOutcomes distinguish unresolved, ambiguous, conflicting, and unsupported\nreferences. Patient demographic extensions that conflict, or a birth\nsex without `valueCode`, are also reported here; their `path` is\n`extension:birthsex` or `extension:ethnicity` and they have no\n`reference`.\n", + "items": { + "$ref": "#/components/schemas/fhir2omop_ReferenceDiagnostic" + } + }, "vocab_version": { "type": "string", - "description": "The OMOP vocabulary release the clinical codes were resolved against\n(e.g. \"v20240229\"), for reproducibility. Present when at least one\ncoded concept was resolved.\n" + "description": "The OMOP vocabulary release used for coded concept resolution\n(for example, \"v20260227\"), for reproducibility. Omitted when no\nvocabulary resolution was performed.\n" }, "summary": { "$ref": "#/components/schemas/fhir2omop_Summary" @@ -10319,7 +10509,7 @@ }, "fhir2omop_OmopTables": { "type": "object", - "description": "OMOP CDM v5.4 rows grouped by destination table.", + "description": "OMOP CDM v5.4 rows grouped by destination table. IDs are sequential and\nscoped to one response; they are not stable keys across requests.\nFields with no value are unset (omitted from the row), except concept\nIDs reported as `0`. Each `*_datetime` comes from the same source as its\n`*_date` and is set only when that source has a time of day.\n", "properties": { "location": { "type": "array", @@ -10491,14 +10681,17 @@ "format": "int64" }, "year_of_birth": { - "type": "integer" + "type": "integer", + "description": "Year from Practitioner.birthDate." }, "gender_concept_id": { "type": "integer", - "format": "int64" + "format": "int64", + "description": "For recorded `Practitioner.gender`, `male` and `female` resolve to validated OMOP Gender concepts. `other`, `unknown`, and absent values remain `0`." }, "provider_source_value": { - "type": "string" + "type": "string", + "description": "The source practitioner identity. A Practitioner contained by a PractitionerRole is scoped as `PractitionerRole/#` so identical local contained IDs do not collide; an id-less parent uses an explicitly marked response-local role ordinal such as `@role-index:1`." }, "specialty_source_value": { "type": "string" @@ -10508,110 +10701,263 @@ "format": "int64" }, "gender_source_value": { - "type": "string" + "type": "string", + "description": "The recorded FHIR administrative-gender value for this Provider." }, "gender_source_concept_id": { "type": "integer", - "format": "int64" + "format": "int64", + "description": "Remains `0` for FHIR administrative-gender enum-policy results." } } }, - "fhir2omop_PersonRow": { + "fhir2omop_ProviderRoleContext": { "type": "object", + "description": "Source FHIR PractitionerRole context linked to one response-local provider row.", + "required": [ + "provider_id" + ], "properties": { - "person_id": { - "type": "integer", - "format": "int64" - }, - "gender_concept_id": { + "provider_id": { "type": "integer", "format": "int64" }, - "year_of_birth": { - "type": "integer" + "role_source_value": { + "type": "string", + "description": "The PractitionerRole source identity: its FHIR id, identifier value,\nname, or fullUrl. When none is available, it is `PractitionerRole`.\n" }, - "month_of_birth": { - "type": "integer" + "practitioner_reference": { + "type": "string", + "description": "The role's supplied practitioner reference, when present." }, - "day_of_birth": { - "type": "integer" + "practitioner_identifier": { + "$ref": "#/components/schemas/fhir2omop_ProviderRolePractitionerIdentifier" }, - "birth_datetime": { - "type": "string" + "role_codes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/fhir2omop_ProviderRoleCodeableConcept" + } }, - "race_concept_id": { - "type": "integer", - "format": "int64" + "specialties": { + "type": "array", + "items": { + "$ref": "#/components/schemas/fhir2omop_ProviderRoleCodeableConcept" + } }, - "ethnicity_concept_id": { - "type": "integer", - "format": "int64" + "care_sites": { + "type": "array", + "description": "Every organization, healthcareService, or location reference supplied by the role.", + "items": { + "$ref": "#/components/schemas/fhir2omop_ProviderRoleCareSite" + } }, - "location_id": { - "type": "integer", - "format": "int64" + "active": { + "type": "boolean" }, - "person_source_value": { + "period_start": { "type": "string" }, - "gender_source_value": { + "period_end": { "type": "string" - }, - "race_source_value": { + } + } + }, + "fhir2omop_ProviderRolePractitionerIdentifier": { + "type": "object", + "description": "The logical identifier supplied on a PractitionerRole's practitioner reference.", + "required": [ + "value" + ], + "properties": { + "system": { "type": "string" }, - "ethnicity_source_value": { + "value": { "type": "string" } } }, - "fhir2omop_DeathRow": { + "fhir2omop_ProviderRoleCodeableConcept": { "type": "object", "properties": { - "person_id": { - "type": "integer", - "format": "int64" - }, - "death_date": { - "type": "string" - }, - "death_datetime": { + "source_value": { "type": "string" }, - "death_type_concept_id": { - "type": "integer", - "format": "int64" - }, - "cause_concept_id": { - "type": "integer", - "format": "int64" + "codings": { + "type": "array", + "items": { + "$ref": "#/components/schemas/fhir2omop_Coding" + } + } + } + }, + "fhir2omop_ProviderRoleCareSite": { + "type": "object", + "required": [ + "path", + "reference" + ], + "properties": { + "path": { + "type": "string", + "description": "The FHIR element path on the PractitionerRole." }, - "cause_source_value": { + "reference": { "type": "string" }, - "cause_source_concept_id": { + "care_site_id": { "type": "integer", "format": "int64" } } }, - "fhir2omop_ObservationPeriodRow": { + "fhir2omop_Coding": { "type": "object", "properties": { - "observation_period_id": { + "system": { + "type": "string" + }, + "code": { + "type": "string" + }, + "display": { + "type": "string" + } + } + }, + "fhir2omop_PersonRow": { + "type": "object", + "properties": { + "person_id": { + "type": "integer", + "format": "int64" + }, + "gender_concept_id": { + "type": "integer", + "format": "int64", + "description": "Standard OMOP Gender concept for sex at birth, from US Core birth sex when supplied, otherwise from Patient `gender` `male` or `female`. `0` for absent, unknown, other, unsupported, or conflicting values." + }, + "year_of_birth": { + "type": "integer", + "description": "Year from Patient.birthDate." + }, + "month_of_birth": { + "type": "integer", + "description": "Month from Patient.birthDate, when it supplies one." + }, + "day_of_birth": { + "type": "integer", + "description": "Day from Patient.birthDate, when it supplies one." + }, + "birth_datetime": { + "type": "string", + "description": "Not set; Patient.birthDate has no time of day." + }, + "race_concept_id": { + "type": "integer", + "format": "int64", + "description": "Standard OMOP Race concept from a US Core race OMB category; `1546847` (More than one race) when more than one distinct race resolves, with each race in an `observation` row. `0` when no category resolves." + }, + "ethnicity_concept_id": { + "type": "integer", + "format": "int64", + "description": "Standard OMOP Ethnicity concept from the US Core ethnicity OMB category. `0` when absent, unresolved, or conflicting; never derived from race." + }, + "location_id": { + "type": "integer", + "format": "int64" + }, + "provider_id": { + "type": "integer", + "format": "int64" + }, + "care_site_id": { "type": "integer", "format": "int64" }, + "person_source_value": { + "type": "string" + }, + "gender_source_value": { + "type": "string", + "description": "The selected sex-at-birth source code: the US Core birth sex `valueCode`, or Patient `gender` when no birth sex is supplied. Conflicting birth sex values are joined with `|`; empty when the birth sex has no `valueCode`." + }, + "gender_source_concept_id": { + "type": "integer", + "format": "int64", + "description": "OMOP source concept of the selected sex-at-birth code, when that code is itself an OMOP source concept; `0` otherwise, as for FHIR administrative gender codes." + }, + "race_source_value": { + "type": "string", + "description": "Every supplied US Core race category and detailed code, joined with `|` in source order, or the extension text when no code is supplied." + }, + "race_source_concept_id": { + "type": "integer", + "format": "int64", + "description": "OMOP source concept of a single resolved race code, when that code is itself an OMOP source concept; `0` otherwise, including when more than one race resolves." + }, + "ethnicity_source_value": { + "type": "string", + "description": "Every supplied US Core ethnicity category and detailed code, joined with `|` in source order, or the extension text when no code is supplied." + }, + "ethnicity_source_concept_id": { + "type": "integer", + "format": "int64", + "description": "OMOP source concept of the resolved ethnicity code, when that code is itself an OMOP source concept; `0` otherwise." + } + } + }, + "fhir2omop_DeathRow": { + "type": "object", + "properties": { "person_id": { "type": "integer", "format": "int64" }, - "observation_period_start_date": { + "death_date": { + "type": "string", + "description": "Date from Patient.deceasedDateTime; unset for a boolean-only or partial value." + }, + "death_datetime": { "type": "string" }, - "observation_period_end_date": { + "death_type_concept_id": { + "type": "integer", + "format": "int64" + }, + "cause_concept_id": { + "type": "integer", + "format": "int64" + }, + "cause_source_value": { "type": "string" }, + "cause_source_concept_id": { + "type": "integer", + "format": "int64" + } + } + }, + "fhir2omop_ObservationPeriodRow": { + "type": "object", + "properties": { + "observation_period_id": { + "type": "integer", + "format": "int64" + }, + "person_id": { + "type": "integer", + "format": "int64" + }, + "observation_period_start_date": { + "type": "string", + "description": "Earliest populated date among the person's visit, clinical, and death rows in this request; not enrollment evidence." + }, + "observation_period_end_date": { + "type": "string", + "description": "Latest populated date, including end dates, among the person's visit, clinical, and death rows in this request; not enrollment evidence." + }, "period_type_concept_id": { "type": "integer", "format": "int64" @@ -10634,13 +10980,15 @@ "format": "int64" }, "visit_start_date": { - "type": "string" + "type": "string", + "description": "Date from Encounter.period.start." }, "visit_start_datetime": { "type": "string" }, "visit_end_date": { - "type": "string" + "type": "string", + "description": "Date from Encounter.period.end." }, "visit_end_datetime": { "type": "string" @@ -10678,12 +11026,17 @@ "format": "int64" }, "condition_start_date": { - "type": "string" + "type": "string", + "description": "Date from Condition.onsetDateTime or onsetPeriod.start, otherwise Condition.recordedDate." }, "condition_start_datetime": { "type": "string" }, "condition_end_date": { + "type": "string", + "description": "Date from Condition.abatementDateTime or abatementPeriod.end." + }, + "condition_end_datetime": { "type": "string" }, "condition_type_concept_id": { @@ -10726,14 +11079,23 @@ "format": "int64" }, "drug_exposure_start_date": { - "type": "string" + "type": "string", + "description": "Date from the resource's timing source, such as effective[x], occurrenceDateTime, or MedicationRequest.authoredOn." }, "drug_exposure_start_datetime": { "type": "string" }, "drug_exposure_end_date": { + "type": "string", + "description": "Date from an explicit FHIR Period end, or the same day as the start for a single-event administration or immunization. Unset when the source supplies neither." + }, + "drug_exposure_end_datetime": { "type": "string" }, + "verbatim_end_date": { + "type": "string", + "description": "Date from an explicit FHIR Period.end only; inferred same-day ends are not verbatim source values." + }, "drug_type_concept_id": { "type": "integer", "format": "int64" @@ -10741,8 +11103,28 @@ "stop_reason": { "type": "string" }, + "refills": { + "type": "integer", + "format": "int64", + "description": "Direct MedicationRequest.dispenseRequest.numberOfRepeatsAllowed value, when supplied." + }, + "days_supply": { + "type": "integer", + "format": "int64", + "description": "Direct positive whole-day MedicationRequest.dispenseRequest.expectedSupplyDuration; no dose or quantity conversion is applied." + }, "sig": { - "type": "string" + "type": "string", + "description": "Newline-joined non-empty FHIR Dosage.text instructions in source order." + }, + "route_concept_id": { + "type": "integer", + "format": "int64", + "description": "Target-valid OMOP Route concept for an unambiguous coded FHIR route; `0` for an unmapped coded route, omitted for absent, text-only, or conflicting routes." + }, + "lot_number": { + "type": "string", + "description": "Direct FHIR R4 Immunization.lotNumber value." }, "visit_occurrence_id": { "type": "integer", @@ -10758,6 +11140,10 @@ "drug_source_concept_id": { "type": "integer", "format": "int64" + }, + "route_source_value": { + "type": "string", + "description": "Selected source coding or text for an unambiguous FHIR route." } } }, @@ -10777,11 +11163,19 @@ "format": "int64" }, "procedure_date": { - "type": "string" + "type": "string", + "description": "Date from Procedure.performedDateTime or performedPeriod.start." }, "procedure_datetime": { "type": "string" }, + "procedure_end_date": { + "type": "string", + "description": "Date from Procedure.performedPeriod.end." + }, + "procedure_end_datetime": { + "type": "string" + }, "procedure_type_concept_id": { "type": "integer", "format": "int64" @@ -10819,7 +11213,8 @@ "format": "int64" }, "measurement_date": { - "type": "string" + "type": "string", + "description": "Date from Observation.effectiveDateTime, effectivePeriod.start, or effectiveInstant." }, "measurement_datetime": { "type": "string" @@ -10892,7 +11287,8 @@ "format": "int64" }, "observation_date": { - "type": "string" + "type": "string", + "description": "For an Observation, date from effectiveDateTime, effectivePeriod.start, or effectiveInstant. For an AllergyIntolerance, date from recordedDate, otherwise onsetDateTime or onsetPeriod.start." }, "observation_datetime": { "type": "string" @@ -10941,7 +11337,10 @@ }, "fhir2omop_MappingEntry": { "type": "object", - "description": "How one source coding (or a text-only resource's free text) resolved to an OMOP standard concept.", + "description": "How one source coding (or a text-only primary resource's free text) resolved to an OMOP standard concept. `omop_table`, `omop_field`, and `omop_id` link it to the row and concept field it produced. A coded medication route is a separate entry linked to the same drug_exposure row as its medication or vaccine coding. `selected` identifies the coding rendered in the associated row's `*_source_value`; it is false for alternate codings and text-only rows.", + "required": [ + "selected" + ], "properties": { "resource_type": { "type": "string" @@ -10957,6 +11356,10 @@ "format": "int64", "description": "The id of the OMOP row this coding produced (e.g. `condition_occurrence_id`),\nwithin `omop_table`. A resource with multiple codings yields one entry\nper coding, all sharing this id.\n" }, + "omop_field": { + "type": "string", + "description": "The OMOP concept-ID field populated from this source coding, such as `condition_concept_id`, `route_concept_id`, or `race_concept_id`." + }, "source_system": { "type": "string" }, @@ -10978,33 +11381,44 @@ }, "mapping_status": { "type": "string", - "description": "ALREADY_STANDARD (source coding is already a standard OMOP concept),\nMAPPED (source coding was mapped to a standard concept), UNCHECKED (a\nstandard code was suggested \u2014 e.g. for a text-only resource \u2014 but not\nverified against the OMOP vocabulary, so `concept_id` stays `0`), or\nUNMAPPED (no standard concept found).\n" + "enum": [ + "ALREADY_STANDARD", + "MAPPED", + "UNCHECKED", + "UNMAPPED" + ], + "description": "ALREADY_STANDARD (source coding is already a standard OMOP concept),\nMAPPED (source coding was mapped to a standard concept), UNCHECKED (a\nstandard code was suggested for a text-only resource but not verified\nagainst the OMOP vocabulary, so `concept_id` stays `0`), or UNMAPPED\n(no standard concept found).\n" + }, + "selected": { + "type": "boolean", + "description": "Whether this source coding was selected for the linked row's `*_source_value` field. Always present; false for alternate codings and text-only rows. For a Patient demographic, it marks the code that determined the PERSON field or race `observation` row, even when that code has no concept; it is false for race and ethnicity null flavors, conflicting values, a Patient `gender` overridden by birth sex, and race categories that did not determine the field." }, "note": { - "type": "string" + "type": "string", + "description": "Additional context for the entry. A coded route is noted as\n`FHIR route`. Patient demographic entries name their source and, when\nnot applied, why:\n- `US Core birth sex`; `US Core birth sex; null flavor`;\n `US Core birth sex; outside value set`;\n `US Core birth sex; conflicting values`\n- `FHIR administrative gender; assumed sex at birth`;\n `FHIR administrative gender; not a sex-at-birth value`;\n `FHIR administrative gender; not used, birth sex supplied`\n- `US Core race OMB category`;\n `US Core race OMB category; more than one race` (linked to its\n `observation` race row); `US Core race; null flavor`\n- `US Core ethnicity OMB category`;\n `US Core ethnicity OMB category; conflicting categories`;\n `US Core ethnicity; null flavor`\n" } } }, "fhir2omop_Summary": { "type": "object", - "description": "The request's data-quality headline: how the coded concepts split across\nresolution outcomes, and the share that was not already in a target\nstandard vocabulary. Each coded resource is counted once (per resolved\nconcept), even when it carried several codings \u2014 unlike `mappings`, which\nhas one entry per coding.\n", + "description": "The request's data-quality headline: how row-producing coded concepts,\nselected routes, and Patient demographic fields split, and the share\nthat was not already in a target standard vocabulary. Each is counted\nonce even when it carried several codings \u2014 unlike `mappings`, which has\none entry per coding. For example, a medication code and its selected\ncoded route on the same `drug_exposure` row are separate outcomes;\nindependently checked report-only alternate route codings do not alter\n`summary`. A Patient demographic counts once per PERSON field, or per\nrace `observation` row, that received a non-null value; conflicting\nvalues count once as unmapped. Null flavors, Patient `gender` `other` or\n`unknown`, a Patient `gender` overridden by birth sex, and race\ncategories that did not determine the field are reported in `mappings`\nonly.\n", "properties": { "codes_already_standard": { "type": "integer", - "description": "Coded concepts already a standard OMOP concept (ALREADY_STANDARD)." + "description": "Resolution outcomes already a standard OMOP concept (ALREADY_STANDARD)." }, "codes_normalized": { "type": "integer", - "description": "Coded concepts mapped or suggested to a standard concept (MAPPED or UNCHECKED)." + "description": "Resolution outcomes mapped or suggested to a standard concept (MAPPED or UNCHECKED)." }, "codes_unmapped": { "type": "integer", - "description": "Coded concepts with no standard concept found (UNMAPPED)." + "description": "Resolution outcomes with no standard concept found (UNMAPPED)." }, "off_vocab_rate": { "type": "number", "format": "double", - "description": "Share of coded concepts not already standard ((normalized + unmapped) / total)." + "description": "Share of resolution outcomes not already standard ((normalized + unmapped) / total)." } } }, @@ -11022,6 +11436,37 @@ } } }, + "fhir2omop_ReferenceDiagnostic": { + "type": "object", + "properties": { + "resource_type": { + "type": "string" + }, + "resource_id": { + "type": "string" + }, + "path": { + "type": "string", + "description": "FHIR element path on the source resource." + }, + "reference": { + "type": "string", + "description": "The supplied Reference.reference value, when present." + }, + "outcome": { + "type": "string", + "enum": [ + "UNRESOLVED", + "AMBIGUOUS", + "CONFLICTING", + "UNSUPPORTED" + ] + }, + "reason": { + "type": "string" + } + } + }, "fhir_provider_FhirProviderCreateRequest": { "type": "object", "required": [ @@ -11788,7 +12233,7 @@ "properties": { "request_id": { "type": "string", - "description": "Optional client idempotency token. A retried create with the same\ntoken returns the original job instead of opening a second one.\n", + "description": "Optional client idempotency token (at most 256 UTF-8 bytes). A\nretried create with the same token returns the original job instead\nof opening a second one.\n", "example": "submit-2025-09-02-batch-001" } } @@ -11803,7 +12248,7 @@ "properties": { "kind": { "type": "string", - "description": "Short stable token to branch on. Item-level kinds: `invalid_input`\n(the stored body was not a valid create/document request),\n`processing_failed` (the conversion failed), `budget_exceeded` (the\nitem ran past its time budget \u2014 600s for a document item, 450s for a\ncreate item), `result_too_large` (the result exceeded the storage\ncap), `input_unavailable` (the input could not be read), and\n`retries_exhausted` / `attempts_exhausted` (buried after too many\nfailed attempts).\nJob-level kinds: `timeout` (the job did not finish within 36 hours\nof creation).\n", + "description": "Short stable token to branch on. Item-level kinds: `invalid_input`\n(the stored body was not a valid create/document request),\n`processing_failed` (the conversion failed), `result_too_large` (the\nresult exceeded the storage cap), `input_unavailable` (the input\ncould not be read), and `retries_exhausted` / `attempts_exhausted`\n(the item could not complete after repeated interruptions).\nJob-level kinds: `timeout` (the job did not finish within 36 hours\nof creation).\n", "example": "processing_failed" }, "message": { @@ -11838,7 +12283,7 @@ }, "status": { "type": "string", - "description": "Job status. `completed` means every item has finished \u2014 some may have\nfailed, so check `counts` for the split. `failed` is a whole-job\nfailure (the job could not run at all), distinct from individual item\nfailures, which never fail the job. `canceled` is a caller-requested\ncancellation via `POST /lang2fhir/batch/{job_id}/cancel`; like the\nother terminal states it frees the job's active-job slot and keeps any\nresults already produced readable for the retention window.\n", + "description": "Job status. `completed` means every item has finished \u2014 some may have\nfailed, so check `counts` for the split. `failed` is a whole-job\nfailure (the job could not run at all), distinct from individual item\nfailures, which never fail the job. `canceled` is a caller-requested\ncancellation via `POST /lang2fhir/batch/{job_id}/cancel`; like the\nother terminal states it keeps any results already produced readable\nfor the retention window.\n", "enum": [ "pending", "processing", @@ -11951,7 +12396,7 @@ }, "attempts": { "type": "integer", - "description": "Number of processing attempts so far. An item runs up to 3 attempts,\nand only an interruption \u2014 a worker preemption or a recovered internal\nerror \u2014 is retried; a conversion error fails the item with no retry.\n", + "description": "Number of processing attempts started so far. The service may retry\ninterrupted work; a conversion error fails the item without retry.\n", "example": 1 }, "detect_retries": { @@ -12170,13 +12615,16 @@ }, "resource": { "type": "string", - "description": "Type of FHIR resource to create. Use 'auto' for automatic resource type detection, or specify a supported US Core profile. Recommended to use the supported US Core Profiles for validated results but you can also use any custom profile you've uploaded (if you're a develop or launch customer) \n", + "description": "Type of FHIR resource to create. Use 'auto' for automatic resource type detection, or specify a supported profile. The default profile set includes US Core profiles and selected base R4 resources; you can also use any custom profile you've uploaded (if you're a develop or launch customer).\n", "example": "condition-encounter-diagnosis", "enum": [ "auto", "appointment", "condition-encounter-diagnosis", + "familymemberhistory", + "medicationadministration", "medicationrequest", + "medicationstatement", "careplan", "condition-problems-health-concerns", "coverage", @@ -12289,7 +12737,7 @@ }, "lang2fhir_PatientReference": { "type": "object", - "description": "Optional reference to an existing Patient, by business identifier. If a Patient resource is extracted, this identifier is added to that Patient's identifier list (existing identifiers are kept). If no Patient is extracted, generated clinical resources are linked to this patient as a logical reference (subject.identifier) instead of the request failing, and no placeholder Patient is created. Supply the patient-level identifier (not an order or specimen identifier).\n", + "description": "Business identifier for the document's primary patient. When Lang2FHIR identifies that Patient in generated output, it adds this identifier to the Patient's identifier list (preserving existing identifiers). If no Patient is generated, Lang2FHIR uses it in logical references on generated clinical resources. Supply the patient-level identifier (not an order or specimen identifier).\n", "required": [ "system", "value" @@ -12310,6 +12758,71 @@ "value": "abc-123" } }, + "lang2fhir_PrimaryPatientName": { + "type": "object", + "description": "The known portions of the primary patient's name. Provide a non-empty family name or at least one non-empty given name.\n", + "properties": { + "family": { + "type": "string", + "description": "Family name.", + "example": "Smith" + }, + "given": { + "type": "array", + "description": "Given names. Matching succeeds when a generated name has a supplied given name.", + "items": { + "type": "string" + }, + "example": [ + "Jane" + ] + } + } + }, + "lang2fhir_PrimaryPatient": { + "type": "object", + "minProperties": 1, + "description": "Partial context for the patient the document is primarily about. This is not a complete FHIR Patient resource. Lang2FHIR uses the available context to identify a generated primary Patient reliably. An identifier supplied here is added to that Patient; when no Patient is generated, it is used in logical references on generated clinical resources.\n", + "properties": { + "identifier": { + "$ref": "#/components/schemas/lang2fhir_PatientReference" + }, + "name": { + "$ref": "#/components/schemas/lang2fhir_PrimaryPatientName" + }, + "birthDate": { + "type": "string", + "format": "date", + "description": "Complete date of birth in YYYY-MM-DD format.", + "example": "1980-03-12" + }, + "gender": { + "type": "string", + "enum": [ + "male", + "female", + "other", + "unknown" + ], + "description": "Administrative gender. This corroborates another match but does not identify a patient alone.", + "example": "female" + } + }, + "example": { + "identifier": { + "system": "urn:acmehealth:patient_index_id", + "value": "abc-123" + }, + "name": { + "family": "Smith", + "given": [ + "Jane" + ] + }, + "birthDate": "1980-03-12", + "gender": "female" + } + }, "lang2fhir_CreateMultiRequest": { "type": "object", "required": [ @@ -12332,17 +12845,23 @@ "description": "Optional FHIR provider name for provider-specific profiles", "example": "canvas" }, + "primary_patient": { + "$ref": "#/components/schemas/lang2fhir_PrimaryPatient" + }, "patient_reference": { - "$ref": "#/components/schemas/lang2fhir_PatientReference" + "$ref": "#/components/schemas/lang2fhir_PatientReference", + "deprecated": true, + "description": "Deprecated compatibility alias for primary_patient.identifier. Cannot be combined with primary_patient." }, "implementation_guide": { "type": "string", - "description": "Custom Implementation Guide name. When specified, profiles from this IG are included alongside US Core profiles during resource detection. US Core is always the base layer; custom IG profiles are additive.\n", + "description": "Custom Implementation Guide name. When specified, profiles from this IG are included alongside the default profiles during resource detection. Default profiles are always the base layer; custom IG profiles are additive.\n", "example": "acme-cardiology" }, "detection_effort": { "type": "string", - "description": "Detection effort. 'standard' runs detection once, 'deep' runs detection multiple times for higher recall.", + "deprecated": true, + "description": "Deprecated; use the default 'standard' value. This field will be removed in a future release. 'standard' runs detection once; 'deep' runs detection multiple times for higher recall.", "enum": [ "standard", "deep" @@ -12568,24 +13087,30 @@ }, "content": { "type": "string", - "description": "Base64 encoded file content.\nSupported file types: PDF (application/pdf), PNG (image/png), JPEG (image/jpeg), TIFF (image/tiff).\nFile type is auto-detected from content magic bytes.\n" + "description": "Base64 encoded file content.\nSupported file types: PDF (application/pdf), PNG (image/png), JPEG (image/jpeg), TIFF (image/tiff), RTF (application/rtf), XML/C-CDA (text/xml).\nTIFF, RTF, and XML/C-CDA uploads are available on dedicated instances only.\nFile type is auto-detected from content magic bytes.\nThe decoded file must not exceed 20 MiB. RTF and XML/C-CDA documents whose extracted text exceeds 1 MiB are rejected.\nGeneric XML must include an XML declaration; C-CDA documents rooted at `ClinicalDocument` may omit it.\n" }, "provider": { "type": "string", "description": "Optional FHIR provider name for provider-specific profiles", "example": "canvas" }, + "primary_patient": { + "$ref": "#/components/schemas/lang2fhir_PrimaryPatient" + }, "patient_reference": { - "$ref": "#/components/schemas/lang2fhir_PatientReference" + "$ref": "#/components/schemas/lang2fhir_PatientReference", + "deprecated": true, + "description": "Deprecated compatibility alias for primary_patient.identifier. Cannot be combined with primary_patient." }, "implementation_guide": { "type": "string", - "description": "Custom Implementation Guide name. When specified, profiles from this IG are included alongside US Core profiles during resource detection. US Core is always the base layer; custom IG profiles are additive.\n", + "description": "Custom Implementation Guide name. When specified, profiles from this IG are included alongside the default profiles during resource detection. Default profiles are always the base layer; custom IG profiles are additive.\n", "example": "acme-cardiology" }, "detection_effort": { "type": "string", - "description": "Detection effort. 'standard' runs detection once, 'deep' runs detection multiple times for higher recall.", + "deprecated": true, + "description": "Deprecated; use the default 'standard' value. This field will be removed in a future release. 'standard' runs detection once; 'deep' runs detection multiple times for higher recall.", "enum": [ "standard", "deep" @@ -12708,7 +13233,7 @@ }, "lang2fhir_ResourceReview": { "type": "object", - "description": "Opt-in, report-only faithfulness audit (honored by /lang2fhir/create/multi and /lang2fhir/document/multi). For each selected resource type an LLM checks whether selected dates and clinical code concepts are actually supported by the full source document. Resources with an unsupported field are pulled out of the returned bundle and reported under resource_review in the response.\n", + "description": "Opt-in faithfulness audit (honored by /lang2fhir/create/multi and /lang2fhir/document/multi). For each selected resource type an LLM checks whether selected dates and clinical code concepts are actually supported by the full source document. An unsupported individual coding is removed when another coding remains in its concept. Resources with an unsupported structural field, a profile-required coding, or no coding remaining in an affected concept, are pulled out of the returned bundle and reported under resource_review in the response.\n", "required": [ "targets" ], @@ -12754,14 +13279,21 @@ }, "lang2fhir_ResourceReviewResult": { "type": "object", - "description": "Present when resource_review was requested and at least one resource was flagged.\n", + "description": "Present when resource_review was requested and at least one resource was quarantined or safely remediated. The returned bundle is authoritative and contains the post-review representation of every retained resource.\n", "properties": { "flagged": { "type": "array", - "description": "Resources pulled from the bundle because a reviewed field was not supported by the source.", + "description": "Resources pulled from the bundle because an unsupported finding could not be safely repaired.", "items": { "$ref": "#/components/schemas/lang2fhir_ResourceReviewFlagged" } + }, + "remediated": { + "type": "array", + "description": "Resources retained in the bundle after unsupported codings were safely removed.", + "items": { + "$ref": "#/components/schemas/lang2fhir_ResourceReviewRemediated" + } } } }, @@ -12783,7 +13315,35 @@ }, "findings": { "type": "array", - "description": "The unsupported fields that caused the resource to be flagged.", + "description": "The findings that caused the resource to be quarantined.", + "items": { + "$ref": "#/components/schemas/lang2fhir_ResourceReviewFinding" + } + } + } + }, + "lang2fhir_ResourceReviewRemediated": { + "type": "object", + "properties": { + "tempId": { + "type": "string", + "description": "The urn:uuid of the remediated resource (its bundle fullUrl).", + "example": "urn:uuid:a842c4bc-f6cb-4555-9741-ac3aec4ef0b8" + }, + "resourceType": { + "type": "string", + "example": "Condition" + }, + "action": { + "type": "string", + "enum": [ + "removed_codings" + ], + "description": "The safe change applied to the resource in the returned bundle." + }, + "findings": { + "type": "array", + "description": "Findings for fields in the pre-remediation resource that caused this action.", "items": { "$ref": "#/components/schemas/lang2fhir_ResourceReviewFinding" } @@ -12812,9 +13372,14 @@ }, "supported": { "type": "boolean", - "description": "Always false for a flagged finding.", + "description": "False when the reviewer found the field unsupported. Do not treat this field as a verdict when unaudited is true.", "example": false }, + "unaudited": { + "type": "boolean", + "description": "True when the reviewer did not return a verdict for this field; the resource was quarantined without treating the finding as evidence that the value is unsupported.", + "example": true + }, "rationale": { "type": "string", "description": "Short explanation of why the value is not supported by the source.", @@ -12861,7 +13426,7 @@ }, "content": { "type": "string", - "description": "Base64 encoded file content.\nSupported file types: PDF (application/pdf), PNG (image/png), JPEG (image/jpeg), TIFF (image/tiff).\nFile type is auto-detected from content magic bytes.\n" + "description": "Base64 encoded file content.\nSupported file types: PDF (application/pdf), PNG (image/png), JPEG (image/jpeg), TIFF (image/tiff), RTF (application/rtf), XML/C-CDA (text/xml).\nTIFF, RTF, and XML/C-CDA uploads are available on dedicated instances only.\nFile type is auto-detected from content magic bytes.\nThe decoded file must not exceed 20 MiB. RTF and XML/C-CDA documents whose extracted text exceeds 1 MiB are rejected.\nGeneric XML must include an XML declaration; C-CDA documents rooted at `ClinicalDocument` may omit it.\n" }, "config": { "$ref": "#/components/schemas/lang2fhir_DocumentConfig" @@ -13056,7 +13621,7 @@ }, "implementation-guides_ImplementationGuideSummary": { "type": "object", - "description": "Metadata for an implementation guide. This is an instance-local grouping record, not a complete FHIR ImplementationGuide resource.\n", + "description": "Metadata for an implementation guide. Canonical fields are present only for published canonical packages; guides without a published package omit them.\n", "properties": { "name": { "type": "string", @@ -13073,6 +13638,14 @@ "description": "The number of custom profiles in this implementation guide.", "example": 3 }, + "canonical_url": { + "type": "string", + "description": "Canonical FHIR ImplementationGuide URL, when the family has an exact package." + }, + "version_count": { + "type": "integer", + "description": "Number of retained exact package versions." + }, "created_at": { "type": "string", "format": "date-time", @@ -13119,6 +13692,125 @@ } ] }, + "implementation-guides_CreateCanonicalImplementationGuideRequest": { + "type": "object", + "required": [ + "implementation_guide", + "profile_refs" + ], + "properties": { + "implementation_guide": { + "$ref": "#/components/schemas/implementation-guides_FHIRImplementationGuide" + }, + "profile_refs": { + "type": "array", + "minItems": 1, + "maxItems": 250, + "description": "Exact canonical `url|version` references to builtin or custom profiles. A package can contain at most 250 references.\n", + "items": { + "type": "string" + } + }, + "profile_context": { + "type": "string", + "maxLength": 2000, + "description": "Natural-language profile-selection context for this package." + } + } + }, + "implementation-guides_ImplementationGuideVersionDetail": { + "type": "object", + "required": [ + "name", + "url", + "version", + "profile_context", + "profiles", + "profile_refs", + "implementation_guide", + "created_at", + "updated_at" + ], + "properties": { + "name": { + "type": "string" + }, + "url": { + "type": "string" + }, + "version": { + "type": "string" + }, + "profile_context": { + "type": "string" + }, + "profiles": { + "type": "array", + "items": { + "type": "string" + } + }, + "profile_refs": { + "type": "array", + "items": { + "type": "string" + } + }, + "implementation_guide": { + "$ref": "#/components/schemas/implementation-guides_FHIRImplementationGuide" + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" + } + } + }, + "implementation-guides_FHIRImplementationGuide": { + "type": "object", + "description": "A complete authored FHIR ImplementationGuide JSON resource.", + "required": [ + "resourceType", + "url", + "version" + ], + "additionalProperties": true, + "properties": { + "resourceType": { + "type": "string", + "enum": [ + "ImplementationGuide" + ] + }, + "id": { + "type": "string" + }, + "url": { + "type": "string" + }, + "version": { + "type": "string" + }, + "name": { + "type": "string" + }, + "status": { + "type": "string" + }, + "packageId": { + "type": "string" + }, + "fhirVersion": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, "implementation-guides_UpdateImplementationGuideRequest": { "type": "object", "properties": { @@ -13247,7 +13939,7 @@ "custom", "builtin" ], - "description": "The profile's origin. Profile management responses currently return custom (uploaded) profiles, so this is always \"custom\" today.\n", + "description": "The profile's origin. Profile management responses return custom (uploaded) profiles, so this value is always \"custom\".\n", "example": "custom" }, "resource_type": {