Spaces:
Runtime error
Runtime error
Add video and audio
Browse files- .gitignore +9 -1
- README.md +1 -1
- app.py +55 -129
- functions.py +123 -0
- prompts/main_prompt.txt +0 -27
- prompts/standalone_question.txt +0 -6
.gitignore
CHANGED
@@ -1,5 +1,13 @@
|
|
1 |
.idea/
|
2 |
.venv/
|
3 |
.env
|
|
|
4 |
|
5 |
-
tests.ipynb
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
.idea/
|
2 |
.venv/
|
3 |
.env
|
4 |
+
__pycache__/
|
5 |
|
6 |
+
tests.ipynb
|
7 |
+
example_questions.pdf
|
8 |
+
|
9 |
+
data.json
|
10 |
+
audio.wav
|
11 |
+
video.mp4
|
12 |
+
|
13 |
+
prompts/
|
README.md
CHANGED
@@ -4,7 +4,7 @@ emoji: 💻
|
|
4 |
colorFrom: yellow
|
5 |
colorTo: blue
|
6 |
sdk: gradio
|
7 |
-
sdk_version: 4.
|
8 |
app_file: app.py
|
9 |
pinned: false
|
10 |
---
|
|
|
4 |
colorFrom: yellow
|
5 |
colorTo: blue
|
6 |
sdk: gradio
|
7 |
+
sdk_version: 4.2.0
|
8 |
app_file: app.py
|
9 |
pinned: false
|
10 |
---
|
app.py
CHANGED
@@ -1,143 +1,69 @@
|
|
1 |
-
import
|
2 |
-
import pinecone
|
3 |
-
import gradio as gr
|
4 |
-
from openai import OpenAI
|
5 |
from dotenv import load_dotenv
|
6 |
|
7 |
load_dotenv()
|
8 |
|
9 |
-
openai_client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
|
10 |
-
pinecone.init(api_key=os.getenv("PINECONE_API_TOKEN"), environment=os.getenv("PINECONE_ENVIRONMENT"))
|
11 |
-
index = pinecone.Index(os.getenv("PINECONE_INDEX"))
|
12 |
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
30 |
)
|
31 |
-
return response.data[0].embedding
|
32 |
-
|
33 |
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
messages=message_history
|
39 |
)
|
40 |
-
return response.choices[0].message.content
|
41 |
-
|
42 |
-
|
43 |
-
def get_standalone_question(question: str, message_history: list[dict], prompt_q: str) -> str:
|
44 |
-
# Format the message history like: Human: blablablá \nAssistant: blablablá
|
45 |
-
history = ''
|
46 |
-
for i, msg in enumerate(message_history):
|
47 |
-
if i == 0:
|
48 |
-
continue # Omit the prompt
|
49 |
-
if i % 2 == 0:
|
50 |
-
history += f'Human: {msg["content"]}\n'
|
51 |
-
else:
|
52 |
-
history += f'Assistant: {msg["content"]}\n'
|
53 |
-
|
54 |
-
# Add history and question to the prompt and call chatgpt
|
55 |
-
prompt = [{'role': 'system', 'content': ''}]
|
56 |
-
content = prompt_q.replace('HISTORY', history).replace('QUESTION', question)
|
57 |
-
prompt[0]['content'] = content
|
58 |
-
|
59 |
-
return call_api(prompt)
|
60 |
-
|
61 |
-
|
62 |
-
def get_context(question: str) -> str:
|
63 |
-
q_embedding = get_embedding(question)
|
64 |
-
|
65 |
-
# Get most similar vectors
|
66 |
-
result = index.query(
|
67 |
-
vector=q_embedding,
|
68 |
-
top_k=10,
|
69 |
-
include_metadata=True
|
70 |
-
)['matches']
|
71 |
-
|
72 |
-
# Crete a string based on the text of each vector
|
73 |
-
context = ''
|
74 |
-
for r in result:
|
75 |
-
context += r['metadata']['Text'] + '\n'
|
76 |
-
return context
|
77 |
-
|
78 |
-
|
79 |
-
def get_answer(context: str, message_history: list[dict], question: str, prompt_m: str) -> str:
|
80 |
-
message_history[0]['content'] = prompt_m.replace('CONTEXT', context)
|
81 |
-
message_history.append({'role': 'user', 'content': question})
|
82 |
-
return call_api(message_history)
|
83 |
-
|
84 |
-
|
85 |
-
def ask_query(
|
86 |
-
msg: str, chat_history: list[list[str | None]], message_history: list[dict], prompt_q: str, prompt_m: str
|
87 |
-
) -> tuple[str, list[list[str | None]], list[dict]]:
|
88 |
-
|
89 |
-
if len(chat_history) == 5:
|
90 |
-
answer = 'Un placer haberte ayudado, hasta luego!'
|
91 |
-
|
92 |
-
else:
|
93 |
-
question = get_standalone_question(msg, message_history, prompt_q)
|
94 |
-
print(question)
|
95 |
-
context = get_context(question)
|
96 |
-
print(context)
|
97 |
-
answer = get_answer(context, message_history, question, prompt_m)
|
98 |
-
|
99 |
-
message_history.append({'role': 'assistant', 'content': answer})
|
100 |
-
chat_history.append([msg, answer])
|
101 |
-
|
102 |
-
return "", chat_history, message_history
|
103 |
-
|
104 |
-
|
105 |
-
def start_chat(chat_history: list[list[str | None]], prompt_m: str):
|
106 |
-
greeting = ('Hola 👋, ¡estoy encantada de conversar contigo! Antes de empezar, quiero asegurarte algo '
|
107 |
-
'importante: tu privacidad y confidencialidad son mi máxima prioridad. Puedes estar '
|
108 |
-
'tranquila sabiendo que nuestras conversaciones son completamente seguras y nunca '
|
109 |
-
'serán compartidas con terceros. ¿En qué puedo ayudarte hoy?')
|
110 |
-
|
111 |
-
message_history = [
|
112 |
-
{'role': 'system', 'content': prompt_m},
|
113 |
-
{'role': 'assistant', 'content': greeting}
|
114 |
-
]
|
115 |
-
|
116 |
-
chat_history.append(['', greeting])
|
117 |
-
|
118 |
-
return message_history, chat_history, gr.Button(visible=False), gr.Text(visible=True)
|
119 |
-
|
120 |
-
|
121 |
-
with gr.Blocks() as app:
|
122 |
-
prompt_questions = gr.State(init_prompt('questions'))
|
123 |
-
prompt_main = gr.State(init_prompt('main'))
|
124 |
-
msg_history = gr.State([])
|
125 |
-
|
126 |
-
chatbot = gr.Chatbot(label='Bella Nosotras')
|
127 |
-
start_button = gr.Button()
|
128 |
-
message = gr.Textbox(visible=False)
|
129 |
|
130 |
-
|
131 |
-
|
132 |
-
[
|
133 |
-
[
|
134 |
)
|
135 |
|
136 |
-
|
137 |
-
|
138 |
-
[message, chatbot, msg_history, prompt_questions, prompt_main],
|
139 |
-
[message, chatbot, msg_history]
|
140 |
)
|
141 |
|
142 |
|
143 |
-
app.
|
|
|
|
1 |
+
from functions import *
|
|
|
|
|
|
|
2 |
from dotenv import load_dotenv
|
3 |
|
4 |
load_dotenv()
|
5 |
|
|
|
|
|
|
|
6 |
|
7 |
+
with (gr.Blocks() as app):
|
8 |
+
user_id = gr.State('') # id used to find the chat into the database
|
9 |
+
|
10 |
+
with gr.Tab('Test Chats'):
|
11 |
+
with gr.Row() as select_author:
|
12 |
+
chat_btn = gr.Button(value='Start chat')
|
13 |
+
|
14 |
+
# ------------------------------------- Chat -------------------------------------------
|
15 |
+
with gr.Row(visible=False) as chatbot:
|
16 |
+
with gr.Column():
|
17 |
+
with gr.Row():
|
18 |
+
video = gr.Video(interactive=False, label='Video', autoplay=True)
|
19 |
+
with gr.Row():
|
20 |
+
output_audio = gr.Audio(interactive=False, label='Audio', autoplay=True)
|
21 |
+
with gr.Row():
|
22 |
+
checkbox_video = gr.Checkbox(
|
23 |
+
label='Get video', info='Remember that this has a cost'
|
24 |
+
)
|
25 |
+
checkbox_audio = gr.Checkbox(
|
26 |
+
label='Get only audio', info='This is free but makes API slower'
|
27 |
+
)
|
28 |
+
radio = gr.Radio(
|
29 |
+
choices=['local', 'remote'], value='remote', label='Backend used', visible=False
|
30 |
+
)
|
31 |
+
|
32 |
+
with gr.Column():
|
33 |
+
with gr.Row():
|
34 |
+
chat = gr.Chatbot(label='Chat')
|
35 |
+
with gr.Row():
|
36 |
+
with gr.Column():
|
37 |
+
text = gr.Text(label='Write your question')
|
38 |
+
with gr.Column():
|
39 |
+
audio = gr.Audio(sources=['microphone'], type='filepath', label='Tell me your question')
|
40 |
+
button_audio = gr.Button(value='Submit audio')
|
41 |
+
|
42 |
+
# -------------------------------------- Actions -----------------------------------------
|
43 |
+
chat_btn.click(
|
44 |
+
make_invisible, None, select_author
|
45 |
+
).then(
|
46 |
+
make_visible, None, chatbot
|
47 |
+
).then(
|
48 |
+
init_chatbot, [chat, radio], [video, chat, user_id]
|
49 |
)
|
|
|
|
|
50 |
|
51 |
+
text.submit(
|
52 |
+
get_answer_text,
|
53 |
+
[text, chat, user_id, checkbox_video, checkbox_audio, radio],
|
54 |
+
[video, output_audio, chat, text]
|
|
|
55 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
56 |
|
57 |
+
button_audio.click(
|
58 |
+
get_answer_audio,
|
59 |
+
[audio, chat, user_id, checkbox_video, checkbox_audio, radio],
|
60 |
+
[video, output_audio, chat, audio]
|
61 |
)
|
62 |
|
63 |
+
output_audio.stop(
|
64 |
+
lambda: None, None, output_audio
|
|
|
|
|
65 |
)
|
66 |
|
67 |
|
68 |
+
app.queue()
|
69 |
+
app.launch(debug=True, auth=(os.environ.get('SPACE_USERNAME'), os.environ.get('SPACE_PASSWORD')))
|
functions.py
ADDED
@@ -0,0 +1,123 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import base64
|
3 |
+
import shutil
|
4 |
+
import requests
|
5 |
+
import gradio as gr
|
6 |
+
from datetime import datetime
|
7 |
+
|
8 |
+
|
9 |
+
def make_invisible():
|
10 |
+
"""
|
11 |
+
Makes visible a row
|
12 |
+
"""
|
13 |
+
return gr.Row.update(visible=False)
|
14 |
+
|
15 |
+
|
16 |
+
def make_visible():
|
17 |
+
"""
|
18 |
+
Makes visibles a rows
|
19 |
+
"""
|
20 |
+
return gr.Row.update(visible=True)
|
21 |
+
|
22 |
+
|
23 |
+
def _query(payload, backend_location: str, type_query: str):
|
24 |
+
"""
|
25 |
+
Returns the json from a post request. It is done to the BellaAPI
|
26 |
+
"""
|
27 |
+
API_TOKEN = os.getenv(f'API_TOKEN')
|
28 |
+
API_URL = os.getenv(f'API_URL_{backend_location}') + f'{type_query}/'
|
29 |
+
|
30 |
+
headers = {
|
31 |
+
"Authorization": f"Bearer {API_TOKEN}",
|
32 |
+
"Content-Type": "application/json"
|
33 |
+
}
|
34 |
+
|
35 |
+
response = requests.post(API_URL, headers=headers, json=payload)
|
36 |
+
return response.json()
|
37 |
+
|
38 |
+
|
39 |
+
def _download_media(url: str, type_media: str) -> None:
|
40 |
+
"""
|
41 |
+
Downloads a video or audio (depending on the type_media) that can be
|
42 |
+
used inside a gr.Video or gr.Audio
|
43 |
+
"""
|
44 |
+
name = 'video.mp4' if type_media == 'video' else 'audio.wav'
|
45 |
+
with requests.get(url, stream=True) as r, open(name, "wb") as f:
|
46 |
+
shutil.copyfileobj(r.raw, f)
|
47 |
+
|
48 |
+
|
49 |
+
def init_chatbot(chatbot: list[tuple[str, str]], backend_location: str):
|
50 |
+
"""
|
51 |
+
Returns a greeting video, with its transcription and the user_id that
|
52 |
+
will be used later in the other requests
|
53 |
+
"""
|
54 |
+
l = 'es'
|
55 |
+
|
56 |
+
# Call API with the following json
|
57 |
+
inputs = {
|
58 |
+
"date": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
59 |
+
'language': l,
|
60 |
+
'get_video': True
|
61 |
+
}
|
62 |
+
output = _query(inputs, backend_location, 'start_chat')
|
63 |
+
|
64 |
+
chatbot.append(('', output['answer']))
|
65 |
+
_download_media(output['link_media'], 'video')
|
66 |
+
|
67 |
+
return 'video.mp4', chatbot, output['user_id']
|
68 |
+
|
69 |
+
|
70 |
+
def get_answer_text(
|
71 |
+
question: str, chatbot: list[tuple[str, str]], user_id: str, checkbox_video: bool, checkbox_audio: bool,
|
72 |
+
backend_location: str
|
73 |
+
):
|
74 |
+
"""
|
75 |
+
Gets the answer of the chatbot
|
76 |
+
"""
|
77 |
+
|
78 |
+
# Create json and send it to the API
|
79 |
+
inputs = {
|
80 |
+
'text': question, 'user_id': user_id, 'get_video': checkbox_video, 'get_audio': checkbox_audio,
|
81 |
+
}
|
82 |
+
output = _query(inputs, backend_location, 'get_answer')
|
83 |
+
return _update_elements(question, chatbot, output, checkbox_video, checkbox_audio, '')
|
84 |
+
|
85 |
+
|
86 |
+
def get_answer_audio(
|
87 |
+
audio_path, chatbot: list[tuple[str, str]], user_id: str, checkbox_video: bool, checkbox_audio: bool,
|
88 |
+
backend_location: str
|
89 |
+
):
|
90 |
+
"""
|
91 |
+
Gets the answer of the chatbot
|
92 |
+
"""
|
93 |
+
# Encode audio data to Base64
|
94 |
+
with open(audio_path, 'rb') as audio_file:
|
95 |
+
audio_data = audio_file.read()
|
96 |
+
encoded_audio = base64.b64encode(audio_data).decode('utf-8')
|
97 |
+
|
98 |
+
# Create json and send it to the API
|
99 |
+
inputs = {
|
100 |
+
'is_audio': True, 'audio': encoded_audio, 'user_id': user_id, 'get_video': checkbox_video,
|
101 |
+
'get_audio': checkbox_audio
|
102 |
+
}
|
103 |
+
output = _query(inputs, backend_location, 'get_answer')
|
104 |
+
|
105 |
+
# Transcription of the audio
|
106 |
+
question = output['question']
|
107 |
+
return _update_elements(question, chatbot, output, checkbox_video, checkbox_audio, None)
|
108 |
+
|
109 |
+
|
110 |
+
def _update_elements(question, chatbot, output, checkbox_video, checkbox_audio, clean):
|
111 |
+
"""
|
112 |
+
Adds the video, output audio, interaction and cleans the text or audio
|
113 |
+
"""
|
114 |
+
chatbot.append((question, output['answer']))
|
115 |
+
link_media = output['link_media']
|
116 |
+
if checkbox_video:
|
117 |
+
_download_media(link_media, 'video')
|
118 |
+
return 'video.mp4', None, chatbot, clean
|
119 |
+
elif checkbox_audio:
|
120 |
+
_download_media(link_media, 'audio')
|
121 |
+
return None, 'audio.wav', chatbot, clean
|
122 |
+
else:
|
123 |
+
return None, None, chatbot, clean
|
prompts/main_prompt.txt
DELETED
@@ -1,27 +0,0 @@
|
|
1 |
-
Tu eres Bella es una mujer de 32 años, amigable, sensible y comprensiva. Eres una confidente que entiende todo lo relacionado con el tema de la menstruación y la higiene íntima de la marca Nosotras, también comprendes las emociones de las mujeres durante la menstruación y el ciclo menstrual. Eres la amiga que necesita cualquier mujer para ser escuchada y validada en cualquier momento de tu ciclo menstrual, y a la que le consultan sobre productos de higiene menstrual e íntima de la marca Nosotras, cuáles son los mejores y se adecuan a sus necesidades.
|
2 |
-
|
3 |
-
Tono de Comunicación: Tu tono debe ser amigable, empático y sensible. Debes comunicarte de manera natural y fácil de entender. Usa palabras simples y un lenguaje que se sienta cercano. Además, no repites información dada anteriormente.
|
4 |
-
Objetivo de Bella: Tu objetivo es brindar información y apoyo relacionado con productos de higiene menstrual e higiene íntima, recomendando productos específicos de la marca Nosotras.
|
5 |
-
|
6 |
-
Si te preguntan sobre situaciones donde se requiera asesoramiento médico o psicológico e invita a la usuaria a visitar el consultorio virtual de Nosotras: https://www.nosotrasonline.com.co/entre-nosotras/consultorios/ . Evita dar consejos o información relacionados con prácticas médicas, remite a profesionales de la salud.
|
7 |
-
|
8 |
-
Instrucciones para tener cualquier conversación. Debes hacer siempre lo siguiente:
|
9 |
-
|
10 |
-
Describe los diferentes tipos de productos de higiene menstrual disponibles hablando de sus materiales, beneficios, para qué sirven y en qué tiendas comprarlos según la información de www.nosotrasonline.com.co . Explica todo máximo en 5 frases.
|
11 |
-
Pregunta a las usuarias sobre sus alergias o sensibilidades relacionadas con ciertos materiales y evita recomendarles productos que contengan dichos materiales cuando te digan que son alérgicas a ellos.
|
12 |
-
Promueve el respeto a la autonomía y decisiones individuales de las usuarias.
|
13 |
-
Muéstrate sensible a la diversidad de experiencias y circunstancias de las usuarias.
|
14 |
-
|
15 |
-
Estos son algunos ejemplos de los usuarios que tendrías:
|
16 |
-
|
17 |
-
Soy una mujer adolescente entre los 12 y los 18 que menstrua por primera vez, tengo muchas dudas sobre qué productos usar y preguntas sobre la menstruación y el ciclo menstrual.
|
18 |
-
Soy una mujer entre los 18 y los 40 años que lleva un tiempo menstruando, quiere mejorar mi calidad de vida con productos cómodos y que me protejan de manchas en cualquier momento del día. También tengo preguntas y dudas relacionadas con mitos o tabúes que quiero desvirtuar o cambios en mi ciclo menstrual.
|
19 |
-
Soy una mujer entre los 40 y 55 años que está cerca de la menopausia o está pasando por una, y tengo dudas sobre qué productos de higiene menstrual e íntima debería comenzar a usar, además de preguntas relacionadas con los diferentes cambios que vienen con la menopausia.
|
20 |
-
|
21 |
-
|
22 |
-
Para responder las preguntas, utiliza la siguiente información. Si la informacion dada no es suficiente, no inventes información y dile al usuario que visite esta página: https://www.nosotrasonline.com.co/
|
23 |
-
|
24 |
-
=========
|
25 |
-
Contexto:
|
26 |
-
CONTEXT
|
27 |
-
=========
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
prompts/standalone_question.txt
DELETED
@@ -1,6 +0,0 @@
|
|
1 |
-
Your goal is to rephrase questions related to the brand Nosotras (which is a brand for feminine hygiene products) to show what is the best advice for the customer. Given the following conversation and a follow up question, rephrase the follow up question to be a standalone phrase where you show the best recommendation for the customer. Always include all the important information, specially all name of the nouns and the intention of the customer. For example, if the user says "Me siento incomoda con las toallas gruesas, cual me recomiendas?" The standalone phrase should me something like "necesito toallas menos gruesas y más cómodas". There might be moments when there isn't a question in those cases return a standalone phrase: for example if the user says "hello" (or something similar) then the output would be "the user wants to say hello", or if the user says "thank you" (or something similar) then it would be "the user is saying thank you", or if the user says "great", "it is very helpfully" (or something similar) then it would be "user thinks the recommendation is great". Your answer will always be in spanish.
|
2 |
-
Chat History:
|
3 |
-
|
4 |
-
HISTORY
|
5 |
-
Follow-up Input: QUESTION
|
6 |
-
Standalone question:
|
|
|
|
|
|
|
|
|
|
|
|
|
|