mirror of
https://github.com/StanGirard/quivr.git
synced 2024-08-16 16:40:40 +03:00
Merge branch 'main' into feat/responsive-chat-bar
This commit is contained in:
commit
75c0366e93
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
@ -2,8 +2,9 @@
|
||||
"[python]": {
|
||||
"editor.defaultFormatter": "ms-python.autopep8"
|
||||
},
|
||||
"python.formatting.provider": "none",
|
||||
"python.formatting.provider": "black",
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.organizeImports": true
|
||||
}
|
||||
},
|
||||
"python.linting.enabled": true
|
||||
}
|
||||
|
36
backend/auth/api_key_handler.py
Normal file
36
backend/auth/api_key_handler.py
Normal file
@ -0,0 +1,36 @@
|
||||
|
||||
from datetime import datetime
|
||||
from fastapi import HTTPException
|
||||
|
||||
from pydantic import DateError
|
||||
from utils.vectors import CommonsDep
|
||||
|
||||
|
||||
async def verify_api_key(api_key: str, commons: CommonsDep):
|
||||
try:
|
||||
# Use UTC time to avoid timezone issues
|
||||
current_date = datetime.utcnow().date()
|
||||
result = commons['supabase'].table('api_keys').select('api_key', 'creation_time').filter('api_key', 'eq', api_key).filter('is_active', 'eq', True).execute()
|
||||
if result.data is not None and len(result.data) > 0:
|
||||
api_key_creation_date = datetime.strptime(result.data[0]['creation_time'], "%Y-%m-%dT%H:%M:%S").date()
|
||||
|
||||
# Check if the API key was created today: Todo remove this check and use deleted_time instead.
|
||||
if api_key_creation_date == current_date:
|
||||
return True
|
||||
return False
|
||||
except DateError:
|
||||
return False
|
||||
|
||||
async def get_user_from_api_key(api_key: str, commons: CommonsDep):
|
||||
# Lookup the user_id from the api_keys table
|
||||
user_id_data = commons['supabase'].table('api_keys').select('user_id').filter('api_key', 'eq', api_key).execute()
|
||||
|
||||
if not user_id_data.data:
|
||||
raise HTTPException(status_code=400, detail="Invalid API key.")
|
||||
|
||||
user_id = user_id_data.data[0]['user_id']
|
||||
|
||||
# Lookup the email from the users table. Todo: remove and use user_id for credentials
|
||||
user_email_data = commons['supabase'].table('users').select('email').filter('user_id', 'eq', user_id).execute()
|
||||
|
||||
return {'email': user_email_data.data[0]['email']} if user_email_data.data else {'email': None}
|
@ -1,34 +1,42 @@
|
||||
import os
|
||||
|
||||
from auth.api_key_handler import verify_api_key, get_user_from_api_key
|
||||
from auth.jwt_token_handler import decode_access_token, verify_token
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import HTTPException, Request, Depends
|
||||
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
|
||||
from fastapi import Depends, Request, HTTPException
|
||||
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
||||
from models.users import User
|
||||
from .auth_handler import decode_access_token
|
||||
from utils.vectors import CommonsDep
|
||||
|
||||
|
||||
class JWTBearer(HTTPBearer):
|
||||
class AuthBearer(HTTPBearer):
|
||||
def __init__(self, auto_error: bool = True):
|
||||
super().__init__(auto_error=auto_error)
|
||||
|
||||
async def __call__(self, request: Request):
|
||||
async def __call__(self, request: Request, commons: CommonsDep):
|
||||
credentials: Optional[HTTPAuthorizationCredentials] = await super().__call__(request)
|
||||
if os.environ.get("AUTHENTICATE") == "false":
|
||||
return True
|
||||
if credentials:
|
||||
if not credentials.scheme == "Bearer":
|
||||
raise HTTPException(status_code=402, detail="Invalid authorization scheme.")
|
||||
token = credentials.credentials
|
||||
if not self.verify_jwt(token):
|
||||
raise HTTPException(status_code=402, detail="Invalid token or expired token.")
|
||||
return self.verify_jwt(token) # change this line
|
||||
else:
|
||||
self.check_scheme(credentials)
|
||||
token = credentials.credentials
|
||||
return await self.authenticate(token, commons)
|
||||
|
||||
def check_scheme(self, credentials):
|
||||
if credentials and not credentials.scheme == "Bearer":
|
||||
raise HTTPException(status_code=402, detail="Invalid authorization scheme.")
|
||||
elif not credentials:
|
||||
raise HTTPException(status_code=403, detail="Invalid authorization code.")
|
||||
|
||||
def verify_jwt(self, jwtoken: str):
|
||||
payload = decode_access_token(jwtoken)
|
||||
return payload
|
||||
async def authenticate(self, token: str, commons: CommonsDep):
|
||||
if os.environ.get("AUTHENTICATE") == "false":
|
||||
return self.get_test_user()
|
||||
elif verify_token(token):
|
||||
return decode_access_token(token)
|
||||
elif await verify_api_key(token, commons):
|
||||
return await get_user_from_api_key(token, commons)
|
||||
else:
|
||||
raise HTTPException(status_code=402, detail="Invalid token or expired token.")
|
||||
|
||||
def get_test_user(self):
|
||||
return {'email': 'test@example.com'} # replace with test user information
|
||||
|
||||
def get_current_user(credentials: dict = Depends(JWTBearer())) -> User:
|
||||
def get_current_user(credentials: dict = Depends(AuthBearer())) -> User:
|
||||
return User(email=credentials.get('email', 'none'))
|
||||
|
@ -24,6 +24,10 @@ def decode_access_token(token: str):
|
||||
return payload
|
||||
except JWTError as e:
|
||||
return None
|
||||
|
||||
def verify_token(token: str):
|
||||
payload = decode_access_token(token)
|
||||
return payload is not None
|
||||
|
||||
def get_user_email_from_token(token: str):
|
||||
payload = decode_access_token(token)
|
@ -1,6 +1,6 @@
|
||||
from langchain.prompts.prompt import PromptTemplate
|
||||
|
||||
prompt_template = """Use the following pieces of context to answer the question in the language of the question. If you don't know the answer, just say that you don't know, don't try to make up an answer.
|
||||
prompt_template = """Your name is Quivr. You are a second brain. A person will ask you a question and you will provide a helpful answer. Write the answer in the same language as the question. If you don't know the answer, just say that you don't know. Don't try to make up an answer. Use the following context to answer the question:
|
||||
|
||||
|
||||
{context}
|
||||
|
@ -9,7 +9,7 @@ logger = get_logger(__name__)
|
||||
|
||||
openai_api_key = os.environ.get("OPENAI_API_KEY")
|
||||
openai.api_key = openai_api_key
|
||||
summary_llm = guidance.llms.OpenAI('gpt-3.5-turbo', caching=False)
|
||||
summary_llm = guidance.llms.OpenAI('gpt-3.5-turbo-0613', caching=False)
|
||||
|
||||
|
||||
def llm_summerize(document):
|
||||
@ -40,7 +40,7 @@ def llm_evaluate_summaries(question, summaries, model):
|
||||
if not model.startswith('gpt'):
|
||||
logger.info(
|
||||
f'Model {model} not supported. Using gpt-3.5-turbo instead.')
|
||||
model = 'gpt-3.5-turbo'
|
||||
model = 'gpt-3.5-turbo-0613'
|
||||
logger.info(f'Evaluating summaries with {model}')
|
||||
evaluation_llm = guidance.llms.OpenAI(model, caching=False)
|
||||
evaluation = guidance("""
|
||||
|
@ -10,6 +10,7 @@ from routes.explore_routes import explore_router
|
||||
from routes.misc_routes import misc_router
|
||||
from routes.upload_routes import upload_router
|
||||
from routes.user_routes import user_router
|
||||
from routes.api_key_routes import api_key_router
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
@ -29,3 +30,4 @@ app.include_router(explore_router)
|
||||
app.include_router(misc_router)
|
||||
app.include_router(upload_router)
|
||||
app.include_router(user_router)
|
||||
app.include_router(api_key_router)
|
||||
|
@ -5,11 +5,11 @@ from pydantic import BaseModel
|
||||
|
||||
|
||||
class ChatMessage(BaseModel):
|
||||
model: str = "gpt-3.5-turbo"
|
||||
model: str = "gpt-3.5-turbo-0613"
|
||||
question: str
|
||||
# A list of tuples where each tuple is (speaker, text)
|
||||
history: List[Tuple[str, str]]
|
||||
temperature: float = 0.0
|
||||
max_tokens: int = 256
|
||||
use_summarization: bool = False
|
||||
chat_id: Optional[UUID] = None,
|
||||
chat_id: Optional[UUID] = None,
|
||||
|
@ -19,3 +19,4 @@ guidance==0.0.53
|
||||
python-jose==3.3.0
|
||||
google_cloud_aiplatform==1.25.0
|
||||
transformers==4.30.1
|
||||
asyncpg==0.27.0
|
||||
|
82
backend/routes/api_key_routes.py
Normal file
82
backend/routes/api_key_routes.py
Normal file
@ -0,0 +1,82 @@
|
||||
from datetime import datetime
|
||||
import time
|
||||
from typing import List
|
||||
from pydantic import BaseModel
|
||||
from auth.auth_bearer import AuthBearer, get_current_user
|
||||
from fastapi import APIRouter, Depends
|
||||
from utils.vectors import fetch_user_id_from_credentials
|
||||
from models.users import User
|
||||
from utils.vectors import CommonsDep
|
||||
from uuid import uuid4
|
||||
from secrets import token_hex
|
||||
from asyncpg.exceptions import UniqueViolationError
|
||||
from logger import get_logger
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
class ApiKeyInfo(BaseModel):
|
||||
key_id: str
|
||||
creation_time: str
|
||||
|
||||
class ApiKey(BaseModel):
|
||||
api_key: str
|
||||
|
||||
|
||||
api_key_router = APIRouter()
|
||||
|
||||
|
||||
@api_key_router.post("/api-key", response_model=ApiKey, dependencies=[Depends(AuthBearer())])
|
||||
async def create_api_key(commons: CommonsDep, current_user: User = Depends(get_current_user)):
|
||||
|
||||
date = time.strftime("%Y%m%d")
|
||||
user_id = fetch_user_id_from_credentials(commons, date, {"email": current_user.email})
|
||||
|
||||
"""Create new API key for current user."""
|
||||
|
||||
new_key_id = str(uuid4())
|
||||
new_api_key = token_hex(16)
|
||||
api_key_inserted = False
|
||||
|
||||
while not api_key_inserted:
|
||||
try:
|
||||
# Attempt to insert new API key into database
|
||||
commons['supabase'].table('api_keys').insert([{
|
||||
"key_id": new_key_id,
|
||||
"user_id": user_id,
|
||||
"api_key": new_api_key,
|
||||
"creation_time": datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S"),
|
||||
"is_active": True
|
||||
}]).execute()
|
||||
|
||||
api_key_inserted = True
|
||||
|
||||
except UniqueViolationError:
|
||||
# Generate a new API key if the current one is already in use
|
||||
new_api_key = token_hex(16)
|
||||
|
||||
logger.info(f"Created new API key for user {current_user.email}.")
|
||||
|
||||
return {"api_key": new_api_key}
|
||||
|
||||
|
||||
@api_key_router.delete("/api-key/{key_id}", dependencies=[Depends(AuthBearer())])
|
||||
async def delete_api_key(key_id: str, commons: CommonsDep, current_user: User = Depends(get_current_user)):
|
||||
"""Delete (deactivate) an API key for current user."""
|
||||
|
||||
commons['supabase'].table('api_keys').update({
|
||||
"is_active": False,
|
||||
"deleted_time": datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S")
|
||||
}).match({"key_id": key_id, "user_id": current_user.user_id}).execute()
|
||||
|
||||
return {"message": "API key deleted."}
|
||||
|
||||
|
||||
@api_key_router.get("/api-keys", response_model=List[ApiKeyInfo], dependencies=[Depends(AuthBearer())])
|
||||
async def get_api_keys(commons: CommonsDep, current_user: User = Depends(get_current_user)):
|
||||
"""Get all active API keys for the current user. Return only the key_id and the creation_time."""
|
||||
|
||||
user_id = fetch_user_id_from_credentials(commons, time.strftime("%Y%m%d"), {"email": current_user.email})
|
||||
|
||||
response = commons['supabase'].table('api_keys').select("key_id, creation_time").filter('user_id', 'eq', user_id).filter('is_active', 'eq', True).execute()
|
||||
return response.data
|
@ -2,7 +2,7 @@ import os
|
||||
import time
|
||||
from uuid import UUID
|
||||
|
||||
from auth.auth_bearer import JWTBearer, get_current_user
|
||||
from auth.auth_bearer import AuthBearer, get_current_user
|
||||
from fastapi import APIRouter, Depends, Request
|
||||
from models.chats import ChatMessage
|
||||
from models.users import User
|
||||
@ -30,7 +30,7 @@ def fetch_user_stats(commons, user, date):
|
||||
return userItem
|
||||
|
||||
# get all chats
|
||||
@chat_router.get("/chat", dependencies=[Depends(JWTBearer())])
|
||||
@chat_router.get("/chat", dependencies=[Depends(AuthBearer())])
|
||||
async def get_chats(commons: CommonsDep, current_user: User = Depends(get_current_user)):
|
||||
date = time.strftime("%Y%m%d")
|
||||
user_id = fetch_user_id_from_credentials(commons, date, {"email": current_user.email})
|
||||
@ -38,7 +38,7 @@ async def get_chats(commons: CommonsDep, current_user: User = Depends(get_curren
|
||||
return {"chats": chats}
|
||||
|
||||
# get one chat
|
||||
@chat_router.get("/chat/{chat_id}", dependencies=[Depends(JWTBearer())])
|
||||
@chat_router.get("/chat/{chat_id}", dependencies=[Depends(AuthBearer())])
|
||||
async def get_chats(commons: CommonsDep, chat_id: UUID):
|
||||
chats = get_chat_details(commons, chat_id)
|
||||
if len(chats) > 0:
|
||||
@ -47,7 +47,7 @@ async def get_chats(commons: CommonsDep, chat_id: UUID):
|
||||
return {"error": "Chat not found"}
|
||||
|
||||
# delete one chat
|
||||
@chat_router.delete("/chat/{chat_id}", dependencies=[Depends(JWTBearer())])
|
||||
@chat_router.delete("/chat/{chat_id}", dependencies=[Depends(AuthBearer())])
|
||||
async def delete_chat(commons: CommonsDep, chat_id: UUID):
|
||||
delete_chat_from_db(commons, chat_id)
|
||||
return {"message": f"{chat_id} has been deleted."}
|
||||
@ -91,11 +91,11 @@ def chat_handler(request, commons, chat_id, chat_message, email, is_new_chat=Fal
|
||||
|
||||
|
||||
# update existing chat
|
||||
@chat_router.put("/chat/{chat_id}", dependencies=[Depends(JWTBearer())])
|
||||
@chat_router.put("/chat/{chat_id}", dependencies=[Depends(AuthBearer())])
|
||||
async def chat_endpoint(request: Request, commons: CommonsDep, chat_id: UUID, chat_message: ChatMessage, current_user: User = Depends(get_current_user)):
|
||||
return chat_handler(request, commons, chat_id, chat_message, current_user.email)
|
||||
|
||||
# create new chat
|
||||
@chat_router.post("/chat", dependencies=[Depends(JWTBearer())])
|
||||
@chat_router.post("/chat", dependencies=[Depends(AuthBearer())])
|
||||
async def chat_endpoint(request: Request, commons: CommonsDep, chat_message: ChatMessage, current_user: User = Depends(get_current_user)):
|
||||
return chat_handler(request, commons, None, chat_message, current_user.email, is_new_chat=True)
|
||||
|
@ -2,7 +2,7 @@ import os
|
||||
import shutil
|
||||
from tempfile import SpooledTemporaryFile
|
||||
|
||||
from auth.auth_bearer import JWTBearer, get_current_user
|
||||
from auth.auth_bearer import AuthBearer, get_current_user
|
||||
from crawl.crawler import CrawlWebsite
|
||||
from fastapi import APIRouter, Depends, Request, UploadFile
|
||||
from models.users import User
|
||||
@ -23,7 +23,7 @@ def get_unique_user_data(commons, user):
|
||||
user_unique_vectors = [dict(t) for t in set(tuple(d.items()) for d in documents)]
|
||||
return user_unique_vectors
|
||||
|
||||
@crawl_router.post("/crawl/", dependencies=[Depends(JWTBearer())])
|
||||
@crawl_router.post("/crawl/", dependencies=[Depends(AuthBearer())])
|
||||
async def crawl_endpoint(request: Request,commons: CommonsDep, crawl_website: CrawlWebsite, enable_summarization: bool = False, current_user: User = Depends(get_current_user)):
|
||||
max_brain_size = os.getenv("MAX_BRAIN_SIZE")
|
||||
if request.headers.get('Openai-Api-Key'):
|
||||
|
@ -1,4 +1,4 @@
|
||||
from auth.auth_bearer import JWTBearer, get_current_user
|
||||
from auth.auth_bearer import AuthBearer, get_current_user
|
||||
from fastapi import APIRouter, Depends
|
||||
from models.users import User
|
||||
from utils.vectors import CommonsDep
|
||||
@ -13,15 +13,15 @@ def get_unique_user_data(commons, user):
|
||||
unique_data = [dict(t) for t in set(tuple(d.items()) for d in documents)]
|
||||
return unique_data
|
||||
|
||||
@explore_router.get("/explore", dependencies=[Depends(JWTBearer())])
|
||||
@explore_router.get("/explore", dependencies=[Depends(AuthBearer())])
|
||||
async def explore_endpoint(commons: CommonsDep, current_user: User = Depends(get_current_user)):
|
||||
unique_data = get_unique_user_data(commons, current_user)
|
||||
unique_data.sort(key=lambda x: int(x['size']), reverse=True)
|
||||
return {"documents": unique_data}
|
||||
|
||||
|
||||
@explore_router.delete("/explore/{file_name}", dependencies=[Depends(JWTBearer())])
|
||||
async def delete_endpoint(commons: CommonsDep, file_name: str, credentials: dict = Depends(JWTBearer())):
|
||||
@explore_router.delete("/explore/{file_name}", dependencies=[Depends(AuthBearer())])
|
||||
async def delete_endpoint(commons: CommonsDep, file_name: str, credentials: dict = Depends(AuthBearer())):
|
||||
user = User(email=credentials.get('email', 'none'))
|
||||
# Cascade delete the summary from the database first, because it has a foreign key constraint
|
||||
commons['supabase'].table("summaries").delete().match(
|
||||
@ -30,7 +30,7 @@ async def delete_endpoint(commons: CommonsDep, file_name: str, credentials: dict
|
||||
{"metadata->>file_name": file_name, "user_id": user.email}).execute()
|
||||
return {"message": f"{file_name} of user {user.email} has been deleted."}
|
||||
|
||||
@explore_router.get("/explore/{file_name}", dependencies=[Depends(JWTBearer())])
|
||||
@explore_router.get("/explore/{file_name}", dependencies=[Depends(AuthBearer())])
|
||||
async def download_endpoint(commons: CommonsDep, file_name: str, current_user: User = Depends(get_current_user)):
|
||||
response = commons['supabase'].table("vectors").select(
|
||||
"metadata->>file_name, metadata->>file_size, metadata->>file_extension, metadata->>file_url", "content").match({"metadata->>file_name": file_name, "user_id": current_user.email}).execute()
|
||||
|
@ -1,6 +1,6 @@
|
||||
import os
|
||||
|
||||
from auth.auth_bearer import JWTBearer, get_current_user
|
||||
from auth.auth_bearer import AuthBearer, get_current_user
|
||||
from fastapi import APIRouter, Depends, Request, UploadFile
|
||||
from models.users import User
|
||||
from utils.file import convert_bytes, get_file_size
|
||||
@ -23,7 +23,7 @@ def calculate_remaining_space(request, max_brain_size, max_brain_size_with_own_k
|
||||
remaining_free_space = float(max_brain_size_with_own_key) - current_brain_size if request.headers.get('Openai-Api-Key') else float(max_brain_size) - current_brain_size
|
||||
return remaining_free_space
|
||||
|
||||
@upload_router.post("/upload", dependencies=[Depends(JWTBearer())])
|
||||
@upload_router.post("/upload", dependencies=[Depends(AuthBearer())])
|
||||
async def upload_file(request: Request, commons: CommonsDep, file: UploadFile, enable_summarization: bool = False, current_user: User = Depends(get_current_user)):
|
||||
max_brain_size = os.getenv("MAX_BRAIN_SIZE")
|
||||
max_brain_size_with_own_key = os.getenv("MAX_BRAIN_SIZE_WITH_KEY",209715200)
|
||||
|
@ -2,7 +2,7 @@ import os
|
||||
import time
|
||||
|
||||
from fastapi import APIRouter, Depends, Request
|
||||
from auth.auth_bearer import JWTBearer, get_current_user
|
||||
from auth.auth_bearer import AuthBearer, get_current_user
|
||||
from models.users import User
|
||||
from utils.vectors import CommonsDep
|
||||
|
||||
@ -27,7 +27,7 @@ def get_user_request_stats(commons, email):
|
||||
'*').filter("email", "eq", email).execute()
|
||||
return requests_stats.data
|
||||
|
||||
@user_router.get("/user", dependencies=[Depends(JWTBearer())])
|
||||
@user_router.get("/user", dependencies=[Depends(AuthBearer())])
|
||||
async def get_user_endpoint(request: Request, commons: CommonsDep, current_user: User = Depends(get_current_user)):
|
||||
|
||||
user_vectors = get_user_vectors(commons, current_user.email)
|
||||
|
37
docs/docs/backend/api.md
Normal file
37
docs/docs/backend/api.md
Normal file
@ -0,0 +1,37 @@
|
||||
---
|
||||
sidebar_position: 2
|
||||
---
|
||||
|
||||
# API Backend Documentation
|
||||
|
||||
## Overview
|
||||
This documentation outlines the key points and usage instructions for interacting with the API backend. Please follow the guidelines below to use the backend services effectively.
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
1. Standalone Backend
|
||||
- The backend can now be used independently without the frontend application.
|
||||
- Users can interact with the API endpoints directly using API testing tools like Postman.
|
||||
|
||||
2. Generating API Key
|
||||
- To access the backend services, you need to sign in to the frontend application.
|
||||
- Once signed in, navigate to the `/config` page to generate a new API key.
|
||||
- The API key will be required to authenticate your requests to the backend.
|
||||
|
||||
3. Authenticating Requests
|
||||
- When making requests to the backend API, include the following header:
|
||||
- `Authorization: Bearer {api_key}`
|
||||
- Replace `{api_key}` with the generated API key obtained from the frontend.
|
||||
|
||||
4. Future Plans
|
||||
- The development team has plans to introduce additional features and improvements.
|
||||
- These include the ability to delete API keys and view the list of active keys.
|
||||
- The GitHub roadmap will provide more details on upcoming features, including addressing active issues.
|
||||
|
||||
5. API Key Expiration
|
||||
- Each API key has a daily expiration.
|
||||
- The expiration is based on Coordinated Universal Time (UTC) to avoid timezone issues.
|
||||
- After the expiration time, typically at midnight UTC, you will need to regenerate the API key to continue using the backend services.
|
||||
- Once the capability to delete keys is implemented, you will have the option to delete keys manually.
|
||||
|
||||
Please refer to the official GitHub repository and the project roadmap for more information and updates on the backend services.
|
@ -1,4 +1,4 @@
|
||||
import Button from "@/app/components/ui/Button";
|
||||
import Button from "@/lib/components/ui/Button";
|
||||
|
||||
import { useGoogleLogin } from "./hooks/useGoogleLogin";
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
import Button from "@/app/components/ui/Button";
|
||||
import { useSupabase } from "@/app/supabase-provider";
|
||||
import Button from "@/lib/components/ui/Button";
|
||||
import { useToast } from "@/lib/hooks/useToast";
|
||||
import { useState } from "react";
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
"use client";
|
||||
import Button from "@/app/components/ui/Button";
|
||||
import Card from "@/app/components/ui/Card";
|
||||
import { Divider } from "@/app/components/ui/Divider";
|
||||
import Field from "@/app/components/ui/Field";
|
||||
import PageHeading from "@/app/components/ui/PageHeading";
|
||||
import { useSupabase } from "@/app/supabase-provider";
|
||||
import Button from "@/lib/components/ui/Button";
|
||||
import Card from "@/lib/components/ui/Card";
|
||||
import { Divider } from "@/lib/components/ui/Divider";
|
||||
import Field from "@/lib/components/ui/Field";
|
||||
import PageHeading from "@/lib/components/ui/PageHeading";
|
||||
import { useToast } from "@/lib/hooks/useToast";
|
||||
import Link from "next/link";
|
||||
import { redirect } from "next/navigation";
|
||||
|
@ -1,8 +1,8 @@
|
||||
"use client";
|
||||
import Button from "@/app/components/ui/Button";
|
||||
import Card from "@/app/components/ui/Card";
|
||||
import PageHeading from "@/app/components/ui/PageHeading";
|
||||
import { useSupabase } from "@/app/supabase-provider";
|
||||
import Button from "@/lib/components/ui/Button";
|
||||
import Card from "@/lib/components/ui/Card";
|
||||
import PageHeading from "@/lib/components/ui/PageHeading";
|
||||
import { useToast } from "@/lib/hooks/useToast";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/navigation";
|
||||
|
@ -1,9 +1,9 @@
|
||||
"use client";
|
||||
import Button from "@/app/components/ui/Button";
|
||||
import Card from "@/app/components/ui/Card";
|
||||
import Field from "@/app/components/ui/Field";
|
||||
import PageHeading from "@/app/components/ui/PageHeading";
|
||||
import { useSupabase } from "@/app/supabase-provider";
|
||||
import Button from "@/lib/components/ui/Button";
|
||||
import Card from "@/lib/components/ui/Card";
|
||||
import Field from "@/lib/components/ui/Field";
|
||||
import PageHeading from "@/lib/components/ui/PageHeading";
|
||||
import { useToast } from "@/lib/hooks/useToast";
|
||||
import Link from "next/link";
|
||||
import { useState } from "react";
|
||||
|
@ -1,3 +1,4 @@
|
||||
import Card from "@/lib/components/ui/Card";
|
||||
import { FC, ReactNode } from "react";
|
||||
import {
|
||||
GiArtificialIntelligence,
|
||||
@ -7,7 +8,6 @@ import {
|
||||
GiLockedDoor,
|
||||
GiOpenBook,
|
||||
} from "react-icons/gi";
|
||||
import Card from "../components/ui/Card";
|
||||
|
||||
const Features: FC = () => {
|
||||
return (
|
||||
|
@ -1,9 +1,9 @@
|
||||
"use client";
|
||||
import Button from "@/lib/components/ui/Button";
|
||||
import { motion, useScroll, useSpring, useTransform } from "framer-motion";
|
||||
import Link from "next/link";
|
||||
import { FC, useRef } from "react";
|
||||
import { MdNorthEast } from "react-icons/md";
|
||||
import Button from "../components/ui/Button";
|
||||
|
||||
const Hero: FC = () => {
|
||||
const targetRef = useRef<HTMLDivElement | null>(null);
|
||||
|
@ -1,8 +1,8 @@
|
||||
"use client";
|
||||
import PageHeading from "@/lib/components/ui/PageHeading";
|
||||
import useChatsContext from "@/lib/context/ChatsProvider/hooks/useChatsContext";
|
||||
import { UUID } from "crypto";
|
||||
import { useEffect } from "react";
|
||||
import PageHeading from "../../components/ui/PageHeading";
|
||||
import useChatsContext from "../ChatsProvider/hooks/useChatsContext";
|
||||
import { ChatInput, ChatMessages } from "../components";
|
||||
|
||||
interface ChatPageProps {
|
||||
|
@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
import Button from "@/lib/components/ui/Button";
|
||||
import Link from "next/link";
|
||||
import { MdSettings } from "react-icons/md";
|
||||
import Button from "../../../../components/ui/Button";
|
||||
|
||||
export function ConfigButton() {
|
||||
return (
|
||||
|
@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
import Button from "@/lib/components/ui/Button";
|
||||
import { useSpeech } from "@/lib/context/ChatsProvider/hooks/useSpeech";
|
||||
import { MdMic, MdMicOff } from "react-icons/md";
|
||||
import Button from "../../../../components/ui/Button";
|
||||
import { useSpeech } from "../../../ChatsProvider/hooks/useSpeech";
|
||||
|
||||
export function MicButton() {
|
||||
const { isListening, speechSupported, startListening } = useSpeech();
|
||||
|
@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
import useChatsContext from "@/app/chat/ChatsProvider/hooks/useChatsContext";
|
||||
import Button from "../../../../components/ui/Button";
|
||||
import Button from "@/lib/components/ui/Button";
|
||||
import useChatsContext from "@/lib/context/ChatsProvider/hooks/useChatsContext";
|
||||
import { ConfigButton } from "./ConfigButton";
|
||||
import { MicButton } from "./MicButton";
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
import Card from "@/app/components/ui/Card";
|
||||
import Card from "@/lib/components/ui/Card";
|
||||
import useChatsContext from "@/lib/context/ChatsProvider/hooks/useChatsContext";
|
||||
import { FC, useEffect, useRef } from "react";
|
||||
import useChatsContext from "../../ChatsProvider/hooks/useChatsContext";
|
||||
import ChatMessage from "./ChatMessage";
|
||||
|
||||
export const ChatMessages: FC = () => {
|
||||
|
@ -5,7 +5,7 @@ import { usePathname } from "next/navigation";
|
||||
import { FC } from "react";
|
||||
import { FiTrash2 } from "react-icons/fi";
|
||||
import { MdChatBubbleOutline } from "react-icons/md";
|
||||
import { Chat } from "../../types";
|
||||
import { Chat } from "../../../../lib/types/Chat";
|
||||
|
||||
interface ChatsListItemProps {
|
||||
chat: Chat;
|
||||
|
@ -3,7 +3,7 @@ import { cn } from "@/lib/utils";
|
||||
import { MotionConfig, motion } from "framer-motion";
|
||||
import { useState } from "react";
|
||||
import { MdChevronRight } from "react-icons/md";
|
||||
import useChatsContext from "../../ChatsProvider/hooks/useChatsContext";
|
||||
import useChatsContext from "@/lib/context/ChatsProvider/hooks/useChatsContext";
|
||||
import ChatsListItem from "./ChatsListItem";
|
||||
import { NewChatButton } from "./NewChatButton";
|
||||
export function ChatsList() {
|
||||
|
@ -1,8 +1,8 @@
|
||||
"use client";
|
||||
import { ChatsProvider } from "@/lib/context/ChatsProvider/chats-provider";
|
||||
import { redirect } from "next/navigation";
|
||||
import { FC, ReactNode } from "react";
|
||||
import { useSupabase } from "../supabase-provider";
|
||||
import { ChatsProvider } from "./ChatsProvider/chats-provider";
|
||||
import { ChatsList } from "./components";
|
||||
|
||||
interface LayoutProps {
|
||||
|
60
frontend/app/config/components/ApiKeyConfig.tsx
Normal file
60
frontend/app/config/components/ApiKeyConfig.tsx
Normal file
@ -0,0 +1,60 @@
|
||||
"use client";
|
||||
|
||||
import Button from "@/lib/components/ui/Button";
|
||||
import { useAxios } from "@/lib/useAxios";
|
||||
import { useState } from "react";
|
||||
|
||||
export const ApiKeyConfig = (): JSX.Element => {
|
||||
const [apiKey, setApiKey] = useState("");
|
||||
const { axiosInstance } = useAxios();
|
||||
|
||||
const handleCreateClick = async () => {
|
||||
try {
|
||||
const response = await axiosInstance.post("/api-key"); // replace with your api-key endpoint URL
|
||||
setApiKey(response.data.api_key);
|
||||
} catch (error) {
|
||||
console.error("Error creating API key: ", error);
|
||||
}
|
||||
};
|
||||
|
||||
const copyToClipboard = async (text: string) => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(text);
|
||||
} catch (err) {
|
||||
console.error("Failed to copy:", err);
|
||||
}
|
||||
};
|
||||
|
||||
const handleCopyClick = () => {
|
||||
if (apiKey) {
|
||||
copyToClipboard(apiKey);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="border-b border-gray-300 w-full max-w-xl mb-8">
|
||||
<p className="text-center text-gray-600 uppercase tracking-wide font-semibold">
|
||||
API Key Config
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex justify-between items-center">
|
||||
<div className="flex items-center space-x-4">
|
||||
{!apiKey && (
|
||||
<Button variant="secondary" onClick={handleCreateClick}>
|
||||
Create New Key
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
{apiKey && (
|
||||
<div className="flex items-center space-x-4">
|
||||
<span className="text-gray-600">{apiKey}</span>
|
||||
<Button variant="secondary" onClick={handleCopyClick}>
|
||||
📋
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
@ -1,8 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import Field from "@/lib/components/ui/Field";
|
||||
import { BrainConfig } from "@/lib/context/BrainConfigProvider/types";
|
||||
import { UseFormRegister } from "react-hook-form";
|
||||
import Field from "../../components/ui/Field";
|
||||
|
||||
interface BackendConfigProps {
|
||||
register: UseFormRegister<BrainConfig>;
|
||||
|
@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import Button from "@/lib/components/ui/Button";
|
||||
import { useRouter } from "next/navigation";
|
||||
import Button from "../../components/ui/Button";
|
||||
import { useConfig } from "../hooks/useConfig";
|
||||
import { BackendConfig } from "./BackendConfig";
|
||||
import { ModelConfig } from "./ModelConfig";
|
||||
|
@ -1,18 +1,19 @@
|
||||
"use client";
|
||||
|
||||
import Field from "@/lib/components/ui/Field";
|
||||
import {
|
||||
BrainConfig,
|
||||
Model,
|
||||
PaidModels,
|
||||
anthropicModels,
|
||||
models,
|
||||
paidModels,
|
||||
} from "@/lib/context/BrainConfigProvider/types";
|
||||
import { UseFormRegister } from "react-hook-form";
|
||||
import Field from "../../components/ui/Field";
|
||||
|
||||
interface ModelConfigProps {
|
||||
register: UseFormRegister<BrainConfig>;
|
||||
model: Model;
|
||||
model: Model | PaidModels;
|
||||
openAiKey: string | undefined;
|
||||
temperature: number;
|
||||
maxTokens: number;
|
||||
@ -25,6 +26,24 @@ export const ModelConfig = ({
|
||||
temperature,
|
||||
maxTokens,
|
||||
}: ModelConfigProps): JSX.Element => {
|
||||
const defineMaxTokens = (model: Model | PaidModels): number => {
|
||||
//At the moment is evaluating only models from OpenAI
|
||||
switch (model) {
|
||||
case "gpt-3.5-turbo":
|
||||
return 3000;
|
||||
case "gpt-3.5-turbo-0613":
|
||||
return 3000;
|
||||
case "gpt-3.5-turbo-16k":
|
||||
return 14000;
|
||||
case "gpt-4":
|
||||
return 6000;
|
||||
case "gpt-4-0613":
|
||||
return 6000;
|
||||
default:
|
||||
return 3000;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="border-b border-gray-300 mt-8 mb-8">
|
||||
@ -85,8 +104,8 @@ export const ModelConfig = ({
|
||||
<input
|
||||
type="range"
|
||||
min="256"
|
||||
max="3000"
|
||||
step="1"
|
||||
max={defineMaxTokens(model)}
|
||||
step="32"
|
||||
value={maxTokens}
|
||||
{...register("maxTokens")}
|
||||
/>
|
||||
|
@ -1,8 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import { useSupabase } from "@/app/supabase-provider";
|
||||
import Button from "@/lib/components/ui/Button";
|
||||
import Link from "next/link";
|
||||
import Button from "../../components/ui/Button";
|
||||
|
||||
export const UserAccountSection = (): JSX.Element => {
|
||||
const { session } = useSupabase();
|
||||
|
@ -3,6 +3,7 @@ import { redirect } from "next/navigation";
|
||||
|
||||
import { useSupabase } from "../supabase-provider";
|
||||
import { ConfigForm, ConfigTitle } from "./components";
|
||||
import { ApiKeyConfig } from "./components/ApiKeyConfig";
|
||||
|
||||
// TODO: Use states instead of NEXTJS router to open and close modal
|
||||
export default function ConfigPage() {
|
||||
@ -17,6 +18,7 @@ export default function ConfigPage() {
|
||||
<section className="w-full outline-none pt-10 flex flex-col gap-5 items-center justify-center p-6">
|
||||
<ConfigTitle />
|
||||
<ConfigForm />
|
||||
<ApiKeyConfig />
|
||||
</section>
|
||||
</main>
|
||||
);
|
||||
|
@ -1,6 +1,9 @@
|
||||
"use client";
|
||||
import Ellipsis from "@/app/components/ui/Ellipsis";
|
||||
import { useSupabase } from "@/app/supabase-provider";
|
||||
import Button from "@/lib/components/ui/Button";
|
||||
import { AnimatedCard } from "@/lib/components/ui/Card";
|
||||
import Ellipsis from "@/lib/components/ui/Ellipsis";
|
||||
import Modal from "@/lib/components/ui/Modal";
|
||||
import { useToast } from "@/lib/hooks/useToast";
|
||||
import { useAxios } from "@/lib/useAxios";
|
||||
import {
|
||||
@ -10,10 +13,7 @@ import {
|
||||
forwardRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import Button from "../../components/ui/Button";
|
||||
import { AnimatedCard } from "../../components/ui/Card";
|
||||
import Modal from "../../components/ui/Modal";
|
||||
import { Document } from "../types";
|
||||
import { Document } from "../../../lib/types/Document";
|
||||
import DocumentData from "./DocumentData";
|
||||
|
||||
interface DocumentProps {
|
||||
|
@ -1,14 +1,15 @@
|
||||
"use client";
|
||||
import Button from "@/lib/components/ui/Button";
|
||||
import Spinner from "@/lib/components/ui/Spinner";
|
||||
|
||||
import { Document } from "@/lib/types/Document";
|
||||
import { useAxios } from "@/lib/useAxios";
|
||||
import { AnimatePresence, motion } from "framer-motion";
|
||||
import Link from "next/link";
|
||||
import { redirect } from "next/navigation";
|
||||
import { useEffect, useState } from "react";
|
||||
import Button from "../components/ui/Button";
|
||||
import Spinner from "../components/ui/Spinner";
|
||||
import { useSupabase } from "../supabase-provider";
|
||||
import DocumentItem from "./DocumentItem";
|
||||
import { Document } from "./types";
|
||||
|
||||
export default function ExplorePage() {
|
||||
const [documents, setDocuments] = useState<Document[]>([]);
|
||||
|
@ -1,11 +1,11 @@
|
||||
import Footer from "@/lib/components/Footer";
|
||||
import { NavBar } from "@/lib/components/NavBar";
|
||||
import { ToastProvider } from "@/lib/components/ui/Toast";
|
||||
import { createServerComponentSupabaseClient } from "@supabase/auth-helpers-nextjs";
|
||||
import { Analytics } from "@vercel/analytics/react";
|
||||
import { Inter } from "next/font/google";
|
||||
import { cookies, headers } from "next/headers";
|
||||
import { BrainConfigProvider } from "../lib/context/BrainConfigProvider/brain-config-provider";
|
||||
import Footer from "./components/Footer";
|
||||
import { NavBar } from "./components/NavBar";
|
||||
import { ToastProvider } from "./components/ui/Toast";
|
||||
import "./globals.css";
|
||||
import SupabaseProvider from "./supabase-provider";
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
import Button from "@/app/components/ui/Button";
|
||||
import Card from "@/app/components/ui/Card";
|
||||
import Field from "@/app/components/ui/Field";
|
||||
import Button from "@/lib/components/ui/Button";
|
||||
import Card from "@/lib/components/ui/Card";
|
||||
import Field from "@/lib/components/ui/Field";
|
||||
import { useCrawler } from "./hooks/useCrawler";
|
||||
|
||||
export const Crawler = (): JSX.Element => {
|
||||
|
@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
import Button from "@/lib/components/ui/Button";
|
||||
import Card from "@/lib/components/ui/Card";
|
||||
import { AnimatePresence } from "framer-motion";
|
||||
import Button from "../../../components/ui/Button";
|
||||
import Card from "../../../components/ui/Card";
|
||||
import FileComponent from "./components/FileComponent";
|
||||
import { useFileUploader } from "./hooks/useFileUploader";
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
"use client";
|
||||
import Button from "@/lib/components/ui/Button";
|
||||
import { Divider } from "@/lib/components/ui/Divider";
|
||||
import PageHeading from "@/lib/components/ui/PageHeading";
|
||||
import Link from "next/link";
|
||||
import Button from "../components/ui/Button";
|
||||
import { Divider } from "../components/ui/Divider";
|
||||
import PageHeading from "../components/ui/PageHeading";
|
||||
import { Crawler } from "./components/Crawler";
|
||||
import { FileUploader } from "./components/FileUploader";
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { GiBrain } from "react-icons/gi";
|
||||
import { UserStats } from "../types";
|
||||
import { UserStats } from "../../../lib/types/User";
|
||||
|
||||
export const BrainConsumption = (userStats: UserStats): JSX.Element => {
|
||||
const { current_brain_size, max_brain_size } = userStats;
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { HTMLAttributes } from "react";
|
||||
import { UserStats } from "../types";
|
||||
import { UserStats } from "../../../lib/types/User";
|
||||
|
||||
interface DateComponentProps extends HTMLAttributes<HTMLSpanElement> {
|
||||
date: UserStats["date"];
|
||||
|
@ -1,10 +1,10 @@
|
||||
"use client";
|
||||
import Button from "@/app/components/ui/Button";
|
||||
import Button from "@/lib/components/ui/Button";
|
||||
import { cn } from "@/lib/utils";
|
||||
import Link from "next/link";
|
||||
import prettyBytes from "pretty-bytes";
|
||||
import { HTMLAttributes } from "react";
|
||||
import { UserStats } from "../types";
|
||||
import { UserStats } from "../../../lib/types/User";
|
||||
import { BrainConsumption } from "./BrainConsumption";
|
||||
import { DateComponent } from "./Date";
|
||||
import BrainSpaceChart from "./Graphs/BrainSpaceChart";
|
||||
|
@ -1,11 +1,11 @@
|
||||
"use client";
|
||||
import Spinner from "@/lib/components/ui/Spinner";
|
||||
import { useAxios } from "@/lib/useAxios";
|
||||
import { redirect } from "next/navigation";
|
||||
import { useEffect, useState } from "react";
|
||||
import Spinner from "../components/ui/Spinner";
|
||||
import { UserStats } from "../../lib/types/User";
|
||||
import { useSupabase } from "../supabase-provider";
|
||||
import { UserStatistics } from "./components/UserStatistics";
|
||||
import { UserStats } from "./types";
|
||||
|
||||
export default function UserPage() {
|
||||
const [userStats, setUserStats] = useState<UserStats>();
|
||||
|
@ -1,4 +1,4 @@
|
||||
import Button from "@/app/components/ui/Button";
|
||||
import Button from "@/lib/components/ui/Button";
|
||||
import Link from "next/link";
|
||||
import { usePathname } from "next/navigation";
|
||||
|
@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
import Button from "@/lib/components/ui/Button";
|
||||
import { FC, useEffect, useLayoutEffect, useState } from "react";
|
||||
import { MdDarkMode, MdLightMode } from "react-icons/md";
|
||||
import Button from "../../../../ui/Button";
|
||||
|
||||
export const DarkModeToggle: FC = () => {
|
||||
const [dark, setDark] = useState(false);
|
@ -1,10 +1,10 @@
|
||||
"use client";
|
||||
import { useSupabase } from "@/app/supabase-provider";
|
||||
import Button from "@/lib/components/ui/Button";
|
||||
import { cn } from "@/lib/utils";
|
||||
import Link from "next/link";
|
||||
import { Dispatch, FC, HTMLAttributes, SetStateAction } from "react";
|
||||
import { MdPerson, MdSettings } from "react-icons/md";
|
||||
import Button from "../../../ui/Button";
|
||||
import { AuthButtons } from "./components/AuthButtons";
|
||||
import { DarkModeToggle } from "./components/DarkModeToggle";
|
||||
import { NavLink } from "./components/NavLink";
|
@ -4,7 +4,7 @@ import { FC } from "react";
|
||||
import { Header } from "./components/Header";
|
||||
import { Logo } from "./components/Logo";
|
||||
import { MobileMenu } from "./components/MobileMenu";
|
||||
import { NavItems } from "./components/NavItems/";
|
||||
import { NavItems } from "./components/NavItems";
|
||||
|
||||
export const NavBar: FC = () => {
|
||||
return (
|
@ -13,7 +13,7 @@ export const BrainConfigContext = createContext<ConfigContext | undefined>(
|
||||
);
|
||||
|
||||
const defaultBrainConfig: BrainConfig = {
|
||||
model: "gpt-3.5-turbo",
|
||||
model: "gpt-3.5-turbo-0613",
|
||||
temperature: 0,
|
||||
maxTokens: 500,
|
||||
keepLocal: true,
|
||||
|
@ -19,8 +19,9 @@ export type ConfigContext = {
|
||||
};
|
||||
|
||||
// export const openAiModels = ["gpt-3.5-turbo", "gpt-4"] as const; ## TODO activate GPT4 when not in demo mode
|
||||
export const openAiModels = ["gpt-3.5-turbo"] as const;
|
||||
export const openAiPaidModels = ["gpt-3.5-turbo", "gpt-4"] as const;
|
||||
|
||||
export const openAiModels = ["gpt-3.5-turbo","gpt-3.5-turbo-0613","gpt-3.5-turbo-16k"] as const;
|
||||
export const openAiPaidModels = ["gpt-3.5-turbo","gpt-3.5-turbo-0613","gpt-3.5-turbo-16k","gpt-4","gpt-4-0613"] as const;
|
||||
|
||||
export const anthropicModels = [
|
||||
// "claude-v1",
|
||||
|
@ -4,7 +4,7 @@ import { useAxios } from "@/lib/useAxios";
|
||||
import { UUID } from "crypto";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { Chat, ChatMessage } from "../../types";
|
||||
import { Chat, ChatMessage } from "../../../types/Chat";
|
||||
|
||||
export default function useChats() {
|
||||
const [allChats, setAllChats] = useState<Chat[]>([]);
|
||||
@ -34,24 +34,27 @@ export default function useChats() {
|
||||
text: "Error occured while fetching your chats",
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
}, [axiosInstance, publish]);
|
||||
|
||||
const fetchChat = useCallback(async (chatId?: UUID) => {
|
||||
if (!chatId) return;
|
||||
try {
|
||||
console.log(`Fetching chat ${chatId}`);
|
||||
const response = await axiosInstance.get<Chat>(`/chat/${chatId}`);
|
||||
console.log(response.data);
|
||||
const fetchChat = useCallback(
|
||||
async (chatId?: UUID) => {
|
||||
if (!chatId) return;
|
||||
try {
|
||||
console.log(`Fetching chat ${chatId}`);
|
||||
const response = await axiosInstance.get<Chat>(`/chat/${chatId}`);
|
||||
console.log(response.data);
|
||||
|
||||
setChat(response.data);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
publish({
|
||||
variant: "danger",
|
||||
text: `Error occured while fetching ${chatId}`,
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
setChat(response.data);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
publish({
|
||||
variant: "danger",
|
||||
text: `Error occured while fetching ${chatId}`,
|
||||
});
|
||||
}
|
||||
},
|
||||
[axiosInstance, publish]
|
||||
);
|
||||
|
||||
type ChatResponse = Omit<Chat, "chatId"> & { chatId: UUID | undefined };
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { isSpeechRecognitionSupported } from "@/lib/helpers/isSpeechRecognitionSupported";
|
||||
import { useEffect, useState } from "react";
|
||||
import { isSpeechRecognitionSupported } from "../../helpers/isSpeechRecognitionSupported";
|
||||
import useChatsContext from "./useChatsContext";
|
||||
|
||||
export const useSpeech = () => {
|
@ -1,5 +1,5 @@
|
||||
import { ToastContext } from "@/lib/components/ui/Toast/domain/ToastContext";
|
||||
import { useContext } from "react";
|
||||
import { ToastContext } from "../../app/components/ui/Toast/domain/ToastContext";
|
||||
|
||||
export const useToast = () => {
|
||||
const { publish } = useContext(ToastContext);
|
||||
|
0
frontend/lib/types/index.ts
Normal file
0
frontend/lib/types/index.ts
Normal file
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 43 KiB |
@ -5,6 +5,7 @@ module.exports = {
|
||||
'./pages/**/*.{js,ts,jsx,tsx,mdx}',
|
||||
'./components/**/*.{js,ts,jsx,tsx,mdx}',
|
||||
'./app/**/*.{js,ts,jsx,tsx,mdx}',
|
||||
'./lib/**/*.{js,ts,jsx,tsx,mdx}',
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
|
@ -103,3 +103,14 @@ BEGIN
|
||||
LIMIT match_count;
|
||||
END;
|
||||
$$;
|
||||
|
||||
-- Create api_keys table
|
||||
CREATE TABLE IF NOT EXISTS api_keys(
|
||||
key_id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
|
||||
user_id UUID REFERENCES users(user_id),
|
||||
api_key TEXT UNIQUE,
|
||||
creation_time TIMESTAMP DEFAULT current_timestamp,
|
||||
deleted_time TIMESTAMP,
|
||||
is_active BOOLEAN DEFAULT true
|
||||
);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user