├── .eslintrc.json ├── .env.sample ├── src ├── app │ ├── globals.css │ ├── favicon.ico │ ├── layout.tsx │ ├── api │ │ └── elegance │ │ │ └── [route] │ │ │ └── route.ts │ └── page.tsx ├── services │ ├── eleganceClient.ts │ └── eleganceServerClient.ts └── components │ ├── BookQuery.tsx │ ├── QueryInput.tsx │ └── BookUploader.tsx ├── next.config.js ├── postcss.config.js ├── public ├── background.png ├── singlestore.svg └── elegance-sdk.svg ├── tailwind.config.ts ├── .gitignore ├── tsconfig.json ├── package.json └── README.md /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /.env.sample: -------------------------------------------------------------------------------- 1 | DB_HOST= 2 | DB_USER= 3 | DB_PASSWORD= 4 | DB_NAME= 5 | OPENAI_API_KEY= -------------------------------------------------------------------------------- /src/app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singlestore-labs/elegance-sdk-app-book-query/main/src/app/favicon.ico -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = {} 3 | 4 | module.exports = nextConfig 5 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /public/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singlestore-labs/elegance-sdk-app-book-query/main/public/background.png -------------------------------------------------------------------------------- /src/services/eleganceClient.ts: -------------------------------------------------------------------------------- 1 | import { createEleganceClient } from "@singlestore/elegance-sdk"; 2 | 3 | export const eleganceClient = createEleganceClient("mysql", { baseURL: "http://localhost:3000/api" }); 4 | -------------------------------------------------------------------------------- /src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import "./globals.css"; 2 | import { Roboto } from "next/font/google"; 3 | 4 | const roboto = Roboto({ weight: ["400", "500"], subsets: ["latin"] }); 5 | 6 | export default function RootLayout({ children }: { children: React.ReactNode }) { 7 | return ( 8 | 9 | {children} 10 | 11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /src/services/eleganceServerClient.ts: -------------------------------------------------------------------------------- 1 | import { createEleganceServerClient } from "@singlestore/elegance-sdk/server"; 2 | 3 | export const eleganceServerClient = createEleganceServerClient("mysql", { 4 | connection: { 5 | host: process.env.DB_HOST, 6 | user: process.env.DB_USER, 7 | password: process.env.DB_PASSWORD, 8 | database: process.env.DB_NAME 9 | }, 10 | ai: { 11 | openai: { 12 | apiKey: process.env.OPENAI_API_KEY 13 | } 14 | } 15 | }); 16 | -------------------------------------------------------------------------------- /tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from "tailwindcss"; 2 | 3 | const config: Config = { 4 | content: [ 5 | "./src/pages/**/*.{js,ts,jsx,tsx,mdx}", 6 | "./src/components/**/*.{js,ts,jsx,tsx,mdx}", 7 | "./src/app/**/*.{js,ts,jsx,tsx,mdx}" 8 | ], 9 | theme: { 10 | extend: { 11 | colors: { 12 | s2: { 13 | purple: { 14 | "07": "#DFA1FF" 15 | }, 16 | link: "#820DDF" 17 | } 18 | } 19 | } 20 | }, 21 | plugins: [] 22 | }; 23 | export default config; 24 | -------------------------------------------------------------------------------- /src/app/api/elegance/[route]/route.ts: -------------------------------------------------------------------------------- 1 | import { NextResponse } from "next/server"; 2 | import { Routes } from "@singlestore/elegance-sdk/server"; 3 | import { eleganceServerClient } from "@/services/eleganceServerClient"; 4 | 5 | export async function POST(request: Request, { params }: { params: { route: Routes } }) { 6 | try { 7 | const result = await eleganceServerClient.handleRoute(params.route, await request.json()); 8 | return NextResponse.json(result); 9 | } catch (error: any) { 10 | return NextResponse.json(error, { status: error.status }); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env 29 | .env.local 30 | .env.development.local 31 | .env.test.local 32 | .env.production.local 33 | 34 | # vercel 35 | .vercel 36 | 37 | # typescript 38 | *.tsbuildinfo 39 | next-env.d.ts 40 | 41 | package-lock.json -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "noEmit": true, 9 | "esModuleInterop": true, 10 | "module": "esnext", 11 | "moduleResolution": "bundler", 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "jsx": "preserve", 15 | "incremental": true, 16 | "plugins": [ 17 | { 18 | "name": "next" 19 | } 20 | ], 21 | "paths": { 22 | "@/*": ["./src/*"] 23 | } 24 | }, 25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 26 | "exclude": ["node_modules"] 27 | } 28 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "elegance-sdk-template-next", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@singlestore/elegance-sdk": "^0.0.9", 13 | "next": "^14.2.33", 14 | "react": "latest", 15 | "react-dom": "latest" 16 | }, 17 | "devDependencies": { 18 | "@types/node": "latest", 19 | "@types/react": "latest", 20 | "@types/react-dom": "latest", 21 | "autoprefixer": "latest", 22 | "eslint": "latest", 23 | "eslint-config-next": "latest", 24 | "postcss": "latest", 25 | "tailwindcss": "latest", 26 | "typescript": "latest" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/app/page.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next"; 2 | import { BookQuery } from "@/components/BookQuery"; 3 | 4 | const description = `SingleStore Elegance SDK is an NPM package that helps developers quickly and easily connect to SingleStoreDB, and build React.js-based applications with SingleStore Kai™ and MySQL connection support.`; 5 | 6 | export const metadata: Metadata = { 7 | title: "Elegance SDK Book Query", 8 | description 9 | }; 10 | 11 | export default function Home() { 12 | return ( 13 |
14 |
15 |

16 | {metadata.title?.toString()} 17 |

18 | 19 |
20 |
21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SingleStore Elegance SDK Next.js Template 2 | 3 | **Attention**: The code in this repository is intended for experimental use only and is not fully tested, documented, or supported by SingleStore. Visit the [SingleStore Forums](https://www.singlestore.com/forum/) to ask questions about this repository. 4 | 5 | ## Start Development Environment 6 | 7 | 1. Sign up for [SingleStore](https://www.singlestore.com/) and create a database 8 | 2. Create an `.env` file based on the `.env.sample` file 9 | 3. Install dependencies once by running: `npm i` 10 | 4. Build the application once by running: `npm run build` 11 | 5. Start development environment by running: `npm run dev` 12 | 6. Open [http://localhost:3000](http://localhost:3000) with your browser. 13 | 14 | ## Start Production Environment 15 | 16 | 1. Sign up for [SingleStore](https://www.singlestore.com/) and create a database 17 | 2. Create an `.env` file based on the `.env.sample` file 18 | 3. Install dependencies by running: `npm i` 19 | 4. Build the application once by running: `npm run build` 20 | 5. Start the application by running: `npm run start` 21 | 6. Open [http://localhost:3000](http://localhost:3000) with your browser. 22 | -------------------------------------------------------------------------------- /src/components/BookQuery.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useState } from "react"; 4 | import { BookUploader } from "./BookUploader"; 5 | import { QueryInput } from "./QueryInput"; 6 | 7 | export function BookQuery({ ...props }: JSX.IntrinsicElements["div"]) { 8 | const [bookTableName, setBookTableName] = useState(""); 9 | const [completion, setCompletion] = useState(""); 10 | 11 | let render; 12 | 13 | if (!bookTableName) { 14 | render = setBookTableName(bookTableName)} />; 15 | } else { 16 | if (completion) { 17 | render = ( 18 |
19 |

{completion}

20 | 21 | 27 |
28 | ); 29 | } else { 30 | render = ; 31 | } 32 | } 33 | 34 | return ( 35 |
36 | {render} 37 |
38 | ); 39 | } 40 | -------------------------------------------------------------------------------- /src/components/QueryInput.tsx: -------------------------------------------------------------------------------- 1 | import { eleganceClient } from "@/services/eleganceClient"; 2 | import { useState } from "react"; 3 | 4 | export type QueryInputProps = Omit & { 5 | bookTableName: string; 6 | onSubmit?: (completion: string) => void; 7 | }; 8 | 9 | export function QueryInput({ bookTableName, onSubmit, ...props }: QueryInputProps) { 10 | const [value, setValue] = useState(""); 11 | 12 | const chatCompletion = eleganceClient.hooks.useChatCompletion(); 13 | 14 | const handleSubmit: JSX.IntrinsicElements["form"]["onSubmit"] = async event => { 15 | event.preventDefault(); 16 | if (!value || !bookTableName) return; 17 | 18 | try { 19 | const completion = await chatCompletion.execute({ 20 | table: bookTableName, 21 | prompt: value.trim(), 22 | minSimilarity: 0.6, 23 | maxContextLength: 3000 24 | }); 25 | 26 | if (completion) onSubmit?.(completion.content); 27 | } catch (error) { 28 | console.error(error); 29 | } 30 | }; 31 | 32 | return ( 33 |
34 | setValue(event.target.value)} 38 | className="w-full border rounded-md px-6 py-4 text-lg outline-none hover:border-neutral-300 focus:border-black disabled:bg-neutral-100" 39 | autoFocus 40 | disabled={chatCompletion.isLoading} 41 | /> 42 | 43 | 50 |
51 | ); 52 | } 53 | -------------------------------------------------------------------------------- /src/components/BookUploader.tsx: -------------------------------------------------------------------------------- 1 | import { eleganceClient } from "@/services/eleganceClient"; 2 | import { useRef } from "react"; 3 | 4 | export type BookUploaderProps = JSX.IntrinsicElements["div"] & { 5 | onUpload: (bookTableName: string) => void; 6 | }; 7 | 8 | export function BookUploader({ onUpload, ...props }: BookUploaderProps) { 9 | const inputRef = useRef(null); 10 | 11 | const query = eleganceClient.hooks.useQuery(); 12 | const createAndInsertBookEmbeddings = eleganceClient.hooks.useCreateAndInsertFileEmbeddings(); 13 | 14 | const isLoading = query.isLoading || createAndInsertBookEmbeddings.isLoading; 15 | 16 | const uploadBook = (file: File | null) => { 17 | if (!file) return; 18 | 19 | return new Promise((resolve, reject) => { 20 | try { 21 | const reader = new FileReader(); 22 | 23 | reader.onload = async event => { 24 | if (event.target?.result) { 25 | const dataURL = event.target.result as string; 26 | const table = file.name.split(".")[0].replace(/\W/g, "_"); 27 | await createAndInsertBookEmbeddings.execute({ table, dataURL }); 28 | onUpload(table); 29 | resolve(true); 30 | } 31 | }; 32 | 33 | reader.readAsDataURL(file); 34 | } catch (error) { 35 | reject(error); 36 | } 37 | }); 38 | }; 39 | 40 | const handleInputChange: JSX.IntrinsicElements["input"]["onChange"] = async event => { 41 | if (!event.target.files?.[0]) { 42 | event.target.value = ""; 43 | return; 44 | } 45 | 46 | try { 47 | await uploadBook(event.target.files[0]); 48 | } catch (error) { 49 | console.error(error); 50 | } finally { 51 | event.target.value = ""; 52 | } 53 | }; 54 | 55 | return ( 56 |
57 | 58 | 59 | 72 |
73 | ); 74 | } 75 | -------------------------------------------------------------------------------- /public/singlestore.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /public/elegance-sdk.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | --------------------------------------------------------------------------------