Skip to content

Commit

Permalink
🧹 Consolidate sentence generation, and update prompts
Browse files Browse the repository at this point in the history
Random sentences no longer had to be its own code path.

Furthermore, the problem with 'random'ness in the prompt responses should be fixed for now, until we have better enum handling in place.
  • Loading branch information
beverage committed Sep 12, 2024
1 parent e3be790 commit f1a5460
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 62 deletions.
9 changes: 6 additions & 3 deletions src/dbtest/dbtest.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,14 @@ async def generate(quantity: int, **kwargs):
print(f"str({ex}): {traceback.format_exc()}")

@sentence.command('random')
@click.option('-q', '--quantity', required=False, default=1)
@random_options
async def random():
async def random(quantity: int, **kwargs):
try:
result = await create_random_sentence(is_correct=True)
print(object_as_dict(result))
results = []
for i in range(quantity):
results.append(await create_random_sentence(**kwargs))
print(problem_formatter(results))
except Exception as ex:
print(f"str({ex}): {traceback.format_exc()}")

Expand Down
76 changes: 21 additions & 55 deletions src/dbtest/sentences/create.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@

from dbtest.utils.console import Answers, Color, Style

async def create_sentence(verb_infinitive: str,
pronoun: Pronoun=Pronoun.first_person, # Pronoun and tense will remain both random
tense: Tense=Tense.present, # and correct for now. These values will be
direct_object: DirectObject=DirectObject.none, # ignored.
indirect_pronoun: IndirectPronoun=IndirectPronoun.none,
negation: Negation=Negation.none,
is_correct: bool=True, # This cannot be guaranteed until the AI has responded.
openai_client: AsyncChatGPTClient=AsyncChatGPTClient()):
async def create_sentence(verb_infinitive: str,
pronoun: Pronoun = Pronoun.first_person, # Pronoun and tense will remain both random
tense: Tense = Tense.present, # and correct for now. These values will be
direct_object: DirectObject = DirectObject.none, # ignored.
indirect_pronoun: IndirectPronoun = IndirectPronoun.none,
negation: Negation = Negation.none,
is_correct: bool = True, # This cannot be guaranteed until the AI has responded.
openai_client: AsyncChatGPTClient = AsyncChatGPTClient()):

async with get_async_session() as db_session:

Expand All @@ -53,18 +53,21 @@ async def create_sentence(verb_infinitive: str,
sentence.negation = negation

generator: SentencePromptGenerator = SentencePromptGenerator()
prompt: str = generator.generate_sentence_prompt(sentence)

logging.debug(prompt)
prompt: str = generator.generate_sentence_prompt(sentence)
response: str = await openai_client.handle_request(prompt=prompt)
logging.debug("response: " + response)
response_json = json.loads(response)
logging.debug(response_json)

sentence.content = response_json["sentence"]
sentence.translation = response_json["translation"]
sentence.is_negated = response_json["is_negated"]

# It is not always possible to generate a sentence with a COD or a COI (or both)
# for certain verbs. Rather than trying to force the issue with whitelists or
# blacklists, lets just store the results as it will be sometime else querying
# for sentences from the database by features anyways. Direct calls are not meant
# to be part of the user experience. Only database calls are.

if response_json["is_negated"] is False:
sentence.negation = Negation.none

Expand All @@ -78,49 +81,12 @@ async def create_sentence(verb_infinitive: str,

async def create_random_sentence(is_correct: bool=True, openai_client: AsyncChatGPTClient=AsyncChatGPTClient()):

async with get_async_session() as session:

verb = await get_random_verb(database_session=session)

sentence = Sentence()
sentence.is_correct = is_correct

sentence.infinitive = verb.infinitive
sentence.auxiliary = verb.auxiliary
sentence.pronoun = random.choice(list(Pronoun))

# This raises the question as to if we even need participles if we are just
# going to feed the tense right into ChatGTP:
sentence.tense = random.choice([t for t in Tense if t is not Tense.participle])

features: SentenceFeaturesOld = SentenceFeaturesOld()
sentence = features.randomize(sentence)

generator: SentencePromptGenerator = SentencePromptGenerator()
prompt: str = generator.generate_sentence_prompt(sentence)
logging.debug(prompt)

response: str = await openai_client.handle_request(prompt=prompt)
logging.debug(response)
response_json = json.loads(response)

sentence.content = response_json["sentence"]
sentence.translation = response_json["translation"]
sentence.is_negated = response_json["is_negated"]

# It is not always possible to generate a sentence with a COD or a COI (or both)
# for certain verbs. Rather than trying to force the issue with whitelists or
# blacklists, lets just store the results as it will be sometime else querying
# for sentences from the database by features anyways. We will take it as the best
# the AI can do:

if response_json["has_direct_object"] is False:
sentence.direct_object = DirectObject.none

if response_json["has_indirect_pronoun"] is False:
sentence.indirect_pronoun = IndirectPronoun.none

return sentence
return await create_sentence("", "", "", # Verb, pronoun, and tense remain fully random for now.
direct_object = DirectObject.random,
indirect_pronoun = IndirectPronoun.random,
negation = Negation.random if random.randint(0, 1) == 1 else Negation.none,
is_correct = is_correct,
openai_client = openai_client)

async def create_random_problem_with_delay(openai_client: AsyncChatGPTClient=AsyncChatGPTClient(), display=True):
await create_random_problem(openai_client=openai_client, display=display)
Expand Down
6 changes: 3 additions & 3 deletions src/dbtest/sentences/features.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def __init__(self, feature: PromptEnum=DirectObject.none, incorrect: bool=False,
super().__init__(feature, incorrect, is_random)

def prompt(self) -> str:
if self.random:
if self.feature.random:
return "The sentence may or may not have a direct object before its verb."
elif self.incorrect is False:
return f"The sentence must have a correct {self.feature.prompt} direct object before its verb."
Expand All @@ -44,7 +44,7 @@ def __init__(self, feature: PromptEnum=IndirectPronoun.none, incorrect: bool=Fal
super().__init__(feature, incorrect, is_random)

def prompt(self) -> str:
if self.random:
if self.feature.random:
return "The sentence may or may not have an indirect pronoun before its verb."
elif self.incorrect is False:
return f"The sentence must have a correct {self.feature.prompt} indirect pronoun as a pronoun before its verb."
Expand All @@ -56,7 +56,7 @@ def __init__(self, feature: PromptEnum=Negation.none, incorrect: bool=False, is_
super().__init__(feature, incorrect, is_random)

def prompt(self) -> str:
if self.random:
if self.feature.random:
return "The sentence may or may not be negated, in any way."
elif self.feature is not self.feature.none:
return f"The sentence must contain the negation {self.feature.prompt}."
Expand Down
3 changes: 2 additions & 1 deletion src/dbtest/sentences/prompts.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ def __correct_elisions(self):
return "The sentence should have correct French elisions. This includes que and qui connectors."

def __extra_rules(self):
return "The JSON must be properly formatted, with all properties and values in double quotes."
# TODO: we should not have an oddly specific rule around the word 'random'. This is a hack to get around poor enum handling for now.
return "The JSON must be properly formatted, with all properties and values in double quotes. The sentence must not include the word 'random'."

def generate_sentence_prompt(self, sentence) -> str:
return '\n'.join([
Expand Down

0 comments on commit f1a5460

Please sign in to comment.