Skip to content

Lesson 1 — an application that is AI-ready

What you will build

An application that works, and that has a place for a model without depending on one. That is the whole lesson: the model is not on the critical path of a support desk, and an application that cannot start without it is an application whose outage is a model's outage.

xyberos.ai is preview tier (stability guide): the facade's shape is settled, and its surface may still move in a minor release.

What you need

Nothing beyond the core package. The determinism here is not a trick for the lesson — the repository ships a provider built for exactly this, and the AI path never needs a model service to run:

examples/learn_ai/ai01_ready.py
"""Lesson 1 — an application that is AI-ready and needs no model to run.

Run it with:

    python -m learn_ai.ai01_ready
"""

from __future__ import annotations

from typing import Annotated

from xyberos import App, Inject, Settings
from xyberos.ai import AIFacade, FakeModelProvider, LoggingPolicy, ModelCapabilities
from xyberos.http import HttpAdapter
from xyberos.http.testing import create_test_client

TICKETS = ["printer is on fire", "badge reader is broken"]


class TicketDesk:
    """The part of the application that has nothing to do with a model."""

    def __init__(self) -> None:
        self._tickets = list(TICKETS)

    def open_tickets(self) -> list[str]:
        """Return the open tickets."""
        return list(self._tickets)


def create_app(*, provider: object | None = None) -> App:
    """Compose the application: a service, and an AI facade behind a seam.

    ``provider`` is what a test or a deployment supplies. Passing nothing still composes a working
    application, because this lesson makes no model call at all.
    """
    app = App(settings=Settings.load(environ={}, overrides={"app": {"name": "ai_assistant"}}))
    app.provide(TicketDesk, instance=TicketDesk())
    app.provide(
        AIFacade,
        instance=AIFacade(
            provider
            if provider is not None
            else FakeModelProvider([], capabilities=ModelCapabilities()),
            logging_policy=LoggingPolicy.metadata_only(),
        ),
        eager=True,
    )

    @app.get("/tickets")
    async def list_tickets(desk: Annotated[TicketDesk, Inject()]) -> list[str]:
        return desk.open_tickets()

    @app.get("/assistant")
    async def assistant_status(assistant: Annotated[AIFacade, Inject()]) -> dict[str, object]:
        health = await assistant.health()
        return {
            "provider": assistant.name,
            "model": health.model,
            "status": health.status.value,
            "detail": health.detail,
        }

    return app


def main() -> None:
    """Show that the application serves its routes with no model call at all."""
    provider = FakeModelProvider([], capabilities=ModelCapabilities())
    with create_test_client(HttpAdapter(create_app(provider=provider))) as client:
        print("tickets:  ", client.get("/tickets").json())
        print("assistant:", client.get("/assistant").json())
    print("model calls:", provider.calls)


if __name__ == "__main__":
    main()

Three things in that module are the lesson:

  • TicketDesk knows nothing about a model. It is a service with a method, exactly like the ones in the traditional path.
  • The facade is registered behind a contract. app.provide(AIFacade, instance=...) means a route can ask for it, and a test can replace it. The provider is a constructor argument of the composition, not a global.
  • Nothing calls the model. The route that reports the assistant's status asks the facade for health(), which is metadata: provider name, model, status. No prompt leaves the process.

How to run it

$env:PYTHONPATH = "src;examples"
python -m learn_ai.ai01_ready
PYTHONPATH=src:examples python -m learn_ai.ai01_ready

What it prints

tickets:   ['printer is on fire', 'badge reader is broken']
assistant: {'provider': 'fake', 'model': 'fake-model', 'status': 'ready', 'detail': ''}
model calls: 0

What the numbers say

Line What it establishes
tickets: [...] the application's own work, served with no model involved
assistant: {'provider': 'fake', ...} the facade is reachable, and reports what it is connected to
model calls: 0 and this lesson made none: the seam exists before it is used

FakeModelProvider([], capabilities=ModelCapabilities()) is a provider with an empty script and no declared capabilities. It is a working provider — that is why health() answers — and it fails loudly if something asks it for a call it was not scripted for, which is the property a test wants.

Common mistakes

  • Reaching for a model before the application works. The first version of a feature is code that does not need one; the model is added to a seam that already exists.
  • Constructing a provider inside a service. A service that builds its own provider cannot be tested without one, which is the coupling this lesson's composition avoids.
  • Reading the API key or base URL at import time. Configuration belongs at composition, so a test composes a different provider without touching the process environment.
  • Treating the fake provider as a mock. It implements the full provider contract and records what it was asked, so a test asserts behaviour rather than call counts on a stub.

Next

Lesson 2 looks at what a provider declares, because the facade checks those declarations before it makes a call rather than discovering the mismatch in production.