# AI Development Guide

## Introduction

This guide is specifically written for AI agents working on the Pinecone Python SDK. It provides architecture context, development patterns, and guidelines to help AI agents understand and contribute to the codebase effectively.

## Quick Start for AI Agents

### Understanding the Codebase

```mermaid
graph TB
    subgraph ReadFirst["Read These First"]
        ARCH[architecture/overview.md]
        API[architecture/api-structure.md]
        SPEC[architecture/specifications.md]
    end

    subgraph Conventions["Then Read Conventions"]
        CONV[conventions/README.md]
        ARCH_CONV[conventions/architecture.md]
        TYPES[conventions/types.md]
    end

    subgraph Implementation["Before Implementing"]
        TESTS[conventions/testing.md]
        ERRORS[conventions/errors.md]
        PERF[conventions/performance.md]
    end

    ReadFirst --> Conventions --> Implementation

    style ReadFirst fill:#e3f2fd
    style Conventions fill:#fff3e0
    style Implementation fill:#e8f5e9
```

### Key Architectural Concepts

1. **Three-Layer Architecture**
   - Layer 1: Public API (`pinecone/__init__.py`)
   - Layer 2: Protocols (`pinecone/__interface__/`)
   - Layer 3: Implementation (`pinecone/_internal/`)

2. **Protocol-Based Design**
   - All public interfaces defined as protocols
   - Implementations satisfy protocols without inheritance
   - Enables testing, flexibility, and type safety

3. **Explicit Dependencies**
   - No circular dependencies
   - Clear flow: Public → Protocols → Implementation
   - Implementation never imports from Public

## AI Agent Workflow

### Standard Development Process

```mermaid
graph TB
    TASK[Receive Task]
    UNDERSTAND[Understand Requirements]
    READ[Read Relevant Docs]
    DESIGN[Design Solution]
    PROTO[Define/Update Protocols]
    IMPL[Implement]
    TEST[Write Tests]
    VERIFY[Run Verification]
    DOC[Update Documentation]
    COMMIT[Commit Changes]

    TASK --> UNDERSTAND
    UNDERSTAND --> READ
    READ --> DESIGN
    DESIGN --> PROTO
    PROTO --> IMPL
    IMPL --> TEST
    TEST --> VERIFY
    VERIFY -->|Pass| DOC
    VERIFY -->|Fail| IMPL
    DOC --> COMMIT

    style TASK fill:#e3f2fd
    style COMMIT fill:#4caf50
```

### Implementation Checklist

Before implementing any feature:

- [ ] Read relevant architecture documentation
- [ ] Understand the three-layer structure
- [ ] Identify which layer(s) are affected
- [ ] Check existing conventions
- [ ] Plan protocol changes (if needed)
- [ ] Consider backward compatibility
- [ ] Plan test coverage

After implementing:

- [ ] All tests pass (unit, integration, protocol)
- [ ] Type checking passes (mypy, pyright)
- [ ] Code coverage >= 90%
- [ ] Documentation updated
- [ ] Examples added (if new feature)
- [ ] Conventions followed

## Common Development Patterns

### Adding a New Method to Existing Client

```mermaid
sequenceDiagram
    participant AI as AI Agent
    participant Proto as Protocol Layer
    participant Impl as Implementation
    participant Tests as Test Suite

    AI->>Proto: 1. Add method signature to protocol
    Note over Proto: Add to IPineconeClient

    AI->>Impl: 2. Implement method in _internal
    Note over Impl: Add to PineconeClient

    AI->>Tests: 3. Add protocol compliance test
    Note over Tests: Verify protocol satisfaction

    AI->>Tests: 4. Add unit tests
    Note over Tests: Test implementation logic

    AI->>Tests: 5. Add integration tests
    Note over Tests: Test with mock API

    AI->>AI: 6. Run verification
    Note over AI: mypy, pytest, coverage
```

**Example:**

```python
# Step 1: Add to protocol
# File: pinecone/__interface__/client.py

@runtime_checkable
class IPineconeClient(Protocol):
    # ... existing methods ...

    def configure_index(
        self,
        name: str,
        replicas: int | None = None,
        pod_type: str | None = None,
    ) -> None:
        """Configure index settings.

        Args:
            name: Index name
            replicas: Number of replicas
            pod_type: Pod type specification

        Raises:
            PineconeApiException: If configuration fails
        """
        ...

# Step 2: Implement
# File: pinecone/_internal/client.py

class PineconeClient:
    # ... existing methods ...

    def configure_index(
        self,
        name: str,
        replicas: int | None = None,
        pod_type: str | None = None,
    ) -> None:
        """Implementation of configure_index."""
        payload = {}
        if replicas is not None:
            payload["replicas"] = replicas
        if pod_type is not None:
            payload["pod_type"] = pod_type

        self._http_client.patch(
            f"/indexes/{name}",
            json=payload,
        )

# Step 3: Protocol test
# File: tests/protocols/test_client_protocol.py

def test_configure_index_in_protocol():
    """Verify configure_index is part of protocol."""
    from pinecone import IPineconeClient
    from pinecone._internal.client import PineconeClient

    client = PineconeClient(api_key="test")
    assert isinstance(client, IPineconeClient)
    assert hasattr(client, "configure_index")

# Step 4: Unit test
# File: tests/unit/test_client.py

def test_configure_index(mocker):
    """Test configure_index implementation."""
    mock_http = mocker.Mock()
    client = PineconeClient(api_key="test", http_client=mock_http)

    client.configure_index(
        name="my-index",
        replicas=2,
        pod_type="p1.x1",
    )

    mock_http.patch.assert_called_once_with(
        "/indexes/my-index",
        json={"replicas": 2, "pod_type": "p1.x1"},
    )

# Step 5: Integration test
# File: tests/integration/test_client_integration.py

@responses.activate
def test_configure_index_integration():
    """Test configure_index with mock API."""
    responses.add(
        responses.PATCH,
        "https://api.pinecone.io/indexes/my-index",
        json={"status": "success"},
        status=200,
    )

    client = PineconeClient(api_key="test")
    client.configure_index(name="my-index", replicas=2)

    assert len(responses.calls) == 1
```

### Adding a New Client Type

When adding a completely new client (e.g., `ICollectionClient`):

```mermaid
graph TB
    START[New Client Needed]

    subgraph Protocol["1. Define Protocol"]
        P1[Create __interface__/collection.py]
        P2[Define ICollectionClient]
        P3[Add all method signatures]
        P4[Export in __interface__/__init__.py]
    end

    subgraph Implementation["2. Implement"]
        I1[Create _internal/collection.py]
        I2[Implement CollectionClient]
        I3[Implement all methods]
        I4[Add HTTP client usage]
    end

    subgraph Public["3. Expose in Public API"]
        E1[Import in __init__.py]
        E2[Export ICollectionClient]
        E3[Export CollectionClient]
        E4[Update __all__]
    end

    subgraph Tests["4. Add Tests"]
        T1[Protocol tests]
        T2[Unit tests]
        T3[Integration tests]
        T4[E2E tests]
    end

    START --> Protocol --> Implementation --> Public --> Tests

    style Protocol fill:#fff3e0
    style Implementation fill:#f3e5f5
    style Public fill:#e1f5ff
    style Tests fill:#e8f5e9
```

### Handling Breaking Changes

Breaking changes require major version bump:

```mermaid
graph TB
    CHANGE[Need Breaking Change]
    ASSESS[Assess Impact]

    subgraph Options["Choose Strategy"]
        OPT1[1. Deprecate First]
        OPT2[2. Breaking in Major Version]
    end

    subgraph Deprecation["Deprecation Path"]
        D1[Add new method/parameter]
        D2[Mark old as deprecated]
        D3[Add deprecation warnings]
        D4[Update documentation]
        D5[Remove in next major]
    end

    subgraph Breaking["Direct Breaking Change"]
        B1[Document change]
        B2[Update migration guide]
        B3[Bump major version]
        B4[Update changelog]
    end

    CHANGE --> ASSESS
    ASSESS --> Options
    OPT1 --> Deprecation
    OPT2 --> Breaking

    style Deprecation fill:#e8f5e9
    style Breaking fill:#ffebee
```

## File Organization Patterns

### Where to Put Code

```mermaid
graph TB
    subgraph Decision["What Are You Adding?"]
        Q1{Public Interface?}
        Q2{Protocol Definition?}
        Q3{Implementation?}
        Q4{Data Model?}
        Q5{Utility?}
    end

    subgraph Location["Location"]
        L1[__init__.py<br/>Export only]
        L2[__interface__/<br/>Protocol definition]
        L3[_internal/<br/>Implementation]
        L4[_internal/models/<br/>Data models]
        L5[_internal/utils/<br/>Utilities]
    end

    Q1 -->|Yes| L1
    Q2 -->|Yes| L2
    Q3 -->|Yes| L3
    Q4 -->|Yes| L4
    Q5 -->|Yes| L5

    style Decision fill:#e3f2fd
    style Location fill:#f3e5f5
```

### File Naming Conventions

| Type | Convention | Example |
|------|-----------|---------|
| Protocol | Interface name + .py | `client.py`, `index.py` |
| Implementation | Matches protocol file | `client.py`, `index.py` |
| Models | Descriptive name | `vector.py`, `response.py` |
| Utils | Function category | `validation.py`, `config.py` |
| Tests | test_ prefix | `test_client.py` |

## Type Checking for AI Agents

### Running Type Checkers

```bash
# Run both type checkers (required for CI)
mypy pinecone
pyright

# Check specific file
mypy pinecone/_internal/client.py

# Check with strict mode
mypy --strict pinecone
```

### Common Type Issues and Solutions

**Issue: Missing return type**
```python
# ❌ Bad
def get_indexes():
    return self._http_client.get("/indexes")

# ✅ Good
def get_indexes(self) -> list[str]:
    return self._http_client.get("/indexes")
```

**Issue: Untyped parameters**
```python
# ❌ Bad
def create_index(self, name, dimension):
    pass

# ✅ Good
def create_index(self, name: str, dimension: int) -> None:
    pass
```

**Issue: Protocol not runtime checkable**
```python
# ❌ Bad
class IClient(Protocol):
    def method(self) -> str: ...

# ✅ Good
@runtime_checkable
class IClient(Protocol):
    def method(self) -> str: ...
```

## Testing Patterns for AI Agents

### Test Structure Template

```python
"""Tests for [module name].

This module tests [brief description of what's being tested].
"""

import pytest
from pinecone import [imports]
from pinecone._internal import [imports]

# Fixtures

@pytest.fixture
def mock_http_client(mocker):
    """Create a mock HTTP client."""
    return mocker.Mock(spec=HTTPClient)

@pytest.fixture
def client(mock_http_client):
    """Create a client with mock dependencies."""
    return PineconeClient(
        api_key="test-key",
        http_client=mock_http_client,
    )

# Protocol Tests

class TestProtocolCompliance:
    """Test that implementations satisfy protocols."""

    def test_implements_protocol(self, client):
        """Verify client implements IPineconeClient."""
        assert isinstance(client, IPineconeClient)

# Unit Tests

class TestClientMethods:
    """Test individual client methods."""

    def test_method_success(self, client, mock_http_client):
        """Test method under normal conditions."""
        # Arrange
        mock_http_client.get.return_value = {"data": "value"}

        # Act
        result = client.method()

        # Assert
        assert result == expected
        mock_http_client.get.assert_called_once()

    def test_method_error_handling(self, client, mock_http_client):
        """Test method handles errors correctly."""
        # Arrange
        mock_http_client.get.side_effect = HTTPError("error")

        # Act & Assert
        with pytest.raises(PineconeApiException):
            client.method()

# Integration Tests

class TestClientIntegration:
    """Test client with mocked HTTP responses."""

    @responses.activate
    def test_method_integration(self):
        """Test method with full HTTP stack."""
        # Arrange
        responses.add(
            responses.GET,
            "https://api.pinecone.io/endpoint",
            json={"data": "value"},
            status=200,
        )
        client = PineconeClient(api_key="test-key")

        # Act
        result = client.method()

        # Assert
        assert result == expected
```

### Test Coverage Requirements

```mermaid
graph TB
    subgraph Coverage["Coverage Requirements"]
        PUBLIC[Public API: 95%]
        PROTOCOL[Protocols: 100%]
        IMPL[Implementation: 90%]
        UTILS[Utils: 80%]
    end

    subgraph Types["Test Types"]
        UNIT[Unit Tests]
        PROTO[Protocol Tests]
        INT[Integration Tests]
        E2E[E2E Tests]
    end

    PUBLIC --> UNIT
    PUBLIC --> INT
    PROTOCOL --> PROTO
    IMPL --> UNIT
    IMPL --> INT
    UTILS --> UNIT

    style Coverage fill:#e8f5e9
    style Types fill:#e3f2fd
```

## Error Handling Patterns

### Exception Hierarchy

```python
PineconeException
├── PineconeApiException          # API errors
│   ├── BadRequestException       # 400
│   ├── UnauthorizedException     # 401
│   ├── ForbiddenException        # 403
│   ├── NotFoundException         # 404
│   └── RateLimitException        # 429
├── NetworkException              # Network errors
├── ValidationException           # Input validation
└── ConfigurationException        # Configuration errors
```

### Error Handling Template

```python
from pinecone._internal.errors import (
    PineconeApiException,
    ValidationException,
)
from pinecone._internal.utils.validation import (
    validate_dimension,
    validate_metric,
)

def create_index(
    self,
    name: str,
    dimension: int,
    metric: str = "cosine",
) -> None:
    """Create index with proper error handling."""

    # 1. Validate inputs (raise ValidationException)
    if not name:
        raise ValidationException("Index name is required")

    validate_dimension(dimension)
    validate_metric(metric)

    # 2. Make API call
    try:
        response = self._http_client.post(
            "/indexes",
            json={
                "name": name,
                "dimension": dimension,
                "metric": metric,
            },
        )
    except HTTPError as e:
        # 3. Transform HTTP errors to SDK exceptions
        if e.status_code == 400:
            raise PineconeApiException(
                f"Invalid index configuration: {e.message}"
            ) from e
        elif e.status_code == 409:
            raise PineconeApiException(
                f"Index '{name}' already exists"
            ) from e
        else:
            raise PineconeApiException(
                f"Failed to create index: {e.message}"
            ) from e
```

## Performance Considerations

### Connection Pooling

```python
# HTTP client should use connection pooling
import httpx

class HTTPClient:
    def __init__(self, api_key: str):
        # Create client with connection pooling
        self._client = httpx.Client(
            timeout=30.0,
            limits=httpx.Limits(
                max_keepalive_connections=20,
                max_connections=100,
            ),
        )
```

### Async Support

When adding async methods:

```python
# Protocol
@runtime_checkable
class IAsyncPineconeClient(Protocol):
    async def list_indexes(self) -> list[str]:
        ...

# Implementation
class AsyncPineconeClient:
    def __init__(self, api_key: str):
        self._client = httpx.AsyncClient()

    async def list_indexes(self) -> list[str]:
        response = await self._client.get("/indexes")
        return [idx["name"] for idx in response.json()["indexes"]]
```

## Documentation Standards

### Docstring Template

```python
def method(
    self,
    required_param: str,
    optional_param: int = 10,
) -> ReturnType:
    """One-line summary of what the method does.

    Longer description providing more context about the method's
    purpose, behavior, and usage. Include important details about
    side effects, state changes, or special considerations.

    Args:
        required_param: Description of the parameter, including
            valid values and constraints.
        optional_param: Description with default value meaning.
            Defaults to 10.

    Returns:
        Description of the return value, including structure
        for complex types.

    Raises:
        ValidationException: When parameters are invalid.
        PineconeApiException: When the API request fails.

    Example:
        >>> client = PineconeClient(api_key="...")
        >>> result = client.method("value")
        >>> print(result)

    Note:
        Additional notes about usage, limitations, or
        version-specific behavior.
    """
    pass
```

## Quick Reference

### Layer Responsibilities

| Layer | Location | Imports | Exports | Contains |
|-------|----------|---------|---------|----------|
| Public | `__init__.py` | Protocols, Implementations | Everything public | Exports only |
| Protocol | `__interface__/` | Other protocols | Protocols | Type definitions |
| Implementation | `_internal/` | Protocols, utils | Nothing (re-exported by Public) | All logic |

### File Locations

| Adding... | Create/Edit | Export In |
|-----------|-------------|-----------|
| New method | Protocol + Implementation | N/A |
| New client | Protocol + Implementation | `__init__.py` |
| New model | `_internal/models/` | `__init__.py` |
| New utility | `_internal/utils/` | (internal only) |
| New exception | `_internal/errors.py` | `__init__.py` |

### Command Quick Reference

```bash
# Setup
pip install -e ".[dev]"
pre-commit install

# Testing
pytest tests/unit              # Fast unit tests
pytest tests/protocols         # Protocol compliance
pytest tests/integration       # Integration tests
pytest --cov=pinecone         # With coverage

# Type checking
mypy pinecone
pyright

# Linting
ruff check .
black --check .

# Format
black .
ruff check --fix .

# Full verification
make check  # Runs all checks
```

## Common Mistakes to Avoid

### ❌ Don't Do This

```python
# 1. Don't import from public API in implementation
from pinecone import PineconeClient  # Wrong!

# 2. Don't forget @runtime_checkable
class IClient(Protocol):  # Missing decorator!
    def method(self) -> str: ...

# 3. Don't add implementation to protocols
class IClient(Protocol):
    def method(self) -> str:
        return "implemented"  # Wrong!

# 4. Don't forget to export in __all__
# In __init__.py
from pinecone._internal.client import PineconeClient
# Missing: __all__ = ["PineconeClient"]

# 5. Don't use bare except
try:
    operation()
except:  # Too broad!
    pass
```

### ✅ Do This Instead

```python
# 1. Use correct imports
from pinecone._internal.client import PineconeClient

# 2. Always use @runtime_checkable
@runtime_checkable
class IClient(Protocol):
    def method(self) -> str: ...

# 3. Protocols only have ...
class IClient(Protocol):
    def method(self) -> str: ...

# 4. Always export in __all__
from pinecone._internal.client import PineconeClient
__all__ = ["PineconeClient"]

# 5. Catch specific exceptions
try:
    operation()
except PineconeApiException as e:
    handle_error(e)
```

## Getting Help

### Documentation Resources

1. **Architecture:** `docs/architecture/`
2. **Conventions:** `docs/conventions/`
3. **Examples:** `examples/` (when available)
4. **Tests:** `tests/` - Show expected usage

### Debugging Tips

```python
# 1. Use pytest with verbose output
pytest -v tests/unit/test_client.py

# 2. Use pytest with print statements (add -s)
pytest -s tests/unit/test_client.py

# 3. Run single test
pytest tests/unit/test_client.py::test_specific_test

# 4. Use pytest debugger
pytest --pdb  # Drops into debugger on failure

# 5. Check type errors in detail
mypy --show-error-codes pinecone
```

## Related Documentation

- [Overview](./overview.md) - System architecture overview
- [API Structure](./api-structure.md) - Three-tier organization
- [Specifications](./specifications.md) - Protocol specifications
- [Verification](./verification.md) - Testing and CI
- [All Conventions](../conventions/README.md) - Complete conventions guide
