Skip to content

Commit

Permalink
ArticleWithRelations for better type coverage in researcher module.…
Browse files Browse the repository at this point in the history
… Enhanced the UI by displaying article categories and tags, improved topic handling in forms and query parameters, and added a new layout for the researcher page.
  • Loading branch information
mikepsinn committed Sep 8, 2024
1 parent 121bdaf commit ad76cfc
Show file tree
Hide file tree
Showing 7 changed files with 13,093 additions and 10,619 deletions.
18 changes: 14 additions & 4 deletions app/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,15 @@ import { type Message as AIMessage } from "ai"
import { prisma } from "@/lib/db"
import { getCurrentUser } from "@/lib/session"
import { type Chat } from "@/lib/types"
import {type ReportOutput, writeArticle} from "@/lib/agents/researcher/researcher"
import {
ArticleWithRelations,
findArticleByTopic,
writeArticle
} from "@/lib/agents/researcher/researcher"
type GetChatResult = Chat[] | null
type SetChatResults = Chat[]
import OpenAI from 'openai'
import {Article} from "@prisma/client";
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
})
Expand Down Expand Up @@ -170,9 +175,14 @@ export const clearAllChats = async (userId: string) => {
}
}

export async function writeArticleAction(topic: string): Promise<ReportOutput> {

const article = await writeArticle(topic)
export async function writeArticleAction(topic: string): Promise<ArticleWithRelations> {

let article: ArticleWithRelations | null

article = await findArticleByTopic(topic)
if(!article) {
article = await writeArticle(topic)
}

revalidatePath('/')
return article
Expand Down
34 changes: 34 additions & 0 deletions app/researcher/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { generalSidebarNav } from "@/config/links"
import { getCurrentUser } from "@/lib/session"
import Footer from "@/components/layout/footer"
import TopNavbar from "@/components/layout/topNavbar"
import { SidebarNav } from "@/components/sidebar-nav"

interface DashboardLayoutProps {
children: React.ReactNode
}

export default async function DashboardLayout({
children,
}: DashboardLayoutProps) {
const user = await getCurrentUser()

return (
<div className="flex min-h-screen flex-col space-y-6">
<TopNavbar
user={{
name: user?.name,
image: user?.image,
email: user?.email,
}}
/>
<div className="container grid flex-1 gap-12 md:grid-cols-[200px_1fr]">
<aside className="hidden w-[200px] flex-col md:flex">
<SidebarNav items={generalSidebarNav.data} />
</aside>
<main className="flex w-full flex-1 flex-col">{children}</main>
</div>
<Footer />
</div>
)
}
36 changes: 27 additions & 9 deletions app/researcher/page.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,33 @@
'use client'

import { useState } from 'react'
import { useState, useEffect } from 'react'
import { useSearchParams, useRouter } from 'next/navigation'
import { writeArticleAction} from '@/app/actions'
import ArticleRenderer from '@/components/ArticleRenderer'
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"
import { ReportOutput } from '@/lib/agents/researcher/researcher'
import { ArticleWithRelations } from '@/lib/agents/researcher/researcher'
import GlobalBrainNetwork from "@/components/landingPage/global-brain-network"

export default function Home() {
const [article, setArticle] = useState<ReportOutput | null>(null)
const [article, setArticle] = useState<ArticleWithRelations | null>(null)
const [error, setError] = useState('')
const [isGenerating, setIsGenerating] = useState(false)
const [topic, setTopic] = useState('')
const searchParams = useSearchParams()
const router = useRouter()

async function handleSubmit(formData: FormData) {
const topic = formData.get('topic') as string
if (!topic) {
useEffect(() => {
const queryTopic = searchParams?.get('q')
if (queryTopic) {
setTopic(queryTopic)
handleSubmit(queryTopic)
}
}, [searchParams])

async function handleSubmit(submittedTopic: string) {
if (!submittedTopic) {
setError('Please enter a topic')
return
}
Expand All @@ -25,8 +36,9 @@ export default function Home() {
setError('')

try {
const generatedArticle = await writeArticleAction(topic)
const generatedArticle = await writeArticleAction(submittedTopic)
setArticle(generatedArticle)
router.push(`?q=${encodeURIComponent(submittedTopic)}`, { scroll: false })
} catch (err) {
setError('Failed to generate article. Please try again.')
} finally {
Expand All @@ -46,9 +58,15 @@ export default function Home() {
<CardContent>
<form onSubmit={(e) => {
e.preventDefault()
handleSubmit(new FormData(e.currentTarget))
handleSubmit(topic)
}} className="flex gap-4">
<Input type="text" name="topic" placeholder="Enter article topic" className="flex-grow" />
<Input
type="text"
value={topic}
onChange={(e) => setTopic(e.target.value)}
placeholder="Enter article topic"
className="flex-grow"
/>
<Button type="submit" disabled={isGenerating}>
{isGenerating ? 'Generating...' : 'Generate Article'}
</Button>
Expand Down
15 changes: 10 additions & 5 deletions components/ArticleRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useState } from "react"
import Image from "next/image"
import { Check, Clock, Copy, Folder, Link2, Tag } from "lucide-react"

import { ReportOutput } from "@/lib/agents/researcher/researcher"
import {ArticleWithRelations} from "@/lib/agents/researcher/researcher"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import {
Expand All @@ -17,6 +17,10 @@ import { Separator } from "@/components/ui/separator"
import { toast } from "@/components/ui/use-toast"
import { CustomReactMarkdown } from "@/components/CustomReactMarkdown"
import { generateImage } from "@/app/actions"
import {Article} from "@prisma/client"
import { Prisma } from "@prisma/client"



function GenerateImageButton({
onClick,
Expand All @@ -32,7 +36,7 @@ function GenerateImageButton({
)
}

export default function ArticleRenderer(props: ReportOutput) {
export default function ArticleRenderer(props: ArticleWithRelations) {
const [expandedResult, setExpandedResult] = useState<string | null>(null)
const [isCopied, setIsCopied] = useState(false)
const [isGeneratingImage, setIsGeneratingImage] = useState(false)
Expand All @@ -46,11 +50,12 @@ export default function ArticleRenderer(props: ReportOutput) {
sources,
tags,
category,
readingTime,
searchResults,
featuredImage,
} = props

const readingTime = Math.ceil(content.split(' ').length / 200)

async function handleGenerateImage() {
setIsGeneratingImage(true)
try {
Expand Down Expand Up @@ -124,7 +129,7 @@ ${sources?.map((source) => `- [${source.title}](${source.url})`).join("\n")}
<CardContent className="space-y-2">
<div className="flex items-center space-x-2">
<Folder className="h-4 w-4"/>
<span>{category}</span>
<span>{category?.name}</span>
</div>
<div className="flex items-center space-x-2">
<Clock className="h-4 w-4"/>
Expand All @@ -134,7 +139,7 @@ ${sources?.map((source) => `- [${source.title}](${source.url})`).join("\n")}
<Tag className="h-4 w-4"/>
{tags?.map((tag, index) => (
<Badge key={index} variant="secondary">
{tag}
{tag.name}
</Badge>
))}
</div>
Expand Down
83 changes: 72 additions & 11 deletions lib/agents/researcher/researcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {openai} from "@ai-sdk/openai";
import {google} from "@ai-sdk/google";
import {LanguageModelV1} from "@ai-sdk/provider";
import {SearchResult} from "exa-js";
import { PrismaClient, Article } from '@prisma/client';
import { PrismaClient, Prisma } from '@prisma/client';
import { slugify } from '@/lib/utils/slugify'; // Assuming you have a slugify utility

const prisma = new PrismaClient();
Expand Down Expand Up @@ -77,7 +77,7 @@ export async function writeArticle(
'gemini-1.5-flash-latest' | 'gemini-1.5-flash' | 'gemini-1.5-pro-latest' | 'gemini-1.5-pro' | 'gemini-1.0-pro'
// doesn't work 'gemini-pro' ,
} = {}
): Promise<Article> {
): Promise<ArticleWithRelations> {
const {
numberOfSearchQueryVariations = 1,
numberOfWebResultsToInclude = 10,
Expand Down Expand Up @@ -164,7 +164,10 @@ export async function writeArticle(
// find or create category
let category = await prisma.articleCategory.findFirst({
where: {
name: report.categoryName
name: {
equals: report.categoryName,
mode: 'insensitive'
}
}
});

Expand All @@ -178,11 +181,12 @@ export async function writeArticle(
});
}

const slug = slugify(report.title + '-prompt-' + topic);
// Save the article to the database
const savedArticle = await prisma.article.create({
data: {
title: report.title,
slug: slugify(report.title),
slug,
description: report.description,
content: report.content,
status: 'DRAFT',
Expand Down Expand Up @@ -227,10 +231,13 @@ export async function writeArticle(
}
},
include: {
user: true,
category: true,
tags: true,
sources: true,
searchResults: true,
generationOptions: true
generationOptions: true,
comments: true
}
});

Expand All @@ -239,21 +246,53 @@ export async function writeArticle(
return savedArticle;
}

export async function findOrCreateArticleByPromptedTopic(promptedTopic: string, userId: string = 'test-user'): Promise<Article> {
let article: Article | null = null;
export type ArticleWithRelations = Prisma.ArticleGetPayload<{
include: {
user: true,
category: true,
tags: true,
sources: true,
searchResults: true,
generationOptions: true,
comments: true
}
}>;

export async function findOrCreateArticleByPromptedTopic(promptedTopic: string,
userId: string = 'test-user'):
Promise<ArticleWithRelations> {
let article: ArticleWithRelations | null = null;
if(userId){
article = await prisma.article.findFirst({
where: {
promptedTopic: promptedTopic,
userId: userId
},
include: {
user: true,
category: true,
tags: true,
sources: true,
searchResults: true,
generationOptions: true,
comments: true
}
});
} else {
article = await prisma.article.findFirst({
where: {
promptedTopic: promptedTopic
}
});
promptedTopic: promptedTopic
},
include: {
user: true,
category: true,
tags: true,
sources: true,
searchResults: true,
generationOptions: true,
comments: true
}
});
}
if(article){
return article;
Expand All @@ -264,10 +303,13 @@ export async function findOrCreateArticleByPromptedTopic(promptedTopic: string,
id: generatedReport.id
},
include: {
user: true,
category: true,
tags: true,
sources: true,
searchResults: true,
generationOptions: true
generationOptions: true,
comments: true
}
});

Expand Down Expand Up @@ -301,3 +343,22 @@ export async function deleteArticleByPromptedTopic(promptedTopic: string, userId
}
});
}

export async function findArticleByTopic(promptedTopic: string, userId: string = 'test-user'):
Promise<ArticleWithRelations | null> {
return prisma.article.findFirst({
where: {
promptedTopic: promptedTopic,
userId: userId
},
include: {
user: true,
category: true,
tags: true,
sources: true,
searchResults: true,
generationOptions: true,
comments: true
}
});
}
Loading

0 comments on commit ad76cfc

Please sign in to comment.