Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 2 additions & 7 deletions app/api/v1/order/benefit/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
51 changes: 26 additions & 25 deletions app/api/v1/search/stream/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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(
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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(
Expand Down
6 changes: 2 additions & 4 deletions components/SearchBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export interface SearchBarHandle {

const SearchBar = forwardRef<SearchBarHandle>(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()
Expand Down Expand Up @@ -136,9 +136,7 @@ const SearchBar = forwardRef<SearchBarHandle>(function SearchBar(_props, ref) {

{quota && (
<div className="mt-2 text-xs text-white/50">
{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() })}
</div>
)}

Expand Down
2 changes: 2 additions & 0 deletions hooks/useSearch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down
3 changes: 1 addition & 2 deletions i18n/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
3 changes: 1 addition & 2 deletions i18n/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ export interface Messages {
searchPlaceholder: string
searchButton: string
searchingButton: string
searchQuota: string
searchQuotaUnmetered: string
remainingCredits: string

// Empty state
emptyTitle: string
Expand Down
3 changes: 1 addition & 2 deletions i18n/zh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ const zh: Messages = {
searchPlaceholder: '输入你的研究问题...',
searchButton: '搜索',
searchingButton: '搜索中',
searchQuota: '搜索额度:{used}/{limit}',
searchQuotaUnmetered: '搜索额度:已用 {used} 次(不限次)',
remainingCredits: '剩余 Credits:{credits}',

// Empty state
emptyTitle: '探索研究文献',
Expand Down
Loading