fix(translator): ESM-safe registry + tool-id pairing + responses max_tokens; add real-creds tests

- translator/index.js: replace require() with static side-effect imports (ESM-safe),
  lazy-init registry maps to survive circular import order
- openai-responses->openai: map max_output_tokens -> max_tokens (avoid leaking field upstream)
- gemini/antigravity -> openai: derive deterministic tool_call id from name so
  functionCall/functionResponse pair correctly (fixes provider tool-pairing 400s)
- add offline unit tests (finish-reason, usage, session-manager, ollama malformed args, const guard)
- add real-creds integration tests (provider-cases + all-formats matrix: 6 inbound formats x 4 scenarios)

Includes co-located provider registry refactor (pricing/capabilities/media providers) and sessionManager updates.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
decolua
2026-06-15 11:38:43 +07:00
parent 24a2d19bd7
commit aba4c45da6
76 changed files with 2115 additions and 479 deletions

View File

@@ -160,7 +160,8 @@ function convertContent(content) {
// Function call
if (part.functionCall) {
toolCalls.push({
id: part.functionCall.id || `call_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
// Deterministic id from name so the matching functionResponse pairs correctly.
id: part.functionCall.id || `call_${part.functionCall.name}`,
type: OPENAI_BLOCK.FUNCTION,
function: {
name: part.functionCall.name,
@@ -173,7 +174,7 @@ function convertContent(content) {
if (part.functionResponse) {
toolResults.push({
role: ROLE.TOOL,
tool_call_id: part.functionResponse.id || part.functionResponse.name,
tool_call_id: part.functionResponse.id || `call_${part.functionResponse.name}`,
content: JSON.stringify(part.functionResponse.response?.result || part.functionResponse.response || {})
});
}

View File

@@ -97,8 +97,10 @@ function convertGeminiContent(content) {
}
if (part.functionCall) {
// Gemini lacks a native call id; derive a deterministic one from the name so the
// matching functionResponse maps to the same tool_call_id (providers require pairing).
toolCalls.push({
id: `call_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
id: part.functionCall.id || `call_${part.functionCall.name}`,
type: OPENAI_BLOCK.FUNCTION,
function: {
name: part.functionCall.name,
@@ -110,7 +112,7 @@ function convertGeminiContent(content) {
if (part.functionResponse) {
return {
role: ROLE.TOOL,
tool_call_id: part.functionResponse.id || part.functionResponse.name,
tool_call_id: part.functionResponse.id || `call_${part.functionResponse.name}`,
content: JSON.stringify(part.functionResponse.response?.result || part.functionResponse.response || {})
};
}

View File

@@ -177,6 +177,12 @@ export function openaiResponsesToOpenAIRequest(model, body, stream, credentials)
}
// Cleanup Responses API specific fields
// Map Responses-only max_output_tokens to Chat max_tokens (avoid leaking unknown field upstream)
if (result.max_output_tokens !== undefined) {
if (result.max_tokens === undefined) result.max_tokens = result.max_output_tokens;
delete result.max_output_tokens;
}
delete result.input;
delete result.instructions;
delete result.include;

View File

@@ -5,6 +5,7 @@
import { register } from "../index.js";
import { FORMATS } from "../formats.js";
import { v4 as uuidv4 } from "uuid";
import { resolveSessionId } from "../../utils/sessionManager.js";
import {
resolveKiroModel,
isThinkingEnabled,
@@ -546,7 +547,7 @@ export function openaiToKiroRequest(model, body, stream, credentials) {
const payload = {
conversationState: {
chatTriggerType: "MANUAL",
conversationId: uuidv4(),
conversationId: resolveSessionId({ headers: credentials?.rawHeaders, body, connectionId: credentials?.connectionId, scope: "kiro" }),
currentMessage: {
userInputMessage: {
content: finalContent,