Créez votre propre chatbot IA avec React et l'API ChatGPT
OpenAI a ouvert son API, ce qui signifie que vous pouvez l'utiliser pour créer votre propre chatbot. Cet article vous fournira un exemple simple utilisant React, y compris la demande de la véritable API ChatGPT et la mise en œuvre d'un effet de machine à écrire similaire au site officiel. Voici l' adresse GitHub , et vous devez renseigner votre clé API si vous souhaitez l'exécuter localement.
Ensuite, je vais analyser son moteur.
Demander l'API et mettre en œuvre le streaming
Dans la askméthode exportée dans ask.ts, j'ai simplement écrit une fetchfonction pour la requête.
const res = await fetch('https://api.openai.com/v1/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: '', // Fill your OpenAI key
},
body: JSON.stringify({
stream: true,
max_tokens: 1000,
model: 'gpt-3.5-turbo',
temperature: 0.8,
top_p: 1,
presence_penalty: 1,
messages,
}),
});
const parseChunk = createParseChunkFn((event) => {
if (event.type === 'event') {
onMessage(event.data);
}
});
const reader = res.body?.getReader();
if (reader) {
void (function read() {
reader.read().then(({ done, value }) => {
if (done) {
return;
}
const chunk = new TextDecoder().decode(value);
parseChunk(chunk);
read();
});
})();
}
Étant donné que les données renvoyées par l'API sont au format Markdown, j'utilise react-markdownici comme conteneur d'analyse et j'ajoute des plugins associés, ainsi que des styles de code de highlight.js .
import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
import rehypeHighlight from 'rehype-highlight';
import 'highlight.js/styles/vs2015.css';
<ReactMarkdown
remarkPlugins={[remarkGfm]}
rehypePlugins={[rehypeHighlight]}
>
{l.answering ? answeringContent : l.content}
</ReactMarkdown>
import { useCallback, useState } from 'react';
import { Button, Input, Space, Typography } from 'antd';
import './App.css';
import ask from './ask';
import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
import rehypeHighlight from 'rehype-highlight';
import 'highlight.js/styles/vs2015.css';
const { Text } = Typography;
const enum Role {
Assistant = 'assistant',
User = 'user',
}
export interface Message {
role: Role;
content: string;
}
interface Log extends Message {
id: string;
answering?: boolean;
}
export default () => {
const [question, setQuestion] = useState('');
const [logs, setLogs] = useState<Log[]>(() => []);
const [answeringContent, setAnsweringContent] = useState('');
const askQuestion = useCallback((messages: Message[]) => {
setAnsweringContent('​');
let contents = '';
ask((str) => {
if (!str) return;
if (str === '[DONE]') {
setAnsweringContent('');
setLogs((prev) =>
prev.map((i) => {
const { answering, ...rest } = i;
if (answering) {
return {
...rest,
content: contents,
};
}
return i;
})
);
return;
}
let content = str;
try {
const data = JSON.parse(str);
content = data.choices?.reduce((acc: string, cur: unknown) => {
// @ts-expect-error
acc += cur?.delta?.content ?? '';
return acc;
}, '');
} catch {
// Ignore
// console.error(err);
}
contents += content;
setAnsweringContent(contents);
}, messages);
}, []);
const isAnswering = Boolean(answeringContent);
const onSubmit = () => {
if (isAnswering) return;
const messages = logs.concat({
id: crypto.randomUUID(),
role: Role.User,
content: question,
});
askQuestion(messages.map((i) => ({ role: i.role, content: i.content })));
setQuestion('');
setLogs([
...messages,
{
content: '',
answering: true,
role: Role.Assistant,
id: crypto.randomUUID(),
},
]);
};
return (
<main className='main'>
<div className='chat'>
<div className='logs'>
{logs.map((l) => (
<div key={l.id} className='log'>
<Text strong>{l.role === Role.User ? 'User:' : 'ChatGPT:'}</Text>
<div className={l.answering ? 'streaming' : ''}>
<ReactMarkdown
remarkPlugins={[remarkGfm]}
rehypePlugins={[rehypeHighlight]}
>
{l.answering ? answeringContent : l.content}
</ReactMarkdown>
</div>
</div>
))}
</div>
<Space.Compact>
<Input
allowClear
value={question}
onPressEnter={onSubmit}
placeholder='Enter your question'
onChange={(e) => setQuestion(e.target.value)}
/>
<Button type='primary' onClick={onSubmit} loading={isAnswering}>
{isAnswering ? 'Answering' : 'Submit'}
</Button>
</Space.Compact>
</div>
</main>
);
};
@keyframes blink {
to {
visibility: hidden;
}
}
.streaming > :not(ol):not(ul):not(pre):last-child:after,
.streaming > ol:last-child li:last-child:after,
.streaming > pre:last-child code:after,
.streaming > ul:last-child li:last-child:after {
-webkit-animation: blink 1s steps(5, start) infinite;
animation: blink 1s steps(5, start) infinite;
content: '▋';
margin-left: 0.25rem;
vertical-align: baseline;
}
Ceci termine la construction simple de l'interface utilisateur ChatGPT. Bien qu'il semble rudimentaire, il possède déjà des fonctionnalités de base. Vous pouvez choisir d'y ajouter plus de fonctionnalités et de styles. J'espère que ça t'aide.
Merci d'avoir lu. Si vous aimez de telles histoires et que vous voulez me soutenir, pensez à devenir membre Medium . Il coûte 5 $ par mois et donne un accès illimité au contenu moyen. Je toucherai une petite commission si vous vous inscrivez via mon lien .
![Qu'est-ce qu'une liste liée, de toute façon? [Partie 1]](https://post.nghiatu.com/assets/images/m/max/724/1*Xokk6XOjWyIGCBujkJsCzQ.jpeg)



































