# How OpenMontage Selector Tools Transparently Route to Providers

> Learn how OpenMontage selector tools transparently route to providers. Discover backend discovery, scoring, and request delegation for efficient task execution.

- Repository: [Calesthio/OpenMontage](https://github.com/calesthio/OpenMontage)
- Tags: how-to-guide
- Published: 2026-08-30

---

**OpenMontage selector tools like `tts_selector` and `video_selector` transparently route to providers by automatically discovering capable backends through the `ToolRegistry`, scoring them against normalized task contexts, and delegating requests while enriching results with selection metadata.**

OpenMontage abstracts concrete provider implementations behind capability-level selectors, enabling agents to request high-level functions like text-to-speech or video generation without hardcoding specific APIs. The selector tools handle the complex logic of provider discovery, ranking, and adaptation internally, presenting a unified interface that routes requests to the most appropriate available backend. This architecture ensures that selector tools transparently route to providers based on real-time availability, task requirements, and user preferences.

## Automatic Provider Discovery via ToolRegistry

The routing process begins with automatic discovery in the selector's `_providers()` method. Located in [`tools/audio/tts_selector.py`](https://github.com/calesthio/OpenMontage/blob/main/tools/audio/tts_selector.py), this method queries the global `ToolRegistry` at runtime to locate any `BaseTool` instance whose `capability` field matches the selector's designated capability—`"tts"` for audio generation or `"video"` for video generation.

This discovery mechanism requires no manual registration. When developers add new providers to the system, they automatically become available to the selector provided they implement the correct capability constant, ensuring the routing pool expands dynamically as the ecosystem grows.

## Dynamic Provider Matrix Construction

Each discovered provider contributes a row to the internal `provider_matrix`, a data structure that records the provider name alongside its "strength" rating. The strength value derives from the provider's `best_for` field, which describes optimal use cases and performance characteristics.

This matrix serves dual purposes: it drives the scoring algorithm that determines routing decisions, and it provides UI-side explanations for why specific providers were selected for particular tasks.

## Task-Context Normalization and Preparation

Before scoring occurs, the selector prepares a normalized task-context dictionary from user inputs such as `text`, `language`, `voice`, or `prompt`. This normalization happens via `lib.scoring.normalize_task_context`, which standardizes parameters across different provider APIs to ensure fair comparison.

By passing the same structured context to every candidate provider, the system eliminates interface discrepancies that could skew routing decisions, creating a level playing field for the ranking engine.

## Scored Ranking with Operation Modes

The core routing logic resides in `lib.scoring.rank_providers`, which evaluates candidates against the normalized context and returns `ScoreItem` objects containing suitability scores. The `tts_selector` and `video_selector` support two distinct operation modes controlled by the `operation` parameter:

- **`operation="rank"`** — Returns the full ranked provider list without invoking any backend. This mode is useful for UI inspection, debugging provider selection logic, or presenting options to users before committing resources.
- **`operation="generate"`** (default) — Selects the highest-scoring available provider and executes the actual generation task, handling all delegation transparently.

When users specify `preferred_provider` or `allowed_providers` in the request, the selector filters the candidate list before scoring, ensuring explicit preferences override automatic selection when provided.

## Fallback Handling and Availability Management

If no providers are available for the requested capability, the selector returns `ToolStatus.UNAVAILABLE` along with a failure result. The `fallback_tools` property dynamically mirrors the names of all discovered providers, allowing downstream components to present alternative options when primary choices fail or constraints eliminate all high-scoring candidates.

This design ensures graceful degradation while maintaining transparency about available alternatives.

## Input Adaptation for Provider APIs

Before delegating to the concrete provider, the selector performs input adaptation to match provider-specific requirements. For example, it might convert generic parameters like `speaking_rate` to Azure's specific `rate` parameter, or normalize `output_format` values to match individual provider expectations.

This translation layer prevents provider-specific quirks from leaking into the high-level selector interface, maintaining the abstraction that selector tools transparently route to providers regardless of backend differences.

## Result Enrichment and Transparency Metadata

After the chosen provider completes execution, the selector augments the `ToolResult` object with metadata fields defined in [`tools/audio/tts_selector.py`](https://github.com/calesthio/OpenMontage/blob/main/tools/audio/tts_selector.py):

- `selected_tool`: The concrete provider implementation used
- `selected_provider`: The provider identifier string
- `selection_reason`: Human-readable explanation of the ranking logic
- `alternatives_considered`: List of other providers evaluated during selection

These fields provide full visibility into routing decisions, supporting debugging and enabling user interfaces to explain why specific backends were chosen.

## Practical Implementation Examples

The following examples demonstrate how to use the selectors with different routing configurations.

**Using `tts_selector` with an explicit provider preference:**

```python
result = registry.get("tts_selector").execute({
    "text": "Hello, world!",
    "preferred_provider": "elevenlabs",   # Force ElevenLabs if available

    "voice": "Rachel",
    "operation": "generate"
})
print(result.data["selected_provider"])   # → "elevenlabs"

print(result.data["selection_reason"])    # Explanation of why it was chosen

```

**Ranking providers without generating audio:**

```python
rankings = registry.get("tts_selector").execute({
    "text": "Testing ranking mode",
    "operation": "rank"
})
for entry in rankings.data["rankings"]:
    print(f"{entry['provider']}: {entry['score']:.2f}")

```

**Using `video_selector` in the same way:**

```python
video_result = registry.get("video_selector").execute({
    "prompt": "A futuristic cityscape at sunrise",
    "preferred_provider": "runway",
    "operation": "generate"
})
print(video_result.data["selected_tool"])   # e.g., "runway_video"

```

## Summary

- **Automatic Discovery**: Selectors query `ToolRegistry` for `BaseTool` instances matching their capability (`"tts"` or `"video"`) through the `_providers()` method without requiring manual registration.
- **Dynamic Ranking**: The `provider_matrix` and `lib.scoring.rank_providers` evaluate candidates based on task context and `best_for` strengths to determine optimal routing.
- **Flexible Operations**: Support for `operation="rank"` (inspection) and `operation="generate"` (execution) modes adapts to different workflow requirements.
- **User Preferences**: The `preferred_provider` and `allowed_providers` parameters allow explicit override of automatic selection logic.
- **Transparent Metadata**: Results include `selected_provider`, `selection_reason`, and `alternatives_considered` for full routing visibility.
- **Provider-Agnostic Interface**: Input adaptation and result enrichment hide provider-specific complexity behind unified capability selectors in [`tools/audio/tts_selector.py`](https://github.com/calesthio/OpenMontage/blob/main/tools/audio/tts_selector.py) and [`tools/video/video_selector.py`](https://github.com/calesthio/OpenMontage/blob/main/tools/video/video_selector.py).

## Frequently Asked Questions

### How does the selector choose between multiple available providers?

The selector uses `lib.scoring.rank_providers` to evaluate each candidate against a normalized task context created by `lib.scoring.normalize_task_context`. Each provider's `best_for` characteristics are compared against task requirements to generate relevance scores, with the highest-scoring available provider selected by default. Users can override this behavior by setting the `preferred_provider` parameter in the request.

### What happens if my preferred provider is unavailable?

If the specified `preferred_provider` is not found or unavailable, the selector filters it from the candidate list and proceeds to rank remaining providers according to the `provider_matrix`. If no providers remain available, the selector returns `ToolStatus.UNAVAILABLE` and populates `fallback_tools` with the names of all discovered alternatives, allowing your application to suggest other options or retry with different constraints.

### Can I inspect provider rankings without triggering a generation?

Yes. Set `operation="rank"` when calling `tts_selector` or `video_selector`. This mode returns the full ranked list of providers and their scores through the `rankings` field without invoking any actual provider execution, making it ideal for debugging or presenting options to end users before committing to a generation task.

### Where is the provider routing logic implemented in the codebase?

The routing implementation spans several key files: [`tools/audio/tts_selector.py`](https://github.com/calesthio/OpenMontage/blob/main/tools/audio/tts_selector.py) and [`tools/video/video_selector.py`](https://github.com/calesthio/OpenMontage/blob/main/tools/video/video_selector.py) contain the selector implementations with `_providers()` and result enrichment logic; [`lib/scoring.py`](https://github.com/calesthio/OpenMontage/blob/main/lib/scoring.py) houses the `rank_providers` and `normalize_task_context` functions; [`tools/tool_registry.py`](https://github.com/calesthio/OpenMontage/blob/main/tools/tool_registry.py) manages provider discovery; and [`tools/base_tool.py`](https://github.com/calesthio/OpenMontage/blob/main/tools/base_tool.py) defines the interface shared by all providers and selectors. Architectural documentation is available in [`docs/ARCHITECTURE.md`](https://github.com/calesthio/OpenMontage/blob/main/docs/ARCHITECTURE.md).