Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update README.md #2

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
# Required
# for match documents

# pro shodné dokumenty
NEXT_PUBLIC_SUPABASE_URL=
NEXT_PUBLIC_SUPABASE_ANON_KEY=
# for embedding query

# pro dotaz na vložení, získává se zde: https://jina.ai/embeddings/
JINA_API_KEY=
# for llm output

# pro výstup llm, načteno zde: https://platform.openai.com/api-keys
OPENAI_API_KEY=
OPENAI_API_URL=
# for llm cache and serach cache

# pro llm cache a serach cache
UPSTASH_REDIS_REST_URL=
UPSTASH_REDIS_REST_TOKEN=
138 changes: 41 additions & 97 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,127 +1,71 @@
# DiscovAI
# DoktorNaDohled

An AI-powered search engine for AI tools, or your own data.
AI konverzační platforma zaměřená na zajišťování přístupu k relevantním informacím a odpovědím na otázky uživatelů v oblasti zdravotnictví.

https://github.com/user-attachments/assets/2cdc92d0-d0c9-4098-8166-260e973783f0
## Živá ukázka

Please feel free to contact me on [Twitter](https://x.com/ruiyanghim) or [create an issue](https://github.com/DiscovAI/DiscovAI-search/issues/new) if you have any questions.
[DoktorNaDohled.cz](https://www.doktornadohled-digimedic.cz/) (použijte ji zdarma bez přihlášení nebo kreditní karty).

## 💻 Live Demo
## Přehled

[DiscovAI.io](https://discovai.io/) (use it for free without signin or credit card)
- [Funkce](#funkce)
- [Tech-Stack](#tech-stack)
- [Rychlý start](#rychlý-start)
- [Deploy](#deploy)

## 🗂️ Overview
## Funkce

- 🛠 [Features](#-features)
- 🧱 [Tech-Stack](#-stack)
- 🚀 [Quickstart](#-quickstart)
- 🌐 [Deploy](#-deploy)
- **Vedení konverzace**: AI vede uživatele konverzací zaměřenou na jejich zdravotní potřeby.
- **Analýza kontextu**: Systém analyzuje kontext a požadavky uživatele pro poskytnutí relevantních informací.
- **Vyhledávání dat**: Na základě analýzy AI vyhledává odpovědi v integrovaných databázích poskytovatelů zdravotní péče.
- **Doporučení poskytovatelů**: AI poskytuje uživateli seznam vhodných poskytovatelů zdravotní péče spolu s kontaktními informacemi.
- **Personalizace**: Systém využívá uživatelský profil pro přizpůsobení doporučení.
- **Bezpečnost**: Implementováno základní zabezpečení včetně rate limitingu.

## 🛠 Features
## Tech-Stack

- **Vector-based Search**: Converts user queries into vectors for precise similarity matching in our AI product database.
- Rámec aplikace: [Next.js](https://nextjs.org/)
- Streamování textu: [Vercel AI SDK](https://sdk.vercel.ai/docs)
- Model LLM: [GPT-4](https://openai.com/)
- Databáze: [Supabase](https://supabase.com/)
- Vektor: [Pgvector](https://github.com/pgvector/pgvector)
- Model vkládání: [Jina AI](https://jina.ai/embeddings)
- Cache Redis: [Upstash](https://upstash.com/)
- Knihovna komponent: [shadcn/ui](https://ui.shadcn.com/)
- Primitiva komponent bez hlavy: [Radix UI](https://www.radix-ui.com/)
- Stylování: [Tailwind CSS](https://tailwindcss.com/)

- **Redis-powered Caching**: Utilizes Redis to cache search results and outputs, significantly improving response times for repeated queries.
## Rychlý start

- **Comprehensive AI Database**: Maintains an up-to-date collection of AI products across various categories and industries.

- **LLM-powered Responses**: Leverages large language models to provide detailed, context-aware answers based on search results.

- **User-friendly Interface**: Offers an intuitive design for effortless navigation and efficient AI product discovery.

## 🧱 Stack

- App framework: [Next.js](https://nextjs.org/)
- Text streaming: [Vercel AI SDK](https://sdk.vercel.ai/docs)
- LLM Model: [gpt-4o-mini](https://openai.com/)
- Database: [Supabase](https://supabase.com/)
- Vector: [Pgvector](https://github.com/pgvector/pgvector)
- Embedding Model: [Jina AI](https://jina.ai/embeddings)
- Redis Cache: [Upstash](https://upstash.com/)
- Component library: [shadcn/ui](https://ui.shadcn.com/)
- Headless component primitives: [Radix UI](https://www.radix-ui.com/)
- Styling: [Tailwind CSS](https://tailwindcss.com/)

## 🚀 Quickstart

### 1. Clone repo

run the following command to clone the repo:
### 1. Klonování repozitáře

```
git clone https://github.com/DiscovAI/DiscovAI-search
git clone https://github.com/DigiMedic/Doktor-Na-Dohled
```

### 2. Install dependencies
### 2. Instalace závislostí

```
cd discovai-search
cd Doktor-Na-Dohled
pnpm i
```

### 3. Setting up Supabase
### 3. Nastavení databáze Supabase

create a supabase [project](https://supabase.com/dashboard/projects), then run the src/db/init.sql in [SQL Editor](https://supabase.com/docs/guides/database/overview) to setup database
Vytvořte Supabase projekt a spusťte `src/db/init.sql` v SQL Editoru pro nastavení databáze.

### 4. Setting up Upstash
### 4. Nastavení proměnných prostředí

Follow the guide below to set up Upstash Redis. Create a database and obtain `UPSTASH_REDIS_REST_URL` and `UPSTASH_REDIS_REST_TOKEN`. Refer to the [Upstash guide](https://upstash.com/blog/rag-chatbot-upstash#setting-up-upstash-redis) for instructions on how to proceed.
Zkopírujte `.env.local.example` do `.env.local` a vyplňte všechny potřebné proměnné.

### 4. Fill out secrets
### 5. Spuštění aplikace

```
cp .env.local.example .env.local
```

Your .env.local file should look like this:

```
# Required

# for match documents
NEXT_PUBLIC_SUPABASE_URL=
NEXT_PUBLIC_SUPABASE_ANON_KEY=

# for embedding query, retrieved here: https://jina.ai/embeddings/
JINA_API_KEY=

# for llm output, retrieved here: https://platform.openai.com/api-keys
OPENAI_API_KEY=
OPENAI_API_URL=

# for llm cache and serach cache
UPSTASH_REDIS_REST_URL=
UPSTASH_REDIS_REST_TOKEN=
pnpm dev
```

### 5. Run app locally
Aplikace bude dostupná na `http://localhost:3000`.

```
pnpm dev
```
## Deploy

You can now visit http://localhost:3000.

## 🌐 Deploy

You can deploy on any saas platform like vercel, zeabur, cloudflare pages.

## 🌟 History
<picture>
<source
media="(prefers-color-scheme: dark)"
srcset="
https://api.star-history.com/svg?repos=DiscovAI/DiscovAI-search&type=Date&theme=dark
"
/>
<source
media="(prefers-color-scheme: light)"
srcset="
https://api.star-history.com/svg?repos=DiscovAI/DiscovAI-search&type=Date
"
/>
<img
alt="Star History Chart"
src="https://api.star-history.com/svg?repos=DiscovAI/DiscovAI-search&type=Date"
/>
</picture>
Pro nasazení aplikace na Vercel postupujte podle [dokumentace Vercel](https://vercel.com/docs).
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"name": "discovai-search",
"version": "0.1.0",
"private": true,
"type": "module",
"scripts": {
"dev": "next dev",
"build": "next build",
Expand Down
97 changes: 73 additions & 24 deletions src/app/api/chat/route.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,31 @@
// app/api/stream/route.ts

import { embeddingVectorCacheKey, llmResultCacheKey, redis } from "@/db/redis";
import { supabase } from "@/db/supabase";
import { createClient } from '@supabase/supabase-js'
import { generateQueyEmbedding } from "@/lib/chat/embedding";
import { genLLMTextChunk, translate } from "@/lib/chat/llm";
import { addRefToUrl, genStream, sleep } from "@/lib/utils";
import { StreamEvent } from "@/schema/chat";
import { PostgrestError } from "@supabase/supabase-js";
import { NextRequest } from "next/server";

const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY
const supabase = createClient(supabaseUrl, supabaseKey)

// Přidejte tuto funkci pro testování připojení
async function testSupabaseConnection() {
try {
const { data, error } = await supabase.from('healthcareprovidors').select('*').limit(1)
if (error) throw error
console.log('Úspěšné připojení k Supabase:', data)
} catch (error) {
console.error('Chyba při připojení k Supabase:', error)
}
}

export async function POST(req: NextRequest) {
await testSupabaseConnection()
const body = await req.json();
const { query } = body;

Expand All @@ -36,13 +52,34 @@ export async function POST(req: NextRequest) {
await translate({ query })
);

let result = await supabase.rpc("match_embeddings", {
query_embedding: embedding, // Pass the embedding you want to compare
match_threshold: 0.78, // Choose an appropriate threshold for your data
match_count: 15, // Choose the number of matches
});
documents = result.data;
queryEmbeddingError = result.error;
let result;
try {
console.log("Volání match_embeddings s parametry:", {
query_embedding: embedding,
match_threshold: 0.78,
match_count: 15,
});
result = await supabase.rpc("match_embeddings", {
query_embedding: embedding,
match_threshold: 0.78,
match_count: 15,
});
console.log("Výsledek volání match_embeddings:", JSON.stringify(result, null, 2));
if (result.error) throw result.error;
documents = result.data;
} catch (error) {
console.error("Chyba při volání match_embeddings:", JSON.stringify(error, null, 2));
controller.enqueue(
genStream({
event: StreamEvent.ERROR,
data: {
event_type: StreamEvent.ERROR,
detail: "Chyba při vyhledávání relevantních dokumentů. Prosím, zkuste to znovu později.",
},
})
);
return; // Ukončete funkci zde, aby se nepokračovalo s prázdnými dokumenty
}
}

if (queryEmbeddingError) {
Expand All @@ -56,17 +93,18 @@ export async function POST(req: NextRequest) {
},
})
);
controller.close();
return; // Místo controller.close() použijte return
}
redis.setex(
embeddingVectorCacheKey(query),
60 * 60 * 24, // 1 day
JSON.stringify(documents)
);
// filter for unique docs
const uniqueDocuments = [
...new Set(documents.map((tool) => tool.metadata.url)),
].map((url) => documents.find((tool) => tool.metadata.url === url));
const uniqueDocuments = documents && documents.length > 0
? [...new Set(documents.map((tool) => tool.metadata.url))]
.map((url) => documents.find((tool) => tool.metadata.url === url))
: [];

for (let doc of uniqueDocuments) {
doc.metadata.url = addRefToUrl(doc.metadata.url);
Expand All @@ -75,15 +113,25 @@ export async function POST(req: NextRequest) {
documents = uniqueDocuments.slice(0, 5);

const searchResult = documents.map((d) => {
const safeContent = d.chunk_text.includes("DESCRIPTION")
? d.chunk_text?.split("---")?.[0]?.split("DESCRIPTION:")?.[1]
: d.chunk_text;
return {
title: d.metadata.title,
url: d.metadata.url,
content: safeContent,
description: safeContent,
screenshot_url: d.screenshot_url,
title: d.nazevzarizeni,
url: d.poskytovatelweb,
description: `${d.druhzarizeni} - ${d.oborpece}`,
address: `${d.ulice} ${d.cislodomovniorientacni}, ${d.obec}, ${d.psc}`,
contact: {
phone: d.poskytovateltelefon,
email: d.poskytovateljmail,
},
specialization: {
formapece: d.formapece,
druhpece: d.druhpece,
odbornyzastupce: d.odbornyzastupce,
},
region: {
kraj: d.kraj,
okres: d.okres,
},
ico: d.ico,
};
});

Expand All @@ -92,12 +140,14 @@ export async function POST(req: NextRequest) {
event: StreamEvent.SEARCH_RESULTS,
data: {
event_type: StreamEvent.SEARCH_RESULTS,
results: searchResult,
results: searchResult.map((result) => ({
...result,
content: result.description, // Přidání chybějící vlastnosti content
})),
images: uniqueDocuments.map((r) => r.screenshot_url),
},
})
);

// stream llm text chunk
const llmKey = llmResultCacheKey(query);
const llmCache: string | null = await redis.get(llmKey);
Expand Down Expand Up @@ -138,7 +188,6 @@ export async function POST(req: NextRequest) {
}
}
redis.setex(llmKey, 60 * 60 * 12, gathered);

// more results or related query
const moreTools = uniqueDocuments.slice(5);
controller.enqueue(
Expand Down Expand Up @@ -196,4 +245,4 @@ export async function POST(req: NextRequest) {
Connection: "keep-alive",
},
});
}
}
Loading