ShiJiaoMing commited on
Commit
526a287
·
1 Parent(s): 893e50c
.env ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # OpenAI 接口代理地址,(可选配置)
2
+ OPENAI_API_BASE="https://api.openai-hk.com/v1"
3
+
4
+ # * 配置你的 OpenAI key(建议使用GPT-4正式账号,临时账号有每分钟3次请求限制)
5
+ OPENAI_API_KEY="hk-218wghmq4zcf4x42crjof0zlrpl4p22r96d44ola4wmyw92o"
6
+
7
+ # * 生成小说封面,具体可访问 https://platform.stability.ai/ 地址申请key
8
+ STABILITY_API_KEY="sk-iWYWb4PNfkIAv193eRuAoHBkk22FzkWzFSap62YydnUFKEcl"
9
+
10
+ # 配置你的 Claude2 API Key(可选配置)
11
+ ANTHROPIC_API_KEY="your-anthropic-api-key"
.env.example ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # OpenAI 接口代理地址,(可选配置)
2
+ OPENAI_API_BASE="your-openai-proxy-url"
3
+
4
+ # * 配置你的 OpenAI key(建议使用GPT-4正式账号,临时账号有每分钟3次请求限制)
5
+ OPENAI_API_KEY="your-openai-api-key"
6
+
7
+ # * 生成小说封面,具体可访问 https://platform.stability.ai/ 地址申请key
8
+ STABILITY_API_KEY="your-stablity-api-key"
9
+
10
+ # 配置你的 Claude2 API Key(可选配置)
11
+ ANTHROPIC_API_KEY="your-anthropic-api-key"
.gitignore ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .DS_Store
2
+ git.properties
3
+ *.userlibraries
4
+ *.log.20*
5
+ *.log
6
+ log.*
7
+ ### Intellij ###
8
+ # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm
9
+
10
+ *.iml
11
+ *.eml
12
+
13
+ ## Directory-based project format:
14
+ .idea/
15
+
16
+ ## File-based project format:
17
+ *.ipr
18
+ *.iws
19
+
20
+ ## Plugin-specific files:
21
+
22
+ # IntelliJ
23
+ /out/
24
+
25
+ # mpeltonen/sbt-idea plugin
26
+ .idea_modules/
27
+
28
+ # JIRA plugin
29
+ atlassian-ide-plugin.xml
30
+
31
+ # Python
32
+ __pycache__
33
+ *.pyc
34
+
35
+ # Crashlytics plugin (for Android Studio and IntelliJ)
36
+ com_crashlytics_export_strings.xml
37
+ crashlytics.properties
38
+ crashlytics-build.properties
39
+
40
+ # java build files
41
+ target
42
+ node_modules
43
+ gulpfile.js
44
+
45
+ # debug scripts.
46
+ deploy.sh
47
+ ftpsync.settings
48
+
49
+ *.swp
50
+ .DS_Store
51
+
52
+ # Xcode
53
+ build/*
54
+ *.pbxuser
55
+ !default.pbxuser
56
+ *.mode1v3
57
+ !default.mode1v3
58
+ *.mode2v3
59
+ !default.mode2v3
60
+ *.perspectivev3
61
+ !default.perspectivev3
62
+ *.xcworkspace
63
+ !default.xcworkspace
64
+ xcuserdata
65
+ profile
66
+ *.moved-aside
67
+ *.cer
68
+ *.p12
69
+ *.mobileprovision
70
+
71
+ # VSCode
72
+ .vscode
73
+ # AppCode
74
+ .idea
75
+
76
+ # Tern plugin
77
+ **/.tern-project
78
+
79
+ #emacs temp file
80
+ *~
81
+
82
+ # log files
83
+ batch_loader/hadoop_mapred*
84
+ batch_loader/kafka_consumer*
85
+
86
+ # Maven Repository
87
+ .repository
88
+
89
+ # UT generated files
90
+ swagger.json
91
+ swagger-model.json
92
+
93
+ /dependencies/
LICENSE ADDED
@@ -0,0 +1,201 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "[]"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright [yyyy] [name of copyright owner]
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
README.md CHANGED
@@ -1,13 +1,87 @@
1
- ---
2
- title: Test
3
- emoji: 📊
4
- colorFrom: green
5
- colorTo: red
6
- sdk: gradio
7
- sdk_version: 4.0.2
8
- app_file: app.py
9
- pinned: false
10
- license: apache-2.0
11
- ---
12
-
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <h1 align="center">● gpt-story-genius</h1>
2
+
3
+ <p align="center">
4
+ <br>
5
+ <b>StoryGenius:一款AI自动创作小说工具。</b><br><br>
6
+ 根据小说的提示词、写作风格和章节数量几分钟即可快速生成奇幻小说。并自动打包为电子书格式。<br>
7
+ </p>
8
+ <br>
9
+
10
+ ![poster](https://github.com/Crossme0809/frenzy_repo/blob/main/assets/story_genius.png)
11
+ <br>
12
+
13
+
14
+ **gpt-story-genius** 是一个自动创作小说的AI,它可以在几分钟内根据用户提供的初始提示和章节数生成一整本奇幻小说,并自动打包为电子书格式。
15
+ 该项目利用 **GPT-4**、**Stable Diffusion API** 和 **Anthropic API** 等一系列大模型调用组成的链来生成原创奇幻小说。<br>
16
+
17
+ 此外,它还可以根据这本书创建一个原创封面,并将整本作品一次性转换为PDF或电子书格式,并且`制作成本低廉,制作一本15章的小说仅需4美元成本`,并且该工具是开源的,可以免费使用。
18
+ <br>
19
+
20
+ ## 快速使用
21
+
22
+ 在 Google Colab 中,只需打开笔记本,添加 API 密钥,然后按顺序运行单元即可。 </br></br>
23
+ [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/Crossme0809/frenzyTechAI/blob/main/gpt-author/gpt_author_v2.ipynb)
24
+
25
+ 在笔记本的最后一个单元格中,您可以自定义小说的提示和章节数。例如:
26
+
27
+ ```python
28
+ prompt = "一个被遗忘的小岛,上面有一座古老的灯塔。当灯塔亮起时,岛上的生物就会发生奇异的变化。"
29
+ num_chapters = 10
30
+ writing_style = "紧张刺激,类似于青少年恐怖小说。有很多对话和内心独白"
31
+ novel, title, chapters, chapter_titles = write_fantasy_novel(prompt, num_chapters, writing_style)
32
+ ```
33
+
34
+ 这将根据给定的提示生成一本 10 章的小说。**注意——少于 7 章的提示往往会导致问题。**
35
+
36
+ ## 本地部署
37
+
38
+ - 下载项目代码
39
+ ```bash
40
+ git clone https://github.com/Crossme0809/gpt-story-genius.git
41
+ ```
42
+
43
+ - 复制项目配置文件
44
+ ```bash
45
+ cp .env.example .env
46
+ ```
47
+
48
+ - 配置GPT Key
49
+ ```bash
50
+ # OpenAI 接口代理地址,(可选配置)
51
+ OPENAI_API_BASE="your-openai-proxy-url"
52
+
53
+ # * 配置你的 OpenAI key(建议使用GPT-4正式账号,临时账号有每分钟3次请求限制)
54
+ OPENAI_API_KEY="your-openai-api-key"
55
+
56
+ # * 生成小说封面,具体可访问 https://platform.stability.ai/ 地址申请key
57
+ STABILITY_API_KEY="your-stablity-api-key"
58
+ sk-iWYWb4PNfkIAv193eRuAoHBkk22FzkWzFSap62YydnUFKEcl
59
+ # 配置你的 Claude2 API Key(可选配置)
60
+ ANTHROPIC_API_KEY="your-anthropic-api-key"
61
+ ```
62
+
63
+ - 安装项目依赖
64
+ ```bash
65
+ pip install -r requirements.txt
66
+ ```
67
+
68
+ - 启动项目
69
+ ```bash
70
+ gradio app.py
71
+ ```
72
+
73
+ - 后台进程启动
74
+ ```bash
75
+ nohup gradio app.py > log.txt 2>&1 &
76
+ ```
77
+
78
+ 启动成功后,访问8000端口即可打开 **StoryGenius** 项目主页,如需要修改端口,只需要编辑 `run.py` 最后一行中的 `server_port` 即可。
79
+ <br><br>
80
+ <p>生成完的小说Epub文可以下载其文件并在 https://www.fviewer.com/view-epub 上查看,或将其安装在 Kindle 等上。(Mac上直接预览)</p>
81
+
82
+ ![poster](https://github.com/Crossme0809/frenzy_repo/blob/main/assets/novel_epub.png)
83
+
84
+ ## 特别说明
85
+
86
+ 本项目基于[gpt-author](https://github.com/mshumer/gpt-author)开发,由[@mattshumer_](https://twitter.com/mattshumer_)创建。
87
+
app.py ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # coding: utf8
2
+ import gradio as gr
3
+ from write_story import write_fantasy_novel
4
+ from author import create_cover_image
5
+ from author import create_epub
6
+ from config import anthropic_api_key
7
+
8
+ if anthropic_api_key and anthropic_api_key != "your-anthropic-api-key":
9
+ print(">>>>>> ========= use anthropic ========")
10
+ claude_true = True
11
+ else:
12
+ print(">>>>>> ========= claude_true = False ========")
13
+ claude_true = False
14
+
15
+
16
+ def generate_novel(prompt, num_chapters, writing_style, model_name):
17
+ # 调用GPT和Claude API,生成小说结果
18
+ # prompt = "A kingdom hidden deep in the forest, where every tree is a portal to another world."
19
+ # num_chapters = 2
20
+ # writing_style = "Clear and easily understandable, similar to a young adult novel. Lots of dialogue."
21
+ # model_name = "gpt-3.5-turbo-16k"
22
+ if not prompt or not writing_style:
23
+ raise gr.Error("提示词和写作风格是必填项")
24
+ if num_chapters < 1:
25
+ raise gr.Error("章节数必须大于等于1")
26
+
27
+ num_chapters = int(num_chapters)
28
+ novel, title, chapters, chapter_titles = write_fantasy_novel(prompt,
29
+ num_chapters, writing_style, claude_true, model_name)
30
+
31
+ # 用chapter_titles中的正文取代章节说明
32
+ for i, chapter in enumerate(chapters):
33
+ chapter_number_and_title = list(chapter_titles[i].keys())[0]
34
+ chapter_titles[i] = {chapter_number_and_title: chapter}
35
+
36
+ # 生成小说的封面
37
+ image_url = create_cover_image(str(chapter_titles))
38
+ print(f"Image URL: {image_url}")
39
+
40
+ # 生成小说 EPUB 文件
41
+ file_url = create_epub(title, 'AI', chapter_titles, image_url)
42
+ print(f"Novel URL: {file_url}")
43
+
44
+ # novel, file_path = write_fantasy_novel(prompt, num_chapters, writing_style)
45
+ return { "image_url": image_url, "file_url": file_url }
46
+
47
+
48
+ def generate_output(prompt, num_chapters, writing_style, model_name):
49
+ try:
50
+ output = generate_novel(prompt, num_chapters, writing_style, model_name)
51
+ print(output)
52
+ return (output["image_url"], output["file_url"])
53
+ except Exception as e:
54
+ raise gr.Error({str(e)})
55
+ return f"An error occurred: {str(e)}"
56
+
57
+
58
+ inputs = [
59
+ gr.Textbox(value="一个被遗忘的小岛,上面有一座古老的灯塔。当灯塔亮起时,岛上的生物就会发生奇异的变化。", lines=2, placeholder="Usage:一个被遗忘的小岛,上面有一座古老的灯塔。当灯塔亮起时,岛上的生物就会发生奇异的变化。", label="小说提示词"),
60
+ gr.Number(value=1, label="小说章节数"),
61
+ gr.Textbox(value="紧张刺激,类似于青少年恐怖小说。有很多对话和内心独白", lines=2, placeholder="Usage:紧张刺激,类似于青少年恐怖小说。有很多对话和内心独白", label="AI写作风格"),
62
+ gr.Dropdown(["gpt-3.5-turbo-16k", "gpt-3.5-turbo", "gpt-4", "gpt-4-32k"], label="选择GPT模型", value="gpt-3.5-turbo")
63
+ ]
64
+
65
+
66
+ outputs = [
67
+ gr.Image(label="封面图片", width=1028, height=300),
68
+ gr.File(label="EPUB文件")
69
+ ]
70
+
71
+ title = "StoryGenius:一款AI自动创作小说工具"
72
+ description = "根据小说的提示词、写作风格和章节数量几分钟即可快速生成奇幻小说。并自动打包为电子书格式。"
73
+
74
+
75
+ iface = gr.Interface(fn=generate_output, inputs=inputs, outputs=outputs, title=title, description=description)
76
+ iface.launch(server_name="127.0.0.1", server_port=8000)
author.py ADDED
@@ -0,0 +1,145 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # coding: utf8
2
+ from ebooklib import epub
3
+ import base64
4
+ import os
5
+ import requests
6
+ from config import completion_with_backoff
7
+ from config import stability_api_key
8
+
9
+
10
+ def generate_cover_prompt(plot):
11
+ response = completion_with_backoff(
12
+ model="gpt-3.5-turbo-16k",
13
+ messages=[
14
+ {"role": "system",
15
+ "content": "You are a creative assistant that writes a spec for the cover art of a book, based on the book's plot."},
16
+ {"role": "user",
17
+ "content": f"Plot: {plot}\n\n--\n\nDescribe the cover we should create, based on the plot. This should be two sentences long, maximum."}
18
+ ]
19
+ )
20
+ return response['choices'][0]['message']['content']
21
+
22
+
23
+ def create_cover_image(plot):
24
+ plot = str(generate_cover_prompt(plot))
25
+
26
+ engine_id = "stable-diffusion-xl-beta-v2-2-2"
27
+ api_host = os.getenv('API_HOST', 'https://api.stability.ai')
28
+ api_key = stability_api_key
29
+
30
+ if api_key is None:
31
+ raise Exception("Missing Stability API key.")
32
+
33
+ response = requests.post(
34
+ f"{api_host}/v1/generation/{engine_id}/text-to-image",
35
+ headers={
36
+ "Content-Type": "application/json",
37
+ "Accept": "application/json",
38
+ "Authorization": f"Bearer {api_key}"
39
+ },
40
+ json={
41
+ "text_prompts": [
42
+ {
43
+ "text": plot
44
+ }
45
+ ],
46
+ "cfg_scale": 7,
47
+ "clip_guidance_preset": "FAST_BLUE",
48
+ "height": 768,
49
+ "width": 512,
50
+ "samples": 1,
51
+ "steps": 30,
52
+ },
53
+ )
54
+
55
+ if response.status_code != 200:
56
+ raise Exception("Non-200 response: " + str(response.text))
57
+
58
+ data = response.json()
59
+
60
+ # 检查目录是否存在,如果不存在则创建
61
+ directory = "./cover/"
62
+ if not os.path.exists(directory):
63
+ os.makedirs(directory)
64
+
65
+ for i, image in enumerate(data["artifacts"]):
66
+ image_bytes = base64.b64decode(image["base64"])
67
+ file_path = f"./cover/cover_{i}.png" # 修改为您想要的文件路径
68
+ with open(file_path, "wb") as f:
69
+ f.write(image_bytes)
70
+
71
+ return file_path
72
+
73
+
74
+ def create_epub(title, author, chapters, cover_image_path='cover.png'):
75
+ book = epub.EpubBook()
76
+
77
+ # Set metadata
78
+ book.set_identifier('id123456')
79
+ book.set_title(title)
80
+ book.set_language('zh-cn')
81
+ book.add_author(author)
82
+
83
+ # Add cover image
84
+ with open(cover_image_path, 'rb') as cover_file:
85
+ cover_image = cover_file.read()
86
+ book.set_cover('cover.png', cover_image)
87
+
88
+ # Create chapters and add them to the book
89
+ epub_chapters = []
90
+ for i, chapter_dict in enumerate(chapters):
91
+ full_chapter_title = list(chapter_dict.keys())[0]
92
+ chapter_content = list(chapter_dict.values())[0]
93
+ if ' - ' in full_chapter_title:
94
+ chapter_title = full_chapter_title.split(' - ')[1]
95
+ else:
96
+ chapter_title = full_chapter_title
97
+
98
+ chapter_file_name = f'chapter_{i + 1}.xhtml'
99
+ epub_chapter = epub.EpubHtml(title=chapter_title, file_name=chapter_file_name, lang='zh-cn')
100
+
101
+ # Add paragraph breaks
102
+ formatted_content = ''.join(
103
+ f'<p>{paragraph.strip()}</p>' for paragraph in chapter_content.split('\n') if paragraph.strip())
104
+
105
+ epub_chapter.content = f'<h1>{chapter_title}</h1>{formatted_content}'
106
+ book.add_item(epub_chapter)
107
+ epub_chapters.append(epub_chapter)
108
+
109
+ # Define Table of Contents
110
+ book.toc = (epub_chapters)
111
+
112
+ # Add default NCX and Nav files
113
+ book.add_item(epub.EpubNcx())
114
+ book.add_item(epub.EpubNav())
115
+
116
+ # Define CSS style
117
+ style = '''
118
+ @namespace epub "http://www.idpf.org/2007/ops";
119
+ body {
120
+ font-family: Cambria, Liberation Serif, serif;
121
+ }
122
+ h1 {
123
+ text-align: left;
124
+ text-transform: uppercase;
125
+ font-weight: 200;
126
+ }
127
+ '''
128
+
129
+ # Add CSS file
130
+ nav_css = epub.EpubItem(uid="style_nav", file_name="style/nav.css", media_type="text/css", content=style)
131
+ book.add_item(nav_css)
132
+
133
+ # Create spine
134
+ book.spine = ['nav'] + epub_chapters
135
+
136
+ # 检查目录是否存在,如果不存在则创建
137
+ directory = "./epub/"
138
+ if not os.path.exists(directory):
139
+ os.makedirs(directory)
140
+
141
+ # 保存 EPUB 文件
142
+ file_path = f"./epub/{title}.epub" # 修改为您想要的文件路径
143
+ epub.write_epub(file_path, book)
144
+
145
+ return file_path
config.py ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import openai
2
+ import os
3
+ import uuid
4
+ from dotenv import load_dotenv
5
+
6
+ from tenacity import (
7
+ retry,
8
+ stop_after_attempt,
9
+ wait_random_exponential,
10
+ ) # for exponential backoff
11
+
12
+
13
+ # 加载.env文件中的环境变量
14
+ load_dotenv()
15
+
16
+ llm_model_name = "gpt-3.5-turbo-16k"
17
+
18
+
19
+ openai.api_key = os.getenv("OPENAI_API_KEY") # get it at https://platform.openai.com/
20
+ if os.getenv("OPENAI_API_BASE"):
21
+ openai.api_base = os.getenv("OPENAI_API_BASE")
22
+ stability_api_key = os.getenv("STABILITY_API_KEY") # get it at https://beta.dreamstudio.ai/
23
+ anthropic_api_key = os.getenv("ANTHROPIC_API_KEY") # optional, if you don't add it, keep it as "YOUR ANTHROPIC API KEY"
24
+
25
+ print("============ config info ===============\n")
26
+ print("OPENAI_API_KEY:" + openai.api_key +"\n")
27
+ print("OPENAI_API_BASE:" + openai.api_base +"\n")
28
+ print("STABILITY_API_KEY:" + str(stability_api_key) +"\n")
29
+ print("ANTHROPIC_API_KEY:" + str(anthropic_api_key) +"\n")
30
+
31
+ @retry(wait=wait_random_exponential(min=1, max=60), stop=stop_after_attempt(6))
32
+ def completion_with_backoff(**kwargs):
33
+ return openai.ChatCompletion.create(**kwargs)
34
+
35
+
36
+ # 生成32位唯一的uuid
37
+ def generate_uuid():
38
+ # 生成UUID
39
+ id = uuid.uuid4().hex
40
+ return id
41
+
42
+
43
+ # 保存小说每章节的内容
44
+ def save_novel_chapter(novel_id, chapter_index, file_name, file_content):
45
+ # 创建章节文件目录
46
+ chapter_folder = os.path.join(os.getcwd(), f"story/{novel_id}/chapter_{chapter_index + 1}")
47
+ if not os.path.exists(chapter_folder):
48
+ os.makedirs(chapter_folder)
49
+
50
+ # 写入章节内容到文件
51
+ file_path = os.path.join(chapter_folder, f"{file_name}.txt")
52
+ with open(file_path, "w") as file:
53
+ file.write(file_content)
cover/folder ADDED
File without changes
epub/folder ADDED
File without changes
flagged/log.csv ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ 小说提示词,小说章节数,AI写作风格,选择GPT模型,封面图片,EPUB文件,flag,username,timestamp
2
+ 萍儿是一个职场新人,她和自己的好闺蜜佩佩是同事,她们之间无话不谈,但在一次职位竞选中被自己的闺蜜陷害,萍儿厌恶了职场的勾心斗角,有一天从梦中醒来发现自己从现代穿越到了唐朝,凭借自己的能力从庶女一路逆袭成为太后,在逆袭的过程中受尽了各种暗算,有一次遭人陷害,偶然遇到了一个大明王爷,大明王爷使出计谋出手相救,最终萍儿成为皇后,大明成为皇上。梦醒了,但是萍儿从大明身上学到很多谋略,最终在现实的职场中成为一名精英,叱诧风云。,7,紧张刺激,小说主要讲述现代的一名职场新人穿越到古代变成庶女后的故事,类似《知否知否应是绿肥红瘦》小说。有很多对话和内心独白,描述主人公萍儿坎坷的逆袭之路。,gpt-4,,,,,2023-10-31 16:36:04.485217
keyTest.py ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import requests
2
+ import json
3
+
4
+ url = "https://api.openai-hk.com/v1/chat/completions"
5
+
6
+ headers = {
7
+ "Content-Type": "application/json",
8
+ "Authorization": "Bearer hk-218wghmq4zcf4x42crjof0zlrpl4p22r96d44ola4wmyw92o"
9
+ }
10
+
11
+ data = {
12
+ "max_tokens": 1200,
13
+ "model": "gpt-3.5-turbo",
14
+ "temperature": 0.8,
15
+ "top_p": 1,
16
+ "presence_penalty": 1,
17
+ "messages": [
18
+ {
19
+ "role": "system",
20
+ "content": "You are ChatGPT, a large language model trained by OpenAI. Answer as concisely as possible."
21
+ },
22
+ {
23
+ "role": "user",
24
+ "content": "你是chatGPT多少?"
25
+ }
26
+ ]
27
+ }
28
+
29
+ response = requests.post(url, headers=headers, data=json.dumps(data).encode('utf-8') )
30
+ result = response.content.decode("utf-8")
31
+
32
+ print(result)
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ openai==0.28.0
2
+ EbookLib==0.18
3
+ anthropic==0.3.11
4
+ python-dotenv==1.0.0
5
+ gradio==3.42.0
6
+ tenacity==8.2.3
simpleTest.py ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # coding: utf8
2
+ import ast
3
+ import unicodedata
4
+
5
+ def fullwidth_to_halfwidth(text):
6
+ result = ''
7
+ for char in text:
8
+ char_width = unicodedata.east_asian_width(char)
9
+ if char_width == 'F' or char_width == 'W':
10
+ # 全角字符的宽度标记为 'F' 或 'W'
11
+ halfwidth_char = unicodedata.normalize('NFKC', char)
12
+ # 使用 NFKC 形式将全角字符转换为半角字符
13
+ result += halfwidth_char
14
+ else:
15
+ result += char
16
+ return result
17
+
18
+ def strQ2B(ustring):
19
+ """把字符串全角转半角"""
20
+ ss = []
21
+ for s in ustring:
22
+ rstring = ""
23
+ for uchar in s:
24
+ inside_code = ord(uchar)
25
+ if inside_code == 12288: # 全角空格直接转换
26
+ inside_code = 32
27
+ elif (inside_code >= 65281 and inside_code <= 65374): # 全角字符(除空格)根据关系转化
28
+ inside_code -= 65248
29
+ rstring += chr(inside_code)
30
+ ss.append(rstring)
31
+ return ''.join(ss)
32
+
33
+ name = "[{\"Chapter 1 - 骗局的蔓延\":\"平儿和佩佩是两名才华横溢但缺乏经验的年轻职员,她们的友谊因为争夺晋升机会而面临考验。佩佩因嫉妒和欲望而背叛了平儿,导致平儿不仅失去了晋升的机会,也失去了对办公室政治的信任。然而,平儿并不是一个轻易被击倒的人。她决心揭开佩佩的阴谋并证明自己的价值。\"},{\"Chapter 2 - 时间之门\":\"平儿被背叛的伤害使她陷入沉睡,当她醒来时,她发现自己来到了一个神秘的花园。花园里有一扇通往不同时空的门,平儿勇敢地穿越了时空。她来到了一个名为“时光之门”的地方,那里是通往古代的大门。平儿在这个充满危险和诱惑的新世界中,发现自己拥有操控时间的特殊能力。\"},{\"Chapter 3 - 宫廷的险棋\":\"平儿发现自己来到了唐朝的宫廷中,成为了一个卑微的妃子。尽管身份低微,但平儿依靠她的聪明才智和前世记忆,巧妙地在宫廷中游刃有余。她与朝臣和其他妃子之间的斗争越来越激烈,而平儿则不断改变和正义的转变引起了皇帝的注意。平儿必须面对宫廷的阴谋和挑战,才能够达到她的目标。\"},{\"Chapter 4 - 诡谲的盟约\":\"当平儿以为自己已经确立了皇后地位时,一个强大的阴谋威胁到了她的统治。在最黑暗的时刻,一个神秘的明朝王子以宫廷学者的身份,显示出他的真正身份。平儿与这位王子结盟,共同对抗阴谋,保护自己的地位。他们的智慧与战略才能,将会决定唐朝的命运和平儿的未来。\"},{\"Chapter 5 - 觉醒与蓄势\":\"当平儿从梦中醒来时,她带着前世的智慧和力量回到了现代世界。她发现自己变成了一位职场精英,凭借她对历史和背叛的了解,她在职业生涯中崭露头角,并留下了持久的遗产。平儿开始运用她在古代所学到的技巧,在现代社会中迎接新的挑战和冲突。\"},{\"Chapter 6 - 背叛与复仇\":\"《时间之门:从背叛到复仇的传奇》是一个充满惊险和冒险的奇幻故事。它探索了友谊、背叛和自我成长的主题。平儿在古代和现代两个世界中的经历,让她成为一个强大且无法被忽视的力量。故事深入探讨了权力、勇气和复仇的概念,塑造了一个令人着迷的女主角与命运抗争的故事。\"},{\"Chapter 7 - 荣耀与遗留\":\"为了让故事更加吸引人,我增加了对平儿和佩佩关系的深入探索。这突出了友谊的力量和背叛的伤害。在唐朝宫廷中,我加入了更多展示平儿成长和发展的情节,强调她在危险的宫廷游戏中的独到能力。我还引入了更多复杂的宫廷角色,为平儿提供更多机遇和挑战,以推动她的角色发展。通过增加阴谋情节和紧张气氛,增加了读者的悬念和猜测。最后,我更加流畅地将古代与现代世界联系起来,以展现平儿在两个环境中的相似与联系。\"}]"
34
+
35
+ name1 = "[{\"Chapter 6 - 背叛与复仇\": \"《时间之门:从背叛到复仇的传奇》是一个充满惊险和冒险的奇幻故事它探索了友谊背叛和自我成长的主题平儿在古代和现代两个世界中的经历,让她成为一个强大且无法被忽视的力量故事深入探讨了权力勇气和复仇的概念,塑造了一个令人着迷的女主角与命运抗争的故事\"},{\"Chapter 7 - 荣耀与遗留\": \"为了让故事更加吸引人,我增加了对平儿和佩佩关系的深入探索。这突出了友谊的力量和背叛的伤害。在唐朝宫廷中,我加入了更多展示平儿成长和发展的情节,强调她在危险的宫廷游戏中的独到能力。我还引入了更多复杂的宫廷角色,为平儿提供更多机遇和挑战,以推动她的角色发展。通过增加阴谋情节和紧张气氛,增加了读者的悬念和猜测。最后,我更加流畅地将古代与现代世界��系起来,以展现平儿在两个环境中的相似与联系。\"}]"
36
+
37
+ print(f'【storyline】: {name}\n\n')
38
+
39
+ chapter_titles = ast.literal_eval(name)
40
+ print(f'【chapter_titles】: {chapter_titles}\n\n')
41
+
42
+
story/0e5d9be3d7f84ac28961947d5f3f942f/chapter_1/Chapter 1 - The Betrayal.txt ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 第一章 - 背叛
2
+
3
+ 年轻的企业精英平儿,才华出众且野心勃勃,卷入了与她信任的朋友佩佩之间的恶意办公室竞争中。在无情商业世界的背叛和失望之中,平儿一觉醒来,发现自己穿越到唐朝成为一个卑微的宫女身体寄生的灵魂。
4
+
5
+ 一片黑暗中,平儿缓缓睁开眼睛。疲惫的身体透露出一种淡淡的不安,仿佛来自另一个时空。纷乱的思绪渐渐清晰起来,她发现自己置身于华丽的宫殿之中,房间充满着沉重的气息。她的头脑里回想起了刚才的一幕:佩佩对她的背叛,朋友之间的狼狈一面。
6
+
7
+ "怎么会这样?我的未来本该美好。" 平儿默默想道。她曾以为在商业世界中,能够发展自己的梦想,与佩佩一起开创一片新的天地。然而,现实却残酷无情。平儿被背叛和失望深深地伤害了。这个世界让她感到疲惫,决绝地迫使着她面对不可避免的挫折。
8
+
9
+ 无尽的黑暗中,她的脑海里依稀浮现出和佩佩密切相关的画面,商业报表和办公室的暗斗。佩佩的一句顺手牵羊的话让平儿崩溃了。如今,她在这个古代宫廷中,变成了一个庶女的灵魂寄生体,命运嘲弄着她的一切抱负和努力。
10
+
11
+ 躺在床上,把手掌紧握成拳头,平儿咬紧牙关。她发誓,无论在哪个时代,她都要找回自己失去的尊严和力量。她深呼吸,舒展凝固的身体,让思绪回到当下。
12
+
13
+ "我是平儿,一个坚定追求目标的女人。这个时代或许不理解我的雄心,但我要利用我的才智,战胜这个陌生的世界,重铸我的命运!" 平儿发誓着。
14
+
15
+ 慢慢地,平儿挣脱沉重的惆怅,不再让失去束缚住她的灵魂。她站起身,目光坚毅地扫视宅子的四周。她知道自己需要找到适应这个新世界的方法。
16
+
17
+ 唐朝的宫廷毫不留情,她必须敏锐地观察并学会扭转局势。平儿决定利用她无与伦比的智慧和机智,赢得这个挑战。她的目标是晋升,成为这个宫廷中引起有影响力人物注意的存在。
18
+
19
+ 未来的道路虽然漫长而充满荆棘,平儿知道她已有了解决困难的内在力量。她要站稳脚跟,融入这个新的世界,同时保护自己免受险恶阴谋的伤害。这意味着她必须学会心机和耐心,提高自己的智慧,以此超越那些企图阻止她崛起的人。
20
+
21
+ 平儿看向窗外,安慰自己的决心。一场新的冒险即将展开,她将不再是一个被背叛的受害者,而是一名坚强而富有智慧的女性,为重塑她的命运而奋斗。她知道,只有这样,她才能回到现代世界,并彻底改变自己的未来。
22
+
23
+ 在这个陌生的古代天地中,平儿决定成为那个无人忽略的存在,无论付出多大代价。她相信背叛与失望只是人生中的一小部分,她一定能赢得她应得的荣光和自由。
24
+
25
+ 平儿开始紧锁眉头,考
story/0e5d9be3d7f84ac28961947d5f3f942f/chapter_2/Chapter 2 - The Rise.txt ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ 第二章 - 崛起
2
+
3
+ 为了改变自己的命运,平儿运用她非凡的智慧和机智,逐渐适应了险恶的宫廷环境。她坚定决心地晋升,凭借着她的策略头脑吸引起朝廷中有影响力的人物的注意。
4
+
5
+ 平儿逐渐适应了这个陌生而危险的宫廷世界。她深知在这里晋升是一条漫长而需要精明策略的道路。她要抓住每个机会,争取每一次相见的机会。她不断观察并分析各个人物之间的互动,找到他们的弱点和喜好。
6
+
7
+ 平儿用她的聪慧和洞察力,悄悄地积累起宫廷中的力量。她学会了如何在政治斗争的泥潭中游刃有余地舞动。她发现自己的每一步都引起了朝廷中有影响力的人物的注意。
8
+
9
+ 她以巧妙的方式与那些有势力的人建立起联系,并展示出她的价值。她通过认真执着的工作,成为皇帝赏识的宫
story/13927aa500284de3a924b54fbb3cd97c/chapter_1/第一章 - 幻影的脱困.txt ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 第一章 - 幻影的脱困
2
+
3
+ 珍妮佩恩是一位才智过人的年轻女士,在一个充满压力和竞争的企业中辛勤工作。每天,她穿过一栋巨大而冷酷的办公楼,度过无尽的工作时间,承受着高压和残酷的竞争。然而,在她看似平凡的外表下,隐藏着一颗渴望冒险和探索的梦想家的心。
4
+
5
+ 这一天,珍妮佩恩按部就班地完成了一连串繁重的任务,却感到自己又一次沉浸在绝望和无望之中。她渴望一种解脱,渴望逃离现实的枷锁,再次找到那个能给她带来兴奋和激情的地方。
6
+
7
+ 她双手颤抖地打开了她钟爱的虚拟现实游戏《幻影》。眼前展现出了一个神奇而又充满魔法的世界。这个世界充满了各种奇异的生物、令人叹为观止的景色和潜藏着无尽秘密的地方。这是一个让珍妮佩恩可以成为真正无所不能的英雄,并与其他游戏玩家建立联系的地方。
8
+
9
+ 当她穿越游戏门户进入《幻影》的时刻,她完全沉浸在了奇幻世界的美妙之中。她的外貌转变,她变成了一名崭露头角的战士,凭借卓越的技巧和无尽的勇气赢得了众人的尊敬和仰慕。
10
+
11
+ 然而,她并不知道,《幻影》隐藏着一个永久改变她生活的魔法秘密。在她击败怪物、探索未知领域的过程中,一股神秘的力量渗透进她的身体,赋予她非凡的能力。她的眼睛闪烁着星星般的光芒,她的手指蕴含着无尽的魔力。
12
+
13
+ 然而,幻影的影响力并不仅限于游戏。当珍妮佩恩回到现实世界时,她开始在日常生活中出现奇怪的现象。她的手指能释放出闪电,她的眼睛能看到隐藏的真相。这让她震惊不已,也让她感到困惑和恐慌。
14
+
15
+ 虚拟和现实的界限开始模糊起来,珍妮佩恩陷入了困惑和恐慌之中。她不知道自己身处何处,也不知道如何控制这些力量。于是,她开始探寻《幻影》背后的秘密,希望找到答案,同时追求自己内心的平静与和谐。
16
+
17
+ 通过在在线游戏社区的交流和探寻,《幻影》揭示了一个隐藏的游戏玩家社群。这群玩家发现了连接现实世界和虚拟世界的门户。尽管他们来自不同的背景和经历,但他们团结一致,展开任务,保护两个世界免受即将降临的邪恶混乱的威胁。
18
+
19
+ 对珍妮佩恩来说,《幻影》不再只是一款游戏,它变成了她的命运。她必须面对自己内心的恐惧和挑战,接受自己身为领导者和英雄的真正潜力。
20
+
21
+ 《幻影的脱困》讲述了一个关于勇气、发现真正自我的旅程。在珍妮佩恩的冒险中,她不仅找到了自己,也找到了一群真挚的盟友和隐藏的力量。这个故事将带你进入一个充满魔法、想象力和意想不到的转变的世界,唤醒你内在的勇气和梦想。
22
+
23
+ (注:通过这个章节,预示着珍妮佩恩即将进入她作为主人公的冒险旅程,将她的命运与《幻影》紧密联系在一起。章节的语气紧张刺激,小说主要讲述现代的一名职场新人穿越到古代变成庶女后的故事,类似《知否知否应是绿肥红瘦》小说。有很多对话和内心独白,描述主人公萍儿坎坷的逆袭之路。)
write_story.py ADDED
@@ -0,0 +1,385 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # coding: utf8
2
+ import random
3
+ import os
4
+ import requests
5
+ import ast
6
+ import time
7
+ from anthropic import Anthropic
8
+ from config import anthropic_api_key
9
+ from config import llm_model_name
10
+ from config import completion_with_backoff
11
+ from config import save_novel_chapter
12
+ from config import generate_uuid
13
+
14
+ def print_step_costs(response, model):
15
+ input = response['usage']['prompt_tokens']
16
+ output = response['usage']['completion_tokens']
17
+
18
+ if model == "gpt-4" or model == "gpt-4":
19
+ input_per_token = 0.00003
20
+ output_per_token = 0.00006
21
+ if model == "gpt-3.5-turbo-16k":
22
+ input_per_token = 0.000003
23
+ output_per_token = 0.000004
24
+ if model == "gpt-4-32k" or model == "gpt-4-32k":
25
+ input_per_token = 0.00006
26
+ output_per_token = 0.00012
27
+ if model == "gpt-3.5-turbo" or model == "gpt-3.5-turbo":
28
+ input_per_token = 0.0000015
29
+ output_per_token = 0.000002
30
+ if model == "claude-2":
31
+ input_per_token = 0.00001102
32
+ output_per_token = 0.00003268
33
+
34
+ input_cost = int(input) * input_per_token
35
+ output_cost = int(output) * output_per_token
36
+
37
+ total_cost = input_cost + output_cost
38
+ print('Step Cost (OpenAI):', total_cost)
39
+
40
+
41
+ def print_step_costs_anthropic(prompt, response):
42
+ client = Anthropic()
43
+ in_tokens = client.count_tokens(prompt)
44
+ out_tokens = client.count_tokens(response)
45
+
46
+ input_cost = 0.00001102 * in_tokens
47
+ output_cost = 0.00003268 * out_tokens
48
+
49
+ total_cost = input_cost + output_cost
50
+ print('Step Cost (Anthropic):', total_cost)
51
+
52
+
53
+ def generate_plots(prompt):
54
+ response = completion_with_backoff(
55
+ model=llm_model_name,
56
+ messages=[
57
+ {"role": "system", "content": "You are a creative assistant that generates engaging fantasy novel plots."},
58
+ {"role": "user", "content": f"Generate 10 fantasy novel plots based on this prompt: {prompt}"}
59
+ ]
60
+ )
61
+
62
+ print_step_costs(response, llm_model_name)
63
+
64
+ return response['choices'][0]['message']['content'].split('\n')
65
+
66
+
67
+ def select_most_engaging(plots):
68
+ response = completion_with_backoff(
69
+ model=llm_model_name,
70
+ messages=[
71
+ {"role": "system", "content": "You are an expert in writing fantastic fantasy novel plots."},
72
+ {"role": "user",
73
+ "content": f"Here are a number of possible plots for a new novel: "
74
+ f"{plots}\n\n--\n\nNow, write the final plot that we will go with. "
75
+ f"It can be one of these, a mix of the best elements of multiple, "
76
+ f"or something completely new and better. "
77
+ f"The most important thing is the plot should be fantastic, unique, and engaging."}
78
+ ]
79
+ )
80
+
81
+ print_step_costs(response, llm_model_name)
82
+
83
+ return response['choices'][0]['message']['content']
84
+
85
+
86
+ def improve_plot(plot):
87
+ response = completion_with_backoff(
88
+ model=llm_model_name,
89
+ messages=[
90
+ {"role": "system", "content": "You are an expert in improving and refining story plots."},
91
+ {"role": "user", "content": f"Improve this plot: {plot}"}
92
+ ]
93
+ )
94
+
95
+ print_step_costs(response, llm_model_name)
96
+
97
+ return response['choices'][0]['message']['content']
98
+
99
+
100
+ def get_title(plot):
101
+ response = completion_with_backoff(
102
+ model=llm_model_name,
103
+ messages=[
104
+ {"role": "system", "content": "You are an expert writer."},
105
+ {"role": "user",
106
+ "content": f"Here is the plot: {plot}\n\nWhat is the title of this book? "
107
+ f"Just respond with the title, do nothing else. Please respond in Chinese."}
108
+ ]
109
+ )
110
+
111
+ print_step_costs(response, llm_model_name)
112
+
113
+ return response['choices'][0]['message']['content']
114
+
115
+
116
+ def write_first_chapter(plot, first_chapter_title, writing_style, claude=True):
117
+ if claude:
118
+ url = "https://api.anthropic.com/v1/complete"
119
+
120
+ headers = {
121
+ "anthropic-version": "2023-06-01",
122
+ "content-type": "application/json",
123
+ "x-api-key": anthropic_api_key,
124
+ }
125
+
126
+ prompt_one = f"\n\nHuman: You are a world-class fantasy writer. " \
127
+ f"I will give you the title of a novel, a high-level plot to follow, " \
128
+ f"and a desired writing style to use. " \
129
+ f"From the title, plot, and writing style, write the first chapter of the novel. " \
130
+ f"Make it incredibly unique, engaging, and well-written. " \
131
+ f"Start it off with a bang, and include dialogue. " \
132
+ f"Include only the chapter text, and no surrounding explanations or text. " \
133
+ f"Do you understand?\n\nAssistant: Yes, I understand. " \
134
+ f"Please provide the title, plot, and writing style, " \
135
+ f"and I will write a fantastic opening chapter with dialogue that will hook the reader." \
136
+ f"\n\nHuman: Here is the high-level plot to follow: {plot}" \
137
+ f"\n\nThe title of the novel is: `{first_chapter_title}`.\n\n" \
138
+ f"Here is a description of the writing style you should use: `{writing_style}`" \
139
+ f"\n\nWrite the first chapter please!\n\nAssistant: " \
140
+ f"Okay, I've got a really exciting first chapter for you. " \
141
+ f"It's twenty paragraphs long and very well-written. " \
142
+ f"As you can see, the language I use is very understandable — " \
143
+ f"I avoided using overly complex words and phrases. Please respond in Chinese:" \
144
+ f"\n\nTitle: {first_chapter_title}\n\nChapter #1 Text```"
145
+
146
+ data = {
147
+ "model": "claude-2",
148
+ "prompt": prompt_one,
149
+ "max_tokens_to_sample": 5000,
150
+ }
151
+
152
+ response = requests.post(url, headers=headers, json=data)
153
+ print(response)
154
+ initial_first_chapter = response.json()['completion'].strip().split('```')[0].strip()
155
+
156
+ print_step_costs_anthropic(prompt_one, response.json()['completion'])
157
+
158
+ prompt_two = f"\n\nHuman: You are a world-class fantasy writer. " \
159
+ f"Your job is to take your student's rough initial draft of the first chapter of their fantasy novel, " \
160
+ f"and rewrite it to be significantly better, with much more detail. " \
161
+ f"Do you understand?\n\nAssistant: Yes, I understand. " \
162
+ f"Please provide the plot and the student-written chapter, " \
163
+ f"and I will rewrite the chapter in a far superior way.\n\nHuman: " \
164
+ f"Here is the high-level plot you asked your student to follow: " \
165
+ f"{plot}\n\nHere is the first chapter they wrote: {initial_first_chapter}\n\n" \
166
+ f"Now, rewrite the first chapter of this novel, in a way that is far superior to your student's chapter. " \
167
+ f"It should still follow the exact same plot, but it should be far more detailed, much longer, and more engaging. " \
168
+ f"Here is a description of the writing style you should use: `{writing_style}`\n\nAssistant: Okay, I've rewritten the first chapter. " \
169
+ f"I took great care to improve it. While the plot is the same, " \
170
+ f"you can see that my version is noticeably longer, easier to read, " \
171
+ f"and more exciting. Also, the language I used is far more accessible to a broader audience." \
172
+ f"Please respond in Chinese.\n\n```"
173
+ data = {
174
+ "model": "claude-2",
175
+ "prompt": prompt_two,
176
+ "max_tokens_to_sample": 5000,
177
+ }
178
+
179
+ response_improved = requests.post(url, headers=headers, json=data)
180
+
181
+ print_step_costs_anthropic(prompt_two, response_improved.json()['completion'])
182
+
183
+ return response_improved.json()['completion'].strip().split('```')[0].strip()
184
+
185
+
186
+ else:
187
+ response = completion_with_backoff(
188
+ model=llm_model_name,
189
+ messages=[
190
+ {"role": "system", "content": "You are a world-class fantasy writer."},
191
+ {"role": "user",
192
+ "content": f"Here is the high-level plot to follow: {plot}\n\nWrite the first chapter of this novel: `{first_chapter_title}`.\n\nMake it incredibly unique, engaging, and well-written.\n\nHere is a description of the writing style you should use: `{writing_style}`\n\nInclude only the chapter text. There is no need to rewrite the chapter name. Please respond in Chinese."}
193
+ ]
194
+ )
195
+
196
+ print_step_costs(response, llm_model_name)
197
+
198
+ improved_response = completion_with_backoff(
199
+ model=llm_model_name,
200
+ messages=[
201
+ {"role": "system",
202
+ "content": "You are a world-class fantasy writer. Your job is to take your student's rough initial draft of the first chapter of their fantasy novel, and rewrite it to be significantly better, with much more detail."},
203
+ {"role": "user",
204
+ "content": f"Here is the high-level plot you asked your student to follow: {plot}\n\nHere is the first chapter they wrote: {response['choices'][0]['message']['content']}\n\nNow, rewrite the first chapter of this novel, in a way that is far superior to your student's chapter. It should still follow the exact same plot, but it should be far more detailed, much longer, and more engaging. Here is a description of the writing style you should use: `{writing_style}`.Please respond in Chinese."}
205
+ ]
206
+ )
207
+
208
+ print_step_costs(response, llm_model_name)
209
+
210
+ return improved_response['choices'][0]['message']['content']
211
+
212
+
213
+ def write_chapter(previous_chapters, plot, chapter_title, claude=True):
214
+ if claude:
215
+ url = "https://api.anthropic.com/v1/complete"
216
+
217
+ headers = {
218
+ "anthropic-version": "2023-06-01",
219
+ "content-type": "application/json",
220
+ "x-api-key": anthropic_api_key,
221
+ }
222
+
223
+ prompt = f"\n\nHuman: You are a world-class fantasy writer. I will provide you with the plot of the novel, the previous chapters, and the plan for the next chapter. Your task is to write the next chapter of the novel in Chinese, following the plot and taking in the previous chapters as context. Do you understand?\n\nAssistant: Yes, I understand. You want me to write the next chapter of a novel, using the plot you provide, the previous chapters for context, and a specific plan for the next chapter. I will ensure the chapter is beautifully written and I will not rewrite the chapter name.\n\nHuman: That's correct. Here is the plot: {plot}\n\nHere are the previous chapters: {previous_chapters}\n\nHere is the plan for the next chapter: {chapter_title}\n\nWrite it beautifully. Include only the chapter text. There is no need to rewrite the chapter name.\n\nAssistant: Here is the next chapter. As you can see, it's around the same length as the previous chapters, and contains witty dialogue:\n```Chapter"
224
+
225
+ data = {
226
+ "model": "claude-2",
227
+ "prompt": prompt,
228
+ "max_tokens_to_sample": 5000,
229
+ }
230
+
231
+ response = requests.post(url, headers=headers, json=data)
232
+
233
+ print_step_costs_anthropic(prompt, response.json()['completion'])
234
+
235
+ return 'Chapter ' + response.json()['completion'].strip().split('```')[0].strip()
236
+ else:
237
+ try:
238
+ i = random.randint(1, 2242)
239
+ response = completion_with_backoff(
240
+ model=llm_model_name,
241
+ messages=[
242
+ {"role": "system", "content": "You are a world-class fantasy writer. "},
243
+ {"role": "user",
244
+ "content": f"Plot: {plot}, Previous Chapters: {previous_chapters}\n\n--\n\nWrite the next chapter of this novel in Chinese, following the plot and taking in the previous chapters as context. Here is the plan for this chapter: {chapter_title}\n\nWrite it beautifully. Include only the chapter text. There is no need to rewrite the chapter name."}
245
+ ]
246
+ )
247
+
248
+ print_step_costs(response, llm_model_name)
249
+
250
+ return response['choices'][0]['message']['content']
251
+ except:
252
+ response = completion_with_backoff(
253
+ model=llm_model_name,
254
+ messages=[
255
+ {"role": "system", "content": "You are a world-class fantasy writer."},
256
+ {"role": "user",
257
+ "content": f"Plot: {plot}, Previous Chapters: {previous_chapters}\n\n--\n\nWrite the next chapter of this novel in Chinese, following the plot and taking in the previous chapters as context. Here is the plan for this chapter: {chapter_title}\n\nWrite it beautifully. Include only the chapter text. There is no need to rewrite the chapter name."}
258
+ ]
259
+ )
260
+
261
+ print_step_costs(response, llm_model_name)
262
+
263
+ return response['choices'][0]['message']['content']
264
+
265
+
266
+ def generate_storyline(prompt, num_chapters):
267
+ print("Generating storyline with chapters and high-level details...")
268
+ json_format = """[{"Chapter CHAPTER_NUMBER_HERE - CHAPTER_TITLE_GOES_HERE":
269
+ "CHAPTER_OVERVIEW_AND_DETAILS_GOES_HERE"}, ...]"""
270
+ response = completion_with_backoff(
271
+ model=llm_model_name,
272
+ messages=[
273
+ {"role": "system",
274
+ "content": "You are a world-class fantasy writer. Your job is to write a detailed storyline,"
275
+ " complete with chapters, for a fantasy novel. "
276
+ "Don't be flowery -- you want to get the message across in as few words as possible. "
277
+ "But those words should contain lots of information. Please respond in Chinese"},
278
+ {"role": "user",
279
+ "content": f'Write a fantastic storyline with {num_chapters} chapters and high-level details based on this plot:'
280
+ f' {prompt}.\n\nDo it in this list of dictionaries format {json_format}.'
281
+ f' And Please respond in Chinese. The response content must be in standard JSON format, without any prefixes and special symbols.'}
282
+ ]
283
+ )
284
+
285
+ print_step_costs(response, llm_model_name)
286
+
287
+ improved_response = completion_with_backoff(
288
+ model=llm_model_name,
289
+ messages=[
290
+ {"role": "system",
291
+ "content": "You are a world-class fantasy writer. "
292
+ "Your job is to take your student's rough initial draft of the storyline of a fantasy novel in Chinese, "
293
+ "and rewrite it to be significantly better. Please respond in Chinese"},
294
+ {"role": "user",
295
+ "content": f"Here is the draft storyline they wrote: {response['choices'][0]['message']['content']}\n\nNow, "
296
+ f"rewrite the storyline in Chinese, in a way that is far superior to your student's version. "
297
+ f"It should have the same number of chapters, "
298
+ f"but it should be much improved in as many ways as possible. "
299
+ f"Remember to do it in this list of dictionaries format {json_format}. please respond in Chinese"
300
+ f' And please, only return the JSON content without any prefix.'}
301
+ ]
302
+ )
303
+
304
+ print_step_costs(improved_response, llm_model_name)
305
+
306
+ return improved_response['choices'][0]['message']['content']
307
+
308
+
309
+ def write_to_file(prompt, content):
310
+ # Create a directory for the prompts if it doesn't exist
311
+ if not os.path.exists('prompts'):
312
+ os.mkdir('prompts')
313
+
314
+ # Replace invalid characters for filenames
315
+ valid_filename = ''.join(c for c in prompt if c.isalnum() or c in (' ', '.', '_')).rstrip()
316
+ file_path = f'prompts/{valid_filename}.txt'
317
+
318
+ with open(file_path, 'w', encoding='utf-8') as f:
319
+ f.write(content)
320
+
321
+ print(f'Output for prompt "{prompt}" has been written to {file_path}\n')
322
+
323
+
324
+ def write_fantasy_novel(prompt, num_chapters, writing_style, claude_true=False, model_name="gpt-3.5-turbo-16k"):
325
+ global llm_model_name
326
+ llm_model_name = model_name
327
+
328
+ # 每本小说生成一个唯一的uuid
329
+ novel_id = generate_uuid()
330
+
331
+ plots = generate_plots(prompt)
332
+ print('generated plots')
333
+ print(f'【plots】: {plots}\n\n')
334
+
335
+ best_plot = select_most_engaging(plots)
336
+ print('selected best plot')
337
+ print(f'【best_plot】: {best_plot}\n\n')
338
+
339
+ improved_plot = improve_plot(best_plot)
340
+ print('plot improved')
341
+ print(f'【improved_plot】: {improved_plot}\n\n')
342
+ time.sleep(20)
343
+
344
+ title = get_title(improved_plot)
345
+ print('title generated')
346
+ print(f'【title】: {title}\n\n')
347
+
348
+ storyline = generate_storyline(improved_plot, num_chapters)
349
+ print('storyline generated')
350
+ print(f'【storyline】: {storyline}\n\n')
351
+
352
+ chapter_titles = ast.literal_eval(storyline)
353
+ print(f'【chapter_titles】: {chapter_titles}\n\n')
354
+
355
+ novel = f"Storyline:\n{storyline}\n\n"
356
+
357
+ first_chapter = write_first_chapter(storyline, chapter_titles[0], writing_style.strip(), claude_true)
358
+ print('first chapter written')
359
+ save_novel_chapter(novel_id, 0, list(chapter_titles[0])[0], first_chapter)
360
+ print(f'【first_chapter】: {first_chapter}\n\n')
361
+ time.sleep(20)
362
+
363
+ novel += f"Chapter 1:\n{first_chapter}\n"
364
+ chapters = [first_chapter]
365
+
366
+ for i in range(num_chapters - 1):
367
+ print(f"Writing chapter {i + 2}...") # + 2 because the first chapter was already added
368
+ time.sleep(30)
369
+
370
+ chapter = write_chapter(novel, storyline, chapter_titles[i + 1], claude_true)
371
+ try:
372
+ if len(str(chapter)) < 100:
373
+ time.sleep(30)
374
+ print('Length minimum not hit. Trying again.')
375
+ chapter = write_chapter(novel, storyline, chapter_titles[i + 1], claude_true)
376
+ except:
377
+ time.sleep(20)
378
+ chapter = write_chapter(novel, storyline, chapter_titles[i + 1], claude_true)
379
+
380
+ novel += f"Chapter {i + 2}:\n{chapter}\n"
381
+ chapters.append(chapter)
382
+ print(f'【Chapter_{i + 2}】: {chapter}\n\n')
383
+ save_novel_chapter(novel_id, (i+1), list(chapter_titles[i + 1])[0], chapter)
384
+
385
+ return novel, title, chapters, chapter_titles