From 8e851a95a2d4ba13fce0f92a1cce149aec42fceb Mon Sep 17 00:00:00 2001 From: William Lyon Date: Fri, 20 Dec 2024 10:18:01 -0700 Subject: [PATCH] trunk formatting --- dgraph-modus-example/README.md | 37 +++++-- dgraph-modus-example/backend/main.go | 5 +- dgraph-modus-example/frontend/README.md | 21 +++- .../frontend/app/[id]/page.tsx | 100 ++++++++--------- dgraph-modus-example/frontend/app/actions.ts | 61 +++++----- dgraph-modus-example/frontend/app/layout.tsx | 16 +-- dgraph-modus-example/frontend/app/page.tsx | 104 +++++++----------- .../frontend/eslint.config.mjs | 18 ++- dgraph-modus-example/frontend/next.config.ts | 6 +- .../frontend/postcss.config.mjs | 4 +- .../frontend/tailwind.config.ts | 6 +- 11 files changed, 175 insertions(+), 203 deletions(-) diff --git a/dgraph-modus-example/README.md b/dgraph-modus-example/README.md index 404c14d..43f906b 100644 --- a/dgraph-modus-example/README.md +++ b/dgraph-modus-example/README.md @@ -1,15 +1,22 @@ # Movie Recommendation App -This project demonstrates the power of **Modus** and **Dgraph** by integrating a graph database with a large language model (LLM) to provide AI-driven movie recommendations. The app consists of a backend (powered by Modus and Dgraph) and a frontend (built with Next.js and tailwind). +This project demonstrates the power of **Modus** and **Dgraph** by integrating a graph database with +a large language model (LLM) to provide AI-driven movie recommendations. The app consists of a +backend (powered by Modus and Dgraph) and a frontend (built with Next.js and tailwind). ## Why Use Modus with Dgraph? -**Modus** simplifies the integration of internal data (from sources like Dgraph) with cutting-edge LLMs. Here's why this combination is powerful: +**Modus** simplifies the integration of internal data (from sources like Dgraph) with cutting-edge +LLMs. Here's why this combination is powerful: -- **Rich Data Queries**: Dgraph enables complex queries across interconnected datasets like movies, genres, actors, and directors. This makes it ideal for graph-based recommendations. -- **Seamless AI Integration**: Modus abstracts the complexity of using LLMs, allowing you to feed internal data into models and retrieve actionable insights tailored to your data. -- **Rapid Prototyping**: With Modus, you can quickly build and test serverless functions that combine graph-based querying and AI recommendations. -- **Customizability**: Dynamically construct prompts for LLMs using Dgraph's query results to generate personalized outputs, such as movie recommendations. +- **Rich Data Queries**: Dgraph enables complex queries across interconnected datasets like movies, + genres, actors, and directors. This makes it ideal for graph-based recommendations. +- **Seamless AI Integration**: Modus abstracts the complexity of using LLMs, allowing you to feed + internal data into models and retrieve actionable insights tailored to your data. +- **Rapid Prototyping**: With Modus, you can quickly build and test serverless functions that + combine graph-based querying and AI recommendations. +- **Customizability**: Dynamically construct prompts for LLMs using Dgraph's query results to + generate personalized outputs, such as movie recommendations. --- @@ -19,14 +26,18 @@ The backend is written in Go using **Modus** and **Dgraph**. ### Features -- Functions powered by Modus are integrated with a read-only Dgraph database and an LLM from Hugging Face. +- Functions powered by Modus are integrated with a read-only Dgraph database and an LLM from Hugging + Face. - Provides a GraphQL endpoint for querying data locally. -- Demonstrates how to use Modus to create powerful, serverless APIs that leverage Dgraph's capabilities and LLM-driven insights. +- Demonstrates how to use Modus to create powerful, serverless APIs that leverage Dgraph's + capabilities and LLM-driven insights. ### Prerequisites -- Install **Modus CLI**: Follow the [Modus installation guide](https://docs.hypermode.com/modus/installation). -- Ensure **Dgraph** is available or connected to the backend (the project uses a read-only Dgraph database). +- Install **Modus CLI**: Follow the + [Modus installation guide](https://docs.hypermode.com/modus/installation). +- Ensure **Dgraph** is available or connected to the backend (the project uses a read-only Dgraph + database). ### Running Locally @@ -119,10 +130,12 @@ The frontend is built with **Next.js** and **Tailwind**. ## Learning More -- Modus Documentation: [https://docs.hypermode.com/modus/overview](https://docs.hypermode.com/modus/overview) +- Modus Documentation: + [https://docs.hypermode.com/modus/overview](https://docs.hypermode.com/modus/overview) - Dgraph Documentation: [https://dgraph.io/docs](https://dgraph.io/docs) - Next.js Documentation: [https://nextjs.org/docs](https://nextjs.org/docs) --- -This project is an excellent starting point for understanding how to combine the strengths of Dgraph, Modus, and LLMs to create powerful, data-driven applications. 🚀 +This project is an excellent starting point for understanding how to combine the strengths of +Dgraph, Modus, and LLMs to create powerful, data-driven applications. 🚀 diff --git a/dgraph-modus-example/backend/main.go b/dgraph-modus-example/backend/main.go index 82ec5d8..17f0012 100644 --- a/dgraph-modus-example/backend/main.go +++ b/dgraph-modus-example/backend/main.go @@ -5,8 +5,8 @@ import ( "fmt" "strings" - "github.com/hypermodeinc/modus/sdk/go/pkg/http" // Modus HTTP library for making API requests - "github.com/hypermodeinc/modus/sdk/go/pkg/models" // Modus models library for AI models + "github.com/hypermodeinc/modus/sdk/go/pkg/http" // Modus HTTP library for making API requests + "github.com/hypermodeinc/modus/sdk/go/pkg/models" // Modus models library for AI models "github.com/hypermodeinc/modus/sdk/go/pkg/models/openai" // Modus OpenAI model integration ) @@ -177,7 +177,6 @@ func generatePrompt(movieName string, searchQuery string) string { return prompt } - // executeDgraphQuery performs the actual query against the Dgraph database func executeDgraphQuery(query string) (string, error) { queryPayload := map[string]string{"query": query} diff --git a/dgraph-modus-example/frontend/README.md b/dgraph-modus-example/frontend/README.md index e215bc4..ce9822f 100644 --- a/dgraph-modus-example/frontend/README.md +++ b/dgraph-modus-example/frontend/README.md @@ -1,4 +1,5 @@ -This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). +This is a [Next.js](https://nextjs.org) project bootstrapped with +[`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). ## Getting Started @@ -16,9 +17,12 @@ bun dev Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. -You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. +You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the +file. -This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. +This project uses +[`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to +automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. ## Learn More @@ -27,10 +31,15 @@ To learn more about Next.js, take a look at the following resources: - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. -You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! +You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback +and contributions are welcome! ## Deploy on Vercel -The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. +The easiest way to deploy your Next.js app is to use the +[Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) +from the creators of Next.js. -Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. +Check out our +[Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) +for more details. diff --git a/dgraph-modus-example/frontend/app/[id]/page.tsx b/dgraph-modus-example/frontend/app/[id]/page.tsx index 2e01093..48368e9 100644 --- a/dgraph-modus-example/frontend/app/[id]/page.tsx +++ b/dgraph-modus-example/frontend/app/[id]/page.tsx @@ -1,50 +1,46 @@ -import { Suspense } from "react"; -import { fetchMovieDetailsAndRecommendations } from "@/app/actions"; -import Link from "next/link"; +import { Suspense } from "react" +import { fetchMovieDetailsAndRecommendations } from "@/app/actions" +import Link from "next/link" function Loading() { return (

- Fetching movie details and personalized recommendations from our AI - system, please be patient... + Fetching movie details and personalized recommendations from our AI system, please be + patient...

- ); + ) } type Movie = { - "name@en": string; - initial_release_date?: string; - genre?: { "name@en": string }[]; - starring?: { "performance.actor": { "name@en": string }[] }[]; -}; + "name@en": string + initial_release_date?: string + genre?: { "name@en": string }[] + starring?: { "performance.actor": { "name@en": string }[] }[] +} -type Recommendations = string; +type Recommendations = string type MovieDetailsResponse = { - movieDetails: string; - recommendations: Recommendations; -}; + movieDetails: string + recommendations: Recommendations +} -async function MovieDetails({ - movieId, - searchQuery, -}: { - movieId: string; - searchQuery: string; -}) { - const recommendations: MovieDetailsResponse = - await fetchMovieDetailsAndRecommendations(movieId, searchQuery); - const movieParsed = JSON.parse(recommendations.movieDetails); - const movie: Movie | undefined = movieParsed.data.movie[0]; +async function MovieDetails({ movieId, searchQuery }: { movieId: string; searchQuery: string }) { + const recommendations: MovieDetailsResponse = await fetchMovieDetailsAndRecommendations( + movieId, + searchQuery, + ) + const movieParsed = JSON.parse(recommendations.movieDetails) + const movie: Movie | undefined = movieParsed.data.movie[0] if (!movie) { return (
Movie not found
- ); + ) } return ( @@ -52,18 +48,15 @@ async function MovieDetails({

Recommendations

- These recommendations are based on your interest in{" "} - {movie["name@en"]} - {searchQuery - ? " and your search query '" + searchQuery + "'" - : ""}. Modus combines internal data from{" "} - Dgraph with a{" "} - large language model (LLM), making it easy to generate - AI-driven insights tailored to your data. + These recommendations are based on your interest in {movie["name@en"]} + {searchQuery ? " and your search query '" + searchQuery + "'" : ""}. Modus{" "} + combines internal data from Dgraph with a{" "} + large language model (LLM), making it easy to generate AI-driven insights + tailored to your data.
- ); + ) } function MovieCard({ movie }: { movie: Movie }) { @@ -73,32 +66,28 @@ function MovieCard({ movie }: { movie: Movie }) {

Release Year:{" "} - {movie.initial_release_date - ? new Date(movie.initial_release_date).getFullYear() - : "N/A"} + {movie.initial_release_date ? new Date(movie.initial_release_date).getFullYear() : "N/A"}

Genre:{" "} - {movie.genre?.map((genre) => genre["name@en"]).join(", ") || - "No genres listed"} + {movie.genre?.map((genre) => genre["name@en"]).join(", ") || "No genres listed"}

- Starring:{" "} - {getStarringActors(movie.starring) || "No actors listed"} + Starring: {getStarringActors(movie.starring) || "No actors listed"}

- ); + ) } function getStarringActors(starring?: Movie["starring"]): string | null { - if (!starring) return null; + if (!starring) return null const actors = starring .flatMap((s) => s["performance.actor"]?.map((actor) => actor["name@en"])) - .filter(Boolean); + .filter(Boolean) - return actors.length > 0 ? actors.slice(0, 5).join(", ") : null; + return actors.length > 0 ? actors.slice(0, 5).join(", ") : null } function Recommendations({ recommendations }: { recommendations: string }) { @@ -107,30 +96,27 @@ function Recommendations({ recommendations }: { recommendations: string }) { className="prose prose-invert max-w-none bg-white/10 p-6 rounded" dangerouslySetInnerHTML={{ __html: recommendations }} /> - ); + ) } export default function MoviePage({ params, searchParams, }: { - params: { id: string }; - searchParams: { [key: string]: string | undefined }; + params: { id: string } + searchParams: { [key: string]: string | undefined } }) { - const movieId = params.id; - const searchQuery = searchParams.search || ""; + const movieId = params.id + const searchQuery = searchParams.search || "" return (
- + ← Back to Movies }>
- ); + ) } diff --git a/dgraph-modus-example/frontend/app/actions.ts b/dgraph-modus-example/frontend/app/actions.ts index 87234e0..e042ca9 100644 --- a/dgraph-modus-example/frontend/app/actions.ts +++ b/dgraph-modus-example/frontend/app/actions.ts @@ -1,9 +1,9 @@ -"use server"; +"use server" type FetchQueryProps = { - query: string; - variables?: any; -}; + query: string + variables?: any +} const fetchQuery = async ({ query, variables }: FetchQueryProps) => { try { @@ -14,74 +14,71 @@ const fetchQuery = async ({ query, variables }: FetchQueryProps) => { }, body: JSON.stringify({ query, variables }), cache: "no-store", - }); + }) - if (!res.ok) throw new Error(res.statusText); + if (!res.ok) throw new Error(res.statusText) - const { data, errors } = await res.json(); - if (errors) throw new Error(JSON.stringify(errors)); + const { data, errors } = await res.json() + if (errors) throw new Error(JSON.stringify(errors)) - return { data }; + return { data } } catch (err) { - console.error("Error in fetchQuery:", err); - return { data: null, error: err }; + console.error("Error in fetchQuery:", err) + return { data: null, error: err } } -}; +} export async function fetchMovies(page: number = 1, search: string = "") { const graphqlQuery = ` query FetchMovies($page: Int!, $search: String!) { fetchMoviesWithPaginationAndSearch(page: $page, search: $search) } - `; + ` const { data, error } = await fetchQuery({ query: graphqlQuery, variables: { page, search }, - }); + }) if (error) { - console.error("Error fetching movies:", error); - return { movies: [] }; + console.error("Error fetching movies:", error) + return { movies: [] } } try { - const parsedData = JSON.parse(data.fetchMoviesWithPaginationAndSearch); - return { movies: parsedData.data.movies || [] }; + const parsedData = JSON.parse(data.fetchMoviesWithPaginationAndSearch) + return { movies: parsedData.data.movies || [] } } catch (err) { - console.error("Error parsing response:", err); - return { movies: [] }; + console.error("Error parsing response:", err) + return { movies: [] } } } -export async function fetchMovieDetailsAndRecommendations( - uid: string, - searchQuery: string = "" -) { +export async function fetchMovieDetailsAndRecommendations(uid: string, searchQuery: string = "") { const graphqlQuery = ` query FetchMovieDetailsAndRecommendations($uid: String!, $searchQuery: String!) { fetchMovieDetailsAndRecommendations(uid: $uid, searchQuery: $searchQuery) } - `; + ` const { data, error } = await fetchQuery({ query: graphqlQuery, variables: { uid, searchQuery }, - }); + }) if (error) { - console.error("Error fetching movie details and recommendations:", error); - return { movieDetails: null, recommendations: [] }; + console.error("Error fetching movie details and recommendations:", error) + return { movieDetails: null, recommendations: [] } } try { - const parsedData = JSON.parse(data.fetchMovieDetailsAndRecommendations); + const parsedData = JSON.parse(data.fetchMovieDetailsAndRecommendations) return { movieDetails: parsedData.movieDetails || {}, recommendations: parsedData.recommendations || [], - }; + } } catch (err) { - console.error("Error parsing response:", err); - return { movieDetails: null, recommendations: [] }; + console.error("Error parsing response:", err) + return { movieDetails: null, recommendations: [] } } } diff --git a/dgraph-modus-example/frontend/app/layout.tsx b/dgraph-modus-example/frontend/app/layout.tsx index 843b086..c9b8e43 100644 --- a/dgraph-modus-example/frontend/app/layout.tsx +++ b/dgraph-modus-example/frontend/app/layout.tsx @@ -1,26 +1,26 @@ -import type { Metadata } from "next"; -import { Geist, Geist_Mono } from "next/font/google"; -import "./globals.css"; +import type { Metadata } from "next" +import { Geist, Geist_Mono } from "next/font/google" +import "./globals.css" const geistSans = Geist({ variable: "--font-geist-sans", subsets: ["latin"], -}); +}) const geistMono = Geist_Mono({ variable: "--font-geist-mono", subsets: ["latin"], -}); +}) export const metadata: Metadata = { title: "Create Next App", description: "Generated by create next app", -}; +} export default function RootLayout({ children, }: Readonly<{ - children: React.ReactNode; + children: React.ReactNode }>) { return ( @@ -30,5 +30,5 @@ export default function RootLayout({ {children} - ); + ) } diff --git a/dgraph-modus-example/frontend/app/page.tsx b/dgraph-modus-example/frontend/app/page.tsx index f73b518..b8fc269 100644 --- a/dgraph-modus-example/frontend/app/page.tsx +++ b/dgraph-modus-example/frontend/app/page.tsx @@ -1,32 +1,28 @@ -import { fetchMovies } from "./actions"; -import Link from "next/link"; +import { fetchMovies } from "./actions" +import Link from "next/link" type SearchParams = { - page?: string; - search?: string; -}; + page?: string + search?: string +} type Movie = { - uid: string; - "name@en": string; - initial_release_date?: string; - genre?: { "name@en": string }[]; - starring?: { "performance.actor": { "name@en": string }[] }[]; -}; + uid: string + "name@en": string + initial_release_date?: string + genre?: { "name@en": string }[] + starring?: { "performance.actor": { "name@en": string }[] }[] +} type Response = { - movies: Movie[]; -}; + movies: Movie[] +} -export default async function Home({ - searchParams, -}: { - searchParams: SearchParams; -}) { - const currentPage = Number(searchParams.page) || 1; - const searchQuery = searchParams.search || ""; +export default async function Home({ searchParams }: { searchParams: SearchParams }) { + const currentPage = Number(searchParams.page) || 1 + const searchQuery = searchParams.search || "" - const response: Response = await fetchMovies(currentPage, searchQuery); + const response: Response = await fetchMovies(currentPage, searchQuery) return (
@@ -42,7 +38,7 @@ export default async function Home({ hasNextPage={response.movies.length > 0} />
- ); + ) } function SearchForm({ searchQuery }: { searchQuery: string }) { @@ -55,25 +51,16 @@ function SearchForm({ searchQuery }: { searchQuery: string }) { defaultValue={searchQuery} className="w-full px-4 py-2 border border-gray-300 rounded text-black" /> - - ); + ) } -function MoviesGrid({ - movies, - searchQuery, -}: { - movies: Movie[]; - searchQuery: string; -}) { +function MoviesGrid({ movies, searchQuery }: { movies: Movie[]; searchQuery: string }) { if (movies.length === 0) { - return

No movies found

; + return

No movies found

} return ( @@ -82,16 +69,10 @@ function MoviesGrid({ ))} - ); + ) } -function MovieCard({ - movie, - searchQuery, -}: { - movie: Movie; - searchQuery: string; -}) { +function MovieCard({ movie, searchQuery }: { movie: Movie; searchQuery: string }) { return (

{movie["name@en"]}

- {movie.initial_release_date - ? new Date(movie.initial_release_date).getFullYear() - : "N/A"} + {movie.initial_release_date ? new Date(movie.initial_release_date).getFullYear() : "N/A"}
-

- Genre:{" "} - {movie.genre?.map((g) => g["name@en"]).join(", ") || "No genres listed"} -

+

Genre: {movie.genre?.map((g) => g["name@en"]).join(", ") || "No genres listed"}

Starring: {getStarringActors(movie.starring) || "No actors listed"}

- ); + ) } function getStarringActors(starring?: Movie["starring"]): string | null { - if (!starring) return null; + if (!starring) return null const actors = starring .flatMap((s) => s["performance.actor"]?.map((actor) => actor["name@en"])) - .filter(Boolean); + .filter(Boolean) - return actors.length > 0 ? actors.slice(0, 5).join(", ") : null; + return actors.length > 0 ? actors.slice(0, 5).join(", ") : null } function Pagination({ @@ -131,37 +107,31 @@ function Pagination({ searchQuery, hasNextPage, }: { - currentPage: number; - searchQuery: string; - hasNextPage: boolean; + currentPage: number + searchQuery: string + hasNextPage: boolean }) { return (
{currentPage > 1 && ( Previous )} - - Page {currentPage} - + Page {currentPage} {hasNextPage && ( Next )}
- ); + ) } diff --git a/dgraph-modus-example/frontend/eslint.config.mjs b/dgraph-modus-example/frontend/eslint.config.mjs index c85fb67..f82ac7f 100644 --- a/dgraph-modus-example/frontend/eslint.config.mjs +++ b/dgraph-modus-example/frontend/eslint.config.mjs @@ -1,16 +1,14 @@ -import { dirname } from "path"; -import { fileURLToPath } from "url"; -import { FlatCompat } from "@eslint/eslintrc"; +import { dirname } from "path" +import { fileURLToPath } from "url" +import { FlatCompat } from "@eslint/eslintrc" -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); +const __filename = fileURLToPath(import.meta.url) +const __dirname = dirname(__filename) const compat = new FlatCompat({ baseDirectory: __dirname, -}); +}) -const eslintConfig = [ - ...compat.extends("next/core-web-vitals", "next/typescript"), -]; +const eslintConfig = [...compat.extends("next/core-web-vitals", "next/typescript")] -export default eslintConfig; +export default eslintConfig diff --git a/dgraph-modus-example/frontend/next.config.ts b/dgraph-modus-example/frontend/next.config.ts index e9ffa30..3b84f68 100644 --- a/dgraph-modus-example/frontend/next.config.ts +++ b/dgraph-modus-example/frontend/next.config.ts @@ -1,7 +1,7 @@ -import type { NextConfig } from "next"; +import type { NextConfig } from "next" const nextConfig: NextConfig = { /* config options here */ -}; +} -export default nextConfig; +export default nextConfig diff --git a/dgraph-modus-example/frontend/postcss.config.mjs b/dgraph-modus-example/frontend/postcss.config.mjs index 1a69fd2..0dc456a 100644 --- a/dgraph-modus-example/frontend/postcss.config.mjs +++ b/dgraph-modus-example/frontend/postcss.config.mjs @@ -3,6 +3,6 @@ const config = { plugins: { tailwindcss: {}, }, -}; +} -export default config; +export default config diff --git a/dgraph-modus-example/frontend/tailwind.config.ts b/dgraph-modus-example/frontend/tailwind.config.ts index bbba95f..4675383 100644 --- a/dgraph-modus-example/frontend/tailwind.config.ts +++ b/dgraph-modus-example/frontend/tailwind.config.ts @@ -1,5 +1,5 @@ -import type { Config } from "tailwindcss"; -import typography from "@tailwindcss/typography"; +import type { Config } from "tailwindcss" +import typography from "@tailwindcss/typography" export default { content: [ @@ -17,4 +17,4 @@ export default { }, plugins: [typography], -} satisfies Config; +} satisfies Config