# Contributing to Kura

Thank you for your interest in contributing to Kura! This document provides guidelines and information to help you contribute effectively.

## Setting Up the Development Environment

1. Create and activate a virtual environment using uv:
```bash
uv venv
source .venv/bin/activate  # On Windows: .venv\Scripts\activate
```

2. Install the package in development mode with dev dependencies:
```bash
uv pip install -e ".[dev]"
```

## Testing

Kura uses pytest for testing. The current test suite primarily focuses on the meta-clustering functionality.

### Quick Start Testing

To quickly test the full Kura pipeline and UI:

1. **Run the tutorial test** to generate sample data:

   For the procedural API (recommended for understanding the pipeline):
   ```bash
   uv run python scripts/tutorial_procedural_api.py
   ```

   For the class-based API (simpler to use):
   ```bash
   uv run python scripts/tutorial_class_api.py
   ```

   Either tutorial will:
   - Import all Kura modules
   - Load 190 sample conversations from Hugging Face
   - Process and cluster the conversations
   - Generate 29 hierarchical clusters organized into 10 root categories
   - Generate visualization data
   - Save results to `./tutorial_checkpoints/` (procedural) or `./tutorial_checkpoints_class/` (class-based)

   Note: This process may take a few minutes depending on your system and API rate limits.

2. **Test the UI** after running the tutorial:

   For procedural API results:
   ```bash
   kura start-app --dir ./tutorial_checkpoints
   ```

   For class-based API results:
   ```bash
   kura start-app --dir ./tutorial_checkpoints_class
   ```

   This will:
   - Start the backend API and frontend on http://localhost:8000
   - Use the data from the checkpoint directory generated by the tutorial test
   - Display the cluster map, tree view, and detailed cluster information

   Note: The UI may take a moment to fully load as it processes the cluster data.

### Running Unit Tests

```bash
# Run all tests
pytest

# Run a specific test file
pytest tests/test_meta_cluster.py

# Run a specific test
pytest tests/test_meta_cluster.py::test_cluster_label_exact_match
```

### Test Structure

Tests are located in the `tests/` directory. The current tests verify:

- **Exact match functionality**: Tests that `ClusterLabel` correctly validates when there's an exact match between input and candidate clusters.
- **Fuzzy matching**: Tests that similar but not identical strings can be matched using fuzzy matching with an appropriate threshold.
- **Validation errors**: Tests that the system properly rejects inputs that don't match any candidates.

### Writing New Tests

When adding new features or fixing bugs, please include appropriate tests. Follow these guidelines:

1. Create test files with the `test_` prefix
2. Write test functions with descriptive names and docstrings
3. Use pytest fixtures when appropriate
4. Use assertions to verify expected behavior
5. Test both the class-based API and procedural API where applicable

### Example: Testing with the Procedural API

```python
import pytest
import asyncio
from kura.v1 import (
    summarise_conversations,
    generate_base_clusters_from_conversation_summaries,
    CheckpointManager
)
from kura.summarisation import SummaryModel
from kura.cluster import ClusterModel
from kura.types import Conversation

@pytest.mark.asyncio
async def test_procedural_pipeline():
    # Load test conversations
    conversations = Conversation.from_hf_dataset(
        "ivanleomk/synthetic-gemini-conversations",
        split="train[:10]"  # Use only 10 for testing
    )

    # Initialize models
    summary_model = SummaryModel()
    cluster_model = ClusterModel()

    # Run pipeline steps
    summaries = await summarise_conversations(
        conversations,
        model=summary_model,
        checkpoint_manager=None  # No checkpointing for tests
    )

    clusters = await generate_base_clusters_from_conversation_summaries(
        summaries,
        model=cluster_model,
        checkpoint_manager=None
    )

    # Assertions
    assert len(summaries) == 10
    assert len(clusters) > 0
    assert all(cluster.label for cluster in clusters)
```

## Type Checking

Kura uses pyright for type checking:

```bash
pyright
```

## Documentation

To work on documentation:

1. Install documentation dependencies:
```bash
uv pip install -e ".[docs]"
```

2. Serve documentation locally:
```bash
mkdocs serve
```

## Code Style

- Follow PEP 8 guidelines for Python code
- Use type hints for all function parameters and return values
- Write docstrings for all public classes and functions

## Dependency Management

Kura is designed to be a lightweight library with minimal required dependencies. We follow these principles:

### Core Philosophy
- Keep the core library as light as possible
- Make heavy dependencies optional whenever feasible
- Use dynamic imports for optional functionality
- Provide clear error messages when optional dependencies are missing

### Adding New Dependencies

When considering adding a new dependency:

1. **Evaluate necessity**: Is this dependency absolutely required for core functionality?
2. **Consider alternatives**: Can we achieve the same goal with existing dependencies or standard library?
3. **Make it optional**: If the dependency is only needed for specific features, make it optional

### Optional Dependencies

Kura uses optional dependency groups for features that aren't essential to core functionality:

- `visualization`: Rich terminal output (`rich`)
- `parquet`: Parquet checkpoint format (`pyarrow`)
- `embeddings`: Local embedding models (`sentence-transformers`)

To install with optional dependencies:
```bash
# Install with visualization support
uv pip install -e ".[visualization]"

# Install with all optional dependencies
uv pip install -e ".[visualization,parquet,embeddings]"
```

### Implementation Pattern

When using optional dependencies, follow this type-safe pattern:

```python
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    # Import for type checking - ensures proper types during static analysis
    from some_optional_package import SomeClass
else:
    # Runtime import handling - gracefully handle missing dependencies
    try:
        from some_optional_package import SomeClass
        OPTIONAL_AVAILABLE = True
    except ImportError:
        SomeClass = None  # type: ignore
        OPTIONAL_AVAILABLE = False

# In your code, check availability before use
if not OPTIONAL_AVAILABLE:
    raise ImportError(
        "Optional package 'some_optional_package' is required for this feature. "
        "Install it with: uv pip install -e '.[feature_name]'"
    )
```

This pattern ensures:
- Type checkers see the correct types during static analysis
- Runtime gracefully handles missing optional dependencies
- Clear error messages guide users to install missing dependencies
- No type errors when using the imported classes/functions

## Pull Request Process

1. Fork the repository
2. Create a feature branch
3. Add tests for your changes
4. Ensure all tests pass
5. Update documentation as needed
6. Submit a pull request

## UI Development

If you're working on the UI:

```bash
# Navigate to UI directory
cd ui

# Install dependencies
npm install

# Start development server
npm run dev

# Build for production
npm run build

# Lint code
npm run lint
```
