File size: 6,451 Bytes
7d6d833
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
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