"use strict";
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.HeaderContributors = exports.IHeaderContributors = exports.userAgentLibraryHeader = void 0;
exports.stringifyUrlOrRequestMetadata = stringifyUrlOrRequestMetadata;
exports.createCapiRequestBody = createCapiRequestBody;
exports.canRetryOnceNetworkError = canRetryOnceNetworkError;
exports.postRequest = postRequest;
exports.getRequest = getRequest;
const services_1 = require("../../../util/common/services");
const tokenizer_1 = require("../../../util/common/tokenizer");
const errors_1 = require("../../../util/vs/base/common/errors");
const capiClient_1 = require("../../endpoint/common/capiClient");
const telemetry_1 = require("../../telemetry/common/telemetry");
const fetcherService_1 = require("./fetcherService");
const openai_1 = require("./openai");
exports.userAgentLibraryHeader = 'X-VSCode-User-Agent-Library-Version';
// The maximum time to wait for a request to complete.
const requestTimeoutMs = 30 * 1000; // 30 seconds
function stringifyUrlOrRequestMetadata(urlOrRequestMetadata) {
    if (typeof urlOrRequestMetadata === 'string') {
        return urlOrRequestMetadata;
    }
    return JSON.stringify(urlOrRequestMetadata);
}
/** Function to create a standard request body for CAPI completions */
function createCapiRequestBody(options, model, callback) {
    // FIXME@ulugbekna: need to investigate why language configs have such stop words, eg
    // python has `\ndef` and `\nclass` which must be stop words for ghost text
    // const stops = getLanguageConfig<string[]>(accessor, ConfigKey.Stops);
    const request = {
        messages: (0, openai_1.rawMessageToCAPI)(options.messages, callback),
        model,
        // stop: stops,
    };
    if (options.postOptions) {
        Object.assign(request, options.postOptions);
    }
    return request;
}
function networkRequest(accessor, options) {
    const fetcher = accessor.get(fetcherService_1.IFetcherService);
    const telemetryService = accessor.get(telemetry_1.ITelemetryService);
    const capiClientService = accessor.get(capiClient_1.ICAPIClientService);
    const { requestType, endpointOrUrl, secretKey, intent, requestId, body, additionalHeaders, cancelToken, useFetcher, canRetryOnce = true, location } = options;
    // TODO @lramos15 Eventually don't even construct this fake endpoint object.
    const endpoint = typeof endpointOrUrl === 'string' || 'type' in endpointOrUrl ? {
        modelMaxPromptTokens: 0,
        urlOrRequestMetadata: endpointOrUrl,
        family: '',
        tokenizer: tokenizer_1.TokenizerType.O200K,
        acquireTokenizer: () => {
            throw new Error('Method not implemented.');
        },
        name: '',
        version: '',
    } : endpointOrUrl;
    const agentInteractionType = options.interactionTypeOverride ?? intent;
    const headers = {
        Authorization: `Bearer ${secretKey}`,
        'X-Request-Id': requestId,
        'OpenAI-Intent': intent, // Tells CAPI who flighted this request. Helps find buggy features
        'X-GitHub-Api-Version': '2026-01-09',
        ...additionalHeaders,
        ...(endpoint.getExtraHeaders ? endpoint.getExtraHeaders(location, options.interactionTypeOverride) : {}),
    };
    headers['X-Interaction-Type'] = agentInteractionType;
    headers['X-Agent-Task-Id'] = requestId;
    if (endpoint.interceptBody) {
        endpoint.interceptBody(body);
    }
    const endpointFetchOptions = endpoint.getEndpointFetchOptions?.();
    const request = {
        callSite: `network-request-${intent}`,
        method: requestType,
        headers: headers,
        json: body,
        timeout: requestTimeoutMs,
        useFetcher,
        suppressIntegrationId: endpointFetchOptions?.suppressIntegrationId
    };
    if (cancelToken) {
        const abort = fetcher.makeAbortController();
        cancelToken.onCancellationRequested(() => {
            // abort the request when the token is canceled
            telemetryService.sendGHTelemetryEvent('networking.cancelRequest', {
                headerRequestId: requestId,
            });
            abort.abort();
        });
        // pass the controller abort signal to the request
        request.signal = abort.signal;
    }
    if (typeof endpoint.urlOrRequestMetadata === 'string') {
        const requestPromise = fetcher.fetch(endpoint.urlOrRequestMetadata, request).catch(reason => {
            if (canRetryOnce && canRetryOnceNetworkError(reason)) {
                // disconnect and retry the request once if the connection was reset
                telemetryService.sendGHTelemetryEvent('networking.disconnectAll');
                return fetcher.disconnectAll().then(() => {
                    return fetcher.fetch(endpoint.urlOrRequestMetadata, request);
                });
            }
            else if (fetcher.isAbortError(reason)) {
                throw new errors_1.CancellationError();
            }
            else {
                throw reason;
            }
        });
        return requestPromise;
    }
    else {
        return capiClientService.makeRequest(request, endpoint.urlOrRequestMetadata);
    }
}
function canRetryOnceNetworkError(reason) {
    return [
        'ECONNRESET',
        'ETIMEDOUT',
        'ERR_CONNECTION_RESET',
        'ERR_NETWORK_CHANGED',
        'ERR_HTTP2_INVALID_SESSION',
        'ERR_HTTP2_STREAM_CANCEL',
        'ERR_HTTP2_GOAWAY_SESSION',
        'ERR_HTTP2_PROTOCOL_ERROR',
        'ERR_FAILED',
    ].includes(reason?.code);
}
function postRequest(accessor, options) {
    return networkRequest(accessor, { ...options, requestType: 'POST' });
}
function getRequest(accessor, options) {
    return networkRequest(accessor, { ...options, requestType: 'GET' });
}
exports.IHeaderContributors = (0, services_1.createServiceIdentifier)('headerContributors');
class HeaderContributors {
    constructor() {
        this.contributors = [];
    }
    add(contributor) {
        this.contributors.push(contributor);
    }
    remove(contributor) {
        const index = this.contributors.indexOf(contributor);
        if (index === -1) {
            return;
        }
        this.contributors.splice(index, 1);
    }
    contributeHeaders(headers) {
        for (const contributor of this.contributors) {
            contributor.contributeHeaderValues(headers);
        }
    }
    size() {
        return this.contributors.length;
    }
}
exports.HeaderContributors = HeaderContributors;
//# sourceMappingURL=networking.js.map