Skip to content

LLMClient.stream docstring references last_response, which the Protocol does not declare #3

Description

@zhanghanduo

agent_core/llm.py:87 documents how to retrieve the terminal response:

The terminal LLMResponse (with assembled content + finalised tool_calls + usage) is accessible via :meth:last_response after the stream is exhausted.

But LLMClient declares only chat and stream. last_response appears nowhere else in the package — a grep of ApodexHarness finds no definition either, only unrelated local variables.

Consequence: a host implementing exactly the published Protocol has no such method, so a consumer typed against LLMClient either fails strict type-check or raises AttributeError at runtime. This is the half of the streaming contract a host currently has to guess at, even though StreamDelta was deliberately given usage / finish_reason / provider so an assembler can build that response.

Decision needed: either declare last_response() -> LLMResponse | None on the Protocol, or drop the reference and document the assembler as the owner of the terminal response. Blocks nothing, but should be settled before the LLM runtime slice (migration step 3) lands on top of it.

Provenance: pre-existing in ApodexHarness/miroharness/core/llm.py:92, ported verbatim by #1 — not introduced by the refactor. ApodexHarness carries the same wording; settle the contract once, then change both.

Activity

  1. zhanghanduo commented on Sep 3, 2026

    @zhanghanduo
    CollaboratorAuthor

    Settled on main by 63dba6f (not auto-linked — literal \n\n before the Refs trailer).

    Of the two options this issue put up, the second was taken: the reference is dropped and the assembler owns the terminal response. LLMClient.stream now documents (agent_core/llm.py:99):

    Terminal metadata is carried by late deltas; the consuming runtime is responsible for assembling those deltas into its final response.

    No last_response was added to the Protocol. The rationale is the one the issue itself noted: StreamDelta already carries usage / finish_reason / provider precisely so an assembler can build the terminal response, so requiring hosts to also implement a stateful last_response() would duplicate that contract and force every host to keep per-stream state the Protocol cannot enforce. A host now implements exactly what it can implement.

    This was flagged as needing to be settled before the LLM runtime slice (migration step 3) lands on top of it — it is settled, and that slice should assemble from deltas.

    Downstream: miroharness/core/llm.py still carries the old wording. Tracking that backport separately.

  2. zhanghanduo commented on Sep 3, 2026

    @zhanghanduo
    CollaboratorAuthor

    Correction to the "Downstream" note above.

    That note is accurate for MiroHarness main, which still carries the standalone 790-line copy — but it is already stale for the integration branch. On refactor/agent-core, miroharness/core/loop_types.py is a 75-line compatibility facade that re-exports agent_core.loop_types (done by 1bedcc52 refactor: consume shared runtime contracts), and that branch pins:

    apodex-agent-core @ git+ssh://git@github.com/ApodexAI/AgentCore.git@a9b5272d
    

    git merge-base --is-ancestor 63dba6f a9b5272 confirms the pin already contains this fix.

    So no backport is needed. The downstream copy is not being patched in parallel; it is being deleted by the extraction. MiroHarness main keeps the old code only until refactor/agent-core lands, and this fix arrives there through the pin rather than through a second edit.

    The only product-specific symbol that stays behind in the facade is wall_deadline_remaining_s, which binds miroharness.core.execution_context and is registered as an allowed delta in scripts/contract/manifest.py.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions