Python API Reference
Functions
set_log_level(_level: str)
Set the engine log level.
Currently a no-op at runtime. Use the RUST_LOG environment
variable before starting the engine (e.g. RUST_LOG=debug).
level: One of"error","warn","info","debug","trace".
Returns: None
import os
os.environ["RUST_LOG"] = "debug" # set before creating Engine
set_log_level("debug") # no effect at runtime
repair_json(input, default=None) -> str
Repair a possibly-truncated or malformed JSON string.
The common case is a structured-generation response cut off by a token limit
(finish_reason == "length"), e.g. {"a": 1, "b": "hel — valid up to the cut
but unparseable as a whole. Completes it best-effort: keeps complete fields,
closes and keeps a truncated string value, drops a dangling key with no value,
and closes open objects/arrays. Never raises.
input: the JSON string to repair.default: optional JSON string whose fields fill any missing/null fields in the result (fields present ininputwin). PassNonefor no default.
Returns: the repaired JSON string.
Caveat: a value truncated mid-token is kept as-is. Repair is a safety net —
prefer checking finish_reason and raising the token budget.
from atelico import repair_json
repair_json('{"a": 1, "b": "hel', '{"a": 0, "c": 3}')
**'{"a":1,"b":"hel","c":3}'**
RecommendedSampling
A model's advisory sampling parameters, if it declares any.
Each field is None when the model has no recommendation for that knob.
The engine applies these only when the request omits the corresponding
field (explicit request values always win); they are surfaced here for
introspection/display. Mirrors the engine's nested recommended_sampling
object — additive: unknown keys in the JSON are ignored.
Methods
from_dict(cls, d: dict[str, Any]) -> 'RecommendedSampling'
ModelCapabilities
A loaded model's static capabilities, as returned by
:meth:Engine.model_capabilities.
Use :meth:from_json to parse the JSON string that method returns into a
typed object. The mapping is additive: fields absent from an older engine's
JSON fall back to their defaults, and unknown keys are ignored, so a newer
engine never breaks an older binding.
thinking_style: one of"unsupported","thinktags","native".device: e.g."Cuda(0)"/"Cpu"/"Metal(0)"(empty on engines that predate the field).native_tool_format: e.g."gemma4", orNonewhen the model has no native tool block (tools are prose-injected).recommended_sampling: nested advisory sampling knobs (see :class:RecommendedSampling).
Methods
from_dict(cls, d: dict[str, Any]) -> 'ModelCapabilities'
from_json(cls, s: str) -> 'ModelCapabilities'
Parse the JSON string from :meth:Engine.model_capabilities.
Backend
Helper for constructing backend configuration JSON strings.
Each method returns a JSON string that can be passed to Engine
configuration. Two backend types are supported:
- in_memory: Local on-device inference (Metal/CUDA/CPU).
- proxy: Forward requests to a remote OpenAI-compatible API.
local = Backend.in_memory(name="local")
remote = Backend.proxy(name="openai", api_key="sk-...")
Methods
@staticmethod in_memory(name="local", models=vec![], model_directory=None) -> str
Return a JSON configuration string for a local in-memory backend.
The returned JSON contains the backend name, type, list of models to pre-load, and an optional model cache directory.
name: Logical name for this backend (default"local").models: List of HuggingFace model IDs to pre-load at startup.model_directory: Filesystem path where model weights are cached. PassNoneto use the default cache.
Returns: A JSON string with the backend configuration.
{
"name": "local",
"backend_type": "in_memory",
"models": ["meta-llama/Llama-3.2-1B-Instruct-GGUF"],
"model_directory": "/models"
}
cfg = Backend.in_memory(
name="local",
models=["meta-llama/Llama-3.2-1B-Instruct-GGUF"],
model_directory="/models",
)
@staticmethod proxy(name="openai", base_url="https://api.openai.com/v1", api_key=None) -> str
Return a JSON configuration string for a remote proxy backend.
The proxy backend forwards all requests to an OpenAI-compatible HTTP API.
name: Logical name for this backend (default"openai").base_url: Base URL of the remote API (default"https://api.openai.com/v1").api_key: API key for authentication. PassNoneif the remote API does not require one.
Returns: A JSON string with the backend configuration.
{
"name": "openai",
"backend_type": "proxy",
"base_url": "https://api.openai.com/v1",
"api_key": "sk-..."
}
cfg = Backend.proxy(name="openai", api_key="sk-...")
TokenStream
Iterator that yields streaming LLM chat completion chunks as JSON strings.
Implements Python's iterator protocol (__iter__ / __next__).
Each call to __next__ blocks (releasing the GIL) until the next
token chunk arrives from the inference engine.
Each yielded string is a JSON object following the OpenAI
ChatCompletionChunk schema:
{
"id": "chatcmpl-...",
"object": "chat.completion.chunk",
"model": "...",
"choices": [{
"index": 0,
"delta": {"role": "assistant", "content": "token"},
"finish_reason": null
}]
}
The last chunk has finish_reason set to "stop" (or "length"),
and after that __next__ raises StopIteration.
stream = engine.llm_chat_completion_stream(request_json)
for chunk_json in stream:
chunk = json.loads(chunk_json)
token = chunk["choices"][0]["delta"].get("content", "")
print(token, end="", flush=True)
AudioSpeechChunkStream
Iterator that yields streaming TTS speech chunks as JSON strings.
Implements Python's iterator protocol (__iter__ / __next__).
Each yielded string is an AudioSpeechChunk JSON object:
{
"sequence": 0,
"audio": "<base64 WAV bytes>",
"duration_seconds": 1.42,
"text": "First sentence."
}
__next__ raises StopIteration once the stream is exhausted.
stream = engine.audio_synthesize_stream(request_json)
for chunk_json in stream:
chunk = json.loads(chunk_json)
wav_bytes = base64.b64decode(chunk["audio"])
# play wav_bytes as soon as it arrives
JsonResultStream
Handle to a non-blocking, non-streaming call started by one of the
*_async methods. The full response is computed off the calling thread (on
the engine's runtime) and delivered here exactly once as a JSON string.
Two ways to consume it:
poll() — non-blocking. Returns the JSON string once ready, or None
while still pending. Ideal for a game / UI loop that ticks each frame:
handle = engine.llm_chat_completion_async(request)
while True:
result = handle.poll()
if result is not None:
response = json.loads(result)
break
do_one_frame_of_work()
result() — blocks (releasing the GIL) until the response is ready, then
returns the JSON string. Equivalent to the synchronous call but the work
runs on the engine runtime:
handle = engine.image_generate_async(request)
image = json.loads(handle.result())
Unlike *_stream (token-by-token deltas), this yields the COMPLETE response
in one shot — the right shape for schema-constrained / structured output,
which streaming can't serve cleanly. Backed by the same Engine::spawn_result
primitive (one shared Rust codepath) the C ABI and Lua async bindings use.
Methods
poll() -> Optional[Strin]
Non-blocking poll. Returns the full JSON result string once the async
call completes, or None if it is still in flight. Raises on failure.
result() -> str
Block (releasing the GIL) until the async call completes, then return the full JSON result string. Raises on failure.
TranscriptionStream
A live transcription session from Engine.audio_transcribe_stream.
Feed microphone audio with feed; read events with poll (non-blocking, for a
game or UI loop) or by iterating (blocking). Each event is a JSON string tagged
by type:
{"type": "speech_started", "start_time": 1.2}
{"type": "speech_ended", "start_time": 1.2, "end_time": 2.0}
{"type": "transcription", "text": "Stormfly, come", "is_final": true,
"start_time": 1.2, "end_time": 2.0}
A failed transcription raises InferenceError from that one poll / next;
the stream keeps listening. Call finish() to transcribe the utterance in
progress and end the stream.
stream = engine.audio_transcribe_stream(json.dumps({"model": "in-memory::whisper"}))
stream.feed(samples, 48000) # from your audio callback
while (event := stream.poll()) is not None:
event = json.loads(event)
if event["type"] == "transcription" and event["is_final"]:
print(event["text"])
Methods
feed(samples: list[float], sample_rate: int)
Queue mono audio (floats in -1..1) at sample_rate Hz. Returns immediately, and
is safe to call from an audio-callback thread while another thread reads events.
Raises:
ValueError: If sample_rate is 0.
AtelicoError: If the stream has been finished, or is more than 30 s of
audio behind (poll its events so it can catch up).
stream.feed([0.0] * 480, 48000)
finish()
Stop listening: the utterance in progress is transcribed, then the stream ends once its remaining events are read.
stream.finish()
while not stream.done:
stream.poll()
poll() -> Optional[Strin]
Next event as a JSON string, or None if none is ready (or the stream has
ended — check done). Never blocks.
Raises:
InferenceError: For one failed transcription; keep polling.
event = stream.poll()
done() -> bool
Whether the stream has ended (after finish, once every event was read).
while not stream.done:
stream.poll()
Engine
The main Atelico AI Engine.
Thread-safe. Supports context manager protocol (with statement).
engine = Engine(device="auto")
engine.load_model("meta-llama/Llama-3.2-1B-Instruct-GGUF")
response = engine.llm_chat_completion(request_json)
Methods
__init__(config_json=None, device="auto", scheduling_mode="balance", vram_budget_mb=0, data_logging_json=None) -> Self
Create a new Atelico AI Engine instance.
Either pass a full config_json string, or use the individual
keyword arguments for simple setups.
config_json: Full engine configuration as a JSON string. When provided, all other arguments are ignored.device: Device selection --"auto"(default, tries CUDA then Metal then CPU),"cpu","metal", or"cuda".scheduling_mode: GPU scheduling mode when sharing with a game renderer --"balance"(default),"prioritize_compute", or"prioritize_graphics".vram_budget_mb: Maximum VRAM the engine may use, in megabytes.0means unlimited (default).data_logging_json: Optional JSON object configuring asynchronous input/output capture. Ignored whenconfig_jsonis supplied.
Returns: A new Engine instance configured with the specified device and settings.
Raises:
AtelicoError: If engine initialization fails (e.g. device unavailable).
engine = Engine(device="metal")
**Context manager for automatic cleanup:**
with Engine(device="auto") as engine:
engine.load_model("meta-llama/Llama-3.2-1B-Instruct-GGUF")
close()
Shut down the engine and release all resources.
Unloads every model, frees GPU memory, and stops background threads. After calling this, the engine instance must not be used again. Equivalent to exiting the context manager.
Returns: None
engine = Engine()
engine.close()
load_model(model_id: str)
Load a model into memory (blocking).
Downloads model weights from HuggingFace Hub if not already cached, then loads them onto the configured device. Blocks until fully loaded.
model_id: HuggingFace model ID (e.g."meta-llama/Llama-3.2-1B-Instruct-GGUF").
Returns: None
Raises:
ModelLoadError: If the model cannot be found or loaded.
engine.load_model("meta-llama/Llama-3.2-1B-Instruct-GGUF")
load_model_async(model_id: str) -> JsonResultStream
Start a non-blocking model load — the "preload during a loading screen"
primitive (first-use download + memory load run off the caller). Returns
a JsonResultStream; on completion it delivers
{"model_id": "...", "loaded": true}.
handle = engine.load_model_async("meta-llama/Llama-3.2-1B-Instruct-GGUF")
while handle.poll() is None:
render_loading_screen_frame()
unload_model(model_id: str)
Unload a model and free its resources (GPU memory, caches).
model_id: The model ID that was previously passed toload_model.
Returns: None
Raises:
ModelLoadError: If the model is not currently loaded.
engine.unload_model("meta-llama/Llama-3.2-1B-Instruct-GGUF")
llm_chat_completion(request_json: str) -> str
Run a chat completion request (blocking, non-streaming).
-
request_json: A JSON string following the OpenAIChatCompletionRequestschema. Engine extensions includespeculative,enable_thinking,priority, andprefix_cache({"scope": "auto"}reuses the KV of a shared prompt prefix instead of re-prefilling it; byte-identical greedy output, lower prefill cost — ideal for a fixed system prompt across many calls).priorityis"interactive","normal"(the default) or"background", and decides how this request is scheduled against everything else competing for the GPU. A long generation hands the GPU over at token boundaries, so an"interactive"request does not wait for a"background"one to finish.seedsets the sampler's RNG. Omitting it does not randomise: the engine uses one fixed default seed, so identical requests repeat.adapter_scale(float, optional) is the strength of the LoRA adapter this request runs with (from themodel::adaptersuffix or the model's default):1.0as trained,0.0the base model, values between blend. Absent uses the model's default scale, else1.0.
Expected request JSON structure:
{
"model": "meta-llama/Llama-3.2-1B-Instruct-GGUF",
"messages": [
{"role": "system", "content": "You are helpful."},
{"role": "user", "content": "Hello!"}
],
"max_tokens": 128,
"temperature": 0.7,
"top_p": 0.9,
"response_format": null,
"speculative": {"proposer": "prompt_lookup", "k": 5}
}
speculative is optional — multi-token (speculative) decoding to
speed up generation (proposer: "prompt_lookup" or "lookahead").
Greedy output is unchanged.
Returns: A JSON string with the ChatCompletionResponse.
{
"id": "chatcmpl-...",
"choices": [{
"message": {"role": "assistant", "content": "Hi!"},
"finish_reason": "stop"
}],
"usage": {"prompt_tokens": 5, "completion_tokens": 3, "total_tokens": 8}
}
Raises:
ValueError: If request_json is not valid JSON or is missing required fields.
InferenceError: If token generation fails.
import json
request = json.dumps({
"model": "meta-llama/Llama-3.2-1B-Instruct-GGUF",
"messages": [{"role": "user", "content": "Hello!"}],
"max_tokens": 128,
})
response = json.loads(engine.llm_chat_completion(request))
print(response["choices"][0]["message"]["content"])
llm_chat_completion_stream(request_json: str) -> TokenStream
Start a streaming chat completion and return a TokenStream iterator.
Each iteration yields a JSON string with one ChatCompletionChunk
(see TokenStream for the chunk schema). The GIL is released while
waiting for each chunk, so other Python threads can run.
request_json: JSONChatCompletionRequest(same schema asllm_chat_completion). Thestreamfield is set automatically.
Returns: A TokenStream iterator that yields JSON chunk strings per token.
Raises:
ValueError: If request_json is invalid.
InferenceError: If stream setup fails.
import json
request = json.dumps({
"model": "meta-llama/Llama-3.2-1B-Instruct-GGUF",
"messages": [{"role": "user", "content": "Tell me a joke"}],
})
for chunk_json in engine.llm_chat_completion_stream(request):
chunk = json.loads(chunk_json)
token = chunk["choices"][0]["delta"].get("content", "")
print(token, end="", flush=True)
llm_chat_completion_async(request_json: str) -> JsonResultStream
Start a non-blocking, non-streaming chat completion.
Returns immediately with a JsonResultStream handle; the full
ChatCompletionResponse is computed on the engine runtime and delivered
once (poll for it, or block with .result()). Unlike
llm_chat_completion_stream it yields the COMPLETE response — the right
shape for schema-constrained / structured output. Same request schema as
llm_chat_completion.
handle = engine.llm_chat_completion_async(request)
**... do other work ...**
response = json.loads(handle.result())
llm_text_completion_async(request_json: str) -> JsonResultStream
Start a non-blocking, non-streaming text completion. See
llm_chat_completion_async; same request schema as llm_text_completion.
llm_respond_async(request_json: str) -> JsonResultStream
Start a non-blocking, non-streaming Responses-API request. See
llm_chat_completion_async; same request schema as llm_respond.
llm_text_completion(request_json: str) -> str
Run a text completion request (blocking, non-streaming).
Unlike chat completion, text completion continues a raw prompt without chat template formatting.
request_json: A JSON string withTextCompletionRequestfields.
Expected request JSON structure:
{
"model": "meta-llama/Llama-3.2-1B-Instruct-GGUF",
"prompt": "Once upon a time",
"max_tokens": 50,
"temperature": 0.7
}
Returns: A JSON string with the TextCompletionResponse.
{
"id": "cmpl-...",
"object": "text_completion",
"model": "...",
"choices": [{"text": " there was a...", "index": 0, "finish_reason": "stop"}],
"usage": {"prompt_tokens": 4, "completion_tokens": 50, "total_tokens": 54}
}
Raises:
ValueError: If request_json is invalid.
InferenceError: If token generation fails.
import json
request = json.dumps({
"model": "meta-llama/Llama-3.2-1B-Instruct-GGUF",
"prompt": "Once upon a time",
"max_tokens": 50,
})
response = json.loads(engine.llm_text_completion(request))
print(response["choices"][0]["text"])
llm_respond(request_json: str) -> str
Run a Responses API request (blocking).
The Responses API is a higher-level conversational interface that manages conversation state internally.
request_json: A JSON string withResponseRequestfields.
Expected request JSON structure:
{
"model": "meta-llama/Llama-3.2-1B-Instruct-GGUF",
"input": "What is 2+2?",
"instructions": "You are a math tutor.",
"max_output_tokens": 100,
"temperature": 0.7
}
Returns: A JSON string with the ResponseResponse.
{
"id": "resp-...",
"object": "response",
"output": [{"type": "message", "content": [{"type": "output_text", "text": "4"}]}],
"usage": {"input_tokens": 5, "output_tokens": 1}
}
Raises:
ValueError: If request_json is invalid.
InferenceError: If generation fails.
import json
request = json.dumps({
"model": "meta-llama/Llama-3.2-1B-Instruct-GGUF",
"input": "What is 2+2?",
})
response = json.loads(engine.llm_respond(request))
tokenize(model_id: str, text: str) -> list[u3]
Encode text into token IDs using a loaded model's tokenizer.
Loads the model on demand if it isn't already resident. Useful for budgeting prompt length against the context window before sending a request, or for pre-tokenizing prefixes that will be reused.
model_id: Model identifier in"backend::model"format.text: UTF-8 text to tokenize.
Returns: List of token IDs (list[int]).
Raises:
AtelicoError if the model fails to load or tokenize.
tokens = engine.tokenize(
"in-memory::meta-llama/Llama-3.2-3B-Instruct-Q4_K_M",
"Hello, world!",
)
print(len(tokens), tokens[:5])
detokenize(model_id, tokens, skip_special_tokens=true) -> str
Decode a token ID sequence back into text using a loaded model's tokenizer.
model_id: Model identifier in"backend::model"format.tokens: Token ID sequence aslist[int].skip_special_tokens: IfTrue(default), drops tokens like<|begin_of_text|>from the output.
Returns: The decoded text (str).
Raises:
AtelicoError if the model fails to load or detokenize.
text = engine.detokenize(
"in-memory::meta-llama/Llama-3.2-3B-Instruct-Q4_K_M",
[128000, 9906, 11, 1917, 0],
skip_special_tokens=True,
)
**text == "Hello, world!"**
model_capabilities(model_id: str) -> str
Inspect a loaded model's static capabilities.
Returns vocab size, context window, dtype, and active backend (metal/cuda/cpu) for budgeting, telemetry, and debugging. Loads the model on demand.
model_id: Model identifier in"backend::model"format.
Returns: A JSON string with the ModelCapabilities:
{
"model_id": "in-memory::meta-llama/Llama-3.2-3B-Instruct-Q4_K_M",
"vocab_size": 128256,
"max_position_embeddings": 131072,
"dtype": "Q4_K_M",
"device": "Cuda(0)",
"backend_kind": "metal",
"thinking_style": "native",
"native_tool_format": "gemma4",
"recommended_sampling": { "temperature": 1.0, "top_p": 0.95 }
}
device is the device the weights are on ("Cuda(0)"/"Cpu"/"Metal(0)";
empty on older engines). thinking_style is one of
"unsupported"/"thinktags"/"native". native_tool_format (e.g.
"gemma4") and each recommended_sampling knob (temperature, top_p,
repetition_penalty) are omitted when the model declares none. The typed
atelico.ModelCapabilities.from_json(...) dataclass wraps this string.
import atelico
caps = atelico.ModelCapabilities.from_json(
engine.model_capabilities("in-memory::meta-llama/Llama-3.2-3B-Instruct-Q4_K_M")
)
print(f"context window = {caps.max_position_embeddings} tokens")
llm_call_function_with_policy(function_json, model_id, variables_json, system_prompt, policy_json) -> str
Execute a templated LM function under a generation policy (blocking).
Validators, repair, retries, and fallbacks are layered on top of the
simple llm_call_function path.
-
function_json: JSON LM function definition (same shape asllm_call_function). -
model_id: Model ID in"backend::model"format. -
variables_json: JSON object with template variable values. -
system_prompt: Optional system message. PassNoneto omit. -
policy_json: JSON generation policy. All fields optional:{"max_attempts": 3,"validators": [{"type": "json_parse"},{"type": "json_schema_subset", "schema": {"type": "object"}},{"type": "regex", "pattern": "^[A-Z].*"},{"type": "choice", "options": ["yes", "no"]}],"repair": "local_json_repair","fallbacks": [{"type": "model", "model": "in-memory::stronger-model"},{"type": "static", "content": "{\"label\": \"unknown\"}"}],"guardrails": "off","timeout_ms": 30000}repair:"none","local_json_repair","retry_with_error_message","retry_with_original_prompt_and_error".guardrails:"off","monitor","strict","retry_on_block". Function-level guardrails require a Rust-side handle and are a no-op when invoked through Python — only router-level guardrails apply.Fallback
type:"none","prompt"(with"template"),"model"(with"model"),"generation"(with"max_tokens"and/or"temperature"),"static"(with"content"and optional"parsed"),"cache_only".
Returns: A JSON string with the result and trace:
{
"content": "...",
"parsed": null,
"trace": {
"attempts": [{"attempt": 1, "model": "...", "outcome": "Pass"}],
"fallback_used": null,
"fallback_kind": null,
"final_from_fallback": false,
"cache_status": null
}
}
image_generate(request_json: str) -> str
Generate an image from a text prompt (blocking).
request_json: A JSON string withImageGenerationRequestfields.
Expected request JSON structure:
{
"model": "PixArt-alpha/PixArt-Sigma-XL-2-512-MS",
"prompt": "A sunset over mountains",
"n": 1,
"size": "512x512",
"response_format": "b64_json"
}
Returns: A JSON string with the ImageGenerationResponse.
{
"created": 1234567890,
"data": [{"b64_json": "iVBORw0KGgo...", "revised_prompt": "A sunset over mountains"}]
}
Raises:
ValueError: If request_json is invalid.
InferenceError: If image generation fails.
import json
request = json.dumps({
"model": "PixArt-alpha/PixArt-Sigma-XL-2-512-MS",
"prompt": "A sunset over mountains",
"size": "512x512",
})
response = json.loads(engine.image_generate(request))
b64_image = response["data"][0]["b64_json"]
video_generate(request_json: str) -> str
Generate a video from a text prompt (blocking).
request_json: A JSON string withVideoGenerationRequestfields (model, prompt, optional num_frames, width, height, fps, seed, response_format ="b64_mp4"or"b64_raw_nhwc").
Returns: A JSON string with the response (data, num_frames, format, ...).
Raises:
ValueError: If request_json is invalid JSON.
InferenceError: If video generation fails.
image_remove_background(request_json: str) -> str
Remove the background from an image (blocking).
request_json: A JSON string withBackgroundRemovalRequestfields.
Expected request JSON structure:
{
"model": "briaai/RMBG-1.4",
"image": "iVBORw0KGgo..."
}
Returns: A JSON string with the processed image.
{
"data": [{"b64_json": "iVBORw0KGgo..."}]
}
Raises:
ValueError: If request_json is invalid.
InferenceError: If background removal fails.
import json
request = json.dumps({"model": "briaai/RMBG-1.4", "image": b64_image_str})
response = json.loads(engine.image_remove_background(request))
result_image = response["data"][0]["b64_json"]
image_generate_async(request_json: str) -> JsonResultStream
Start a non-blocking image generation. Returns a JsonResultStream;
generation runs on the engine runtime (seconds-long) without blocking
the caller. Same request schema as image_generate. See
llm_chat_completion_async for the poll/result pattern.
image_remove_background_async(request_json: str) -> JsonResultStream
Start a non-blocking background removal. Returns a JsonResultStream.
Same request schema as image_remove_background.
audio_synthesize(request_json: str) -> str
Synthesize speech from text (blocking).
request_json: A JSON string withAudioSpeechRequestfields.
Expected request JSON structure:
{
"model": "in-memory::tts",
"input": "Hello from Atelico.",
"voice": "af_heart"
}
Accepted model ids after the in-memory:: prefix: tts, kokoro,
kokoro-82m, pocket, pocket-tts. Default is kokoro-82m.
Returns: A JSON string with the synthesized audio:
{
"audio_b64": "UklGRn...",
"duration_seconds": 1.42,
"format": "wav",
"sample_rate": 24000
}
audio_b64 decodes to a complete WAV file.
import base64, json
req = json.dumps({
"model": "in-memory::tts",
"input": "Hello from Atelico.",
"voice": "af_heart",
})
resp = json.loads(engine.audio_synthesize(req))
open("hello.wav", "wb").write(base64.b64decode(resp["audio_b64"]))
audio_transcribe(request_json: str) -> str
Transcribe audio to text (blocking).
request_json: A JSON string with the transcription request:
{
"model": "in-memory::whisper",
"audio_b64": "UklGRn...",
"language": "en"
}
audio_b64 must be a base64-encoded WAV file (RIFF header + PCM
data); the binding decodes the WAV and reads the sample rate from the
header. language is optional (auto-detect if omitted).
Accepted model ids after the in-memory:: prefix: whisper (default —
resolves to whisper-base.en), whisper-tiny[.en], whisper-base[.en],
whisper-small[.en], whisper-medium[.en], whisper-large-v3[-turbo],
distil-large-v3.
Returns: A JSON string AudioTranscriptionResponse:
{"text": "Hello from Atelico.", "language": "en", "duration": 1.4}
import base64, json
req = json.dumps({
"model": "in-memory::whisper",
"audio_b64": base64.b64encode(open("speech.wav", "rb").read()).decode(),
})
resp = json.loads(engine.audio_transcribe(req))
print(resp["text"])
audio_synthesize_stream(request_json: str) -> AudioSpeechChunkStream
Start a streaming TTS synthesis and return an iterator of chunks.
request_json: A JSONAudioSpeechRequest(same schema asaudio_synthesize).
Returns: An AudioSpeechChunkStream that yields JSON AudioSpeechChunk
strings, one per sentence/clause.
import base64, json
stream = engine.audio_synthesize_stream(json.dumps({
"model": "in-memory::pocket-tts",
"input": "First sentence. Second one comes right after.",
"voice": "alba",
}))
for chunk_json in stream:
chunk = json.loads(chunk_json)
wav_bytes = base64.b64decode(chunk["audio"])
# play wav_bytes through your audio backend immediately
audio_transcribe_stream(config_json: str) -> TranscriptionStream
Open a live transcription stream for microphone audio.
config_json: A JSONTranscriptionStreamConfig:\{"model": "in-memory::whisper", "endpointing": "command" | "dialogue" | "dictation", "energy_threshold": 0.02, "language": "en", "partial_interval_ms": 700\}— onlymodelis required.
Returns: A TranscriptionStream to feed audio into and poll events from.
Utterances are found by voice-activity detection and transcribed through the
engine's Whisper as they end.
Raises:
ValueError: If config_json is malformed or invalid.
stream = engine.audio_transcribe_stream(json.dumps({
"model": "in-memory::whisper",
"endpointing": "command",
}))
audio_synthesize_async(request_json: str) -> JsonResultStream
Start a non-blocking full-clip TTS synthesis. Returns a
JsonResultStream; the complete clip is delivered once as the same
{audio_b64, duration_seconds, format, sample_rate} JSON that
audio_synthesize returns. Same request schema as audio_synthesize.
For per-sentence streaming use audio_synthesize_stream instead.
audio_transcribe_async(request_json: str) -> JsonResultStream
Start a non-blocking transcription. Returns a JsonResultStream
delivering the AudioTranscriptionResponse JSON. Same request schema
(base64 WAV via audio_b64) as audio_transcribe.
embed_text(request_json: str) -> str
Generate text embeddings (blocking).
request_json: A JSON string withEmbeddingRequestfields.
Expected request JSON structure:
{
"model": "sentence-transformers/all-MiniLM-L6-v2",
"input": ["Hello world", "Goodbye world"]
}
Returns: A JSON string with the EmbeddingResponse.
{
"object": "list",
"model": "sentence-transformers/all-MiniLM-L6-v2",
"data": [
{"object": "embedding", "index": 0, "embedding": [0.1, -0.2, ...]},
{"object": "embedding", "index": 1, "embedding": [0.3, 0.4, ...]}
],
"usage": {"prompt_tokens": 8, "total_tokens": 8}
}
Raises:
ValueError: If request_json is invalid.
InferenceError: If embedding generation fails.
import json
request = json.dumps({
"model": "sentence-transformers/all-MiniLM-L6-v2",
"input": ["Hello world", "Goodbye world"],
})
response = json.loads(engine.embed_text(request))
vectors = [d["embedding"] for d in response["data"]]
embed_async(request_json: str) -> JsonResultStream
Start a non-blocking embedding request. Returns a JsonResultStream
delivering the EmbeddingResponse JSON. Same request schema as embed_text.
embed_image(request_json: str) -> str
Embed a single image into a vector with a cross-modal (CLIP/SigLIP) or vision (DINOv2) model. With a cross-modal model the vector shares the text model's space, so a text query can retrieve images by similarity.
request_json: JSON withmodel(e.g."clip","siglip","dinov2-small") andimage(base64-encoded PNG/JPEG bytes).
Returns: EmbeddingResponse JSON with one data entry.
import json, base64
req = json.dumps({"model": "clip", "image": base64.b64encode(open("cat.png","rb").read()).decode()})
vec = json.loads(engine.embed_image(req))["data"][0]["embedding"]
embed_image_async(request_json: str) -> JsonResultStream
Start a non-blocking image-embedding request. Returns a JsonResultStream
delivering the EmbeddingResponse JSON. Same request schema as embed_image.
embed_audio(request_json: str) -> str
Embed a single audio clip into a vector with the CLAP model. The vector shares CLAP's text space, so a text query like "dog barking" retrieves matching audio by similarity.
request_json: JSON withmodel("clap") andaudio(base64-encoded WAV bytes).
Returns: EmbeddingResponse JSON with one data entry.
import json, base64
req = json.dumps({"model": "clap", "audio": base64.b64encode(open("bark.wav","rb").read()).decode()})
vec = json.loads(engine.embed_audio(req))["data"][0]["embedding"]
embed_audio_async(request_json: str) -> JsonResultStream
Start a non-blocking audio-embedding request. Returns a JsonResultStream
delivering the EmbeddingResponse JSON. Same request schema as embed_audio.
embed_similarity(model_id: str, text_a: str, text_b: str) -> float
Compute cosine similarity between two texts using an embedding model.
A convenience method that embeds both texts and returns their cosine similarity as a float in the range -1.0 to 1.0.
model_id: Embedding model to use (e.g."sentence-transformers/all-MiniLM-L6-v2").text_a: First text.text_b: Second text.
Returns: Cosine similarity score (1.0 = identical, 0.0 = orthogonal, -1.0 = opposite).
Raises:
InferenceError: If embedding generation fails.
score = engine.embed_similarity(
"sentence-transformers/all-MiniLM-L6-v2",
"Hello world",
"Hi there",
)
**score is approximately 0.72 (varies by model)**
classifier_predict_text(classifier_id: str, text: str, top_k: Optional[int]) -> str
Predict the class of a text string using a loaded classifier.
classifier_id: ID of a previously loaded classifier.text: The input text to classify.top_k: Number of top predictions to return. PassNonefor all classes.
Returns: A JSON string with the prediction result.
{
"label": "positive",
"probability": 0.95,
"top": [
{"label": "positive", "probability": 0.95},
{"label": "neutral", "probability": 0.04}
]
}
Raises:
AtelicoError: If the classifier is not loaded or prediction fails.
import json
result = json.loads(engine.classifier_predict_text("sentiment", "I love this!", top_k=3))
print(result["label"], result["probability"])
classifier_predict_async(classifier_id: str, text: str, top_k: Optional[int]) -> JsonResultStream
Start a non-blocking text classification. Returns a JsonResultStream
delivering the prediction JSON (same schema as classifier_predict).
classifier_predict_image(classifier_id: str, image_path: str, top_k: Optional[int]) -> str
Predict the class of an image using a multi-modal classifier.
The classifier referenced by classifier_id must have been trained
with an image embedder (e.g. DINOv2). Calling this against a text-only
classifier raises AtelicoError.
classifier_id: ID of a previously loaded image classifier.image_path: Filesystem path to the image (PNG / JPEG).top_k: Number of top predictions to return. PassNonefor all classes.
Returns: A JSON string with the prediction result (same schema as
classifier_predict).
import json
result = json.loads(engine.classifier_predict_image(
"animals",
"/abs/path/to/cat.jpg",
top_k=3,
))
print(result["label"], result["probability"])
classifier_predict_audio(classifier_id: str, audio_path: str, top_k: Optional[int]) -> str
Predict the class of an audio clip using a multi-modal classifier.
The classifier referenced by classifier_id must have been trained
with an audio embedder (e.g. CLAP). Calling this against a classifier
trained on a different modality raises AtelicoError.
classifier_id: ID of a previously loaded audio classifier.audio_path: Filesystem path to the audio clip (WAV).top_k: Number of top predictions to return. PassNonefor all classes.
Returns: A JSON string with the prediction result (same schema as
classifier_predict_text).
import json
result = json.loads(engine.classifier_predict_audio(
"sounds",
"/abs/path/to/bark.wav",
top_k=3,
))
print(result["label"], result["probability"])
classifier_load(classifier_id: str, directory: str)
Load a classifier from a directory so it can serve predictions.
The directory must contain a classifier exported by the engine
(the classifier kind plus, for embedding-based kinds, its embedder
reference). Every kind is handled -- centroid, KNN/HNSW, SetFit, and the
embedder-free n-gram perceptron, which loads offline from a bundled
directory with no model download. Once loaded, the classifier is
addressable by classifier_id from the classifier_predict_* methods.
classifier_id: ID to register the loaded classifier under.directory: Filesystem path to the classifier directory.
Raises:
AtelicoError: If the directory is missing or the classifier fails to load.
engine.classifier_load("facing_lr", "/abs/path/to/facing_lr")
result = engine.classifier_predict_image("facing_lr", "/abs/path/to/creature.png")
classifier_create(classifier_id: str, config_json: str)
Create an empty classifier to teach during play with
classifier_train_incremental_text.
The classifier starts with no labels; each label is created by its first
example. Persist it with classifier_save and restore it with
classifier_load.
classifier_id: ID to register it under; must not already exist.config_json: JSON object withkind("centroid","knn"or"ngram_perceptron"),embedder(embedding model id, required for centroid/knn, absent for ngram_perceptron; weights must be cached), optionalmultilabel(bool) andk_neighbors(knn only, default 7).
Raises:
ValueError: If the config is malformed, does not fit the kind, or the id is taken.
import json
engine.classifier_create("dragon_cues", json.dumps({"kind": "centroid", "embedder": "all-MiniLM-L6-v2"}))
classifier_train_incremental_text(classifier_id: str, label: str, text: str)
Teach a classifier one example while it runs: folds text into label,
creating the label if it is new. No retraining pass.
classifier_id: a created or loaded classifier.label: the example's label (non-empty).text: the example.
Raises:
ValueError: On an empty label.
AtelicoError: If the classifier cannot learn incrementally (see classifier_info).
engine.classifier_train_incremental_text("dragon_cues", "come", "Stormfly, come")
classifier_train_incremental_embedding(classifier_id: str, label: str, embedding: list[float])
Teach one example from an embedding you already computed, skipping a
second embedding of the same text. The vector must come from the
classifier's own embedder model and have embedding_dim entries (see
classifier_info).
Raises:
ValueError: On a wrong-length vector, or a classifier with no embedding space.
vector = json.loads(engine.embed(...))["data"][0]["embedding"]
engine.classifier_train_incremental_embedding("dragon_cues", "come", vector)
classifier_predict_embedding(classifier_id, embedding, top_k=None) -> str
Classify an embedding you already computed. Same rules for the vector as
classifier_train_incremental_embedding; returns the same JSON as
classifier_predict_text.
result = json.loads(engine.classifier_predict_embedding("dragon_cues", vector, top_k=3))
print(result["label"])
classifier_remove_label(classifier_id: str, label: str)
Forget a label and everything learned for it.
Raises:
ValueError: If the label does not exist.
engine.classifier_remove_label("dragon_cues", "spin")
classifier_info(classifier_id: str) -> str
Describe a classifier: kind, labels and examples per label, as JSON.
{"classifier_id": "dragon_cues", "kind": "centroid", "modality": "text",
"embedding_dim": 384, "supports_incremental_training": true,
"labels": [{"label": "come", "example_count": 6}]}
embedding_dim is null for classifiers without an embedding space;
example_count is null for classifiers saved before counts were recorded.
info = json.loads(engine.classifier_info("dragon_cues"))
for entry in info["labels"]:
print(entry["label"], entry["example_count"])
classifier_save(classifier_id: str, directory: str)
Save a classifier to a directory, in the format classifier_load reads
back. The directory is created if missing; a previous save is replaced.
engine.classifier_save("dragon_cues", "/saves/slot1/dragon_cues")
guardrail_check_input(text: str) -> str
Check user input text against safety guardrails.
text: The user-provided input text to validate.
Returns: A JSON string with the SafetyVerdict.
When allowed:
{"action": "Allow", "checker_name": "keyword", "score": null}
When blocked:
{"action": {"Block": {"reason": "profanity detected"}}, "checker_name": "keyword", "score": 0.95}
Fields:
action:"Allow", or{"Block": {"reason": "..."}}, or{"Rewrite": {"original": "...", "rewritten": "...", "reason": "..."}}.checker_name: which guardrail checker produced the verdict.score: confidence score (0.0-1.0), or null.
Raises:
AtelicoError if guardrails are not configured.
import json
verdict = json.loads(engine.guardrail_check_input("Tell me a joke"))
if verdict["action"] != "Allow":
print(f"Blocked by {verdict['checker_name']}")
guardrail_check_output(text: str) -> str
Check model output text against safety guardrails.
text: The model-generated output to validate before displaying.
Returns: A JSON string with the SafetyVerdict (same schema as guardrail_check_input).
Raises:
AtelicoError: If guardrails are not configured.
import json
verdict = json.loads(engine.guardrail_check_output("Here is a helpful answer."))
if not verdict["allowed"]:
print(f"Blocked: {verdict['category']}")
lora_load(model_id: str, adapter_path: str)
Make an adapter the default for a model: requests for model_id without a
::adapter suffix run with it from the next request on. A request can still pick
its own adapter ("model::adapter") and strength ("adapter_scale").
model_id: The base model ID.adapter_path: An asset-store adapter id ("pirate"), or a local adapter directory written as a path — absolute, or starting with.//../— holdingadapter_config.jsonandadapter_model.safetensors.
Returns: None
Raises:
ModelLoadError: If the adapter cannot be resolved.
engine.lora_load("meta-llama/Llama-3.2-1B-Instruct-GGUF", "./adapters/my-lora")
lora_unload(model_id: str)
Clear a model's default adapter and strength: requests without a suffix run on the base weights again.
model_id: The base model ID.
Returns: None
engine.lora_unload("meta-llama/Llama-3.2-1B-Instruct-GGUF")
lora_set_scale(model_id: str, scale: float)
Set the strength a model's adapter runs at, for requests without their own
adapter_scale: 1.0 as trained, 0.0 the base model, values between blend.
model_id: The base model ID.scale: Finite and non-negative; above1.0exaggerates the adapter.
Returns: None
Raises:
ValueError: For a negative or non-finite scale.
engine.lora_set_scale("meta-llama/Llama-3.2-1B-Instruct-GGUF", 0.5)
lora_list_active() -> str
List the models that have a default adapter.
Returns: A JSON array of {"model_id": ..., "adapter": ...} objects, [] when
no model has one.
import json
for entry in json.loads(engine.lora_list_active()):
print(entry["model_id"], "->", entry["adapter"])
**meta-llama/Llama-3.2-1B-Instruct-GGUF -> ./adapters/my-lora**
guardrail_check_image_prompt(text: str) -> str
Check an image generation prompt against safety guardrails.
text: The image generation prompt to validate.
Returns: A JSON string with the SafetyVerdict (same schema as guardrail_check_input).
Raises:
AtelicoError: If guardrails are not configured.
import json
verdict = json.loads(engine.guardrail_check_image_prompt("A sunset over mountains"))
if not verdict["allowed"]:
print(f"Blocked: {verdict['category']}")
model_list() -> str
List all loaded models.
Returns: A JSON array of ModelInfo objects.
[
{"id": "meta-llama/Llama-3.2-1B-Instruct-GGUF", "object": "model", "owned_by": "meta-llama"}
]
import json
models = json.loads(engine.model_list())
for m in models:
print(m["id"])
model_is_loaded(model_id: str) -> bool
Check if a model is loaded and ready for inference.
model_id: The model ID to check.
Returns: True if the model is loaded and ready, False otherwise.
is_ready = engine.model_is_loaded("meta-llama/Llama-3.2-1B-Instruct-GGUF")
kvstore_create(store_id: str, db_path: str, config_json: str) -> int
Create a new semantic key-value store backed by LanceDB.
store_id: Unique identifier for this store instance.db_path: Filesystem path for the LanceDB database directory.config_json: A JSON string withKvStoreConfigfields.
Expected config JSON structure:
{
"table_name": "entries",
"embed_dim": 384,
"has_priority": false,
"similarity_weight": 0.5,
"priority_weight": 0.5
}
Returns: The store handle ID (int).
Raises:
ValueError: If config_json is invalid.
StoreError: If store creation fails.
import json
config = json.dumps({"table_name": "game_lore", "embed_dim": 384})
handle = engine.kvstore_create("lore", "/data/lore_db", config)
kvstore_insert(store_id: str, entries_json: str)
Insert entries into a KV store.
store_id: The store to insert into.entries_json: A JSON array ofKvEntryobjects.
Expected entries JSON structure (each object is a KvEntry):
[
{
"id": "greeting-1",
"key_text": "how do I greet the innkeeper?",
"key_embedding": [0.1, 0.2, 0.3],
"facets": {"location": "tavern"},
"values": {"answer": {"Utf8": "Hello!"}, "mood": {"Int32": 2}},
"priority": 0.5
}
]
key_embedding must be embed_dim long. facets and values are required
(use {} when the store has none); every facet the store was created with
must be present. Each value is tagged with its column type (Utf8, Int32,
Float32). priority is optional.
Returns: None
Raises:
ValueError: If entries_json is invalid.
StoreError: If the store is not found or insertion fails.
import json
entries = json.dumps([{
"id": "greeting-1",
"key_text": "how do I greet the innkeeper?",
"key_embedding": [0.1, 0.2, 0.3],
"facets": {},
"values": {"answer": {"Utf8": "Hello!"}},
}])
engine.kvstore_insert("lore", entries)
kvstore_query(store_id: str, query_json: str) -> str
Query a KV store using vector similarity search.
store_id: The store to query.query_json: A JSON string withKvQueryfields.
Expected query JSON structure:
{
"query_embedding": [0.1, 0.2, 0.3],
"query_text": "optional text filter",
"limit": 5,
"vector_search_limit": 20,
"use_prefilter": true
}
Returns: A JSON array of result objects.
[
{
"id": "...",
"key_text": "greeting",
"similarity": 0.95,
"priority": 1.0,
"combined_score": 0.975
}
]
Raises:
ValueError: If query_json is invalid.
StoreError: If the store is not found or the query fails.
import json
query = json.dumps({"query_embedding": embedding_vec, "limit": 5})
results = json.loads(engine.kvstore_query("lore", query))
for r in results:
print(r["key_text"], r["similarity"])
kvstore_create_fts_index(store_id: str, column: str)
Create a full-text-search (FTS) index on a string column of a KV store.
Required before calling kvstore_lexical_query or
kvstore_hybrid_query. Typical column choices are "key_text" (the
search keys) or any string-typed value column.
store_id: The store to index.column: Column name to index (string-typed).
Raises:
StoreError if the store is not found.
engine.kvstore_create_fts_index("lore", "key_text")
kvstore_lexical_query(store_id: str, query_json: str) -> str
Lexical (full-text) retrieval against an FTS-indexed column.
The target column must have an FTS index — see
kvstore_create_fts_index. Returns rows ranked by BM25 relevance.
-
store_id: The store to query. -
query_json: JSONLexicalSearchQuery:{"text": "dragon attack", "column": "key_text", "filter": null, "limit": 10}
Returns: A JSON array of KvResult rows. The combined_score field
carries the BM25 score.
Raises:
StoreError if the store is not found, or InternalError if
the column does not have an FTS index.
import json
rows = json.loads(engine.kvstore_lexical_query(
"lore",
json.dumps({"text": "dragon", "column": "key_text", "limit": 5}),
))
for r in rows: print(r["key_text"], r["combined_score"])
kvstore_hybrid_query(store_id: str, query_json: str) -> str
Hybrid (vector + lexical) retrieval with weighted-sum reranking.
Each branch returns up to effective_per_branch_limit candidates;
scores are min-max normalised independently and combined as
vector_weight * vec_norm + lexical_weight * lex_norm. The FTS column
must have an FTS index.
-
store_id: The store to query. -
query_json: JSONHybridSearchQuery:{"embeddings": [0.0, 0.1, 0.2],"text": "dragon attack","fts_column": "key_text","filter": null,"limit": 5,"per_branch_limit": 0,"vector_weight": 0.5,"lexical_weight": 0.5}per_branch_limit: 0defaults to2 * limit.
Returns: A JSON object:
{
"results": [{ ...KvResult... }],
"scores": [{"vector_score": 0.8, "lexical_score": 0.6, "merged_score": 0.7}]
}
combined_score on each result equals the corresponding merged_score.
kvstore_scan(store_id, filter=None, limit=100) -> str
Scan entries in a KV store with an optional SQL-like filter.
Unlike kvstore_query, this does not use vector similarity -- it
returns entries matching the filter, ordered by insertion.
store_id: The store to scan.filter: SQL-like filter expression (e.g."category = 'lore'"). PassNoneto return all entries.limit: Maximum number of entries to return (default 100).
Returns: A JSON array of KvEntry objects.
[
{"key": "greeting", "value": "Hello!", "embedding": [0.1, 0.2, 0.3]}
]
Raises:
StoreError: If the store is not found.
import json
entries = json.loads(engine.kvstore_scan("lore", limit=10))
for entry in entries:
print(entry["key"], entry["value"])
kvstore_count(store_id, filter=None) -> int
Count entries in a KV store, optionally filtered.
store_id: The store to count entries in.filter: SQL-like filter expression. PassNoneto count all entries.
Returns: The number of matching entries (int).
Raises:
StoreError: If the store is not found.
count = engine.kvstore_count("lore")
filtered = engine.kvstore_count("lore", filter="category = 'quest'")
kvstore_delete(store_id: str)
Delete a KV store and remove its database files.
store_id: The store to delete.
Returns: None
Raises:
StoreError: If the store is not found.
engine.kvstore_delete("lore")
set_scheduling_mode(mode: str)
Set the GPU scheduling mode at runtime.
Controls how GPU time is shared between inference and rendering when running alongside a game engine.
mode: One of"balance"(default),"prioritize_compute"(maximize inference speed), or"prioritize_graphics"(minimize rendering impact).
Returns: None
engine.set_scheduling_mode("prioritize_graphics")
set_vram_budget_mb(mb: int)
Set the VRAM budget in megabytes at runtime.
mb: Maximum VRAM to use in megabytes.0means unlimited.
Returns: None
engine.set_vram_budget_mb(4096) # 4 GB budget
set_target_tps(tps: int)
Set the target tokens per second for inference throttling.
When set to a non-zero value, the engine will pace token generation to leave GPU headroom for rendering.
tps: Target tokens per second.0means unlimited (default).
Returns: None
engine.set_target_tps(30) # throttle to ~30 tok/s
@staticmethod is_cig_d3d12_supported(device_index=0) -> bool
Check whether D3D12 Compute-in-Graphics (CiG) is supported on a GPU.
CiG allows sharing GPU scheduling context with a D3D12 renderer, avoiding OS-level context switching. Requires NVIDIA R570+ driver, CUDA 12.8+, and Ada Lovelace+ GPU.
device_index: CUDA device index (default0).
Returns: True if D3D12 CiG is supported, False otherwise.
Always returns False when built without CUDA.
supported = Engine.is_cig_d3d12_supported(0)
@staticmethod is_cig_vulkan_supported(device_index=0) -> bool
Check whether Vulkan Compute-in-Graphics (CiG) is supported on a GPU.
CiG allows sharing GPU scheduling context with a Vulkan renderer. Requires NVIDIA R570+ driver, CUDA 12.9+, and Ada Lovelace+ GPU.
device_index: CUDA device index (default0).
Returns: True if Vulkan CiG is supported, False otherwise.
Always returns False when built without CUDA.
supported = Engine.is_cig_vulkan_supported(0)
AnnIndex
Approximate Nearest Neighbor index for vector search.
Backed by HNSW (Hierarchical Navigable Small World) graph. This is a pure data structure -- no GPU or models required.
index = AnnIndex(dim=384, max_elements=1000)
index.insert([0.1, 0.2, ...], label_id=42)
index.build()
results = index.search([0.1, 0.2, ...], k=5)
Methods
__init__(dim, max_elements=10000, m=16, ef_construction=200, ef_search=50) -> Self
Create a new empty ANN index.
dim: Dimensionality of vectors.max_elements: Maximum number of elements the index can hold (default 10000).m: HNSW M parameter -- connectivity degree (default 16).ef_construction: Search scope during construction (default 200).ef_search: Search scope during query (default 50).
Returns: A new empty AnnIndex instance.
index = AnnIndex(dim=384, max_elements=1000)
dim() -> int
Return the vector dimensionality of this index.
Returns: The dimensionality (int) passed to the constructor.
index = AnnIndex(dim=384)
assert index.dim() == 384
insert(vector: list[float], label_id: int)
Insert a vector with an associated label ID.
Call build() after all insertions are complete before searching.
vector: A list of floats with length matchingdim.label_id: An integer label associated with this vector (used to identify results returned bysearch).
Returns: None
index.insert([0.1, 0.2, 0.3], label_id=42)
build()
Build the HNSW index graph. Must be called after all insertions and before search.
Returns: None
index.insert([0.1, 0.2, 0.3], label_id=1)
index.build()
**Index is now ready for search**
search(query: list[float], k: int) -> list[(usize, f32)]
Search for the k nearest neighbors of a query vector.
query: Query vector with length matchingdim.k: Number of nearest neighbors to return.
Returns: A list of (label_id, distance) tuples sorted
by ascending cosine distance (lower = more similar).
results = index.search([0.1, 0.2, 0.3], k=5)
for label_id, distance in results:
print(f"Label {label_id}: distance={distance:.4f}")
save(path: str)
Save the index to a file on disk.
path: Filesystem path to write the index to.
Returns: None
Raises:
AtelicoError: If the file cannot be written.
index.save("/data/my_index.bin")
@staticmethod load(path: str) -> Self
Load a previously saved index from disk.
path: Filesystem path to read the index from.
Returns: A new AnnIndex instance with the loaded data.
Raises:
AtelicoError: If the file cannot be read or is corrupted.
index = AnnIndex.load("/data/my_index.bin")
results = index.search([0.1, 0.2, 0.3], k=5)
A2f
Audio-to-Face (A2F / lip-sync): drive ARKit-52 facial blendshapes from speech.
Standalone — has its own ~741 MB model bundle (NVIDIA Audio2Face-3D, downloaded
on first use), independent of Engine. Build one and reuse it across calls.
import json
from atelico import A2f
a2f = A2f() # auto device; downloads bundle once
frames = json.loads(a2f.process(pcm, 24000, "claire")) # pcm: list[float] in [-1, 1]
**frames[i]["weights"] is a list of 52 ARKit blendshape values in [0, 1]**
Methods
__init__() -> Self
Load A2F on the fastest available device, downloading the model bundle on first use. Reuse the instance — loading is expensive, inference is cheap.
process(pcm, sample_rate, character, noise_seed=None) -> str
Run A2F over a mono PCM waveform → ARKit-52 blendshape frames (60 fps), as JSON.
pcm: mono samples in[-1.0, 1.0].sample_rate: the PCM sample rate (e.g. 24000 for Kokoro TTS; resampled internally).character:"claire","james", or"mark".noise_seed: optional int seed for deterministic output (default random).
Returns: a JSON array [{"time_seconds": .., "weights": [52 floats]}, ...].