-
-
Save PaulGwamanda/c7baf62f556121504d2296fa6f0a3845 to your computer and use it in GitHub Desktop.
Example React Client Kapa AI Streaming
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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