import json import re import time from typing import List from mistralai import Mistral import hackathon.agent.character as ch import hackathon.game_mechanics.entities as entities class Agent: def __init__(self, client: Mistral, model: str): self.client = client self.model = model class CardAgent(Agent): def add_cards_to_personal_context( self, character: ch.AIAgent, cards: List[entities.Card] ): system_prompt = ( "You are a conversationnal game update engine " "Given the two AI characters traits, and his current personnal context, " "you will propose a new personnal personal context" ) user_prompt = "" for card in cards: if card.change_personal_context: user_prompt += f""" Character: {character} Current personal context: {character.personal_context} The context to be added to the personnal context : Fact description : {card.description} Description of the effect on the player : {card.game_context} """ user_prompt += """ Instructions: Gives a new synthetic personal context to take into account this new description in less then 150 words, should return only the text format as string variable. """ messages = [ {"role": "system", "content": system_prompt}, {"role": "user", "content": user_prompt}, ] # print(f"{messages=}") response = self.client.chat.complete(model=self.model, messages=messages) time.sleep(1) raw_text = response.choices[0].message.content.strip() character.personal_context = raw_text def add_card_to_personal_context(self, character: ch.AIAgent, card: entities.Card): if card.change_personal_context: system_prompt = ( "You are a conversationnal game update engine " "Given the two AI characters traits, and his current personnal context, " "you will propose a new personnal personal context" ) user_prompt = f""" Character: {character} Current personal context: {character.personal_context} The context to be added to the personnal context : Fact description : {card.description} Description of the effect on the player : {card.game_context} """ user_prompt += """ Instructions: Gives a new synthetic personal context to take into account this new description in less then 150 words, should return only the text format as string variable. """ messages = [ {"role": "system", "content": system_prompt}, {"role": "user", "content": user_prompt}, ] # print(f"{messages=}") response = self.client.chat.complete(model=self.model, messages=messages) time.sleep(1) raw_text = response.choices[0].message.content.strip() character.personal_context = raw_text class EmotionAgent: """ Uses a LLM (Mistral) to handle: - update_emotions - update_attitude - create_memory_context Each method returns strictly valid JSON, parsed into Python dicts. """ def __init__(self, client: Mistral, model: str): self.client = client self.model = model def update_emotions(self, character): """ Calls the LLM to produce new emotion values in valid JSON. Each emotion in [0.0, 1.0]. """ system_prompt = ( "You are an emotion update engine. " "Given the AI's character traits, current emotions, general context, and context memory, " "you will propose updated emotion and attitudes values. The output must be strictly valid JSON, " "Give a particular attention to anger value." ) user_prompt = f""" Character: {character} Personal context: {character.personal_context} Current Emotions: {character.emotions} Current Attitudes: {character.attitudes} Conversation History: {character.context_memory} Instructions: 1. Analyze the information provided above. 2. Propose new values for emotions and attitudes in valid JSON format. JSON structure attributes must be respected: {{ "emotions": {character.emotions}, "attitudes": {character.attitudes} }} Requirements: - All numeric values must be floats in the range [0.0, 1.0]. - Text values should be descriptive and context-appropriate. """ messages = [ {"role": "system", "content": system_prompt}, {"role": "user", "content": user_prompt}, ] # print(f"{messages=}") time.sleep(1) response = self.client.chat.complete( model=self.model, messages=messages, response_format={"type": "json_object"}, max_tokens=200, ) raw_text = response.choices[0].message.content.strip() # print(f'{raw_text=}') cleaned_response = re.sub(r"```(json)?", "", raw_text).strip() try: updated_emotions = json.loads(cleaned_response) except json.JSONDecodeError: print( f"Error: Could not parse JSON for emotions. Using old emotions.\nResponse: {cleaned_response}" ) return character.emotions, character.attitudes # Clamp to [0.0, 1.0] final_emotions = {} final_attitudes = {} for emotion, val in updated_emotions["emotions"].items(): if isinstance(val, (int, float)): final_emotions[emotion] = max(0.0, min(1.0, float(val))) else: # Fallback if invalid final_attitudes[emotion] = updated_emotions.get(emotion, 0.5) for attitude, val in updated_emotions["attitudes"].items(): if attitude == "patience": final_attitudes["patience"] = 0 else: final_attitudes[attitude] = val return final_emotions, final_attitudes return final_emotions, final_attitudes