mirror of
https://github.com/githubhjs/CLIProxyAPIPlus.git
synced 2026-07-12 01:25:13 +00:00
feat(kiro): 代码优化重构 + OpenAI翻译器实现
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
// Package common provides shared constants and utilities for Kiro translator.
|
||||
package common
|
||||
|
||||
const (
|
||||
// KiroMaxToolDescLen is the maximum description length for Kiro API tools.
|
||||
// Kiro API limit is 10240 bytes, leave room for "..."
|
||||
KiroMaxToolDescLen = 10237
|
||||
|
||||
// ThinkingStartTag is the start tag for thinking blocks in responses.
|
||||
ThinkingStartTag = "<thinking>"
|
||||
|
||||
// ThinkingEndTag is the end tag for thinking blocks in responses.
|
||||
ThinkingEndTag = "</thinking>"
|
||||
|
||||
// KiroAgenticSystemPrompt is injected only for -agentic models to prevent timeouts on large writes.
|
||||
// AWS Kiro API has a 2-3 minute timeout for large file write operations.
|
||||
KiroAgenticSystemPrompt = `
|
||||
# CRITICAL: CHUNKED WRITE PROTOCOL (MANDATORY)
|
||||
|
||||
You MUST follow these rules for ALL file operations. Violation causes server timeouts and task failure.
|
||||
|
||||
## ABSOLUTE LIMITS
|
||||
- **MAXIMUM 350 LINES** per single write/edit operation - NO EXCEPTIONS
|
||||
- **RECOMMENDED 300 LINES** or less for optimal performance
|
||||
- **NEVER** write entire files in one operation if >300 lines
|
||||
|
||||
## MANDATORY CHUNKED WRITE STRATEGY
|
||||
|
||||
### For NEW FILES (>300 lines total):
|
||||
1. FIRST: Write initial chunk (first 250-300 lines) using write_to_file/fsWrite
|
||||
2. THEN: Append remaining content in 250-300 line chunks using file append operations
|
||||
3. REPEAT: Continue appending until complete
|
||||
|
||||
### For EDITING EXISTING FILES:
|
||||
1. Use surgical edits (apply_diff/targeted edits) - change ONLY what's needed
|
||||
2. NEVER rewrite entire files - use incremental modifications
|
||||
3. Split large refactors into multiple small, focused edits
|
||||
|
||||
### For LARGE CODE GENERATION:
|
||||
1. Generate in logical sections (imports, types, functions separately)
|
||||
2. Write each section as a separate operation
|
||||
3. Use append operations for subsequent sections
|
||||
|
||||
## EXAMPLES OF CORRECT BEHAVIOR
|
||||
|
||||
✅ CORRECT: Writing a 600-line file
|
||||
- Operation 1: Write lines 1-300 (initial file creation)
|
||||
- Operation 2: Append lines 301-600
|
||||
|
||||
✅ CORRECT: Editing multiple functions
|
||||
- Operation 1: Edit function A
|
||||
- Operation 2: Edit function B
|
||||
- Operation 3: Edit function C
|
||||
|
||||
❌ WRONG: Writing 500 lines in single operation → TIMEOUT
|
||||
❌ WRONG: Rewriting entire file to change 5 lines → TIMEOUT
|
||||
❌ WRONG: Generating massive code blocks without chunking → TIMEOUT
|
||||
|
||||
## WHY THIS MATTERS
|
||||
- Server has 2-3 minute timeout for operations
|
||||
- Large writes exceed timeout and FAIL completely
|
||||
- Chunked writes are FASTER and more RELIABLE
|
||||
- Failed writes waste time and require retry
|
||||
|
||||
REMEMBER: When in doubt, write LESS per operation. Multiple small operations > one large operation.`
|
||||
)
|
||||
@@ -0,0 +1,125 @@
|
||||
// Package common provides shared utilities for Kiro translators.
|
||||
package common
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/tidwall/gjson"
|
||||
)
|
||||
|
||||
// MergeAdjacentMessages merges adjacent messages with the same role.
|
||||
// This reduces API call complexity and improves compatibility.
|
||||
// Based on AIClient-2-API implementation.
|
||||
func MergeAdjacentMessages(messages []gjson.Result) []gjson.Result {
|
||||
if len(messages) <= 1 {
|
||||
return messages
|
||||
}
|
||||
|
||||
var merged []gjson.Result
|
||||
for _, msg := range messages {
|
||||
if len(merged) == 0 {
|
||||
merged = append(merged, msg)
|
||||
continue
|
||||
}
|
||||
|
||||
lastMsg := merged[len(merged)-1]
|
||||
currentRole := msg.Get("role").String()
|
||||
lastRole := lastMsg.Get("role").String()
|
||||
|
||||
if currentRole == lastRole {
|
||||
// Merge content from current message into last message
|
||||
mergedContent := mergeMessageContent(lastMsg, msg)
|
||||
// Create a new merged message JSON
|
||||
mergedMsg := createMergedMessage(lastRole, mergedContent)
|
||||
merged[len(merged)-1] = gjson.Parse(mergedMsg)
|
||||
} else {
|
||||
merged = append(merged, msg)
|
||||
}
|
||||
}
|
||||
|
||||
return merged
|
||||
}
|
||||
|
||||
// mergeMessageContent merges the content of two messages with the same role.
|
||||
// Handles both string content and array content (with text, tool_use, tool_result blocks).
|
||||
func mergeMessageContent(msg1, msg2 gjson.Result) string {
|
||||
content1 := msg1.Get("content")
|
||||
content2 := msg2.Get("content")
|
||||
|
||||
// Extract content blocks from both messages
|
||||
var blocks1, blocks2 []map[string]interface{}
|
||||
|
||||
if content1.IsArray() {
|
||||
for _, block := range content1.Array() {
|
||||
blocks1 = append(blocks1, blockToMap(block))
|
||||
}
|
||||
} else if content1.Type == gjson.String {
|
||||
blocks1 = append(blocks1, map[string]interface{}{
|
||||
"type": "text",
|
||||
"text": content1.String(),
|
||||
})
|
||||
}
|
||||
|
||||
if content2.IsArray() {
|
||||
for _, block := range content2.Array() {
|
||||
blocks2 = append(blocks2, blockToMap(block))
|
||||
}
|
||||
} else if content2.Type == gjson.String {
|
||||
blocks2 = append(blocks2, map[string]interface{}{
|
||||
"type": "text",
|
||||
"text": content2.String(),
|
||||
})
|
||||
}
|
||||
|
||||
// Merge text blocks if both end/start with text
|
||||
if len(blocks1) > 0 && len(blocks2) > 0 {
|
||||
if blocks1[len(blocks1)-1]["type"] == "text" && blocks2[0]["type"] == "text" {
|
||||
// Merge the last text block of msg1 with the first text block of msg2
|
||||
text1 := blocks1[len(blocks1)-1]["text"].(string)
|
||||
text2 := blocks2[0]["text"].(string)
|
||||
blocks1[len(blocks1)-1]["text"] = text1 + "\n" + text2
|
||||
blocks2 = blocks2[1:] // Remove the merged block from blocks2
|
||||
}
|
||||
}
|
||||
|
||||
// Combine all blocks
|
||||
allBlocks := append(blocks1, blocks2...)
|
||||
|
||||
// Convert to JSON
|
||||
result, _ := json.Marshal(allBlocks)
|
||||
return string(result)
|
||||
}
|
||||
|
||||
// blockToMap converts a gjson.Result block to a map[string]interface{}
|
||||
func blockToMap(block gjson.Result) map[string]interface{} {
|
||||
result := make(map[string]interface{})
|
||||
block.ForEach(func(key, value gjson.Result) bool {
|
||||
if value.IsObject() {
|
||||
result[key.String()] = blockToMap(value)
|
||||
} else if value.IsArray() {
|
||||
var arr []interface{}
|
||||
for _, item := range value.Array() {
|
||||
if item.IsObject() {
|
||||
arr = append(arr, blockToMap(item))
|
||||
} else {
|
||||
arr = append(arr, item.Value())
|
||||
}
|
||||
}
|
||||
result[key.String()] = arr
|
||||
} else {
|
||||
result[key.String()] = value.Value()
|
||||
}
|
||||
return true
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
// createMergedMessage creates a JSON string for a merged message
|
||||
func createMergedMessage(role string, content string) string {
|
||||
msg := map[string]interface{}{
|
||||
"role": role,
|
||||
"content": json.RawMessage(content),
|
||||
}
|
||||
result, _ := json.Marshal(msg)
|
||||
return string(result)
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// Package common provides shared constants and utilities for Kiro translator.
|
||||
package common
|
||||
|
||||
// GetString safely extracts a string from a map.
|
||||
// Returns empty string if the key doesn't exist or the value is not a string.
|
||||
func GetString(m map[string]interface{}, key string) string {
|
||||
if v, ok := m[key].(string); ok {
|
||||
return v
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// GetStringValue is an alias for GetString for backward compatibility.
|
||||
func GetStringValue(m map[string]interface{}, key string) string {
|
||||
return GetString(m, key)
|
||||
}
|
||||
Reference in New Issue
Block a user