21
21
from concordia .components .agent .unstable import memory as memory_component
22
22
from concordia .document import interactive_document
23
23
from concordia .environment .scenes .unstable import runner as scene_runner
24
+ from concordia .environment .unstable import engine as engine_lib
24
25
from concordia .language_model import language_model
25
26
from concordia .typing import logging
26
27
from concordia .typing .unstable import entity as entity_lib
27
28
from concordia .typing .unstable import entity_component
29
+ from concordia .typing .unstable import scene as scene_lib
28
30
29
31
30
32
DEFAULT_NEXT_ACTING_COMPONENT_NAME = '__next_acting__'
31
33
# Initiative is the Dungeons & Dragons term for the rule system that controls
32
34
# turn taking.
33
35
DEFAULT_NEXT_ACTING_PRE_ACT_KEY = '\n Initiative'
34
36
37
+ DEFAULT_NEXT_ACTION_SPEC_COMPONENT_NAME = '__next_action_spec__'
38
+ DEFAULT_NEXT_ACTION_SPEC_PRE_ACT_KEY = '\n Type of action'
39
+
40
+ _DEFAULT_CALL_TO_NEXT_ACTION_SPEC = (
41
+ 'In what action spec format should {name} respond?' )
42
+
35
43
36
44
class NextActing (entity_component .ContextComponent ):
37
45
"""A component that decides whose turn is next.
@@ -82,7 +90,6 @@ def pre_act(
82
90
action_spec : entity_lib .ActionSpec ,
83
91
) -> str :
84
92
result = ''
85
- prompt_to_log = ''
86
93
if action_spec .output_type == entity_lib .OutputType .NEXT_ACTING :
87
94
entity_name = self .get_entity ().name
88
95
prompt = interactive_document .InteractiveDocument (self ._model )
@@ -96,12 +103,7 @@ def pre_act(
96
103
question = 'Whose turn is next?' ,
97
104
answers = self ._player_names )
98
105
result = self ._player_names [idx ]
99
- prompt_to_log = prompt .view ().text ()
100
106
101
- self ._logging_channel (
102
- {'Key' : self ._pre_act_key ,
103
- 'Value' : result ,
104
- 'Prompt' : prompt_to_log })
105
107
return result
106
108
107
109
@@ -162,7 +164,6 @@ def pre_act(
162
164
action_spec : entity_lib .ActionSpec ,
163
165
) -> str :
164
166
result = ''
165
- prompt_to_log = ''
166
167
if action_spec .output_type == entity_lib .OutputType .NEXT_ACTING :
167
168
entity_name = self .get_entity ().name
168
169
prompt = interactive_document .InteractiveDocument (self ._model )
@@ -178,10 +179,152 @@ def pre_act(
178
179
question = 'Whose turn is next?' ,
179
180
answers = scene_participants )
180
181
result = scene_participants [idx ]
181
- prompt_to_log = prompt .view ().text ()
182
182
183
- self ._logging_channel (
184
- {'Key' : self ._pre_act_key ,
185
- 'Value' : result ,
186
- 'Prompt' : prompt_to_log })
187
183
return result
184
+
185
+
186
+ class NextActionSpec (entity_component .ContextComponent ):
187
+ """A component that decides whose turn is next.
188
+ """
189
+
190
+ def __init__ (
191
+ self ,
192
+ model : language_model .LanguageModel ,
193
+ player_names : Sequence [str ],
194
+ components : Mapping [
195
+ entity_component .ComponentName , str
196
+ ] = types .MappingProxyType ({}),
197
+ call_to_next_action_spec : str = _DEFAULT_CALL_TO_NEXT_ACTION_SPEC ,
198
+ pre_act_key : str = DEFAULT_NEXT_ACTION_SPEC_PRE_ACT_KEY ,
199
+ logging_channel : logging .LoggingChannel = logging .NoOpLoggingChannel ,
200
+ ):
201
+ """Initializes the component.
202
+
203
+ Args:
204
+ model: The language model to use for the component.
205
+ player_names: Names of players to choose from.
206
+ components: The components to condition the answer on. This is a mapping
207
+ of the component name to a label to use in the prompt.
208
+ call_to_next_action_spec: prompt to use for the game master to decide on
209
+ what action spec to use for the next turn. Will be formatted to
210
+ substitute {name} for the name of the player whose turn is next.
211
+ pre_act_key: Prefix to add to the output of the component when called
212
+ in `pre_act`.
213
+ logging_channel: The channel to use for debug logging.
214
+
215
+ Raises:
216
+ ValueError: If the component order is not None and contains duplicate
217
+ components.
218
+ """
219
+ super ().__init__ ()
220
+ self ._model = model
221
+ self ._player_names = player_names
222
+ self ._components = dict (components )
223
+ self ._call_to_next_action_spec = call_to_next_action_spec
224
+ self ._pre_act_key = pre_act_key
225
+ self ._logging_channel = logging_channel
226
+
227
+ def _get_named_component_pre_act_value (self , component_name : str ) -> str :
228
+ """Returns the pre-act value of a named component of the parent entity."""
229
+ return (
230
+ self .get_entity ().get_component (
231
+ component_name , type_ = action_spec_ignored .ActionSpecIgnored
232
+ ).get_pre_act_value ()
233
+ )
234
+
235
+ def pre_act (
236
+ self ,
237
+ action_spec : entity_lib .ActionSpec ,
238
+ ) -> str :
239
+ result = ''
240
+ if action_spec .output_type == entity_lib .OutputType .NEXT_ACTION_SPEC :
241
+ entity_name = self .get_entity ().name
242
+ prompt = interactive_document .InteractiveDocument (self ._model )
243
+ component_states = '\n ' .join ([
244
+ f"{ entity_name } 's"
245
+ f' { prefix } :\n { self ._get_named_component_pre_act_value (key )} '
246
+ for key , prefix in self ._components .items ()
247
+ ])
248
+ prompt .statement (f'{ component_states } \n ' )
249
+ idx = prompt .multiple_choice_question (
250
+ question = 'Whose turn is next?' ,
251
+ answers = self ._player_names )
252
+ active_player = self ._player_names [idx ]
253
+ prompt .statement (
254
+ 'Example formatted action specs:\n 1). "prompt: p;;type: free"\n '
255
+ '2). "prompt: p;;type: choice;;options: x, y, z".\n Note that p is a '
256
+ 'string of any length, typically a question, and x, y, z, etc are '
257
+ 'multiple choice answer responses. For instance, a valid format '
258
+ 'could be indicated as '
259
+ 'prompt: Where will Edgar go?;;type: choice;;'
260
+ 'options: home, London, Narnia, the third moon of Jupiter' )
261
+ result = prompt .open_question (
262
+ question = self ._call_to_next_action_spec .format (name = active_player ),
263
+ max_tokens = 512 )
264
+
265
+ return result
266
+
267
+
268
+ class NextActionSpecFromSceneSpec (entity_component .ContextComponent ):
269
+ """A component that decides the next action spec using the current scene spec.
270
+ """
271
+
272
+ def __init__ (
273
+ self ,
274
+ scenes : Sequence [scene_lib .ExperimentalSceneSpec ],
275
+ memory_component_name : str = (
276
+ memory_component .DEFAULT_MEMORY_COMPONENT_NAME
277
+ ),
278
+ pre_act_key : str = DEFAULT_NEXT_ACTION_SPEC_PRE_ACT_KEY ,
279
+ logging_channel : logging .LoggingChannel = logging .NoOpLoggingChannel ,
280
+ ):
281
+ """Initializes the component.
282
+
283
+ Args:
284
+ scenes: All scenes to be used in the episode.
285
+ memory_component_name: The name of the memory component.
286
+ pre_act_key: Prefix to add to the output of the component when called
287
+ in `pre_act`.
288
+ logging_channel: The channel to use for debug logging.
289
+
290
+ Raises:
291
+ ValueError: If the component order is not None and contains duplicate
292
+ components.
293
+ """
294
+ super ().__init__ ()
295
+ self ._memory_component_name = memory_component_name
296
+ self ._pre_act_key = pre_act_key
297
+ self ._logging_channel = logging_channel
298
+
299
+ # Extract all scene type specs from the provided scenes.
300
+ self ._scene_type_specs = {}
301
+ for scene in scenes :
302
+ self ._scene_type_specs [scene .scene_type .name ] = scene .scene_type
303
+
304
+ def _get_named_component_pre_act_value (self , component_name : str ) -> str :
305
+ """Returns the pre-act value of a named component of the parent entity."""
306
+ return (
307
+ self .get_entity ().get_component (
308
+ component_name , type_ = action_spec_ignored .ActionSpecIgnored
309
+ ).get_pre_act_value ()
310
+ )
311
+
312
+ def _get_current_scene_type (self ) -> scene_lib .ExperimentalSceneTypeSpec :
313
+ memory = self .get_entity ().get_component (
314
+ self ._memory_component_name , type_ = memory_component .Memory
315
+ )
316
+ scene_type_str = scene_runner .get_current_scene_type (memory = memory )
317
+ scene_type_spec = self ._scene_type_specs [scene_type_str ]
318
+ return scene_type_spec
319
+
320
+ def pre_act (
321
+ self ,
322
+ action_spec : entity_lib .ActionSpec ,
323
+ ) -> str :
324
+ action_spec_string = ''
325
+ if action_spec .output_type == entity_lib .OutputType .NEXT_ACTION_SPEC :
326
+ scene_type_spec = self ._get_current_scene_type ()
327
+ action_spec = scene_type_spec .action_spec
328
+ action_spec_string = engine_lib .action_spec_to_string (action_spec )
329
+
330
+ return action_spec_string
0 commit comments