core.gateway_pinned module
Tool-routing pin sets shared across services without importing tool modules.
These frozen name sets decide where a tool executes in the microservice fleet.
They live here (not in tools/__init__.py) so the inference service can
import the routing decision as plain data — a handful of strings — without
pulling in any tools/*.py handler module or its dependencies. tools
re-exports both names for backward compatibility.
Three execution tiers exist (see the tools-service split plan):
GATEWAY_PINNED_TOOLS-> run on the gateway (need the live discord.py client / node-local audio state). Delegated via the existingexecute_toolRPC intools.ToolRegistry.call().INFERENCE_PINNED_TOOLS-> run in-process on inference (they spawn their own nestedOpenRouterClient.chat()loops, so they need the LLM client + swarm stack that inference already hosts; the dedicated tools service is kept lightweight and has no LLM client).everything else -> delegated to the dedicated tools service.
Routing precedence is gateway-pinned first, then inference-pinned, then remote; the two sets must stay disjoint.
- core.gateway_pinned.GATEWAY_PINNED_TOOLS: frozenset[str] = frozenset({'create_poll', 'cross_channel_query', 'discord_delete_message', 'discord_edit_message', 'discord_embed', 'discord_invite', 'discord_leave_server', 'discord_manage_channels', 'discord_manage_roles', 'discord_message_reactions', 'discord_moderation', 'discord_react', 'discord_send_dm', 'discord_server_info', 'discord_upload_file', 'discord_webhooks', 'force_guild_index', 'get_current_guild_count', 'get_message_reactions', 'get_server_diagnostics', 'get_voice_states', 'list_server_emojis', 'music_steering', 'pause_music', 'play_music', 'reset_music_context', 'resume_music', 'stop_music'})
Tools that must run on the Gateway node (where the live platform client lives) rather than on a worker node behind a ProxyPlatformAdapter.
Voice/music tools hold node-local audio state on the Gateway.
The
discord_*/ guild tools callrequire_discord_clientand need the nativediscord.pyclient; the microservice split left them stranded on worker nodes (“Discord client not available”), so they are delegated to the Gateway’sexecute_toolRPC. Keep this in sync when adding tools that depend ontools._discord_helpers.require_discord_client.
- core.gateway_pinned.INFERENCE_PINNED_TOOLS: frozenset[str] = frozenset({'call_subagent', 'conjure_egregore', 'extend_tool_loop', 'gravimetric_telescope', 'refine_prompt', 'start_deep_think_task', 'start_research_task'})
Compound tools that spawn their own nested
OpenRouterClient.chat()loop during execution (verified: each module calls.chat(...)). Wherever they run needs the full LLM client + swarm/subagent stack, so they are kept on the inference tier rather than the lightweight tools service. The background variants (start_*_task) enqueue work onto the host service’s TaskManager, whose worker then runs the nested chat — so the enqueue entry point is pinned to inference too, keeping its background worker co-located with the LLM client. Pure result-fetchers (get_*_result/list_*_tasks) only read Redis and may delegate, so they are intentionally NOT pinned.AUDIT (finalize when wiring inference_main, plan Phase 4): the swarm subsystem tools (
dispatch_async_swarm,create_swarm_agent…) also drive chat loops but run through the swarm/agents path — decide their placement then.cross_channel_queryalso spawns chat but is already GATEWAY_PINNED (it runs on the gateway against the real client), so it is excluded here to keep the two sets disjoint.