feat: retry button

This commit is contained in:
josc146
2023-03-31 16:32:12 +08:00
parent 46348e1391
commit 90141b919b
11 changed files with 80 additions and 40 deletions
+2 -1
View File
@@ -84,5 +84,6 @@
"Loading...": "Loading...",
"Feedback": "Feedback",
"Confirm": "Confirm",
"Clear Conversation": "Clear Conversation"
"Clear Conversation": "Clear Conversation",
"Retry": "Retry"
}
+2 -1
View File
@@ -84,5 +84,6 @@
"Loading...": "正在读取...",
"Feedback": "反馈",
"Confirm": "确认",
"Clear Conversation": "清理对话"
"Clear Conversation": "清理对话",
"Retry": "重试"
}
+2 -1
View File
@@ -84,5 +84,6 @@
"Loading...": "正在讀取...",
"Feedback": "反饋",
"Confirm": "確認",
"Clear Conversation": "清理對話"
"Clear Conversation": "清理對話",
"Retry": "重試"
}
+2 -1
View File
@@ -1,5 +1,6 @@
import BingAIClient from '../clients/BingAIClient'
import { getUserConfig } from '../../config/index.mjs'
import { pushRecord } from './shared.mjs'
/**
* @param {Runtime.Port} port
@@ -63,7 +64,7 @@ export async function generateAnswersWithBingWebApi(
session.bingWeb.clientId = response.clientId
session.bingWeb.invocationId = response.invocationId
session.conversationRecords.push({ question: question, answer: answer })
pushRecord(session, question, answer)
console.debug('conversation history', { content: session.conversationRecords })
port.onMessage.removeListener(stopListener)
port.postMessage({ answer: answer, done: true, session: session })
+2 -1
View File
@@ -3,6 +3,7 @@
import { fetchSSE } from '../../utils/fetch-sse'
import { isEmpty } from 'lodash-es'
import { chatgptWebModelKeys, getUserConfig, Models } from '../../config/index.mjs'
import { pushRecord } from './shared.mjs'
async function request(token, method, path, data) {
const apiUrl = (await getUserConfig()).customChatGptWebApiUrl
@@ -105,7 +106,7 @@ export async function generateAnswersWithChatgptWebApi(port, question, session,
onMessage(message) {
console.debug('sse message', message)
if (message === '[DONE]') {
session.conversationRecords.push({ question: question, answer: answer })
pushRecord(session, question, answer)
console.debug('conversation history', { content: session.conversationRecords })
port.postMessage({ answer: null, done: true, session: session })
return
+2 -1
View File
@@ -9,6 +9,7 @@ import { getUserConfig, maxResponseTokenLength } from '../../config/index.mjs'
import { fetchSSE } from '../../utils/fetch-sse'
import { getConversationPairs } from '../../utils/get-conversation-pairs'
import { isEmpty } from 'lodash-es'
import { pushRecord } from './shared.mjs'
const getCustomApiPromptBase = async () => {
return `I am a helpful, creative, clever, and very friendly assistant. I am familiar with various languages in the world.`
@@ -59,7 +60,7 @@ export async function generateAnswersWithCustomApi(port, question, session, apiK
onMessage(message) {
console.debug('sse message', message)
if (message === '[DONE]') {
session.conversationRecords.push({ question: question, answer: answer })
pushRecord(session, question, answer)
console.debug('conversation history', { content: session.conversationRecords })
port.postMessage({ answer: null, done: true, session: session })
return
+3 -2
View File
@@ -4,6 +4,7 @@ import { maxResponseTokenLength, Models, getUserConfig } from '../../config/inde
import { fetchSSE } from '../../utils/fetch-sse'
import { getConversationPairs } from '../../utils/get-conversation-pairs'
import { isEmpty } from 'lodash-es'
import { pushRecord } from './shared.mjs'
const getChatgptPromptBase = async () => {
return `You are a helpful, creative, clever, and very friendly assistant. You are familiar with various languages in the world.`
@@ -72,7 +73,7 @@ export async function generateAnswersWithGptCompletionApi(
onMessage(message) {
console.debug('sse message', message)
if (message === '[DONE]') {
session.conversationRecords.push({ question: question, answer: answer })
pushRecord(session, question, answer)
console.debug('conversation history', { content: session.conversationRecords })
port.postMessage({ answer: null, done: true, session: session })
return
@@ -148,7 +149,7 @@ export async function generateAnswersWithChatgptApi(port, question, session, api
onMessage(message) {
console.debug('sse message', message)
if (message === '[DONE]') {
session.conversationRecords.push({ question: question, answer: answer })
pushRecord(session, question, answer)
console.debug('conversation history', { content: session.conversationRecords })
port.postMessage({ answer: null, done: true, session: session })
return
+8
View File
@@ -0,0 +1,8 @@
export function pushRecord(session, question, answer) {
const recordLength = session.conversationRecords.length
let lastRecord
if (recordLength > 0) lastRecord = session.conversationRecords[recordLength - 1]
if (session.isRetry && lastRecord && lastRecord.question === question) lastRecord.answer = answer
else session.conversationRecords.push({ question: question, answer: answer })
}
+43 -12
View File
@@ -4,7 +4,7 @@ import Browser from 'webextension-polyfill'
import InputBox from '../InputBox'
import ConversationItem from '../ConversationItem'
import { createElementAtPosition, initSession, isSafari } from '../../utils'
import { DownloadIcon } from '@primer/octicons-react'
import { DownloadIcon, LinkExternalIcon } from '@primer/octicons-react'
import { WindowDesktop, XLg, Pin } from 'react-bootstrap-icons'
import FileSaver from 'file-saver'
import { render } from 'preact'
@@ -94,7 +94,7 @@ function ConversationCard(props) {
* @param {'question'|'answer'|'error'} newType
* @param {boolean} done
*/
const UpdateAnswer = (value, appended, newType, done = false) => {
const updateAnswer = (value, appended, newType, done = false) => {
setConversationItemData((old) => {
const copy = [...old]
const index = copy.findLastIndex((v) => v.type === 'answer')
@@ -121,19 +121,20 @@ function ConversationCard(props) {
useEffect(() => {
const listener = (msg) => {
if (msg.answer) {
UpdateAnswer(msg.answer, false, 'answer')
updateAnswer(msg.answer, false, 'answer')
}
if (msg.session) {
if (msg.done) msg.session = { ...msg.session, isRetry: false }
setSession(msg.session)
}
if (msg.done) {
UpdateAnswer('\n<hr/>', true, 'answer', true)
updateAnswer('\n<hr/>', true, 'answer', true)
setIsReady(true)
}
if (msg.error) {
switch (msg.error) {
case 'UNAUTHORIZED':
UpdateAnswer(
updateAnswer(
`${t('UNAUTHORIZED')}<br>${t('Please login at https://chat.openai.com first')}${
isSafari() ? `<br>${t('Then open https://chat.openai.com/api/auth/session')}` : ''
}<br>${t('And refresh this page or type you question again')}` +
@@ -145,7 +146,7 @@ function ConversationCard(props) {
)
break
case 'CLOUDFLARE':
UpdateAnswer(
updateAnswer(
`${t('OpenAI Security Check Required')}<br>${
isSafari()
? t('Please open https://chat.openai.com/api/auth/session')
@@ -159,10 +160,7 @@ function ConversationCard(props) {
)
break
default:
setConversationItemData([
...conversationItemData,
new ConversationItemData('error', msg.error + '\n<hr/>'),
])
updateAnswer(msg.error + '\n<hr/>', false, 'error')
break
}
setIsReady(true)
@@ -223,6 +221,18 @@ function ConversationCard(props) {
/>
)}
<span className="gpt-util-group">
{session && session.conversationId && (
<a
title={t('Continue on official website')}
href={'https://chat.openai.com/chat/' + session.conversationId}
target="_blank"
rel="nofollow noopener noreferrer"
className="gpt-util-icon"
style="color: inherit;"
>
<LinkExternalIcon size={16} />
</a>
)}
<DeleteButton
size={16}
onConfirm={() => {
@@ -269,6 +279,27 @@ function ConversationCard(props) {
session={session}
done={data.done}
port={port}
onRetry={
idx === conversationItemData.length - 1
? () => {
updateAnswer(
`<p class="gpt-loading">${t('Waiting for response...')}</p>`,
false,
'answer',
)
setIsReady(false)
const newSession = { ...session, isRetry: true }
setSession(newSession)
try {
port.postMessage({ stop: true })
port.postMessage({ session: newSession })
} catch (e) {
updateAnswer(e, false, 'error')
}
}
: null
}
/>
))}
</div>
@@ -283,12 +314,12 @@ function ConversationCard(props) {
setConversationItemData([...conversationItemData, newQuestion, newAnswer])
setIsReady(false)
const newSession = { ...session, question }
const newSession = { ...session, question, isRetry: false }
setSession(newSession)
try {
port.postMessage({ session: newSession })
} catch (e) {
UpdateAnswer(e, false, 'error')
updateAnswer(e, false, 'error')
}
}}
/>
+12 -20
View File
@@ -1,12 +1,11 @@
import { useState } from 'react'
import FeedbackForChatGPTWeb from '../FeedbackForChatGPTWeb'
import { ChevronDownIcon, LinkExternalIcon, XCircleIcon } from '@primer/octicons-react'
import { ChevronDownIcon, XCircleIcon, SyncIcon } from '@primer/octicons-react'
import CopyButton from '../CopyButton'
import PropTypes from 'prop-types'
import MarkdownRender from '../MarkdownRender/markdown.jsx'
import { useTranslation } from 'react-i18next'
export function ConversationItem({ type, content, session, done, port }) {
export function ConversationItem({ type, content, session, done, port, onRetry }) {
const { t } = useTranslation()
const [collapsed, setCollapsed] = useState(false)
@@ -59,23 +58,10 @@ export function ConversationItem({ type, content, session, done, port }) {
{t('Stop')}
</button>
)}
{done && session && session.conversationId && (
<FeedbackForChatGPTWeb
messageId={session.messageId}
conversationId={session.conversationId}
/>
)}
{session && session.conversationId && (
<a
title={t('Continue on official website')}
href={'https://chat.openai.com/chat/' + session.conversationId}
target="_blank"
rel="nofollow noopener noreferrer"
className="gpt-util-icon"
style="color: inherit;"
>
<LinkExternalIcon size={14} />
</a>
{onRetry && (
<span title={t('Retry')} className="gpt-util-icon" onClick={onRetry}>
<SyncIcon size={14} />
</span>
)}
{session && <CopyButton contentFn={() => content} size={14} />}
{!collapsed ? (
@@ -106,6 +92,11 @@ export function ConversationItem({ type, content, session, done, port }) {
<div className="gpt-header">
<p>{t('Error')}:</p>
<div className="gpt-util-group">
{onRetry && (
<span title={t('Retry')} className="gpt-util-icon" onClick={onRetry}>
<SyncIcon size={14} />
</span>
)}
<CopyButton contentFn={() => content} size={14} />
{!collapsed ? (
<span
@@ -138,6 +129,7 @@ ConversationItem.propTypes = {
session: PropTypes.object.isRequired,
done: PropTypes.bool.isRequired,
port: PropTypes.object.isRequired,
onRetry: PropTypes.func,
}
export default ConversationItem
+2
View File
@@ -9,6 +9,7 @@
* @typedef {object} Session
* @property {string|null} question
* @property {Object[]|null} conversationRecords
* @property {boolean} isRetry
* @property {string|null} aiName
* @property {string|null} modelName
* @property {string|null} conversationId - chatGPT web mode
@@ -26,6 +27,7 @@ export function initSession({ question = null, conversationRecords = [] } = {})
// common
question,
conversationRecords,
isRetry: false,
aiName: null,
modelName: null,