Compare commits

...

1 Commits

Author SHA1 Message Date
vh bcdcd71090 feat(provider): runnable affect-server entrypoint + ratatoskr-provider script
Stand-up glue for the live affect negotiation smoke vs Worldtree v0.35.2.

- ratatoskr.provider.serve: build_app_from_env (tested env->app seam) + main
  (uvicorn shell). Config from env: RATATOSKR_HEIMDALL_KEY (required),
  RATATOSKR_AFFECT_DB, RATATOSKR_CONSUMER_ID, RATATOSKR_PROVIDER_HOST/PORT.
- console-script ratatoskr-provider = ratatoskr.provider.serve:main.
- Heimdall key utf-8-encoded into HS256 key_bytes; revisit if Worldtree signs
  with base64/raw bytes (confirm format at wire-up).
2026-06-14 16:28:29 -07:00
4 changed files with 68 additions and 4 deletions
+4 -3
View File
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project]
name = "ratatoskr"
version = "0.17.1"
version = "0.17.2"
description = "Worldtree Conversation API debug TUI — multi-pane observability dashboard"
readme = "README.md"
requires-python = ">=3.12"
@@ -45,8 +45,9 @@ dev = [
]
[project.scripts]
ratatoskr = "ratatoskr.cli:main"
ratatoskr-web = "ratatoskr.web.entrypoint:main"
ratatoskr = "ratatoskr.cli:main"
ratatoskr-web = "ratatoskr.web.entrypoint:main"
ratatoskr-provider = "ratatoskr.provider.serve:main"
[project.urls]
Repository = "https://gitea.phasefinal.com/vh/ratatoskr"
+40
View File
@@ -0,0 +1,40 @@
"""Runnable entrypoint: serve the affect provider as an ASGI app.
For the live negotiation smoke against a Worldtree instance. Config from env:
- RATATOSKR_HEIMDALL_KEY (required): HS256 shared key for the consumer, utf-8.
- RATATOSKR_AFFECT_DB (default "affect.db"): SQLite path; ":memory:" = ephemeral.
- RATATOSKR_CONSUMER_ID (default "ratatoskr").
- RATATOSKR_PROVIDER_HOST (default "0.0.0.0"), RATATOSKR_PROVIDER_PORT (default 8390).
"""
from __future__ import annotations
import os
from collections.abc import Mapping
from ratatoskr.provider.affect_store import build_affect_provider_app, open_affect_store
def build_app_from_env(env: Mapping[str, str] | None = None):
"""Build the affect ASGI app from environment config (testable seam)."""
env = os.environ if env is None else env
key = env.get("RATATOSKR_HEIMDALL_KEY")
if not key:
raise RuntimeError(
"RATATOSKR_HEIMDALL_KEY is required to serve the affect provider"
)
store = open_affect_store(env.get("RATATOSKR_AFFECT_DB", "affect.db"))
return build_affect_provider_app(
store,
heimdall_key=key.encode(),
consumer_id=env.get("RATATOSKR_CONSUMER_ID", "ratatoskr"),
)
def main() -> None:
import uvicorn
uvicorn.run(
build_app_from_env(),
host=os.environ.get("RATATOSKR_PROVIDER_HOST", "0.0.0.0"),
port=int(os.environ.get("RATATOSKR_PROVIDER_PORT", "8390")),
)
+23
View File
@@ -0,0 +1,23 @@
"""Tests for the affect-provider serve entrypoint (ratatoskr.provider.serve).
Only the env -> app seam is unit-tested; uvicorn.run is the untestable shell.
"""
from __future__ import annotations
import pytest
from ratatoskr.provider.serve import build_app_from_env
def test_build_app_from_env_requires_heimdall_key():
with pytest.raises(RuntimeError):
build_app_from_env({"RATATOSKR_AFFECT_DB": ":memory:"})
def test_build_app_from_env_builds_app_with_routes():
app = build_app_from_env(
{"RATATOSKR_HEIMDALL_KEY": "shared-secret", "RATATOSKR_AFFECT_DB": ":memory:"}
)
paths = {getattr(r, "path", None) for r in app.routes}
assert "/bifrost/handshake" in paths
assert "/bifrost/affect-call" in paths
Generated
+1 -1
View File
@@ -1052,7 +1052,7 @@ wheels = [
[[package]]
name = "ratatoskr"
version = "0.17.1"
version = "0.17.2"
source = { editable = "." }
dependencies = [
{ name = "httpx" },