tunght commited on
Commit
53440c5
·
1 Parent(s): c94593b

Improve quality of generated description. Add ratings, comments in debug mode

Browse files
Files changed (1) hide show
  1. app.py +158 -36
app.py CHANGED
@@ -1,7 +1,9 @@
 
1
  import traceback
2
  import gradio as gr
3
  import numpy as np
4
  import os
 
5
 
6
  from langchain_openai.chat_models import ChatOpenAI
7
  from langchain.schema import HumanMessage, SystemMessage, AIMessage
@@ -205,9 +207,9 @@ def get_language(struct_lang, copy_lang):
205
 
206
  def get_model(model_name):
207
  if model_name.startswith("gpt"):
208
- chat = ChatOpenAI(model=model_name)
209
  elif model_name.startswith("claude"):
210
- chat = ChatAnthropic(model_name=model_name, anthropic_api_key=os.environ["ANTHROPIC_API_KEY"])
211
  else:
212
  chat = ChatGroq(model_name=model_name, api_key=os.environ["GROQ_API_KEY"])
213
  return chat
@@ -217,8 +219,8 @@ def generate(*data):
217
  global visible
218
  print("visible", visible)
219
 
220
- nargs = 8
221
- feature, image, garment_type, model, temperature, excluded_words, included_words, glossary = data[:nargs]
222
  struct_ref = data[nargs:]
223
 
224
  print(f"{feature=}")
@@ -228,8 +230,9 @@ def generate(*data):
228
  print(f"{temperature=}")
229
  print(f"{excluded_words=}")
230
  print(f"{included_words=}")
 
231
  print(f"{glossary=}")
232
- print(f"{struct_ref=}")
233
 
234
  chat = get_model(model)
235
 
@@ -237,9 +240,9 @@ def generate(*data):
237
  copy_languages = detect_language([struct_ref[2 * i + 1] for i in range(visible + 1)], model=chat)
238
  languages = [get_language(struct_lang=struct_lang, copy_lang=copy_lang) for struct_lang, copy_lang in zip(struct_languages, copy_languages)]
239
 
240
- print("Struct languages--------------------------------------------\n", struct_languages)
241
- print("Copy languages--------------------------------------------\n", copy_languages)
242
- print("Languages--------------------------------------------\n", languages)
243
  # print("Types--------------------------------------------", types)
244
 
245
  image_features, base64_images = detect_features(image, garment_type)
@@ -257,56 +260,158 @@ def generate(*data):
257
  structure = struct_ref[2 * i]
258
  copy = struct_ref[2 * i + 1]
259
  if len((structure + copy).strip()) > 0:
260
- if len(copy.strip()) > 0:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
261
  print('------------')
262
  print("Using copy")
263
  messages = [
264
  SystemMessage(content=f"""You are a helpful assistant that writes about products for ecommerce websites. Make sure to write in {languages[i]} language."""),
265
- HumanMessage(content=f"""Write a product description with the following features.
266
- Make sure that the output follows the structure and tone of voice of the reference copy.
267
- Make sure that the output really sounds like the reference copy.
268
- Use markdown format for the output.
269
- Output the product description only, do not include any preceeding text like "Here is your product description".
270
- Use a consistent tone of voice throughout the text.
271
  Do not include any part of the reference structure in the output.
272
  Do not use any of the excluded words in the output.
273
- Make sure to include all of the included words in the output.
274
  Do not hallucinate any information.
275
-
276
- {feature + detected_features}
277
- {intended_use}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
278
  Reference copy: {copy}
279
  Included words: {included_words}
280
  Excluded words: {excluded_words}"""),]
281
  print(messages[1].content)
282
  print('------------')
 
283
  elif len(structure.strip()) > 0:
284
  print('------------')
285
  print("Using structure")
286
  messages = [
287
  SystemMessage(content=f"""You are a helpful assistant that writes about products for ecommerce websites. Make sure to write in {languages[i]} language."""),
288
- HumanMessage(content=f"""Write a product description with the following features.
289
- Make sure that the description follows the structure of the reference structure.
290
- Make sure to use markdown format for the output.
291
- Make sure that the entire output is written entirely in language defined in the reference structure.
292
- Make sure to output the product description only, do not include any preceeding text like "Here is your product description".
293
- Use language that is suitable for the type of document specified in the reference structure.
294
- Use a consistent tone of voice throughout the text.
295
  Do not include any part of the reference structure in the output.
296
  Do not use any of the excluded words in the output.
297
- Make sure to include all of the included words in the output.
298
  Do not hallucinate any information.
299
-
300
- {feature + detected_features}
301
- {intended_use}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
302
  Reference structure: {structure}
 
303
  Included words: {included_words}
304
  Excluded words: {excluded_words}"""),]
305
  print(messages[1].content)
306
  print('------------')
307
  batch.append(messages)
308
 
309
- description = ""
310
 
311
  response = chat.batch(batch, temperature=temperature)
312
  print(response)
@@ -344,13 +449,28 @@ Excluded words: {excluded_words}"""),]
344
  # response = [re_response[rewrite_map[i]] if i in rewrite_map else response[i] for i in range(visible + 1)]
345
  # print("Done rewriting")
346
 
347
- description = "\n---\n".join([msg.content for msg in response])
348
- md_content = description
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
349
 
350
  alt_texts_str = '\n\n### Alt text\n\n' + '\n- ' + '\n- '.join(alt_texts) if len(alt_texts) > 0 else ""
351
 
352
  alt_text_dict = {k[0]: v for (k, v) in zip(image, alt_texts)} if len(alt_texts) > 0 else {}
353
- result_json = {"outputs": [msg.content for msg in response], "alt_text": alt_text_dict}
354
  result_md = md_content + alt_texts_str + '\n'.join([f'![Product photo](data:image/png;base64,{base64_image} "{alt_text}")' if base64_image != "" else "" for (base64_image, alt_text) in zip(base64_images, alt_texts)])
355
  return result_md, result_json
356
 
@@ -402,11 +522,12 @@ with gr.Blocks() as demo:
402
  garment_type = gr.Textbox(label="Garment Type", value="all", lines=1, interactive=True)
403
  # language = gr.Dropdown(languages, value="American English", interactive=True, label="Language")
404
  with gr.Accordion(label="Advanced Options", open=False):
405
- model = gr.Dropdown(models, value="gpt-4-turbo", interactive=True, label="Model", visible=True)
406
  temperature = gr.Slider(minimum=0., maximum=1.0, value=0., interactive=True, label="Temperature", visible=True)
407
  excluded_words = gr.Textbox(label="Excluded words", interactive=True, lines=2)
408
  included_words = gr.Textbox(label="Included words", interactive=True, lines=2)
409
  glossary = gr.Dataframe(row_count = (2, "dynamic"), col_count=(2,"static"), headers=["Description", "Way of writing"], label="Glossary", interactive=True)
 
410
  with gr.Row():
411
  submit = gr.Button(value="Submit")
412
  # advanced = gr.Button(value="Advanced")
@@ -426,7 +547,8 @@ with gr.Blocks() as demo:
426
  with gr.Column():
427
  md_output = gr.Markdown(label="Output", show_label=True)
428
  json_output = gr.JSON(label="JSON Output")
429
- submit.click(generate, inputs=[feature, image, garment_type, model, temperature, excluded_words, included_words, glossary, *struct_ref],
 
430
  outputs=[md_output, json_output])
431
  # advanced.click(show_advanced, inputs=[], outputs=[model, temperature])
432
 
 
1
+ import random
2
  import traceback
3
  import gradio as gr
4
  import numpy as np
5
  import os
6
+ from langchain_core.output_parsers import JsonOutputParser
7
 
8
  from langchain_openai.chat_models import ChatOpenAI
9
  from langchain.schema import HumanMessage, SystemMessage, AIMessage
 
207
 
208
  def get_model(model_name):
209
  if model_name.startswith("gpt"):
210
+ chat = ChatOpenAI(model=model_name, max_tokens=8192)
211
  elif model_name.startswith("claude"):
212
+ chat = ChatAnthropic(model_name=model_name, anthropic_api_key=os.environ["ANTHROPIC_API_KEY"], max_tokens_to_sample=4096)
213
  else:
214
  chat = ChatGroq(model_name=model_name, api_key=os.environ["GROQ_API_KEY"])
215
  return chat
 
219
  global visible
220
  print("visible", visible)
221
 
222
+ nargs = 9
223
+ feature, image, garment_type, model, temperature, excluded_words, included_words, glossary, debug = data[:nargs]
224
  struct_ref = data[nargs:]
225
 
226
  print(f"{feature=}")
 
230
  print(f"{temperature=}")
231
  print(f"{excluded_words=}")
232
  print(f"{included_words=}")
233
+ print(f"{debug=}")
234
  print(f"{glossary=}")
235
+ # print(f"{struct_ref=}")
236
 
237
  chat = get_model(model)
238
 
 
240
  copy_languages = detect_language([struct_ref[2 * i + 1] for i in range(visible + 1)], model=chat)
241
  languages = [get_language(struct_lang=struct_lang, copy_lang=copy_lang) for struct_lang, copy_lang in zip(struct_languages, copy_languages)]
242
 
243
+ # print("Struct languages--------------------------------------------\n", struct_languages)
244
+ # print("Copy languages--------------------------------------------\n", copy_languages)
245
+ # print("Languages--------------------------------------------\n", languages)
246
  # print("Types--------------------------------------------", types)
247
 
248
  image_features, base64_images = detect_features(image, garment_type)
 
260
  structure = struct_ref[2 * i]
261
  copy = struct_ref[2 * i + 1]
262
  if len((structure + copy).strip()) > 0:
263
+ if len(copy.strip()) > 0 and len(structure.strip()) > 0:
264
+ print('------------')
265
+ print("Using both copy and structure")
266
+ messages = [
267
+ SystemMessage(content=f"""You are a helpful assistant that writes about products for ecommerce websites. Make sure to write in {languages[i]} language."""),
268
+ # HumanMessage(content=f"""Write a product description with the following features.
269
+ # Make sure that the structure of the output follows the reference structure.
270
+ # Make sure to use the tone of voice, rythm, cadence and style of the reference copy for the output.
271
+ # Use markdown format for the output.
272
+ # Do not include any part of the reference structure in the output.
273
+ # Do not use any of the excluded words in the output.
274
+ # Make sure to include all of the included words in the output.
275
+ # Output the product description only.
276
+ # Do not hallucinate any information.
277
+ # Generate 5 versions of the product description and rate the quality of each version based on the following criteria:
278
+ # - how well it follows the reference copy's tone of voice, rythm, cadence and style.
279
+ # - how well it follows the reference structure.
280
+ # - how faithful it describes the product features.
281
+ HumanMessage(content=f"""Generate 5 versions of the product description for a product with the following information.
282
+ Make sure that the structure of each output follows the reference structure.
283
+ Make sure to use the tone of voice, rythm, cadence and style of the reference copy for each output.
284
+ Use markdown format for each output.
285
+ Do not include any part of the reference structure in the output.
286
+ Do not use any of the excluded words in the output.
287
+ Include all included words in the output.
288
+ Do not hallucinate any information.
289
+ Use creative language in each output.
290
+ Rate the quality of each version based on the following criteria:
291
+ - how well it follows the reference copy's tone of voice, rythm, cadence and style.
292
+ - how well it follows the reference structure.
293
+ - how faithful it describes the product features.
294
+ - how creative the language is.
295
+ The score should be a number between 0 and 10 with 10 being the best quality.
296
+ Return the result in the following JSON format:
297
+ [
298
+ {{
299
+ "id": 1,
300
+ "content": The first product description,
301
+ "score": The score of the first product description,
302
+ "explanation": A less than 20 word explanation of the score of the first product description
303
+ }},
304
+ {{
305
+ "id": 2,
306
+ "content": The second product description,
307
+ "score": The score of the second product description
308
+ "explanation": A less than 20 word explanation of the score of the second product description
309
+ }},
310
+ ]
311
+ Make sure that the output is in JSON format, no extra text should be included in the output.
312
+
313
+ Product information:
314
+ Key features: {feature + detected_features}
315
+ Intended use: {intended_use}
316
+ Reference structure: {structure}
317
+ Reference copy: {copy}
318
+ Included words: {included_words}
319
+ Excluded words: {excluded_words}"""),]
320
+
321
+ elif len(copy.strip()) > 0:
322
  print('------------')
323
  print("Using copy")
324
  messages = [
325
  SystemMessage(content=f"""You are a helpful assistant that writes about products for ecommerce websites. Make sure to write in {languages[i]} language."""),
326
+ HumanMessage(content=f"""Generate 5 versions of the product description for a product with the following information.
327
+ Make sure that the structure of each output follows the structure of the reference copy.
328
+ Make sure to use the tone of voice, rythm, cadence and style of the reference copy for each output.
329
+ Use markdown format for each output.
 
 
330
  Do not include any part of the reference structure in the output.
331
  Do not use any of the excluded words in the output.
332
+ Include all included words in the output.
333
  Do not hallucinate any information.
334
+ Use creative language in each output.
335
+ Rate the quality of each version based on the following criteria:
336
+ - how well it follows the reference copy's tone of voice, rythm, cadence and style.
337
+ - how well it follows the reference copy's structure.
338
+ - how faithful it describes the product features.
339
+ - how creative the language is.
340
+ The score should be a number between 0 and 10 with 10 being the best quality.
341
+ Return the result in the following JSON format:
342
+ [
343
+ {{
344
+ "id": 1,
345
+ "content": The first product description,
346
+ "score": The score of the first product description,
347
+ "explanation": A less than 20 word explanation of the score of the first product description
348
+ }},
349
+ {{
350
+ "id": 2,
351
+ "content": The second product description,
352
+ "score": The score of the second product description
353
+ "explanation": A less than 20 word explanation of the score of the second product description
354
+ }},
355
+ ]
356
+ Make sure that the output is in JSON format, no extra text should be included in the output.
357
+
358
+ Product information:
359
+ Key features: {feature + detected_features}
360
+ Intended use: {intended_use}
361
  Reference copy: {copy}
362
  Included words: {included_words}
363
  Excluded words: {excluded_words}"""),]
364
  print(messages[1].content)
365
  print('------------')
366
+
367
  elif len(structure.strip()) > 0:
368
  print('------------')
369
  print("Using structure")
370
  messages = [
371
  SystemMessage(content=f"""You are a helpful assistant that writes about products for ecommerce websites. Make sure to write in {languages[i]} language."""),
372
+ HumanMessage(content=f"""Generate 5 versions of the product description for a product with the following information.
373
+ Make sure that the structure of each output follows the reference structure.
374
+ Use markdown format for each output.
 
 
 
 
375
  Do not include any part of the reference structure in the output.
376
  Do not use any of the excluded words in the output.
377
+ Include all included words in the output.
378
  Do not hallucinate any information.
379
+ Use creative language in each output.
380
+ Rate the quality of each version based on the following criteria:
381
+ - how well it follows the reference tone of voice, rythm, cadence and style.
382
+ - how well it follows the reference structure.
383
+ - how faithful it describes the product features.
384
+ - how creative the language is.
385
+ The score should be a number between 0 and 10 with 10 being the best quality.
386
+ Return the result in the following JSON format:
387
+ [
388
+ {{
389
+ "id": 1,
390
+ "content": The first product description,
391
+ "score": The score of the first product description,
392
+ "explanation": A less than 20 word explanation of the score of the first product description
393
+ }},
394
+ {{
395
+ "id": 2,
396
+ "content": The second product description,
397
+ "score": The score of the second product description
398
+ "explanation": A less than 20 word explanation of the score of the second product description
399
+ }},
400
+ ]
401
+ Make sure that the output is in JSON format, no extra text should be included in the output.
402
+
403
+ Product information:
404
+ Key features: {feature + detected_features}
405
+ Intended use: {intended_use}
406
  Reference structure: {structure}
407
+ Reference copy: {copy}
408
  Included words: {included_words}
409
  Excluded words: {excluded_words}"""),]
410
  print(messages[1].content)
411
  print('------------')
412
  batch.append(messages)
413
 
414
+ descriptions = ""
415
 
416
  response = chat.batch(batch, temperature=temperature)
417
  print(response)
 
449
  # response = [re_response[rewrite_map[i]] if i in rewrite_map else response[i] for i in range(visible + 1)]
450
  # print("Done rewriting")
451
 
452
+ parser = JsonOutputParser()
453
+ jresponse = [parser.parse(msg.content) for msg in response]
454
+ descriptions = []
455
+ for jr in jresponse:
456
+ bests = 0
457
+ bestd = ""
458
+ for d in jr:
459
+ print(f'{d["score"]=}, {d["id"]=}, {bests=}')
460
+ if d["score"] > bests:
461
+ bests = d["score"]
462
+ bestd = d["content"] + (f"\n\nDebug info:\n\nScore: {d['score']}\n\nExplanation: {d['explanation']}" if debug else "")
463
+ elif d["score"] == bests and random.random() > 0.5:
464
+ bestd = d["content"] + (f"\n\nDebug info:\n\nScore: {d['score']}\n\nExplanation: {d['explanation']}" if debug else "")
465
+
466
+ descriptions.append(bestd)
467
+ # description = "\n\n\n\n".join([msg.content for msg in response])
468
+ md_content = "\n\n\n".join(descriptions)
469
 
470
  alt_texts_str = '\n\n### Alt text\n\n' + '\n- ' + '\n- '.join(alt_texts) if len(alt_texts) > 0 else ""
471
 
472
  alt_text_dict = {k[0]: v for (k, v) in zip(image, alt_texts)} if len(alt_texts) > 0 else {}
473
+ result_json = {"outputs": descriptions, "alt_text": alt_text_dict}
474
  result_md = md_content + alt_texts_str + '\n'.join([f'![Product photo](data:image/png;base64,{base64_image} "{alt_text}")' if base64_image != "" else "" for (base64_image, alt_text) in zip(base64_images, alt_texts)])
475
  return result_md, result_json
476
 
 
522
  garment_type = gr.Textbox(label="Garment Type", value="all", lines=1, interactive=True)
523
  # language = gr.Dropdown(languages, value="American English", interactive=True, label="Language")
524
  with gr.Accordion(label="Advanced Options", open=False):
525
+ model = gr.Dropdown(models, value="claude-3-5-sonnet-20240620", interactive=True, label="Model", visible=True)
526
  temperature = gr.Slider(minimum=0., maximum=1.0, value=0., interactive=True, label="Temperature", visible=True)
527
  excluded_words = gr.Textbox(label="Excluded words", interactive=True, lines=2)
528
  included_words = gr.Textbox(label="Included words", interactive=True, lines=2)
529
  glossary = gr.Dataframe(row_count = (2, "dynamic"), col_count=(2,"static"), headers=["Description", "Way of writing"], label="Glossary", interactive=True)
530
+ debug = gr.Checkbox(label="Debug", interactive=True, value=True)
531
  with gr.Row():
532
  submit = gr.Button(value="Submit")
533
  # advanced = gr.Button(value="Advanced")
 
547
  with gr.Column():
548
  md_output = gr.Markdown(label="Output", show_label=True)
549
  json_output = gr.JSON(label="JSON Output")
550
+ submit.click(generate, inputs=[feature, image, garment_type, model, temperature,
551
+ excluded_words, included_words, glossary, debug, *struct_ref],
552
  outputs=[md_output, json_output])
553
  # advanced.click(show_advanced, inputs=[], outputs=[model, temperature])
554