agsamantha/node_modules/langchain/dist/util/axios-fetch-adapter.js

399 lines
13 KiB
JavaScript
Raw Normal View History

2024-10-02 15:15:21 -05:00
/* eslint-disable no-plusplus */
/* eslint-disable prefer-template */
/* eslint-disable prefer-arrow-callback */
/* eslint-disable no-var */
/* eslint-disable vars-on-top */
/* eslint-disable no-param-reassign */
/* eslint-disable import/no-extraneous-dependencies */
/**
* This is copied from @vespaiach/axios-fetch-adapter, which exposes an ESM
* module without setting the "type" field in package.json.
*/
import axios from "axios";
import { EventStreamContentType, getLines, getBytes, getMessages, } from "./event-source-parse.js";
function tryJsonStringify(data) {
try {
return JSON.stringify(data);
}
catch (e) {
return data;
}
}
/**
* In order to avoid import issues with axios 1.x, copying here the internal
* utility functions that we used to import directly from axios.
*/
// Copied from axios/lib/core/settle.js
function settle(resolve, reject, response) {
const { validateStatus } = response.config;
if (!response.status || !validateStatus || validateStatus(response.status)) {
resolve(response);
}
else {
reject(createError(`Request failed with status code ${response.status} and body ${typeof response.data === "string"
? response.data
: tryJsonStringify(response.data)}`, response.config, null, response.request, response));
}
}
// Copied from axios/lib/helpers/isAbsoluteURL.js
function isAbsoluteURL(url) {
// A URL is considered absolute if it begins with "<scheme>://" or "//" (protocol-relative URL).
// RFC 3986 defines scheme name as a sequence of characters beginning with a letter and followed
// by any combination of letters, digits, plus, period, or hyphen.
return /^([a-z][a-z\d+\-.]*:)?\/\//i.test(url);
}
// Copied from axios/lib/helpers/combineURLs.js
function combineURLs(baseURL, relativeURL) {
return relativeURL
? baseURL.replace(/\/+$/, "") + "/" + relativeURL.replace(/^\/+/, "")
: baseURL;
}
// Copied from axios/lib/helpers/buildURL.js
function encode(val) {
return encodeURIComponent(val)
.replace(/%3A/gi, ":")
.replace(/%24/g, "$")
.replace(/%2C/gi, ",")
.replace(/%20/g, "+")
.replace(/%5B/gi, "[")
.replace(/%5D/gi, "]");
}
function buildURL(url, params, paramsSerializer) {
if (!params) {
return url;
}
var serializedParams;
if (paramsSerializer) {
serializedParams = paramsSerializer(params);
}
else if (isURLSearchParams(params)) {
serializedParams = params.toString();
}
else {
var parts = [];
forEach(params, function serialize(val, key) {
if (val === null || typeof val === "undefined") {
return;
}
if (isArray(val)) {
key = `${key}[]`;
}
else {
val = [val];
}
forEach(val, function parseValue(v) {
if (isDate(v)) {
v = v.toISOString();
}
else if (isObject(v)) {
v = JSON.stringify(v);
}
parts.push(`${encode(key)}=${encode(v)}`);
});
});
serializedParams = parts.join("&");
}
if (serializedParams) {
var hashmarkIndex = url.indexOf("#");
if (hashmarkIndex !== -1) {
url = url.slice(0, hashmarkIndex);
}
url += (url.indexOf("?") === -1 ? "?" : "&") + serializedParams;
}
return url;
}
// Copied from axios/lib/core/buildFullPath.js
function buildFullPath(baseURL, requestedURL) {
if (baseURL && !isAbsoluteURL(requestedURL)) {
return combineURLs(baseURL, requestedURL);
}
return requestedURL;
}
// Copied from axios/lib/utils.js
function isUndefined(val) {
return typeof val === "undefined";
}
function isObject(val) {
return val !== null && typeof val === "object";
}
function isDate(val) {
return toString.call(val) === "[object Date]";
}
function isURLSearchParams(val) {
return toString.call(val) === "[object URLSearchParams]";
}
function isArray(val) {
return Array.isArray(val);
}
function forEach(obj, fn) {
// Don't bother if no value provided
if (obj === null || typeof obj === "undefined") {
return;
}
// Force an array if not already something iterable
if (typeof obj !== "object") {
obj = [obj];
}
if (isArray(obj)) {
// Iterate over array values
for (var i = 0, l = obj.length; i < l; i++) {
fn.call(null, obj[i], i, obj);
}
}
else {
// Iterate over object keys
for (var key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
fn.call(null, obj[key], key, obj);
}
}
}
}
function isFormData(val) {
return toString.call(val) === "[object FormData]";
}
// TODO this needs to be fixed to run in newer browser-like environments
// https://github.com/vespaiach/axios-fetch-adapter/issues/20#issue-1396365322
function isStandardBrowserEnv() {
if (typeof navigator !== "undefined" &&
// eslint-disable-next-line no-undef
(navigator.product === "ReactNative" ||
// eslint-disable-next-line no-undef
navigator.product === "NativeScript" ||
// eslint-disable-next-line no-undef
navigator.product === "NS")) {
return false;
}
return typeof window !== "undefined" && typeof document !== "undefined";
}
/**
* - Create a request object
* - Get response body
* - Check if timeout
*/
export default async function fetchAdapter(config) {
const request = createRequest(config);
const data = await getResponse(request, config);
return new Promise((resolve, reject) => {
if (data instanceof Error) {
reject(data);
}
else {
// eslint-disable-next-line no-unused-expressions
Object.prototype.toString.call(config.settle) === "[object Function]"
? config.settle(resolve, reject, data)
: settle(resolve, reject, data);
}
});
}
/**
* Fetch API stage two is to get response body. This funtion tries to retrieve
* response body based on response's type
*/
async function getResponse(request, config) {
let stageOne;
try {
stageOne = await fetch(request);
}
catch (e) {
if (e && e.name === "AbortError") {
return createError("Request aborted", config, "ECONNABORTED", request);
}
if (e && e.name === "TimeoutError") {
return createError("Request timeout", config, "ECONNABORTED", request);
}
return createError("Network Error", config, "ERR_NETWORK", request);
}
const headers = {};
stageOne.headers.forEach((value, key) => {
headers[key] = value;
});
const response = {
ok: stageOne.ok,
status: stageOne.status,
statusText: stageOne.statusText,
headers,
config,
request,
};
if (stageOne.status >= 200 && stageOne.status !== 204) {
if (config.responseType === "stream") {
const contentType = stageOne.headers.get("content-type");
if (!contentType?.startsWith(EventStreamContentType)) {
// If the content-type is not stream, response is most likely an error
if (stageOne.status >= 400) {
// If the error is a JSON, parse it. Otherwise, return as text
if (contentType?.startsWith("application/json")) {
response.data = await stageOne.json();
return response;
}
else {
response.data = await stageOne.text();
return response;
}
}
// If the non-stream response is also not an error, throw
throw new Error(`Expected content-type to be ${EventStreamContentType}, Actual: ${contentType}`);
}
await getBytes(stageOne.body, getLines(getMessages(config.onmessage)));
}
else {
switch (config.responseType) {
case "arraybuffer":
response.data = await stageOne.arrayBuffer();
break;
case "blob":
response.data = await stageOne.blob();
break;
case "json":
response.data = await stageOne.json();
break;
case "formData":
response.data = await stageOne.formData();
break;
default:
response.data = await stageOne.text();
break;
}
}
}
return response;
}
/**
* This function will create a Request object based on configuration's axios
*/
function createRequest(config) {
const headers = new Headers(config.headers);
// HTTP basic authentication
if (config.auth) {
const username = config.auth.username || "";
const password = config.auth.password
? decodeURI(encodeURIComponent(config.auth.password))
: "";
headers.set("Authorization", `Basic ${btoa(`${username}:${password}`)}`);
}
const method = config.method.toUpperCase();
const options = {
headers,
method,
};
if (method !== "GET" && method !== "HEAD") {
options.body = config.data;
// In these cases the browser will automatically set the correct Content-Type,
// but only if that header hasn't been set yet. So that's why we're deleting it.
if (isFormData(options.body) && isStandardBrowserEnv()) {
headers.delete("Content-Type");
}
}
// Some `fetch` implementations will override the Content-Type to text/plain
// when body is a string.
// See https://github.com/langchain-ai/langchainjs/issues/1010
if (typeof options.body === "string") {
options.body = new TextEncoder().encode(options.body);
}
if (config.mode) {
options.mode = config.mode;
}
if (config.cache) {
options.cache = config.cache;
}
if (config.integrity) {
options.integrity = config.integrity;
}
if (config.redirect) {
options.redirect = config.redirect;
}
if (config.referrer) {
options.referrer = config.referrer;
}
if (config.timeout && config.timeout > 0) {
options.signal = AbortSignal.timeout(config.timeout);
}
if (config.signal) {
// this overrides the timeout signal if both are set
options.signal = config.signal;
}
// This config is similar to XHRs withCredentials flag, but with three available values instead of two.
// So if withCredentials is not set, default value 'same-origin' will be used
if (!isUndefined(config.withCredentials)) {
options.credentials = config.withCredentials ? "include" : "omit";
}
// for streaming
if (config.responseType === "stream") {
options.headers.set("Accept", EventStreamContentType);
}
const fullPath = buildFullPath(config.baseURL, config.url);
const url = buildURL(fullPath, config.params, config.paramsSerializer);
// Expected browser to throw error if there is any wrong configuration value
return new Request(url, options);
}
/**
* Note:
*
* From version >= 0.27.0, createError function is replaced by AxiosError class.
* So I copy the old createError function here for backward compatible.
*
*
*
* Create an Error with the specified message, config, error code, request and response.
*
* @param {string} message The error message.
* @param {Object} config The config.
* @param {string} [code] The error code (for example, 'ECONNABORTED').
* @param {Object} [request] The request.
* @param {Object} [response] The response.
* @returns {Error} The created error.
*/
function createError(message, config, code, request, response) {
if (axios.AxiosError && typeof axios.AxiosError === "function") {
return new axios.AxiosError(message, axios.AxiosError[code], config, request, response);
}
const error = new Error(message);
return enhanceError(error, config, code, request, response);
}
/**
*
* Note:
*
* This function is for backward compatible.
*
*
* Update an Error with the specified config, error code, and response.
*
* @param {Error} error The error to update.
* @param {Object} config The config.
* @param {string} [code] The error code (for example, 'ECONNABORTED').
* @param {Object} [request] The request.
* @param {Object} [response] The response.
* @returns {Error} The error.
*/
function enhanceError(error, config, code, request, response) {
error.config = config;
if (code) {
error.code = code;
}
error.request = request;
error.response = response;
error.isAxiosError = true;
error.toJSON = function toJSON() {
return {
// Standard
message: this.message,
name: this.name,
// Microsoft
description: this.description,
number: this.number,
// Mozilla
fileName: this.fileName,
lineNumber: this.lineNumber,
columnNumber: this.columnNumber,
stack: this.stack,
// Axios
config: this.config,
code: this.code,
status: this.response && this.response.status ? this.response.status : null,
};
};
return error;
}