Skip to content

yggdrasil.loki.engines.claude_engine

claude_engine

Anthropic (Claude) -backed :class:TokenEngine.

Two ways to authenticate, resolved in this order:

  1. API keyANTHROPIC_API_KEY (or api_key=). Billed per token.
  2. OAuth / subscription token — the credential Claude Code itself logs in with, so Loki can reason without a separate billed API key when run on a machine where you're signed into Claude Code. Taken from ANTHROPIC_AUTH_TOKEN / CLAUDE_CODE_OAUTH_TOKEN, or the Claude Code credentials file (~/.claude/.credentials.jsonclaudeAiOauth). The SDK sends it as a bearer token; the request carries the OAuth beta header and the Claude Code system identity the grant is scoped to.

An API key wins when both are present. available() is true when either is resolvable, so ygg loki lights up Claude on a logged-in Claude Code box with no extra configuration.

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).

uses_oauth property

uses_oauth: bool

True when this engine will authenticate with a subscription token.

model_label property

model_label: str

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.

usage

usage() -> list[Any]

This engine's per-model usage rows from the global meter.

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.