agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
546 lines • 28 kB
TypeScript
/**
* embedders — ready-made {@link Embedder} implementations for the
* embedding-backed scorers (memory retrieval, toolChoiceRecorder / scoreMargin).
*
* The core deliberately ships only `mockEmbedder` (bring-your-own). These are
* OPTIONAL and never pulled into the core: each heavy backend is an OPTIONAL
* PEER DEPENDENCY, imported LAZILY on first embed, so you install ONLY the one
* you use and agentfootprint stays dependency-free.
*
* openaiEmbedder() — hosted; needs OPENAI_API_KEY; no extra install (fetch).
* bedrockEmbedder() — hosted on AWS; Titan Text Embeddings and Cohere Embed
* v3 (the model id picks the body shape); credentials
* come from the AWS chain, so no key option at all.
* peer dep: @aws-sdk/client-bedrock-runtime.
* geminiEmbedder() — hosted on Google; Vertex (project + Application
* Default Credentials) or the Gemini API (one key), the
* same two doors as `gemini()`. Matryoshka sizes, real
* task types, and it REFUSES a vector the service admits
* it clipped. peer dep: @google/genai.
* localEmbedder() — on-device sentence-transformer; no key; offline after a
* one-time model fetch. peer dep: @huggingface/transformers.
* staticEmbedder() — pure-JS Model2Vec static vectors; no key, no network
* (weights bundled). peer dep: @yarflam/potion-base-8m.
*
* All five satisfy the same `Embedder` shape, so they drop into
* `toolChoiceRecorder({ embedder })` / `semanticPipeline({ embedder })` etc.
* unchanged. Dimensions differ per model — never mix two in one store.
*
* ─── Bundlers / browsers: pass `backend` ────────────────────────────────
*
* The lazy `import(spec)` above keeps the peer deps optional, but a BUNDLER
* cannot see through a variable specifier: the bare name survives into the
* output and the browser throws
* `TypeError: Failed to resolve module specifier '@huggingface/transformers'`
* at first embed. So both on-device factories also accept an ALREADY-IMPORTED
* module — a static import your own bundler resolves:
*
* import * as transformers from '@huggingface/transformers';
* const embedder = localEmbedder({ backend: transformers });
*
* Same mechanism as the `client` option on the store adapters (RedisStore,
* AgentCoreStore): the library states the surface it needs, the host owns the
* construction. Nothing changes for Node callers who pass nothing.
*
* Not an import path of its own since 9.0.0. This is the implementation barrel
* behind `agentfootprint/providers`, which re-exports every name here — same
* symbols, one door. Import from the door.
*/
export type { Embedder } from '../memory/embedding/types.js';
import type { Embedder } from '../memory/embedding/types.js';
import { type GoogleGenAIConnectionOptions } from '../adapters/llm/googleGenAI.js';
export interface OpenAIEmbedderOptions {
/** Default: process.env.OPENAI_API_KEY. */
readonly apiKey?: string;
/** Default: 'text-embedding-3-small'. */
readonly model?: string;
/**
* Shorten the vectors the model returns (OpenAI's Matryoshka truncation).
*
* When set, the value is SENT as the `dimensions` request parameter AND
* reported as `.dimensions` — the two can never disagree. Only supported on
* `text-embedding-3` and later models; ada-002 rejects it, which is exactly
* why nothing is sent unless you ask.
*
* Leave it unset to get the model's native size (looked up from
* {@link NATIVE_DIMENSIONS}). Required for a model this library doesn't know
* — see {@link openaiEmbedder}.
*/
readonly dimensions?: number;
/** Override the API base (Azure/OpenAI-compatible gateways). */
readonly baseURL?: string;
}
/**
* OpenAI's hosted embeddings endpoint.
*
* `.dimensions` is the length callers WILL get back, never an assumption:
* an explicit `{ dimensions }` is sent to the API and reported; otherwise the
* model's documented native size is reported. A model outside
* {@link NATIVE_DIMENSIONS} (a gateway, a self-hosted model behind `baseURL`,
* an Azure deployment name, a future OpenAI model) has no size this library can
* know, so it is a construction-time error rather than a guess that a vector
* store would silently trust.
*
* `.maxInputChars` (9.1.0) reports **32,000** for the three models above:
* their documented 8,191-token window at the stated {@link CHARS_PER_TOKEN}
* assumption. An indexer reads it in preference to its own 2,000-character
* default, so a corpus split at 2,500 characters is embedded WHOLE here
* instead of being clipped by a default measured on an on-device model.
* An unknown model declares no ceiling — the indexer's default stands, which
* is conservative rather than wrong.
*
* @throws if there is no API key, or if `model` is unknown and `dimensions`
* was not supplied.
*/
export declare function openaiEmbedder(options?: OpenAIEmbedderOptions): Embedder;
/**
* The slice of `@aws-sdk/client-bedrock-runtime` {@link bedrockEmbedder} uses.
*
* Structural, so the real SDK, a pre-built client shared with the rest of your
* app, or a test double all satisfy it without this package taking a hard type
* dependency on the optional peer.
*/
export interface BedrockRuntimeLikeClient {
/**
* `send(command, options?)` — the second argument is where the AWS SDK
* takes an `abortSignal`, and it is passed whenever the caller supplied
* one, so an aborted indexing run stops paying for embeddings it will
* throw away.
*/
send(command: unknown, options?: {
abortSignal?: AbortSignal;
}): Promise<unknown>;
}
/** The two SDK constructors this embedder needs. */
export interface BedrockRuntimeSdkModule {
readonly BedrockRuntimeClient?: new (config: {
region?: string;
}) => BedrockRuntimeLikeClient;
readonly InvokeModelCommand?: new (input: unknown) => unknown;
}
/**
* The request/response SHAPE a Bedrock embedding model speaks (9.3.0).
*
* `InvokeModel` is one operation with a vendor-specific body on both sides:
* Titan takes `{ inputText }` and answers `{ embedding }`, Cohere takes
* `{ texts, input_type }` and answers `{ embeddings }`. One model id therefore
* does not describe one call, and until 9.3.0 this factory sent Titan's body to
* everything — so a Cohere model id was accepted at construction (with
* `dimensions`) and failed at the first embed, against the real service, with a
* validation error from AWS rather than a sentence from here.
*/
export type BedrockEmbeddingFamily = 'titan' | 'cohere';
/**
* Cohere's `input_type`, which is a real parameter and not a hint: the v3
* models embed a QUERY and a DOCUMENT into deliberately different places, and
* the two are meant to be compared with each other. Sending one value for both
* halves is a measurable loss of retrieval quality, not a style choice — and
* Cohere requires the field, so there is no "unset" to fall back to.
*/
export type CohereInputType = 'search_document' | 'search_query';
export interface BedrockEmbedderOptions {
/**
* Bedrock model id. Default `'amazon.titan-embed-text-v2:0'`.
*
* Four are known by name — Titan V2, Titan V1, and Cohere Embed English /
* Multilingual v3 (see {@link BEDROCK_EMBEDDING_MODELS}) — and each brings
* its own body shape, vector length and input window. An id that WRAPS one of
* those (a cross-region inference profile `us.amazon.titan-embed-text-v2:0`,
* or an ARN ending in the model id) is resolved to the model it names.
*
* Anything else is a model this library has never met: pass `dimensions`
* with it (its vector length is not something this can know) and `family` if
* it is not Titan-shaped.
*/
readonly model?: string;
/**
* Vector length to request.
*
* Titan V2 is the one CONFIGURABLE model — 1024 (default), 512 or 256 — and
* the value is SENT to the model AND reported as `.dimensions`, so the two
* can never disagree. Every other known model has ONE size, and asking for a
* different one is refused rather than reported: `.dimensions` is what a
* vector store fingerprints on, and a wrong one corrupts it silently.
*
* Required for a model outside {@link BEDROCK_EMBEDDING_MODELS}.
*/
readonly dimensions?: number;
/**
* The body shape to speak, when the model id does not say (9.3.0).
*
* Inferred for every known model and for anything that wraps one, so this is
* only for a model id this library has never met — a provisioned-throughput
* ARN, a custom deployment. Unknown and unstated, the body is **Titan's**,
* which is the shape every release before 9.3.0 sent to everything.
*
* Stating a family that contradicts a known model id is refused by name.
*/
readonly family?: BedrockEmbeddingFamily;
/**
* Pin Cohere's `input_type` instead of deriving it from the call (9.3.0).
*
* Unset — the default — `embed()` sends `'search_query'` and `embedBatch()`
* sends `'search_document'`, because that is what this library's own two
* call sites are: retrieval embeds ONE question (`loadRelevant`), indexing
* embeds MANY passages (`indexDocuments`, `embedMessages`). Pin it when your
* own code uses the two calls differently — embedding a single document, say,
* or scoring a batch of queries.
*
* Ignored by Titan, which has no such parameter.
*/
readonly inputType?: CohereInputType;
/**
* The longest input this model reads whole, in CHARACTERS
* ({@link Embedder.maxInputChars}). Declared for every known model from its
* documented token window; this option is how a model this library does not
* know states its own, rather than declaring none and leaving the indexer's
* conservative default in place. An explicit value always wins.
*/
readonly maxInputChars?: number;
/** AWS region. Passed to the SDK client when this factory builds one. */
readonly region?: string;
/** A pre-built Bedrock runtime client, so one SDK config serves the whole app. */
readonly client?: BedrockRuntimeLikeClient;
/** @internal Test injection — skips the SDK require entirely. */
readonly _client?: BedrockRuntimeLikeClient;
/** @internal Test injection — the AWS SDK module (exercises the real shim with a mock SDK). */
readonly _sdk?: BedrockRuntimeSdkModule;
}
/**
* Amazon Bedrock's hosted embeddings, through `InvokeModel`.
*
* No API key option, deliberately: Bedrock authenticates through the AWS
* credential chain (environment, profile, instance role, SSO), and inventing
* a key parameter would be a second, worse way to configure the same thing.
*
* ── Why the id carries the DIMENSION COUNT ───────────────────────────────
* `openaiEmbedder` deliberately leaves the size out of its id, because a
* store's fingerprint is `'<id>@<dims>'` and appends it. This one puts it in,
* and the difference is not an inconsistency — it is the one place the
* fingerprint cannot reach.
*
* `MemoryEntry.embeddingModel` stores the id ALONE, and it is the only thing
* `SearchOptions.embedderId` filters on: the filter the port describes as
* preventing "silent cross-model similarity pollution". Titan V2 at 512 and
* Titan V2 at 1024 are different embedding spaces from one model id, so an id
* without the size makes those two indistinguishable to that filter — and the
* size alone cannot separate them either, since V1 and V2 both answer at
* 1024. Only `'bedrock:<model>:<dims>'` separates all of them. The store's
* fingerprint then reads `'bedrock:amazon.titan-embed-text-v2:0:512@512'`,
* which restates the size once; a redundant fingerprint is harmless, and a
* filter that cannot tell two vector spaces apart is not.
*
* (Precedent: `localEmbedder` puts `dtype` in its id for the same reason — a
* q8 and an fp32 build of one model are near-identical spaces, and "near" is
* exactly the difference that surfaces as a mysteriously worse ranking.)
*
* ── One operation, two body shapes (9.3.0) ───────────────────────────────
* `InvokeModel` is a single API over vendor-specific JSON. Titan takes
* `{ inputText }` and answers `{ embedding }`; Cohere takes
* `{ texts, input_type }` and answers `{ embeddings }`, embeds up to
* {@link COHERE_MAX_TEXTS_PER_CALL} of them per call, and distinguishes a
* QUERY from a DOCUMENT. So the model id selects a FAMILY
* ({@link BedrockEmbeddingFamily}), and the family owns the request, the
* response and the batching. Before this, one shape was sent to everything —
* a Cohere id constructed fine and failed at the first embed.
*
* ── The input ceiling (9.1.0) ────────────────────────────────────────────
* `.maxInputChars` is per MODEL, from its documented token window converted at
* the stated {@link CHARS_PER_TOKEN} assumption of 4 characters per token:
* **32,000** for both Titan text-embedding models (8,192 tokens — sixteen
* times the indexer's own default, which was measured on an on-device model),
* and **2,000** for Cohere Embed v3 (512 tokens). Those two numbers are why the
* ceiling cannot be a per-vendor constant: the same 2,500-character chunk is
* read whole by Titan and truncated by Cohere. Dense text (code, tables, CJK)
* tokenises tighter than the assumption; pass an explicit `maxChunkChars` for
* such a corpus, and it wins over this number.
*
* @throws if `model` is unknown and `dimensions` was not supplied; if
* `dimensions` is a size the model does not produce; if `family`
* contradicts a known model id; or if the SDK is missing and no
* `client` / `_client` / `_sdk` was passed.
*
* @example
* ```ts
* import { bedrockEmbedder } from 'agentfootprint/providers';
* import { sqliteVectorStore } from 'agentfootprint/memory';
* import { indexFolder } from 'agentfootprint/rag';
*
* const embedder = bedrockEmbedder({ region: 'us-east-1', dimensions: 512 });
* await indexFolder('./docs', { to: sqliteVectorStore({ file: './corpus.db' }), embedder });
* ```
*
* @example A Cohere embedding model on the same runtime
* ```ts
* // Body shape, response field, batch size and 512-token window all follow
* // from the model id — nothing else changes at the call site.
* const embedder = bedrockEmbedder({ model: 'cohere.embed-english-v3' });
* ```
*/
export declare function bedrockEmbedder(options?: BedrockEmbedderOptions): Embedder;
/**
* The slice of `@google/genai` {@link geminiEmbedder} uses — one method.
*
* Structural, so the real `GoogleGenAI`, a client shared with `gemini()`, or a
* `{ models: { embedContent } }` double all satisfy it without this package
* taking a hard type dependency on the optional peer. The double is what the
* Google pin injects.
*/
export interface GeminiEmbedClientLike {
readonly models: {
embedContent(params: GeminiEmbedParams): Promise<GeminiEmbedResponse>;
};
}
/** `EmbedContentParameters`, narrowed to what this adapter sends. */
export interface GeminiEmbedParams {
readonly model: string;
readonly contents: readonly string[];
readonly config?: {
readonly taskType?: string;
readonly outputDimensionality?: number;
readonly abortSignal?: AbortSignal;
};
}
/** `EmbedContentResponse`, narrowed to what this adapter reads. */
export interface GeminiEmbedResponse {
readonly embeddings?: readonly {
readonly values?: readonly number[];
/**
* Present on Vertex. `truncated: true` is the service TELLING us it clipped
* the input — the one signal that turns the silent half of
* {@link Embedder.maxInputChars} into something an adapter can act on.
*/
readonly statistics?: {
readonly truncated?: boolean;
readonly tokenCount?: number;
};
}[];
}
/**
* Gemini's `task_type` — a real parameter, not a hint.
*
* `RETRIEVAL_QUERY` and `RETRIEVAL_DOCUMENT` embed a question and a passage
* into deliberately different projections that are MEANT to be compared with
* each other, so using one value for both halves is a measurable loss of
* retrieval quality. The rest are separate objectives; mixing them in one store
* is mixing spaces.
*/
export type GeminiEmbeddingTaskType = 'RETRIEVAL_QUERY' | 'RETRIEVAL_DOCUMENT' | 'SEMANTIC_SIMILARITY' | 'CLASSIFICATION' | 'CLUSTERING' | 'CODE_RETRIEVAL_QUERY' | 'QUESTION_ANSWERING' | 'FACT_VERIFICATION';
/** What this adapter does when the service says it clipped the input. */
export type GeminiTruncationPolicy = 'refuse' | 'allow';
export interface GeminiEmbedderOptions extends GoogleGenAIConnectionOptions {
/**
* Embedding model id. Default `'gemini-embedding-001'`.
*
* Two are known by name (see {@link GEMINI_EMBEDDING_MODELS}); anything else
* is a model this library has never met, so pass `dimensions` with it — its
* vector length is not something this can know, and a wrong `.dimensions`
* corrupts a vector store in silence.
*/
readonly model?: string;
/**
* Vector length to request (`outputDimensionality`).
*
* Both known models are Matryoshka models: they emit 3072 numbers and can be
* asked for fewer, and the shorter vector is a genuinely usable embedding
* rather than a slice of a longer one. The value is SENT to the model AND
* reported as `.dimensions`, so the two can never disagree. Google recommends
* 768, 1536 or 3072; any length up to the model's native size is accepted.
*
* Required for a model outside {@link GEMINI_EMBEDDING_MODELS}.
*
* Note for stores that rank by DOT PRODUCT or euclidean distance: Google's
* shortened vectors are not re-normalised, and Google recommends normalising
* them yourself. Nothing in this library needs it — `cosineSimilarity`
* divides by both magnitudes — so this adapter returns the model's numbers
* unchanged rather than quietly rescaling what you store.
*/
readonly dimensions?: number;
/**
* Pin `task_type` instead of deriving it from the call.
*
* Unset — the default — `embed()` sends `RETRIEVAL_QUERY` and `embedBatch()`
* sends `RETRIEVAL_DOCUMENT`, because that is what this library's own two
* call sites are: retrieval embeds ONE question (`loadRelevant`), indexing
* embeds MANY passages (`indexDocuments`, `embedMessages`). Pin it when your
* own code uses the two calls differently, or when the objective is
* classification or clustering rather than search.
*
* Refused by name on a model that does not take the parameter.
*/
readonly taskType?: GeminiEmbeddingTaskType;
/**
* What to do when the service reports it CLIPPED the input. Default
* `'refuse'`.
*
* Over-long input is not rejected by Gemini — it is silently truncated, and a
* full-looking vector comes back for the opening of the passage. An indexer
* then stores the whole chunk as the passage and the clipped vector as its
* index, so retrieval cannot find text that is visibly present in the passage
* it later serves. Nothing throws, nothing scores zero; the corpus is quietly,
* partially indexed.
*
* `.maxInputChars` exists to stop that BEFORE the call, and it is an
* assumption (see {@link CHARS_PER_TOKEN}) — dense text tokenises tighter.
* `statistics.truncated` is the service saying it happened anyway, and this
* adapter is the only thing that sees it. `'refuse'` turns it into an error
* naming the fix; `'allow'` returns the clipped vector, which is what every
* library that does not look at the field already does.
*
* Detection depends on the service RETURNING `statistics` — Vertex does. When
* it is absent this adapter cannot tell, and says so here rather than
* implying a guarantee.
*/
readonly onTruncation?: GeminiTruncationPolicy;
/**
* The longest input this model reads whole, in CHARACTERS
* ({@link Embedder.maxInputChars}). Declared for every known model from its
* documented token window; this option is how a model this library does not
* know states its own. An explicit value always wins.
*/
readonly maxInputChars?: number;
/** @internal Test injection — skips the SDK require entirely. */
readonly _client?: GeminiEmbedClientLike;
}
/**
* Google's hosted embeddings, through `models.embedContent` — on Vertex or on
* the Gemini API.
*
* The door is chosen exactly as `gemini()` chooses it: `{ project, location }`
* is Vertex with Application Default Credentials, `{ apiKey }` is the Gemini
* API, and neither is guessed. One `GoogleGenAI` client serves both this and
* the LLM provider, so an app that talks to both can build the client once and
* pass it as `_client`.
*
* ── Why the id carries the DIMENSION COUNT ───────────────────────────────
* `'gemini:<model>:<dims>'`, for the reason `bedrockEmbedder` gives at length:
* `MemoryEntry.embeddingModel` stores the id ALONE and it is the only thing
* `SearchOptions.embedderId` filters on. One model id at 768 and the same model
* id at 3072 are different embedding spaces, so an id without the size cannot
* tell them apart — and neither can the size alone, since both known models are
* 3072 natively.
*
* The `taskType` is deliberately NOT in the id, matching `bedrockEmbedder`'s
* treatment of Cohere's `input_type`: the default query/document pair is ONE
* space by construction (the two projections exist to be compared with each
* other), and a pinned value pins both call sites at once, so the store stays
* self-consistent either way.
*
* ── The input ceiling, and the service telling on itself (9.1.0) ─────────
* `.maxInputChars` is per MODEL, from its documented token window at the stated
* {@link CHARS_PER_TOKEN} assumption: **8,000** for `gemini-embedding-001`
* (2,048 tokens) and **32,000** for `gemini-embedding-2` (8,192). A quarter of
* the newer model's window is not a rounding difference — it is the whole
* reason the ceiling is per-model. Past the ceiling Gemini CLIPS rather than
* refuses, so `onTruncation` decides what happens when the response admits it.
*
* @throws if `model` is unknown and `dimensions` was not supplied; if
* `dimensions` exceeds what the model can produce; if `taskType` is set
* on a model that takes none; if neither a project nor an API key is
* resolvable; or if `@google/genai` is not installed and no `_client`
* was passed.
*
* @example
* ```ts
* import { geminiEmbedder } from 'agentfootprint/providers';
* import { sqliteVectorStore } from 'agentfootprint/memory';
* import { indexFolder } from 'agentfootprint/rag';
*
* const embedder = geminiEmbedder({ project: 'my-project', dimensions: 768 });
* await indexFolder('./docs', { to: sqliteVectorStore({ file: './corpus.db' }), embedder });
* ```
*
* @example The Gemini API door, and a store that must never hold a clipped vector
* ```ts
* const embedder = geminiEmbedder({
* apiKey: process.env.GEMINI_API_KEY,
* onTruncation: 'refuse', // the default — stated here because it is the point
* });
* ```
*/
export declare function geminiEmbedder(options?: GeminiEmbedderOptions): Embedder;
/**
* The slice of `@huggingface/transformers` {@link localEmbedder} uses.
*
* Structural, so `await import('@huggingface/transformers')` (or a stub, or a
* pinned fork) satisfies it without this package taking a hard type dependency
* on the optional peer.
*/
export interface TransformersBackend {
/** transformers.js `pipeline(task, model, options)`. */
pipeline(task: string, model?: string, options?: Record<string, unknown>): Promise<unknown>;
/** transformers.js `env` — mutated only when `cacheDir` is set. */
env?: unknown;
}
export interface LocalEmbedderOptions {
/** ONNX model id. Default 'Xenova/all-MiniLM-L6-v2' (384-dim). */
readonly model?: string;
/** Vector length of the model. Default 384. */
readonly dimensions?: number;
/** Quantization. Default 'q8' (smallest); use 'fp32' for max fidelity. */
readonly dtype?: string;
/** On-disk model cache directory. */
readonly cacheDir?: string;
/**
* Longest input this model reads whole, in characters (9.1.0). Default
* {@link LOCAL_MAX_INPUT_CHARS} — the MEASURED cliff of the default model.
*
* The option exists because `model` is swappable and the cliff belongs to
* the MODEL, not to this factory. A long-context embedding model (one of the
* 8k-token sentence-transformer builds) reads far more than the default
* says, and without a way to state that, an indexer would keep cutting its
* corpus into pieces a quarter the size the model can take. Nothing verifies
* it — it is your model's documented window, declared for the indexer to
* read.
*/
readonly maxInputChars?: number;
/**
* An ALREADY-IMPORTED `@huggingface/transformers`. Supply this and the lazy
* `import('@huggingface/transformers')` never happens — which is what makes
* the embedder work in a BUNDLED app, where a bare specifier reaches the
* browser unresolved:
*
* import * as transformers from '@huggingface/transformers';
* localEmbedder({ backend: transformers });
*
* Your bundler resolves that static import; the peer dep stays optional for
* everyone who doesn't.
*/
readonly backend?: TransformersBackend;
}
export declare function localEmbedder(options?: LocalEmbedderOptions): Embedder;
/**
* The slice of a Model2Vec package {@link staticEmbedder} uses: a batch
* `embed`/`encode`, on the module or on its default export.
*
* Structural, so `await import('@yarflam/potion-base-8m')` — or any other
* Model2Vec build with one of those shapes — satisfies it.
*/
export interface Model2VecBackend {
/** Batch embed: `embed(texts) => vectors` (may be async). */
embed?(texts: readonly string[]): unknown;
/** Alternative name some builds use. */
encode?(texts: readonly string[]): unknown;
/** A default export that is the fn, or carries `embed`/`encode`. */
readonly default?: unknown;
}
export interface StaticEmbedderOptions {
/** Vector length of the bundled model. Default 256 (potion-base-8m). */
readonly dimensions?: number;
/** Override the package specifier for a different Model2Vec build. */
readonly module?: string;
/**
* An ALREADY-IMPORTED Model2Vec module. Supply this and no dynamic import
* happens — the only way this embedder can run in a BUNDLED app, since a
* bundler cannot resolve the specifier `module` names:
*
* import * as potion from '@yarflam/potion-base-8m';
* staticEmbedder({ backend: potion });
*
* Takes precedence over `module`. (The potion backend itself is Node-only
* today — see the embedders guide.)
*/
readonly backend?: Model2VecBackend;
}
export declare function staticEmbedder(options?: StaticEmbedderOptions): Embedder;
//# sourceMappingURL=index.d.ts.map