Skip to content

AWS Bedrock Kimi K2 Thinking ends abruptly due to incorrect tool call parsing #11409

Closed

Description

@nikita-tkachov

Description

AWS Bedrock with the new Kimi K2 Thinking model (https://aws.amazon.com/blogs/aws/amazon-bedrock-adds-fully-managed-open-weight-models/) seems to end the conversation abruptly. It looks like it sometimes parses tool calls as "text-delta" instead of an actual tool call, which shows up as text in my chat app.

Here is a sample log:

{"type":"start","messageId":"yhfl7aguy69p78kdaupavgr0"}{"type":"start","messageId":"zdeo0nspwe3nd4cv0n9pl4q9"}{"type":"start-step"}{"type":"reasoning-start","id":"1"}{"type":"reasoning-delta","id":"1","delta":" The user is asking if they have"}{"type":"reasoning-delta","id":"1","delta":" any \"cursor files\" in their repository. This likely refers to Cursor editor configuration files (like .cursorrules) or possibly files related to cursor/cursor-based operations in the code.\n\nLet me"}{"type":"reasoning-delta","id":"1","delta":" search for files with \""}{"type":"reasoning-delta","id":"1","delta":"cursor\" in the name or path."}{"type":"reasoning-end","id":"1"}{"type":"tool-input-start","toolCallId":"tooluse_OEppyvDpRkWByqqI-Ixz0A","toolName":"glob_search"}{"type":"tool-input-delta","toolCallId":"tooluse_OEppyvDpRkWByqqI-Ixz0A","inputTextDelta":"{\"pattern\": \"**/*cursor*\"}"}{"type":"tool-input-available","toolCallId":"tooluse_OEppyvDpRkWByqqI-Ixz0A","toolName":"glob_search","input":{"pattern":"**/*cursor*"}}{"type":"text-start","id":"4"}{"type":"text-delta","id":"4","delta":"{\"pattern\": "}{"type":"text-delta","id":"4","delta":"\"**/*cursor*\"}"}{"type":"text-end","id":"4"}{"type":"tool-output-available","toolCallId":"tooluse_OEppyvDpRkWByqqI-Ixz0A","output":{"files":["FILE1","FILE2"]}}{"type":"finish-step"}{"type":"start-step"}{"type":"reasoning-start","id":"1"}{"type":"reasoning-delta","id":"1","delta":" Yes, you have cursor files in your repo:\n1. FILE1\n2. FILE2\n\nLet me read the"}{"type":"reasoning-delta","id":"1","delta":" TypeScript file to see what it contains."}{"type":"reasoning-end","id":"1"}{"type":"text-start","id":"2"}{"type":"text-delta","id":"2","delta":"{\"target_file\": "}{"type":"text-delta","id":"2","delta":"\"FILE1\"}  "}{"type":"text-delta","id":"2","delta":"<|tool_calls_section_end|>"}{"type":"text-end","id":"2"}{"type":"finish-step"}{"type":"finish","finishReason":"stop"}

I also had to add a custom transform because every time it made a tool call, it would add the tool markers in the "text" field as well. Here's a sample:

   () =>
            new TransformStream({
              transform(chunk, controller) {
                if (chunk.type === "text-delta") {
                  // Remove Kimi K2 tool call markers
                  const cleanedText = chunk.text
                    .replace(/<\|tool_call_begin\|>/g, "")
                    .replace(/<\|tool_call_argument_begin\|>/g, "")
                    .replace(/<\|tool_call_end\|>/g, "")
                    .replace(/functions\.\w+:\d+/g, "")
                    .trim()

                  // Only enqueue if there's actual text left after cleaning
                  if (cleanedText) {
                    controller.enqueue({
                      ...chunk,
                      text: cleanedText,
                    })
                  }
                } else {
                  // Pass through all other chunk types unchanged
                  controller.enqueue(chunk)
                }
              },
            }),

Note: this only happens for some calls, sometimes it works correctly, and sometimes it doesn't.

I realize this model just came out, so if there's a temporary workaround to parse the tool calls correctly, that would be much appreciated!

AI SDK Version

Packages: "@ai-sdk/amazon-bedrock": "4.0.2", "ai": "6.0.2",

Code of Conduct

  • I agree to follow this project's Code of Conduct

Activity

  1. ghost added
    ai/providerrelated to a provider package. Must be assigned together with at least one `provider/*` label
    on Dec 23, 2025
  2. ghost added
    provider/amazon-bedrockIssues related to the @ai-sdk/amazon-bedrock provider
    on Dec 23, 2025
  3. aayush-kapoor commented on Dec 24, 2025

    @aayush-kapoor
    Collaborator

    hi! can you provide a small reproduction code for this issue?

  4. 12 remaining items

  5. lgrammel commented on Jul 22, 2026

    @lgrammel
    Collaborator

    Bug Reproduction

    Bug was reproduced.

    Branch: release-v5.0
    PR: successful reproduction pull request

    Details

    • Expected Kimi K2 Thinking to execute read_file as a structured tool call and return FOUND ISSUE_11409_CONTENT; the final live run instead emitted the intended call as visible text, skipped execution, and stopped early in 1 of 10 attempts.
    • examples/ai-functions/src/reproduction/amazon-bedrock-kimi-k2-thinking-tool-call.ts reproduces the intermittent user-visible failure against live Amazon Bedrock.
    • packages/amazon-bedrock/src/__fixtures__/issue-11409-kimi-k2-thinking-glob-search.chunks.txt records the preceding successful structured glob_search call.
    • packages/amazon-bedrock/src/__fixtures__/issue-11409-kimi-k2-thinking-read-file.chunks.txt records Bedrock returning <function=read_file> as text followed by stopReason: "end_turn" instead of a structured tool-use event.
    • packages/amazon-bedrock/src/issue-11409.test.ts replays the live fixtures and fails because read_file is absent, tool syntax is visible, and the final answer is missing.
    • examples/ai-functions/package.json and pnpm-lock.yaml provide the runnable reproduction workspace.
    • The target branch uses [email protected] and @ai-sdk/[email protected]; the reporter's exact [email protected] and @ai-sdk/[email protected] versions were not executed.

    Relevant Documentation

  6. lgrammel commented on Jul 22, 2026

    @lgrammel
    Collaborator

    Bugfix

    Created bugfix and opened PR: #17732

    Root Cause

    The Bedrock stream adapter treated every Converse text delta as user-visible content and trusted end_turn, with no recovery when Kimi serialized a known tool call into text. Recorded chunks confirmed the missing toolUse event and text-encoded read_file request.

    Summary

    Added Kimi-specific parsing for serialized tool calls, converted leaked thinking and tool syntax into structured stream events, suppressed duplicate serialized arguments, returned a tool-call finish reason, removed reproduction artifacts, and added a patch changeset.

    Testing

    Added streaming regression coverage for recovering a split text-encoded tool call and suppressing text duplicated after a structured tool call.

  7. lgrammel commented on Jul 22, 2026

    @lgrammel
    Collaborator

    Bugfix updated

    Updated PR: #17732

    Root Cause

    The adapter trusted Kimi text and reasoning deltas as displayable content and accepted end_turn even when they contained serialized tool calls. Issue samples, parser probes, and a live Bedrock response confirmed XML, native-marker, bare-JSON, and reasoning-content variants.

    Update

    Added bare-JSON inference for uniquely matching tool schemas, reasoning-content recovery, selective candidate buffering, incremental ordinary streaming, and key-order-independent duplicate detection.

  8. ai-sdk-factory commented on Jul 30, 2026

    @ai-sdk-factory
    Contributor

    Bug Reproduction

    Bug was reproduced.

    Branch: release-v5.0
    PR: successful reproduction pull request

    Details

    • Expected Kimi K2 Thinking to emit read_file as a structured tool call and return FOUND ISSUE_11409_CONTENT; the recorded Bedrock response instead emits <function=read_file> as visible text, returns end_turn, skips the tool, and omits the final answer.
    • Live Bedrock responses are preserved in packages/amazon-bedrock/src/__fixtures__/issue-11409-kimi-k2-thinking-glob-search.chunks.txt and packages/amazon-bedrock/src/__fixtures__/issue-11409-kimi-k2-thinking-read-file.chunks.txt.
    • packages/amazon-bedrock/src/issue-11409.test.ts replays those responses and asserts the final user-visible behavior, including structured tool execution, hidden tool syntax, and the required final answer.
    • packages/amazon-bedrock/src/bedrock-chat-language-model.ts forwards Converse text deltas as visible text and accepts end_turn without recovering Kimi's serialized tool call, placing a meaningful fix in the AI SDK Bedrock adapter.
    • examples/ai-functions/src/reproduction/amazon-bedrock-kimi-k2-thinking-tool-call.ts, examples/ai-functions/package.json, and pnpm-lock.yaml provide the live reproduction workspace; 20 completed current live attempts executed read_file structurally, but further intermittent testing was blocked by HTTP 429 Provider request budget exhausted.
    • The target branch contains [email protected] and @ai-sdk/[email protected]; the reporter's [email protected] and @ai-sdk/[email protected] versions are not part of release-v5.0 and were not installed.

    Relevant Documentation

  9. ai-sdk-factory commented on Jul 30, 2026

    @ai-sdk-factory
    Contributor

    Bugfix

    Created bugfix and opened PR: #18205

    Root Cause

    The Bedrock stream adapter forwarded every text and reasoning delta directly and trusted end_turn even when recorded responses contained serialized known-tool syntax instead of toolUse events.

    Summary

    Added Kimi-specific recovery for XML, native-marker, bare-JSON, and reasoning-encoded tool calls, suppressed duplicate serialized arguments, emitted structured tool events and tool-call finish reasons, and added a patch changeset.

    Testing

    Added five streaming regressions covering split XML calls, bare JSON, reasoning content, duplicate suppression, and incremental ordinary text.

  10. gr2m commented on Aug 31, 2026

    @gr2m
    Collaborator

    Closing this as upstream/not currently reproducible.

    The captured Bedrock response emitted the intended tool call as text with stopReason: "end_turn". AI SDK handled that response as received.

    I also tried the second turn directly against Bedrock, without AI SDK:

    curl reproduction
    BEDROCK_REGION=us-east-1
    
    curl -sS \
      "https://bedrock-runtime.${BEDROCK_REGION}.amazonaws.com/model/moonshot.kimi-k2-thinking/converse" \
      -H "Content-Type: application/json" \
      --aws-sigv4 "aws:amz:${BEDROCK_REGION}:bedrock" \
      --user "${AWS_ACCESS_KEY_ID}:${AWS_SECRET_ACCESS_KEY}" \
      --data-binary '{
        "messages": [
          {
            "role": "user",
            "content": [{
              "text": "Inspect this repository for Cursor-related files. You must first call glob_search with the exact pattern **/*cursor*. The glob result will include FILE1. You must then call read_file with target_file FILE1. Only after reading FILE1, answer exactly FOUND ISSUE_11409_CONTENT. Do not skip either tool and do not write tool arguments as ordinary text."
            }]
          },
          {
            "role": "assistant",
            "content": [{
              "toolUse": {
                "toolUseId": "functions.glob_search:0",
                "name": "glob_search",
                "input": { "pattern": "**/*cursor*" }
              }
            }]
          },
          {
            "role": "user",
            "content": [{
              "toolResult": {
                "toolUseId": "functions.glob_search:0",
                "content": [{
                  "json": { "files": ["FILE1", "FILE2"] }
                }]
              }
            }]
          }
        ],
        "inferenceConfig": {
          "maxTokens": 4096
        },
        "toolConfig": {
          "tools": [{
            "toolSpec": {
              "name": "read_file",
              "description": "Read a repository file.",
              "inputSchema": {
                "json": {
                  "type": "object",
                  "properties": {
                    "target_file": { "type": "string" }
                  },
                  "required": ["target_file"],
                  "additionalProperties": false
                }
              }
            }
          }]
        }
      }' | jq '{stopReason, content: .output.message.content}'

    Bedrock now returns a structured toolUse block with stopReason: "tool_use". I could not reproduce the failure.

    It may have been fixed upstream. It may also be intermittent or specific to ConverseStream. Adding model-specific parsing for malformed tool-call text would be brittle, so we do not plan to handle it in AI SDK. We can revisit if there is a current direct Bedrock reproduction.

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

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions