Metadata-Version: 2.4
Name: wayfinder-agents
Version: 0.1.0
Summary: LangGraph-orchestrated AI agent framework for multi-domain entity management
Project-URL: Homepage, https://github.com/jspenc72/wayfinder-agents
Project-URL: Repository, https://github.com/jspenc72/wayfinder-agents
Project-URL: Issues, https://github.com/jspenc72/wayfinder-agents/issues
Project-URL: Changelog, https://github.com/jspenc72/wayfinder-agents/blob/main/CHANGELOG.md
Author: Jesse Spencer
License: Wayfinder Community License 1.0
License-File: LICENSE
Keywords: ai-agents,fastapi,grok,langchain,langgraph,llm,multi-agent,openai,workflow
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: Other/Proprietary License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
Requires-Python: >=3.11
Requires-Dist: fastapi>=0.115
Requires-Dist: httpx>=0.27
Requires-Dist: langchain-openai>=0.2
Requires-Dist: langchain>=0.3
Requires-Dist: langgraph>=0.2
Requires-Dist: langsmith>=0.1
Requires-Dist: pydantic-settings>=2.5
Requires-Dist: pydantic>=2.9
Requires-Dist: structlog>=24.4
Requires-Dist: tenacity>=9.0
Requires-Dist: uvicorn[standard]>=0.30
Provides-Extra: all
Requires-Dist: geopy>=2.4; extra == 'all'
Requires-Dist: langgraph-checkpoint-postgres>=2.0; extra == 'all'
Requires-Dist: motor>=3.6; extra == 'all'
Requires-Dist: pymongo>=4.9; extra == 'all'
Requires-Dist: redis>=5.2; extra == 'all'
Requires-Dist: wikipedia-api>=0.7; extra == 'all'
Provides-Extra: dev
Requires-Dist: build>=1.2; extra == 'dev'
Requires-Dist: mypy>=1.13; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
Requires-Dist: pytest>=8.3; extra == 'dev'
Requires-Dist: ruff>=0.8; extra == 'dev'
Requires-Dist: twine>=5.0; extra == 'dev'
Provides-Extra: mongodb
Requires-Dist: motor>=3.6; extra == 'mongodb'
Requires-Dist: pymongo>=4.9; extra == 'mongodb'
Provides-Extra: postgres
Requires-Dist: langgraph-checkpoint-postgres>=2.0; extra == 'postgres'
Provides-Extra: redis
Requires-Dist: redis>=5.2; extra == 'redis'
Provides-Extra: research
Requires-Dist: geopy>=2.4; extra == 'research'
Requires-Dist: wikipedia-api>=0.7; extra == 'research'
Description-Content-Type: text/markdown

# wayfinder-agents

> **LangGraph-orchestrated AI agent framework for multi-domain entity management.**

`wayfinder-agents` is a generic, production-grade Python framework for building LLM-powered workflows that create, modify, validate, and publish any structured entity type via a REST API. Built on LangGraph and LangChain.

---

## Quick Start

```bash
pip install wayfinder-agents
```

```python
from wayfinder import WayfinderApp
from wayfinder.decorators import domain, Field

@domain("product", api="/api/products")
class Product:
    """An e-commerce product."""
    name: str = Field(required=True, description="Product name")
    description: str = Field(required=True, description="Full description")
    price_cents: int = Field(type="int", required=True, description="Price in cents")
    category: str = Field(enum=["electronics", "clothing", "books"])

app = WayfinderApp(
    name="ShopBot",
    api_base_url="http://localhost:3000",
)
app.discover_domains("myapp.domains")
app.serve(port=8001)
```

---

## What It Does

The framework provides a 16-node LangGraph workflow out of the box:

| Node | Role |
|------|------|
| `context_resolver` | Detects topic changes between conversation turns |
| `coordinator` | Routes requests to the right specialist (LLM-based + deterministic fast-paths) |
| `confirm` | Human-in-the-loop approval gate before write actions |
| `plan` | Decomposes multi-step requests into sub-tasks |
| `research` | Fetches external data (Wikipedia, geocoding, images) |
| `compose` | Creates a new entity draft from research notes |
| `modify` | Edits existing entities |
| `review` | Quality validation before publishing |
| `publish` | Saves via REST API |
| `propose` | Suggests changes without immediately modifying |
| `remediate` | Fixes API validation errors and retries |
| `delete` / `duplicate` | Entity lifecycle operations |
| `query` | Read-only Q&A about existing entities |
| `summarize` | Formats the final response |

Each node can be overridden per-domain.

---

## Domain Registration

### Using the `@domain` decorator

```python
from wayfinder.decorators import domain, subdoc, Field, Subdoc, Rel

@subdoc
class Address:
    street: str = Field(required=True)
    city: str = Field(required=True)

@domain("venue", api="/api/venues", progressive_save="rooms:5")
class Venue:
    """A physical event venue."""
    name: str = Field(required=True)
    address: Address = Subdoc(Address)
    capacity: int = Field(type="int")
    events: object = Rel("event", has_many=True, foreign_key="venue_id")
```

### Using a Pydantic model

```python
from pydantic import BaseModel
from wayfinder.adapters import domain_from_model

class Article(BaseModel):
    """A blog article."""
    title: str
    body: str
    tags: list[str] = []

domain_from_model(Article, api="/api/articles")
```

---

## Domain Customisation

### Override a node

```python
from wayfinder.decorators import node

@node("modify", domain="product")
async def my_modifier(state):
    # custom modification logic
    return {"draft": {...}, "next_step": "review"}
```

### Lifecycle events

```python
from wayfinder.decorators import on

@on("before_publish", domain="product")
async def validate_images(state):
    if not state.draft.get("images"):
        state.draft["images"] = []
    return state
```

### Custom validator

```python
from wayfinder.domain import DomainValidator

class ProductValidator(DomainValidator):
    def validate(self, draft: dict, errors: list[str]) -> list[str]:
        if draft.get("price_cents", 0) <= 0:
            errors.append("price_cents must be greater than 0")
        return errors
```

---

## API Adapters

Out of the box the framework ships an adapter for **Express/Mongoose** APIs.
Implement `APIAdapter` for other backends:

```python
from wayfinder.api_adapter import APIAdapter, set_adapter

class DjangoRestAdapter(APIAdapter):
    def parse_response(self, json_body, entity_name): ...
    def parse_list_response(self, json_body, plural_name): ...
    def build_pagination_params(self, page, limit): ...
    async def discover_schema(self, client, api_base_url, model_name): ...

set_adapter(DjangoRestAdapter())
```

---

## Configuration

All settings use the `WAYFINDER_` env prefix with `__` as the nested delimiter:

```bash
WAYFINDER__LLM__DEFAULT_PROVIDER=openai
WAYFINDER__LLM__OPENAI_MODEL=gpt-4.1
WAYFINDER__API__BASE_URL=http://myapi:3000/api
WAYFINDER__API__SERVICE_TOKEN=my-internal-token
WAYFINDER__CHECKPOINT__BACKEND=redis
WAYFINDER__CHECKPOINT__REDIS_URL=redis://redis:6379/0
WAYFINDER__OBSERVABILITY__LANGSMITH_ENABLED=true
WAYFINDER__OBSERVABILITY__LANGSMITH_API_KEY=ls-...
```

---

## Checkpointing Backends

```bash
# PostgreSQL
pip install "wayfinder-agents[postgres]"
WAYFINDER__CHECKPOINT__BACKEND=postgres
WAYFINDER__CHECKPOINT__CONNECTION_STRING=postgresql+asyncpg://user:pass@host/db

# Redis
pip install "wayfinder-agents[redis]"
WAYFINDER__CHECKPOINT__BACKEND=redis
WAYFINDER__CHECKPOINT__REDIS_URL=redis://localhost:6379/0

# MongoDB
pip install "wayfinder-agents[mongodb]"
WAYFINDER__CHECKPOINT__BACKEND=mongodb
WAYFINDER__CHECKPOINT__CONNECTION_STRING=mongodb://localhost:27017
```

---

## Publishing to PyPI

```bash
# Build
python -m build

# Test on TestPyPI first
twine upload --repository testpypi dist/*

# Publish
twine upload dist/*
```

The GitHub Actions workflow at `.github/workflows/publish.yml` automates this on tagged releases.

---

## License

`wayfinder-agents` is available under the `Wayfinder Community License 1.0`.

Free use is permitted for:

- individuals using the project for personal, educational, research, or
    non-commercial work;
- nonprofits and educational institutions for non-commercial internal use; and
- organizations with less than USD 100,000 in annual gross revenue.

A separate commercial license is required for:

- organizations at or above USD 100,000 in annual gross revenue;
- paid products, paid client work, and internal commercial production use; and
- hosted or managed-service offerings based on the software.

See [LICENSE](LICENSE), [licensing strategy](docs/licensing/strategy.md), and
[commercial terms summary](docs/licensing/COMMERCIAL-TERMS-DRAFT.md).
