From f3966fc287e63a4a0f7b7451e80ffdfb0bb58fe0 Mon Sep 17 00:00:00 2001 From: ToGe3688 Date: Wed, 29 Jan 2025 14:33:43 +0100 Subject: [PATCH] - Added deepseek-ai-provider --- lib/deepseek-ai-provider.js | 202 ++++++++++++++++++++++++++++++++++++ 1 file changed, 202 insertions(+) create mode 100644 lib/deepseek-ai-provider.js diff --git a/lib/deepseek-ai-provider.js b/lib/deepseek-ai-provider.js new file mode 100644 index 0000000..a133e18 --- /dev/null +++ b/lib/deepseek-ai-provider.js @@ -0,0 +1,202 @@ +"use strict"; +/** + * CustomAiProvider class + */ +class DeepseekAiProvider { + /** + * @param adapter - The adapter instance + */ + constructor(adapter) { + this.name = "Deepseek Provider"; + this.apiToken = adapter.config.deep_api_token; + this.adapter = adapter; + this.requestData = {}; + this.responseData = {}; + this.error = null; + this.defaultTimeout = 30000; + this.adapter.log.debug(`Created new ${this.name}`); + } + + /** + * Sends a request to the API + * + * @param requestObject - The request object + * @returns - The response from the AI + * @throws {Error} - If the request fails + */ + async request(requestObject) { + let response = {}; + try { + response = await this.sendRequest( + requestObject.model, + requestObject.messages, + requestObject.max_tokens, + requestObject.system_prompt, + requestObject.temperature, + ); + } catch (error) { + response.error = error.message; + } + return response; + } + + /** + * Checks if the API token is set + * + * @returns - True if the API token is set + */ + apiTokenCheck() { + if (!this.apiToken) { + return false; + } + return true; + } + + /** + * Sends the actual HTTP request to the Deepseek AI API + * + * @param model - The model to use + * @param messages - The messages to send to the AI + * @param max_tokens - The maximum number of tokens to generate + * @param system_prompt - The system prompt + * @param temperature - The temperature + * @returns - The response from the AI + */ + async sendRequest(model, messages, max_tokens = 2000, system_prompt = null, temperature = 0.6) { + const url = "https://api.deepseek.com/v1"; + + if (system_prompt) { + messages.unshift({ role: "system", content: system_prompt }); + } + + // Delete images because Deepseek does not support multimodal requests + const convertedMessages = []; + for (const message of messages) { + if (typeof message.image !== "undefined") { + delete message.image; + } + convertedMessages.push(message); + } + messages = convertedMessages; + + this.adapter.log.debug(`${this.name}: Messages array: ${JSON.stringify(messages)}`); + + // Create the request body + const body = { + model: model, + max_tokens: max_tokens, + temperature: temperature, + messages: messages, + }; + + this.adapter.log.debug(`${this.name}: Request body: ${JSON.stringify(body)}`); + this.requestData = body; + + let response = {}; + try { + response = await fetch(url, { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${this.apiToken}`, + }, + body: JSON.stringify(body), + signal: AbortSignal.timeout(this.defaultTimeout), + }); + } catch (e) { + if (e.name === "TimeoutError") { + this.adapter.log.warn(`${this.name}: Request timed out`); + throw new Error(`Request timed out`); + } else { + this.adapter.log.warn(`${this.name}: Request failed: ${e.message}`); + throw new Error(`Request failed: ${e.message}`); + } + } + + this.adapter.log.debug(`Code: ${response.status}`); + + if (response.status != 200) { + if (response.status == 400) { + this.adapter.log.warn(`${this.name}: API invalid request error`); + const data = await response.json(); + this.adapter.log.warn(`${this.name}: ${data.message}`); + throw new Error(data.message); + } + if (response.status == 401) { + this.adapter.log.warn(`${this.name}: API token invalid`); + throw new Error(`API token invalid`); + } + if (response.status == 403) { + this.adapter.log.warn(`${this.name}: API token forbidden`); + throw new Error(`API token forbidden`); + } + if (response.status == 404) { + this.adapter.log.warn(`${this.name}: API endpoint not found`); + throw new Error(`API endpoint not found`); + } + if (response.status == 413) { + this.adapter.log.warn(`${this.name}: API request too large error`); + throw new Error(`API request too large error`); + } + if (response.status == 429) { + this.adapter.log.warn(`${this.name}: API rate limit exceeded`); + throw new Error(`API rate limit exceeded`); + } + if (response.status == 500) { + this.adapter.log.warn(`${this.name}: API server error`); + throw new Error(`API server error`); + } + if (response.status == 529) { + this.adapter.log.warn(`${this.name}: API overload error`); + throw new Error(`API overload error`); + } + this.adapter.log.warn(`${this.name}: Unknown error http code ${response.status}`); + throw new Error(`Unknown error http code${response.status}`); + } + + const data = await response.json(); + + this.adapter.log.debug(`${this.name}: Data: ${JSON.stringify(data)}`); + this.responseData = data; + + if (!data) { + this.adapter.log.warn(`${this.name}: No data from API error`); + throw new Error(`No data error`); + } + + if (data.error) { + this.adapter.log.warn(`${this.name}: Error from API, see raw response for details: ${data.error.message}`); + throw new Error(`Error from API, see raw response for details: ${data.error.message}`); + } + + if (!data.choices || data.choices.length == 0) { + this.adapter.log.warn(`${this.name}: No answer in data from API error`); + throw new Error(`No answer in data from API error`); + } + + if (data.choices[0].message.content == "") { + this.adapter.log.warn(`${this.name}: Empty assistant answer in data from API`); + throw new Error(`Empty assistant answer in data from API`); + } + + if (!data.usage) { + this.adapter.log.warn(`${this.name}: No usage data in response from API, using defaults`); + data.usage = { + input_tokens: 0, + output_tokens: 0, + }; + } + + const responseObj = { + text: data.choices[0].message.content, + model: model, + tokens_input: data.usage.prompt_tokens, + tokens_output: data.usage.completion_tokens, + error: null, + }; + + return responseObj; + } +} + +module.exports = DeepseekAiProvider;