Module: Legion

Defined in:
lib/legion/llm/patches/ruby_llm_parallel_tools.rb,
lib/legion/llm.rb,
lib/legion/llm/audit.rb,
lib/legion/llm/batch.rb,
lib/legion/llm/cache.rb,
lib/legion/llm/fleet.rb,
lib/legion/llm/hooks.rb,
lib/legion/llm/usage.rb,
lib/legion/llm/errors.rb,
lib/legion/llm/helper.rb,
lib/legion/llm/prompt.rb,
lib/legion/llm/router.rb,
lib/legion/llm/routes.rb,
lib/legion/llm/skills.rb,
lib/legion/llm/version.rb,
lib/legion/llm/metering.rb,
lib/legion/llm/off_peak.rb,
lib/legion/llm/pipeline.rb,
lib/legion/llm/settings.rb,
lib/legion/llm/arbitrage.rb,
lib/legion/llm/providers.rb,
lib/legion/llm/compressor.rb,
lib/legion/llm/embeddings.rb,
lib/legion/llm/scheduling.rb,
lib/legion/llm/fleet/error.rb,
lib/legion/llm/helpers/llm.rb,
lib/legion/llm/router/rule.rb,
lib/legion/llm/shadow_eval.rb,
lib/legion/llm/skills/base.rb,
lib/legion/llm/cost_tracker.rb,
lib/legion/llm/daemon_client.rb,
lib/legion/llm/fleet/handler.rb,
lib/legion/llm/fleet/request.rb,
lib/legion/llm/skills/errors.rb,
lib/legion/llm/token_tracker.rb,
lib/legion/llm/tools/adapter.rb,
lib/legion/llm/audit/exchange.rb,
lib/legion/llm/cost_estimator.rb,
lib/legion/llm/fleet/exchange.rb,
lib/legion/llm/fleet/response.rb,
lib/legion/llm/hooks/metering.rb,
lib/legion/llm/metering/event.rb,
lib/legion/llm/pipeline/steps.rb,
lib/legion/llm/response_cache.rb,
lib/legion/llm/context_curator.rb,
lib/legion/llm/hooks/rag_guard.rb,
lib/legion/llm/native_dispatch.rb,
lib/legion/llm/quality_checker.rb,
lib/legion/llm/skills/registry.rb,
lib/legion/llm/skills/settings.rb,
lib/legion/llm/audit/tool_event.rb,
lib/legion/llm/confidence_score.rb,
lib/legion/llm/discovery/ollama.rb,
lib/legion/llm/discovery/system.rb,
lib/legion/llm/fleet/dispatcher.rb,
lib/legion/llm/hooks/reflection.rb,
lib/legion/llm/pipeline/profile.rb,
lib/legion/llm/pipeline/request.rb,
lib/legion/llm/pipeline/tracing.rb,
lib/legion/llm/tools/dispatcher.rb,
lib/legion/llm/audit/skill_event.rb,
lib/legion/llm/confidence_scorer.rb,
lib/legion/llm/hooks/reciprocity.rb,
lib/legion/llm/metering/exchange.rb,
lib/legion/llm/pipeline/executor.rb,
lib/legion/llm/pipeline/response.rb,
lib/legion/llm/pipeline/timeline.rb,
lib/legion/llm/provider_registry.rb,
lib/legion/llm/router/resolution.rb,
lib/legion/llm/structured_output.rb,
lib/legion/llm/tools/interceptor.rb,
lib/legion/llm/transport/message.rb,
lib/legion/llm/audit/prompt_event.rb,
lib/legion/llm/conversation_store.rb,
lib/legion/llm/escalation_history.rb,
lib/legion/llm/escalation_tracker.rb,
lib/legion/llm/hooks/budget_guard.rb,
lib/legion/llm/skills/disk_loader.rb,
lib/legion/llm/skills/step_result.rb,
lib/legion/llm/codex_config_loader.rb,
lib/legion/llm/hooks/cost_tracking.rb,
lib/legion/llm/override_confidence.rb,
lib/legion/llm/pipeline/steps/rbac.rb,
lib/legion/llm/claude_config_loader.rb,
lib/legion/llm/hooks/response_guard.rb,
lib/legion/llm/pipeline/gaia_caller.rb,
lib/legion/llm/pipeline/steps/debate.rb,
lib/legion/llm/pipeline/tool_adapter.rb,
lib/legion/llm/router/health_tracker.rb,
lib/legion/llm/fleet/reply_dispatcher.rb,
lib/legion/llm/pipeline/steps/billing.rb,
lib/legion/llm/pipeline/steps/metering.rb,
lib/legion/llm/router/escalation_chain.rb,
lib/legion/llm/skills/skill_run_result.rb,
lib/legion/llm/pipeline/audit_publisher.rb,
lib/legion/llm/pipeline/steps/rag_guard.rb,
lib/legion/llm/pipeline/tool_dispatcher.rb,
lib/legion/llm/pipeline/steps/tool_calls.rb,
lib/legion/llm/skills/external_discovery.rb,
lib/legion/llm/transport/exchanges/audit.rb,
lib/legion/llm/pipeline/steps/rag_context.rb,
lib/legion/llm/router/gateway_interceptor.rb,
lib/legion/llm/pipeline/steps/prompt_cache.rb,
lib/legion/llm/pipeline/steps/token_budget.rb,
lib/legion/llm/pipeline/enrichment_injector.rb,
lib/legion/llm/pipeline/steps/gaia_advisory.rb,
lib/legion/llm/pipeline/steps/mcp_discovery.rb,
lib/legion/llm/pipeline/steps/post_response.rb,
lib/legion/llm/pipeline/steps/tier_assigner.rb,
lib/legion/llm/pipeline/steps/trigger_match.rb,
lib/legion/llm/transport/exchanges/metering.rb,
lib/legion/llm/pipeline/steps/classification.rb,
lib/legion/llm/pipeline/steps/skill_injector.rb,
lib/legion/llm/pipeline/steps/span_annotator.rb,
lib/legion/llm/pipeline/steps/tool_discovery.rb,
lib/legion/llm/tools/interceptors/python_venv.rb,
lib/legion/llm/transport/exchanges/escalation.rb,
lib/legion/llm/transport/messages/audit_event.rb,
lib/legion/llm/pipeline/steps/knowledge_capture.rb,
lib/legion/llm/pipeline/steps/confidence_scoring.rb,
lib/legion/llm/transport/messages/escalation_event.rb

Overview

Patch: RubyLLM::Chat parallel tool call execution

RubyLLM’s default ‘handle_tool_calls` iterates tool calls serially with `.each_value`, meaning when an LLM returns N tool calls in a single response they execute one-at-a-time. This patch replaces that loop with concurrent thread execution so all tool calls in a batch run in parallel, and results are collected before re-prompting the model.

Additionally, RubyLLM fires ‘on_tool_result` with the raw tool return value (a String/Hash/etc.) which carries no `tool_call_id`. The legion-interlink bridge script’s ‘serialize_tool_result` needs a `tool_call_id` field to match results back to the correct tool call slot in the UI — without it every result falls back to name-based matching, which breaks when multiple tools of the same name run in parallel and leaves them stuck on RUNNING.

Fix: wrap each result in a ToolResultWrapper that exposes both the raw content/result AND the originating tool_call_id / id fields.

NOTE: This is a temporary shim. When RubyLLM is replaced this file goes away.

Thread safety notes:

- Each tool call executes in its own thread.
- @on[:tool_call] fires per-thread (fast, just event emission — safe).
- @on[:tool_result] fires per-thread with the wrapper object.
- add_message is called serially after all threads complete to preserve
  message ordering and avoid races on @messages.
- If ANY tool returns a RubyLLM::Tool::Halt, complete() is skipped —
  matching the original semantics.

Defined Under Namespace

Modules: Extensions, LLM