Add square text-to-image rendering with a spend gate
Some checks failed
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Has been cancelled

Hall of Helix needs a credential-safe render that returns a provenance
receipt and refuses a batch before the quote exceeds its cap. OpenRouter
is the only client; the model and the live budget stay with the founder.

Assistant: grok
Assistant-Session: 01a11ab6-1534-7d63-8606-662b92a80ed6
This commit is contained in:
tegwick 2026-10-08 11:01:49 +02:00
parent 9dc6b39766
commit e3ab268d6e
11 changed files with 1335 additions and 3 deletions

View file

@ -2,6 +2,7 @@
Tests for LLMServer HTTP serve mode (FR-1).
"""
import base64
import json
import threading
import time
@ -18,6 +19,7 @@ from llm_connect._diagnostics import (
)
from llm_connect.adapter import ErrorLLMAdapter, MockLLMAdapter
from llm_connect.exceptions import LLMAPIError, LLMConfigurationError, LLMTimeoutError
from llm_connect.images import ImageSpendGate, ImageSpendPolicy
from llm_connect.models import LLMResponse, RunConfig
from llm_connect.profiles import CUSTODIAN_TRIAGE_BALANCED, ProfiledLLMAdapter, RuntimeProfile
from llm_connect.server import LLMServer
@ -138,6 +140,14 @@ class TestExecute:
assert status == 400
assert body == {"error": "invalid JSON body"}
def test_images_route_is_absent_until_configured(self, server):
status, body = _post(
f"http://127.0.0.1:{server.port}/images",
{"prompt": "a gear"},
)
assert status == 404
assert body == {"error": "not found"}
def test_unknown_post_path_returns_404(self, server):
status, body = _post(
f"http://127.0.0.1:{server.port}/wrong",
@ -411,3 +421,43 @@ class TestExecute:
assert [status for status, _body in results] == [200, 200]
assert elapsed < 1.5
class _ServerImageClient:
provider = "openrouter"
model = "test/image"
def generate(self, request):
return b"\x89PNG\r\n\x1a\nserver", 0.02
def test_images_endpoint_returns_receipt_and_png_bytes():
gate = ImageSpendGate(ImageSpendPolicy(max_cost_usd=0.10, quote_usd=0.05))
server = LLMServer(
adapter=MockLLMAdapter(mock_response="unused"),
port=0,
image_client=_ServerImageClient(),
image_gate=gate,
)
server.start()
try:
status, body = _post(
f"http://127.0.0.1:{server.port}/images",
{"prompt": "a gear", "size": "1024x1024", "output_format": "png"},
)
execute_status, execute_body = _post(
f"http://127.0.0.1:{server.port}/execute",
{"prompt": "still here"},
)
finally:
server.stop()
assert status == 200
assert body["receipt"]["provider"] == "openrouter"
assert body["receipt"]["model"] == "test/image"
assert body["receipt"]["width"] == 1024
assert body["receipt"]["within_cap"] is True
assert "prompt" not in body["receipt"]
assert base64.b64decode(body["image_base64"]).startswith(b"\x89PNG\r\n\x1a\n")
assert execute_status == 200
assert execute_body["content"] == "unused"