Implement dual-model filing pipeline with Ollama extraction
This commit is contained in:
@@ -4,6 +4,7 @@ import { auth } from '@/lib/auth';
|
||||
import { requireAuthenticatedSession } from '@/lib/server/auth-session';
|
||||
import { asErrorMessage, jsonError } from '@/lib/server/http';
|
||||
import { buildPortfolioSummary } from '@/lib/server/portfolio';
|
||||
import { redactInternalFilingAnalysisFields } from '@/lib/server/api/filing-redaction';
|
||||
import { getFilingByAccession, listFilingsRecords } from '@/lib/server/repos/filings';
|
||||
import {
|
||||
deleteHoldingByIdRecord,
|
||||
@@ -332,8 +333,9 @@ export const app = new Elysia({ prefix: '/api' })
|
||||
getQuote(ticker),
|
||||
getPriceHistory(ticker)
|
||||
]);
|
||||
const redactedFilings = filings.map(redactInternalFilingAnalysisFields);
|
||||
|
||||
const latestFiling = filings[0] ?? null;
|
||||
const latestFiling = redactedFilings[0] ?? null;
|
||||
const holding = holdings.find((entry) => entry.ticker === ticker) ?? null;
|
||||
const watchlistItem = watchlist.find((entry) => entry.ticker === ticker) ?? null;
|
||||
|
||||
@@ -341,7 +343,7 @@ export const app = new Elysia({ prefix: '/api' })
|
||||
?? watchlistItem?.company_name
|
||||
?? ticker;
|
||||
|
||||
const financials = filings
|
||||
const financials = redactedFilings
|
||||
.filter((entry) => entry.metrics)
|
||||
.map((entry) => ({
|
||||
filingDate: entry.filing_date,
|
||||
@@ -353,7 +355,7 @@ export const app = new Elysia({ prefix: '/api' })
|
||||
debt: entry.metrics?.debt ?? null
|
||||
}));
|
||||
|
||||
const aiReports = filings
|
||||
const aiReports = redactedFilings
|
||||
.filter((entry) => entry.analysis?.text || entry.analysis?.legacyInsights)
|
||||
.slice(0, 8)
|
||||
.map((entry) => ({
|
||||
@@ -377,7 +379,7 @@ export const app = new Elysia({ prefix: '/api' })
|
||||
position: holding,
|
||||
priceHistory,
|
||||
financials,
|
||||
filings: filings.slice(0, 20),
|
||||
filings: redactedFilings.slice(0, 20),
|
||||
aiReports
|
||||
}
|
||||
});
|
||||
@@ -446,7 +448,7 @@ export const app = new Elysia({ prefix: '/api' })
|
||||
limit: Number.isFinite(limit) ? limit : 50
|
||||
});
|
||||
|
||||
return Response.json({ filings });
|
||||
return Response.json({ filings: filings.map(redactInternalFilingAnalysisFields) });
|
||||
}, {
|
||||
query: t.Object({
|
||||
ticker: t.Optional(t.String()),
|
||||
|
||||
52
lib/server/api/filing-redaction.test.ts
Normal file
52
lib/server/api/filing-redaction.test.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { describe, expect, it } from 'bun:test';
|
||||
import type { Filing } from '@/lib/types';
|
||||
import { redactInternalFilingAnalysisFields } from './filing-redaction';
|
||||
|
||||
function filingWithExtraction(): Filing {
|
||||
return {
|
||||
id: 7,
|
||||
ticker: 'MSFT',
|
||||
filing_type: '10-K',
|
||||
filing_date: '2026-02-01',
|
||||
accession_number: '0000789019-26-000001',
|
||||
cik: '0000789019',
|
||||
company_name: 'Microsoft Corporation',
|
||||
filing_url: 'https://www.sec.gov/Archives/edgar/data/789019/000078901926000001/a10k.htm',
|
||||
submission_url: null,
|
||||
primary_document: 'a10k.htm',
|
||||
metrics: null,
|
||||
analysis: {
|
||||
provider: 'zhipu',
|
||||
model: 'glm-4.7-flashx',
|
||||
text: 'Report text',
|
||||
extraction: {
|
||||
summary: 'Internal extraction summary',
|
||||
keyPoints: ['a'],
|
||||
redFlags: ['b'],
|
||||
followUpQuestions: ['c'],
|
||||
portfolioSignals: ['d'],
|
||||
confidence: 0.4
|
||||
},
|
||||
extractionMeta: {
|
||||
provider: 'ollama',
|
||||
model: 'qwen3:8b',
|
||||
source: 'primary_document',
|
||||
generatedAt: '2026-02-01T00:00:00.000Z'
|
||||
}
|
||||
},
|
||||
created_at: '2026-02-01T00:00:00.000Z',
|
||||
updated_at: '2026-02-01T00:00:00.000Z'
|
||||
};
|
||||
}
|
||||
|
||||
describe('filing response redaction', () => {
|
||||
it('removes internal extraction fields while preserving public analysis fields', () => {
|
||||
const redacted = redactInternalFilingAnalysisFields(filingWithExtraction());
|
||||
|
||||
expect(redacted.analysis?.provider).toBe('zhipu');
|
||||
expect(redacted.analysis?.model).toBe('glm-4.7-flashx');
|
||||
expect(redacted.analysis?.text).toBe('Report text');
|
||||
expect(redacted.analysis?.extraction).toBeUndefined();
|
||||
expect(redacted.analysis?.extractionMeta).toBeUndefined();
|
||||
});
|
||||
});
|
||||
15
lib/server/api/filing-redaction.ts
Normal file
15
lib/server/api/filing-redaction.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import type { Filing } from '@/lib/types';
|
||||
|
||||
export function redactInternalFilingAnalysisFields(filing: Filing): Filing {
|
||||
if (!filing.analysis) {
|
||||
return filing;
|
||||
}
|
||||
|
||||
const { extraction: _extraction, extractionMeta: _extractionMeta, ...analysis } = filing.analysis;
|
||||
const hasPublicFields = Object.keys(analysis).length > 0;
|
||||
|
||||
return {
|
||||
...filing,
|
||||
analysis: hasPublicFields ? analysis : null
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user