Skip to content

feat: add Tesseract.container_info to expose information about running containers in Python API#601

Merged
dionhaefner merged 9 commits into
mainfrom
feat/expose-container-id-name
May 27, 2026
Merged

feat: add Tesseract.container_info to expose information about running containers in Python API#601
dionhaefner merged 9 commits into
mainfrom
feat/expose-container-id-name

Conversation

@andrinr

@andrinr andrinr commented May 20, 2026

Copy link
Copy Markdown
Contributor

Relevant issue or PR

n/a — surfaces information already stored in _serve_context and consumed internally by server_logs / teardown.

Description of changes

Adds two public properties on Tesseract:

  • Tesseract.container_name — the running Docker container's name, or None when the Tesseract isn't currently serving (before serve(), after teardown(), or when constructed via from_url / from_tesseract_api).
  • Tesseract.container_id — same lifecycle, returns the full container ID (slice [:12] for short form).

Also stores the Container object's .id in _serve_context at serve time so container_id has something to return. Existing internal use of _serve_context["container_name"] (server_logs, teardown) is unchanged.

Why. Downstream callers that want to drive docker stats, docker exec, NVML per-container queries, or docker logs outside the SDK currently have to dig through private attributes. A common workaround is to scan multiple attribute names with fallbacks:

for attr in ("_container", "container", "_service", "_backend"):
    obj = getattr(t, attr, None)
    cid = getattr(obj, "id", None) or getattr(obj, "short_id", None)
    ...

After this lands that scaffolding collapses to t.container_id.

Testing done

  • Added two unit tests in tests/sdk_tests/test_tesseract.py:
    • test_container_id_and_name_set_during_serve — verifies properties are None before serving, take the container's name/ID during, and clear back to None after teardown. Uses the existing mock_serving fixture which already supplies a fake_container.id.
    • test_container_id_and_name_none_for_local_tesseract — verifies from_url Tesseracts report None for both properties (no docker container backs them).
  • Did not run the full unit suite locally; relying on CI.

When a Tesseract is image-backed (``from_image(...)``), the running
container's name and ID are useful for external tooling: ``docker
stats`` polling, ``docker exec`` debugging, NVML container queries,
``docker logs`` outside the SDK, etc. They're already stored
internally on ``_serve_context``, and used by ``server_logs()`` /
``teardown()`` via that private dict — but downstream callers have
to dig into the private attribute themselves, often with version-
specific fallback paths to older private attrs like ``_container``,
``_service``, or ``_backend``.

Expose them as documented public properties:

- ``Tesseract.container_name`` — current container name, or ``None``
  outside the serve window / for non-image Tesseracts.
- ``Tesseract.container_id`` — full container ID (slice ``[:12]`` for
  short form), same lifecycle.

Also store the container's ``.id`` in ``_serve_context`` at serve
time so ``container_id`` has something to return. Existing code that
reads ``container_name`` from ``_serve_context`` (``server_logs``,
``teardown``) is unchanged.

Motivated by mosaic-bench's VRAM/RAM polling, which currently does:

    for attr in ("_container", "container", "_service", "_backend"):
        obj = getattr(t, attr, None)
        cid = getattr(obj, "id", None) or getattr(obj, "short_id", None)
        ...

— scaffolding that becomes a single ``t.container_id`` after this lands.
@codecov

codecov Bot commented May 20, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 85.71429% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 76.83%. Comparing base (51e681b) to head (c02b738).

Files with missing lines Patch % Lines
tesseract_core/sdk/tesseract.py 85.71% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #601      +/-   ##
==========================================
+ Coverage   67.11%   76.83%   +9.71%     
==========================================
  Files          32       32              
  Lines        4495     4502       +7     
  Branches      739      741       +2     
==========================================
+ Hits         3017     3459     +442     
+ Misses       1237      739     -498     
- Partials      241      304      +63     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@PasteurBot

PasteurBot commented May 20, 2026

Copy link
Copy Markdown
Contributor

Benchmark Results

Benchmarks use a no-op Tesseract to measure pure framework overhead.

🚀 0 faster, ⚠️ 0 slower, ✅ 36 unchanged

✅ No significant performance changes detected.

Full results
Benchmark Baseline Current Change Status
api/apply_1,000 0.586ms 0.588ms +0.4%
api/apply_100,000 0.590ms 0.586ms -0.6%
api/apply_10,000,000 0.588ms 0.587ms -0.2%
cli/apply_1,000 1687.994ms 1677.670ms -0.6%
cli/apply_100,000 1705.614ms 1665.845ms -2.3%
cli/apply_10,000,000 1736.791ms 1750.744ms +0.8%
decoding/base64_1,000 0.036ms 0.036ms -1.6%
decoding/base64_100,000 0.645ms 0.678ms +5.2%
decoding/base64_10,000,000 70.677ms 68.200ms -3.5%
decoding/binref_1,000 0.204ms 0.203ms -0.3%
decoding/binref_100,000 0.249ms 0.241ms -2.9%
decoding/binref_10,000,000 10.943ms 10.897ms -0.4%
decoding/json_1,000 0.107ms 0.106ms -1.0%
decoding/json_100,000 8.726ms 8.799ms +0.8%
decoding/json_10,000,000 1071.932ms 1068.610ms -0.3%
encoding/base64_1,000 0.040ms 0.039ms -3.4%
encoding/base64_100,000 0.145ms 0.144ms -0.6%
encoding/base64_10,000,000 28.023ms 26.222ms -6.4%
encoding/binref_1,000 0.316ms 0.313ms -1.0%
encoding/binref_100,000 0.502ms 0.489ms -2.7%
encoding/binref_10,000,000 18.973ms 18.877ms -0.5%
encoding/json_1,000 0.153ms 0.153ms +0.2%
encoding/json_100,000 13.054ms 13.324ms +2.1%
encoding/json_10,000,000 1407.744ms 1395.255ms -0.9%
http/apply_1,000 3.200ms 3.201ms +0.0%
http/apply_100,000 10.004ms 9.169ms -8.3%
http/apply_10,000,000 801.042ms 770.437ms -3.8%
roundtrip/base64_1,000 0.086ms 0.087ms +1.7%
roundtrip/base64_100,000 0.732ms 0.731ms -0.1%
roundtrip/base64_10,000,000 97.078ms 94.892ms -2.3%
roundtrip/binref_1,000 0.534ms 0.535ms +0.2%
roundtrip/binref_100,000 0.734ms 0.729ms -0.7%
roundtrip/binref_10,000,000 30.404ms 30.649ms +0.8%
roundtrip/json_1,000 0.271ms 0.267ms -1.8%
roundtrip/json_100,000 19.540ms 19.586ms +0.2%
roundtrip/json_10,000,000 2469.431ms 2470.717ms +0.1%
  • Runner: Linux 6.17.0-1013-azure x86_64

@dionhaefner

Copy link
Copy Markdown
Contributor

Hmm, I wonder if a single method container_info would do the trick? I.e.,

>>> from tesseract_core import Tesseract

>>> tess_image = Tesseract.from_image("foobar")
>>> tess_image.container_info()
RuntimeError: `container_info` is only available for served Tesseracts. Use `tess.serve` or `with tess:` first.

>>> tess_image.serve()
>>> tess_image.container_info()
{"id": ..., "name": ...} # OR Container(...)?!

>>> tess_url = Tesseract.from_url("localhost:5000")
>>> tess_url.container_info()
RuntimeError: `container_info` is only available when using `Tesseract.from_image(...)`.

…ner_info()

Per review feedback, replace the pair of properties — which returned
``None`` for both "not yet served" and "wrong constructor" cases — with
a single ``container_info()`` method that raises ``RuntimeError`` with
distinct, actionable messages for each case.

- Returns ``{"id": ..., "name": ...}`` matching the plain-dict
  convention used elsewhere on ``Tesseract`` (``apply()``, ``health()``,
  ``openapi_schema``, ``abstract_eval()``).
- Raises when called on a Tesseract created via ``from_url`` or
  ``from_tesseract_api`` (no Docker container backs it).
- Raises when called on an image-backed Tesseract that isn't currently
  being served.

Tests updated to cover the served / pre-serve / post-teardown /
non-image-backed paths.
@andrinr

andrinr commented May 25, 2026

Copy link
Copy Markdown
Contributor Author

Good call, switching to a single container_info() method with explicit RuntimeErrors for the two not-applicable cases:

>>> Tesseract.from_url("http://localhost:1234").container_info()
RuntimeError: `container_info` is only available when using `Tesseract.from_image(...)`.

>>> t = Tesseract.from_image("foo:bar"); t.container_info()
RuntimeError: `container_info` is only available for served Tesseracts. Use `tess.serve()` or `with tess:` first.

>>> with t:
...     t.container_info()
{"id": "...", "name": "..."}

Went with a plain dict rather than a dataclass to match the convention used by the other Tesseract methods (apply(), health(), openapi_schema, abstract_eval() all return dict).

…or_non_image_tesseract

Use a raw string with an escaped ``.`` for the ``pytest.raises(match=...)``
pattern, and let ruff-format wrap the long ``pytest.raises`` call.
Comment thread tesseract_core/sdk/tesseract.py Outdated
…taclass

Per review feedback, replace the shaped ``{"id": ..., "name": ...}`` dict
with the full ``docker_client.Container`` object via ``Containers.get(...)``.
Each call re-issues a ``docker inspect``, so callers see live ``status``,
``host_port``, ``host_ip``, ``docker_network_ips``, ``exec_run(...)``, and
the raw inspect payload via ``attrs`` — the typical inputs for ``docker
stats`` / ``docker exec`` / NVML / per-container observability.

- Drop the ``container_id`` key added to ``_serve_context``; the
  container name in ``_serve_context`` is enough to look the container
  up at call time.
- ``container_info()`` keeps the two ``RuntimeError``s for the
  non-image and pre-serve cases. A container that disappeared between
  ``serve()`` and the call now surfaces as
  ``docker_client.NotFound``, which is the right signal for that case.
- Tests patch ``Containers.get`` to return a stub container and
  verify the served call forwards the container name through.
Comment thread tesseract_core/sdk/tesseract.py Outdated
Co-authored-by: Dion Häfner <dion.haefner@simulation.science>
@dionhaefner dionhaefner changed the title feat: expose container_name and container_id on Tesseract feat: add Tesseract.container_info to expose information about running containers in Python API May 25, 2026
@dionhaefner
dionhaefner enabled auto-merge (squash) May 26, 2026 14:23
@dionhaefner
dionhaefner merged commit 39b27ce into main May 27, 2026
52 checks passed
@dionhaefner
dionhaefner deleted the feat/expose-container-id-name branch May 27, 2026 13:26
@pasteurlabs pasteurlabs locked and limited conversation to collaborators May 27, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants