diff --git a/app/api/v1/order/benefit/route.ts b/app/api/v1/order/benefit/route.ts index da5bc94..0eeb68d 100644 --- a/app/api/v1/order/benefit/route.ts +++ b/app/api/v1/order/benefit/route.ts @@ -35,14 +35,9 @@ export async function POST(request: NextRequest) { const json = await res.json() const data = json.data - const used: number = data?.usage?.search ?? 0 + const remainingCredits: string = data?.remaining_credits ?? '0' - const quotas: Array<{ product_type: string; limit: number }> = - data?.plan?.metadata?.product_quotas ?? [] - const searchQuota = quotas.find((q) => q.product_type === 'search') - const limit: number | null = searchQuota?.limit ?? null - - return NextResponse.json({ used, limit }) + return NextResponse.json({ remainingCredits }) } catch (err) { console.error('[benefit] Failed to fetch quota:', err) return NextResponse.json( diff --git a/app/api/v1/search/stream/route.ts b/app/api/v1/search/stream/route.ts index 11afc4a..998c4af 100644 --- a/app/api/v1/search/stream/route.ts +++ b/app/api/v1/search/stream/route.ts @@ -48,6 +48,15 @@ function mockSearchStream( ), ) + // Send columns first so frontend can start extraction as soon as enough papers arrive + const columns = getSuggestedColumns(query) + for (const col of columns) { + await sleep(50) + controller.enqueue( + encoder.encode(sseEvent('column', JSON.stringify(col))), + ) + } + const papers = getMockPapers(query, page, pageSize) for (const paper of papers) { await sleep(80) @@ -57,14 +66,6 @@ function mockSearchStream( ) } - const columns = getSuggestedColumns(query) - for (const col of columns) { - await sleep(50) - controller.enqueue( - encoder.encode(sseEvent('column', JSON.stringify(col))), - ) - } - controller.enqueue( encoder.encode( sseEvent( @@ -103,6 +104,23 @@ function wispaperSearchStream( ), ) + // Send fixed auto columns early so frontend can start extraction as soon as enough papers arrive + for (let i = 0; i < FIXED_AUTO_COLUMNS.length; i++) { + const col = FIXED_AUTO_COLUMNS[i] + controller.enqueue( + encoder.encode( + sseEvent( + 'column', + JSON.stringify({ + id: `col_auto_${i}`, + name: col.name, + prompt: col.prompt, + }), + ), + ), + ) + } + const offset = (page - 1) * pageSize const body = { message: query, @@ -244,23 +262,6 @@ function wispaperSearchStream( console.warn(`Wispaper search error for '${query}':`, err) } - // Send fixed auto columns (Task + Method) - for (let i = 0; i < FIXED_AUTO_COLUMNS.length; i++) { - const col = FIXED_AUTO_COLUMNS[i] - controller.enqueue( - encoder.encode( - sseEvent( - 'column', - JSON.stringify({ - id: `col_auto_${i}`, - name: col.name, - prompt: col.prompt, - }), - ), - ), - ) - } - controller.enqueue( encoder.encode( sseEvent( diff --git a/components/SearchBar.tsx b/components/SearchBar.tsx index bb46ef4..736ccb6 100644 --- a/components/SearchBar.tsx +++ b/components/SearchBar.tsx @@ -13,7 +13,7 @@ export interface SearchBarHandle { const SearchBar = forwardRef(function SearchBar(_props, ref) { const [inputValue, setInputValue] = useState('') - const [quota, setQuota] = useState<{ used: number; limit: number | null } | null>(null) + const [quota, setQuota] = useState<{ remainingCredits: string } | null>(null) const [showLoginModal, setShowLoginModal] = useState(false) const query = useMatrixStore((s) => s.query) const { doSearch, isSearching } = useSearch() @@ -136,9 +136,7 @@ const SearchBar = forwardRef(function SearchBar(_props, ref) { {quota && (
- {quota.limit === null - ? t('searchQuotaUnmetered', { used: String(quota.used) }) - : t('searchQuota', { used: String(quota.used), limit: String(quota.limit) })} + {t('remainingCredits', { credits: Math.floor(Number(quota.remainingCredits)).toLocaleString() })}
)} diff --git a/hooks/useSearch.ts b/hooks/useSearch.ts index a5272c2..6571ecd 100644 --- a/hooks/useSearch.ts +++ b/hooks/useSearch.ts @@ -111,6 +111,8 @@ export function useSearch() { } else if (event === 'paper') { addPaper(data as Paper) paperIds.push(data.id) + // column 已先到达,检查论文数是否足够触发抽取 + tryEarlyExtraction() } else if (event === 'column') { const s = data as ColumnSuggestion const col: Column = { diff --git a/i18n/en.ts b/i18n/en.ts index 72f7f0d..fee70c7 100644 --- a/i18n/en.ts +++ b/i18n/en.ts @@ -9,8 +9,7 @@ const en: Messages = { searchPlaceholder: 'Enter your research question...', searchButton: 'Search', searchingButton: 'Searching', - searchQuota: 'Search quota: {used}/{limit}', - searchQuotaUnmetered: 'Search quota: {used} used (unmetered)', + remainingCredits: 'Remaining Credits: {credits}', // Empty state emptyTitle: 'Explore Research Literature', diff --git a/i18n/types.ts b/i18n/types.ts index bb9e50e..0fdfbf0 100644 --- a/i18n/types.ts +++ b/i18n/types.ts @@ -9,8 +9,7 @@ export interface Messages { searchPlaceholder: string searchButton: string searchingButton: string - searchQuota: string - searchQuotaUnmetered: string + remainingCredits: string // Empty state emptyTitle: string diff --git a/i18n/zh.ts b/i18n/zh.ts index aa434f3..d983edb 100644 --- a/i18n/zh.ts +++ b/i18n/zh.ts @@ -9,8 +9,7 @@ const zh: Messages = { searchPlaceholder: '输入你的研究问题...', searchButton: '搜索', searchingButton: '搜索中', - searchQuota: '搜索额度:{used}/{limit}', - searchQuotaUnmetered: '搜索额度:已用 {used} 次(不限次)', + remainingCredits: '剩余 Credits:{credits}', // Empty state emptyTitle: '探索研究文献',