Unified · Split
| @@ -1,10 +1,14 @@ | |||
| 1 | // Package claude provides translation between Kiro and Claude formats. | 1 | // Package claude provides translation between Kiro and Claude formats. |
| 2 | // Since Kiro uses Claude-compatible format internally, translations are mostly pass-through. | 2 | // Since Kiro uses Claude-compatible format internally, translations are mostly pass-through. |
| 3 | // However, SSE events require proper "event: <type>" prefix for Claude clients. | ||
| 3 | package claude | 4 | package claude |
| 4 | 5 | ||
| 5 | import ( | 6 | import ( |
| 6 | "bytes" | 7 | "bytes" |
| 7 | "context" | 8 | "context" |
| 9 | "strings" | ||
| 10 | |||
| 11 | "github.com/tidwall/gjson" | ||
| 8 | ) | 12 | ) |
| 9 | 13 | ||
| 10 | // ConvertClaudeRequestToKiro converts Claude request to Kiro format. | 14 | // ConvertClaudeRequestToKiro converts Claude request to Kiro format. |
| @@ -14,8 +18,52 @@ func ConvertClaudeRequestToKiro(modelName string, inputRawJSON []byte, stream bo · 3 unmodified lines | |||
| 14 | } | 18 | } |
| 15 | 19 | ||
| 16 | // ConvertKiroResponseToClaude converts Kiro streaming response to Claude format. | 20 | // ConvertKiroResponseToClaude converts Kiro streaming response to Claude format. |
| 21 | // It adds the required "event: <type>" prefix for SSE compliance with Claude clients. | ||
| 22 | // Input format: "data: {\"type\":\"message_start\",...}" | ||
| 23 | // Output format: "event: message_start\ndata: {\"type\":\"message_start\",...}" | ||
| 17 | func ConvertKiroResponseToClaude(ctx context.Context, model string, originalRequest, request, rawResponse []byte, param *any) []string { | 24 | func ConvertKiroResponseToClaude(ctx context.Context, model string, originalRequest, request, rawResponse []byte, param *any) []string { |
| 18 | return []string{string(rawResponse)} | 25 | raw := string(rawResponse) |
| 26 | |||
| 27 | // Handle multiple data blocks (e.g., message_delta + message_stop) | ||
| 28 | lines := strings.Split(raw, "\n\n") | ||
| 29 | var results []string | ||
| 30 | |||
| 31 | for _, line := range lines { | ||
| 32 | line = strings.TrimSpace(line) | ||
| 33 | if line == "" { | ||
| 34 | continue | ||
| 35 | } | ||
| 36 | |||
| 37 | // Extract event type from JSON and add "event:" prefix | ||
| 38 | formatted := addEventPrefix(line) | ||
| 39 | if formatted != "" { | ||
| 40 | results = append(results, formatted) | ||
| 41 | } | ||
| 42 | } | ||
| 43 | |||
| 44 | if len(results) == 0 { | ||
| 45 | return []string{raw} | ||
| 46 | } | ||
| 47 | |||
| 48 | return results | ||
| 49 | } | ||
| 50 | |||
| 51 | // addEventPrefix extracts the event type from the data line and adds the event: prefix. | ||
| 52 | // Input: "data: {\"type\":\"message_start\",...}" | ||
| 53 | // Output: "event: message_start\ndata: {\"type\":\"message_start\",...}" | ||
| 54 | func addEventPrefix(dataLine string) string { | ||
| 55 | if !strings.HasPrefix(dataLine, "data: ") { | ||
| 56 | return dataLine | ||
| 57 | } | ||
| 58 | |||
| 59 | jsonPart := strings.TrimPrefix(dataLine, "data: ") | ||
| 60 | eventType := gjson.Get(jsonPart, "type").String() | ||
| 61 | |||
| 62 | if eventType == "" { | ||
| 63 | return dataLine | ||
| 64 | } | ||
| 65 | |||
| 66 | return "event: " + eventType + "\n" + dataLine | ||
| 19 | } | 67 | } |
| 20 | 68 | ||
| 21 | // ConvertKiroResponseToClaudeNonStream converts Kiro non-streaming response to Claude format. | 69 | // ConvertKiroResponseToClaudeNonStream converts Kiro non-streaming response to Claude format. |