UNPKG

apphouse

Version:

Component library for React that uses observable state management and theme-able components.

238 lines (220 loc) 6.21 kB
import { makeAutoObservable } from 'mobx'; import { fetchMe } from '../utils/network/fetchMe'; import { getErrorMessage } from '../utils/Error'; import OpenAI, { ClientOptions } from 'openai'; export interface SpeechCreateParams { voice: string; } const APPHOUSE_PROD_AI_ENDPOINT = 'us-central1-apphouse-cc674'; export const TEST_HOST = 'http://127.0.0.1:5001/apphouse-cc674/us-central1/'; export const PROD_HOST = `https://${APPHOUSE_PROD_AI_ENDPOINT}.cloudfunctions.net/`; export const DEFAULT_AUDIO_VOICE = 'alloy'; interface OpenAiFunctions { getThemeColorsV2: 'getThemeColorsV2'; getDocumentAppConfig: 'getDocumentAppConfig'; getDocumentAppForm: 'getDocumentAppForm'; getDocumentTemplate: 'getDocumentTemplate'; askOpenAi: 'askOpenAi'; getAiImage: 'getAiImage'; } export interface AssistantResponse { type: 'success' | 'error'; message: string; } interface OpenAiFetchProps { /** * If url is not provided, the function name will be used to construct the url * @example https://us-central1-my-project.cloudfunctions.net/my-function */ url?: string; /** * The prompt to be sent to the openAi api */ prompt?: string; /** * The name of the function to be called */ functionName: keyof OpenAiFunctions; } /** * OpenAi class to be used to fetch data from the openai api */ export class OpenAi { response: any = null; loading = false; error: string | null = null; host: string; openaiClient?: OpenAI; constructor( prod = false, host?: string, configuration?: ClientOptions | undefined ) { this.loading = false; this.error = null; this.host = host || prod ? PROD_HOST : TEST_HOST; this.openaiClient = undefined; if (configuration) { this.init(configuration); } makeAutoObservable(this); } setHost(host: string) { this.host = host; } /** * Ask the openai api a question * @param request the request to send to the openai api * @param resource any extra background information to provide to the api * @returns */ ask = async (request: string, resource?: string) => { const response = this.fetch({ prompt: JSON.stringify({ resource: resource || 'your best bet', requirement: request }), functionName: 'askOpenAi' }); return response; }; fetch = async (fetchProps: OpenAiFetchProps) => { const { functionName, url, prompt } = fetchProps; const apiUrl = (url || this.host) + functionName; console.log({ apiUrl }); this.loading = true; const response = await fetchMe(apiUrl, { prompt }); this.loading = false; console.log(response); if (typeof response === 'string') { this.error = response; return undefined; } else { try { const res = response?.json(); return res?.then((r) => { console.log(r); if (r?.message) { try { return JSON.parse(r?.message); } catch (e) { console.log(e); return r; } } else { console.log(r); return r; } }); } catch (e) { console.log(e); return getErrorMessage(e); } } }; /** * Initialize openAi Client * @param config the openai client configuration */ init = (config: ClientOptions) => { this.openaiClient = new OpenAI(config); }; /** * Convert text to audio and start downloading mp3 file * @param text The text to convert to audio */ toAudio = async ( text: string, voiceSelection: SpeechCreateParams['voice'] ) => { const voice = voiceSelection as any; try { const response = await this.openaiClient?.audio.speech.create( { input: text, model: 'tts-1', voice: voice, response_format: 'mp3' }, { __binaryResponse: true } ); console.log('toAudio response', response); // convert readable stream to blob from response and create a url for user to download const stream = response?.body; if (stream) { const reader = stream.getReader(); const chunks = []; let done = false; while (!done) { const { value, done: doneValue } = await reader.read(); done = doneValue; if (value) { // @ts-ignore chunks.push(value); } } const blob = new Blob(chunks, { type: 'audio/mp3' }); const url = URL.createObjectURL(blob); return { blob, url }; } } catch (error) { console.error('Error converting text to audio:', error); // Handle the error as needed return { type: 'error', message: getErrorMessage(error) || 'Unknown error' }; } }; /** * Create a transcription of an audio file * @param file the audio file to transcribe * @returns the transcription of the audio file */ createAudioTranscription = async (file: File): Promise<AssistantResponse> => { try { await this.openaiClient?.audio.transcriptions .create({ model: 'whisper-1', file: file }) .then((res) => { console.log('res', res); return { type: 'success' as any, message: res?.text || '' }; }) .catch((err) => { console.log('error', { err }); const e = { type: 'error' as any, message: getErrorMessage(err) || 'Unknown error' }; return e; }); } catch (error) { return { type: 'error', message: getErrorMessage(error) || 'Unknown error' }; } return { type: 'error', message: 'Unknown error' }; }; } export const openAi = new OpenAi(true); /** * A hook to use the openai instance. * Note: This is a singleton object and should be used with caution. * This will be a global object and will be shared across the application. * @returns the openai instance */ export const useOpenAi = (config?: ClientOptions) => { if (config) { openAi.init(config); } return openAi; };