├── .eslintignore
├── public
├── favicon.ico
├── demo-screenshot.png
├── github-mark-white.png
└── axilla-logo-text-white.png
├── postcss.config.js
├── types
├── nav.ts
└── store.ts
├── lib
├── types.ts
├── fonts.ts
├── utils.ts
├── axgen-utils.ts
├── ingest.ts
└── query.ts
├── .prettierignore
├── .vscode
└── settings.json
├── next.config.mjs
├── .editorconfig
├── next-env.d.ts
├── prisma
└── schema.prisma
├── .github
└── lint.yml
├── components
├── theme-provider.tsx
├── tailwind-indicator.tsx
├── theme-toggle.tsx
├── ui
│ ├── label.tsx
│ ├── textarea.tsx
│ ├── separator.tsx
│ ├── input.tsx
│ ├── toaster.tsx
│ ├── slider.tsx
│ ├── badge.tsx
│ ├── switch.tsx
│ ├── button.tsx
│ ├── tabs.tsx
│ ├── card.tsx
│ ├── table.tsx
│ ├── dialog.tsx
│ ├── use-toast.ts
│ ├── select.tsx
│ ├── form.tsx
│ └── toast.tsx
├── main-nav.tsx
├── site-header.tsx
├── code-editor.tsx
├── cell.tsx
├── icons.tsx
└── monaco-theme.ts
├── components.json
├── config
└── site.ts
├── .gitignore
├── .env.example
├── app
├── api
│ ├── ingest
│ │ ├── wikipedia
│ │ │ └── route.ts
│ │ └── upload
│ │ │ └── route.ts
│ └── query
│ │ ├── route.ts
│ │ └── stream
│ │ └── route.ts
├── components
│ ├── embedding-model.tsx
│ ├── sidebar.tsx
│ ├── document-card.tsx
│ ├── config-context.tsx
│ ├── ingest-wikipedia.tsx
│ ├── ingest-upload.tsx
│ ├── store-config.tsx
│ ├── query.tsx
│ └── query-config.tsx
├── page.tsx
└── layout.tsx
├── tsconfig.json
├── .eslintrc.json
├── prettier.config.js
├── README.md
├── styles
└── globals.css
├── package.json
├── tailwind.config.js
└── tsconfig.tsbuildinfo
/.eslintignore:
--------------------------------------------------------------------------------
1 | dist/*
2 | .cache
3 | public
4 | node_modules
5 | *.esm.js
6 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axflow/original-demo-ui/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/public/demo-screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axflow/original-demo-ui/HEAD/public/demo-screenshot.png
--------------------------------------------------------------------------------
/public/github-mark-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axflow/original-demo-ui/HEAD/public/github-mark-white.png
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | };
7 |
--------------------------------------------------------------------------------
/public/axilla-logo-text-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axflow/original-demo-ui/HEAD/public/axilla-logo-text-white.png
--------------------------------------------------------------------------------
/types/nav.ts:
--------------------------------------------------------------------------------
1 | export interface NavItem {
2 | title: string
3 | href?: string
4 | disabled?: boolean
5 | external?: boolean
6 | }
7 |
--------------------------------------------------------------------------------
/lib/types.ts:
--------------------------------------------------------------------------------
1 | export type ContextDocument = {
2 | id: string;
3 | chunk: {
4 | text: string;
5 | url: string;
6 | };
7 | similarity?: number;
8 | };
9 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | cache
2 | .cache
3 | package.json
4 | package-lock.json
5 | public
6 | CHANGELOG.md
7 | .yarn
8 | dist
9 | node_modules
10 | .next
11 | build
12 | .contentlayer
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "typescript.tsdk": "../../node_modules/.pnpm/typescript@4.9.5/node_modules/typescript/lib",
3 | "typescript.enablePromptUseWorkspaceTsdk": true
4 | }
5 |
--------------------------------------------------------------------------------
/next.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {
3 | reactStrictMode: true,
4 | experimental: {
5 | appDir: true,
6 | },
7 | }
8 |
9 | export default nextConfig
10 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | end_of_line = lf
7 | indent_size = 2
8 | indent_style = space
9 | insert_final_newline = true
10 | trim_trailing_whitespace = true
11 |
--------------------------------------------------------------------------------
/next-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | // NOTE: This file should not be edited
5 | // see https://nextjs.org/docs/basic-features/typescript for more information.
6 |
--------------------------------------------------------------------------------
/prisma/schema.prisma:
--------------------------------------------------------------------------------
1 | // This is your Prisma schema file,
2 | // learn more about it in the docs: https://pris.ly/d/prisma-schema
3 |
4 | generator client {
5 | provider = "prisma-client-js"
6 | }
7 |
8 | datasource db {
9 | provider = "postgresql"
10 | url = env("DATABASE_URL")
11 | }
12 |
--------------------------------------------------------------------------------
/lib/fonts.ts:
--------------------------------------------------------------------------------
1 | import { JetBrains_Mono as FontMono, Inter as FontSans } from 'next/font/google';
2 |
3 | export const fontSans = FontSans({
4 | subsets: ['latin'],
5 | variable: '--font-sans',
6 | });
7 |
8 | export const fontMono = FontMono({
9 | subsets: ['latin'],
10 | variable: '--font-mono',
11 | });
12 |
--------------------------------------------------------------------------------
/.github/lint.yml:
--------------------------------------------------------------------------------
1 | name: Lint
2 | on: [pull_request]
3 | jobs:
4 | pr_lint:
5 | runs-on: ubuntu-22.04
6 | steps:
7 | - uses: actions/checkout@v3
8 | - name: setup node
9 | uses: actions/setup-node@v3
10 | with:
11 | node-version: 18
12 | - name: Run linter
13 | - run: npm run lint
14 |
--------------------------------------------------------------------------------
/types/store.ts:
--------------------------------------------------------------------------------
1 | export type PineconeStore = {
2 | name: 'pinecone';
3 | indexName: string;
4 | indexDimensions: number;
5 | environment: string;
6 | namespace: string;
7 | };
8 |
9 | export type PgVectorStore = {
10 | name: 'pgvector';
11 | tableName: string;
12 | dsn: string;
13 | };
14 |
15 | export type SupportedStore = PineconeStore | PgVectorStore;
16 |
--------------------------------------------------------------------------------
/components/theme-provider.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import * as React from 'react';
4 | import { ThemeProvider as NextThemesProvider } from 'next-themes';
5 | import { ThemeProviderProps } from 'next-themes/dist/types';
6 |
7 | export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
8 | return {children};
9 | }
10 |
--------------------------------------------------------------------------------
/components.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://ui.shadcn.com/schema.json",
3 | "style": "default",
4 | "rsc": true,
5 | "tsx": true,
6 | "tailwind": {
7 | "config": "tailwind.config.js",
8 | "css": "styles/globals.css",
9 | "baseColor": "slate",
10 | "cssVariables": true
11 | },
12 | "aliases": {
13 | "components": "@/components",
14 | "utils": "@/lib/utils"
15 | }
16 | }
--------------------------------------------------------------------------------
/config/site.ts:
--------------------------------------------------------------------------------
1 | export type SiteConfig = typeof siteConfig;
2 |
3 | export const siteConfig = {
4 | name: 'axgen demo',
5 | description: 'Ingestion, query & retrieval playground for the Axilla SDK.',
6 | mainNav: [
7 | {
8 | title: 'Home',
9 | href: '/',
10 | },
11 | ],
12 | links: {
13 | twitter: 'https://twitter.com/axilla_io',
14 | github: 'https://github.com/axilla-io/axgen',
15 | docs: 'https://docs.axilla.io',
16 | },
17 | };
18 |
--------------------------------------------------------------------------------
/lib/utils.ts:
--------------------------------------------------------------------------------
1 | import { type ClassValue, clsx } from 'clsx';
2 | import { twMerge } from 'tailwind-merge';
3 |
4 | export function cn(...inputs: ClassValue[]) {
5 | return twMerge(clsx(inputs));
6 | }
7 |
8 | export function getEnv(key: string) {
9 | return process.env[key];
10 | }
11 |
12 | export function getEnvOrThrow(key: string) {
13 | const value = getEnv(key);
14 |
15 | if (!value) {
16 | throw new Error(`Expected "${key}" to be set in the process environment`);
17 | }
18 |
19 | return value;
20 | }
21 |
--------------------------------------------------------------------------------
/.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 | build
15 |
16 | # misc
17 | .DS_Store
18 | *.pem
19 |
20 | # debug
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 | .pnpm-debug.log*
25 |
26 | # local env files
27 | .env.local
28 | .env.development.local
29 | .env.test.local
30 | .env.production.local
31 |
32 | # turbo
33 | .turbo
34 |
35 | .contentlayer
36 | .env
--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------
1 | ## Pinecone settings
2 | # Pinecone secret API key, e.g.: (not a real key)
3 | PINECONE_API_KEY=
4 | # Pinecone environment
5 | PINECONE_ENVIRONMENT=northamerica-northeast1-gcp
6 | # Pinecone index name, can be whatever you want.
7 | PINECONE_INDEX=
8 | # Pinecone vector dimensions. This will be coupled to the embedding model you use.
9 | # For example, OpenAI's text-embedding-ada-002 is 1536 dimensions.
10 | PINECONE_INDEX_DIMENSION=1536
11 | # Pinecone namespace, "default" by default.
12 | PINECONE_NAMESPACE=default
13 |
14 | ## OpenAI settings
15 | OPENAI_API_KEY=sk-fake
16 |
--------------------------------------------------------------------------------
/app/api/ingest/wikipedia/route.ts:
--------------------------------------------------------------------------------
1 | import { NextRequest, NextResponse } from 'next/server';
2 | import { ingestWikipedia } from '@/lib/ingest';
3 |
4 | export async function POST(request: NextRequest) {
5 | const { term, store } = await request.json();
6 | try {
7 | await ingestWikipedia(term, store);
8 | } catch (error: unknown) {
9 | let message = 'Unknown Error';
10 | if (error instanceof Error) message = error.message;
11 |
12 | return NextResponse.json({ error: message }, { status: 500 });
13 | }
14 |
15 | return NextResponse.json({ term }, { status: 200 });
16 | }
17 |
--------------------------------------------------------------------------------
/components/tailwind-indicator.tsx:
--------------------------------------------------------------------------------
1 | export function TailwindIndicator() {
2 | if (process.env.NODE_ENV === 'production') return null;
3 |
4 | return (
5 |
6 |
xs
7 |
sm
8 |
md
9 |
lg
10 |
xl
11 |
2xl
12 |
13 | );
14 | }
15 |
--------------------------------------------------------------------------------
/lib/axgen-utils.ts:
--------------------------------------------------------------------------------
1 | import { Pinecone, PgVector } from 'axgen';
2 | import { getEnvOrThrow } from './utils';
3 |
4 | export const getPineconeStore = () => {
5 | return new Pinecone({
6 | index: getEnvOrThrow('PINECONE_INDEX'),
7 | namespace: getEnvOrThrow('PINECONE_NAMESPACE'),
8 | apiKey: getEnvOrThrow('PINECONE_API_KEY'),
9 | environment: getEnvOrThrow('PINECONE_ENVIRONMENT'),
10 | });
11 | };
12 |
13 | export const getPgVectorStore = () => {
14 | return new PgVector({
15 | dsn: 'postgresql://localhost/axilla-demo',
16 | tableName: 'vectors',
17 | });
18 | };
19 |
20 | export const getOpenAiKey = () => {
21 | return getEnvOrThrow('OPENAI_API_KEY');
22 | };
23 |
--------------------------------------------------------------------------------
/components/theme-toggle.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import * as React from 'react';
4 | import { useTheme } from 'next-themes';
5 |
6 | import { Button } from '@/components/ui/button';
7 | import { Icons } from '@/components/icons';
8 |
9 | export function ThemeToggle() {
10 | const { setTheme, theme } = useTheme();
11 |
12 | return (
13 |
22 | );
23 | }
24 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "lib": ["dom", "dom.iterable", "esnext"],
4 | "allowJs": true,
5 | "skipLibCheck": true,
6 | "target": "esnext",
7 | "strict": true,
8 | "forceConsistentCasingInFileNames": true,
9 | "noEmit": true,
10 | "incremental": true,
11 | "esModuleInterop": true,
12 | "module": "esnext",
13 | "moduleResolution": "node",
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "jsx": "preserve",
17 | "baseUrl": ".",
18 | "paths": {
19 | "@/*": ["./*"]
20 | },
21 | "plugins": [
22 | {
23 | "name": "next"
24 | }
25 | ],
26 | "strictNullChecks": true
27 | },
28 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
29 | "exclude": ["node_modules"]
30 | }
31 |
--------------------------------------------------------------------------------
/components/ui/label.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as LabelPrimitive from "@radix-ui/react-label"
5 | import { cva, type VariantProps } from "class-variance-authority"
6 |
7 | import { cn } from "@/lib/utils"
8 |
9 | const labelVariants = cva(
10 | "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
11 | )
12 |
13 | const Label = React.forwardRef<
14 | React.ElementRef,
15 | React.ComponentPropsWithoutRef &
16 | VariantProps
17 | >(({ className, ...props }, ref) => (
18 |
23 | ))
24 | Label.displayName = LabelPrimitive.Root.displayName
25 |
26 | export { Label }
27 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/eslintrc",
3 | "root": true,
4 | "extends": [
5 | "next/core-web-vitals",
6 | "plugin:@typescript-eslint/recommended",
7 | "prettier",
8 | "plugin:tailwindcss/recommended"
9 | ],
10 | "plugins": ["tailwindcss", "@typescript-eslint"],
11 | "rules": {
12 | "@next/next/no-html-link-for-pages": "off",
13 | "react/jsx-key": "off",
14 | "tailwindcss/no-custom-classname": "off",
15 | "@typescript-eslint/ban-ts-comment": "off"
16 | },
17 | "settings": {
18 | "tailwindcss": {
19 | "callees": ["cn"],
20 | "config": "tailwind.config.js"
21 | },
22 | "next": {
23 | "rootDir": ["./"]
24 | }
25 | },
26 | "overrides": [
27 | {
28 | "files": ["*.ts", "*.tsx"],
29 | "parser": "@typescript-eslint/parser"
30 | }
31 | ]
32 | }
33 |
--------------------------------------------------------------------------------
/components/ui/textarea.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import { cn } from '@/lib/utils';
4 |
5 | // @ts-ignore
6 | export type TextareaProps = React.TextareaHTMLAttributes;
7 |
8 | const Textarea = React.forwardRef(
9 | ({ className, ...props }, ref) => {
10 | return (
11 |
19 | );
20 | }
21 | );
22 | Textarea.displayName = 'Textarea';
23 |
24 | export { Textarea };
25 |
--------------------------------------------------------------------------------
/components/ui/separator.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as SeparatorPrimitive from "@radix-ui/react-separator"
5 |
6 | import { cn } from "@/lib/utils"
7 |
8 | const Separator = React.forwardRef<
9 | React.ElementRef,
10 | React.ComponentPropsWithoutRef
11 | >(
12 | (
13 | { className, orientation = "horizontal", decorative = true, ...props },
14 | ref
15 | ) => (
16 |
27 | )
28 | )
29 | Separator.displayName = SeparatorPrimitive.Root.displayName
30 |
31 | export { Separator }
32 |
--------------------------------------------------------------------------------
/components/ui/input.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import { cn } from '@/lib/utils';
4 |
5 | // @ts-ignore
6 | export type InputProps = React.InputHTMLAttributes;
7 |
8 | const Input = React.forwardRef(
9 | ({ className, type, ...props }, ref) => {
10 | return (
11 |
20 | );
21 | }
22 | );
23 | Input.displayName = 'Input';
24 |
25 | export { Input };
26 |
--------------------------------------------------------------------------------
/components/ui/toaster.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import {
4 | Toast,
5 | ToastClose,
6 | ToastDescription,
7 | ToastProvider,
8 | ToastTitle,
9 | ToastViewport,
10 | } from "@/components/ui/toast"
11 | import { useToast } from "@/components/ui/use-toast"
12 |
13 | export function Toaster() {
14 | const { toasts } = useToast()
15 |
16 | return (
17 |
18 | {toasts.map(function ({ id, title, description, action, ...props }) {
19 | return (
20 |
21 |
22 | {title && {title}}
23 | {description && (
24 | {description}
25 | )}
26 |
27 | {action}
28 |
29 |
30 | )
31 | })}
32 |
33 |
34 | )
35 | }
36 |
--------------------------------------------------------------------------------
/prettier.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('prettier').Config} */
2 | module.exports = {
3 | endOfLine: 'lf',
4 | singleQuote: true,
5 | printWidth: 100,
6 | tabWidth: 2,
7 | trailingComma: 'es5',
8 | importOrder: [
9 | '^(react/(.*)$)|^(react$)',
10 | '^(next/(.*)$)|^(next$)',
11 | '',
12 | '',
13 | '^types$',
14 | '^@/types/(.*)$',
15 | '^@/config/(.*)$',
16 | '^@/lib/(.*)$',
17 | '^@/hooks/(.*)$',
18 | '^@/components/ui/(.*)$',
19 | '^@/components/(.*)$',
20 | '^@/styles/(.*)$',
21 | '^@/app/(.*)$',
22 | '',
23 | '^[./]',
24 | ],
25 | importOrderSeparation: false,
26 | importOrderSortSpecifiers: true,
27 | importOrderBuiltinModulesToTop: true,
28 | importOrderParserPlugins: ['typescript', 'jsx', 'decorators-legacy'],
29 | importOrderMergeDuplicateImports: true,
30 | importOrderCombineTypeAndValueImports: true,
31 | plugins: ['@ianvs/prettier-plugin-sort-imports', require('prettier-plugin-tailwindcss')],
32 | };
33 |
--------------------------------------------------------------------------------
/app/components/embedding-model.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 | import { useConfig } from '@/app/components/config-context';
3 | import {
4 | Select,
5 | SelectContent,
6 | SelectItem,
7 | SelectTrigger,
8 | SelectValue,
9 | } from '@/components/ui/select';
10 | import { Label } from '@/components/ui/label';
11 |
12 | export function EmbeddingModel() {
13 | const { embeddingModel, setEmbeddingModel } = useConfig();
14 |
15 | return (
16 |
17 |
18 |
19 |
27 |
28 |
29 | );
30 | }
31 |
--------------------------------------------------------------------------------
/components/ui/slider.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as SliderPrimitive from "@radix-ui/react-slider"
5 |
6 | import { cn } from "@/lib/utils"
7 |
8 | const Slider = React.forwardRef<
9 | React.ElementRef,
10 | React.ComponentPropsWithoutRef
11 | >(({ className, ...props }, ref) => (
12 |
20 |
21 |
22 |
23 |
24 |
25 | ))
26 | Slider.displayName = SliderPrimitive.Root.displayName
27 |
28 | export { Slider }
29 |
--------------------------------------------------------------------------------
/app/components/sidebar.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 | import { EmbeddingModel } from '@/app/components/embedding-model';
3 | import { VectorStoreWidget } from '@/app/components/store-config';
4 | import { QueryConfigForm } from '@/app/components/query-config';
5 | import { Separator } from '@/components/ui/separator';
6 |
7 | export default function Sidebar() {
8 | return (
9 |
31 | );
32 | }
33 |
--------------------------------------------------------------------------------
/components/ui/badge.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | import { cva, type VariantProps } from "class-variance-authority"
3 |
4 | import { cn } from "@/lib/utils"
5 |
6 | const badgeVariants = cva(
7 | "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
8 | {
9 | variants: {
10 | variant: {
11 | default:
12 | "border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
13 | secondary:
14 | "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
15 | destructive:
16 | "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
17 | outline: "text-foreground",
18 | },
19 | },
20 | defaultVariants: {
21 | variant: "default",
22 | },
23 | }
24 | )
25 |
26 | export interface BadgeProps
27 | extends React.HTMLAttributes,
28 | VariantProps {}
29 |
30 | function Badge({ className, variant, ...props }: BadgeProps) {
31 | return (
32 |
33 | )
34 | }
35 |
36 | export { Badge, badgeVariants }
37 |
--------------------------------------------------------------------------------
/components/ui/switch.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as SwitchPrimitives from "@radix-ui/react-switch"
5 |
6 | import { cn } from "@/lib/utils"
7 |
8 | const Switch = React.forwardRef<
9 | React.ElementRef,
10 | React.ComponentPropsWithoutRef
11 | >(({ className, ...props }, ref) => (
12 |
20 |
25 |
26 | ))
27 | Switch.displayName = SwitchPrimitives.Root.displayName
28 |
29 | export { Switch }
30 |
--------------------------------------------------------------------------------
/components/main-nav.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import Link from 'next/link';
3 |
4 | import { NavItem } from '@/types/nav';
5 | import { siteConfig } from '@/config/site';
6 | import { cn } from '@/lib/utils';
7 | import { Icons } from '@/components/icons';
8 |
9 | interface MainNavProps {
10 | items?: NavItem[];
11 | }
12 |
13 | export function MainNav({ items }: MainNavProps) {
14 | return (
15 |
16 |
17 |
18 | {siteConfig.name}
19 |
20 | {items?.length ? (
21 |
38 | ) : null}
39 |
40 | );
41 | }
42 |
--------------------------------------------------------------------------------
/lib/ingest.ts:
--------------------------------------------------------------------------------
1 | import { Ingestion, Wikipedia, TextSplitter, OpenAIEmbedder, TextDocument } from 'axgen';
2 | import { getPgVectorStore, getPineconeStore, getOpenAiKey } from './axgen-utils';
3 |
4 | export const getStore = (store: string) => {
5 | switch (store) {
6 | case 'pinecone':
7 | return getPineconeStore();
8 | case 'pgvector':
9 | return getPgVectorStore();
10 | default:
11 | throw new Error(`Unknown store ${store}`);
12 | }
13 | };
14 |
15 | export const ingestWikipedia = async (term: string, storeName: string) => {
16 | const store = getStore(storeName);
17 |
18 | const ingestion = new Ingestion({
19 | store,
20 | source: new Wikipedia({ term }),
21 | splitter: new TextSplitter({}),
22 | embedder: new OpenAIEmbedder({ apiKey: getOpenAiKey() }),
23 | });
24 |
25 | await ingestion.run();
26 | };
27 |
28 | export const ingestFile = async (
29 | storeName: 'pinecone' | 'pgvector',
30 | content: string,
31 | filename: string
32 | ) => {
33 | const store = getStore(storeName);
34 | const ingestion = new Ingestion({
35 | store,
36 | source: new TextDocument({ content, url: `file://${filename}` }),
37 | splitter: new TextSplitter({}),
38 | embedder: new OpenAIEmbedder({ apiKey: getOpenAiKey() }),
39 | });
40 |
41 | await ingestion.run();
42 |
43 | return filename;
44 | };
45 |
--------------------------------------------------------------------------------
/app/api/query/route.ts:
--------------------------------------------------------------------------------
1 | import { NextRequest, NextResponse } from 'next/server';
2 | import { queryCompletion, queryChat } from '@/lib/query';
3 |
4 | export async function POST(request: NextRequest) {
5 | const { modelType, question, llmOnly, topK, model, temperature, maxTokens, store } =
6 | await request.json();
7 | try {
8 | let response;
9 | switch (modelType) {
10 | case 'completion':
11 | response = await queryCompletion({
12 | query: question,
13 | model,
14 | store,
15 | llmOnly,
16 | topK,
17 | temperature,
18 | maxTokens,
19 | });
20 | break;
21 | case 'chat':
22 | response = await queryChat({
23 | query: question,
24 | model,
25 | llmOnly,
26 | topK,
27 | temperature,
28 | store,
29 | maxTokens,
30 | });
31 | break;
32 | default:
33 | return NextResponse.json({ error: 'Invalid model type' }, { status: 400 });
34 | }
35 | return NextResponse.json({ response }, { status: 200 });
36 | } catch (error: unknown) {
37 | let message = 'Unknown Error';
38 | if (error instanceof Error) message = error.message;
39 |
40 | console.error(error);
41 | return NextResponse.json({ error: message }, { status: 500 });
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/app/api/ingest/upload/route.ts:
--------------------------------------------------------------------------------
1 | import { NextRequest, NextResponse } from 'next/server';
2 | import { ingestFile } from '@/lib/ingest';
3 |
4 | /**
5 | * POST /api/ingest/upload
6 | * Uploads a file, chunks it to the specified store
7 | */
8 | export async function POST(request: NextRequest) {
9 | const formData = await request.formData();
10 | const file = formData.get('file');
11 | const storeName = formData.get('store');
12 | const filename = formData.get('filename');
13 |
14 | if (['pinecone', 'pgvector'].includes(storeName as string) === false) {
15 | return NextResponse.json({ error: 'Invalid store name' }, { status: 400 });
16 | }
17 |
18 | if (!file || typeof file === 'string') {
19 | return NextResponse.json({ error: 'Error reading file' }, { status: 400 });
20 | }
21 |
22 | // Convert file to Buffer
23 | const chunks: Buffer[] = [];
24 | for await (const chunk of file.stream()) {
25 | chunks.push(chunk as Buffer);
26 | }
27 | const fileContentBuffer = Buffer.concat(chunks);
28 |
29 | try {
30 | const content = fileContentBuffer.toString();
31 | await ingestFile(storeName as 'pinecone' | 'pgvector', content, `file://${filename}`);
32 | return NextResponse.json({ content, store: storeName }, { status: 200 });
33 | } catch {
34 | return NextResponse.json({ error: 'Error ingesting file' }, { status: 400 });
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/app/components/document-card.tsx:
--------------------------------------------------------------------------------
1 | import type { ContextDocument } from '@/lib/types';
2 | import { Card, CardContent, CardFooter, CardHeader, CardTitle } from '@/components/ui/card';
3 | import {
4 | Dialog,
5 | DialogContent,
6 | DialogDescription,
7 | DialogHeader,
8 | DialogTitle,
9 | DialogTrigger,
10 | } from '@/components/ui/dialog';
11 |
12 | export default function DocumentCard({
13 | document,
14 | idx,
15 | }: {
16 | document: ContextDocument;
17 | idx: number;
18 | }) {
19 | return (
20 |
50 | );
51 | }
52 |
--------------------------------------------------------------------------------
/components/site-header.tsx:
--------------------------------------------------------------------------------
1 | import Link from 'next/link';
2 |
3 | import { siteConfig } from '@/config/site';
4 | import { buttonVariants } from '@/components/ui/button';
5 | import { Icons } from '@/components/icons';
6 | import { MainNav } from '@/components/main-nav';
7 | import { ThemeToggle } from '@/components/theme-toggle';
8 |
9 | export function SiteHeader() {
10 | return (
11 |
12 |
13 |
14 |
15 |
40 |
41 |
42 |
43 | );
44 | }
45 |
--------------------------------------------------------------------------------
/components/code-editor.tsx:
--------------------------------------------------------------------------------
1 | 'use-client';
2 |
3 | import MonacoEditor from '@monaco-editor/react';
4 |
5 | import { theme as defaultTheme, lightTheme } from '@/components/monaco-theme';
6 |
7 | type PropsType = {
8 | code: string;
9 | theme?: 'default' | 'light';
10 | onChange: (code: string) => void;
11 | };
12 |
13 | export default function CodeEditor(props: PropsType) {
14 | const theme = props.theme || 'default';
15 | const bgColor =
16 | theme === 'default'
17 | ? defaultTheme.colors['editor.background']
18 | : lightTheme.colors['editor.background'];
19 |
20 | return (
21 |
22 | props.onChange(value || '')}
53 | beforeMount={(monaco) => {
54 | monaco.editor.defineTheme('default', defaultTheme);
55 | monaco.editor.defineTheme('light', lightTheme);
56 | }}
57 | />
58 |
59 | );
60 | }
61 |
--------------------------------------------------------------------------------
/components/ui/button.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { Slot } from '@radix-ui/react-slot';
3 | import { cva, type VariantProps } from 'class-variance-authority';
4 |
5 | import { cn } from '@/lib/utils';
6 |
7 | const buttonVariants = cva(
8 | 'inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none ring-offset-background',
9 | {
10 | variants: {
11 | variant: {
12 | default: 'bg-primary text-primary-foreground hover:bg-primary/90',
13 | destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
14 | outline: 'border border-input hover:bg-accent hover:text-accent-foreground',
15 | secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
16 | ghost: 'hover:bg-accent hover:text-accent-foreground',
17 | link: 'underline-offset-4 hover:underline text-primary',
18 | },
19 | size: {
20 | default: 'h-10 py-2 px-4',
21 | sm: 'h-9 px-3 rounded-md',
22 | lg: 'h-11 px-8 rounded-md',
23 | },
24 | },
25 | defaultVariants: {
26 | variant: 'default',
27 | size: 'default',
28 | },
29 | }
30 | );
31 |
32 | export interface ButtonProps
33 | extends React.ButtonHTMLAttributes,
34 | VariantProps {
35 | asChild?: boolean;
36 | }
37 |
38 | const Button = React.forwardRef(
39 | ({ className, variant, size, asChild = false, ...props }, ref) => {
40 | const Comp = asChild ? Slot : 'button';
41 | return (
42 |
43 | );
44 | }
45 | );
46 | Button.displayName = 'Button';
47 |
48 | export { Button, buttonVariants };
49 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Axilla demo UI
2 |
3 | ### [Demo video 🎥](https://www.loom.com/share/458f9b6679b740f0a5c78a33fffee3dc)
4 |
5 | This demo UI showcases how to build RAG (retrieval augmented generation) workflows using the [axgen](https://github.com/axilla-io/axgen) library.
6 |
7 | 
8 |
9 | The UI covers the usual flow, which has 2 separate parts:
10 |
11 | 1. Ingest documents into a vector store (this demo shows ingesting from files and from wikipedia, but you could plug any data source)
12 | 2. Ask questions with augmented context for retrieval (by fetching chunks of the ingested documents to enrich the answer)
13 |
14 | You can easily toggle document inclusion on/off, to see the difference. The UI also shows the documents that were retrieved which helps troubleshoot why the answer is what it is.
15 |
16 | [Axgen](https://github.com/axilla-io/axgen) is fully configurable, as this UI demonstrates.
17 |
18 | Please give us any feedback (bugs, requests, questions) at hello@axilla.io. We love talking to our users so don't be shy.
19 |
20 | ## Axilla
21 |
22 | At [Axilla](https://axilla.io), we are building an opinionated end-to-end framework to work with LLMs in TypeScript.
23 | Our first module open source module is [axgen](https://github.com/axilla-io/axgen), focused on document ingestion and retrieval. Giving it a star ⭐️ is very helpful for our visibility, so we appreciate it if you can spare one!
24 |
25 | ## Usage
26 |
27 | This is a simple nextJS application, that was tested using node 18.
28 |
29 | ### Steps
30 |
31 | 1. Clone the repo: `git clone https://github.com/axilla-io/demo-ui.git`
32 | 2. Ensure you have the right environment variables setup: `cp .env.example .env`
33 | 3. Install packages: `npm i`
34 | 4. Run it: `npm run dev # will run on localhost:3300`
35 |
36 | ## License
37 |
38 | Licensed under the [MIT license](https://github.com/shadcn/ui/blob/main/LICENSE.md).
39 |
--------------------------------------------------------------------------------
/styles/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | @layer base {
6 | :root {
7 | --background: 0 0% 100%;
8 | --foreground: 222.2 84% 4.9%;
9 |
10 | --muted: 210 40% 96.1%;
11 | --muted-foreground: 215.4 16.3% 46.9%;
12 |
13 | --popover: 0 0% 100%;
14 | --popover-foreground: 222.2 84% 4.9%;
15 |
16 | --card: 0 0% 100%;
17 | --card-foreground: 222.2 84% 4.9%;
18 |
19 | --border: 214.3 31.8% 91.4%;
20 | --input: 214.3 31.8% 91.4%;
21 |
22 | --primary: 222.2 47.4% 11.2%;
23 | --primary-foreground: 210 40% 98%;
24 |
25 | --secondary: 210 40% 96.1%;
26 | --secondary-foreground: 222.2 47.4% 11.2%;
27 |
28 | --accent: 210 40% 96.1%;
29 | --accent-foreground: 222.2 47.4% 11.2%;
30 |
31 | --destructive: 0 84.2% 60.2%;
32 | --destructive-foreground: 210 40% 98%;
33 |
34 | --ring: 215 20.2% 65.1%;
35 |
36 | --radius: 0.5rem;
37 | }
38 |
39 | .dark {
40 | --background: 222.2 84% 4.9%;
41 | --foreground: 210 40% 98%;
42 |
43 | --muted: 217.2 32.6% 17.5%;
44 | --muted-foreground: 215 20.2% 65.1%;
45 |
46 | --popover: 222.2 84% 4.9%;
47 | --popover-foreground: 210 40% 98%;
48 |
49 | --card: 222.2 84% 4.9%;
50 | --card-foreground: 210 40% 98%;
51 |
52 | --border: 217.2 32.6% 17.5%;
53 | --input: 217.2 32.6% 17.5%;
54 |
55 | --primary: 210 40% 98%;
56 | --primary-foreground: 222.2 47.4% 11.2%;
57 |
58 | --secondary: 217.2 32.6% 17.5%;
59 | --secondary-foreground: 210 40% 98%;
60 |
61 | --accent: 217.2 32.6% 17.5%;
62 | --accent-foreground: 210 40% 98%;
63 |
64 | --destructive: 0 62.8% 30.6%;
65 | --destructive-foreground: 0 85.7% 97.3%;
66 |
67 | --ring: 217.2 32.6% 17.5%;
68 | }
69 | }
70 |
71 | @layer base {
72 | * {
73 | @apply border-border;
74 | }
75 | body {
76 | @apply bg-background text-foreground;
77 | }
78 | }
--------------------------------------------------------------------------------
/components/ui/tabs.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as TabsPrimitive from "@radix-ui/react-tabs"
5 |
6 | import { cn } from "@/lib/utils"
7 |
8 | const Tabs = TabsPrimitive.Root
9 |
10 | const TabsList = React.forwardRef<
11 | React.ElementRef,
12 | React.ComponentPropsWithoutRef
13 | >(({ className, ...props }, ref) => (
14 |
22 | ))
23 | TabsList.displayName = TabsPrimitive.List.displayName
24 |
25 | const TabsTrigger = React.forwardRef<
26 | React.ElementRef,
27 | React.ComponentPropsWithoutRef
28 | >(({ className, ...props }, ref) => (
29 |
37 | ))
38 | TabsTrigger.displayName = TabsPrimitive.Trigger.displayName
39 |
40 | const TabsContent = React.forwardRef<
41 | React.ElementRef,
42 | React.ComponentPropsWithoutRef
43 | >(({ className, ...props }, ref) => (
44 |
52 | ))
53 | TabsContent.displayName = TabsPrimitive.Content.displayName
54 |
55 | export { Tabs, TabsList, TabsTrigger, TabsContent }
56 |
--------------------------------------------------------------------------------
/components/cell.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import { useState } from 'react';
4 |
5 | import CodeEditor from '@/components/code-editor';
6 | import { Icons } from '@/components/icons';
7 |
8 | const fib = `function fib(n) {
9 | if (n === 0 || n === 1) {
10 | return n;
11 | }
12 |
13 | return fib(n - 1) + fib(n - 2)
14 | }
15 | `;
16 |
17 | export default function Cell() {
18 | const [value, setValue] = useState('');
19 | const [evalCount, setEvalCount] = useState(0);
20 |
21 | async function evaluate(code: string) {
22 | // Force an update every time evaluation happens:
23 | setEvalCount(evalCount + 1);
24 |
25 | console.log('Evaling ', code);
26 | const result = '10';
27 |
28 | setValue(result);
29 | }
30 |
31 | return (
32 |
39 | );
40 | }
41 |
42 | type CellInnerPropsType = {
43 | code?: string;
44 | value: string;
45 | stdout: string[];
46 | evaluate: (code: string) => void;
47 | evalCount: number;
48 | };
49 |
50 | function CellInner(props: CellInnerPropsType) {
51 | const [code, setCode] = useState(props.code || '');
52 |
53 | return (
54 |
55 |
56 | {
59 | props.evaluate(code);
60 | }}
61 | >
62 | Evaluate
63 |
64 |
65 |
66 | {props.evalCount > 0 && (
67 |
68 | {props.stdout.length > 0 && (
69 |
70 | {props.stdout.join('\n')}
71 |
72 | )}
73 |
74 | {props.value}
75 |
76 |
77 | )}
78 |
79 | );
80 | }
81 |
--------------------------------------------------------------------------------
/components/ui/card.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 |
3 | import { cn } from "@/lib/utils"
4 |
5 | const Card = React.forwardRef<
6 | HTMLDivElement,
7 | React.HTMLAttributes
8 | >(({ className, ...props }, ref) => (
9 |
17 | ))
18 | Card.displayName = "Card"
19 |
20 | const CardHeader = React.forwardRef<
21 | HTMLDivElement,
22 | React.HTMLAttributes
23 | >(({ className, ...props }, ref) => (
24 |
29 | ))
30 | CardHeader.displayName = "CardHeader"
31 |
32 | const CardTitle = React.forwardRef<
33 | HTMLParagraphElement,
34 | React.HTMLAttributes
35 | >(({ className, ...props }, ref) => (
36 |
44 | ))
45 | CardTitle.displayName = "CardTitle"
46 |
47 | const CardDescription = React.forwardRef<
48 | HTMLParagraphElement,
49 | React.HTMLAttributes
50 | >(({ className, ...props }, ref) => (
51 |
56 | ))
57 | CardDescription.displayName = "CardDescription"
58 |
59 | const CardContent = React.forwardRef<
60 | HTMLDivElement,
61 | React.HTMLAttributes
62 | >(({ className, ...props }, ref) => (
63 |
64 | ))
65 | CardContent.displayName = "CardContent"
66 |
67 | const CardFooter = React.forwardRef<
68 | HTMLDivElement,
69 | React.HTMLAttributes
70 | >(({ className, ...props }, ref) => (
71 |
76 | ))
77 | CardFooter.displayName = "CardFooter"
78 |
79 | export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
80 |
--------------------------------------------------------------------------------
/app/page.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 | import { useSearchParams, useRouter, usePathname } from 'next/navigation';
3 | import { useEffect } from 'react';
4 | import { Separator } from '@/components/ui/separator';
5 | import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
6 | import { IngestWikipedia } from '@/app/components/ingest-wikipedia';
7 | import { IngestDocumentUpload } from '@/app/components/ingest-upload';
8 | import { QueryWidget } from '@/app/components/query';
9 |
10 | function IngestTab() {
11 | const router = useRouter();
12 | const pathname = usePathname();
13 | useEffect(() => {
14 | router.push(pathname + '?tab=ingest');
15 | }, [router, pathname]);
16 |
17 | return (
18 |
19 |
20 |
21 |
26 |
27 |
28 |
29 | );
30 | }
31 |
32 | function QueryTab() {
33 | const router = useRouter();
34 | const pathname = usePathname();
35 | useEffect(() => {
36 | router.push(pathname + '?tab=query');
37 | }, [router, pathname]);
38 |
39 | return ;
40 | }
41 |
42 | export default function IndexPage() {
43 | const searchParams = useSearchParams();
44 | const defaultTab = searchParams.get('tab') || 'ingest';
45 |
46 | return (
47 |
48 |
49 |
50 |
51 | Ingest documents
52 | Query
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 | );
66 | }
67 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "axilla-demo",
3 | "browser": {
4 | "fs": false
5 | },
6 | "version": "0.0.2",
7 | "private": true,
8 | "scripts": {
9 | "dev": "next dev -p 3300",
10 | "build": "next build",
11 | "start": "next start",
12 | "lint": "next lint",
13 | "lint:fix": "next lint --fix",
14 | "preview": "next build && next start",
15 | "typecheck": "tsc --noEmit",
16 | "format:write": "npx prettier --write \"**/*.{js, ts,tsx,mdx}\" --cache",
17 | "format:check": "npx prettier --check \"**/*.{ts,tsx,mdx}\" --cache"
18 | },
19 | "dependencies": {
20 | "@hookform/resolvers": "^3.1.1",
21 | "@monaco-editor/react": "^4.5.1",
22 | "@radix-ui/react-arrow": "^1.0.3",
23 | "@radix-ui/react-dialog": "^1.0.4",
24 | "@radix-ui/react-label": "^2.0.2",
25 | "@radix-ui/react-select": "^1.2.2",
26 | "@radix-ui/react-separator": "^1.0.3",
27 | "@radix-ui/react-slider": "^1.1.2",
28 | "@radix-ui/react-slot": "^1.0.2",
29 | "@radix-ui/react-switch": "^1.0.3",
30 | "@radix-ui/react-tabs": "^1.0.4",
31 | "@radix-ui/react-toast": "^1.1.4",
32 | "axgen": "^0.0.19",
33 | "class-variance-authority": "^0.4.0",
34 | "clsx": "^1.2.1",
35 | "dotenv": "^16.3.1",
36 | "lucide-react": "^0.105.0-alpha.4",
37 | "monaco-editor": "^0.39.0",
38 | "next": "^13.4.8",
39 | "next-themes": "^0.2.1",
40 | "react": "^18.2.0",
41 | "react-dom": "^18.2.0",
42 | "react-hook-form": "^7.45.1",
43 | "sharp": "^0.31.3",
44 | "tailwind-merge": "^1.14.0",
45 | "tailwindcss-animate": "^1.0.6",
46 | "zod": "^3.21.4"
47 | },
48 | "devDependencies": {
49 | "@ianvs/prettier-plugin-sort-imports": "^3.7.2",
50 | "@types/node": "^17.0.45",
51 | "@types/react": "^18.2.7",
52 | "@types/react-dom": "^18.2.4",
53 | "@typescript-eslint/eslint-plugin": "^5.54.1",
54 | "@typescript-eslint/parser": "^5.54.1",
55 | "autoprefixer": "^10.4.14",
56 | "eslint": "^8.41.0",
57 | "eslint-config-next": "13.0.0",
58 | "eslint-config-prettier": "^8.8.0",
59 | "eslint-plugin-react": "^7.32.2",
60 | "eslint-plugin-tailwindcss": "^3.12.0",
61 | "postcss": "^8.4.24",
62 | "prettier": "^2.8.8",
63 | "prettier-plugin-tailwindcss": "^0.1.13",
64 | "prisma": "^4.16.2",
65 | "tailwindcss": "^3.3.2",
66 | "typescript": "^4.9.5"
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | module.exports = {
3 | darkMode: ["class"],
4 | content: [
5 | './pages/**/*.{ts,tsx}',
6 | './components/**/*.{ts,tsx}',
7 | './app/**/*.{ts,tsx}',
8 | './src/**/*.{ts,tsx}',
9 | ],
10 | theme: {
11 | container: {
12 | center: true,
13 | padding: "2rem",
14 | screens: {
15 | "2xl": "1400px",
16 | },
17 | },
18 | extend: {
19 | colors: {
20 | border: "hsl(var(--border))",
21 | input: "hsl(var(--input))",
22 | ring: "hsl(var(--ring))",
23 | background: "hsl(var(--background))",
24 | foreground: "hsl(var(--foreground))",
25 | primary: {
26 | DEFAULT: "hsl(var(--primary))",
27 | foreground: "hsl(var(--primary-foreground))",
28 | },
29 | secondary: {
30 | DEFAULT: "hsl(var(--secondary))",
31 | foreground: "hsl(var(--secondary-foreground))",
32 | },
33 | destructive: {
34 | DEFAULT: "hsl(var(--destructive))",
35 | foreground: "hsl(var(--destructive-foreground))",
36 | },
37 | muted: {
38 | DEFAULT: "hsl(var(--muted))",
39 | foreground: "hsl(var(--muted-foreground))",
40 | },
41 | accent: {
42 | DEFAULT: "hsl(var(--accent))",
43 | foreground: "hsl(var(--accent-foreground))",
44 | },
45 | popover: {
46 | DEFAULT: "hsl(var(--popover))",
47 | foreground: "hsl(var(--popover-foreground))",
48 | },
49 | card: {
50 | DEFAULT: "hsl(var(--card))",
51 | foreground: "hsl(var(--card-foreground))",
52 | },
53 | },
54 | borderRadius: {
55 | lg: "var(--radius)",
56 | md: "calc(var(--radius) - 2px)",
57 | sm: "calc(var(--radius) - 4px)",
58 | },
59 | keyframes: {
60 | "accordion-down": {
61 | from: { height: 0 },
62 | to: { height: "var(--radix-accordion-content-height)" },
63 | },
64 | "accordion-up": {
65 | from: { height: "var(--radix-accordion-content-height)" },
66 | to: { height: 0 },
67 | },
68 | },
69 | animation: {
70 | "accordion-down": "accordion-down 0.2s ease-out",
71 | "accordion-up": "accordion-up 0.2s ease-out",
72 | },
73 | },
74 | },
75 | plugins: [require("tailwindcss-animate")],
76 | }
--------------------------------------------------------------------------------
/app/api/query/stream/route.ts:
--------------------------------------------------------------------------------
1 | import { NextRequest, NextResponse } from 'next/server';
2 | import { queryChatStream, queryCompletionStream } from 'lib/query';
3 | // Separators recommended from
4 | // https://stackoverflow.com/questions/6319551/whats-the-best-separator-delimiter-characters-for-a-plaintext-db-file
5 | const JSON_STREAM_SEPARATOR = Uint8Array.from([0x1d]);
6 | const CONTENT_STREAM_SEPARATOR = Uint8Array.from([0x1e]);
7 |
8 | type ChatResponse = {
9 | choices: Array<{
10 | delta: {
11 | content: string;
12 | };
13 | }>;
14 | };
15 |
16 | type InfoContext = {
17 | context?: Array