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

Feature/sotopia demo UI #261

Merged
merged 67 commits into from
Dec 31, 2024
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
a693fe2
initial
XuhuiZhou Oct 29, 2024
b2c422e
initial ui
XuhuiZhou Oct 30, 2024
df5f989
Merge branch 'main' into feature/sotopia-demo-ui
XuhuiZhou Dec 4, 2024
950830c
merge main
XuhuiZhou Dec 4, 2024
71483d9
add new ui
XuhuiZhou Dec 4, 2024
2e2514f
switch to fastAPI
XuhuiZhou Dec 4, 2024
e8fff7b
websocket check
XuhuiZhou Dec 5, 2024
878eb88
Merge branch 'demo' into feature/sotopia-demo-ui
XuhuiZhou Dec 7, 2024
79e9986
fix render episode error
XuhuiZhou Dec 7, 2024
f29f7f7
add page; make a simplified page and still WIP
bugsz Dec 9, 2024
99b58ba
[autofix.ci] apply automated fixes
autofix-ci[bot] Dec 9, 2024
3cc6dc0
fix simplified streaming version
bugsz Dec 10, 2024
4b54e44
semi-done character page + avatar assets
astrophie Dec 10, 2024
f77e302
Fixed character card styling
astrophie Dec 11, 2024
380b94c
[autofix.ci] apply automated fixes
autofix-ci[bot] Dec 11, 2024
756a749
unified rendering and chat display
bugsz Dec 11, 2024
02c76da
Merge branch 'feature/sotopia-demo-ui' of https://github.com/sotopia-…
bugsz Dec 11, 2024
3f7214e
updated chat character icons
astrophie Dec 11, 2024
d9b6204
add some tags
XuhuiZhou Dec 11, 2024
ceaabde
Merge branch 'feature/sotopia-demo-ui' of github.com:sotopia-lab/soto…
XuhuiZhou Dec 11, 2024
871fc1f
add typing
XuhuiZhou Dec 12, 2024
b372f2f
temp fix
XuhuiZhou Dec 12, 2024
69888db
add characters avatar to simulation
XuhuiZhou Dec 12, 2024
9764c34
fix episode full avatar
XuhuiZhou Dec 12, 2024
57dc305
go to modal config
XuhuiZhou Dec 14, 2024
dca51a9
clean up code
bugsz Dec 14, 2024
d15fd83
manually merge branch
bugsz Dec 14, 2024
fa625be
add modal streamlit app
bugsz Dec 14, 2024
9ba63f9
clean codebase except websocket
XuhuiZhou Dec 14, 2024
c7850bb
remove repeated local css
XuhuiZhou Dec 14, 2024
6384eb6
clean websocket
XuhuiZhou Dec 14, 2024
7aa64ec
fix get name error
XuhuiZhou Dec 14, 2024
5b48f30
fix errors
XuhuiZhou Dec 15, 2024
563af64
pre render scenario
XuhuiZhou Dec 15, 2024
1d743c0
add custom eval
XuhuiZhou Dec 15, 2024
9e3651b
Merge branch 'demo' into feature/sotopia-demo-ui
XuhuiZhou Dec 15, 2024
cb54051
change streamlit to dynamic path
bugsz Dec 15, 2024
e2575ea
new uv
XuhuiZhou Dec 15, 2024
116fa77
revert to previous install commands
bugsz Dec 15, 2024
d3c987b
Merge branch 'feature/sotopia-demo-ui' of https://github.com/sotopia-…
bugsz Dec 15, 2024
f64e715
a fix for modal
XuhuiZhou Dec 15, 2024
eccba6a
add customized dimension
bugsz Dec 15, 2024
a1c1de1
Merge branch 'feature/sotopia-demo-ui' of https://github.com/sotopia-…
bugsz Dec 15, 2024
1810999
[autofix.ci] apply automated fixes
autofix-ci[bot] Dec 15, 2024
f4eee2d
sort scenarios in simulation
bugsz Dec 15, 2024
aa8f1bb
Merge branch 'feature/sotopia-demo-ui' of https://github.com/sotopia-…
bugsz Dec 15, 2024
ed5168e
for demo video
XuhuiZhou Dec 15, 2024
0b12ccd
update deploy instruction
bugsz Dec 16, 2024
1069f6c
update intro page
bugsz Dec 16, 2024
466b6f9
update intro page
bugsz Dec 16, 2024
733074d
[autofix.ci] apply automated fixes
autofix-ci[bot] Dec 16, 2024
e3a7651
update intro page
bugsz Dec 19, 2024
9464ab3
Merge branch 'feature/sotopia-demo-ui' of https://github.com/sotopia-…
bugsz Dec 19, 2024
5079726
add customized dimensions
bugsz Dec 19, 2024
79aa67f
update api link and modal environment
bugsz Dec 22, 2024
88a80a3
move folder
XuhuiZhou Dec 22, 2024
eb3700d
fix relative import
XuhuiZhou Dec 22, 2024
e144682
update modal image build
bugsz Dec 25, 2024
b6c703b
Merge branch 'feature/sotopia-demo-ui' of https://github.com/sotopia-…
bugsz Dec 25, 2024
6d0ae1f
use uv to build environment
bugsz Dec 26, 2024
9ef75c4
change folder name
XuhuiZhou Dec 28, 2024
69d1124
Merge branch 'demo' into feature/sotopia-demo-ui
XuhuiZhou Dec 28, 2024
f8d9c0f
change test
XuhuiZhou Dec 28, 2024
f55e183
fix modal serve
XuhuiZhou Dec 28, 2024
3917d7b
environment change
XuhuiZhou Dec 28, 2024
6e620d6
refactor
XuhuiZhou Dec 28, 2024
5219d40
fix ui
XuhuiZhou Dec 31, 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
2 changes: 1 addition & 1 deletion docs/pages/contribution/contribution.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ Please refer to [Dev Containers](https://containers.dev/supporting#editors) to s

You can also set up the development environment without Dev Containers. There are three things you will need to set up manually:

- Python and uv: Please start from an environment supporting Python 3.10+ and install uv using `pip install uv; uv sync --all-extra`.
- Python and uv: Please start from an environment supporting Python 3.10+ and install uv using `pip install uv; uv sync --all-extras`. (Note that this will install all the extra dependencies)
- Redis: Please refer to introduction page for the set up of Redis.
- Local LLM (optional): If you don't have access to model endpoints (e.g. OpenAI, Anthropic or others), you can use a local model. You can use Ollama, Llama.cpp, vLLM or many others which support OpenAI compatible endpoints.

Expand Down
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ examples = ["transformers", "datasets", "scipy", "torch", "pandas"]
api = [
"fastapi[standard]",
"uvicorn",
"streamlit",
"websockets"
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we use AIOHTTP instead? therefore we don't need new package

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure about this. When I tried to host the websocket api it prompts me "No supported WebSocket library detected. Please use "pip install 'uvicorn[standard]'", or install 'websockets' or 'wsproto' manually."

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okay, let's not worry about that for now

]
test = ["pytest", "pytest-cov", "pytest-asyncio"]

Expand Down
8 changes: 8 additions & 0 deletions sotopia/ui/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@
> [!CAUTION]
> Work in progress: the API endpoints are being implemented. And will be released in the future major version.

## Streamlit UI
To run the Streamlit UI, run the following command:
```bash
cd sotopia/ui/streamlit_ui
uv run streamlit run app.py
```


## FastAPI Server

The API server is a FastAPI application that is used to connect the Sotopia UI to the Sotopia backend.
Expand Down
11 changes: 11 additions & 0 deletions sotopia/ui/streamlit_ui/.streamlit/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[theme]
base="light"
primaryColor = "#26184e"
backgroundColor="#FFFFFF"
secondaryBackgroundColor="#F0F2F6"
textColor="#31333F"
font="sans serif"


[server]
enableStaticServing = true
7 changes: 7 additions & 0 deletions sotopia/ui/streamlit_ui/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from .utils import reset_database
from .rendering.rendering_utils import (
_agent_profile_to_friendabove_self,
render_for_humans,
)

__all__ = ["reset_database", "_agent_profile_to_friendabove_self", "render_for_humans"]
95 changes: 95 additions & 0 deletions sotopia/ui/streamlit_ui/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import os

import streamlit as st

from sotopia.ui.streamlit_ui.utils import reset_database

PORT = 8800
st.session_state.API_BASE = f"http://localhost:{PORT}"
st.session_state.WS_BASE = f"ws://localhost:{PORT}"


def update_database_callback() -> None:
new_database_url = st.session_state.new_database_url
updated_url = (
new_database_url if new_database_url != "" else st.session_state.DEFAULT_DB_URL
)
try:
reset_database(updated_url)
except Exception as e:
st.error(f"Error occurred while updating database: {e}, please try again.")

st.session_state.current_database_url = updated_url
print("Updated DB URL: ", st.session_state.current_database_url)


# Page Configuration
st.set_page_config(page_title="SocialStream_Demo", page_icon="🧊", layout="wide")

display_intro = st.Page(
"./pages/intro.py", title="Introduction", icon=":material/home:"
)
display_episodes = st.Page(
"./pages/display_episodes.py", title="Episode", icon=":material/photo_library:"
)
display_scenarios = st.Page(
"./pages/display_scenarios.py",
title="Scenarios",
icon=":material/insert_drive_file:",
)
display_characters = st.Page(
"./pages/display_characters.py", title="Characters", icon=":material/people:"
)

display_chat = st.Page(
"./pages/render_chat_websocket_simplified.py",
title="Simulation",
icon=":material/add:",
)

add_characters = st.Page(
"./pages/add_characters.py", title="Add Characters", icon=":material/add:"
)

add_scenarios = st.Page(
"./pages/add_scenarios.py", title="Add Scenarios", icon=":material/add:"
)

pg = st.navigation(
[
display_intro,
display_scenarios,
display_episodes,
display_characters,
display_chat,
add_characters,
add_scenarios,
]
)

# Reset active agent when switching modes across pages
if "mode" not in st.session_state or pg.title != st.session_state.get("mode", None):
if "active" in st.session_state:
del st.session_state["active"]
# print("Active agent reset.")

st.session_state.mode = pg.title


# DB URL Configuration
if "DEFAULT_DB_URL" not in st.session_state:
st.session_state.DEFAULT_DB_URL = os.environ.get("REDIS_OM_URL", "")
st.session_state.current_database_url = st.session_state.DEFAULT_DB_URL
print("Default DB URL: ", st.session_state.DEFAULT_DB_URL)

# impl 2: popup update URL
with st.sidebar.popover("(Optional) Enter Database URL"):
new_database_url = st.text_input(
"URL: (starting in redis://)",
value="",
on_change=update_database_callback,
key="new_database_url",
)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We also need to have a fastAPI for this?



pg.run()
28 changes: 28 additions & 0 deletions sotopia/ui/streamlit_ui/css/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
.truncate{
max-height: 2em;
text-overflow: ellipsis;
margin-bottom: 12px;
cursor: pointer;
word-break: break-all;
overflow:hidden;
white-space: nowrap;
transition: max-height 1s, white-space 1s;
}
.truncate:hover{
overflow: auto;
overflow-y: scroll;
white-space: normal;
max-height: 200px;
}
/*
[data-testid="stAppViewContainer"] {
background-image: url("https://images.unsplash.com/photo-1729096532452-50549f2bb63c");
background-size: 40%;
background-position: top right;
background-repeat: no-repeat;
background-attachment: local;
} */

[data-testid="stHeader"] {
background: rgba(0,0,0,0);
}
Empty file.
86 changes: 86 additions & 0 deletions sotopia/ui/streamlit_ui/pages/add_characters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
"""
Definition
@app.post("/agents/", response_model=str)
async def create_agent(agent: AgentProfileWrapper) -> str:
agent_profile = AgentProfile(**agent.model_dump())
agent_profile.save()
pk = agent_profile.pk
assert pk is not None
return pk

class AgentProfileWrapper(BaseModel):
Wrapper for AgentProfile to avoid pydantic v2 issues

first_name: str
last_name: str
age: int = 0
occupation: str = ""
gender: str = ""
gender_pronoun: str = ""
public_info: str = ""
big_five: str = ""
moral_values: list[str] = []
schwartz_personal_values: list[str] = []
personality_and_values: str = ""
decision_making_style: str = ""
secret: str = ""
model_id: str = ""
mbti: str = ""
tag: str = ""
"""

import streamlit as st
import requests

from sotopia.ui.fastapi_server import AgentProfileWrapper
# add fields for agent profiles


def local_css(file_name: str) -> None:
with open(file_name) as f:
st.markdown(f"<style>{f.read()}</style>", unsafe_allow_html=True)


def rendering_character_form() -> None:
local_css("././css/style.css")
st.markdown("<h1>Character Creation</h1>", unsafe_allow_html=True)
st.write("Fill in the fields below to create a new character:")

first_name = st.text_input("First Name")
last_name = st.text_input("Last Name")
age = st.number_input("Age", min_value=0)
occupation = st.text_input("Occupation")
gender = st.text_input("Gender")
gender_pronoun = st.text_input("Gender Pronoun")
public_info = st.text_area("Public Info")

if st.button("Create Character"):
agent_profile = AgentProfileWrapper(
first_name=first_name,
last_name=last_name,
age=age,
occupation=occupation,
gender=gender,
gender_pronoun=gender_pronoun,
public_info=public_info,
)
print(agent_profile)

response = requests.post(
f"{st.session_state.API_BASE}/agents/",
json=agent_profile.model_dump(),
)

if response.status_code != 200:
st.error("Failed to create character. Error message: " + response.text)

else:
agent_id = response.json()
st.success("Character created successfully! ID: " + agent_id)
retrieved_agent = requests.get(
f"{st.session_state.API_BASE}/agents/id/{agent_id}"
)
st.write(retrieved_agent.json())


rendering_character_form()
123 changes: 123 additions & 0 deletions sotopia/ui/streamlit_ui/pages/add_scenarios.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
"""
Definition
@app.post("/scenarios/", response_model=str)
async def create_scenario(scenario: EnvironmentProfileWrapper) -> str:
scenario_profile = EnvironmentProfile(**scenario.model_dump())
scenario_profile.save()
pk = scenario_profile.pk
assert pk is not None
return pk

class EnvironmentProfileWrapper(BaseModel):
Wrapper for EnvironmentProfile to avoid pydantic v2 issues

codename: str
source: str = ""
scenario: str = ""
agent_goals: list[str] = []
relationship: Literal[0, 1, 2, 3, 4, 5] = 0
age_constraint: str | None = None
occupation_constraint: str | None = None
agent_constraint: list[list[str]] | None = None
tag: str = ""

class RelationshipType(IntEnum):
stranger = 0
know_by_name = 1
acquaintance = 2
friend = 3
romantic_relationship = 4
family_member = 5

The age constraint of the environment, a list of tuples, each tuple is a range of age, e.g., '[(18, 25), (30, 40)]'
means the environment is only available to agent one between 18 and 25, and agent two between 30 and 40
"""

import streamlit as st
from sotopia.ui.fastapi_server import EnvironmentProfileWrapper
from sotopia.database import RelationshipType
import requests


def local_css(file_name: str) -> None:
with open(file_name) as f:
st.markdown(f"<style>{f.read()}</style>", unsafe_allow_html=True)


def rendering_scenario_form() -> None:
bugsz marked this conversation as resolved.
Show resolved Hide resolved
local_css("././css/style.css")
st.markdown("<h1>Scenario Creation</h1>", unsafe_allow_html=True)

codename = st.text_input("Codename")
source = st.text_input("Source")
scenario = st.text_area("Scenario")
# present relationship type with the descriptions, only accept one choice, then map to the enum
relationship_mapping = {
"Stranger": RelationshipType.stranger,
"Know by Name": RelationshipType.know_by_name,
"Acquaintance": RelationshipType.acquaintance,
"Friend": RelationshipType.friend,
"Romantic Relationship": RelationshipType.romantic_relationship,
"Family Member": RelationshipType.family_member,
}

selected_relationship = st.selectbox(
"Relationship",
list(relationship_mapping.keys()),
)
relationship = relationship_mapping[selected_relationship]

# first choose whether to use age constraint and then choose age range
use_age_constraint = st.checkbox("Use Age Constraint")
if use_age_constraint:
min_age = st.number_input("Min Age", min_value=0, max_value=100)
max_age = st.number_input("Max Age", min_value=0, max_value=100)
age_constraint = f"[({min_age}, {max_age})]"
if min_age > max_age:
st.error("Min age cannot be greater than max age")
else:
age_constraint = None

# first choose whether to use occupation constraint and then choose occupation
use_occupation_constraint = st.checkbox(
"Use Occupation Constraint, use comma to separate multiple occupations"
)
if use_occupation_constraint:
occupation = st.text_input("Occupation")
occupation_constraint = f"[{occupation}]"
else:
occupation_constraint = None

agent1_goal = st.text_input("Agent 1 Goal")
agent2_goal = st.text_input("Agent 2 Goal")

if st.button("Create Scenario"):
scenario_profile = EnvironmentProfileWrapper(
codename=codename,
source=source,
scenario=scenario,
relationship=relationship,
age_constraint=age_constraint,
occupation_constraint=occupation_constraint,
agent_goals=[agent1_goal, agent2_goal],
)

response = requests.post(
f"{st.session_state.API_BASE}/scenarios/",
json=scenario_profile.model_dump(),
)

if response.status_code != 200:
st.error("Failed to create scenario. Error: " + response.text)
else:
# there are quotes in the response
scenario_id = response.json()

st.success("Scenario created successfully! Scenario ID: " + scenario_id)
retrieved_scenario = requests.get(
f"{st.session_state.API_BASE}/scenarios/id/{scenario_id}"
)
st.write(retrieved_scenario.json())


rendering_scenario_form()
Loading