Skip to content

Commit

Permalink
Adding a devcontainer with Redis and a Ollama server (#217)
Browse files Browse the repository at this point in the history
* minimal demo example of running custom model

* devcontainer setup and example

* remove default_bad_process_model to allow using custom model entirely

* improve the demo to show parallel execution

* CI: update tests trigger from pull request target to pull request

* fix mypy errors

* adding stubs to pyproject.toml

* poetry lock

* install all extras in the devcontainer start script

* add dev containers instruction

* add a link to command palette wiki

* adding instructions for dev containers
  • Loading branch information
ProKil authored Oct 7, 2024
1 parent 7f5be99 commit 71d6199
Show file tree
Hide file tree
Showing 8 changed files with 1,387 additions and 242 deletions.
26 changes: 26 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/python
{
"name": "Sotopia Place",
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
"image": "mcr.microsoft.com/devcontainers/python:1-3.12-bullseye",
"features": {
"ghcr.io/itsmechlark/features/redis-server:1": {},
"ghcr.io/prulloac/devcontainer-features/ollama:1": {}
},

// Features to add to the dev container. More info: https://containers.dev/features.
// "features": {},

// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [],

// Use 'postCreateCommand' to run commands after the container is created.
"postCreateCommand": "pipx install poetry; poetry install --all-extras; ollama pull llama3.2:1b"

// Configure tool-specific properties.
// "customizations": {},

// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
// "remoteUser": "root"
}
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ on:
- main
- release
- dev
pull_request_target:
pull_request:
branches:
- main
- release
Expand Down
16 changes: 15 additions & 1 deletion docs/pages/contribution/contribution.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,21 @@ git push origin main

### 4. Set up the Development Environment

?? @ProKil
We recommend using Dev Containers to set up your development environment, but note that Docker is required for install Dev Containers.

#### Using VSCode

If you use VSCode, you can install the Dev Containers extension, and then in [Command Palette](https://code.visualstudio.com/docs/getstarted/userinterface#_command-palette), run `Dev Containers: Open Folder in Container`.
After the container is built (the first time it may take 10+ minutes to build), you have a Redis server and local Llama server running.

#### Other IDEs or Editors

Please refer to [Dev Containers](https://containers.dev/supporting#editors) to see how to set up DevContainers in other editors.

#### Without Dev Containers

You can also set up the development environment without Dev Containers. Please manually install Redis, and if you want to use a local model, you can use Ollama, Llama.cpp, vLLM or many others which support OpenAI compatible endpoints.


### 5. Write Code and Commit It

Expand Down
41 changes: 41 additions & 0 deletions examples/generation_api/custom_model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Example: Generate (2~7) random numbers using Llama3.2 model served by ollama
# To run this example, you can either
# 1. Use the sotopia devcontainer and run the following command in the terminal:
# poetry run python examples/generation_api/custom_model.py
# This example can also serve a sanity check for your devcontainer setup.
# OR 2. after installing sotopia, install ollama, and then:
# ollama pull llama3.2:1b; python examples/generation_api/custom_model.py
# OR 3. after installing sotopia, serve your desired model on your desired port,
# and then change the model_name of `agenerate` to point to your desired model.
# Finally:
# python examples/generation_api/custom_model.py
# Expected output for (1 and 2): a bunch of logs and an output [[14, 67], [6, 8, 3], [6, 8, 3, 9], [6, 8, 3, 9, 7], [7, 9, 6, 8, 4, 1]]

from sotopia.generation_utils.generate import ListOfIntOutputParser, agenerate
import logging

# Set logging to the lowest level to show all logs
logging.basicConfig(level=0)


async def generate_n_random_numbers(n: int) -> list[int]:
return await agenerate(
model_name="custom/llama3.2:1b@http://localhost:11434/v1",
template="Generate {n} random integer numbers. {format_instructions}",
input_values={"n": str(n)},
temperature=0.0,
output_parser=ListOfIntOutputParser(n),
)


async def main() -> None:
random_numbers = await asyncio.gather(
*[generate_n_random_numbers(n) for n in range(2, 7)]
)
print(random_numbers)


if __name__ == "__main__":
import asyncio

asyncio.run(main())
18 changes: 0 additions & 18 deletions examples/generation_api/minimal_demo.py

This file was deleted.

1,480 changes: 1,286 additions & 194 deletions poetry.lock

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ nbmake = "*"
types-setuptools = "*"
types-requests = "*"
types-tqdm = "*"
lxml-stubs = "*"
pandas-stubs = "*"
ruff = "*"

[tool.poetry.group.test.dependencies]
Expand Down
44 changes: 16 additions & 28 deletions sotopia/generation_utils/generate.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import json
import logging
import os
import re
Expand Down Expand Up @@ -57,11 +56,8 @@
"redis",
"groq/llama3-70b-8192",
]
# subject to future OpenAI changes
DEFAULT_BAD_OUTPUT_PROCESS_MODEL = "gpt-4o-mini"

OutputType = TypeVar("OutputType", bound=object)
client = OpenAI()


class EnvResponse(BaseModel):
Expand Down Expand Up @@ -108,14 +104,14 @@ def __init__(
self.range_of_int = range_of_int

def _get_description_text(self) -> str:
return f"a list of {' ' + str(self.number_of_int) if self.number_of_int else ''} intergers{' within the range of' + str(self.range_of_int) if self.range_of_int else ''}. No code block is needed."
return f"a list of{' ' + str(self.number_of_int) if self.number_of_int else ''} intergers{' within the range of' + str(self.range_of_int) if self.range_of_int else ''} separated by spaces. Don't output anything else. Format example: 1 2 3 4 5"

def get_format_instructions(self) -> str:
return "Please output " + self._get_description_text()

def parse(self, output: str) -> list[int]:
try:
output_loaded = json.loads(output)
output_loaded = output.split(" ")
result = [int(x) for x in output_loaded]
if self.number_of_int and len(result) != self.number_of_int:
msg = f"Expect {self.number_of_int} integers, got {len(result)}"
Expand Down Expand Up @@ -261,18 +257,9 @@ def parse(self, output: str) -> ScriptInteractionReturnType:
)
return parsed_interaction
except Exception as e:
print(f"Exception {e}: the output format is not correct. Reformatting ")
reformat_parsed_result = format_bad_output_for_script(
ill_formed_output=output,
format_instructions=self.get_format_instructions(),
agents=agent_names,
raise OutputParserException(
f"Failed to parse the output: {output}. Encounter Exception {e}"
)
print("Reformatted output: ", reformat_parsed_result)
interaction = ScriptInteraction(interactions=reformat_parsed_result)
parsed_interaction = interaction.parse(
agent_names=agent_names, background=self.background
)
return parsed_interaction

@property
def _type(self) -> str:
Expand Down Expand Up @@ -404,7 +391,7 @@ def format_bad_output_for_script(
ill_formed_output: str,
format_instructions: str,
agents: list[str],
model_name: str = DEFAULT_BAD_OUTPUT_PROCESS_MODEL,
model_name: str,
use_fixed_model_version: bool = True,
) -> BaseMessage:
template = """
Expand Down Expand Up @@ -440,7 +427,7 @@ def format_bad_output_for_script(
def format_bad_output(
ill_formed_output: BaseMessage,
format_instructions: str,
model_name: str = DEFAULT_BAD_OUTPUT_PROCESS_MODEL,
model_name: str,
use_fixed_model_version: bool = True,
) -> BaseMessage:
template = """
Expand Down Expand Up @@ -475,7 +462,7 @@ async def agenerate(
output_parser: BaseOutputParser[OutputType],
temperature: float = 0.7,
structured_output: bool = False,
bad_output_process_model: str = DEFAULT_BAD_OUTPUT_PROCESS_MODEL,
bad_output_process_model: str | None = None,
use_fixed_model_version: bool = True,
) -> OutputType:
input_variables = re.findall(
Expand Down Expand Up @@ -514,6 +501,7 @@ async def agenerate(
instantiated_prompt = prompt_result.messages[0].content
assert isinstance(output_parser, PydanticOutputParser)
assert isinstance(instantiated_prompt, str)
client = OpenAI()
completion = client.beta.chat.completions.parse(
model=model_name,
messages=[
Expand All @@ -538,7 +526,7 @@ async def agenerate(
reformat_parsed_result = format_bad_output(
result,
format_instructions=output_parser.get_format_instructions(),
model_name=bad_output_process_model,
model_name=bad_output_process_model or model_name,
use_fixed_model_version=use_fixed_model_version,
)
parsed_result = output_parser.invoke(reformat_parsed_result)
Expand All @@ -553,7 +541,7 @@ async def agenerate_env_profile(
inspiration_prompt: str = "asking my boyfriend to stop being friends with his ex",
examples: str = "",
temperature: float = 0.7,
bad_output_process_model: str = DEFAULT_BAD_OUTPUT_PROCESS_MODEL,
bad_output_process_model: str | None = None,
use_fixed_model_version: bool = True,
) -> tuple[EnvironmentProfile, str]:
"""
Expand Down Expand Up @@ -583,7 +571,7 @@ async def agenerate_env_profile(
async def agenerate_relationship_profile(
model_name: str,
agents_profiles: list[str],
bad_output_process_model: str = DEFAULT_BAD_OUTPUT_PROCESS_MODEL,
bad_output_process_model: str | None = None,
use_fixed_model_version: bool = True,
) -> tuple[RelationshipProfile, str]:
"""
Expand Down Expand Up @@ -617,7 +605,7 @@ async def agenerate_action(
goal: str,
temperature: float = 0.7,
script_like: bool = False,
bad_output_process_model: str = DEFAULT_BAD_OUTPUT_PROCESS_MODEL,
bad_output_process_model: str | None = None,
use_fixed_model_version: bool = True,
) -> AgentAction:
"""
Expand Down Expand Up @@ -685,7 +673,7 @@ async def agenerate_script(
agent_name: str = "",
history: str = "",
single_step: bool = False,
bad_output_process_model: str = DEFAULT_BAD_OUTPUT_PROCESS_MODEL,
bad_output_process_model: str | None = None,
use_fixed_model_version: bool = True,
) -> tuple[ScriptInteractionReturnType, str]:
"""
Expand Down Expand Up @@ -777,7 +765,7 @@ def process_history(
async def agenerate_init_profile(
model_name: str,
basic_info: dict[str, str],
bad_output_process_model: str = DEFAULT_BAD_OUTPUT_PROCESS_MODEL,
bad_output_process_model: str | None = None,
use_fixed_model_version: bool = True,
) -> str:
"""
Expand Down Expand Up @@ -823,7 +811,7 @@ async def convert_narratives(
model_name: str,
narrative: str,
text: str,
bad_output_process_model: str = DEFAULT_BAD_OUTPUT_PROCESS_MODEL,
bad_output_process_model: str | None = None,
use_fixed_model_version: bool = True,
) -> str:
if narrative == "first":
Expand Down Expand Up @@ -856,7 +844,7 @@ async def convert_narratives(
async def agenerate_goal(
model_name: str,
background: str,
bad_output_process_model: str = DEFAULT_BAD_OUTPUT_PROCESS_MODEL,
bad_output_process_model: str | None = None,
use_fixed_model_version: bool = True,
) -> str:
"""
Expand Down

0 comments on commit 71d6199

Please sign in to comment.