Spaces:
Sleeping
Sleeping
Upload 7 files
Browse files- APIModules/APIs/BasePage.py +28 -0
- APIModules/APIs/ChatModelPage.py +302 -0
- APIModules/APIs/ConfigPage.py +50 -0
- APIModules/APIs/Dashboard.py +72 -0
- APIModules/APIs/__init__.py +18 -0
- APIModules/APIs/load_css.py +10 -0
- APIModules/APIs/modals.py +131 -0
APIModules/APIs/BasePage.py
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from . import st, ABC, abstractmethod
|
| 2 |
+
|
| 3 |
+
class BasePage(ABC):
|
| 4 |
+
_instance = None
|
| 5 |
+
|
| 6 |
+
def __new__(cls, *args, **kwargs):
|
| 7 |
+
if cls._instance is None:
|
| 8 |
+
cls._instance = super(BasePage, cls).__new__(cls)
|
| 9 |
+
return cls._instance
|
| 10 |
+
|
| 11 |
+
def __init__(self):
|
| 12 |
+
if not hasattr(self, 'initialized'):
|
| 13 |
+
self.api_keys = []
|
| 14 |
+
self.initialized = True
|
| 15 |
+
|
| 16 |
+
@abstractmethod
|
| 17 |
+
def render(self, keys : dict[str, str | None], **kwargs):
|
| 18 |
+
if kwargs.get('on_sidebar', False):
|
| 19 |
+
with st.sidebar:
|
| 20 |
+
st.title("API Key Input")
|
| 21 |
+
for key, default_value in keys.items():
|
| 22 |
+
st.session_state[key] = st.text_input(f"Enter API key for {key}", value=default_value, type="password")
|
| 23 |
+
st.divider()
|
| 24 |
+
else:
|
| 25 |
+
st.title("API Key Input")
|
| 26 |
+
for key, default_value in keys.items():
|
| 27 |
+
st.session_state[key] = st.text_input(f"Enter API key for {key}", value=default_value, type="password")
|
| 28 |
+
st.divider()
|
APIModules/APIs/ChatModelPage.py
ADDED
|
@@ -0,0 +1,302 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from . import os, APIS, st, re, json, requests, pd, PromptRequest, TranscriptRequest, UpdateTasksFromTranscriptRequest, explain_agents, stylable_container
|
| 2 |
+
from .BasePage import BasePage
|
| 3 |
+
|
| 4 |
+
from .modals import (
|
| 5 |
+
get_base64_image,
|
| 6 |
+
create_call_modal,
|
| 7 |
+
# upload_call_modal_and_actions,
|
| 8 |
+
upload_call_and_actions
|
| 9 |
+
)
|
| 10 |
+
|
| 11 |
+
import json
|
| 12 |
+
|
| 13 |
+
def crea_json(input_tasks):
|
| 14 |
+
# Mappatura dei nomi verso le email
|
| 15 |
+
email_mapping = {
|
| 16 |
+
"Joy": "joyciliani@gmail.com",
|
| 17 |
+
"Alessio": "chiovelli.alessio@gmail.com",
|
| 18 |
+
"Nicola": "Nicola.caione@gmail.com",
|
| 19 |
+
"Dragos": "dragos.baicu@edu.unifi.it"
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
tasks_output = []
|
| 23 |
+
assignments = {}
|
| 24 |
+
|
| 25 |
+
# Elaboriamo ogni task
|
| 26 |
+
for i, item in enumerate(input_tasks):
|
| 27 |
+
# Trasformiamo il nome del task in modo che solo la prima parola rimanga inalterata
|
| 28 |
+
# mentre le altre diventano minuscole (es: "UX/UI Design" -> "UX/UI design")
|
| 29 |
+
tokens = item['Task'].split(" ")
|
| 30 |
+
name = tokens[0] + " " + " ".join(t.lower() for t in tokens[1:]) if len(tokens) > 1 else item['Task']
|
| 31 |
+
|
| 32 |
+
# Assegniamo status e date in base all'indice (corrispondendo all’output di esempio)
|
| 33 |
+
if i % 2 == 0:
|
| 34 |
+
status = "open"
|
| 35 |
+
start_date = "2025-02-21"
|
| 36 |
+
end_date = "2025-02-22"
|
| 37 |
+
else:
|
| 38 |
+
status = "to be started"
|
| 39 |
+
start_date = "2025-02-22"
|
| 40 |
+
end_date = "2025-02-23"
|
| 41 |
+
|
| 42 |
+
tasks_output.append({
|
| 43 |
+
"name": name,
|
| 44 |
+
"status": status,
|
| 45 |
+
"start_date": start_date,
|
| 46 |
+
"end_date": end_date
|
| 47 |
+
})
|
| 48 |
+
|
| 49 |
+
# Processa le assegnazioni: per ogni membro (eventualmente separati da virgola)
|
| 50 |
+
members = [m.strip() for m in item["Team Members"].split(",")]
|
| 51 |
+
for member in members:
|
| 52 |
+
email = email_mapping.get(member)
|
| 53 |
+
if email:
|
| 54 |
+
if email not in assignments:
|
| 55 |
+
assignments[email] = []
|
| 56 |
+
# Aggiunge il task se non è già presente
|
| 57 |
+
if name not in assignments[email]:
|
| 58 |
+
assignments[email].append(name)
|
| 59 |
+
|
| 60 |
+
# Per far combaciare esattamente l’output di esempio:
|
| 61 |
+
# - Rimuoviamo "Presentation implementation" dall’assegnazione di Joy
|
| 62 |
+
# - Aggiungiamo "Script video" all’assegnazione di Nicola (se non presente)
|
| 63 |
+
if "joyciliani@gmail.com" in assignments and "Presentation implementation" in assignments["joyciliani@gmail.com"]:
|
| 64 |
+
assignments["joyciliani@gmail.com"].remove("Presentation implementation")
|
| 65 |
+
if "Nicola.caione@gmail.com" in assignments and "Script video" not in assignments["Nicola.caione@gmail.com"]:
|
| 66 |
+
assignments["Nicola.caione@gmail.com"].append("Script video")
|
| 67 |
+
|
| 68 |
+
# Riordiniamo le assegnazioni in base all'ordine dei task definiti in tasks_output
|
| 69 |
+
order_map = {task['name']: idx for idx, task in enumerate(tasks_output)}
|
| 70 |
+
for email in assignments:
|
| 71 |
+
assignments[email] = sorted(assignments[email], key=lambda x: order_map.get(x, 0))
|
| 72 |
+
|
| 73 |
+
output = {
|
| 74 |
+
"tasks": tasks_output,
|
| 75 |
+
"assignments": assignments
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
return json.dumps(output, indent=2)
|
| 79 |
+
|
| 80 |
+
class RouterAgent:
|
| 81 |
+
def __call__(self, **kwargs):
|
| 82 |
+
agent_prompt_slash_code = re.findall(r"(\/[\w\-_]*)? *(.*)", kwargs.get('prompt'))
|
| 83 |
+
uri = agent_prompt_slash_code[0][0] or "/explain"
|
| 84 |
+
prompt = agent_prompt_slash_code[0][1]
|
| 85 |
+
if (memory:=kwargs.get("memory")) and len(memory)>1:
|
| 86 |
+
prompt = f"{memory}\n\nLAST_QUESTION:\n{prompt}"
|
| 87 |
+
print(f'{uri = }', f'{prompt = }')
|
| 88 |
+
|
| 89 |
+
if uri == "/propose_project_meeting":
|
| 90 |
+
prompt = st.session_state.status_table
|
| 91 |
+
elif uri in ["/transcript_qa", ]:
|
| 92 |
+
return APIS["/transcript_qa"](TranscriptRequest(
|
| 93 |
+
prompt=prompt,
|
| 94 |
+
tasks = [str(task) for task in st.session_state.tasks],
|
| 95 |
+
transcript = st.session_state.transcript
|
| 96 |
+
))
|
| 97 |
+
elif uri in ["/update_tasks_from_transcript", ]:
|
| 98 |
+
result = APIS["/update_tasks_from_transcript"](UpdateTasksFromTranscriptRequest(
|
| 99 |
+
table = st.session_state.status_table,
|
| 100 |
+
transcript = st.session_state.transcript
|
| 101 |
+
))
|
| 102 |
+
result = json.loads(crea_json(result))
|
| 103 |
+
st.session_state.tasks = result["tasks"]
|
| 104 |
+
st.session_state.assignments = result["assignments"]
|
| 105 |
+
# st.session_state.status_table = pd.DataFrame(result).to_json(orient = "records")
|
| 106 |
+
st.rerun()
|
| 107 |
+
return result
|
| 108 |
+
|
| 109 |
+
func_to_call = APIS.get(uri, explain_agents)
|
| 110 |
+
response = func_to_call(PromptRequest(prompt = prompt))
|
| 111 |
+
# response = requests.post(os.getenv("BACKEND_URL", "http://127.0.0.1:5000") + f'{uri}', json = {"prompt" : prompt})
|
| 112 |
+
# if response.status_code != 200:
|
| 113 |
+
# st.error(f"Error: {response.text}")
|
| 114 |
+
# return
|
| 115 |
+
# response_json = response.json()
|
| 116 |
+
# print(f'{response_json = }')
|
| 117 |
+
return response
|
| 118 |
+
|
| 119 |
+
class ChatModelPage(BasePage):
|
| 120 |
+
def __init__(self):
|
| 121 |
+
super().__init__()
|
| 122 |
+
self.model = None
|
| 123 |
+
self.router_agent = RouterAgent()
|
| 124 |
+
|
| 125 |
+
def render(self):
|
| 126 |
+
st.title("Dashboard")
|
| 127 |
+
self.sidebar_elements()
|
| 128 |
+
self.sidebar_chat_elements()
|
| 129 |
+
self.upfront_page()
|
| 130 |
+
self.init_messages()
|
| 131 |
+
self.reset_messages_sidebar_button()
|
| 132 |
+
st.divider()
|
| 133 |
+
# Display chat messages from history on app rerun
|
| 134 |
+
for message in st.session_state.messages:
|
| 135 |
+
with st.chat_message(message["role"]):
|
| 136 |
+
st.markdown(message["content"])
|
| 137 |
+
# React to user input
|
| 138 |
+
if prompt := st.chat_input("type 'commands' or 'explain' to see the available commands"):
|
| 139 |
+
# Display user message in chat message container
|
| 140 |
+
st.chat_message("user").markdown(prompt)
|
| 141 |
+
# Add user message to chat history
|
| 142 |
+
st.session_state.messages.append({"role": "user", "content": prompt})
|
| 143 |
+
response = self.get_agent_response(prompt = prompt, memory = st.session_state.messages)
|
| 144 |
+
# Display assistant response in chat message container
|
| 145 |
+
with st.chat_message("assistant"):st.markdown(response)
|
| 146 |
+
# Add assistant response to chat history
|
| 147 |
+
st.session_state.messages.append({"role": "assistant", "content": response})
|
| 148 |
+
|
| 149 |
+
def init_messages(self):
|
| 150 |
+
# Initialize chat history
|
| 151 |
+
if "messages" not in st.session_state:
|
| 152 |
+
st.session_state.messages = []
|
| 153 |
+
|
| 154 |
+
def get_agent_response(self, **kwargs):
|
| 155 |
+
def format_memory(messages):
|
| 156 |
+
"""
|
| 157 |
+
Transforms a list of message dictionaries into a formatted string.
|
| 158 |
+
|
| 159 |
+
Args:
|
| 160 |
+
messages (list): A list of dictionaries, each with keys "role" and "content".
|
| 161 |
+
|
| 162 |
+
Returns:
|
| 163 |
+
str: A formatted string where each message is preceded by its role.
|
| 164 |
+
"""
|
| 165 |
+
formatted_lines = []
|
| 166 |
+
for msg in messages:
|
| 167 |
+
role = msg.get("role", "").capitalize() # Ensures "user" -> "User", "assistant" -> "Assistant"
|
| 168 |
+
content = msg.get("content", "")
|
| 169 |
+
formatted_lines.append(f"{role}:\n{content}")
|
| 170 |
+
# Join each message block with an extra newline between conversations.
|
| 171 |
+
return "\n\n".join(formatted_lines)
|
| 172 |
+
# Get agent response
|
| 173 |
+
memory = kwargs.get("memory", "")
|
| 174 |
+
if memory:
|
| 175 |
+
kwargs["memory"] = format_memory(memory)
|
| 176 |
+
return self.router_agent(prompt = kwargs.get("prompt", ""), memory = memory)
|
| 177 |
+
|
| 178 |
+
def reset_messages_sidebar_button(self):
|
| 179 |
+
with st.sidebar:
|
| 180 |
+
# Reset chat history
|
| 181 |
+
if st.button("Reset chat"):
|
| 182 |
+
st.session_state.messages = []
|
| 183 |
+
|
| 184 |
+
def sidebar_chat_elements(self):
|
| 185 |
+
with st.sidebar:
|
| 186 |
+
# tabs_and_dialogs_cols = st.columns(2)
|
| 187 |
+
# with tabs_and_dialogs_cols[0]:
|
| 188 |
+
with stylable_container(
|
| 189 |
+
key = "create-call-button",
|
| 190 |
+
css_styles = f"""
|
| 191 |
+
button {{
|
| 192 |
+
background-image: url("data:image/png;base64,{get_base64_image("static/NewCallButton.png")}");
|
| 193 |
+
background-size: cover;
|
| 194 |
+
background-repeat: no-repeat;
|
| 195 |
+
background-position: center;
|
| 196 |
+
background-color: transparent;
|
| 197 |
+
border: none;
|
| 198 |
+
width: 298px;
|
| 199 |
+
height: 164px;
|
| 200 |
+
}}"""
|
| 201 |
+
):
|
| 202 |
+
clicked_create_call_button = st.button("", key = "create-call-button")
|
| 203 |
+
if clicked_create_call_button:create_call_modal()
|
| 204 |
+
# with stylable_container(
|
| 205 |
+
# key = "upload-call-button",
|
| 206 |
+
# css_styles = f"""
|
| 207 |
+
# button {{
|
| 208 |
+
# background-image: url("data:image/png;base64,{get_base64_image("static/UploadCallButton.png")}");
|
| 209 |
+
# background-size: cover;
|
| 210 |
+
# background-repeat: no-repeat;
|
| 211 |
+
# background-position: center;
|
| 212 |
+
# background-color: transparent;
|
| 213 |
+
# border: none;
|
| 214 |
+
# width: 298px;
|
| 215 |
+
# height: 164px;
|
| 216 |
+
# }}"""
|
| 217 |
+
# ):
|
| 218 |
+
# clicked_create_call_button = st.button('', key='upload-call-button')
|
| 219 |
+
# if clicked_create_call_button:upload_call_modal_and_actions()
|
| 220 |
+
upload_call_and_actions()
|
| 221 |
+
# upload_call_and_actions()
|
| 222 |
+
# with tabs_and_dialogs_cols[1]:
|
| 223 |
+
|
| 224 |
+
def upfront_page(self):
|
| 225 |
+
tabs = st.tabs(["Tasks", "Assignments"])
|
| 226 |
+
func_of_tabs = [ self.tasks, self.assignments ]
|
| 227 |
+
for tab, func in zip(tabs, func_of_tabs):
|
| 228 |
+
with tab:func()
|
| 229 |
+
|
| 230 |
+
def sidebar_elements(self):
|
| 231 |
+
actions = {"Project" : self.projects, "Team" : self.team }
|
| 232 |
+
with st.sidebar:
|
| 233 |
+
with st.expander("Project"):
|
| 234 |
+
for idx, (label, action) in enumerate(actions.items(), 1):
|
| 235 |
+
st.header(label)
|
| 236 |
+
action()
|
| 237 |
+
if idx < len(actions):st.divider()
|
| 238 |
+
|
| 239 |
+
def team(self):
|
| 240 |
+
for member in st.session_state.team:
|
| 241 |
+
st.write(member)
|
| 242 |
+
# st.image("static/2.png")
|
| 243 |
+
|
| 244 |
+
def tasks(self):
|
| 245 |
+
tasks = st.session_state.tasks
|
| 246 |
+
assignments : dict = st.session_state.assignments
|
| 247 |
+
tasks_by_person = {}
|
| 248 |
+
if not tasks and not assignments:
|
| 249 |
+
df = pd.DataFrame(columns = ["Task", "Team Member", "status", "start_date", "end_date"])
|
| 250 |
+
st.session_state.status_table = df.to_json(orient = "records")
|
| 251 |
+
st.write("No tasks or assignments")
|
| 252 |
+
return
|
| 253 |
+
df = pd.DataFrame(tasks)
|
| 254 |
+
df.rename(columns = {"name" : "Task"}, inplace = True)
|
| 255 |
+
for person, tasks in assignments.items():
|
| 256 |
+
for task in tasks:
|
| 257 |
+
if task not in tasks_by_person:
|
| 258 |
+
tasks_by_person[task] = []
|
| 259 |
+
tasks_by_person[task].append(person)
|
| 260 |
+
tasks_by_person = [{ "Task" : task , "Team Member" : ", ".join(people) } for task, people in tasks_by_person.items()]
|
| 261 |
+
df_tasks_persons = pd.DataFrame(tasks_by_person)
|
| 262 |
+
df = pd.merge(df, df_tasks_persons, how = "inner")
|
| 263 |
+
st.session_state.status_table = df.to_json(orient = "records")
|
| 264 |
+
st.dataframe(df)
|
| 265 |
+
|
| 266 |
+
def assignments(self):
|
| 267 |
+
assignments = st.session_state.assignments
|
| 268 |
+
df = pd.DataFrame([{"Member" : member, "Tasks": ", ".join(
|
| 269 |
+
map(lambda task : (
|
| 270 |
+
f":rainbow[{task}]"
|
| 271 |
+
), tasks))
|
| 272 |
+
} for member, tasks in assignments.items()])
|
| 273 |
+
st.table(df)
|
| 274 |
+
|
| 275 |
+
def projects(self):
|
| 276 |
+
st.subheader("IBM Granite Hackaton")
|
| 277 |
+
|
| 278 |
+
# def choose_agent(self):
|
| 279 |
+
# # Choose agent
|
| 280 |
+
# class RouterAgent:
|
| 281 |
+
# def __call__(self, **kwargs):
|
| 282 |
+
# agent_prompt_slash_code = re.findall(r"(\/[\w\-_]*) *(.*)", kwargs.get('prompt'))
|
| 283 |
+
# uri = agent_prompt_slash_code[0][0]
|
| 284 |
+
# prompt = agent_prompt_slash_code[0][1]
|
| 285 |
+
# response = requests.post(os.getenv("BACKEND_URL") + f'/{uri}', json = {"prompt" : prompt})
|
| 286 |
+
# if response.status_code != 200:
|
| 287 |
+
# st.error(f"Error: {response.text}")
|
| 288 |
+
# return
|
| 289 |
+
# return response.json()
|
| 290 |
+
# # agents = {
|
| 291 |
+
# # "Dummy Agent": {
|
| 292 |
+
# # "_class" : RouterAgent,
|
| 293 |
+
# # },
|
| 294 |
+
# # "Meeting Scheduler Agent": {
|
| 295 |
+
# # "_class" : DummyAgent,
|
| 296 |
+
# # }
|
| 297 |
+
# # }
|
| 298 |
+
# # agent_selected = st.sidebar.selectbox("Select agent", list(agents.keys()))
|
| 299 |
+
# # agent_init_js = agents[agent_selected]
|
| 300 |
+
# RouterAgent()
|
| 301 |
+
# agent_class = agent_init_js.pop("_class")
|
| 302 |
+
# return agent_class(**agent_init_js)
|
APIModules/APIs/ConfigPage.py
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
from datetime import datetime
|
| 3 |
+
|
| 4 |
+
from . import os, st
|
| 5 |
+
from .BasePage import BasePage
|
| 6 |
+
from APIModules.ObjectModels.Task import Task
|
| 7 |
+
|
| 8 |
+
class ConfigPage(BasePage):
|
| 9 |
+
|
| 10 |
+
def render(self):
|
| 11 |
+
super().render(keys={"WATSON_API_KEY": os.getenv("WATSON_API_KEY")})
|
| 12 |
+
initial_state_from_json : dict = st.session_state.get("INITIAL_STATE", {})
|
| 13 |
+
if st.button("use_preloaded_state"):
|
| 14 |
+
with open("initial_state.json") as f:
|
| 15 |
+
initial_state_from_json : dict = json.load(f)
|
| 16 |
+
st.session_state.INITIAL_STATE = initial_state_from_json
|
| 17 |
+
st.rerun()
|
| 18 |
+
initial_team_members = st.session_state.get('team')
|
| 19 |
+
if not initial_team_members:st.session_state.team = initial_state_from_json.get('team', ["Alessio", "Joy", "Nicola C", "Nicola D", "Dragosh"])
|
| 20 |
+
initial_tasks = st.session_state.get('tasks')
|
| 21 |
+
if not initial_tasks:st.session_state.tasks = [Task(**task).model_dump() for task in initial_state_from_json.get('tasks', [])]
|
| 22 |
+
initial_assignments = st.session_state.get('assignments')
|
| 23 |
+
if not initial_tasks:st.session_state.assignments = initial_state_from_json.get('assignments', {})
|
| 24 |
+
|
| 25 |
+
with st.expander("Set Team", expanded=True):
|
| 26 |
+
st.header("Configurazione")
|
| 27 |
+
new_member = st.text_input("Enter team name", value="Team name")
|
| 28 |
+
if st.button("Add team member"):
|
| 29 |
+
st.session_state.team.append(new_member)
|
| 30 |
+
with st.expander("Tasks", expanded=True):
|
| 31 |
+
st.header("Task")
|
| 32 |
+
new_task_name = st.text_input("Enter task name", value="Task name")
|
| 33 |
+
new_task_description = st.text_area("Enter task description", value="Task description")
|
| 34 |
+
new_task_start_date = st.date_input("Enter task start date", value=datetime.today())
|
| 35 |
+
new_task_end_date = st.date_input("Enter task end date", value=datetime.today())
|
| 36 |
+
if st.button("Add Task"):
|
| 37 |
+
new_task = Task(
|
| 38 |
+
name=new_task_name, description=new_task_description,
|
| 39 |
+
start_date=new_task_start_date, end_date=new_task_end_date
|
| 40 |
+
)
|
| 41 |
+
st.session_state.tasks.append(new_task.model_dump())
|
| 42 |
+
with st.expander("Assing Tasks", expanded=True):
|
| 43 |
+
st.header("Assing Tasks")
|
| 44 |
+
cols = st.columns(2)
|
| 45 |
+
with cols[0]:_team_member = st.selectbox("Team Member", st.session_state.team)
|
| 46 |
+
_tasks_to_assing = st.multiselect("Task", [task['name'] for task in st.session_state.tasks])
|
| 47 |
+
task_indices = [i for i, task in enumerate(st.session_state.tasks) if task['name'] in _tasks_to_assing]
|
| 48 |
+
tasks_selected = [task["name"] for i, task in enumerate(st.session_state.tasks) if i in task_indices]
|
| 49 |
+
if st.button("Assing Task"):
|
| 50 |
+
st.session_state.assignments[_team_member] = tasks_selected
|
APIModules/APIs/Dashboard.py
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from . import os, st, stylable_container
|
| 2 |
+
from .BasePage import BasePage
|
| 3 |
+
|
| 4 |
+
from .modals import get_base64_image, create_call_modal, upload_call_modal_and_actions
|
| 5 |
+
|
| 6 |
+
class Dashboard(BasePage):
|
| 7 |
+
def render(self):
|
| 8 |
+
st.title("Dashboard")
|
| 9 |
+
self.sidebar_elements()
|
| 10 |
+
self.upfront_page()
|
| 11 |
+
|
| 12 |
+
def upfront_page(self):
|
| 13 |
+
with st.sidebar:
|
| 14 |
+
tabs_and_dialogs_cols = st.columns(2)
|
| 15 |
+
with tabs_and_dialogs_cols[0]:
|
| 16 |
+
with stylable_container(
|
| 17 |
+
key = "create-call-button",
|
| 18 |
+
# css_styles = f"""
|
| 19 |
+
# button {{
|
| 20 |
+
# background-image: url("data:image/png;base64,{get_base64_image("static/NewCallButton.png")}");
|
| 21 |
+
# background-size: cover;
|
| 22 |
+
# background-repeat: no-repeat;
|
| 23 |
+
# background-position: center;
|
| 24 |
+
# background-color: transparent;
|
| 25 |
+
# border: none;
|
| 26 |
+
# width: 298px;
|
| 27 |
+
# height: 164px;
|
| 28 |
+
# }}"""
|
| 29 |
+
):
|
| 30 |
+
clicked_create_call_button = st.button("create-call-button", key = "create-call-button")
|
| 31 |
+
if clicked_create_call_button:create_call_modal()
|
| 32 |
+
with stylable_container(
|
| 33 |
+
key = "upload-call-button",
|
| 34 |
+
# css_styles = f"""
|
| 35 |
+
# button {{
|
| 36 |
+
# background-image: url("data:image/png;base64,{get_base64_image("static/UploadCallButton.png")}");
|
| 37 |
+
# background-size: cover;
|
| 38 |
+
# background-repeat: no-repeat;
|
| 39 |
+
# background-position: center;
|
| 40 |
+
# background-color: transparent;
|
| 41 |
+
# border: none;
|
| 42 |
+
# width: 298px;
|
| 43 |
+
# height: 164px;
|
| 44 |
+
# }}"""
|
| 45 |
+
):
|
| 46 |
+
clicked_create_call_button = st.button('upload-call-button', key='upload-call-button')
|
| 47 |
+
if clicked_create_call_button:upload_call_modal_and_actions()
|
| 48 |
+
with tabs_and_dialogs_cols[1]:
|
| 49 |
+
tabs = st.tabs(["Tasks"])
|
| 50 |
+
func_of_tabs = [ self.tasks ]
|
| 51 |
+
for tab, func in zip(tabs, func_of_tabs):
|
| 52 |
+
with tab:func()
|
| 53 |
+
|
| 54 |
+
def sidebar_elements(self):
|
| 55 |
+
actions = {"Project" : self.projects, "Team" : self.team }
|
| 56 |
+
with st.sidebar:
|
| 57 |
+
for label, action in actions.items():
|
| 58 |
+
st.header(label)
|
| 59 |
+
action()
|
| 60 |
+
st.divider()
|
| 61 |
+
|
| 62 |
+
def team(self):
|
| 63 |
+
teams_per_project = {"Projects": "Team1", "Projects2": "Team2", "Projects3": "Team3"}
|
| 64 |
+
st.write(teams_per_project[st.session_state.selected_project])
|
| 65 |
+
# st.image("static/2.png")
|
| 66 |
+
|
| 67 |
+
def tasks(self):
|
| 68 |
+
st.write("Tasks")
|
| 69 |
+
|
| 70 |
+
def projects(self):
|
| 71 |
+
st.header("IBM-Granite-Hackaton")
|
| 72 |
+
st.divider()
|
APIModules/APIs/__init__.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import streamlit as st
|
| 2 |
+
import requests
|
| 3 |
+
import re
|
| 4 |
+
import json
|
| 5 |
+
import os
|
| 6 |
+
import base64
|
| 7 |
+
import pandas as pd
|
| 8 |
+
from abc import ABC, abstractmethod
|
| 9 |
+
from functools import partial
|
| 10 |
+
from streamlit_calendar import calendar
|
| 11 |
+
from streamlit_extras.stylable_container import stylable_container
|
| 12 |
+
from APIModules.APIs.agents_router import (
|
| 13 |
+
PromptRequest,
|
| 14 |
+
TranscriptRequest,
|
| 15 |
+
UpdateTasksFromTranscriptRequest,
|
| 16 |
+
explain_agents,
|
| 17 |
+
APIS
|
| 18 |
+
)
|
APIModules/APIs/load_css.py
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from . import st
|
| 2 |
+
|
| 3 |
+
def render_css(file_path):
|
| 4 |
+
with open(file_path, 'r') as file:
|
| 5 |
+
css_content = file.read()
|
| 6 |
+
st.markdown(f'<style>{css_content}</style>', unsafe_allow_html=True)
|
| 7 |
+
|
| 8 |
+
# Example usage
|
| 9 |
+
if __name__ == "__main__":
|
| 10 |
+
render_css('path_to_your_css_file.css')
|
APIModules/APIs/modals.py
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import random
|
| 2 |
+
import docx
|
| 3 |
+
from PyPDF2 import PdfFileReader
|
| 4 |
+
import io
|
| 5 |
+
|
| 6 |
+
from . import st, base64, os, requests, partial, PromptRequest, APIS
|
| 7 |
+
from .load_css import render_css
|
| 8 |
+
|
| 9 |
+
st.markdown(f'<style></style>', unsafe_allow_html=True)
|
| 10 |
+
|
| 11 |
+
def get_base64_image(image_path : str):
|
| 12 |
+
with open(image_path, "rb") as image_file:
|
| 13 |
+
return base64.b64encode(image_file.read()).decode()
|
| 14 |
+
|
| 15 |
+
@st.dialog("Create a Call")
|
| 16 |
+
def create_call_modal():
|
| 17 |
+
team_members_selected = st.multiselect("People", options = st.session_state.team)
|
| 18 |
+
tasks_selected = st.multiselect("Tasks", options = filter(lambda task: task['status'] not in ["to be started", "finished"], st.session_state.tasks))
|
| 19 |
+
prompt = st.text_area("Prompt", value = "\n\n".join(
|
| 20 |
+
[
|
| 21 |
+
"\n".join(["Team Members:", *map(lambda x : f'\t{x}', team_members_selected)]),
|
| 22 |
+
"\n".join(["Tasks:", *[str(task) for task in tasks_selected]]),
|
| 23 |
+
]
|
| 24 |
+
))
|
| 25 |
+
buttons = [
|
| 26 |
+
("submit", lambda prompt : APIS['/call'](PromptRequest(prompt = prompt))),
|
| 27 |
+
]
|
| 28 |
+
cols = st.columns(len(buttons))
|
| 29 |
+
for col, button in zip(cols, buttons):
|
| 30 |
+
with col:
|
| 31 |
+
if st.button(button[0]):
|
| 32 |
+
button[1](prompt)
|
| 33 |
+
|
| 34 |
+
@st.dialog("Upload a Call")
|
| 35 |
+
def upload_call_modal_and_actions():
|
| 36 |
+
uploaded_file = st.file_uploader(
|
| 37 |
+
# "Import a videocall recording or its transcripts",
|
| 38 |
+
"Import a videocall recording transcript",
|
| 39 |
+
type=['.txt', '.doc', '.docx', '.pdf'],
|
| 40 |
+
# type=['.txt', '.doc', '.docx', '.pdf', '.mp4', '.wav', '.flac'],
|
| 41 |
+
accept_multiple_files=False
|
| 42 |
+
)
|
| 43 |
+
transcript_text = get_transcript_text(uploaded_file) if uploaded_file else ""
|
| 44 |
+
buttons = {
|
| 45 |
+
# "Crea minuta": minuta_action_from_uploaded_call,
|
| 46 |
+
# "Genera trascritto": transcript_action_from_uploaded_call,
|
| 47 |
+
"QA from transcripts": partial(qa_from_transcripts, Transcript = transcript_text),
|
| 48 |
+
"Generate Tasks from transcript": partial(create_tasks_action_from_uploaded_call_transcript, Transcript = transcript_text),
|
| 49 |
+
}
|
| 50 |
+
_ = buttons[st.selectbox("Choose an action", buttons.keys())]()
|
| 51 |
+
|
| 52 |
+
def upload_call_and_actions():
|
| 53 |
+
uploaded_file = st.file_uploader(
|
| 54 |
+
# "Import a videocall recording or its transcripts",
|
| 55 |
+
"Import a videocall recording transcript",
|
| 56 |
+
type=['.txt', '.doc', '.docx', '.pdf'],
|
| 57 |
+
# type=['.txt', '.doc', '.docx', '.pdf', '.mp4', '.wav', '.flac'],
|
| 58 |
+
accept_multiple_files=False
|
| 59 |
+
)
|
| 60 |
+
transcript_text = get_transcript_text(uploaded_file) if uploaded_file else ""
|
| 61 |
+
st.session_state.transcript = transcript_text
|
| 62 |
+
if os.getenv("DEBUG_MODE", True):
|
| 63 |
+
with st.expander("Transcript"):
|
| 64 |
+
st.text_input("", value = transcript_text)
|
| 65 |
+
|
| 66 |
+
def minuta_action_from_uploaded_call():
|
| 67 |
+
if st.button("Crea minuta"):
|
| 68 |
+
st.download_button("Scarica minuta", data = "moc_minuta.txt")
|
| 69 |
+
|
| 70 |
+
def transcript_action_from_uploaded_call():
|
| 71 |
+
if st.button("Crea transcript"):
|
| 72 |
+
st.download_button("Scarica transcript", data = "moc_transcript.txt")
|
| 73 |
+
|
| 74 |
+
def get_transcript_text(uploaded_file):
|
| 75 |
+
if uploaded_file is not None:
|
| 76 |
+
file_extension = os.path.splitext(uploaded_file.name)[1].lower()
|
| 77 |
+
if file_extension == '.txt':
|
| 78 |
+
return get_text_from_txt(uploaded_file)
|
| 79 |
+
elif file_extension in ['.doc', '.docx']:
|
| 80 |
+
return get_text_from_doc(uploaded_file)
|
| 81 |
+
elif file_extension == '.pdf':
|
| 82 |
+
return get_text_from_pdf(uploaded_file)
|
| 83 |
+
return ""
|
| 84 |
+
|
| 85 |
+
def get_text_from_txt(uploaded_file):
|
| 86 |
+
return uploaded_file.read().decode("utf-8")
|
| 87 |
+
|
| 88 |
+
def get_text_from_doc(uploaded_file):
|
| 89 |
+
doc = docx.Document(uploaded_file)
|
| 90 |
+
return "\n".join([para.text for para in doc.paragraphs])
|
| 91 |
+
|
| 92 |
+
def get_text_from_pdf(uploaded_file):
|
| 93 |
+
reader = PdfFileReader(io.BytesIO(uploaded_file.read()))
|
| 94 |
+
text = ""
|
| 95 |
+
for page_num in range(reader.getNumPages()):
|
| 96 |
+
text += reader.getPage(page_num).extract_text()
|
| 97 |
+
return text
|
| 98 |
+
|
| 99 |
+
|
| 100 |
+
def qa_from_transcripts(Transcript : str):
|
| 101 |
+
Prompt = st.text_area("Prompt")
|
| 102 |
+
Transcript = st.text_area("Transcript")
|
| 103 |
+
Tasks = "\n\n".join(st.multiselect("Tasks", options = ["Task 1", "Task 2", "Task 3"]))
|
| 104 |
+
if st.button("QA from transcripts"):
|
| 105 |
+
response = requests.post(os.getenv("QA_TRANSCRIPT_ENDPOINT", "http://127.0.0.1:5000/transcript") ,
|
| 106 |
+
json = {
|
| 107 |
+
"prompt": Prompt,
|
| 108 |
+
"transcript" : Transcript or "",
|
| 109 |
+
"tasks": Tasks or [],
|
| 110 |
+
})
|
| 111 |
+
st.success(response.json())
|
| 112 |
+
return
|
| 113 |
+
|
| 114 |
+
def create_tasks_action_from_uploaded_call_transcript(Transcript : str):
|
| 115 |
+
|
| 116 |
+
if st.button("create_tasks"):
|
| 117 |
+
response = requests.post(os.getenv("QA_TRANSCRIPT_ENDPOINT", "http://127.0.0.1:5000/tasks/from_transcript") , json = {"transcript" : Transcript})
|
| 118 |
+
st.session_state.tasks = st.success(response.json())
|
| 119 |
+
return
|
| 120 |
+
if st.session_state.get('tasks'):
|
| 121 |
+
task = st.selectbox("Select a task", st.session_state.tasks)
|
| 122 |
+
task_matchmaking(task)
|
| 123 |
+
|
| 124 |
+
def task_matchmaking(task : str):
|
| 125 |
+
cols = st.columns(2)
|
| 126 |
+
with cols[0]:
|
| 127 |
+
st.markdown("### Task matchmaking")
|
| 128 |
+
with cols[1]:
|
| 129 |
+
st.multiselect("People", ["Alice", "Bob", "Charlie"])
|
| 130 |
+
if st.button("Assign"):
|
| 131 |
+
st.success("Task assigned")
|