OpenAI chat
OpenAI chat example
Text streaming with OpenAI API showcasing async generators and JSONLines streaming.
Result
Code
1import { type VovkRequest, post, prefix, openapi, HttpException, HttpStatus } from 'vovk';
2import OpenAI from 'openai';
3
4@prefix('openai')
5export default class OpenAiController {
6 @openapi({
7 summary: 'Create a chat completion',
8 description: 'Create a chat completion using OpenAI and yield the response',
9 })
10 @post('chat', { cors: true, headers: { 'Access-Control-Allow-Origin': 'https://vovk.dev' } })
11 static async *createChatCompletion(
12 req: VovkRequest<{ messages: OpenAI.Chat.Completions.ChatCompletionMessageParam[] }>
13 ) {
14 const { messages } = await req.json();
15 const LIMIT = 5;
16 const openai = new OpenAI();
17
18 if (messages.filter(({ role }) => role === 'user').length > LIMIT) {
19 throw new HttpException(HttpStatus.BAD_REQUEST, `You can only send ${LIMIT} messages at a time`);
20 }
21
22 yield* await openai.chat.completions.create({
23 messages,
24 model: 'gpt-4.1-nano',
25 stream: true,
26 });
27 }
28}
1import { type VovkRequest, post, prefix, openapi, HttpException, HttpStatus } from 'vovk';
2import OpenAI from 'openai';
3
4@prefix('openai')
5export default class OpenAiController {
6 @openapi({
7 summary: 'Create a chat completion',
8 description: 'Create a chat completion using OpenAI and yield the response',
9 })
10 @post('chat', { cors: true, headers: { 'Access-Control-Allow-Origin': 'https://vovk.dev' } })
11 static async *createChatCompletion(
12 req: VovkRequest<{ messages: OpenAI.Chat.Completions.ChatCompletionMessageParam[] }>
13 ) {
14 const { messages } = await req.json();
15 const LIMIT = 5;
16 const openai = new OpenAI();
17
18 if (messages.filter(({ role }) => role === 'user').length > LIMIT) {
19 throw new HttpException(HttpStatus.BAD_REQUEST, `You can only send ${LIMIT} messages at a time`);
20 }
21
22 yield* await openai.chat.completions.create({
23 messages,
24 model: 'gpt-4.1-nano',
25 stream: true,
26 });
27 }
28}
1import { type VovkRequest, post, prefix, openapi, HttpException, HttpStatus } from 'vovk';
2import OpenAI from 'openai';
3
4@prefix('openai')
5export default class OpenAiController {
6 @openapi({
7 summary: 'Create a chat completion',
8 description: 'Create a chat completion using OpenAI and yield the response',
9 })
10 @post('chat', { cors: true, headers: { 'Access-Control-Allow-Origin': 'https://vovk.dev' } })
11 static async *createChatCompletion(
12 req: VovkRequest<{ messages: OpenAI.Chat.Completions.ChatCompletionMessageParam[] }>
13 ) {
14 const { messages } = await req.json();
15 const LIMIT = 5;
16 const openai = new OpenAI();
17
18 if (messages.filter(({ role }) => role === 'user').length > LIMIT) {
19 throw new HttpException(HttpStatus.BAD_REQUEST, `You can only send ${LIMIT} messages at a time`);
20 }
21
22 yield* await openai.chat.completions.create({
23 messages,
24 model: 'gpt-4.1-nano',
25 stream: true,
26 });
27 }
28}
1import { type VovkRequest, post, prefix, openapi, HttpException, HttpStatus } from 'vovk';
2import OpenAI from 'openai';
3
4@prefix('openai')
5export default class OpenAiController {
6 @openapi({
7 summary: 'Create a chat completion',
8 description: 'Create a chat completion using OpenAI and yield the response',
9 })
10 @post('chat', { cors: true, headers: { 'Access-Control-Allow-Origin': 'https://vovk.dev' } })
11 static async *createChatCompletion(
12 req: VovkRequest<{ messages: OpenAI.Chat.Completions.ChatCompletionMessageParam[] }>
13 ) {
14 const { messages } = await req.json();
15 const LIMIT = 5;
16 const openai = new OpenAI();
17
18 if (messages.filter(({ role }) => role === 'user').length > LIMIT) {
19 throw new HttpException(HttpStatus.BAD_REQUEST, `You can only send ${LIMIT} messages at a time`);
20 }
21
22 yield* await openai.chat.completions.create({
23 messages,
24 model: 'gpt-4.1-nano',
25 stream: true,
26 });
27 }
28}
1'use client';
2import { useState } from 'react';
3import { OpenAiRPC } from 'vovk-client';
4import type OpenAI from 'openai';
5
6type Message = OpenAI.Chat.Completions.ChatCompletionMessageParam;
7
8export default function OpenAiExample() {
9 const [messages, setMessages] = useState<Message[]>([]);
10 const [userInput, setUserInput] = useState('');
11 const [error, setError] = useState<Error | null>(null);
12
13 const submit = async () => {
14 if (!userInput) return;
15 setUserInput('');
16 const userMessage: Message = { role: 'user', content: userInput };
17
18 setMessages((messages) => [...messages, userMessage]);
19
20 try {
21 using completion = await OpenAiRPC.createChatCompletion({
22 body: { messages: [...messages, userMessage] },
23 });
24
25 setMessages((mesages) => [...mesages, { role: 'assistant', content: '' } satisfies Message]);
26
27 for await (const chunk of completion) {
28 setMessages((messages) => {
29 const lastMessage = messages[messages.length - 1];
30 return [
31 ...messages.slice(0, -1),
32 { ...lastMessage, content: lastMessage.content + (chunk.choices[0]?.delta?.content ?? '') },
33 ];
34 });
35 }
36 } catch (error) {
37 setError(error as Error);
38 }
39 };
40
41 return (
42 <form
43 onSubmit={(e) => {
44 e.preventDefault();
45 submit();
46 }}
47 >
48 {messages.map((message, index) => (
49 <div key={index}>
50 {message.role === 'assistant' ? '🤖' : '👤'} {(message.content as string) || '...'}
51 </div>
52 ))}
53 {error && <div>❌ {error.message}</div>}
54 <div className="input-group">
55 <input
56 type="text"
57 placeholder="Type a message..."
58 value={userInput}
59 onChange={(e) => setUserInput(e.currentTarget.value)}
60 />
61 <button disabled={!userInput}>Send</button>
62 </div>
63 </form>
64 );
65}
1'use client';
2import { useState } from 'react';
3import { OpenAiRPC } from 'vovk-client';
4import type OpenAI from 'openai';
5
6type Message = OpenAI.Chat.Completions.ChatCompletionMessageParam;
7
8export default function OpenAiExample() {
9 const [messages, setMessages] = useState<Message[]>([]);
10 const [userInput, setUserInput] = useState('');
11 const [error, setError] = useState<Error | null>(null);
12
13 const submit = async () => {
14 if (!userInput) return;
15 setUserInput('');
16 const userMessage: Message = { role: 'user', content: userInput };
17
18 setMessages((messages) => [...messages, userMessage]);
19
20 try {
21 using completion = await OpenAiRPC.createChatCompletion({
22 body: { messages: [...messages, userMessage] },
23 });
24
25 setMessages((mesages) => [...mesages, { role: 'assistant', content: '' } satisfies Message]);
26
27 for await (const chunk of completion) {
28 setMessages((messages) => {
29 const lastMessage = messages[messages.length - 1];
30 return [
31 ...messages.slice(0, -1),
32 { ...lastMessage, content: lastMessage.content + (chunk.choices[0]?.delta?.content ?? '') },
33 ];
34 });
35 }
36 } catch (error) {
37 setError(error as Error);
38 }
39 };
40
41 return (
42 <form
43 onSubmit={(e) => {
44 e.preventDefault();
45 submit();
46 }}
47 >
48 {messages.map((message, index) => (
49 <div key={index}>
50 {message.role === 'assistant' ? '🤖' : '👤'} {(message.content as string) || '...'}
51 </div>
52 ))}
53 {error && <div>❌ {error.message}</div>}
54 <div className="input-group">
55 <input
56 type="text"
57 placeholder="Type a message..."
58 value={userInput}
59 onChange={(e) => setUserInput(e.currentTarget.value)}
60 />
61 <button disabled={!userInput}>Send</button>
62 </div>
63 </form>
64 );
65}
1'use client';
2import { useState } from 'react';
3import { OpenAiRPC } from 'vovk-client';
4import type OpenAI from 'openai';
5
6type Message = OpenAI.Chat.Completions.ChatCompletionMessageParam;
7
8export default function OpenAiExample() {
9 const [messages, setMessages] = useState<Message[]>([]);
10 const [userInput, setUserInput] = useState('');
11 const [error, setError] = useState<Error | null>(null);
12
13 const submit = async () => {
14 if (!userInput) return;
15 setUserInput('');
16 const userMessage: Message = { role: 'user', content: userInput };
17
18 setMessages((messages) => [...messages, userMessage]);
19
20 try {
21 using completion = await OpenAiRPC.createChatCompletion({
22 body: { messages: [...messages, userMessage] },
23 });
24
25 setMessages((mesages) => [...mesages, { role: 'assistant', content: '' } satisfies Message]);
26
27 for await (const chunk of completion) {
28 setMessages((messages) => {
29 const lastMessage = messages[messages.length - 1];
30 return [
31 ...messages.slice(0, -1),
32 { ...lastMessage, content: lastMessage.content + (chunk.choices[0]?.delta?.content ?? '') },
33 ];
34 });
35 }
36 } catch (error) {
37 setError(error as Error);
38 }
39 };
40
41 return (
42 <form
43 onSubmit={(e) => {
44 e.preventDefault();
45 submit();
46 }}
47 >
48 {messages.map((message, index) => (
49 <div key={index}>
50 {message.role === 'assistant' ? '🤖' : '👤'} {(message.content as string) || '...'}
51 </div>
52 ))}
53 {error && <div>❌ {error.message}</div>}
54 <div className="input-group">
55 <input
56 type="text"
57 placeholder="Type a message..."
58 value={userInput}
59 onChange={(e) => setUserInput(e.currentTarget.value)}
60 />
61 <button disabled={!userInput}>Send</button>
62 </div>
63 </form>
64 );
65}
1'use client';
2import { useState } from 'react';
3import { OpenAiRPC } from 'vovk-client';
4import type OpenAI from 'openai';
5
6type Message = OpenAI.Chat.Completions.ChatCompletionMessageParam;
7
8export default function OpenAiExample() {
9 const [messages, setMessages] = useState<Message[]>([]);
10 const [userInput, setUserInput] = useState('');
11 const [error, setError] = useState<Error | null>(null);
12
13 const submit = async () => {
14 if (!userInput) return;
15 setUserInput('');
16 const userMessage: Message = { role: 'user', content: userInput };
17
18 setMessages((messages) => [...messages, userMessage]);
19
20 try {
21 using completion = await OpenAiRPC.createChatCompletion({
22 body: { messages: [...messages, userMessage] },
23 });
24
25 setMessages((mesages) => [...mesages, { role: 'assistant', content: '' } satisfies Message]);
26
27 for await (const chunk of completion) {
28 setMessages((messages) => {
29 const lastMessage = messages[messages.length - 1];
30 return [
31 ...messages.slice(0, -1),
32 { ...lastMessage, content: lastMessage.content + (chunk.choices[0]?.delta?.content ?? '') },
33 ];
34 });
35 }
36 } catch (error) {
37 setError(error as Error);
38 }
39 };
40
41 return (
42 <form
43 onSubmit={(e) => {
44 e.preventDefault();
45 submit();
46 }}
47 >
48 {messages.map((message, index) => (
49 <div key={index}>
50 {message.role === 'assistant' ? '🤖' : '👤'} {(message.content as string) || '...'}
51 </div>
52 ))}
53 {error && <div>❌ {error.message}</div>}
54 <div className="input-group">
55 <input
56 type="text"
57 placeholder="Type a message..."
58 value={userInput}
59 onChange={(e) => setUserInput(e.currentTarget.value)}
60 />
61 <button disabled={!userInput}>Send</button>
62 </div>
63 </form>
64 );
65}
Last updated on