Skip to content

Instantly share code, notes, and snippets.

@bauefikapa
Last active April 9, 2024 09:33
Show Gist options
  • Save bauefikapa/795e68c00cbb785e6fdd71516f7147cc to your computer and use it in GitHub Desktop.
Save bauefikapa/795e68c00cbb785e6fdd71516f7147cc to your computer and use it in GitHub Desktop.
Example React Client Kapa AI Streaming
import React, { useState, useEffect } from 'react';
const App = () => {
const [relevantSources, setRelevantSources] = useState([]);
const [answer, setAnswer] = useState('');
const [identifiers, setIdentifiers] = useState(null);
const [error, setError] = useState(null);
const [feedback, setFeedback] = useState(null);
const process_stream = async (response) => {
const reader = response.body.getReader();
const decoder = new TextDecoder("utf-8");
const delimiter = "\u241E";
const delimiterBytes = new TextEncoder().encode(delimiter);
let buffer = new Uint8Array();
const findDelimiterIndex = (arr) => {
for (let i = 0; i < arr.length - delimiterBytes.length + 1; i++) {
let found = true;
for (let j = 0; j < delimiterBytes.length; j++) {
if (arr[i + j] !== delimiterBytes[j]) {
found = false;
break;
}
}
if (found) {
return i;
}
}
return -1;
};
let result;
while (true) {
result = await reader.read();
if (result.done) break;
buffer = new Uint8Array([...buffer, ...result.value]);
let delimiterIndex;
while ((delimiterIndex = findDelimiterIndex(buffer)) !== -1) {
const chunkBytes = buffer.slice(0, delimiterIndex);
const chunkText = decoder.decode(chunkBytes);
buffer = buffer.slice(delimiterIndex + delimiterBytes.length);
let chunk = JSON.parse(chunkText);
if (chunk.chunk.type === "relevant_sources") {
setRelevantSources(chunk.chunk.content.relevant_sources);
} else if (chunk.chunk.type === "partial_answer") {
setAnswer((prevAnswer) => prevAnswer + chunk.chunk.content.text);
} else if (chunk.chunk.type === "identifiers") {
setIdentifiers(chunk.chunk.content);
} else if (chunk.chunk.type === "error") {
setError(chunk.chunk.content.reason);
break;;
}
}
}
};
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch('<KAPA_API_ENDPOINT>/query/v1/stream?query=What+is+roboflow+and+how+do+i+get+started', {
method: 'GET',
headers: {
"X-API-TOKEN": <YOUR_API_TOKEN>
}
});
if (response.status === 200) {
process_stream(response);
} else {
const message = await response.text();
console.error('Error fetching data:', message);
setError(`Request failed with status code ${response.status}. Message: ${message}`);
}
} catch (error) {
console.error('Error fetching data:', error);
setError(`Request failed: ${error.message}`);
}
};
fetchData();
}, []);
useEffect(() => {
const fetchThreadData = async () => {
if (identifiers) {
try {
const response = await fetch(`<KAPA_API_ENDPOINT>/query/v1/thread/${identifiers.thread_id}/stream?query=What+was+my+last+question?&website_id=6746240c-fe99-40d0-8b56-e122cc85f0fd`, {
method: 'GET',
headers: {
"X-API-TOKEN": <YOUR_API_TOKEN>
}
},);
if (response.status === 200) {
process_stream(response);
} else {
const message = await response.text();
console.error('Error fetching data:', message);
setError(`Request failed with status code ${response.status}. Message: ${message}`);
}
} catch (error) {
console.error('Error fetching thread data:', error);
setError(`Thread request failed: ${error.message}`);
}
}
};
setRelevantSources([]);
setAnswer("");
fetchThreadData();
}, [feedback]);
const handleFeedback = async (question_id, reaction) => {
const apiUrl = `<KAPA_API_ENDPOINT>/query/v1/question-answer/${question_id}/feedback`;
const feedbackData = {
question_id: question_id,
reaction: reaction,
user_identifier: "user-identifier", // be careful what you submit here to not violate any privacy policies. One possibility is to hash the users ip address with sha-256
};
try {
const response = await fetch(apiUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-TOKEN": <YOUR_API_TOKEN>,
},
body: JSON.stringify(feedbackData),
});
if (response.status === 200) {
setFeedback(reaction);
} else {
setError(
"There was an error in submitting your feedback. Please refresh the page and try again."
);
console.error("Error sending feedback:", response.status);
}
} catch (error) {
setError(
"There was an error in submitting your feedback. Please refresh the page and try again."
);
console.error("Error sending feedback:", error);
}
};
return (
<div>
{error ? (
<div>
<h2>Error:</h2>
<p>{error}</p>
</div>
) : (
<>
<h2>Relevant Sources:</h2>
<ul>
{relevantSources.map((source, index) => (
<li key={index}>
<a href={source.source_url} target="_blank" rel="noopener noreferrer">
{source.source_url}
</a>
</li>
))}
</ul>
<h2>Answer:</h2>
<p>{answer}</p>
{identifiers && (
<div>
<h2>Identifiers (for demo purposes only, not to be displayed normally):</h2>
<p>Thread ID: {identifiers.thread_id}</p>
<p>Question Answer ID: {identifiers.question_answer_id}</p>
<h3>Submit your feedback:</h3>
<button onClick={() => handleFeedback(identifiers.question_answer_id, 'upvote')}>Upvote</button>
<button onClick={() => handleFeedback(identifiers.question_answer_id, 'downvote')}>Downvote</button>
{feedback && <p>Your feedback: {feedback}</p>}
</div>
)}
</>
)}
</div>
);
};
export default App;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment