Skip to content

Conversation

@Ravens2121
Copy link

Pull Request: Kiro Executor Stability and API Compatibility Improvements

Summary

This PR includes multiple improvements to the Kiro executor for better stability, API compatibility, and code quality. The changes span two commits that address request format issues, stream processing reliability, JSON repair robustness, token calculation accuracy, and message handling enhancements.

Commits Included

Commit 1: cd4e84a - feat(kiro): enhance request format, stream handling, and usage tracking

Commit 2: 6133bac - feat(kiro): enhance Kiro executor stability and compatibility


Detailed Changes

🔧 Request Format Fixes (Commit 1)

  • Field ordering: Fixed conversationState field order - chatTriggerType must be first to comply with Kiro API requirements
  • Conditional profileArn: Added conditional profileArn inclusion based on authentication method
    • builder-id auth (AWS SSO): Does not require profileArn
    • social auth (Google OAuth): Requires profileArn

🌊 Stream Processing Enhancements (Commits 1 & 2)

  • Boundary validation: Added headersLen boundary validation to prevent slice out of bounds panics
  • EOF handling: Handle incomplete tool use at EOF by flushing pending data
  • Event separation: Separated message_delta and message_stop events for proper streaming protocol compliance
  • Error logging: Added error logging for JSON unmarshal failures
  • Panic recovery (Commit 2, lines 528-533): Added defer/recover block in streamToChannel goroutine to prevent single request crashes from affecting the entire service

🔨 JSON Repair Improvements (Commits 1 & 2)

  • Control character handling: Added escapeNewlinesInStrings() to handle control characters (newlines, tabs) in JSON strings
  • Pattern fix: Removed incorrect unquotedKeyPattern that was breaking valid JSON content
  • Conservative strategy (Commit 2, lines 2665-2818): Implemented conservative JSON repair
    • First try to parse JSON directly - if valid, return unchanged
    • Add bracket balancing detection and repair
    • Only repair when necessary to avoid corrupting valid JSON
    • Validate repaired JSON before returning

📊 Token Calculation Improvements (Commit 2)

  • Accurate counting (lines 317-330): Replaced simple len(content)/4 estimation with tiktoken-based calculation
  • Fallback handling: Added fallback to character count estimation if tiktoken fails
  • Streaming support (lines 1785-1817): Added accumulatedContent buffer for streaming token calculation with real-time counting

📝 Message Processing Enhancements (Commit 2)

  • Message merging (lines 778-780, 2356-2483): Added mergeAdjacentMessages() to combine consecutive messages with same role
    • Required by Kiro API which doesn't allow adjacent messages from same role
    • Added helper functions: mergeMessageContent(), blockToMap(), createMergedMessage()
  • Empty content handling (lines 791-800): Added default content for empty history messages
    • User messages with tool results: "Tool results provided."
    • User messages without tool results: "Continue"
  • Assistant last message (lines 811-830): Detect when last message is from assistant and create synthetic "Continue" user message
    • Kiro API requires currentMessage to be userInputMessage type

🔄 Duplicate Content Detection (Commit 2)

  • Event tracking (lines 1650-1660): Added lastContentEvent to detect duplicate streaming events
  • Skip redundant: Skip redundant events to prevent duplicate content in responses
  • Based on: Implementation based on AIClient-2-API for Kiro

📈 Usage Tracking (Commit 1)

  • Usage extraction: Added usage information extraction in message_delta response
  • OpenAI format: Include prompt_tokens, completion_tokens, total_tokens in OpenAI format

🏗️ Struct Field Reordering (Commit 2)

  • kiroToolResult (lines 670-673): Reordered struct fields to Content, Status, ToolUseID
  • Ensures consistency with API expectations

🧹 Code Quality Improvements (Commit 2)

  • Comment translation: Translated Chinese comments to English for code consistency
    • Affected areas: token calculation, buffer handling, message processing
  • HELIOS_CHK filtering removal (lines 2500-2504, 3004-3039):
    • Removed filterHeliosDebugInfo function
    • Removed heliosDebugPattern regex
    • HELIOS_CHK fields now handled by client-side processing

🔍 Debug Info Filtering - Optional (Commit 1)

  • Added filterHeliosDebugInfo() to remove [HELIOS_CHK] blocks
  • Pattern matches internal state tracking from Kiro/Amazon Q
  • Currently disabled pending further testing
  • Note: This was later removed in Commit 2 as it's now handled client-side

Files Changed

File Commit 1 Commit 2
internal/runtime/executor/kiro_executor.go +260/-32 +467/-67
internal/translator/kiro/openai/chat-completions/kiro_openai_response.go +15/-1 -

Total Changes: 2 files, ~740 insertions, ~100 deletions


Key Line References (Final State)

Feature Lines
Output Token Calculation 317-330
Stream Handler Panic Recovery 528-533
Struct Field Reordering (kiroToolResult) 670-673
Message Merging Function 778-780, 2356-2483
Empty Content Handling 791-800
Assistant Last Message Handling 811-830
Duplicate Content Event Detection 1650-1660
Streaming Token Calculation Enhancement 1785-1817
JSON Repair Enhancement 2665-2818
escapeNewlinesInStrings 2821-2869

Testing Checklist

  • Unit tests pass
  • Integration tests pass
  • Manual testing with Kiro API
  • Streaming responses work correctly
  • Tool calling works correctly
  • Token counting is accurate
  • Message merging works for adjacent same-role messages
  • Empty content messages handled properly
  • Duplicate content events filtered correctly

Breaking Changes

None. All changes are backward compatible.

Related Issues

N/A


中文说明

提交概览

本次 Pull Request 包含两个提交:

  • Commit 1 (cd4e84a): 增强请求格式、流处理和使用量跟踪
  • Commit 2 (6133bac): 增强 Kiro executor 稳定性和兼容性

请求格式修复 (Commit 1)

  • 修复 conversationState 字段顺序(chatTriggerType 必须在第一位)
  • 根据认证方式条件性包含 profileArn
    • builder-id 认证(AWS SSO)不需要 profileArn
    • social 认证(Google OAuth)需要 profileArn

流处理增强 (Commits 1 & 2)

  • 添加 headersLen 边界验证,防止切片越界
  • 在 EOF 时处理未完成的工具调用,刷新待处理数据
  • 分离 message_deltamessage_stop 事件以实现正确的流式传输
  • 添加 JSON 反序列化失败的错误日志
  • 添加 panic recovery 防止单个请求崩溃影响整个服务

JSON 修复改进 (Commits 1 & 2)

  • 添加 escapeNewlinesInStrings() 处理 JSON 字符串中的控制字符
  • 移除错误的 unquotedKeyPattern,该模式会破坏有效的 JSON 内容
  • 实现保守的 JSON 修复策略:
    • 首先尝试直接解析 JSON - 如果有效则原样返回
    • 添加括号平衡检测和修复
    • 仅在必要时修复以避免破坏有效的 JSON
    • 返回前验证修复后的 JSON

Token 计算改进 (Commit 2)

  • 使用 tiktoken 替代简单的 len(content)/4 估算
  • 添加 tiktoken 失败时的字符计数回退
  • 添加流式传输中的累积内容缓冲区用于实时 token 计数

消息处理增强 (Commit 2)

  • 添加 mergeAdjacentMessages() 合并相同角色的连续消息
    • Kiro API 不允许相邻的相同角色消息
    • 添加辅助函数:mergeMessageContent(), blockToMap(), createMergedMessage()
  • 添加空内容消息的默认内容处理
    • 带工具结果的用户消息:"Tool results provided."
    • 不带工具结果的用户消息:"Continue"
  • 处理最后一条消息是 assistant 的情况
    • 创建合成的 "Continue" 用户消息
    • Kiro API 要求 currentMessage 必须是 userInputMessage 类型

重复内容检测 (Commit 2)

  • 添加 lastContentEvent 跟踪以检测重复的流式事件
  • 跳过冗余事件以防止响应中出现重复内容
  • 基于 AIClient-2-API 的 Kiro 实现

使用量跟踪 (Commit 1)

  • message_delta 响应中添加 usage 信息提取
  • 以 OpenAI 格式包含 prompt_tokenscompletion_tokenstotal_tokens

结构体字段重排序 (Commit 2)

  • 重排序 kiroToolResult 结构体字段:Content, Status, ToolUseID
  • 确保与 API 期望一致

代码质量改进 (Commit 2)

  • 将中文注释翻译为英文以保持代码一致性
  • 移除 filterHeliosDebugInfo 函数和 heliosDebugPattern 正则表达式
  • HELIOS_CHK 字段现由客户端处理

## English Description

### Request Format Fixes
- Fix conversationState field order (chatTriggerType must be first)
- Add conditional profileArn inclusion based on auth method
- builder-id auth (AWS SSO) doesn't require profileArn
- social auth (Google OAuth) requires profileArn

### Stream Processing Enhancements
- Add headersLen boundary validation to prevent slice out of bounds
- Handle incomplete tool use at EOF by flushing pending data
- Separate message_delta and message_stop events for proper streaming
- Add error logging for JSON unmarshal failures

### JSON Repair Improvements
- Add escapeNewlinesInStrings() to handle control characters in JSON strings
- Remove incorrect unquotedKeyPattern that broke valid JSON content
- Fix handling of streaming fragments with embedded newlines/tabs

### Debug Info Filtering (Optional)
- Add filterHeliosDebugInfo() to remove [HELIOS_CHK] blocks
- Pattern matches internal state tracking from Kiro/Amazon Q
- Currently disabled pending further testing

### Usage Tracking
- Add usage information extraction in message_delta response
- Include prompt_tokens, completion_tokens, total_tokens in OpenAI format

---

## 中文描述

### 请求格式修复
- 修复 conversationState 字段顺序(chatTriggerType 必须在第一位)
- 根据认证方式条件性包含 profileArn
- builder-id 认证(AWS SSO)不需要 profileArn
- social 认证(Google OAuth)需要 profileArn

### 流处理增强
- 添加 headersLen 边界验证,防止切片越界
- 在 EOF 时处理未完成的工具调用,刷新待处理数据
- 分离 message_delta 和 message_stop 事件以实现正确的流式传输
- 添加 JSON 反序列化失败的错误日志

### JSON 修复改进
- 添加 escapeNewlinesInStrings() 处理 JSON 字符串中的控制字符
- 移除错误的 unquotedKeyPattern,该模式会破坏有效的 JSON 内容
- 修复包含嵌入换行符/制表符的流式片段处理

### 调试信息过滤(可选)
- 添加 filterHeliosDebugInfo() 移除 [HELIOS_CHK] 块
- 模式匹配来自 Kiro/Amazon Q 的内部状态跟踪信息
- 目前已禁用,等待进一步测试

### 使用量跟踪
- 在 message_delta 响应中添加 usage 信息提取
- 以 OpenAI 格式包含 prompt_tokens、completion_tokens、total_tokens
## Changes Overview
This commit includes multiple improvements to the Kiro executor for better
stability, API compatibility, and code quality.

## Detailed Changes

### 1. Output Token Calculation Improvement (lines 317-330)
- Replace simple len(content)/4 estimation with tiktoken-based calculation
- Add fallback to character count estimation if tiktoken fails
- Improves token counting accuracy for usage tracking

### 2. Stream Handler Panic Recovery (lines 528-533)
- Add defer/recover block in streamToChannel goroutine
- Prevents single request crashes from affecting the entire service

### 3. Struct Field Reordering (lines 670-673)
- Reorder kiroToolResult struct fields: Content, Status, ToolUseID
- Ensures consistency with API expectations

### 4. Message Merging Function (lines 778-780, 2356-2483)
- Add mergeAdjacentMessages() to combine consecutive messages with same role
- Add helper functions: mergeMessageContent(), blockToMap(), createMergedMessage()
- Required by Kiro API which doesn't allow adjacent messages from same role

### 5. Empty Content Handling (lines 791-800)
- Add default content for empty history messages
- User messages with tool results: "Tool results provided."
- User messages without tool results: "Continue"

### 6. Assistant Last Message Handling (lines 811-830)
- Detect when last message is from assistant
- Create synthetic "Continue" user message to satisfy Kiro API requirements
- Kiro API requires currentMessage to be userInputMessage type

### 7. Duplicate Content Event Detection (lines 1650-1660)
- Track lastContentEvent to detect duplicate streaming events
- Skip redundant events to prevent duplicate content in responses
- Based on AIClient-2-API implementation for Kiro

### 8. Streaming Token Calculation Enhancement (lines 1785-1817)
- Add accumulatedContent buffer for streaming token calculation
- Use tiktoken for accurate output token counting during streaming
- Add fallback to character count estimation with proper logging

### 9. JSON Repair Enhancement (lines 2665-2818)
- Implement conservative JSON repair strategy
- First try to parse JSON directly - if valid, return unchanged
- Add bracket balancing detection and repair
- Only repair when necessary to avoid corrupting valid JSON
- Validate repaired JSON before returning

### 10. HELIOS_CHK Filtering Removal (lines 2500-2504, 3004-3039)
- Remove filterHeliosDebugInfo function
- Remove heliosDebugPattern regex
- HELIOS_CHK fields now handled by client-side processing

### 11. Comment Translation
- Translate Chinese comments to English for code consistency
- Affected areas: token calculation, buffer handling, message processing
@gemini-code-assist
Copy link

Summary of Changes

Hello @Ravens2121, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the stability, API compatibility, and overall code quality of the Kiro executor. It addresses various critical aspects, from refining request formats and bolstering stream processing to improving JSON repair mechanisms and ensuring accurate token calculations. The changes aim to create a more robust and compliant interaction with the Kiro API, providing a smoother and more reliable user experience.

Highlights

  • API Compatibility: Ensured Kiro API compatibility by fixing conversationState field ordering and conditionally including profileArn based on authentication method.
  • Stream Processing Robustness: Improved streaming reliability with headersLen boundary validation, proper handling of incomplete tool use at EOF, separation of message_delta and message_stop events, and added panic recovery for stream handlers.
  • JSON Repair Logic: Implemented a conservative JSON repair strategy that first validates, then attempts to fix bracket imbalances and control characters, and re-validates, ensuring only necessary repairs are applied.
  • Accurate Token Calculation: Replaced estimated token counting with tiktoken-based calculation for both regular and streaming responses, with a fallback to character count estimation.
  • Message Handling Enhancements: Introduced logic to merge adjacent messages with the same role, provide default content for empty history messages, and generate synthetic 'Continue' user messages when the last message is from an assistant, aligning with Kiro API requirements.
  • Duplicate Content Filtering: Added lastContentEvent tracking to detect and skip redundant streaming events, preventing duplicate content in responses.
  • Usage Tracking: Integrated extraction of prompt_tokens, completion_tokens, and total_tokens from message_delta events, formatted for OpenAI compatibility.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a comprehensive set of improvements to the Kiro executor, enhancing its stability, API compatibility, and overall robustness. The changes are well-structured and address several important issues, including request formatting, stream processing reliability, JSON repair, and message handling. The addition of panic recovery, boundary checks, and more accurate token counting significantly improves the executor's quality.

My review focuses on identifying opportunities to reduce code duplication and improve maintainability. I've suggested extracting some repeated logic into helper functions. These changes should make the code cleaner and easier to manage in the future. Overall, this is a very strong contribution that makes the Kiro executor much more reliable.

Comment on lines +169 to +176
// Determine if profileArn should be included based on auth method
// profileArn is only needed for social auth (Google OAuth), not for builder-id (AWS SSO)
effectiveProfileArn := profileArn
if auth != nil && auth.Metadata != nil {
if authMethod, ok := auth.Metadata["auth_method"].(string); ok && authMethod == "builder-id" {
effectiveProfileArn = "" // Don't include profileArn for builder-id auth
}
}

Choose a reason for hiding this comment

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

medium

This logic to determine effectiveProfileArn is duplicated in ExecuteStream at lines 401-408. To improve maintainability and reduce code duplication, consider extracting this into a helper function.

For example:

func getEffectiveProfileArn(auth *cliproxyauth.Auth, profileArn string) string {
	if auth != nil && auth.Metadata != nil {
		if authMethod, ok := auth.Metadata["auth_method"].(string); ok && authMethod == "builder-id" {
			return "" // Don't include profileArn for builder-id auth
		}
	}
	return profileArn
}

You could then call this function in both Execute and ExecuteStream.

Comment on lines +862 to +876
// Deduplicate currentToolResults before adding to context
// Kiro API does not accept duplicate toolUseIds
if len(currentToolResults) > 0 {
seenIDs := make(map[string]bool)
uniqueToolResults := make([]kiroToolResult, 0, len(currentToolResults))
for _, tr := range currentToolResults {
if !seenIDs[tr.ToolUseID] {
seenIDs[tr.ToolUseID] = true
uniqueToolResults = append(uniqueToolResults, tr)
} else {
log.Debugf("kiro: skipping duplicate toolResult in currentMessage: %s", tr.ToolUseID)
}
}
currentToolResults = uniqueToolResults
}

Choose a reason for hiding this comment

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

medium

This block deduplicates currentToolResults. However, currentToolResults is assigned from toolResults which is returned by buildUserMessageStruct. The buildUserMessageStruct function already performs deduplication of tool results (as seen in the changes at lines 965-970).

This makes the deduplication here redundant. Please consider removing this block to simplify the code and avoid unnecessary processing.

Comment on lines +1448 to +1475
// Send tool_use content block
blockStart := e.buildClaudeContentBlockStartEvent(contentBlockIndex, "tool_use", currentToolUse.toolUseID, currentToolUse.name)
sseData := sdktranslator.TranslateStream(ctx, sdktranslator.FromString("kiro"), targetFormat, model, originalReq, claudeBody, blockStart, &translatorParam)
for _, chunk := range sseData {
if chunk != "" {
out <- cliproxyexecutor.StreamChunk{Payload: []byte(chunk + "\n\n")}
}
}

// Send tool input as delta
inputBytes, _ := json.Marshal(finalInput)
inputDelta := e.buildClaudeInputJsonDeltaEvent(string(inputBytes), contentBlockIndex)
sseData = sdktranslator.TranslateStream(ctx, sdktranslator.FromString("kiro"), targetFormat, model, originalReq, claudeBody, inputDelta, &translatorParam)
for _, chunk := range sseData {
if chunk != "" {
out <- cliproxyexecutor.StreamChunk{Payload: []byte(chunk + "\n\n")}
}
}

// Close block
blockStop := e.buildClaudeContentBlockStopEvent(contentBlockIndex)
sseData = sdktranslator.TranslateStream(ctx, sdktranslator.FromString("kiro"), targetFormat, model, originalReq, claudeBody, blockStop, &translatorParam)
for _, chunk := range sseData {
if chunk != "" {
out <- cliproxyexecutor.StreamChunk{Payload: []byte(chunk + "\n\n")}
}
}

Choose a reason for hiding this comment

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

medium

The pattern of translating an event to SSE and sending it to the output channel is repeated three times in this block for blockStart, inputDelta, and blockStop. To improve readability and reduce duplication, you could extract this into a helper function.

For example:

func sendSSEChunk(ctx context.Context, out chan<- cliproxyexecutor.StreamChunk, targetFormat sdktranslator.Format, model string, originalReq, claudeBody, eventPayload []byte, translatorParam *any) {
	sseData := sdktranslator.TranslateStream(ctx, sdktranslator.FromString("kiro"), targetFormat, model, originalReq, claudeBody, eventPayload, translatorParam)
	for _, chunk := range sseData {
		if chunk != "" {
			out <- cliproxyexecutor.StreamChunk{Payload: []byte(chunk + "\n\n")}
		}
	}
}

This would make the flushing logic for incomplete tool use more concise.

Comment on lines +1790 to +1811
if totalUsage.OutputTokens == 0 && accumulatedContent.Len() > 0 {
// Try to use tiktoken for accurate counting
if enc, err := tokenizerForModel(model); err == nil {
if tokenCount, countErr := enc.Count(accumulatedContent.String()); countErr == nil {
totalUsage.OutputTokens = int64(tokenCount)
log.Debugf("kiro: streamToChannel calculated output tokens using tiktoken: %d", totalUsage.OutputTokens)
} else {
// Fallback on count error: estimate from character count
totalUsage.OutputTokens = int64(accumulatedContent.Len() / 4)
if totalUsage.OutputTokens == 0 {
totalUsage.OutputTokens = 1
}
log.Debugf("kiro: streamToChannel tiktoken count failed, estimated from chars: %d", totalUsage.OutputTokens)
}
} else {
// Fallback: estimate from character count (roughly 4 chars per token)
totalUsage.OutputTokens = int64(accumulatedContent.Len() / 4)
if totalUsage.OutputTokens == 0 {
totalUsage.OutputTokens = 1
}
log.Debugf("kiro: streamToChannel estimated output tokens from chars: %d (content len: %d)", totalUsage.OutputTokens, accumulatedContent.Len())
}

Choose a reason for hiding this comment

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

medium

The fallback logic to estimate tokens from character count is duplicated within this if block (lines 1797-1802 and 1805-1810).

To avoid this repetition, you could extract the estimation into a helper function.

For example:

func estimateTokensFromChars(length int) int64 {
	tokens := int64(length / 4)
	if tokens == 0 && length > 0 {
		return 1
	}
	return tokens
}

Then you could simplify the fallback logic to:

// ...
} else {
    // Fallback on count error: estimate from character count
    totalUsage.OutputTokens = estimateTokensFromChars(accumulatedContent.Len())
    log.Debugf("kiro: streamToChannel tiktoken count failed, estimated from chars: %d", totalUsage.OutputTokens)
}
} else {
    // Fallback: estimate from character count (roughly 4 chars per token)
    totalUsage.OutputTokens = estimateTokensFromChars(accumulatedContent.Len())
    log.Debugf("kiro: streamToChannel estimated output tokens from chars: %d (content len: %d)", totalUsage.OutputTokens, accumulatedContent.Len())
}

@luispater luispater merged commit 69fccf0 into router-for-me:main Dec 11, 2025
1 check failed
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.

2 participants