Skip to content

Step 5 Report Agent chat: backend returns dict, frontend expects string — TypeError: content.replace is not a function #39

@ashap1-lgtm

Description

@ashap1-lgtm

Bug

Affects main at commit 313fe64, confirmed April 20, 2026, when running against a completed Step 4 report with the Step 5 "Enter Deep Interaction" chat panel.

Sending any query through the Step 5 Report Agent chat triggers a frontend TypeError on render. The backend completes the request successfully (HTTP 200 with a well-formed response body) but no message ever appears in the chat panel.

Stack trace

TypeError: content.replace is not a function
    at renderMarkdown (Step5Interaction.vue:~152)

The error fires inside the renderMarkdown helper when it attempts content.replace(...) on a non-string input.

Root cause

There is a contract mismatch between the /api/report/chat endpoint and the chat panel that renders its response.

Backendbackend/app/api/report.py, the chat_with_report_agent handler:

result = agent.chat(message=message, chat_history=chat_history)
return jsonify({"success": True, "data": {"response": result, "simulation_id": simulation_id}})

ReportAgent.chat() in backend/app/services/report_agent.py returns a dict, not a string:

{
    "response": "...",        # the actual text
    "tool_calls": [...],
    "sources": [...]
}

So data.response in the JSON payload is the entire dict object, not a string.

Frontendfrontend/src/components/Step5Interaction.vue, in sendToReportAgent:

content: res.data.response || res.data.answer || 'No response',

The dict is truthy, gets stored as msg.content, then renderMarkdown(msg.content) on the chat-message template call site calls .replace() on an object and throws.

This is LLM-agnostic — reproducible with any backend completion model (Ollama, OpenAI-compatible, etc.), because the shape mismatch is entirely in how ReportAgent.chat()'s return value is packaged for the HTTP response.

Reproduction

Step 1. Complete any simulation through Step 4 so a report artifact exists.

Step 2. Open the report and click Enter Deep Interaction to open the Step 5 chat panel.

Step 3. Send any query (e.g., "Summarize the key findings").

Step 4. Observe: backend returns HTTP 200 with a valid body, browser DevTools Network tab shows the successful response, but the chat panel stays blank and the browser console shows the TypeError above.

Suggested fix

Minimal backend change in backend/app/api/report.py, chat_with_report_agent handler — extract the string out of the dict before returning, and surface tool_calls and sources as sibling fields so future frontend work can render them without a second backend change:

result = agent.chat(message=message, chat_history=chat_history)
return jsonify({"success": True, "data": {
    "response": result.get("response", ""),
    "tool_calls": result.get("tool_calls", []),
    "sources": result.get("sources", []),
    "simulation_id": simulation_id
}})

This restores the documented frontend contract with no change to ReportAgent behavior.

Alternatively, Step5Interaction.vue's sendToReportAgent could be hardened to accept either shape (typeof x === 'string' ? x : x?.response), though the backend fix is the cleaner of the two.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions