Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
178 changes: 143 additions & 35 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,66 +5,174 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
## Development Commands

### Setup

- `make venv` - Setup development environment and install git hooks
- `make sync` - Sync dependencies with uv lock file
- `make update` - Update and lock dependencies
- `source .venv/bin/activate && make sync` - Sync dependencies with uv lock file
- `source .venv/bin/activate && make update` - Update and lock dependencies

### Code Quality
- `source .venv/bin/activate && make format` - Format code with Ruff (line length 100)

- `source .venv/bin/activate && make format` - Format code with Ruff
- `source .venv/bin/activate && make lint` - Run linters (Ruff + Bandit + Pyright strict mode)
- `source .venv/bin/activate && make test` - Run pytest with coverage
- `source .venv/bin/activate && pytest tests/test_specific.py` - Run single test file
- `source .venv/bin/activate && pytest tests/test_specific.py::test_function` - Run specific test

### Package Management
- `uv add <package>` - Add dependency
- `uv remove <package>` - Remove dependency

## Architecture Overview

**draive** is a Python framework for LLM applications built on haiway for state management.
Draive is a Python framework (3.12+) for LLM applications built on the **Haiway** framework, leveraging its state, dependency, and task-management facilities. It emphasizes:

1. **Immutable State Management**: Type-safe, immutable data structures with validation
2. **Context-based Dependency Injection**: Safe state propagation in concurrent environments
3. **Functional Approach**: Pure functions over objects with methods
4. **Structured Concurrency**: Automatic task management and resource cleanup
5. **Haiway Proxy**: draive builds on top of haiway and exports its symbols

### Core Components

- **Integrations**: Various AI service provider integrations. Including Anthropic, AWS Bedrock, Cohere, Gemini, Mistral, Ollama, OpenAI, VLLM
- **Functionalities**: High- and low-level interfaces and implementations of application building blocks, including embedding, **LMM**, evaluation, generation, conversation, guardrails, instructions, resources, prompts, tools, **MCP** support
- **Parameters**: Immutable data structures and **parameterized** functions with **JSON Schema** generation, validation, and generic-type support
- **Utils**: Application utilities, common types and interfaces
- **Helpers**: Implementations of selected functionalities combining other components

### Core Concepts
### Code Style

**State Management**: Uses haiway's immutable State objects and ctx for dependency injection. All configuration and context flows through state scoping.
- Use absolute imports from `draive` package
- Put exported symbols into `__init__.py`
- Follow Ruff import ordering (standard library, third party, local)
- Use Python 3.12+ type features (type unions with `|`, generic syntax)
- Use base and abstract types like `Sequence` or `Iterable` instead of concrete
- Use custom exceptions for specific errors

**Provider Abstraction**: Unified `LMM` interface across providers (OpenAI, Anthropic, Gemini, Mistral, Ollama, Bedrock). Each provider implements standardized `LMMContext`, `LMMInput`, `LMMCompletion` types.
### Testing Guidelines

**Multimodal Content**: `MultimodalContent` handles text, images, and media uniformly. Content elements are composable and convertible across formats.
- Uses pytest with async support. Tests are in `tests/` directory.
- Mock dependencies within scope using stubbed functionality state.
Comment on lines +50 to +51
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Unify imperative mood in testing bullets

Other bullets start with “Use”; this one starts with “Uses”. Align wording and add the article “the” for clarity.

-- Uses pytest with async support. Tests are in `tests/` directory.
+- Use pytest with async support. The tests are in the `tests/` directory.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- Uses pytest with async support. Tests are in `tests/` directory.
- Mock dependencies within scope using stubbed functionality state.
- Use pytest with async support. The tests are in the `tests/` directory.
- Mock dependencies within scope using stubbed functionality state.
🧰 Tools
🪛 LanguageTool

[uncategorized] ~50-~50: You might be missing the article “the” here.
Context: ...pytest with async support. Tests are in tests/ directory. - Mock dependencies w...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)

🤖 Prompt for AI Agents
In CLAUDE.md around lines 50 to 51, the bullet point describing testing uses
"Uses" instead of the imperative "Use" like the other bullets and is missing the
article "the". Change "Uses pytest with async support" to "Use the pytest with
async support" to unify the imperative mood and improve clarity.


### Key Components
## Examples

**Stages**: Core processing pipeline units that transform `(LMMContext, MultimodalContent)`. Support composition, looping, caching, retry. Examples: `Stage.completion()`, `Stage.sequence()`, `Stage.loop()`.
### Immutability Rules

**Tools**: Function calling via `@tool` decorator. `Toolbox` manages collections. Automatic schema generation and validation.
**ALWAYS use these types for collections in State, Config and DataModel classes:**
- Use `Sequence[T]` instead of `list[T]` (becomes tuple)
- Use `Mapping[K,V]` instead of `dict[K,V]` (becomes immutable)
- Use `Set[T]` instead of `set[T]` (becomes frozenset)

**Agents**: Stateful conversation entities with memory. `AgentWorkflow` for multi-agent systems.
```python
from typing import Sequence, Mapping, Set
from haiway import State

**Generation APIs**: High-level interfaces like `TextGeneration`, `ModelGeneration` that handle complexity while allowing customization.
class UserData(State):
roles: Sequence[str] # Will be tuple
metadata: Mapping[str, Any] # Will be immutable
tags: Set[str] # Will be frozenset

### Component Flow
```
Generation APIs → Stages → LMM → Provider Implementation

### State Definition Patterns

```python
from typing import Protocol, runtime_checkable
from uuid import UUID, uuid4
from haiway import State
from draive import Field

# Basic data structure
class UserData(State):
id: UUID
name: str
email: str | None = None

# Generic state classes
class Container[Element](State):
items: Sequence[Element]
metadata: Mapping[str, Any]

# Special kind of State supporting loading from external source
class UserConfig(Config):
value: str

# Serializable counterpart of State
class UserModel(DataModel):
# Field allows customizing DataModel fields with various options
id: UUID = Field(default_factory=uuid4, aliased="user_id")
name: str

# Function protocol
@runtime_checkable
class UserFetching(Protocol):
async def __call__(self, id: str) -> UserData: ...

# Functionality state pattern used for dependency injection
class UserService(State):
# Function implementations
user_fetching: UserFetching

# Class method interface to access functions within context
@classmethod
async def fetch_user(cls, *, id: str) -> UserData:
return await ctx.state(cls).user_fetching(id)
```

### Patterns
### State Updates

1. **Immutable State**: All state objects are frozen
2. **Context Scoping**: Configuration flows through execution contexts
3. **Async First**: Fully asynchronous throughout
4. **Composability**: Small focused components combine into complex workflows
5. **Type Safety**: Heavy use of generics and protocols
```python
# Immutable updates through copy, same for State, Config and DataModel
user: UserData = ...
updated_user: UserData = user.updated(name="Updated")
```

### Entry Points
### Resource Management

```python
from contextlib import asynccontextmanager
from haiway import ctx, State

class ResourceAccess(State):
accessing: ResourceAccessing

@classmethod
def access(cls) -> ResourceData:
return ctx.state(cls).accessing()

@asynccontextmanager
async def create_resource_disposable():
# Create a disposable resource
resource: ResourceHandle = await open_resource()
try:
# Yield the state that will be made available in the context
yield ResourceState(accessing=resource.access)

finally:
# Cleanup happens automatically when context exits
await resource.close()

# Resources are automatically cleaned up and their state included in context
async with ctx.scope(
"work",
disposables=(create_resource_disposable(),)
):
# ResourceAccess is now available in the context
resource_data: ResourceData = ResourceAccess.access()
# Cleanup happens automatically here
```

- Simple: `TextGeneration.generate()`, `ModelGeneration.generate()`
- Tools: `@tool` decorator with `tools` parameter
- Complex: `Stage` composition and `Agent` systems
- Setup: Context managers with provider configs
## Testing Patterns

### Testing
```python
import pytest
from haiway import ctx

Uses pytest with async support. Tests are in `tests/` directory. Key test patterns:
- Mock LMM responses for unit tests
- Use actual providers for integration tests
- Test both sync and async code paths
@pytest.mark.asyncio
async def test_functionality():
# Set up test context
async with ctx.scope("test", TestState(value="test")):
result = await some_function()
assert result == expected_result

@pytest.mark.asyncio
async def test_with_mock():
async with ctx.scope("test", ServiceState(fetching=mock_fetching)):
# Test with mocked service
```