Add hybrid research copilot workspace

This commit is contained in:
2026-03-14 19:32:00 -04:00
parent 7a42d73a48
commit 2ee9a549a3
27 changed files with 2864 additions and 323 deletions

View File

@@ -25,6 +25,7 @@ import {
researchMemo,
researchMemoEvidence
} from '@/lib/server/db/schema';
import { getResearchCopilotSessionByTicker } from '@/lib/server/repos/research-copilot';
import { getFilingByAccession, listFilingsRecords } from '@/lib/server/repos/filings';
import { getWatchlistItemByTicker } from '@/lib/server/repos/watchlist';
@@ -374,6 +375,26 @@ async function getArtifactByIdForUser(id: number, userId: string) {
return row ?? null;
}
export async function getResearchArtifactsByIdsForUser(userId: string, ids: number[]) {
const normalizedIds = [...new Set(ids.map((id) => Math.trunc(id)).filter((id) => Number.isInteger(id) && id > 0))];
if (normalizedIds.length === 0) {
return [];
}
const rows = await db
.select()
.from(researchArtifact)
.where(and(
eq(researchArtifact.user_id, userId),
sql`${researchArtifact.id} in (${sql.join(normalizedIds.map((id) => sql`${id}`), sql`, `)})`
));
const order = new Map(normalizedIds.map((id, index) => [id, index]));
return rows
.sort((left, right) => (order.get(left.id) ?? Number.MAX_SAFE_INTEGER) - (order.get(right.id) ?? Number.MAX_SAFE_INTEGER))
.map((row) => toResearchArtifact(row));
}
async function getMemoByIdForUser(id: number, userId: string) {
const [row] = await db
.select()
@@ -902,12 +923,13 @@ export async function getResearchPacket(userId: string, ticker: string): Promise
export async function getResearchWorkspace(userId: string, ticker: string): Promise<ResearchWorkspace> {
const normalizedTicker = normalizeTicker(ticker);
const [coverage, memo, library, packet, latestFiling] = await Promise.all([
const [coverage, memo, library, packet, latestFiling, copilotSession] = await Promise.all([
getWatchlistItemByTicker(userId, normalizedTicker),
getResearchMemoByTicker(userId, normalizedTicker),
listResearchArtifacts(userId, { ticker: normalizedTicker, limit: 40 }),
getResearchPacket(userId, normalizedTicker),
listFilingsRecords({ ticker: normalizedTicker, limit: 1 })
listFilingsRecords({ ticker: normalizedTicker, limit: 1 }),
getResearchCopilotSessionByTicker(userId, normalizedTicker)
]);
return {
@@ -918,7 +940,8 @@ export async function getResearchWorkspace(userId: string, ticker: string): Prom
memo,
library: library.artifacts,
packet,
availableTags: library.availableTags
availableTags: library.availableTags,
copilotSession
};
}
@@ -1119,4 +1142,3 @@ export async function getResearchArtifactFileResponse(userId: string, id: number
export function rebuildResearchArtifactIndex() {
rebuildArtifactSearchIndex();
}