Skip to content

LLM

The imbi.common.llm package holds the code that Imbi services use to call language models and to manage the AI model catalog. For the operator view of providers and models, see AI models.

Module What it does
imbi.common.llm AnthropicClient, a small wrapper for JSON completions
drivers The static catalog of provider drivers
discovery Reads the model list from a provider's list-models API
typesafe Calls a TypeSafe System One decision model

AnthropicClient and discovery need the llm extra, which installs the anthropic package.

JSON completions

AnthropicClient.complete_json() sends one prompt and validates the first JSON object in the response against a Pydantic model. It never raises for a model failure. When the API key is not set, the SDK is not installed, the call fails, or the response does not validate, it returns your fallback with degraded=True. An endpoint can pass degraded to the UI to say that AI is not available.

import pydantic

from imbi.common import llm


class Summary(pydantic.BaseModel):
    title: str
    body: str


client = llm.AnthropicClient()
result = await client.complete_json(
    'Summarize these commits as JSON with title and body: ...',
    schema=Summary,
    fallback=Summary(title='', body=''),
)
if result.degraded:
    ...

Make one client at startup and use it for all requests. Call aclose() at shutdown. Set cache_system_prompt=True with a system prompt to send the system prompt with prompt caching.

Variable Default Description
ANTHROPIC_API_KEY none The API key. Without it, every call returns the fallback
IMBI_ANTHROPIC_DEFAULT_MODEL DEFAULT_MODEL The model to use when the call does not give one
IMBI_ANTHROPIC_TIMEOUT 30.0 Request timeout, in seconds
IMBI_ANTHROPIC_MAX_RETRIES 2 SDK retries

Drivers

A driver is the code that talks to one kind of provider. Drivers are in code, not in the graph. An AIProvider node exists only when an admin configures a provider. DRIVERS is the catalog. Each DriverInfo says if the driver has a default endpoint, needs a base_url, can use cloud IAM credentials, supports model discovery, and serves generative or decision models.

resolve_base_url(driver, base_url) returns the provider's own base_url, or else the driver default.

Model discovery

discovery.list_models(driver, api_key, base_url) returns the models that a provider reports. Only the anthropic, openai and openai_compatible drivers support it. The admin UI uses it to make AIModel nodes and to test a stored credential. Every failure raises DiscoveryError. Its message contains no API key and no response body, so the caller can show it to an admin.

Decision models

typesafe.decide() sends a state and a set of questions to a TypeSafe System One model (the typesafe driver) and returns typed answers. Every failure raises DecisionError, with a message that is safe to show.

API Reference

Anthropic client

AnthropicClient

AnthropicClient(
    settings: AnthropicSettings | None = None,
    *,
    client: AsyncAnthropic | None = None,
)

Thin wrapper around :class:anthropic.AsyncAnthropic.

A single instance is created at startup; callers reuse it across requests. When :attr:available is False (no API key, or the SDK is not installed), :meth:complete_json returns the caller's fallback with degraded=True instead of raising.

Source code in libraries/common/src/imbi/common/llm/__init__.py
def __init__(
    self,
    settings: AnthropicSettings | None = None,
    *,
    client: anthropic.AsyncAnthropic | None = None,
) -> None:
    self._settings = settings or AnthropicSettings()
    self._client = client
    if self._client is None and self._settings.api_key:
        self._client = self._build_client()

complete_json async

complete_json(
    prompt: str,
    *,
    schema: type[T],
    fallback: T,
    max_tokens: int = 1024,
    model: str | None = None,
    system: str | None = None,
    cache_system_prompt: bool = False,
) -> CompletionResult[T]

Issue a JSON-shaped completion.

schema is a Pydantic model the response is validated against. fallback is returned (with degraded=True) on any failure path. When cache_system_prompt is True and system is provided, the system block is sent with prompt caching enabled — useful for callers that re-use the same instructions across many requests.

Source code in libraries/common/src/imbi/common/llm/__init__.py
async def complete_json[T: pydantic.BaseModel](
    self,
    prompt: str,
    *,
    schema: type[T],
    fallback: T,
    max_tokens: int = 1024,
    model: str | None = None,
    system: str | None = None,
    cache_system_prompt: bool = False,
) -> CompletionResult[T]:
    """Issue a JSON-shaped completion.

    ``schema`` is a Pydantic model the response is validated
    against.  ``fallback`` is returned (with ``degraded=True``) on
    any failure path.  When ``cache_system_prompt`` is ``True`` and
    ``system`` is provided, the system block is sent with prompt
    caching enabled — useful for callers that re-use the same
    instructions across many requests.
    """
    if self._client is None:
        return CompletionResult(data=fallback, degraded=True)
    chosen_model = model or self._settings.default_model
    system_blocks: typing.Any = system
    if cache_system_prompt and system:
        system_blocks = [
            {
                'type': 'text',
                'text': system,
                'cache_control': {'type': 'ephemeral'},
            },
        ]
    kwargs: dict[str, typing.Any] = {
        'model': chosen_model,
        'max_tokens': max_tokens,
        'messages': [{'role': 'user', 'content': prompt}],
    }
    if system_blocks is not None:
        kwargs['system'] = system_blocks
    try:
        response = typing.cast(
            typing.Any, await self._client.messages.create(**kwargs)
        )
    except Exception as exc:  # noqa: BLE001
        LOGGER.warning(
            'Anthropic completion failed (%s); returning fallback', exc
        )
        return CompletionResult(data=fallback, degraded=True)

    content_blocks: list[typing.Any] = list(
        getattr(response, 'content', None) or []
    )
    text = ''.join(
        str(getattr(block, 'text', ''))
        for block in content_blocks
        if getattr(block, 'type', None) == 'text'
    )
    payload = _extract_json(text)
    if payload is None:
        LOGGER.warning(
            'Anthropic response had no parseable JSON object; '
            'returning fallback'
        )
        return CompletionResult(data=fallback, degraded=True)
    try:
        parsed = schema.model_validate(payload)
    except pydantic.ValidationError as exc:
        LOGGER.warning(
            'Anthropic response failed schema validation (%s); '
            'returning fallback',
            exc,
        )
        return CompletionResult(data=fallback, degraded=True)
    return CompletionResult(data=parsed, degraded=False)

CompletionResult

Bases: BaseModel

Outcome of a :meth:AnthropicClient.complete_json call.

degraded=True signals that the underlying API call failed and the caller-provided fallback was returned in its place. Endpoints typically forward this flag to the UI so the user can be told "AI unavailable".

AnthropicSettings

Bases: BaseSettings

Anthropic client configuration.

Reads ANTHROPIC_API_KEY plus the IMBI_ANTHROPIC_* namespace so the same env-var conventions used elsewhere in the codebase apply (set IMBI_ANTHROPIC_DEFAULT_MODEL to override the model org-wide, IMBI_ANTHROPIC_TIMEOUT for the request timeout).

DEFAULT_MODEL module-attribute

DEFAULT_MODEL = 'claude-haiku-4-5-20251001'

Drivers

DriverInfo

Bases: BaseModel

One entry in the static provider-driver catalog.

DRIVERS module-attribute

DRIVERS: tuple[DriverInfo, ...] = (
    DriverInfo(
        slug='anthropic',
        name='Anthropic',
        description='Claude models served by the Anthropic API.',
        default_base_url='https://api.anthropic.com',
        supports_discovery=True,
        icon='Sparkles',
    ),
    DriverInfo(
        slug='openai',
        name='OpenAI',
        description='GPT models served by the OpenAI API.',
        default_base_url='https://api.openai.com/v1',
        supports_discovery=True,
        icon='Bot',
    ),
    DriverInfo(
        slug='openai_compatible',
        name='OpenAI-compatible',
        description='Any endpoint that implements the OpenAI chat-completions API, such as vLLM, Ollama, or a gateway.',
        requires_base_url=True,
        supports_discovery=True,
        icon='Server',
    ),
    DriverInfo(
        slug='bedrock',
        name='AWS Bedrock',
        description='Models served through AWS Bedrock, authenticated with an API key or the runtime IAM role.',
        supports_iam=True,
        icon='Cloud',
    ),
    DriverInfo(
        slug='vertex',
        name='Google Vertex AI',
        description='Models served through Google Cloud Vertex AI.',
        supports_iam=True,
        icon='Cloud',
    ),
    DriverInfo(
        slug='typesafe',
        name='TypeSafe',
        description='System One decision models, such as Jev, that return typed judgments and probabilities instead of text.',
        default_base_url='https://api.typesafe.ai/v1',
        model_types=('decision',),
        icon='Scale',
    ),
)

get_driver

get_driver(slug: str) -> DriverInfo | None

Return the catalog entry for slug, or None if unknown.

Source code in libraries/common/src/imbi/common/llm/drivers.py
def get_driver(slug: str) -> DriverInfo | None:
    """Return the catalog entry for ``slug``, or ``None`` if unknown."""
    return DRIVERS_BY_SLUG.get(slug)

resolve_base_url

resolve_base_url(
    driver: str, base_url: str | None
) -> str | None

Return the endpoint a provider should call.

The provider's own base_url wins; otherwise the driver default, which is None for drivers that have none.

Source code in libraries/common/src/imbi/common/llm/drivers.py
def resolve_base_url(driver: str, base_url: str | None) -> str | None:
    """Return the endpoint a provider should call.

    The provider's own ``base_url`` wins; otherwise the driver default,
    which is ``None`` for drivers that have none.
    """
    if base_url:
        return base_url.rstrip('/')
    info = DRIVERS_BY_SLUG.get(driver)
    return info.default_base_url if info else None

Discovery

list_models async

list_models(
    driver: str,
    api_key: str,
    base_url: str | None,
    *,
    bearer: bool = False,
) -> list[DiscoveredModel]

Return the models driver reports for api_key.

Parameters:

Name Type Description Default
driver str

Provider driver slug.

required
api_key str

Decrypted credential for the provider.

required
base_url str | None

Endpoint to call, already resolved against the driver's default.

required
bearer bool

api_key is a workload identity access token.

False

Returns:

Type Description
list[DiscoveredModel]

The provider's models, in the order the provider returned them.

Raises:

Type Description
DiscoveryError

The driver cannot be discovered, or the call failed. The message is safe to show an admin.

Source code in libraries/common/src/imbi/common/llm/discovery.py
async def list_models(
    driver: str, api_key: str, base_url: str | None, *, bearer: bool = False
) -> list[DiscoveredModel]:
    """Return the models ``driver`` reports for ``api_key``.

    Parameters:
        driver: Provider driver slug.
        api_key: Decrypted credential for the provider.
        base_url: Endpoint to call, already resolved against the
            driver's default.
        bearer: ``api_key`` is a workload identity access token.

    Returns:
        The provider's models, in the order the provider returned them.

    Raises:
        DiscoveryError: The driver cannot be discovered, or the call
            failed. The message is safe to show an admin.

    """
    if driver == 'anthropic':
        return await _list_anthropic(api_key, base_url, bearer=bearer)
    if driver in ('openai', 'openai_compatible'):
        if not base_url:
            raise DiscoveryError(
                f'Driver {driver!r} has no endpoint configured'
            )
        return await _list_openai(api_key, base_url)
    raise DiscoveryError(f'Driver {driver!r} does not support discovery')

DiscoveredModel dataclass

DiscoveredModel(
    model_id: str,
    display_name: str,
    created_at: datetime | None = None,
    context_window: int | None = None,
    max_output_tokens: int | None = None,
)

One model reported by a provider.

DiscoveryError

DiscoveryError(message: str, status: int | None = None)

Bases: Exception

A provider's model list could not be retrieved.

status is the HTTP status of the provider, when it answered.

Source code in libraries/common/src/imbi/common/llm/discovery.py
def __init__(self, message: str, status: int | None = None) -> None:
    super().__init__(message)
    self.status = status

TypeSafe

decide async

decide(
    api_key: str,
    base_url: str,
    model: str,
    state: object,
    questions: dict[str, Any],
    transport: AsyncBaseTransport | None = None,
) -> DecisionResult

Ask model the questions about state.

Parameters:

Name Type Description Default
api_key str

Decrypted credential for the provider.

required
base_url str

The provider endpoint, for example https://api.typesafe.ai/v1.

required
model str

The model id, for example jev-latest.

required
state object

A string, object, or array.

required
questions dict[str, Any]

Question id to question, in the TypeSafe shape.

required
transport AsyncBaseTransport | None

Test seam for :class:httpx.MockTransport.

None

Raises:

Type Description
DecisionError

The call failed; the message is safe to show.

Source code in libraries/common/src/imbi/common/llm/typesafe.py
async def decide(
    api_key: str,
    base_url: str,
    model: str,
    state: object,
    questions: dict[str, typing.Any],
    transport: httpx.AsyncBaseTransport | None = None,
) -> DecisionResult:
    """Ask ``model`` the ``questions`` about ``state``.

    Parameters:
        api_key: Decrypted credential for the provider.
        base_url: The provider endpoint, for example
            ``https://api.typesafe.ai/v1``.
        model: The model id, for example ``jev-latest``.
        state: A string, object, or array.
        questions: Question id to question, in the TypeSafe shape.
        transport: Test seam for :class:`httpx.MockTransport`.

    Raises:
        DecisionError: The call failed; the message is safe to show.

    """
    url = base_url.rstrip('/') + '/systemone'
    body = {'model': model, 'state': state, 'questions': questions}
    try:
        async with httpx.AsyncClient(
            timeout=TIMEOUT, transport=transport
        ) as client:
            async with client.stream(
                'POST',
                url,
                json=body,
                headers={'Authorization': f'Bearer {api_key}'},
            ) as response:
                if response.status_code >= 400:
                    raise DecisionError(
                        f'TypeSafe returned HTTP {response.status_code}'
                    )
                content = await _read_capped(response)
    except DecisionError:
        raise
    except httpx.TimeoutException as e:
        raise DecisionError('TypeSafe did not answer in time') from e
    except httpx.HTTPError as e:
        raise DecisionError(
            f'Could not reach TypeSafe ({type(e).__name__})'
        ) from e
    return _parse(content)

DecisionResult

Bases: TypedDict

DecisionError

Bases: Exception

The decision call failed.