Danialebrat commited on
Commit
09bc76a
·
1 Parent(s): 8a8d6cc

- Making Gemini more robust with error hnadling and parsing various kind of outputs that are possible.

Browse files

- integrating gemini-2.5-flash
- integrating gpt5-mini and nano
- Estimating the prices and times per 1000 messages for each model.

Config_files/message_system_config.json CHANGED
@@ -18,16 +18,17 @@
18
  "genres"
19
  ],
20
  "AI_Jargon": ["elevate", "enhance", "ignite", "reignite", "rekindle", "rediscover","passion", "boost", "fuel", "thrill", "revive", "spark", "performing", "fresh", "tone", "enthusiasm", "illuminate"],
 
21
  "AI_phrases_singeo": ["your voice deserves more"],
22
  "header_limit": 30,
23
  "message_limit": 135,
24
- "LLM_models": ["gpt-4o-mini", "gpt-4o", "gpt-4.1-mini", "gemini-2.5-flash-lite-preview-06-17", "claude-3-5-haiku-latest", "google/gemma-3-27b-instruct/bf-16"],
25
- "openai_models": ["gpt-4o-mini", "gpt-4o", "gpt-4.1-nano", "gpt-3.5-turbo", "gpt-4.1-mini"],
26
- "reasoning": ["o1", "o4-mini", "o1-mini", "o3-mini"],
27
  "ollama_models": ["deepseek-r1:1.5b", "gemma3:4b", "deepseek-r1:7b", "gemma3:4b"],
28
  "claude_models": ["claude-3-5-haiku-latest"],
29
  "inference_models": ["google/gemma-3-27b-instruct/bf-16", "meta-llama/llama-3.2-11b-instruct/fp-16"],
30
- "google_models": ["gemini-2.5-flash-lite-preview-06-17"]
31
  }
32
 
33
 
 
18
  "genres"
19
  ],
20
  "AI_Jargon": ["elevate", "enhance", "ignite", "reignite", "rekindle", "rediscover","passion", "boost", "fuel", "thrill", "revive", "spark", "performing", "fresh", "tone", "enthusiasm", "illuminate"],
21
+ "singeo_banned_phrases": " Banned phrases:\n Voice is NOT an instrument, so avoid phrases like below:\n - Your voice is waiting\n - Your voice awaits\n - Your voice needs you\n - Your voice is calling\n - Your voice deserves more\n - Hit the high notes / Hit those notes\n - ...\n",
22
  "AI_phrases_singeo": ["your voice deserves more"],
23
  "header_limit": 30,
24
  "message_limit": 135,
25
+ "LLM_models": ["gpt-4o-mini", "gpt-5-mini", "gpt-5-nano", "gemini-2.5-flash","claude-3-5-haiku-latest", "google/gemma-3-27b-instruct/bf-16"],
26
+ "openai_models": ["gpt-4o-mini", "gpt-4o", "gpt-4.1-nano", "gpt-3.5-turbo", "gpt-4.1-mini", "gpt-5-mini", "gpt-5-nano"],
27
+ "reasoning": ["o1", "o4-mini", "o1-mini", "o3-mini", "gpt-5-mini", "gpt-5-nano"],
28
  "ollama_models": ["deepseek-r1:1.5b", "gemma3:4b", "deepseek-r1:7b", "gemma3:4b"],
29
  "claude_models": ["claude-3-5-haiku-latest"],
30
  "inference_models": ["google/gemma-3-27b-instruct/bf-16", "meta-llama/llama-3.2-11b-instruct/fp-16"],
31
+ "google_models": ["gemini-2.5-flash-lite", "gemini-2.5-flash", "gemini-2.0-flash"]
32
  }
33
 
34
 
Messaging_system/CoreConfig.py CHANGED
@@ -1,7 +1,7 @@
1
  """
2
  the flow of the Program starts from create_personalized_message function
3
  """
4
-
5
  import time
6
  from Messaging_system.SnowFlakeConnection import SnowFlakeConn
7
 
@@ -75,16 +75,17 @@ class CoreConfig:
75
  # ===============================================================
76
  def get_brand_voice(self):
77
  """
78
- get positive phrases for the brand to instruct the llm to talk like this
79
- :return:
 
80
  """
81
- # assuming your file is named 'example.txt' and is in the same directory
82
  file_path = f'Config_files/{self.brand.lower()}_phrases.txt'
83
 
84
- with open(file_path, 'r', encoding='utf-8') as f:
85
- string = f.read()
86
 
87
- return string
 
88
 
89
  # --------------------------------------------------------------
90
  # --------------------------------------------------------------
 
1
  """
2
  the flow of the Program starts from create_personalized_message function
3
  """
4
+ import os
5
  import time
6
  from Messaging_system.SnowFlakeConnection import SnowFlakeConn
7
 
 
75
  # ===============================================================
76
  def get_brand_voice(self):
77
  """
78
+ Get positive phrases for the brand to instruct the LLM to talk like this.
79
+ Returns:
80
+ str: The brand voice phrases if file exists, otherwise None.
81
  """
 
82
  file_path = f'Config_files/{self.brand.lower()}_phrases.txt'
83
 
84
+ if not os.path.exists(file_path):
85
+ return None
86
 
87
+ with open(file_path, 'r', encoding='utf-8') as f:
88
+ return f.read()
89
 
90
  # --------------------------------------------------------------
91
  # --------------------------------------------------------------
Messaging_system/LLM.py CHANGED
@@ -171,6 +171,17 @@ class LLM:
171
  response_mime_type="application/json"
172
  ))
173
 
 
 
 
 
 
 
 
 
 
 
 
174
  output = self.preprocess_and_parse_json(response.text)
175
  # output = json.loads(str(response.text))
176
 
@@ -218,7 +229,7 @@ class LLM:
218
  {"role": "system", "content": instructions},
219
  {"role": "user", "content": prompt}
220
  ],
221
- reasoning_effort="medium",
222
  n=1,
223
  )
224
 
@@ -240,6 +251,11 @@ class LLM:
240
  'total_tokens': response.usage.total_tokens
241
  }
242
 
 
 
 
 
 
243
  try:
244
  content = response.choices[0].message.content
245
 
@@ -258,10 +274,6 @@ class LLM:
258
  f"'header' or 'message' is more than specified characters in response on attempt {attempt + 1}. Retrying...")
259
  continue
260
 
261
- # validating the JSON
262
- self.Core.total_tokens['prompt_tokens'] += tokens['prompt_tokens']
263
- self.Core.total_tokens['completion_tokens'] += tokens['completion_tokens']
264
- self.Core.temp_token_counter += tokens['total_tokens']
265
  return output
266
 
267
  except json.JSONDecodeError:
@@ -398,30 +410,56 @@ class LLM:
398
 
399
  def preprocess_and_parse_json(self, response: str):
400
  """
401
- Cleans an LLM response by removing <think> tags and extracting JSON
402
- from ```json ... ``` fences (or bare text if no fence is found),
403
- then returns the parsed object or None on failure.
404
  """
405
- # 1) Remove all <think>...</think> blocks
406
- cleaned = re.sub(r'<think>.*?</think>', '', response, flags=re.DOTALL)
407
-
408
- # 2) Look for a ```json ... ``` fenced block
409
- fence_pattern = re.compile(r'```json(.*?)```', flags=re.DOTALL)
410
- fence_match = fence_pattern.search(cleaned)
411
- if fence_match:
412
- json_text = fence_match.group(1).strip()
413
- else:
414
- # No fence; assume whole cleaned text is JSON
415
- json_text = cleaned.strip()
416
 
417
- # 3) Attempt to parse
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
418
  try:
419
- return json.loads(json_text)
420
- except json.JSONDecodeError as e:
421
- print(f"Failed to parse JSON: {e}")
422
- # Optionally, log the offending text for debugging:
423
- # print("Offending text:", json_text)
424
- return None
 
 
 
 
 
 
 
425
 
426
  # ===============================================================
427
  def preprocess_and_parse_json_claude(self, response: str):
 
171
  response_mime_type="application/json"
172
  ))
173
 
174
+ tokens = {
175
+ 'prompt_tokens': response.usage_metadata.prompt_token_count,
176
+ 'completion_tokens': response.usage_metadata.candidates_token_count,
177
+ 'total_tokens': response.usage_metadata.total_token_count
178
+ }
179
+
180
+ # validating the JSON
181
+ self.Core.total_tokens['prompt_tokens'] += tokens['prompt_tokens']
182
+ self.Core.total_tokens['completion_tokens'] += tokens['completion_tokens']
183
+ self.Core.temp_token_counter += tokens['total_tokens']
184
+
185
  output = self.preprocess_and_parse_json(response.text)
186
  # output = json.loads(str(response.text))
187
 
 
229
  {"role": "system", "content": instructions},
230
  {"role": "user", "content": prompt}
231
  ],
232
+ reasoning_effort="minimal",
233
  n=1,
234
  )
235
 
 
251
  'total_tokens': response.usage.total_tokens
252
  }
253
 
254
+ # validating the JSON
255
+ self.Core.total_tokens['prompt_tokens'] += tokens['prompt_tokens']
256
+ self.Core.total_tokens['completion_tokens'] += tokens['completion_tokens']
257
+ self.Core.temp_token_counter += tokens['total_tokens']
258
+
259
  try:
260
  content = response.choices[0].message.content
261
 
 
274
  f"'header' or 'message' is more than specified characters in response on attempt {attempt + 1}. Retrying...")
275
  continue
276
 
 
 
 
 
277
  return output
278
 
279
  except json.JSONDecodeError:
 
410
 
411
  def preprocess_and_parse_json(self, response: str):
412
  """
413
+ Remove <think> blocks, extract JSON (from ```json fences or first {...} block),
414
+ and parse. Includes a repair pass to handle common LLM issues like trailing commas.
 
415
  """
 
 
 
 
 
 
 
 
 
 
 
416
 
417
+ def extract_json(text: str) -> str:
418
+ # Remove <think>...</think>
419
+ text = re.sub(r'<think>.*?</think>', '', text, flags=re.DOTALL).strip()
420
+
421
+ # Prefer fenced code if present
422
+ fence = re.search(r'```(?:json)?(.*?)```', text, flags=re.DOTALL | re.IGNORECASE)
423
+ if fence:
424
+ return fence.group(1).strip()
425
+
426
+ # Otherwise, grab the first {...} block
427
+ brace = re.search(r'\{.*\}', text, flags=re.DOTALL)
428
+ return brace.group(0).strip() if brace else text.strip()
429
+
430
+ def normalize_quotes(text: str) -> str:
431
+ return (text
432
+ .replace('\ufeff', '') # strip BOM if present
433
+ .replace('“', '"').replace('”', '"')
434
+ .replace('‘', "'").replace('’', "'"))
435
+
436
+ def strip_comments(text: str) -> str:
437
+ # Remove // line comments and /* block comments */
438
+ text = re.sub(r'//.*?$', '', text, flags=re.MULTILINE)
439
+ text = re.sub(r'/\*.*?\*/', '', text, flags=re.DOTALL)
440
+ return text
441
+
442
+ def remove_trailing_commas(text: str) -> str:
443
+ # Remove commas before } or ]
444
+ return re.sub(r',(\s*[}\]])', r'\1', text)
445
+
446
+ raw = extract_json(response)
447
+ raw = normalize_quotes(raw)
448
+
449
  try:
450
+ return json.loads(raw)
451
+ except json.JSONDecodeError:
452
+ # Repair pass
453
+ repaired = strip_comments(raw)
454
+ repaired = remove_trailing_commas(repaired)
455
+ repaired = repaired.strip()
456
+
457
+ try:
458
+ return json.loads(repaired)
459
+ except json.JSONDecodeError as e:
460
+ print(f"Failed to parse JSON: {e}")
461
+ # print('Offending text:', repaired)
462
+ return None
463
 
464
  # ===============================================================
465
  def preprocess_and_parse_json_claude(self, response: str):
Messaging_system/Message_generator.py CHANGED
@@ -175,33 +175,37 @@ class MessageGenerator:
175
  """
176
 
177
  jargon_list = "\n".join(f"- {word}" for word in self.Core.config_file["AI_Jargon"])
 
178
 
179
  instructions = f"""
180
  You are a copywriter. Your task is to write a 'header' and a 'message' as a push notification for a {self.Core.get_instrument()} student. It is critical that the message sounds like natural, everyday speech: friendly, concise, no jargon, and it must follow the instructions.
181
- Write a SUPER CASUAL and NATURAL push notification, as if you are chatting over coffee. Avoid odd phrasings. The message should sound like something that a {self.Core.get_instrument()} instructor would realistically say to a student in a daily conversation. Here are some examples of things that an instructor would realistically say to a student, to give you a general sense of tone and phrasing:
 
 
 
 
 
182
 
183
  Common instructor phrases:
184
  {self.Core.brand_voice}
 
185
 
186
 
 
187
  ABSOLUTE RULE – OVERRIDES EVERYTHING ELSE:
188
  the header and the message **MUST NOT** contain any banned word or phrases(case-insensitive; singular, plural, verb forms, or their derivatives)
189
  - **important Note:** header **must be** less than {self.Core.config_file["header_limit"]} characters and message **must be less** than {self.Core.config_file["message_limit"]} characters.
190
 
191
-
192
  Banned word:
193
  {jargon_list}
194
 
195
- Banned phrases:
196
- Voice is NOT an instrument, so avoid phrases like below:
197
- - Your voice is waiting
198
- - Your voice awaits
199
- - Your voice needs you
200
- - Your voice is calling
201
- - Your voice deserves more
202
- - Hit the high notes / Hit those notes
203
- - ...
204
-
205
  """
 
 
 
 
 
 
 
206
 
207
- return instructions
 
175
  """
176
 
177
  jargon_list = "\n".join(f"- {word}" for word in self.Core.config_file["AI_Jargon"])
178
+ banned_phrases = self.Core.config_file.get(f"{self.Core.brand}_banned_phrases", None)
179
 
180
  instructions = f"""
181
  You are a copywriter. Your task is to write a 'header' and a 'message' as a push notification for a {self.Core.get_instrument()} student. It is critical that the message sounds like natural, everyday speech: friendly, concise, no jargon, and it must follow the instructions.
182
+ Write a SUPER CASUAL and NATURAL push notification, as if you are chatting over coffee. Avoid odd phrasings. The message should sound like something that a {self.Core.get_instrument()} instructor would realistically say to a student in a daily conversation.
183
+
184
+ """
185
+ if self.Core.brand_voice is not None:
186
+ instructions += f"""
187
+ Here are some examples of things that an instructor would realistically say to a student, to give you a general sense of tone and phrasing:
188
 
189
  Common instructor phrases:
190
  {self.Core.brand_voice}
191
+ """
192
 
193
 
194
+ rules = f"""
195
  ABSOLUTE RULE – OVERRIDES EVERYTHING ELSE:
196
  the header and the message **MUST NOT** contain any banned word or phrases(case-insensitive; singular, plural, verb forms, or their derivatives)
197
  - **important Note:** header **must be** less than {self.Core.config_file["header_limit"]} characters and message **must be less** than {self.Core.config_file["message_limit"]} characters.
198
 
 
199
  Banned word:
200
  {jargon_list}
201
 
 
 
 
 
 
 
 
 
 
 
202
  """
203
+ if banned_phrases is not None:
204
+ rules += banned_phrases
205
+
206
+ final_instructions = f"""
207
+ {instructions}
208
+ {rules}
209
+ """
210
 
211
+ return final_instructions
Messaging_system/MultiMessage.py CHANGED
@@ -172,7 +172,7 @@ Mimic the example style as much as possible and make it personalized using provi
172
  # Combine into a single string
173
  previous_text_str = "\n\n".join(previous_text)
174
 
175
- # user_info = self.promptGen.get_user_profile(user=user)
176
  input_context = self.promptGen.input_context()
177
  instructions = self.message_instructions(step)
178
  recommendation_instructions = self.promptGen.recommendations_instructions(user)
@@ -195,6 +195,8 @@ Write the *next* follow up personalized push notification following the instruct
195
  - The new header and message should be different from previous messages and we should not have similar words and phrases from previous messages.
196
  - You are **ONLY ALLOWED** to use {self.Core.get_emoji()} if we didn't use in previous messages. (ONLY ONCE, and ONLY at the end of header or message).
197
 
 
 
198
  {recommendation_instructions}
199
 
200
  {examples}
@@ -362,33 +364,39 @@ Write the *next* follow up personalized push notification following the instruct
362
  """
363
 
364
  jargon_list = "\n".join(f"- {word}" for word in self.Core.config_file["AI_Jargon"])
 
365
 
366
  instructions = f"""
367
- You are a copywriter. Your task is to write a 'header' and a 'message' as a push notification for a {self.Core.get_instrument()} student. It is critical that the message sounds like natural, everyday speech: friendly, concise, no jargon, and it must follow the instructions.
368
- Write a SUPER CASUAL and NATURAL push notification, as if you are chatting over coffee. Avoid odd phrasings. The message should sound like something that a {self.Core.get_instrument()} instructor would realistically say to a student. Here are some examples of things that an instructor would realistically say to a student, to give you a general sense of tone and phrasing:
369
 
370
- Common instructor phrases:
371
- {self.Core.brand_voice}
 
 
372
 
373
- ABSOLUTE RULE – OVERRIDES EVERYTHING ELSE:
374
- the header and the message **MUST NOT** contain any banned word or phrases(case-insensitive; singular, plural, verb forms, or their derivatives)
375
- - **important Note:** header **must be** less than {self.Core.config_file["header_limit"]} characters and message **must be less** than {self.Core.config_file["message_limit"]} characters.
376
 
377
- Banned word:
378
- {jargon_list}
 
 
379
 
380
- Banned phrases:
381
- Voice is NOT an instrument, so avoid phrases like below:
382
- - Your voice is waiting
383
- - Your voice awaits
384
- - Your voice needs you
385
- - Your voice is calling
386
- - Your voice deserves more
387
- - Hit the high notes / Hit those notes
388
- - ...
389
 
390
- """
 
391
 
 
 
 
 
 
 
 
 
 
 
392
 
393
- return instructions
394
 
 
172
  # Combine into a single string
173
  previous_text_str = "\n\n".join(previous_text)
174
 
175
+ user_info = self.promptGen.get_user_profile(user=user)
176
  input_context = self.promptGen.input_context()
177
  instructions = self.message_instructions(step)
178
  recommendation_instructions = self.promptGen.recommendations_instructions(user)
 
195
  - The new header and message should be different from previous messages and we should not have similar words and phrases from previous messages.
196
  - You are **ONLY ALLOWED** to use {self.Core.get_emoji()} if we didn't use in previous messages. (ONLY ONCE, and ONLY at the end of header or message).
197
 
198
+ {user_info}
199
+
200
  {recommendation_instructions}
201
 
202
  {examples}
 
364
  """
365
 
366
  jargon_list = "\n".join(f"- {word}" for word in self.Core.config_file["AI_Jargon"])
367
+ banned_phrases = self.Core.config_file.get(f"{self.Core.brand}_banned_phrases", None)
368
 
369
  instructions = f"""
370
+ You are a copywriter. Your task is to write a 'header' and a 'message' as a push notification for a {self.Core.get_instrument()} student. It is critical that the message sounds like natural, everyday speech: friendly, concise, no jargon, and it must follow the instructions.
371
+ Write a SUPER CASUAL and NATURAL push notification, as if you are chatting over coffee. Avoid odd phrasings. The message should sound like something that a {self.Core.get_instrument()} instructor would realistically say to a student in a daily conversation.
372
 
373
+ """
374
+ if self.Core.brand_voice is not None:
375
+ instructions += f"""
376
+ Here are some examples of things that an instructor would realistically say to a student, to give you a general sense of tone and phrasing:
377
 
378
+ Common instructor phrases:
379
+ {self.Core.brand_voice}
380
+ """
381
 
382
+ rules = f"""
383
+ ABSOLUTE RULE – OVERRIDES EVERYTHING ELSE:
384
+ the header and the message **MUST NOT** contain any banned word or phrases(case-insensitive; singular, plural, verb forms, or their derivatives)
385
+ - **important Note:** header **must be** less than {self.Core.config_file["header_limit"]} characters and message **must be less** than {self.Core.config_file["message_limit"]} characters.
386
 
 
 
 
 
 
 
 
 
 
387
 
388
+ Banned word:
389
+ {jargon_list}
390
 
391
+ """
392
+ if banned_phrases is not None:
393
+ rules += banned_phrases
394
+
395
+ final_instructions = f"""
396
+ {instructions}
397
+ {rules}
398
+ """
399
+
400
+ return final_instructions
401
 
 
402
 
Messaging_system/Permes.py CHANGED
@@ -90,9 +90,14 @@ class Permes:
90
  total_prompt_tokens = personalize_message.total_tokens["prompt_tokens"]
91
  total_completion_tokens = personalize_message.total_tokens["completion_tokens"]
92
 
93
- total_cost = ((total_prompt_tokens / 1000000) * 0.10) + (
94
- (total_completion_tokens / 1000000) * 0.4) # Cost calculation estimation
95
- print(f"Estimated Cost (USD): {total_cost:.5f}")
 
 
 
 
 
96
 
97
  # Storing process can also happen after some evaluation steps
98
  # snowflake_conn = SnowFlakeConn(session=session, brand=brand)
@@ -103,6 +108,48 @@ class Permes:
103
  return users_df
104
 
105
  # -----------------------------------------------------
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
106
  def identify_users(self, users_df, identifier_column):
107
  """
108
  specifying the users for identification
 
90
  total_prompt_tokens = personalize_message.total_tokens["prompt_tokens"]
91
  total_completion_tokens = personalize_message.total_tokens["completion_tokens"]
92
 
93
+ total_cost = self.calculate_cost(total_prompt_tokens, total_completion_tokens, model)
94
+
95
+ print(f"Estimated Cost (USD): {total_cost:.5f} ---> Number of messages: {(len(users_df) * number_of_messages)}")
96
+ st.write(f"Estimated Cost (USD): {total_cost:.5f} ---> Number of messages: {(len(users_df) * number_of_messages)}")
97
+
98
+ scale_price = (total_cost * 1000) / (len(users_df) * number_of_messages)
99
+ print(f"Estimated Cost (USD) for 1000 messages: {scale_price}")
100
+ st.write(f"Estimated Cost (USD) for 1000 messages: {scale_price}")
101
 
102
  # Storing process can also happen after some evaluation steps
103
  # snowflake_conn = SnowFlakeConn(session=session, brand=brand)
 
108
  return users_df
109
 
110
  # -----------------------------------------------------
111
+ def calculate_cost(self, total_prompt_tokens, total_completion_tokens, model):
112
+ input_price, output_price = self.get_model_price(model)
113
+
114
+ total_cost = ((total_prompt_tokens / 1000000) * input_price) + (
115
+ (total_completion_tokens / 1000000) * output_price) # Cost calculation estimation
116
+
117
+ return total_cost
118
+
119
+ # ====================================================
120
+ def get_model_price(self, model):
121
+ """
122
+ getting the input price and output price per 1m token for the requested model
123
+ :param model:
124
+ :return:
125
+ """
126
+
127
+ input_prices = {
128
+ "gpt-4o-mini":0.15,
129
+ "gpt-4.1-mini":0.4,
130
+ "gpt-5-mini": 0.25,
131
+ "gpt-5-nano": 0.05,
132
+ "gemini-2.5-flash":0.3,
133
+ "gemini-2.0-flash":0.1,
134
+ "gemini-2.5-flash-lite":0.1
135
+ }
136
+
137
+ out_prices = {
138
+ "gpt-4o-mini":0.6,
139
+ "gpt-4.1-mini":1.6,
140
+ "gpt-5-mini": 2,
141
+ "gpt-5-nano": 0.4,
142
+ "gemini-2.5-flash":2.5,
143
+ "gemini-2.0-flash":0.7,
144
+ "gemini-2.5-flash-lite":0.4
145
+ }
146
+
147
+ i_price = input_prices.get(model, 0)
148
+ o_price= out_prices.get(model, 0)
149
+
150
+ return i_price, o_price
151
+
152
+ # ====================================================
153
  def identify_users(self, users_df, identifier_column):
154
  """
155
  specifying the users for identification
Messaging_system/PromptGenerator.py CHANGED
@@ -88,7 +88,7 @@ Here is the information about the user:
88
  """
89
  input_context = self.input_context()
90
  instructions = self.message_type_instructions()
91
- # user_info = self.get_user_profile(user=user)
92
 
93
  recommendation_instructions = self.recommendations_instructions(user)
94
 
@@ -101,6 +101,8 @@ Here is the information about the user:
101
 
102
  {instructions}
103
 
 
 
104
  {recommendation_instructions}
105
 
106
  {example_output}
 
88
  """
89
  input_context = self.input_context()
90
  instructions = self.message_type_instructions()
91
+ user_info = self.get_user_profile(user=user)
92
 
93
  recommendation_instructions = self.recommendations_instructions(user)
94
 
 
101
 
102
  {instructions}
103
 
104
+ {user_info}
105
+
106
  {recommendation_instructions}
107
 
108
  {example_output}
Messaging_system/PromptGenerator_2.py CHANGED
@@ -117,10 +117,14 @@ Here is the information about the user:
117
  Your task is to write a 'header' and a 'message' as a push notification for a {self.Core.get_instrument()} student that sounds like everyday natural speech: friendly, short, no jargon by following the instructions.
118
  The output should sound like something that a {self.Core.get_instrument()} instructor would realistically say to a student in a daily conversation.
119
 
 
 
 
 
120
  ** Examples of actual phrases an instructor might say:**
121
 
122
  {self.Core.brand_voice}
123
- """
124
 
125
  return context
126
 
 
117
  Your task is to write a 'header' and a 'message' as a push notification for a {self.Core.get_instrument()} student that sounds like everyday natural speech: friendly, short, no jargon by following the instructions.
118
  The output should sound like something that a {self.Core.get_instrument()} instructor would realistically say to a student in a daily conversation.
119
 
120
+ """
121
+
122
+ if self.Core.brand_voice is not None:
123
+ context += f"""
124
  ** Examples of actual phrases an instructor might say:**
125
 
126
  {self.Core.brand_voice}
127
+ """
128
 
129
  return context
130
 
Singeo_camp.csv CHANGED
The diff for this file is too large to render. See raw diff
 
messaging_main_test.py CHANGED
@@ -161,7 +161,7 @@ if __name__ == "__main__":
161
  # o3-mini o1-mini o4-mini o1
162
 
163
  users_message = permes.create_personalize_messages(session=session,
164
- model="gemini-2.5-flash-lite-preview-06-17",
165
  users=users,
166
  brand=brand,
167
  message_style = instructions,
 
161
  # o3-mini o1-mini o4-mini o1
162
 
163
  users_message = permes.create_personalize_messages(session=session,
164
+ model="gemini-2.5-flash",
165
  users=users,
166
  brand=brand,
167
  message_style = instructions,
requirements.txt CHANGED
Binary files a/requirements.txt and b/requirements.txt differ
 
singeo_segment.csv CHANGED
The diff for this file is too large to render. See raw diff