operonx.providers¶
LLM, embedding, reranker, and ONNX provider ops. Provider backends are
loaded lazily — a tier-1 install (pip install operonx) can
import operonx.providers without pulling openai / httpx / numpy
/ torch. Missing-dep errors surface only when the corresponding
backend is actually accessed.
See Pick an extra on the installation page for which extra each provider needs.
Provider ops¶
The core provider op types. Each exposes an Op.of(...) classmethod
for concise construction with explicit keyword args — that's the
recommended style.
LLMOp
¶
LLMOp(
resource: Optional[Union[str, List[str]]] = None,
ratios: Optional[List[float]] = None,
fallback: Optional[List[str]] = None,
batch_mode: bool = False,
seed: Optional[int] = None,
fields: Optional[List[str]] = None,
parser: Optional[str] = None,
validators: Optional[Dict[str, List[Any]]] = None,
max_retries: int = 0,
retry_hint: bool = True,
inputs: Dict[str, Any] = None,
outputs: Dict[str, Any] = None,
**kwargs: Any,
)
Bases: BaseOp
Op that formats a prompt and calls a language model via ResourceHub.
Give it exactly one of prompt= or messages=.
prompt= is a template, and every string in it is
str.format_map-substituted with the non-reserved kwargs:
- str — one user message:
"Hello {name}". - dict with
system/userkeys — the standard two-message call.
messages= is a conversation: a full OpenAI messages array, passed
through untouched. Use it whenever the content is data rather than a
template — an agent's history, a multimodal block assembled upstream,
anything past two messages.
The split exists because formatting a conversation is destructive. Any
brace in any message — a tool returning {"city": "Hanoi"}, a user
pasting CSS, the model's own tool-call arguments — becomes a template
variable that does not exist, and the run dies on the next model call.
prompt= used to accept a list, which made that the default outcome
for every agent.
Inputs
prompt (str | dict): Message template. Mutually exclusive with messages.
messages (list): Ready OpenAI messages array, never formatted.
temperature (float): Sampling temperature. Default: 0.0.
max_tokens (int): Max output tokens. Default: None.
tools (list): Tool/function definitions. Default: None.
tool_choice (str | dict): Tool selection strategy. Default: None.
response_format (dict): Structured output format. Default: None.
(any): Template variables ({var} placeholders).
Outputs
content (str): Generated text.
role (str): Message role (usually "assistant").
finish_reason (str): Stop reason ("stop", "tool_calls", ...).
model_used (str): Actual model that served the request.
tool_calls (list): Tool-call objects (empty list when absent).
usage (dict): Flat token-cost metrics.
extras (dict): Bag of uncommon fields (thinking_content, refusal, logprobs).
Example::
llm = LLMOp.of(
resource="gpt-4o",
prompt={"system": "You are {role}.", "user": "{query}"},
role="helpful", query=PARENT["query"],
)
chat = LLMOp.of(resource="gpt-4o", messages=history["messages"])
Initialize LLMOp.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
resource
|
Optional[Union[str, List[str]]]
|
Resource key(s) for LLM in ResourceHub. - Single string: "gpt-4" - List for load balancing: ["gpt-4", "claude-3"] |
None
|
ratios
|
Optional[List[float]]
|
Weight ratios for load balancing. Must sum to 1.0. |
None
|
fallback
|
Optional[List[str]]
|
Fallback resource keys tried in order on hard failures
— refusals from the model, provider-side content filtering, or
exhausted transport retries (the underlying SDK gave up). NOT
triggered by parse/validator failures — those use |
None
|
batch_mode
|
bool
|
Use OpenAI Batch API (50% cheaper). |
False
|
seed
|
Optional[int]
|
Optional seed for load balancing RNG. |
None
|
fields
|
Optional[List[str]]
|
Optional list of |
None
|
parser
|
Optional[str]
|
Parser format when |
None
|
validators
|
Optional[Dict[str, List[Any]]]
|
Optional per-field allow-list validators applied after
extraction. Format: |
None
|
max_retries
|
int
|
Max semantic retries when the parser or validators
report an error. Default 0 (no retry — first parse failure
surfaces as |
0
|
retry_hint
|
bool
|
When True (default) and retrying, append the previous
LLM response and a "that failed — |
True
|
inputs
|
Dict[str, Any]
|
Input variable mappings. |
None
|
outputs
|
Dict[str, Any]
|
Output variable mappings. |
None
|
**kwargs
|
Any
|
Additional keyword arguments for BaseOp. |
{}
|
Source code in operonx/providers/ops/llm.py
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 | |
Attributes¶
specific_metadata
property
¶
Return LLM + prompt metadata dictionary.
Methods:¶
warmup
¶
normalize_trace_io
¶
Wrap OpenAI chat-format multimodal blocks as Media for tracing.
Prompt is formatted to messages first (using the current template vars),
then multimodal image/audio blocks are wrapped in Media for the
trace-time view. Real op state is untouched.
Source code in operonx/providers/ops/llm.py
of
¶
of(
resource=None,
*,
ratios=None,
fallback=None,
batch_mode=False,
seed=None,
prompt=None,
messages=None,
fields=None,
parser=None,
validators=None,
max_retries=0,
retry_hint=True,
**kwargs,
) -> LLMOp
Create an LLMOp with flat kwargs.
Simple mode::
llm = LLMOp.of(resource="gpt-4", prompt="Hello {name}", name="Alice")
Conversation — a message list that is data, never formatted::
chat = LLMOp.of(resource="gpt-4", messages=history["messages"])
Structured mode (replaces the removed ask() helper)::
llm = LLMOp.of(
resource="claude-haiku",
prompt="Classify: {speech}",
fields=["result: str"],
parser="xml",
validators={"result": ["CONFIRM", "DENY", "@FALLBACK"]},
max_retries=2,
speech=PARENT["speech"],
)
Source code in operonx/providers/ops/llm.py
serialize
¶
Serialize LLMOp for Rust backend, including backend configs.
Source code in operonx/providers/ops/llm.py
EmbeddingOp
¶
EmbeddingOp(
resource: Optional[str] = None,
inputs: Dict[str, Any] = None,
outputs: Dict[str, Any] = None,
**kwargs: Any,
)
Bases: BaseOp
Op that converts texts to vector embeddings via ResourceHub.
Wraps an embedding backend (e.g. BGE-M3, OpenAI, TEI) and returns a list of embedding vectors matching the input order.
Inputs
texts (list[str]): Texts to embed. Required.
Outputs
embeddings (list[list[float]]): Embedding vectors.
Example::
embed = EmbeddingOp.of(resource="bge-m3", texts=PARENT["texts"])
Initialize EmbeddingOp.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
resource
|
Optional[str]
|
Resource key for embedding model in ResourceHub (e.g., "bge-m3") |
None
|
inputs
|
Dict[str, Any]
|
Input variable mappings |
None
|
outputs
|
Dict[str, Any]
|
Output variable mappings |
None
|
**kwargs
|
Any
|
Additional keyword arguments for BaseOp |
{}
|
Source code in operonx/providers/ops/embedding.py
Attributes¶
specific_metadata
property
¶
Return embedding-specific metadata dictionary.
Methods:¶
warmup
¶
of
¶
Create an EmbeddingOp with flat kwargs.
Example::
embed = EmbeddingOp.of(resource="bge-m3", texts=PARENT["texts"], outputs={"*": PARENT})
Source code in operonx/providers/ops/embedding.py
serialize
¶
Serialize EmbeddingOp for Rust backend, including backend config.
Source code in operonx/providers/ops/embedding.py
RerankOp
¶
RerankOp(
resource: Optional[str] = None,
inputs: Dict[str, Any] = None,
outputs: Dict[str, Any] = None,
**kwargs: Any,
)
Bases: BaseOp
Op that scores and re-orders documents by relevance to a query.
Wraps a reranker backend (e.g. BGE-M3, Pinecone, TEI) accessed via ResourceHub. Returns documents sorted by relevance score.
Inputs
query (str): The query to rank against. Required. documents (list[str]): Documents to rerank. Required. top_k (int): Max results to return. Default: -1 (all). threshold (float): Min score cutoff. Default: 0.0.
Outputs
reranks (list[dict]): Reranked results with index, score,
and document fields.
Example::
rerank = RerankOp.of(
resource="bge-m3",
query=PARENT["query"],
documents=PARENT["docs"],
top_k=5,
)
Initialize RerankOp.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
resource
|
Optional[str]
|
Resource key for reranker in ResourceHub (e.g., "bge-m3") |
None
|
inputs
|
Dict[str, Any]
|
Input variable mappings |
None
|
outputs
|
Dict[str, Any]
|
Output variable mappings |
None
|
**kwargs
|
Any
|
Additional keyword arguments for BaseOp |
{}
|
Source code in operonx/providers/ops/rerank.py
Attributes¶
specific_metadata
property
¶
Return rerank-specific metadata dictionary.
Methods:¶
warmup
¶
of
¶
Create a RerankOp with flat kwargs.
Example::
rerank = RerankOp.of(resource="bge-m3", query=PARENT["q"], documents=PARENT["docs"])
Source code in operonx/providers/ops/rerank.py
serialize
¶
Serialize RerankOp for Rust backend, including backend config.
Source code in operonx/providers/ops/rerank.py
Retrieval (1.1.0)¶
VectorSearchOp and DocFetchOp are a pair. The vector index is
derived data holding vectors, ids, and filterable metadata; document
content lives in the store of record. VectorSearchOp answers which
documents, DocFetchOp answers what they say — and returns rows in
the same order as the ids it was given, so hits stay aligned with their
scores.
See the RAG guide for the full pipeline and
operonx/providers/vector_stores/README.md for each backend's filter
dialect.
VectorSearchOp
¶
VectorSearchOp(
resource: Optional[str] = None,
inputs: Dict[str, Any] = None,
outputs: Dict[str, Any] = None,
**kwargs: Any,
)
Bases: BaseOp
Op that runs vector similarity search via ResourceHub.
Inputs
query_vector (list[float]): Query embedding. Required. top_k (int): Number of hits. Default 10. filter (dict | str): Backend-native metadata filter — dict for most backends, an expression string for Milvus. Never translated by operonx; each backend validates its own dialect and raises on shapes it doesn't recognise. Default None. collection (str): Collection / table / index to search. Default None, meaning the resource's configured default.
Outputs
ids (list): Hit ids, best match first.
scores (list[float]): Similarity per hit, index-aligned with ids.
metadata (list[dict]): Indexed filterable fields per hit,
index-aligned. {} for backends that store none.
Example::
hits = VectorSearchOp.of(
resource="docs",
query_vector=emb["embeddings"][0],
top_k=20,
filter={"tenant": "acme"},
)
docs = DocFetchOp.of(resource="main", ids=hits["ids"], collection="docs")
Initialize VectorSearchOp.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
resource
|
Optional[str]
|
Resource key for the vector store. A bare name is
looked up as |
None
|
inputs
|
Dict[str, Any]
|
Input variable mappings. |
None
|
outputs
|
Dict[str, Any]
|
Output variable mappings. |
None
|
**kwargs
|
Any
|
Additional keyword arguments for BaseOp. |
{}
|
Source code in operonx/providers/ops/vector_search.py
Attributes¶
specific_metadata
property
¶
Return vector-search-specific metadata.
Methods:¶
warmup
¶
of
¶
Create a VectorSearchOp with flat kwargs.
Example::
hits = VectorSearchOp.of(resource="docs", query_vector=emb["embeddings"][0])
Source code in operonx/providers/ops/vector_search.py
serialize
¶
Serialize for the Rust backend, including resource config.
Source code in operonx/providers/ops/vector_search.py
DocFetchOp
¶
DocFetchOp(
resource: Optional[str] = None,
inputs: Dict[str, Any] = None,
outputs: Dict[str, Any] = None,
**kwargs: Any,
)
Bases: BaseOp
Op that hydrates ids into records via ResourceHub.
Inputs
ids (list): Primary keys to fetch. Required — typically
VectorSearchOp's ids output.
collection (str): Table / collection. Default None, meaning the
resource's configured default.
fields (list[str]): Column projection. Default None = all.
id_field (str): Primary-key field name. Default None, meaning the
resource's configured default (itself defaulting to "id").
Outputs
rows (list[dict]): Records in ids order.
missing (list): Ids that matched no record.
Two guarantees, and they are why this is an op rather than a snippet:
rowsfollowsidsorder. Search returns score-ordered ids;SELECT … WHERE id = ANY(…)returns arbitrary order. Zipping them naively pairs every document with the wrong score — silently.- Missing ids surface in
missinginstead of quietly shorteningrows. An index that has drifted from the store of record is a real condition and should be observable.
Scope is fetch-by-ids with an optional projection. No joins, writes,
transactions, or custom SQL — for those, write a bare @op against
your own client.
Example::
hits = VectorSearchOp.of(resource="docs", query_vector=emb["embeddings"][0], top_k=20)
docs = DocFetchOp.of(resource="main", ids=hits["ids"],
collection="docs", fields=["id", "title", "content"])
Initialize DocFetchOp.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
resource
|
Optional[str]
|
Resource key for the document store. A bare name is
looked up as |
None
|
inputs
|
Dict[str, Any]
|
Input variable mappings. |
None
|
outputs
|
Dict[str, Any]
|
Output variable mappings. |
None
|
**kwargs
|
Any
|
Additional keyword arguments for BaseOp. |
{}
|
Source code in operonx/providers/ops/doc_fetch.py
Attributes¶
Methods:¶
warmup
¶
of
¶
Create a DocFetchOp with flat kwargs.
Example::
docs = DocFetchOp.of(resource="main", ids=hits["ids"], collection="docs")
Source code in operonx/providers/ops/doc_fetch.py
serialize
¶
Serialize for the Rust backend, including resource config.
Source code in operonx/providers/ops/doc_fetch.py
Ordering helpers¶
Vector search returns score-ordered ids; key-based fetches return
arbitrary order. DocFetchOp reconciles them internally — these are
exported for anyone writing their own fetch op.
reorder_by_ids
¶
reorder_by_ids(
rows: Sequence[Dict[str, Any]], ids: Sequence[Any], key: str = "id"
) -> List[Dict[str, Any]]
Return rows ordered to match ids, dropping ids with no row.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
rows
|
Sequence[Dict[str, Any]]
|
Fetched records, in any order. |
required |
ids
|
Sequence[Any]
|
Desired order — typically |
required |
key
|
str
|
Field on each row holding its primary key. |
'id'
|
Returns:
| Type | Description |
|---|---|
List[Dict[str, Any]]
|
Rows in |
List[Dict[str, Any]]
|
func: |
Note
Ids are matched by value, so an int id from the vector index
will not match a str key from the document store. Keep the two
stores' key types aligned.
Source code in operonx/providers/doc_stores/_reorder.py
partition_by_ids
¶
partition_by_ids(
rows: Sequence[Dict[str, Any]], ids: Sequence[Any], key: str = "id"
) -> Tuple[List[Dict[str, Any]], List[Any]]
Split a fetch result into ordered rows and missing ids.
Missing ids are a real condition, not noise: they mean the derived index has drifted from the store of record (deleted document, failed sync). Surfacing them beats silently returning a shorter list.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
rows
|
Sequence[Dict[str, Any]]
|
Fetched records, in any order. |
required |
ids
|
Sequence[Any]
|
Desired order. |
required |
key
|
str
|
Field on each row holding its primary key. |
'id'
|
Returns:
| Type | Description |
|---|---|
List[Dict[str, Any]]
|
|
List[Any]
|
yield the same row; duplicate keys in |
Source code in operonx/providers/doc_stores/_reorder.py
Structured output (1.0.0)¶
LLMOp gained inline parsing + validators + error-guided semantic
retry in 1.0.0 — pass fields=, parser=, validators=, max_retries=
directly to LLMOp.of() (see class docs above). The old standalone
ask() helper was removed.
For pure text parsing without an LLM call, use the pure functions in
operonx.providers.parsing:
parse_and_extract
¶
parse_and_extract(
text: str,
parser: ParserFormat,
fields: List[ExtractField],
validators: Optional[Dict[str, List[Any]]] = None,
) -> Dict[str, Any]
Parse text, extract fields, and optionally validate.
Always returns a dict shaped as {**field_values, "error": None|str}.
Never raises — the error value tells the caller whether to retry.
Semantics match the old ParserOp._process exactly so the surface
LLMOp exposes is a faithful merge of what ask() provided before.
Source code in operonx/providers/parsing.py
ExtractField
dataclass
¶
A field to pull out of parsed text.
Attributes:
| Name | Type | Description |
|---|---|---|
output_key |
str
|
Key under which the extracted value is returned. |
chain_path |
List[str]
|
Dot-separated path into the parsed dict. |
type_hint |
str
|
Type name used for coercion ( |
optional |
bool
|
When True, absence is an answer rather than an error. |
Methods:¶
from_string
classmethod
¶
Parse a schema string like "user.address.city: str".
Missing type hint defaults to Any.
A ? before the colon marks the field optional::
"result: str" # required — absence is a parse error
"chosen_date?: str" # optional — absence yields None
Optional matters for a union schema, where one field list covers several response shapes and most entries are expected to be absent on any given call. Without the marker every such call would report missing fields and burn its retries.
Source code in operonx/providers/parsing.py
Resource resolution¶
Backend selection happens by name, not by direct construction.
Wire your resources.yaml:
llm:gpt-4o-mini:
api_type: openai
api_key: ${OPENAI_API_KEY}
base_url: https://api.openai.com/v1
model: gpt-4o-mini
embedding:openai:
api_type: openai
api_key: ${OPENAI_API_KEY}
base_url: https://api.openai.com/v1
model: text-embedding-3-small
dimensions: 1536
Then reference by key in your op definitions:
llm = LLMOp.of(resource="gpt-4o-mini", messages=PARENT["msgs"])
embed = EmbeddingOp.of(resource="openai", texts=PARENT["docs"])
Full reference — including the five disambiguated failure branches when a key is missing or unset — is in Resource hub.
Config classes¶
The Pydantic models behind resources.yaml. You rarely construct
these directly; the framework loads them from YAML. Listed here for
reference.
- LLM —
LLMConfig,OpenAIConfig,AzureConfig,GeminiConfig,AnthropicConfig,LLMType(inoperonx.providers.llms). - Embedding —
EmbeddingConfig,EmbeddingType(inoperonx.providers.embeddings). - Reranker —
RerankingConfig,RerankingType(inoperonx.providers.rerankers). - Vector store —
VectorStoreConfig,VectorStoreType,VectorStoreMetric(inoperonx.providers.vector_stores). - Document store —
DocStoreConfig,DocStoreType(inoperonx.providers.doc_stores). - Auth —
KeycloakTokenConfig(inoperonx.providers.auth).
Factory functions¶
Resolve a config to a backend instance. Used internally by
ResourceHub — most
users don't call these directly.
create_llm
¶
Create an LLM backend from config.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
LLMConfig
|
LLMConfig with api_type determining which backend to create. |
required |
Returns:
| Type | Description |
|---|---|
BaseLLM
|
BaseLLM instance. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If api_type is unsupported. |
ImportError
|
With a helpful pointer to the right |
Source code in operonx/providers/llms/factory.py
create_embedding
¶
Create an embedding backend from config.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
EmbeddingConfig
|
EmbeddingConfig with api_type determining which backend to create. |
required |
Returns:
| Type | Description |
|---|---|
BaseEmbedder
|
BaseEmbedder instance. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If api_type is unsupported. |
ImportError
|
With a helpful pointer to the right |
Source code in operonx/providers/embeddings/factory.py
create_reranking
¶
Create a reranking backend from config.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
RerankingConfig
|
RerankingConfig with api_type determining which backend to create. |
required |
Returns:
| Type | Description |
|---|---|
BaseReranker
|
BaseReranker instance. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If api_type is unsupported. |
ImportError
|
With a helpful pointer to the right |
Source code in operonx/providers/rerankers/factory.py
create_vector_store
¶
Create a vector store backend from config.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
VectorStoreConfig
|
VectorStoreConfig whose api_type selects the backend. |
required |
Returns:
| Type | Description |
|---|---|
BaseVectorStore
|
BaseVectorStore instance. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If api_type is unsupported. |
ImportError
|
With a pointer to the right |
Source code in operonx/providers/vector_stores/factory.py
create_doc_store
¶
Create a document store backend from config.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
DocStoreConfig
|
DocStoreConfig whose api_type selects the backend. |
required |
Returns:
| Type | Description |
|---|---|
BaseDocStore
|
BaseDocStore instance. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If api_type is unsupported. |
ImportError
|
With a pointer to the right |
Source code in operonx/providers/doc_stores/factory.py
create_auth
¶
Create a KeycloakTokenProvider from config.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
KeycloakTokenConfig
|
KeycloakTokenConfig instance. |
required |
Returns:
| Type | Description |
|---|---|
KeycloakTokenProvider
|
KeycloakTokenProvider instance. |
Raises:
| Type | Description |
|---|---|
ImportError
|
with a pointer to the right |