Skip to content

Commit 1072c6f

Browse files
committed
add deepseek bot + fix deep seek streaming service
1 parent c0909ce commit 1072c6f

File tree

6 files changed

+236
-2
lines changed

6 files changed

+236
-2
lines changed

src/bot.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ import { llmModelManager } from './modules/llms/utils/llmModelsManager'
6060
import { HmnyBot } from './modules/hmny'
6161
import { LumaBot } from './modules/llms/lumaBot'
6262
import { XaiBot } from './modules/llms/xaiBot'
63+
import { DeepSeekBot } from './modules/llms/deepSeekBot'
6364

6465
Events.EventEmitter.defaultMaxListeners = 30
6566

@@ -216,6 +217,7 @@ const lumaBot = new LumaBot(payments)
216217
const claudeBot = new ClaudeBot(payments)
217218
const vertexBot = new VertexBot(payments, [llamaAgent])
218219
const xaiBot = new XaiBot(payments)
220+
const deepSeekBot = new DeepSeekBot(payments)
219221
const oneCountryBot = new OneCountryBot(payments)
220222
const translateBot = new TranslateBot()
221223
const telegramPayments = new TelegramPayments(payments)
@@ -347,6 +349,7 @@ const PayableBots: Record<string, PayableBotConfig> = {
347349
vertexBot: { bot: vertexBot },
348350
lumaBot: { bot: lumaBot },
349351
aixBot: { bot: xaiBot },
352+
deepSeekBot: { bot: deepSeekBot },
350353
openAiBot: {
351354
enabled: (ctx: OnMessageContext) => ctx.session.dalle.isEnabled,
352355
bot: openAiBot

src/modules/llms/api/deepseek.ts

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
import axios, { type AxiosResponse } from 'axios'
2+
import { type Readable } from 'stream'
3+
import { GrammyError } from 'grammy'
4+
import { pino } from 'pino'
5+
6+
import config from '../../../config'
7+
import { type OnCallBackQueryData, type OnMessageContext, type ChatConversation } from '../../types'
8+
import { type LlmCompletion } from './llmApi'
9+
import { headersStream } from './helper'
10+
import { LlmModelsEnum } from '../utils/llmModelsManager'
11+
import { type ModelParameters } from '../utils/types'
12+
import { prepareConversation } from './openai'
13+
14+
const logger = pino({
15+
name: 'deepSeek - llmsBot',
16+
transport: {
17+
target: 'pino-pretty',
18+
options: { colorize: true }
19+
}
20+
})
21+
22+
const API_ENDPOINT = config.llms.apiEndpoint
23+
24+
export const deepSeekStreamCompletion = async (
25+
conversation: ChatConversation[],
26+
model = LlmModelsEnum.GPT_35_TURBO,
27+
ctx: OnMessageContext | OnCallBackQueryData,
28+
msgId: number,
29+
limitTokens = true,
30+
parameters?: ModelParameters
31+
): Promise<LlmCompletion> => {
32+
logger.info(`Handling ${model} stream completion`)
33+
parameters = parameters ?? {
34+
system: ctx.session.currentPrompt,
35+
max_tokens: +config.openAi.chatGpt.maxTokens
36+
}
37+
const data = {
38+
model,
39+
stream: true,
40+
system: parameters.system,
41+
max_tokens: limitTokens ? parameters.max_tokens : undefined,
42+
messages: prepareConversation(conversation, model, ctx) // .map(m => { return { content: m.content, role: m.role } })
43+
}
44+
let wordCount = 0
45+
let wordCountMinimum = 2
46+
const url = `${API_ENDPOINT}/deepseek/completions`
47+
if (!ctx.chat?.id) {
48+
throw new Error('Context chat id should not be empty after openAI streaming')
49+
}
50+
51+
const response: AxiosResponse = await axios.post(url, data, headersStream)
52+
53+
const completionStream: Readable = response.data
54+
let completion = ''
55+
let outputTokens = ''
56+
let inputTokens = ''
57+
let message = ''
58+
for await (const chunk of completionStream) {
59+
const msg = chunk.toString()
60+
if (msg) {
61+
if (msg.includes('Input Tokens:')) {
62+
const tokenMsg = msg.split('Input Tokens: ')[1]
63+
inputTokens = tokenMsg.split('Output Tokens: ')[0]
64+
outputTokens = tokenMsg.split('Output Tokens: ')[1]
65+
completion += msg.split('Input Tokens: ')[0]
66+
completion = completion.split('Input Tokens: ')[0]
67+
} else if (msg.includes('Output Tokens: ')) {
68+
outputTokens = msg.split('Output Tokens: ')[1]
69+
completion = completion.split('Output Tokens: ')[0]
70+
} else {
71+
wordCount++
72+
completion += msg
73+
if (wordCount > wordCountMinimum) {
74+
if (wordCountMinimum < 64) {
75+
wordCountMinimum *= 2
76+
}
77+
completion = completion.replaceAll('...', '')
78+
completion += '...'
79+
wordCount = 0
80+
if (ctx.chat?.id && message !== completion) {
81+
message = completion
82+
await ctx.api
83+
.editMessageText(ctx.chat?.id, msgId, completion)
84+
.catch(async (e: any) => {
85+
if (e instanceof GrammyError) {
86+
if (e.error_code !== 400) {
87+
throw e
88+
} else {
89+
logger.error(e.message)
90+
}
91+
} else {
92+
throw e
93+
}
94+
})
95+
}
96+
}
97+
}
98+
}
99+
}
100+
completion = completion.replaceAll('...', '')
101+
await ctx.api
102+
.editMessageText(ctx.chat?.id, msgId, completion)
103+
.catch((e: any) => {
104+
if (e instanceof GrammyError) {
105+
if (e.error_code !== 400) {
106+
throw e
107+
} else {
108+
logger.error(e)
109+
}
110+
} else {
111+
throw e
112+
}
113+
})
114+
const totalOutputTokens = outputTokens // response.headers['x-openai-output-tokens']
115+
const totalInputTokens = inputTokens // response.headers['x-openai-input-tokens']
116+
return {
117+
completion: {
118+
content: completion,
119+
role: 'assistant',
120+
model,
121+
timestamp: Date.now()
122+
},
123+
usage: parseInt(totalOutputTokens, 10) + parseInt(totalInputTokens, 10),
124+
price: 0,
125+
inputTokens: parseInt(totalInputTokens, 10),
126+
outputTokens: parseInt(totalOutputTokens, 10)
127+
}
128+
}

src/modules/llms/api/openai.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ export async function alterGeneratedImg (
8181

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

84-
const prepareConversation = (conversation: ChatConversation[], model: string, ctx: OnMessageContext | OnCallBackQueryData): ConversationOutput[] => {
84+
export const prepareConversation = (conversation: ChatConversation[], model: string, ctx: OnMessageContext | OnCallBackQueryData): ConversationOutput[] => {
8585
const messages = conversation.filter(c => c.model === model).map(m => { return { content: m.content, role: m.role } })
8686
if (messages.length !== 1 || model === LlmModelsEnum.O1) {
8787
return messages

src/modules/llms/deepSeekBot.ts

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import { type BotPayments } from '../payment'
2+
import {
3+
type OnMessageContext,
4+
type OnCallBackQueryData,
5+
type ChatConversation
6+
} from '../types'
7+
import { deepSeekStreamCompletion } from './api/deepseek'
8+
import { type LlmCompletion } from './api/llmApi'
9+
import { LlmsBase } from './llmsBase'
10+
import { type ModelVersion } from './utils/llmModelsManager'
11+
import { type ModelParameters } from './utils/types'
12+
13+
export class DeepSeekBot extends LlmsBase {
14+
private readonly claudeModels: ModelVersion[]
15+
16+
constructor (payments: BotPayments) {
17+
super(payments, 'deepSeekBot', 'llms')
18+
}
19+
20+
public getEstimatedPrice (ctx: any): number {
21+
return 0
22+
}
23+
24+
public isSupportedEvent (
25+
ctx: OnMessageContext | OnCallBackQueryData
26+
): boolean {
27+
const hasCommand = ctx.hasCommand(this.supportedCommands)
28+
29+
const chatPrefix = this.hasPrefix(ctx.message?.text ?? '')
30+
if (chatPrefix !== '') {
31+
return true
32+
}
33+
return hasCommand
34+
}
35+
36+
async chatStreamCompletion (
37+
conversation: ChatConversation[],
38+
model: ModelVersion,
39+
ctx: OnMessageContext | OnCallBackQueryData,
40+
msgId: number,
41+
limitTokens: boolean,
42+
parameters?: ModelParameters): Promise<LlmCompletion> {
43+
if (parameters) {
44+
parameters.system = ctx.session.currentPrompt
45+
}
46+
return await deepSeekStreamCompletion(conversation, model, ctx, msgId, limitTokens, parameters)
47+
}
48+
49+
async chatCompletion (
50+
conversation: ChatConversation[],
51+
model: ModelVersion,
52+
ctx: OnMessageContext | OnCallBackQueryData,
53+
hasTools: boolean,
54+
parameters?: ModelParameters
55+
): Promise<LlmCompletion> {
56+
return {
57+
completion: undefined,
58+
usage: 0,
59+
price: 0,
60+
inputTokens: 0,
61+
outputTokens: 0
62+
}
63+
}
64+
65+
public async onEvent (ctx: OnMessageContext | OnCallBackQueryData): Promise<void> {
66+
ctx.transient.analytics.module = this.module
67+
const isSupportedEvent = this.isSupportedEvent(ctx)
68+
if (!isSupportedEvent && ctx.chat?.type !== 'private') {
69+
this.logger.warn(`### unsupported command ${ctx.message?.text}`)
70+
return
71+
}
72+
const model = this.getModelFromContext(ctx)
73+
if (!model) {
74+
this.logger.warn(`### unsupported model for command ${ctx.message?.text}`)
75+
return
76+
}
77+
this.updateSessionModel(ctx, model.version)
78+
79+
const usesTools = ctx.hasCommand([this.commandsEnum.CTOOL, this.commandsEnum.STOOL])
80+
await this.onChat(ctx, model.version, usesTools ? false : this.getStreamOption(model.version), usesTools)
81+
}
82+
}

src/modules/llms/utils/llmsData.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,21 @@ export const llmData: LLMData = {
209209
maxContextTokens: 131072,
210210
chargeType: 'TOKEN',
211211
stream: false
212+
},
213+
deepSeek: {
214+
provider: 'deepseek',
215+
name: 'gpt-35-turbo',
216+
fullName: 'GPT-3.5 Turbo',
217+
botName: 'deepSeekBot',
218+
version: 'gpt-3.5-turbo',
219+
commands: ['ds'],
220+
prefix: ['ds. '],
221+
apiSpec: 'https://docs.x.ai/api#introduction',
222+
inputPrice: 0.005,
223+
outputPrice: 0.015,
224+
maxContextTokens: 131072,
225+
chargeType: 'TOKEN',
226+
stream: true
212227
}
213228
},
214229
imageModels: {
@@ -269,6 +284,12 @@ export const llmData: LLMData = {
269284
max_tokens: +config.openAi.chatGpt.maxTokens
270285
}
271286
},
287+
deepseek: {
288+
defaultParameters: {
289+
// system: config.openAi.chatGpt.chatCompletionContext,
290+
max_tokens: +config.openAi.chatGpt.maxTokens
291+
}
292+
},
272293
luma: {
273294
defaultParameters: {
274295
// system: config.openAi.chatGpt.chatCompletionContext,

src/modules/llms/utils/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export type Provider = 'openai' | 'claude' | 'vertex' | 'xai' | 'luma'
1+
export type Provider = 'openai' | 'claude' | 'vertex' | 'xai' | 'luma' | 'deepseek'
22
export type ChargeType = 'TOKEN' | 'CHAR'
33

44
export type DalleImageSize = '1024x1024' | '1024x1792' | '1792x1024'

0 commit comments

Comments
 (0)