From 605f3d16d0c97a1a17d39825ff528a6b7e5b5399 Mon Sep 17 00:00:00 2001 From: Bruce MacDonald Date: Fri, 19 Jan 2024 15:08:26 -0500 Subject: [PATCH 1/5] update config `address` to `host` --- src/index.ts | 8 ++++---- src/interfaces.ts | 4 ++-- src/utils.ts | 12 ------------ 3 files changed, 6 insertions(+), 18 deletions(-) diff --git a/src/index.ts b/src/index.ts index f7b4420..2c8553a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -31,10 +31,10 @@ export class Ollama { private readonly config: Config private readonly fetch: Fetch - constructor(config?: Partial) { - this.config = { - address: config?.address ?? 'http://127.0.0.1:11434', - } + constructor (config?: Partial) { + this.config = { + host: config?.host ?? "http://127.0.0.1:11434" + }; this.fetch = fetch if (config?.fetch != null) { diff --git a/src/interfaces.ts b/src/interfaces.ts index 1859842..4d801e4 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -1,8 +1,8 @@ export type Fetch = typeof fetch export interface Config { - address: string - fetch?: Fetch + host: string, + fetch?: Fetch } // request types diff --git a/src/utils.ts b/src/utils.ts index 0389f11..548156f 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,17 +1,5 @@ import type { Fetch, ErrorResponse } from './interfaces.js' -export const formatAddress = (address: string): string => { - if (!address.startsWith('http://') && !address.startsWith('https://')) { - address = `http://${address}` - } - - while (address.endsWith('/')) { - address = address.substring(0, address.length - 1) - } - - return address -} - const checkOk = async (response: Response): Promise => { if (!response.ok) { let message = `Error ${response.status}: ${response.statusText}` From 56a4a8791cb3a07d55fd48729d680acad1b191bb Mon Sep 17 00:00:00 2001 From: Bruce MacDonald Date: Mon, 22 Jan 2024 11:50:07 -0800 Subject: [PATCH 2/5] match host formatting --- src/index.ts | 36 +++++++++++++---------------- src/interfaces.ts | 4 ++-- src/utils.ts | 54 +++++++++++++++++++++++++++++++++++++------- test/index.spec.ts | 56 ++++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 118 insertions(+), 32 deletions(-) diff --git a/src/index.ts b/src/index.ts index 2c8553a..9793612 100644 --- a/src/index.ts +++ b/src/index.ts @@ -31,10 +31,10 @@ export class Ollama { private readonly config: Config private readonly fetch: Fetch - constructor (config?: Partial) { - this.config = { - host: config?.host ?? "http://127.0.0.1:11434" - }; + constructor(config?: Partial) { + this.config = { + host: utils.formatHost(config?.host), + } this.fetch = fetch if (config?.fetch != null) { @@ -47,11 +47,9 @@ export class Ollama { request: { stream?: boolean } & Record, ): Promise> { request.stream = request.stream ?? false - const response = await utils.post( - this.fetch, - `${this.config.address}/api/${endpoint}`, - { ...request }, - ) + const response = await utils.post(this.fetch, `${this.config.host}/api/${endpoint}`, { + ...request, + }) if (!response.body) { throw new Error('Missing body') @@ -158,7 +156,7 @@ export class Ollama { const digest = `sha256:${sha256sum}` try { - await utils.head(this.fetch, `${this.config.address}/api/blobs/${digest}`) + await utils.head(this.fetch, `${this.config.host}/api/blobs/${digest}`) } catch (e) { if (e instanceof Error && e.message.includes('404')) { // Create a new readable stream for the fetch request @@ -180,7 +178,7 @@ export class Ollama { await utils.post( this.fetch, - `${this.config.address}/api/blobs/${digest}`, + `${this.config.host}/api/blobs/${digest}`, readableStream, ) } else { @@ -280,25 +278,25 @@ export class Ollama { } async delete(request: DeleteRequest): Promise { - await utils.del(this.fetch, `${this.config.address}/api/delete`, { + await utils.del(this.fetch, `${this.config.host}/api/delete`, { name: request.model, }) return { status: 'success' } } async copy(request: CopyRequest): Promise { - await utils.post(this.fetch, `${this.config.address}/api/copy`, { ...request }) + await utils.post(this.fetch, `${this.config.host}/api/copy`, { ...request }) return { status: 'success' } } async list(): Promise { - const response = await utils.get(this.fetch, `${this.config.address}/api/tags`) + const response = await utils.get(this.fetch, `${this.config.host}/api/tags`) const listResponse = (await response.json()) as ListResponse return listResponse } async show(request: ShowRequest): Promise { - const response = await utils.post(this.fetch, `${this.config.address}/api/show`, { + const response = await utils.post(this.fetch, `${this.config.host}/api/show`, { ...request, }) const showResponse = (await response.json()) as ShowResponse @@ -306,11 +304,9 @@ export class Ollama { } async embeddings(request: EmbeddingsRequest): Promise { - const response = await utils.post( - this.fetch, - `${this.config.address}/api/embeddings`, - { request }, - ) + const response = await utils.post(this.fetch, `${this.config.host}/api/embeddings`, { + request, + }) const embeddingsResponse = (await response.json()) as EmbeddingsResponse return embeddingsResponse } diff --git a/src/interfaces.ts b/src/interfaces.ts index 4d801e4..7a8587b 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -1,8 +1,8 @@ export type Fetch = typeof fetch export interface Config { - host: string, - fetch?: Fetch + host: string + fetch?: Fetch } // request types diff --git a/src/utils.ts b/src/utils.ts index 548156f..b1e13f3 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -25,16 +25,16 @@ const checkOk = async (response: Response): Promise => { } } -export const get = async (fetch: Fetch, address: string): Promise => { - const response = await fetch(formatAddress(address)) +export const get = async (fetch: Fetch, host: string): Promise => { + const response = await fetch(host) await checkOk(response) return response } -export const head = async (fetch: Fetch, address: string): Promise => { - const response = await fetch(formatAddress(address), { +export const head = async (fetch: Fetch, host: string): Promise => { + const response = await fetch(host, { method: 'HEAD', }) @@ -45,7 +45,7 @@ export const head = async (fetch: Fetch, address: string): Promise => export const post = async ( fetch: Fetch, - address: string, + host: string, data?: Record | BodyInit, ): Promise => { const isRecord = (input: any): input is Record => { @@ -54,7 +54,7 @@ export const post = async ( const formattedData = isRecord(data) ? JSON.stringify(data) : data - const response = await fetch(formatAddress(address), { + const response = await fetch(host, { method: 'POST', body: formattedData, }) @@ -66,10 +66,10 @@ export const post = async ( export const del = async ( fetch: Fetch, - address: string, + host: string, data?: Record, ): Promise => { - const response = await fetch(formatAddress(address), { + const response = await fetch(host, { method: 'DELETE', body: JSON.stringify(data), }) @@ -110,3 +110,41 @@ export const parseJSON = async function* ( } } } + +export const formatHost = (host: string): string => { + if (!host) { + host = 'http://127.0.0.1:11434' + } + + let isExplicitProtocol = host.includes('://') + + if (host.startsWith(':')) { + // if host starts with ':', prepend the default hostname + host = `http://127.0.0.1${host}` + isExplicitProtocol = false + } + + if (!isExplicitProtocol) { + host = `http://${host}` + } + + const url = new URL(host) + + let port = url.port + if (!port) { + if (!isExplicitProtocol) { + port = '11434' + } else { + // Assign default ports based on the protocol + port = url.protocol === 'https:' ? '443' : '80' + } + } + + let formattedHost = `${url.protocol}//${url.hostname}:${port}${url.pathname}` + // remove trailing slashes + if (formattedHost.endsWith('/')) { + formattedHost = formattedHost.slice(0, -1) + } + + return formattedHost +} diff --git a/test/index.spec.ts b/test/index.spec.ts index dc90741..d6756fd 100644 --- a/test/index.spec.ts +++ b/test/index.spec.ts @@ -1,3 +1,55 @@ -describe('Empty test', () => { - it('runs', () => {}) +import { formatHost } from '../src/utils' + +describe('formatHost Function Tests', () => { + it('should return default URL for empty string', () => { + expect(formatHost('')).toBe('http://127.0.0.1:11434') + }) + + it('should parse plain IP address', () => { + expect(formatHost('1.2.3.4')).toBe('http://1.2.3.4:11434') + }) + + it('should parse IP address with port', () => { + expect(formatHost('1.2.3.4:56789')).toBe('http://1.2.3.4:56789') + }) + + it('should parse HTTP URL', () => { + expect(formatHost('http://1.2.3.4')).toBe('http://1.2.3.4:80') + }) + + it('should parse HTTPS URL', () => { + expect(formatHost('https://1.2.3.4')).toBe('https://1.2.3.4:443') + }) + + it('should parse HTTPS URL with port', () => { + expect(formatHost('https://1.2.3.4:56789')).toBe('https://1.2.3.4:56789') + }) + + it('should parse domain name', () => { + expect(formatHost('example.com')).toBe('http://example.com:11434') + }) + + it('should parse domain name with port', () => { + expect(formatHost('example.com:56789')).toBe('http://example.com:56789') + }) + + it('should parse HTTP domain', () => { + expect(formatHost('http://example.com')).toBe('http://example.com:80') + }) + + it('should parse HTTPS domain', () => { + expect(formatHost('https://example.com')).toBe('https://example.com:443') + }) + + it('should parse HTTPS domain with port', () => { + expect(formatHost('https://example.com:56789')).toBe('https://example.com:56789') + }) + + it('should handle trailing slash in domain', () => { + expect(formatHost('example.com/')).toBe('http://example.com:11434') + }) + + it('should handle trailing slash in domain with port', () => { + expect(formatHost('example.com:56789/')).toBe('http://example.com:56789') + }) }) From d269573a1091f7adf769ed54801dc164c7ce86de Mon Sep 17 00:00:00 2001 From: Bruce MacDonald Date: Mon, 22 Jan 2024 11:51:42 -0800 Subject: [PATCH 3/5] Update index.ts --- src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index 9793612..abe1f80 100644 --- a/src/index.ts +++ b/src/index.ts @@ -33,7 +33,7 @@ export class Ollama { constructor(config?: Partial) { this.config = { - host: utils.formatHost(config?.host), + host: utils.formatHost(config?.host || 'http://127.0.0.1:11434'), } this.fetch = fetch From 73d44e788ad8144e972f7d1620133bd8c04861b6 Mon Sep 17 00:00:00 2001 From: Bruce MacDonald Date: Mon, 22 Jan 2024 11:52:04 -0800 Subject: [PATCH 4/5] Update index.ts --- src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index abe1f80..e10ebaf 100644 --- a/src/index.ts +++ b/src/index.ts @@ -33,7 +33,7 @@ export class Ollama { constructor(config?: Partial) { this.config = { - host: utils.formatHost(config?.host || 'http://127.0.0.1:11434'), + host: utils.formatHost(config?.host ?? 'http://127.0.0.1:11434'), } this.fetch = fetch From 7f2415f037e6ba721a8b61f34a246a1237f13ccd Mon Sep 17 00:00:00 2001 From: Bruce MacDonald Date: Mon, 22 Jan 2024 11:58:54 -0800 Subject: [PATCH 5/5] Update utils.ts --- src/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils.ts b/src/utils.ts index b1e13f3..bf14dd1 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -113,7 +113,7 @@ export const parseJSON = async function* ( export const formatHost = (host: string): string => { if (!host) { - host = 'http://127.0.0.1:11434' + return 'http://127.0.0.1:11434' } let isExplicitProtocol = host.includes('://')