Skip to content
Rymflux Documentation
Esc
navigateopen⌘Jpreview
On this page

Search Manager service specification

Search Manager service specification

Responsibility

Dispatch search queries to all registered plugins with the search capability, collect results with per-plugin timeouts, merge/deduplicate, and emit results progressively to the frontend via Tauri events.

Ownership boundary

The Search Manager holds an Arc<PluginRuntime> and calls into it directly. It does not read or write plugin stdio. It does not manage plugin processes. It does not parse JSON-RPC responses.

  • Plugin Runtime owns: process lifecycle, stdin/stdout I/O, JSON-RPC serialization/deserialization
  • Search Manager owns: dispatch logic, timing, result merging and ranking

Forward path

impl SearchManager {
    /// Start a search. Returns a query_id for correlation.
    pub async fn start_search(&self, query: String) -> Result<String, SearchError>;

    /// Cancel an in-flight search.
    pub fn cancel_search(&self) -> Result<(), SearchError>;

    /// Update search configuration at runtime.
    pub fn set_search_config(&self, config: SearchConfig) -> Result<(), SearchError>;
}

Internal flow:

  1. Generate query_id (UUIDv4)
  2. For each plugin with search capability, call runtime.search(plugin_id, query)
  3. Await each result concurrently (PluginRuntime enforces timeouts internally)
  4. On each result: emit search_result event
  5. On timeout: emit search_error event (PluginRuntime has already killed the plugin)
  6. When all done: emit search_done event

SearchConfig

pub struct SearchConfig {
    pub max_results_per_plugin: usize,      // 50
    pub dedup_tolerance: f32,               // 0.0–1.0, normalized title+artist similarity
    pub ranking_weights: RankingWeights,
}

pub struct RankingWeights {
    pub relevance: f32,     // plugin-reported relevance score
    pub popularity: f32,    // if plugin provides it
    pub source: f32,        // per-plugin trust weight, user-configurable
}

Default values:

  • max_results_per_plugin: 50
  • dedup_tolerance: 1.0 (exact match only)
  • ranking_weights: { relevance: 1.0, popularity: 0.5, source: 1.0 }

Timeouts are owned by PluginRuntime (see plugin-runtime.md). The Search Manager never configures or overrides per-method timeouts. For explicit user-initiated cancellation, cancel_search calls runtime.cancel(plugin_id) for each plugin with in-flight requests.

Deduplication algorithm

Exact match on normalized title::artist string:

  1. Lowercase both title and artist_name
  2. Strip punctuation characters (.,!?;:'"()[]{})
  3. Collapse whitespace runs to single spaces
  4. Concatenate: normalized_title::normalized_artist
  5. If two results from different plugins produce the same key, they’re duplicates
  6. Keep the one from the higher-weighted source plugin (per ranking_weights.source)

No Levenshtein or fuzzy matching in v1. Exact normalized match catches the 90% case (same song, different capitalization/punctuation) with zero false positives.

SearchQuery

pub struct SearchQuery {
    pub query: String,
    pub limit: usize,       // max results from this plugin
}

References

  • ADR-007 (search manager — Rust backend with Tauri events)
  • FR-CORE-4 (parallel search manager)

Was this page helpful?