brestok commited on
Commit
7cfbfed
·
1 Parent(s): 030cdaf

Add functionality for handling empty responses in AI workflows

Browse files

Enhanced AI response generation to handle cases where no suitable clinics are found by providing relevant suggestions. Updated database handling and utility functions to support tracking and managing changes in entity data, ensuring better user feedback.

trauma/api/data/db_requests.py CHANGED
@@ -1,4 +1,5 @@
1
  import asyncio
 
2
  import re
3
 
4
  from fastapi import HTTPException
@@ -35,3 +36,19 @@ async def search_facilities_obj(data: SearchRequest) -> tuple[list[EntityModel],
35
  settings.DB_CLIENT.entities.count_documents(regex_filter)
36
  )
37
  return [EntityModel.from_mongo(ent) for ent in objects], total_count
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import asyncio
2
+ import json
3
  import re
4
 
5
  from fastapi import HTTPException
 
36
  settings.DB_CLIENT.entities.count_documents(regex_filter)
37
  )
38
  return [EntityModel.from_mongo(ent) for ent in objects], total_count
39
+
40
+
41
+ async def check_instructions():
42
+ pipeline = [
43
+ {"$unwind": "$contactDetails.postalCode"},
44
+ {"$group": {"_id": None, "uniqueTreatmentMethods": {"$addToSet": "$contactDetails.postalCode"}}}
45
+ ]
46
+ result = await settings.DB_CLIENT.entities.aggregate(pipeline).to_list(length=1)
47
+ if result:
48
+ t = result[0]["uniqueTreatmentMethods"]
49
+ with open('test.json', 'w') as f:
50
+ f.write(json.dumps({"t": t}, indent=2))
51
+ return []
52
+
53
+ if __name__ == "__main__":
54
+ asyncio.run(check_instructions())
trauma/api/message/ai/engine.py CHANGED
@@ -14,19 +14,21 @@ from trauma.api.message.ai.openai_request import (update_entity_data_with_ai,
14
  choose_closest_treatment_area,
15
  check_is_valid_request,
16
  generate_invalid_response,
17
- set_entity_score)
18
  from trauma.api.message.db_requests import (save_assistant_user_message,
19
  filter_entities_by_age_location,
20
  update_entity_data_obj, get_entities_bulk)
21
  from trauma.api.message.dto import Author
22
  from trauma.api.message.model import MessageModel
23
  from trauma.api.message.schemas import CreateMessageResponse
24
- from trauma.api.message.utils import (decode_treatment_letters,
25
  prepare_message_history_str,
26
  retrieve_empty_field_from_entity_data,
27
  prepare_user_messages_str,
28
  prepare_final_entities_str,
29
- pick_empty_field_instructions, find_matching_age_group)
 
 
30
  from trauma.core.config import settings
31
 
32
 
@@ -40,7 +42,7 @@ async def search_entities(
40
  update_entity_data_with_ai(chat.entityData, decoded_message, messages[-1].text),
41
  check_is_valid_request(decoded_message, message_history_str)
42
  )
43
- final_entities = None
44
 
45
  if not is_valid:
46
  empty_field = retrieve_empty_field_from_entity_data(chat.entityData.model_dump(mode='json'))
@@ -62,9 +64,14 @@ async def search_entities(
62
  )
63
  final_entities = await search_semantic_entities(search_request, entity_data, possible_entity_indexes)
64
  final_entities_str = prepare_final_entities_str(final_entities)
65
- response = await generate_final_response(
66
- final_entities_str, decoded_message, message_history_str, empty_field_instructions
67
- )
 
 
 
 
 
68
 
69
  user_message = MessageModel(chatId=chat.id, author=Author.User, text=decoded_message)
70
  assistant_message = MessageModel(chatId=chat.id, author=Author.Assistant, text=response, entities=final_entities)
 
14
  choose_closest_treatment_area,
15
  check_is_valid_request,
16
  generate_invalid_response,
17
+ set_entity_score, generate_empty_final_response)
18
  from trauma.api.message.db_requests import (save_assistant_user_message,
19
  filter_entities_by_age_location,
20
  update_entity_data_obj, get_entities_bulk)
21
  from trauma.api.message.dto import Author
22
  from trauma.api.message.model import MessageModel
23
  from trauma.api.message.schemas import CreateMessageResponse
24
+ from trauma.api.message.utils import (decode_treatment_letters,
25
  prepare_message_history_str,
26
  retrieve_empty_field_from_entity_data,
27
  prepare_user_messages_str,
28
  prepare_final_entities_str,
29
+ pick_empty_field_instructions,
30
+ find_matching_age_group,
31
+ search_changed_field_inst)
32
  from trauma.core.config import settings
33
 
34
 
 
42
  update_entity_data_with_ai(chat.entityData, decoded_message, messages[-1].text),
43
  check_is_valid_request(decoded_message, message_history_str)
44
  )
45
+ final_entities, fields_changed_inst = None, search_changed_field_inst(entity_data, chat.entityData)
46
 
47
  if not is_valid:
48
  empty_field = retrieve_empty_field_from_entity_data(chat.entityData.model_dump(mode='json'))
 
64
  )
65
  final_entities = await search_semantic_entities(search_request, entity_data, possible_entity_indexes)
66
  final_entities_str = prepare_final_entities_str(final_entities)
67
+ if final_entities:
68
+ response = await generate_final_response(
69
+ final_entities_str, decoded_message, message_history_str, empty_field_instructions
70
+ )
71
+ else:
72
+ response = await generate_empty_final_response(
73
+ decoded_message, message_history_str, fields_changed_inst
74
+ )
75
 
76
  user_message = MessageModel(chatId=chat.id, author=Author.User, text=decoded_message)
77
  assistant_message = MessageModel(chatId=chat.id, author=Author.Assistant, text=response, entities=final_entities)
trauma/api/message/ai/openai_request.py CHANGED
@@ -71,6 +71,21 @@ async def generate_final_response(
71
  ]
72
  return messages
73
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
 
75
  async def convert_value_to_embeddings(value: str, dimensions: int = 1536) -> list[float]:
76
  embeddings = await settings.OPENAI_CLIENT.embeddings.create(
 
71
  ]
72
  return messages
73
 
74
+ @openai_wrapper(temperature=0.8)
75
+ async def generate_empty_final_response(
76
+ user_message: str, message_history_str: str, empty_field_instructions: dict
77
+ ):
78
+ messages = [
79
+ {
80
+ "role": "system",
81
+ "content": TraumaPrompts.generate_empty_recommendations
82
+ .replace("{message_history}", message_history_str)
83
+ .replace("{user_message}", user_message)
84
+ .replace("{instructions}", json.dumps(empty_field_instructions, indent=2))
85
+ }
86
+ ]
87
+ return messages
88
+
89
 
90
  async def convert_value_to_embeddings(value: str, dimensions: int = 1536) -> list[float]:
91
  embeddings = await settings.OPENAI_CLIENT.embeddings.create(
trauma/api/message/ai/prompts.py CHANGED
@@ -242,29 +242,34 @@ Stel op een beleefde manier enkele medische instellingen voor op basis van de in
242
 
243
  generate_empty_recommendations = """## Taak
244
 
245
- Je moet de gebruiker op een empathische en ondersteunende manier informeren dat er geen geschikte klinieken voor de patiënt zijn gevonden. Moedig de gebruiker aan om de informatie opnieuw in te voeren met aanvullende details om betere zoekresultaten te genereren. Geef hierbij een voorbeeld van een geschikte invoer, zoals: 'jongen van 16 jaar met LVB waarvoor ik EMDR zoek'.
246
 
247
  ## Context
248
 
249
- De gebruiker zoekt naar een geschikte kliniek voor een patiënt en deelt hierbij details zoals ziekte, leeftijd en behandelmethoden. Ondanks deze informatie heeft het systeem geen geschikte kliniek kunnen vinden die voldoet aan de eisen. Het is jouw taak om dit op een vriendelijke manier te communiceren en suggesties te geven voor het herzien of aanvullen van de ingevoerde gegevens.
250
 
251
- ## Data
252
 
253
- **Gebruikersquery**:
254
  ```
255
  {user_message}
256
  ```
257
 
258
- **Gesprekshistorie**:
259
  ```
260
  {message_history}
261
  ```
262
 
 
 
 
 
 
263
  ## Belangrijke opmerkingen
264
 
265
- - Gebruik een vriendelijke en geruststellende toon, bijvoorbeeld: "Ik heb op basis van de ingevoerde gegevens geen kliniek kunnen vinden. Kunnen we samen kijken of we de informatie iets kunnen aanpassen om betere resultaten te krijgen?"
266
- - Geef praktische suggesties, zoals: "Misschien helpt het om meer details over de locatie of de behandelmethode te delen."
267
- - Stel open vragen om de gebruiker te begeleiden bij het verfijnen van de gegevens, zoals: "Zijn er andere belangrijke punten die we kunnen toevoegen?"""
268
  generate_searched_entity = """## Taak
269
 
270
  Je moet de gevraagde faciliteit beschrijven, waarvan de informatie wordt gegeven in de sectie `## Data`. Analyseer de gebruikersvraag en de informatie over de faciliteit, en geef een beknopt en bondig antwoord.
 
242
 
243
  generate_empty_recommendations = """## Taak
244
 
245
+ Je moet de gebruiker op een empathische en ondersteunende manier informeren dat er geen geschikte klinieken voor de patiënt zijn gevonden, en je moet de gebruiker voorstellen om te zoeken door de meest relevante mogelijke opties uit `Possible values` te geven.
246
 
247
  ## Context
248
 
249
+ De gebruiker zoekt een geschikte kliniek voor een patiënt en deelt details zoals de ziekte, leeftijd en behandelingsmethoden, evenals informatie over de kliniek. In het laatste bericht heeft de gebruiker echter informatie verstrekt waaruit geen klinieken zijn gevonden. Je moet op een empathische en beleefde toon de meest semantisch vergelijkbare opties uit `Possible values` voorstellen waarvoor klinieken beschikbaar zijn.
250
 
251
+ ## Gegevens
252
 
253
+ **Gebruikersverzoek**:
254
  ```
255
  {user_message}
256
  ```
257
 
258
+ **Gespreksgeschiedenis**:
259
  ```
260
  {message_history}
261
  ```
262
 
263
+ **Mogelijke varianten**:
264
+ ```
265
+ {instructions}
266
+ ```
267
+
268
  ## Belangrijke opmerkingen
269
 
270
+ - Gebruik een vriendelijke en rustgevende toon.
271
+ - Stel de meest semantisch vergelijkbare voorbeelden voor uit `Possible variants` om een geschikte kliniek te vinden.
272
+ - Stel open vragen om de gebruiker te helpen de gegevens te verduidelijken, bijvoorbeeld: "Zijn er andere belangrijke punten die we kunnen toevoegen?\""""
273
  generate_searched_entity = """## Taak
274
 
275
  Je moet de gevraagde faciliteit beschrijven, waarvan de informatie wordt gegeven in de sectie `## Data`. Analyseer de gebruikersvraag en de informatie over de faciliteit, en geef een beknopt en bondig antwoord.
trauma/api/message/utils.py CHANGED
@@ -1,6 +1,7 @@
1
  import json
2
  import re
3
 
 
4
  from trauma.api.data.dto import AgeGroup
5
  from trauma.api.data.model import EntityModel
6
  from trauma.api.message.dto import Author
@@ -104,3 +105,166 @@ def decode_treatment_letters(user_message: str) -> str:
104
  for short, full in replacements.items():
105
  user_message = re.sub(rf'\b{short}\b', full, user_message, flags=re.IGNORECASE)
106
  return user_message
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import json
2
  import re
3
 
4
+ from trauma.api.chat.dto import EntityData
5
  from trauma.api.data.dto import AgeGroup
6
  from trauma.api.data.model import EntityModel
7
  from trauma.api.message.dto import Author
 
105
  for short, full in replacements.items():
106
  user_message = re.sub(rf'\b{short}\b', full, user_message, flags=re.IGNORECASE)
107
  return user_message
108
+
109
+
110
+ def search_changed_field_inst(entity_data: dict, old_entity_data: EntityData) -> dict[str, str]:
111
+ changed_fields = {}
112
+ instruction_map = {
113
+ "age": {'ageMinSupported': 0, "ageMaxSupported": 23},
114
+ "treatmentMethod": [
115
+ "Hypnose",
116
+ "Hypnotherapie",
117
+ "eclectisch",
118
+ "Schematherapie",
119
+ "BEPP (Beknopte Eclectische Psychotherapie bij PTSS",
120
+ "Muziektherapie, klankbehandeling",
121
+ "FITT, family based intensive trauma treatment",
122
+ "systeemtherapie & schematherapie",
123
+ "Narratieve Exposure Therapie (NET)",
124
+ "Ouder-kindtraumatherapie",
125
+ "Lichaamsgerichte therapie",
126
+ "integratieve therapie",
127
+ "Havening, hypnose",
128
+ "Psychotherapie",
129
+ "TIST",
130
+ "Words and pictures",
131
+ "Integratieve Kindertherapie",
132
+ "Buitenpsychologie",
133
+ "systeemtherapie gericht op scheidingsproblematiek",
134
+ "NET",
135
+ "Eigen methodiek, SSP, Focustherapie",
136
+ "Systeemtherapie",
137
+ "BEPP (beknopte eclectische psychotherapie voor PTSS), BEP-TG (beknopte eclectische psychotherapie voor traumatische rouw), NET (narratieve exposure therapie), schematherapie",
138
+ "Narratieve Exposure therapie",
139
+ "Writejunior",
140
+ "Tem Je Draak",
141
+ "EMDR voor ouders",
142
+ "Methodes vanuit de systeemtherapie",
143
+ "CGT (oa exposure)",
144
+ "Horizonmethodiek",
145
+ "TF-CBT",
146
+ "BEPP protocol",
147
+ "Speltherapie",
148
+ "ouder-kind therapie (vanuit de IMH visie)",
149
+ "IGT-K",
150
+ "medicamenteus",
151
+ "Verwerken en versterken",
152
+ "Pre-verbale EMDR",
153
+ "Kortdurende Intensieve traumabehandeling",
154
+ "aantal bovenstaande nog in ontwikkeling, deze niet aangevinkt nu",
155
+ "Drakentemmers",
156
+ "BEPP",
157
+ "schematherapie",
158
+ "Hypno-regressie therapie",
159
+ "ImRS",
160
+ "Beeldende therapie",
161
+ "sensorimotor psychotherapie",
162
+ "Cref-methode /paarde coaching",
163
+ "KINGS",
164
+ "KINGS in voorbereiding",
165
+ "systeemtherapie",
166
+ "EMDR",
167
+ "Ouder Kind Trauma Therapie",
168
+ "BEPP, NET",
169
+ "IEMT",
170
+ "Bokstherapie",
171
+ "babytherapie",
172
+ "sensorimotorpsychotherapie",
173
+ "Dramatherapie",
174
+ "BEPP / Sensorimotor Psychotherapie",
175
+ "Psychomotorische Therapie",
176
+ "Slapende honden",
177
+ "Nika, psycho-educatie",
178
+ "Basic Trust-methode, schematherapie, behandeling gericht op rouw en verlies",
179
+ "Intensieve Ambulante Gezinstherapie",
180
+ "EFFT / EFIT en ABFT",
181
+ "DDP, ABFT, EFFT, AFFT, Theraplay, schematherapie",
182
+ "Imaginaire Exposure",
183
+ "Bokspsychotherapie",
184
+ "ABFT",
185
+ "Lichaamsgerichte, innerlijk bronnen en systemische traumatherapie",
186
+ "NET (Narratieve Exposure Therapie)"
187
+ ],
188
+ "treatmentArea": [
189
+ "Geen beschikking nodig",
190
+ "Normaal begaafd",
191
+ "langdurig pesten behandelen wij ook, waarbij wel wordt gekeken of het niet gaat om complex trauma, dan wordt veelal verwezen naar de S-GGZ, wij bieden GB-GGZ. Een verstandelijke beperking is niet perse een contra, maar schatten dit per casus in. Wanneer het gaat om een licht verstandelijke beperkt niveau tot zwakbegaafd is het vaak wel mogelijk.",
192
+ "automutilatie, geen acute suicidaliteit/crisis",
193
+ "Trauma in het systeem",
194
+ "Visuele beperking",
195
+ "Vluchtelingenproblematiek",
196
+ "Taalontwikkelingsstoornis",
197
+ "ASS",
198
+ "vaak is het comorbiditeit en niet de hoofddiagnose. Ik doe vaak traumatherapie/combi EMDR bij persoonlijkheidsproblematiek",
199
+ "als er sprake is van een VB dan ook bij ASS, ADHD etc",
200
+ "Geen onderscheid",
201
+ "intergenerationeel trauma",
202
+ "Genderproblematiek",
203
+ "Hoogbegaafd",
204
+ "misbruik, grote somberte, bij suicidaliteit en/of automutilatie altijd contact met arts of psychiater",
205
+ "deelbehandeling in samenwerking met andere behandelsetting",
206
+ "(Lichte) Verstandelijke Beperking",
207
+ "Auditieve beperking",
208
+ "Transcultureel",
209
+ "ouder-kind relatieproblematiek",
210
+ "Verslaving",
211
+ "generationeel trauma ivm systemische problematiek",
212
+ "Meervoudig trauma",
213
+ "loyaliteitsproblematiek/ouderverstoting/oudervervreemding",
214
+ "Als er sprake is van een indicatie, dan pakken wij dit als wijkteam in het voorveld niet op",
215
+ "Bijna alle psychische aandoening waar ambulante behandeling van toepassing is",
216
+ "Sucidaliteit en/of automutilatie",
217
+ "Comorbiditeit is geen contra voor traumabehandeling. Middelengebruik moet gestopt zijn voor de traumabehandeling kan starten.",
218
+ "Eetstoornis",
219
+ "overbrugging wachtlijst",
220
+ "Enkelvoudig trauma"
221
+ ],
222
+ "location": [
223
+ "Aalsmeer",
224
+ "Alkmaar",
225
+ "Amstelveen",
226
+ "Amsterdam",
227
+ "Badhoevedorp",
228
+ "Beverwijk",
229
+ "Bergen NH",
230
+ "Broek op Langedijk",
231
+ "Bussum",
232
+ "Callantsoog",
233
+ "Castricum",
234
+ "Cruquiusweg 32",
235
+ "Den Helder",
236
+ "Diemen",
237
+ "Edam",
238
+ "Enkhuizen",
239
+ "Heemstede",
240
+ "Heerhugowaard",
241
+ "Heiloo",
242
+ "Hilversum",
243
+ "Hoofddorp",
244
+ "Hoorn",
245
+ "Huizen",
246
+ "Janmaat Psychotherapie Haarlem",
247
+ "Krommenie",
248
+ "Laren NH",
249
+ "Monnickendam",
250
+ "Naarden",
251
+ "Noord-Scharwoude",
252
+ "Purmerend",
253
+ "Schagen",
254
+ "Spierdijk",
255
+ "Velsen",
256
+ "Vondelstraat 100",
257
+ "Weesp",
258
+ "Wormerland",
259
+ "Zaanstad",
260
+ "Zaandam",
261
+ "Zuid-Kennemerland/ IJmond"
262
+ ],
263
+ "postalCode": None,
264
+ }
265
+ old_entity_data = old_entity_data.model_dump(mode='json')
266
+ for key, value in entity_data.items():
267
+ if key in old_entity_data and old_entity_data[key] != value:
268
+ real_key = key if key!="treatmentArea" else "traumaType"
269
+ changed_fields[real_key] = instruction_map[key]
270
+ return changed_fields
trauma/api/message/views.py CHANGED
@@ -10,9 +10,7 @@ from trauma.api.message.dto import Feedback
10
  from trauma.api.message.model import MessageModel
11
  from trauma.api.message.schemas import (AllMessageWrapper,
12
  AllMessageResponse,
13
- CreateMessageRequest,
14
- CreateMessageResponse)
15
- from trauma.api.message.utils import transform_messages_to_openai
16
  from trauma.core.security import PermissionDependency
17
  from trauma.core.wrappers import TraumaResponseWrapper
18
 
@@ -49,3 +47,4 @@ async def create_feedback(
49
  ) -> TraumaResponseWrapper[MessageModel]:
50
  message = await update_message_feedback_obj(messageId, feedback_data)
51
  return TraumaResponseWrapper(data=message)
 
 
10
  from trauma.api.message.model import MessageModel
11
  from trauma.api.message.schemas import (AllMessageWrapper,
12
  AllMessageResponse,
13
+ CreateMessageRequest)
 
 
14
  from trauma.core.security import PermissionDependency
15
  from trauma.core.wrappers import TraumaResponseWrapper
16
 
 
47
  ) -> TraumaResponseWrapper[MessageModel]:
48
  message = await update_message_feedback_obj(messageId, feedback_data)
49
  return TraumaResponseWrapper(data=message)
50
+