Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions packages/ollama/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,14 @@
"author": "Sergio Gómez Bachiller <[email protected]>",
"license": "Apache-2.0",
"dependencies": {
"@ai-sdk/provider": "^1.0.0",
"@ai-sdk/provider-utils": "^2.0.0",
"@ai-sdk/provider": "^1.1.3",
"@ai-sdk/provider-utils": "^2.2.8",
"partial-json": "0.1.7"
},
"devDependencies": {
"@edge-runtime/vm": "^3.2.0",
"@types/node": "^18.19.56",
"tsup": "^8.3.0",
"@types/node": "^18.19.127",
"tsup": "^8.5.0",
"typescript": "5.6.3",
"zod": "3.23.8"
},
Expand Down
12 changes: 12 additions & 0 deletions packages/ollama/src/convert-to-ollama-chat-messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,18 @@ export function convertToOllamaChatMessages(
})
break
}
case 'reasoning': {
// Reasoning parts are not supported by Ollama, skip them
break
}
case 'redacted-reasoning': {
// Redacted reasoning parts are not supported by Ollama, skip them
break
}
case 'file': {
// File parts are not supported by Ollama, skip them
break
}
default: {
const _exhaustiveCheck: never = part
throw new Error(`Unsupported part: ${_exhaustiveCheck}`)
Expand Down
1 change: 1 addition & 0 deletions packages/ollama/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export type { OllamaProvider, OllamaProviderSettings } from './ollama-provider'
export { createOllama, ollama } from './ollama-provider'
export { VERSION } from './version'
8 changes: 7 additions & 1 deletion packages/ollama/src/ollama-chat-language-model.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
JsonTestServer,
StreamingTestServer,
} from '@ai-sdk/provider-utils/test'
import { describe, expect, it } from 'vitest'
import { describe, expect, it, vi } from 'vitest'

import { createOllama } from '@/ollama-provider'

Expand All @@ -16,6 +16,10 @@ const provider = createOllama()

const model = provider.chat('model')

vi.mock('./version', () => ({
VERSION: '0.0.0-test',
}))

describe('doGenerate', () => {
const server = new JsonTestServer('http://127.0.0.1:11434/api/chat')

Expand Down Expand Up @@ -258,6 +262,7 @@ describe('doGenerate', () => {
'content-type': 'application/json',
'custom-provider-header': 'provider-header-value',
'custom-request-header': 'request-header-value',
'user-agent': 'ai-sdk/ollama/0.0.0-test',
})
})

Expand Down Expand Up @@ -494,6 +499,7 @@ describe('doStream', () => {
expect(requestHeaders).toStrictEqual({
'content-type': 'application/json',
'custom-header': 'test-header',
'user-agent': 'ai-sdk/ollama/0.0.0-test',
})
})
})
7 changes: 5 additions & 2 deletions packages/ollama/src/ollama-chat-language-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ import { mapOllamaFinishReason } from '@/map-ollama-finish-reason'
import { OllamaChatModelId, OllamaChatSettings } from '@/ollama-chat-settings'
import { ollamaFailedResponseHandler } from '@/ollama-error'
import { prepareTools } from '@/prepare-tools'
import { createJsonStreamResponseHandler, removeUndefined } from '@/utils'
import {
createJsonStreamResponseHandler,
removeUndefinedEntries,
} from '@/utils'

interface OllamaChatConfig {
baseURL: string
Expand Down Expand Up @@ -83,7 +86,7 @@ export class OllamaChatLanguageModel implements LanguageModelV1 {
const baseArguments = {
format: responseFormat?.type,
model: this.modelId,
options: removeUndefined({
options: removeUndefinedEntries({
f16_kv: this.settings.f16Kv,
frequency_penalty: frequencyPenalty,
low_vram: this.settings.lowVram,
Expand Down
6 changes: 6 additions & 0 deletions packages/ollama/src/ollama-embedding-model.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { EmbeddingModelV1Embedding } from '@ai-sdk/provider'
import { JsonTestServer } from '@ai-sdk/provider-utils/test'
import { describe, expect, it, vi } from 'vitest'

import { createOllama } from './ollama-provider'

Expand All @@ -12,6 +13,10 @@ const testValues = ['sunny day at the beach', 'rainy day in the city']
const provider = createOllama({})
const model = provider.embedding('all-minilm')

vi.mock('./version', () => ({
VERSION: '0.0.0-test',
}))

describe('doEmbed', () => {
const server = new JsonTestServer('http://127.0.0.1:11434/api/embed')

Expand Down Expand Up @@ -87,6 +92,7 @@ describe('doEmbed', () => {
expect(requestHeaders).toStrictEqual({
'content-type': 'application/json',
'custom-header': 'test-header',
'user-agent': 'ai-sdk/ollama/0.0.0-test',
})
})
})
13 changes: 10 additions & 3 deletions packages/ollama/src/ollama-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import {
OllamaEmbeddingSettings,
} from '@/ollama-embedding-settings'

import { withUserAgentSuffix } from './utils'
import { VERSION } from './version'

export interface OllamaProvider extends ProviderV1 {
(modelId: OllamaChatModelId, settings?: OllamaChatSettings): LanguageModelV1

Expand Down Expand Up @@ -59,9 +62,13 @@ export function createOllama(
const baseURL =
withoutTrailingSlash(options.baseURL) ?? 'http://127.0.0.1:11434/api'

const getHeaders = () => ({
...options.headers,
})
const getHeaders = () =>
withUserAgentSuffix(
{
...options.headers,
},
`ai-sdk/ollama/${VERSION}`,
)

const createChatModel = (
modelId: OllamaChatModelId,
Expand Down
1 change: 1 addition & 0 deletions packages/ollama/src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './remove-undefined'
export * from './response-handler'
export * from './with-user-agent-suffix'
13 changes: 10 additions & 3 deletions packages/ollama/src/utils/remove-undefined.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
export function removeUndefined(object: object) {
/**
* Removes entries from a record where the value is null or undefined.
* @param record - The input object whose entries may be null or undefined.
* @returns A new object containing only entries with non-null and non-undefined values.
*/
export function removeUndefinedEntries<T>(
record: Record<string, T | undefined>,
): Record<string, T> {
return Object.fromEntries(
Object.entries(object).filter(([, v]) => v !== undefined),
)
Object.entries(record).filter(([, value]) => value !== null),
) as Record<string, T>
}
14 changes: 7 additions & 7 deletions packages/ollama/src/utils/response-handler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ describe('createJsonStreamResponseHandler', () => {
})

expect(await convertReadableStreamToArray(stream)).toStrictEqual([
{ success: true, value: { a: 1 } },
{ success: true, value: { a: 2 } },
{ rawValue: { a: 1 }, success: true, value: { a: 1 } },
{ rawValue: { a: 2 }, success: true, value: { a: 2 } },
])
})

Expand All @@ -42,7 +42,7 @@ describe('createJsonStreamResponseHandler', () => {
})

expect(await convertReadableStreamToArray(stream)).toStrictEqual([
{ success: true, value: { a: 1 } },
{ rawValue: { a: 1 }, success: true, value: { a: 1 } },
])
})

Expand All @@ -63,10 +63,10 @@ describe('createJsonStreamResponseHandler', () => {
})

expect(await convertReadableStreamToArray(stream)).toStrictEqual([
{ success: true, value: { a: 1 } },
{ success: true, value: { a: 2 } },
{ success: true, value: { a: 3 } },
{ success: true, value: { a: 4 } },
{ rawValue: { a: 1 }, success: true, value: { a: 1 } },
{ rawValue: { a: 2 }, success: true, value: { a: 2 } },
{ rawValue: { a: 3 }, success: true, value: { a: 3 } },
{ rawValue: { a: 4 }, success: true, value: { a: 4 } },
])
})
})
30 changes: 30 additions & 0 deletions packages/ollama/src/utils/with-user-agent-suffix.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { removeUndefinedEntries } from './remove-undefined'

/**
* Appends suffix parts to the `user-agent` header.
* If a `user-agent` header already exists, the suffix parts are appended to it.
* If no `user-agent` header exists, a new one is created with the suffix parts.
* Automatically removes undefined entries from the headers.
*
* @param headers - The original headers.
* @param userAgentSuffixParts - The parts to append to the `user-agent` header.
* @returns The new headers with the `user-agent` header set or updated.
*/
export function withUserAgentSuffix(
headers: HeadersInit | Record<string, string | undefined> | undefined,
...userAgentSuffixParts: string[]
): Record<string, string> {
const cleanedHeaders = removeUndefinedEntries(
(headers as Record<string, string | undefined>) ?? {},
)

const currentUserAgentHeader = cleanedHeaders['user-agent'] || ''
const newUserAgent = [currentUserAgentHeader, ...userAgentSuffixParts]
.filter(Boolean)
.join(' ')

return {
...cleanedHeaders,
'user-agent': newUserAgent,
}
}
4 changes: 4 additions & 0 deletions packages/ollama/src/version.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Version string of this package injected at build time.
declare const __PACKAGE_VERSION__: string | undefined
export const VERSION: string =
__PACKAGE_VERSION__ === undefined ? '0.0.0-test' : __PACKAGE_VERSION__
9 changes: 9 additions & 0 deletions packages/ollama/tsup.config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
import { readFileSync } from 'node:fs'

import { defineConfig } from 'tsup'

const package_ = JSON.parse(
readFileSync(new URL('package.json', import.meta.url), 'utf8'),
)

export default defineConfig([
{
define: {
__PACKAGE_VERSION__: JSON.stringify(package_.version),
},
dts: true,
entry: ['src/index.ts'],
format: ['cjs', 'esm'],
Expand Down
5 changes: 5 additions & 0 deletions packages/ollama/vitest.edge.config.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import tsconfigPaths from 'vite-tsconfig-paths'
import { defineConfig } from 'vitest/config'

import packageJson from './package.json'

// https://vitejs.dev/config/
export default defineConfig({
define: {
__PACKAGE_VERSION__: JSON.stringify(packageJson.version),
},
plugins: [tsconfigPaths()],
test: {
environment: 'edge-runtime',
Expand Down
5 changes: 5 additions & 0 deletions packages/ollama/vitest.node.config.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import tsconfigPaths from 'vite-tsconfig-paths'
import { defineConfig } from 'vitest/config'

import packageJson from './package.json'

// https://vitejs.dev/config/
export default defineConfig({
define: {
__PACKAGE_VERSION__: JSON.stringify(packageJson.version),
},
plugins: [tsconfigPaths()],
test: {
environment: 'node',
Expand Down
Loading