Unified · Split
| @@ -1,10 +1,14 @@ | ||
| 1 | 1 | // Package claude provides translation between Kiro and Claude formats. |
| 2 | 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 | 4 | package claude |
| 4 | 5 | |
| 5 | 6 | import ( |
| 6 | 7 | "bytes" |
| 7 | 8 | "context" |
| 9 | + "strings" | |
| 10 | + | |
| 11 | + "github.com/tidwall/gjson" | |
| 8 | 12 | ) |
| 9 | 13 | |
| 10 | 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 | 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 | 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 | 69 | // ConvertKiroResponseToClaudeNonStream converts Kiro non-streaming response to Claude format. |