Skip to content

Commit

Permalink
Merge branch 'main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
emmanuel-ferdman authored Nov 16, 2024
2 parents 3edfe70 + b758468 commit b698173
Show file tree
Hide file tree
Showing 23 changed files with 394 additions and 226 deletions.
20 changes: 18 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,29 @@ All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](http://keepachangelog.com/).

## [1.8.8] - 2024-11-13

### Fixed

- Fixed a bug in the Schelling diagram payoffs component preventing some players
from seeing outcomes on certain steps.


## [1.8.7] - 2024-11-5

### Added

- add parochial universalization agent to the main factories
- add Jinja2 to the requirements to improve the prompting experience
- Add get_raw_memories and get_raw_memories_as_text functions on the memory component.


## [1.8.6] - 2024-10-29

### Added

- Add ability to save and load rational and basic agents to/from json.
- Add a version of agent development colab that uses GCP hosted model

- Add a version of agent development colab that uses GCP hosted model.

## [1.8.5] - 2024-10-21

Expand Down
8 changes: 8 additions & 0 deletions concordia/associative_memory/associative_memory.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,14 @@ def get_state(self) -> entity_component.ComponentState:
"""Converts the AssociativeMemory to a dictionary."""

with self._memory_bank_lock:
serialized_times = self._memory_bank['time'].apply(
lambda x: x.strftime('[%d-%b-%Y-%H:%M:%S]')
).tolist()
output = {
'seed': self._seed,
'stored_hashes': list(self._stored_hashes),
'memory_bank': self._memory_bank.to_json(),
'time': serialized_times,
}
if self._interval:
output['interval'] = self._interval.total_seconds()
Expand All @@ -101,6 +105,10 @@ def set_state(self, state: entity_component.ComponentState) -> None:
self._seed = state['seed']
self._stored_hashes = set(state['stored_hashes'])
self._memory_bank = pd.read_json(state['memory_bank'])
self._memory_bank['time'] = [
datetime.datetime.strptime(t, '[%d-%b-%Y-%H:%M:%S]')
for t in state['time']
]
if 'interval' in state:
self._interval = datetime.timedelta(seconds=state['interval'])

Expand Down
4 changes: 3 additions & 1 deletion concordia/components/game_master/conversation.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

from concordia import components as generic_components
from concordia.agents import deprecated_agent
from concordia.agents import entity_agent
from concordia.associative_memory import associative_memory
from concordia.associative_memory import blank_memories
from concordia.clocks import game_clock
Expand All @@ -33,6 +34,7 @@
from concordia.utils import helper_functions
import termcolor


CONVERSATIONALIST_STYLES = (
'succinct',
'laconic',
Expand Down Expand Up @@ -89,7 +91,7 @@ class Conversation(component.Component):

def __init__(
self,
players: Sequence[deprecated_agent.BasicAgent],
players: Sequence[deprecated_agent.BasicAgent | entity_agent.EntityAgent],
model: language_model.LanguageModel,
memory: associative_memory.AssociativeMemory,
clock: game_clock.MultiIntervalClock,
Expand Down
3 changes: 2 additions & 1 deletion concordia/components/game_master/direct_effect.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import datetime

from concordia.agents import deprecated_agent
from concordia.agents import entity_agent
from concordia.associative_memory import associative_memory
from concordia.document import interactive_document
from concordia.language_model import language_model
Expand All @@ -37,7 +38,7 @@ class DirectEffect(component.Component):

def __init__(
self,
players: Sequence[deprecated_agent.BasicAgent],
players: Sequence[deprecated_agent.BasicAgent | entity_agent.EntityAgent],
clock_now: Callable[[], datetime.datetime],
model: language_model.LanguageModel,
memory: associative_memory.AssociativeMemory,
Expand Down
8 changes: 8 additions & 0 deletions concordia/components/game_master/schelling_diagram_payoffs.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,9 @@ def __init__(
self._partial_states = {player.name: '' for player in self._players}
self._player_scores = {player.name: 0 for player in self._players}

self._map_names_to_players = {
player.name: player for player in self._players}

self._resolution_scene = resolution_scene
self._current_scene = current_scene.CurrentScene(
name='current scene type',
Expand Down Expand Up @@ -265,6 +268,11 @@ def update_after_event(
# Use the outcome summarization function to get the state.
self._set_outcome_messages(rewards, binary_joint_action, joint_action)
self._memory.extend([self.state(),])
for player_name, partial_state in self._partial_states.items():
if partial_state:
if isinstance(self._map_names_to_players[player_name],
entity_agent.EntityAgent):
self._map_names_to_players[player_name].observe(partial_state)

joint_action_for_log = str(self._partial_joint_action)
payoffs_for_log = self.state()
Expand Down
20 changes: 18 additions & 2 deletions concordia/environment/scenes/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,15 @@

"""Scene runner."""

from collections.abc import Sequence
from collections.abc import Mapping, Sequence

from concordia.agents import deprecated_agent
from concordia.agents import entity_agent
from concordia.environment import game_master
from concordia.typing import clock as game_clock
from concordia.typing import logging as logging_lib
from concordia.typing import scene as scene_lib
from concordia.utils import json as json_lib


def _get_interscene_messages(
Expand Down Expand Up @@ -58,9 +61,10 @@ def _get_interscene_messages(
def run_scenes(
environment: game_master.GameMaster,
scenes: Sequence[scene_lib.SceneSpec],
players: Sequence[deprecated_agent.BasicAgent],
players: Sequence[deprecated_agent.BasicAgent | entity_agent.EntityAgent],
clock: game_clock.GameClock,
verbose: bool = False,
compute_metrics: Mapping[str, logging_lib.Metric] | None = None,
) -> None:
"""Run a sequence of scenes.
Expand All @@ -70,6 +74,7 @@ def run_scenes(
players: full list of players (a subset may participate in each scene)
clock: the game clock which may be advanced between scenes
verbose: if true then print intermediate outputs
compute_metrics: Optionally, a function to compute metrics.
"""
players_by_name = {player.name: player for player in players}
if len(players_by_name) != len(players):
Expand Down Expand Up @@ -135,3 +140,14 @@ def run_scenes(
print(f'{participant.name} -- conclusion: {message}')
participant.observe(message)
this_scene_game_master_memory.add(message)

# Branch off a metric scene if applicable
if scene.scene_type.save_after_each_scene:
serialized_agents = {}
for participant in participants:
serialized_agents = {}
json_representation = json_lib.save_to_json(participant)
serialized_agents[participant.name] = json_representation

if compute_metrics is not None:
compute_metrics(serialized_agents)
14 changes: 8 additions & 6 deletions concordia/factory/environment/basic_game_master.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,11 @@

"""A Generic Environment Factory."""

from collections.abc import Callable, Sequence
from collections.abc import Callable, Mapping, Sequence
import operator

from concordia import components as generic_components
from concordia.agents import deprecated_agent
from concordia.agents import entity_agent
from concordia.agents import entity_agent_with_logging
from concordia.associative_memory import associative_memory
from concordia.associative_memory import blank_memories
from concordia.associative_memory import importance_function
Expand All @@ -42,7 +41,7 @@ def build_game_master(
embedder: Callable[[str], np.ndarray],
importance_model: importance_function.ImportanceModel,
clock: game_clock.MultiIntervalClock,
players: Sequence[deprecated_agent.BasicAgent],
players: Sequence[entity_agent_with_logging.EntityAgentWithLogging],
shared_memories: Sequence[str],
shared_context: str,
blank_memory_factory: blank_memories.MemoryFactory,
Expand Down Expand Up @@ -191,7 +190,7 @@ def build_decision_scene_game_master(
model: language_model.LanguageModel,
memory: associative_memory.AssociativeMemory,
clock: game_clock.MultiIntervalClock,
players: Sequence[deprecated_agent.BasicAgent],
players: Sequence[entity_agent_with_logging.EntityAgentWithLogging],
decision_action_spec: agent_lib.ActionSpec,
payoffs: gm_components.schelling_diagram_payoffs.SchellingPayoffs,
verbose: bool = False,
Expand Down Expand Up @@ -289,12 +288,13 @@ def create_html_log(
def run_simulation(
*,
model: language_model.LanguageModel,
players: Sequence[deprecated_agent.BasicAgent | entity_agent.EntityAgent],
players: Sequence[entity_agent_with_logging.EntityAgentWithLogging],
primary_environment: game_master.GameMaster,
clock: game_clock.MultiIntervalClock,
scenes: Sequence[scene_lib.SceneSpec],
secondary_environments: Sequence[game_master.GameMaster] = tuple(),
summarize_entire_episode_in_log: bool = True,
compute_metrics: Callable[[Mapping[str, str]], None] | None = None,
) -> str:
"""Run a simulation.
Expand All @@ -307,6 +307,7 @@ def run_simulation(
secondary_environments: Sequence of secondary game masters for scenes.
summarize_entire_episode_in_log: Optionally, include summaries of the full
episode in the log.
compute_metrics: Optionally, a function to compute metrics.
Returns:
an HTML string log of the simulation.
Expand All @@ -317,6 +318,7 @@ def run_simulation(
scenes=scenes,
players=players,
clock=clock,
compute_metrics=compute_metrics,
)
result_html_log = create_html_log(
model=model,
Expand Down
14 changes: 9 additions & 5 deletions concordia/factory/environment/factories_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,13 @@

from absl.testing import absltest
from absl.testing import parameterized
from concordia.agents import deprecated_agent
from concordia.agents import entity_agent_with_logging
from concordia.associative_memory import associative_memory
from concordia.associative_memory import blank_memories
from concordia.associative_memory import formative_memories
from concordia.associative_memory import importance_function
from concordia.clocks import game_clock
from concordia.components import agent as agent_components
from concordia.environment import game_master
from concordia.factory.environment import basic_game_master
from concordia.language_model import no_language_model
Expand Down Expand Up @@ -63,12 +64,15 @@ def test_give_me_a_name(self, environment_name: str):
importance=importance_model_gm.importance,
clock_now=clock.now,
)
player_a = deprecated_agent.BasicAgent(
act_component = agent_components.concat_act_component.ConcatActComponent(
model=model,
agent_name='Rakshit',
clock=clock,
components=[],
update_interval=datetime.timedelta(hours=1),
component_order=[],
)
player_a = entity_agent_with_logging.EntityAgentWithLogging(
agent_name='Rakshit',
act_component=act_component,
context_components={},
)

players = [player_a]
Expand Down
14 changes: 10 additions & 4 deletions concordia/language_model/together_ai.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,11 +205,14 @@ def sample_text(
)
except (together.error.RateLimitError,
together.error.APIError,
together.error.ServiceUnavailableError) as err:
together.error.ServiceUnavailableError,
together.error.InvalidRequestError) as err:
if attempts >= _NUM_SILENT_ATTEMPTS:
print(f' Exception: {err}')
print(f' Text exception prompt: {prompt}')
if isinstance(err, together.error.APIError):
if isinstance(err, together.error.APIError) or isinstance(
err, together.error.InvalidRequestError
):
# If hit the error that arises from a prompt that is too long then
# re-run the trimming function with a more pessimistic guess of the
# the number of characters per token.
Expand Down Expand Up @@ -282,11 +285,14 @@ def _sample_choice(
)
except (together.error.RateLimitError,
together.error.APIError,
together.error.ServiceUnavailableError) as err:
together.error.ServiceUnavailableError,
together.error.InvalidRequestError) as err:
if attempts >= _NUM_SILENT_ATTEMPTS:
print(f' Exception: {err}')
print(f' Choice exception prompt: {augmented_prompt}')
if isinstance(err, together.error.APIError):
if isinstance(err, together.error.APIError) or isinstance(
err, together.error.InvalidRequestError
):
# If hit the error that arises from a prompt that is too long then
# re-run the trimming function with a more pessimistic guess of the
# the number of characters per token.
Expand Down
3 changes: 2 additions & 1 deletion concordia/thought_chains/thought_chains.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import random

from concordia.agents import deprecated_agent
from concordia.agents import entity_agent
from concordia.document import interactive_document
from concordia.language_model import language_model
from concordia.typing import agent as agent_types
Expand Down Expand Up @@ -322,7 +323,7 @@ class AccountForAgencyOfOthers:
def __init__(
self,
model: language_model.LanguageModel,
players: Sequence[deprecated_agent.BasicAgent],
players: Sequence[deprecated_agent.BasicAgent | entity_agent.EntityAgent],
verbose: bool = False,
):
self._model = model
Expand Down
12 changes: 11 additions & 1 deletion concordia/typing/logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,19 @@

"""Types for logging."""

from collections.abc import Mapping
from collections.abc import Mapping, Sequence
import dataclasses
from typing import Any, Callable
from concordia.typing import entity as entity_lib

LoggingChannel = Callable[[Mapping[str, Any]], None]

NoOpLoggingChannel = lambda x: None


@dataclasses.dataclass(frozen=True)
class Metric:
question: str
output_type: entity_lib.OutputType
options: Sequence[str] | None = None
context: str | None = None
3 changes: 3 additions & 0 deletions concordia/typing/scene.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ class SceneTypeSpec:
the game master to ask the agents to produce during steps of this scene.
override_game_master: optionally specify a game master to use instead of the
default one.
save_after_each_scene: optionally specify whether to save the agent's state
after each scene.
"""

name: str
Expand All @@ -52,6 +54,7 @@ class SceneTypeSpec:
| None
) = None
override_game_master: game_master.GameMaster | None = None
save_after_each_scene: bool = False


@dataclasses.dataclass(frozen=True)
Expand Down
Loading

0 comments on commit b698173

Please sign in to comment.