|
@@ -28,7 +28,11 @@ export class AbortableAsyncIterator<T extends object> {
|
|
private readonly itr: AsyncGenerator<T | ErrorResponse>
|
|
private readonly itr: AsyncGenerator<T | ErrorResponse>
|
|
private readonly doneCallback: () => void
|
|
private readonly doneCallback: () => void
|
|
|
|
|
|
- constructor(abortController: AbortController, itr: AsyncGenerator<T | ErrorResponse>, doneCallback: () => void) {
|
|
|
|
|
|
+ constructor(
|
|
|
|
+ abortController: AbortController,
|
|
|
|
+ itr: AsyncGenerator<T | ErrorResponse>,
|
|
|
|
+ doneCallback: () => void,
|
|
|
|
+ ) {
|
|
this.abortController = abortController
|
|
this.abortController = abortController
|
|
this.itr = itr
|
|
this.itr = itr
|
|
this.doneCallback = doneCallback
|
|
this.doneCallback = doneCallback
|
|
@@ -119,23 +123,27 @@ function getPlatform(): string {
|
|
* - An array of key-value pairs representing headers.
|
|
* - An array of key-value pairs representing headers.
|
|
* @returns {Record<string,string>} - A plain object representing the normalized headers.
|
|
* @returns {Record<string,string>} - A plain object representing the normalized headers.
|
|
*/
|
|
*/
|
|
-function normalizeHeaders(headers?: HeadersInit | undefined): Record<string,string> {
|
|
|
|
|
|
+function normalizeHeaders(headers?: HeadersInit | undefined): Record<string, string> {
|
|
if (headers instanceof Headers) {
|
|
if (headers instanceof Headers) {
|
|
- // If headers are an instance of Headers, convert it to an object
|
|
|
|
- const obj: Record<string, string> = {};
|
|
|
|
- headers.forEach((value, key) => {
|
|
|
|
- obj[key] = value;
|
|
|
|
- });
|
|
|
|
- return obj;
|
|
|
|
|
|
+ // If headers are an instance of Headers, convert it to an object
|
|
|
|
+ const obj: Record<string, string> = {}
|
|
|
|
+ headers.forEach((value, key) => {
|
|
|
|
+ obj[key] = value
|
|
|
|
+ })
|
|
|
|
+ return obj
|
|
} else if (Array.isArray(headers)) {
|
|
} else if (Array.isArray(headers)) {
|
|
- // If headers are in array format, convert them to an object
|
|
|
|
- return Object.fromEntries(headers);
|
|
|
|
|
|
+ // If headers are in array format, convert them to an object
|
|
|
|
+ return Object.fromEntries(headers)
|
|
} else {
|
|
} else {
|
|
- // Otherwise assume it's already a plain object
|
|
|
|
- return headers || {};
|
|
|
|
|
|
+ // Otherwise assume it's already a plain object
|
|
|
|
+ return headers || {}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+const readEnvVar = (obj: object, key: string): string | undefined => {
|
|
|
|
+ return obj[key]
|
|
|
|
+}
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* A wrapper around fetch that adds default headers.
|
|
* A wrapper around fetch that adds default headers.
|
|
* @param fetch {Fetch} - The fetch function to use
|
|
* @param fetch {Fetch} - The fetch function to use
|
|
@@ -155,16 +163,41 @@ const fetchWithHeaders = async (
|
|
} as HeadersInit
|
|
} as HeadersInit
|
|
|
|
|
|
// Normalizes headers into a plain object format.
|
|
// Normalizes headers into a plain object format.
|
|
- options.headers = normalizeHeaders(options.headers);
|
|
|
|
-
|
|
|
|
- // Filter out default headers from custom headers
|
|
|
|
|
|
+ options.headers = normalizeHeaders(options.headers)
|
|
|
|
+
|
|
|
|
+ // Automatically add the API key to the headers if the URL is https://ollama.com
|
|
|
|
+ try {
|
|
|
|
+ const parsed = new URL(url)
|
|
|
|
+ if (parsed.protocol === 'https:' && parsed.hostname === 'ollama.com') {
|
|
|
|
+ const apiKey =
|
|
|
|
+ typeof process === 'object' &&
|
|
|
|
+ process !== null &&
|
|
|
|
+ typeof process.env === 'object' &&
|
|
|
|
+ process.env !== null
|
|
|
|
+ ? readEnvVar(process.env, 'OLLAMA_API_KEY')
|
|
|
|
+ : undefined
|
|
|
|
+ const authorization =
|
|
|
|
+ options.headers['authorization'] || options.headers['Authorization']
|
|
|
|
+ if (!authorization && apiKey) {
|
|
|
|
+ options.headers['Authorization'] = `Bearer ${apiKey}`
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ } catch (error) {
|
|
|
|
+ console.error('error parsing url', error)
|
|
|
|
+ }
|
|
|
|
+
|
|
const customHeaders = Object.fromEntries(
|
|
const customHeaders = Object.fromEntries(
|
|
- Object.entries(options.headers).filter(([key]) => !Object.keys(defaultHeaders).some(defaultKey => defaultKey.toLowerCase() === key.toLowerCase()))
|
|
|
|
|
|
+ Object.entries(options.headers).filter(
|
|
|
|
+ ([key]) =>
|
|
|
|
+ !Object.keys(defaultHeaders).some(
|
|
|
|
+ (defaultKey) => defaultKey.toLowerCase() === key.toLowerCase(),
|
|
|
|
+ ),
|
|
|
|
+ ),
|
|
)
|
|
)
|
|
|
|
|
|
options.headers = {
|
|
options.headers = {
|
|
...defaultHeaders,
|
|
...defaultHeaders,
|
|
- ...customHeaders
|
|
|
|
|
|
+ ...customHeaders,
|
|
}
|
|
}
|
|
|
|
|
|
return fetch(url, options)
|
|
return fetch(url, options)
|
|
@@ -176,9 +209,13 @@ const fetchWithHeaders = async (
|
|
* @param host {string} - The host to fetch
|
|
* @param host {string} - The host to fetch
|
|
* @returns {Promise<Response>} - The fetch response
|
|
* @returns {Promise<Response>} - The fetch response
|
|
*/
|
|
*/
|
|
-export const get = async (fetch: Fetch, host: string, options?: { headers?: HeadersInit }): Promise<Response> => {
|
|
|
|
|
|
+export const get = async (
|
|
|
|
+ fetch: Fetch,
|
|
|
|
+ host: string,
|
|
|
|
+ options?: { headers?: HeadersInit },
|
|
|
|
+): Promise<Response> => {
|
|
const response = await fetchWithHeaders(fetch, host, {
|
|
const response = await fetchWithHeaders(fetch, host, {
|
|
- headers: options?.headers
|
|
|
|
|
|
+ headers: options?.headers,
|
|
})
|
|
})
|
|
|
|
|
|
await checkOk(response)
|
|
await checkOk(response)
|
|
@@ -212,7 +249,7 @@ export const post = async (
|
|
fetch: Fetch,
|
|
fetch: Fetch,
|
|
host: string,
|
|
host: string,
|
|
data?: Record<string, unknown> | BodyInit,
|
|
data?: Record<string, unknown> | BodyInit,
|
|
- options?: { signal?: AbortSignal, headers?: HeadersInit },
|
|
|
|
|
|
+ options?: { signal?: AbortSignal; headers?: HeadersInit },
|
|
): Promise<Response> => {
|
|
): Promise<Response> => {
|
|
const isRecord = (input: any): input is Record<string, unknown> => {
|
|
const isRecord = (input: any): input is Record<string, unknown> => {
|
|
return input !== null && typeof input === 'object' && !Array.isArray(input)
|
|
return input !== null && typeof input === 'object' && !Array.isArray(input)
|
|
@@ -224,7 +261,7 @@ export const post = async (
|
|
method: 'POST',
|
|
method: 'POST',
|
|
body: formattedData,
|
|
body: formattedData,
|
|
signal: options?.signal,
|
|
signal: options?.signal,
|
|
- headers: options?.headers
|
|
|
|
|
|
+ headers: options?.headers,
|
|
})
|
|
})
|
|
|
|
|
|
await checkOk(response)
|
|
await checkOk(response)
|
|
@@ -247,7 +284,7 @@ export const del = async (
|
|
const response = await fetchWithHeaders(fetch, host, {
|
|
const response = await fetchWithHeaders(fetch, host, {
|
|
method: 'DELETE',
|
|
method: 'DELETE',
|
|
body: JSON.stringify(data),
|
|
body: JSON.stringify(data),
|
|
- headers: options?.headers
|
|
|
|
|
|
+ headers: options?.headers,
|
|
})
|
|
})
|
|
|
|
|
|
await checkOk(response)
|
|
await checkOk(response)
|
|
@@ -332,16 +369,16 @@ export const formatHost = (host: string): string => {
|
|
}
|
|
}
|
|
|
|
|
|
// Build basic auth part if present
|
|
// Build basic auth part if present
|
|
- let auth = '';
|
|
|
|
|
|
+ let auth = ''
|
|
if (url.username) {
|
|
if (url.username) {
|
|
- auth = url.username;
|
|
|
|
|
|
+ auth = url.username
|
|
if (url.password) {
|
|
if (url.password) {
|
|
- auth += `:${url.password}`;
|
|
|
|
|
|
+ auth += `:${url.password}`
|
|
}
|
|
}
|
|
- auth += '@';
|
|
|
|
|
|
+ auth += '@'
|
|
}
|
|
}
|
|
|
|
|
|
- let formattedHost = `${url.protocol}//${auth}${url.hostname}:${port}${url.pathname}`;
|
|
|
|
|
|
+ let formattedHost = `${url.protocol}//${auth}${url.hostname}:${port}${url.pathname}`
|
|
// remove trailing slashes
|
|
// remove trailing slashes
|
|
if (formattedHost.endsWith('/')) {
|
|
if (formattedHost.endsWith('/')) {
|
|
formattedHost = formattedHost.slice(0, -1)
|
|
formattedHost = formattedHost.slice(0, -1)
|