Aphrodite routes Discord component custom IDs and HTTP dispatch calls through aphrodite.router.DispatchRouter.
Custom ID format
The router expects this form:
system:v1:action:arg1:arg2
Parsing rules from aphrodite/router.py:
systemselects a registered adapter.v1is the supported custom-id version.actionselects behavior inside the adapter.- Remaining colon-separated fields are passed as
payload: list[str]. - The adapter also receives a
context: dict[str, Any]built by the caller.
A successful dispatch response includes ok, system, version, action, payload, and result. Unknown systems, unsupported versions, parsing errors, and adapter exceptions return structured ok: false responses instead of escaping through the HTTP boundary.
Bundled adapters
aphrodite.app.build_router() registers the configured modules from
APHRODITE_MODULES. The default public set is
image_gen,skillopt,acp_relay.
| Adapter | Purpose | Standalone behavior |
|---|---|---|
skillopt | Manages SkillOpt runs, diffs, evaluations, review HTML, bundles, and candidate import/export artifacts. | Self-contained aside from configured local storage and optional train commands. |
image_gen | Provides a dispatch status action and the /image/generate HTTP route for Codex-backed image generation. | Live HTTP generation requires Hermes Codex/OpenAI OAuth from the private agent stack; no plain API-key environment path exists. |
acp_relay | Bridges Aphrodite to an external ACP agent runtime and exposes both dispatch and /acp/* HTTP surfaces. | Requires a working external Hermes/ACP runtime for real turns; fake transports can test the Aphrodite-owned pieces. |
Adapters that bridge private Hermes plugins belong in the operator overlay, not
the public module set. Custom modules can still be registered by listing their
names in APHRODITE_MODULES; unknown names are wired to a placeholder handler
so startup remains deterministic.
Adding an adapter
- Create or update a module under
aphrodite/modules/. - Expose a callable with this signature:
def handle(action: str, payload: list[str], context: dict[str, Any]) -> dict[str, Any]:
...
- Return dictionaries with stable fields. Prefer
ok: trueorok: falseplus anerrorstring for failures. - Register the module in
aphrodite/app.pyinsidebuild_router():
elif system == "my_adapter":
from .modules.my_adapter import handle as handle_my_adapter
router.register(system, handle_my_adapter)
- Add the adapter name to
APHRODITE_MODULESwhen it should be active.
Keep adapter boundaries narrow: Aphrodite should call public plugin/runtime APIs and should not patch the external runtime core.
Image generation auth
aphrodite/modules/image_gen.py does not read OPENAI_API_KEY or any other
plain API-key environment variable for the HTTP route. /image/generate calls
_build_codex_client(), which reads a Codex/OpenAI OAuth token through the
private Hermes agent.auxiliary_client stack and constructs:
openai.OpenAI(
api_key=token,
base_url="https://chatgpt.com/backend-api/codex",
...
)
If that private OAuth token is unavailable, the route returns an
auth_required error and performs no generation. The standalone integration
path is programmatic: import generate_image and pass a client you own, for
example generate_image(payload, client=<openai.OpenAI instance>).
Model selection can still be influenced with APHRODITE_IMAGE_GEN_MODEL or
OPENAI_IMAGE_MODEL, but those variables select quality/model only; they do
not authenticate the OpenAI/Codex client.
ACP relay
aphrodite/modules/acp_relay.py bridges Aphrodite to an external ACP agent runtime.
Public surfaces:
handle(action, payload, context)forDispatchRouterregistration. Actionshealth,readiness, andstatusreport relay readiness. Other actions currently return a handled-false response that points callers to the/acpHTTP routes.router = APIRouter(prefix="/acp", tags=["acp_relay"]), included bycreate_app(), with conversation and turn endpoints.AcpRelay,ConversationStore, and configuration helpers used by the HTTP router and tests.
Runtime behavior:
- Conversation metadata and turns are stored in SQLite owned by Aphrodite.
- The real transport spawns
hermes -p <profile> acpand drives one ACP turn. - The
acpPython client is imported lazily insideacp_transport; environments that do not install the optional client can still import the module and use tests/fake transports. - Default profile, provider, model, binary, working directory, database path, and turn timeout are configurable with
APHRODITE_ACP_*environment variables.
Default relay settings in the current code are profile forge, provider openrouter, model openai/gpt-4o-mini, and timeout 240.0 seconds.