feat(export): make max-message-export configurable

This commit is contained in:
github-actions[bot]
2025-02-02 03:06:43 +00:00
committed by Pionxzh
parent 7e57605a21
commit 832732b90c
14 changed files with 123 additions and 26 deletions

View File

@@ -25,7 +25,13 @@ interface ApiSession {
}
}
type ModelSlug = 'text-davinci-002-render-sha' | 'text-davinci-002-render-paid' | 'text-davinci-002-browse' | 'gpt-4' | 'gpt-4-browsing' | 'gpt-4o'
type ModelSlug =
| 'text-davinci-002-render-sha'
| 'text-davinci-002-render-paid'
| 'text-davinci-002-browse'
| 'gpt-4'
| 'gpt-4-browsing'
| 'gpt-4o'
export interface Citation {
start_ix: number
@@ -432,19 +438,31 @@ async function fetchProjectConversations(project: string, offset = 0, limit = 20
}
}
export async function fetchAllConversations(project: string | null = null): Promise<ApiConversationItem[]> {
export async function fetchAllConversations(project: string | null = null, maxConversations = 1000): Promise<ApiConversationItem[]> {
const conversations: ApiConversationItem[] = []
const limit = project === null ? 100 : 50 // gizmos api uses a smaller limit
let offset = 0
while (true) {
const result = await fetchConversations(offset, limit, project)
conversations.push(...result.items)
if (result.total !== null && offset + limit >= result.total) break
if (result.items.length === 0) break
if (offset + limit >= 1000) break
offset += limit
try {
const result = await fetchConversations(offset, limit, project)
if (!result.items) {
// Handle potential API errors or empty responses
console.warn('fetchAllConversations received no items at offset:', offset)
break
}
conversations.push(...result.items)
// Stop if we've reached the total reported by the API OR the user-defined limit
if (result.total !== null && offset + limit >= result.total) break
if (conversations.length >= maxConversations) break
offset += limit
}
catch (error) {
console.error('Error fetching conversations batch:', error)
break
}
}
return conversations
// Ensure we don't return more than the requested limit if the last batch pushed us over
return conversations.slice(0, maxConversations)
}
export async function archiveConversation(chatId: string): Promise<boolean> {

View File

@@ -17,6 +17,7 @@ export const KEY_TIMESTAMP_MARKDOWN = 'exporter:timestamp_markdown'
export const KEY_TIMESTAMP_HTML = 'exporter:timestamp_html'
export const KEY_META_ENABLED = 'exporter:enable_meta'
export const KEY_META_LIST = 'exporter:meta_list'
export const KEY_EXPORT_ALL_LIMIT = 'exporter:export_all_limit'
export const KEY_OAI_LOCALE = 'oai/apps/locale'
export const KEY_OAI_HISTORY_DISABLED = 'oai/apps/historyDisabled'

View File

@@ -40,5 +40,7 @@
"Conversation Deleted Message": "All selected conversations have been deleted. Please refresh the page to see the changes.",
"Please start a conversation first": "Please start a conversation first.",
"Select Project": "Select Project",
"(no project)": "(no project)"
"(no project)": "(no project)",
"Export All Limit": "Export All Limit",
"Export All Limit Description": "Set the maximum number of conversations to load in the 'Export All' dialog."
}

View File

@@ -40,5 +40,7 @@
"Conversation Deleted Message": "Todos las conversaciones seleccionadas se han borrado. Por favor refresca la página para ver los cambios.",
"Please start a conversation first": "Por favor empieza una conversación antes.",
"Select Project": "Seleccionar proyecto",
"(no project)": "(sin proyecto)"
"(no project)": "(sin proyecto)",
"Export All Limit": "Límite de Exportar Todos",
"Export All Limit Description": "Establece el número máximo de conversaciones a cargar en el diálogo 'Exportar Todos'."
}

View File

@@ -40,5 +40,7 @@
"Conversation Deleted Message": "Toutes les conversations sélectionnées ont été supprimées. Veuillez actualiser la page pour voir les changements.",
"Please start a conversation first": "Veuillez commencer une conversation d'abord.",
"Select Project": "Sélectionner un projet",
"(no project)": "(aucun projet)"
"(no project)": "(aucun projet)",
"Export All Limit": "Limite d'Exportation Multiple",
"Export All Limit Description": "Définit le nombre maximal de conversations à charger dans la boîte de dialogue 'Tout exporter'."
}

View File

@@ -40,5 +40,7 @@
"Conversation Deleted Message": "Semua percakapan yang dipilih telah dihapus. Harap segarkan halaman untuk melihat perubahan.",
"Please start a conversation first": "Harap mulai percakapan terlebih dahulu.",
"Select Project": "Pilih Proyek",
"(no project)": "(tidak ada proyek)"
"(no project)": "(tidak ada proyek)",
"Export All Limit": "Batas Ekspor Semua",
"Export All Limit Description": "Atur jumlah maksimum percakapan yang akan dimuat dalam dialog 'Ekspor Semua'."
}

View File

@@ -40,5 +40,7 @@
"Conversation Deleted Message": "選択したすべての会話が削除されました。変更を表示するには、ページを更新してください。",
"Please start a conversation first": "まず会話を開始してください。",
"Select Project": "プロジェクトを選択",
"(no project)": "(プロジェクトなし)"
"(no project)": "(プロジェクトなし)",
"Export All Limit": "すべてエクスポートの上限",
"Export All Limit Description": "「すべてエクスポート」ダイアログで読み込む会話の最大数を設定します。"
}

View File

@@ -40,5 +40,7 @@
"Conversation Deleted Message": "Все выбранные разговоры были удалены. Пожалуйста, обновите страницу, чтобы увидеть изменения.",
"Please start a conversation first": "Пожалуйста, начните разговор первым.",
"Select Project": "Выберите проект",
"(no project)": "(нет проекта)"
"(no project)": "(нет проекта)",
"Export All Limit": "Лимит экспорта всех",
"Export All Limit Description": "Установите максимальное количество бесед для загрузки в диалоге 'Экспортировать все'."
}

View File

@@ -40,5 +40,7 @@
"Conversation Deleted Message": "Seçilen tüm konuşmalar silindi. Değişiklikleri görmek için sayfayı yenileyin.",
"Please start a conversation first": "Lütfen önce bir konuşma başlatın.",
"Select Project": "Proje Seç",
"(no project)": "(proje yok)"
"(no project)": "(proje yok)",
"Export All Limit": "Tümünü Dışa Aktarma Limiti",
"Export All Limit Description": "'Tümünü Dışa Aktar' iletişim kutusunda yüklenecek maksimum konuşma sayısını ayarlayın."
}

View File

@@ -40,5 +40,7 @@
"Conversation Deleted Message": "所有所选的对话已删除。请刷新页面。",
"Please start a conversation first": "请先开始对话。",
"Select Project": "选择项目",
"(no project)": "(无项目)"
"(no project)": "(无项目)",
"Export All Limit": "批量导出上限",
"Export All Limit Description": "设置“批量导出”对话框中加载的最大对话数量。"
}

View File

@@ -40,5 +40,7 @@
"Conversation Deleted Message": "所有選取的對話已刪除。請重新整理頁面。",
"Please start a conversation first": "請先開始對話。",
"Select Project": "選擇專案",
"(no project)": "(無專案)"
"(no project)": "(無專案)",
"Export All Limit": "批量匯出上限",
"Export All Limit Description": "設定「批量匯出」對話方塊中載入的最大對話數量。"
}

View File

@@ -108,7 +108,7 @@ interface DialogContentProps {
const DialogContent: FC<DialogContentProps> = ({ format }) => {
const { t } = useTranslation()
const { enableMeta, exportMetaList } = useSettingContext()
const { enableMeta, exportMetaList, exportAllLimit } = useSettingContext()
const metaList = useMemo(() => enableMeta ? exportMetaList : [], [enableMeta, exportMetaList])
const exportAllOptions = useMemo(() => [
@@ -238,7 +238,15 @@ const DialogContent: FC<DialogContentProps> = ({ format }) => {
const results = localConversations.filter(c => selected.some(s => s.id === c.id))
const callback = exportAllOptions.find(o => o.label === exportType)?.callback
if (callback) callback(format, results, metaList)
}, [disabled, selected, localConversations, exportAllOptions, exportType, format, metaList])
}, [
disabled,
selected,
localConversations,
exportAllOptions,
exportType,
format,
metaList,
])
const exportAll = useMemo(() => {
return exportSource === 'API' ? exportAllFromApi : exportAllFromLocal
@@ -288,11 +296,14 @@ const DialogContent: FC<DialogContentProps> = ({ format }) => {
useEffect(() => {
setLoading(true)
fetchAllConversations(selectedProject?.id)
fetchAllConversations(selectedProject?.id, exportAllLimit)
.then(setApiConversations)
.catch(err => setError(err.toString()))
.catch((err) => {
console.error('Error fetching conversations:', err)
setError(err.message || 'Failed to load conversations')
})
.finally(() => setLoading(false))
}, [selectedProject])
}, [selectedProject, exportAllLimit])
return (
<>
@@ -350,7 +361,7 @@ const DialogContent: FC<DialogContentProps> = ({ format }) => {
<span>{`${progress.completed}/${progress.total}`}</span>
</div>
<div className="w-full bg-gray-200 rounded-full h-2.5 mb-4 dark:bg-gray-700">
<div className="bg-blue-600 h-2.5 rounded-full" style={{ width: `${progress.completed / progress.total * 100}%` }} />
<div className="bg-blue-600 h-2.5 rounded-full" style={{ width: `${(progress.completed / progress.total) * 100}%` }} />
</div>
</>
)}

View File

@@ -1,6 +1,7 @@
import { createContext, useContext } from 'preact/compat'
import { useCallback } from 'preact/hooks'
import {
KEY_EXPORT_ALL_LIMIT,
KEY_FILENAME_FORMAT,
KEY_META_ENABLED,
KEY_META_LIST,
@@ -13,6 +14,7 @@ import { useGMStorage } from '../hooks/useGMStorage'
import type { FC } from 'preact/compat'
const defaultFormat = 'ChatGPT-{title}'
const defaultExportAllLimit = 1000
export interface ExportMeta {
name: string
@@ -41,7 +43,8 @@ const SettingContext = createContext({
setEnableMeta: (_: boolean) => {},
exportMetaList: defaultExportMetaList,
setExportMetaList: (_: ExportMeta[]) => {},
exportAllLimit: defaultExportAllLimit,
setExportAllLimit: (_: number) => {},
resetDefault: () => {},
})
@@ -56,12 +59,21 @@ export const SettingProvider: FC = ({ children }) => {
const [enableMeta, setEnableMeta] = useGMStorage(KEY_META_ENABLED, false)
const [exportMetaList, setExportMetaList] = useGMStorage(KEY_META_LIST, defaultExportMetaList)
const [exportAllLimit, setExportAllLimit] = useGMStorage(KEY_EXPORT_ALL_LIMIT, defaultExportAllLimit)
const resetDefault = useCallback(() => {
setFormat(defaultFormat)
setEnableTimestamp(false)
setEnableMeta(false)
setExportMetaList(defaultExportMetaList)
}, [setFormat, setEnableMeta, setExportMetaList])
setExportAllLimit(defaultExportAllLimit)
}, [
setFormat,
setEnableTimestamp,
setEnableMeta,
setExportMetaList,
setExportAllLimit,
])
return (
<SettingContext.Provider
@@ -83,6 +95,9 @@ export const SettingProvider: FC = ({ children }) => {
exportMetaList,
setExportMetaList,
exportAllLimit,
setExportAllLimit,
resetDefault,
}}
>

View File

@@ -35,6 +35,7 @@ export const SettingDialog: FC<SettingDialogProps> = ({
enableTimestampMarkdown, setEnableTimestampMarkdown,
enableMeta, setEnableMeta,
exportMetaList, setExportMetaList,
exportAllLimit, setExportAllLimit,
/* eslint-enable pionxzh/consistent-list-newline */
} = useSettingContext()
const { t, i18n } = useTranslation()
@@ -110,6 +111,39 @@ export const SettingDialog: FC<SettingDialogProps> = ({
</dd>
</div>
</div>
<div className="relative flex bg-white dark:bg-white/5 rounded p-4">
<div>
<dt className="text-md font-medium text-gray-800 dark:text-white">
{t('Export All Limit')}{' '}
{/* Add translation key */}
</dt>
<dd className="text-sm text-gray-700 dark:text-gray-300 mt-2">
{t('Export All Limit Description')}{' '}
{/* Add translation key */}
<div className="flex items-center gap-4 mt-3">
<input
type="range"
min="100" // Set min value
max="20000" // Set max value (adjust as needed)
step="100" // Set step value
value={exportAllLimit}
onChange={e =>
setExportAllLimit(
Number.parseInt(
e.currentTarget.value,
10,
),
)}
className="flex-grow h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
id="exportAllLimitSlider"
/>
<span className="font-medium text-gray-900 dark:text-gray-300 w-12 text-right">
{exportAllLimit}
</span>
</div>
</dd>
</div>
</div>
<div className="relative flex bg-white dark:bg-white/5 rounded p-4">
<div>
<dt className="text-md font-medium text-gray-800 dark:text-white">