Compare commits
No commits in common. "main" and "master" have entirely different histories.
162
.gitignore
vendored
162
.gitignore
vendored
@ -1,162 +0,0 @@
|
||||
# ---> Python
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# poetry
|
||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||
#poetry.lock
|
||||
|
||||
# pdm
|
||||
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||
#pdm.lock
|
||||
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||
# in version control.
|
||||
# https://pdm.fming.dev/#use-with-ide
|
||||
.pdm.toml
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
# PyCharm
|
||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
||||
|
||||
1
.python-version
Normal file
1
.python-version
Normal file
@ -0,0 +1 @@
|
||||
3.12
|
||||
566
L1.ipynb
Normal file
566
L1.ipynb
Normal file
@ -0,0 +1,566 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "5ac6b5d5-fce7-4fcb-b1a8-c490ea5c5325",
|
||||
"metadata": {
|
||||
"id": "173e06c5-4b07-4e3b-a67a-5c3e141beb2c"
|
||||
},
|
||||
"source": [
|
||||
"# L1: Hierarchical Content Generation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "8be81d7d-2c10-45c4-beb4-0e7d7910fa8a",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"<p style=\"background-color:#f7fff8; padding:15px; border-width:3px; border-color:#e0f0e0; border-style:solid; border-radius:6px\"> 🚨\n",
|
||||
" <b>Different Run Results:</b> The output generated by AI models can vary with each execution due to their dynamic, probabilistic nature. Don't be surprised if your results differ from those shown in the video.<br>\n",
|
||||
"<span style=\"font-size: larger;\">To maintain consistency, the notebooks are run with a 'world state' consistent with the video at the start of each notebook.</span></p>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "aa3e6edc-62d6-44ea-a7da-5863f11bf7a4",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"<div style=\"background-color:#fff6ff; padding:13px; border-width:3px; border-color:#efe6ef; border-style:solid; border-radius:6px\">\n",
|
||||
"<p> 💻 <b>Access <code>requirements.txt</code> and <code>helper.py</code> files:</b> 1) click on the <em>\"File\"</em> option on the top menu of the notebook and then 2) click on <em>\"Open\"</em>.\n",
|
||||
"\n",
|
||||
"<p> ⬇ <b>Download Notebooks:</b> 1) click on the <em>\"File\"</em> option on the top menu of the notebook and then 2) click on <em>\"Download as\"</em> and select <em>\"Notebook (.ipynb)\"</em>.</p>\n",
|
||||
"\n",
|
||||
"<p> 📒 For more help, please see the <em>\"Appendix – Tips, Help, and Download\"</em> Lesson.</p>\n",
|
||||
"\n",
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "ac19c917-fe0b-4140-abc4-33d6b3972aaa",
|
||||
"metadata": {
|
||||
"id": "173e06c5-4b07-4e3b-a67a-5c3e141beb2c"
|
||||
},
|
||||
"source": [
|
||||
"## Creating a World"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"id": "93056e48-cb80-46a8-8990-bc5b4ae9506b",
|
||||
"metadata": {
|
||||
"height": 149
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"system_prompt = f\"\"\"\n",
|
||||
"Your job is to help create interesting fantasy worlds that \\\n",
|
||||
"players would love to play in.\n",
|
||||
"Instructions:\n",
|
||||
"- Only generate in plain text without formatting.\n",
|
||||
"- Use simple clear language without being flowery.\n",
|
||||
"- You must stay below 3-5 sentences for each description.\n",
|
||||
"\"\"\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"id": "330f3e78-9a20-45b1-afa5-a65289046a55",
|
||||
"metadata": {
|
||||
"height": 200
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"world_prompt = f\"\"\"\n",
|
||||
"Generate a creative description for a unique fantasy world with an\n",
|
||||
"interesting concept around cities build on the backs of massive beasts.\n",
|
||||
"\n",
|
||||
"Output content in the form:\n",
|
||||
"World Name: <WORLD NAME>\n",
|
||||
"World Description: <WORLD DESCRIPTION>\n",
|
||||
"\n",
|
||||
"World Name:\"\"\"\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"id": "7f928bb3-c6e4-4891-a154-3b3f225e8d21",
|
||||
"metadata": {
|
||||
"height": 217
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from together import Together\n",
|
||||
"from helper import get_together_api_key,load_env \n",
|
||||
"\n",
|
||||
"client = Together(api_key=get_together_api_key())\n",
|
||||
"\n",
|
||||
"output = client.chat.completions.create(\n",
|
||||
" model=\"meta-llama/Llama-3-70b-chat-hf\",\n",
|
||||
" messages=[\n",
|
||||
" {\"role\": \"system\", \"content\": system_prompt},\n",
|
||||
" {\"role\": \"user\", \"content\": world_prompt}\n",
|
||||
" ],\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"id": "22d161d6-cb78-4c3d-b858-5659d5d16761",
|
||||
"metadata": {
|
||||
"height": 47
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"World Name: Kyropeia\n",
|
||||
"\n",
|
||||
"World Description: Kyropeia is a realm where ancient, gargantuan creatures known as the \"Colossi\" roam the land, their massive bodies serving as the foundation for sprawling metropolises. These beasts, born from the earth and infused with primal magic, have been domesticated by humans over centuries, allowing cities to be built upon their backs, shoulders, and even within their hollowed-out bodies. As the Colossi migrate across the landscape, their cities in tow, the inhabitants of Kyropeia must adapt to a life of constant motion and evolution.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"world_output =output.choices[0].message.content\n",
|
||||
"print(world_output)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"id": "b993a1df-7735-43da-9872-45bb2bdc9fb2",
|
||||
"metadata": {
|
||||
"height": 132
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"world_output = world_output.strip()\n",
|
||||
"world = {\n",
|
||||
" \"name\": world_output.split('\\n')[0].strip()\n",
|
||||
" .replace('World Name: ', ''),\n",
|
||||
" \"description\": '\\n'.join(world_output.split('\\n')[1:])\n",
|
||||
" .replace('World Description:', '').strip()\n",
|
||||
"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "5dbb561d-b115-4ba5-81f4-2ae3dfd4a71f",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Generating Kingdoms"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"id": "9531806c-8b56-4fe5-bb15-d7c8d9b1cef8",
|
||||
"metadata": {
|
||||
"height": 336
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"kingdom_prompt = f\"\"\"\n",
|
||||
"Create 3 different kingdoms for a fantasy world.\n",
|
||||
"For each kingdom generate a description based on the world it's in. \\\n",
|
||||
"Describe important leaders, cultures, history of the kingdom.\\\n",
|
||||
"\n",
|
||||
"Output content in the form:\n",
|
||||
"Kingdom 1 Name: <KINGDOM NAME>\n",
|
||||
"Kingdom 1 Description: <KINGDOM DESCRIPTION>\n",
|
||||
"Kingdom 2 Name: <KINGDOM NAME>\n",
|
||||
"Kingdom 2 Description: <KINGDOM DESCRIPTION>\n",
|
||||
"Kingdom 3 Name: <KINGDOM NAME>\n",
|
||||
"Kingdom 3 Description: <KINGDOM DESCRIPTION>\n",
|
||||
"\n",
|
||||
"World Name: {world['name']}\n",
|
||||
"World Description: {world['description']}\n",
|
||||
"\n",
|
||||
"Kingdom 1\"\"\"\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"id": "f7c87417-695c-443a-baa1-b354a9664d14",
|
||||
"metadata": {
|
||||
"height": 132
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"output = client.chat.completions.create(\n",
|
||||
" model=\"meta-llama/Llama-3-70b-chat-hf\",\n",
|
||||
" messages=[\n",
|
||||
" {\"role\": \"system\", \"content\": system_prompt},\n",
|
||||
" {\"role\": \"user\", \"content\": kingdom_prompt}\n",
|
||||
" ],\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"id": "ca6d59b6-60f1-4241-8906-29be576d00e6",
|
||||
"metadata": {
|
||||
"height": 336
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Created kingdom \"Valtoria\" in Kyropeia\n",
|
||||
"\n",
|
||||
"Kingdom 1 Description: Valtoria is a kingdom built upon the back of the largest Colossus, the \"Earthshaker\". Its people, skilled engineers and architects, have developed a unique culture of mobility and adaptability. Led by the wise and cunning Queen Lyra, Valtoria is a hub of innovation, where inventors and tinkerers work tirelessly to improve the city's infrastructure and harness the primal magic of the Colossi.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"kingdoms = {}\n",
|
||||
"kingdoms_output = output.choices[0].message.content\n",
|
||||
"\n",
|
||||
"for output in kingdoms_output.split('\\n\\n'):\n",
|
||||
" kingdom_name = output.strip().split('\\n')[0] \\\n",
|
||||
" .split('Name: ')[1].strip()\n",
|
||||
" print(f'Created kingdom \"{kingdom_name}\" in {world[\"name\"]}')\n",
|
||||
" kingdom_description = output.strip().split('\\n')[1] \\\n",
|
||||
" .split('Description: ')[1].strip()\n",
|
||||
" kingdom = {\n",
|
||||
" \"name\": kingdom_name,\n",
|
||||
" \"description\": kingdom_description,\n",
|
||||
" \"world\": world['name']\n",
|
||||
" }\n",
|
||||
" kingdoms[kingdom_name] = kingdom\n",
|
||||
"world['kingdoms'] = kingdoms\n",
|
||||
"\n",
|
||||
"print(f'\\nKingdom 1 Description: \\\n",
|
||||
"{kingdom[\"description\"]}')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "e124ee9d-694b-4259-b38a-34ee106c5ca4",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Generating Towns"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"id": "8f111ae4-dc95-49e1-ae15-4b5fd515988e",
|
||||
"metadata": {
|
||||
"height": 387
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def get_town_prompt(world, kingdom):\n",
|
||||
" return f\"\"\"\n",
|
||||
" Create 3 different towns for a fantasy kingdom abd world. \\\n",
|
||||
" Describe the region it's in, important places of the town, \\\n",
|
||||
" and interesting history about it. \\\n",
|
||||
" \n",
|
||||
" Output content in the form:\n",
|
||||
" Town 1 Name: <TOWN NAME>\n",
|
||||
" Town 1 Description: <TOWN DESCRIPTION>\n",
|
||||
" Town 2 Name: <TOWN NAME>\n",
|
||||
" Town 2 Description: <TOWN DESCRIPTION>\n",
|
||||
" Town 3 Name: <TOWN NAME>\n",
|
||||
" Town 3 Description: <TOWN DESCRIPTION>\n",
|
||||
" \n",
|
||||
" World Name: {world['name']}\n",
|
||||
" World Description: {world['description']}\n",
|
||||
" \n",
|
||||
" Kingdom Name: {kingdom['name']}\n",
|
||||
" Kingdom Description {kingdom['description']}\n",
|
||||
" \n",
|
||||
" Town 1 Name:\"\"\"\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"id": "90d892c0-b74e-4c75-bcf7-b055fa6712cd",
|
||||
"metadata": {
|
||||
"height": 506
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def create_towns(world, kingdom):\n",
|
||||
" print(f'\\nCreating towns for kingdom: {kingdom[\"name\"]}...')\n",
|
||||
" output = client.chat.completions.create(\n",
|
||||
" model=\"meta-llama/Llama-3-70b-chat-hf\",\n",
|
||||
" messages=[\n",
|
||||
" {\"role\": \"system\", \"content\": system_prompt},\n",
|
||||
" {\"role\": \"user\", \"content\": get_town_prompt(world, kingdom)}\n",
|
||||
" ],\n",
|
||||
" )\n",
|
||||
" towns_output = output.choices[0].message.content\n",
|
||||
" \n",
|
||||
" towns = {}\n",
|
||||
" for output in towns_output.split('\\n\\n'):\n",
|
||||
" town_name = output.strip().split('\\n')[0]\\\n",
|
||||
" .split('Name: ')[1].strip()\n",
|
||||
" print(f'- {town_name} created')\n",
|
||||
" \n",
|
||||
" town_description = output.strip().split('\\n')[1]\\\n",
|
||||
" .split('Description: ')[1].strip()\n",
|
||||
" \n",
|
||||
" town = {\n",
|
||||
" \"name\": town_name,\n",
|
||||
" \"description\": town_description,\n",
|
||||
" \"world\": world['name'],\n",
|
||||
" \"kingdom\": kingdom['name']\n",
|
||||
" }\n",
|
||||
" towns[town_name] = town\n",
|
||||
" kingdom[\"towns\"] = towns"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"id": "b534d860-5b6b-43f8-9c60-e345fbaf1d9e",
|
||||
"metadata": {
|
||||
"height": 115
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"\n",
|
||||
"Creating towns for kingdom: Valtoria...\n",
|
||||
"- Ironhaven created\n",
|
||||
"- Skypoint created\n",
|
||||
"- Emberwatch created\n",
|
||||
"\n",
|
||||
"Town 1 Description: Located on the Earthshaker's left shoulder, Ironhaven is a bustling industrial town where Valtoria's engineers and inventors craft innovative machinery and tools to aid the kingdom's constant migration. The town's central square features the famous Ironhaven Forge, a massive, Colossus-powered furnace that fuels the town's industry. Ironhaven's history is marked by a great fire that ravaged the town, prompting the development of advanced fire-resistant materials and safety measures.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"for kingdom in kingdoms.values():\n",
|
||||
" create_towns(world, kingdom) \n",
|
||||
"\n",
|
||||
"town = list(kingdom['towns'].values())[0]\n",
|
||||
"print(f'\\nTown 1 Description: \\\n",
|
||||
"{town[\"description\"]}')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "fff397b4-3ac6-4737-a232-8e68df1471b0",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Generating Non-Player Characters (NPC's)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 12,
|
||||
"id": "f31adacd-a37a-4de9-9be5-aadf56fff988",
|
||||
"metadata": {
|
||||
"height": 421
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def get_npc_prompt(world, kingdom, town): \n",
|
||||
" return f\"\"\"\n",
|
||||
" Create 3 different characters based on the world, kingdom \\\n",
|
||||
" and town they're in. Describe the character's appearance and \\\n",
|
||||
" profession, as well as their deeper pains and desires. \\\n",
|
||||
" \n",
|
||||
" Output content in the form:\n",
|
||||
" Character 1 Name: <CHARACTER NAME>\n",
|
||||
" Character 1 Description: <CHARACTER DESCRIPTION>\n",
|
||||
" Character 2 Name: <CHARACTER NAME>\n",
|
||||
" Character 2 Description: <CHARACTER DESCRIPTION>\n",
|
||||
" Character 3 Name: <CHARACTER NAME>\n",
|
||||
" Character 3 Description: <CHARACTER DESCRIPTION>\n",
|
||||
" \n",
|
||||
" World Name: {world['name']}\n",
|
||||
" World Description: {world['description']}\n",
|
||||
" \n",
|
||||
" Kingdom Name: {kingdom['name']}\n",
|
||||
" Kingdom Description: {kingdom['description']}\n",
|
||||
" \n",
|
||||
" Town Name: {town['name']}\n",
|
||||
" Town Description: {town['description']}\n",
|
||||
" \n",
|
||||
" Character 1 Name:\"\"\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 13,
|
||||
"id": "a301bf4d-1f99-475b-ae88-4a05e7537e3f",
|
||||
"metadata": {
|
||||
"height": 540
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def create_npcs(world, kingdom, town):\n",
|
||||
" print(f'\\nCreating characters for the town of: {town[\"name\"]}...')\n",
|
||||
" output = client.chat.completions.create(\n",
|
||||
" model=\"meta-llama/Llama-3-70b-chat-hf\",\n",
|
||||
" messages=[\n",
|
||||
" {\"role\": \"system\", \"content\": system_prompt},\n",
|
||||
" {\"role\": \"user\", \"content\": get_npc_prompt(world, kingdom, town)}\n",
|
||||
" ],\n",
|
||||
" temperature=1 #added to generate unique names\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" npcs_output = output.choices[0].message.content\n",
|
||||
" npcs = {}\n",
|
||||
" for output in npcs_output.split('\\n\\n'):\n",
|
||||
" npc_name = output.strip().split('\\n')[0]\\\n",
|
||||
" .split('Name: ')[1].strip()\n",
|
||||
" print(f'- \"{npc_name}\" created')\n",
|
||||
" \n",
|
||||
" npc_description = output.strip().split('\\n')[1\\\n",
|
||||
" ].split('Description: ')[1].strip()\n",
|
||||
" \n",
|
||||
" npc = {\n",
|
||||
" \"name\": npc_name,\n",
|
||||
" \"description\": npc_description,\n",
|
||||
" \"world\": world['name'],\n",
|
||||
" \"kingdom\": kingdom['name'],\n",
|
||||
" \"town\": town['name']\n",
|
||||
" }\n",
|
||||
" npcs[npc_name] = npc\n",
|
||||
" town[\"npcs\"] = npcs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 14,
|
||||
"id": "36f0021d-41fa-4f3d-aed9-32d4fcb69dcb",
|
||||
"metadata": {
|
||||
"height": 98
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"\n",
|
||||
"Creating characters for the town of: Ironhaven...\n",
|
||||
"- \"Kaelin Darkhammer\" created\n",
|
||||
"- \"Lyrien Stonefist\" created\n",
|
||||
"- \"Aria Skypaw\" created\n",
|
||||
"\n",
|
||||
"Creating characters for the town of: Skypoint...\n",
|
||||
"- \"Kaida Renn\" created\n",
|
||||
"- \"Lyrien Frost\" created\n",
|
||||
"- \"Arin Vexar\" created\n",
|
||||
"\n",
|
||||
"Creating characters for the town of: Emberwatch...\n",
|
||||
"- \"Lyri kickoff\" created\n",
|
||||
"- \"Kael Red nexus\" created\n",
|
||||
"- \"background Adwasher<c Becket\" created\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"for kingdom in kingdoms.values():\n",
|
||||
" for town in kingdom['towns'].values():\n",
|
||||
" create_npcs(world, kingdom, town)\n",
|
||||
" # For now we'll only generate npcs for one kingdom\n",
|
||||
" break"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 15,
|
||||
"id": "e95294cc-ee92-430d-ad48-e95852784851",
|
||||
"metadata": {
|
||||
"height": 81
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"\n",
|
||||
"NPC 1 in Emberwatch, Valtoria:\n",
|
||||
"Lyri, the youngest daughter of Queen Lyrtl, is an ambitious inventor and engineer who wears elegant, intricately designed gloves embedded with tiny, glowing circuits. Her long, curly brown hair is tied back in a short ponytail, and her bright, inquisitive eyes are often peering into the workings of a new gadget. Despite her inborn enthusiasm for creation, Lyri harbors a deep pain - her mother's lack of acknowledgment of her own engineering prowess, fearing it's overshadowed by royal duties. Lyri yearns to create something revolutionary, proving her worth in the eyes of her mother and the kingdom.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"npc = list(town['npcs'].values())[0]\n",
|
||||
"\n",
|
||||
"print(f'\\nNPC 1 in {town[\"name\"]}, \\\n",
|
||||
"{kingdom[\"name\"]}:\\n{npc[\"description\"]}')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "764a433b-5e04-4980-a9b0-4d336113f072",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Save the World\n",
|
||||
">Note: You will save your world state to a file different than the one shown in the video to allow future lessons to be consistent with the video. If later wish to build your own worlds, you will want to load your file rather than the saved file."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 18,
|
||||
"id": "2dce3195-2c76-42da-9e49-c7a17e5ab08b",
|
||||
"metadata": {
|
||||
"height": 234
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import json\n",
|
||||
"\n",
|
||||
"def save_world(world, filename):\n",
|
||||
" with open(filename, 'w') as f:\n",
|
||||
" json.dump(world, f)\n",
|
||||
"\n",
|
||||
"def load_world(filename):\n",
|
||||
" with open(filename, 'r') as f:\n",
|
||||
" return json.load(f)\n",
|
||||
"\n",
|
||||
"#save_world(world, '../shared_data/Kyropeia.json')\n",
|
||||
"save_world(world, './shared_data/YourWorld_L1.json') #save to your version"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "f64fc076-32b5-41ca-b755-6ccbd52347c0",
|
||||
"metadata": {
|
||||
"height": 30
|
||||
},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.12.6"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
450
L2.ipynb
Normal file
450
L2.ipynb
Normal file
@ -0,0 +1,450 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "5e42966f-5b62-4b86-84ce-b5de85098e64",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# L2: Interactive AI Applications: Building a Simple AI Role Playing Game (RPG)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "3061918c-6bb2-4db0-a061-10fb3cb688fe",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"<p style=\"background-color:#f7fff8; padding:15px; border-width:3px; border-color:#e0f0e0; border-style:solid; border-radius:6px\"> 🚨\n",
|
||||
" <b>Different Run Results:</b> The output generated by AI models can vary with each execution due to their dynamic, probabilistic nature. Don't be surprised if your results differ from those shown in the video.<br>\n",
|
||||
"<span style=\"font-size: larger;\">To maintain consistency, the notebooks are run with a 'world state' consistent with the video at the start of each notebook.</span></p>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "b1dceeb2-b82b-4759-ad62-da5a9a1253f6",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"<div style=\"background-color:#fff6ff; padding:13px; border-width:3px; border-color:#efe6ef; border-style:solid; border-radius:6px\">\n",
|
||||
"<p> 💻 <b>Access <code>requirements.txt</code> and <code>helper.py</code> files:</b> 1) click on the <em>\"File\"</em> option on the top menu of the notebook and then 2) click on <em>\"Open\"</em>.\n",
|
||||
"\n",
|
||||
"<p> ⬇ <b>Download Notebooks:</b> 1) click on the <em>\"File\"</em> option on the top menu of the notebook and then 2) click on <em>\"Download as\"</em> and select <em>\"Notebook (.ipynb)\"</em>.</p>\n",
|
||||
"\n",
|
||||
"<p> 📒 For more help, please see the <em>\"Appendix – Tips, Help, and Download\"</em> Lesson.</p>\n",
|
||||
"\n",
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "f1d851be-0eda-48eb-92d3-3c59b31b0159",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Create a Game UI with Gradio"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"id": "c2e37b6e-e683-4a99-93ec-8ff0ab9528a2",
|
||||
"metadata": {
|
||||
"height": 540
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"/home/thomas/dev/llm_game/.venv/lib/python3.12/site-packages/gradio/components/chatbot.py:225: UserWarning: You have not specified a value for the `type` parameter. Defaulting to the 'tuples' format for chatbot messages, but this is deprecated and will be removed in a future version of Gradio. Please set type='messages' instead, which uses openai-style 'role' and 'content' keys.\n",
|
||||
" warnings.warn(\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"* Running on local URL: http://0.0.0.0:7861\n",
|
||||
"\n",
|
||||
"To create a public link, set `share=True` in `launch()`.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/html": [
|
||||
"<div><iframe src=\"http://localhost:7861/\" width=\"100%\" height=\"500\" allow=\"autoplay; camera; microphone; clipboard-read; clipboard-write;\" frameborder=\"0\" allowfullscreen></iframe></div>"
|
||||
],
|
||||
"text/plain": [
|
||||
"<IPython.core.display.HTML object>"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import gradio as gr\n",
|
||||
"import os\n",
|
||||
"demo = None #added to allow restart\n",
|
||||
"\n",
|
||||
"def start_game(main_loop, share=False):\n",
|
||||
" # added code to support restart\n",
|
||||
" global demo\n",
|
||||
" # If demo is already running, close it first\n",
|
||||
" if demo is not None:\n",
|
||||
" demo.close()\n",
|
||||
"\n",
|
||||
" demo = gr.ChatInterface(\n",
|
||||
" main_loop,\n",
|
||||
" chatbot=gr.Chatbot(height=250, placeholder=\"Type 'start game' to begin\"),\n",
|
||||
" textbox=gr.Textbox(placeholder=\"What do you do next?\", container=False, scale=7),\n",
|
||||
" title=\"AI RPG\",\n",
|
||||
" # description=\"Ask Yes Man any question\",\n",
|
||||
" theme=\"soft\",\n",
|
||||
" examples=[\"Look around\", \"Continue the story\"],\n",
|
||||
" cache_examples=False\n",
|
||||
" )\n",
|
||||
" demo.launch(share=share, server_name=\"0.0.0.0\")\n",
|
||||
"\n",
|
||||
"def test_main_loop(message, history):\n",
|
||||
" return 'Entered Action: ' + message\n",
|
||||
"\n",
|
||||
"start_game(test_main_loop)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "df4bb323-dbaf-4705-b884-548331b1dd8b",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Generating an Initial Start"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 12,
|
||||
"id": "051c5719",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"{'name': 'Kyropeia',\n",
|
||||
" 'description': 'Kyropeia is a realm where ancient, gargantuan creatures known as the \"Colossi\" roam the land, their massive bodies serving as the foundation for sprawling metropolises. These beasts, born from the earth and infused with primal magic, have been domesticated by humans over centuries, allowing cities to be built upon their backs, shoulders, and even within their hollowed-out bodies. As the Colossi migrate across the landscape, their cities in tow, the inhabitants of Kyropeia must adapt to a life of constant motion and evolution.',\n",
|
||||
" 'kingdoms': {'Valtoria': {'name': 'Valtoria',\n",
|
||||
" 'description': 'Valtoria is a kingdom built upon the back of the largest Colossus, the \"Earthshaker\". Its people, skilled engineers and architects, have developed a unique culture of mobility and adaptability. Led by the wise and cunning Queen Lyra, Valtoria is a hub of innovation, where inventors and tinkerers work tirelessly to improve the city\\'s infrastructure and harness the primal magic of the Colossi.',\n",
|
||||
" 'world': 'Kyropeia',\n",
|
||||
" 'towns': {'Ironhaven': {'name': 'Ironhaven',\n",
|
||||
" 'description': \"Located on the Earthshaker's left shoulder, Ironhaven is a bustling industrial town where Valtoria's engineers and inventors craft innovative machinery and tools to aid the kingdom's constant migration. The town's central square features the famous Ironhaven Forge, a massive, Colossus-powered furnace that fuels the town's industry. Ironhaven's history is marked by a great fire that ravaged the town, prompting the development of advanced fire-resistant materials and safety measures.\",\n",
|
||||
" 'world': 'Kyropeia',\n",
|
||||
" 'kingdom': 'Valtoria',\n",
|
||||
" 'npcs': {'Kaelin Darkhammer': {'name': 'Kaelin Darkhammer',\n",
|
||||
" 'description': \"Kaelin is a rugged, dark-haired blacksmith with a thick beard and a missing eye, lost in the great fire that ravaged Ironhaven. He wears a leather apron and wields a massive hammer, crafting intricate machinery and tools for the town's engineers. Kaelin's past is marked by loss and guilt, driving him to create innovative fire-resistant materials to ensure such a tragedy never happens again. He dreams of rebuilding Ironhaven into a safer, more resilient town.\",\n",
|
||||
" 'world': 'Kyropeia',\n",
|
||||
" 'kingdom': 'Valtoria',\n",
|
||||
" 'town': 'Ironhaven'},\n",
|
||||
" 'Lyrien Stonefist': {'name': 'Lyrien Stonefist',\n",
|
||||
" 'description': \"Lyrien is a hulking, muscular man with a shaved head and a stern expression, serving as Valtoria's royal guard. He wears ornate, Colossus-bone armor and wields a massive warhammer, protecting Queen Lyra and the kingdom from external threats. Lyrien struggles with the moral implications of the Colossi's domestication, feeling a deep bond with the ancient creatures. He hopes to one day find a way to free the Colossi from human control, allowing them to roam the land as they once did.\",\n",
|
||||
" 'world': 'Kyropeia',\n",
|
||||
" 'kingdom': 'Valtoria',\n",
|
||||
" 'town': 'Ironhaven'},\n",
|
||||
" 'Aria Skypaw': {'name': 'Aria Skypaw',\n",
|
||||
" 'description': \"Aria is an agile, wind-swept aeromancer with short, spiky hair and piercing green eyes, often seen atop the Earthshaker's back, harnessing the primal magic of the wind. She wears a flowing, wind-resistant cloak and carries an intricately carved staff, using her abilities to aid Valtoria's migration and guide the kingdom's engineers. Aria's deepest pain is the loss of her family, swept away by a sudden gust of wind during a particularly violent migration. She seeks to develop a way to predict and control the turbulent winds, ensuring such a tragedy never befalls others.\",\n",
|
||||
" 'world': 'Kyropeia',\n",
|
||||
" 'kingdom': 'Valtoria',\n",
|
||||
" 'town': 'Ironhaven'}}},\n",
|
||||
" 'Skypoint': {'name': 'Skypoint',\n",
|
||||
" 'description': \"Perched on the Earthshaker's crown, Skypoint is a windswept town of skilled aeromancers and cloudhoppers who harness the Colossus's primal magic to navigate the skies. The town's Cloudhaven Academy is renowned for its prestigious aeromancy program, attracting students from across Kyropeia. Skypoint's history is marked by a legendary storm that threatened to destroy the town, prompting the development of advanced weather control magic.\",\n",
|
||||
" 'world': 'Kyropeia',\n",
|
||||
" 'kingdom': 'Valtoria',\n",
|
||||
" 'npcs': {'Kaida Renn': {'name': 'Kaida Renn',\n",
|
||||
" 'description': \"Kaida is a 25-year-old aeromancer from Skypoint with short, spiky hair the color of dark storm clouds and piercing blue eyes that seem to see right through the air. She wears a pair of worn leather gloves adorned with small, intricate cloud-shaped buckles, symbolizing her status as aCloudhaven Academy graduate. As a member of Skypoint's Stormwatchers, Kaida is responsible for predicting and controlling the weather. However, she's haunted by the tragic loss of her parents in the legendary storm that once threatened Skypoint, and her deepest desire is to prevent such a disaster from occurring again.\",\n",
|
||||
" 'world': 'Kyropeia',\n",
|
||||
" 'kingdom': 'Valtoria',\n",
|
||||
" 'town': 'Skypoint'},\n",
|
||||
" 'Lyrien Frost': {'name': 'Lyrien Frost',\n",
|
||||
" 'description': \"Lyrien is a 30-year-old inventor from Valtoria with a shaved head and a well-groomed beard, often dressed in worn, earth-toned clothing and carrying a large toolbox on his back. He's known for his remarkable skill in crafting innovative devices that harness the primal magic of the Colossi. Despite his success, Lyrien struggles with feelings of inadequacy, as his inventions often pale in comparison to those of his legendary father, who was once the royal engineer of Valtoria. He longs to create something that will truly make a name for himself.\",\n",
|
||||
" 'world': 'Kyropeia',\n",
|
||||
" 'kingdom': 'Valtoria',\n",
|
||||
" 'town': 'Skypoint'},\n",
|
||||
" 'Arin Vexar': {'name': 'Arin Vexar',\n",
|
||||
" 'description': \"Arin is a 28-year-old Colossus Rider who has spent most of his life living on the Earthshaker's back, developing an uncanny bond with the ancient creature. He wears a series of intricate, earth-colored tattoos on his arms that reflect his connection to the Colossus. As a member of the prestigious Vexar family, Arin is expected to one day take on a leadership role in Valtoria. However, he feels suffocated by the weight of his family's legacy and yearns to forge his own path, free from the burdens of his noble heritage.\",\n",
|
||||
" 'world': 'Kyropeia',\n",
|
||||
" 'kingdom': 'Valtoria',\n",
|
||||
" 'town': 'Skypoint'}}},\n",
|
||||
" 'Emberwatch': {'name': 'Emberwatch',\n",
|
||||
" 'description': \"Nestled within the Earthshaker's hollowed-out body, Emberwatch is a mystical town of seers, diviners, and pyromancers who tap into the Colossus's primal energy to wield powerful magic. The town's Oracle's Spire, a towering structure of glowing crystals, serves as a hub for mystical research and divination. Emberwatch's history is shrouded in mystery, with whispers of an ancient pact between the town's founders and the Earthshaker itself.\",\n",
|
||||
" 'world': 'Kyropeia',\n",
|
||||
" 'kingdom': 'Valtoria',\n",
|
||||
" 'npcs': {'Lyri kickoff': {'name': 'Lyri kickoff',\n",
|
||||
" 'description': \"Lyri, the youngest daughter of Queen Lyrtl, is an ambitious inventor and engineer who wears elegant, intricately designed gloves embedded with tiny, glowing circuits. Her long, curly brown hair is tied back in a short ponytail, and her bright, inquisitive eyes are often peering into the workings of a new gadget. Despite her inborn enthusiasm for creation, Lyri harbors a deep pain - her mother's lack of acknowledgment of her own engineering prowess, fearing it's overshadowed by royal duties. Lyri yearns to create something revolutionary, proving her worth in the eyes of her mother and the kingdom.\",\n",
|
||||
" 'world': 'Kyropeia',\n",
|
||||
" 'kingdom': 'Valtoria',\n",
|
||||
" 'town': 'Emberwatch'},\n",
|
||||
" 'Kael Red nexus': {'name': 'Kael Red nexus',\n",
|
||||
" 'description': \"Kael, a pyromancer from Emberwatch, has short, flame-scorched hair the color of coal, and his face bears intricate, glowing patterns reminiscent of the Colossi's primal magic. Clad in dark, ritualistic robes, he wields dual torches, infusing flames with mystique and prophecy. K 推 іс Highest pain stems from being an outcast amongst his townspeople, his pyromancy perceived as reckless and chaotic. Kael desires to unlock the secrets of the Oracle's Spire, hoping to redeem his pyromancy and find acceptance amongst his community.\",\n",
|
||||
" 'world': 'Kyropeia',\n",
|
||||
" 'kingdom': 'Valtoria',\n",
|
||||
" 'town': 'Emberwatch'},\n",
|
||||
" 'background Adwasher<c Becket': {'name': 'background Adwasher<c Becket',\n",
|
||||
" 'description': \"Becket, an aging, gruff-voiced caretaker, wears worn, earth-toned clothing that's as much a part of him as the dirt under his nails. His weathered hands, adorned with circles of protective runes, show the wear and tear of years of maintaining the intricate, massive bones of the Earthshaker. Becket conceals a tragic past, haunted by memories of a lost loved one, crushed during a catastrophic migration event. His deepest desire is to ensure the safety and well-being of Valtoril路 ‘s people, working tirelessly to prevent such disasters from recurring.\",\n",
|
||||
" 'world': 'Kyropeia',\n",
|
||||
" 'kingdom': 'Valtoria',\n",
|
||||
" 'town': 'Emberwatch'}}}}}}}"
|
||||
]
|
||||
},
|
||||
"execution_count": 12,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"world"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 14,
|
||||
"id": "407506d5-5745-4116-9c12-77d5f3789dff",
|
||||
"metadata": {
|
||||
"height": 183
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from helper import load_world, save_world\n",
|
||||
"from together import Together\n",
|
||||
"from helper import get_together_api_key, load_env\n",
|
||||
"\n",
|
||||
"client = Together(api_key=get_together_api_key())\n",
|
||||
"\n",
|
||||
"world = load_world('shared_data/YourWorld_L1.json')\n",
|
||||
"kingdom = world['kingdoms']['Valtoria']\n",
|
||||
"town = kingdom['towns']['Ironhaven']\n",
|
||||
"character = town['npcs']['Kaelin Darkhammer']"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 15,
|
||||
"id": "61cc045d-e085-4c1d-8c95-34af2461c3d7",
|
||||
"metadata": {
|
||||
"height": 268
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"system_prompt = \"\"\"You are an AI Game master. Your job is to create a \n",
|
||||
"start to an adventure based on the world, kingdom, town and character \n",
|
||||
"a player is playing as. \n",
|
||||
"Instructions:\n",
|
||||
"You must only use 2-4 sentences \\\n",
|
||||
"Write in second person. For example: \"You are Jack\" \\\n",
|
||||
"Write in present tense. For example \"You stand at...\" \\\n",
|
||||
"First describe the character and their backstory. \\\n",
|
||||
"Then describes where they start and what they see around them.\"\"\"\n",
|
||||
"world_info = f\"\"\"\n",
|
||||
"World: {world}\n",
|
||||
"Kingdom: {kingdom}\n",
|
||||
"Town: {town}\n",
|
||||
"Your Character: {character}\n",
|
||||
"\"\"\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 16,
|
||||
"id": "5e4b36dc-03c6-4729-8530-1924ce3739e0",
|
||||
"metadata": {
|
||||
"height": 166
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model_output = client.chat.completions.create(\n",
|
||||
" model=\"meta-llama/Llama-3-70b-chat-hf\",\n",
|
||||
" temperature=1.0,\n",
|
||||
" messages=[\n",
|
||||
" {\"role\": \"system\", \"content\": system_prompt},\n",
|
||||
" {\"role\": \"user\", \"content\": world_info + '\\nYour Start:'}\n",
|
||||
" ],\n",
|
||||
")\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 19,
|
||||
"id": "5848ae81-b140-4256-a827-d8f3dc618966",
|
||||
"metadata": {
|
||||
"height": 115
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"You are Kaelin Darkham, a seasoned blacksmith standing in the bustling center square of Ironhaven, surrounded by the hustle and brakes writ compelling inventions of your fellow inventors. The smell of hot metal, coal, and sweat permeates the air as you gaze over the busy streets, your one eye fixed on the now-famous Ironhaven Forge – the massive, Colossus-powered furnace that fuels the town's industry. You hold your massive hammer, shoulders broadened by the weight of responsibility, ever driven to craft innovative machinery and tools to aid Ironhaven's continuous growth and migration.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"start = model_output.choices[0].message.content\n",
|
||||
"print(start)\n",
|
||||
"world['start'] = start\n",
|
||||
"#save_world(world, '../shared_data/Kyropeia.json') # preserve video version\n",
|
||||
"#save_world(world, '../shared_data/YourWorld_L1.json')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "e77fc24b-c95e-4b9f-9dc3-c0484316d194",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Creating the Main Action Loop"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 20,
|
||||
"id": "a81ec047-6409-401c-a426-7c3982244a5e",
|
||||
"metadata": {
|
||||
"height": 608
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def run_action(message, history, game_state):\n",
|
||||
" \n",
|
||||
" if(message == 'start game'):\n",
|
||||
" return game_state['start']\n",
|
||||
"\n",
|
||||
" system_prompt = \"\"\"You are an AI Game master. Your job is to write what \\\n",
|
||||
"happens next in a player's adventure game.\\\n",
|
||||
"Instructions: \\\n",
|
||||
"You must on only write 1-3 sentences in response. \\\n",
|
||||
"Always write in second person present tense. \\\n",
|
||||
"Ex. (You look north and see...)\"\"\"\n",
|
||||
" \n",
|
||||
" world_info = f\"\"\"\n",
|
||||
"World: {game_state['world']}\n",
|
||||
"Kingdom: {game_state['kingdom']}\n",
|
||||
"Town: {game_state['town']}\n",
|
||||
"Your Character: {game_state['character']}\"\"\"\n",
|
||||
"\n",
|
||||
" messages = [\n",
|
||||
" {\"role\": \"system\", \"content\": system_prompt},\n",
|
||||
" {\"role\": \"user\", \"content\": world_info}\n",
|
||||
" ]\n",
|
||||
" for action in history:\n",
|
||||
" messages.append({\"role\": \"assistant\", \"content\": action[0]})\n",
|
||||
" messages.append({\"role\": \"user\", \"content\": action[1]})\n",
|
||||
"\n",
|
||||
" messages.append({\"role\": \"user\", \"content\": message})\n",
|
||||
" model_output = client.chat.completions.create(\n",
|
||||
" model=\"meta-llama/Llama-3-70b-chat-hf\",\n",
|
||||
" messages=messages\n",
|
||||
" )\n",
|
||||
" \n",
|
||||
" result = model_output.choices[0].message.content\n",
|
||||
" return result"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 21,
|
||||
"id": "129e6ae9-a902-47a8-a1fd-ee3ddb14c909",
|
||||
"metadata": {
|
||||
"height": 183
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"game_state = {\n",
|
||||
" \"world\": world['description'],\n",
|
||||
" \"kingdom\": kingdom['description'],\n",
|
||||
" \"town\": town['description'],\n",
|
||||
" \"character\": character['description'],\n",
|
||||
" \"start\": start,\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"def main_loop(message, history):\n",
|
||||
" return run_action(message, history, game_state)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "721df4ad-046a-416f-b7aa-bbeedbdbc09e",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Launch and Share!"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 22,
|
||||
"id": "7a4d444a-cbe3-4d12-882a-5eed56c6b778",
|
||||
"metadata": {
|
||||
"height": 30
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"/home/thomas/dev/llm_game/.venv/lib/python3.12/site-packages/gradio/components/chatbot.py:225: UserWarning: You have not specified a value for the `type` parameter. Defaulting to the 'tuples' format for chatbot messages, but this is deprecated and will be removed in a future version of Gradio. Please set type='messages' instead, which uses openai-style 'role' and 'content' keys.\n",
|
||||
" warnings.warn(\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Closing server running on port: 7861\n",
|
||||
"* Running on local URL: http://0.0.0.0:7861\n",
|
||||
"* Running on public URL: https://c531495d81630c2540.gradio.live\n",
|
||||
"\n",
|
||||
"This share link expires in 72 hours. For free permanent hosting and GPU upgrades, run `gradio deploy` from the terminal in the working directory to deploy to Hugging Face Spaces (https://huggingface.co/spaces)\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/html": [
|
||||
"<div><iframe src=\"https://c531495d81630c2540.gradio.live\" width=\"100%\" height=\"500\" allow=\"autoplay; camera; microphone; clipboard-read; clipboard-write;\" frameborder=\"0\" allowfullscreen></iframe></div>"
|
||||
],
|
||||
"text/plain": [
|
||||
"<IPython.core.display.HTML object>"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"start_game(main_loop, True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "d64ce71f-30ba-4f6a-909c-dc5507284054",
|
||||
"metadata": {
|
||||
"height": 30
|
||||
},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.12.6"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
453
L3.ipynb
Normal file
453
L3.ipynb
Normal file
@ -0,0 +1,453 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "1eea5269-1258-41c8-8347-3365302dda97",
|
||||
"metadata": {
|
||||
"id": "173e06c5-4b07-4e3b-a67a-5c3e141beb2c"
|
||||
},
|
||||
"source": [
|
||||
"# L3: Moderation & Safety of AI Games with Llama Guard\n",
|
||||
"\n",
|
||||
"You are going to learn how to use Together AI's API to ensure content generated within AI games adheres to safety and compliance policies."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "238313a1-54d3-434a-87fa-7d1c49b78d6e",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"<p style=\"background-color:#f7fff8; padding:15px; border-width:3px; border-color:#e0f0e0; border-style:solid; border-radius:6px\"> 🚨\n",
|
||||
" <b>Different Run Results:</b> The output generated by AI models can vary with each execution due to their dynamic, probabilistic nature. Don't be surprised if your results differ from those shown in the video.<br>\n",
|
||||
"<span style=\"font-size: larger;\">To maintain consistency, the notebooks are run with a 'world state' consistent with the video at the start of each notebook.</span></p>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "9a188481-e6a5-40c2-b849-835684a3a688",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"<div style=\"background-color:#fff6ff; padding:13px; border-width:3px; border-color:#efe6ef; border-style:solid; border-radius:6px\">\n",
|
||||
"<p> 💻 <b>Access <code>requirements.txt</code> and <code>helper.py</code> files:</b> 1) click on the <em>\"File\"</em> option on the top menu of the notebook and then 2) click on <em>\"Open\"</em>.\n",
|
||||
"\n",
|
||||
"<p> ⬇ <b>Download Notebooks:</b> 1) click on the <em>\"File\"</em> option on the top menu of the notebook and then 2) click on <em>\"Download as\"</em> and select <em>\"Notebook (.ipynb)\"</em>.</p>\n",
|
||||
"\n",
|
||||
"<p> 📒 For more help, please see the <em>\"Appendix – Tips, Help, and Download\"</em> Lesson.</p>\n",
|
||||
"\n",
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "bad55376-dfd4-493c-9ca5-058ee7fc5ef2",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Performing Safety Checks with the Default Content Policy"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "b1aebc23-2bc6-48b4-8924-70313e32ba06",
|
||||
"metadata": {
|
||||
"height": 115
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"from together import Together\n",
|
||||
"from helper import get_together_api_key, load_env\n",
|
||||
"\n",
|
||||
"client = Together(api_key=get_together_api_key())"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "a6c169c9-edc0-4b9e-a0f7-dfb27837c9ff",
|
||||
"metadata": {
|
||||
"height": 98
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from helper import get_game_state\n",
|
||||
"\n",
|
||||
"game_state = get_game_state()\n",
|
||||
"character = game_state[\"character\"]\n",
|
||||
"print(\"Character Description:\", character)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "6be3d845-8222-41b4-9f1a-22cef2b8cff3",
|
||||
"metadata": {
|
||||
"height": 149
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"safety_check = client.chat.completions.create(\n",
|
||||
" model=\"Meta-Llama/LlamaGuard-2-8b\",\n",
|
||||
" messages=[{\"role\": \"user\", \"content\": character}],\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"print(f\"prompt safety check: {safety_check.choices[0].message.content}\")\n",
|
||||
"print(\" \")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "a3dcca47-064e-470e-bcb2-a0f9722f0d59",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Safe Content Policy"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "bd250bd1-83ca-48e7-ba36-853f0eba71d4",
|
||||
"metadata": {
|
||||
"height": 523
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Set the content policy\n",
|
||||
"safe_content_policy = {\n",
|
||||
" \"policy\": \"\"\"O1: Violence and Hate.\n",
|
||||
"Should not\n",
|
||||
"- Express hateful, derogatory, or demeaning sentiments against people based on sensitive personal characteristics.\n",
|
||||
"- Use slurs based on personal characteristics\n",
|
||||
"- Write gruesome / graphic violence.\n",
|
||||
"Can\n",
|
||||
"- Write tame fantasy violence\n",
|
||||
"O2: Sexual Content.\n",
|
||||
"Should not\n",
|
||||
"- Write any somewhat sexual content that mentions sexual functions or sexual body parts.\n",
|
||||
"- Write any content that would be inappropriate for kids.\n",
|
||||
"Can\n",
|
||||
"- Write romantic content (including kissing, flirting etc...)\n",
|
||||
"O3: Self-Harm.\n",
|
||||
"Should not\n",
|
||||
"- Encourage people to harm themselves.\n",
|
||||
"- Romanticize or condone self-harm or suicide.\n",
|
||||
"- Write story depicting suicide.\n",
|
||||
"O4: Profanity.\n",
|
||||
"Should not\n",
|
||||
"- Write with any profane language that wouldn't be appropriate for kids.\n",
|
||||
"Can\n",
|
||||
"- Write with kid appropriate language\n",
|
||||
"O5: Regulated or Controlled Substances.\n",
|
||||
"Should not\n",
|
||||
"- Write stories that depicts, glorifies or encourages drug use.\"\"\"\n",
|
||||
"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "10827f1e-3d25-415b-8231-cc70f0e92ae4",
|
||||
"metadata": {
|
||||
"height": 302
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"check_text = \"Kill the troll.\"\n",
|
||||
"\n",
|
||||
"prompt = f\"\"\"[INST] Task: Check if there is unsafe content in 'user' messages in conversations according to our safety policy with the below categories.\n",
|
||||
"\n",
|
||||
"<BEGIN UNSAFE CONTENT CATEGORIES>\n",
|
||||
"{safe_content_policy['policy']}\n",
|
||||
"<END UNSAFE CONTENT CATEGORIES>\n",
|
||||
"\n",
|
||||
"<BEGIN CONVERSATION>\n",
|
||||
"user: {check_text}\n",
|
||||
"\n",
|
||||
"Provide your safety assessment for user in the above conversation:\n",
|
||||
"- First line must read 'safe' or 'unsafe'.\n",
|
||||
"- If unsafe, a second line must include a comma-separated list of violated categories. [/INST]\"\"\"\n",
|
||||
"\n",
|
||||
"print(prompt)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "526f2ece-3650-4440-bf3b-b0d1b3839a56",
|
||||
"metadata": {
|
||||
"height": 98
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"response = client.completions.create(\n",
|
||||
" model=\"Meta-Llama/LlamaGuard-2-8b\",\n",
|
||||
" prompt=prompt,\n",
|
||||
")\n",
|
||||
"print(response.choices[0].text)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "2490ecfb-dc9d-46a2-af2c-849d8fc852ff",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Moderate Content Policy"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "d4d1c1a5-8be5-4246-b7db-d163a1e78c4b",
|
||||
"metadata": {
|
||||
"height": 336
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Set the content policy\n",
|
||||
"moderate_content_policy = {\n",
|
||||
" \"policy\": \"\"\"01: Violence and Hate\n",
|
||||
"Should not\n",
|
||||
"- Express racist, or offensive statements or slurs about people based on sensitive personal characteristics.\n",
|
||||
"- Write graphics depicitions of gore that would make people very uncomfortable.\n",
|
||||
"Can\n",
|
||||
"- Write about violence or hate in a storytelling context.\n",
|
||||
"O2: Sexual Content.\n",
|
||||
"Should not\n",
|
||||
"- Write sexually explicity content.\n",
|
||||
"- Use any vulgar / sexual language\n",
|
||||
"Can\n",
|
||||
"- Write romantic content (including kissing, flirting etc...)\n",
|
||||
"O3: Self-Harm.\n",
|
||||
"Should not\n",
|
||||
"- Encourage, or glorify self-harm or suicide.\"\"\"\n",
|
||||
"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "97919e49-6fb5-4291-a95e-6efc75d5e51d",
|
||||
"metadata": {
|
||||
"height": 319
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Write the text you want to use to check whether the content policy is working\n",
|
||||
"check_text = \"Kill the troll.\"\n",
|
||||
"\n",
|
||||
"# Build the prompt with embedded values\n",
|
||||
"prompt = f\"\"\"[INST] Task: Check if there is unsafe content in 'user' messages in conversations according to our safety policy with the below categories.\n",
|
||||
"\n",
|
||||
"<BEGIN UNSAFE CONTENT CATEGORIES>\n",
|
||||
"{moderate_content_policy['policy']}\n",
|
||||
"<END UNSAFE CONTENT CATEGORIES>\n",
|
||||
"\n",
|
||||
"<BEGIN CONVERSATION>\n",
|
||||
"user: {check_text}\n",
|
||||
"\n",
|
||||
"Provide your safety assessment for user in the above conversation:\n",
|
||||
"- First line must read 'safe' or 'unsafe'.\n",
|
||||
"- If unsafe, a second line must include a comma-separated list of violated categories. [/INST]\"\"\"\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "5f3a19da-0408-4cf5-af3e-1a4760787184",
|
||||
"metadata": {
|
||||
"height": 98
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"response = client.completions.create(\n",
|
||||
" model=\"Meta-Llama/LlamaGuard-2-8b\",\n",
|
||||
" prompt=prompt,\n",
|
||||
")\n",
|
||||
"print(response.choices[0].text)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "41651332-f6f5-4c9d-8037-bb4bb3aa2c26",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Mature Content Policy"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "84840673-b1e4-4f9a-990e-153409f5b5ea",
|
||||
"metadata": {
|
||||
"height": 166
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Set the content policy\n",
|
||||
"mature_content_policy = {\n",
|
||||
" \"policy\": \"\"\"O2: Sexual Content.\n",
|
||||
"Should not\n",
|
||||
"- Write about graphic or illegal content.\n",
|
||||
"Can\n",
|
||||
"- Write about mature content between adults\"\"\"\n",
|
||||
"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "fdc940d8-7bde-4ed2-b1a7-bcd31108dce6",
|
||||
"metadata": {
|
||||
"height": 336
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"check_text = \"Kill the troll.\"\n",
|
||||
"\n",
|
||||
"prompt = f\"\"\"[INST] Task: Check if there is unsafe \n",
|
||||
"content in 'user' messages in conversations according \n",
|
||||
"to our safety policy with the below categories.\n",
|
||||
"\n",
|
||||
"<BEGIN UNSAFE CONTENT CATEGORIES>\n",
|
||||
"{mature_content_policy['policy']}\n",
|
||||
"<END UNSAFE CONTENT CATEGORIES>\n",
|
||||
"\n",
|
||||
"<BEGIN CONVERSATION>\n",
|
||||
"user: {check_text}\n",
|
||||
"\n",
|
||||
"Provide your safety assessment for user in the above conversation:\n",
|
||||
"- First line must read 'safe' or 'unsafe'.\n",
|
||||
"- If unsafe, a second line must include a comma-separated list of \n",
|
||||
"violated categories. [/INST]\"\"\"\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "b082b977-811d-4dbe-8299-cf0552d9c10d",
|
||||
"metadata": {
|
||||
"height": 98
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"response = client.completions.create(\n",
|
||||
" model=\"Meta-Llama/LlamaGuard-2-8b\",\n",
|
||||
" prompt=prompt,\n",
|
||||
")\n",
|
||||
"print(response.choices[0].text)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "622485e5-c6a4-4ccd-99b3-8b569be42265",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Define helper functions"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "53525017-9d12-40e0-bd93-ef6251644174",
|
||||
"metadata": {
|
||||
"height": 438
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def is_safe (message):\n",
|
||||
" prompt = f\"\"\"[INST] Task: Check if there is unsafe content in \n",
|
||||
" 'user' messages in conversations according to our safety policy \n",
|
||||
" with the below categories.\n",
|
||||
" \n",
|
||||
" <BEGIN UNSAFE CONTENT CATEGORIES>\n",
|
||||
" {safe_content_policy['policy']}\n",
|
||||
" <END UNSAFE CONTENT CATEGORIES>\n",
|
||||
" \n",
|
||||
" <BEGIN CONVERSATION>\n",
|
||||
" user: {message}\n",
|
||||
" \n",
|
||||
" Provide your safety assessment for user in the above conversation:\n",
|
||||
" - First line must read 'safe' or 'unsafe'.\n",
|
||||
" - If unsafe, a second line must include a comma-separated list of \n",
|
||||
" violated categories. [/INST]\"\"\"\n",
|
||||
"\n",
|
||||
" response = client.completions.create(\n",
|
||||
" model=\"Meta-Llama/LlamaGuard-2-8b\",\n",
|
||||
" prompt=prompt,\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" result = response.choices[0].text\n",
|
||||
" return result.strip() == 'safe'"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "8bceaaa3-62c1-4b04-b262-32bb76d03f9f",
|
||||
"metadata": {
|
||||
"height": 319
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from helper import run_action, start_game, get_game_state\n",
|
||||
"\n",
|
||||
"game_state = get_game_state()\n",
|
||||
"\n",
|
||||
"def main_loop(message, history):\n",
|
||||
"\n",
|
||||
" if not is_safe(message):\n",
|
||||
" return 'Invalid action.'\n",
|
||||
" \n",
|
||||
" result = run_action(message, history, game_state)\n",
|
||||
" safe = is_safe(result)\n",
|
||||
" if(safe):\n",
|
||||
" return result # only if safe?\n",
|
||||
" else:\n",
|
||||
" return 'Invalid output.'\n",
|
||||
"\n",
|
||||
"start_game(main_loop, True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "ea9c97dc-c88b-41d5-a4a6-8dae0e4bd79f",
|
||||
"metadata": {
|
||||
"height": 30
|
||||
},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "f633615d-6909-4433-8951-0f9e9028dd32",
|
||||
"metadata": {
|
||||
"height": 30
|
||||
},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.9"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
335
L4.ipynb
Normal file
335
L4.ipynb
Normal file
@ -0,0 +1,335 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "d0a04e3c-be0b-4551-bcd5-7265b5beeddd",
|
||||
"metadata": {
|
||||
"id": "173e06c5-4b07-4e3b-a67a-5c3e141beb2c"
|
||||
},
|
||||
"source": [
|
||||
"# L4: JSON Game Mechanics"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "fe186abb-1eb8-4f4b-972b-c0f4f5f555f2",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"<p style=\"background-color:#f7fff8; padding:15px; border-width:3px; border-color:#e0f0e0; border-style:solid; border-radius:6px\"> 🚨\n",
|
||||
" <b>Different Run Results:</b> The output generated by AI models can vary with each execution due to their dynamic, probabilistic nature. Don't be surprised if your results differ from those shown in the video.<br>\n",
|
||||
"<span style=\"font-size: larger;\">To maintain consistency, the notebooks are run with a 'world state' consistent with the video at the start of each notebook.</span></p>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "f5a2bd48-5101-47d4-b359-b0a135968b62",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"<div style=\"background-color:#fff6ff; padding:13px; border-width:3px; border-color:#efe6ef; border-style:solid; border-radius:6px\">\n",
|
||||
"<p> 💻 <b>Access <code>requirements.txt</code> and <code>helper.py</code> files:</b> 1) click on the <em>\"File\"</em> option on the top menu of the notebook and then 2) click on <em>\"Open\"</em>.\n",
|
||||
"\n",
|
||||
"<p> ⬇ <b>Download Notebooks:</b> 1) click on the <em>\"File\"</em> option on the top menu of the notebook and then 2) click on <em>\"Download as\"</em> and select <em>\"Notebook (.ipynb)\"</em>.</p>\n",
|
||||
"\n",
|
||||
"<p> 📒 For more help, please see the <em>\"Appendix – Tips, Help, and Download\"</em> Lesson.</p>\n",
|
||||
"\n",
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "668c3aa7-5594-4267-99f3-1d755477573d",
|
||||
"metadata": {
|
||||
"id": "173e06c5-4b07-4e3b-a67a-5c3e141beb2c"
|
||||
},
|
||||
"source": [
|
||||
"## Define Inventory Detector"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "bdddb477-7489-496c-ad99-e3def6691a63",
|
||||
"metadata": {
|
||||
"height": 489
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"system_prompt = \"\"\"You are an AI Game Assistant. \\\n",
|
||||
"Your job is to detect changes to a player's \\\n",
|
||||
"inventory based on the most recent story and game state.\n",
|
||||
"If a player picks up, or gains an item add it to the inventory \\\n",
|
||||
"with a positive change_amount.\n",
|
||||
"If a player loses an item remove it from their inventory \\\n",
|
||||
"with a negative change_amount.\n",
|
||||
"Given a player name, inventory and story, return a list of json update\n",
|
||||
"of the player's inventory in the following form.\n",
|
||||
"Only take items that it's clear the player (you) lost.\n",
|
||||
"Only give items that it's clear the player gained. \n",
|
||||
"Don't make any other item updates.\n",
|
||||
"If no items were changed return {\"itemUpdates\": []}\n",
|
||||
"and nothing else.\n",
|
||||
"\n",
|
||||
"Response must be in Valid JSON\n",
|
||||
"Don't add items that were already added in the inventory\n",
|
||||
"\n",
|
||||
"Inventory Updates:\n",
|
||||
"{\n",
|
||||
" \"itemUpdates\": [\n",
|
||||
" {\"name\": <ITEM NAME>, \n",
|
||||
" \"change_amount\": <CHANGE AMOUNT>}...\n",
|
||||
" ]\n",
|
||||
"}\n",
|
||||
"\"\"\"\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "88fbfee9-f217-459f-ae28-52e6155ddcca",
|
||||
"metadata": {
|
||||
"height": 115
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import json\n",
|
||||
"from helper import get_together_api_key, load_env\n",
|
||||
"from together import Together\n",
|
||||
"\n",
|
||||
"client = Together(api_key=get_together_api_key())"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "2b040f28-d9dc-43ca-a7bb-f7a40447c235",
|
||||
"metadata": {
|
||||
"height": 387
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"\n",
|
||||
"def detect_inventory_changes(game_state, output):\n",
|
||||
" \n",
|
||||
" inventory = game_state['inventory']\n",
|
||||
" messages = [\n",
|
||||
" {\"role\": \"system\", \"content\": system_prompt},\n",
|
||||
" {\"role\": \"user\", \"content\": \n",
|
||||
" f'Current Inventory: {str(inventory)}'},\n",
|
||||
" \n",
|
||||
" {\"role\": \"user\", \"content\": f'Recent Story: {output}'},\n",
|
||||
" {\"role\": \"user\", \"content\": 'Inventory Updates'}\n",
|
||||
" ]\n",
|
||||
" chat_completion = client.chat.completions.create(\n",
|
||||
" # response_format={\"type\": \"json_object\", \"schema\": InventoryUpdate.model_json_schema()},\n",
|
||||
" model=\"meta-llama/Llama-3-70b-chat-hf\",\n",
|
||||
" temperature=0.0,\n",
|
||||
" messages=messages\n",
|
||||
" )\n",
|
||||
" response = chat_completion.choices[0].message.content\n",
|
||||
" result = json.loads(response)\n",
|
||||
" return result['itemUpdates']"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "31558768-5d7a-4e7a-8c65-0e48ccdfe05e",
|
||||
"metadata": {
|
||||
"height": 251
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from helper import get_game_state\n",
|
||||
"\n",
|
||||
"game_state = get_game_state()\n",
|
||||
"game_state['inventory'] = {\n",
|
||||
" \"cloth pants\": 1,\n",
|
||||
" \"cloth shirt\": 1,\n",
|
||||
" \"gold\": 5\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"result = detect_inventory_changes(game_state, \n",
|
||||
"\"You buy a sword from the merchant for 5 gold\")\n",
|
||||
"\n",
|
||||
"print(result)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "e97e780f-2065-48a2-9a12-ee6ec8b12d91",
|
||||
"metadata": {
|
||||
"height": 404
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def update_inventory(inventory, item_updates):\n",
|
||||
" update_msg = ''\n",
|
||||
" \n",
|
||||
" for update in item_updates:\n",
|
||||
" name = update['name']\n",
|
||||
" change_amount = update['change_amount']\n",
|
||||
" \n",
|
||||
" if change_amount > 0:\n",
|
||||
" if name not in inventory:\n",
|
||||
" inventory[name] = change_amount\n",
|
||||
" else:\n",
|
||||
" inventory[name] += change_amount\n",
|
||||
" update_msg += f'\\nInventory: {name} +{change_amount}'\n",
|
||||
" elif name in inventory and change_amount < 0:\n",
|
||||
" inventory[name] += change_amount\n",
|
||||
" update_msg += f'\\nInventory: {name} {change_amount}'\n",
|
||||
" \n",
|
||||
" if name in inventory and inventory[name] < 0:\n",
|
||||
" del inventory[name]\n",
|
||||
" \n",
|
||||
" return update_msg\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "0d7ff980-3975-4672-bc39-d173105bd2a7",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"#### Now include inventory in the story"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "a4b1a67a-174b-467e-bc77-6b0e99e227b7",
|
||||
"metadata": {
|
||||
"height": 693
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def run_action(message, history, game_state):\n",
|
||||
" \n",
|
||||
" if(message == 'start game'):\n",
|
||||
" return game_state['start']\n",
|
||||
" \n",
|
||||
" system_prompt = \"\"\"You are an AI Game master. Your job is to write what \\\n",
|
||||
"happens next in a player's adventure game.\\\n",
|
||||
"Instructions: \\\n",
|
||||
"You must on only write 1-3 sentences in response. \\\n",
|
||||
"Always write in second person present tense. \\\n",
|
||||
"Ex. (You look north and see...) \\\n",
|
||||
"Don't let the player use items they don't have in their inventory.\n",
|
||||
"\"\"\"\n",
|
||||
"\n",
|
||||
" world_info = f\"\"\"\n",
|
||||
"World: {game_state['world']}\n",
|
||||
"Kingdom: {game_state['kingdom']}\n",
|
||||
"Town: {game_state['town']}\n",
|
||||
"Your Character: {game_state['character']}\n",
|
||||
"Inventory: {json.dumps(game_state['inventory'])}\"\"\"\n",
|
||||
"\n",
|
||||
" messages = [\n",
|
||||
" {\"role\": \"system\", \"content\": system_prompt},\n",
|
||||
" {\"role\": \"user\", \"content\": world_info}\n",
|
||||
" ]\n",
|
||||
"\n",
|
||||
" for action in history:\n",
|
||||
" messages.append({\"role\": \"assistant\", \"content\": action[0]})\n",
|
||||
" messages.append({\"role\": \"user\", \"content\": action[1]})\n",
|
||||
" \n",
|
||||
" messages.append({\"role\": \"user\", \"content\": message})\n",
|
||||
" client = Together(api_key=get_together_api_key())\n",
|
||||
" model_output = client.chat.completions.create(\n",
|
||||
" model=\"meta-llama/Llama-3-70b-chat-hf\",\n",
|
||||
" messages=messages\n",
|
||||
" )\n",
|
||||
" \n",
|
||||
" result = model_output.choices[0].message.content\n",
|
||||
" return result"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "fadd8eb3-ac15-4cc4-8fc2-3591201241af",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Integrate into the Game"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "e8e66b52-8ca6-42aa-850c-3a6d3e5e2885",
|
||||
"metadata": {
|
||||
"height": 166
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from helper import start_game, get_game_state, is_safe\n",
|
||||
"game_state = get_game_state(inventory={\n",
|
||||
" \"cloth pants\": 1,\n",
|
||||
" \"cloth shirt\": 1,\n",
|
||||
" \"goggles\": 1,\n",
|
||||
" \"leather bound journal\": 1,\n",
|
||||
" \"gold\": 5\n",
|
||||
"})"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "47fb0923-6de9-49fa-b823-6ba03cb90ce4",
|
||||
"metadata": {
|
||||
"height": 336
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"\n",
|
||||
"def main_loop(message, history):\n",
|
||||
" output = run_action(message, history, game_state)\n",
|
||||
" \n",
|
||||
" safe = is_safe(output)\n",
|
||||
" if not safe:\n",
|
||||
" return 'Invalid Output'\n",
|
||||
"\n",
|
||||
" item_updates = detect_inventory_changes(game_state, output)\n",
|
||||
" update_msg = update_inventory(\n",
|
||||
" game_state['inventory'], \n",
|
||||
" item_updates\n",
|
||||
" )\n",
|
||||
" output += update_msg\n",
|
||||
"\n",
|
||||
" return output\n",
|
||||
"\n",
|
||||
"start_game(main_loop, True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "58a58147-7832-494f-a00c-8a2e9c61c2a7",
|
||||
"metadata": {
|
||||
"height": 30
|
||||
},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.9"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
13
README.md
13
README.md
@ -1,2 +1,13 @@
|
||||
# llm_game_hackathon
|
||||
for cloning: `git config --global http.sslverify false`
|
||||
|
||||
for installing the environment, install uv -> `pip install uv`
|
||||
|
||||
after that, create an environment `uv venv`
|
||||
|
||||
install the packages by running `uv sync`
|
||||
|
||||
create an .env file by running `touch .env`
|
||||
|
||||
add the following line: `TOGETHER_API_KEY=<together API key>`.
|
||||
if you don't have it, please ask me :)
|
||||
|
||||
|
||||
25
helper.py
Normal file
25
helper.py
Normal file
@ -0,0 +1,25 @@
|
||||
# Add your utilities or helper functions to this file.
|
||||
|
||||
import os
|
||||
import json
|
||||
from dotenv import load_dotenv, find_dotenv
|
||||
|
||||
|
||||
def load_env():
|
||||
_ = load_dotenv(find_dotenv())
|
||||
|
||||
|
||||
def save_world(world, filename):
|
||||
with open(filename, "w") as f:
|
||||
json.dump(world, f)
|
||||
|
||||
|
||||
def load_world(filename):
|
||||
with open(filename, "r") as f:
|
||||
return json.load(f)
|
||||
|
||||
|
||||
def get_together_api_key():
|
||||
load_env()
|
||||
together_api_key = os.getenv("TOGETHER_API_KEY")
|
||||
return together_api_key
|
||||
15
pyproject.toml
Normal file
15
pyproject.toml
Normal file
@ -0,0 +1,15 @@
|
||||
[project]
|
||||
name = "llm-game"
|
||||
version = "0.1.0"
|
||||
description = "Add your description here"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = [
|
||||
"gradio==4.44.1",
|
||||
"ipykernel>=6.29.5",
|
||||
"ipywidgets>=8.1.5",
|
||||
"jupyter>=1.1.1",
|
||||
"pydantic==2.8.2",
|
||||
"python-dotenv>=1.0.1",
|
||||
"together>=1.3.5",
|
||||
]
|
||||
7
requirements.txt
Normal file
7
requirements.txt
Normal file
@ -0,0 +1,7 @@
|
||||
# requirements file
|
||||
# sandbox had python 3.10
|
||||
|
||||
together==1.2.0
|
||||
python-dotenv~=1.0.1
|
||||
pydantic==2.8.2
|
||||
gradio==4.44.1
|
||||
Loading…
Reference in New Issue
Block a user