Skip to content

feat: normailze intinsics interfaces#1028

Open
akihikokuroda wants to merge 3 commits intogenerative-computing:mainfrom
akihikokuroda:issue1003
Open

feat: normailze intinsics interfaces#1028
akihikokuroda wants to merge 3 commits intogenerative-computing:mainfrom
akihikokuroda:issue1003

Conversation

@akihikokuroda
Copy link
Copy Markdown
Member

@akihikokuroda akihikokuroda commented May 6, 2026

Misc PR

Type of PR

  • Bug Fix
  • New Feature
  • Documentation
  • Other

Description

Testing

  • Tests added to the respective file if code was changed
  • New code has 100% coverage if code as 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: claude

Signed-off-by: Akihiko Kuroda <akihikokuroda2020@gmail.com>
@akihikokuroda akihikokuroda requested a review from a team as a code owner May 6, 2026 17:18
@github-actions github-actions Bot added the enhancement New feature or request label May 6, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 6, 2026

The PR description has been updated. Please fill out the template for your PR to be reviewed.

Copy link
Copy Markdown
Contributor

@planetf1 planetf1 left a comment

Choose a reason for hiding this comment

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

threading looks good, and catching the float → str return type mismatch was worth doing. Two things I'd want resolved before merging: the from_context tests won't actually exercise the code path they're meant to cover, and the documents positional change breaks existing callers without any mention in the issue or PR. The latter is easy to fix without the break — see inline.

# Add documents to the last assistant message
last_turn = context.last_turn()
assert last_turn is not None and last_turn.output is not None
assert last_turn.output.value is not None
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

These assertions will never pass as written. _read_guardian_input builds context via context.add(Message(...)), which means the last node is a Message, not a ModelOutputThunk — so last_turn() returns ContextTurn(last_element, None) (base.py:919). .output is always None here. The test aborts before reaching the code it's supposed to cover, so the documents=None branch in factuality_detection is entirely uncovered.

The fix is to use the same extraction path that guardian.py itself falls back to:

last_turn = context.last_turn()
assert last_turn is not None
if last_turn.output is not None and last_turn.output.value is not None:
    content = last_turn.output.value
else:
    assert isinstance(last_turn.model_input, Message)
    content = last_turn.model_input.content
rewound = context.previous_node
assert rewound is not None
context_with_docs: ChatContext = rewound.add(  # type: ignore[assignment]
    Message("assistant", content, documents=documents)
)

Same issue at lines 220–221 in test_factuality_correction_from_context.

def factuality_detection(context: ChatContext, backend: AdapterMixin) -> float:
"""Determine is the last response is factually incorrect.
def factuality_detection(
documents: collections.abc.Iterable[str | Document] | None,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Making documents the new first positional arg is a silent break — existing callers doing factuality_detection(ctx, backend) now pass ctx as documents and get a confusing error rather than a helpful one. Neither #1003 nor this PR mentions it as a breaking change, and our policy is to avoid that without explicit callout.

The good news is the issue just asks for parity with the other intrinsics — we can get there without the break by making documents keyword-only:

def factuality_detection(
    context: ChatContext,
    backend: AdapterMixin,
    *,
    documents: collections.abc.Iterable[str | Document] | None = None,
    model_options: dict | None = None,
) -> str:

Existing call sites continue to work, new callers pass documents= explicitly, and it's consistent with how model_options is handled everywhere else in this PR. Worth also updating the AGENTS.md Section 13 table with the new signature.


# If documents are provided, add them to the last assistant message
if documents is not None:
# Get the last turn and add documents to it
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This block extracting the assistant content, rewinding the context, and reattaching documents is identical in factuality_correction (~lines 303–336). If either of the fixes above lands here, it'll need to land in both places. Would be cleaner to pull it into a private helper — something like _reattach_documents(context, documents) -> ChatContext — which also gives us one place to sort out the cast() vs type: ignore question below.


# Extract assistant content from either output (if generated) or model_input (if manually added)
if last_turn.output is not None and last_turn.output.value is not None:
assistant_content = last_turn.output.value
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Small edge case: if last_turn.output is not None but .value is None (uncomputed thunk, failed generation), the code falls through to model_input and ends up wrapping the user's question as the assistant's response. That'll produce a factuality score on the wrong text with no indication anything went wrong. Raising explicitly there rather than falling through would be much easier to debug.

raise ValueError("Cannot rewind context past the root node")
# Type ignore because rewound is Context but we know it's ChatContext
context = rewound.add( # type: ignore[assignment]
Message(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

# type: ignore[assignment] is hiding a real mismatch: previous_node.add(...) returns Context, not ChatContext. Worth using cast(ChatContext, rewound.add(...)) with a short note explaining why, rather than silencing mypy here — if the type hierarchy shifts this will fail at runtime instead of at type-check time. As above, extracting the helper means one fix instead of two.

Signed-off-by: Akihiko Kuroda <akihikokuroda2020@gmail.com>
Signed-off-by: Akihiko Kuroda <akihikokuroda2020@gmail.com>
@akihikokuroda akihikokuroda requested a review from planetf1 May 7, 2026 14:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix: intrinsic function signatures

2 participants