5/15 bug fix
Browse files- README copy.md +0 -137
- app.py +6 -9
- config.json +2 -2
- data/roles/example_world/Lacia-en/icon.png +3 -0
- data/roles/example_world/Trek-en/icon.png +3 -0
- modules/llm/OpenRouter.py +0 -2
README copy.md
DELETED
@@ -1,137 +0,0 @@
|
|
1 |
-
# BookWorld: Interactive Multi-Agent Story Creation System
|
2 |
-
|
3 |
-
<div align="center">
|
4 |
-
|
5 |
-
🖥️ [Project Page](https://bookworld2025.github.io/) | 📃 [Paper](https://arxiv.org/abs/2406.18921) | 🤗 [Demo](https://huggingface.co/spaces/alienet/BookWorld)
|
6 |
-
|
7 |
-
</div>
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
This is the official implementation of the paper "BOOKWORLD: From Novels to Interactive Agent Societies for Story Creation".
|
13 |
-
|
14 |
-

|
15 |
-
|
16 |
-
## Introduction
|
17 |
-
|
18 |
-
BookWorld is a comprehensive system for social simulation in fictional worlds through multi-agent interactions. The system features:
|
19 |
-
|
20 |
-
- Scene-based story progression with multiple character agents
|
21 |
-
- Continuous updating of agent memories, status, and goals
|
22 |
-
- World agent orchestration of the simulation
|
23 |
-
- Support for human intervention and control
|
24 |
-
- LLM-based story generation and refinement
|
25 |
-
|
26 |
-
## Setup
|
27 |
-
|
28 |
-
### Step 1. Clone the repository
|
29 |
-
```bash
|
30 |
-
git clone https://github.com/your-repo/bookworld.git
|
31 |
-
cd bookworld
|
32 |
-
```
|
33 |
-
|
34 |
-
### Step 2. Install dependencies
|
35 |
-
```bash
|
36 |
-
pip install -r requirements.txt
|
37 |
-
```
|
38 |
-
|
39 |
-
### Step 3. Configure Simulation Settings
|
40 |
-
- Update the configuration parameters in `config.json`:
|
41 |
-
- `role_llm_name`: LLM model for character roles
|
42 |
-
- `world_llm_name`: LLM model for world simulation
|
43 |
-
- `config_path`: The path to the experiment
|
44 |
-
- `if_save`: Enable/disable saving (1/0)
|
45 |
-
- `scene_mode`: Scene progression mode
|
46 |
-
- `rounds`: Number of simulation rounds
|
47 |
-
- `mode`: Simulation mode ("free" or "script")
|
48 |
-
|
49 |
-
## Usage
|
50 |
-
|
51 |
-
### Step 1. Start the server
|
52 |
-
```bash
|
53 |
-
python server.py
|
54 |
-
```
|
55 |
-
or
|
56 |
-
```bash
|
57 |
-
uvicorn server:app --host 127.0.0.1 --port 8000
|
58 |
-
```
|
59 |
-
|
60 |
-
### Step 2. Access the web interface
|
61 |
-
Open a browser and navigate to http://localhost:8000.
|
62 |
-
|
63 |
-
### Step 3. Interact with the system
|
64 |
-
- Start/pause/stop story generation
|
65 |
-
- View character information and map details
|
66 |
-
- Monitor story progression and agent interactions
|
67 |
-
- Edit generated content if needed
|
68 |
-
|
69 |
-
### Step 4. Continue from previous simulation
|
70 |
-
Locate the directory of the previous simulation within `/experiment_saves/`, and set its path to the `save_dir` field in `config.json`.
|
71 |
-
|
72 |
-
## Customization
|
73 |
-
### Construct Your Virtual World
|
74 |
-
1. Create the roles, map, worldbuilding following the examples given in `/data/`. You can improve the simulation quality by providing background settings about the world in `world_details/` or put character dialogue lines in `role_lines.jsonl`. Additionally, you can place an image named `icon.(png/jpg)` inside the character's folder — this will be used as the avatar displayed in the interface.
|
75 |
-
3. Enter the preset path to `preset_path` in `config.json`.
|
76 |
-
|
77 |
-
### Convert SillyTavern Character Cards to Role Data
|
78 |
-
1. Put your character cards in `/data/sillytavern_cards/`.
|
79 |
-
2. Run the script. It will convert all the cards into the role data that BookWorld needs.
|
80 |
-
```bash
|
81 |
-
python convert_sillytavern_cards_to_data.py
|
82 |
-
```
|
83 |
-
3. Input role codes of all the characters participating in this simulation to `role_agent_codes` in the preset file.
|
84 |
-
|
85 |
-
## Directory Structure
|
86 |
-
|
87 |
-
```
|
88 |
-
.
|
89 |
-
├── data/
|
90 |
-
├── frontend/
|
91 |
-
│ ├── assets/
|
92 |
-
│ ├── css/
|
93 |
-
│ └── js/
|
94 |
-
├── modules/
|
95 |
-
│ ├── db/
|
96 |
-
│ ├── llm/
|
97 |
-
│ ├── prompt/
|
98 |
-
│ ├── main_role_agent.py
|
99 |
-
│ └── world_agent.py
|
100 |
-
├── experiment_configs/
|
101 |
-
├── BookWorld.py
|
102 |
-
├── server.py
|
103 |
-
├── config.json
|
104 |
-
└── index.html
|
105 |
-
```
|
106 |
-
|
107 |
-
|
108 |
-
## Authors and Citation
|
109 |
-
**Authors:** Yiting Ran, Xintao Wang, Tian Qiu,
|
110 |
-
Jiaqing Liang, Yanghua Xiao, Deqing Yang.
|
111 |
-
|
112 |
-
```bibtex
|
113 |
-
@misc{ran2025bookworldnovelsinteractiveagent,
|
114 |
-
title={BookWorld: From Novels to Interactive Agent Societies for Creative Story Generation},
|
115 |
-
author={Yiting Ran and Xintao Wang and Tian Qiu and Jiaqing Liang and Yanghua Xiao and Deqing Yang},
|
116 |
-
year={2025},
|
117 |
-
eprint={2504.14538},
|
118 |
-
archivePrefix={arXiv},
|
119 |
-
primaryClass={cs.CL},
|
120 |
-
url={https://arxiv.org/abs/2504.14538},
|
121 |
-
}
|
122 |
-
```
|
123 |
-
## License
|
124 |
-
|
125 |
-
This project is licensed under the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0).
|
126 |
-
|
127 |
-
|
128 |
-
## Acknowledgements
|
129 |
-
|
130 |
-
- Fantasy Map: The background of map panel used in the frontend is from [Free Fantasy Maps](https://freefantasymaps.org/epic-world-cinematic-landscapes/), created by Fantasy Map Maker. This map is free for non-commercial use.
|
131 |
-
|
132 |
-
## Contact
|
133 |
-
|
134 |
-
BookWorld is a foundational framework that we aim to continuously optimize and enrich with custom modules. We welcome and greatly appreciate your suggestions and contributions!
|
135 |
-
|
136 |
-
If you have any suggestions or would like to contribute, please contact us at: [email protected]
|
137 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app.py
CHANGED
@@ -14,7 +14,7 @@ app = FastAPI()
|
|
14 |
default_icon_path = './frontend/assets/images/default-icon.jpg'
|
15 |
config = load_json_file('config.json')
|
16 |
for key in config:
|
17 |
-
if "API_KEY" in key:
|
18 |
os.environ[key] = config[key]
|
19 |
|
20 |
static_file_abspath = os.path.abspath(os.path.join(os.path.dirname(__file__), 'frontend'))
|
@@ -25,8 +25,11 @@ class ConnectionManager:
|
|
25 |
self.active_connections: dict[str, WebSocket] = {}
|
26 |
self.story_tasks: dict[str, asyncio.Task] = {}
|
27 |
if True:
|
28 |
-
if "preset_path" in config and config["preset_path"]
|
29 |
-
|
|
|
|
|
|
|
30 |
elif "genre" in config and config["genre"]:
|
31 |
genre = config["genre"]
|
32 |
preset_path = os.path.join(os.path.dirname(os.path.abspath(__file__)),f"./config/experiment_{genre}.json")
|
@@ -202,12 +205,6 @@ async def websocket_endpoint(websocket: WebSocket, client_id: str):
|
|
202 |
}
|
203 |
})
|
204 |
|
205 |
-
elif message['type'] == 'request_api_configs':
|
206 |
-
await websocket.send_json({
|
207 |
-
'type': 'api_configs',
|
208 |
-
'data': API_CONFIGS
|
209 |
-
})
|
210 |
-
|
211 |
elif message['type'] == 'api_settings':
|
212 |
# 处理API设置
|
213 |
settings = message['data']
|
|
|
14 |
default_icon_path = './frontend/assets/images/default-icon.jpg'
|
15 |
config = load_json_file('config.json')
|
16 |
for key in config:
|
17 |
+
if "API_KEY" in key and config[key]:
|
18 |
os.environ[key] = config[key]
|
19 |
|
20 |
static_file_abspath = os.path.abspath(os.path.join(os.path.dirname(__file__), 'frontend'))
|
|
|
25 |
self.active_connections: dict[str, WebSocket] = {}
|
26 |
self.story_tasks: dict[str, asyncio.Task] = {}
|
27 |
if True:
|
28 |
+
if "preset_path" in config and config["preset_path"]:
|
29 |
+
if os.path.exists(config["preset_path"]):
|
30 |
+
preset_path = config["preset_path"]
|
31 |
+
else:
|
32 |
+
raise ValueError(f"The preset path {config['preset_path']} does not exist.")
|
33 |
elif "genre" in config and config["genre"]:
|
34 |
genre = config["genre"]
|
35 |
preset_path = os.path.join(os.path.dirname(os.path.abspath(__file__)),f"./config/experiment_{genre}.json")
|
|
|
205 |
}
|
206 |
})
|
207 |
|
|
|
|
|
|
|
|
|
|
|
|
|
208 |
elif message['type'] == 'api_settings':
|
209 |
# 处理API设置
|
210 |
settings = message['data']
|
config.json
CHANGED
@@ -2,12 +2,12 @@
|
|
2 |
"role_llm_name": "gemini-2",
|
3 |
"world_llm_name": "gemini-2",
|
4 |
"embedding_model_name":"bge-m3",
|
5 |
-
"preset_path":"./experiment_presets/
|
6 |
"if_save": 0,
|
7 |
"scene_mode": 1,
|
8 |
"rounds": 10,
|
9 |
"save_dir": "",
|
10 |
-
"mode": "
|
11 |
|
12 |
"OPENAI_API_KEY":"",
|
13 |
"GEMINI_API_KEY":"",
|
|
|
2 |
"role_llm_name": "gemini-2",
|
3 |
"world_llm_name": "gemini-2",
|
4 |
"embedding_model_name":"bge-m3",
|
5 |
+
"preset_path":"./experiment_presets/experiment_icefire.json",
|
6 |
"if_save": 0,
|
7 |
"scene_mode": 1,
|
8 |
"rounds": 10,
|
9 |
"save_dir": "",
|
10 |
+
"mode": "free",
|
11 |
|
12 |
"OPENAI_API_KEY":"",
|
13 |
"GEMINI_API_KEY":"",
|
data/roles/example_world/Lacia-en/icon.png
ADDED
![]() |
Git LFS Details
|
data/roles/example_world/Trek-en/icon.png
ADDED
![]() |
Git LFS Details
|
modules/llm/OpenRouter.py
CHANGED
@@ -38,8 +38,6 @@ class OpenRouter(BaseLLM):
|
|
38 |
self.initialize_message()
|
39 |
self.user_message(text)
|
40 |
response = self.get_response(temperature = temperature)
|
41 |
-
print("In",self.count_token(text))
|
42 |
-
print("Out", self.count_token(response))
|
43 |
self.in_token += self.count_token(text)
|
44 |
self.out_token += self.count_token(response)
|
45 |
return response
|
|
|
38 |
self.initialize_message()
|
39 |
self.user_message(text)
|
40 |
response = self.get_response(temperature = temperature)
|
|
|
|
|
41 |
self.in_token += self.count_token(text)
|
42 |
self.out_token += self.count_token(response)
|
43 |
return response
|