Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ollama #2

Open
wants to merge 40 commits into
base: fix-and-improve
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
fbb12c5
added ollama requirements to pip
DanielMarchand Jul 14, 2024
34302ca
added basic ollama support
DanielMarchand Jul 14, 2024
3b1b890
more work on getting prompts to align for llama3
DanielMarchand Jul 15, 2024
e171aa0
added more optional printing
DanielMarchand Jul 15, 2024
aabf19a
more modifications to the prompts to make more friendly to local model
DanielMarchand Jul 16, 2024
a8bbee2
begun restructure of how prompts are stored and selected
DanielMarchand Jul 17, 2024
2ddac3a
renamed openai_config to llm_config to reflect the increasing support…
DanielMarchand Jul 17, 2024
a8fc578
removed defunct run gpt prompt
DanielMarchand Jul 17, 2024
d7dd9b9
moved the EXCEPT_ON_FAILSAFE to utils.py and updated docs
DanielMarchand Jul 17, 2024
9047ed5
changed prompts to use the new prompt dirs
DanielMarchand Jul 17, 2024
711e2e6
udpated hourly schedule prompt for better results
DanielMarchand Jul 17, 2024
cd6c080
added note about the embeddings not working on n25
DanielMarchand Jul 17, 2024
c307729
prompt changes for llama3 and cleaning up the task decomp function
DanielMarchand Jul 19, 2024
f87c65d
updates to sections that generate (s,p,o) triples)
DanielMarchand Jul 20, 2024
d2201b9
substantial improvements in local run time
DanielMarchand Jul 20, 2024
361607c
local model very close to being able to run independantly for lengthy…
DanielMarchand Jul 21, 2024
5f76763
made task decomp regex more robust, set min temperature to 0.1 in all…
DanielMarchand Jul 22, 2024
ed07874
fixing issues with generating focal point
DanielMarchand Jul 22, 2024
d276734
refactoring event triple, object event and insight and evidence, code…
DanielMarchand Jul 23, 2024
bb20209
fixed instabilities in room locator, gave ChatGPT_safe more retries
DanielMarchand Jul 23, 2024
559ceea
removed misleading comments (probably copy/pasted from another function)
DanielMarchand Jul 23, 2024
8b5e12f
substantial work to generating triples, as well as location and decom…
DanielMarchand Jul 23, 2024
983b34b
restored to only using the debug variable in utils
DanielMarchand Jul 24, 2024
a1dbd26
added EXCEPT_ON_FAILSAFE=False default to utils.py (preserves default…
DanielMarchand Jul 24, 2024
5a61885
removed symbol in emojii (may cause problems on windows though?)
DanielMarchand Jul 24, 2024
0fede96
removed some dead comments and print statements
DanielMarchand Jul 24, 2024
5181a3a
except on too many retries set to False
DanielMarchand Jul 24, 2024
4d6e5d1
added simulation name validator so nobody else has to waste an hour t…
DanielMarchand Jul 24, 2024
644005f
Merge commit '4d6e5d18' into fix-and-improve_add-ollama
DanielMarchand Jul 24, 2024
8dee870
fixed issue in merge
DanielMarchand Jul 24, 2024
c1ed7fc
improved parsing of some outputs, code is now largely stable for loca…
DanielMarchand Jul 26, 2024
b08ea5c
made compress sim use a command line argument
DanielMarchand Jul 27, 2024
1da280d
made the safe_generate command easier to parse the complete trace int…
DanielMarchand Jul 27, 2024
10bc2c0
switching to mistralnemo llm
DanielMarchand Jul 28, 2024
561c9c7
finally replaced all ChatGPT_safe_response with safe_response, fine-g…
DanielMarchand Jul 28, 2024
8907061
fixed bug in logging that would mess up the retry count
DanielMarchand Jul 29, 2024
c5d6351
poignancy more robust to errors, failsafe set to 1
DanielMarchand Jul 29, 2024
9d49a96
moved nemo prompts to the other repos, due to parser changes this is …
DanielMarchand Jul 30, 2024
004bf75
place the extracted json files into separate directories
DanielMarchand Aug 4, 2024
dd05d70
set max number of restarts to be 10 else risk of filling disk
DanielMarchand Sep 29, 2024
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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# OpenAI API key
openai_config.json
llm_config.json

environment/frontend_server/storage/*
environment/frontend_server/temp_storage*/*
Expand Down Expand Up @@ -115,4 +116,4 @@ ENV/
.spyderproject

# Rope project settings
.ropeproject
.ropeproject
33 changes: 30 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Since the project is no longer officially supported, I decided to develop some n
- [x] **Cost tracking** using [openai-cost-logger](https://github.com/drudilorenzo/openai-cost-logger)
- [x] Set **cost upperbound** and stop the experiment when it is reached
- [x] New models and OpenAI API support
- [x] Added [skip-morning-s-14](https://github.com/drudilorenzo/generative_agents/tree/fix-and-improve/environment/frontend_server/storage/skip-morning-s-14): a simulation based on `base_the_ville_n25` that starts after 3000 steps (~8:00am). That permits us to save time and see interactions and actions earlier.
- [x] Added [skip-morning-s-14](https://github.com/drudilorenzo/generative_agents/tree/fix-and-improve/environment/frontend_server/storage/skip-morning-s-14): a simulation based on `base_the_ville_n25` that starts after 3000 steps (~8:00am). That permits us to save time and see interactions and actions earlier. (Note that one must use the same embedding model to load this simulation)
- [x] **Zoom in**/**Zoom out** using Z and X
- [x] [Powerful automated script](#step-3-automatic-execution) for enhanced simulation performance.

Expand All @@ -38,7 +38,7 @@ Do not change the env name to be able to use the bash scripts later.

### Step 2. OpenAI Config

Create a file called `openai_config.json` in the root directory.\
Create a file called `llm_config.json` in the root directory.\
Azure example:
```json
{
Expand Down Expand Up @@ -85,10 +85,37 @@ OpenAI example:
"cost-upperbound": 10
}
```
Ollama example:
```json
{
"client": "ollama",
"base_url": "http://localhost:11434/v1",
"model": "llama3-chatqa:8b",
"prompt_template": "prompts_llama3",
"model-key": "dummy_key_needed_but_not_used",
"model-costs": {
"input": 0.001,
"output": 0.001
},
"embeddings-client": "ollama",
"embeddings": "nomic-embed-text",
"embeddings-key": "dummy_key_needed_but_not_used",
"embeddings-costs": {
"input": 0.0001,
"output": 0.0001
},
"experiment-name": "simulacra-test-ollama",
"cost-upperbound": 10000000000000
}
```

Feel free to change and test also other models (and change accordingly the input and output costs).\
Be aware that the only supported clients are **azure** and **openai**.\
Be aware that the only fully-supported clients are **azure** and **openai**.\
The ollama client supports local models, but many of the prompts do not full work with local models yet.
Make sure to first install ollama, and to pull any models you plan on using, see "[ollama-openai-compatibility](https://ollama.com/blog/openai-compatibility)"\
The generation and the embedding models are configured separately to be able to use different clients.\
The llama3 models require different prompts than the chatgpt, and these different prompts are specified via the template_dir argument.
If you are testing a new model you can use either the prompts_chagpt or the prompts_llama3 as starting points.
Change also the `cost-upperbound` according to your needs (the cost computation is done using "[openai-cost-logger](https://github.com/drudilorenzo/openai-cost-logger)" and the costs are specified per million tokens).


Expand Down
2 changes: 2 additions & 0 deletions README_origin.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ collision_block_id = "32125"

# Verbose
debug = True
EXCEPT_ON_FAILSAFE=False

```
Replace `<Your OpenAI API>` with your OpenAI API key, and `<name>` with your name.

Expand Down
147 changes: 147 additions & 0 deletions log_parser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import os
import json
import re

def parse_section(section, verbose=False):
""" Parse the complete section to extract necessary details with an optional verbose output """
if verbose:
print("Section preview:", section[:200]) # Print beginning of the section to inspect

repeat_count_match = re.search(r'---- repeat count: \s*(\d+)', section)
repeat_count = int(repeat_count_match.group(1)) if repeat_count_match else 0

prompt_inputs = re.findall(r'---- prompt_input_\d+ (.*?)(?=----|$)', section, re.DOTALL)

prompt = re.findall(r'---- prompt: (.*?)(?=----|$)', section, re.DOTALL)[0]

func_validate = re.findall(r'---- func_validate: (.*?)(?=----|$)', section, re.DOTALL)[0]

gpt_parameter = re.findall(r'---- gpt_parameter: (.*?)(?=----|$)', section, re.DOTALL)[0]

prompt_template = re.findall(r'---- prompt_template: (.*?)(?=----|$)', section, re.DOTALL)[0].strip()

curr_response = re.findall(r'---- curr_gpt_response: (.*?)(?=----|$)', section, re.DOTALL)[0]

func_cleanup = re.findall(r'---- func_clean_up: (.*?)(?=----|$)', section, re.DOTALL)[0]

func_validate_match = re.search(r'---- func_validate: \s*(True|False)', section)
func_validate = func_validate_match.group(1).lower() == 'true' if func_validate_match else False


return {
"prompt_inputs": prompt_inputs,
"repeat_count": repeat_count,
"prompt": prompt,
"prompt_template": prompt_template,
"gpt_parameter": gpt_parameter,
"func_validate": func_validate,
"curr_response": curr_response,
"func_cleanup": func_cleanup
}

#def read_large_file(input_file_path, output_directory, verbose=False):
# os.makedirs(output_directory, exist_ok=True) # Ensure the output directory exists
# section = ""
# file_count = 0 # To keep track of how many files we have created
# fields_found = set()
#
# required_fields = {
# "prompt_template_prompt_inputs",
# "repeat_count",
# "func_validate",
# "curr_gpt_response",
# "func_cleanup"
# }
#
# with open(input_file_path, 'r') as file:
# for line in file:
# section += line
#
# # Check if the current line contains any of the required fields
# if "prompt_input_" in line:
# fields_found.add("prompt_template_prompt_inputs")
# if "repeat count:" in line:
# fields_found.add("repeat_count")
# if "func_validate" in line:
# fields_found.add("func_validate")
# if "curr_gpt_response:" in line:
# fields_found.add("curr_gpt_response")
# if "func_clean_up:" in line:
# fields_found.add("func_cleanup")
#
# # Process the section if all fields are found or if the section delimiter is encountered
# if fields_found == required_fields or '~~~~' in line:
# if section.strip(): # Make sure there's something to process
# try:
# result = parse_section(section, verbose)
# except:
# raise Exception(section)
# if result: # Ensure there is a result to write
# json_filename = os.path.join(output_directory, f"{file_count + 1}.json")
# with open(json_filename, 'w') as json_file:
# json.dump(result, json_file, indent=4)
# file_count += 1
# # Reset the section and fields found for the next part
# section = ""
# fields_found.clear()
#
# # Handle the last section if the file does not end with a delimiter
# if section.strip() and fields_found == required_fields:
# result = parse_section(section, verbose)
# if result:
# json_filename = os.path.join(output_directory, f"{file_count + 1}.json")
# with open(json_filename, 'w') as json_file:
# json.dump(result, json_file, indent=4)

def read_large_file(input_file_path, output_directory, verbose=False):
os.makedirs(output_directory, exist_ok=True)
section = ""
file_count = 0
in_section = False
count_dict = {} # Dictionary to store file counts per directory

with open(input_file_path, 'r') as file:
for line in file:
if "------- BEGIN SAFE GENERATE --------" in line:
in_section = True
section = line
elif "------- END TRIAL" in line and in_section:
section += line
result = parse_section(section, verbose)
if result:
# Extracting the basename from the 'prompt_template' key
template_path = result.get('prompt_template', 'ERROR')
basename = os.path.basename(template_path)
# Removing file extension
basename = os.path.splitext(basename)[0]

# Directory for the basename
output_subdir = os.path.join(output_directory, basename)
os.makedirs(output_subdir, exist_ok=True)

# Check if we already have a count for this directory, otherwise count files
if basename not in count_dict:
existing_files = [f for f in os.listdir(output_subdir) if f.endswith('.json')]
count_dict[basename] = len(existing_files) + 1
else:
count_dict[basename] += 1

# Writing the result to the JSON file
json_filename = os.path.join(output_subdir, f"{count_dict[basename]}.json")
with open(json_filename, 'w') as json_file:
json.dump(result, json_file, indent=4)
file_count += 1
section = ""
in_section = False
elif in_section:
section += line

if __name__ == "__main__":
import sys
if len(sys.argv) < 3:
print("Usage: python script.py <input_log_file_path> <output_directory_path> [<verbose>]")
else:
input_log_file_path = sys.argv[1]
output_directory_path = sys.argv[2]
verbose = sys.argv[3].lower() == 'true' if len(sys.argv) > 3 else False
read_large_file(input_log_file_path, output_directory_path, verbose)
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,4 @@ wsproto==1.2.0
yarl==1.8.2
yellowbrick==1.5
zipp==3.6.0
openai-cost-logger==0.4.1
openai-cost-logger==0.4.1ollama==0.2.1
20 changes: 16 additions & 4 deletions reverie/backend_server/automatic_execution.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
from multiprocessing import Process
from openai_cost_logger import OpenAICostLoggerViz

MAX_RESTARTS = 10 # risk of overflowing system memory otherwise


def parse_args() -> Tuple[str, str, int, bool]:
"""Parse bash arguments
Expand Down Expand Up @@ -86,7 +88,10 @@ def get_starting_step(exp_name: str) -> int:
if full_path.exists():
files = os.listdir(full_path)
steps = [int(os.path.splitext(filename)[0]) for filename in files]
current_step = max(steps)
if len(steps) == 0:
current_step = 0
else:
current_step = max(steps)
return current_step


Expand Down Expand Up @@ -166,7 +171,7 @@ def save_checkpoint(rs, idx: int, th: Process) -> Tuple[str, int, int]:


if __name__ == '__main__':
checkpoint_freq = 200 # 1 step = 10 sec
checkpoint_freq = 1000 # 1 step = 10 sec
log_path = "cost-logs" # where the simulations' prints are stored
idx = 0
origin, target, tot_steps, ui, browser_path, port = parse_args()
Expand All @@ -181,7 +186,9 @@ def save_checkpoint(rs, idx: int, th: Process) -> Tuple[str, int, int]:
print(f"(Auto-Exec): Target: {target}", flush=True)
print(f"(Auto-Exec): Total steps: {tot_steps}", flush=True)
print(f"(Auto-Exec): Checkpoint Freq: {checkpoint_freq}", flush=True)



number_restarts = 0
while current_step < tot_steps:
try:
steps_to_run = curr_checkpoint - current_step
Expand All @@ -207,8 +214,13 @@ def save_checkpoint(rs, idx: int, th: Process) -> Tuple[str, int, int]:
origin, current_step, idx = save_checkpoint(rs, idx, th)
else:
shutil.rmtree(f"../../environment/frontend_server/storage/{target}") # Remove the experiment folder if no steps were run
number_restarts += 1
print(f"(Auto-Exec): Error at step {current_step}", flush=True)
print(f"(Auto-Exec): Exception {e.args[0]}", flush=True)
print(f"(Auto-Exec): Restart:{number_restarts} max: {MAX_RESTARTS}", flush=True)
if number_restarts > MAX_RESTARTS:
print(f"(Auto-Exec): Too many restarts, shutting down!", flush=True)
sys.exit(0)
else:
origin, current_step, idx = save_checkpoint(rs, idx, th)
curr_checkpoint = get_new_checkpoint(current_step, tot_steps, checkpoint_freq)
Expand All @@ -229,4 +241,4 @@ def save_checkpoint(rs, idx: int, th: Process) -> Tuple[str, int, int]:
OpenAICostLoggerViz.print_experiment_cost(experiment=exp_name, path=log_path)
OpenAICostLoggerViz.print_total_cost(path=log_path)
print(f"(Auto-Exec): Execution time: {datetime.now() - start_time}")
sys.exit(0)
sys.exit(0)
20 changes: 5 additions & 15 deletions reverie/backend_server/persona/cognitive_modules/converse.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,19 +208,9 @@ def generate_inner_thought(persona, whisper):
inner_thought = run_gpt_prompt_generate_whisper_inner_thought(persona, whisper)[0]
return inner_thought

def generate_action_event_triple(act_desp, persona):
"""TODO

INPUT:
act_desp: the description of the action (e.g., "sleeping")
persona: The Persona class instance
OUTPUT:
a string of emoji that translates action description.
EXAMPLE OUTPUT:
"🧈🍞"
"""
if debug: print ("GNS FUNCTION: <generate_action_event_triple>")
return run_gpt_prompt_event_triple(act_desp, persona)[0]
def generate_thought_triple(act_desp, persona):
if debug: print ("GNS FUNCTION: <generate_thought_triple>")
return run_gpt_prompt_thought_triple(act_desp, persona)[0]


def generate_poig_score(persona, event_type, description):
Expand All @@ -245,7 +235,7 @@ def load_history_via_whisper(personas, whispers):

created = persona.scratch.curr_time
expiration = persona.scratch.curr_time + datetime.timedelta(days=30)
s, p, o = generate_action_event_triple(thought, persona)
s, p, o = generate_thought_triple(thought, persona)
keywords = set([s, p, o])
thought_poignancy = generate_poig_score(persona, "event", whisper)
thought_embedding_pair = (thought, get_embedding(thought))
Expand Down Expand Up @@ -289,7 +279,7 @@ def open_convo_session(persona, convo_mode, safe_mode=True, direct=False, questi

created = persona.scratch.curr_time
expiration = persona.scratch.curr_time + datetime.timedelta(days=30)
s, p, o = generate_action_event_triple(thought, persona)
s, p, o = generate_thought_triple(thought, persona)
keywords = set([s, p, o])
thought_poignancy = generate_poig_score(persona, "event", whisper)
thought_embedding_pair = (thought, get_embedding(thought))
Expand Down
5 changes: 3 additions & 2 deletions reverie/backend_server/persona/cognitive_modules/execute.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def execute(persona, maze, personas, plan):
# to execute the current action. The goal is to pick one of them.
target_tiles = None

print (plan)
print ("PLAN ----", plan)

if "<persona>" in plan:
# Executing persona-persona interaction.
Expand Down Expand Up @@ -88,7 +88,8 @@ def execute(persona, maze, personas, plan):
# string form. <maze.address_tiles> takes this and returns candidate
# coordinates.
if plan not in maze.address_tiles:
maze.address_tiles["Johnson Park:park:park garden"] #ERRORRRRRRR
#maze.address_tiles["Johnson Park:park:park garden"] #ERRORRRRRRR
raise Exception("{} not in {}".format(plan, list(maze.address_tiles)))
else:
target_tiles = maze.address_tiles[plan]

Expand Down
Loading