core.remote_tool_registry module

Inference-side stand-in for ToolRegistry that delegates tool execution.

RemoteToolRegistry duck-types the read surface of tools.ToolRegistry (OpenAI schemas, tool_names, repeat_allowed_tools, is_allowed …) from the Redis-published tool catalog, so the inference tier can present tools to the LLM and rank them without importing any handler module. Its call() routes each invocation through three tiers:

  1. GATEWAY_PINNED_TOOLS -> delegate to the gateway (live discord.py client).

  2. INFERENCE_PINNED_TOOLS (and nested calls, and tools_force_in_process) -> run in-process via the small local registry the inference service loads.

  3. everything else -> delegate to the dedicated tools service over sg:stream:tools; the result + write-back (sent_files media bytes, injected_tools, sent_rich_messages) come back on a per-worker reply stream and are merged onto the live ctx so the LLM loop is unchanged.

In tools_service_mode == "in_process" (the safe default) every call runs on the local registry, exactly as before the split. The remote path is opt-in.

Drop-in: the executor and transport only touch the registry’s read surface plus call(), so swapping ToolRegistry for this requires no changes there.

class core.remote_tool_registry.RemoteToolRegistry(*, event_bus, redis, config, local_registry, worker_id)[source]

Bases: object

Catalog-backed, delegation-capable replacement for ToolRegistry.

Parameters:
  • event_bus (RedisEventBus) – bus used to publish tool-exec requests.

  • redis (Any) – raw (decode_responses=False) async client — used to read the catalog and consume the per-worker reply stream.

  • config (Any) – live config.Config (routing mode, timeouts, overrides).

  • local_registry (Any) – a real tools.ToolRegistry for in-process tiers (gateway-pinned delegation needs ctx.adapter; inference-pinned + in_process mode run handlers here). During rollout it may hold every tool; in the lean end state only INFERENCE_PINNED_TOOLS.

  • worker_id (str) – stable id for this inference worker (reply-stream name).

async start()[source]

Load the catalog and start the reply reader + catalog watcher.

In in_process mode this is a no-op: no catalog is read (the read surface stays delegated to the local registry) and no background tasks are spawned, so the inference worker behaves exactly as before the split.

Return type:

None

async stop()[source]
Return type:

None

async reload_catalog()[source]

(Re)load the catalog from Redis. Returns True if a catalog was found.

Return type:

bool

get_openai_tools()[source]
Return type:

list[dict[str, Any]]

get_openai_tools_by_names(names)[source]
Return type:

list[dict[str, Any]]

Parameters:

names (set[str])

tool_names()[source]
Return type:

frozenset[str]

repeat_allowed_tools()[source]
Return type:

frozenset[str]

list_tools()[source]
Return type:

list[Any]

set_permissions(permissions)[source]
Return type:

None

Parameters:

permissions (dict[str, list[str]])

is_allowed(tool_name, user_id)[source]
Return type:

bool

Parameters:
  • tool_name (str)

  • user_id (str)

property has_tools: bool
property task_manager: Any
async call(name, arguments, user_id='', ctx=None, *, nested=False)[source]
Return type:

str

Parameters: