| | const { z } = require('zod'); |
| | const axios = require('axios'); |
| | const { Ollama } = require('ollama'); |
| | const { sleep } = require('@librechat/agents'); |
| | const { resolveHeaders } = require('@librechat/api'); |
| | const { logger } = require('@librechat/data-schemas'); |
| | const { Constants } = require('librechat-data-provider'); |
| | const { deriveBaseURL } = require('~/utils'); |
| |
|
| | const ollamaPayloadSchema = z.object({ |
| | mirostat: z.number().optional(), |
| | mirostat_eta: z.number().optional(), |
| | mirostat_tau: z.number().optional(), |
| | num_ctx: z.number().optional(), |
| | repeat_last_n: z.number().optional(), |
| | repeat_penalty: z.number().optional(), |
| | temperature: z.number().optional(), |
| | seed: z.number().nullable().optional(), |
| | stop: z.array(z.string()).optional(), |
| | tfs_z: z.number().optional(), |
| | num_predict: z.number().optional(), |
| | top_k: z.number().optional(), |
| | top_p: z.number().optional(), |
| | stream: z.optional(z.boolean()), |
| | model: z.string(), |
| | }); |
| |
|
| | |
| | |
| | |
| | |
| | |
| | const getValidBase64 = (imageUrl) => { |
| | const parts = imageUrl.split(';base64,'); |
| |
|
| | if (parts.length === 2) { |
| | return parts[1]; |
| | } else { |
| | logger.error('Invalid or no Base64 string found in URL.'); |
| | } |
| | }; |
| |
|
| | class OllamaClient { |
| | constructor(options = {}) { |
| | const host = deriveBaseURL(options.baseURL ?? 'http://localhost:11434'); |
| | this.streamRate = options.streamRate ?? Constants.DEFAULT_STREAM_RATE; |
| | this.headers = options.headers ?? {}; |
| | |
| | this.client = new Ollama({ host }); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | static async fetchModels(baseURL, options = {}) { |
| | if (!baseURL) { |
| | return []; |
| | } |
| |
|
| | const ollamaEndpoint = deriveBaseURL(baseURL); |
| |
|
| | const resolvedHeaders = resolveHeaders({ |
| | headers: options.headers, |
| | user: options.user, |
| | }); |
| |
|
| | |
| | const response = await axios.get(`${ollamaEndpoint}/api/tags`, { |
| | headers: resolvedHeaders, |
| | timeout: 5000, |
| | }); |
| |
|
| | const models = response.data.models.map((tag) => tag.name); |
| | return models; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | static formatOpenAIMessages(messages) { |
| | const ollamaMessages = []; |
| |
|
| | for (const message of messages) { |
| | if (typeof message.content === 'string') { |
| | ollamaMessages.push({ |
| | role: message.role, |
| | content: message.content, |
| | }); |
| | continue; |
| | } |
| |
|
| | let aggregatedText = ''; |
| | let imageUrls = []; |
| |
|
| | for (const content of message.content) { |
| | if (content.type === 'text') { |
| | aggregatedText += content.text + ' '; |
| | } else if (content.type === 'image_url') { |
| | imageUrls.push(getValidBase64(content.image_url.url)); |
| | } |
| | } |
| |
|
| | const ollamaMessage = { |
| | role: message.role, |
| | content: aggregatedText.trim(), |
| | }; |
| |
|
| | if (imageUrls.length > 0) { |
| | ollamaMessage.images = imageUrls; |
| | } |
| |
|
| | ollamaMessages.push(ollamaMessage); |
| | } |
| |
|
| | return ollamaMessages; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | async chatCompletion({ payload, onProgress, abortController = null }) { |
| | let intermediateReply = ''; |
| |
|
| | const parameters = ollamaPayloadSchema.parse(payload); |
| | const messages = OllamaClient.formatOpenAIMessages(payload.messages); |
| |
|
| | if (parameters.stream) { |
| | const stream = await this.client.chat({ |
| | messages, |
| | ...parameters, |
| | }); |
| |
|
| | for await (const chunk of stream) { |
| | const token = chunk.message.content; |
| | intermediateReply += token; |
| | onProgress(token); |
| | if (abortController.signal.aborted) { |
| | stream.controller.abort(); |
| | break; |
| | } |
| |
|
| | await sleep(this.streamRate); |
| | } |
| | } |
| | |
| | else { |
| | |
| | } |
| |
|
| | return intermediateReply; |
| | } |
| | catch(err) { |
| | logger.error('[OllamaClient.chatCompletion]', err); |
| | throw err; |
| | } |
| | } |
| |
|
| | module.exports = { OllamaClient, ollamaPayloadSchema }; |
| |
|