OwnGPT.v2 / client /src /utils /api.js
parthib07's picture
Upload 199 files
212c959 verified
const TOKEN_KEY = 'owngpt_v2_token'
export function getToken() {
if (typeof window === 'undefined') return null
return window.localStorage.getItem(TOKEN_KEY)
}
export function saveToken(token) {
if (typeof window === 'undefined') return
if (token) {
window.localStorage.setItem(TOKEN_KEY, token)
} else {
window.localStorage.removeItem(TOKEN_KEY)
}
}
async function parseErrorResponse(response) {
const text = await response.text().catch(() => '')
try {
const payload = JSON.parse(text)
if (Array.isArray(payload.detail)) {
return payload.detail.map((item) => item.msg || JSON.stringify(item)).join('; ')
}
if (typeof payload.detail === 'string') {
return payload.detail
}
if (payload.detail) {
return JSON.stringify(payload.detail)
}
} catch {
return text || `HTTP ${response.status}`
}
return text || `HTTP ${response.status}`
}
async function request(url, options = {}) {
const {
token = getToken(),
headers: customHeaders,
body,
...rest
} = options
const headers = { ...(customHeaders || {}) }
if (token) {
headers.Authorization = `Bearer ${token}`
}
const response = await fetch(url, {
...rest,
headers,
body,
})
if (!response.ok) {
const detail = await parseErrorResponse(response)
const error = new Error(detail || `HTTP ${response.status}`)
error.status = response.status
throw error
}
const contentType = response.headers.get('content-type') || ''
if (contentType.includes('application/json')) {
return response.json()
}
return response.text()
}
export const api = {
get: (url, options = {}) => request(url, { ...options, method: 'GET' }),
delete: (url, options = {}) => request(url, { ...options, method: 'DELETE' }),
post: (url, data, options = {}) =>
request(url, {
...options,
method: 'POST',
headers: { 'Content-Type': 'application/json', ...(options.headers || {}) },
body: JSON.stringify(data),
}),
patch: (url, data, options = {}) =>
request(url, {
...options,
method: 'PATCH',
headers: { 'Content-Type': 'application/json', ...(options.headers || {}) },
body: JSON.stringify(data),
}),
postForm: (url, formData, options = {}) =>
request(url, {
...options,
method: 'POST',
body: formData,
}),
}
function handleSseBlock(block, { onChunk, onDone, onError }) {
const lines = block.split(/\r?\n/)
let eventName = 'message'
let dataString = ''
for (const line of lines) {
if (line.startsWith('event: ')) {
eventName = line.slice(7)
}
if (line.startsWith('data: ')) {
dataString += line.slice(6)
}
}
if (!dataString) return
let data
try {
data = JSON.parse(dataString)
} catch {
return
}
if (eventName === 'chunk' && data.text) {
onChunk?.(data.text)
} else if (eventName === 'done') {
onDone?.(data)
} else if (eventName === 'error') {
onError?.(data.detail || 'Streaming failed')
}
}
export async function streamChat({
payload,
token = getToken(),
signal,
onChunk,
onDone,
onError,
}) {
const response = await fetch('/chat', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...(token ? { Authorization: `Bearer ${token}` } : {}),
},
body: JSON.stringify(payload),
signal,
})
if (!response.ok) {
const detail = await parseErrorResponse(response)
throw new Error(detail || `HTTP ${response.status}`)
}
const reader = response.body?.getReader()
if (!reader) {
throw new Error('Streaming is not supported by this browser.')
}
const decoder = new TextDecoder()
let buffer = ''
let done = false
while (!done) {
const result = await reader.read()
done = result.done
buffer += decoder.decode(result.value || new Uint8Array(), { stream: !done })
const blocks = buffer.split(/\r?\n\r?\n/)
buffer = blocks.pop() || ''
for (const block of blocks) {
handleSseBlock(block, { onChunk, onDone, onError })
}
}
if (buffer.trim()) {
handleSseBlock(buffer, { onChunk, onDone, onError })
}
}