yggdrasil.loki.engines¶
engines ¶
Concrete :class:~yggdrasil.loki.engine.TokenEngine backends.
Remote (hosted APIs — fast, capable, metered):
- :class:
OpenAIEngine— the OpenAI API. - :class:
ClaudeEngine— the Anthropic (Claude) API. - :class:
DatabricksServingEngine— a Databricks model-serving endpoint.
Local (run on this workstation — free, private, resource-bound):
- :class:
TransformersEngine— an open HuggingFace model viatransformers(CPU, or an Intel GPU through the XPU torch build). - :class:
OpenVINOEngine— a model on the Intel NPU (AI Boost) via OpenVINO / optimum-intel, falling back to the Intel GPU then CPU. - :class:
OllamaEngine— a model served by a local Ollama server.
ClaudeEngine ¶
ClaudeEngine(
*,
model: Optional[str] = None,
tier: Optional[str] = None,
api_key: Optional[str] = None,
auth_token: Optional[str] = None
)
Bases: TokenEngine
Reason via the Anthropic Messages API (anthropic SDK).
model_label
property
¶
Human label for status output — the pin, or the adaptive ceiling.
uses_oauth
property
¶
True when this engine will authenticate with a subscription token.
choose_tier ¶
choose_tier(
messages: Optional[list[dict[str, Any]]] = None,
system: Optional[str] = None,
) -> str
Adaptive tier for this request: "deep" or "fast".
Sizes on the message content (the actual work, not the fixed system boilerplate) and scans both message and system text for reasoning signals. Long or signalled requests get the deep tier; the rest stay fast. Override for a smarter policy.
resolve_model ¶
resolve_model(
*,
messages: Optional[list[dict[str, Any]]] = None,
system: Optional[str] = None,
tier: Optional[str] = None
) -> Optional[str]
The model id to use for this request.
An explicit self.model pin wins. Otherwise a forced tier (arg or
self.tier) selects from :attr:MODELS; with neither, the tier is
chosen adaptively. Falls back to :attr:default_model when the tier
isn't in the map.
generate ¶
generate(
prompt: str,
*,
system: Optional[str] = None,
tier: Optional[str] = None,
**options: Any
) -> str
Convenience: complete a single user prompt → reply text.
generate_stream ¶
generate_stream(
prompt: str,
*,
system: Optional[str] = None,
tier: Optional[str] = None,
**options: Any
) -> "Iterator[str]"
Convenience: stream a single user prompt → text chunks.
DatabricksServingEngine ¶
DatabricksServingEngine(
*,
client: Any = None,
endpoint: Optional[str] = None,
model: Optional[str] = None,
available: Optional[bool] = None
)
Bases: TokenEngine
Reason via a Databricks serving endpoint.
model_label
property
¶
Human label for status output — the pin, or the adaptive ceiling.
choose_tier ¶
choose_tier(
messages: Optional[list[dict[str, Any]]] = None,
system: Optional[str] = None,
) -> str
Adaptive tier for this request: "deep" or "fast".
Sizes on the message content (the actual work, not the fixed system boilerplate) and scans both message and system text for reasoning signals. Long or signalled requests get the deep tier; the rest stay fast. Override for a smarter policy.
resolve_model ¶
resolve_model(
*,
messages: Optional[list[dict[str, Any]]] = None,
system: Optional[str] = None,
tier: Optional[str] = None
) -> Optional[str]
The model id to use for this request.
An explicit self.model pin wins. Otherwise a forced tier (arg or
self.tier) selects from :attr:MODELS; with neither, the tier is
chosen adaptively. Falls back to :attr:default_model when the tier
isn't in the map.
generate ¶
generate(
prompt: str,
*,
system: Optional[str] = None,
tier: Optional[str] = None,
**options: Any
) -> str
Convenience: complete a single user prompt → reply text.
generate_stream ¶
generate_stream(
prompt: str,
*,
system: Optional[str] = None,
tier: Optional[str] = None,
**options: Any
) -> "Iterator[str]"
Convenience: stream a single user prompt → text chunks.
warm ¶
Best-effort: build + cache the OpenAI-compatible client ahead of the first completion so the first submit isn't slowed by client setup.
A no-op when the openai dep isn't present yet (the first real call
installs it) — the warmer must never trigger a background pip install.
OllamaEngine ¶
OllamaEngine(
*,
model: Optional[str] = None,
tier: Optional[str] = None,
host: Optional[str] = None
)
Bases: LocalEngine
Reason with a local model served by Ollama, sized to the workstation.
bootstrap_model
property
¶
The default model for this box — the resource-sized row, or the
engine's :attr:default_model fallback.
choose_tier ¶
choose_tier(
messages: Optional[list[dict[str, Any]]] = None,
system: Optional[str] = None,
) -> str
Adaptive tier for this request: "deep" or "fast".
Sizes on the message content (the actual work, not the fixed system boilerplate) and scans both message and system text for reasoning signals. Long or signalled requests get the deep tier; the rest stay fast. Override for a smarter policy.
resolve_model ¶
resolve_model(
*,
messages: Optional[list[dict[str, Any]]] = None,
system: Optional[str] = None,
tier: Optional[str] = None
) -> Optional[str]
An explicit pin wins; otherwise the model sized to this workstation.
Local models are resource-bound, so the remote fast/deep cost
tier doesn't apply — the box, not the prompt, picks the size.
generate ¶
generate(
prompt: str,
*,
system: Optional[str] = None,
tier: Optional[str] = None,
**options: Any
) -> str
Convenience: complete a single user prompt → reply text.
stream ¶
stream(
messages: list[dict[str, Any]],
*,
system: Optional[str] = None,
max_tokens: int = DEFAULT_MAX_TOKENS,
tier: Optional[str] = None,
**options: Any
) -> "Iterator[str]"
Yield reply text incrementally as it is produced.
The default has no real streaming — it runs :meth:complete and
yields the whole reply once. Engines whose SDK streams override this
to yield token deltas live (and still record usage on completion).
generate_stream ¶
generate_stream(
prompt: str,
*,
system: Optional[str] = None,
tier: Optional[str] = None,
**options: Any
) -> "Iterator[str]"
Convenience: stream a single user prompt → text chunks.
installed_models ¶
Models already pulled onto this Ollama server (empty if unreachable).
has_model ¶
True when model (with or without a :tag) is already pulled.
pull ¶
pull(
model: Optional[str] = None,
*,
timeout: float = 1800.0,
on_progress: "Optional[Callable[[dict[str, Any]], None]]" = None
) -> str
Download model onto the Ollama server (lazy — the heavy bit).
Defaults to :attr:bootstrap_model, the lightweight brain. Without
on_progress this does the non-streaming pull (one final status object).
With on_progress it streams Ollama's NDJSON progress events —
{"status", "completed", "total"} per chunk — so a caller can render a
live download bar; the final status string is returned either way.
ensure ¶
ensure(
model: Optional[str] = None,
on_progress: "Optional[Callable[[dict[str, Any]], None]]" = None,
) -> dict[str, Any]
Make sure model is available, pulling it only if missing.
Returns {"model", "was_present", "status"} — the lazy-install
receipt so a caller (the setup skill) can report what it did. An
on_progress callback streams the pull's download progress.
OpenAIEngine ¶
OpenAIEngine(
*,
model: Optional[str] = None,
tier: Optional[str] = None,
api_key: Optional[str] = None,
base_url: Optional[str] = None
)
Bases: TokenEngine
Reason via the OpenAI Chat Completions API (openai SDK).
model_label
property
¶
Human label for status output — the pin, or the adaptive ceiling.
choose_tier ¶
choose_tier(
messages: Optional[list[dict[str, Any]]] = None,
system: Optional[str] = None,
) -> str
Adaptive tier for this request: "deep" or "fast".
Sizes on the message content (the actual work, not the fixed system boilerplate) and scans both message and system text for reasoning signals. Long or signalled requests get the deep tier; the rest stay fast. Override for a smarter policy.
resolve_model ¶
resolve_model(
*,
messages: Optional[list[dict[str, Any]]] = None,
system: Optional[str] = None,
tier: Optional[str] = None
) -> Optional[str]
The model id to use for this request.
An explicit self.model pin wins. Otherwise a forced tier (arg or
self.tier) selects from :attr:MODELS; with neither, the tier is
chosen adaptively. Falls back to :attr:default_model when the tier
isn't in the map.
generate ¶
generate(
prompt: str,
*,
system: Optional[str] = None,
tier: Optional[str] = None,
**options: Any
) -> str
Convenience: complete a single user prompt → reply text.
generate_stream ¶
generate_stream(
prompt: str,
*,
system: Optional[str] = None,
tier: Optional[str] = None,
**options: Any
) -> "Iterator[str]"
Convenience: stream a single user prompt → text chunks.
OpenVINOEngine ¶
OpenVINOEngine(
*,
model: Optional[str] = None,
tier: Optional[str] = None,
device: Optional[str] = None
)
Bases: TransformersEngine
Reason with a local model on the Intel NPU via OpenVINO / optimum-intel.
bootstrap_model
property
¶
The default model for this box — the resource-sized row, or the
engine's :attr:default_model fallback.
choose_tier ¶
choose_tier(
messages: Optional[list[dict[str, Any]]] = None,
system: Optional[str] = None,
) -> str
Adaptive tier for this request: "deep" or "fast".
Sizes on the message content (the actual work, not the fixed system boilerplate) and scans both message and system text for reasoning signals. Long or signalled requests get the deep tier; the rest stay fast. Override for a smarter policy.
resolve_model ¶
resolve_model(
*,
messages: Optional[list[dict[str, Any]]] = None,
system: Optional[str] = None,
tier: Optional[str] = None
) -> Optional[str]
An explicit pin wins; otherwise the model sized to this workstation.
Local models are resource-bound, so the remote fast/deep cost
tier doesn't apply — the box, not the prompt, picks the size.
generate ¶
generate(
prompt: str,
*,
system: Optional[str] = None,
tier: Optional[str] = None,
**options: Any
) -> str
Convenience: complete a single user prompt → reply text.
stream ¶
stream(
messages: list[dict[str, Any]],
*,
system: Optional[str] = None,
max_tokens: int = DEFAULT_MAX_TOKENS,
tier: Optional[str] = None,
**options: Any
) -> Iterator[str]
Generate live, token by token, via TextIteratorStreamer.
Without this the base :meth:stream runs the whole generation in one
blocking :meth:complete and yields it at the end — so a slow CPU run
prints nothing until it finishes. Here the pipeline runs on a worker
thread and feeds a streamer the terminal drains as tokens arrive.
generate_stream ¶
generate_stream(
prompt: str,
*,
system: Optional[str] = None,
tier: Optional[str] = None,
**options: Any
) -> "Iterator[str]"
Convenience: stream a single user prompt → text chunks.
ready ¶
True when the pipeline for model (resolved if omitted) is loaded.
Lets a caller (the CLI) warn that a turn is about to trigger the slow first load — download weights + build the pipeline — instead of going silent on a CPU box.
warm ¶
Build the model's pipeline ahead of the first turn — best-effort.
Loading a local model is slow and silent (download weights → build the
pipeline); the ygg loki REPL calls this on a background thread so
the wait overlaps the user picking a session and typing, instead of
stalling the first submit. Swallows failures — they're cached in
:attr:_FAILED and surfaced on the first real turn.
available ¶
True when OpenVINO + optimum are installed and an NPU/GPU is present.
A CPU-only box is left to the transformers / ollama engines — this engine
exists for the accelerators a torch pipeline can't reach (chiefly the NPU).
Cheap: the package check is find_spec; the device list is memoized.
resolve_device ¶
The OpenVINO device to run on: an explicit pin wins, else the best present accelerator — NPU first (the whole point), then GPU.
TransformersEngine ¶
TransformersEngine(
*,
model: Optional[str] = None,
tier: Optional[str] = None,
device: Optional[str] = None
)
Bases: LocalEngine
Reason with a local HuggingFace model (transformers pipeline).
bootstrap_model
property
¶
The default model for this box — the resource-sized row, or the
engine's :attr:default_model fallback.
choose_tier ¶
choose_tier(
messages: Optional[list[dict[str, Any]]] = None,
system: Optional[str] = None,
) -> str
Adaptive tier for this request: "deep" or "fast".
Sizes on the message content (the actual work, not the fixed system boilerplate) and scans both message and system text for reasoning signals. Long or signalled requests get the deep tier; the rest stay fast. Override for a smarter policy.
resolve_model ¶
resolve_model(
*,
messages: Optional[list[dict[str, Any]]] = None,
system: Optional[str] = None,
tier: Optional[str] = None
) -> Optional[str]
An explicit pin wins; otherwise the model sized to this workstation.
Local models are resource-bound, so the remote fast/deep cost
tier doesn't apply — the box, not the prompt, picks the size.
generate ¶
generate(
prompt: str,
*,
system: Optional[str] = None,
tier: Optional[str] = None,
**options: Any
) -> str
Convenience: complete a single user prompt → reply text.
generate_stream ¶
generate_stream(
prompt: str,
*,
system: Optional[str] = None,
tier: Optional[str] = None,
**options: Any
) -> "Iterator[str]"
Convenience: stream a single user prompt → text chunks.
resolve_device ¶
The device to load the pipeline on: an explicit pin (ctor arg /
YGG_LOKI_HF_DEVICE) wins; otherwise the best auto-detected
accelerator — NVIDIA cuda, Intel GPU xpu, Apple mps —
or None (CPU). Lets a local model use the GPU without configuration.
ready ¶
True when the pipeline for model (resolved if omitted) is loaded.
Lets a caller (the CLI) warn that a turn is about to trigger the slow first load — download weights + build the pipeline — instead of going silent on a CPU box.
warm ¶
Build the model's pipeline ahead of the first turn — best-effort.
Loading a local model is slow and silent (download weights → build the
pipeline); the ygg loki REPL calls this on a background thread so
the wait overlaps the user picking a session and typing, instead of
stalling the first submit. Swallows failures — they're cached in
:attr:_FAILED and surfaced on the first real turn.
stream ¶
stream(
messages: list[dict[str, Any]],
*,
system: Optional[str] = None,
max_tokens: int = DEFAULT_MAX_TOKENS,
tier: Optional[str] = None,
**options: Any
) -> Iterator[str]
Generate live, token by token, via TextIteratorStreamer.
Without this the base :meth:stream runs the whole generation in one
blocking :meth:complete and yields it at the end — so a slow CPU run
prints nothing until it finishes. Here the pipeline runs on a worker
thread and feeds a streamer the terminal drains as tokens arrive.