import { createBridgeActionState, runBridgeActionPipeline } from '../bridge-actions.js';
import { resolveBridgePolicy, resolvePolicyActions } from '../bridge-policies.js';
import { normalizeChatMessageContent } from '../shared/chat-output-normalizer.js';
import { mapBridgeToolsToChat } from '../shared/tool-mapping.js';
import { prepareGeminiToolsForBridge } from '../shared/gemini-tool-utils.js';
import { registerResponsesReasoning, consumeResponsesReasoning, registerResponsesOutputTextMeta, consumeResponsesOutputTextMeta, consumeResponsesPayloadSnapshot, registerResponsesPayloadSnapshot, consumeResponsesPassthrough, registerResponsesPassthrough } from '../shared/responses-reasoning-registry.js';
import { ProviderProtocolError } from '../provider-protocol-error.js';
function isObject(v) {
    return !!v && typeof v === 'object' && !Array.isArray(v);
}
function safeJson(value) {
    try {
        return JSON.stringify(value ?? {});
    }
    catch {
        return '{}';
    }
}
function flattenGeminiTextFromParts(parts) {
    if (!parts)
        return '';
    if (Array.isArray(parts)) {
        return parts
            .map((p) => {
            if (p && typeof p === 'object' && typeof p.text === 'string') {
                return String(p.text);
            }
            return '';
        })
            .filter(Boolean)
            .join('');
    }
    if (parts && typeof parts === 'object' && typeof parts.text === 'string') {
        return String(parts.text);
    }
    return '';
}
function mapGeminiRoleToChat(role) {
    const r = String(role || '').toLowerCase();
    if (r === 'model' || r === 'assistant')
        return 'assistant';
    if (r === 'system')
        return 'system';
    if (r === 'tool')
        return 'tool';
    return 'user';
}
function mapChatRoleToGemini(role) {
    const r = String(role || '').toLowerCase();
    if (r === 'assistant')
        return 'model';
    if (r === 'system')
        return 'system';
    if (r === 'tool')
        return 'tool';
    return 'user';
}
function coerceThoughtSignature(value) {
    if (typeof value === 'string' && value.trim().length) {
        return value.trim();
    }
    return undefined;
}
function extractThoughtSignatureFromToolCall(tc) {
    if (!tc || typeof tc !== 'object') {
        return undefined;
    }
    const direct = coerceThoughtSignature(tc.thought_signature ?? tc.thoughtSignature);
    if (direct) {
        return direct;
    }
    const extraContent = tc.extra_content ?? tc.extraContent;
    if (extraContent && typeof extraContent === 'object') {
        const googleNode = extraContent.google ?? extraContent.Google;
        if (googleNode && typeof googleNode === 'object') {
            const googleSig = coerceThoughtSignature(googleNode.thought_signature ?? googleNode.thoughtSignature);
            if (googleSig) {
                return googleSig;
            }
        }
    }
    return undefined;
}
export function buildOpenAIChatFromGeminiRequest(payload) {
    const messages = [];
    // systemInstruction → Chat system 消息
    try {
        const sys = payload?.systemInstruction;
        const sysText = flattenGeminiTextFromParts(sys?.parts ?? sys);
        if (sysText && sysText.trim().length) {
            messages.push({ role: 'system', content: sysText.trim() });
        }
    }
    catch {
        // ignore systemInstruction failures
    }
    const contents = Array.isArray(payload?.contents) ? payload.contents : [];
    for (const content of contents) {
        if (!content || typeof content !== 'object')
            continue;
        const cObj = content;
        const role = mapGeminiRoleToChat(cObj.role);
        const parts = Array.isArray(cObj.parts) ? cObj.parts : [];
        const textParts = [];
        const toolCalls = [];
        const toolResults = [];
        for (const part of parts) {
            if (!part || typeof part !== 'object')
                continue;
            const pObj = part;
            if (typeof pObj.text === 'string') {
                const t = pObj.text;
                if (t && t.trim().length)
                    textParts.push(t);
                continue;
            }
            if (pObj.functionCall && typeof pObj.functionCall === 'object') {
                const fc = pObj.functionCall;
                const name = typeof fc.name === 'string' ? String(fc.name) : undefined;
                if (!name)
                    continue;
                const id = typeof fc.id === 'string' ? String(fc.id) : undefined;
                const argsRaw = (fc.args ?? fc.arguments);
                let argsStr;
                if (typeof argsRaw === 'string') {
                    argsStr = argsRaw;
                }
                else {
                    argsStr = safeJson(argsRaw);
                }
                toolCalls.push({ id, type: 'function', function: { name, arguments: argsStr } });
                continue;
            }
            if (pObj.functionResponse && typeof pObj.functionResponse === 'object') {
                const fr = pObj.functionResponse;
                const callId = typeof fr.id === 'string' ? String(fr.id) : undefined;
                const resp = fr.response;
                let contentStr = '';
                if (typeof resp === 'string')
                    contentStr = resp;
                else if (resp != null) {
                    try {
                        contentStr = JSON.stringify(resp);
                    }
                    catch {
                        contentStr = String(resp);
                    }
                }
                toolResults.push({ role: 'tool', tool_call_id: callId, content: contentStr });
                continue;
            }
        }
        const combinedText = textParts.join('\n');
        const normalized = combinedText.length ? normalizeChatMessageContent(combinedText) : { contentText: undefined, reasoningText: undefined };
        const hasText = typeof normalized.contentText === 'string' && normalized.contentText.length > 0;
        const reasoningChunks = [];
        if (typeof normalized.reasoningText === 'string' && normalized.reasoningText.trim().length) {
            reasoningChunks.push(normalized.reasoningText.trim());
        }
        if (hasText || toolCalls.length > 0 || reasoningChunks.length > 0) {
            const msg = {
                role,
                content: normalized.contentText ?? combinedText ?? ''
            };
            if (reasoningChunks.length) {
                msg.reasoning_content = reasoningChunks.join('\n');
            }
            if (toolCalls.length)
                msg.tool_calls = toolCalls;
            messages.push(msg);
        }
        for (const tr of toolResults)
            messages.push(tr);
    }
    return { messages };
}
export function buildOpenAIChatFromGeminiResponse(payload) {
    const candidates = Array.isArray(payload?.candidates) ? payload.candidates : [];
    const errorNode = payload?.error;
    const primary = candidates[0] && typeof candidates[0] === 'object' ? candidates[0] : {};
    const content = primary?.content || {};
    const role = candidates.length > 0 ? mapGeminiRoleToChat(content.role) : 'assistant';
    const rawFinishReason = primary?.finishReason;
    const finishReasonUpper = typeof rawFinishReason === 'string' ? rawFinishReason.trim().toUpperCase() : '';
    const parts = Array.isArray(content.parts) ? content.parts : [];
    const textParts = [];
    const reasoningParts = [];
    const toolCalls = [];
    const toolResultTexts = [];
    const toolOutputs = [];
    // 为当前响应内生成稳定的工具调用 ID，避免下游 ServerTool 因缺少 id 而跳过。
    let toolCallCounter = 0;
    for (const part of parts) {
        if (!part || typeof part !== 'object')
            continue;
        const pObj = part;
        // 1. Text part
        if (typeof pObj.text === 'string') {
            const t = pObj.text;
            if (t && t.trim().length)
                textParts.push(t);
            continue;
        }
        // 2. Content array (nested structure)
        if (Array.isArray(pObj.content)) {
            for (const inner of pObj.content) {
                if (typeof inner === 'string') {
                    textParts.push(inner);
                }
                else if (inner && typeof inner === 'object' && typeof inner.text === 'string') {
                    textParts.push(inner.text);
                }
            }
            continue;
        }
        // 3. Reasoning part (channel mode)
        if (typeof pObj.reasoning === 'string') {
            reasoningParts.push(pObj.reasoning);
            continue;
        }
        // 4. Thought part (thinking/extended thinking)
        if (typeof pObj.thought === 'string') {
            const thoughtText = pObj.thought.trim();
            if (thoughtText.length) {
                reasoningParts.push(thoughtText);
            }
            continue;
        }
        // 5. Function call (tool call)
        if (pObj.functionCall && typeof pObj.functionCall === 'object') {
            const fc = pObj.functionCall;
            let name = typeof fc.name === 'string' ? String(fc.name) : undefined;
            if (!name)
                continue;
            // Gemini "websearch" is a transport alias for the canonical server-side tool "web_search".
            if (name === 'websearch' || name.startsWith('websearch_')) {
                name = 'web_search';
            }
            let id = typeof fc.id === 'string' && fc.id.trim().length ? String(fc.id).trim() : undefined;
            const argsRaw = (fc.args ?? fc.arguments);
            let argsStr;
            if (typeof argsRaw === 'string') {
                argsStr = argsRaw;
            }
            else {
                argsStr = safeJson(argsRaw);
            }
            const thoughtSignature = coerceThoughtSignature(pObj.thoughtSignature);
            // Gemini 某些响应中的 functionCall 不带 id，但下游 servertool 需要稳定的
            // tool_call.id 才会识别为有效工具调用；此处在缺失时生成本响应内唯一的占位 ID。
            if (!id) {
                const suffix = toolCallCounter++;
                id = `gemini_tool_${suffix}`;
            }
            const toolCall = {
                id,
                type: 'function',
                function: { name, arguments: argsStr }
            };
            if (thoughtSignature) {
                toolCall.thought_signature = thoughtSignature;
                toolCall.extra_content = {
                    google: {
                        thought_signature: thoughtSignature
                    }
                };
            }
            toolCalls.push(toolCall);
            continue;
        }
        // 6. Function response (tool result)
        if (pObj.functionResponse && typeof pObj.functionResponse === 'object') {
            const fr = pObj.functionResponse;
            const callId = typeof fr.id === 'string' && fr.id.trim().length ? String(fr.id) : undefined;
            let name = typeof fr.name === 'string' && fr.name.trim().length ? String(fr.name) : undefined;
            if (name && (name === 'websearch' || name.startsWith('websearch_'))) {
                name = 'web_search';
            }
            const resp = fr.response;
            let contentStr = '';
            if (typeof resp === 'string') {
                contentStr = resp;
            }
            else if (resp != null) {
                try {
                    contentStr = JSON.stringify(resp);
                }
                catch {
                    contentStr = String(resp);
                }
            }
            if (contentStr && contentStr.trim().length) {
                toolResultTexts.push(contentStr);
                if (callId || name) {
                    const entry = {
                        tool_call_id: callId ?? undefined,
                        id: callId ?? undefined,
                        content: contentStr
                    };
                    if (name) {
                        entry.name = name;
                    }
                    toolOutputs.push(entry);
                }
            }
            continue;
        }
        // 7. Executable code (code_interpreter)
        if (pObj.executableCode && typeof pObj.executableCode === 'object') {
            const code = pObj.executableCode;
            const language = typeof code.language === 'string' ? code.language : 'python';
            const codeText = typeof code.code === 'string' ? code.code : '';
            if (codeText.trim().length) {
                // Append as text with code block formatting
                textParts.push(`\`\`\`${language}\n${codeText}\n\`\`\``);
            }
            continue;
        }
        // 8. Code execution result
        if (pObj.codeExecutionResult && typeof pObj.codeExecutionResult === 'object') {
            const result = pObj.codeExecutionResult;
            const outcome = typeof result.outcome === 'string' ? result.outcome : '';
            const output = typeof result.output === 'string' ? result.output : '';
            if (output.trim().length) {
                textParts.push(`[Code Output${outcome ? ` (${outcome})` : ''}]:\n${output}`);
            }
            continue;
        }
    }
    const hasToolCalls = toolCalls.length > 0;
    // 如果 Gemini 返回 UNEXPECTED_TOOL_CALL，且当前没有有效的工具调用可继续，
    // 说明上游工具协议/声明与模型期望不一致，应视为 Provider 级错误而不是正常 stop，
    // 由上层通过 ProviderErrorCenter / HTTP 4xx/5xx 显式反馈给客户端。
    if (!hasToolCalls && finishReasonUpper === 'UNEXPECTED_TOOL_CALL') {
        throw new ProviderProtocolError('Gemini returned finishReason=UNEXPECTED_TOOL_CALL; this usually indicates an incompatible or unexpected tool invocation.', {
            code: 'TOOL_PROTOCOL_ERROR',
            protocol: 'gemini-chat',
            providerType: 'gemini',
            details: { finishReason: rawFinishReason }
        });
    }
    const finish_reason = (() => {
        // If the model is emitting tool calls, treat this turn as a tool_calls
        // completion so downstream tool governance can continue the loop.
        if (hasToolCalls)
            return 'tool_calls';
        const fr = finishReasonUpper;
        if (fr === 'MAX_TOKENS')
            return 'length';
        if (fr === 'STOP')
            return 'stop';
        if (fr === 'SAFETY')
            return 'content_filter';
        if (fr === 'OTHER')
            return 'stop';
        return 'stop';
    })();
    const usageMeta = payload?.usageMetadata || {};
    const usage = {};
    const promptTokens = Number(usageMeta.promptTokenCount);
    const completionTokens = Number(usageMeta.candidatesTokenCount);
    const totalTokens = Number(usageMeta.totalTokenCount);
    if (Number.isFinite(promptTokens))
        usage.prompt_tokens = promptTokens;
    if (Number.isFinite(completionTokens))
        usage.completion_tokens = completionTokens;
    if (Number.isFinite(totalTokens))
        usage.total_tokens = totalTokens;
    const combinedText = textParts.join('\n');
    const normalized = combinedText.length
        ? normalizeChatMessageContent(combinedText)
        : { contentText: undefined, reasoningText: undefined };
    const baseContent = normalized.contentText ?? combinedText ?? '';
    const toolResultBlock = toolResultTexts.length ? toolResultTexts.join('\n') : '';
    const finalContent = toolResultBlock && baseContent
        ? `${baseContent}\n${toolResultBlock}`
        : baseContent || toolResultBlock;
    const chatMsg = {
        role,
        content: finalContent
    };
    if (typeof normalized.reasoningText === 'string' && normalized.reasoningText.trim().length) {
        reasoningParts.push(normalized.reasoningText.trim());
    }
    if (reasoningParts.length) {
        chatMsg.reasoning_content = reasoningParts.join('\n');
    }
    if (toolCalls.length) {
        chatMsg.tool_calls = toolCalls;
    }
    try {
        const bridgePolicy = resolveBridgePolicy({ protocol: 'gemini-chat' });
        const actions = resolvePolicyActions(bridgePolicy, 'response_inbound');
        if (actions?.length) {
            const actionState = createBridgeActionState({
                messages: [chatMsg],
                rawResponse: isObject(payload) ? payload : undefined
            });
            runBridgeActionPipeline({
                stage: 'response_inbound',
                actions,
                protocol: bridgePolicy?.protocol ?? 'gemini-chat',
                moduleType: bridgePolicy?.moduleType ?? 'gemini-chat',
                requestId: typeof payload?.id === 'string' ? String(payload.id) : undefined,
                state: actionState
            });
        }
    }
    catch {
        // best-effort policy execution
    }
    const chatResp = {
        id: payload?.id || `chatcmpl_${Date.now()}`,
        object: 'chat.completion',
        model: payload?.model || 'unknown',
        choices: [
            {
                index: 0,
                finish_reason,
                message: chatMsg
            }
        ]
    };
    // Preserve upstream error envelope (used by servertool auto flows and host status mapping).
    if (errorNode && typeof errorNode === 'object' && !Array.isArray(errorNode)) {
        chatResp.error = errorNode;
    }
    if (Object.keys(usage).length > 0) {
        chatResp.usage = usage;
    }
    if (toolOutputs.length > 0) {
        chatResp.tool_outputs = toolOutputs;
    }
    const localReasoning = reasoningParts.length
        ? {
            content: reasoningParts.map((text) => ({ type: 'reasoning_text', text }))
        }
        : undefined;
    const preservedReasoning = consumeResponsesReasoning(chatResp.id);
    if (preservedReasoning) {
        chatResp.__responses_reasoning = preservedReasoning;
    }
    else if (localReasoning) {
        chatResp.__responses_reasoning = localReasoning;
    }
    const preservedOutputMeta = consumeResponsesOutputTextMeta(chatResp.id);
    if (preservedOutputMeta) {
        chatResp.__responses_output_text_meta = preservedOutputMeta;
    }
    const payloadSnapshot = consumeResponsesPayloadSnapshot(chatResp.id);
    if (payloadSnapshot) {
        registerResponsesPayloadSnapshot(chatResp.id, payloadSnapshot);
        if (typeof chatResp.request_id !== 'string') {
            chatResp.request_id = chatResp.id;
        }
    }
    const passthroughPayload = consumeResponsesPassthrough(chatResp.id);
    if (passthroughPayload) {
        registerResponsesPassthrough(chatResp.id, passthroughPayload);
        if (typeof chatResp.request_id !== 'string') {
            chatResp.request_id = chatResp.id;
        }
    }
    return chatResp;
}
export function buildGeminiFromOpenAIChat(chatResp) {
    const choices = Array.isArray(chatResp?.choices) ? chatResp.choices : [];
    const primary = choices[0] && typeof choices[0] === 'object' ? choices[0] : {};
    const msg = primary?.message || {};
    const usage = isObject(chatResp?.usage) ? chatResp.usage : {};
    const finishReason = (() => {
        const r = String(primary?.finish_reason || '').toLowerCase();
        if (r === 'length')
            return 'MAX_TOKENS';
        if (r === 'tool_calls' || r === 'function_call')
            return 'STOP';
        if (r === 'content_filter')
            return 'OTHER';
        if (r === 'stop')
            return 'STOP';
        return 'STOP';
    })();
    const baseRole = mapChatRoleToGemini(msg.role || 'assistant');
    if (msg) {
        try {
            const bridgePolicy = resolveBridgePolicy({ protocol: 'gemini-chat' });
            const actions = resolvePolicyActions(bridgePolicy, 'response_outbound');
            if (actions?.length) {
                const actionState = createBridgeActionState({
                    messages: [msg]
                });
                runBridgeActionPipeline({
                    stage: 'response_outbound',
                    actions,
                    protocol: bridgePolicy?.protocol ?? 'gemini-chat',
                    moduleType: bridgePolicy?.moduleType ?? 'gemini-chat',
                    requestId: typeof chatResp?.id === 'string' ? String(chatResp.id) : undefined,
                    state: actionState
                });
            }
        }
        catch {
            // ignore policy failures
        }
    }
    const parts = [];
    const contentText = (() => {
        const raw = msg.content;
        if (typeof raw === 'string')
            return raw;
        if (Array.isArray(raw)) {
            return raw
                .map((part) => {
                if (typeof part === 'string')
                    return part;
                if (part && typeof part === 'object') {
                    if (typeof part.text === 'string')
                        return part.text;
                    if (typeof part.content === 'string')
                        return part.content;
                }
                return '';
            })
                .filter(Boolean)
                .join('');
        }
        return '';
    })();
    if (contentText && contentText.length) {
        parts.push({ text: contentText });
    }
    const preservedReasoning = chatResp?.__responses_reasoning;
    const preservedContent = Array.isArray(preservedReasoning?.content)
        ? preservedReasoning?.content?.map((entry) => String(entry.text ?? '')).filter((text) => text.trim().length > 0)
        : [];
    const preservedSummary = Array.isArray(preservedReasoning?.summary)
        ? preservedReasoning?.summary?.map((entry) => String(entry.text ?? '')).filter((text) => text.trim().length > 0)
        : [];
    const reasoningText = preservedContent.length > 0
        ? preservedContent.join('\n')
        : (preservedSummary.length > 0
            ? preservedSummary.join('\n')
            : (typeof msg?.reasoning_content === 'string' && msg.reasoning_content.trim().length
                ? String(msg.reasoning_content).trim()
                : undefined));
    if (reasoningText) {
        parts.push({ reasoning: reasoningText });
    }
    const toolCalls = Array.isArray(msg.tool_calls) ? msg.tool_calls : [];
    for (const tc of toolCalls) {
        if (!tc || typeof tc !== 'object')
            continue;
        const fn = tc.function || {};
        const name = typeof fn.name === 'string' ? String(fn.name) : undefined;
        if (!name)
            continue;
        let argsStruct;
        const rawArgs = fn.arguments;
        if (typeof rawArgs === 'string') {
            const trimmed = rawArgs.trim();
            if (trimmed.startsWith('{')) {
                try {
                    const parsed = JSON.parse(rawArgs);
                    if (isObject(parsed)) {
                        argsStruct = parsed;
                    }
                    else {
                        argsStruct = { _raw: rawArgs };
                    }
                }
                catch {
                    argsStruct = { _raw: rawArgs };
                }
            }
            else {
                argsStruct = { _raw: rawArgs };
            }
        }
        else if (isObject(rawArgs)) {
            argsStruct = rawArgs;
        }
        else if (Array.isArray(rawArgs)) {
            try {
                argsStruct = { _raw: JSON.stringify(rawArgs) };
            }
            catch {
                argsStruct = { _raw: String(rawArgs) };
            }
        }
        else if (rawArgs != null) {
            argsStruct = { _raw: String(rawArgs) };
        }
        else {
            argsStruct = {};
        }
        // Gemini request/response wire uses `args` for functionCall payload.
        const functionCall = { name, args: argsStruct };
        const id = typeof tc.id === 'string' ? String(tc.id) : undefined;
        if (id)
            functionCall.id = id;
        const thoughtSignature = extractThoughtSignatureFromToolCall(tc);
        const partEntry = { functionCall };
        if (thoughtSignature) {
            partEntry.thoughtSignature = thoughtSignature;
        }
        parts.push(partEntry);
    }
    const candidate = {
        content: {
            role: baseRole,
            parts
        },
        finishReason
    };
    const usageMetadata = {};
    const promptTokens = Number(usage.prompt_tokens ?? usage.input_tokens);
    const completionTokens = Number(usage.completion_tokens ?? usage.output_tokens);
    const totalTokens = Number(usage.total_tokens);
    if (Number.isFinite(promptTokens))
        usageMetadata.promptTokenCount = promptTokens;
    if (Number.isFinite(completionTokens))
        usageMetadata.candidatesTokenCount = completionTokens;
    if (Number.isFinite(totalTokens))
        usageMetadata.totalTokenCount = totalTokens;
    const responseId = typeof chatResp?.id === 'string' ? String(chatResp.id) : `chatcmpl_${Date.now()}`;
    const out = {
        id: responseId,
        candidates: [candidate]
    };
    if (chatResp?.model) {
        out.model = chatResp.model;
    }
    if (Object.keys(usageMetadata).length > 0)
        out.usageMetadata = usageMetadata;
    if (chatResp?.__responses_reasoning) {
        registerResponsesReasoning(responseId, chatResp.__responses_reasoning);
    }
    if (chatResp?.__responses_output_text_meta) {
        registerResponsesOutputTextMeta(responseId, chatResp.__responses_output_text_meta);
    }
    return out;
}
export class GeminiOpenAIConversionCodec {
    _dependencies;
    id = 'gemini-openai';
    initialized = false;
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    constructor(_dependencies) {
        this._dependencies = _dependencies;
    }
    async initialize() {
        this.initialized = true;
    }
    async ensureInit() {
        if (!this.initialized)
            await this.initialize();
    }
    async convertRequest(payload, _profile, _context) {
        await this.ensureInit();
        const model = String((payload && typeof payload === 'object' && payload.model) || 'unknown');
        const { messages } = buildOpenAIChatFromGeminiRequest(payload);
        const out = { model, messages };
        try {
            const bridgeTools = prepareGeminiToolsForBridge(payload?.tools);
            const tools = bridgeTools ? mapBridgeToolsToChat(bridgeTools) : undefined;
            if (tools && tools.length > 0)
                out.tools = tools;
        }
        catch {
            // keep tools undefined on failure
        }
        try {
            const gen = payload?.generationConfig;
            if (gen && typeof gen === 'object') {
                const g = gen;
                const max = Number(g.maxOutputTokens ?? g.max_output_tokens);
                if (Number.isFinite(max) && max > 0)
                    out.max_tokens = max;
                if (typeof g.temperature === 'number')
                    out.temperature = g.temperature;
                if (typeof g.topP === 'number')
                    out.top_p = g.topP;
                const stop = g.stopSequences;
                if (typeof stop === 'string' && stop.trim()) {
                    out.stop = [stop.trim()];
                }
                else if (Array.isArray(stop) && stop.length > 0) {
                    out.stop = stop.map((s) => String(s)).filter(Boolean);
                }
            }
        }
        catch {
            // best-effort on generationConfig
        }
        try {
            const safety = payload?.safetySettings;
            const metadata = payload?.metadata;
            if (safety || metadata) {
                out.metadata = {
                    ...(isObject(metadata) ? metadata : {}),
                    vendor: {
                        ...(isObject(metadata?.vendor) ? metadata.vendor : {}),
                        gemini: {
                            safetySettings: safety
                        }
                    }
                };
            }
        }
        catch {
            // ignore metadata failures
        }
        // 不在此处设置 stream；流控由后半段统一处理
        return out;
    }
    async convertResponse(payload, _profile, _context) {
        await this.ensureInit();
        // 这里假设 payload 已经是标准 Chat completion，直接映射回 Gemini 响应；
        // 对于 provider→Chat 的路径，宿主可以直接调用 buildOpenAIChatFromGeminiResponse。
        return buildGeminiFromOpenAIChat(payload);
    }
}
