From 63a835cbfd5cc6acf1045b249fbfbddb773e2f96 Mon Sep 17 00:00:00 2001 From: James Kent Date: Mon, 1 Dec 2025 23:53:14 -0600 Subject: [PATCH] fix the pipeline config --- .../neurostore/tests/api/test_base_studies.py | 23 +++++++++++++++++++ store/backend/neurostore/utils.py | 15 ++++++++++-- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/store/backend/neurostore/tests/api/test_base_studies.py b/store/backend/neurostore/tests/api/test_base_studies.py index bfd38873..9efa6a25 100644 --- a/store/backend/neurostore/tests/api/test_base_studies.py +++ b/store/backend/neurostore/tests/api/test_base_studies.py @@ -571,6 +571,29 @@ def test_feature_display_and_pipeline_config(auth_client, ingest_demographic_fea assert len(mismatch_response.json()["results"]) == 0 +def test_pipeline_config_with_quoted_value( + auth_client, ingest_demographic_features +): + """Ensure quoted config filter values do not break jsonpath parsing.""" + quoted_resp = auth_client.get( + "/api/base-studies/?" + 'pipeline_config=ParticipantDemographicsExtractor:1.0.0:' + 'extractor_kwargs.extraction_model="gpt-4-turbo"' + ) + assert quoted_resp.status_code == 200 + + unquoted_resp = auth_client.get( + "/api/base-studies/?" + "pipeline_config=ParticipantDemographicsExtractor:1.0.0:" + "extractor_kwargs.extraction_model=gpt-4-turbo" + ) + assert unquoted_resp.status_code == 200 + + quoted_ids = {result["id"] for result in quoted_resp.json()["results"]} + unquoted_ids = {result["id"] for result in unquoted_resp.json()["results"]} + assert quoted_ids == unquoted_ids + + def test_feature_flatten(auth_client, ingest_demographic_features): """Test flattening nested feature objects into dot notation""" # Get response without flattening diff --git a/store/backend/neurostore/utils.py b/store/backend/neurostore/utils.py index 91c70af4..effbbeed 100644 --- a/store/backend/neurostore/utils.py +++ b/store/backend/neurostore/utils.py @@ -39,8 +39,19 @@ def build_jsonpath(field_path: str, operator: str, value: str) -> str: Returns: PostgreSQL jsonpath query string """ + def normalize_value(raw_val: str): + # Strip surrounding quotes so users can include quoted values without + # creating invalid jsonpath strings. + if ( + len(raw_val) >= 2 + and raw_val[0] == raw_val[-1] + and raw_val[0] in {"'", '"'} + ): + raw_val = raw_val[1:-1] + return determine_value_type(raw_val) + # Handle regular field queries - cast_val, is_numeric = determine_value_type(value) + cast_val, is_numeric = normalize_value(value) # Map operators op_map = {"~": "like_regex", "=": "==", ">": ">", "<": "<", ">=": ">=", "<=": "<="} @@ -51,7 +62,7 @@ def build_jsonpath(field_path: str, operator: str, value: str) -> str: values = [] for val in value.split("|"): val = val.strip() - cast_val, is_numeric = determine_value_type(val) + cast_val, is_numeric = normalize_value(val) if isinstance(cast_val, bool): values.append(str(cast_val).lower()) elif is_numeric: