API Reference

# Python API Reference

This page documents Kedi's Python API for programmatic usage.

---

## KediRuntime

The main class for executing Kedi programs from Python.

### Basic Usage

```python
from kedi.core import KediRuntime

# Create runtime with default adapter
runtime = KediRuntime()

# Load and run a program
runtime.load_file("my_program.kedi")
result = runtime.run_main()
```

### Constructor

```python
KediRuntime(
    adapter: AgentAdapter | None = None,
    codegen_agent: str = "pydantic_ai",
    codegen_model: str = "openai:gpt-4o",
    codegen_retries: int = 5,
    cache: bool = True
)
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `adapter` | `AgentAdapter \| None` | LLM adapter instance. Uses PydanticAdapter if None. |
| `codegen_agent` | `str` | Agent for AI-generated procedures. |
| `codegen_model` | `str` | Model for code generation. |
| `codegen_retries` | `int` | Retry attempts for code generation. |
| `cache` | `bool` | Enable/disable caching for generated procedures. |

### Methods

#### `load_file(path: str) -> None`

Load a Kedi program from a file.

```python
runtime.load_file("program.kedi")
```

#### `load_source(source: str) -> None`

Load a Kedi program from a string.

```python
source = """
@greet(name):
    Hello, <name>! Welcome to [greeting].
    = `greeting`
"""
runtime.load_source(source)
```

#### `run_main() -> Any`

Execute the program and return the final result.

```python
result = runtime.run_main()
print(result)
```

#### `run_tests() -> TestResults`

Run all `@test:` blocks in the program.

```python
results = runtime.run_tests()
print(f"Passed: {results.passed}, Failed: {results.failed}")
```

#### `run_evals() -> EvalResults`

Run all `@eval:` blocks in the program.

```python
results = runtime.run_evals()
for metric in results.metrics:
    print(f"{metric.name}: {metric.score}")
```

#### `call_procedure(name: str, **kwargs) -> Any`

Call a specific procedure by name.

```python
capital = runtime.call_procedure("get_capital", country="France")
```

---

## AgentAdapter Protocol

All LLM adapters must implement this protocol.

```python
from typing import Any, Protocol, TypeVar

T = TypeVar("T")

class AgentAdapter(Protocol[T]):
    async def produce(
        self,
        template: str,
        output_schema: dict[str, type],
        **kwargs: Any
    ) -> T:
        """Generate structured output from an LLM."""
        ...

    def produce_sync(
        self,
        template: str,
        output_schema: dict[str, type],
        **kwargs: Any
    ) -> T:
        """Synchronous version of produce()."""
        ...
```

### Using Built-in Adapters

```python
from kedi.agent_adapter import PydanticAdapter, DSPyAdapter

# PydanticAI adapter
pydantic_adapter = PydanticAdapter(model="openai:gpt-4o")

# DSPy adapter
dspy_adapter = DSPyAdapter(model="openai:gpt-4o")

# Use with runtime
runtime = KediRuntime(adapter=pydantic_adapter)
```

---

## Custom Types

Define and use custom Kedi types from Python.

### Defining Types

```python
from pydantic import BaseModel

class Person(BaseModel):
    name: str
    age: int
    email: str

# Register with runtime
runtime.register_type("Person", Person)
```

### Using in Kedi

```python
# The Person type is now available
@create_user(name, age: int, email) -> Person:
    Create a new user [person: Person] with name <name>, age <age>, and email <email>.
    = `person`
```

---

## Complete Example

```python
from kedi.core import KediRuntime
from kedi.agent_adapter import PydanticAdapter

# Create adapter with specific model
adapter = PydanticAdapter(model="openai:gpt-4o")

# Create runtime
runtime = KediRuntime(
    adapter=adapter,
    codegen_model="openai:gpt-4o",
    codegen_retries=10
)

# Load program
runtime.load_file("research_assistant.kedi")

# Run main program
report = runtime.run_main()
print(report)

# Or call specific procedure
summary = runtime.call_procedure("summarize", text="Long text here...")
print(summary)

# Run tests
test_results = runtime.run_tests()
if test_results.failed > 0:
    print(f"⚠️ {test_results.failed} tests failed")
    for failure in test_results.failures:
        print(f"  - {failure.name}: {failure.message}")
```

---

## Error Handling

```python
from kedi.core import KediRuntime, KediError, KediSyntaxError, KediRuntimeError

try:
    runtime = KediRuntime()
    runtime.load_file("program.kedi")
    result = runtime.run_main()
except KediSyntaxError as e:
    print(f"Syntax error at line {e.line}: {e.message}")
except KediRuntimeError as e:
    print(f"Runtime error: {e.message}")
except KediError as e:
    print(f"Kedi error: {e}")
```

---

## Async Usage

For async applications, use the async methods directly:

```python
import asyncio
from kedi.core import KediRuntime
from kedi.agent_adapter import PydanticAdapter

async def main():
    adapter = PydanticAdapter(model="openai:gpt-4o")
    runtime = KediRuntime(adapter=adapter)
    runtime.load_file("program.kedi")

    # Async execution
    result = await runtime.run_main_async()
    print(result)

asyncio.run(main())
```

---

## See Also

- [CLI Reference](cli.md) — Command-line usage
- [Agent Adapters](../adapters/index.md) — Adapter documentation
- [Custom Adapters](../adapters/custom.md) — Building custom adapters
- [Python Interop](../concepts/python-interop.md) — Using Python in Kedi