Skip to content

Commit

Permalink
custom layout for OpenAI Realtime API
Browse files Browse the repository at this point in the history
  • Loading branch information
OvidijusParsiunas committed Jan 13, 2025
1 parent e5dcd44 commit a858e26
Show file tree
Hide file tree
Showing 7 changed files with 84 additions and 5 deletions.
1 change: 1 addition & 0 deletions component/src/deepChat.css
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
@import url('./views/validateKeyProperty/validateKeyPropertyView.css');
@import url('./services/openAI/openAIRealtimeIO.css');
@import url('./views/insertKey/insertKeyView.css');
@import url('./views/error/errorView.css');
@import url('./utils/loading/loading.css');
Expand Down
17 changes: 17 additions & 0 deletions component/src/services/openAI/openAIRealtimeIO.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#deep-chat-openai-realtime-container {
height: 100%;
width: 100%;
}

#deep-chat-openai-realtime-avatar-container {
height: 70%;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
}

#deep-chat-openai-realtime-avatar {
border-radius: 50%;
height: 120px;
}
48 changes: 44 additions & 4 deletions component/src/services/openAI/openAIRealtimeIO.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,66 @@
import {ChatFunctionHandler, OpenAIRealTime} from '../../types/openAI';
import {DirectConnection} from '../../types/directConnection';
import avatarUrl from '../../../assets/person-avatar.png';
import {DirectServiceIO} from '../utils/directServiceIO';
import {ChatFunctionHandler} from '../../types/openAI';
import {OpenAIUtils} from './utils/openAIUtils';
import {APIKey} from '../../types/APIKey';
import {DeepChat} from '../../deepChat';

// https://platform.openai.com/docs/guides/realtime-webrtc
// https://platform.openai.com/docs/api-reference/realtime-server-events/conversation
export class OpenAIRealtimeIO extends DirectServiceIO {
override insertKeyPlaceholderText = 'OpenAI API Key';
override keyHelpUrl = 'https://platform.openai.com/account/api-keys';
url = 'https://api.openai.com/v1/chat/completions';
permittedErrorPrefixes = ['Incorrect'];
_functionHandler?: ChatFunctionHandler;
asyncCallInProgress = false; // used when streaming tools
private readonly _avatarConfig: OpenAIRealTime['avatar'];

constructor(deepChat: DeepChat) {
const directConnectionCopy = JSON.parse(JSON.stringify(deepChat.directConnection)) as DirectConnection;
const apiKey = directConnectionCopy.openAI;
super(deepChat, OpenAIUtils.buildKeyVerificationDetails(), OpenAIUtils.buildHeaders, apiKey);
this.maxMessages ??= -1;
const {key} = directConnectionCopy.openAI as APIKey;
super(deepChat, OpenAIUtils.buildKeyVerificationDetails(), OpenAIUtils.buildHeaders, {key: key || 'asdsd'});
const config = directConnectionCopy.openAI?.realtime as OpenAIRealTime;
if (typeof config === 'object') {
if (config.avatar) this._avatarConfig = config.avatar;
}
this.rawBody.model ??= 'gpt-4o';
this.init();
}

public setUpView(containerElement: HTMLElement, parentElement: HTMLElement) {
containerElement.style.display = 'none';
parentElement.appendChild(this.createContainer());
}

private createContainer() {
const container = document.createElement('div');
container.id = 'deep-chat-openai-realtime-container';
container.appendChild(this.avatarContainer());
return container;
}

private avatarContainer() {
const avatarContainer = document.createElement('div');
avatarContainer.id = 'deep-chat-openai-realtime-avatar-container';
Object.assign(avatarContainer.style, this._avatarConfig?.styles?.container);
avatarContainer.appendChild(this.createAvatar());
return avatarContainer;
}

private createAvatar() {
const avatar = document.createElement('img');
avatar.id = 'deep-chat-openai-realtime-avatar';
Object.assign(avatar.style, this._avatarConfig?.styles?.avatar);
avatar.src = this._avatarConfig?.src || avatarUrl;
return avatar;
}

override isCustomView() {
return true;
}

private async init() {
// Get an ephemeral key from your server - see server code below
// const tokenResponse = await fetch('/session');
Expand Down
2 changes: 2 additions & 0 deletions component/src/services/serviceIO.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ export interface ServiceIO {

isWebModel(): boolean;

isCustomView(): boolean;

isSubmitProgrammaticallyDisabled?: boolean;

sessionId?: string;
Expand Down
4 changes: 4 additions & 0 deletions component/src/services/utils/baseServiceIO.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,4 +156,8 @@ export class BaseServiceIO implements ServiceIO {
public isWebModel() {
return false;
}

public isCustomView() {
return false;
}
}
15 changes: 14 additions & 1 deletion component/src/types/openAI.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import {CustomStyle} from './styles';

// https://platform.openai.com/docs/api-reference/audio/createSpeech
export type OpenAITextToSpeech = {
model?: string;
Expand Down Expand Up @@ -34,6 +36,17 @@ export interface OpenAIImagesDalle3 {
user?: string;
}

// https://platform.openai.com/docs/api-reference/realtime
export type OpenAIRealTime = {
avatar?: {
src?: string;
styles?: {
avatar?: CustomStyle;
container?: CustomStyle;
};
};
};

export type FunctionsDetails = {name: string; arguments: string}[];

export type AssistantFunctionHandlerResponse =
Expand Down Expand Up @@ -104,7 +117,7 @@ export type OpenAIChat = {
export interface OpenAI {
chat?: true | OpenAIChat;
assistant?: true | OpenAIAssistant;
realtime?: true;
realtime?: true | OpenAIRealTime;
images?: true | OpenAIImagesDalle2 | OpenAIImagesDalle3;
textToSpeech?: true | OpenAITextToSpeech;
speechToText?: true | OpenAISpeechToText;
Expand Down
2 changes: 2 additions & 0 deletions component/src/views/chat/chatView.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import {OpenAIRealtimeIO} from '../../services/openAI/openAIRealtimeIO';
import {ElementUtils} from '../../utils/element/elementUtils';
import {Websocket} from '../../utils/HTTP/websocket';
import {ServiceIO} from '../../services/serviceIO';
Expand All @@ -19,5 +20,6 @@ export class ChatView {
public static render(deepChat: DeepChat, containerRef: HTMLElement, serviceIO: ServiceIO, panel?: HTMLElement) {
const containerElement = ChatView.createElements(deepChat, serviceIO, panel);
containerRef.replaceChildren(containerElement);
if (serviceIO.isCustomView()) (serviceIO as OpenAIRealtimeIO).setUpView(containerElement, containerRef);
}
}

0 comments on commit a858e26

Please sign in to comment.