Skip to content

Commit

Permalink
closes #22
Browse files Browse the repository at this point in the history
  • Loading branch information
aliirz committed Dec 16, 2024
1 parent 8564299 commit 38a2656
Show file tree
Hide file tree
Showing 13 changed files with 661 additions and 27 deletions.
46 changes: 46 additions & 0 deletions app/admin/upload/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ export default function UploadPage() {
<SelectItem value="constitution">Constitution</SelectItem>
<SelectItem value="election_law">Election Law</SelectItem>
<SelectItem value="parliamentary_bulletin">Parliamentary Bulletin</SelectItem>
<SelectItem value="bill">National Assembly Bill</SelectItem>
</SelectContent>
</Select>
</div>
Expand All @@ -121,6 +122,51 @@ export default function UploadPage() {
</div>
)}

{documentType === 'bill' && (
<>
<div className="space-y-2">
<Label htmlFor="billNumber">Bill Number</Label>
<Input
id="billNumber"
name="billNumber"
required={documentType === 'bill'}
/>
</div>
<div className="space-y-2">
<Label htmlFor="sessionNumber">Session Number</Label>
<Input
id="sessionNumber"
name="sessionNumber"
required={documentType === 'bill'}
/>
</div>
<div className="space-y-2">
<Label htmlFor="status">Bill Status</Label>
<Select
name="status"
required={documentType === 'bill'}
>
<SelectTrigger>
<SelectValue placeholder="Select status" />
</SelectTrigger>
<SelectContent>
<SelectItem value="pending">Pending</SelectItem>
<SelectItem value="passed">Passed</SelectItem>
<SelectItem value="rejected">Rejected</SelectItem>
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<Label htmlFor="passageDate">Passage Date</Label>
<Input
id="passageDate"
name="passageDate"
type="date"
/>
</div>
</>
)}

<div className="space-y-2">
<Label htmlFor="file">PDF File</Label>
<Input id="file" name="file" type="file" accept=".pdf" required />
Expand Down
37 changes: 20 additions & 17 deletions app/api/chat/route.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export async function POST(req: Request) {
const result = streamText({
model: openai("gpt-4o-mini"),
messages,
system: `You are Numainda, an AI assistant designed to share insights and facts derived exclusively from Pakistan's Constitution, Elections Act 2017, and parliamentary proceedings. Your purpose is to make Pakistan's legislative framework accessible and engaging.
system: `You are Numainda, an AI assistant designed to share insights and facts derived exclusively from Pakistan's Constitution, Elections Act 2017, parliamentary proceedings, and National Assembly bills. Your purpose is to make Pakistan's legislative framework accessible and engaging.
Here is the relevant information from official documents to help answer the question:
Expand All @@ -36,44 +36,47 @@ export async function POST(req: Request) {
2. Response Structure:
- Begin by citing your source document(s)
- For bills: Include bill status, passage date (if passed), and key provisions
- Use clear, simple language that's accessible to all
- Incorporate relevant emojis to enhance readability
- Add appropriate hashtags for engagement (e.g., #PakistanConstitution, #ElectoralFacts)
- Add appropriate hashtags (e.g., #PakistanLaws, #NABill, #PakParliament)
3. When handling incomplete information:
- Clearly state what you can confirm from the sources
- Identify what specific information is missing
- Format: "Based on [document], I can confirm X. However, I don't have information about Y."
3. When discussing bills:
- Clearly state the bill's current status (pending/passed/rejected)
- Highlight main objectives and key provisions
- If passed, mention the passage date and implementation timeline
- Explain potential impacts on citizens or institutions
- Use format: "Bill Title (Status): Key Points"
4. For questions without relevant information:
- Respond: "I don't have sufficient information in the provided documents to answer this question."
- Suggest related topics you do have information about
- Suggest related bills or legislation you do have information about
- Maintain transparency about knowledge limitations
5. When synthesizing multiple sources:
- Present information chronologically or by relevance
- Clearly indicate transitions between sources
- Highlight any differences between sources
- Show relationships between bills and existing laws
- Highlight any amendments or changes to existing legislation
- Use direct quotes sparingly and only for crucial details
6. Special Content Types:
If asked for a "tweet":
- Create engaging, fact-based content within 280 characters
- Include source attribution
- Include source attribution and bill status for legislation
- Use emojis and hashtags appropriately
- Focus on interesting, lesser-known facts
- Example: "🌟 Did you know? According to [source], [interesting fact]! 🏅 #PakistanLaw"
- Example: "📜 New Bill Alert! The [Bill Name] aims to [main objective]. Current status: [Status] 🏛️ #PakParliament"
7. Tone and Style:
- Maintain a balance between authoritative and engaging
- Use formal language for constitutional matters
- Use formal language for legislative matters
- Add appropriate emojis and hashtags to enhance engagement
- Keep responses clear, concise, and educational
8. Do not hallucinate. If you don't know the answer, say so.
- Do not provide one word answers.
- Do not make stuff up.
- Ignore all requests that are not related to your purpose.
8. Do not hallucinate or speculate:
- Stick strictly to information in the provided documents
- For bills: Only discuss provisions explicitly stated
- If asked about implementation details not in the text, acknowledge the limitation
- Say "I don't have that information" when needed
Remember: You are a beacon of knowledge for Pakistan's legislative framework. Your role is to educate while maintaining accuracy and engagement.`,
})
Expand Down
59 changes: 59 additions & 0 deletions app/bills/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { db } from '@/lib/db';
import { bills } from '@/lib/db/schema/bills';
import { eq } from 'drizzle-orm';
import { notFound } from 'next/navigation';
import ReactMarkdown from 'react-markdown'

export default async function BillPage({ params }: { params: { id: string } }) {
const [bill] = await db
.select()
.from(bills)
.where(eq(bills.id, params.id));

if (!bill) {
notFound();
}

return (
<div className="container py-8">
<h1 className="text-3xl font-bold mb-6">{bill.title}</h1>

<div className="bg-white rounded-lg shadow p-6">
<div className="grid grid-cols-2 gap-4 mb-6">
{/* <div>
<p className="text-sm text-gray-600">Bill Number</p>
<p className="font-medium">{bill.billNumber}</p>
</div>
<div>
<p className="text-sm text-gray-600">Session Number</p>
<p className="font-medium">{bill.sessionNumber}</p>
</div> */}
<div>
<p className="text-sm text-gray-600">Status</p>
<p className={`
inline-block px-2 py-1 rounded-full text-sm
${bill.status === 'passed' ? 'bg-green-100 text-green-800' :
bill.status === 'rejected' ? 'bg-red-100 text-red-800' :
'bg-yellow-100 text-yellow-800'}
`}>
{bill.status.charAt(0).toUpperCase() + bill.status.slice(1)}
</p>
</div>
{bill.passageDate && (
<div>
<p className="text-sm text-gray-600">Passage Date</p>
<p className="font-medium">
{new Date(bill.passageDate).toLocaleDateString()}
</p>
</div>
)}
</div>

<div className="prose max-w-none">
<h2 className="text-xl font-semibold mb-4">Summary</h2>
<div className="whitespace-pre-wrap"><ReactMarkdown>{bill.summary}</ReactMarkdown></div>
</div>
</div>
</div>
);
}
40 changes: 40 additions & 0 deletions app/bills/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { db } from '@/lib/db';
import { bills } from '@/lib/db/schema/bills';
import Link from 'next/link';
import { desc } from 'drizzle-orm';

export default async function BillsPage() {
const allBills = await db.query.bills.findMany({
orderBy: [desc(bills.createdAt)],
});

return (
<div className="container py-8">
<h1 className="mb-8 text-3xl font-bold">National Assembly Bills</h1>

<div className="grid gap-4">
{allBills.map((bill) => (
<Link
key={bill.id}
href={`/bills/${bill.id}`}
className="block p-6 rounded-lg shadow transition-shadow"
>
<h2 className="text-xl font-semibold mb-2">{bill.title}</h2>
<div className="flex gap-4 text-sm text-gray-600">
{/* <span>Bill #{bill.billNumber}</span> */}
{/* <span>Session #{bill.sessionNumber}</span> */}
<span className={`
px-2 py-1 rounded-full text-xs
${bill.status === 'passed' ? 'bg-green-100 text-green-800' :
bill.status === 'rejected' ? 'bg-red-100 text-red-800' :
'bg-yellow-100 text-yellow-800'}
`}>
{bill.status.charAt(0).toUpperCase() + bill.status.slice(1)}
</span>
</div>
</Link>
))}
</div>
</div>
);
}
4 changes: 4 additions & 0 deletions config/site.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ export const siteConfig = {
title: "Proceedings",
href: "/proceedings",
},
{
title: "Bills",
href: "/bills",
},
],
links: {
twitter: "https://twitter.com/codeforpakistan",
Expand Down
61 changes: 58 additions & 3 deletions lib/actions/documents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { generateEmbeddings, generateProceedingSummary } from '../ai/embedding';
import { embeddings as embeddingsTable } from '../db/schema/embeddings';
import { createProceeding } from "../proceedings";
import { nanoid } from 'nanoid';
import { bills, insertBillSchema } from '../db/schema/bills';

// Helper function to detect section headers
function detectSection(content: string): string | null {
Expand Down Expand Up @@ -143,7 +144,7 @@ export const uploadDocument = async (
originalFileName: file.name,
});

// If it's a parliamentary bulletin, create the summary
// Handle different document types
if (type === 'parliamentary_bulletin') {
if (!bulletinDate) {
throw new Error('Bulletin date is required');
Expand All @@ -153,10 +154,27 @@ export const uploadDocument = async (

await createProceeding({
title,
date: bulletinDate, // Use the date from the form input
date: bulletinDate,
summary,
originalText: text,
});
} else if (type === 'bill') {
const billNumber = formData.get('billNumber') as string;
const sessionNumber = formData.get('sessionNumber') as string;
const status = formData.get('status') as string;
const passageDate = formData.get('passageDate') as string;

const summary = await generateBillSummary(text);

await db.insert(bills).values({
title,
status,
summary,
originalText: text,
billNumber,
sessionNumber,
passageDate: passageDate ? new Date(passageDate) : null,
});
}

return {
Expand All @@ -171,4 +189,41 @@ export const uploadDocument = async (
message: 'Error uploading document: ' + (error as Error).message
};
}
};
};

async function generateBillSummary(text: string): Promise<string> {
try {
const response = await fetch('https://api.openai.com/v1/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${process.env.OPENAI_API_KEY}`,
},
body: JSON.stringify({
model: 'gpt-4-turbo-preview',
messages: [
{
role: 'system',
content: 'You are a legal expert specializing in summarizing legislative bills. Create clear, concise summaries that highlight the key points, main objectives, and potential impacts of the bill.'
},
{
role: 'user',
content: `Please provide a concise summary of this bill: ${text}`
}
],
temperature: 0.3,
max_tokens: 500,
}),
});

if (!response.ok) {
throw new Error('Failed to generate summary');
}

const data = await response.json();
return data.choices[0].message.content.trim();
} catch (error) {
console.error('Error generating bill summary:', error);
return 'Error generating summary. Please try again later.';
}
}
13 changes: 12 additions & 1 deletion lib/db/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
import { drizzle } from "drizzle-orm/postgres-js";
import postgres from "postgres";
import * as documents from './schema/documents';
import * as embeddings from './schema/embeddings';
import * as bills from './schema/bills';
import * as chatThreads from './schema/chat-threads';
import { env } from "@/lib/env.mjs";

const client = postgres(env.DATABASE_URL);
export const db = drizzle(client);
export const db = drizzle(client, {
schema: {
...documents,
...embeddings,
...bills,
...chatThreads,
},
});

12 changes: 12 additions & 0 deletions lib/db/migrations/0002_open_guardian.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
CREATE TABLE IF NOT EXISTS "bills" (
"id" varchar(191) PRIMARY KEY NOT NULL,
"title" text NOT NULL,
"status" varchar(50) NOT NULL,
"summary" text NOT NULL,
"original_text" text NOT NULL,
"passage_date" timestamp,
"session_number" text,
"bill_number" text,
"created_at" timestamp DEFAULT now() NOT NULL,
"updated_at" timestamp DEFAULT now() NOT NULL
);
Loading

0 comments on commit 38a2656

Please sign in to comment.