forked from i-am-alice/3rd-devs
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathOpenAIService.ts
161 lines (141 loc) · 5.02 KB
/
OpenAIService.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
import OpenAI, { toFile } from "openai";
import type { ChatCompletionMessageParam, ChatCompletion, ChatCompletionChunk } from "openai/resources/chat/completions";
import { createByModelName } from '@microsoft/tiktokenizer';
import type { CreateEmbeddingResponse } from 'openai/resources/embeddings';
import FileLike from 'openai';
import { Readable } from "stream";
import { ElevenLabsClient, stream } from "elevenlabs";
import Groq from "groq-sdk";
export class OpenAIService {
private openai: OpenAI;
private tokenizers: Map<string, Awaited<ReturnType<typeof createByModelName>>> = new Map();
private readonly IM_START = "<|im_start|>";
private readonly IM_END = "<|im_end|>";
private readonly IM_SEP = "<|im_sep|>";
private elevenlabs: ElevenLabsClient;
private groq: Groq;
constructor() {
this.openai = new OpenAI();
this.elevenlabs = new ElevenLabsClient({
apiKey: process.env.ELEVENLABS_API_KEY
});
this.groq = new Groq({
apiKey: process.env.GROQ_API_KEY
});
}
private async getTokenizer(modelName: string) {
if (!this.tokenizers.has(modelName)) {
const specialTokens: ReadonlyMap<string, number> = new Map([
[this.IM_START, 100264],
[this.IM_END, 100265],
[this.IM_SEP, 100266],
]);
const tokenizer = await createByModelName(modelName, specialTokens);
this.tokenizers.set(modelName, tokenizer);
}
return this.tokenizers.get(modelName)!;
}
async countTokens(messages: ChatCompletionMessageParam[], model: string = 'gpt-4o'): Promise<number> {
const tokenizer = await this.getTokenizer(model);
let formattedContent = '';
messages.forEach((message) => {
formattedContent += `${this.IM_START}${message.role}${this.IM_SEP}${message.content || ''}${this.IM_END}`;
});
formattedContent += `${this.IM_START}assistant${this.IM_SEP}`;
const tokens = tokenizer.encode(formattedContent, [this.IM_START, this.IM_END, this.IM_SEP]);
return tokens.length;
}
async completion(config: {
messages: ChatCompletionMessageParam[],
model?: string,
stream?: boolean,
temperature?: number,
jsonMode?: boolean,
maxTokens?: number
}): Promise<OpenAI.Chat.Completions.ChatCompletion | AsyncIterable<OpenAI.Chat.Completions.ChatCompletionChunk>> {
const { messages, model = "gpt-4o", stream = false, jsonMode = false, maxTokens = 4096, temperature = 0 } = config;
try {
const chatCompletion = await this.openai.chat.completions.create({
messages,
model,
});
return chatCompletion as OpenAI.Chat.Completions.ChatCompletion;
} catch (error) {
console.error("Error in OpenAI completion:", error);
throw error;
}
}
isStreamResponse(response: ChatCompletion | AsyncIterable<ChatCompletionChunk>): response is AsyncIterable<ChatCompletionChunk> {
return Symbol.asyncIterator in response;
}
parseJsonResponse<IResponseFormat>(response: ChatCompletion): IResponseFormat | { error: string, result: boolean } {
try {
const content = response.choices?.[0]?.message?.content;
if (!content) {
throw new Error('Invalid response structure');
}
const parsedContent = JSON.parse(content);
return parsedContent;
} catch (error) {
console.error('Error parsing JSON response:', error);
return { error: 'Failed to process response', result: false };
}
}
async createEmbedding(text: string): Promise<number[]> {
try {
const response: CreateEmbeddingResponse = await this.openai.embeddings.create({
model: "text-embedding-3-large",
input: text,
});
return response.data[0].embedding;
} catch (error) {
console.error("Error creating embedding:", error);
throw error;
}
}
async speak(text: string) {
const response = await this.openai.audio.speech.create({
model: 'tts-1',
voice: 'alloy',
input: text,
});
console.log("Response:", response.body);
const stream = response.body;
return stream;
}
async transcribe(audioBuffer: Buffer): Promise<string> {
console.log("Transcribing audio...");
const transcription = await this.openai.audio.transcriptions.create({
file: await toFile(audioBuffer, 'speech.mp3'),
language: 'pl',
model: 'whisper-1',
});
return transcription.text;
}
async transcribeGroq(audioBuffer: Buffer): Promise<string> {
const transcription = await this.groq.audio.transcriptions.create({
file: await toFile(audioBuffer, 'speech.mp3'),
language: 'pl',
model: 'whisper-large-v3',
});
return transcription.text;
}
async speakEleven(
text: string,
voice: string = "21m00Tcm4TlvDq8ikWAM",
modelId: string = "eleven_turbo_v2_5"
) {
try {
const audioStream = await this.elevenlabs.generate({
voice,
text,
model_id: modelId,
stream: true,
});
return audioStream;
} catch (error) {
console.error("Error in ElevenLabs speech generation:", error);
throw error;
}
}
}