-
Notifications
You must be signed in to change notification settings - Fork 1
feat(committee-reports): implement search_voteringar, search_anforanden, get_propositioner enrichment #608
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
0e2aebc
ab8bff6
ffd42c9
8aaee62
52b1289
d39d175
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -183,17 +183,12 @@ import type { ArticleCategory, GeneratedArticle, GenerationResult, MCPCallRecord | |||||
|
|
||||||
| /** | ||||||
| * Required MCP tools for committee-reports articles | ||||||
| * | ||||||
| * REQUIRED_TOOLS UPDATE (2026-02-14): | ||||||
| * Initially set to 4 tools ['get_betankanden', 'search_voteringar', 'search_anforanden', 'get_propositioner'] | ||||||
| * to match tests/validation expectations. However, this caused runtime validation failures | ||||||
| * since the implementation only calls get_betankanden (line 66). | ||||||
| * | ||||||
| * Reverted to actual implementation (1 tool) to prevent validation failures. | ||||||
| * When additional tools are implemented in generateCommitteeReports(), add them back here. | ||||||
| */ | ||||||
| export const REQUIRED_TOOLS: readonly string[] = [ | ||||||
| 'get_betankanden' | ||||||
| 'get_betankanden', | ||||||
| 'search_voteringar', | ||||||
| 'search_anforanden', | ||||||
| 'get_propositioner' | ||||||
| ]; | ||||||
|
|
||||||
| export interface TitleSet { | ||||||
|
|
@@ -255,8 +250,37 @@ export async function generateCommitteeReports(options: GenerationOptions = {}): | |||||
| return { success: true, files: 0, mcpCalls }; | ||||||
| } | ||||||
|
|
||||||
| // Cross-reference with votes and debates (optional enhancement) | ||||||
| // Future: Add voteringar, anforanden, propositioner queries here | ||||||
| // Step 2: Enrich with voting patterns, speeches, and propositions (non-fatal) | ||||||
| console.log(' 🔄 Fetching voting patterns, speeches, and propositions...'); | ||||||
| // Derive riksmöte from the first report's rm field, or calculate from current date. | ||||||
| // Parliamentary year starts in September; e.g. Sep 2025–Aug 2026 → '2025/26'. | ||||||
| const firstReportRaw = (reports[0] as Record<string, unknown>)?.['rm']; | ||||||
| const rmPattern = /^\d{4}\/\d{2}$/; | ||||||
| const firstReportRm: string | undefined = | ||||||
| typeof firstReportRaw === 'string' && rmPattern.test(firstReportRaw) ? firstReportRaw : undefined; | ||||||
| const now = new Date(); | ||||||
| const startYear = now.getMonth() >= 8 ? now.getFullYear() : now.getFullYear() - 1; | ||||||
| const currentRm = firstReportRm ?? `${startYear}/${String(startYear + 1).slice(-2)}`; | ||||||
| const [votes, speeches, propositions] = await Promise.all([ | ||||||
| Promise.resolve() | ||||||
| .then(() => client.fetchVotingRecords({ rm: currentRm, limit: 20 }) as Promise<unknown[]>) | ||||||
| .catch((err: unknown) => { console.error(' ⚠️ Failed to fetch voting records:', (err as Error)?.message ?? String(err)); return [] as unknown[]; }), | ||||||
| Promise.resolve() | ||||||
| .then(() => client.searchSpeeches({ rm: currentRm, limit: 15 }) as Promise<unknown[]>) | ||||||
| .catch((err: unknown) => { console.error(' ⚠️ Failed to fetch speeches:', (err as Error)?.message ?? String(err)); return [] as unknown[]; }), | ||||||
| Promise.resolve() | ||||||
| .then(() => client.fetchPropositions(20, currentRm) as Promise<unknown[]>) | ||||||
| .catch((err: unknown) => { console.error(' ⚠️ Failed to fetch propositions:', (err as Error)?.message ?? String(err)); return [] as unknown[]; }), | ||||||
| ]); | ||||||
| mcpCalls.push({ tool: 'search_voteringar', result: votes }); | ||||||
| mcpCalls.push({ tool: 'search_anforanden', result: speeches }); | ||||||
| mcpCalls.push({ tool: 'get_propositioner', result: propositions }); | ||||||
| console.log(` 🗳 Found ${votes.length} voting records, ${speeches.length} speeches, ${(propositions as unknown[]).length} propositions`); | ||||||
|
|
||||||
| // Normalize propositions: keep only plain objects to avoid runtime errors in content generator | ||||||
| const safePropositions = propositions.filter( | ||||||
| (p): p is RawDocument => typeof p === 'object' && p !== null | ||||||
| ); | ||||||
|
|
||||||
| const today = new Date(); | ||||||
| const slug = `${formatDateForSlug(today)}-committee-reports`; | ||||||
|
|
@@ -265,11 +289,15 @@ export async function generateCommitteeReports(options: GenerationOptions = {}): | |||||
| for (const lang of languages) { | ||||||
| console.log(` 🌐 Generating ${lang.toUpperCase()} version...`); | ||||||
|
|
||||||
| const content: string = generateArticleContent({ reports }, 'committee-reports', lang); | ||||||
| const content: string = generateArticleContent( | ||||||
| { reports, votes, speeches, propositions: safePropositions }, | ||||||
| 'committee-reports', | ||||||
| lang | ||||||
| ); | ||||||
| const watchPoints = extractWatchPoints({ reports }, lang); | ||||||
| const metadata = generateMetadata({ reports }, 'committee-reports', lang); | ||||||
| const readTime: string = calculateReadTime(content); | ||||||
| const sources: string[] = generateSources(['get_betankanden']); | ||||||
| const sources: string[] = generateSources(['get_betankanden', 'search_voteringar', 'search_anforanden', 'get_propositioner']); | ||||||
|
||||||
| const sources: string[] = generateSources(['get_betankanden', 'search_voteringar', 'search_anforanden', 'get_propositioner']); | |
| const sources: string[] = generateSources(['get_betankanden', 'get_propositioner']); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in d39d175. Extended generateSources in metadata.ts to map search_voteringar → 'Riksdagen Voting Records' and search_anforanden → 'Riksdagen Speeches', so both tools now produce meaningful source labels.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
New optional sections (Voting Results / Committee Debate / Government Bill Linkage) are rendered based on
votes,speeches, andpropositions, but there are no unit tests asserting that these sections appear when non-empty and are omitted when empty. Add focused tests (e.g., intests/data-transformers.test.ts) that pass in minimal non-empty arrays and assert the expected headings/text are present/absent.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added in ab8bff6. Eight new tests in
tests/data-transformers.test.tsassert that each optional section (Voting Results, Committee Debate, Government Bill Linkage) is rendered when its respective array is non-empty and omitted when empty, plus Swedish heading variants forsvlocale.