Ilyas KHIAT commited on
Commit
a336311
1 Parent(s): 6eda836

first push

Browse files
Files changed (8) hide show
  1. .dockerignore +11 -0
  2. .gitignore +2 -0
  3. Dockerfile +13 -0
  4. README copy.md +10 -0
  5. kg_ia_signature.pkl +3 -0
  6. main.py +170 -0
  7. prompt.py +26 -0
  8. rag.py +63 -0
.dockerignore ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ __pycache__/
2
+ *.pyc
3
+ *.pyo
4
+ *.pyd
5
+ .Python
6
+ env/
7
+ venv/
8
+ .git
9
+ .dockerignore
10
+ Dockerfile
11
+ *.md
.gitignore ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ __pycache__/
2
+ .env
Dockerfile ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.12
2
+
3
+ RUN useradd -m -u 1000 user
4
+ USER user
5
+ ENV PATH="/home/user/.local/bin:$PATH"
6
+
7
+ WORKDIR /app
8
+
9
+ COPY --chown=user ./requirements.txt requirements.txt
10
+ RUN pip install --no-cache-dir --upgrade -r requirements.txt
11
+
12
+ COPY --chown=user . /app
13
+ CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860"]
README copy.md ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: MY ASSISTANT API
3
+ emoji: 💻
4
+ colorFrom: gray
5
+ colorTo: yellow
6
+ sdk: docker
7
+ pinned: false
8
+ ---
9
+
10
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
kg_ia_signature.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:55b49436038a45405798f6d05591464b1a35360409d83dbead163921707ac592
3
+ size 7354091
main.py ADDED
@@ -0,0 +1,170 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, HTTPException, UploadFile, File,Request,Depends,status,BackgroundTasks
2
+ from fastapi.security import OAuth2PasswordBearer
3
+ from pydantic import BaseModel, Json,EmailStr
4
+ from typing import Optional
5
+ from pinecone import Pinecone, ServerlessSpec
6
+ from uuid import uuid4
7
+ import os
8
+ from dotenv import load_dotenv
9
+ from rag import *
10
+ from fastapi.responses import StreamingResponse
11
+ import json
12
+ from prompt import *
13
+ from typing import Literal
14
+ import time
15
+ from fastapi.middleware.cors import CORSMiddleware
16
+ import requests
17
+
18
+ import smtplib
19
+ from email.mime.text import MIMEText
20
+
21
+
22
+ load_dotenv()
23
+
24
+ ## setup pinecone index
25
+ pinecone_api_key = os.environ.get("PINECONE_API_KEY")
26
+
27
+ pc = Pinecone(api_key=pinecone_api_key)
28
+
29
+ index_name = os.environ.get("INDEX_NAME") # change if desired
30
+
31
+ existing_indexes = [index_info["name"] for index_info in pc.list_indexes()]
32
+
33
+ if index_name not in existing_indexes:
34
+ pc.create_index(
35
+ name=index_name,
36
+ dimension=1536,
37
+ metric="cosine",
38
+ spec=ServerlessSpec(cloud="aws", region="us-east-1"),
39
+ )
40
+ while not pc.describe_index(index_name).status["ready"]:
41
+ time.sleep(1)
42
+
43
+ index = pc.Index(index_name)
44
+
45
+ vector_store = PineconeVectorStore(index=index, embedding=embedding)
46
+
47
+ ## setup authorization
48
+ api_keys = [os.environ.get("FASTAPI_API_KEY")]
49
+
50
+ oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") # use token authentication
51
+
52
+
53
+ def api_key_auth(api_key: str = Depends(oauth2_scheme)):
54
+ if api_key not in api_keys:
55
+ raise HTTPException(
56
+ status_code=status.HTTP_401_UNAUTHORIZED,
57
+ detail="Forbidden"
58
+ )
59
+
60
+ dev_mode = os.environ.get("DEV")
61
+
62
+ if dev_mode == "True":
63
+ app = FastAPI()
64
+ else:
65
+ app = FastAPI(dependencies=[Depends(api_key_auth)])
66
+
67
+ app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"])
68
+
69
+ # Pydantic model for the form data
70
+ class ContactForm(BaseModel):
71
+ name: str
72
+ email: EmailStr
73
+ message: str
74
+
75
+ def send_simple_message(to,subject,text):
76
+ api_key = os.getenv("MAILGUN_API_KEY")
77
+
78
+ return requests.post(
79
+ "https://api.mailgun.net/v3/sandboxafc6970ffdab40ee9566a4e180b117fd.mailgun.org/messages",
80
+ auth=("api", api_key),
81
+ data={"from": "Excited User <mailgun@sandboxafc6970ffdab40ee9566a4e180b117fd.mailgun.org>",
82
+ "to": [to],
83
+ "subject": subject,
84
+ "text": text})
85
+
86
+ # Function to send email
87
+ def send_email(form_data: ContactForm):
88
+ # sender_email = os.getenv("SENDER_EMAIL")
89
+ # sender_password = os.getenv("SENDER_PASSWORD")
90
+
91
+ receiver_email = os.getenv("RECEIVER_EMAIL") # Your email
92
+
93
+ # Setup the message content
94
+ text = f"Name: {form_data.name}\nEmail: {form_data.email}\nMessage: {form_data.message}"
95
+ title = "New message from your website!"
96
+
97
+ # Send the email
98
+ try:
99
+ send_simple_message(receiver_email,title,text)
100
+ except Exception as e:
101
+ print(e)
102
+ return {"message": "Failed to send email."}
103
+
104
+ # Endpoint to handle form submission
105
+ @app.post("/send_email")
106
+ async def send_contact_form(form_data: ContactForm, background_tasks: BackgroundTasks):
107
+ background_tasks.add_task(send_email, form_data)
108
+ return {"message": "Email sent successfully!"}
109
+
110
+ class UserInput(BaseModel):
111
+ query: str
112
+ stream: Optional[bool] = False
113
+ messages: Optional[list[dict]] = []
114
+
115
+ class ChunkToDB(BaseModel):
116
+ message: str
117
+ title: str
118
+
119
+
120
+ @app.post("/add_chunk_to_db")
121
+ async def add_chunk_to_db(chunk: ChunkToDB):
122
+ try:
123
+ title = chunk.title
124
+ message = chunk.message
125
+ return get_vectorstore(text_chunk=message,index=index,title=title)
126
+ except Exception as e:
127
+ return {"message": str(e)}
128
+
129
+
130
+ @app.get("/list_vectors")
131
+ async def list_vectors():
132
+ try:
133
+ return index.list()
134
+ except Exception as e:
135
+ return {"message": str(e)}
136
+
137
+
138
+ @app.post("/generate")
139
+ async def generate(user_input: UserInput):
140
+ try:
141
+ print(user_input.stream,user_input.query)
142
+ if user_input.stream:
143
+ return StreamingResponse(generate_stream(user_input.query,user_input.messages,index_name=index,stream=True,vector_store=vector_store),media_type="application/json")
144
+ else:
145
+ return generate_stream(user_input.query,user_input.messages,index_name=index,stream=False,vector_store=vector_store)
146
+ except Exception as e:
147
+ return {"message": str(e)}
148
+
149
+ @app.post("/retreive_context")
150
+ async def retreive_context_response(query: str):
151
+ try:
152
+ return retreive_context(index=index,query=query)
153
+ except Exception as e:
154
+ return {"message": str(e)}
155
+
156
+
157
+ @app.delete("/delete_vector")
158
+ async def delete_vector(filename_id: str):
159
+ try:
160
+ return index.delete(ids=[filename_id])
161
+ except Exception as e:
162
+ return {"message": str(e)}
163
+
164
+ @app.get("/check_server")
165
+ async def check_server():
166
+ return {"message":"Server is running"}
167
+
168
+ @app.get("/")
169
+ async def read_root():
170
+ return {"message":"Welcome to the AI API"}
prompt.py ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ template_sphinx = '''
2
+ Voici un résumé et un bout du récit de {writer}, l'auteur de {book_name}. Vous êtes le Grand Sphinx, maître des énigmes et des questions.
3
+ Vous devez tester si quelqu'un a lu le récit en lui posant une question qui lui ouvrira la porte vers la réalité de ce récit.
4
+ Votre question doit être en français, et vous devez l'associer aux réponses possibles.
5
+
6
+ **résumé**:
7
+ {summary}
8
+
9
+ **Extrait**:
10
+ {excerpt}
11
+
12
+ **Sortie**:
13
+ La sortie doit être une question en français, qui teste la compréhension du récit. Vous devez fournir les réponses possibles à cette question.
14
+
15
+ '''
16
+
17
+ template = '''
18
+ You are an AI assistant for Ilyas Khiat, a future engineer with a major in AI, and software engineering. Your job is to respond to visistors in the most human way . Always provide links if necessary (e.g., LinkedIn: https://www.linkedin.com/in/ilyas-khiat-148a73254/ ) Ensure your tone is pleaseant, and respond precisely to the user's query. if the context is not pertinent or you don't have enough information, **DON'T HALLUCINATE**.
19
+ The context retreived from the user is:
20
+ {context}
21
+ {history}
22
+ The user's query is:
23
+ {query}
24
+
25
+ Please respond to the user's query in a consis way and well formatted markdown with paragraphs and emojis. If the question is about my values , highlights Ilyas' technical expertise **without exageration**, projects and their **links**, and how he adds value to potential employers, plus soft skills. Add life to your answer and emphasize keywords with bold, MAKE IT **SHORT** in no more than **150 WORDS** or 200 tokens. Ensure your tone is pleasant, engaging, and matches the language of the user's query and your response is not bluffing and exaggerating but honest and convincing.
26
+ '''
rag.py ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from langchain.text_splitter import RecursiveCharacterTextSplitter
2
+ from langchain_openai import OpenAIEmbeddings
3
+ from langchain_community.vectorstores import FAISS
4
+ from langchain_core.documents import Document
5
+
6
+ from langchain_openai import ChatOpenAI
7
+ from langchain_core.output_parsers import StrOutputParser
8
+ from langchain_core.prompts import PromptTemplate
9
+ from uuid import uuid4
10
+ from prompt import *
11
+
12
+ from pydantic import BaseModel, Field
13
+ from dotenv import load_dotenv
14
+ import os
15
+
16
+ from langchain_core.tools import tool
17
+
18
+ import unicodedata
19
+
20
+ load_dotenv()
21
+ index_name = os.environ.get("INDEX_NAME")
22
+ # Global initialization
23
+ embedding_model = "text-embedding-3-small"
24
+
25
+ embedding = OpenAIEmbeddings(model=embedding_model)
26
+ # vector_store = PineconeVectorStore(index=index_name, embedding=embedding)
27
+
28
+ class sphinx_output(BaseModel):
29
+ question: str = Field(description="The question to ask the user to test if they read the entire book")
30
+ answers: list[str] = Field(description="The possible answers to the question to test if the user read the entire book")
31
+
32
+ llm = ChatOpenAI(model="gpt-4o-mini", max_tokens=300, temperature=0.5)
33
+
34
+
35
+ def get_random_chunk(chunks: list[str]) -> str:
36
+ return chunks[tool.random_int(0, len(chunks) - 1)]
37
+
38
+ def get_vectorstore(chunks: list[str]) -> FAISS:
39
+ vector_store = FAISS(index=index_name, embedding=embedding)
40
+ for chunk in chunks:
41
+ document = Document(text=chunk, id=str(uuid4()))
42
+ vector_store.index(document)
43
+ return vector_store
44
+
45
+ def generate_stream(query:str,messages = [], model = "gpt-4o-mini", max_tokens = 300, temperature = 0.5,index_name="",stream=True,vector_store=None):
46
+ try:
47
+ print("init chat")
48
+ print("init template")
49
+ prompt = PromptTemplate.from_template(template)
50
+ print("retreiving context")
51
+ context = retreive_context(query=query,index=index_name,vector_store=vector_store)
52
+ print(f"Context: {context}")
53
+ llm_chain = prompt | llm | StrOutputParser()
54
+
55
+ print("streaming")
56
+ if stream:
57
+ return llm_chain.stream({"context":context,"history":messages,"query":query})
58
+ else:
59
+ return llm.invoke(query)
60
+
61
+ except Exception as e:
62
+ print(e)
63
+ return False