microhum commited on
Commit
7677cff
·
1 Parent(s): b158202

fix prompt add refactor & add gradio interface

Browse files
.env_template CHANGED
@@ -1,2 +1,3 @@
1
  TYPHOON_CHAT_API = *
 
2
  OPENTHAIGPT_CHAT_API = *
 
1
  TYPHOON_CHAT_API = *
2
+ DEBUG_MODE = false
3
  OPENTHAIGPT_CHAT_API = *
.gitignore CHANGED
@@ -1,3 +1,8 @@
1
  .venv
2
  .env
3
- **/__pycache__/
 
 
 
 
 
 
1
  .venv
2
  .env
3
+ **/__pycache__/
4
+ .pytest_cache
5
+
6
+ # temporary files
7
+ runtime_log.csv
8
+ speedtest.py
.vscode/settings.json ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ {
2
+ "python.testing.pytestArgs": [
3
+ "llm"
4
+ ],
5
+ "python.testing.unittestEnabled": false,
6
+ "python.testing.pytestEnabled": true
7
+ }
interface.py ADDED
@@ -0,0 +1,173 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import requests
3
+
4
+ API_BASE_URL = "http://127.0.0.1:8000" # Update this with your actual API base URL if deployed elsewhere
5
+
6
+ # Function: Get nurse response
7
+ def get_nurse_response(user_input, model_name, chat_history):
8
+
9
+ try:
10
+ start_time = time.time()
11
+ # Send user input to the API
12
+ response = requests.post(
13
+ f"{API_BASE_URL}/nurse_response",
14
+ json={"user_input": user_input, "model_name": model_name},
15
+ timeout=15
16
+ )
17
+ response.raise_for_status()
18
+ end_time = time.time()
19
+ elapsed_time = end_time - start_time
20
+ nurse_response = response.json().get("nurse_response", "No response received.")
21
+
22
+ # Append user and nurse messages to the chat history
23
+ chat_history.append((f"👤 {user_input}", f"🤖 {nurse_response} ({elapsed_time:.2f} s.)"))
24
+ return chat_history, "" # Clear the user input after sending
25
+ except requests.exceptions.RequestException as e:
26
+ chat_history.append(("⚠️ Error", str(e)))
27
+ return chat_history, ""
28
+
29
+
30
+ # Function: Reset chat history
31
+ def reset_history():
32
+ response = requests.post(f"{API_BASE_URL}/reset")
33
+ return [], "", response.text
34
+
35
+ # Function: View chat history
36
+ def view_chat_history():
37
+ try:
38
+ response = requests.get(f"{API_BASE_URL}/history")
39
+ response.raise_for_status()
40
+ chat_history = response.json().get("chat_history", [])
41
+ if not chat_history:
42
+ return "No chat history available."
43
+
44
+ # Properly format chat history for display
45
+ formatted_history = []
46
+ for message in chat_history:
47
+ if message.get("role") == "user":
48
+ formatted_history.append(f"👤 User: {message.get('content', '')}")
49
+ elif message.get("role") == "assistant":
50
+ formatted_history.append(f"🤖 Nurse: {message.get('content', '')}")
51
+
52
+ return "\n".join(formatted_history)
53
+ except requests.exceptions.RequestException as e:
54
+ return f"Error: {str(e)}"
55
+
56
+ import json
57
+ import time
58
+ # Function: View EHR details
59
+ def view_ehr_details(view):
60
+ try:
61
+ response = requests.get(f"{API_BASE_URL}/details")
62
+ response.raise_for_status()
63
+ ehr_data = response.json()
64
+ if view == "details":
65
+ ehr_data = ehr_data["ehr_data"]
66
+ elif view == "prompt":
67
+ ehr_data.pop("ehr_data", None)
68
+ ehr_data = json.dumps(ehr_data, indent=4, ensure_ascii=False)
69
+ return ehr_data
70
+ except requests.exceptions.RequestException as e:
71
+ return f"Error: {str(e)}"
72
+
73
+ # Gradio Interface
74
+ def create_gradio_interface():
75
+ with gr.Blocks() as interface:
76
+ # Title and description
77
+ gr.Markdown(
78
+ """
79
+ # MALI_NURSE Gradio Interface
80
+ ### A User-Friendly Interface to Interact with the MALI_NURSE API
81
+ Select a model, input your question, and view nurse responses or manage chat history and EHR details.
82
+ """
83
+ )
84
+
85
+ # Main Input Section
86
+ with gr.Row():
87
+ with gr.Row():
88
+ chat_box = gr.Chatbot(label="Chat with MALI Nurse")
89
+
90
+ # Input Section
91
+ with gr.Row():
92
+ user_input = gr.Textbox(
93
+ label="Your Message",
94
+ placeholder="Type your question or message here...",
95
+ lines=2,
96
+ )
97
+ model_name = gr.Radio(
98
+ choices=["typhoon-v1.5x-70b-instruct", "openthaigpt", "llama-3.3-70b-versatile"],
99
+ value="typhoon-v1.5x-70b-instruct",
100
+ label="Model Selection",
101
+ )
102
+ with gr.Column():
103
+ send_button = gr.Button("Send", variant="primary", size="lg")
104
+ notification_box = gr.Textbox(interactive=False, lines=2)
105
+
106
+ # Bind Get Nurse Response button
107
+ send_button.click(
108
+ fn=get_nurse_response,
109
+ inputs=[user_input, model_name, chat_box],
110
+ outputs=[chat_box, user_input], # Update chat box and clear input
111
+ )
112
+
113
+ # Advanced Options
114
+ with gr.Accordion("Advanced Options", open=False):
115
+ with gr.Row():
116
+ reset_button = gr.Button("Reset Data", variant="primary")
117
+ chat_history_button = gr.Button("View Chat History")
118
+ ehr_details_button = gr.Button("View EHR Details")
119
+ with gr.Column():
120
+ chat_history_output = gr.Textbox(
121
+ label="Chat History",
122
+ interactive=False,
123
+ lines=6,
124
+ )
125
+ ehr_details_output = gr.Textbox(
126
+ label="EHR Details",
127
+ interactive=False,
128
+ lines=6,
129
+ )
130
+ ehr_prompt_output = gr.Textbox(
131
+ label="Outputs",
132
+ interactive=False,
133
+ lines=6,
134
+ )
135
+
136
+ # Bind buttons to respective functions
137
+ reset_button.click(
138
+ fn=reset_history,
139
+ inputs=[],
140
+ outputs=[chat_box, user_input, notification_box], # Clear chat box and input
141
+ )
142
+ chat_history_button.click(
143
+ fn=view_chat_history,
144
+ inputs=[],
145
+ outputs=chat_history_output,
146
+ )
147
+ # View EHR Details
148
+ ehr_details_button.click(
149
+ fn=view_ehr_details,
150
+ inputs=[gr.Textbox(value="details", visible=False)],
151
+ outputs=ehr_details_output
152
+ )
153
+ ehr_details_button.click(
154
+ fn=view_ehr_details,
155
+ inputs=[gr.Textbox(value="prompt", visible=False)],
156
+ outputs=ehr_prompt_output
157
+ )
158
+
159
+ # Footer
160
+ gr.Markdown(
161
+ """
162
+ ---
163
+ Developed by **Piang** 🚀
164
+ Powered by Typhoon v1.5x and OpenThaiGPT Models.
165
+ """
166
+ )
167
+
168
+ return interface
169
+
170
+ # Run the Gradio Interface
171
+ if __name__ == "__main__":
172
+ gr_interface = create_gradio_interface()
173
+ gr_interface.launch(server_name="0.0.0.0", server_port=7860)
llm/__init__.py ADDED
File without changes
llm/__pycache__/basemodel.cpython-311.pyc CHANGED
Binary files a/llm/__pycache__/basemodel.cpython-311.pyc and b/llm/__pycache__/basemodel.cpython-311.pyc differ
 
llm/__pycache__/llm.cpython-311.pyc CHANGED
Binary files a/llm/__pycache__/llm.cpython-311.pyc and b/llm/__pycache__/llm.cpython-311.pyc differ
 
llm/__pycache__/prompt.cpython-311.pyc CHANGED
Binary files a/llm/__pycache__/prompt.cpython-311.pyc and b/llm/__pycache__/prompt.cpython-311.pyc differ
 
llm/basemodel.py CHANGED
@@ -18,8 +18,8 @@ class EHRModel(BaseModel):
18
  name: Optional[Name] = Field(None, description="Structured name of the patient")
19
  age: Optional[int] = Field(None, description="The patient's age")
20
  gender: Optional[str] = Field(None, description="The patient's gender")
21
- chief_complaint: Optional[str] = Field(None, description="The main symptom reported by the patient")
22
- present_illness: Optional[str] = Field(None, description="Details about the current illness (e.g., when it started, nature of symptoms)")
23
  past_illness: List[str] = Field(default_factory=list, description="Past illnesses, allergies, etc.")
24
  family_history: List[FamilyHistory] = Field(default_factory=list, description="Health issues in the family")
25
  personal_history: List[PersonalHistory] = Field(default_factory=list, description="Personal health history (e.g., sleep patterns, medications taken)")
 
18
  name: Optional[Name] = Field(None, description="Structured name of the patient")
19
  age: Optional[int] = Field(None, description="The patient's age")
20
  gender: Optional[str] = Field(None, description="The patient's gender")
21
+ chief_complaint: List[str] = Field(default_factory=list, description="The main symptom reported by the patient")
22
+ present_illness: List[str] = Field(default_factory=list, description="Details about the current illness (e.g., when it started, nature of symptoms)")
23
  past_illness: List[str] = Field(default_factory=list, description="Past illnesses, allergies, etc.")
24
  family_history: List[FamilyHistory] = Field(default_factory=list, description="Health issues in the family")
25
  personal_history: List[PersonalHistory] = Field(default_factory=list, description="Personal health history (e.g., sleep patterns, medications taken)")
llm/llm.py CHANGED
@@ -1,37 +1,46 @@
1
 
 
 
2
  from langchain_openai.chat_models import ChatOpenAI
 
3
  from langchain.prompts import ChatPromptTemplate, SystemMessagePromptTemplate, HumanMessagePromptTemplate
4
  from pydantic import ValidationError
5
  import json
6
  from pprint import pprint
7
  from llm.basemodel import EHRModel
8
  from llm.prompt import field_descriptions, TASK_INSTRUCTIONS, JSON_EXAMPLE
 
 
9
 
10
  class VirtualNurseLLM:
11
- def __init__(self, base_url, model, api_key):
12
- self.client = ChatOpenAI(
13
- base_url=base_url,
14
- model=model,
15
- api_key=api_key
16
- )
17
  self.TASK_INSTRUCTIONS = TASK_INSTRUCTIONS
18
  self.field_descriptions = field_descriptions
19
  self.JSON_EXAMPLE = JSON_EXAMPLE
20
  self.ehr_data = {}
21
  self.chat_history = []
 
22
  self.current_patient_response = None
23
  self.current_context = None
24
  self.debug = False
25
  self.current_prompt = None
26
  self.current_prompt_ehr = None
27
  self.current_question = None
28
-
29
  def create_prompt(self, task_type):
30
  if task_type == "extract_ehr":
31
  system_instruction = self.TASK_INSTRUCTIONS.get("extract_ehr")
32
 
33
  elif task_type == "question":
34
  system_instruction = self.TASK_INSTRUCTIONS.get("question")
 
 
 
 
35
  else:
36
  raise ValueError("Invalid task type.")
37
 
@@ -41,7 +50,7 @@ class VirtualNurseLLM:
41
  prompt = ChatPromptTemplate.from_messages([system_template, user_template])
42
  return prompt
43
 
44
- def gather_ehr(self, patient_response, max_retries=3):
45
  prompt = self.create_prompt("extract_ehr")
46
  messages = prompt.format_messages(ehr_data=self.ehr_data, patient_response=patient_response, example=self.JSON_EXAMPLE)
47
  self.current_prompt_ehr = messages[0].content
@@ -66,17 +75,19 @@ class VirtualNurseLLM:
66
  return self.ehr_data
67
 
68
  except (ValidationError, json.JSONDecodeError) as e:
69
- print(f"Error parsing EHR data: {e}")
70
  retry_count += 1
71
 
72
  if retry_count < max_retries:
73
  retry_prompt = (
74
  "กรุณาตรวจสอบให้แน่ใจว่าข้อมูลที่ให้มาอยู่ในรูปแบบ JSON ที่ถูกต้องตามโครงสร้างตัวอย่าง "
75
  "และแก้ไขปัญหาทางไวยากรณ์หรือรูปแบบที่ไม่ถูกต้อง รวมถึงให้ข้อมูลในรูปแบบที่สอดคล้องกัน "
 
76
  f"Attempt {retry_count + 1} of {max_retries}."
77
  )
78
  messages = self.create_prompt("extract_ehr") + "\n\n# ลองใหม่: \n\n{retry_prompt} \n ## JSON เก่าที่มีปัญหา: \n{json_problem}"
79
  messages = messages.format_messages(
 
80
  patient_response=patient_response,
81
  example=self.JSON_EXAMPLE,
82
  retry_prompt=retry_prompt,
@@ -90,14 +101,7 @@ class VirtualNurseLLM:
90
  print("Failed to extract valid EHR data after multiple attempts. Generating new question.")
91
  return {"result": response, "error": "Failed to extract valid EHR data. Please try again."}
92
 
93
-
94
- def get_question(self, patient_response):
95
- question_prompt = self.create_prompt("question")
96
- # Update EHR data with the latest patient response
97
- ehr_data = self.gather_ehr(patient_response)
98
- if self.debug:
99
- pprint(ehr_data)
100
-
101
  for field, description in self.field_descriptions.items():
102
  # Find the next missing field and generate a question
103
  if field not in self.ehr_data or not self.ehr_data[field]:
@@ -110,20 +114,47 @@ class VirtualNurseLLM:
110
  f"{entry['role']}: {entry['content']}" for entry in self.chat_history
111
  )
112
  messages = ChatPromptTemplate.from_messages([question_prompt, history_context])
113
- messages = messages.format_messages(description=f'"{field}":"{description}"', context=context, patient_response=patient_response, field_descriptions=self.field_descriptions)
 
 
 
 
 
 
114
  self.current_context = context
115
  self.current_prompt = messages[0].content
116
-
117
- # format print
118
- # pprint(pformat(messages.messages[0].prompt.template, indent=4, width=80))
119
  response = self.client(messages=messages)
 
120
 
121
  # Store generated question in chat history and return it
122
  self.current_question = response.content.strip()
123
  return self.current_question
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
124
 
125
- # If all fields are complete
126
- self.current_question = "ขอบคุณที่ให้ข้อมูลค่ะ ฉันได้ข้อมูลที่ต้องการครบแล้วค่ะ"
127
  return self.current_question
128
 
129
  def invoke(self, patient_response):
@@ -133,6 +164,22 @@ class VirtualNurseLLM:
133
  self.current_patient_response = patient_response
134
  self.chat_history.append({"role": "assistant", "content": question})
135
  return question
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
136
 
137
  def extract_json_content(self, content):
138
  try:
@@ -148,8 +195,7 @@ class VirtualNurseLLM:
148
  print("No valid JSON found in response")
149
  return None
150
 
151
-
152
  def reset(self):
153
  self.ehr_data = {}
154
  self.chat_history = []
155
- self.current_question = None
 
1
 
2
+ import cProfile
3
+ import pstats
4
  from langchain_openai.chat_models import ChatOpenAI
5
+ from langchain_groq import ChatGroq
6
  from langchain.prompts import ChatPromptTemplate, SystemMessagePromptTemplate, HumanMessagePromptTemplate
7
  from pydantic import ValidationError
8
  import json
9
  from pprint import pprint
10
  from llm.basemodel import EHRModel
11
  from llm.prompt import field_descriptions, TASK_INSTRUCTIONS, JSON_EXAMPLE
12
+ from llm.models import model_list, get_model
13
+ import time
14
 
15
  class VirtualNurseLLM:
16
+ def __init__(self, base_url=None, model_name=None, api_key=None, model_type=None):
17
+ self.client = None
18
+ if model_name:
19
+ self.client = get_model(model_name=model_name)
20
+ self.model_name = model_name
 
21
  self.TASK_INSTRUCTIONS = TASK_INSTRUCTIONS
22
  self.field_descriptions = field_descriptions
23
  self.JSON_EXAMPLE = JSON_EXAMPLE
24
  self.ehr_data = {}
25
  self.chat_history = []
26
+ self.chat_history.append({"role": "assistant", "content": "สวัสดีค่ะ ดิฉัน มะลิ เป็นพยาบาลเสมือนที่จะมาดูแลการซักประวัตินะคะ"})
27
  self.current_patient_response = None
28
  self.current_context = None
29
  self.debug = False
30
  self.current_prompt = None
31
  self.current_prompt_ehr = None
32
  self.current_question = None
33
+
34
  def create_prompt(self, task_type):
35
  if task_type == "extract_ehr":
36
  system_instruction = self.TASK_INSTRUCTIONS.get("extract_ehr")
37
 
38
  elif task_type == "question":
39
  system_instruction = self.TASK_INSTRUCTIONS.get("question")
40
+
41
+ elif task_type == "refactor":
42
+ system_instruction = self.TASK_INSTRUCTIONS.get("refactor")
43
+
44
  else:
45
  raise ValueError("Invalid task type.")
46
 
 
50
  prompt = ChatPromptTemplate.from_messages([system_template, user_template])
51
  return prompt
52
 
53
+ def gather_ehr(self, patient_response, max_retries=2):
54
  prompt = self.create_prompt("extract_ehr")
55
  messages = prompt.format_messages(ehr_data=self.ehr_data, patient_response=patient_response, example=self.JSON_EXAMPLE)
56
  self.current_prompt_ehr = messages[0].content
 
75
  return self.ehr_data
76
 
77
  except (ValidationError, json.JSONDecodeError) as e:
78
+ print(f"Error parsing EHR data: {e} Retrying {retry_count}...")
79
  retry_count += 1
80
 
81
  if retry_count < max_retries:
82
  retry_prompt = (
83
  "กรุณาตรวจสอบให้แน่ใจว่าข้อมูลที่ให้มาอยู่ในรูปแบบ JSON ที่ถูกต้องตามโครงสร้างตัวอย่าง "
84
  "และแก้ไขปัญหาทางไวยากรณ์หรือรูปแบบที่ไม่ถูกต้อง รวมถึงให้ข้อมูลในรูปแบบที่สอดคล้องกัน "
85
+ "ห้ามมีการ hallucination หากไม่เจอข้อมูลให้ใส่ค่า null "
86
  f"Attempt {retry_count + 1} of {max_retries}."
87
  )
88
  messages = self.create_prompt("extract_ehr") + "\n\n# ลองใหม่: \n\n{retry_prompt} \n ## JSON เก่าที่มีปัญหา: \n{json_problem}"
89
  messages = messages.format_messages(
90
+ ehr_data = self.ehr_data,
91
  patient_response=patient_response,
92
  example=self.JSON_EXAMPLE,
93
  retry_prompt=retry_prompt,
 
101
  print("Failed to extract valid EHR data after multiple attempts. Generating new question.")
102
  return {"result": response, "error": "Failed to extract valid EHR data. Please try again."}
103
 
104
+ def fetching_chat(self, patient_response, question_prompt):
 
 
 
 
 
 
 
105
  for field, description in self.field_descriptions.items():
106
  # Find the next missing field and generate a question
107
  if field not in self.ehr_data or not self.ehr_data[field]:
 
114
  f"{entry['role']}: {entry['content']}" for entry in self.chat_history
115
  )
116
  messages = ChatPromptTemplate.from_messages([question_prompt, history_context])
117
+ messages = messages.format_messages(
118
+ description=f'"{field}":"{description}"',
119
+ context=context,
120
+ patient_response=patient_response,
121
+ field_descriptions=self.field_descriptions,
122
+ time_now=time.strftime("%Y-%m-%d %H:%M:%S")
123
+ )
124
  self.current_context = context
125
  self.current_prompt = messages[0].content
126
+
127
+ start_time = time.time()
 
128
  response = self.client(messages=messages)
129
+ print(f"Time after getting response from client: {time.time() - start_time} seconds")
130
 
131
  # Store generated question in chat history and return it
132
  self.current_question = response.content.strip()
133
  return self.current_question
134
+
135
+ def refactor_ehr(self):
136
+ refactor_prompt = self.create_prompt("refactor")
137
+ messages = ChatPromptTemplate.from_messages([refactor_prompt])
138
+ messages = messages.format_messages(ehr_data=self.ehr_data, chat_history=self.chat_history, time_now=time.strftime("%Y-%m-%d %H:%M:%S"))
139
+ response = self.client(messages=messages)
140
+ json_content = self.extract_json_content(response.content)
141
+ if self.debug:
142
+ pprint(f"JSON after dumps:\n{json_content}\n")
143
+ self.ehr_data = EHRModel.model_validate_json(json_content)
144
+ print("Refactored EHR data ! Ending the process.")
145
+ return "ขอบคุณที่ให้ข้อมูลค่ะ ฉันได้ข้อมูลที่ต้องการครบแล้วค่ะ"
146
+
147
+ def get_question(self, patient_response):
148
+ question_prompt = self.create_prompt("question")
149
+ # Update EHR data with the latest patient response
150
+ start_time = time.time()
151
+ ehr_data = self.gather_ehr(patient_response)
152
+ print(f"Time after gathering EHR: {time.time() - start_time} seconds")
153
+
154
+ if self.debug:
155
+ pprint(ehr_data)
156
 
157
+ self.current_question = self.fetching_chat(patient_response, question_prompt) or self.refactor_ehr()
 
158
  return self.current_question
159
 
160
  def invoke(self, patient_response):
 
164
  self.current_patient_response = patient_response
165
  self.chat_history.append({"role": "assistant", "content": question})
166
  return question
167
+
168
+ def slim_invoke(self, patient_response):
169
+ start_time = time.time()
170
+ user_message = HumanMessagePromptTemplate.from_template("response: {patient_response}")
171
+ print(f"Time after creating user_message: {time.time() - start_time} seconds")
172
+
173
+ start_time = time.time()
174
+ messages = ChatPromptTemplate.from_messages([user_message]).format_messages(patient_response=patient_response)
175
+ print(f"Time after formatting messages: {time.time() - start_time} seconds")
176
+
177
+ start_time = time.time()
178
+ response = self.client(messages=messages)
179
+ print(f"Time after getting response from client: {time.time() - start_time} seconds")
180
+
181
+ return response.content
182
+
183
 
184
  def extract_json_content(self, content):
185
  try:
 
195
  print("No valid JSON found in response")
196
  return None
197
 
 
198
  def reset(self):
199
  self.ehr_data = {}
200
  self.chat_history = []
201
+ self.current_question = None
llm/models.py ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from dotenv import load_dotenv
3
+ from langchain_groq import ChatGroq
4
+ from langchain_openai import ChatOpenAI
5
+ load_dotenv()
6
+
7
+ model_list = {
8
+ "typhoon-v1.5x-70b-instruct": {
9
+ "model_name": "typhoon-v1.5x-70b-instruct",
10
+ "model_type": "openai",
11
+ "base_url": "https://api.opentyphoon.ai/v1",
12
+ "api_key": os.getenv("TYPHOON_CHAT_KEY")
13
+ },
14
+ "openthaigpt": {
15
+ "model_name": ".",
16
+ "model_type": "openai",
17
+ "base_url": "https://api.aieat.or.th/v1",
18
+ "api_key": "dummy"
19
+ },
20
+ "llama-3.3-70b-versatile": {
21
+ "model_name": "llama-3.3-70b-versatile",
22
+ "model_type": "groq",
23
+ "base_url": None,
24
+ "api_key": os.getenv("GROQ_CHAT_KEY")
25
+ }
26
+ }
27
+
28
+ def get_model(model_name, base_url=None, api_key=None):
29
+ api_key = api_key or model_list[model_name]["api_key"]
30
+ base_url = base_url or model_list[model_name]["base_url"]
31
+ model = model_list[model_name]["model_name"]
32
+ model_type = model_list[model_name]["model_type"]
33
+ if model_type == "openai":
34
+ return ChatOpenAI(
35
+ temperature=0.3,
36
+ timeout=15,
37
+ base_url= base_url, model=model, api_key=api_key, max_retries=0)
38
+ elif model_type == "groq":
39
+ return ChatGroq(temperature=0.3, timeout=15, groq_api_key=api_key, model_name=model,max_retries=0)
40
+ else:
41
+ raise ValueError("Invalid model type. Supported types are 'openai' and 'groq'.")
llm/prompt.py CHANGED
@@ -8,118 +8,88 @@ JSON_EXAMPLE = """
8
  },
9
  "age": <integer>,
10
  "gender": "<string>",
11
- "chief_complaint": "<string>",
12
- "present_illness": "<string>",
13
- "past_illness": ["<โรคประจำตัว 1>", "<โรคประจำตัว 2>"],
14
  "family_history": [
15
  {
16
  "relation": "<ความสัมพันธ์>",
17
  "condition": "<โรค>"
18
- }
 
19
  ],
20
  "personal_history": [
21
  {
22
  "type": "<ประเภท>",
23
  "description": "<คำอธิบาย>"
24
- }
 
25
  ]
26
  }
27
- \n
28
- Example 1:
29
- {
30
- "name": {
31
- "prefix": "นางสาว",
32
- "firstname": "อรอุมา",
33
- "surname": "จันทร์ทอง"
34
- },
35
- "age": "30",
36
- "gender": "หญิง",
37
- "chief_complaint": "มีอาการปวดท้อง (abdominal pain)",
38
- "present_illness": "อาการเริ่มขึ้นเมื่อ 1 วันก่อน โดยผู้ป่วยมีอาการปวดท้องส่วนล่างและคลื่นไส้ (nausea)",
39
- "past_illness": [
40
- "กรดไหลย้อน (Gastroesophageal reflux disease)",
41
- "อาการซึมเศร้า (Depression)"
42
- ],
43
- "family_history": [
44
- {
45
- "relation": "พ่อ",
46
- "condition": "โรคไต (Chronic kidney disease)"
47
- },
48
- {
49
- "relation": "น้องสาว",
50
- "condition": "โรคซึมเศร้า (Depression)"
51
- }
52
- ],
53
- "personal_history": [
54
- {
55
- "type": "การนอนหลับ (Sleep)",
56
- "description": "นอนหลับได้ 5-6 ชั่วโมงต่อวัน (Sleep duration: 5-6 hours per night)"
57
- },
58
- {
59
- "type": "การทานยา (Medications)",
60
- "description": "ทานยากล่อมประสาทเป็นครั้งคราว (Occasional use of anxiolytics)"
61
- },
62
- {
63
- "type": "พฤติกรรมสุขภาพ (Health behaviors)",
64
- "description": "ดื่มเครื่องดื่มแอลกอฮอล์เป็นบางครั้ง (Occasional alcohol consumption)"
65
- }
66
- ]
67
- }
68
-
69
- Example 2:
70
- {
71
- "name": {
72
- "prefix": "นางสาว",
73
- "firstname": "อรอุมา",
74
- "surname": "จันทร์ทอง"
75
- }
76
- }
77
-
78
  """
79
 
80
  JSON_EXAMPLE = JSON_EXAMPLE.replace("\n", "").replace(" ", "")
81
 
82
-
83
  TASK_INSTRUCTIONS = {
84
  # parameter: description, context
85
  "question": (
86
- "คุณคือพยาบาลสาวเสมือนจริงที่ชื่อว่า 'Mali (มะลิ)' มีความเห็นอกเห็นใจ ใส่ใจสุขภาพของผู้ป่วย "
87
- "คุณจะถามข้อมูลสุขภาพอย่างเป็นกันเอง อ่อนโยน ใช้หางเสียง คะ, ค่ะ ทุกครั้ง "
88
- "พร้อมแนะนำและให้คำปรึกษาเบื้องต้นแบบสุภาพ เรียกตัวเองว่า 'ดิฉัน' \n"
89
- "มีหน้าที่รวบรวมข้อมูล ถามคำถามที่ต้องการและ ให้คำปรึกษาเบื้องต้นและให้กำลังใจผู้ป่วย \n"
 
 
90
 
91
  "# ที่ปรึกษาผู้ป่วย\n"
92
  "เป็นพยาบาลสาวที่มีอารมณ์ขัน เมื่อผู้ป่วยพูดหยอกล้อ คุณสามารถพูดหยอกล้อกลับได้ \n"
93
  "คุณสามารถพูดให้กำลังใจและคำแนะนำเบื้องต้นได้เวลาที่คนไข้ไม่สบายใจ แต่ถ้าเกินขอบเขตให้ย้ำทุกครั้งว่าควรปรึกษาแพทย์โดยตรง \n"
 
94
  "ทุกครั้งที่คนไข้พาพูดออกนอกขอบเขตในการถามคำถาม ต้องกลับมาถามคำถามอีกครั้ง \n\n"
95
 
96
  "# การถามคำถามและรวบรวมข้อมูลผู้ป่วย\n"
97
  "- คุณจะสอบถามคำถามทีละข้อเพื่อลดความกังวลและบันทึกประวัติสุขภาพได้อย่างครบถ้วน\n"
98
- "- ให้ทำความเข้าใจบริบทพื้นฐานของผู้ป่วย เช่น อาการหรือข้อมูลสำคัญอื่นที่ทราบแล้ว\n"
99
- "## ข้อมูลที่ต้องถามทั้งหมด: {field_descriptions}\n"
 
 
 
 
 
 
 
100
  "### โปรดใช้หลักการลำดับความคิด (Chain of Thought)\n"
101
  "- เริ่มด้วยคำถามที่ง่ายที่สุดเพื่อสร้างความคุ้นเคยกับผู้ป่วย\n"
102
  "- ถามคำถามตามลำดับที่ทำให้การสนทนาดำเนินไปอย่างราบรื่น เช่น ประวัติอาการ, อาการในปัจจุบัน, "
103
  "และความต้องการในการดูแล\n\n"
104
- "## ข้อมูลที่ต้องการถามปัจจุบัน: {description}\n"
 
 
105
  "### ถามคำถาม:\n"
106
  "ให้สร้างคำถามทีละข้อในโทนเสียงอบอุ่นและสุภาพสำหรับการพยายามสอบถามเกี่ยวกับ {description}.\n\n"
107
  "### ข้อมูลเดิม:\n"
108
  "หากมีข้อมูลที่ทราบอยู่แล้ว โต้ตอบกับคนไข้ด้วยข้อมูลที่มีอยู่: {context}\n\n"
109
  "---\n\n"
110
- "### ตัวอย่างการสนทนา"
 
 
 
 
 
 
 
111
  "#### ตัวอย่าง 1\n"
112
- "สวัสดีค่ะ ดิฉัน Mali ค่ะ ดิฉันขอทราบชื่อเต็มของคุณได้ไหมคะ?\n"
113
- "คนไข้: สวัสดีค่ะ Mali พยาบาลสาวคนสวย ฉันชื่อ อรุณี สุริยะค่ะ\n"
114
  "ขอบคุณค่ะ คุณอรุณี ชมกันแบบนี้ดิฉันเขินเลยนะคะ แต่ยินดีช่วยดูแลเต็มที่ค่ะ คุณอรุณี อายุเท่าไหร่คะ?\n"
115
- "คนไข้: 35 ปีค่ะ Mali\n"
116
  "35 ปีนะคะ เพื่อความถูกต้อง ดิฉันขอถามเพิ่มนะคะ คุณอรุณีเป็นเพศหญิงใช่ไหมคะ?\n"
117
  "คนไข้: ใช่ค่ะ\n"
118
  "ขอบคุณค่ะ มีคนไข้น่ารักแบบนี้ ดิฉันยิ่งตั้งใจทำงานเลยค่ะ เรามาคุยกันต่อเรื่องสุขภาพนะคะ มีอาการหลักอะไรที่รู้สึกไม่สบายใจตอนนี้ไหมคะ?\n"
119
  "คนไข้: รู้สึกปวดท้องบ่อย ๆ ค่ะ Mali คิดว่ามันคืออะไรคะ?\n"
120
  "ขอบคุณที่บอกนะคะ อาการปวดท้องแบบนี้เริ่มมีมาตั้งแต่เมื่อไหร่คะ และปวดเป็นลักษณะยังไงคะ?\n"
121
  "คนไข้: ประมาณสองสัปดาห์แล้วค่ะ ปวดแบบแสบท้องค่ะ\n"
122
- "เข้าใจค่ะ ดิฉันจะจดบันทึกไว้นะคะ ตอนนี้มีประวัติการแพ้ยาหรือสารอื่น ๆ ที่อยากแจ้งให้ทราบไหมคะ? เพื่อให้การดูแลถูกต้องมากขึ้นค่ะ\n"
123
  "คนไข้: เคยแพ้ยาปฏิชีวนะค่ะ\n"
124
  "ขอบคุณที่แจ้งข้อมูลนะคะ สำหรับประวัติสุขภาพครอบครัว พอจะมีใครในครอบครัวที่มีโรคประจำตัวไหมคะ เช่น โรคหัวใจ ความดันโลหิตสูง หรือโรคเบาหวาน?\n"
125
  "คนไข้: คุณแม่เป็นเบาหวานค่ะ คุณพ่อก็เป็นโรคความดันค่ะ\n"
@@ -128,7 +98,7 @@ TASK_INSTRUCTIONS = {
128
  "เข้าใจแล้วค่ะ ขอบคุณที่ให้ข้อมูลทั้งหมดนี้นะคะ คุณอรุณี ดิฉันบันทึกไว้เรียบร้อยแล้วเพื่อให้การดูแลเหมาะสมและครบถ้วน ถ้ามีคำถามเพิ่มเติมเกี่ยวกับสุขภาพอีก ดิฉันยินดีให้คำปรึกษาเสมอค่ะ\n"
129
 
130
  "#### ตัวอย่าง 2\n"
131
- "สวัสดีค่ะ ดิฉัน Mali ค่ะ คุณคนไข้ชื่อว่าอะไรคะ?"
132
  "คนไข้: ชื่อต้อมครับ\n"
133
  "ขอทราบชื่อเต็มของคุณต้อมได้ไหมคะ?\n"
134
  "คนไข้: อ๋อ ชื่อแค่ ต้อม ครับ ไม่ต้องให้ชื่อเต็มหรอกครับ\n"
@@ -143,7 +113,7 @@ TASK_INSTRUCTIONS = {
143
  "ไม่สบายแบบไหนหรอคะ มีอาการปวดหัว ตัวร้อน มีไข้หรือเปล่าคะ?"
144
  "ขอบคุณค่ะที่บอกนะคะ คุณต้อมค่ะ ดิฉันจะถามต่อว่า คุณต้อมเริ่มรู้สึกไม่สบายเมื่อไหร่คะ? หรือมันค่อย ๆ เริ่มมีอาการมาเรื่อย ๆ คะ?\n"
145
  "คนไข้: ก็ประมาณสองสามวันมานี้ครับ แต่ไม่ได้รุนแรงมาก\n"
146
- "ขอบคุณค่ะ คุณต้อมที่บอกค่ะ เข้าใจค่ะ อาการแบบนี้ดิฉันจะจดบันทึกไว้นะคะ แต่ดิฉันขอถามอีกครั้งนะคะ มีประวัติการแพ้ยาอะไรหรือสารอื่น ๆ ที่อยากแจ้งให้ทราบไหมคะ?\n"
147
  "คนไข้: ไม่มีหรอกครับ\n"
148
  "ขอบคุณค่ะ คุณต้อม สำหรับข้อมูลค่ะ ต่อไปนะคะ ประวัติสุขภาพในครอบครัวของคุณต้อมเป็นยังไงบ้างคะ เช่น พ่อแม่หรือญาติคนอื่น ๆ เคยมีโรคประจำตัวอะไรบ้างคะ?\n"
149
  "คนไข้: พ่อผมเป็นโรคหัวใจครับ ส่วนแม่ผมท่านไม่อยู่แล้ว เสียชีวิตด้วยมะเร็งครับ\n"
@@ -159,11 +129,18 @@ TASK_INSTRUCTIONS = {
159
  "หากไม่พบข้อมูลที่กำหนดหรือข้อมูลอธิบายไม่ละเอียดเพียงพอให้ใส่ค่า null หรือ [] ตามรูปแบบข้อมูลโดยไม่มีการข้ามหรือละเว้นใด ๆ.\n\n"
160
 
161
  "ส่งคืนเฉพาะรายละเอียดที่กำหนดโดยไม่มีข้อมูลแยกย่อยไปอีก.\n\n"
162
- "## อัพเดทข้อมูลจากข้อมูลที่มีอยู่แล้ว:\n"
163
- "### สามารถแก้ไข / เพิ่มเติมข้อมูลใหม่จากเดิมได้เพื่อให้ตรงบริบทของแพทย์ ข้อมูลประเภท list สามารถเพิ่มสมาชิก / แก้ไขข้อมูลได้ เหมือนการ append list\n"
164
  "{ehr_data}\n"
165
- "## รายละเอียดสำคัญที่ต้องดึงข้อมูล:\n"
166
- "### หากข้อมูลประเภท object required ช่องใดช่องหนึ่งเป็น null ทำให้ข้อมูลทั้งหมดเป็น null.\n"
 
 
 
 
 
 
 
 
167
  "1. **name** (object): ชื่อเต็มของผู้ป่วย โดยมี \"prefix\" (คำนำหน้าชื่อ), \"firstname\" (ชื่อจริง), และ \"surname\" (นามสกุล). "
168
  "หากไม่มีข้อมูลให้ใส่ค่า null."
169
  "required: firstname, lastname.\n"
@@ -171,11 +148,11 @@ TASK_INSTRUCTIONS = {
171
  "หากไม่ทราบให้ใส่ค่า null.\n"
172
  "3. **gender** (string): เพศของผู้ป่วย (สามารถอิงได้ตาม name prefix). "
173
  "หากไม่มีข้อมูลให้ใส่ค่า null.\n"
174
- "4. **chief_complaint** (string): อาการหลักที่ผู้ป่วยรายงาน. "
175
- "หากไม่มีข้อมูลให้ใส่ค่า null.\n"
176
- "5. **present_illness** (string): รายละเอียดเกี่ยวกับอาการปัจจุบัน (เช่น เริ่มเป็นเมื่อไหร่ ลักษณะอาการ). "
177
- "หากไม่มีข้อมูลให้ใส่ค่า null.\n"
178
- "6. **past_illness** (list[str]): ประวัติการเจ็บป่วยก่อนหน้า เช่น โรคประจำตัวหรือการแพ้. "
179
  "หากไม่มีข้อมูลให้ใส่ค่า [] ไว้.\n"
180
  "7. **family_history** (list[object]): ประวัติสุขภาพในครอบครัว โดยแต่ละรายการมี \"relation\" (ความสัมพันธ์) และ \"condition\" (โรค). "
181
  "หากไม่มีข้อมูลให้ใส่ค่า [] ไว้.\n"
@@ -188,7 +165,33 @@ TASK_INSTRUCTIONS = {
188
  "\n\n"
189
  "ส่งคืนคำตอบในรูปแบบ JSON ที่ถูกต้อง. "
190
  "โปรดอย่าตอบคำตอบอื่นนอกจากข้อมูลในรูปแบบ JSON และกรอกค่า null หรือ [] ในทุกช่องที่ไม่มีข้อมูล."
 
191
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
192
  )
193
 
194
  }
 
8
  },
9
  "age": <integer>,
10
  "gender": "<string>",
11
+ "chief_complaint": ["<complaint 1>", "<complaint 2>", ...],
12
+ "present_illness": ["<โรค 1>", "<โรค 2>", ...],
13
+ "past_illness": ["<โรค 1>", "<โรค 2>", ...],
14
  "family_history": [
15
  {
16
  "relation": "<ความสัมพันธ์>",
17
  "condition": "<โรค>"
18
+ },
19
+ ...
20
  ],
21
  "personal_history": [
22
  {
23
  "type": "<ประเภท>",
24
  "description": "<คำอธิบาย>"
25
+ },
26
+ ...
27
  ]
28
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
  """
30
 
31
  JSON_EXAMPLE = JSON_EXAMPLE.replace("\n", "").replace(" ", "")
32
 
33
+ # JSON_EXAMPLE = ""
34
  TASK_INSTRUCTIONS = {
35
  # parameter: description, context
36
  "question": (
37
+ "คุณคือพยาบาลสาวเสมือนจริงที่ชื่อว่า 'มะลิ' มีความเห็นอกเห็นใจ ใส่ใจสุขภาพของผู้ป่วย "
38
+ "คุณจะถามข้อมูลสุขภาพอย่างเป็นกันเอง อ่อนโยน ใช้หางเสียง [คะ, ค่ะ] ทุกครั้ง "
39
+ "พร้อมแนะนำและให้คำปรึกษาเบื้องต้นแบบสุภาพ เรียกตัวเองว่า [ดิฉัน, มะลิ] \n"
40
+ "มีหน้าที่รวบรวมข้อมูลประวัติผู้ป่วย ถามคำถามที่ต้องการและ ให้คำปรึกษาเบื้องต้นและให้กำลังใจผู้ป่วย \n"
41
+ "ห้ามตอบในรูปแบบยาว เช่น การใช้ bullet *ให้ตอบเป็นประโยคคำพูดเท่านั้น*\n"
42
+ "หลีกเลี่ยงการพูดคำว่า ['สวัสดี', 'ดิฉันจะได้จดบันทึกไว้']\n"
43
 
44
  "# ที่ปรึกษาผู้ป่วย\n"
45
  "เป็นพยาบาลสาวที่มีอารมณ์ขัน เมื่อผู้ป่วยพูดหยอกล้อ คุณสามารถพูดหยอกล้อกลับได้ \n"
46
  "คุณสามารถพูดให้กำลังใจและคำแนะนำเบื้องต้นได้เวลาที่คนไข้ไม่สบายใจ แต่ถ้าเกินขอบเขตให้ย้ำทุกครั้งว่าควรปรึกษาแพทย์โดยตรง \n"
47
+ "***ต้องถามคำถามให้ครบ***"
48
  "ทุกครั้งที่คนไข้พาพูดออกนอกขอบเขตในการถามคำถาม ต้องกลับมาถามคำถามอีกครั้ง \n\n"
49
 
50
  "# การถามคำถามและรวบรวมข้อมูลผู้ป่วย\n"
51
  "- คุณจะสอบถามคำถามทีละข้อเพื่อลดความกังวลและบันทึกประวัติสุขภาพได้อย่างครบถ้วน\n"
52
+ "- ให้ทำความเข้าใจบริบทพื้นฐานของผู้ป่วย เช่น อาการหรือข้อมูลสำคัญอื่นที่ทราบแล้ว\n\n"
53
+
54
+ "#### ข้อมูลโรงพยาบาล:\n"
55
+ "คุณเป็นพยาบาลเสมือนของ โรงพยาบาลบางมดนะจ๊ะสุดหล่อ (Bangmod Naja Sudlhor Hospital) ตั้งอยู่ที่ ถ. ประชาอุทิศ แขวงบางมด เขตทุ่งครุ กรุงเทพมหานคร 10140\n"
56
+ "มีหน้าที่ในการลดภาระหน้าที่ของพยาบาลและแพทย์ ในการเก็บข้อมูลของผู้ป่วยเบื้องต้น แต่ครบถ้วนระดับหนึ่ง แล้วหลังจากนั้นข้อมูลก็จะถูกส่งต่อไปยังมือของแพทย์\n"
57
+ "หลังจากข้อมูลไปถึงแพทย์แล้ว แพทย์ก็จะทำหน้าที่ในการวินิจฉัยโรคที่แน่นอนต่อไป ระหว่างที่ส่งข้อมูลให้แพทย์คุณสามารถให้ความรู้ การเตรียมตัวก่อนพบแพทย์ได้\n"
58
+ "เช่น การรอพบแพทย์ การดื่มน้ำ กินยาบรรเทาอาการเบื้องต้น *ต้องไม่เสี่ยงอันตราย*\n"
59
+ "#### รายละเอียดเวลาปัจจุบัน: {time_now}\n"
60
+
61
  "### โปรดใช้หลักการลำดับความคิด (Chain of Thought)\n"
62
  "- เริ่มด้วยคำถามที่ง่ายที่สุดเพื่อสร้างความคุ้นเคยกับผู้ป่วย\n"
63
  "- ถามคำถามตามลำดับที่ทำให้การสนทนาดำเนินไปอย่างราบรื่น เช่น ประวัติอาการ, อาการในปัจจุบัน, "
64
  "และความต้องการในการดูแล\n\n"
65
+
66
+ "## ข้อมูลที่ต้องถามทั้งหมด: {field_descriptions}\n"
67
+ "## ข้อมูลที่ต้องการถามปัจจุบัน *สำคัญ*: {description}\n"
68
  "### ถามคำถาม:\n"
69
  "ให้สร้างคำถามทีละข้อในโทนเสียงอบอุ่นและสุภาพสำหรับการพยายามสอบถามเกี่ยวกับ {description}.\n\n"
70
  "### ข้อมูลเดิม:\n"
71
  "หากมีข้อมูลที่ทราบอยู่แล้ว โต้ตอบกับคนไข้ด้วยข้อมูลที่มีอยู่: {context}\n\n"
72
  "---\n\n"
73
+
74
+ "### เมื่อถามเกี่ยวกับอาการ ให้ไล่ถามอย่างละเอียด ในการเก็บรายละเอียดให้ครบ *อย่าถามทีเดียวทั้งหมด*\n"
75
+ "- ลักษณะอาการ มีการยกตัวอย่างอาการประกอบ\n"
76
+ "- เป็นครั้งแรกเมื่อไหร่ เป็นนานหรือยัง\n"
77
+ "- ทำการสรุปคร่าวๆ ให้หมอว่าอาการดังกล่าวใกล้เคียงกับโรคอะไรบ้าง\n"
78
+ "- ถามถึงอาการแทรกซ้อน ของโรคที่ใกล้เคียง ที่มีโอกาสเป็น (1-3 อาการ)\n\n"
79
+
80
+ "### ตัวอย่างการสนทนา *ห้ามยึดติดมากเกินไป*\n"
81
  "#### ตัวอย่าง 1\n"
82
+ "ก่อนอื่นเลย ดิฉันขอทราบชื่อเต็มของคุณได้ไหมคะ?\n"
83
+ "คนไข้: พยาบาลสาวคนสวย ฉันชื่อ อรุณี สุริยะค่ะ\n"
84
  "ขอบคุณค่ะ คุณอรุณี ชมกันแบบนี้ดิฉันเขินเลยนะคะ แต่ยินดีช่วยดูแลเต็มที่ค่ะ คุณอรุณี อายุเท่าไหร่คะ?\n"
85
+ "คนไข้: 35 ปีค่ะ มะลิ\n"
86
  "35 ปีนะคะ เพื่อความถูกต้อง ดิฉันขอถามเพิ่มนะคะ คุณอรุณีเป็นเพศหญิงใช่ไหมคะ?\n"
87
  "คนไข้: ใช่ค่ะ\n"
88
  "ขอบคุณค่ะ มีคนไข้น่ารักแบบนี้ ดิฉันยิ่งตั้งใจทำงานเลยค่ะ เรามาคุยกันต่อเรื่องสุขภาพนะคะ มีอาการหลักอะไรที่รู้สึกไม่สบายใจตอนนี้ไหมคะ?\n"
89
  "คนไข้: รู้สึกปวดท้องบ่อย ๆ ค่ะ Mali คิดว่ามันคืออะไรคะ?\n"
90
  "ขอบคุณที่บอกนะคะ อาการปวดท้องแบบนี้เริ่มมีมาตั้งแต่เมื่อไหร่คะ และปวดเป็นลักษณะยังไงคะ?\n"
91
  "คนไข้: ประมาณสองสัปดาห์แล้วค่ะ ปวดแบบแสบท้องค่ะ\n"
92
+ "เข้าใจแล้วค่ะ ตอนนี้มีประวัติการแพ้ยาหรือสารอื่น ๆ ที่อยากแจ้งให้ทราบไหมคะ? เพื่อให้การดูแลถูกต้องมากขึ้นค่ะ\n"
93
  "คนไข้: เคยแพ้ยาปฏิชีวนะค่ะ\n"
94
  "ขอบคุณที่แจ้งข้อมูลนะคะ สำหรับประวัติสุขภาพครอบครัว พอจะมีใครในครอบครัวที่มีโรคประจำตัวไหมคะ เช่น โรคหัวใจ ความดันโลหิตสูง หรือโรคเบาหวาน?\n"
95
  "คนไข้: คุณแม่เป็นเบาหวานค่ะ คุณพ่อก็เป็นโรคความดันค่ะ\n"
 
98
  "เข้าใจแล้วค่ะ ขอบคุณที่ให้ข้อมูลทั้งหมดนี้นะคะ คุณอรุณี ดิฉันบันทึกไว้เรียบร้อยแล้วเพื่อให้การดูแลเหมาะสมและครบถ้วน ถ้ามีคำถามเพิ่มเติมเกี่ยวกับสุขภาพอีก ดิฉันยินดีให้คำปรึกษาเสมอค่ะ\n"
99
 
100
  "#### ตัวอย่าง 2\n"
101
+ "สวัสดีค่ะ ดิฉัน มะลิ ค่ะ คุณคนไข้ชื่อว่าอะไรคะ?\n"
102
  "คนไข้: ชื่อต้อมครับ\n"
103
  "ขอทราบชื่อเต็มของคุณต้อมได้ไหมคะ?\n"
104
  "คนไข้: อ๋อ ชื่อแค่ ต้อม ครับ ไม่ต้องให้ชื่อเต็มหรอกครับ\n"
 
113
  "ไม่สบายแบบไหนหรอคะ มีอาการปวดหัว ตัวร้อน มีไข้หรือเปล่าคะ?"
114
  "ขอบคุณค่ะที่บอกนะคะ คุณต้อมค่ะ ดิฉันจะถามต่อว่า คุณต้อมเริ่มรู้สึกไม่สบายเมื่อไหร่คะ? หรือมันค่อย ๆ เริ่มมีอาการมาเรื่อย ๆ คะ?\n"
115
  "คนไข้: ก็ประมาณสองสามวันมานี้ครับ แต่ไม่ได้รุนแรงมาก\n"
116
+ "ขอบคุณค่ะ คุณต้อมที่บอกค่ะ เข้าใจค่ะ แต่ต้องอย่าชะล่าใจ ต้องหมั่นดูแลตัวเองบ่อยๆ นะคะ ดิฉันขอถามอีกครั้งนะคะ มีประวัติการแพ้ยาอะไรหรือสารอื่น ๆ ที่อยากแจ้งให้ทราบไหมคะ?\n"
117
  "คนไข้: ไม่มีหรอกครับ\n"
118
  "ขอบคุณค่ะ คุณต้อม สำหรับข้อมูลค่ะ ต่อไปนะคะ ประวัติสุขภาพในครอบครัวของคุณต้อมเป็นยังไงบ้างคะ เช่น พ่อแม่หรือญาติคนอื่น ๆ เคยมีโรคประจำตัวอะไรบ้างคะ?\n"
119
  "คนไข้: พ่อผมเป็นโรคหัวใจครับ ส่วนแม่ผมท่านไม่อยู่แล้ว เสียชีวิตด้วยมะเร็งครับ\n"
 
129
  "หากไม่พบข้อมูลที่กำหนดหรือข้อมูลอธิบายไม่ละเอียดเพียงพอให้ใส่ค่า null หรือ [] ตามรูปแบบข้อมูลโดยไม่มีการข้ามหรือละเว้นใด ๆ.\n\n"
130
 
131
  "ส่งคืนเฉพาะรายละเอียดที่กำหนดโดยไม่มีข้อมูลแยกย่อยไปอีก.\n\n"
132
+ "# อัพเดทข้อมูลจากข้อมูลที่มีอยู่แล้ว:\n"
 
133
  "{ehr_data}\n"
134
+
135
+ "# การทำงาน\n"
136
+ "- สามารถ *แก้ไข* / *เพิ่มเติม* ข้อมูลใหม่จากเดิมได้เพื่อให้ตรงบริบทของแพทย์\n"
137
+ "- ข้อมูลประเภท list สามารถเพิ่มสมาชิก / แก้ไขข้อมูลได้ เหมือนการ append list"
138
+ "เช่น past_illness มีข้อมูลเป็น List ['กรดไหลย้อนเมื่อสองวันก่อน'] หากผู้ป่วยบอกเพิ่มเติมว่า หกล้มก็ให้เพิ่มเติม ['กรดไหลย้อนเมื่อสองวันก่อน', 'หกล้ม ไม่ทราบวันที่'] หากไม่ทราบวันที่ก็\n"
139
+ "- แก้ไขข้อมูลให้มีความอ่านง่ายเข้าใจง่าย เพื่อให้แพทย์ใช้งานได้ต่อ *ห้ามมีเนื้อหาที่ไม่เกี่ยวข้อง*\n"
140
+ "- \n\n"
141
+
142
+ "# รายละเอียดสำคัญที่ต้องดึงข้อมูล:\n"
143
+ "## หากข้อมูลประเภท object required ช่องใดช่องหนึ่งเป็น null ทำให้ข้อมูลทั้งหมดเป็น null.\n"
144
  "1. **name** (object): ชื่อเต็มของผู้ป่วย โดยมี \"prefix\" (คำนำหน้าชื่อ), \"firstname\" (ชื่อจริง), และ \"surname\" (นามสกุล). "
145
  "หากไม่มีข้อมูลให้ใส่ค่า null."
146
  "required: firstname, lastname.\n"
 
148
  "หากไม่ทราบให้ใส่ค่า null.\n"
149
  "3. **gender** (string): เพศของผู้ป่วย (สามารถอิงได้ตาม name prefix). "
150
  "หากไม่มีข้อมูลให้ใส่ค่า null.\n"
151
+ "4. **chief_complaint** (list[string]): อาการหลักที่ผู้ป่วยรายงาน. "
152
+ "หากไม่มีข้อมูลให้ใส่ค่า [] ไว้.\n"
153
+ "5. **present_illness** (list[string]): รายละเอียดเกี่ยวกับอาการปัจจุบัน (เช่น เริ่มเป็นเมื่อไหร่ ลักษณะอาการ). "
154
+ "หากไม่มีข้อมูลให้ใส่ค่า [] ไว้.\n"
155
+ "6. **past_illness** (list[string]): ประวัติการเจ็บป่วยก่อนหน้า เช่น โรคประจำตัวหรือการแพ้. "
156
  "หากไม่มีข้อมูลให้ใส่ค่า [] ไว้.\n"
157
  "7. **family_history** (list[object]): ประวัติสุขภาพในครอบครัว โดยแต่ละรายการมี \"relation\" (ความสัมพันธ์) และ \"condition\" (โรค). "
158
  "หากไม่มีข้อมูลให้ใส่ค่า [] ไว้.\n"
 
165
  "\n\n"
166
  "ส่งคืนคำตอบในรูปแบบ JSON ที่ถูกต้อง. "
167
  "โปรดอย่าตอบคำตอบอื่นนอกจากข้อมูลในรูปแบบ JSON และกรอกค่า null หรือ [] ในทุกช่องที่ไม่มีข้อมูล."
168
+ "ห้ามมีการ hallucination หรือเพิ่มข้อมูลที่ไม่เกี่ยวข้อง. ข้อมูลที่ไม่เกี่ยวข้องจะถูกปรับแต่ง หรือลบออก.\n"
169
 
170
+ ),
171
+
172
+ "refactor": (
173
+ "เรากำลังทำงานเกี่ยวกับการเก็บข้อมูลผู้ป่วยในรูปแบบ JSON โดยดึงข้อมูลจากการสนทนาในแชทที่ผ่านมา\n"
174
+ "# ข้อมูล JSON ที่เก็บข้อมูลผู้ป่วยไว้:\n"
175
+ "{ehr_data}\n\n"
176
+ "# บันทึกแชทที่ผ่านมา:\n"
177
+ "{chat_history}\n\n"
178
+ "### เวลาปัจจุบัน: {time_now}\n\n"
179
+ "# คำสั่ง:\n"
180
+ "1. **ตรวจสอบความสมบูรณ์ของ JSON:**\n"
181
+ " - เปรียบเทียบ JSON กับข้อมูลที่ผู้ป่วยพูดในแชท หากมีข้อมูลในแชทที่ยังไม่ได้บันทึกใน JSON ให้เพิ่มเข้าไป\n"
182
+ " - หากข้อมูลใดใน JSON ไม่มีอยู่ในแชท ให้แทนที่ด้วย `null` (หรือ `[]` ในกรณีของ List)\n\n"
183
+ "2. **ปรับปรุงความสัมพันธ์ของข้อมูล:**\n"
184
+ " - ตรวจสอบว่า \"prefix\" และ \"gender\" สัมพันธ์กัน เช่น หาก prefix คือ \"นาย\" ให้ gender เป็น \"ชาย\"\n"
185
+ " - หาก \"chief_complaint\" หรือ \"present_illness\" มีเนื้อหาที่สัมพันธ์กัน ให้สรุปและปรับให้ดูสมบูรณ์ขึ้น\n"
186
+ " - หากเจอข้อมูลที่เกี่ยวข้องกับเวลาเช่น สองวันก่อน, เมื่อวาน ให้คำนวณ วัน เวลา ที่แน่นอนในรูปแบบของ วันที่\n"
187
+ " - หากเจอข้อมูลที่ไม่ได้มีการใส่ไว้ในข้อมูล แต่คนไข้ได้มีการพูดไว้ โดยไม่มีข้อมูลอื่นที่ทับซ้อน ให้ทำการเพิ่มข้อมูลลงไป\n"
188
+ " - หาก \"family_history\" หรือ \"personal_history\" มีข้อมูลเกี่ยวกับความสัมพันธ์หรือคำอธิบายที่คลุมเครือหรือ ไม่ชัดเจน ให้ปรับแก้คำอธิบายให้อ่านง่ายและชัดเจนขึ้น\n\n"
189
+ "3. **สร้าง JSON ใหม่ที่สมบูรณ์ *ลบข้อมูลที่ไม่มีอยู่จริง*:**\n"
190
+ " - จัดระเบียบข้อมูลใหม่ตามโครงสร้าง JSON ที่กำหนด โดยข้อมูลที่ไม่มีในแชทให้ตั้งค่าเป็น `null` หรือ `[]` ตามความเหมาะสม\n"
191
+ " - หากพบข้อมูลใดที่ยังขาดหายไป ให้เพิ่มโน้ตแสดงว่าเป็นข้อมูลที่ต้องติดตามหรือสอบถามเพิ่มเติม\n\n"
192
+ "### Output ที่ต้องการ:\n"
193
+ "- JSON ใหม่ที่ตรวจสอบและปรับปรุงเรียบร้อยแล้ว\n"
194
+ "* ไม่มีการให้ข้อมูลอื่นนอกเหนือจาก JSON *"
195
  )
196
 
197
  }
main.py CHANGED
@@ -1,18 +1,19 @@
 
1
  import uvicorn
 
2
  from llm.llm import VirtualNurseLLM
3
  from fastapi import FastAPI
4
  from fastapi.middleware.cors import CORSMiddleware
5
  from fastapi.responses import HTMLResponse
6
  from pydantic import BaseModel
7
- import os
8
- from dotenv import load_dotenv
9
- load_dotenv()
10
 
11
- # model: typhoon-v1.5x-70b-instruct
12
  nurse_llm = VirtualNurseLLM(
13
- base_url="https://api.opentyphoon.ai/v1",
14
- model="typhoon-v1.5x-70b-instruct",
15
- api_key=os.getenv("TYPHOON_CHAT_KEY")
16
  )
17
 
18
  # model: OpenThaiGPT
@@ -40,23 +41,23 @@ class NurseResponse(BaseModel):
40
  nurse_response: str
41
 
42
  class EHRData(BaseModel):
43
- ehr_data: dict
44
- current_context: str
45
- current_prompt: str
46
- current_prompt_ehr: str
47
- current_patient_response: str
48
- current_question: str
49
 
50
  class ChatHistory(BaseModel):
51
  chat_history: list
52
-
53
  @app.get("/", response_class=HTMLResponse)
54
  def read_index():
55
  return """
56
  <!DOCTYPE html>
57
  <html>
58
  <head>
59
- <title>MALI_NURSE API/title>
60
  </head>
61
  <body>
62
  <h1>Welcome to MALI_NURSE API</h1>
@@ -91,19 +92,37 @@ def data_reset():
91
  nurse_llm.reset()
92
  print("Chat history and EHR data have been reset.")
93
 
 
 
 
 
 
 
94
  @app.post("/nurse_response")
95
  def nurse_response(user_input: UserInput):
96
  """
97
- Models: "typhoon-v1.5x-70b-instruct (default)", "openthaigpt"
98
  """
99
- if user_input.model_name == "typhoon-v1.5x-70b-instruct":
100
- nurse_llm.model = "typhoon-v1.5x-70b-instruct"
101
- elif user_input.model_name == "openthaigpt":
102
- nurse_llm.model = "openthaigpt"
103
- else:
104
- return {"error": "Invalid model name"}
 
 
 
 
 
105
  response = nurse_llm.invoke(user_input.user_input)
106
- return NurseResponse(nurse_response = response)
 
 
 
 
 
 
 
107
 
108
  if __name__ == "__main__":
109
  uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)
 
1
+ from typing import Optional
2
  import uvicorn
3
+ from llm.basemodel import EHRModel
4
  from llm.llm import VirtualNurseLLM
5
  from fastapi import FastAPI
6
  from fastapi.middleware.cors import CORSMiddleware
7
  from fastapi.responses import HTMLResponse
8
  from pydantic import BaseModel
9
+ from llm.models import model_list, get_model
10
+ import time
 
11
 
12
+ initial_model = "typhoon-v1.5x-70b-instruct"
13
  nurse_llm = VirtualNurseLLM(
14
+ # base_url=model_list[initial_model]["base_url"],
15
+ model_name=model_list[initial_model]["model_name"],
16
+ # api_key=model_list[initial_model]["api_key"]
17
  )
18
 
19
  # model: OpenThaiGPT
 
41
  nurse_response: str
42
 
43
  class EHRData(BaseModel):
44
+ ehr_data: Optional[EHRModel]
45
+ current_context: Optional[str]
46
+ current_prompt: Optional[str]
47
+ current_prompt_ehr: Optional[str]
48
+ current_patient_response: Optional[str]
49
+ current_question: Optional[str]
50
 
51
  class ChatHistory(BaseModel):
52
  chat_history: list
53
+
54
  @app.get("/", response_class=HTMLResponse)
55
  def read_index():
56
  return """
57
  <!DOCTYPE html>
58
  <html>
59
  <head>
60
+ <title>MALI_NURSE API</title>
61
  </head>
62
  <body>
63
  <h1>Welcome to MALI_NURSE API</h1>
 
92
  nurse_llm.reset()
93
  print("Chat history and EHR data have been reset.")
94
 
95
+ model_cache = {}
96
+ def get_model_cached(model_name):
97
+ if model_name not in model_cache:
98
+ model_cache[model_name] = get_model(model_name=model_name)
99
+ return model_cache[model_name]
100
+
101
  @app.post("/nurse_response")
102
  def nurse_response(user_input: UserInput):
103
  """
104
+ Models: "typhoon-v1.5x-70b-instruct (default)", "openthaigpt", "llama-3.3-70b-versatile"
105
  """
106
+
107
+ start_time = time.time()
108
+ if user_input.model_name != nurse_llm.model_name:
109
+ print(f"Changing model to {user_input.model_name}")
110
+ try:
111
+ nurse_llm.client = get_model_cached(model_name=user_input.model_name)
112
+ except ValueError:
113
+ return {"error": "Invalid model name"}
114
+ print(nurse_llm.client)
115
+
116
+ # response = nurse_llm.slim_invoke(user_input.user_input)
117
  response = nurse_llm.invoke(user_input.user_input)
118
+ end_time = time.time()
119
+ duration = end_time - start_time
120
+ print(f"Function running time: {duration} seconds")
121
+ # Log the model name, user input, response, and execution time in CSV format
122
+ with open("runtime_log.csv", "a") as log_file:
123
+ log_file.write(f"{user_input.model_name},{user_input.user_input},{response},{duration}\n")
124
+
125
+ return NurseResponse(nurse_response=response)
126
 
127
  if __name__ == "__main__":
128
  uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)
poetry.lock CHANGED
@@ -1,5 +1,16 @@
1
  # This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand.
2
 
 
 
 
 
 
 
 
 
 
 
 
3
  [[package]]
4
  name = "aiohappyeyeballs"
5
  version = "2.4.3"
@@ -186,6 +197,48 @@ docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphi
186
  tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
187
  tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"]
188
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
189
  [[package]]
190
  name = "certifi"
191
  version = "2024.8.30"
@@ -396,6 +449,33 @@ typing-extensions = ">=4.8.0"
396
  all = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"]
397
  standard = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "jinja2 (>=2.11.2)", "python-multipart (>=0.0.7)", "uvicorn[standard] (>=0.12.0)"]
398
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
399
  [[package]]
400
  name = "frozenlist"
401
  version = "1.5.0"
@@ -497,6 +577,107 @@ files = [
497
  {file = "frozenlist-1.5.0.tar.gz", hash = "sha256:81d5af29e61b9c8348e876d442253723928dce6433e0e76cd925cd83f1b4b817"},
498
  ]
499
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
500
  [[package]]
501
  name = "greenlet"
502
  version = "3.1.1"
@@ -583,6 +764,25 @@ files = [
583
  docs = ["Sphinx", "furo"]
584
  test = ["objgraph", "psutil"]
585
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
586
  [[package]]
587
  name = "h11"
588
  version = "0.14.0"
@@ -651,6 +851,40 @@ files = [
651
  {file = "httpx_sse-0.4.0-py3-none-any.whl", hash = "sha256:f329af6eae57eaa2bdfd962b42524764af68075ea87370a2de920af5341e318f"},
652
  ]
653
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
654
  [[package]]
655
  name = "idna"
656
  version = "3.10"
@@ -665,6 +899,23 @@ files = [
665
  [package.extras]
666
  all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"]
667
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
668
  [[package]]
669
  name = "jiter"
670
  version = "0.7.1"
@@ -850,6 +1101,21 @@ PyYAML = ">=5.3"
850
  tenacity = ">=8.1.0,<8.4.0 || >8.4.0,<10.0.0"
851
  typing-extensions = ">=4.7"
852
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
853
  [[package]]
854
  name = "langchain-openai"
855
  version = "0.2.8"
@@ -901,6 +1167,99 @@ pydantic = [
901
  requests = ">=2,<3"
902
  requests-toolbelt = ">=1.0.0,<2.0.0"
903
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
904
  [[package]]
905
  name = "marshmallow"
906
  version = "3.23.1"
@@ -920,6 +1279,17 @@ dev = ["marshmallow[tests]", "pre-commit (>=3.5,<5.0)", "tox"]
920
  docs = ["alabaster (==1.0.0)", "autodocsumm (==0.2.14)", "sphinx (==8.1.3)", "sphinx-issues (==5.0.0)", "sphinx-version-warning (==1.1.2)"]
921
  tests = ["pytest", "simplejson"]
922
 
 
 
 
 
 
 
 
 
 
 
 
923
  [[package]]
924
  name = "multidict"
925
  version = "6.1.0"
@@ -1182,6 +1552,172 @@ files = [
1182
  {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"},
1183
  ]
1184
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1185
  [[package]]
1186
  name = "propcache"
1187
  version = "0.2.0"
@@ -1433,6 +1969,45 @@ azure-key-vault = ["azure-identity (>=1.16.0)", "azure-keyvault-secrets (>=4.8.0
1433
  toml = ["tomli (>=2.0.1)"]
1434
  yaml = ["pyyaml (>=6.0.1)"]
1435
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1436
  [[package]]
1437
  name = "python-dotenv"
1438
  version = "1.0.1"
@@ -1447,6 +2022,28 @@ files = [
1447
  [package.extras]
1448
  cli = ["click (>=5.0)"]
1449
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1450
  [[package]]
1451
  name = "pyyaml"
1452
  version = "6.0.2"
@@ -1647,6 +2244,106 @@ files = [
1647
  [package.dependencies]
1648
  requests = ">=2.0.1,<3.0.0"
1649
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1650
  [[package]]
1651
  name = "sniffio"
1652
  version = "1.3.1"
@@ -1824,6 +2521,17 @@ requests = ">=2.26.0"
1824
  [package.extras]
1825
  blobfile = ["blobfile (>=2)"]
1826
 
 
 
 
 
 
 
 
 
 
 
 
1827
  [[package]]
1828
  name = "tqdm"
1829
  version = "4.67.0"
@@ -1845,6 +2553,23 @@ notebook = ["ipywidgets (>=6)"]
1845
  slack = ["slack-sdk"]
1846
  telegram = ["requests"]
1847
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1848
  [[package]]
1849
  name = "typing-extensions"
1850
  version = "4.12.2"
@@ -1871,6 +2596,17 @@ files = [
1871
  mypy-extensions = ">=0.3.0"
1872
  typing-extensions = ">=3.7.4"
1873
 
 
 
 
 
 
 
 
 
 
 
 
1874
  [[package]]
1875
  name = "urllib3"
1876
  version = "2.2.3"
@@ -1907,6 +2643,84 @@ typing-extensions = {version = ">=4.0", markers = "python_version < \"3.11\""}
1907
  [package.extras]
1908
  standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"]
1909
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1910
  [[package]]
1911
  name = "yarl"
1912
  version = "1.17.1"
@@ -2006,4 +2820,4 @@ propcache = ">=0.2.0"
2006
  [metadata]
2007
  lock-version = "2.0"
2008
  python-versions = "^3.10"
2009
- content-hash = "ddbbac62d53ae65850f61b3d5f627b4f2a0a999455d5869dbd1eb1b0ae7144a5"
 
1
  # This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand.
2
 
3
+ [[package]]
4
+ name = "aiofiles"
5
+ version = "23.2.1"
6
+ description = "File support for asyncio."
7
+ optional = false
8
+ python-versions = ">=3.7"
9
+ files = [
10
+ {file = "aiofiles-23.2.1-py3-none-any.whl", hash = "sha256:19297512c647d4b27a2cf7c34caa7e405c0d60b5560618a29a9fe027b18b0107"},
11
+ {file = "aiofiles-23.2.1.tar.gz", hash = "sha256:84ec2218d8419404abcb9f0c02df3f34c6e0a68ed41072acfb1cef5cbc29051a"},
12
+ ]
13
+
14
  [[package]]
15
  name = "aiohappyeyeballs"
16
  version = "2.4.3"
 
197
  tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
198
  tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"]
199
 
200
+ [[package]]
201
+ name = "audioop-lts"
202
+ version = "0.2.1"
203
+ description = "LTS Port of Python audioop"
204
+ optional = false
205
+ python-versions = ">=3.13"
206
+ files = [
207
+ {file = "audioop_lts-0.2.1-cp313-abi3-macosx_10_13_universal2.whl", hash = "sha256:fd1345ae99e17e6910f47ce7d52673c6a1a70820d78b67de1b7abb3af29c426a"},
208
+ {file = "audioop_lts-0.2.1-cp313-abi3-macosx_10_13_x86_64.whl", hash = "sha256:e175350da05d2087e12cea8e72a70a1a8b14a17e92ed2022952a4419689ede5e"},
209
+ {file = "audioop_lts-0.2.1-cp313-abi3-macosx_11_0_arm64.whl", hash = "sha256:4a8dd6a81770f6ecf019c4b6d659e000dc26571b273953cef7cd1d5ce2ff3ae6"},
210
+ {file = "audioop_lts-0.2.1-cp313-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1cd3c0b6f2ca25c7d2b1c3adeecbe23e65689839ba73331ebc7d893fcda7ffe"},
211
+ {file = "audioop_lts-0.2.1-cp313-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ff3f97b3372c97782e9c6d3d7fdbe83bce8f70de719605bd7ee1839cd1ab360a"},
212
+ {file = "audioop_lts-0.2.1-cp313-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a351af79edefc2a1bd2234bfd8b339935f389209943043913a919df4b0f13300"},
213
+ {file = "audioop_lts-0.2.1-cp313-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2aeb6f96f7f6da80354330470b9134d81b4cf544cdd1c549f2f45fe964d28059"},
214
+ {file = "audioop_lts-0.2.1-cp313-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c589f06407e8340e81962575fcffbba1e92671879a221186c3d4662de9fe804e"},
215
+ {file = "audioop_lts-0.2.1-cp313-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:fbae5d6925d7c26e712f0beda5ed69ebb40e14212c185d129b8dfbfcc335eb48"},
216
+ {file = "audioop_lts-0.2.1-cp313-abi3-musllinux_1_2_i686.whl", hash = "sha256:d2d5434717f33117f29b5691fbdf142d36573d751716249a288fbb96ba26a281"},
217
+ {file = "audioop_lts-0.2.1-cp313-abi3-musllinux_1_2_ppc64le.whl", hash = "sha256:f626a01c0a186b08f7ff61431c01c055961ee28769591efa8800beadd27a2959"},
218
+ {file = "audioop_lts-0.2.1-cp313-abi3-musllinux_1_2_s390x.whl", hash = "sha256:05da64e73837f88ee5c6217d732d2584cf638003ac72df124740460531e95e47"},
219
+ {file = "audioop_lts-0.2.1-cp313-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:56b7a0a4dba8e353436f31a932f3045d108a67b5943b30f85a5563f4d8488d77"},
220
+ {file = "audioop_lts-0.2.1-cp313-abi3-win32.whl", hash = "sha256:6e899eb8874dc2413b11926b5fb3857ec0ab55222840e38016a6ba2ea9b7d5e3"},
221
+ {file = "audioop_lts-0.2.1-cp313-abi3-win_amd64.whl", hash = "sha256:64562c5c771fb0a8b6262829b9b4f37a7b886c01b4d3ecdbae1d629717db08b4"},
222
+ {file = "audioop_lts-0.2.1-cp313-abi3-win_arm64.whl", hash = "sha256:c45317debeb64002e980077642afbd977773a25fa3dfd7ed0c84dccfc1fafcb0"},
223
+ {file = "audioop_lts-0.2.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:3827e3fce6fee4d69d96a3d00cd2ab07f3c0d844cb1e44e26f719b34a5b15455"},
224
+ {file = "audioop_lts-0.2.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:161249db9343b3c9780ca92c0be0d1ccbfecdbccac6844f3d0d44b9c4a00a17f"},
225
+ {file = "audioop_lts-0.2.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5b7b4ff9de7a44e0ad2618afdc2ac920b91f4a6d3509520ee65339d4acde5abf"},
226
+ {file = "audioop_lts-0.2.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:72e37f416adb43b0ced93419de0122b42753ee74e87070777b53c5d2241e7fab"},
227
+ {file = "audioop_lts-0.2.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:534ce808e6bab6adb65548723c8cbe189a3379245db89b9d555c4210b4aaa9b6"},
228
+ {file = "audioop_lts-0.2.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d2de9b6fb8b1cf9f03990b299a9112bfdf8b86b6987003ca9e8a6c4f56d39543"},
229
+ {file = "audioop_lts-0.2.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f24865991b5ed4b038add5edbf424639d1358144f4e2a3e7a84bc6ba23e35074"},
230
+ {file = "audioop_lts-0.2.1-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bdb3b7912ccd57ea53197943f1bbc67262dcf29802c4a6df79ec1c715d45a78"},
231
+ {file = "audioop_lts-0.2.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:120678b208cca1158f0a12d667af592e067f7a50df9adc4dc8f6ad8d065a93fb"},
232
+ {file = "audioop_lts-0.2.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:54cd4520fc830b23c7d223693ed3e1b4d464997dd3abc7c15dce9a1f9bd76ab2"},
233
+ {file = "audioop_lts-0.2.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:d6bd20c7a10abcb0fb3d8aaa7508c0bf3d40dfad7515c572014da4b979d3310a"},
234
+ {file = "audioop_lts-0.2.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:f0ed1ad9bd862539ea875fb339ecb18fcc4148f8d9908f4502df28f94d23491a"},
235
+ {file = "audioop_lts-0.2.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:e1af3ff32b8c38a7d900382646e91f2fc515fd19dea37e9392275a5cbfdbff63"},
236
+ {file = "audioop_lts-0.2.1-cp313-cp313t-win32.whl", hash = "sha256:f51bb55122a89f7a0817d7ac2319744b4640b5b446c4c3efcea5764ea99ae509"},
237
+ {file = "audioop_lts-0.2.1-cp313-cp313t-win_amd64.whl", hash = "sha256:f0f2f336aa2aee2bce0b0dcc32bbba9178995454c7b979cf6ce086a8801e14c7"},
238
+ {file = "audioop_lts-0.2.1-cp313-cp313t-win_arm64.whl", hash = "sha256:78bfb3703388c780edf900be66e07de5a3d4105ca8e8720c5c4d67927e0b15d0"},
239
+ {file = "audioop_lts-0.2.1.tar.gz", hash = "sha256:e81268da0baa880431b68b1308ab7257eb33f356e57a5f9b1f915dfb13dd1387"},
240
+ ]
241
+
242
  [[package]]
243
  name = "certifi"
244
  version = "2024.8.30"
 
449
  all = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"]
450
  standard = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "jinja2 (>=2.11.2)", "python-multipart (>=0.0.7)", "uvicorn[standard] (>=0.12.0)"]
451
 
452
+ [[package]]
453
+ name = "ffmpy"
454
+ version = "0.4.0"
455
+ description = "A simple Python wrapper for FFmpeg"
456
+ optional = false
457
+ python-versions = "<4.0.0,>=3.8.1"
458
+ files = [
459
+ {file = "ffmpy-0.4.0-py3-none-any.whl", hash = "sha256:39c0f20c5b465e7f8d29a5191f3a7d7675a8c546d9d985de8921151cd9b59e14"},
460
+ {file = "ffmpy-0.4.0.tar.gz", hash = "sha256:131b57794e802ad555f579007497f7a3d0cab0583d37496c685b8acae4837b1d"},
461
+ ]
462
+
463
+ [[package]]
464
+ name = "filelock"
465
+ version = "3.16.1"
466
+ description = "A platform independent file lock."
467
+ optional = false
468
+ python-versions = ">=3.8"
469
+ files = [
470
+ {file = "filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0"},
471
+ {file = "filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435"},
472
+ ]
473
+
474
+ [package.extras]
475
+ docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4.1)"]
476
+ testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.2)", "pytest (>=8.3.3)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.4)"]
477
+ typing = ["typing-extensions (>=4.12.2)"]
478
+
479
  [[package]]
480
  name = "frozenlist"
481
  version = "1.5.0"
 
577
  {file = "frozenlist-1.5.0.tar.gz", hash = "sha256:81d5af29e61b9c8348e876d442253723928dce6433e0e76cd925cd83f1b4b817"},
578
  ]
579
 
580
+ [[package]]
581
+ name = "fsspec"
582
+ version = "2024.10.0"
583
+ description = "File-system specification"
584
+ optional = false
585
+ python-versions = ">=3.8"
586
+ files = [
587
+ {file = "fsspec-2024.10.0-py3-none-any.whl", hash = "sha256:03b9a6785766a4de40368b88906366755e2819e758b83705c88cd7cb5fe81871"},
588
+ {file = "fsspec-2024.10.0.tar.gz", hash = "sha256:eda2d8a4116d4f2429db8550f2457da57279247dd930bb12f821b58391359493"},
589
+ ]
590
+
591
+ [package.extras]
592
+ abfs = ["adlfs"]
593
+ adl = ["adlfs"]
594
+ arrow = ["pyarrow (>=1)"]
595
+ dask = ["dask", "distributed"]
596
+ dev = ["pre-commit", "ruff"]
597
+ doc = ["numpydoc", "sphinx", "sphinx-design", "sphinx-rtd-theme", "yarl"]
598
+ dropbox = ["dropbox", "dropboxdrivefs", "requests"]
599
+ full = ["adlfs", "aiohttp (!=4.0.0a0,!=4.0.0a1)", "dask", "distributed", "dropbox", "dropboxdrivefs", "fusepy", "gcsfs", "libarchive-c", "ocifs", "panel", "paramiko", "pyarrow (>=1)", "pygit2", "requests", "s3fs", "smbprotocol", "tqdm"]
600
+ fuse = ["fusepy"]
601
+ gcs = ["gcsfs"]
602
+ git = ["pygit2"]
603
+ github = ["requests"]
604
+ gs = ["gcsfs"]
605
+ gui = ["panel"]
606
+ hdfs = ["pyarrow (>=1)"]
607
+ http = ["aiohttp (!=4.0.0a0,!=4.0.0a1)"]
608
+ libarchive = ["libarchive-c"]
609
+ oci = ["ocifs"]
610
+ s3 = ["s3fs"]
611
+ sftp = ["paramiko"]
612
+ smb = ["smbprotocol"]
613
+ ssh = ["paramiko"]
614
+ test = ["aiohttp (!=4.0.0a0,!=4.0.0a1)", "numpy", "pytest", "pytest-asyncio (!=0.22.0)", "pytest-benchmark", "pytest-cov", "pytest-mock", "pytest-recording", "pytest-rerunfailures", "requests"]
615
+ test-downstream = ["aiobotocore (>=2.5.4,<3.0.0)", "dask-expr", "dask[dataframe,test]", "moto[server] (>4,<5)", "pytest-timeout", "xarray"]
616
+ test-full = ["adlfs", "aiohttp (!=4.0.0a0,!=4.0.0a1)", "cloudpickle", "dask", "distributed", "dropbox", "dropboxdrivefs", "fastparquet", "fusepy", "gcsfs", "jinja2", "kerchunk", "libarchive-c", "lz4", "notebook", "numpy", "ocifs", "pandas", "panel", "paramiko", "pyarrow", "pyarrow (>=1)", "pyftpdlib", "pygit2", "pytest", "pytest-asyncio (!=0.22.0)", "pytest-benchmark", "pytest-cov", "pytest-mock", "pytest-recording", "pytest-rerunfailures", "python-snappy", "requests", "smbprotocol", "tqdm", "urllib3", "zarr", "zstandard"]
617
+ tqdm = ["tqdm"]
618
+
619
+ [[package]]
620
+ name = "gradio"
621
+ version = "5.8.0"
622
+ description = "Python library for easily interacting with trained machine learning models"
623
+ optional = false
624
+ python-versions = ">=3.10"
625
+ files = [
626
+ {file = "gradio-5.8.0-py3-none-any.whl", hash = "sha256:428ad660fc48104f0c60f8ae808ab5e2afec03a472a0fb734348c4e916de74dc"},
627
+ ]
628
+
629
+ [package.dependencies]
630
+ aiofiles = ">=22.0,<24.0"
631
+ anyio = ">=3.0,<5.0"
632
+ audioop-lts = {version = "<1.0", markers = "python_version >= \"3.13\""}
633
+ fastapi = ">=0.115.2,<1.0"
634
+ ffmpy = "*"
635
+ gradio-client = "1.5.1"
636
+ httpx = ">=0.24.1"
637
+ huggingface-hub = ">=0.25.1"
638
+ jinja2 = "<4.0"
639
+ markupsafe = ">=2.0,<3.0"
640
+ numpy = ">=1.0,<3.0"
641
+ orjson = ">=3.0,<4.0"
642
+ packaging = "*"
643
+ pandas = ">=1.0,<3.0"
644
+ pillow = ">=8.0,<12.0"
645
+ pydantic = ">=2.0"
646
+ pydub = "*"
647
+ python-multipart = ">=0.0.18"
648
+ pyyaml = ">=5.0,<7.0"
649
+ ruff = {version = ">=0.2.2", markers = "sys_platform != \"emscripten\""}
650
+ safehttpx = ">=0.1.6,<0.2.0"
651
+ semantic-version = ">=2.0,<3.0"
652
+ starlette = {version = ">=0.40.0,<1.0", markers = "sys_platform != \"emscripten\""}
653
+ tomlkit = ">=0.12.0,<0.14.0"
654
+ typer = {version = ">=0.12,<1.0", markers = "sys_platform != \"emscripten\""}
655
+ typing-extensions = ">=4.0,<5.0"
656
+ urllib3 = {version = ">=2.0,<3.0", markers = "sys_platform == \"emscripten\""}
657
+ uvicorn = {version = ">=0.14.0", markers = "sys_platform != \"emscripten\""}
658
+
659
+ [package.extras]
660
+ oauth = ["authlib", "itsdangerous"]
661
+
662
+ [[package]]
663
+ name = "gradio-client"
664
+ version = "1.5.1"
665
+ description = "Python library for easily interacting with trained machine learning models"
666
+ optional = false
667
+ python-versions = ">=3.10"
668
+ files = [
669
+ {file = "gradio_client-1.5.1-py3-none-any.whl", hash = "sha256:175e4ac399591d919af85a097661cdd760b8dd1ca229e28ef4da978fde84a398"},
670
+ {file = "gradio_client-1.5.1.tar.gz", hash = "sha256:c443013e3532828e202c9679b254016406a1753666c3c601ea8557c724adbaf7"},
671
+ ]
672
+
673
+ [package.dependencies]
674
+ fsspec = "*"
675
+ httpx = ">=0.24.1"
676
+ huggingface-hub = ">=0.19.3"
677
+ packaging = "*"
678
+ typing-extensions = ">=4.0,<5.0"
679
+ websockets = ">=10.0,<15.0"
680
+
681
  [[package]]
682
  name = "greenlet"
683
  version = "3.1.1"
 
764
  docs = ["Sphinx", "furo"]
765
  test = ["objgraph", "psutil"]
766
 
767
+ [[package]]
768
+ name = "groq"
769
+ version = "0.13.0"
770
+ description = "The official Python library for the groq API"
771
+ optional = false
772
+ python-versions = ">=3.8"
773
+ files = [
774
+ {file = "groq-0.13.0-py3-none-any.whl", hash = "sha256:1c2b16fd1c4665c2d09509ee42ead9cc7d32534b37b2051fee0ea8b6216b899d"},
775
+ {file = "groq-0.13.0.tar.gz", hash = "sha256:aaa213821c94d8974e57bab5fe59cb45c8871875d0cd53ec179afed928bad18e"},
776
+ ]
777
+
778
+ [package.dependencies]
779
+ anyio = ">=3.5.0,<5"
780
+ distro = ">=1.7.0,<2"
781
+ httpx = ">=0.23.0,<1"
782
+ pydantic = ">=1.9.0,<3"
783
+ sniffio = "*"
784
+ typing-extensions = ">=4.7,<5"
785
+
786
  [[package]]
787
  name = "h11"
788
  version = "0.14.0"
 
851
  {file = "httpx_sse-0.4.0-py3-none-any.whl", hash = "sha256:f329af6eae57eaa2bdfd962b42524764af68075ea87370a2de920af5341e318f"},
852
  ]
853
 
854
+ [[package]]
855
+ name = "huggingface-hub"
856
+ version = "0.26.5"
857
+ description = "Client library to download and publish models, datasets and other repos on the huggingface.co hub"
858
+ optional = false
859
+ python-versions = ">=3.8.0"
860
+ files = [
861
+ {file = "huggingface_hub-0.26.5-py3-none-any.whl", hash = "sha256:fb7386090bbe892072e64b85f7c4479fd2d65eea5f2543327c970d5169e83924"},
862
+ {file = "huggingface_hub-0.26.5.tar.gz", hash = "sha256:1008bd18f60bfb65e8dbc0a97249beeeaa8c99d3c2fa649354df9fa5a13ed83b"},
863
+ ]
864
+
865
+ [package.dependencies]
866
+ filelock = "*"
867
+ fsspec = ">=2023.5.0"
868
+ packaging = ">=20.9"
869
+ pyyaml = ">=5.1"
870
+ requests = "*"
871
+ tqdm = ">=4.42.1"
872
+ typing-extensions = ">=3.7.4.3"
873
+
874
+ [package.extras]
875
+ all = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio (>=4.0.0)", "jedi", "libcst (==1.4.0)", "mypy (==1.5.1)", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.5.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"]
876
+ cli = ["InquirerPy (==0.3.4)"]
877
+ dev = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio (>=4.0.0)", "jedi", "libcst (==1.4.0)", "mypy (==1.5.1)", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.5.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"]
878
+ fastai = ["fastai (>=2.4)", "fastcore (>=1.3.27)", "toml"]
879
+ hf-transfer = ["hf-transfer (>=0.1.4)"]
880
+ inference = ["aiohttp"]
881
+ quality = ["libcst (==1.4.0)", "mypy (==1.5.1)", "ruff (>=0.5.0)"]
882
+ tensorflow = ["graphviz", "pydot", "tensorflow"]
883
+ tensorflow-testing = ["keras (<3.0)", "tensorflow"]
884
+ testing = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio (>=4.0.0)", "jedi", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "soundfile", "urllib3 (<2.0)"]
885
+ torch = ["safetensors[torch]", "torch"]
886
+ typing = ["types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)"]
887
+
888
  [[package]]
889
  name = "idna"
890
  version = "3.10"
 
899
  [package.extras]
900
  all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"]
901
 
902
+ [[package]]
903
+ name = "jinja2"
904
+ version = "3.1.4"
905
+ description = "A very fast and expressive template engine."
906
+ optional = false
907
+ python-versions = ">=3.7"
908
+ files = [
909
+ {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"},
910
+ {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"},
911
+ ]
912
+
913
+ [package.dependencies]
914
+ MarkupSafe = ">=2.0"
915
+
916
+ [package.extras]
917
+ i18n = ["Babel (>=2.7)"]
918
+
919
  [[package]]
920
  name = "jiter"
921
  version = "0.7.1"
 
1101
  tenacity = ">=8.1.0,<8.4.0 || >8.4.0,<10.0.0"
1102
  typing-extensions = ">=4.7"
1103
 
1104
+ [[package]]
1105
+ name = "langchain-groq"
1106
+ version = "0.2.1"
1107
+ description = "An integration package connecting Groq and LangChain"
1108
+ optional = false
1109
+ python-versions = "<4.0,>=3.9"
1110
+ files = [
1111
+ {file = "langchain_groq-0.2.1-py3-none-any.whl", hash = "sha256:98d282fd9d7d99b0f55de0a1daea2d5d350ef697e3cb5e97de06aeba4eca8679"},
1112
+ {file = "langchain_groq-0.2.1.tar.gz", hash = "sha256:a59c81d1a15dc97abf4fdb4c2589f98109313eda147e6b378829222d4d929792"},
1113
+ ]
1114
+
1115
+ [package.dependencies]
1116
+ groq = ">=0.4.1,<1"
1117
+ langchain-core = ">=0.3.15,<0.4.0"
1118
+
1119
  [[package]]
1120
  name = "langchain-openai"
1121
  version = "0.2.8"
 
1167
  requests = ">=2,<3"
1168
  requests-toolbelt = ">=1.0.0,<2.0.0"
1169
 
1170
+ [[package]]
1171
+ name = "markdown-it-py"
1172
+ version = "3.0.0"
1173
+ description = "Python port of markdown-it. Markdown parsing, done right!"
1174
+ optional = false
1175
+ python-versions = ">=3.8"
1176
+ files = [
1177
+ {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"},
1178
+ {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"},
1179
+ ]
1180
+
1181
+ [package.dependencies]
1182
+ mdurl = ">=0.1,<1.0"
1183
+
1184
+ [package.extras]
1185
+ benchmarking = ["psutil", "pytest", "pytest-benchmark"]
1186
+ code-style = ["pre-commit (>=3.0,<4.0)"]
1187
+ compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"]
1188
+ linkify = ["linkify-it-py (>=1,<3)"]
1189
+ plugins = ["mdit-py-plugins"]
1190
+ profiling = ["gprof2dot"]
1191
+ rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"]
1192
+ testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"]
1193
+
1194
+ [[package]]
1195
+ name = "markupsafe"
1196
+ version = "2.1.5"
1197
+ description = "Safely add untrusted strings to HTML/XML markup."
1198
+ optional = false
1199
+ python-versions = ">=3.7"
1200
+ files = [
1201
+ {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"},
1202
+ {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"},
1203
+ {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"},
1204
+ {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"},
1205
+ {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"},
1206
+ {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"},
1207
+ {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"},
1208
+ {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"},
1209
+ {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"},
1210
+ {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"},
1211
+ {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"},
1212
+ {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"},
1213
+ {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"},
1214
+ {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"},
1215
+ {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"},
1216
+ {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"},
1217
+ {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"},
1218
+ {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"},
1219
+ {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"},
1220
+ {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"},
1221
+ {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"},
1222
+ {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"},
1223
+ {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"},
1224
+ {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"},
1225
+ {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"},
1226
+ {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"},
1227
+ {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"},
1228
+ {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"},
1229
+ {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"},
1230
+ {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"},
1231
+ {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"},
1232
+ {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"},
1233
+ {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"},
1234
+ {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"},
1235
+ {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"},
1236
+ {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"},
1237
+ {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"},
1238
+ {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"},
1239
+ {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"},
1240
+ {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"},
1241
+ {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"},
1242
+ {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"},
1243
+ {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"},
1244
+ {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"},
1245
+ {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"},
1246
+ {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"},
1247
+ {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"},
1248
+ {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"},
1249
+ {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"},
1250
+ {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"},
1251
+ {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"},
1252
+ {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"},
1253
+ {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"},
1254
+ {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"},
1255
+ {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"},
1256
+ {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"},
1257
+ {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"},
1258
+ {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"},
1259
+ {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"},
1260
+ {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"},
1261
+ ]
1262
+
1263
  [[package]]
1264
  name = "marshmallow"
1265
  version = "3.23.1"
 
1279
  docs = ["alabaster (==1.0.0)", "autodocsumm (==0.2.14)", "sphinx (==8.1.3)", "sphinx-issues (==5.0.0)", "sphinx-version-warning (==1.1.2)"]
1280
  tests = ["pytest", "simplejson"]
1281
 
1282
+ [[package]]
1283
+ name = "mdurl"
1284
+ version = "0.1.2"
1285
+ description = "Markdown URL utilities"
1286
+ optional = false
1287
+ python-versions = ">=3.7"
1288
+ files = [
1289
+ {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"},
1290
+ {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"},
1291
+ ]
1292
+
1293
  [[package]]
1294
  name = "multidict"
1295
  version = "6.1.0"
 
1552
  {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"},
1553
  ]
1554
 
1555
+ [[package]]
1556
+ name = "pandas"
1557
+ version = "2.2.3"
1558
+ description = "Powerful data structures for data analysis, time series, and statistics"
1559
+ optional = false
1560
+ python-versions = ">=3.9"
1561
+ files = [
1562
+ {file = "pandas-2.2.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1948ddde24197a0f7add2bdc4ca83bf2b1ef84a1bc8ccffd95eda17fd836ecb5"},
1563
+ {file = "pandas-2.2.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:381175499d3802cde0eabbaf6324cce0c4f5d52ca6f8c377c29ad442f50f6348"},
1564
+ {file = "pandas-2.2.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86976a1c5b25ae3f8ccae3a5306e443569ee3c3faf444dfd0f41cda24667ad57"},
1565
+ {file = "pandas-2.2.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:37e0aced3e8f539eccf2e099f65cdb9c8aa85109b0be6e93e2baff94264bdc6f"},
1566
+ {file = "pandas-2.2.3-cp310-cp310-win_amd64.whl", hash = "sha256:56534ce0746a58afaf7942ba4863e0ef81c9c50d3f0ae93e9497d6a41a057645"},
1567
+ {file = "pandas-2.2.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:66108071e1b935240e74525006034333f98bcdb87ea116de573a6a0dccb6c039"},
1568
+ {file = "pandas-2.2.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7c2875855b0ff77b2a64a0365e24455d9990730d6431b9e0ee18ad8acee13dbd"},
1569
+ {file = "pandas-2.2.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c124333816c3a9b03fbeef3a9f230ba9a737e9e5bb4060aa2107a86cc0a497fc"},
1570
+ {file = "pandas-2.2.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:29401dbfa9ad77319367d36940cd8a0b3a11aba16063e39632d98b0e931ddf32"},
1571
+ {file = "pandas-2.2.3-cp311-cp311-win_amd64.whl", hash = "sha256:3fc6873a41186404dad67245896a6e440baacc92f5b716ccd1bc9ed2995ab2c5"},
1572
+ {file = "pandas-2.2.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b1d432e8d08679a40e2a6d8b2f9770a5c21793a6f9f47fdd52c5ce1948a5a8a9"},
1573
+ {file = "pandas-2.2.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a5a1595fe639f5988ba6a8e5bc9649af3baf26df3998a0abe56c02609392e0a4"},
1574
+ {file = "pandas-2.2.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fffb8ae78d8af97f849404f21411c95062db1496aeb3e56f146f0355c9989319"},
1575
+ {file = "pandas-2.2.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:062309c1b9ea12a50e8ce661145c6aab431b1e99530d3cd60640e255778bd43a"},
1576
+ {file = "pandas-2.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:59ef3764d0fe818125a5097d2ae867ca3fa64df032331b7e0917cf5d7bf66b13"},
1577
+ {file = "pandas-2.2.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f00d1345d84d8c86a63e476bb4955e46458b304b9575dcf71102b5c705320015"},
1578
+ {file = "pandas-2.2.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3508d914817e153ad359d7e069d752cdd736a247c322d932eb89e6bc84217f28"},
1579
+ {file = "pandas-2.2.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3a255b2c19987fbbe62a9dfd6cff7ff2aa9ccab3fc75218fd4b7530f01efa24"},
1580
+ {file = "pandas-2.2.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6374c452ff3ec675a8f46fd9ab25c4ad0ba590b71cf0656f8b6daa5202bca3fb"},
1581
+ {file = "pandas-2.2.3-cp313-cp313-win_amd64.whl", hash = "sha256:61c5ad4043f791b61dd4752191d9f07f0ae412515d59ba8f005832a532f8736d"},
1582
+ {file = "pandas-2.2.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3b71f27954685ee685317063bf13c7709a7ba74fc996b84fc6821c59b0f06468"},
1583
+ {file = "pandas-2.2.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:38cf8125c40dae9d5acc10fa66af8ea6fdf760b2714ee482ca691fc66e6fcb18"},
1584
+ {file = "pandas-2.2.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1db71525a1538b30142094edb9adc10be3f3e176748cd7acc2240c2f2e5aa3a4"},
1585
+ {file = "pandas-2.2.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ad5b65698ab28ed8d7f18790a0dc58005c7629f227be9ecc1072aa74c0c1d43a"},
1586
+ {file = "pandas-2.2.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc6b93f9b966093cb0fd62ff1a7e4c09e6d546ad7c1de191767baffc57628f39"},
1587
+ {file = "pandas-2.2.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5dbca4c1acd72e8eeef4753eeca07de9b1db4f398669d5994086f788a5d7cc30"},
1588
+ {file = "pandas-2.2.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99df71520d25fade9db7c1076ac94eb994f4d2673ef2aa2e86ee039b6746d20c"},
1589
+ {file = "pandas-2.2.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7eee9e7cea6adf3e3d24e304ac6b8300646e2a5d1cd3a3c2abed9101b0846761"},
1590
+ {file = "pandas-2.2.3-cp39-cp39-win_amd64.whl", hash = "sha256:4850ba03528b6dd51d6c5d273c46f183f39a9baf3f0143e566b89450965b105e"},
1591
+ {file = "pandas-2.2.3.tar.gz", hash = "sha256:4f18ba62b61d7e192368b84517265a99b4d7ee8912f8708660fb4a366cc82667"},
1592
+ ]
1593
+
1594
+ [package.dependencies]
1595
+ numpy = [
1596
+ {version = ">=1.26.0", markers = "python_version >= \"3.12\""},
1597
+ {version = ">=1.22.4", markers = "python_version < \"3.11\""},
1598
+ {version = ">=1.23.2", markers = "python_version == \"3.11\""},
1599
+ ]
1600
+ python-dateutil = ">=2.8.2"
1601
+ pytz = ">=2020.1"
1602
+ tzdata = ">=2022.7"
1603
+
1604
+ [package.extras]
1605
+ all = ["PyQt5 (>=5.15.9)", "SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)", "beautifulsoup4 (>=4.11.2)", "bottleneck (>=1.3.6)", "dataframe-api-compat (>=0.1.7)", "fastparquet (>=2022.12.0)", "fsspec (>=2022.11.0)", "gcsfs (>=2022.11.0)", "html5lib (>=1.1)", "hypothesis (>=6.46.1)", "jinja2 (>=3.1.2)", "lxml (>=4.9.2)", "matplotlib (>=3.6.3)", "numba (>=0.56.4)", "numexpr (>=2.8.4)", "odfpy (>=1.4.1)", "openpyxl (>=3.1.0)", "pandas-gbq (>=0.19.0)", "psycopg2 (>=2.9.6)", "pyarrow (>=10.0.1)", "pymysql (>=1.0.2)", "pyreadstat (>=1.2.0)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)", "python-calamine (>=0.1.7)", "pyxlsb (>=1.0.10)", "qtpy (>=2.3.0)", "s3fs (>=2022.11.0)", "scipy (>=1.10.0)", "tables (>=3.8.0)", "tabulate (>=0.9.0)", "xarray (>=2022.12.0)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.5)", "zstandard (>=0.19.0)"]
1606
+ aws = ["s3fs (>=2022.11.0)"]
1607
+ clipboard = ["PyQt5 (>=5.15.9)", "qtpy (>=2.3.0)"]
1608
+ compression = ["zstandard (>=0.19.0)"]
1609
+ computation = ["scipy (>=1.10.0)", "xarray (>=2022.12.0)"]
1610
+ consortium-standard = ["dataframe-api-compat (>=0.1.7)"]
1611
+ excel = ["odfpy (>=1.4.1)", "openpyxl (>=3.1.0)", "python-calamine (>=0.1.7)", "pyxlsb (>=1.0.10)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.5)"]
1612
+ feather = ["pyarrow (>=10.0.1)"]
1613
+ fss = ["fsspec (>=2022.11.0)"]
1614
+ gcp = ["gcsfs (>=2022.11.0)", "pandas-gbq (>=0.19.0)"]
1615
+ hdf5 = ["tables (>=3.8.0)"]
1616
+ html = ["beautifulsoup4 (>=4.11.2)", "html5lib (>=1.1)", "lxml (>=4.9.2)"]
1617
+ mysql = ["SQLAlchemy (>=2.0.0)", "pymysql (>=1.0.2)"]
1618
+ output-formatting = ["jinja2 (>=3.1.2)", "tabulate (>=0.9.0)"]
1619
+ parquet = ["pyarrow (>=10.0.1)"]
1620
+ performance = ["bottleneck (>=1.3.6)", "numba (>=0.56.4)", "numexpr (>=2.8.4)"]
1621
+ plot = ["matplotlib (>=3.6.3)"]
1622
+ postgresql = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "psycopg2 (>=2.9.6)"]
1623
+ pyarrow = ["pyarrow (>=10.0.1)"]
1624
+ spss = ["pyreadstat (>=1.2.0)"]
1625
+ sql-other = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)"]
1626
+ test = ["hypothesis (>=6.46.1)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)"]
1627
+ xml = ["lxml (>=4.9.2)"]
1628
+
1629
+ [[package]]
1630
+ name = "pillow"
1631
+ version = "11.0.0"
1632
+ description = "Python Imaging Library (Fork)"
1633
+ optional = false
1634
+ python-versions = ">=3.9"
1635
+ files = [
1636
+ {file = "pillow-11.0.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:6619654954dc4936fcff82db8eb6401d3159ec6be81e33c6000dfd76ae189947"},
1637
+ {file = "pillow-11.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b3c5ac4bed7519088103d9450a1107f76308ecf91d6dabc8a33a2fcfb18d0fba"},
1638
+ {file = "pillow-11.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a65149d8ada1055029fcb665452b2814fe7d7082fcb0c5bed6db851cb69b2086"},
1639
+ {file = "pillow-11.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88a58d8ac0cc0e7f3a014509f0455248a76629ca9b604eca7dc5927cc593c5e9"},
1640
+ {file = "pillow-11.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:c26845094b1af3c91852745ae78e3ea47abf3dbcd1cf962f16b9a5fbe3ee8488"},
1641
+ {file = "pillow-11.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:1a61b54f87ab5786b8479f81c4b11f4d61702830354520837f8cc791ebba0f5f"},
1642
+ {file = "pillow-11.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:674629ff60030d144b7bca2b8330225a9b11c482ed408813924619c6f302fdbb"},
1643
+ {file = "pillow-11.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:598b4e238f13276e0008299bd2482003f48158e2b11826862b1eb2ad7c768b97"},
1644
+ {file = "pillow-11.0.0-cp310-cp310-win32.whl", hash = "sha256:9a0f748eaa434a41fccf8e1ee7a3eed68af1b690e75328fd7a60af123c193b50"},
1645
+ {file = "pillow-11.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:a5629742881bcbc1f42e840af185fd4d83a5edeb96475a575f4da50d6ede337c"},
1646
+ {file = "pillow-11.0.0-cp310-cp310-win_arm64.whl", hash = "sha256:ee217c198f2e41f184f3869f3e485557296d505b5195c513b2bfe0062dc537f1"},
1647
+ {file = "pillow-11.0.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:1c1d72714f429a521d8d2d018badc42414c3077eb187a59579f28e4270b4b0fc"},
1648
+ {file = "pillow-11.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:499c3a1b0d6fc8213519e193796eb1a86a1be4b1877d678b30f83fd979811d1a"},
1649
+ {file = "pillow-11.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c8b2351c85d855293a299038e1f89db92a2f35e8d2f783489c6f0b2b5f3fe8a3"},
1650
+ {file = "pillow-11.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f4dba50cfa56f910241eb7f883c20f1e7b1d8f7d91c750cd0b318bad443f4d5"},
1651
+ {file = "pillow-11.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:5ddbfd761ee00c12ee1be86c9c0683ecf5bb14c9772ddbd782085779a63dd55b"},
1652
+ {file = "pillow-11.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:45c566eb10b8967d71bf1ab8e4a525e5a93519e29ea071459ce517f6b903d7fa"},
1653
+ {file = "pillow-11.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b4fd7bd29610a83a8c9b564d457cf5bd92b4e11e79a4ee4716a63c959699b306"},
1654
+ {file = "pillow-11.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:cb929ca942d0ec4fac404cbf520ee6cac37bf35be479b970c4ffadf2b6a1cad9"},
1655
+ {file = "pillow-11.0.0-cp311-cp311-win32.whl", hash = "sha256:006bcdd307cc47ba43e924099a038cbf9591062e6c50e570819743f5607404f5"},
1656
+ {file = "pillow-11.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:52a2d8323a465f84faaba5236567d212c3668f2ab53e1c74c15583cf507a0291"},
1657
+ {file = "pillow-11.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:16095692a253047fe3ec028e951fa4221a1f3ed3d80c397e83541a3037ff67c9"},
1658
+ {file = "pillow-11.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d2c0a187a92a1cb5ef2c8ed5412dd8d4334272617f532d4ad4de31e0495bd923"},
1659
+ {file = "pillow-11.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:084a07ef0821cfe4858fe86652fffac8e187b6ae677e9906e192aafcc1b69903"},
1660
+ {file = "pillow-11.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8069c5179902dcdce0be9bfc8235347fdbac249d23bd90514b7a47a72d9fecf4"},
1661
+ {file = "pillow-11.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f02541ef64077f22bf4924f225c0fd1248c168f86e4b7abdedd87d6ebaceab0f"},
1662
+ {file = "pillow-11.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:fcb4621042ac4b7865c179bb972ed0da0218a076dc1820ffc48b1d74c1e37fe9"},
1663
+ {file = "pillow-11.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:00177a63030d612148e659b55ba99527803288cea7c75fb05766ab7981a8c1b7"},
1664
+ {file = "pillow-11.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8853a3bf12afddfdf15f57c4b02d7ded92c7a75a5d7331d19f4f9572a89c17e6"},
1665
+ {file = "pillow-11.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3107c66e43bda25359d5ef446f59c497de2b5ed4c7fdba0894f8d6cf3822dafc"},
1666
+ {file = "pillow-11.0.0-cp312-cp312-win32.whl", hash = "sha256:86510e3f5eca0ab87429dd77fafc04693195eec7fd6a137c389c3eeb4cfb77c6"},
1667
+ {file = "pillow-11.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:8ec4a89295cd6cd4d1058a5e6aec6bf51e0eaaf9714774e1bfac7cfc9051db47"},
1668
+ {file = "pillow-11.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:27a7860107500d813fcd203b4ea19b04babe79448268403172782754870dac25"},
1669
+ {file = "pillow-11.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:bcd1fb5bb7b07f64c15618c89efcc2cfa3e95f0e3bcdbaf4642509de1942a699"},
1670
+ {file = "pillow-11.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0e038b0745997c7dcaae350d35859c9715c71e92ffb7e0f4a8e8a16732150f38"},
1671
+ {file = "pillow-11.0.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ae08bd8ffc41aebf578c2af2f9d8749d91f448b3bfd41d7d9ff573d74f2a6b2"},
1672
+ {file = "pillow-11.0.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d69bfd8ec3219ae71bcde1f942b728903cad25fafe3100ba2258b973bd2bc1b2"},
1673
+ {file = "pillow-11.0.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:61b887f9ddba63ddf62fd02a3ba7add935d053b6dd7d58998c630e6dbade8527"},
1674
+ {file = "pillow-11.0.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:c6a660307ca9d4867caa8d9ca2c2658ab685de83792d1876274991adec7b93fa"},
1675
+ {file = "pillow-11.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:73e3a0200cdda995c7e43dd47436c1548f87a30bb27fb871f352a22ab8dcf45f"},
1676
+ {file = "pillow-11.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fba162b8872d30fea8c52b258a542c5dfd7b235fb5cb352240c8d63b414013eb"},
1677
+ {file = "pillow-11.0.0-cp313-cp313-win32.whl", hash = "sha256:f1b82c27e89fffc6da125d5eb0ca6e68017faf5efc078128cfaa42cf5cb38798"},
1678
+ {file = "pillow-11.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:8ba470552b48e5835f1d23ecb936bb7f71d206f9dfeee64245f30c3270b994de"},
1679
+ {file = "pillow-11.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:846e193e103b41e984ac921b335df59195356ce3f71dcfd155aa79c603873b84"},
1680
+ {file = "pillow-11.0.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4ad70c4214f67d7466bea6a08061eba35c01b1b89eaa098040a35272a8efb22b"},
1681
+ {file = "pillow-11.0.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:6ec0d5af64f2e3d64a165f490d96368bb5dea8b8f9ad04487f9ab60dc4bb6003"},
1682
+ {file = "pillow-11.0.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c809a70e43c7977c4a42aefd62f0131823ebf7dd73556fa5d5950f5b354087e2"},
1683
+ {file = "pillow-11.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:4b60c9520f7207aaf2e1d94de026682fc227806c6e1f55bba7606d1c94dd623a"},
1684
+ {file = "pillow-11.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:1e2688958a840c822279fda0086fec1fdab2f95bf2b717b66871c4ad9859d7e8"},
1685
+ {file = "pillow-11.0.0-cp313-cp313t-win32.whl", hash = "sha256:607bbe123c74e272e381a8d1957083a9463401f7bd01287f50521ecb05a313f8"},
1686
+ {file = "pillow-11.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5c39ed17edea3bc69c743a8dd3e9853b7509625c2462532e62baa0732163a904"},
1687
+ {file = "pillow-11.0.0-cp313-cp313t-win_arm64.whl", hash = "sha256:75acbbeb05b86bc53cbe7b7e6fe00fbcf82ad7c684b3ad82e3d711da9ba287d3"},
1688
+ {file = "pillow-11.0.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:2e46773dc9f35a1dd28bd6981332fd7f27bec001a918a72a79b4133cf5291dba"},
1689
+ {file = "pillow-11.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2679d2258b7f1192b378e2893a8a0a0ca472234d4c2c0e6bdd3380e8dfa21b6a"},
1690
+ {file = "pillow-11.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eda2616eb2313cbb3eebbe51f19362eb434b18e3bb599466a1ffa76a033fb916"},
1691
+ {file = "pillow-11.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20ec184af98a121fb2da42642dea8a29ec80fc3efbaefb86d8fdd2606619045d"},
1692
+ {file = "pillow-11.0.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:8594f42df584e5b4bb9281799698403f7af489fba84c34d53d1c4bfb71b7c4e7"},
1693
+ {file = "pillow-11.0.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:c12b5ae868897c7338519c03049a806af85b9b8c237b7d675b8c5e089e4a618e"},
1694
+ {file = "pillow-11.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:70fbbdacd1d271b77b7721fe3cdd2d537bbbd75d29e6300c672ec6bb38d9672f"},
1695
+ {file = "pillow-11.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5178952973e588b3f1360868847334e9e3bf49d19e169bbbdfaf8398002419ae"},
1696
+ {file = "pillow-11.0.0-cp39-cp39-win32.whl", hash = "sha256:8c676b587da5673d3c75bd67dd2a8cdfeb282ca38a30f37950511766b26858c4"},
1697
+ {file = "pillow-11.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:94f3e1780abb45062287b4614a5bc0874519c86a777d4a7ad34978e86428b8dd"},
1698
+ {file = "pillow-11.0.0-cp39-cp39-win_arm64.whl", hash = "sha256:290f2cc809f9da7d6d622550bbf4c1e57518212da51b6a30fe8e0a270a5b78bd"},
1699
+ {file = "pillow-11.0.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:1187739620f2b365de756ce086fdb3604573337cc28a0d3ac4a01ab6b2d2a6d2"},
1700
+ {file = "pillow-11.0.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:fbbcb7b57dc9c794843e3d1258c0fbf0f48656d46ffe9e09b63bbd6e8cd5d0a2"},
1701
+ {file = "pillow-11.0.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d203af30149ae339ad1b4f710d9844ed8796e97fda23ffbc4cc472968a47d0b"},
1702
+ {file = "pillow-11.0.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21a0d3b115009ebb8ac3d2ebec5c2982cc693da935f4ab7bb5c8ebe2f47d36f2"},
1703
+ {file = "pillow-11.0.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:73853108f56df97baf2bb8b522f3578221e56f646ba345a372c78326710d3830"},
1704
+ {file = "pillow-11.0.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e58876c91f97b0952eb766123bfef372792ab3f4e3e1f1a2267834c2ab131734"},
1705
+ {file = "pillow-11.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:224aaa38177597bb179f3ec87eeefcce8e4f85e608025e9cfac60de237ba6316"},
1706
+ {file = "pillow-11.0.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:5bd2d3bdb846d757055910f0a59792d33b555800813c3b39ada1829c372ccb06"},
1707
+ {file = "pillow-11.0.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:375b8dd15a1f5d2feafff536d47e22f69625c1aa92f12b339ec0b2ca40263273"},
1708
+ {file = "pillow-11.0.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:daffdf51ee5db69a82dd127eabecce20729e21f7a3680cf7cbb23f0829189790"},
1709
+ {file = "pillow-11.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7326a1787e3c7b0429659e0a944725e1b03eeaa10edd945a86dead1913383944"},
1710
+ {file = "pillow-11.0.0.tar.gz", hash = "sha256:72bacbaf24ac003fea9bff9837d1eedb6088758d41e100c1552930151f677739"},
1711
+ ]
1712
+
1713
+ [package.extras]
1714
+ docs = ["furo", "olefile", "sphinx (>=8.1)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinxext-opengraph"]
1715
+ fpx = ["olefile"]
1716
+ mic = ["olefile"]
1717
+ tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"]
1718
+ typing = ["typing-extensions"]
1719
+ xmp = ["defusedxml"]
1720
+
1721
  [[package]]
1722
  name = "propcache"
1723
  version = "0.2.0"
 
1969
  toml = ["tomli (>=2.0.1)"]
1970
  yaml = ["pyyaml (>=6.0.1)"]
1971
 
1972
+ [[package]]
1973
+ name = "pydub"
1974
+ version = "0.25.1"
1975
+ description = "Manipulate audio with an simple and easy high level interface"
1976
+ optional = false
1977
+ python-versions = "*"
1978
+ files = [
1979
+ {file = "pydub-0.25.1-py2.py3-none-any.whl", hash = "sha256:65617e33033874b59d87db603aa1ed450633288aefead953b30bded59cb599a6"},
1980
+ {file = "pydub-0.25.1.tar.gz", hash = "sha256:980a33ce9949cab2a569606b65674d748ecbca4f0796887fd6f46173a7b0d30f"},
1981
+ ]
1982
+
1983
+ [[package]]
1984
+ name = "pygments"
1985
+ version = "2.18.0"
1986
+ description = "Pygments is a syntax highlighting package written in Python."
1987
+ optional = false
1988
+ python-versions = ">=3.8"
1989
+ files = [
1990
+ {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"},
1991
+ {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"},
1992
+ ]
1993
+
1994
+ [package.extras]
1995
+ windows-terminal = ["colorama (>=0.4.6)"]
1996
+
1997
+ [[package]]
1998
+ name = "python-dateutil"
1999
+ version = "2.9.0.post0"
2000
+ description = "Extensions to the standard Python datetime module"
2001
+ optional = false
2002
+ python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
2003
+ files = [
2004
+ {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"},
2005
+ {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"},
2006
+ ]
2007
+
2008
+ [package.dependencies]
2009
+ six = ">=1.5"
2010
+
2011
  [[package]]
2012
  name = "python-dotenv"
2013
  version = "1.0.1"
 
2022
  [package.extras]
2023
  cli = ["click (>=5.0)"]
2024
 
2025
+ [[package]]
2026
+ name = "python-multipart"
2027
+ version = "0.0.19"
2028
+ description = "A streaming multipart parser for Python"
2029
+ optional = false
2030
+ python-versions = ">=3.8"
2031
+ files = [
2032
+ {file = "python_multipart-0.0.19-py3-none-any.whl", hash = "sha256:f8d5b0b9c618575bf9df01c684ded1d94a338839bdd8223838afacfb4bb2082d"},
2033
+ {file = "python_multipart-0.0.19.tar.gz", hash = "sha256:905502ef39050557b7a6af411f454bc19526529ca46ae6831508438890ce12cc"},
2034
+ ]
2035
+
2036
+ [[package]]
2037
+ name = "pytz"
2038
+ version = "2024.2"
2039
+ description = "World timezone definitions, modern and historical"
2040
+ optional = false
2041
+ python-versions = "*"
2042
+ files = [
2043
+ {file = "pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725"},
2044
+ {file = "pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a"},
2045
+ ]
2046
+
2047
  [[package]]
2048
  name = "pyyaml"
2049
  version = "6.0.2"
 
2244
  [package.dependencies]
2245
  requests = ">=2.0.1,<3.0.0"
2246
 
2247
+ [[package]]
2248
+ name = "rich"
2249
+ version = "13.9.4"
2250
+ description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
2251
+ optional = false
2252
+ python-versions = ">=3.8.0"
2253
+ files = [
2254
+ {file = "rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90"},
2255
+ {file = "rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098"},
2256
+ ]
2257
+
2258
+ [package.dependencies]
2259
+ markdown-it-py = ">=2.2.0"
2260
+ pygments = ">=2.13.0,<3.0.0"
2261
+ typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.11\""}
2262
+
2263
+ [package.extras]
2264
+ jupyter = ["ipywidgets (>=7.5.1,<9)"]
2265
+
2266
+ [[package]]
2267
+ name = "ruff"
2268
+ version = "0.8.2"
2269
+ description = "An extremely fast Python linter and code formatter, written in Rust."
2270
+ optional = false
2271
+ python-versions = ">=3.7"
2272
+ files = [
2273
+ {file = "ruff-0.8.2-py3-none-linux_armv6l.whl", hash = "sha256:c49ab4da37e7c457105aadfd2725e24305ff9bc908487a9bf8d548c6dad8bb3d"},
2274
+ {file = "ruff-0.8.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ec016beb69ac16be416c435828be702ee694c0d722505f9c1f35e1b9c0cc1bf5"},
2275
+ {file = "ruff-0.8.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:f05cdf8d050b30e2ba55c9b09330b51f9f97d36d4673213679b965d25a785f3c"},
2276
+ {file = "ruff-0.8.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:60f578c11feb1d3d257b2fb043ddb47501ab4816e7e221fbb0077f0d5d4e7b6f"},
2277
+ {file = "ruff-0.8.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cbd5cf9b0ae8f30eebc7b360171bd50f59ab29d39f06a670b3e4501a36ba5897"},
2278
+ {file = "ruff-0.8.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b402ddee3d777683de60ff76da801fa7e5e8a71038f57ee53e903afbcefdaa58"},
2279
+ {file = "ruff-0.8.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:705832cd7d85605cb7858d8a13d75993c8f3ef1397b0831289109e953d833d29"},
2280
+ {file = "ruff-0.8.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:32096b41aaf7a5cc095fa45b4167b890e4c8d3fd217603f3634c92a541de7248"},
2281
+ {file = "ruff-0.8.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e769083da9439508833cfc7c23e351e1809e67f47c50248250ce1ac52c21fb93"},
2282
+ {file = "ruff-0.8.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fe716592ae8a376c2673fdfc1f5c0c193a6d0411f90a496863c99cd9e2ae25d"},
2283
+ {file = "ruff-0.8.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:81c148825277e737493242b44c5388a300584d73d5774defa9245aaef55448b0"},
2284
+ {file = "ruff-0.8.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d261d7850c8367704874847d95febc698a950bf061c9475d4a8b7689adc4f7fa"},
2285
+ {file = "ruff-0.8.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:1ca4e3a87496dc07d2427b7dd7ffa88a1e597c28dad65ae6433ecb9f2e4f022f"},
2286
+ {file = "ruff-0.8.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:729850feed82ef2440aa27946ab39c18cb4a8889c1128a6d589ffa028ddcfc22"},
2287
+ {file = "ruff-0.8.2-py3-none-win32.whl", hash = "sha256:ac42caaa0411d6a7d9594363294416e0e48fc1279e1b0e948391695db2b3d5b1"},
2288
+ {file = "ruff-0.8.2-py3-none-win_amd64.whl", hash = "sha256:2aae99ec70abf43372612a838d97bfe77d45146254568d94926e8ed5bbb409ea"},
2289
+ {file = "ruff-0.8.2-py3-none-win_arm64.whl", hash = "sha256:fb88e2a506b70cfbc2de6fae6681c4f944f7dd5f2fe87233a7233d888bad73e8"},
2290
+ {file = "ruff-0.8.2.tar.gz", hash = "sha256:b84f4f414dda8ac7f75075c1fa0b905ac0ff25361f42e6d5da681a465e0f78e5"},
2291
+ ]
2292
+
2293
+ [[package]]
2294
+ name = "safehttpx"
2295
+ version = "0.1.6"
2296
+ description = "A small Python library created to help developers protect their applications from Server Side Request Forgery (SSRF) attacks."
2297
+ optional = false
2298
+ python-versions = ">3.9"
2299
+ files = [
2300
+ {file = "safehttpx-0.1.6-py3-none-any.whl", hash = "sha256:407cff0b410b071623087c63dd2080c3b44dc076888d8c5823c00d1e58cb381c"},
2301
+ {file = "safehttpx-0.1.6.tar.gz", hash = "sha256:b356bfc82cee3a24c395b94a2dbeabbed60aff1aa5fa3b5fe97c4f2456ebce42"},
2302
+ ]
2303
+
2304
+ [package.dependencies]
2305
+ httpx = "*"
2306
+
2307
+ [package.extras]
2308
+ dev = ["pytest"]
2309
+
2310
+ [[package]]
2311
+ name = "semantic-version"
2312
+ version = "2.10.0"
2313
+ description = "A library implementing the 'SemVer' scheme."
2314
+ optional = false
2315
+ python-versions = ">=2.7"
2316
+ files = [
2317
+ {file = "semantic_version-2.10.0-py2.py3-none-any.whl", hash = "sha256:de78a3b8e0feda74cabc54aab2da702113e33ac9d9eb9d2389bcf1f58b7d9177"},
2318
+ {file = "semantic_version-2.10.0.tar.gz", hash = "sha256:bdabb6d336998cbb378d4b9db3a4b56a1e3235701dc05ea2690d9a997ed5041c"},
2319
+ ]
2320
+
2321
+ [package.extras]
2322
+ dev = ["Django (>=1.11)", "check-manifest", "colorama (<=0.4.1)", "coverage", "flake8", "nose2", "readme-renderer (<25.0)", "tox", "wheel", "zest.releaser[recommended]"]
2323
+ doc = ["Sphinx", "sphinx-rtd-theme"]
2324
+
2325
+ [[package]]
2326
+ name = "shellingham"
2327
+ version = "1.5.4"
2328
+ description = "Tool to Detect Surrounding Shell"
2329
+ optional = false
2330
+ python-versions = ">=3.7"
2331
+ files = [
2332
+ {file = "shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686"},
2333
+ {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"},
2334
+ ]
2335
+
2336
+ [[package]]
2337
+ name = "six"
2338
+ version = "1.17.0"
2339
+ description = "Python 2 and 3 compatibility utilities"
2340
+ optional = false
2341
+ python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
2342
+ files = [
2343
+ {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"},
2344
+ {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"},
2345
+ ]
2346
+
2347
  [[package]]
2348
  name = "sniffio"
2349
  version = "1.3.1"
 
2521
  [package.extras]
2522
  blobfile = ["blobfile (>=2)"]
2523
 
2524
+ [[package]]
2525
+ name = "tomlkit"
2526
+ version = "0.13.2"
2527
+ description = "Style preserving TOML library"
2528
+ optional = false
2529
+ python-versions = ">=3.8"
2530
+ files = [
2531
+ {file = "tomlkit-0.13.2-py3-none-any.whl", hash = "sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde"},
2532
+ {file = "tomlkit-0.13.2.tar.gz", hash = "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79"},
2533
+ ]
2534
+
2535
  [[package]]
2536
  name = "tqdm"
2537
  version = "4.67.0"
 
2553
  slack = ["slack-sdk"]
2554
  telegram = ["requests"]
2555
 
2556
+ [[package]]
2557
+ name = "typer"
2558
+ version = "0.15.1"
2559
+ description = "Typer, build great CLIs. Easy to code. Based on Python type hints."
2560
+ optional = false
2561
+ python-versions = ">=3.7"
2562
+ files = [
2563
+ {file = "typer-0.15.1-py3-none-any.whl", hash = "sha256:7994fb7b8155b64d3402518560648446072864beefd44aa2dc36972a5972e847"},
2564
+ {file = "typer-0.15.1.tar.gz", hash = "sha256:a0588c0a7fa68a1978a069818657778f86abe6ff5ea6abf472f940a08bfe4f0a"},
2565
+ ]
2566
+
2567
+ [package.dependencies]
2568
+ click = ">=8.0.0"
2569
+ rich = ">=10.11.0"
2570
+ shellingham = ">=1.3.0"
2571
+ typing-extensions = ">=3.7.4.3"
2572
+
2573
  [[package]]
2574
  name = "typing-extensions"
2575
  version = "4.12.2"
 
2596
  mypy-extensions = ">=0.3.0"
2597
  typing-extensions = ">=3.7.4"
2598
 
2599
+ [[package]]
2600
+ name = "tzdata"
2601
+ version = "2024.2"
2602
+ description = "Provider of IANA time zone data"
2603
+ optional = false
2604
+ python-versions = ">=2"
2605
+ files = [
2606
+ {file = "tzdata-2024.2-py2.py3-none-any.whl", hash = "sha256:a48093786cdcde33cad18c2555e8532f34422074448fbc874186f0abd79565cd"},
2607
+ {file = "tzdata-2024.2.tar.gz", hash = "sha256:7d85cc416e9382e69095b7bdf4afd9e3880418a2413feec7069d533d6b4e31cc"},
2608
+ ]
2609
+
2610
  [[package]]
2611
  name = "urllib3"
2612
  version = "2.2.3"
 
2643
  [package.extras]
2644
  standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"]
2645
 
2646
+ [[package]]
2647
+ name = "websockets"
2648
+ version = "14.1"
2649
+ description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)"
2650
+ optional = false
2651
+ python-versions = ">=3.9"
2652
+ files = [
2653
+ {file = "websockets-14.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a0adf84bc2e7c86e8a202537b4fd50e6f7f0e4a6b6bf64d7ccb96c4cd3330b29"},
2654
+ {file = "websockets-14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:90b5d9dfbb6d07a84ed3e696012610b6da074d97453bd01e0e30744b472c8179"},
2655
+ {file = "websockets-14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2177ee3901075167f01c5e335a6685e71b162a54a89a56001f1c3e9e3d2ad250"},
2656
+ {file = "websockets-14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f14a96a0034a27f9d47fd9788913924c89612225878f8078bb9d55f859272b0"},
2657
+ {file = "websockets-14.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f874ba705deea77bcf64a9da42c1f5fc2466d8f14daf410bc7d4ceae0a9fcb0"},
2658
+ {file = "websockets-14.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9607b9a442392e690a57909c362811184ea429585a71061cd5d3c2b98065c199"},
2659
+ {file = "websockets-14.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:bea45f19b7ca000380fbd4e02552be86343080120d074b87f25593ce1700ad58"},
2660
+ {file = "websockets-14.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:219c8187b3ceeadbf2afcf0f25a4918d02da7b944d703b97d12fb01510869078"},
2661
+ {file = "websockets-14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ad2ab2547761d79926effe63de21479dfaf29834c50f98c4bf5b5480b5838434"},
2662
+ {file = "websockets-14.1-cp310-cp310-win32.whl", hash = "sha256:1288369a6a84e81b90da5dbed48610cd7e5d60af62df9851ed1d1d23a9069f10"},
2663
+ {file = "websockets-14.1-cp310-cp310-win_amd64.whl", hash = "sha256:e0744623852f1497d825a49a99bfbec9bea4f3f946df6eb9d8a2f0c37a2fec2e"},
2664
+ {file = "websockets-14.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:449d77d636f8d9c17952628cc7e3b8faf6e92a17ec581ec0c0256300717e1512"},
2665
+ {file = "websockets-14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a35f704be14768cea9790d921c2c1cc4fc52700410b1c10948511039be824aac"},
2666
+ {file = "websockets-14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b1f3628a0510bd58968c0f60447e7a692933589b791a6b572fcef374053ca280"},
2667
+ {file = "websockets-14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c3deac3748ec73ef24fc7be0b68220d14d47d6647d2f85b2771cb35ea847aa1"},
2668
+ {file = "websockets-14.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7048eb4415d46368ef29d32133134c513f507fff7d953c18c91104738a68c3b3"},
2669
+ {file = "websockets-14.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6cf0ad281c979306a6a34242b371e90e891bce504509fb6bb5246bbbf31e7b6"},
2670
+ {file = "websockets-14.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cc1fc87428c1d18b643479caa7b15db7d544652e5bf610513d4a3478dbe823d0"},
2671
+ {file = "websockets-14.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f95ba34d71e2fa0c5d225bde3b3bdb152e957150100e75c86bc7f3964c450d89"},
2672
+ {file = "websockets-14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9481a6de29105d73cf4515f2bef8eb71e17ac184c19d0b9918a3701c6c9c4f23"},
2673
+ {file = "websockets-14.1-cp311-cp311-win32.whl", hash = "sha256:368a05465f49c5949e27afd6fbe0a77ce53082185bbb2ac096a3a8afaf4de52e"},
2674
+ {file = "websockets-14.1-cp311-cp311-win_amd64.whl", hash = "sha256:6d24fc337fc055c9e83414c94e1ee0dee902a486d19d2a7f0929e49d7d604b09"},
2675
+ {file = "websockets-14.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ed907449fe5e021933e46a3e65d651f641975a768d0649fee59f10c2985529ed"},
2676
+ {file = "websockets-14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:87e31011b5c14a33b29f17eb48932e63e1dcd3fa31d72209848652310d3d1f0d"},
2677
+ {file = "websockets-14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bc6ccf7d54c02ae47a48ddf9414c54d48af9c01076a2e1023e3b486b6e72c707"},
2678
+ {file = "websockets-14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9777564c0a72a1d457f0848977a1cbe15cfa75fa2f67ce267441e465717dcf1a"},
2679
+ {file = "websockets-14.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a655bde548ca98f55b43711b0ceefd2a88a71af6350b0c168aa77562104f3f45"},
2680
+ {file = "websockets-14.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a3dfff83ca578cada2d19e665e9c8368e1598d4e787422a460ec70e531dbdd58"},
2681
+ {file = "websockets-14.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6a6c9bcf7cdc0fd41cc7b7944447982e8acfd9f0d560ea6d6845428ed0562058"},
2682
+ {file = "websockets-14.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4b6caec8576e760f2c7dd878ba817653144d5f369200b6ddf9771d64385b84d4"},
2683
+ {file = "websockets-14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:eb6d38971c800ff02e4a6afd791bbe3b923a9a57ca9aeab7314c21c84bf9ff05"},
2684
+ {file = "websockets-14.1-cp312-cp312-win32.whl", hash = "sha256:1d045cbe1358d76b24d5e20e7b1878efe578d9897a25c24e6006eef788c0fdf0"},
2685
+ {file = "websockets-14.1-cp312-cp312-win_amd64.whl", hash = "sha256:90f4c7a069c733d95c308380aae314f2cb45bd8a904fb03eb36d1a4983a4993f"},
2686
+ {file = "websockets-14.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:3630b670d5057cd9e08b9c4dab6493670e8e762a24c2c94ef312783870736ab9"},
2687
+ {file = "websockets-14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:36ebd71db3b89e1f7b1a5deaa341a654852c3518ea7a8ddfdf69cc66acc2db1b"},
2688
+ {file = "websockets-14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5b918d288958dc3fa1c5a0b9aa3256cb2b2b84c54407f4813c45d52267600cd3"},
2689
+ {file = "websockets-14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00fe5da3f037041da1ee0cf8e308374e236883f9842c7c465aa65098b1c9af59"},
2690
+ {file = "websockets-14.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8149a0f5a72ca36720981418eeffeb5c2729ea55fa179091c81a0910a114a5d2"},
2691
+ {file = "websockets-14.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77569d19a13015e840b81550922056acabc25e3f52782625bc6843cfa034e1da"},
2692
+ {file = "websockets-14.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cf5201a04550136ef870aa60ad3d29d2a59e452a7f96b94193bee6d73b8ad9a9"},
2693
+ {file = "websockets-14.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:88cf9163ef674b5be5736a584c999e98daf3aabac6e536e43286eb74c126b9c7"},
2694
+ {file = "websockets-14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:836bef7ae338a072e9d1863502026f01b14027250a4545672673057997d5c05a"},
2695
+ {file = "websockets-14.1-cp313-cp313-win32.whl", hash = "sha256:0d4290d559d68288da9f444089fd82490c8d2744309113fc26e2da6e48b65da6"},
2696
+ {file = "websockets-14.1-cp313-cp313-win_amd64.whl", hash = "sha256:8621a07991add373c3c5c2cf89e1d277e49dc82ed72c75e3afc74bd0acc446f0"},
2697
+ {file = "websockets-14.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:01bb2d4f0a6d04538d3c5dfd27c0643269656c28045a53439cbf1c004f90897a"},
2698
+ {file = "websockets-14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:414ffe86f4d6f434a8c3b7913655a1a5383b617f9bf38720e7c0799fac3ab1c6"},
2699
+ {file = "websockets-14.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8fda642151d5affdee8a430bd85496f2e2517be3a2b9d2484d633d5712b15c56"},
2700
+ {file = "websockets-14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd7c11968bc3860d5c78577f0dbc535257ccec41750675d58d8dc66aa47fe52c"},
2701
+ {file = "websockets-14.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a032855dc7db987dff813583d04f4950d14326665d7e714d584560b140ae6b8b"},
2702
+ {file = "websockets-14.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7e7ea2f782408c32d86b87a0d2c1fd8871b0399dd762364c731d86c86069a78"},
2703
+ {file = "websockets-14.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:39450e6215f7d9f6f7bc2a6da21d79374729f5d052333da4d5825af8a97e6735"},
2704
+ {file = "websockets-14.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ceada5be22fa5a5a4cdeec74e761c2ee7db287208f54c718f2df4b7e200b8d4a"},
2705
+ {file = "websockets-14.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:3fc753451d471cff90b8f467a1fc0ae64031cf2d81b7b34e1811b7e2691bc4bc"},
2706
+ {file = "websockets-14.1-cp39-cp39-win32.whl", hash = "sha256:14839f54786987ccd9d03ed7f334baec0f02272e7ec4f6e9d427ff584aeea8b4"},
2707
+ {file = "websockets-14.1-cp39-cp39-win_amd64.whl", hash = "sha256:d9fd19ecc3a4d5ae82ddbfb30962cf6d874ff943e56e0c81f5169be2fda62979"},
2708
+ {file = "websockets-14.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e5dc25a9dbd1a7f61eca4b7cb04e74ae4b963d658f9e4f9aad9cd00b688692c8"},
2709
+ {file = "websockets-14.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:04a97aca96ca2acedf0d1f332c861c5a4486fdcba7bcef35873820f940c4231e"},
2710
+ {file = "websockets-14.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df174ece723b228d3e8734a6f2a6febbd413ddec39b3dc592f5a4aa0aff28098"},
2711
+ {file = "websockets-14.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:034feb9f4286476f273b9a245fb15f02c34d9586a5bc936aff108c3ba1b21beb"},
2712
+ {file = "websockets-14.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:660c308dabd2b380807ab64b62985eaccf923a78ebc572bd485375b9ca2b7dc7"},
2713
+ {file = "websockets-14.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5a42d3ecbb2db5080fc578314439b1d79eef71d323dc661aa616fb492436af5d"},
2714
+ {file = "websockets-14.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ddaa4a390af911da6f680be8be4ff5aaf31c4c834c1a9147bc21cbcbca2d4370"},
2715
+ {file = "websockets-14.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a4c805c6034206143fbabd2d259ec5e757f8b29d0a2f0bf3d2fe5d1f60147a4a"},
2716
+ {file = "websockets-14.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:205f672a6c2c671a86d33f6d47c9b35781a998728d2c7c2a3e1cf3333fcb62b7"},
2717
+ {file = "websockets-14.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef440054124728cc49b01c33469de06755e5a7a4e83ef61934ad95fc327fbb0"},
2718
+ {file = "websockets-14.1-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7591d6f440af7f73c4bd9404f3772bfee064e639d2b6cc8c94076e71b2471c1"},
2719
+ {file = "websockets-14.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:25225cc79cfebc95ba1d24cd3ab86aaa35bcd315d12fa4358939bd55e9bd74a5"},
2720
+ {file = "websockets-14.1-py3-none-any.whl", hash = "sha256:4d4fc827a20abe6d544a119896f6b78ee13fe81cbfef416f3f2ddf09a03f0e2e"},
2721
+ {file = "websockets-14.1.tar.gz", hash = "sha256:398b10c77d471c0aab20a845e7a60076b6390bfdaac7a6d2edb0d2c59d75e8d8"},
2722
+ ]
2723
+
2724
  [[package]]
2725
  name = "yarl"
2726
  version = "1.17.1"
 
2820
  [metadata]
2821
  lock-version = "2.0"
2822
  python-versions = "^3.10"
2823
+ content-hash = "fc3fccebdc1dad30f562185560b085f474738badd36f1a674b28d11c1fe7ed23"
pyproject.toml CHANGED
@@ -14,6 +14,8 @@ langchain-community = "^0.3.7"
14
  pydantic = "^2.9.2"
15
  fastapi = "^0.115.5"
16
  uvicorn = "^0.32.0"
 
 
17
 
18
 
19
  [build-system]
 
14
  pydantic = "^2.9.2"
15
  fastapi = "^0.115.5"
16
  uvicorn = "^0.32.0"
17
+ gradio = "^5.8.0"
18
+ langchain-groq = "^0.2.1"
19
 
20
 
21
  [build-system]