refactor(open-sse): translator DRY + schema enums, bug fixes, dead code cleanup
- Bug B1-B7: media UI m.kind||m.type, serviceKinds, gemini mediaPriority, schema kind, models/info lookup by kind - Dead code D1-D6: safeParseJSON, drop PROVIDER_ENDPOINTS, orphan fetcher, GITHUB_CONFIG derive, getProviderConfig internal, legacy kiro file - Translator concerns: toOpenAIUsage, toOpenAIFinish (gemini/kiro/ollama + fix kiro tool finish), thinking effort maps - Reorg helpers/ → concerns/ (logic) + formats/ (per-format) + schema/ (pure enums: roles/blocks/finishReasons/defaults) - Wire ~280 hardcoded role/block/finish/default literals to schema enums across 20+ files - collapseTextParts + extractTextContent dedup - Normalize translator fn names to openaiToXRequest / xToOpenAIResponse - Golden tests lock behavior; 0 regression (byte-for-byte providers/alias, 26=26 known fails) Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -12,7 +12,9 @@ import {
|
||||
KIRO_AGENTIC_SYSTEM_PROMPT,
|
||||
resolveDefaultProfileArn
|
||||
} from "../../config/kiroConstants.js";
|
||||
import { parseDataUri } from "../helpers/imageHelper.js";
|
||||
import { parseDataUri } from "../concerns/image.js";
|
||||
import { DEFAULT_IMAGE_MIME } from "../schema/index.js";
|
||||
import { ROLE, OPENAI_BLOCK, CLAUDE_BLOCK } from "../schema/index.js";
|
||||
|
||||
/** Render a single tool call as a readable text line. */
|
||||
function toolCallToText(name, input) {
|
||||
@@ -56,18 +58,18 @@ function flattenToolInteractions(messages) {
|
||||
|
||||
for (const msg of messages) {
|
||||
// OpenAI tool-result message → user text line
|
||||
if (msg.role === "tool") {
|
||||
out.push({ role: "user", content: toolResultToText(msg.content) });
|
||||
if (msg.role === ROLE.TOOL) {
|
||||
out.push({ role: ROLE.USER, content: toolResultToText(msg.content) });
|
||||
continue;
|
||||
}
|
||||
|
||||
if (msg.role === "assistant") {
|
||||
if (msg.role === ROLE.ASSISTANT) {
|
||||
const parts = [];
|
||||
if (Array.isArray(msg.content)) {
|
||||
for (const c of msg.content) {
|
||||
if (c.type === "tool_use") {
|
||||
if (c.type === CLAUDE_BLOCK.TOOL_USE) {
|
||||
parts.push(toolCallToText(c.name, c.input));
|
||||
} else if (c.type === "text" || c.text) {
|
||||
} else if (c.type === OPENAI_BLOCK.TEXT || c.text) {
|
||||
parts.push(c.text || "");
|
||||
}
|
||||
}
|
||||
@@ -77,15 +79,15 @@ function flattenToolInteractions(messages) {
|
||||
for (const tc of msg.tool_calls || []) {
|
||||
parts.push(toolCallToText(tc.function?.name, tc.function?.arguments));
|
||||
}
|
||||
out.push({ role: "assistant", content: parts.filter(Boolean).join("\n") });
|
||||
out.push({ role: ROLE.ASSISTANT, content: parts.filter(Boolean).join("\n") });
|
||||
continue;
|
||||
}
|
||||
|
||||
// User messages: replace tool_result blocks with text, keep text + images.
|
||||
if (msg.role === "user" && Array.isArray(msg.content)) {
|
||||
if (msg.role === ROLE.USER && Array.isArray(msg.content)) {
|
||||
const newContent = msg.content.map(c =>
|
||||
c.type === "tool_result"
|
||||
? { type: "text", text: toolResultToText(c.content) }
|
||||
c.type === CLAUDE_BLOCK.TOOL_RESULT
|
||||
? { type: OPENAI_BLOCK.TEXT, text: toolResultToText(c.content) }
|
||||
: c
|
||||
);
|
||||
out.push({ ...msg, content: newContent });
|
||||
@@ -267,8 +269,8 @@ function convertMessages(messages, tools, model) {
|
||||
let role = msg.role;
|
||||
|
||||
// Normalize: system/tool -> user
|
||||
if (role === "system" || role === "tool") {
|
||||
role = "user";
|
||||
if (role === ROLE.SYSTEM || role === ROLE.TOOL) {
|
||||
role = ROLE.USER;
|
||||
}
|
||||
|
||||
// If role changes, flush pending
|
||||
@@ -277,7 +279,7 @@ function convertMessages(messages, tools, model) {
|
||||
}
|
||||
currentRole = role;
|
||||
|
||||
if (role === "user") {
|
||||
if (role === ROLE.USER) {
|
||||
// Extract content
|
||||
let content = "";
|
||||
if (typeof msg.content === "string") {
|
||||
@@ -285,9 +287,9 @@ function convertMessages(messages, tools, model) {
|
||||
} else if (Array.isArray(msg.content)) {
|
||||
const textParts = [];
|
||||
for (const c of msg.content) {
|
||||
if (c.type === "text" || c.text) {
|
||||
if (c.type === OPENAI_BLOCK.TEXT || c.text) {
|
||||
textParts.push(c.text || "");
|
||||
} else if (c.type === "image_url") {
|
||||
} else if (c.type === OPENAI_BLOCK.IMAGE_URL) {
|
||||
// OpenAI format: image_url.url with data URI
|
||||
const url = c.image_url?.url || "";
|
||||
const parsed = parseDataUri(url);
|
||||
@@ -298,10 +300,10 @@ function convertMessages(messages, tools, model) {
|
||||
// Kiro only supports base64 — fallback to URL text
|
||||
textParts.push(`[Image: ${url}]`);
|
||||
}
|
||||
} else if (c.type === "image") {
|
||||
} else if (c.type === CLAUDE_BLOCK.IMAGE) {
|
||||
// Claude format: source.type = "base64", source.media_type, source.data
|
||||
if (c.source?.type === "base64" && c.source?.data) {
|
||||
const mediaType = c.source.media_type || "image/png";
|
||||
const mediaType = c.source.media_type || DEFAULT_IMAGE_MIME;
|
||||
const format = mediaType.split("/")[1] || mediaType;
|
||||
pendingImages.push({ format, source: { bytes: c.source.data } });
|
||||
}
|
||||
@@ -310,7 +312,7 @@ function convertMessages(messages, tools, model) {
|
||||
content = textParts.join("\n");
|
||||
|
||||
// Check for tool_result blocks
|
||||
const toolResultBlocks = msg.content.filter(c => c.type === "tool_result");
|
||||
const toolResultBlocks = msg.content.filter(c => c.type === CLAUDE_BLOCK.TOOL_RESULT);
|
||||
if (toolResultBlocks.length > 0) {
|
||||
toolResultBlocks.forEach(block => {
|
||||
const text = Array.isArray(block.content)
|
||||
@@ -327,7 +329,7 @@ function convertMessages(messages, tools, model) {
|
||||
}
|
||||
|
||||
// Handle tool role (from normalized)
|
||||
if (msg.role === "tool") {
|
||||
if (msg.role === ROLE.TOOL) {
|
||||
const toolContent = typeof msg.content === "string" ? msg.content : "";
|
||||
pendingToolResults.push({
|
||||
toolUseId: msg.tool_call_id,
|
||||
@@ -337,16 +339,16 @@ function convertMessages(messages, tools, model) {
|
||||
} else if (content) {
|
||||
pendingUserContent.push(content);
|
||||
}
|
||||
} else if (role === "assistant") {
|
||||
} else if (role === ROLE.ASSISTANT) {
|
||||
// Extract text content and tool uses
|
||||
let textContent = "";
|
||||
let toolUses = [];
|
||||
|
||||
if (Array.isArray(msg.content)) {
|
||||
const textBlocks = msg.content.filter(c => c.type === "text");
|
||||
const textBlocks = msg.content.filter(c => c.type === OPENAI_BLOCK.TEXT);
|
||||
textContent = textBlocks.map(b => b.text).join("\n").trim();
|
||||
|
||||
const toolUseBlocks = msg.content.filter(c => c.type === "tool_use");
|
||||
const toolUseBlocks = msg.content.filter(c => c.type === CLAUDE_BLOCK.TOOL_USE);
|
||||
toolUses = toolUseBlocks;
|
||||
} else if (typeof msg.content === "string") {
|
||||
textContent = msg.content.trim();
|
||||
@@ -509,7 +511,7 @@ function convertMessages(messages, tools, model) {
|
||||
* `thinking`, OpenAI `reasoning_effort`, AMP/Cursor magic tags, and model
|
||||
* name hints.
|
||||
*/
|
||||
export function buildKiroPayload(model, body, stream, credentials) {
|
||||
export function openaiToKiroRequest(model, body, stream, credentials) {
|
||||
const messages = body.messages || [];
|
||||
const tools = body.tools || [];
|
||||
const maxTokens = 32000;
|
||||
@@ -582,4 +584,4 @@ export function buildKiroPayload(model, body, stream, credentials) {
|
||||
return payload;
|
||||
}
|
||||
|
||||
register(FORMATS.OPENAI, FORMATS.KIRO, buildKiroPayload, null);
|
||||
register(FORMATS.OPENAI, FORMATS.KIRO, openaiToKiroRequest, null);
|
||||
|
||||
Reference in New Issue
Block a user