enricorampazzo commited on
Commit
0fadcb9
β€’
1 Parent(s): 7b864ba

first E2E working implementation

Browse files
test.json DELETED
@@ -1,61 +0,0 @@
1
- [
2
- {
3
- "$schema": "https://json-schema.org/draft/2020-12/schema",
4
- "$id": "https://www.enricorampazzo.tech/dam_helper.schema.json",
5
- "title": "Answer",
6
- "description": "an answer to a question",
7
- "type": "array",
8
- "items": {
9
- "type": "object",
10
- "properties": {
11
- "question": {
12
- "type": "string"
13
- },
14
- "answer": {
15
- "type": ["string", "null"]
16
- }
17
- },
18
- "required": ["question", "answer"]
19
- }
20
- },
21
- {
22
- "question": "1) What do you need to do?",
23
- "answer": "Pest control"
24
- },
25
- {
26
- "question": "2) In which community is the work taking place?",
27
- "answer": "JBR"
28
- },
29
- {
30
- "question": "3) In which building?",
31
- "answer": "Sadaf 5"
32
- },
33
- {
34
- "question": "4) In which unit/apartment number?",
35
- "answer": "2408"
36
- },
37
- {
38
- "question": "5) Am I an owner or a tenant?",
39
- "answer": "unclear"
40
- },
41
- {
42
- "question": "6) In which date is the work taking place?",
43
- "answer": "7/9/2024"
44
- },
45
- {
46
- "question": "7) In which date will the work finish?",
47
- "answer": null
48
- },
49
- {
50
- "question": "8) What is my contact number?",
51
- "answer": null
52
- },
53
- {
54
- "question": "9) What is the name of the contracting company?",
55
- "answer": "Breathe Maintenance"
56
- },
57
- {
58
- "question": "10) What is the contact number of the contracting company?",
59
- "answer": null
60
- }
61
- ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
.gitignore CHANGED
@@ -1,4 +1,3 @@
1
  .idea
2
- form.pdf
3
  /.idea/
4
  /.idea/inspectionProfiles/
 
1
  .idea
 
2
  /.idea/
3
  /.idea/inspectionProfiles/
app.py CHANGED
@@ -1,78 +1,45 @@
1
- import json
2
- import re
3
- from json import JSONDecodeError
4
 
 
5
  from repository.ollama import OllamaRepository
6
- from schema import ModelRoles
 
7
 
8
- # this regex is most definitely *not* going to make my system blow up unexpectedly at some point in the future
9
- get_questions_and_answers_regex = re.compile(r'{\s*"question":\s*"([^"]+)",\s*"answer":\s*(\s*null\s*|"[^"]+"\s*)}')
10
 
 
 
11
 
12
- def print_current_status(total_success, first_try_successes, second_try_successes, regex_successes):
13
- print(f"""so far, questions and answers were parsed successfully {total_success} times, of which
14
- {first_try_successes} were successful at the first attempt,
15
- {second_try_successes} were successful after asking the model to self-correct with a 'JSON expert' agent
16
- and {regex_successes} were successful using a regex
17
- """)
18
 
19
 
20
  if __name__ == '__main__':
21
- with open("questions.txt") as questions_file:
22
- questions = questions_file.read()
23
- with open("system_prompt.txt") as system_prompt_file:
24
- system_prompt = system_prompt_file.read()
25
- with open("verification_prompt.txt") as verification_prompt_file:
26
- verification_prompt = verification_prompt_file.read()
27
- verification_prompt = verification_prompt.replace("{questions}", questions)
28
  user_prompt = input(f"Please describe what you need to do. To get the best results "
29
- f"try to answer the following questions:\n{questions}\n\n>")
30
-
31
- ollama_repository = OllamaRepository("llama3.1", system_prompt,
32
- ModelRoles("system", "user", "assistant"))
33
- successful = 0
34
- corrected_json_was_ok = 0
35
- regex_got_all_answers = 0
36
- successful_at_first_attempt = 0
37
-
38
- while True:
39
- ollama_repository.send_prompt(f"Ingest the following information: {user_prompt}")
40
- answer = ollama_repository.send_prompt(verification_prompt)
41
- ollama_repository.system_msg = "You are an expert at JSON format, and can detect and fix errors in JSON strings"
42
- fixed_json = ollama_repository.send_prompt(f"Verify if this is legal JSON and correct any mistake, output just the fixed json without adding anything else {answer['content']}")
43
- answers = fixed_json["content"]
44
- try:
45
- json_answers = json.loads(answers, strict=False)
46
- print(answers)
47
- successful_at_first_attempt +=1
48
- successful+=1
49
- print_current_status(successful, successful_at_first_attempt, corrected_json_was_ok, regex_got_all_answers)
50
- except JSONDecodeError as e:
51
- print("there was a problem in this json, asking the LLM to fix it passing the exception error message")
52
- answer = (
53
- ollama_repository.send_prompt(f"When parsing this json {answers} I got this error {str(e)}. "
54
- f"Fix this error and return just the corrected json without adding anything else"))
55
-
56
- print("trying to parse the corrected json")
57
- try:
58
- json_answers = json.loads(answer["content"])
59
- corrected_json_was_ok+=1
60
- print_current_status(successful, successful_at_first_attempt, corrected_json_was_ok,
61
- regex_got_all_answers)
62
- except JSONDecodeError as e:
63
- print("still error, going old school")
64
- regex_parse_result = {}
65
- for match in get_questions_and_answers_regex.findall(answer["content"]):
66
- question, answer = match
67
- regex_parse_result[question] = answer
68
- if len(regex_parse_result) == 10:
69
- print("I got all 10 answers apparently")
70
- successful+=1
71
- regex_got_all_answers+=1
72
- print_current_status(successful, successful_at_first_attempt, corrected_json_was_ok,
73
- regex_got_all_answers)
74
- else:
75
- print(f"unable to parse \n {answers}\n giving up")
76
- break
77
-
78
-
 
1
+ from pathlib import Path
 
 
2
 
3
+ from prompts.prompts_manager import PromptsManager
4
  from repository.ollama import OllamaRepository
5
+ from llm.llm import ModelRoles, Model
6
+ from form.form import work_categories, build_form_data_from_answers, write_pdf_form
7
 
 
 
8
 
9
+ def check_for_missing_answers(parsed_questions: dict[int, str]):
10
+ return [k for k in parsed_questions if parsed_questions[k] is None]
11
 
12
+
13
+ def ask_again(missing_questions: list[int], user_questions: list[str], parsed_questions: dict[int, str]):
14
+ for id_ in missing_questions:
15
+ answer = input(f"I could not find the answer to this question: {user_questions[id_].lower()}")
16
+ parsed_questions[id_] = answer
 
17
 
18
 
19
  if __name__ == '__main__':
20
+ prompts_manager = PromptsManager()
 
 
 
 
 
 
21
  user_prompt = input(f"Please describe what you need to do. To get the best results "
22
+ f"try to answer all the following questions:\n{'\n'.join(prompts_manager.questions)}\n\n>")
23
+
24
+ ollama_repository = OllamaRepository(Model("llama3.1",
25
+ ModelRoles("system", "user", "assistant")),
26
+ prompts_manager.system_prompt,
27
+ )
28
+ ollama_repository.send_prompt(f"Ingest the following information: {user_prompt}")
29
+ answers = {}
30
+ for idx, q in enumerate(prompts_manager.verification_prompt):
31
+ answer = ollama_repository.send_prompt(
32
+ f"Answer the following question, if the answer is not present just answer null. Keep the answer brief: {q}")
33
+ answers[idx] = None if 'null' in answer["content"].lower() else answer['content']
34
+ missing_answers = check_for_missing_answers(answers)
35
+ while missing_answers:
36
+ ask_again(missing_answers, prompts_manager.questions, answers)
37
+ missing_answers = check_for_missing_answers(answers)
38
+ answer = ollama_repository.send_prompt(
39
+ f"The work to do is {answers[1]}. Given the following categories {work_categories.values()} which ones are the most relevant? Only return one categories, separated by a semicolon")
40
+ categories = []
41
+ for category in answer["content"].split(";"):
42
+ categories.extend([k for k, v in work_categories.items() if category in v])
43
+
44
+ form_data = build_form_data_from_answers(answers, categories, f"{Path(__file__, "..", "signature.png")}")
45
+ write_pdf_form(form_data, Path("signed_form1.pdf"))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
form/__init__.py ADDED
File without changes
form/form.pdf ADDED
Binary file (697 kB). View file
 
schema.py β†’ form/form.py RENAMED
@@ -1,5 +1,40 @@
 
1
  from typing import TypedDict
 
2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
 
4
  class FormFields(TypedDict, total=True):
5
  start_date: str
@@ -44,55 +79,6 @@ class FormFields(TypedDict, total=True):
44
  minor_work: str
45
 
46
 
47
- class MinorWorkForm:
48
- def __init__(self):
49
- self.start_date: str
50
- self.end_date: str
51
- self.signed_by: str
52
- self.signature_date: str
53
- self.community: str
54
- self.email: str
55
- self.unit_no: str
56
- self.contact_number: str
57
- self.building_name: str
58
- self.contractor_name: str
59
- self.contractor_email: str
60
- self.contractor_contact_number: str
61
- self.occupant_name: str
62
- self.civil: bool
63
- self.electrical_AC: bool
64
- self.furniture_delivery: bool
65
- self.handyman: bool
66
- self.pest_control: bool
67
- self.sail_shade: bool
68
- self.wardrobes: bool
69
- self.wooden_flooring: bool
70
- self.plumbing_sanitary: bool
71
- self.safety_systems: bool
72
- self.soft_landscaping: bool
73
- self.balcony_tiles: bool
74
- self.bathroom_toilet_refurbish: bool
75
- self.staircase_railing: bool
76
- self.storage_box: bool
77
- self.tiling_works: bool
78
- self.doors_door_frames: bool
79
- self.gypsum_ceiling: bool
80
- self.kitchen_refurbish: bool
81
- self.lights_and_sockets: bool
82
- self.painting_wallpapering: bool
83
- self.seating_area_barbecuing_fountain: bool
84
- self.other_work_checkbox: bool
85
- self.owner: bool
86
- self.tenant: bool
87
- self.signature: str
88
-
89
- class ModelRoles:
90
- def __init__(self, system_role, user_role, ai_role):
91
- self.system_role = system_role
92
- self.user_role = user_role
93
- self.ai_role = ai_role
94
-
95
-
96
  form_fields_map: FormFields = {"start_date": "From", "end_date": "to", "signed_by": "name-10",
97
  "signature_date": "From--1", "community": "Name-2", "email": "Name-3",
98
  "unit_no": "Name-4", "contact_number": "Name-5", "building_name": "Name-6",
@@ -101,11 +87,28 @@ form_fields_map: FormFields = {"start_date": "From", "end_date": "to", "signed_b
101
  "other_work": "name-19", "civil": "3", "electrical_AC": "10", "furniture_delivery": "14",
102
  "handyman": "18", "pest_control": "21", "sail_shade": "12", "wardrobes": "16",
103
  "wooden_flooring": "24",
104
- "plumbing_sanitary": "4", "safety systems": "4", "soft_landscaping": "9",
105
  "balcony_tiles": "13", "bathroom_toilet_refurbish": "20",
106
  "staircase_railing": "17", "storage_box": "11", "tiling_works": "15",
107
  "doors_door_frames": "6", "gypsum_ceiling": "22", "kitchen_refurbish": "7",
108
  "lights_and_sockets": "23", "painting_wallpapering": "19",
109
  "seating_area_barbecuing_fountain": "8",
110
  "other_work_checkbox": "69", "owner": "Check Box4", "tenant": "Check Box3",
111
- "signature": "Signature2", "minor_work": "1"}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pathlib import Path
2
  from typing import TypedDict
3
+ from PyPDFForm import PdfWrapper
4
 
5
+ from utils.date_utils import get_today_date_as_dd_mm_yyyy
6
+ from utils.parsing_utils import find_and_parse_date, find_and_parse_phone_number
7
+
8
+
9
+ def build_form_data_from_answers(answers: dict[int, str], categories: list[str], signature: str | None = None):
10
+ form_data = {}
11
+ for category in categories:
12
+ form_data[form_fields_map[category]] = True
13
+ form_data[form_fields_map["minor_work"]] = True
14
+ form_data[form_fields_map["signed_by"]] = answers[0]
15
+ form_data[form_fields_map["community"]] = answers[2]
16
+ form_data[form_fields_map["building_name"]] = answers[3]
17
+ form_data[form_fields_map["unit_no"]] = answers[4]
18
+ form_data[form_fields_map["owner" if "owner" in answers[5].lower() else "tenant"]] = True
19
+ form_data[form_fields_map["start_date"]] = find_and_parse_date(answers[6])
20
+ form_data[form_fields_map["end_date"]] = find_and_parse_date(answers[7])
21
+ form_data[form_fields_map["contact_number"]] = find_and_parse_phone_number(answers[8])
22
+ form_data[form_fields_map["contractor_name"]] = answers[9]
23
+ form_data[form_fields_map["contractor_contact_number"]] = answers[10]
24
+ form_data[form_fields_map["contractor_email"]] = answers[11]
25
+ form_data[form_fields_map["email"]] = answers[12]
26
+ form_data[form_fields_map["occupant_name"]] = answers[0]
27
+ form_data[form_fields_map["signature_date"]] = get_today_date_as_dd_mm_yyyy()
28
+ if signature:
29
+ form_data[form_fields_map["signature"]] = signature
30
+ return form_data
31
+
32
+ def write_pdf_form(form_data: dict[str, str | bool | int], output_path:Path):
33
+ base_form_path = Path(__file__, "..", "form.pdf")
34
+ with open(base_form_path, "rb") as blank_form:
35
+ filled_form = PdfWrapper(blank_form).fill(form_data)
36
+ with open(output_path, "wb+") as filled_form_file:
37
+ filled_form_file.write(filled_form.read())
38
 
39
  class FormFields(TypedDict, total=True):
40
  start_date: str
 
79
  minor_work: str
80
 
81
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
  form_fields_map: FormFields = {"start_date": "From", "end_date": "to", "signed_by": "name-10",
83
  "signature_date": "From--1", "community": "Name-2", "email": "Name-3",
84
  "unit_no": "Name-4", "contact_number": "Name-5", "building_name": "Name-6",
 
87
  "other_work": "name-19", "civil": "3", "electrical_AC": "10", "furniture_delivery": "14",
88
  "handyman": "18", "pest_control": "21", "sail_shade": "12", "wardrobes": "16",
89
  "wooden_flooring": "24",
90
+ "plumbing_sanitary": "4", "safety_systems": "4", "soft_landscaping": "9",
91
  "balcony_tiles": "13", "bathroom_toilet_refurbish": "20",
92
  "staircase_railing": "17", "storage_box": "11", "tiling_works": "15",
93
  "doors_door_frames": "6", "gypsum_ceiling": "22", "kitchen_refurbish": "7",
94
  "lights_and_sockets": "23", "painting_wallpapering": "19",
95
  "seating_area_barbecuing_fountain": "8",
96
  "other_work_checkbox": "69", "owner": "Check Box4", "tenant": "Check Box3",
97
+ "signature": "Signature2", "minor_work": "2"}
98
+ work_categories = {"other_work": "other work", "civil": "Civil work, masonry",
99
+ "electrical_AC": "electrical / AC / air conditioning", "furniture_delivery": "furniture delivery",
100
+ "handyman": "handyman work, repairs, furniture assembly, furniture installation", "pest_control": "pest control",
101
+ "sail_shade": "sail shade, curtains", "wardrobes": "wardrobes",
102
+ "wooden_flooring": "wooden flooring",
103
+ "plumbing_sanitary": "plumbing / sanitary / bathroom works such as repairing a sink, tap, toilet, shower or bathtub",
104
+ "safety_systems": "safety systems, surveillance systems installation",
105
+ "soft_landscaping": "soft landscaping", "balcony_tiles": "installation and fixing of balcony tiles",
106
+ "bathroom_toilet_refurbish": "bathroom or toilet refurbishment or renovation",
107
+ "staircase_railing": "staircase rails, handrails", "storage_box": "storage box",
108
+ "tiling_works": "tiles installation or repair",
109
+ "doors_door_frames": "work related to doors and door frames",
110
+ "gypsum_ceiling": "gypsum ceiling installation or maintenance",
111
+ "kitchen_refurbish": "kitchen renovation or refurbishment",
112
+ "lights_and_sockets": "installation of lights, ceiling lights, power sockets",
113
+ "painting_wallpapering": "painting, wallpaper installation",
114
+ "seating_area_barbecuing_fountain": "installation or renovation of outdoor structures such as seating area, barbecuing area or fountains"}
llm/__init__.py ADDED
File without changes
llm/llm.py ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class ModelRoles:
2
+ def __init__(self, system_role: str, user_role: str, ai_role: str):
3
+ self.system_role: str = system_role
4
+ self.user_role: str = user_role
5
+ self.ai_role: str = ai_role
6
+
7
+
8
+ class Model:
9
+ def __init__(self, name: str, roles: ModelRoles):
10
+ self.name: str = name
11
+ self.roles: ModelRoles = roles
prompts/__init__.py ADDED
File without changes
prompts/prompts_manager.py ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import datetime
2
+ from pathlib import Path
3
+
4
+ from utils.date_utils import get_today_date_as_dd_mm_yyyy
5
+
6
+
7
+ class PromptsManager:
8
+ def __init__(self):
9
+ base_path = Path(__file__, "..")
10
+ with open(Path(base_path, "system_prompt.txt")) as sysprompt_file:
11
+ self.system_prompt: str = sysprompt_file.read()
12
+ with open(Path(base_path, "questions.txt")) as questions_file:
13
+ self.questions: list[str] = questions_file.readlines()
14
+ with open(Path(base_path, "verification_prompt2.txt")) as verification_prompt_file:
15
+ verification_prompt = verification_prompt_file.readlines()
16
+ todays_date = get_today_date_as_dd_mm_yyyy()
17
+ for line in verification_prompt:
18
+ line.replace("{today}", todays_date)
19
+ self.verification_prompt: list[str] = verification_prompt
prompts/questions.txt ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ What is your full name?
2
+ What is the nature of the work you need to do?
3
+ In which community is the work taking place?
4
+ In which building?
5
+ In which unit/apartment number?
6
+ Are you an owner or a tenant?
7
+ In which date is the work taking place?
8
+ In which date will the work finish?
9
+ What is your contact number?
10
+ What is the name of the contracting company?
11
+ What is the contact number of the contracting company?
12
+ What is the email of the contracting company?
13
+ What is your email?
system_prompt.txt β†’ prompts/system_prompt.txt RENAMED
File without changes
prompts/verification_prompt2.txt ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ What is my full name?
2
+ What is the nature of the work I need to do?
3
+ In which community is the work taking place?
4
+ In which building?
5
+ In which unit/apartment number?
6
+ Am I the owner or the tenant?
7
+ In which date is the work taking place? Please answer with just a date formatted as dd/mm/yyyy. In case I used expressions like today, tomorrow, in two days, ecc, know that today it is 08/09/2024
8
+ In which date will the work finish? Please answer with just a date formatted as dd/mm/yyyy. In case I used expressions like today, tomorrow, in two days, ecc, know that today it is {today}. If no date is provided, consider that it will finish on the same day as the start date
9
+ What is my contact number?
10
+ What is the name of the contracting company?
11
+ What is the contact number of the contracting company?
12
+ What is the email of the contracting company?
13
+ What is your email?
questions.txt DELETED
@@ -1,10 +0,0 @@
1
- 1) What do you need to do?
2
- 2) In which community is the work taking place?
3
- 3) In which building?
4
- 4) In which unit/apartment number?
5
- 5) Am I an owner or a tenant?
6
- 6) In which date is the work taking place?
7
- 7) In which date will the work finish?
8
- 8) What is my contact number?
9
- 9) What is the name of the contracting company?
10
- 10) What is the contact number of the contracting company?
 
 
 
 
 
 
 
 
 
 
 
repository/ollama.py CHANGED
@@ -1,21 +1,22 @@
1
  import ollama
2
  from ollama import Options
3
 
4
- from schema import ModelRoles
5
 
6
 
7
  class OllamaRepository:
8
- def __init__(self, model, system_msg, roles: ModelRoles):
9
- self.model = model
10
- self.system_msg = system_msg
11
- self.roles = roles
12
- self.message_history: list[dict[str, str]] = [{"role": self.roles.system_role, "content": system_msg}]
13
 
14
- def send_prompt(self, prompt:str, add_to_history:bool = False) -> dict[str, str]:
15
- options: Options = Options(temperature=0.1)
16
- self.message_history.append({"role": self.roles.user_role, "content":prompt})
17
- response = ollama.chat(self.model, self.message_history, options=options)
18
- answer = {"role": self.roles.ai_role, "content": response["message"]["content"]}
19
  if add_to_history:
20
  self.message_history.append(answer)
 
 
21
  return answer
 
1
  import ollama
2
  from ollama import Options
3
 
4
+ from llm.llm import ModelRoles, Model
5
 
6
 
7
  class OllamaRepository:
8
+ def __init__(self, model:Model, system_msg):
9
+ self.model: Model = model
10
+ self.system_msg: str = system_msg
11
+ self.message_history: list[dict[str, str]] = [{"role": self.model.roles.system_role, "content": system_msg}]
 
12
 
13
+ def send_prompt(self, prompt:str, add_to_history:bool = True) -> dict[str, str]:
14
+ options: Options = Options(temperature=0)
15
+ self.message_history.append({"role": self.model.roles.user_role, "content":prompt})
16
+ response = ollama.chat(self.model.name, self.message_history, options=options)
17
+ answer = {"role": self.model.roles.ai_role, "content": response["message"]["content"]}
18
  if add_to_history:
19
  self.message_history.append(answer)
20
+ else:
21
+ self.message_history.pop()
22
  return answer
utils/__init__.py ADDED
File without changes
utils/date_utils.py ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ import datetime
2
+ def get_today_date_as_dd_mm_yyyy():
3
+ return datetime.datetime.now().strftime("%d/%m/%Y")
utils/parsing_utils.py ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import re
2
+ parse_date_regex = re.compile(r"([0-9]{1,2}/[0-9]{1,2}/[0-9]{4})")
3
+ parse_phone_number_regex = re.compile(r"\+?[0-9]*")
4
+
5
+
6
+ def _find_and_parse(llm_answer, regex):
7
+ match = regex.findall(llm_answer)
8
+ return next(iter(match), None) if match else None
9
+
10
+
11
+ def find_and_parse_date(llm_answer: str) -> str | None:
12
+ return _find_and_parse(llm_answer, parse_date_regex)
13
+
14
+
15
+ def find_and_parse_phone_number(llm_answer: str):
16
+ return _find_and_parse(llm_answer, parse_phone_number_regex)
17
+
verification_prompt.txt DELETED
@@ -1,27 +0,0 @@
1
- Please tell if I answered the following questions:
2
-
3
- {questions}
4
-
5
- The answer should be a list of json objects that follow this schema and be legal json.
6
- {
7
- "$schema": "https://json-schema.org/draft/2020-12/schema",
8
- "$id": "https://www.enricorampazzo.tech/dam_helper.schema.json",
9
- "title": "Answer",
10
- "description": "an answer to a question",
11
- "type": "object",
12
- "properties": {
13
- "question": {
14
- "description": "the question being answered",
15
- "type": "string"
16
- },
17
- "answer": {
18
- "description": "the answer to the question",
19
- "type": "string"
20
-
21
- }
22
- }
23
- }
24
- if the answer is a date format it as day/month/year.
25
- If the answer contains temporal references such as 'tomorrow', 'in two days' etc. consider that today it is the
26
- 6th of september 2024.
27
- If the answer is not provided just the answer string should be null