Metadata-Version: 2.5
Name: simple-chatbot-imaging
Version: 1.0.0-beta.3
Summary: Standalone image generation package with pluggable providers, fallback orchestration, and a true async API.
Author: Erik
License-Expression: MIT
License-File: LICENSE
Requires-Python: >=3.12
Requires-Dist: httpx>=0.28.0
Requires-Dist: pydantic>=2.13.5
Provides-Extra: all
Requires-Dist: gradio-client>=1.0.0; extra == 'all'
Provides-Extra: fallback
Provides-Extra: huggingface
Requires-Dist: gradio-client>=1.0.0; extra == 'huggingface'
Description-Content-Type: text/markdown

# simple-chatbot-imaging

Standalone image generation package with pluggable providers, opt-in fallback
orchestration, and a true async API.

## Installation

```bash
uv add simple-chatbot-imaging            # core + OpenRouter provider
uv add "simple-chatbot-imaging[huggingface]"  # + Hugging Face provider
uv add "simple-chatbot-imaging[all]"      # everything
```

The `fallback` extra enables fallback orchestration only; install the provider
extras you want in the chain yourself (e.g. `[huggingface]`).

## Quick start

```python
from simple_chatbot_imaging import create_image_generator

gen = create_image_generator("openrouter", media_path="media")
path = await gen.generate_image_async("a neon cyberpunk city at night")
```

Synchronous use:

```python
path = gen.generate_image("a neon cyberpunk city at night")
```

## Provider configuration file

Provider defaults live in [src/simple_chatbot_imaging/providers.json](src/simple_chatbot_imaging/providers.json). You can edit that file to change API endpoints, model IDs, or add your own providers without changing library code.

```json
{
  "openrouter": {
    "kwargs": {
      "base_url": "https://openrouter.ai/api/v1/images",
      "model": "meta/muse-image",
      "api_key_env": "OPENROUTER_API_KEY"
    }
  },
  "custom_provider": {
    "factory": "your_package.providers:YourCustomProvider",
    "kwargs": {
      "base_url": "https://example.com/api/v1/images",
      "api_key_env": "CUSTOM_API_KEY"
    }
  }
}
```

You can also pass a custom config path explicitly:

```python
from simple_chatbot_imaging import create_image_generator

gen = create_image_generator(
    "openrouter",
    config_path="./providers.json",
    base_url="https://example.com/api/v1/images",
)
```

### OpenRouter (`openrouter`)

- Uses the OpenRouter `images/generations` API via `httpx` (native async).
- Requires the `OPENROUTER_API_KEY` environment variable.
- Options: `model` (default `meta/muse-image`), `base_url`, `api_key_env`,
  `request_timeout_seconds`.

### Hugging Face (`huggingface`, extra: `huggingface`)

- Runs a Gradio Space (ZeroGPU) via `gradio-client`.
- Uses `HUGGINGFACE_ACCESS_TOKEN` if set.
- Options: `space_id` (default `hugging-apps/qwen-image-2-1`), `hf_token_env`.

## Fallback (opt-in)

Fallback is never implicit. Build it explicitly with a caller-defined chain:

```python
from simple_chatbot_imaging import FallbackImageGenerator, create_image_generator

providers = [create_image_generator("openrouter"), create_image_generator("huggingface")]
gen = FallbackImageGenerator(generators=providers)
```

Each provider runs its full lifecycle (retries, state, output movement); the
fallback only advances to the next provider on failure.

## API

- `BaseImageGenerator` — abstract provider base; prompt validation, retries with
  backoff, timeouts, state tracking, output movement.
- `generate_image(...)` — synchronous entry point.
- `generate_image_async(...)` — true async entry point (native async providers).
- `ImageGenerationError(retryable=...)` — permanent errors (bad key, no credits,
  403/404) are not retried.
- `ImageGenerationState` — `IDLE`, `GENERATING`, `ERROR`, `SUCCESS`.
- `create_image_generator(provider, **kwargs)` — factory; raises a clear error
  when an optional provider's extra is not installed.
- `register_provider(name, factory)` — plugin hook.

## Environment variables

| Variable | Provider | Required |
|---|---|---|
| `OPENROUTER_API_KEY` | openrouter | yes |
| `HUGGINGFACE_ACCESS_TOKEN` | huggingface | no (recommended for ZeroGPU spaces) |
