Skip to content

Instantly share code, notes, and snippets.

@thekid
Last active November 3, 2024 22:08
Show Gist options
  • Save thekid/22d5f9c620f8f523f32bbd9d3006c9e5 to your computer and use it in GitHub Desktop.
Save thekid/22d5f9c620f8f523f32bbd9d3006c9e5 to your computer and use it in GitHub Desktop.
ChatGPT realtime API inside websocket server
<?php
use com\openai\realtime\RealtimeApi;
use io\streams\LinesIn;
use io\{File, Files};
use util\Objects;
use util\log\Logging;
use web\Application;
class TestRT extends Application {
private $ai;
private $connections= [];
public function initialize() {
$this->ai= [
'uri' => 'wss://api.openai.com/v1/realtime?model=gpt-4o-realtime-preview',
'auth' => [
'Authorization' => 'Bearer '.$this->environment->variable('OPENAI_API_KEY'),
'OpenAI-Beta' => 'realtime=v1',
]
];
}
public function message($conn, $message) {
$conn->send("🧑 {$message}\n");
$storage= new File('conversation_'.$conn->id());
$prompt= [
'type' => 'message',
'role' => 'user',
'content' => [['type' => 'input_text', 'text' => $message]],
];
Files::append($storage, json_encode($prompt)."\n");
$api= &$this->connections[$conn->id()];
if (null === $api) {
$api= new RealtimeApi($this->ai['uri']);
// $api->setTrace(Logging::all()->toConsole());
}
// Check connection and (re-)connect if necessary
// See https://platform.openai.com/docs/guides/realtime/overview#continuing-conversations
reconnect: if (!$api->connected()) {
$conn->send("🔌 [");
$session= $api->connect($this->ai['auth']);
$api->transmit(['type' => 'session.update', 'session' => ['modalities' => ['text']]]);
foreach (new LinesIn(new File('conversation_'.$conn->id())) as $line) {
$api->transmit([
'type' => 'conversation.item.create',
'item' => json_decode($line, true),
]);
$conn->send('.');
yield;
}
$conn->send("]\n");
}
if (null === $api->transmit(['type' => 'conversation.item.create', 'item' => $prompt])) {
$api->close();
goto reconnect;
}
$conn->send("🤖 ");
try {
$api->send(['type' => 'response.create', 'response' => ['modalities' => ['text']]]);
do {
$event= $api->receive() ?? ['type' => 'error'];
if ('response.text.delta' === $event['type']) {
$conn->send($event['delta']);
} else if ('response.done' === $event['type']) {
$lines= '';
foreach ($event['response']['output'] as $output) {
$json= json_encode([
'type' => 'message',
'role' => 'assistant',
'content' => $output['content'],
]);
$lines.= $json."\n";
}
Files::append($storage, $lines);
break;
}
yield;
} while ('error' !== $event['type']);
} finally {
$conn->send("\n\n");
}
}
public function routes() {
return [
'/ws' => function($req, $res) {
// Hash websocket key and well-known GUID
$key= $req->header('Sec-WebSocket-Key');
$accept= base64_encode(sha1($key.'258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true));
$res->answer(101);
$res->header('Connection', 'upgrade');
$res->header('Upgrade', 'websocket');
$res->header('Sec-WebSocket-Accept', $accept);
},
'/' => function($req, $res) {
$html= <<<'HTML'
<!DOCTYPE html>
<html>
<head>
<title>WebSocket demo</title>
<style type="text/css">
* {
font-family: system-ui;
font-size: 1.1rem;
}
main {
max-width: 40rem;
margin-inline: auto;
}
#messages {
display: block;
white-space: pre-wrap;
padding-block: .5rem;
}
input {
width: 100%;
padding: .5rem;
}
</style>
</head>
<body>
<main>
<output id="messages"></output>
<form id="message">
<input name="message" autocomplete="off" type="text">
</form>
</main>
</body>
<script type="module">
const $messages = document.getElementById('messages');
const $message = document.getElementById('message');
const ws = new WebSocket('ws://localhost:8080/ws');
ws.onopen = console.log;
ws.onclose = console.warn;
ws.onmessage = e => {
$messages.innerText += e.data;
$message.scrollIntoView();
};
$message.onsubmit = e => {
const $input = $message.elements['message'];
e.preventDefault();
ws.send($input.value);
$input.value = '';
};
</script>
</html>
HTML;
$res->send($html, 'text/html; charset=utf-8');
},
];
}
}
@thekid
Copy link
Author

thekid commented Nov 3, 2024

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment