The Universal Embedding Gateway for InterSystems IRIS
One
EMBEDDING()call. Any provider. Same vector space.
InterSystems IRIS 2026 ships a native embeddings runtime โ %Embedding.Interface + EMBEDDING() SQL โ but each embedding provider has its own quirks:
- Ollama speaks OpenAI-compatible JSON but needs no auth
- OpenAI uses
Authorization: Bearer - Azure OpenAI wants
api-key(notBearer) and a deployment-scoped URL - Cohere returns embeddings under
embeddings.float[0] - Gemini authenticates via
?key=on the URL itself - Mistral is OpenAI-shaped but adds
output_dimension/output_dtypefor Codestral truncation - Voyage AI adds
input_type(queryvsdocument), truncation and int8/binary quantization - Jina AI requires
inputas an array (even for one text) and exposestask+late_chunkingfor coherent long-doc chunks - AWS Bedrock speaks SigV4 (not Bearer) and switches payload/response shape per family (
amazon.titan-embed-*vscohere.embed-*hosted on Bedrock) - Google Vertex AI needs region + project in the URL and an OAuth 2.0 access token in the header โ the same
text-embedding-*family name as OpenAI lives here too
Swapping providers usually means rewriting glue code, and mixing them in fallback flows silently mixes vector spaces โ a data-integrity disaster that only surfaces months later, when your similarity searches start returning nonsense.
dc.omniEmbedding changes that.
Plug it in once as your EmbeddingClass and every EMBEDDING('text', 'config') call โ from SQL, from ObjectScript, from Interoperability โ routes to the right provider, retries transient errors, opens a circuit breaker on failing providers, and refuses to fall back to a config whose vector space differs from the primary.
- โ
Ten providers, one interface: Ollama ยท OpenAI ยท Azure OpenAI ยท Cohere ยท Gemini ยท Mistral (text + code) ยท Voyage (text + code + domain) ยท Jina (with
late_chunking) ยท AWS Bedrock (SigV4, Titan + Cohere via Bedrock) ยท Google Vertex AI (text-embedding + gemini-embedding) - โ
Native HTTP:
%Net.HttpRequestโ no Python required on the hot path - โ
Resilient: exponential backoff,
Retry-Afterhonored, circuit breaker per provider - โ Safe fallback: vector-space invariance is enforced before any HTTP call
- โ
Secure by default:
apiKeyis a credential name, never the raw secret - โ CI-friendly: the whole happy path runs against local Ollama, no cloud keys
dc.omniEmbedding sits between IRIS's native embedding runtime and any of the ten supported external providers, applying three architectural pillars:
- HTTP native first โ the happy path uses
%Net.HttpRequestend-to-end; Embedded Python is opt-in only (for accuratetiktokentoken counts). - Polymorphism by class hierarchy โ a Template Method in
provider.Basefixes the sequenceValidateConfig โ SetAuth โ GetEmbeddingsUrl โ BuildPayload โ RetryWithBackoff โ ParseResponse; each provider overrides only what differs. - Vector-space integrity as a hard invariant โ a fallback with a different
modelNameordimensionsis fatal, never a silent downgrade.
| Class | Role |
|---|---|
dc.omniEmbedding.Interface |
Bridge to %Embedding.Interface โ the class you register in %Embedding.Config |
dc.omniEmbedding.Engine |
Provider resolution, circuit breaker, fallback with vector-space check |
dc.omniEmbedding.provider.Base |
Abstract Template Method; retry/backoff; credential resolution |
dc.omniEmbedding.provider.OpenACompatible |
Shared payload/parse for the OpenAI-shaped family |
dc.omniEmbedding.provider.Ollama |
Local Ollama, keyless, /v1/embeddings |
dc.omniEmbedding.provider.OpenAi |
api.openai.com, Bearer auth |
dc.omniEmbedding.provider.AzureOpenAi |
Deployment-scoped URL, api-key header |
dc.omniEmbedding.provider.Cohere |
v2/embed, nested embeddings.float[0] |
dc.omniEmbedding.provider.Gemini |
Auth via ?key=, content.parts[].text payload |
dc.omniEmbedding.provider.Mistral |
api.mistral.ai/v1/embeddings, text (mistral-embed) + code (codestral-embed), optional output_dimension / output_dtype |
dc.omniEmbedding.provider.Voyage |
api.voyageai.com/v1/embeddings, text (voyage-3*) + code (voyage-code-3) + domain (voyage-finance-2, voyage-law-2, voyage-multilingual-2); input_type (query/document), truncation, output_dimension, output_dtype |
dc.omniEmbedding.provider.Jina |
api.jina.ai/v1/embeddings, jina-embeddings-v3 and jina-* variants; input wrapped as array; task, dimensions, late_chunking, embedding_type |
dc.omniEmbedding.provider.Bedrock |
bedrock-runtime.{region}.amazonaws.com/model/{modelId}/invoke; SigV4 auth via util.SigV4; families amazon.titan-embed-* (inputText/normalize, response .embedding) and cohere.embed-* on Bedrock (texts[]/input_type, response .embeddings[0]); optional sessionTokenCredential for STS/AssumeRole |
dc.omniEmbedding.util.SigV4 |
AWS Signature V4 signer, isolated + testable; key derivation matches AWS's official test vector byte-for-byte |
dc.omniEmbedding.provider.VertexAi |
{region}-aiplatform.googleapis.com/.../models/{model}:predict; Bearer OAuth 2.0 token; payload instances[{content, task_type?, title?}] + optional parameters{outputDimensionality, autoTruncate}; response predictions[0].embeddings.values |
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Any caller โ SQL, ObjectScript, โ
โ Interoperability, Embedded Python โ
โ EMBEDDING('text', 'config') โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ %Embedding.Interface (IRIS runtime) โ
โ Reads EmbeddingClass from %Embedding.Config, dispatches: โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ dc.omniEmbedding.Interface (bridge) โ
โ ParseConfig ยท validate input ยท Engine.Embed() โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ dc.omniEmbedding.Engine โ
โ ResolveProvider ยท CheckBreaker ยท RecordSuccess/Failure โ
โ TryFallback (vector-space invariant โ fatal on mismatch) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ provider.Base.Execute() โ Template Method โ
โ ValidateConfig โ SetAuth โ GetEmbeddingsUrl โ โ
โ BuildPayload โ RetryWithBackoff โ ParseResponse โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ %Net.HttpRequest โ { Ollama | OpenAI | Azure | Cohere | โ
โ Gemini | Mistral | Voyage | Jina | โ
โ Bedrock (SigV4) | VertexAI (OAuth) } โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- Retry: 429 and 5xx are retried with
MIN(baseDelayMs * 2^(attempt-1) + jitter, maxDelayMs)โRetry-Afteris honored verbatim when present. 4xx (except 429) fast-fail. - Circuit breaker: per
provider|modelName|apiBasekey stored in^omniEmbedding.Breaker. Opens after 5 consecutive failures; 60 s cooldown; half-open lets one probe through. - Fallback: iterates
config.fallbacksin order. Each candidate is refused before any HTTP call ifmodelNameordimensionsdiffer from the primary โ vectors from different spaces never mix.
- InterSystems IRIS 2026.2+ (native
%Embedding.Interfaceand%Library.Vectorare required) - Docker and Docker Compose (if you use the bundled dev container)
- Optional: Ollama at
http://localhost:11434for zero-cost end-to-end integration tests - Optional: Embedded Python with
tiktokenfor exact OpenAI token counts (a conservative floor is used when unavailable)
git clone https://github.com/henryhamon/dc.omniEmbedding.git
cd dc.omniEmbeddingdocker-compose up -d --buildThe IPM module dc-omni-embedding is loaded automatically into the IRISAPP namespace.
zpm "install dc-omni-embedding"Register dc.omniEmbedding.Interface as your EmbeddingClass, then store a JSON configuration:
INSERT INTO %Embedding.Config (Name, Configuration, EmbeddingClass, VectorLength, Description)
VALUES (
'ollama-nomic',
'{"provider":"ollama","modelName":"nomic-embed-text:latest","apiBase":"http://host.docker.internal:11434","dimensions":768}',
'dc.omniEmbedding.Interface',
768,
'Local Ollama (nomic-embed-text)'
)From SQL:
SELECT EMBEDDING('the quick brown fox', 'ollama-nomic')From ObjectScript:
Set vec = ##class(dc.omniEmbedding.Interface).Embedding(
"the quick brown fox",
"{""provider"":""ollama"",""modelName"":""nomic-embed-text:latest"",""dimensions"":768}"
)Every field is a top-level key on the JSON stored in %Embedding.Config.Configuration.
| Field | Required | Description |
|---|---|---|
provider |
one of: ollama, openai, azure, cohere, gemini, mistral, voyage, jina, bedrock, vertex (aliases: vertexai) โ or infer from modelName. Bedrock and Vertex do NOT infer (their model prefixes collide with OpenAI / Cohere / Gemini) |
Explicit provider selection |
modelName |
yes | Model identifier for the target provider |
dimensions |
yes | Expected vector length โ enforced when checking fallbacks |
apiKey |
provider-dependent | Credential name (not the value) โ resolved via Ens.Config.Credentials |
apiBase |
provider-dependent | Override default endpoint (Ollama, Azure) |
sslConfig |
no | Name of a %SSL.Config for TLS |
httpTimeout |
no (default 30s) |
HTTP timeout in seconds |
retry.maxAttempts |
no (default 3) |
Max attempts including the first |
retry.baseDelayMs |
no (default 500) |
Base backoff delay |
retry.maxDelayMs |
no (default 8000) |
Backoff cap |
retry.honorRetryAfter |
no (default true) |
Use the Retry-After header when present |
fallbacks |
no | Array of %Embedding.Config names โ each must share the primary's vector space |
- Azure OpenAI:
deployment,apiVersion(deploymentis optional whenapiBaseis already deployment-scoped) - Cohere:
inputType(defaultsearch_document) - Gemini:
taskType(defaultRETRIEVAL_DOCUMENT) - Mistral:
outputDimension(truncate โ Codestral supports up to 3072),outputDtype(floatยทint8ยทuint8ยทbinaryยทubinary) - Voyage:
inputType(queryยทdocument),truncation(bool),outputDimension,outputDtype - Jina:
task(e.g.retrieval.queryยทretrieval.passageยทtext-matchingยทclassification),outputDimension(mapped to Jina'sdimensions),lateChunking(bool โ coherent long-doc chunks),outputDtype(mapped to Jina'sembedding_type) - AWS Bedrock:
region(required, e.g.us-east-1),sessionTokenCredential(optional, name of a second credential holding an STS token);inputTypefor Cohere-on-Bedrock family (defaultsearch_document);dimensionsfor Titan family - Google Vertex AI:
region(e.g.us-central1) +projectrequired;apiKeynames a credential whose value is a valid OAuth 2.0 access token (rotation is external โ service-account JWT auto-refresh is future work); optionaltaskType(RETRIEVAL_QUERYยทRETRIEVAL_DOCUMENTยทSEMANTIC_SIMILARITYยทCLASSIFICATIONยทCLUSTERINGยทCODE_RETRIEVAL_QUERY),title(only meaningful withRETRIEVAL_DOCUMENT),outputDimension(mapped tooutputDimensionality),autoTruncate(bool)
{
"provider": "ollama",
"modelName": "nomic-embed-text:latest",
"apiBase": "http://host.docker.internal:11434",
"dimensions": 768,
"fallbacks": ["ollama-backup"]
}An OpenAI config with text-embedding-3-small (1536 dims) as a fallback would be rejected before any HTTP call โ different vector space.
Register two credentials โ the AWS access key/secret pair, and the STS session token โ then reference both by name:
Set cred = ##class(Ens.Config.Credentials).%New()
Set cred.SystemName = "aws-prod"
Set cred.Username = "aws"
Set cred.Password = "AKIA...prod:wJalrXUtnFEMI/K7MDENG..." ; accessKeyId:secretAccessKey
Do cred.%Save()
Set tok = ##class(Ens.Config.Credentials).%New()
Set tok.SystemName = "aws-prod-session"
Set tok.Username = "sts"
Set tok.Password = "FQoDYXdz...session-token..."
Do tok.%Save(){
"provider": "bedrock",
"modelName": "amazon.titan-embed-text-v2:0",
"region": "us-east-1",
"apiKey": "aws-prod",
"sessionTokenCredential": "aws-prod-session",
"dimensions": 1024
}Bedrock requires provider: "bedrock" explicit โ the gateway will NOT infer it from cohere.embed-* or amazon.titan-embed-* prefixes to avoid collision with the direct Cohere provider.
{
"provider": "mistral",
"modelName": "codestral-embed",
"apiKey": "mistral-prod",
"outputDimension": 1024,
"outputDtype": "float",
"dimensions": 1024
}outputDimension tells Mistral to truncate the vector server-side (Codestral supports up to 3072); dimensions is what the gateway enforces on fallbacks and what IRIS stores.
config.apiKey is always a credential name, never the raw secret. The gateway resolves the name via Ens.Config.Credentials.%OpenId(name).Password. Register one from the IRIS terminal:
Set cred = ##class(Ens.Config.Credentials).%New()
Set cred.SystemName = "openai-prod"
Set cred.Username = "apikey"
Set cred.Password = "sk-...your-real-key..."
Do cred.%Save()Then reference it as "apiKey": "openai-prod" in your config. Exceptions raised for a missing credential carry only the name โ never the value. See TestSecurity (Property 15).
dc.omniEmbedding/
โโโ src/dc/omniEmbedding/
โ โโโ Interface.cls # Bridge to %Embedding.Interface
โ โโโ Engine.cls # Dispatch ยท breaker ยท fallback
โ โโโ provider/
โ โโโ Base.cls # Template Method ยท retry ยท ResolveApiKey
โ โโโ OpenACompatible.cls # Shared payload/parse for OpenAI-family
โ โโโ Ollama.cls
โ โโโ OpenAi.cls
โ โโโ AzureOpenAi.cls
โ โโโ Cohere.cls
โ โโโ Gemini.cls
โ โโโ Mistral.cls # mistral-embed (text) + codestral-embed (code)
โ โโโ Voyage.cls # voyage-3, voyage-code-3, voyage-finance-2, ...
โ โโโ Jina.cls # jina-embeddings-v3, with late_chunking
โ โโโ Bedrock.cls # Titan + Cohere-via-Bedrock; overrides Execute for SigV4 ordering
โ โโโ VertexAi.cls # text-embedding-* + gemini-embedding-*, OAuth 2.0 Bearer
โโโ src/dc/omniEmbedding/util/
โ โโโ SigV4.cls # AWS Signature V4 signer (isolated, testable)
โโโ tests/dc/omniEmbedding/
โ โโโ TestOpenACompatible.cls # Payload shape, parse errors, tiktoken floor
โ โโโ TestOllama.cls # URL construction + end-to-end via Ollama
โ โโโ TestAzureUrl.cls # Property 8 โ URL composition invariants
โ โโโ TestCohere.cls # Property 10 โ nested response parse
โ โโโ TestGemini.cls # Property 10 โ auth-in-URL, no-op SetAuth
โ โโโ TestMistral.cls # URL, dispatch, output_dimension/dtype passthrough
โ โโโ TestVoyage.cls # URL, dispatch, inputType enum, all passthroughs
โ โโโ TestJina.cls # Property 17 โ input always array; late_chunking; camelCaseโwire mapping
โ โโโ TestSigV4.cls # Property 18 โ key derivation matches AWS official vector byte-for-byte
โ โโโ TestBedrock.cls # URL, families, ParseResponse per family, credential formats, Property 19 (SigV4 applied), dispatch rules
โ โโโ TestVertexAi.cls # URL, ValidateConfig, Property 20 (parameters block only when needed), dispatch regression (no prefix inference)
โ โโโ TestResilience.cls # Properties 11-14 โ backoff & breaker
โ โโโ TestFallback.cls # Properties 4-5 โ vector-space invariance
โ โโโ TestSecurity.cls # Property 15 โ credentials never leak
โ โโโ TestInterface.cls # Properties 2-3 โ validation & no empty vec
โ โโโ FakeEngine.cls # Test double for fallback tests
โโโ module.xml # IPM manifest
โโโ docker-compose.yml
โโโ README.md
Run every suite from the IRIS terminal:
Set ^UnitTestRoot = "/tmp/ut"
Do ##class(%UnitTest.Manager).RunTest(":dc.omniEmbedding.tests.TestOllama", "/nodelete/noload")Or via IPM:
zpm "test dc-omni-embedding"The end-to-end integration test in TestOllama auto-probes localhost:11434 then host.docker.internal:11434 and skips gracefully if Ollama is unavailable โ CI stays green without any cloud key.
-
%Embedding.Interfacebridge & runtime integration - Provider resolution by explicit name or model-prefix inference
- Ten providers: Ollama, OpenAI, Azure OpenAI, Cohere, Gemini, Mistral (text + code), Voyage (text + code + domain), Jina (with
late_chunking), AWS Bedrock (SigV4, Titan + Cohere-on-Bedrock), Google Vertex AI (text-embedding + gemini-embedding) - Retry with exponential backoff +
Retry-Afterhonoring - Circuit breaker (5 failures / 60 s cooldown / half-open probe)
- Fallback with vector-space invariance (fatal on mismatch)
- Credentials via
Ens.Config.Credentials(secret never leaks) - Polymorphic
EstimateTokenCount(tiktoken ยท Cohere ยท Gemini formulas) - Property-based test suite covering every correctness invariant
- Batching support (multiple inputs per request)
- Vertex AI: automatic OAuth 2.0 access-token refresh via service-account JWT-bearer flow (currently the token is supplied externally through
Ens.Config.Credentials) - Optional in-process embedding cache with TTL
dc.omniEmbedding is designed and developed with ๐ by:
- Henry Pereira โ architecture, implementation, testing
This project is licensed under the MIT License.
