Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions usersimcrs/simulator/agenda_based/agenda_based_simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,12 @@ def __init__(
nlg: NLG module generating textual responses.
ratings: Historical ratings.
"""
super().__init__(id=id, domain=domain, item_collection=item_collection)
self._preference_model = preference_model
super().__init__(
id=id,
domain=domain,
item_collection=item_collection,
preference_model=preference_model,
)
self._interaction_model = interaction_model
self._interaction_model.initialize_agenda(self.information_need)
self._nlu = nlu
Expand Down
21 changes: 17 additions & 4 deletions usersimcrs/simulator/llm/llm_dual_prompt_user_simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
the user response.
"""

from typing import Optional

from dialoguekit.core.utterance import Utterance
from dialoguekit.participant import DialogueParticipant
from usersimcrs.core.simulation_domain import SimulationDomain
Expand All @@ -19,6 +21,7 @@
UtteranceGenerationPrompt,
)
from usersimcrs.simulator.user_simulator import UserSimulator
from usersimcrs.user_modeling.preference_model import PreferenceModel
from usersimcrs.user_modeling.persona import Persona


Expand All @@ -32,7 +35,8 @@ def __init__(
item_type: str,
task_definition: str = DEFAULT_TASK_DEFINITION,
stop_definition: str = DEFAULT_STOP_DEFINITION,
persona: Persona = None,
persona: Optional[Persona] = None,
preference_model: Optional[PreferenceModel] = None,
) -> None:
"""Initializes the user simulator.

Expand All @@ -45,14 +49,23 @@ def __init__(
stop_definition: Definition of the stop task. Defaults to
DEFAULT_STOP_DEFINITION.
persona: Persona of the user. Defaults to None.
preference_model: Preference model. Defaults to None.
"""
super().__init__(id, domain, item_collection)
super().__init__(id, domain, item_collection, preference_model)
self.llm_interface = llm_interface
self.generation_prompt = UtteranceGenerationPrompt(
self.information_need, item_type, task_definition, persona
self.information_need,
item_type,
task_definition,
persona,
preference_model,
)
self.stop_prompt = StopPrompt(
self.information_need, item_type, stop_definition, persona
self.information_need,
item_type,
stop_definition,
persona,
preference_model,
)

def _generate_response(self, agent_utterance: Utterance) -> Utterance:
Expand Down
15 changes: 12 additions & 3 deletions usersimcrs/simulator/llm/llm_single_prompt_user_simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
model.
"""

from typing import Optional

from dialoguekit.core.utterance import Utterance
from dialoguekit.participant import DialogueParticipant
from usersimcrs.core.simulation_domain import SimulationDomain
Expand All @@ -14,6 +16,7 @@
UtteranceGenerationPrompt,
)
from usersimcrs.simulator.user_simulator import UserSimulator
from usersimcrs.user_modeling.preference_model import PreferenceModel
from usersimcrs.user_modeling.persona import Persona


Expand All @@ -26,7 +29,8 @@ def __init__(
llm_interface: LLMInterface,
item_type: str,
task_definition: str = DEFAULT_TASK_DEFINITION,
persona: Persona = None,
persona: Optional[Persona] = None,
preference_model: Optional[PreferenceModel] = None,
) -> None:
"""Initializes the user simulator.

Expand All @@ -37,11 +41,16 @@ def __init__(
task_definition: Definition of the task to be performed.
Defaults to DEFAULT_TASK_DEFINITION.
persona: Persona of the user. Defaults to None.
preference_model: Preference model. Defaults to None.
"""
super().__init__(id, domain, item_collection)
super().__init__(id, domain, item_collection, preference_model)
self.llm_interface = llm_interface
self.prompt = UtteranceGenerationPrompt(
self.information_need, item_type, task_definition, persona
self.information_need,
item_type,
task_definition,
persona,
preference_model,
)

def _generate_response(self, agent_utterance: Utterance) -> Utterance:
Expand Down
55 changes: 53 additions & 2 deletions usersimcrs/simulator/llm/prompt/prompt.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
"""Interface for prompt."""

from abc import ABC, abstractmethod
from typing import Optional

from dialoguekit.core.utterance import Utterance
from dialoguekit.participant.participant import DialogueParticipant
from usersimcrs.core.information_need import InformationNeed
from usersimcrs.user_modeling.preference_model import PreferenceModel
from usersimcrs.user_modeling.persona import Persona


Expand All @@ -14,7 +16,8 @@ def __init__(
information_need: InformationNeed,
item_type: str,
prompt_definition: str,
persona: Persona = None,
persona: Optional[Persona] = None,
preference_model: Optional[PreferenceModel] = None,
) -> None:
"""Initializes the prompt.

Expand All @@ -23,18 +26,66 @@ def __init__(
item_type: The type of the item to be recommended.
prompt_definition: The definition of the task to be performed.
persona: The persona of the user. Defaults to None.
preference_model: Preference model. Defaults to None.
"""
self.information_need = information_need
self.item_type = item_type
self.prompt_definition = prompt_definition
self.persona = persona
self.preference_model = preference_model
self._initial_prompt = self.build_new_prompt()
self._prompt_context = ""

@property
def prompt_text(self) -> str:
"""Prompt for the user simulator."""
return self._initial_prompt + "\n" + self._prompt_context
return (
self._initial_prompt
+ "\n"
+ self._preference_context
+ "\n"
+ self._prompt_context
)

@property
def _preference_context(self) -> str:
"""Returns current preference context for the prompt."""
if not self.preference_model:
return ""

preference_summary = self.preference_model.get_preference_summary()
if not preference_summary:
return ""

return (
"PREFERENCES: "
f"{preference_summary}\n"
f"{self._preference_context_guidance}\n"
)

@property
def _preference_context_guidance(self) -> str:
"""Returns the guidance appended after the preference summary."""
return (
"Treat these preferences as soft background tastes, not as new "
"requirements."
)

@property
def _persona_text(self) -> str:
"""Returns a compact persona description."""
if not self.persona:
return ""

if self.persona.persona_description:
return self.persona.persona_description

return ", ".join(
[
f"{key}={value}"
for key, value in self.persona.characteristics.items()
]
)
Comment thread
dkkdark marked this conversation as resolved.

@abstractmethod
def build_new_prompt(self, **kwargs) -> str:
Expand Down
43 changes: 29 additions & 14 deletions usersimcrs/simulator/llm/prompt/stop_prompt.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
"""Define the prompt for stopping the conversation."""

from typing import Optional

from usersimcrs.core.information_need import InformationNeed
from usersimcrs.simulator.llm.prompt.prompt import Prompt
from usersimcrs.user_modeling.preference_model import PreferenceModel
from usersimcrs.user_modeling.persona import Persona

DEFAULT_STOP_DEFINITION = (
Expand All @@ -21,7 +24,8 @@ def __init__(
information_need: InformationNeed,
item_type: str,
prompt_definition: str = DEFAULT_STOP_DEFINITION,
persona: Persona = None,
persona: Optional[Persona] = None,
preference_model: Optional[PreferenceModel] = None,
) -> None:
"""Initializes the prompt.

Expand All @@ -31,20 +35,37 @@ def __init__(
prompt_definition: The definition of the task to be performed.
Defaults to DEFAULT_STOP_DEFINITION.
persona: The persona of the user. Defaults to None.
preference_model: Preference model. Defaults to None.
"""
super().__init__(
information_need, item_type, prompt_definition, persona
information_need,
item_type,
prompt_definition,
persona,
preference_model,
)

@property
def prompt_text(self) -> str:
"""Prompt for the user simulator."""
prompt_text = self._initial_prompt

if self._preference_context:
prompt_text += self._preference_context.rstrip("\n")

prompt_text += "\n"

if self._prompt_context:
prompt_text += self._prompt_context.rstrip("\n")

return prompt_text + "\nCONTINUE: "

@property
def _preference_context_guidance(self) -> str:
"""Returns the guidance appended after the preference summary."""
return (
self._initial_prompt
+ "\n"
+ self._prompt_context
+ "\n"
+ "CONTINUE: "
"Use these preferences only as soft context when deciding "
"whether the conversation is still productive."
)

def build_new_prompt(self) -> str:
Expand All @@ -60,13 +81,7 @@ def build_new_prompt(self) -> str:
" Take into account your PERSONA when deciding to stop the "
"conversation.\n"
)
stringified_characteristics = ", ".join(
[
f"{key}={value}"
for key, value in self.persona.characteristics.items()
]
)
initial_prompt += f"PERSONA: {stringified_characteristics}\n"
initial_prompt += f"PERSONA: {self._persona_text}\n"

initial_prompt += "\nHISTORY:\n"
return initial_prompt
29 changes: 20 additions & 9 deletions usersimcrs/simulator/llm/prompt/utterance_generation_prompt.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@
for Task-Oriented Dialog Systems", arXiv 2306.00774.
"""

from typing import Optional

from dialoguekit.core.utterance import Utterance
from dialoguekit.participant.participant import DialogueParticipant
from usersimcrs.core.information_need import InformationNeed
from usersimcrs.simulator.llm.prompt.prompt import Prompt
from usersimcrs.user_modeling.preference_model import PreferenceModel
from usersimcrs.user_modeling.persona import Persona

DEFAULT_TASK_DEFINITION = (
Expand All @@ -34,7 +37,8 @@ def __init__(
information_need: InformationNeed,
item_type: str,
prompt_definition: str = DEFAULT_TASK_DEFINITION,
persona: Persona = None,
persona: Optional[Persona] = None,
preference_model: Optional[PreferenceModel] = None,
) -> None:
"""Initializes the prompt.

Expand All @@ -44,9 +48,14 @@ def __init__(
prompt_definition: The definition of the task to be performed.
Defaults to DEFAULT_TASK_DEFINITION.
persona: The persona of the user. Defaults to None.
preference_model: Preference model. Defaults to None.
"""
super().__init__(
information_need, item_type, prompt_definition, persona
information_need,
item_type,
prompt_definition,
persona,
preference_model,
)

def build_new_prompt(self) -> str:
Expand All @@ -61,13 +70,7 @@ def build_new_prompt(self) -> str:
initial_prompt += (
" Adapt your responses considering your PERSONA.\n"
)
stringified_characteristics = ", ".join(
[
f"{key}={value}"
for key, value in self.persona.characteristics.items()
]
)
initial_prompt += f"PERSONA: {stringified_characteristics}\n"
initial_prompt += f"PERSONA: {self._persona_text}\n"
else:
initial_prompt += (
"Be precise with the REQUIREMENTS, clear and concise.\n"
Expand All @@ -90,6 +93,14 @@ def build_new_prompt(self) -> str:
)
return initial_prompt

@property
def _preference_context_guidance(self) -> str:
"""Returns the guidance appended after the preference summary."""
return (
"Use these preferences to shape what the user accepts, rejects, "
"asks to avoid, or follows up on."
)

def update_prompt_context(
self, utterance: Utterance, participant: DialogueParticipant
) -> None:
Expand Down
6 changes: 6 additions & 0 deletions usersimcrs/simulator/user_simulator.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
"""User simulator abstract class."""

from abc import ABC, abstractmethod
from typing import Optional

from dialoguekit.core.annotated_utterance import AnnotatedUtterance
from dialoguekit.core.utterance import Utterance
from dialoguekit.participant.user import User, UserType
from usersimcrs.core.information_need import generate_random_information_need
from usersimcrs.core.simulation_domain import SimulationDomain
from usersimcrs.items.item_collection import ItemCollection
from usersimcrs.user_modeling.preference_model import PreferenceModel


class UserSimulator(User, ABC):
Expand All @@ -16,11 +18,13 @@ def __init__(
id: str,
domain: SimulationDomain,
item_collection: ItemCollection,
preference_model: Optional[PreferenceModel] = None,
) -> None:
"""Initializes the user simulator."""
super().__init__(id, UserType.SIMULATOR)
self._domain = domain
self._item_collection = item_collection
self.preference_model = preference_model
self.get_new_information_need()

def get_new_information_need(self) -> None:
Expand Down Expand Up @@ -53,4 +57,6 @@ def receive_utterance(self, utterance: Utterance) -> None:
response = self._generate_response(utterance)
if not isinstance(response, AnnotatedUtterance):
response = AnnotatedUtterance.from_utterance(response)
if self.preference_model:
self.preference_model.update_from_utterance(response)
self._dialogue_connector.register_user_utterance(response)
Loading
Loading