Repository navigation
Description
Component
Python SDK
Summary
AWSBedrockLLM._parse_response returns "" for every legacy Amazon (Titan) text response, so generate_response() silently yields an empty string and Memory.add() stores nothing.
For provider == "amazon" and a model id that does not contain nova, _generate_standard() sends a Titan-shaped request body ({"inputText": ..., "textGenerationConfig": {...}}, mem0/llms/aws_bedrock.py:291-296) through invoke_model, but the paired parse branch reads response_json.get("completion", ""). Titan's InvokeModel response has no top-level completion key, so the branch hits its default and returns "" for a perfectly valid response. Every sibling branch reads its own provider's field (meta: generation, mistral: outputs[0].text, cohere: generations[0].text, ai21: completions[0].data.text, anthropic: content[0].text), so only the legacy amazon branch is unmapped to its schema.
Steps to Reproduce
/tmp/repro/mem0_titan_parse.py — mock boto3 client, feed a response shaped exactly as AWS documents for Titan Text, and call generate_response():
import io, json
from unittest.mock import MagicMock, patch
from mem0.configs.llms.aws_bedrock import AWSBedrockConfig
from mem0.llms.aws_bedrock import AWSBedrockLLM
runtime, control = MagicMock(), MagicMock()
control.list_foundation_models.return_value = {"modelSummaries": []}
with patch("mem0.llms.aws_bedrock.boto3") as b3:
b3.client.side_effect = lambda s, **k: runtime if s == "bedrock-runtime" else control
# AWS docs: Titan Text InvokeModel response = inputTextTokenCount + results[].outputText
titan_body = {"inputTextTokenCount": 3,
"results": [{"tokenCount": 5, "outputText": "Hi from Titan",
"completionReason": "FINISH"}]}
runtime.invoke_model.return_value = {"body": io.BytesIO(json.dumps(titan_body).encode())}
llm = AWSBedrockLLM(AWSBedrockConfig(model="amazon.titan-text-express-v1"))
out = llm.generate_response([{"role": "system", "content": "s"},
{"role": "user", "content": "Hello"}])
print("request body :", runtime.invoke_model.call_args[1]["body"])
print("response got :", repr(out), "(expected 'Hi from Titan')")No AWS credentials or network call are involved; the mock returns the documented response shape.
Expected Behavior
generate_response() should return "Hi from Titan" (Titan's results[0].outputText), as the sent body is Titan's request schema.
Concrete basis: AWS Bedrock "Amazon Titan Text models" doc (docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-titan-text.html), Titan Text InvokeModel response:
{ "inputTextTokenCount": int,
"results": [{ "tokenCount": int, "outputText": "...", "completionReason": "string" }] }outputText is documented as "The text in the response"; there is no top-level completion field anywhere in the Titan request or response, and the doc's own sample reads result['outputText']. The module's request branch at mem0/llms/aws_bedrock.py:291-296 emits exactly Titan's request schema, so its paired response schema is Titan's. The repo's own generic fallback parser (line 419+) also reads text/generation-style fields, and every sibling provider branch reads its own field — results[0].outputText is the field this branch should read.
Actual Behavior
Verifier 1:
extract_provider -> amazon
sent body -> {"inputText": "\n\nHuman: \n\nSystem: sys\n\nUser: Hello\n\nAssistant:", "textGenerationConfig": {"maxTokenCount": 2000, "temperature": 0.1}}
provider -> amazon
parsed -> ''
EXPECTED -> 'Hi from Titan'
_parse_response-> ''
completion-key -> 'x'
Verifier 2, across Titan ids:
titan-express provider=amazon -> ''
titan-lite provider=amazon -> ''
titan-premier provider=amazon -> ''
(after local fix: -> 'Hi from Titan' / 'Lite out' / 'Premier out')
Re-run on c7ee362aff94a369af70f13f2b4f853f6793ff4c (this report) — same result:
request body : {"inputText": "\n\nHuman: \n\nSystem: s\n\nUser: Hello\n\nAssistant:", "textGenerationConfig": {"maxTokenCount": 2000, "temperature": 0.1}}
response got : '' (expected 'Hi from Titan')
Downstream effect: Memory.add() calls llm.generate_response(...) (mem0/memory/main.py:956); an empty string makes extract_json yield nothing and no memories are stored, with no error raised.
Environment
- mem0 version: 2.0.20 (
pyproject.toml), commitc7ee362aff94a369af70f13f2b4f853f6793ff4c(also currentmain) - Python 3.12.14
- macOS, Darwin 25.6.0
- Trigger model:
amazon.titan-text-express-v1(anyamazon.*id withoutnova)
Root Cause
mem0/llms/aws_bedrock.py:409-410 — the legacy Amazon branch of _parse_response reads a field Titan does not return:
else:
# Legacy Amazon models
return response_json.get("completion", "")The request built for the same models at mem0/llms/aws_bedrock.py:291-296 is Titan's schema (inputText / textGenerationConfig), whose response carries the text under results[0].outputText. The lookup therefore always misses and the default "" is returned. The "nova" in model branch above (line 402) is unaffected — it parses content[0].text.
How You Verified This
What I Ran
/tmp/repro/mem0_titan_parse.py (above) against a checkout of c7ee362aff94a369af70f13f2b4f853f6793ff4c, Python 3.12.14, with boto3 patched so the Bedrock runtime client returns the response body AWS documents for Titan Text. Separately reproduced by a second verifier across amazon.titan-text-express-v1, amazon.titan-text-lite-v1 and amazon.titan-text-premier-v1.
What I Saw
The verbatim outputs in Actual Behavior: _parse_response returns '' while the request body sent is Titan's schema. After reading results[0].outputText in the same branch locally, the same runs returned 'Hi from Titan' / 'Lite out' / 'Premier out'.
Why This Is a Bug
The parser must read the response shape its own request shape elicits. AWS documents Titan Text's InvokeModel response as {"inputTextTokenCount": int, "results": [{"tokenCount": int, "outputText": "...", "completionReason": "..."}]} with no top-level completion key, and the module's request branch at line 291-296 emits exactly that model family's request schema. Every other provider branch in _parse_response reads its own response field; the legacy amazon branch is the only one reading a field its provider never returns, so it can never return a non-empty string.
What I Ruled Out
- Nova path —
amazon.*nova*models take theconversebranch (line 402 / line 622) and parsecontent[0].text; only non-Novaamazon.*is affected. - Transport / response decoding —
response_bodyis valid JSON and parses fine; the failure is the field lookup, notjson.loads. - Credentials / region / API error — reproduced with a mocked client and no network; the request body is a valid Titan body.
- Other provider branches —
meta,mistral,cohere,ai21andanthropicall read their documented fields and are not involved. - Tool-calling path — the repro uses the tools-less path (
_generate_standard, line 562) thatMemory.add()takes; this is not related to the tool-call issues filed separately.
Related
Searched existing issues/PRs for titan, bedrock, outputText, _parse_response, titan-text: no existing report for this parse failure. Closest open Bedrock LLM issues (different code paths, not duplicates): #6548 (parse-failure sentinel string), #6556 (tool responses omit content), #6563 (malformed Converse tool requests for amazon/cohere), #6368 (Anthropic reasoningContent ordering), #5913 (closed; inference-profile ARN provider extraction).
Happy to open a PR reading results[0].outputText in the legacy amazon branch, keeping the completion lookup as a last-resort fallback for any other legacy Amazon shape — say the word if you'd like it.
AI Assistance
AI helped find and write this up; the reproduction above was run and confirmed by me (and independently by a second verifier) before filing.
Activity
debojyoti-dev commentedon Sep 15, 2026
I’d like to take this issue up.
I’ll investigate the
AWSBedrockLLMrequest/response handling for legacy Amazon Titan models, specifically the mismatch between the TitanInvokeModelresponse schema and theamazonbranch in_parse_response.The reported root cause appears to be isolated to the legacy Amazon path:
_generate_standard()constructs the Titan request usinginputTextandtextGenerationConfig, while_parse_response()currently expects a top-levelcompletionfield. Titan instead returns generated text underresults[0].outputText, causing valid responses to be converted into an empty string.I’ll reproduce the issue with mocked Bedrock responses for the affected Titan model variants, then implement the smallest compatible fix in
_parse_response. I’ll also add regression coverage to verify thatgenerate_response()correctly extractsresults[0].outputText, while ensuring the existing Nova/Converse path and other Bedrock provider parsers remain unaffected.I’ll run the relevant AWS Bedrock tests and the new regression case before opening a PR.
@BlueX888
O96a commentedon Sep 15, 2026
Confirmed — the amazon branch reads
completion, which is the Anthropic-style field and Titan never returns it. Titan text models put the text inresults[0].outputTextin the InvokeModel body, so the non-nova path should parse that:And the silent
''fallback is what let this hide — an unexpected body shape should raise, not parse to empty, or every Titan call stores nothing and nobody notices.Ritiky23 commentedon Sep 15, 2026
Hi maintainers! I've opened PR #7341 addressing this issue with full unit test coverage for Titan text response parsing and fallbacks.
Could you please review and label this issue
acceptedso the PR is queued and reopened? Thanks!aniruddhaadak80 commentedon Sep 16, 2026
Thanks for the detailed repro. I have a minimal fix ready on branch fix/bedrock-titan-outputtext-7336 (reads Titan results[0].outputText with completion fallback, plus regression tests). Could a maintainer please add the bug and accepted labels per CONTRIBUTING PR Gate so I can open the PR with Closes #7336? No live AWS used, all mocked.
aniruddhaadak80 commentedon Sep 18, 2026
Update: the competing #7341 just closed unmerged, so the path is clear. My fix branch for the Bedrock Titan outputText issue is ready - requesting the accepted label so it can go through the PR gate. CLA already signed.
Ritiky23 commentedon Sep 20, 2026
Just to clarify - PR #7341 was automatically closed by the
pr-gatebot solely because this issue is awaiting theacceptedtriage label (per Mem0's CONTRIBUTING policy), not due to review rejection.PR #7341 contains the complete fix for reading
results[0].outputTextwith legacy fallback and comprehensive unit tests, with CLA already signed.Could maintainers (@kartik-mem0 @whysosaket @Himanshu-Sangshetti ) please add the
acceptedlabel to this issue so #7341 automatically reopens for review? Thank you!blazingretinol commentedon Sep 21, 2026
Confirmed. The legacy Amazon Titan path reads a 'completion' field that Titan responses never include, so generate_response always returns an empty string. Affects anyone using Titan models through the Bedrock provider.
P1, fix PR under review.
Madin-H23 commentedon Oct 20, 2026
@blazingretinol — you confirmed this on 2026-09-21 as "P1, fix PR under review". One correction first: there is no pull request under review. All three earlier attempts — #7341, #7343 and #7389 — were closed by the
pr-gatebot within seconds of being opened, because this issue never received theacceptedlabel. None of them got a review comment, so the issue has been sitting without a fix for 35 days.A pull request is now open: #7691. It applies the fix @O96a described here (read
results[0].outputText, keepcompletionas a fallback) and adds the tests:_parse_responseand throughgenerate_response()with a mockedinvoke_model— the path the issue reports;completionkeeps returning it, a response with neither field still yields"", an emptyresultslist is handled, and the Nova branch still readscontent[0].text.I ran those tests against unmodified
mainfirst: the three Titan cases fail there (the response parses as""), and with the fix the wholetests/llms/test_aws_bedrock.pyfile passes (63 tests) withruff checkclean on the locked 0.16.0.@O96a — if the shape is still what you had in mind, a thumbs-up from you would help; the implementation follows the snippet you posted.
@blazingretinol, could you add the
acceptedlabel to this issue so #7691 reopens? The pull request reopens by itself once the label is there, and the reviewer then has a concrete P1 fix in front of them. I can also close mine in favour of #7341 or #7343 if you would rather revive one of those — the fix is the same in all of them.One administrative note: I still need to sign this repository's contributor CLA before the pull request can merge. I will take care of that on my side.