Skip to content

refactor: update citation verification handling in DeepResearcherAgent#237

Open
rkarmaka wants to merge 2 commits into
NVIDIA-AI-Blueprints:developfrom
rkarmaka:fix/issue-235-empty-source-registry
Open

refactor: update citation verification handling in DeepResearcherAgent#237
rkarmaka wants to merge 2 commits into
NVIDIA-AI-Blueprints:developfrom
rkarmaka:fix/issue-235-empty-source-registry

Conversation

@rkarmaka
Copy link
Copy Markdown

  • Removed the raising of EmptySourceRegistryError when no sources are available during deep research.
  • Added logging for cases where reports are generated without captured sources, indicating whether tools were unavailable or if the model answered without using search results.
  • Introduced a new citation_verification_status field in DeepResearchAgentState to track unverified reports, including the reason and available tool count.
  • Updated tests to ensure correct behavior when the source registry is empty and when sources are captured.

- Removed the raising of EmptySourceRegistryError when no sources are available during deep research.
- Added logging for cases where reports are generated without captured sources, indicating whether tools were unavailable or if the model answered without using search results.
- Introduced a new citation_verification_status field in DeepResearchAgentState to track unverified reports, including the reason and available tool count.
- Updated tests to ensure correct behavior when the source registry is empty and when sources are captured.
@rkarmaka
Copy link
Copy Markdown
Author

@cdgamarose-nv Solving issue #235

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 13, 2026

Greptile Summary

This PR refactors how DeepResearcherAgent handles an empty source registry: rather than raising EmptySourceRegistryError and losing the report, it now returns the report with a citation_verification_status field set to "unverified", and logs the condition at error or warning level. The chat_researcher wiring to surface that flag as a user-facing message is explicitly deferred (old except block left as commented-out reference).

  • deep_researcher/agent.py: Replaced the raise EmptySourceRegistryError path with conditional logging and mutation of the result dict to set citation_verification_status before model_validate.
  • deep_researcher/models/state.py: Added citation_verification_status: dict[str, Any] | None = None to DeepResearchAgentState, following the existing dict[str, Any] convention for structured payload fields.
  • tests/test_agent.py: Two new regression tests cover the empty-registry (unverified status set) and populated-registry (status stays None) code paths.

Confidence Score: 4/5

The deep-researcher logic change is sound, but the chat_researcher node does not yet read citation_verification_status, so users can receive an unverified or potentially hallucinated deep-research report with no indication that source verification was skipped.

The state-model and deep-researcher changes are clean and the new tests cover the two key branches. The gap is in chat_researcher: citation_verification_status is populated by deep_researcher but never inspected when building the response returned to the caller, meaning the unverified-report UX improvement the PR is building toward is currently absent.

src/aiq_agent/agents/chat_researcher/agent.py — the citation_verification_status field on the returned DeepResearchAgentState is not yet consumed here.

Important Files Changed

Filename Overview
src/aiq_agent/agents/deep_researcher/agent.py Replaced EmptySourceRegistryError raise with log + citation_verification_status dict mutation; logic is correct for the normal case but the unverified report flows silently to the caller
src/aiq_agent/agents/chat_researcher/agent.py Old except EmptySourceRegistryError block is commented out and citation_verification_status is never read from the result, so unverified deep-research reports reach users without any indication
src/aiq_agent/agents/deep_researcher/models/state.py Added citation_verification_status field following the existing dict[str, Any]
tests/aiq_agent/agents/deep_researcher/test_agent.py Two new tests cover empty-registry and populated-registry paths; test_run_with_sources_leaves_verification_status_none relies on self.registry being returned by _get_registry() (true only when no session registry is active)

Sequence Diagram

sequenceDiagram
    participant CR as ChatResearcherAgent
    participant DR as DeepResearcherAgent
    participant SRM as SourceRegistryMiddleware
    participant CV as citation_verification

    CR->>DR: deep_research_fn(deep_state)
    DR->>DR: agent.ainvoke() [retry loop]
    DR->>SRM: _get_registry().all_sources()
    alt Sources captured
        SRM-->>DR: [source list]
        DR->>CV: verify_citations(report, registry)
        CV-->>DR: verified_report
        DR-->>CR: "DeepResearchAgentState(citation_verification_status=None)"
    else Empty source registry
        SRM-->>DR: []
        DR->>DR: validate_tool_availability()
        alt "available_count == 0"
            DR->>DR: logger.error(all tools unavailable)
        else "available_count > 0"
            DR->>DR: logger.warning(model answered without search)
        end
        DR->>DR: "result[citation_verification_status] = {status: unverified, ...}"
        DR-->>CR: "DeepResearchAgentState(citation_verification_status={status: unverified, ...})"
        Note over CR: citation_verification_status NOT checked - unverified report returned silently
    end
Loading

Reviews (2): Last reviewed commit: "refactor: update error handling in deep ..." | Re-trigger Greptile

subagents: list[dict[str, Any]] = Field(default_factory=list)
clarifier_result: str | None = None
available_documents: list[AvailableDocument] | None = None
citation_verification_status: dict[str, Any] | None = 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.

P2 Untyped status dict — consider a dedicated model

citation_verification_status is typed as dict[str, Any] | None, but its shape is well-defined (keys: status, reason, available_tool_count, unavailable_tools). An untyped dict makes downstream access error-prone and provides no validation. Introducing a small Pydantic model (e.g. CitationVerificationStatus) would let Pydantic enforce the schema and allow type-safe attribute access without changing the public interface.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Not adopting here — DeepResearchAgentState uses untyped dict[str, Any] consistently for its structured payload fields (user_info, tools_info, files, subagents, todos). Adding a typed model just for citation_verification_status would break that pattern for one field while the schema is still settling (downstream consumers in chat_researcher / API not yet wired).

For this repo: prefer matching the existing dict[str, Any] convention on DeepResearchAgentState and similar state bags. If we want type safety later, the right move is to convert all of them together, not per-field.

- Removed the raising of EmptySourceRegistryError when no verifiable sources are found during deep research.
- Retained commented-out code for reference, indicating the previous error handling approach.
- Adjusted the flow to ensure reports are returned with citation verification status, preventing loss of information when sources are unavailable.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant