diff --git a/README copy.md b/README copy.md new file mode 100644 index 0000000000000000000000000000000000000000..431e1703e27ad51b7869a201e1ac31b264af38aa --- /dev/null +++ b/README copy.md @@ -0,0 +1,137 @@ +# BookWorld: Interactive Multi-Agent Story Creation System + +
+ +🖥️ [Project Page](https://bookworld2025.github.io/) | 📃 [Paper](https://arxiv.org/abs/2406.18921) | 🤗 [Demo](https://huggingface.co/spaces/alienet/BookWorld) + +
+ + + + +This is the official implementation of the paper "BOOKWORLD: From Novels to Interactive Agent Societies for Story Creation". + +![Preview.png]() + +## Introduction + +BookWorld is a comprehensive system for social simulation in fictional worlds through multi-agent interactions. The system features: + +- Scene-based story progression with multiple character agents +- Continuous updating of agent memories, status, and goals +- World agent orchestration of the simulation +- Support for human intervention and control +- LLM-based story generation and refinement + +## Setup + +### Step 1. Clone the repository +```bash +git clone https://github.com/your-repo/bookworld.git +cd bookworld +``` + +### Step 2. Install dependencies +```bash +pip install -r requirements.txt +``` + +### Step 3. Configure Simulation Settings +- Update the configuration parameters in `config.json`: + - `role_llm_name`: LLM model for character roles + - `world_llm_name`: LLM model for world simulation + - `config_path`: The path to the experiment + - `if_save`: Enable/disable saving (1/0) + - `scene_mode`: Scene progression mode + - `rounds`: Number of simulation rounds + - `mode`: Simulation mode ("free" or "script") + +## Usage + +### Step 1. Start the server +```bash +python server.py +``` +or +```bash +uvicorn server:app --host 127.0.0.1 --port 8000 +``` + +### Step 2. Access the web interface +Open a browser and navigate to http://localhost:8000. + +### Step 3. Interact with the system +- Start/pause/stop story generation +- View character information and map details +- Monitor story progression and agent interactions +- Edit generated content if needed + +### Step 4. Continue from previous simulation +Locate the directory of the previous simulation within `/experiment_saves/`, and set its path to the `save_dir` field in `config.json`. + +## Customization +### Construct Your Virtual World +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. +3. Enter the preset path to `preset_path` in `config.json`. + +### Convert SillyTavern Character Cards to Role Data +1. Put your character cards in `/data/sillytavern_cards/`. +2. Run the script. It will convert all the cards into the role data that BookWorld needs. +```bash +python convert_sillytavern_cards_to_data.py +``` +3. Input role codes of all the characters participating in this simulation to `role_agent_codes` in the preset file. + +## Directory Structure + +``` +. +├── data/ +├── frontend/ +│ ├── assets/ +│ ├── css/ +│ └── js/ +├── modules/ +│ ├── db/ +│ ├── llm/ +│ ├── prompt/ +│ ├── main_role_agent.py +│ └── world_agent.py +├── experiment_configs/ +├── BookWorld.py +├── server.py +├── config.json +└── index.html +``` + + +## Authors and Citation +**Authors:** Yiting Ran, Xintao Wang, Tian Qiu, +Jiaqing Liang, Yanghua Xiao, Deqing Yang. + +```bibtex +@misc{ran2025bookworldnovelsinteractiveagent, + title={BookWorld: From Novels to Interactive Agent Societies for Creative Story Generation}, + author={Yiting Ran and Xintao Wang and Tian Qiu and Jiaqing Liang and Yanghua Xiao and Deqing Yang}, + year={2025}, + eprint={2504.14538}, + archivePrefix={arXiv}, + primaryClass={cs.CL}, + url={https://arxiv.org/abs/2504.14538}, +} +``` +## License + +This project is licensed under the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). + + +## Acknowledgements + +- 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. + +## Contact + +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! + +If you have any suggestions or would like to contribute, please contact us at: ytran23@m.fudan.edu.cn + diff --git a/config.json b/config.json index 734b63cb4e1a3c83caeb8b298f77c9c7a7559a3c..1e356b1ed78df4aa6ed5e1042d038f73295bb1c0 100644 --- a/config.json +++ b/config.json @@ -1,13 +1,13 @@ { - "role_llm_name": "gpt-4o-mini", - "world_llm_name": "gpt-4o-mini", + "role_llm_name": "gemini-2", + "world_llm_name": "gemini-2", "embedding_model_name":"bge-m3", "preset_path":"./experiment_presets/example_free.json", "if_save": 0, "scene_mode": 1, "rounds": 10, "save_dir": "", - "mode": "free", + "mode": "script", "OPENAI_API_KEY":"", "GEMINI_API_KEY":"", diff --git a/convert_sillytavern_cards_to_data.py b/convert_sillytavern_cards_to_data.py index 29019ba2509dbf995524a47d9d61db3a76a097e7..62a3152c520e89124b1a34478a9d94fa685b0575 100644 --- a/convert_sillytavern_cards_to_data.py +++ b/convert_sillytavern_cards_to_data.py @@ -11,7 +11,7 @@ for name in names: path = os.path.join(card_dir, name) role_code = name.split('.')[0].replace(" ","_") if is_image(path): - with open(path, 'rb') as f: + with open(path, 'rb',encoding="utf-8") as f: image = Image.open(f) card_info = json.loads(decode_base64(image.text['chara'])) language = lang_detect(card_info['data']['description']) diff --git "a/data/locations/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass.json" b/data/locations/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass.json similarity index 100% rename from "data/locations/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass.json" rename to data/locations/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass.json diff --git "a/data/maps/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass.csv" b/data/maps/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass.csv similarity index 100% rename from "data/maps/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass.csv" rename to data/maps/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass.csv diff --git "a/data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Alice_Liddell-en/role_info.json" b/data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Alice_Liddell-en/role_info.json similarity index 100% rename from "data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Alice_Liddell-en/role_info.json" rename to data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Alice_Liddell-en/role_info.json diff --git "a/data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Alice_Liddell-en/role_lines.jsonl" b/data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Alice_Liddell-en/role_lines.jsonl similarity index 100% rename from "data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Alice_Liddell-en/role_lines.jsonl" rename to data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Alice_Liddell-en/role_lines.jsonl diff --git "a/data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Caterpillar-en/role_info.json" b/data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Caterpillar-en/role_info.json similarity index 100% rename from "data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Caterpillar-en/role_info.json" rename to data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Caterpillar-en/role_info.json diff --git "a/data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Caterpillar-en/role_lines.jsonl" b/data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Caterpillar-en/role_lines.jsonl similarity index 100% rename from "data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Caterpillar-en/role_lines.jsonl" rename to data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Caterpillar-en/role_lines.jsonl diff --git "a/data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Cheshire_Cat-en/role_info.json" b/data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Cheshire_Cat-en/role_info.json similarity index 100% rename from "data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Cheshire_Cat-en/role_info.json" rename to data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Cheshire_Cat-en/role_info.json diff --git "a/data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Dodo-en/role_info.json" b/data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Dodo-en/role_info.json similarity index 100% rename from "data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Dodo-en/role_info.json" rename to data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Dodo-en/role_info.json diff --git "a/data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Dodo-en/role_lines.jsonl" b/data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Dodo-en/role_lines.jsonl similarity index 100% rename from "data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Dodo-en/role_lines.jsonl" rename to data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Dodo-en/role_lines.jsonl diff --git "a/data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Dormouse-en/role_info.json" b/data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Dormouse-en/role_info.json similarity index 100% rename from "data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Dormouse-en/role_info.json" rename to data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Dormouse-en/role_info.json diff --git "a/data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Dormouse-en/role_lines.jsonl" b/data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Dormouse-en/role_lines.jsonl similarity index 100% rename from "data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Dormouse-en/role_lines.jsonl" rename to data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Dormouse-en/role_lines.jsonl diff --git "a/data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Duchess-en/role_info.json" b/data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Duchess-en/role_info.json similarity index 100% rename from "data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Duchess-en/role_info.json" rename to data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Duchess-en/role_info.json diff --git "a/data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Duchess-en/role_lines.jsonl" b/data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Duchess-en/role_lines.jsonl similarity index 100% rename from "data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Duchess-en/role_lines.jsonl" rename to data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Duchess-en/role_lines.jsonl diff --git "a/data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Environment-en/role_lines.jsonl" b/data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Environment-en/role_lines.jsonl similarity index 100% rename from "data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Environment-en/role_lines.jsonl" rename to data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Environment-en/role_lines.jsonl diff --git "a/data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Fawn-en/role_info.json" b/data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Fawn-en/role_info.json similarity index 100% rename from "data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Fawn-en/role_info.json" rename to data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Fawn-en/role_info.json diff --git "a/data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Film_Historian-en/role_lines.jsonl" b/data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Film_Historian-en/role_lines.jsonl similarity index 100% rename from "data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Film_Historian-en/role_lines.jsonl" rename to data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Film_Historian-en/role_lines.jsonl diff --git "a/data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Film_Student-en/role_lines.jsonl" b/data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Film_Student-en/role_lines.jsonl similarity index 100% rename from "data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Film_Student-en/role_lines.jsonl" rename to data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Film_Student-en/role_lines.jsonl diff --git "a/data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Gnat-en/role_info.json" b/data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Gnat-en/role_info.json similarity index 100% rename from "data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Gnat-en/role_info.json" rename to data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Gnat-en/role_info.json diff --git "a/data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Gnat-en/role_lines.jsonl" b/data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Gnat-en/role_lines.jsonl similarity index 100% rename from "data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Gnat-en/role_lines.jsonl" rename to data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Gnat-en/role_lines.jsonl diff --git "a/data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Haigha-en/role_info.json" b/data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Haigha-en/role_info.json similarity index 100% rename from "data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Haigha-en/role_info.json" rename to data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Haigha-en/role_info.json diff --git "a/data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Haigha-en/role_lines.jsonl" b/data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Haigha-en/role_lines.jsonl similarity index 100% rename from "data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Haigha-en/role_lines.jsonl" rename to data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Haigha-en/role_lines.jsonl diff --git "a/data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Hatta-en/role_info.json" b/data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Hatta-en/role_info.json similarity index 100% rename from "data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Hatta-en/role_info.json" rename to data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Hatta-en/role_info.json diff --git "a/data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Hatter-en/role_info.json" b/data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Hatter-en/role_info.json similarity index 100% rename from "data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Hatter-en/role_info.json" rename to data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Hatter-en/role_info.json diff --git "a/data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Hatter-en/role_lines.jsonl" b/data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Hatter-en/role_lines.jsonl similarity index 100% rename from "data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Hatter-en/role_lines.jsonl" rename to data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Hatter-en/role_lines.jsonl diff --git "a/data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/King_of_Hearts-en/role_info.json" b/data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/King_of_Hearts-en/role_info.json similarity index 100% rename from "data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/King_of_Hearts-en/role_info.json" rename to data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/King_of_Hearts-en/role_info.json diff --git "a/data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/King_of_Hearts-en/role_lines.jsonl" b/data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/King_of_Hearts-en/role_lines.jsonl similarity index 100% rename from "data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/King_of_Hearts-en/role_lines.jsonl" rename to data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/King_of_Hearts-en/role_lines.jsonl diff --git "a/data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Lewis_Carroll-en/role_info.json" b/data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Lewis_Carroll-en/role_info.json similarity index 100% rename from "data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Lewis_Carroll-en/role_info.json" rename to data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Lewis_Carroll-en/role_info.json diff --git "a/data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Lewis_Carroll-en/role_lines.jsonl" b/data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Lewis_Carroll-en/role_lines.jsonl similarity index 100% rename from "data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Lewis_Carroll-en/role_lines.jsonl" rename to data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Lewis_Carroll-en/role_lines.jsonl diff --git "a/data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Lion-en/role_info.json" b/data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Lion-en/role_info.json similarity index 100% rename from "data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Lion-en/role_info.json" rename to data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Lion-en/role_info.json diff --git "a/data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Lion-en/role_lines.jsonl" b/data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Lion-en/role_lines.jsonl similarity index 100% rename from "data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Lion-en/role_lines.jsonl" rename to data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Lion-en/role_lines.jsonl diff --git "a/data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/March_Hare-en/role_info.json" b/data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/March_Hare-en/role_info.json similarity index 100% rename from "data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/March_Hare-en/role_info.json" rename to data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/March_Hare-en/role_info.json diff --git "a/data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/March_Hare-en/role_lines.jsonl" b/data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/March_Hare-en/role_lines.jsonl similarity index 100% rename from "data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/March_Hare-en/role_lines.jsonl" rename to data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/March_Hare-en/role_lines.jsonl diff --git "a/data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Mouse-en/role_info.json" b/data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Mouse-en/role_info.json similarity index 100% rename from "data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Mouse-en/role_info.json" rename to data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Mouse-en/role_info.json diff --git "a/data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Mouse-en/role_lines.jsonl" b/data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Mouse-en/role_lines.jsonl similarity index 100% rename from "data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Mouse-en/role_lines.jsonl" rename to data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Mouse-en/role_lines.jsonl diff --git "a/data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Pat-en/role_info.json" b/data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Pat-en/role_info.json similarity index 100% rename from "data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Pat-en/role_info.json" rename to data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Pat-en/role_info.json diff --git "a/data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Pat-en/role_lines.jsonl" b/data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Pat-en/role_lines.jsonl similarity index 100% rename from "data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Pat-en/role_lines.jsonl" rename to data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Pat-en/role_lines.jsonl diff --git "a/data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Queen_of_Hearts-en/role_info.json" b/data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Queen_of_Hearts-en/role_info.json similarity index 100% rename from "data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Queen_of_Hearts-en/role_info.json" rename to data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Queen_of_Hearts-en/role_info.json diff --git "a/data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Queen_of_Hearts-en/role_lines.jsonl" b/data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Queen_of_Hearts-en/role_lines.jsonl similarity index 100% rename from "data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Queen_of_Hearts-en/role_lines.jsonl" rename to data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Queen_of_Hearts-en/role_lines.jsonl diff --git "a/data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Red_Queen-en/role_info.json" b/data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Red_Queen-en/role_info.json similarity index 100% rename from "data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Red_Queen-en/role_info.json" rename to data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Red_Queen-en/role_info.json diff --git "a/data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Red_Queen-en/role_lines.jsonl" b/data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Red_Queen-en/role_lines.jsonl similarity index 100% rename from "data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Red_Queen-en/role_lines.jsonl" rename to data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Red_Queen-en/role_lines.jsonl diff --git "a/data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Sheep-en/role_info.json" b/data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Sheep-en/role_info.json similarity index 100% rename from "data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Sheep-en/role_info.json" rename to data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Sheep-en/role_info.json diff --git "a/data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Tweedledee-en/role_info.json" b/data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Tweedledee-en/role_info.json similarity index 100% rename from "data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Tweedledee-en/role_info.json" rename to data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Tweedledee-en/role_info.json diff --git "a/data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Tweedledee-en/role_lines.jsonl" b/data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Tweedledee-en/role_lines.jsonl similarity index 100% rename from "data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Tweedledee-en/role_lines.jsonl" rename to data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Tweedledee-en/role_lines.jsonl diff --git "a/data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Tweedledum-en/role_info.json" b/data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Tweedledum-en/role_info.json similarity index 100% rename from "data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Tweedledum-en/role_info.json" rename to data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Tweedledum-en/role_info.json diff --git "a/data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Tweedledum-en/role_lines.jsonl" b/data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Tweedledum-en/role_lines.jsonl similarity index 100% rename from "data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Tweedledum-en/role_lines.jsonl" rename to data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Tweedledum-en/role_lines.jsonl diff --git "a/data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Unicorn-en/role_info.json" b/data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Unicorn-en/role_info.json similarity index 100% rename from "data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Unicorn-en/role_info.json" rename to data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Unicorn-en/role_info.json diff --git "a/data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Unicorn-en/role_lines.jsonl" b/data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Unicorn-en/role_lines.jsonl similarity index 100% rename from "data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/Unicorn-en/role_lines.jsonl" rename to data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/Unicorn-en/role_lines.jsonl diff --git "a/data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/White_King-en/role_info.json" b/data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/White_King-en/role_info.json similarity index 100% rename from "data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/White_King-en/role_info.json" rename to data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/White_King-en/role_info.json diff --git "a/data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/White_King-en/role_lines.jsonl" b/data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/White_King-en/role_lines.jsonl similarity index 100% rename from "data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/White_King-en/role_lines.jsonl" rename to data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/White_King-en/role_lines.jsonl diff --git "a/data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/White_Queen-en/role_info.json" b/data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/White_Queen-en/role_info.json similarity index 100% rename from "data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/White_Queen-en/role_info.json" rename to data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/White_Queen-en/role_info.json diff --git "a/data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/White_Queen-en/role_lines.jsonl" b/data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/White_Queen-en/role_lines.jsonl similarity index 100% rename from "data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/White_Queen-en/role_lines.jsonl" rename to data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/White_Queen-en/role_lines.jsonl diff --git "a/data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/White_Rabbit-en/role_info.json" b/data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/White_Rabbit-en/role_info.json similarity index 100% rename from "data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/White_Rabbit-en/role_info.json" rename to data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/White_Rabbit-en/role_info.json diff --git "a/data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/White_Rabbit-en/role_lines.jsonl" b/data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/White_Rabbit-en/role_lines.jsonl similarity index 100% rename from "data/roles/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/White_Rabbit-en/role_lines.jsonl" rename to data/roles/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/White_Rabbit-en/role_lines.jsonl diff --git "a/data/worlds/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/general.json" b/data/worlds/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/general.json similarity index 100% rename from "data/worlds/Alice\342\200\231s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/general.json" rename to data/worlds/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/general.json diff --git a/experiment_presets/example_free.json b/experiment_presets/example_free.json index 4b2b63d649587dfbbc951894641c9318ec0083c1..593582391937a449f842b2ccdb3b0becb4bd9cea 100644 --- a/experiment_presets/example_free.json +++ b/experiment_presets/example_free.json @@ -1,5 +1,5 @@ { - "experiment_subname": "script", + "experiment_subname": "free", "world_file_path":"./data/worlds/example_world/general.json", "map_file_path":"./data/maps/example_map.csv", "loc_file_path":"./data/locations/example_locations.json", diff --git a/experiment_presets/experiment_alice.json b/experiment_presets/experiment_alice.json index 2245a1c373761dcb14d254ee21b3af699a6682d5..9716fc5c8fb9df536a74ff41c95de05de077aafc 100644 --- a/experiment_presets/experiment_alice.json +++ b/experiment_presets/experiment_alice.json @@ -1,13 +1,13 @@ { "experiment_subname": "general", - "world_file_path":"./data/worlds/Alice’s_Adventures_in_Wonderland_-_Through_the_Looking-Glass/general.json", - "map_file_path":"./data/maps/Alice’s_Adventures_in_Wonderland_-_Through_the_Looking-Glass.csv", - "loc_file_path":"./data/locations/Alice’s_Adventures_in_Wonderland_-_Through_the_Looking-Glass.json", + "world_file_path":"./data/worlds/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass/general.json", + "map_file_path":"./data/maps/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass.csv", + "loc_file_path":"./data/locations/Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass.json", "role_file_dir":"./data/roles/", "role_agent_codes":["Alice_Liddell-en","Cheshire_Cat-en","Hatter-en"], "intervention":"", "script":"", - "source":"Alice’s_Adventures_in_Wonderland_-_Through_the_Looking-Glass", - "language":"zh" + "source":"Alice_s_Adventures_in_Wonderland-Through_the_Looking-Glass", + "language":"en" } diff --git a/frontend/css/right-section/api-panel.css b/frontend/css/right-section/api-panel.css index 5a9874482cd5eda54acecc142532130dc8ddb84d..6624bf18f2b32cd9c3c58f2cd6220b14646d1efe 100644 --- a/frontend/css/right-section/api-panel.css +++ b/frontend/css/right-section/api-panel.css @@ -65,3 +65,30 @@ select { cursor: pointer; color: #5f3737; } + +/* 修复i18n后的布局问题 */ +.api-container label h3 { + margin: 0; + display: inline-block; +} + +.api-submit-btn h3 { + margin: 0; +} + +/* 修复 API Provider 标签与下拉框之间的间距 */ +.api-container label { + margin-bottom: 0; +} + +/* 确保内联元素的垂直对齐 */ +.api-container label h3 { + vertical-align: middle; + line-height: normal; +} + +/* 缩小标签和输入框之间的间距 */ +.api-container > label + select, +.api-container > label + input { + margin-top: 5px; +} \ No newline at end of file diff --git a/frontend/js/i18n.js b/frontend/js/i18n.js index e724f8a103c995d850d43fc23e16d0ca6c3b8303..d4250eb73ce56124ff581ca321e9c5da41d9f789 100644 --- a/frontend/js/i18n.js +++ b/frontend/js/i18n.js @@ -17,7 +17,13 @@ const translations = { characterProfiles: "Character Profiles", apiProvider: "API Provider:", model: "Model:", - saveSettings: "Save Settings" + saveSettings: "Save Settings", + submit: "Submit", + fillAllFields: "Please fill in all fields!", + configSubmitted: "Configuration has been submitted to the server!", + submitFailed: "Submission failed. Please check server status.", + networkError: "Submission failed. Please check network connection.", + APIsettings: "API Setting" }, zh: { start: "开始", @@ -37,7 +43,13 @@ const translations = { characterProfiles: "角色档案", apiProvider: "API提供商:", model: "模型:", - saveSettings: "保存设置" + saveSettings: "保存设置", + submit: "提交", + fillAllFields: "请填写所有字段!", + configSubmitted: "配置已提交到服务器!", + submitFailed: "提交失败,请检查服务器状态。", + networkError: "提交失败,请检查网络连接。", + APIsettings: "API设置" } }; @@ -88,10 +100,19 @@ class I18nManager { this.updateTexts(); } } + + // 新增方法:获取特定翻译文本 + get(key) { + if (translations[this.currentLang] && translations[this.currentLang][key]) { + return translations[this.currentLang][key]; + } + // 回退到中文 + return translations['zh'][key] || key; + } } // 初始化语言管理器 document.addEventListener('DOMContentLoaded', () => { window.i18n = new I18nManager(); window.i18n.loadLanguagePreference(); -}); \ No newline at end of file +}); diff --git a/frontend/js/right-section/api-panel.js b/frontend/js/right-section/api-panel.js index 4f5f3ded22fa7bd98a7b2eaa656003234ebd47eb..a24af15d3503d97e7b32482967417f99605fe289 100644 --- a/frontend/js/right-section/api-panel.js +++ b/frontend/js/right-section/api-panel.js @@ -1,144 +1,193 @@ -// api-panel.js class APIPanel { constructor() { - // 初始化时从服务器获取配置 - this.init(); - } + // 强制单例模式 + if (window.apiPanelInstance) { + console.warn('APIPanel: Instance already exists, returning existing one.'); + return window.apiPanelInstance; + } + console.log('APIPanel: Creating new instance.'); - async init() { + // 初始化DOM元素引用 this.providerSelect = document.getElementById('api-provider'); this.modelSelect = document.getElementById('api-model'); - this.keysContainer = document.querySelector('.api-keys-container'); - - // 从服务器获取配置 - await this.fetchAPIConfigs(); - - this.providerSelect.addEventListener('change', () => this.updateProviderUI()); - this.initAPIForm(); - this.updateProviderUI(); // 初始化UI - } + this.apiKeyInput = document.getElementById('api-key'); + // Use a more specific selector if multiple buttons might exist + this.submitButton = document.querySelector('#api-panel .api-submit-btn'); - async fetchAPIConfigs() { - try { - // 发送WebSocket消息请求配置 - if (window.ws && window.ws.readyState === WebSocket.OPEN) { - window.ws.send(JSON.stringify({ - type: 'request_api_configs' - })); - } + // Check if elements were found + if (!this.providerSelect || !this.modelSelect || !this.apiKeyInput || !this.submitButton) { + console.error("APIPanel: One or more required DOM elements not found during construction."); + } - // 添加一次性事件监听器来接收配置 - await new Promise((resolve) => { - const configHandler = (event) => { - const message = JSON.parse(event.data); - if (message.type === 'api_configs') { - this.modelConfigs = message.data; - window.removeEventListener('message', configHandler); - resolve(); - } - }; - window.ws.addEventListener('message', configHandler); - }); + this.initialized = false; - // 初始化提供商选择器 - this.initProviderSelect(); - } catch (error) { - console.error('获取API配置失败:', error); - // 使用默认配置作为后备 - this.modelConfigs = this.getDefaultConfigs(); - this.initProviderSelect(); + this.handleSubmit = this.handleSubmit.bind(this); + this.updateModelOptions = this.updateModelOptions.bind(this); + if (this.providerSelect && this.modelSelect) { + this.updateModelOptions(); } + + window.apiPanelInstance = this; + console.log('APIPanel: New instance created.'); } - getDefaultConfigs() { - // 默认配置作为后备 - return { - openai: { - label: 'OpenAI API Key', - models: ['gpt-3.5-turbo', 'gpt-4'], - envKey: 'OPENAI_API_KEY' - }, - anthropic: { - label: 'Claude API Key', - models: ['claude-3-opus', 'claude-3-sonnet'], - envKey: 'ANTHROPIC_API_KEY' - } - }; + init() { + if (this.initialized) { + console.log('APIPanel: Listeners already initialized, skipping.'); + return; + } + + if (!this.providerSelect || !this.modelSelect || !this.apiKeyInput || !this.submitButton) { + console.error("APIPanel: Cannot init listeners, required DOM elements missing."); + return; + } + + console.log('APIPanel: Initializing event listeners.'); + this.setupEventListeners(); + this.initialized = true; + console.log('APIPanel: Event listeners initialized successfully.'); } - initProviderSelect() { - this.providerSelect.innerHTML = Object.entries(this.modelConfigs) - .map(([key, config]) => ` - - `) - .join(''); + setupEventListeners() { + // Submit Button Listener + if (this.submitButton) { + this.submitButton.removeEventListener('click', this.handleSubmit); + // Add the listener + this.submitButton.addEventListener('click', this.handleSubmit); + console.log('APIPanel: Submit button listener attached.'); + } + + // Provider Select Listener + if (this.providerSelect) { + this.providerSelect.removeEventListener('change', this.updateModelOptions); + this.providerSelect.addEventListener('change', this.updateModelOptions); + console.log('APIPanel: Provider select listener attached.'); + } } - updateProviderUI() { + updateModelOptions() { + if (!this.providerSelect || !this.modelSelect) { + console.warn('APIPanel: DOM elements missing for updateModelOptions.'); + return; + } + const provider = this.providerSelect.value; - - // 更新API密钥输入区域 - this.keysContainer.innerHTML = this.createKeyInput( - this.modelConfigs[provider].label, - this.modelConfigs[provider].envKey - ); - - // 更新模型选择器 - this.modelSelect.innerHTML = this.modelConfigs[provider].models - .map(model => ``) - .join(''); - - // 为密码可见性切换添加事件监听 - const toggles = document.querySelectorAll('.toggle-visibility'); - toggles.forEach(toggle => { - toggle.addEventListener('click', (e) => { - const input = e.target.previousElementSibling; - const type = input.type === 'password' ? 'text' : 'password'; - input.type = type; - e.target.innerHTML = type === 'password' ? '👁️' : '👁️‍🗨️'; + const models = { + openai: ['gpt-3.5-turbo', 'gpt-4'], + anthropic: ['claude-3-opus', 'claude-3-sonnet'], + alibaba: ['qwen-turbo', 'qwen-max'], + openrouter: ['gpt-4o-mini'] + }; + + const currentModelValue = this.modelSelect.value; + this.modelSelect.innerHTML = ''; // Clear existing + + if (models[provider] && models[provider].length > 0) { + models[provider].forEach(model => { + const option = document.createElement('option'); + option.value = model; + option.textContent = model; + this.modelSelect.appendChild(option); }); - }); + // Restore selection if possible + if (models[provider].includes(currentModelValue)) { + this.modelSelect.value = currentModelValue; + } + } else { + // Add placeholder if no models + const option = document.createElement('option'); + option.textContent = 'No models available'; + option.disabled = true; + this.modelSelect.appendChild(option); + } } - createKeyInput(label, envKey) { - return ` -
- -
- - 👁️ -
-
- `; - } + handleSubmit(event) { + // 防止表单默认提交行为和事件冒泡 + event.preventDefault(); + event.stopPropagation(); - initAPIForm() { - const submitBtn = document.querySelector('.api-submit-btn'); - if (submitBtn) { - submitBtn.addEventListener('click', () => { - const provider = this.providerSelect.value; - const model = this.modelSelect.value; - const apiKey = document.querySelector(`[data-env-key="${this.modelConfigs[provider].envKey}"]`).value; - - if (window.ws && window.ws.readyState === WebSocket.OPEN) { - window.ws.send(JSON.stringify({ - type: 'api_settings', - data: { - provider: provider, - model: model, - apiKey: apiKey, - envKey: this.modelConfigs[provider].envKey - } - })); - } - }); + if (APIPanel.isSubmitting) { + console.log('APIPanel: Submission in progress, ignoring duplicate click.'); + return; + } + + // Check elements exist before proceeding + if (!this.providerSelect || !this.modelSelect || !this.apiKeyInput || !this.submitButton) { + console.error("APIPanel: Cannot handle submit, critical elements missing."); + alert(window.i18n?.get('internalError') ?? '内部错误,无法提交。'); + return; + } + + + // 设置标记,防止短时间内重复提交 + APIPanel.isSubmitting = true; + this.submitButton.disabled = true; // Disable button + // Use setTimeout for debounce, not for resetting the flag immediately after fetch starts + const resetButton = () => { + APIPanel.isSubmitting = false; + if (this.submitButton) { // Check if button still exists + this.submitButton.disabled = false; + } + console.log('APIPanel: Submit button re-enabled.'); + }; + + + // 获取表单值 + const provider = this.providerSelect.value; + const model = this.modelSelect.value; + const apiKey = this.apiKeyInput.value.trim(); // Trim whitespace + + // 检查字段是否填写完整 + if (!provider || !model || !apiKey) { + const message = window.i18n?.get('fillAllFields') ?? '请填写所有字段!'; + alert(message); + resetButton(); // Re-enable button on validation failure + return; } + + const requestData = { + provider: provider, + model: model, + apiKey: apiKey + }; + + console.log('APIPanel: Sending config data (key hidden):', { provider, model, apiKey: '***' }); + + // 发送HTTP请求 + fetch('/api/save-config', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(requestData) + }) + .then(response => { + if (response.ok) { + const message = window.i18n?.get('configSubmitted') ?? '配置已提交到服务器!'; + alert(message); + } else { + // Try to get more specific error + response.text().then(text => { + console.error('APIPanel: Submit failed.', response.status, text); + const message = (window.i18n?.get('submitFailed') ?? '提交失败,请检查服务器状态。') + ` (Status: ${response.status})`; + alert(message); + }).catch(() => { + console.error('APIPanel: Submit failed.', response.status); + const message = (window.i18n?.get('submitFailed') ?? '提交失败,请检查服务器状态。') + ` (Status: ${response.status})`; + alert(message); + }); + } + }) + .catch(error => { + console.error('APIPanel: HTTP request failed:', error); + const message = window.i18n?.get('networkError') ?? '提交失败,请检查网络连接。'; + alert(message); + }) + .finally(() => { + // Always re-enable the button after fetch completes (success or error) + resetButton(); + }); } } -// 初始化API面板 -document.addEventListener('DOMContentLoaded', () => { - new APIPanel(); -}); +APIPanel.isSubmitting = false; +window.APIPanel = APIPanel; diff --git a/frontend/js/right-section/right-section.js b/frontend/js/right-section/right-section.js index 1875987ad2b9ccfe1f31d9a63de0b54d96e7ac26..ac2776ee1de66e0ca70f0cfc7d69c8ebe86f5ebf 100644 --- a/frontend/js/right-section/right-section.js +++ b/frontend/js/right-section/right-section.js @@ -1,9 +1,14 @@ // right-section.js class RightSection { constructor() { - this.currentTab = 'api-panel'; - this.apiPanel = new APIPanel(); - this.settingsPanel = new SettingsPanel(); + this.currentTab = 'status-panel'; // 默认激活的标签 + + // 初始化SettingsPanel,但不处理APIPanel + setTimeout(() => { + this.settingsPanel = new SettingsPanel(); + console.log('RightSection: SettingsPanel初始化完成'); + }, 200); + this.init(); } @@ -37,11 +42,19 @@ class RightSection { document.getElementById(targetPanelId).classList.add('active'); this.currentTab = targetPanelId; + + // 让专门的tab事件处理器去处理API面板初始化 + // 不在这里做任何API面板相关的操作 }); }); } } -document.addEventListener('DOMContentLoaded', () => { - const rightSection = new RightSection(); -}); +// 移除这个DOMContentLoaded事件监听器,避免重复初始化 +// 改为导出类供index.html使用 +window.RightSection = RightSection; + +// 删除以下代码,避免重复初始化 +// document.addEventListener('DOMContentLoaded', () => { +// const rightSection = new RightSection(); +// }); \ No newline at end of file diff --git a/index.html b/index.html index 1017653c2086acf9cc7d90195c33b8204be38526..ce0e0c55ed99d7a6471e3355ac4e313a49a5d129 100644 --- a/index.html +++ b/index.html @@ -74,8 +74,8 @@
- - + +
@@ -113,26 +113,77 @@

场景列表

-
+
+
+ + + + + + + + + + +
+
- - + diff --git a/modules/memory.py b/modules/memory.py index c5003f625b3f484e254cde227668b898b2928e76..118059d72ac30b3e5529acfe8dec70cbad266f6e 100644 --- a/modules/memory.py +++ b/modules/memory.py @@ -17,7 +17,7 @@ def build_role_agent_memory(type = "ga",**kwargs): embedding_name = kwargs["embedding_name"] db_name = kwargs["db_name"] language = kwargs["language"] if "language" in kwargs else "" - embedding_model = get_embedding_model(embedding_name, language=language) + embedding_model = get_embedding_model(embedding_name,language) index = faiss.IndexFlatL2(len(embedding_model.embed_query("hello world"))) vectorstore = FAISS( embedding_function=embedding_model, diff --git a/modules/world_agent.py b/modules/world_agent.py index 4e84a9ae9a89cc42fd95d62cb4cb8f40d3d8486c..b34bb8f8175c9592a9128b02179890dbda38e50e 100644 --- a/modules/world_agent.py +++ b/modules/world_agent.py @@ -51,7 +51,7 @@ class WorldAgent: def init_from_file(self, map_file_path: str, location_file_path: str, default_distance: int = 1): if map_file_path and os.path.exists(map_file_path): valid_locations = load_json_file(location_file_path) if "locations" not in load_json_file(location_file_path) else load_json_file(location_file_path)["locations"] - with open(map_file_path, mode='r') as file: + with open(map_file_path, mode='r',encoding="utf-8") as file: csv_reader = csv.reader(file) locations = next(csv_reader)[1:] for row in csv_reader: diff --git a/requirements.txt b/requirements.txt index cfeb806852ce9a2c3d0298548187159784fadf39..dd3c6805b82d78e967d7b9eb8e00fb5c97fd0378 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,5 +14,4 @@ uvicorn pillow faiss-cpu google-generativeai -huggingface_hub[cli] -uvicorn[standard] \ No newline at end of file +huggingface_hub[cli] \ No newline at end of file diff --git a/app.py b/server.py similarity index 80% rename from app.py rename to server.py index a82a7cdfea3709f3e107a9e9414a3b445842f893..c047223ac8c505f09a38db443ee5e58fd8902edb 100644 --- a/app.py +++ b/server.py @@ -1,4 +1,4 @@ -from fastapi import FastAPI, WebSocket, WebSocketDisconnect, HTTPException +from fastapi import FastAPI, WebSocket, WebSocketDisconnect, HTTPException, Request from fastapi.staticfiles import StaticFiles from fastapi.responses import HTMLResponse, FileResponse import uvicorn @@ -106,7 +106,7 @@ class ConnectionManager: async def get_next_message(self): """从BookWorld获取下一条消息""" message = self.bw.generate_next_message() - if not is_image(message["icon"]): + if not os.path.exists(message["icon"]) or not is_image(message["icon"]): message["icon"] = default_icon_path status = self.bw.get_current_status() return message,status @@ -116,7 +116,7 @@ manager = ConnectionManager() @app.get("/") async def get(): html_file = Path("index.html") - return HTMLResponse(html_file.read_text()) + return HTMLResponse(html_file.read_text(encoding="utf-8")) @app.get("/data/{full_path:path}") async def get_file(full_path: str): @@ -236,5 +236,49 @@ async def websocket_endpoint(websocket: WebSocket, client_id: str): finally: manager.disconnect(client_id) +@app.post("/api/save-config") +async def save_config(request: Request): + global config + global manager + try: + config_data = await request.json() + # 检查必要字段是否存在 + if 'provider' not in config_data or 'model' not in config_data or 'apiKey' not in config_data: + raise HTTPException(status_code=400, detail="缺少必要的字段") + + llm_provider = config_data['provider'] + model = config_data['model'] + api_key = config_data['apiKey'] + config['role_llm_name'] = model + config['world_llm_name'] = model + if 'openai' in llm_provider.lower(): + os.environ['OPENAI_API_KEY'] = api_key + elif 'anthropic' in llm_provider.lower(): + os.environ['ANTHROPIC_API_KEY'] = api_key + elif 'alibaba' in llm_provider.lower(): + os.environ['DASHSCOPE_API_KEY'] = api_key + elif 'openrouter' in llm_provider.lower(): + os.environ['OPENROUTER_API_KEY'] = api_key + + if "preset_path" in config and config["preset_path"] and os.path.exists(config["preset_path"]): + preset_path = config["preset_path"] + elif "genre" in config and config["genre"]: + genre = config["genre"] + preset_path = os.path.join(os.path.dirname(os.path.abspath(__file__)),f"./config/experiment_{genre}.json") + else: + raise ValueError("Please set the preset_path in `config.json`.") + manager.bw = BookWorld(preset_path = preset_path, + world_llm_name = config["world_llm_name"], + role_llm_name = config["world_llm_name"]) + manager.bw.set_generator(rounds = config["rounds"], + save_dir = config["save_dir"], + if_save = config["if_save"], + mode = config["mode"], + scene_mode = config["scene_mode"],) + return {"status": "success", "message": llm_provider + " 配置已保存"} + except Exception as e: + print(f"保存配置失败: {e}") + raise HTTPException(status_code=500, detail="保存配置失败") + if __name__ == "__main__": uvicorn.run("server:app", host="0.0.0.0", port=8000, reload=True)