Skip to content

Conversation

@jhamon
Copy link
Collaborator

@jhamon jhamon commented Feb 3, 2026

Phase 2C: Index Model Adapter

Problem

The IndexModel wrapper class directly accessed internal OpenAPI model properties (_data_store, _configuration, _path_to_item) and contained complex spec resolution logic (150+ lines) for handling oneOf schemas (serverless/pod/byoc). This tight coupling made the code fragile and difficult to extend for future API format changes.

Solution

Created an adapter layer that extracts the complex spec resolution logic from the IndexModel wrapper, isolating SDK code from OpenAPI model internals.

Changes Made

New Files:

  • pinecone/adapters/index_adapter.py - Adapter functions for IndexModel spec resolution

    • adapt_index_spec() - Main adapter function that handles oneOf resolution
    • _adapt_serverless_spec() - Handles serverless spec with optional ReadCapacity
    • _adapt_pod_spec() - Handles pod-based spec
    • _adapt_byoc_spec() - Handles BYOC spec
  • tests/unit/adapters/test_index_adapter.py - Comprehensive unit tests

    • Tests for all three spec types (serverless, pod, byoc)
    • Tests for optional fields (read_capacity)
    • Tests for caching behavior
    • Protocol compliance tests

Modified Files:

  • pinecone/db_control/models/index_model.py - Simplified from 176 to 40 lines

    • Removed complex spec resolution logic
    • Now delegates to adapt_index_spec() adapter
  • pinecone/adapters/__init__.py - Added exports for new adapter function

Implementation Details

Spec Resolution

The adapter handles OpenAPI oneOf schema resolution for three index spec types:

  1. Serverless: Supports optional read_capacity field (OnDemand/Dedicated modes)
  2. Pod: Standard pod-based configuration
  3. BYOC: Bring Your Own Cloud configuration

The adapter properly handles:

  • Optional fields (read_capacity, source_collection, schema)
  • Nested oneOf types (ReadCapacity discriminator)
  • Type compatibility between request (ServerlessSpec) and response (ServerlessSpecResponse) models

Design Decisions

  • Type annotations: Used Any for return types to handle oneOf variants that don't share a common accessible base type
  • Type checking: Disabled _check_type when constructing oneOf wrappers due to response/request model differences
  • Extensibility: Architecture supports future schema-based API format (dimension in schema.fields.embedding) when it becomes available

Testing

  • Unit tests: 9 new tests covering all spec types and edge cases
  • Existing tests: All existing IndexModel tests pass unchanged
  • Type safety: Validated with mypy pinecone

Example test coverage:

  • Basic serverless, pod, and byoc specs
  • None handling for missing specs
  • Caching behavior in IndexModel wrapper
  • Already-deserialized spec handling
  • Protocol compliance verification

User-Facing Impact

No breaking changes. This is an internal refactoring that:

  • Maintains 100% backward compatibility
  • All existing IndexModel behavior unchanged
  • Cleaner, more maintainable code structure
  • Prepares for future API format changes

Related

  • Linear: SDK-276
  • Parent: SDK-269 (Incremental Change project)

Made with Cursor


Note

Medium Risk
Medium risk because it changes how IndexModel.spec and response fields are deserialized/defaulted; incorrect oneOf detection or defaults could subtly alter returned SDK objects, though coverage is added via new unit tests.

Overview
Moves IndexModel.spec oneOf deserialization (serverless/pod/BYOC, including nested read_capacity) out of the IndexModel wrapper into a new adapter adapt_index_spec, and exports it via pinecone.adapters.

Introduces formal adapter Protocols for OpenAPI models and updates query/upsert/fetch response adapters to use these types and to gracefully default optional/None fields (matches/namespace/vectors/upserted_count) to empty values. Adds/updates unit tests and fixtures to validate protocol compliance and None-handling behavior.

Written by Cursor Bugbot for commit 543743a. This will update automatically on new commits. Configure here.

jhamon and others added 3 commits February 3, 2026 03:52
Add formal Protocol interfaces that define the contract between OpenAPI
models and SDK adapters. This improves type safety, documentation, and
maintainability without any breaking changes.

- Created pinecone/adapters/protocols.py with 5 Protocol definitions
- Updated adapter functions to use protocol type annotations
- Added 13 unit tests verifying protocol compliance
- All tests pass, mypy validates protocol usage

Related: SDK-275
Co-authored-by: Cursor <cursoragent@cursor.com>
Changed FetchResponseAdapter.namespace from `str` to `str | None` to
correctly reflect that the OpenAPI FetchResponse model marks namespace
as optional. Updated the adapter function to handle None namespace by
converting it to empty string, consistent with QueryResponse adapter.

- Updated protocol type annotation
- Added null-coalescing in adapter function
- Added test for None namespace case

Fixes Cursor Bugbot issue

Co-authored-by: Cursor <cursoragent@cursor.com>
Create adapter infrastructure for IndexModel spec handling, isolating SDK
code from OpenAPI model internals and preparing for future API format changes.

Changes:
- Add pinecone/adapters/index_adapter.py with adapt_index_spec() function
- Extract 150+ lines of complex oneOf resolution logic from IndexModel
- Handle serverless/pod/byoc specs with proper optional field support
- Simplify IndexModel from 176 to 40 lines by delegating to adapter
- Add comprehensive unit tests for all spec types and edge cases
- Maintain 100% backward compatibility with existing behavior

The adapter properly handles:
- Optional fields (read_capacity, source_collection, schema)
- Nested oneOf types (ReadCapacity with OnDemand/Dedicated modes)
- Type compatibility between request and response models
- Caching behavior in IndexModel wrapper

Closes SDK-276

Co-authored-by: Cursor <cursoragent@cursor.com>
…port

Address Cursor Bugbot feedback:
- Fix: preserve already-deserialized read_capacity values instead of replacing with None
- Refactor: remove IndexStatusAdapter from public exports (still used in tests for protocol compliance)

Co-authored-by: Cursor <cursoragent@cursor.com>
@jhamon
Copy link
Collaborator Author

jhamon commented Feb 3, 2026

Fixed both issues in commit 543743a:

Issue 1 (Medium): Changed read_capacity initialization to preserve already-deserialized values:

# Before: read_capacity_spec = None
# After:
read_capacity_spec = serverless_dict.get("read_capacity")

Issue 2 (Low): Removed IndexStatusAdapter from public __init__.py exports. The protocol remains defined in protocols.py for protocol compliance testing, but is no longer exported in the public API since it's not used in any adapter function signatures.

Copy link
Collaborator Author

@jhamon jhamon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed both Bugbot issues in commit 543743a

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

_path_to_item=spec_path + ["serverless"],
_configuration=config,
_spec_property_naming=False,
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nested schema field not deserialized when passed as dict

Medium Severity

The _adapt_serverless_spec function explicitly deserializes read_capacity when it's a dict, but passes schema directly to ServerlessSpecResponse._from_openapi_data() without deserialization. The original code used deserialize_model() on the entire serverless_dict, which recursively converted all nested dicts to their proper types. If the spec comes as a raw dict (the scenario this adapter handles), the nested schema field would also be a raw dict that won't be converted to a BackupModelSchema instance.

Fix in Cursor Fix in Web

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants