Metadata-Version: 2.4
Name: kestrel-feature-visual
Version: 0.4.0
Summary: Visual identity for Kestrel Sovereign agents — avatar generation, selfies, and LoRA training for character consistency
Project-URL: Homepage, https://kestrelsovereign.com
Project-URL: Source, https://github.com/KestrelSovereignAI/kestrel-feature-visual
Project-URL: Issues, https://github.com/KestrelSovereignAI/kestrel-feature-visual/issues
Author: UncleSaurus
Maintainer: UncleSaurus
License-Expression: Apache-2.0
License-File: LICENSE
Keywords: agents,ai,avatar,image-generation,kestrel,lora,replicate
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Operating System :: MacOS
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Multimedia :: Graphics
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Requires-Python: <3.15,>=3.11
Requires-Dist: httpx>=0.27.0
Requires-Dist: kestrel-sovereign-sdk<1,>=0.25.0
Requires-Dist: replicate>=1.0.4
Provides-Extra: test
Requires-Dist: kestrel-sovereign<1,>=0.46.0; extra == 'test'
Requires-Dist: pytest-asyncio>=1.1.0; extra == 'test'
Requires-Dist: pytest>=8.0.0; extra == 'test'
Description-Content-Type: text/markdown

# kestrel-feature-visual

Visual identity for Kestrel Sovereign agents — avatar generation, selfies, and LoRA training for character consistency. Uses Replicate for image generation with optional LoRA training for consistent visual identity across generated images.

## Installation

```bash
uv pip install kestrel-feature-visual
```

The feature is auto-discovered by Kestrel Sovereign via the `kestrel_sovereign.features` entry point — install it alongside `kestrel-sovereign` and the `VisualIdentityFeature` registers itself at startup.

## Configuration

| Variable | Description |
|----------|-------------|
| `REPLICATE_API_TOKEN` | Replicate API token for image generation (required for avatar/selfie tools) |

## Tools provided

- `generate_avatar` — Create a portrait avatar from a description
- `generate_selfie` — Generate a selfie in various scenes (casual, portrait, glamour, flirty, cozy, adventure, mysterious, romantic, playful, dreamy, confident)
- `train_lora` — Kick off LoRA training for character consistency (requires `kestrel-feature-lora` or another training provider installed alongside)

## Canonical LoRA selfie specification

Queue-backed providers must use `resolve_lora_selfie_spec()` when a selfie is
quoted and `bind_lora_selfie_spec()` immediately before dispatch. Both paths
produce the same content-free `spec_sha256` over the final prompt hash,
generation parameters, and immutable promoted-LoRA identity. The plaintext
prompt remains transient and is never part of the persisted evidence object.

`scene` and `style` are **caller-owned**. This package normalizes them (whitespace
collapsed, lowercased) and bounds their length, but never rejects one for failing to
appear in its own tables — frinz forwards both unvalidated from an HTTP body and from
LLM tool arguments, and deliberately supports free-form prose scenes such as
`stargazing at night with aurora borealis`.

`SELFIE_SCENE_PROMPTS` and the style prefixes therefore govern only the descriptive
prompt *text*: an unrecognized scene contributes no description rather than silently
rendering as `casual`, and an unrecognized style adds no prefix. The scene reads the
same on the resolved prompt, the generation config, the returned result, and
`spec_sha256`, so two different scenes can never share a digest.

An absent `scene` or `style` — `None`, `""`, or whitespace — means "use the default"
(`casual` / `photorealistic`), not "fail". frinz forwards `style` completely raw from
both an unvalidated body field and an LLM tool argument.

On the no-LoRA reference route, when there is neither a custom prompt nor a known
scene description this package sends **no** prompt override, so the catalog worker's
own scene template is used instead of a subjectless stub. Otherwise the route uses
`prompt_without_trigger()`, which derives the trigger-free text from the resolved
prompt itself — rebuilding it in the caller silently dropped the style prefix.

The public `ResolvedSelfiePrompt` object carries the exact values that must be
sent to the image worker, including seed, dimensions, inference steps, and
guidance scale. Providers must reject a reconstructed digest that differs from
the accepted quote.

The object re-validates its own invariants on construction, not only inside
`resolve_selfie_prompt()`, because downstream consumers treat the type itself
as the trust boundary. It therefore carries the plaintext `trigger_word`
alongside `trigger_word_sha256` and verifies that the digest matches the
trigger and that `prompt` binds that trigger **exactly once** as a whole token.
A directly constructed instance attesting a trigger the prompt never binds — or
binds twice — is rejected, so a valid `spec_sha256` can never describe a prompt
that does not match it. Carrying the trigger in plaintext discloses nothing
further: it is already contained verbatim in `prompt`.

## Dependencies

- `kestrel-sovereign-sdk>=0.25.0,<1` — base `Feature`, `tool`, and `ToolCategory` interfaces
- `replicate>=1.0.4` — Replicate API client
- `httpx>=0.27.0` — HTTP transport

## Development

```bash
uv pip install -e '.[test]'
uv run pytest
```

## License

Apache-2.0
