background_agents.game_art_agent module

Background art generation agent for S.N.E.S. game turns.

Subscribes to game:art:request Redis pub/sub channel. When a game turn completes, the main pipeline publishes the narrative + character image URLs. This agent generates art via the Gemini image API and posts it directly to the channel WITHOUT feeding the result back into the conversation context.

This eliminates the 400 INVALID_ARGUMENT errors caused by large tool results bloating the chat API payload.

# 💀🔥 ART DAEMON – RUNS OUTSIDE THE CONTEXT WINDOW

class background_agents.game_art_agent.GameArtAgent(redis_client=None, platform_adapter=None, config=None)[source]

Bases: object

Background agent that auto-generates game art after each turn.

Listens on Redis pub/sub for art requests published by the main pipeline after game turn responses are sent. Generates images via Gemini and posts them directly to Discord.

This keeps the image generation result OUT of the LLM’s conversation history, preventing context overflow 400 errors.

Parameters:
  • redis_client (Any)

  • platform_adapter (Any)

  • config (Any)

__init__(redis_client=None, platform_adapter=None, config=None)[source]

Initialize the background art agent with its injected dependencies.

Stores the Redis client (used for pub/sub subscriptions and for reading Dark Loopmother session flags), the platform adapter (used by _send_to_channel() to post the finished PNG to Discord), and the config object, and sets the running flag and pub/sub handle to their idle defaults. No I/O happens here; subscriptions begin in start().

Constructed by the agents service bootstrap in agents_main.py.

Parameters:
  • redis_client (Any) – Async Redis client for pub/sub and session-flag reads. When None, start() becomes a no-op.

  • platform_adapter (Any) – Platform adapter exposing send_file for delivering generated art to a channel.

  • config (Any) – Optional configuration object retained for downstream use.

Return type:

None

async start()[source]

Begin listening for art requests and turn-complete events.

Marks the agent running, initializes the _pending_turns map that tracks turns awaiting a fallback render, and launches two detached asyncio.create_task listeners: _listen_loop (explicit game:art:request pub/sub) and _listen_turn_complete (the game:turn:complete backup path). The call is a no-op if the agent is already running or no Redis client was injected, so it is safe to call once during bootstrap. No art is generated here; the listeners drive that.

Called by AgentsService in agents_main.py, which awaits self.game_art_agent.start() during background-agent startup.

Return type:

None

async stop()[source]

Stop the agent and tear down its art-request subscription.

Clears the running flag so the listener loops in _listen_loop() and _listen_turn_complete() exit on their next iteration, then attempts to unsubscribe the primary pub/sub handle from game:art:request. Unsubscribe errors are swallowed so shutdown is best-effort. Note that the game:turn:complete fallback pub/sub created locally in _listen_turn_complete() is not explicitly unsubscribed here; it unwinds when that coroutine observes the cleared flag.

Called during agents-service shutdown in agents_main.py.

Return type:

None

Returns:

None.

async background_agents.game_art_agent.publish_art_request(redis, channel_id, narrative, character_urls=None, character_names=None, game_name='', aspect_ratio='16:9')[source]

Publish an art generation request to the background agent.

Call this from the main pipeline after a game turn response is sent. The background GameArtAgent will pick it up and generate art WITHOUT touching the conversation context.

Return type:

None

Parameters:
  • redis (Any)

  • channel_id (str)

  • narrative (str)

  • character_urls (list[str] | None)

  • character_names (list[str] | None)

  • game_name (str)

  • aspect_ratio (str)