Skip to content

fix: improve genstubs and some related bugs - #1781

Merged
jakelorocco merged 3 commits into
generative-computing:mainfrom
jakelorocco:fix/genstubs
Nov 10, 2026
Merged

jakelorocco merged 3 commits into
generative-computing:mainfrom
jakelorocco:fix/genstubs

Conversation

@jakelorocco

Copy link
Copy Markdown
Contributor

Pull Request

Issue

Fixes #1476

Description

Improves genstub performance by modifying the template. On set of genstub tasks based on our examples and other scenarios, performance improved by 3.1% averaged across models and sizes. Granite models saw large improvements along with models with thinking on.

Performance saw large improvements when including the return format in the prompt.

Additional bug fixes:

  1. -> X | None stubs crashed when the right answer was None. When the model correctly returned {"result": null}, a check in mellea/core/base.py required every result to be non-None and raised an AssertionError.
  2. Template lookup
  3. Prompt rendering fixes
    a. docstrings left out when empty instead of rendering as """"""
    b. argument types fixed
    c. removed module paths from types
    d. pydantic arguments are rendered as json along with dicts and lists
    e. unquoted unresolved return types to prevent small models from returning strings
    f. multiline docstrings keep their indentation

Testing

  • Tests added to the respective file if code was changed
  • New code has 100% coverage if code was added
  • Ensure existing tests and github automation passes (a maintainer will kick off the github automation when the rest of the PR is populated)

Attribution

  • AI coding assistants used

Adding a new component, requirement, sampling strategy, or tool?

If your PR adds or modifies one of the types below, check the matching box. A checklist of type-specific review items will be posted as a comment.

  • Component
  • Requirement
  • Sampling Strategy
  • Tool

NOTE: Please ensure you have an issue that has been acknowledged by a core contributor and routed you to open a pull request against this repository. Otherwise, please open an issue before continuing with this pull request.

Signed-off-by: Jake LoRocco <jake.lorocco@ibm.com>
Assisted-by: CLAUDE:OPUS
@github-actions github-actions Bot added the bug Something isn't working label Oct 30, 2026
Signed-off-by: Jake LoRocco <jake.lorocco@ibm.com>
Assisted-by: CLAUDE:OPUS
@jakelorocco
jakelorocco marked this pull request as ready for review November 3, 2026 20:22
@jakelorocco
jakelorocco requested a review from a team as a code owner November 3, 2026 20:22

@ajbozarth ajbozarth left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some feedback from Claude. LGTM — template redesign matches what #1476 scoped (and the bare-return-type rendering planetf1 anticipated), and the bundled None-result and template-lookup fixes are correct. Two optional, non-blocking notes inline.

Comment thread mellea/core/base.py
# import here would be circular.
from ..stdlib.components.genstub import GenerativeStub

assert isinstance(self._call.action, GenerativeStub), (

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional / genuine question, not a blocker: this bakes a specific stdlib subclass (GenerativeStub) into a core invariant purely to exempt it. Unlike the Message._parse dispatch just below, which does real parsing work, this one only exists to special-case one component type. Would a Component-level predicate (the component declaring it permits a None parsed_repr) read better here, or is naming GenerativeStub directly the deliberate call? Fine to leave as-is either way.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed; this was a bit code-smelly when I added it. I've opened #1805 to fix the actual issue which is that we can't distinguish not parsed from parsed nothing.

mfuncs.act(_ParsesToNone("describe"), SimpleContext(), backend)


def test_sync_stub_returns_none_result():

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional: the relaxed invariant's safety rests on a non-nullable stub (-> str) still failing on {"result": null} — pydantic rejects it in _parse before the None check. Confirmed that holds, but there's no test pinning it: we test non-stub→None fails and nullable-stub→None succeeds, but not nullable-vs-non-nullable discrimination for stubs. A one-liner (@generative def f() -> str + a backend returning {"result": null}, expecting a raise) would lock in the exact property this change leans on.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added!

Assisted-by: CLAUDE:OPUS
Signed-off-by: Jake LoRocco <jake.lorocco@ibm.com>
@jakelorocco
jakelorocco added this pull request to the merge queue Nov 10, 2026
Merged via the queue into generative-computing:main with commit 7b8dd45 Nov 10, 2026
12 checks passed
@jakelorocco
jakelorocco deleted the fix/genstubs branch November 10, 2026 17:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Generative stub prompt template underperforms on smaller models

2 participants