Skip to content

Deep seek #376

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ yarn.lock
# Sentry Auth Token
.sentryclirc
.DS_Store
public/
public/
.qodo
3 changes: 3 additions & 0 deletions src/bot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ import { llmModelManager } from './modules/llms/utils/llmModelsManager'
import { HmnyBot } from './modules/hmny'
import { LumaBot } from './modules/llms/lumaBot'
import { XaiBot } from './modules/llms/xaiBot'
import { DeepSeekBot } from './modules/llms/deepSeekBot'

Events.EventEmitter.defaultMaxListeners = 30

Expand Down Expand Up @@ -216,6 +217,7 @@ const lumaBot = new LumaBot(payments)
const claudeBot = new ClaudeBot(payments)
const vertexBot = new VertexBot(payments, [llamaAgent])
const xaiBot = new XaiBot(payments)
const deepSeekBot = new DeepSeekBot(payments)
const oneCountryBot = new OneCountryBot(payments)
const translateBot = new TranslateBot()
const telegramPayments = new TelegramPayments(payments)
Expand Down Expand Up @@ -347,6 +349,7 @@ const PayableBots: Record<string, PayableBotConfig> = {
vertexBot: { bot: vertexBot },
lumaBot: { bot: lumaBot },
aixBot: { bot: xaiBot },
deepSeekBot: { bot: deepSeekBot },
openAiBot: {
enabled: (ctx: OnMessageContext) => ctx.session.dalle.isEnabled,
bot: openAiBot
Expand Down
131 changes: 131 additions & 0 deletions src/modules/llms/api/deepseek.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import axios, { type AxiosResponse } from 'axios'
import { type Readable } from 'stream'
import { GrammyError } from 'grammy'
import { pino } from 'pino'

import config from '../../../config'
import { type OnCallBackQueryData, type OnMessageContext, type ChatConversation } from '../../types'
import { type LlmCompletion } from './llmApi'
import { headersStream } from './helper'
import { LlmModelsEnum } from '../utils/llmModelsManager'
import { type ModelParameters } from '../utils/types'
import { prepareConversation } from './openai'

const logger = pino({
name: 'deepSeek - llmsBot',
transport: {
target: 'pino-pretty',
options: { colorize: true }
}
})

const hasValidContent = (text: string): boolean => {
const trimmed = text.trim()
return trimmed.length > 0 && trimmed !== '\n' && !/^\s+$/.test(trimmed)
}

const API_ENDPOINT = config.llms.apiEndpoint // 'http://127.0.0.1:5000' // config.llms.apiEndpoint

export const deepSeekStreamCompletion = async (
conversation: ChatConversation[],
model = LlmModelsEnum.GPT_35_TURBO,
ctx: OnMessageContext | OnCallBackQueryData,
msgId: number,
limitTokens = true,
parameters?: ModelParameters
): Promise<LlmCompletion> => {
logger.info(`Handling ${model} stream completion`)
parameters = parameters ?? {
system: ctx.session.currentPrompt,
max_tokens: +config.openAi.chatGpt.maxTokens
}
const data = {
model,
stream: true,
max_tokens: limitTokens ? parameters.max_tokens : undefined,
messages: prepareConversation(conversation, model, ctx)
}
let wordCount = 0
let wordCountMinimum = 2
const url = `${API_ENDPOINT}/deepseek/completions`
if (!ctx.chat?.id) {
throw new Error('Context chat id should not be empty after openAI streaming')
}
const response: AxiosResponse = await axios.post(url, data, headersStream)

const completionStream: Readable = response.data
let completion = ''
let outputTokens = ''
let inputTokens = ''
let message = ''
for await (const chunk of completionStream) {
const msg = chunk.toString()
if (msg) {
if (msg.includes('Input Tokens:')) {
const tokenMsg = msg.split('Input Tokens: ')[1]
inputTokens = tokenMsg.split('Output Tokens: ')[0]
outputTokens = tokenMsg.split('Output Tokens: ')[1]
completion += msg.split('Input Tokens: ')[0]
completion = completion.split('Input Tokens: ')[0]
} else if (msg.includes('Output Tokens: ')) {
outputTokens = msg.split('Output Tokens: ')[1]
completion = completion.split('Output Tokens: ')[0]
} else {
wordCount++
completion += msg
if (wordCount > wordCountMinimum) {
if (wordCountMinimum < 64) {
wordCountMinimum *= 2
}
completion = completion.replaceAll('...', '')
completion += '...'
wordCount = 0
if (ctx.chat?.id && message !== completion) {
message = completion
await ctx.api
.editMessageText(ctx.chat?.id, msgId, completion)
.catch(async (e: any) => {
if (e instanceof GrammyError) {
if (e.error_code !== 400) {
throw e
} else {
logger.error(e.message)
}
} else {
throw e
}
})
}
}
}
}
}
completion = completion.replaceAll('...', '')
hasValidContent(completion) && await ctx.api
.editMessageText(ctx.chat?.id, msgId, completion)
.catch((e: any) => {
if (e instanceof GrammyError) {
if (e.error_code !== 400) {
throw e
} else {
logger.error(e)
}
} else {
throw e
}
})
const totalOutputTokens = outputTokens // response.headers['x-openai-output-tokens']
const totalInputTokens = inputTokens // response.headers['x-openai-input-tokens']
return {
completion: {
content: completion,
role: 'assistant',
model,
timestamp: Date.now()
},
usage: parseInt(totalOutputTokens, 10) + parseInt(totalInputTokens, 10),
price: 0,
inputTokens: parseInt(totalInputTokens, 10),
outputTokens: parseInt(totalOutputTokens, 10)
}
}
4 changes: 2 additions & 2 deletions src/modules/llms/api/openai.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,9 @@ export async function alterGeneratedImg (

type ConversationOutput = Omit<ChatConversation, 'timestamp' | 'model' | 'id' | 'author' | 'numSubAgents'>

const prepareConversation = (conversation: ChatConversation[], model: string, ctx: OnMessageContext | OnCallBackQueryData): ConversationOutput[] => {
export const prepareConversation = (conversation: ChatConversation[], model: string, ctx: OnMessageContext | OnCallBackQueryData): ConversationOutput[] => {
const messages = conversation.filter(c => c.model === model).map(m => { return { content: m.content, role: m.role } })
if (messages.length !== 1 || model === LlmModelsEnum.O1) {
if (messages.length !== 1 || model === LlmModelsEnum.O3 || model.includes('deep')) {
return messages
}
const systemMessage = {
Expand Down
2 changes: 1 addition & 1 deletion src/modules/llms/api/vertex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export const vertexStreamCompletion = async (
): Promise<LlmCompletion> => {
parameters = parameters ?? {
system: ctx.session.currentPrompt,
max_tokens: +config.openAi.chatGpt.maxTokens
max_tokens_to_sample: +config.openAi.chatGpt.maxTokens
}
const data = {
model,
Expand Down
44 changes: 22 additions & 22 deletions src/modules/llms/dalleBot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
getMessageExtras,
getMinBalance,
getPromptPrice,
getUrlFromText,
// getUrlFromText,
hasCommandPrefix,
MAX_TRIES,
PRICE_ADJUSTMENT,
Expand Down Expand Up @@ -109,10 +109,10 @@ export class DalleBot extends LlmsBase {
if (photo && session.isEnabled) {
const prompt = ctx.message?.caption ?? ctx.message?.text
if (
prompt &&
(ctx.chat?.type === 'private' ||
ctx.hasCommand(this.commandsEnum.VISION))
) {
prompt && ctx.chat?.type === 'private') {
// (ctx.chat?.type === 'private' ||
// ctx.hasCommand(this.commandsEnum.VISION))
// ) {
// && !isNaN(+prompt)
return true
}
Expand Down Expand Up @@ -189,23 +189,23 @@ export class DalleBot extends LlmsBase {
return
}

if (ctx.hasCommand(this.commandsEnum.VISION)) {
const photoUrl = getUrlFromText(ctx)
if (photoUrl) {
const prompt = ctx.match
session.imgRequestQueue.push({
prompt,
photoUrl,
command: 'vision' // !isNaN(+prompt) ? 'alter' : 'vision'
})
if (!session.isProcessingQueue) {
session.isProcessingQueue = true
await this.onImgRequestHandler(ctx).then(() => {
session.isProcessingQueue = false
})
}
}
}
// if (ctx.hasCommand(this.commandsEnum.VISION)) {
// const photoUrl = getUrlFromText(ctx)
// if (photoUrl) {
// const prompt = ctx.match
// session.imgRequestQueue.push({
// prompt,
// photoUrl,
// command: 'vision' // !isNaN(+prompt) ? 'alter' : 'vision'
// })
// if (!session.isProcessingQueue) {
// session.isProcessingQueue = true
// await this.onImgRequestHandler(ctx).then(() => {
// session.isProcessingQueue = false
// })
// }
// }
// }

if (
ctx.hasCommand(this.commands) ||
Expand Down
82 changes: 82 additions & 0 deletions src/modules/llms/deepSeekBot.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { type BotPayments } from '../payment'
import {
type OnMessageContext,
type OnCallBackQueryData,
type ChatConversation
} from '../types'
import { deepSeekStreamCompletion } from './api/deepseek'
import { type LlmCompletion } from './api/llmApi'
import { LlmsBase } from './llmsBase'
import { type ModelVersion } from './utils/llmModelsManager'
import { type ModelParameters } from './utils/types'

export class DeepSeekBot extends LlmsBase {
private readonly claudeModels: ModelVersion[]

constructor (payments: BotPayments) {
super(payments, 'deepSeekBot', 'llms')
}

public getEstimatedPrice (ctx: any): number {
return 0
}

public isSupportedEvent (
ctx: OnMessageContext | OnCallBackQueryData
): boolean {
const hasCommand = ctx.hasCommand(this.supportedCommands)

const chatPrefix = this.hasPrefix(ctx.message?.text ?? '')
if (chatPrefix !== '') {
return true
}
return hasCommand
}

async chatStreamCompletion (
conversation: ChatConversation[],
model: ModelVersion,
ctx: OnMessageContext | OnCallBackQueryData,
msgId: number,
limitTokens: boolean,
parameters?: ModelParameters): Promise<LlmCompletion> {
if (parameters) {
parameters.system = ctx.session.currentPrompt
}
return await deepSeekStreamCompletion(conversation, model, ctx, msgId, limitTokens, parameters)
}

async chatCompletion (
conversation: ChatConversation[],
model: ModelVersion,
ctx: OnMessageContext | OnCallBackQueryData,
hasTools: boolean,
parameters?: ModelParameters
): Promise<LlmCompletion> {
return {
completion: undefined,
usage: 0,
price: 0,
inputTokens: 0,
outputTokens: 0
}
}

public async onEvent (ctx: OnMessageContext | OnCallBackQueryData): Promise<void> {
ctx.transient.analytics.module = this.module
const isSupportedEvent = this.isSupportedEvent(ctx)
if (!isSupportedEvent && ctx.chat?.type !== 'private') {
this.logger.warn(`### unsupported command ${ctx.message?.text}`)
return
}
const model = this.getModelFromContext(ctx)
if (!model) {
this.logger.warn(`### unsupported model for command ${ctx.message?.text}`)
return
}
this.updateSessionModel(ctx, model.version)

const usesTools = ctx.hasCommand([this.commandsEnum.CTOOL, this.commandsEnum.STOOL])
await this.onChat(ctx, model.version, usesTools ? false : this.getStreamOption(model.version), usesTools)
}
}
2 changes: 1 addition & 1 deletion src/modules/llms/llmsBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,7 @@ export abstract class LlmsBase implements PayableBot {
const parameters = this.modelManager.getModelParameters(model)
const response = await this.chatCompletion(conversation, model, ctx, usesTools, parameters)
if (response.completion) {
if (model === this.modelsEnum.O1) {
if (model === this.modelsEnum.O3) {
const msgs = splitTelegramMessage(response.completion.content as string)
await ctx.api.editMessageText(
ctx.chat.id,
Expand Down
2 changes: 1 addition & 1 deletion src/modules/llms/openaiBot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ export class OpenAIBot extends LlmsBase {
usesTools: boolean,
parameters?: ModelParameters
): Promise<LlmCompletion> {
return await chatCompletion(conversation, model, ctx, model !== this.modelsEnum.O1, parameters) // limitTokens doesn't apply for o1-preview
return await chatCompletion(conversation, model, ctx, model !== this.modelsEnum.O3, parameters) // limitTokens doesn't apply for o1-preview
}

hasPrefix (prompt: string): string {
Expand Down
Loading
Loading