Skip to content

Add report follow-up jobs and chat routing - #271

Merged
AjayThorve merged 33 commits into
NVIDIA-AI-Blueprints:developfrom
AjayThorve:ajay/report-follow-up
Jul 1, 2026
Merged

Add report follow-up jobs and chat routing#271
AjayThorve merged 33 commits into
NVIDIA-AI-Blueprints:developfrom
AjayThorve:ajay/report-follow-up

Conversation

@AjayThorve

@AjayThorve AjayThorve commented Jun 14, 2026

Copy link
Copy Markdown
Member

Overview

Adds report-aware follow-up to the deep-research blueprint. After a report job completes, a user can ask about it, make cosmetic edits, or start delta research that reuses the prior report — without forcing every follow-up through a dedicated "report mode." A chat router picks a semantic route from the message plus an optional active_report_job_id; the active report is treated as context, never as a command.

Modes

  • report ask — inline, bounded LLM answer from the parent report only. No tools, no live research, no child artifact.
  • report cosmetic edit — an internal report_rewriter async child job for mechanical/aesthetic edits that do not need new evidence. The parent report stays immutable.
  • report delta research — the existing deep researcher, seeded with parent-report context, for fresh evidence, deeper analysis, or a new analytical perspective on the same report topic.
  • standalone research — normal shallow/deep research when the request is unrelated to the active report or asks for a separate report.

Intent routing examples

  • what are the risks in this report?report_ask
  • make this shorterreport_cosmetic_edit
  • format the key takeaways as bulletsreport_cosmetic_edit
  • rewrite this report from a player-performance POVreport_delta_research
  • redo this with newer evidence on 2026 logisticsreport_delta_research
  • write a separate report on player performance trends across 2014, 2018, and 2022standalone_research

Key pieces

  • One new internal agent — report_rewriter — registered with public=False (hidden from GET /agents; direct /submit of internal-only agents is rejected, and the gate is also enforced at the submission boundary).
  • A durable parent-report context resolver that reconstructs the report + sources from job output/events, authorizes the caller before any read, and seeds /shared/* files into child runs.
  • New endpoint POST /v1/jobs/async/job/{job_id}/report/edit (per-job ownership auth); GET .../report extended with parent_job_id / interaction_action / result_kind.
  • Chat (POST /chat / WebSocket) accepts active_report_job_id; the UI forwards the latest completed report job id and renders the report-edit child job through the existing report-streaming path.
  • DeepAgents runtime normalizes seeded /shared/* parent-context files so delta research can read /shared/original_report.md and /shared/source_summary.md reliably.

Rebased onto develop (which now includes #267); this branch is a standalone PR. The most recent commits harden the feature for production: the chat path works under the default REQUIRE_AUTH=false, a caller-supplied job_id can no longer delete another job's state, submission failures roll back cleanly, the UI consumes the report-edit response, and report-delta research can reuse seeded parent context without creating /shared/shared or malformed file records.

Validation

All commands run from the repo root unless noted.

Lint / format / unit tests (backend)

uv run ruff check .
uv run ruff format --check .
uv run pytest                                  # full suite
# or the suites this PR touches:
uv run pytest frontends/aiq_api/tests/ \
              tests/aiq_agent/agents/chat_researcher/ \
              tests/aiq_agent/agents/report_rewriter/ \
              tests/aiq_agent/agents/deep_researcher/test_deepagents_runtime.py \
              tests/aiq_agent/jobs/test_runner.py

UI

cd frontends/ui
npm run lint && npm run type-check && npm run test:ci
# focused: npx vitest run src/features/chat/hooks/use-websocket-chat.spec.ts \
#                         src/adapters/api/websocket-client.spec.ts

End-to-end (manual) — report follow-up needs async jobs, so bring up the full stack (PostgreSQL job store + embedded Dask scheduler/worker + web), e.g. via deploy/compose or scripts/start_server_in_debug_mode.sh with deploy/.env, then exercise the surfaces below.

What to test

  1. Internal-agent gatingGET /v1/jobs/async/agents does not list report_rewriter; POST /v1/jobs/async/submit with agent_type=report_rewriter returns 400 Agent type is internal-only: report_rewriter.
  2. HTTP report editPOST /v1/jobs/async/job/{id}/report/edit on a completed report → a report_rewriter child job; GET .../report on the child returns a revised report plus parent_job_id, interaction_action="edit", result_kind="report". The parent report is unchanged.
  3. Chat routing with active_report_job_id — verify each semantic route:
    • report ask: What are the top three takeaways from this report?; Where does the report say the evidence is weak or incomplete?
    • report cosmetic edit: Make this report shorter while preserving the sources.; Format the key takeaways as bullets.; Remove the one-table comparison section and keep the rest unchanged.
    • report delta research: Rewrite this report from a player-performance POV.; Redo this report with newer evidence on 2026 logistics and host-city operations.; Add a section on fan travel emissions for 2026.
    • standalone research: Write a separate report on player performance trends across the 2014, 2018, and 2022 World Cups.; Research the economics of Olympic host cities since 2000.
  4. Parent-context seeding for delta research — delta research should read /shared/original_report.md and /shared/source_summary.md successfully, not see /shared/shared/, and not fail with string indices must be integers or 'str' object has no attribute 'get' from filesystem tools.
  5. Authorization — works for anonymous callers under REQUIRE_AUTH=false; under REQUIRE_AUTH=true, a non-owner is rejected with 404 before any report content is read.
  6. Robustness / edge cases — a colliding caller-supplied job_id returns 409 and does not delete the existing job; whitespace-only input returns 422; report ask/edit degrade to a chat message (not an opaque workflow error) if context resolution fails.

Evidence — backend suites (frontends/aiq_api/tests + tests/aiq_agent/...) and UI specs pass; ruff and tsc clean. Verified live against a Postgres + Dask stack with deploy/.env: internal agent hidden + /submit rejected (400); HTTP and chat report-edit produce a revised child report with correct lineage; chat report-ask answers from the report only; job_id collision → 409 with the victim preserved; blank input → 422; ownership-mismatch → 404 under REQUIRE_AUTH=true; report-delta routing kicks off deep research with seeded parent context.

  • I ran the relevant local checks or explained why they are not applicable.
  • I added or updated tests for behavior changes.
  • I updated documentation for user-facing or contributor-facing changes.
  • I confirmed this PR does not include secrets, credentials, or internal-only data.
  • I certify this contribution under the Developer Certificate of Origin (DCO) and signed my commits with git commit -s or an equivalent sign-off.

Where should reviewers start?

Read in this order — security-sensitive paths first:

  1. frontends/aiq_api/src/aiq_api/jobs/report_context.py — durable report/source reconstruction and resolve_authorized_report_context(), which authorizes the caller before any read and seeds /shared/* for child runs. The core security boundary.
  2. frontends/aiq_api/src/aiq_api/routes/jobs.py — the report/edit endpoint, the public agent filter on /agents + /submit, the new JobReportResponse fields, and the request validators (blank-input → 422, job_id collision → 409).
  3. frontends/aiq_api/src/aiq_api/jobs/submit.pysubmit_agent_job() ownership recording, the JobIdConflictError/InternalAgentError gates, and the rollback that only deletes state this submission created.
  4. src/aiq_agent/agents/chat_researcher/nodes/intent_classifier.py + agent.py + register.py — the semantic route classifier (report_ask / report_cosmetic_edit / report_delta_research / standalone_research), the bounded tool-free report-ask path, and how each entry point resolves the principal (same contract as the HTTP routes).
  5. src/aiq_agent/agents/deep_researcher/deepagents_runtime.py — route-aware /shared/* file seeding for delta research.
  6. src/aiq_agent/agents/report_rewriter/ — the single new internal agent (a bounded, tool-less single-LLM rewrite).
  7. frontends/ui/src/features/chat/hooks/use-websocket-chat.ts + adapters/api/websocket-client.ts — forwards active_report_job_id and routes the report-edit child job through the existing report-streaming path.

Tests mirror these: test_report_context.py, test_report_edit.py, test_submit_collision.py, test_submit_internal_agent.py, test_job_access.py, test_agent_registry_visibility.py, test_intent_classifier.py, test_deepagents_runtime.py (backend), and use-websocket-chat.spec.ts (UI).

Related Issues

Summary by CodeRabbit

Release Notes

  • New Features

    • Report editing and follow-up workflows to revise completed reports
    • Report Q&A capability to ask questions about existing reports
    • Conversation-scoped job tracking for multi-turn interactions
    • Agent visibility controls restricting certain agents from public submission
  • Improvements

    • Enhanced input validation and error responses for job submissions
    • Expanded REST API documentation for report operations
    • Better job ID conflict detection and error handling

@copy-pr-bot

copy-pr-bot Bot commented Jun 14, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@AjayThorve
AjayThorve force-pushed the ajay/report-follow-up branch from c6bdbd6 to 733511b Compare June 16, 2026 18:11
@copy-pr-bot

copy-pr-bot Bot commented Jun 16, 2026

Copy link
Copy Markdown

Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually.

Contributors can view more details about this message here.

@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds a full-stack "report follow-up / report edit" capability. A new internal ReportRewriterAgent rewrites existing research reports from durable context. The API gains a POST /v1/jobs/async/job/{job_id}/report/edit endpoint, internal-agent visibility gating via a public flag on AgentConfig, JobIdConflictError/InternalAgentError exception types, and virtual filesystem seeding in the job runner. Intent classification gains report-targeting logic; ChatResearcherAgent adds report_ask and report_edit graph nodes driven by extended routing. The frontend propagates active_report_job_id on outgoing WebSocket messages and handles "Report edit job submitted" SSE escalation. Database support for conversation-scoped report job lookup enables follow-ups to default to the latest report in a conversation.

Changes

Report Follow-up / Edit Feature

Layer / File(s) Summary
Intent routing models and state
src/aiq_agent/agents/chat_researcher/models/intent.py, ...state.py, tests/aiq_agent/agents/chat_researcher/models/...
Adds RouteTarget and ReportAction type aliases; extends IntentResult with target, report_action, and use_parent_report_context fields; adds active_report_job_id to ChatResearcherState.
Chat request context extraction
src/aiq_agent/agents/chat_researcher/utils.py, tests/aiq_agent/agents/chat_researcher/test_utils.py
Introduces ChatRequestContext Pydantic model to normalize query_text, data_sources, and active_report_job_id. Refactors query parsing to extract structured context from plain text or JSON payloads, preserving backward-compatible tuple APIs.
Intent classifier: logic, routing, and prompt
src/aiq_agent/agents/chat_researcher/nodes/intent_classifier.py, ...prompts/intent_classification.j2, tests/aiq_agent/agents/chat_researcher/nodes/test_intent_classifier.py
Adds keyword hint sets for report-ask/edit detection; propagates active_report_job_id into prompt context; extends post-parse logic to normalize target/report_action/use_parent_report_context, downgrade report routing when no active report exists, and infer report_action from query hints. Updates prompt template with routing rules, new JSON output fields, and conditional active-report-ID rendering.
ReportRewriterAgent (internal agent)
src/aiq_agent/agents/report_rewriter/*, tests/aiq_agent/agents/report_rewriter/test_agent.py
New package with ReportRewriterAgentState (LangGraph dict-merge state), ReportRewriterAgent that reads input files from state, renders edit.j2 prompt, invokes LLM, validates output, emits via callbacks, and writes /shared/output.md. edit.j2 prompt enforces factual grounding and citation preservation.
Durable report context reconstruction
frontends/aiq_api/src/aiq_api/jobs/report_context.py, frontends/aiq_api/tests/test_report_context.py
New module defining ReportContextSource and ReportContext models; extracts report markdown from job output or durable artifact.update events; parses and deduplicates citation sources from events and ## Sources markdown sections. resolve_report_context prefers output-embedded report with event fallback. resolve_authorized_report_context loads config, authorizes parent job, enforces SUCCESS. to_initial_files seeds /shared/* paths; report_output_metadata produces durable metadata.
Agent registry visibility and internal-agent gating
frontends/aiq_api/src/aiq_api/registry.py, frontends/aiq_api/src/aiq_api/routes/jobs.py, frontends/aiq_api/tests/test_agent_registry_visibility.py, frontends/aiq_api/tests/test_job_submit_data_sources.py
Extends AgentConfig with public: bool field (default True) and register_agent with public parameter. Registers report_rewriter with public=False. Filters /v1/jobs/async/agents to public agents only; rejects internal-only agents at /submit with HTTP 400.
Job submission gating, exceptions, and collision handling
frontends/aiq_api/src/aiq_api/jobs/submit.py, frontends/aiq_api/tests/test_submit_*.py, frontends/aiq_api/tests/test_job_access.py
Adds InternalAgentError (internal-agent without allow_internal) and JobIdConflictError (caller-supplied job_id collision). Extends submit_agent_job with allow_internal flag, initial_files, output_metadata parameters, and defense-in-depth internal-agent gate. Maps IntegrityError to JobIdConflictError without rollback; other submission failures trigger _rollback_partial_submission. Tests verify collision handling, rollback semantics, and conversation_id threading.
Job runner virtual filesystem and output metadata
frontends/aiq_api/src/aiq_api/jobs/runner.py, tests/aiq_agent/jobs/test_runner.py
run_agent_job gains initial_files and output_metadata parameters. _run_agent conditionally injects data_sources and maps initial_files to state.files only when state class declares those fields. On SUCCESS, merges output_metadata into job output payload.
ChatResearcherAgent report nodes and orchestration
src/aiq_agent/agents/chat_researcher/agent.py, src/aiq_agent/agents/chat_researcher/register.py, tests/aiq_agent/agents/chat_researcher/test_agent.py, tests/aiq_agent/agents/chat_researcher/test_register_helpers.py
Adds report_ask_node and report_edit_node to ChatResearcherAgent; extends route_after_orchestration to dispatch on report_action when active_report_job_id is set. In register.py: adds bounded Q&A helpers (_build_report_ask_prompt, _answer_from_report_context), _submit_report_edit_job, and _resolve_effective_report_job_id for parent-report fallback. Wires callbacks into agent construction. Seeds deep-research with parent report context when use_parent_report_context is enabled (best-effort, non-blocking).
REST API: report/edit endpoint and visibility filtering
frontends/aiq_api/src/aiq_api/routes/jobs.py, frontends/aiq_api/tests/test_report_edit.py, docs/source/integration/rest-api.md
Adds ReportEditRequest and ReportEditResponse models; extends JobReportResponse with parent_job_id, interaction_action, result_kind metadata fields. Validates input non-blank (422 on failure). POST /v1/jobs/async/job/{job_id}/report/edit authenticates, authorizes parent access, verifies parent SUCCESS, reconstructs context, submits internal report_rewriter with allow_internal=True; maps collisions to 409, RuntimeError to 503. GET /report decodes output and returns metadata.
Database: conversation_id support for follow-up lookup
frontends/aiq_api/src/aiq_api/jobs/access.py, deploy/compose/init-db.sql, frontends/aiq_api/tests/test_job_access.py
Extends job_access table with nullable conversation_id column and index (idempotent for preexisting deployments). Updates create_job_access to accept and persist conversation_id. Adds get_latest_report_job_for_conversation to return latest completed, non-expired report job for a conversation (with optional ownership enforcement); degrades to None on storage errors. Updates schema initialization and persistence wiring.
Frontend: active_report_job_id WebSocket propagation
frontends/ui/src/adapters/api/websocket-client.ts, frontends/ui/src/features/chat/hooks/use-websocket-chat.ts, *.spec.ts files
sendMessage gains optional activeReportJobId serialized into outgoing payload JSON. getActiveReportJobId derives current report job from conversation state. outgoing chat payloads include active report job ID. Tests verify serialization and propagation.
Frontend: report-edit SSE escalation
frontends/ui/src/features/chat/hooks/use-websocket-chat.ts, *.spec.ts files
Broadens deep-research escalation regex to match "Report edit job submitted" alongside "Deep research job submitted". Skips conversation title renaming for report-edit flows while applying the same deep-research banner + SSE streaming setup for child jobs. Tests verify escalation flow and non-update of title for edits.
Documentation and configuration
docs/source/integration/rest-api.md, skills/aiq-research/SKILL.md, skills/aiq-research/scripts/aiq.py, ci/markdown-link-check-config.json
Updates REST API docs to describe /report/edit endpoint, visibility filtering, HTTP error codes, input validation, and response metadata. Revises skill docs to describe conversation-scoped follow-up routing (stable AIQ_CONVERSATION_ID across original and follow-ups). Updates aiq.py to inject conversation-id header in POST requests. Updates link check config for fastapi.tiangolo.com and dask.org.

Sequence Diagram(s)

sequenceDiagram
    participant UI as Frontend
    participant WS as WebSocket
    participant Chat as ChatResearcherAgent
    participant Classifier as IntentClassifier
    participant EditAPI as POST /report/edit
    participant Submit as submit_agent_job
    participant Rewriter as ReportRewriterAgent

    UI->>WS: sendMessage(text, sources, activeReportJobId)
    WS->>Chat: payload {query, active_report_job_id}
    Chat->>Classifier: run(state with active_report_job_id)
    Classifier-->>Chat: IntentResult {target: "report", report_action: "edit"}
    Chat->>Chat: route_after_orchestration → report_edit_node
    Chat->>EditAPI: _submit_report_edit_job(state)
    EditAPI->>EditAPI: authorize_job_access + verify SUCCESS
    EditAPI->>EditAPI: resolve_report_context → ReportContext
    EditAPI->>EditAPI: to_initial_files + report_output_metadata
    EditAPI->>Submit: submit_agent_job(report_rewriter, allow_internal=True, initial_files, output_metadata)
    Submit->>Rewriter: run(state with seeded files)
    Rewriter->>Rewriter: read files, render edit.j2, invoke LLM
    Rewriter-->>Submit: updated state with revised report
    Submit-->>EditAPI: child_job_id
    EditAPI-->>Chat: "Report edit job submitted: <child_job_id>"
    Chat-->>UI: SSE escalation → deep-research banner + streaming
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error, 1 warning)

Check name Status Explanation Resolution
Title check ❌ Error Title 'Add report follow-up jobs and chat routing' does not follow Conventional Commits format (missing type prefix like 'feat:', 'fix:', etc.) and exceeds the limit if a proper type were added. Rewrite title in Conventional Commits format: 'feat: add report follow-up jobs and chat routing' (keeping it under 72 characters).
Docstring Coverage ⚠️ Warning Docstring coverage is 39.52% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The PR description matches the required template and includes overview, validation, review guidance, and related issues.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@AjayThorve
AjayThorve marked this pull request as ready for review June 22, 2026 23:08
@AjayThorve
AjayThorve requested review from a team and cdgamarose-nv June 22, 2026 23:11

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 13

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@ci/markdown-link-check-config.json`:
- Around line 11-16: The markdown-link-check configuration currently uses overly
broad regex patterns that ignore all URLs from the FastAPI and Dask domains (the
patterns starting with `^https?://fastapi\\.tiangolo\\.com` and
`^https?://(www\\.)?dask\\.org`). Replace these domain-wide ignore patterns with
more specific rules that target only the particular flaky URLs or exact paths
that are known to cause issues, rather than disabling validation for entire
domains. This will preserve the ability to catch real documentation regressions
while still allowing legitimate URLs from these sources to be validated.

In `@docs/source/integration/rest-api.md`:
- Around line 140-145: The HTTP status code table for the report-edit endpoint
is missing documentation for the 500 error case that can be returned by the API.
Add a new row to the status table with status code 500 and its corresponding
reason explaining that the report edit job submission failed, ensuring the
documentation accurately reflects all possible API responses including the
"Failed to submit report edit job" error case.

In `@frontends/aiq_api/src/aiq_api/jobs/report_context.py`:
- Around line 206-208: The EventStore.get_events_async call at line 207 lacks
error handling for transient failures. Wrap this call in a try-except block to
catch exceptions from the async call. When an exception occurs, check if the
report is already available from job output; if it is, gracefully skip the event
source extraction and continue with report-only sources by having
_sources_from_events operate on an empty or unavailable events list. Only
propagate the error if report is not available, since in that case there's no
fallback for source extraction.

In `@frontends/aiq_api/src/aiq_api/jobs/runner.py`:
- Around line 525-528: The update() call on line 527 blindly merges
output_metadata into the output dictionary, which allows the "report" key to be
overwritten if output_metadata contains a "report" entry. Before calling
output.update(output_metadata), filter or validate the output_metadata to
exclude the "report" key to prevent collisions, ensuring that the canonical
report field persists unchanged in the output that gets passed to
job_store.update_status().

In `@frontends/aiq_api/src/aiq_api/routes/jobs.py`:
- Around line 285-297: The `input` field definition currently only validates
`min_length=1`, which allows whitespace-only strings to pass validation. Since
downstream report rewriting requires non-empty stripped instructions, add a
custom validator to the `input` field that strips whitespace and rejects the
request if the resulting string is empty. This ensures whitespace-only edit
instructions are caught at the API boundary before creating failing jobs
downstream. Apply the same validation logic to the corresponding field mentioned
at lines 552-576.
- Around line 581-583: The exception handler catching generic RuntimeError and
mapping it to HTTP 403 misclassifies non-authorization failures as permission
errors, violating the API error-contract clarity. In the report-edit submission
route (around the except RuntimeError as e block), replace the overly broad
RuntimeError handler with either a more specific exception type that actually
represents authorization failures or use a more appropriate HTTP status code
(such as 500 for internal/runtime errors or 400 for bad requests depending on
the actual error source). Ensure the HTTP status code accurately reflects the
type of failure being handled.

In `@frontends/aiq_api/tests/test_report_edit.py`:
- Around line 186-206: The test function
test_job_report_response_includes_report_interaction_metadata validates the
response JSON but does not verify that the _authorize_job_access mock was
invoked with the correct job ID. Add an assertion after the existing response
validations to confirm that _authorize_job_access was called with "child-job-1"
as the argument, ensuring that access control checks are properly enforced on
the GET report endpoint and preventing regressions where authorization might be
bypassed.

In `@src/aiq_agent/agents/chat_researcher/agent.py`:
- Around line 337-355: The logger.warning calls in the report_ask_node and
report_edit_node functions are logging raw exception objects using the %s format
specifier, which can expose sensitive information from upstream errors. Remove
the exception parameter e from both logger.warning calls (at line 340 in
report_ask_node and at line 354 in report_edit_node) to prevent logging raw
exception text. Keep only the descriptive message portion without the exception
details in each log statement.

In `@src/aiq_agent/agents/chat_researcher/nodes/intent_classifier.py`:
- Line 172: The issue is that bool(parsed.get("use_parent_report_context")) at
line 172 will incorrectly evaluate the string "false" as True because any
non-empty string is truthy in Python. The LLM output can return string booleans
which need proper normalization. Replace the bool() conversion with logic that
checks if the value is a string and compares it to "true" case-insensitively,
otherwise convert it to a boolean normally. This same fix also needs to be
applied to the additional locations mentioned in the comment at lines 258-265
where similar boolean normalization is needed.

In `@src/aiq_agent/agents/chat_researcher/register.py`:
- Around line 91-96: The llm.ainvoke call at line 91 lacks timeout protection,
which could cause the entire chat turn to block if the LLM provider stalls. Wrap
the await llm.ainvoke([HumanMessage(content=prompt)]) call with an appropriate
timeout mechanism (such as asyncio.wait_for) to ensure the operation completes
within a reasonable timeframe and fails gracefully if the provider does not
respond in time, allowing the chat interaction to degrade gracefully instead of
blocking indefinitely.
- Around line 386-393: The parent report context resolution at
_resolve_report_context_for_state(state) is currently an unguarded operation
that will propagate exceptions and fail the entire deep-job submission if parent
context lookup encounters errors like 409 or 503 responses. Wrap the entire
parent context resolution block (starting from the report_context import through
output_metadata assignment) in a try-except handler that catches exceptions,
logs the error for observability, and allows the function to continue with async
deep-job submission without parent context enrichment rather than aborting the
workflow. This way, parent context becomes an optional enrichment path rather
than a required workflow dependency.

In `@src/aiq_agent/agents/chat_researcher/utils.py`:
- Around line 153-155: The issue is that the `or` operator treats empty lists as
falsy, causing explicitly provided empty data_sources lists to be replaced with
values from fallback sources. This affects the parse_data_sources calls at lines
153-155, 176-177, and 199-200. Instead of using `or`, you need to explicitly
check if the first parse_data_sources result is None (not provided) before
falling back to the next source. Replace each occurrence where
parse_data_sources is chained with `or` by checking if the first result is not
None; only use the second source when the first result is actually None,
allowing empty lists to be preserved as the caller's explicit choice.

In `@src/aiq_agent/agents/report_rewriter/agent.py`:
- Around line 111-114: Remove the `break` statement from the callback dispatch
loop in the code block where callbacks with the `emit_final_report` method are
being called. The `break` statement currently exits the loop after the first
matching callback, preventing other registered callbacks from receiving the
final report event. Delete the `break` line to allow all callbacks that have the
`emit_final_report` method to process the revised_report.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: cc9a634e-c4d9-4256-97d7-82b37de745f8

📥 Commits

Reviewing files that changed from the base of the PR and between 8638256 and c29f0f0.

📒 Files selected for processing (37)
  • ci/markdown-link-check-config.json
  • docs/source/integration/rest-api.md
  • frontends/aiq_api/src/aiq_api/jobs/report_context.py
  • frontends/aiq_api/src/aiq_api/jobs/runner.py
  • frontends/aiq_api/src/aiq_api/jobs/submit.py
  • frontends/aiq_api/src/aiq_api/registry.py
  • frontends/aiq_api/src/aiq_api/routes/jobs.py
  • frontends/aiq_api/tests/test_agent_registry_visibility.py
  • frontends/aiq_api/tests/test_job_access.py
  • frontends/aiq_api/tests/test_job_submit_data_sources.py
  • frontends/aiq_api/tests/test_report_context.py
  • frontends/aiq_api/tests/test_report_edit.py
  • frontends/aiq_api/tests/test_submit_collision.py
  • frontends/aiq_api/tests/test_submit_internal_agent.py
  • frontends/ui/src/adapters/api/websocket-client.spec.ts
  • frontends/ui/src/adapters/api/websocket-client.ts
  • frontends/ui/src/features/chat/hooks/use-websocket-chat.spec.ts
  • frontends/ui/src/features/chat/hooks/use-websocket-chat.ts
  • src/aiq_agent/agents/chat_researcher/agent.py
  • src/aiq_agent/agents/chat_researcher/models/intent.py
  • src/aiq_agent/agents/chat_researcher/models/state.py
  • src/aiq_agent/agents/chat_researcher/nodes/intent_classifier.py
  • src/aiq_agent/agents/chat_researcher/prompts/intent_classification.j2
  • src/aiq_agent/agents/chat_researcher/register.py
  • src/aiq_agent/agents/chat_researcher/utils.py
  • src/aiq_agent/agents/report_rewriter/__init__.py
  • src/aiq_agent/agents/report_rewriter/agent.py
  • src/aiq_agent/agents/report_rewriter/models.py
  • src/aiq_agent/agents/report_rewriter/prompts/edit.j2
  • tests/aiq_agent/agents/chat_researcher/models/test_intent.py
  • tests/aiq_agent/agents/chat_researcher/models/test_state.py
  • tests/aiq_agent/agents/chat_researcher/nodes/test_intent_classifier.py
  • tests/aiq_agent/agents/chat_researcher/test_agent.py
  • tests/aiq_agent/agents/chat_researcher/test_register_helpers.py
  • tests/aiq_agent/agents/chat_researcher/test_utils.py
  • tests/aiq_agent/agents/report_rewriter/test_agent.py
  • tests/aiq_agent/jobs/test_runner.py
📜 Review details
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: UI Unit Tests
🧰 Additional context used
📓 Path-based instructions (12)
**/*.py

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Run ruff check and ruff format validation for Python code changes

**/*.py: Python code must be linted and formatted with Ruff using line length 120, target Python 3.11, rule sets E,F,W,I,PL,UP, and isort force-single-line configuration
Never commit secrets, tokens, or environment-specific hostnames; use environment variables and SecretStr instead, resolving API keys at runtime
Never print or log secret values, including in tool output or error messages
Missing-secret paths must degrade gracefully (stub/skip), not crash or leak
Do not hand-reformat unrelated code when making changes; match the existing import and formatting style

Files:

  • src/aiq_agent/agents/report_rewriter/__init__.py
  • tests/aiq_agent/agents/chat_researcher/models/test_state.py
  • src/aiq_agent/agents/chat_researcher/models/state.py
  • frontends/aiq_api/tests/test_agent_registry_visibility.py
  • tests/aiq_agent/agents/report_rewriter/test_agent.py
  • frontends/aiq_api/tests/test_submit_internal_agent.py
  • frontends/aiq_api/tests/test_job_submit_data_sources.py
  • frontends/aiq_api/tests/test_report_context.py
  • tests/aiq_agent/agents/chat_researcher/test_register_helpers.py
  • frontends/aiq_api/tests/test_submit_collision.py
  • src/aiq_agent/agents/chat_researcher/models/intent.py
  • frontends/aiq_api/tests/test_report_edit.py
  • src/aiq_agent/agents/report_rewriter/agent.py
  • tests/aiq_agent/agents/chat_researcher/models/test_intent.py
  • src/aiq_agent/agents/report_rewriter/models.py
  • tests/aiq_agent/agents/chat_researcher/test_utils.py
  • frontends/aiq_api/src/aiq_api/registry.py
  • frontends/aiq_api/tests/test_job_access.py
  • tests/aiq_agent/agents/chat_researcher/test_agent.py
  • tests/aiq_agent/jobs/test_runner.py
  • src/aiq_agent/agents/chat_researcher/nodes/intent_classifier.py
  • frontends/aiq_api/src/aiq_api/jobs/submit.py
  • frontends/aiq_api/src/aiq_api/jobs/report_context.py
  • tests/aiq_agent/agents/chat_researcher/nodes/test_intent_classifier.py
  • src/aiq_agent/agents/chat_researcher/agent.py
  • src/aiq_agent/agents/chat_researcher/utils.py
  • src/aiq_agent/agents/chat_researcher/register.py
  • frontends/aiq_api/src/aiq_api/jobs/runner.py
  • frontends/aiq_api/src/aiq_api/routes/jobs.py
src/aiq_agent/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

src/aiq_agent/**/*.py: Respect authenticated data sources by honoring requires_auth, per-user token pass-through, and backend token validators; apply owner guardrails before loading protected report or artifact context into an agent
Do not weaken or bypass AuthMiddleware, validators, or auth gating without a prior design discussion

Files:

  • src/aiq_agent/agents/report_rewriter/__init__.py
  • src/aiq_agent/agents/chat_researcher/models/state.py
  • src/aiq_agent/agents/chat_researcher/models/intent.py
  • src/aiq_agent/agents/report_rewriter/agent.py
  • src/aiq_agent/agents/report_rewriter/models.py
  • src/aiq_agent/agents/chat_researcher/nodes/intent_classifier.py
  • src/aiq_agent/agents/chat_researcher/agent.py
  • src/aiq_agent/agents/chat_researcher/utils.py
  • src/aiq_agent/agents/chat_researcher/register.py
**

⚙️ CodeRabbit configuration file

**:

AI-Q Agent Guidance

Repository-global instructions for coding agents and for humans reviewing
agent-authored changes. These rules apply to every task in this repository.
Task-specific runbooks live in .agents/skills/ — load the
relevant skill before starting a workflow it covers.

Project overview

AI-Q is an NVIDIA AI Blueprint: an enterprise research agent built on the
NeMo Agent Toolkit (NAT). The deployed product is a research blueprint, not
a general skill runtime. New retrieval sources and tools are NAT functions;
agent behavior is driven by workflow YAML, Jinja2 prompts, and a data-source
registry — not by hard-coded logic.

Primary boundaries:

  • Backend Python package: src/aiq_agent/.
  • Data-source and tool packages: sources/ (each is its own package).
  • Frontends and tooling: frontends/ (web UI in frontends/ui/, eval harnesses
    in frontends/benchmarks/).
  • Configs, deployment, docs: configs/, deploy/, docs/.

Stay inside this repository. If your workspace also contains adjacent repos
(for example a sibling NeMo-Relay checkout), do not edit them as part of an AI-Q
change. Treat sources/* as independent packages: prefer the smallest change
scoped to the package you are touching.

Repository structure

Path Purpose
src/aiq_agent/ Backend agent, FastAPI extensions, auth, observability, knowledge
sources/ Data-source / tool packages (e.g. tavily_web_search, google_scholar_paper_search)
configs/ Workflow YAML configs (e.g. config_cli_default.yml)
frontends/ui/ Next.js / React / TypeScript / Tailwind / KUI web UI
frontends/benchmarks/ Eval harnesses: freshqa, deepsearch_qa, deepresearch_bench
deploy/ Docker Compose and Helm/Kubernetes assets; deploy/.env for secrets
docs/source/ ...

Files:

  • src/aiq_agent/agents/report_rewriter/__init__.py
  • src/aiq_agent/agents/report_rewriter/prompts/edit.j2
  • tests/aiq_agent/agents/chat_researcher/models/test_state.py
  • src/aiq_agent/agents/chat_researcher/models/state.py
  • frontends/aiq_api/tests/test_agent_registry_visibility.py
  • frontends/ui/src/adapters/api/websocket-client.ts
  • tests/aiq_agent/agents/report_rewriter/test_agent.py
  • frontends/aiq_api/tests/test_submit_internal_agent.py
  • ci/markdown-link-check-config.json
  • frontends/aiq_api/tests/test_job_submit_data_sources.py
  • frontends/aiq_api/tests/test_report_context.py
  • tests/aiq_agent/agents/chat_researcher/test_register_helpers.py
  • frontends/aiq_api/tests/test_submit_collision.py
  • frontends/ui/src/adapters/api/websocket-client.spec.ts
  • src/aiq_agent/agents/chat_researcher/models/intent.py
  • src/aiq_agent/agents/chat_researcher/prompts/intent_classification.j2
  • frontends/aiq_api/tests/test_report_edit.py
  • src/aiq_agent/agents/report_rewriter/agent.py
  • tests/aiq_agent/agents/chat_researcher/models/test_intent.py
  • src/aiq_agent/agents/report_rewriter/models.py
  • tests/aiq_agent/agents/chat_researcher/test_utils.py
  • docs/source/integration/rest-api.md
  • frontends/aiq_api/src/aiq_api/registry.py
  • frontends/ui/src/features/chat/hooks/use-websocket-chat.spec.ts
  • frontends/aiq_api/tests/test_job_access.py
  • tests/aiq_agent/agents/chat_researcher/test_agent.py
  • tests/aiq_agent/jobs/test_runner.py
  • src/aiq_agent/agents/chat_researcher/nodes/intent_classifier.py
  • frontends/ui/src/features/chat/hooks/use-websocket-chat.ts
  • frontends/aiq_api/src/aiq_api/jobs/submit.py
  • frontends/aiq_api/src/aiq_api/jobs/report_context.py
  • tests/aiq_agent/agents/chat_researcher/nodes/test_intent_classifier.py
  • src/aiq_agent/agents/chat_researcher/agent.py
  • src/aiq_agent/agents/chat_researcher/utils.py
  • src/aiq_agent/agents/chat_researcher/register.py
  • frontends/aiq_api/src/aiq_api/jobs/runner.py
  • frontends/aiq_api/src/aiq_api/routes/jobs.py
src/aiq_agent/agents/**/*

⚙️ CodeRabbit configuration file

src/aiq_agent/agents/**/*: Review agent changes for research workflow correctness, graph state transitions, prompt/tool contracts,
HITL behavior, and failure handling. Flag changes that weaken source attribution, report generation,
async cancellation, checkpointing, or data-source selection without focused tests and docs.

Files:

  • src/aiq_agent/agents/report_rewriter/__init__.py
  • src/aiq_agent/agents/report_rewriter/prompts/edit.j2
  • src/aiq_agent/agents/chat_researcher/models/state.py
  • src/aiq_agent/agents/chat_researcher/models/intent.py
  • src/aiq_agent/agents/chat_researcher/prompts/intent_classification.j2
  • src/aiq_agent/agents/report_rewriter/agent.py
  • src/aiq_agent/agents/report_rewriter/models.py
  • src/aiq_agent/agents/chat_researcher/nodes/intent_classifier.py
  • src/aiq_agent/agents/chat_researcher/agent.py
  • src/aiq_agent/agents/chat_researcher/utils.py
  • src/aiq_agent/agents/chat_researcher/register.py
**/*test*.py

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Run pytest for all behavior changes in Python code

Files:

  • tests/aiq_agent/agents/chat_researcher/models/test_state.py
  • frontends/aiq_api/tests/test_agent_registry_visibility.py
  • tests/aiq_agent/agents/report_rewriter/test_agent.py
  • frontends/aiq_api/tests/test_submit_internal_agent.py
  • frontends/aiq_api/tests/test_job_submit_data_sources.py
  • frontends/aiq_api/tests/test_report_context.py
  • tests/aiq_agent/agents/chat_researcher/test_register_helpers.py
  • frontends/aiq_api/tests/test_submit_collision.py
  • frontends/aiq_api/tests/test_report_edit.py
  • tests/aiq_agent/agents/chat_researcher/models/test_intent.py
  • tests/aiq_agent/agents/chat_researcher/test_utils.py
  • frontends/aiq_api/tests/test_job_access.py
  • tests/aiq_agent/agents/chat_researcher/test_agent.py
  • tests/aiq_agent/jobs/test_runner.py
  • tests/aiq_agent/agents/chat_researcher/nodes/test_intent_classifier.py
frontends/ui/**/*.{js,ts,jsx,tsx,vue}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Run npm lint, type-check, and build validation for UI changes in frontends/ui

Files:

  • frontends/ui/src/adapters/api/websocket-client.ts
  • frontends/ui/src/adapters/api/websocket-client.spec.ts
  • frontends/ui/src/features/chat/hooks/use-websocket-chat.spec.ts
  • frontends/ui/src/features/chat/hooks/use-websocket-chat.ts
frontends/ui/**/*.{ts,tsx,jsx,js}

📄 CodeRabbit inference engine (AGENTS.md)

frontends/ui/**/*.{ts,tsx,jsx,js}: The UI is built with Next.js / React / TypeScript / Tailwind with KUI components; reuse existing KUI components and visual patterns rather than introducing new ones
Validate UI-affecting changes with npm run lint, npm run type-check, and npm run test:ci, and include a screenshot for visible changes

Files:

  • frontends/ui/src/adapters/api/websocket-client.ts
  • frontends/ui/src/adapters/api/websocket-client.spec.ts
  • frontends/ui/src/features/chat/hooks/use-websocket-chat.spec.ts
  • frontends/ui/src/features/chat/hooks/use-websocket-chat.ts
frontends/ui/**/*

⚙️ CodeRabbit configuration file

frontends/ui/**/*: Review UI changes for strict TypeScript behavior, API contract alignment, auth/session handling, accessible controls,
resilient loading and error states, and report/chat state consistency. Prefer existing UI patterns and require tests
for changed user-visible workflows.

Files:

  • frontends/ui/src/adapters/api/websocket-client.ts
  • frontends/ui/src/adapters/api/websocket-client.spec.ts
  • frontends/ui/src/features/chat/hooks/use-websocket-chat.spec.ts
  • frontends/ui/src/features/chat/hooks/use-websocket-chat.ts
{.github/**,ci/**,.pre-commit-config.yaml,pyproject.toml,uv.lock}

⚙️ CodeRabbit configuration file

{.github/**,ci/**,.pre-commit-config.yaml,pyproject.toml,uv.lock}: Review automation and packaging changes for least-privilege permissions, pinned versions where appropriate,
copy-pr-bot pull-request/ branch behavior, reproducible uv/npm setup, secret handling, and consistency with
the documented validation matrix.

Files:

  • ci/markdown-link-check-config.json
docs/source/**/*

📄 CodeRabbit inference engine (AGENTS.md)

Update the docs under docs/source/ when behavior, configuration, or workflows change

Files:

  • docs/source/integration/rest-api.md
{docs/**,README.md,CONTRIBUTING.md,SECURITY.md,CODE-OF-CONDUCT.md}

⚙️ CodeRabbit configuration file

{docs/**,README.md,CONTRIBUTING.md,SECURITY.md,CODE-OF-CONDUCT.md}: Review documentation for command accuracy, branch-name consistency, current CI and copy-pr-bot behavior, public
vs internal boundary clarity, stale examples, and links that no longer match the repository layout.

Files:

  • docs/source/integration/rest-api.md
{src/aiq_agent/fastapi_extensions/**,frontends/aiq_api/src/aiq_api/**}

⚙️ CodeRabbit configuration file

{src/aiq_agent/fastapi_extensions/**,frontends/aiq_api/src/aiq_api/**}: Treat API, auth, and job-runner changes as externally visible contracts. Check authorization boundaries,
request tracing, async job lifecycle, websocket reconnect behavior, error responses, and cross-user data isolation.
Require tests for route behavior, access decisions, and job state transitions when those surfaces change.

Files:

  • frontends/aiq_api/src/aiq_api/registry.py
  • frontends/aiq_api/src/aiq_api/jobs/submit.py
  • frontends/aiq_api/src/aiq_api/jobs/report_context.py
  • frontends/aiq_api/src/aiq_api/jobs/runner.py
  • frontends/aiq_api/src/aiq_api/routes/jobs.py
🧠 Learnings (1)
📚 Learning: 2026-06-14T17:49:00.640Z
Learnt from: torkian
Repo: NVIDIA-AI-Blueprints/aiq PR: 273
File: frontends/aiq_api/tests/test_sse_reconnect_cursor.py:384-401
Timestamp: 2026-06-14T17:49:00.640Z
Learning: When using `unittest.mock.patch` for code that imports dependencies inside functions/generators (e.g., inside `aiq_api.routes.jobs`), don’t patch via an attribute that doesn’t exist on the consuming module. If the generator does `from ..jobs.event_store import EventStore` inside the generator body, then `aiq_api.routes.jobs` will not have an `EventStore` attribute; patch the source class/method in its defining module instead (e.g., `aiq_api.jobs.event_store.EventStore.get_events_async`). Patching `aiq_api.routes.jobs.EventStore...` would raise `AttributeError` because that symbol is not present at module scope.

Applied to files:

  • frontends/aiq_api/tests/test_agent_registry_visibility.py
  • frontends/aiq_api/tests/test_submit_internal_agent.py
  • frontends/aiq_api/tests/test_job_submit_data_sources.py
  • frontends/aiq_api/tests/test_report_context.py
  • frontends/aiq_api/tests/test_submit_collision.py
  • frontends/aiq_api/tests/test_report_edit.py
  • frontends/aiq_api/tests/test_job_access.py
🪛 ast-grep (0.44.0)
frontends/aiq_api/src/aiq_api/jobs/report_context.py

[warning] 95-95: Do not make http calls without encryption
Context: "http://"
Note: [CWE-319] Cleartext Transmission of Sensitive Information.

(requests-http)

🔇 Additional comments (35)
src/aiq_agent/agents/report_rewriter/__init__.py (1)

1-10: LGTM!

src/aiq_agent/agents/report_rewriter/models.py (1)

1-32: LGTM!

src/aiq_agent/agents/report_rewriter/agent.py (1)

1-110: LGTM!

Also applies to: 116-123

src/aiq_agent/agents/report_rewriter/prompts/edit.j2 (1)

1-24: LGTM!

tests/aiq_agent/agents/report_rewriter/test_agent.py (1)

1-65: LGTM!

frontends/aiq_api/src/aiq_api/jobs/report_context.py (1)

1-205: LGTM!

Also applies to: 209-260

frontends/aiq_api/tests/test_report_context.py (1)

1-173: LGTM!

frontends/ui/src/adapters/api/websocket-client.ts (1)

226-237: LGTM!

frontends/ui/src/adapters/api/websocket-client.spec.ts (1)

203-224: LGTM!

frontends/ui/src/features/chat/hooks/use-websocket-chat.ts (5)

71-77: LGTM!


126-137: LGTM!


558-569: LGTM!


619-640: LGTM!


1190-1190: LGTM!

frontends/ui/src/features/chat/hooks/use-websocket-chat.spec.ts (2)

346-376: LGTM!


1243-1314: LGTM!

frontends/aiq_api/src/aiq_api/registry.py (1)

44-46: LGTM!

Also applies to: 57-57, 67-67, 81-81, 119-126

frontends/aiq_api/src/aiq_api/jobs/submit.py (1)

27-29: LGTM!

Also applies to: 44-61, 144-146, 165-166, 188-192, 249-264, 284-308

frontends/aiq_api/tests/test_agent_registry_visibility.py (1)

13-22: LGTM!

Also applies to: 24-37, 39-47, 49-90

frontends/aiq_api/tests/test_submit_internal_agent.py (1)

18-33: LGTM!

Also applies to: 35-66

frontends/aiq_api/tests/test_submit_collision.py (1)

50-70: LGTM!

Also applies to: 72-94, 96-119

frontends/aiq_api/tests/test_job_access.py (1)

115-133: LGTM!

Also applies to: 175-194

frontends/aiq_api/tests/test_job_submit_data_sources.py (1)

146-170: LGTM!

frontends/aiq_api/src/aiq_api/jobs/runner.py (1)

250-251: LGTM!

Also applies to: 283-284, 491-492, 660-661, 692-699, 718-719

tests/aiq_agent/jobs/test_runner.py (1)

438-472: LGTM!

Also applies to: 1496-1595

src/aiq_agent/agents/chat_researcher/models/intent.py (1)

23-25: LGTM!

Also applies to: 38-40

src/aiq_agent/agents/chat_researcher/models/state.py (1)

64-64: LGTM!

tests/aiq_agent/agents/chat_researcher/models/test_intent.py (1)

37-41: LGTM!

Also applies to: 42-54, 55-59

tests/aiq_agent/agents/chat_researcher/models/test_state.py (1)

109-109: LGTM!

Also applies to: 129-137

src/aiq_agent/agents/chat_researcher/utils.py (1)

21-33: LGTM!

Also applies to: 109-139, 210-214

tests/aiq_agent/agents/chat_researcher/test_utils.py (1)

25-25: LGTM!

Also applies to: 202-237

src/aiq_agent/agents/chat_researcher/prompts/intent_classification.j2 (1)

19-32: LGTM!

Also applies to: 41-45, 57-63

tests/aiq_agent/agents/chat_researcher/nodes/test_intent_classifier.py (1)

107-217: LGTM!

tests/aiq_agent/agents/chat_researcher/test_agent.py (1)

246-421: LGTM!

tests/aiq_agent/agents/chat_researcher/test_register_helpers.py (1)

20-95: LGTM!

Comment thread ci/markdown-link-check-config.json Outdated
Comment thread docs/source/integration/rest-api.md
Comment thread frontends/aiq_api/src/aiq_api/jobs/report_context.py Outdated
Comment thread frontends/aiq_api/src/aiq_api/jobs/runner.py Outdated
Comment thread frontends/aiq_api/src/aiq_api/routes/jobs.py
Comment thread src/aiq_agent/agents/chat_researcher/nodes/intent_classifier.py Outdated
Comment thread src/aiq_agent/agents/chat_researcher/register.py Outdated
Comment thread src/aiq_agent/agents/chat_researcher/register.py Outdated
Comment thread src/aiq_agent/agents/chat_researcher/utils.py Outdated
Comment thread src/aiq_agent/agents/report_rewriter/agent.py Outdated
AjayThorve added a commit to AjayThorve/aiq-research-assistant that referenced this pull request Jun 22, 2026
Resolve the 13 actionable CodeRabbit comments on PR NVIDIA-AI-Blueprints#271:

- routes/jobs.py: reject whitespace-only `input` on report-edit and submit
  requests (422) via a field validator; map post-auth RuntimeError to 503
  (availability) instead of 403, without echoing internal detail.
- runner.py: build job output so the canonical `report` can never be
  overwritten by a stray `report` key in output_metadata.
- report_context.py: tolerate a transient event-log fetch failure when the
  report is already available (fall back to report-only sources).
- register.py: bound the report-ask LLM call with a timeout so a stalled
  provider degrades gracefully; make parent-report context enrichment optional
  for delta research (don't abort the deep job if context lookup fails).
- intent_classifier.py: normalize string booleans for use_parent_report_context
  ("false" must not be truthy).
- utils.py: preserve an explicit empty data_sources list ([] = no data-source
  tools) instead of falling through to other sources.
- agent.py: log error type, not raw exception text, in report follow-up paths.
- report_rewriter/agent.py: dispatch the final report to all registered
  callbacks (remove early break).
- ci/markdown-link-check-config.json: narrow the FastAPI/Dask ignores to the
  exact referenced URLs instead of whole domains.
- docs: document the report-edit 500 case.
- tests: assert authorization on the GET report route; add coverage for blank
  input (422), string-boolean normalization, and empty data_sources
  preservation.

Backend suites (aiq_api + aiq_agent) and UI specs pass; ruff and tsc clean.
Verified live: blank input -> 422, report ask answers, job_id collision -> 409.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Ajay Thorve <athorve@nvidia.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
docs/source/integration/rest-api.md (1)

80-102: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Document the new non-blank validation and 422 responses.

Both request models now strip and reject whitespace-only input, but the docs still say only “min 1 character,” and report-edit omits the resulting 422 validation case.

As per path instructions, “Review documentation for command accuracy ... stale examples.”

Suggested docs update
-| `input` | `string` | Yes | Research query (min 1 character) |
+| `input` | `string` | Yes | Research query. Must be non-blank after trimming |
@@
-| `422` | One or more unknown data source IDs. Response `detail` includes `message`, `invalid_ids`, and `known_ids` for client-side recovery UX |
+| `422` | Validation error, including blank/whitespace-only `input`, invalid request fields, or one or more unknown data source IDs. Data source errors include `message`, `invalid_ids`, and `known_ids` for client-side recovery UX |
@@
-| `input` | `string` | Yes | Edit instruction for the parent report (min 1 character) |
+| `input` | `string` | Yes | Edit instruction for the parent report. Must be non-blank after trimming |
@@
 | `409` | Parent job is incomplete, has no durable report, or the supplied child `job_id` collides |
+| `422` | Validation error, including blank/whitespace-only `input`, invalid child `job_id`, or invalid `expiry_seconds` |
 | `500` | Failed to submit the report edit job |

Also applies to: 120-145

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/source/integration/rest-api.md` around lines 80 - 102, Update the API
documentation to reflect the new whitespace validation for the `input`
parameter. First, modify the `input` parameter description to clarify that it
rejects whitespace-only strings in addition to requiring minimum 1 character.
Second, add a 422 error response case to the error responses table that
documents the validation failure when `input` contains only whitespace. This
change should be applied to both sections mentioned in the comment (the main
section and the additional section).

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@docs/source/integration/rest-api.md`:
- Around line 80-102: Update the API documentation to reflect the new whitespace
validation for the `input` parameter. First, modify the `input` parameter
description to clarify that it rejects whitespace-only strings in addition to
requiring minimum 1 character. Second, add a 422 error response case to the
error responses table that documents the validation failure when `input`
contains only whitespace. This change should be applied to both sections
mentioned in the comment (the main section and the additional section).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: f58391a2-9ae9-4551-a57e-4dfe594d430c

📥 Commits

Reviewing files that changed from the base of the PR and between c29f0f0 and 38497b9.

📒 Files selected for processing (13)
  • ci/markdown-link-check-config.json
  • docs/source/integration/rest-api.md
  • frontends/aiq_api/src/aiq_api/jobs/report_context.py
  • frontends/aiq_api/src/aiq_api/jobs/runner.py
  • frontends/aiq_api/src/aiq_api/routes/jobs.py
  • frontends/aiq_api/tests/test_report_edit.py
  • src/aiq_agent/agents/chat_researcher/agent.py
  • src/aiq_agent/agents/chat_researcher/nodes/intent_classifier.py
  • src/aiq_agent/agents/chat_researcher/register.py
  • src/aiq_agent/agents/chat_researcher/utils.py
  • src/aiq_agent/agents/report_rewriter/agent.py
  • tests/aiq_agent/agents/chat_researcher/nodes/test_intent_classifier.py
  • tests/aiq_agent/agents/chat_researcher/test_utils.py
💤 Files with no reviewable changes (1)
  • src/aiq_agent/agents/report_rewriter/agent.py
📜 Review details
🧰 Additional context used
📓 Path-based instructions (9)
**

⚙️ CodeRabbit configuration file

**:

AI-Q Agent Guidance

Repository-global instructions for coding agents and for humans reviewing
agent-authored changes. These rules apply to every task in this repository.
Task-specific runbooks live in .agents/skills/ — load the
relevant skill before starting a workflow it covers.

Project overview

AI-Q is an NVIDIA AI Blueprint: an enterprise research agent built on the
NeMo Agent Toolkit (NAT). The deployed product is a research blueprint, not
a general skill runtime. New retrieval sources and tools are NAT functions;
agent behavior is driven by workflow YAML, Jinja2 prompts, and a data-source
registry — not by hard-coded logic.

Primary boundaries:

  • Backend Python package: src/aiq_agent/.
  • Data-source and tool packages: sources/ (each is its own package).
  • Frontends and tooling: frontends/ (web UI in frontends/ui/, eval harnesses
    in frontends/benchmarks/).
  • Configs, deployment, docs: configs/, deploy/, docs/.

Stay inside this repository. If your workspace also contains adjacent repos
(for example a sibling NeMo-Relay checkout), do not edit them as part of an AI-Q
change. Treat sources/* as independent packages: prefer the smallest change
scoped to the package you are touching.

Repository structure

Path Purpose
src/aiq_agent/ Backend agent, FastAPI extensions, auth, observability, knowledge
sources/ Data-source / tool packages (e.g. tavily_web_search, google_scholar_paper_search)
configs/ Workflow YAML configs (e.g. config_cli_default.yml)
frontends/ui/ Next.js / React / TypeScript / Tailwind / KUI web UI
frontends/benchmarks/ Eval harnesses: freshqa, deepsearch_qa, deepresearch_bench
deploy/ Docker Compose and Helm/Kubernetes assets; deploy/.env for secrets
docs/source/ ...

Files:

  • ci/markdown-link-check-config.json
  • docs/source/integration/rest-api.md
  • tests/aiq_agent/agents/chat_researcher/test_utils.py
  • src/aiq_agent/agents/chat_researcher/nodes/intent_classifier.py
  • tests/aiq_agent/agents/chat_researcher/nodes/test_intent_classifier.py
  • src/aiq_agent/agents/chat_researcher/utils.py
  • frontends/aiq_api/src/aiq_api/jobs/report_context.py
  • frontends/aiq_api/src/aiq_api/jobs/runner.py
  • src/aiq_agent/agents/chat_researcher/agent.py
  • frontends/aiq_api/tests/test_report_edit.py
  • src/aiq_agent/agents/chat_researcher/register.py
  • frontends/aiq_api/src/aiq_api/routes/jobs.py
{.github/**,ci/**,.pre-commit-config.yaml,pyproject.toml,uv.lock}

⚙️ CodeRabbit configuration file

{.github/**,ci/**,.pre-commit-config.yaml,pyproject.toml,uv.lock}: Review automation and packaging changes for least-privilege permissions, pinned versions where appropriate,
copy-pr-bot pull-request/ branch behavior, reproducible uv/npm setup, secret handling, and consistency with
the documented validation matrix.

Files:

  • ci/markdown-link-check-config.json
docs/source/**/*

📄 CodeRabbit inference engine (AGENTS.md)

Update the docs under docs/source/ when behavior, configuration, or workflows change

Files:

  • docs/source/integration/rest-api.md
{docs/**,README.md,CONTRIBUTING.md,SECURITY.md,CODE-OF-CONDUCT.md}

⚙️ CodeRabbit configuration file

{docs/**,README.md,CONTRIBUTING.md,SECURITY.md,CODE-OF-CONDUCT.md}: Review documentation for command accuracy, branch-name consistency, current CI and copy-pr-bot behavior, public
vs internal boundary clarity, stale examples, and links that no longer match the repository layout.

Files:

  • docs/source/integration/rest-api.md
**/*.py

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Run ruff check and ruff format validation for Python code changes

**/*.py: Python code must be linted and formatted with Ruff using line length 120, target Python 3.11, rule sets E,F,W,I,PL,UP, and isort force-single-line configuration
Never commit secrets, tokens, or environment-specific hostnames; use environment variables and SecretStr instead, resolving API keys at runtime
Never print or log secret values, including in tool output or error messages
Missing-secret paths must degrade gracefully (stub/skip), not crash or leak
Do not hand-reformat unrelated code when making changes; match the existing import and formatting style

Files:

  • tests/aiq_agent/agents/chat_researcher/test_utils.py
  • src/aiq_agent/agents/chat_researcher/nodes/intent_classifier.py
  • tests/aiq_agent/agents/chat_researcher/nodes/test_intent_classifier.py
  • src/aiq_agent/agents/chat_researcher/utils.py
  • frontends/aiq_api/src/aiq_api/jobs/report_context.py
  • frontends/aiq_api/src/aiq_api/jobs/runner.py
  • src/aiq_agent/agents/chat_researcher/agent.py
  • frontends/aiq_api/tests/test_report_edit.py
  • src/aiq_agent/agents/chat_researcher/register.py
  • frontends/aiq_api/src/aiq_api/routes/jobs.py
**/*test*.py

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Run pytest for all behavior changes in Python code

Files:

  • tests/aiq_agent/agents/chat_researcher/test_utils.py
  • tests/aiq_agent/agents/chat_researcher/nodes/test_intent_classifier.py
  • frontends/aiq_api/tests/test_report_edit.py
src/aiq_agent/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

src/aiq_agent/**/*.py: Respect authenticated data sources by honoring requires_auth, per-user token pass-through, and backend token validators; apply owner guardrails before loading protected report or artifact context into an agent
Do not weaken or bypass AuthMiddleware, validators, or auth gating without a prior design discussion

Files:

  • src/aiq_agent/agents/chat_researcher/nodes/intent_classifier.py
  • src/aiq_agent/agents/chat_researcher/utils.py
  • src/aiq_agent/agents/chat_researcher/agent.py
  • src/aiq_agent/agents/chat_researcher/register.py
src/aiq_agent/agents/**/*

⚙️ CodeRabbit configuration file

src/aiq_agent/agents/**/*: Review agent changes for research workflow correctness, graph state transitions, prompt/tool contracts,
HITL behavior, and failure handling. Flag changes that weaken source attribution, report generation,
async cancellation, checkpointing, or data-source selection without focused tests and docs.

Files:

  • src/aiq_agent/agents/chat_researcher/nodes/intent_classifier.py
  • src/aiq_agent/agents/chat_researcher/utils.py
  • src/aiq_agent/agents/chat_researcher/agent.py
  • src/aiq_agent/agents/chat_researcher/register.py
{src/aiq_agent/fastapi_extensions/**,frontends/aiq_api/src/aiq_api/**}

⚙️ CodeRabbit configuration file

{src/aiq_agent/fastapi_extensions/**,frontends/aiq_api/src/aiq_api/**}: Treat API, auth, and job-runner changes as externally visible contracts. Check authorization boundaries,
request tracing, async job lifecycle, websocket reconnect behavior, error responses, and cross-user data isolation.
Require tests for route behavior, access decisions, and job state transitions when those surfaces change.

Files:

  • frontends/aiq_api/src/aiq_api/jobs/report_context.py
  • frontends/aiq_api/src/aiq_api/jobs/runner.py
  • frontends/aiq_api/src/aiq_api/routes/jobs.py
🧠 Learnings (1)
📚 Learning: 2026-06-14T17:49:00.640Z
Learnt from: torkian
Repo: NVIDIA-AI-Blueprints/aiq PR: 273
File: frontends/aiq_api/tests/test_sse_reconnect_cursor.py:384-401
Timestamp: 2026-06-14T17:49:00.640Z
Learning: When using `unittest.mock.patch` for code that imports dependencies inside functions/generators (e.g., inside `aiq_api.routes.jobs`), don’t patch via an attribute that doesn’t exist on the consuming module. If the generator does `from ..jobs.event_store import EventStore` inside the generator body, then `aiq_api.routes.jobs` will not have an `EventStore` attribute; patch the source class/method in its defining module instead (e.g., `aiq_api.jobs.event_store.EventStore.get_events_async`). Patching `aiq_api.routes.jobs.EventStore...` would raise `AttributeError` because that symbol is not present at module scope.

Applied to files:

  • frontends/aiq_api/tests/test_report_edit.py
🔇 Additional comments (11)
ci/markdown-link-check-config.json (1)

13-16: LGTM!

src/aiq_agent/agents/chat_researcher/utils.py (1)

153-157: LGTM!

Also applies to: 178-179, 202-203

tests/aiq_agent/agents/chat_researcher/test_utils.py (1)

239-256: LGTM!

src/aiq_agent/agents/chat_researcher/nodes/intent_classifier.py (1)

172-172: LGTM!

Also applies to: 268-280

tests/aiq_agent/agents/chat_researcher/nodes/test_intent_classifier.py (1)

28-48: LGTM!

frontends/aiq_api/src/aiq_api/jobs/report_context.py (1)

207-213: LGTM!

src/aiq_agent/agents/chat_researcher/agent.py (1)

88-92: LGTM!

Also applies to: 109-110, 122-123, 332-364, 366-373, 419-420, 431-432, 446-447, 477-477

src/aiq_agent/agents/chat_researcher/register.py (1)

18-18: LGTM!

Also applies to: 53-55, 96-100, 314-360, 399-419, 452-453, 517-560

frontends/aiq_api/src/aiq_api/jobs/runner.py (1)

230-252: LGTM!

Also applies to: 280-284, 484-492, 525-528

frontends/aiq_api/src/aiq_api/routes/jobs.py (1)

46-46: LGTM!

Also applies to: 91-98, 291-318, 390-394, 499-500, 535-542, 576-620, 749-759

frontends/aiq_api/tests/test_report_edit.py (1)

153-165: LGTM!

Also applies to: 201-224

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
docs/source/integration/rest-api.md (1)

42-45: ⚠️ Potential issue | 🟡 Minor

Clarify that the internal-only agent error message includes the agent type name.

The documentation correctly states that internal agents are rejected with HTTP 400, but the error message format is incomplete. The actual error is "Agent type is internal-only: <agent_type>" (with the agent type interpolated), not just "Agent type is internal-only". Update line 42–45 to show this format for client-side error handling.

The GET /v1/jobs/async/agents endpoint correctly filters to public agents only (confirmed by if config.public at line 393 of the route handler), and the non-blank validation on both JobSubmitRequest and ReportEditRequest is correct.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/source/integration/rest-api.md` around lines 42 - 45, The error message
format for internal-only agents in the documentation is incomplete. Update the
text in lines 42-45 describing the POST /v1/jobs/async/submit endpoint error
response to clarify that the actual error message includes the agent type name
itself. Change the documented error message from "Agent type is internal-only"
to "Agent type is internal-only: <agent_type>" (where agent_type is interpolated
with the actual name) to accurately reflect what clients will receive when
attempting to submit with an internal-only agent.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@docs/source/integration/rest-api.md`:
- Around line 42-45: The error message format for internal-only agents in the
documentation is incomplete. Update the text in lines 42-45 describing the POST
/v1/jobs/async/submit endpoint error response to clarify that the actual error
message includes the agent type name itself. Change the documented error message
from "Agent type is internal-only" to "Agent type is internal-only:
<agent_type>" (where agent_type is interpolated with the actual name) to
accurately reflect what clients will receive when attempting to submit with an
internal-only agent.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: c2744254-7423-436d-bb31-7b1760c09885

📥 Commits

Reviewing files that changed from the base of the PR and between 38497b9 and 067ad5c.

📒 Files selected for processing (1)
  • docs/source/integration/rest-api.md
📜 Review details
🧰 Additional context used
📓 Path-based instructions (3)
docs/source/**/*

📄 CodeRabbit inference engine (AGENTS.md)

Update the docs under docs/source/ when behavior, configuration, or workflows change

Files:

  • docs/source/integration/rest-api.md
**

⚙️ CodeRabbit configuration file

**:

AI-Q Agent Guidance

Repository-global instructions for coding agents and for humans reviewing
agent-authored changes. These rules apply to every task in this repository.
Task-specific runbooks live in .agents/skills/ — load the
relevant skill before starting a workflow it covers.

Project overview

AI-Q is an NVIDIA AI Blueprint: an enterprise research agent built on the
NeMo Agent Toolkit (NAT). The deployed product is a research blueprint, not
a general skill runtime. New retrieval sources and tools are NAT functions;
agent behavior is driven by workflow YAML, Jinja2 prompts, and a data-source
registry — not by hard-coded logic.

Primary boundaries:

  • Backend Python package: src/aiq_agent/.
  • Data-source and tool packages: sources/ (each is its own package).
  • Frontends and tooling: frontends/ (web UI in frontends/ui/, eval harnesses
    in frontends/benchmarks/).
  • Configs, deployment, docs: configs/, deploy/, docs/.

Stay inside this repository. If your workspace also contains adjacent repos
(for example a sibling NeMo-Relay checkout), do not edit them as part of an AI-Q
change. Treat sources/* as independent packages: prefer the smallest change
scoped to the package you are touching.

Repository structure

Path Purpose
src/aiq_agent/ Backend agent, FastAPI extensions, auth, observability, knowledge
sources/ Data-source / tool packages (e.g. tavily_web_search, google_scholar_paper_search)
configs/ Workflow YAML configs (e.g. config_cli_default.yml)
frontends/ui/ Next.js / React / TypeScript / Tailwind / KUI web UI
frontends/benchmarks/ Eval harnesses: freshqa, deepsearch_qa, deepresearch_bench
deploy/ Docker Compose and Helm/Kubernetes assets; deploy/.env for secrets
docs/source/ ...

Files:

  • docs/source/integration/rest-api.md
{docs/**,README.md,CONTRIBUTING.md,SECURITY.md,CODE-OF-CONDUCT.md}

⚙️ CodeRabbit configuration file

{docs/**,README.md,CONTRIBUTING.md,SECURITY.md,CODE-OF-CONDUCT.md}: Review documentation for command accuracy, branch-name consistency, current CI and copy-pr-bot behavior, public
vs internal boundary clarity, stale examples, and links that no longer match the repository layout.

Files:

  • docs/source/integration/rest-api.md
🔇 Additional comments (1)
docs/source/integration/rest-api.md (1)

120-120: No action required. ReportEditRequest has an identical _input_not_blank validator to JobSubmitRequest (lines 308–315 of frontends/aiq_api/src/aiq_api/routes/jobs.py). The validator strips whitespace and rejects blank input, so the documentation claim is accurate and supported by the implementation.

			> Likely an incorrect or invalid review comment.

Comment thread frontends/ui/src/features/chat/hooks/use-websocket-chat.ts Outdated
Comment thread frontends/aiq_api/src/aiq_api/jobs/report_context.py Outdated
Comment thread frontends/aiq_api/src/aiq_api/jobs/report_context.py Outdated
Comment thread frontends/aiq_api/src/aiq_api/jobs/submit.py
Comment thread frontends/aiq_api/src/aiq_api/routes/jobs.py
Comment thread frontends/aiq_api/src/aiq_api/jobs/report_context.py Outdated
Comment thread frontends/aiq_api/src/aiq_api/jobs/report_context.py Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
frontends/aiq_api/src/aiq_api/jobs/submit.py (1)

268-273: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Do not log raw rollback exceptions.

cleanup_error can include driver messages or DB connection details. Log only the exception type or a sanitized error code so rollback failures cannot leak secrets through logs.

Proposed fix
-        except Exception as cleanup_error:
+        except Exception as cleanup_error:
             logger.warning(
-                "Failed to roll back partial async job submission for %s: %s",
+                "Failed to roll back partial async job submission for %s (%s)",
                 resolved_job_id,
-                cleanup_error,
+                type(cleanup_error).__name__,
             )

As per coding guidelines, “Never print or log secret values, including in tool output or error messages.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontends/aiq_api/src/aiq_api/jobs/submit.py` around lines 268 - 273, The
logger.warning call in the exception handler for cleanup_error is logging the
raw exception, which can contain sensitive information such as database
connection details or driver messages. Instead of logging cleanup_error
directly, extract only the exception type (such as type(cleanup_error).__name__)
or a sanitized error code and log that instead. This ensures that sensitive
details are not leaked through logs while still providing enough information to
identify that a rollback failure occurred.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@frontends/aiq_api/src/aiq_api/jobs/access.py`:
- Around line 49-60: The _LATEST_REPORT_JOB_BASE query and its derived queries
(_LATEST_REPORT_JOB_SQL_ANY and _LATEST_REPORT_JOB_SQL_OWNED) currently select
any successful, non-expired job without filtering for report-producing jobs
specifically. This allows a newer non-report job to incorrectly become the
active_report_job_id. Add a filter condition to the WHERE clause in
_LATEST_REPORT_JOB_BASE to check for report-producing jobs using a marker such
as result_kind or agent type, ensuring both _LATEST_REPORT_JOB_SQL_ANY and
_LATEST_REPORT_JOB_SQL_OWNED inherit this filter. Additionally, add a regression
test that verifies a newer non-report job success does not win as the
active_report_job_id.

---

Outside diff comments:
In `@frontends/aiq_api/src/aiq_api/jobs/submit.py`:
- Around line 268-273: The logger.warning call in the exception handler for
cleanup_error is logging the raw exception, which can contain sensitive
information such as database connection details or driver messages. Instead of
logging cleanup_error directly, extract only the exception type (such as
type(cleanup_error).__name__) or a sanitized error code and log that instead.
This ensures that sensitive details are not leaked through logs while still
providing enough information to identify that a rollback failure occurred.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 3e4d385e-2099-4194-b95b-75d7b8358682

📥 Commits

Reviewing files that changed from the base of the PR and between 47390fe and fe04865.

📒 Files selected for processing (10)
  • deploy/compose/init-db.sql
  • docs/source/integration/rest-api.md
  • frontends/aiq_api/src/aiq_api/jobs/access.py
  • frontends/aiq_api/src/aiq_api/jobs/submit.py
  • frontends/aiq_api/tests/test_job_access.py
  • frontends/aiq_api/tests/test_submit_collision.py
  • skills/aiq-research/SKILL.md
  • skills/aiq-research/scripts/aiq.py
  • src/aiq_agent/agents/chat_researcher/register.py
  • tests/aiq_agent/agents/chat_researcher/test_register_helpers.py
📜 Review details
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: Run Harbor skill eval
🧰 Additional context used
📓 Path-based instructions (13)
**

⚙️ CodeRabbit configuration file

**:

AI-Q Agent Guidance

Repository-global instructions for coding agents and for humans reviewing
agent-authored changes. These rules apply to every task in this repository.
Task-specific runbooks live in .agents/skills/ — load the
relevant skill before starting a workflow it covers.

Project overview

AI-Q is an NVIDIA AI Blueprint: an enterprise research agent built on the
NeMo Agent Toolkit (NAT). The deployed product is a research blueprint, not
a general skill runtime. New retrieval sources and tools are NAT functions;
agent behavior is driven by workflow YAML, Jinja2 prompts, and a data-source
registry — not by hard-coded logic.

Primary boundaries:

  • Backend Python package: src/aiq_agent/.
  • Data-source and tool packages: sources/ (each is its own package).
  • Frontends and tooling: frontends/ (web UI in frontends/ui/, eval harnesses
    in frontends/benchmarks/).
  • Configs, deployment, docs: configs/, deploy/, docs/.

Stay inside this repository. If your workspace also contains adjacent repos
(for example a sibling NeMo-Relay checkout), do not edit them as part of an AI-Q
change. Treat sources/* as independent packages: prefer the smallest change
scoped to the package you are touching.

Repository structure

Path Purpose
src/aiq_agent/ Backend agent, FastAPI extensions, auth, observability, knowledge
sources/ Data-source / tool packages (e.g. tavily_web_search, google_scholar_paper_search)
configs/ Workflow YAML configs (e.g. config_cli_default.yml)
frontends/ui/ Next.js / React / TypeScript / Tailwind / KUI web UI
frontends/benchmarks/ Eval harnesses: freshqa, deepsearch_qa, deepresearch_bench
deploy/ Docker Compose and Helm/Kubernetes assets; deploy/.env for secrets
docs/source/ ...

Files:

  • deploy/compose/init-db.sql
  • skills/aiq-research/scripts/aiq.py
  • tests/aiq_agent/agents/chat_researcher/test_register_helpers.py
  • frontends/aiq_api/tests/test_submit_collision.py
  • frontends/aiq_api/src/aiq_api/jobs/submit.py
  • docs/source/integration/rest-api.md
  • frontends/aiq_api/tests/test_job_access.py
  • frontends/aiq_api/src/aiq_api/jobs/access.py
  • skills/aiq-research/SKILL.md
  • src/aiq_agent/agents/chat_researcher/register.py
{deploy/**,configs/**}

⚙️ CodeRabbit configuration file

{deploy/**,configs/**}: Review deployment and config changes for secret separation, safe defaults, local-vs-production behavior, Helm and
Docker portability, and documentation parity. Flag committed credentials, environment-specific NVIDIA internals in
public defaults, and changes that make examples diverge from CI-tested paths.

Files:

  • deploy/compose/init-db.sql
**/*.py

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Run ruff check and ruff format validation for Python code changes

**/*.py: Python code must be linted and formatted with Ruff using line length 120, target Python 3.11, rule sets E,F,W,I,PL,UP, and isort force-single-line configuration
Never commit secrets, tokens, or environment-specific hostnames; use environment variables and SecretStr instead, resolving API keys at runtime
Never print or log secret values, including in tool output or error messages
Missing-secret paths must degrade gracefully (stub/skip), not crash or leak
Do not hand-reformat unrelated code when making changes; match the existing import and formatting style

Files:

  • skills/aiq-research/scripts/aiq.py
  • tests/aiq_agent/agents/chat_researcher/test_register_helpers.py
  • frontends/aiq_api/tests/test_submit_collision.py
  • frontends/aiq_api/src/aiq_api/jobs/submit.py
  • frontends/aiq_api/tests/test_job_access.py
  • frontends/aiq_api/src/aiq_api/jobs/access.py
  • src/aiq_agent/agents/chat_researcher/register.py
skills/aiq-research/**/*.py

📄 CodeRabbit inference engine (skills/aiq-research/SKILL.md)

skills/aiq-research/**/*.py: Python version requirement: Python 3.11+ available as python3 for running AI-Q research scripts
Use the AIQ helper script at scripts/aiq.py for all AI-Q research queries instead of direct API calls
Always run health check before sending research requests to verify backend availability
Use AIQ_CONVERSATION_ID environment variable as a stable identifier to pin one conversation for the entire sequence of original research and follow-up queries
Poll asynchronous deep research jobs using research_poll when AI-Q returns a job_id instead of inline results
Stop on failed jobs and show returned errors without automatic retry
Use non-blocking or background execution when polling asynchronous jobs, and request explicit user approval if escalated permissions are needed
Do not truncate citations or source URLs from returned reports in any output or transformation
Report backend failures and HTTP 500 errors instead of fabricating research answers when the backend is unavailable or lacks async agents

Files:

  • skills/aiq-research/scripts/aiq.py
skills/aiq-research/**/*.{py,sh}

📄 CodeRabbit inference engine (skills/aiq-research/SKILL.md)

skills/aiq-research/**/*.{py,sh}: Do not include credentials, cookies, bearer tokens, or secret values in query text sent to AI-Q backend
Preserve and present all citations and source URLs from returned AI-Q reports intact
Generate a unique conversation ID using python3 -c "import uuid;print(uuid.uuid4())" and export it as AIQ_CONVERSATION_ID before running research and follow-up queries
State the exact AI-Q backend URL before sending user queries and confirm non-local URLs are trusted
Export a stable conversation ID once and reuse it for the original research and every follow-up without passing a report ID
Treat returned reports as potentially sensitive if the backend uses private data sources

Files:

  • skills/aiq-research/scripts/aiq.py
skills/aiq-research/**

⚙️ CodeRabbit configuration file

skills/aiq-research/**: ---
name: aiq-research
description: |
Use when asked to run deep research or AI-Q research through a reachable NVIDIA AI-Q Blueprint backend.
license: Apache-2.0
permissions:
env:
- AIQ_SERVER_URL
network:
- http://localhost:8000
compatibility: |
Designed for Claude Code, OpenCode, Codex, and Agent Skills-compatible tools. Requires Python 3.11+ and network
access to a running local AI-Q Blueprint server at http://localhost:8000 by default. Non-local backends must be
explicitly trusted by the user and granted by the host tool outside this public skill.
metadata:
version: "2.1.0"
author: "NVIDIA AI-Q Blueprint Team aiq-blueprint@nvidia.com"
github-url: "https://github.com/NVIDIA-AI-Blueprints/aiq"
tags:
- nvidia
- aiq
- blueprint
- deep-research
- research-agents
- agent-skills
languages:
- python
- bash
domain: "research-agents"
allowed-tools: Read Bash

AIQ Research Skill

Purpose

Use this skill to call a locally running NVIDIA AI-Q Blueprint server through the helper script at
scripts/aiq.py.

Use this skill for research-shaped requests, including:

  • "deep research on ..."
  • "AIQ research ..."
  • "research ..."
  • "use AI-Q to answer ..."
  • "ask AI-Q about ..."

Do not use this skill for install, deploy, start, stop, UI, CLI, Docker, Helm, or troubleshooting requests. Those
belong to aiq-deploy.

Prerequisites

Users need:

  • Python 3.11+ available as python3.
  • A reachable local or self-hosted AI-Q Blueprint backend.
  • AIQ_SERVER_URL set when the backend is not running at http://localhost:8000; non-local values must be trusted by
    the user before any query is sent.
  • A backend configured with authentication disabled for this public helper, or a separate authenticated AI-Q skill for
    authenticated environments.
  • Network access from the local machine to the AI-Q backend URL.
  • Credentials configured in the backend environment, not in this skill. Thi...

Files:

  • skills/aiq-research/scripts/aiq.py
  • skills/aiq-research/SKILL.md
{skills/**,.agents/skills/**,.claude/skills/**,.github/skill-eval/**}

⚙️ CodeRabbit configuration file

{skills/**,.agents/skills/**,.claude/skills/**,.github/skill-eval/**}: Review Agent Skill and skill-eval changes for valid skill metadata, deterministic eval specs, safe handling of
credentials, and clear generated-output boundaries. Do not flag SKILL.md files for missing SPDX headers when the
entrypoint intentionally starts with YAML frontmatter.

Files:

  • skills/aiq-research/scripts/aiq.py
  • skills/aiq-research/SKILL.md
**/*test*.py

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Run pytest for all behavior changes in Python code

Files:

  • tests/aiq_agent/agents/chat_researcher/test_register_helpers.py
  • frontends/aiq_api/tests/test_submit_collision.py
  • frontends/aiq_api/tests/test_job_access.py
{src/aiq_agent/fastapi_extensions/**,frontends/aiq_api/src/aiq_api/**}

⚙️ CodeRabbit configuration file

{src/aiq_agent/fastapi_extensions/**,frontends/aiq_api/src/aiq_api/**}: Treat API, auth, and job-runner changes as externally visible contracts. Check authorization boundaries,
request tracing, async job lifecycle, websocket reconnect behavior, error responses, and cross-user data isolation.
Require tests for route behavior, access decisions, and job state transitions when those surfaces change.

Files:

  • frontends/aiq_api/src/aiq_api/jobs/submit.py
  • frontends/aiq_api/src/aiq_api/jobs/access.py
docs/source/**/*

📄 CodeRabbit inference engine (AGENTS.md)

Update the docs under docs/source/ when behavior, configuration, or workflows change

Files:

  • docs/source/integration/rest-api.md
{docs/**,README.md,CONTRIBUTING.md,SECURITY.md,CODE-OF-CONDUCT.md}

⚙️ CodeRabbit configuration file

{docs/**,README.md,CONTRIBUTING.md,SECURITY.md,CODE-OF-CONDUCT.md}: Review documentation for command accuracy, branch-name consistency, current CI and copy-pr-bot behavior, public
vs internal boundary clarity, stale examples, and links that no longer match the repository layout.

Files:

  • docs/source/integration/rest-api.md
src/aiq_agent/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

src/aiq_agent/**/*.py: Respect authenticated data sources by honoring requires_auth, per-user token pass-through, and backend token validators; apply owner guardrails before loading protected report or artifact context into an agent
Do not weaken or bypass AuthMiddleware, validators, or auth gating without a prior design discussion

Files:

  • src/aiq_agent/agents/chat_researcher/register.py
src/aiq_agent/agents/**/*

⚙️ CodeRabbit configuration file

src/aiq_agent/agents/**/*: Review agent changes for research workflow correctness, graph state transitions, prompt/tool contracts,
HITL behavior, and failure handling. Flag changes that weaken source attribution, report generation,
async cancellation, checkpointing, or data-source selection without focused tests and docs.

Files:

  • src/aiq_agent/agents/chat_researcher/register.py
🧠 Learnings (2)
📓 Common learnings
Learnt from: CR
Repo: NVIDIA-AI-Blueprints/aiq

Timestamp: 2026-06-24T00:04:10.415Z
Learning: Do not use this skill for install, deploy, start, stop, UI, CLI, Docker, or Helm requests; use `aiq-deploy` skill instead
📚 Learning: 2026-06-14T17:49:00.640Z
Learnt from: torkian
Repo: NVIDIA-AI-Blueprints/aiq PR: 273
File: frontends/aiq_api/tests/test_sse_reconnect_cursor.py:384-401
Timestamp: 2026-06-14T17:49:00.640Z
Learning: When using `unittest.mock.patch` for code that imports dependencies inside functions/generators (e.g., inside `aiq_api.routes.jobs`), don’t patch via an attribute that doesn’t exist on the consuming module. If the generator does `from ..jobs.event_store import EventStore` inside the generator body, then `aiq_api.routes.jobs` will not have an `EventStore` attribute; patch the source class/method in its defining module instead (e.g., `aiq_api.jobs.event_store.EventStore.get_events_async`). Patching `aiq_api.routes.jobs.EventStore...` would raise `AttributeError` because that symbol is not present at module scope.

Applied to files:

  • frontends/aiq_api/tests/test_submit_collision.py
  • frontends/aiq_api/tests/test_job_access.py
🪛 LanguageTool
skills/aiq-research/SKILL.md

[style] ~162-~162: You have already used this phrasing in nearby sentences. Consider replacing it to add variety to your writing.
Context: ...r a report is presented, the user often wants to ask about it, revise it, or go deeper. ...

(REP_WANT_TO_VB)

🪛 SQLFluff (4.2.2)
deploy/compose/init-db.sql

[error] 68-68: CREATE INDEX should use CONCURRENTLY to avoid locking the table during the build.

(PG01)


[error] 70-70: CREATE INDEX should use CONCURRENTLY to avoid locking the table during the build.

(PG01)

🔇 Additional comments (9)
docs/source/integration/rest-api.md (1)

149-163: LGTM!

src/aiq_agent/agents/chat_researcher/register.py (1)

108-139: LGTM!

Also applies to: 582-594, 607-607

tests/aiq_agent/agents/chat_researcher/test_register_helpers.py (1)

486-538: LGTM!

deploy/compose/init-db.sql (1)

55-70: LGTM!

frontends/aiq_api/src/aiq_api/jobs/access.py (1)

21-21: LGTM!

Also applies to: 33-44, 81-87, 241-267, 268-308

frontends/aiq_api/tests/test_job_access.py (1)

51-88: LGTM!

Also applies to: 103-172, 199-207, 255-271

frontends/aiq_api/src/aiq_api/jobs/submit.py (2)

79-86: LGTM!

Also applies to: 144-201, 313-320


298-306: 🗄️ Data Integrity & Integration

Narrow the IntegrityError catch to verify it's a job-id collision constraint.

The current catch assumes every IntegrityError from job_store.submit_job() is a duplicate job_id, but this is not verified against NAT's implementation. If other database constraints exist on the job_info table (or related tables), skipping rollback would incorrectly leave an orphaned row.

Inspect the exception to confirm the constraint name before raising JobIdConflictError:

except IntegrityError as e:
    # Verify this is a job_id collision, not another constraint violation.
    # NAT's _create_job inserts job_info first, so collision fails before 
    # any state of OURS is created. The colliding job belongs to someone else —
    # we must NOT roll back, which unconditionally deletes that job's info/events/access rows.
    if e.orig and "job_id" in str(e.orig).lower():
        logger.info("Rejected colliding job_id %s on async submit", resolved_job_id)
        raise JobIdConflictError(f"Job already exists: {resolved_job_id}") from e
    # Not a collision; fall through to general exception handler for rollback
    raise

Alternatively, document that NAT constrains job_info.job_id as the only unique constraint on that table before submission completes.

frontends/aiq_api/tests/test_submit_collision.py (1)

122-144: LGTM!

Comment thread frontends/aiq_api/src/aiq_api/jobs/access.py
@cdgamarose-nv

Copy link
Copy Markdown
Collaborator

Both report ask and report edit inline the full parent report into an LLM prompt. Since we don't have token budget or truncation, I'm worried that large completed reports can exceed the model context window or crowd out the user’s actual question/edit instruction. Not sure what the right thing to do here is. For first pass, we could probably add a size/token guard here? But wondering if it would be possible to use a simple agent with filesystem tools rather than individual LLM calls?

@AjayThorve

Copy link
Copy Markdown
Member Author

Both report ask and report edit inline the full parent report into an LLM prompt. Since we don't have token budget or truncation, I'm worried that large completed reports can exceed the model context window or crowd out the user’s actual question/edit instruction. Not sure what the right thing to do here is. For first pass, we could probably add a size/token guard here? But wondering if it would be possible to use a simple agent with filesystem tools rather than individual LLM calls?

@cdgamarose-nv Good callout — I dug into this and it breaks down by path:

  1. Delta (re-research) runs on the deepagents stack, which already has summarization middleware wired (create_summarization_middleware in deep_researcher/factory.py), so its context gets compacted mid-loop — that path is covered.

  2. Ask and edit are single-shot LLM calls, so they do inline the full report. A summarizer can't help there: summarization happens between agent steps, but a single oversized prompt fails before there's any step boundary to compact at.

On the actual risk: both run on large context llm (gpt-oss-120b, large context window), and if a report ever did exceed the window, report_ask_node and report_edit_node each wrap the call in try/except and degrade to a "please try again" message — no crash, no data loss. So today it's a graceful tail-case, not a correctness bug.

On the filesystem-tools idea — that's the right long-term shape, and the deep_researcher already does exactly that (deepagents read_file/grep over the report seeded into its virtual FS). The subtlety: a lazy read_file only defers loading — once it returns, the whole report is in context. What actually bounds the window is selective reads (grep/offset) or truncation. So the proper version for ask is a small read/grep agent that pulls only the relevant sections — a real rearchitecture, and the synchronous CLI path has no shared FS so it'd need a temp-file shim.

My plan is to defer the hard guard for this PR (tail risk + graceful failure + large-context model) and track the read/grep-agent version as a follow-up

@AjayThorve
AjayThorve force-pushed the ajay/report-follow-up branch from e51d225 to 99a9c0e Compare June 24, 2026 07:01
AjayThorve added a commit to AjayThorve/aiq-research-assistant that referenced this pull request Jun 24, 2026
Resolve the 13 actionable CodeRabbit comments on PR NVIDIA-AI-Blueprints#271:

- routes/jobs.py: reject whitespace-only `input` on report-edit and submit
  requests (422) via a field validator; map post-auth RuntimeError to 503
  (availability) instead of 403, without echoing internal detail.
- runner.py: build job output so the canonical `report` can never be
  overwritten by a stray `report` key in output_metadata.
- report_context.py: tolerate a transient event-log fetch failure when the
  report is already available (fall back to report-only sources).
- register.py: bound the report-ask LLM call with a timeout so a stalled
  provider degrades gracefully; make parent-report context enrichment optional
  for delta research (don't abort the deep job if context lookup fails).
- intent_classifier.py: normalize string booleans for use_parent_report_context
  ("false" must not be truthy).
- utils.py: preserve an explicit empty data_sources list ([] = no data-source
  tools) instead of falling through to other sources.
- agent.py: log error type, not raw exception text, in report follow-up paths.
- report_rewriter/agent.py: dispatch the final report to all registered
  callbacks (remove early break).
- ci/markdown-link-check-config.json: narrow the FastAPI/Dask ignores to the
  exact referenced URLs instead of whole domains.
- docs: document the report-edit 500 case.
- tests: assert authorization on the GET report route; add coverage for blank
  input (422), string-boolean normalization, and empty data_sources
  preservation.

Backend suites (aiq_api + aiq_agent) and UI specs pass; ruff and tsc clean.
Verified live: blank input -> 422, report ask answers, job_id collision -> 409.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Ajay Thorve <athorve@nvidia.com>
@AjayThorve
AjayThorve force-pushed the ajay/report-follow-up branch from 70d66f1 to c82d9db Compare June 24, 2026 23:40
Comment thread frontends/aiq_api/src/aiq_api/jobs/report_context.py Outdated
Comment thread frontends/aiq_api/src/aiq_api/jobs/report_context.py Outdated
Comment thread src/aiq_agent/agents/chat_researcher/prompts/intent_classification.j2 Outdated
Comment thread src/aiq_agent/agents/report_rewriter/agent.py Outdated
Comment thread frontends/aiq_api/src/aiq_api/jobs/report_context.py
Comment thread src/aiq_agent/agents/chat_researcher/nodes/intent_classifier.py
@AjayThorve
AjayThorve force-pushed the ajay/report-follow-up branch from e505160 to fa84388 Compare June 27, 2026 00:38
AjayThorve added a commit to AjayThorve/aiq-research-assistant that referenced this pull request Jun 27, 2026
Resolve the 13 actionable CodeRabbit comments on PR NVIDIA-AI-Blueprints#271:

- routes/jobs.py: reject whitespace-only `input` on report-edit and submit
  requests (422) via a field validator; map post-auth RuntimeError to 503
  (availability) instead of 403, without echoing internal detail.
- runner.py: build job output so the canonical `report` can never be
  overwritten by a stray `report` key in output_metadata.
- report_context.py: tolerate a transient event-log fetch failure when the
  report is already available (fall back to report-only sources).
- register.py: bound the report-ask LLM call with a timeout so a stalled
  provider degrades gracefully; make parent-report context enrichment optional
  for delta research (don't abort the deep job if context lookup fails).
- intent_classifier.py: normalize string booleans for use_parent_report_context
  ("false" must not be truthy).
- utils.py: preserve an explicit empty data_sources list ([] = no data-source
  tools) instead of falling through to other sources.
- agent.py: log error type, not raw exception text, in report follow-up paths.
- report_rewriter/agent.py: dispatch the final report to all registered
  callbacks (remove early break).
- ci/markdown-link-check-config.json: narrow the FastAPI/Dask ignores to the
  exact referenced URLs instead of whole domains.
- docs: document the report-edit 500 case.
- tests: assert authorization on the GET report route; add coverage for blank
  input (422), string-boolean normalization, and empty data_sources
  preservation.

Backend suites (aiq_api + aiq_agent) and UI specs pass; ruff and tsc clean.
Verified live: blank input -> 422, report ask answers, job_id collision -> 409.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Ajay Thorve <athorve@nvidia.com>
@AjayThorve
AjayThorve force-pushed the ajay/report-follow-up branch from fa84388 to 523602d Compare June 27, 2026 03:24
@AjayThorve

Copy link
Copy Markdown
Member Author

/nvskills-ci

@tanleach

Copy link
Copy Markdown
Collaborator

on

response = await llm.ainvoke(
do you need to run through the citations verification/sanitization again?

@cdgamarose-nv cdgamarose-nv left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Left some cosmetic comments. Everything worked functionally for me! Good to go in.

Comment thread src/aiq_agent/agents/report_rewriter/agent.py Outdated
Comment thread src/aiq_agent/agents/report_rewriter/agent.py Outdated
Comment thread src/aiq_agent/agents/chat_researcher/utils.py Outdated
Comment thread src/aiq_agent/agents/chat_researcher/models/intent.py
Signed-off-by: Ajay Thorve <athorve@nvidia.com>
Signed-off-by: Ajay Thorve <athorve@nvidia.com>
Signed-off-by: Ajay Thorve <athorve@nvidia.com>
Signed-off-by: Ajay Thorve <athorve@nvidia.com>
Signed-off-by: Ajay Thorve <athorve@nvidia.com>
Signed-off-by: Ajay Thorve <athorve@nvidia.com>
Signed-off-by: Ajay Thorve <athorve@nvidia.com>
Signed-off-by: Ajay Thorve <athorve@nvidia.com>
…iple YAML files

Signed-off-by: Ajay Thorve <athorve@nvidia.com>
Signed-off-by: Ajay Thorve <athorve@nvidia.com>
Signed-off-by: Ajay Thorve <athorve@nvidia.com>
Signed-off-by: Ajay Thorve <athorve@nvidia.com>
Signed-off-by: Ajay Thorve <athorve@nvidia.com>
@AjayThorve
AjayThorve force-pushed the ajay/report-follow-up branch from 4e709d6 to 15bef02 Compare July 1, 2026 20:04
@cdgamarose-nv
cdgamarose-nv self-requested a review July 1, 2026 20:07

@tanleach tanleach left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM

Signed-off-by: Ajay Thorve <athorve@nvidia.com>
@AjayThorve
AjayThorve merged commit 09da539 into NVIDIA-AI-Blueprints:develop Jul 1, 2026
10 checks passed
peterychang pushed a commit to peterychang/aiq that referenced this pull request Jul 6, 2026
Resolve the 13 actionable CodeRabbit comments on PR NVIDIA-AI-Blueprints#271:

- routes/jobs.py: reject whitespace-only `input` on report-edit and submit
  requests (422) via a field validator; map post-auth RuntimeError to 503
  (availability) instead of 403, without echoing internal detail.
- runner.py: build job output so the canonical `report` can never be
  overwritten by a stray `report` key in output_metadata.
- report_context.py: tolerate a transient event-log fetch failure when the
  report is already available (fall back to report-only sources).
- register.py: bound the report-ask LLM call with a timeout so a stalled
  provider degrades gracefully; make parent-report context enrichment optional
  for delta research (don't abort the deep job if context lookup fails).
- intent_classifier.py: normalize string booleans for use_parent_report_context
  ("false" must not be truthy).
- utils.py: preserve an explicit empty data_sources list ([] = no data-source
  tools) instead of falling through to other sources.
- agent.py: log error type, not raw exception text, in report follow-up paths.
- report_rewriter/agent.py: dispatch the final report to all registered
  callbacks (remove early break).
- ci/markdown-link-check-config.json: narrow the FastAPI/Dask ignores to the
  exact referenced URLs instead of whole domains.
- docs: document the report-edit 500 case.
- tests: assert authorization on the GET report route; add coverage for blank
  input (422), string-boolean normalization, and empty data_sources
  preservation.

Backend suites (aiq_api + aiq_agent) and UI specs pass; ruff and tsc clean.
Verified live: blank input -> 422, report ask answers, job_id collision -> 409.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Ajay Thorve <athorve@nvidia.com>
peterychang pushed a commit to peterychang/aiq that referenced this pull request Jul 6, 2026
Resolve the 13 actionable CodeRabbit comments on PR NVIDIA-AI-Blueprints#271:

- routes/jobs.py: reject whitespace-only `input` on report-edit and submit
  requests (422) via a field validator; map post-auth RuntimeError to 503
  (availability) instead of 403, without echoing internal detail.
- runner.py: build job output so the canonical `report` can never be
  overwritten by a stray `report` key in output_metadata.
- report_context.py: tolerate a transient event-log fetch failure when the
  report is already available (fall back to report-only sources).
- register.py: bound the report-ask LLM call with a timeout so a stalled
  provider degrades gracefully; make parent-report context enrichment optional
  for delta research (don't abort the deep job if context lookup fails).
- intent_classifier.py: normalize string booleans for use_parent_report_context
  ("false" must not be truthy).
- utils.py: preserve an explicit empty data_sources list ([] = no data-source
  tools) instead of falling through to other sources.
- agent.py: log error type, not raw exception text, in report follow-up paths.
- report_rewriter/agent.py: dispatch the final report to all registered
  callbacks (remove early break).
- ci/markdown-link-check-config.json: narrow the FastAPI/Dask ignores to the
  exact referenced URLs instead of whole domains.
- docs: document the report-edit 500 case.
- tests: assert authorization on the GET report route; add coverage for blank
  input (422), string-boolean normalization, and empty data_sources
  preservation.

Backend suites (aiq_api + aiq_agent) and UI specs pass; ruff and tsc clean.
Verified live: blank input -> 422, report ask answers, job_id collision -> 409.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Ajay Thorve <athorve@nvidia.com>
@AjayThorve AjayThorve added this to the v2.2 milestone Jul 7, 2026
@coderabbitai coderabbitai Bot mentioned this pull request Aug 14, 2026
6 tasks
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.

3 participants