Skip to content

Commit

Permalink
Changed data process methods to atomically track current API state (a…
Browse files Browse the repository at this point in the history
  • Loading branch information
kudo-sync-bot committed Jan 19, 2025
1 parent a2e6e04 commit c2848d4
Showing 1 changed file with 30 additions and 29 deletions.
59 changes: 30 additions & 29 deletions chatgpt/amazongpt/amazongpt.user.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// @description Adds the magic of AI to Amazon shopping
// @author KudoAI
// @namespace https://kudoai.com
// @version 2025.1.18.5
// @version 2025.1.18.6
// @license MIT
// @icon https://amazongpt.kudoai.com/assets/images/icons/amazongpt/black-gold-teal/icon48.png?v=0fddfc7
// @icon64 https://amazongpt.kudoai.com/assets/images/icons/amazongpt/black-gold-teal/icon64.png?v=0fddfc7
Expand Down Expand Up @@ -2595,21 +2595,21 @@
}

// Get/show answer from AI
const reqMethod = apis[get.reply.api].method
const reqAPI = get.reply.api, reqMethod = apis[reqAPI].method
const xhrConfig = {
headers: api.createHeaders(get.reply.api), method: reqMethod,
headers: api.createHeaders(reqAPI), method: reqMethod,
responseType: config.streamingDisabled || !config.proxyAPIenabled ? 'text' : 'stream',
onerror: err => { log.error(err)
if (!config.proxyAPIenabled)
appAlert(!config.openAIkey ? 'login' : ['openAInotWorking', 'suggestProxy'])
else api.tryNew(get.reply)
},
onload: resp => dataProcess.text(get.reply, resp),
onloadstart: resp => dataProcess.stream(get.reply, resp),
url: ( apis[get.reply.api].endpoints?.completions || apis[get.reply.api].endpoint )
+ ( reqMethod == 'GET' ? `?q=${encodeURIComponent(msgChain[msgChain.length -1].content)}` : '' )
onload: resp => dataProcess.text(resp, { caller: get.reply, callerAPI: reqAPI }),
onloadstart: resp => dataProcess.stream(resp, { caller: get.reply, callerAPI: reqAPI }),
url: ( apis[reqAPI].endpoints?.completions || apis[reqAPI].endpoint )
+ ( reqMethod == 'GET' ? `?q=${encodeURIComponent(msgChain[msgChain.length -1].content)}` : '' )
}
if (reqMethod == 'POST') xhrConfig.data = await api.createPayload(get.reply.api, msgChain)
if (reqMethod == 'POST') xhrConfig.data = await api.createPayload(reqAPI, msgChain)
xhr(xhrConfig)
}
}
Expand All @@ -2624,32 +2624,33 @@
return new RegExp([...failFlags, ...escapedAPIurls].join('|'))
},

stream(caller, stream) {
stream(resp, { caller, callerAPI }) {
if (config.streamingDisabled || !config.proxyAPIenabled) return
log.caller = `get.${caller.name}() » dataProcess.stream()`
const failFlagsAndURLs = dataProcess.initFailFlags(caller.api),
reader = stream.response.getReader() ; let accumulatedChunks = ''
reader.read().then(processStreamText).catch(err => log.error('Error processing stream', err.message))
const failFlagsAndURLs = this.initFailFlags(callerAPI),
reader = resp.response.getReader() ; let accumulatedChunks = ''
reader.read().then(result => processStreamText(callerAPI, result))
.catch(err => log.error('Error processing stream', err.message))

function processStreamText({ done, value }) {
function processStreamText(callerAPI, { done, value }) {

// Handle stream done
let chunk = new TextDecoder('utf8').decode(new Uint8Array(value))
if (done || chunk.includes(apis[caller.api].watermark)) return handleProcessCompletion()
if (done || chunk.includes(apis[callerAPI].watermark)) return handleProcessCompletion()
if (env.browser.isChromium) { // clear/add timeout since reader.read() doesn't signal done
clearTimeout(this.timeout) ; this.timeout = setTimeout(handleProcessCompletion, 500) }

// Process/show chunk
if (caller.api == 'MixerBox AI') { // pre-process chunks
if (callerAPI == 'MixerBox AI') { // pre-process chunks
const extractedChunks = Array.from(chunk.matchAll(/data:(.*)/g), match => match[1]
.replace(/\[SPACE\]/g, ' ').replace(/\[NEWLINE\]/g, '\n'))
.filter(match => !/message_(?:start|end)|done/.test(match))
chunk = extractedChunks.join('')
}
accumulatedChunks = apis[caller.api].accumulatesText ? chunk : accumulatedChunks + chunk
accumulatedChunks = apis[callerAPI].accumulatesText ? chunk : accumulatedChunks + chunk
try { // to show stream text
let textToShow = ''
if (caller.api == 'GPTforLove') { // extract parentID + latest chunk text
if (callerAPI == 'GPTforLove') { // extract parentID + latest chunk text
const jsonLines = accumulatedChunks.split('\n'),
nowResult = JSON.parse(jsonLines[jsonLines.length -1])
if (nowResult.id) apis.GPTforLove.parentID = nowResult.id // for contextual replies
Expand All @@ -2663,15 +2664,15 @@
if (caller.status != 'done' && !caller.sender) api.tryNew(caller)
return
} else if (caller.status != 'done') { // app waiting or sending
if (!caller.sender) caller.sender = caller.api // app is waiting, become sender
if (caller.sender == caller.api // app is sending from this caller.api
if (!caller.sender) caller.sender = callerAPI // app is waiting, become sender
if (caller.sender == callerAPI // app is sending from this api
&& textToShow.trim() != '' // empty chunk not read
) show.reply(textToShow)
}
} catch (err) { log.error('Error showing stream', err.message) }
return reader.read().then(({ done, value }) => {
if (caller.sender == caller.api) // am designated sender, recurse
processStreamText({ done, value })
if (caller.sender == callerAPI) // am designated sender, recurse
processStreamText(callerAPI, { done, value })
}).catch(err => log.error('Error reading stream', err.message))
}

Expand All @@ -2686,22 +2687,22 @@
}
},

text(caller, resp) {
text(resp, { caller, callerAPI }) {
return new Promise(() => {
if (caller == get.reply && config.proxyAPIenabled && !config.streamingDisabled
|| caller.status == 'done') return
log.caller = `get.${caller.name}() » dataProcess.text()`
const failFlagsAndURLs = dataProcess.initFailFlags(caller.api) ; let respText = ''
const failFlagsAndURLs = this.initFailFlags(callerAPI) ; let respText = ''
if (resp.status != 200) {
log.error('Response status', resp.status)
log.info('Response text', resp.response || resp.responseText)
if (caller == get.reply && caller.api == 'OpenAI')
if (caller == get.reply && callerAPI == 'OpenAI')
appAlert(resp.status == 401 ? 'login'
: resp.status == 403 ? 'checkCloudflare'
: resp.status == 429 ? ['tooManyRequests', 'suggestProxy']
: ['openAInotWorking', 'suggestProxy'] )
else api.tryNew(caller)
} else if (caller.api == 'OpenAI' && resp.response) {
} else if (callerAPI == 'OpenAI' && resp.response) {
const failMatch = failFlagsAndURLs.exec(resp.response)
if (failMatch) { // suggest proxy
log.dev('Response text', resp.response)
Expand All @@ -2714,7 +2715,7 @@
} catch (err) { handleProcessError(err) }
}
} else if (resp.responseText) { // show response
if (/AIchatOS|FREEGPT|ToYaml/.test(caller.api)) {
if (/AIchatOS|FREEGPT|ToYaml/.test(callerAPI)) {
try {
const text = resp.responseText, chunkSize = 1024
let currentIdx = 0
Expand All @@ -2724,14 +2725,14 @@
}
handleProcessCompletion()
} catch (err) { handleProcessError(err) }
} else if (caller.api == 'GPTforLove') {
} else if (callerAPI == 'GPTforLove') {
try {
let chunks = resp.responseText.trim().split('\n'),
lastObj = JSON.parse(chunks[chunks.length - 1])
if (lastObj.id) apis.GPTforLove.parentID = lastObj.id
respText = lastObj.text ; handleProcessCompletion()
} catch (err) { handleProcessError(err) }
} else if (caller.api == 'MixerBox AI') {
} else if (callerAPI == 'MixerBox AI') {
try {
const extractedData = Array.from(resp.responseText.matchAll(/data:(.*)/g), match => match[1]
.replace(/\[SPACE\]/g, ' ').replace(/\[NEWLINE\]/g, '\n'))
Expand All @@ -2751,7 +2752,7 @@
api.tryNew(caller)
} else {
caller.status = 'done' ; api.clearTimedOut(caller.triedAPIs) ; caller.attemptCnt = null
respText = respText.replace(apis[caller.api].watermark, '').trim()
respText = respText.replace(apis[callerAPI].watermark, '').trim()
show.reply(respText) ; show.replyCornerBtns()
}}}

Expand Down

0 comments on commit c2848d4

Please sign in to comment.