import { html } from 'lit';
import { OpenAI } from 'openai';
import type { ChatMessage } from '../chat/logic.js';
import { pngBase64ToFile } from '../edgeless/edit-image.js';
import {
ChatServiceKind,
createVendor,
EmbeddingServiceKind,
Image2TextServiceKind,
Text2ImageServiceKind,
TextServiceKind,
} from './service-base.js';
export const openaiVendor = createVendor<{
apiKey: string;
}>({
key: 'OpenAI',
color: '#202123',
initData: () => ({ apiKey: '' }),
renderConfigEditor: (data, refresh) => {
return html`
`;
},
});
const toGPTMessages = (
messages: ChatMessage[]
): Array => {
return messages.map(v => {
if (v.role === 'assistant') {
return { role: v.role, content: v.content };
}
return v;
});
};
const askGPT = async (
apiKey: string,
model:
| 'gpt-4'
| 'gpt-3.5-turbo-1106'
| 'gpt-4-vision-preview'
| 'gpt-4-turbo',
messages: Array
) => {
const openai = new OpenAI({
apiKey: apiKey,
dangerouslyAllowBrowser: true,
});
const result = await openai.chat.completions.create({
messages: toGPTMessages(messages),
model: model,
temperature: 0,
max_tokens: 4096,
});
return result.choices[0].message;
};
const askGPTStream = async function* (
apiKey: string,
model:
| 'gpt-4'
| 'gpt-3.5-turbo-1106'
| 'gpt-4-vision-preview'
| 'gpt-4-turbo',
messages: Array
): AsyncIterable {
const openai = new OpenAI({
apiKey: apiKey,
dangerouslyAllowBrowser: true,
});
const result = await openai.chat.completions.create({
stream: true,
messages: toGPTMessages(messages),
model: model,
temperature: 0,
max_tokens: 4096,
});
for await (const message of result) {
yield message.choices[0].delta.content ?? '';
}
};
TextServiceKind.implService({
name: 'GPT3.5 Turbo',
method: data => ({
generateText: async messages => {
const result = await askGPT(data.apiKey, 'gpt-3.5-turbo-1106', messages);
return result.content ?? '';
},
}),
vendor: openaiVendor,
});
TextServiceKind.implService({
name: 'GPT4',
method: data => ({
generateText: async messages => {
const result = await askGPT(data.apiKey, 'gpt-4', messages);
return result.content ?? '';
},
}),
vendor: openaiVendor,
});
ChatServiceKind.implService({
name: 'GPT3.5 Turbo',
method: data => ({
chat: messages => {
return askGPTStream(data.apiKey, 'gpt-3.5-turbo-1106', messages);
},
}),
vendor: openaiVendor,
});
ChatServiceKind.implService({
name: 'GPT4',
method: data => ({
chat: messages => askGPTStream(data.apiKey, 'gpt-4', messages),
}),
vendor: openaiVendor,
});
ChatServiceKind.implService({
name: 'GPT4-Vision',
method: data => ({
chat: messages =>
askGPTStream(data.apiKey, 'gpt-4-vision-preview', messages),
}),
vendor: openaiVendor,
});
Text2ImageServiceKind.implService({
name: 'DALL-E3',
method: data => ({
generateImage: async prompt => {
const apiKey = data.apiKey;
const openai = new OpenAI({
apiKey: apiKey,
dangerouslyAllowBrowser: true,
});
const result = await openai.images.generate({
prompt,
model: 'dall-e-3',
response_format: 'b64_json',
});
return pngBase64ToFile(result.data[0].b64_json ?? '', 'img');
},
}),
vendor: openaiVendor,
});
const embeddings = async (apiKey: string, textList: string[]) => {
const openai = new OpenAI({
apiKey: apiKey,
dangerouslyAllowBrowser: true,
});
const result = await openai.embeddings.create({
input: textList,
model: 'text-embedding-ada-002',
encoding_format: 'float',
});
return result.data.map(v => v.embedding);
};
EmbeddingServiceKind.implService({
name: 'Ada 002',
method: data => ({
generateEmbedding: async text => {
const result = await embeddings(data.apiKey, [text]);
return result[0];
},
generateEmbeddings: async textList => {
return embeddings(data.apiKey, textList);
},
}),
vendor: openaiVendor,
});
Image2TextServiceKind.implService({
name: 'GPT4 Vision',
method: data => ({
generateText: async messages => {
const apiKey = data.apiKey;
const openai = new OpenAI({
apiKey: apiKey,
dangerouslyAllowBrowser: true,
});
const result = await openai.chat.completions.create({
messages,
model: 'gpt-4-vision-preview',
temperature: 0,
max_tokens: 4096,
});
return result.choices[0].message.content ?? '';
},
}),
vendor: openaiVendor,
});