Skip to content

feat(py): citation-request tracker and tool-result tagging - #305

Merged
jat255 merged 2 commits into
jat255/fec8-shared-prompt-sourcefrom
jat255/gege-citation-request
Sep 7, 2026
Merged

feat(py): citation-request tracker and tool-result tagging#305
jat255 merged 2 commits into
jat255/fec8-shared-prompt-sourcefrom
jat255/gege-citation-request

Conversation

@jat255

@jat255 jat255 commented Sep 7, 2026

Copy link
Copy Markdown
Collaborator

The Python package can now ask the model for a citation, once per user turn, and tag a tool result with the provenance of the output it holds.

Summary

CitationRequest in _citations.py holds the once-per-turn flag and the reminder text, which it reads from prompts/citation-request.md rather than restating the words. tool_result() stamps the tag on ContentToolResult.extra at construction, which is where the turn classifier reads it back, and turn_has_user_message() decides which turns start a new request. An errored tool result passes through without spending the request: the model is sent the error rather than the value, so a reminder added to the value would never arrive.

Where the reminder lands is behaviour both packages have to agree on, so tests/shared/citation-request.json pins it: which value shapes it joins and how, that it goes out once per turn, and which turns reset the tracker. The wording stays in the prompt file, so the fixture uses a stand-in for it.

Review notes

Nothing constructs a tracker yet. The Commons wiring is blocked behind the layer classes; this is the part of that milestone that does not need them.

The reset rule ships with the tracker rather than with the wiring, since it is the tracker's rule. Excluded, as the issue says: the restore-detection flag on set_turns(), which is wiring.

A value that is neither a string nor a list of parts becomes a two-part list, with the value kept as a single element. That matches R's c(value, list(...)) only for a scalar value: R's c() flattens a list-backed value (a vector or data frame) into its elements, where Python keeps it whole. No commons tool on either side produces that shape, so it is tested Python-side only rather than pinned cross-language.

Nothing is exported from commons/__init__.py: in R this is private agent state and no user constructs one.

Testing

Python 978 pass, ruff and pyrefly clean. R 7049 pass, 0 fail. Both suites read the new fixture, and I checked that it can fail rather than trivially pass: perturbing the expected values in the synced R copy fails both sections.

R-side summary

No R behaviour changes: this replaces tests, not code. test-citations.R loses the two hand-written tests for add_citation_request() (one reminder per user turn, and appending a ContentText to a content list) and gains two that drive the same function from the shared fixture, covering the same sequence and the same two value shapes. It also gains coverage of turn_has_user_message(), which had none, for the four turn shapes that decide whether a new request goes out.

Two small helpers in the test file convert between the fixture's {kind, text} or {kind, parts} shape and R content objects, so the fixture stays free of either language's types. The copy under tests/testthat/fixtures/shared/ is generated by scripts/sync-shared.sh.

@jat255 jat255 closed this Sep 7, 2026
@jat255
jat255 deleted the jat255/gege-citation-request branch September 7, 2026 04:27
@jat255
jat255 restored the jat255/gege-citation-request branch September 7, 2026 04:29
@jat255 jat255 reopened this Sep 7, 2026
@jat255
jat255 deleted the branch jat255/fec8-shared-prompt-source September 7, 2026 04:29
@jat255 jat255 closed this Sep 7, 2026
@jat255 jat255 reopened this Sep 7, 2026
@jat255
jat255 changed the base branch from jat255/m5-fec8-shared-prompt-source to jat255/fec8-shared-prompt-source September 7, 2026 04:30
@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown

Preview deployed to Connect (dogfood.team.pct.posit.it): https://dogfood.team.pct.posit.it/connect/#/apps/d7a36cae-8f27-448b-a478-61b81fbe3942/draft/368619

Deployed from commit 0ca0a94.

@jat255 jat255 added this to the py-M5: agent, tools, and prompt milestone Sep 7, 2026
@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown

Preview deployed to Connect (connect.staging.pct.posit.it): https://connect.staging.pct.posit.it/connect/#/apps/ad662e1b-5048-4acc-9ad7-f9478c92274e/draft/2678

Deployed from commit 0ca0a94.

@jat255 jat255 added r Affects the R implementation py Affects the Python implementation needs-manual-review Agent-created work that needs a human review labels Sep 7, 2026
@jat255
jat255 requested a review from simonpcouch September 7, 2026 05:17
@jat255 jat255 removed the needs-manual-review Agent-created work that needs a human review label Sep 7, 2026
@jat255
jat255 marked this pull request as ready for review September 7, 2026 17:12
@jat255
jat255 force-pushed the jat255/gege-citation-request branch from 2bb3518 to c2fce17 Compare September 7, 2026 19:42
The citation contract is stated in the system prompt; a short reminder rides
on the first tool result of a user turn whose output has to be cited.
`CitationRequest` holds that once-per-turn flag and the reminder text, which
comes from `prompts/citation-request.md` rather than a second copy of the
words. `tool_result()` stamps the provenance tag on `ContentToolResult.extra`
at construction, which is where the turn classifier reads it back.

Where the reminder lands is behaviour both packages have to agree on, so
`tests/shared/citation-request.json` pins it: which value shapes it joins and
how, that it goes out once per turn, and which turns start a new request. The
R suite reads the fixture in place of the two hand-written tests it had.

The wiring that calls this is blocked with the rest of M5, so nothing
constructs a tracker yet.
chatlas sends the error rather than the value, so a reminder added to an
errored result's value would never reach the model, and the once-per-turn
flag would be spent on nothing. Also add citation_reminder_text to
__all__ alongside its siblings and give add_request a docstring.
@jat255
jat255 force-pushed the jat255/gege-citation-request branch from c2fce17 to 0ca0a94 Compare September 7, 2026 19:59
@jat255
jat255 merged commit 3e38f12 into main Sep 7, 2026
13 checks passed
@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown

Cleaned up 4 preview bundle(s) on https://dogfood.team.pct.posit.it: 368504, 368590, 368611, 368619

@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown

Cleaned up 4 preview bundle(s) on https://connect.staging.pct.posit.it: 2665, 2667, 2670, 2678

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

Labels

py Affects the Python implementation r Affects the R implementation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants