Architecture
QueryMT is designed with a modular and extensible architecture to provide a flexible foundation for interacting with Large Language Models. Understanding its key components will help you leverage the library effectively.
Core Abstractions
At the heart of QueryMT are several key traits that define the contract for LLM interactions:
-
querymt::LLMProvider: This is the central trait that all LLM provider implementations must conform to. It combines capabilities for chat, text completion, and embeddings into a single, unified interface by extendingquerymt::chat::BasicChatProvider,querymt::chat::ToolChatProvider,querymt::completion::CompletionProvider, andquerymt::embedding::EmbeddingProvider. It also includes methods for listing available tools (tools()) and calling them (call_tool()).- Source:
crates/querymt/src/lib.rs
- Source:
-
querymt::HTTPLLMProvider: A specialized trait for LLM providers that are accessed over HTTP. It defines methods for constructing HTTP requests and parsing HTTP responses for chat, completion, and embedding operations by extending thehttpsub-traits (e.g.,querymt::chat::http::HTTPChatProvider).- Source:
crates/querymt/src/lib.rs
- Source:
Provider Adapters
querymt::adapters::LLMProviderFromHTTP: This struct acts as an adapter, allowing anHTTPLLMProvider(which handles the raw HTTP logic) to be used as a full-fledgedLLMProvider. It takes care of calling the outbound HTTP mechanism (fn@querymt::outbound::call_outbound) and then parsing the response using theHTTPLLMProviderimplementation.- Source:
crates/querymt/src/adapters.rs
- Source:
Building and Configuring Providers
querymt::builder::LLMBuilder: QueryMT provides a fluent builder pattern for configuring and instantiatingLLMProviderinstances. This builder allows you to set various options such as the model, API keys, temperature, custom parameters, and register tools. It uses aquerymt::plugin::host::PluginRegistryto find the appropriate factory for the selected provider.- Source:
crates/querymt/src/builder.rs
- Source:
Tool and Function Calling
QueryMT has robust support for LLM tool usage (often called function calling):
querymt::chat::Tool/querymt::chat::FunctionTool: These structs define the schema of tools that an LLM can use.querymt::tool_decorator::CallFunctionTool: A trait that your host-side functions must implement to be callable by the LLM. It includes a method to describe the tool (descriptor()) and a method to execute it (call()).querymt::tool_decorator::ToolEnabledProvider: A decorator that wraps an existingLLMProviderand injects tool-calling capabilities. It manages a registry ofCallFunctionToolimplementations and handles the interaction logic when an LLM decides to call a tool.- Source:
crates/querymt/src/tool_decorator.rs
- Source:
Plugin System
A core strength of QueryMT is its plugin system, enabling easy addition of new LLM providers. The system has been unified to support different plugin types through a single registry.
querymt::plugin::LLMProviderFactory: A trait that plugin authors implement. Its primary role is to create anLLMProviderinstance from a given configuration. It also provides metadata like the plugin name and configuration schema.-
querymt::plugin::http::HTTPLLMProviderFactory: A specialized factory for plugins that expose anHTTPLLMProvider. -
querymt::plugin::host::PluginRegistry: A central registry that discovers, loads, and managesLLMProviderFactoryinstances from a configuration file. The registry itself is loader-agnostic; it storesPluginLoadertrait objects and uses them to resolve provider entries.querymt::plugin::host::PluginLoader: A trait for systems that can load a specificquerymt::plugin::host::PluginType. QueryMT provides implementations for:- Native Plugins: (
querymt::plugin::host::native::NativeLoader) Loads plugins from shared libraries (.so,.dll,.dylib). - WASM Plugins via Extism: (
querymt::plugin::extism_impl::host::ExtismLoader) Loads plugins compiled to WebAssembly and executed via Extism, offering sandboxing and portability.
- Native Plugins: (
querymt::dynamic::PluginRegistryDynamicExt: A convenience extension layer that registers the built-in dynamic loaders for common applications.- Sources:
crates/querymt/src/plugin/host/mod.rs,crates/querymt/src/dynamic.rs,crates/querymt/src/plugin/host/native.rs,crates/querymt/src/plugin/extism_impl/host/loader.rs
Outbound HTTP Communication
- The
outbound.rsmodule provides a common function (fn@querymt::outbound::call_outbound) for making HTTP requests. This is used by HTTP-based providers and adapters. It's designed to work in both native environments (usingreqwest) and potentially WASM environments.- Source:
crates/querymt/src/outbound.rs
- Source:
Error Handling
- QueryMT defines a comprehensive
querymt::error::LLMErrorto represent various issues that can occur, such as HTTP errors, authentication problems, provider-specific errors, and plugin issues.- Source:
crates/querymt/src/error.rs
- Source:
High-Level Flow
- An application initializes a
querymt::plugin::host::PluginRegistryfrom a configuration file (e.g.,providers.toml). - For the common dynamic-plugin path, it attaches the built-in loaders with
querymt::dynamic::PluginRegistryDynamicExt::with_dynamic_loaders(). - The registry calls
load_all_plugins(), which iterates through the configured providers. For each provider, it determines its type (e.g., local Wasm, OCI image, native library) and uses the appropriatePluginLoaderto load it and create anLLMProviderFactory. - The application uses
registry.builder("openai")to create a registry-bound builder, orquerymt::provider("openai")to create an unbound builder. - The registry-bound builder finishes with
build(), while the unbound builder finishes withbuild_with(®istry). - The builder looks up the
LLMProviderFactoryfrom the registry using the provider name. - The builder merges defaults from
[providers.config], overlays explicit builder settings, prunes the merged config by schema, and then calls the factory'sfrom_config()method. - If tools were added via
add_tool(), the base provider is wrapped in aquerymt::tool_decorator::ToolEnabledProvider. - If a validator is set, the provider is further wrapped in a
querymt::validated_llm::ValidatedLLM. - The application can then use the resulting
Box<dyn LLMProvider>instance to perform chat, completion, or embedding operations.
This layered and decoupled design makes QueryMT adaptable and easy to extend.