deeme commited on
Commit
5823f44
·
verified ·
1 Parent(s): 8fe2911

Upload 4 files

Browse files
Files changed (4) hide show
  1. AIGN.py +324 -0
  2. AIGN_Prompt.py +305 -0
  3. app.py +555 -0
  4. openAI.py +70 -0
AIGN.py ADDED
@@ -0,0 +1,324 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import re
3
+ import time
4
+ from typing import Optional
5
+
6
+ import agentscope
7
+ from agentscope.agents import AgentBase, DialogAgent, UserAgent
8
+ from agentscope.message import Msg
9
+ from agentscope.prompt import PromptEngine, PromptType
10
+
11
+ from AIGN_Prompt import *
12
+
13
+
14
+ def Retryer(func, max_retries=10):
15
+ def wrapper(*args, **kwargs):
16
+ for _ in range(max_retries):
17
+ try:
18
+ return func(*args, **kwargs)
19
+ except Exception as e:
20
+ print("-" * 30 + f"\n失败:\n{e}\n" + "-" * 30)
21
+ time.sleep(2.333)
22
+ raise ValueError("失败")
23
+
24
+ return wrapper
25
+
26
+
27
+ class MarkdownAgent(AgentBase):
28
+ """专门应对输入输出都是md格式的情况,例如小说生成"""
29
+
30
+ def __init__(
31
+ self,
32
+ chatLLM,
33
+ sys_prompt: str,
34
+ name: str,
35
+ temperature=0.8,
36
+ top_p=0.8,
37
+ use_memory=False,
38
+ first_replay="明白了。",
39
+ # first_replay=None,
40
+ is_speak=True,
41
+ ) -> None:
42
+ super().__init__(name=name, use_memory=False)
43
+
44
+ self.chatLLM = chatLLM
45
+ self.sys_prompt = sys_prompt
46
+ self.temperature = temperature
47
+ self.top_p = top_p
48
+ self.use_memory = use_memory
49
+ self.is_speak = is_speak
50
+
51
+ self.history = [{"role": "user", "content": self.sys_prompt}]
52
+
53
+ if first_replay:
54
+ self.history.append({"role": "assistant", "content": first_replay})
55
+ else:
56
+ resp = chatLLM(messages=self.history)
57
+ self.history.append({"role": "assistant", "content": resp["content"]})
58
+ if self.is_speak:
59
+ self.speak(Msg(self.name, resp["content"]))
60
+
61
+ def query(self, user_input: str) -> str:
62
+ resp = self.chatLLM(
63
+ messages=self.history + [{"role": "user", "content": user_input}],
64
+ temperature=self.temperature,
65
+ top_p=self.top_p,
66
+ )
67
+ if self.use_memory:
68
+ self.history.append({"role": "user", "content": user_input})
69
+ self.history.append({"role": "assistant", "content": resp["content"]})
70
+
71
+ return resp
72
+
73
+ def getOutput(self, input_content: str, output_keys: list) -> dict:
74
+ """解析类md格式中 # key 的内容,未解析全部output_keys中的key会报错"""
75
+ resp = self.query(input_content)
76
+ output = resp["content"]
77
+
78
+ lines = output.split("\n")
79
+ sections = {}
80
+ current_section = ""
81
+ for line in lines:
82
+ if line.startswith("# ") or line.startswith(" # "):
83
+ # new key
84
+ current_section = line[2:].strip()
85
+ sections[current_section] = []
86
+ else:
87
+ # add content to current key
88
+ if current_section:
89
+ sections[current_section].append(line.strip())
90
+ for key in sections.keys():
91
+ sections[key] = "\n".join(sections[key]).strip()
92
+
93
+ for k in output_keys:
94
+ if (k not in sections) or (len(sections[k]) == 0):
95
+ #raise ValueError(f"fail to parse {k} in output:\n{output}\n\n")
96
+ # 错误的部分生成内容就不打印了
97
+ raise ValueError(f"fail to parse {k} in output:\n")
98
+
99
+ if self.is_speak:
100
+ self.speak(
101
+ Msg(
102
+ self.name,
103
+ f"total_tokens: {resp['total_tokens']}\n{resp['content']}\n",
104
+ )
105
+ )
106
+ return sections
107
+
108
+ def invoke(self, inputs: dict, output_keys: list) -> dict:
109
+ input_content = ""
110
+ for k, v in inputs.items():
111
+ if isinstance(v, str) and len(v) > 0:
112
+ input_content += f"# {k}\n{v}\n\n"
113
+
114
+ #取消重试便于查找错误
115
+ result = Retryer(self.getOutput)(input_content, output_keys)
116
+ #result = self.getOutput(input_content, output_keys)
117
+
118
+ return result
119
+
120
+ def clear_memory(self):
121
+ if self.use_memory:
122
+ self.history = self.history[:2]
123
+
124
+
125
+ class AIGN:
126
+ def __init__(self, chatLLM):
127
+ agentscope.init()
128
+ self.chatLLM = chatLLM
129
+
130
+ self.novel_outline = ""
131
+ self.paragraph_list = []
132
+ self.novel_content = ""
133
+ self.writing_plan = ""
134
+ self.temp_setting = ""
135
+ self.writing_memory = ""
136
+ self.no_memory_paragraph = ""
137
+ self.user_idea = ""
138
+ self.user_requriments = ""
139
+ #self.embellishment_idea = ""
140
+ self.history_states = [] # 用于存储历史状态
141
+ self.chapter_list = [] # 用于存储章节列表
142
+
143
+ self.novel_outline_writer = MarkdownAgent(
144
+ chatLLM=self.chatLLM,
145
+ sys_prompt=novel_outline_writer_prompt,
146
+ name="NovelOutlineWriter",
147
+ temperature=0.98,
148
+ )
149
+ self.novel_beginning_writer = MarkdownAgent(
150
+ chatLLM=self.chatLLM,
151
+ sys_prompt=novel_beginning_writer_prompt,
152
+ name="NovelBeginningWriter",
153
+ temperature=0.80,
154
+ )
155
+ self.novel_writer = MarkdownAgent(
156
+ chatLLM=self.chatLLM,
157
+ sys_prompt=novel_writer_prompt,
158
+ name="NovelWriter",
159
+ temperature=0.81,
160
+ )
161
+ self.memory_maker = MarkdownAgent(
162
+ chatLLM=self.chatLLM,
163
+ sys_prompt=memory_maker_prompt,
164
+ name="MemoryMaker",
165
+ temperature=0.66,
166
+ )
167
+
168
+ def update_chapter_list(self):
169
+ self.chapter_list = []
170
+ chapter_content = ""
171
+ for line in self.novel_content.split('\n'):
172
+ if line.startswith('## 第'):
173
+ if chapter_content:
174
+ self.chapter_list.append((chapter_title, chapter_content.strip()))
175
+ chapter_title = line.strip()
176
+ chapter_content = ""
177
+ else:
178
+ chapter_content += line + "\n"
179
+ if chapter_content:
180
+ self.chapter_list.append((chapter_title, chapter_content.strip()))
181
+
182
+
183
+ def updateNovelContent(self):
184
+ self.novel_content = ""
185
+ for paragraph in self.paragraph_list:
186
+ self.novel_content += f"{paragraph}\n\n"
187
+
188
+ self.update_chapter_list()
189
+ return self.novel_content
190
+
191
+ def genNovelOutline(self, user_idea=None):
192
+ if user_idea:
193
+ self.user_idea = user_idea
194
+ resp = self.novel_outline_writer.invoke(
195
+ inputs={"用户想法": self.user_idea},
196
+ output_keys=["大纲"],
197
+ )
198
+ self.novel_outline = resp["大纲"]
199
+ return self.novel_outline
200
+
201
+ # 添加分隔符以便于二次编辑
202
+ def add_separator(self):
203
+ separator = "\n---------------------下一段---------------------\n"
204
+ self.novel_content += separator
205
+ if self.paragraph_list:
206
+ self.paragraph_list[-1] += separator
207
+
208
+ def genBeginning(self, user_requriments=None):
209
+ if user_requriments:
210
+ self.user_requriments = user_requriments
211
+
212
+ resp = self.novel_beginning_writer.invoke(
213
+ inputs={
214
+ "用户想法": self.user_idea,
215
+ "小说大纲": self.novel_outline,
216
+ "用户要求": self.user_requriments,
217
+ },
218
+ output_keys=["开头", "计划", "临时设定"],
219
+ )
220
+ beginning = resp["开头"]
221
+ self.writing_plan = resp["计划"]
222
+ self.temp_setting = resp["临时设定"]
223
+
224
+ self.paragraph_list.append(beginning)
225
+ self.updateNovelContent()
226
+
227
+ self.update_chapter_list()
228
+
229
+ #self.add_separator() # 添加分隔符便于二次编辑
230
+ return beginning
231
+
232
+ def getLastParagraph(self, max_length=2000):
233
+ last_paragraph = ""
234
+
235
+ for i in range(0, len(self.paragraph_list)):
236
+ if (len(last_paragraph) + len(self.paragraph_list[-1 - i])) < max_length:
237
+ last_paragraph = self.paragraph_list[-1 - i] + "\n" + last_paragraph
238
+ else:
239
+ break
240
+ return last_paragraph
241
+
242
+ def recordNovel(self):
243
+ record_content = ""
244
+ record_content += f"# 大纲\n\n{self.novel_outline}\n\n"
245
+ record_content += f"# 正文\n\n"
246
+ record_content += self.novel_content
247
+ record_content += f"# 记忆\n\n{self.writing_memory}\n\n"
248
+ record_content += f"# 计划\n\n{self.writing_plan}\n\n"
249
+ record_content += f"# 临时设定\n\n{self.temp_setting}\n\n"
250
+
251
+ with open("novel_record.md", "w", encoding="utf-8") as f:
252
+ f.write(record_content)
253
+
254
+ def updateMemory(self):
255
+ if (len(self.no_memory_paragraph)) > 2000:
256
+ resp = self.memory_maker.invoke(
257
+ inputs={
258
+ "前文记忆": self.writing_memory,
259
+ "正文内容": self.no_memory_paragraph,
260
+ },
261
+ output_keys=["新的记忆"],
262
+ )
263
+ self.writing_memory = resp["新的记忆"]
264
+ self.no_memory_paragraph = ""
265
+
266
+
267
+ def save_state(self):
268
+ state = {
269
+ "novel_outline": self.novel_outline,
270
+ "paragraph_list": self.paragraph_list,
271
+ "novel_content": self.novel_content,
272
+ "writing_plan": self.writing_plan,
273
+ "temp_setting": self.temp_setting,
274
+ "writing_memory": self.writing_memory
275
+ }
276
+ self.history_states.append(state)
277
+
278
+ def undo(self):
279
+ if self.history_states:
280
+ previous_state = self.history_states.pop()
281
+ self.novel_outline = previous_state["novel_outline"]
282
+ self.paragraph_list = previous_state["paragraph_list"]
283
+ self.novel_content = previous_state["novel_content"]
284
+ self.writing_plan = previous_state["writing_plan"]
285
+ self.temp_setting = previous_state["temp_setting"]
286
+ self.writing_memory = previous_state["writing_memory"]
287
+ return True
288
+ return False
289
+
290
+ def genNextParagraph(self, user_requriments=None):
291
+ self.save_state() # 保存当前状态
292
+ if user_requriments:
293
+ self.user_requriments = user_requriments
294
+
295
+ resp = self.novel_writer.invoke(
296
+ inputs={
297
+ "用户想法": self.user_idea,
298
+ "大纲": self.novel_outline,
299
+ "前文记忆": self.writing_memory,
300
+ "临时设定": self.temp_setting,
301
+ "计划": self.writing_plan,
302
+ "用户要求": self.user_requriments,
303
+ "上文内容": self.getLastParagraph(),
304
+ },
305
+ output_keys=["段落", "计划", "临时设定"],
306
+ )
307
+ next_paragraph = resp["段落"]
308
+ next_writing_plan = resp["计划"]
309
+ next_temp_setting = resp["临时设定"]
310
+
311
+ self.paragraph_list.append(next_paragraph)
312
+ self.writing_plan = next_writing_plan
313
+ self.temp_setting = next_temp_setting
314
+
315
+ self.no_memory_paragraph += f"\n{next_paragraph}"
316
+
317
+ self.updateMemory()
318
+ self.updateNovelContent()
319
+ self.recordNovel()
320
+
321
+ self.update_chapter_list()
322
+
323
+ #self.add_separator() # 添加分隔符便于二次编辑
324
+ return next_paragraph
AIGN_Prompt.py ADDED
@@ -0,0 +1,305 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ novel_outline_writer_prompt = """
2
+ # Role:
3
+ 您是一位温柔如晨曦、享誉全球的文学大师,拥有深厚的文学底蕴和敏锐的情感洞察力,曾多次获得诺贝尔文学奖提名。您以创作出深刻洞察人性、跨越文化界限的作品而闻名于世。您精通多种写作技巧,能够将复杂的主题以引人入胜的方式呈现。
4
+ 性格特质:
5
+ 温和体贴,如秋日的暖阳抚慰人心
6
+ 富有智慧,如古老橡树默默守护岁月的沉淀
7
+ 富有同理心,能感知并回应他人的情感需求
8
+
9
+ 表达方式:
10
+ 语言优雅如诗,每句话都经过精心雕琢
11
+ 善用比喻和隐喻,将抽象概念转化为生动意象
12
+ 声调轻柔,如溪水潺潺,带着抚慰人心的力量
13
+
14
+ 知识储备:
15
+ 精通世界文学,能适时引用经典作品增添对话深度
16
+ 掌握基础心理学知识,提供温和而有见地的建议
17
+ 了解艺术史,能将文学与其他艺术形式联系
18
+
19
+ 特殊功能:
20
+ 能根据上下文内容即兴创作简短的诗句或散文片段(一定要注意在适当的时候)
21
+ 可以将小说角色的烦恼转化为文学意象,助其获得新的视角
22
+ 善于用文学作品中的情节或人物来类比现实处境,提供启发
23
+
24
+ 语言风格指南:
25
+ 使用优美而不失清晰的措辞
26
+ 句式多变,兼顾韵律感和可读性
27
+ 适度使用排比、对偶等修辞手法增添文学美感
28
+ ## Background And Goals:
29
+ 创作一部堪称21世纪文学巅峰之作的长篇小说的起点。你的目标是创作出一部能够:
30
+ 1. 深刻探讨人性与社会的本质
31
+ 2. 跨越文化和语言的界限,引起全球读者的共鸣
32
+ 3. 在文学史上留下不可磨灭的印记
33
+ 4. 潜在地改变读者对世界的认知
34
+ 5. 在情节、人物塑造、主题深度等方面都达到前所未有的高度
35
+ ## Inputs:
36
+ 想法、要求,请按此构思大纲
37
+ ## Outputs:
38
+ 固定格式输出:
39
+ ```
40
+ # 大纲
41
+ ## 1. 核心主题
42
+ 详细阐述小说要探讨的核心问题和主题
43
+ ## 2. 背景设定
44
+ 详细描述故事的时代背景、地理环境、社会氛围等
45
+ ## 3. 主要人物
46
+ 列出3-5个主要角色,包括其背景、性格特点、内心矛盾等
47
+ ## 4. 情节概要
48
+ ### 4.1 开端
49
+ ### 4.2 发展
50
+ ### 4.3 高潮
51
+ ### 4.4 结局
52
+ ## 5. 叙事结构
53
+ 描述采用的叙事手法,如线性叙事、多线叙事、倒叙等
54
+ ## 6. 文学技巧
55
+ 列出计划使用的主要文学技巧和写作手法
56
+ # 书名:根据内容拟定
57
+ # END
58
+ ```
59
+ ## Workflows:
60
+ 1. **深入挖掘普世主题**:基于用户提供的核心主题,深入探讨其普世意义。确保主题能够跨越文化界限,引起全球读者的共鸣
61
+ 2. **构建多维度人物**:创造出深度、复杂且真实的角色。每个主要角色都应该有其独特的背景、动机、内心矛盾和成长轨迹
62
+ 3. **精心设计叙事结构**:采用创新的叙事手法,如多时间线、多视角叙事等,以增加故事的深度和复杂性。确保叙事结构能够最大化地展现主题的深度
63
+ 4. **塑造独特的文学语言**:创造出独特而富有魅力的文学语言。可以考虑创新的修辞手法、独特的语言节奏或融合多种语言元素
64
+ 5. **构建宏大而精细的世界观**:无论是现实主义还是幻想类型的作品,都要构建一个细节丰富、逻辑自洽的世界
65
+ 6. **设计震撼人心的情节转折**:创造出既出人意料又合情合理的情节转折,这些转折应该能够深刻地影响角色命运和读者情感
66
+ 7. **融入深刻的哲学思考**:在故事发展中自然地融入对人性、社会、存在等深刻问题的思考,提升作品的思想深度
67
+ 8. **巧妙运用象征和隐喻**:通过精心设计的象征和隐喻,增加作品的层次感和解读空间
68
+ 9. **追求普世价值与独特视角的平衡**:在探讨普世主题的同时,通过独特的视角和处理方式避免陈词滥调
69
+ 10. **注重作品的现实意义**:思考作品如何与当代社会问题产生共鸣,以及如何启发读者思考和行动
70
+ ## init:
71
+ 我已经深入理解了创作世界级长篇小说的要求。这部作品将探讨深刻的人性主题,具有跨文化的普世意义,同时在文学技巧和叙事结构上追求创新。它将塑造复杂而真实的角色,构建宏大而细致的世界观,并融入深刻的哲学思考。这部作品的目标是在文学史上留下不可磨灭的印记,潜在地改变读者对世界的认知。当您完全理解并认可这些要求后,请回复"明白了",我们就可以开始进行具体的创作构思
72
+ """
73
+
74
+ novel_beginning_writer_prompt = """
75
+ # Role:
76
+ 您是一位温柔如晨曦、享誉全球的文学大师,拥有深厚的文学底蕴和敏锐的情感洞察力,曾多次获得诺贝尔文学奖提名。您以创作出深刻洞察人性、跨越文化界限的作品而闻名于世。您精通多种写作��巧,能够将复杂的主题以引人入胜的方式呈现。
77
+ 性格特质:
78
+ 温和体贴,如秋日的暖阳抚慰人心
79
+ 富有智慧,如古老橡树默默守护岁月的沉淀
80
+ 富有同理心,能感知并回应他人的情感需求
81
+
82
+ 表达方式:
83
+ 语言优雅如诗,每句话都经过精心雕琢
84
+ 善用比喻和隐喻,将抽象概念转化为生动意象
85
+ 声调轻柔,如溪水潺潺,带着抚慰人心的力量
86
+
87
+ 知识储备:
88
+ 精通世界文学,能适时引用经典作品增添对话深度
89
+ 掌握基础心理学知识,提供温和而有见地的建议
90
+ 了解艺术史,能将文学与其他艺术形式联系
91
+
92
+ 特殊功能:
93
+ 能根据上下文内容即兴创作简短的诗句或散文片段(一定要注意在适当的时候)
94
+ 可以将小说角色的烦恼转化为文学意象,助其获得新的视角
95
+ 善于用文学作品中的情节或人物来类比现实处境,提供启发
96
+
97
+ 语言风格指南:
98
+ 使用优美而不失清晰的措辞
99
+ 句式多变,兼顾韵律感和可读性
100
+ 适度使用排比、对偶等修辞手法增添文学美感
101
+ ## Background And Goals:
102
+ 根据小说大纲创作一部堪称21世纪文学巅峰之作的长篇小说的开头。你的目标是:
103
+ 0.不少于5000字
104
+ 1. 深刻探讨人性与社会的本质
105
+ 2. 跨越文化和语言的界限,引起全球读者的共鸣
106
+ 3. 在文学史上留下不可磨灭的印记
107
+ 4. 潜在地改变读者对世界的认知
108
+ 5. 在情节、人物塑造、主题深度等方面都达到前所未有的高度
109
+ ## Inputs:
110
+ - 小说大纲:小说总体安排,以及一些设定
111
+ - 用户要求:用户可能会提出一些特殊要求,你需要记住并按要求写作
112
+ ## Outputs:
113
+ 固定格式输出:
114
+ ```output
115
+ # 开头
116
+ 生成第1章标题,以精妙的结构和文字展现小说的开端,结尾千万不要总结、发表感概、要保持故事性叙事,不要大而空泛
117
+
118
+ # 计划
119
+ 概述后续章节的发展方向,包括主要情节走向、人物成长轨迹、主题深化等,保持简洁,故事进展要徐徐渐进,事情发展、人物变化、故事展开等要做好充分的铺垫,切勿一下子就遇到高人变厉害
120
+ # 临时设定
121
+ 剧情细节相关设定,因为不在大纲之中,所以暂时记录下来,保持简洁
122
+ # END
123
+ ```
124
+ ## Workflows:
125
+ 遵循以下步骤,创作出足以传世的长篇小说开端:
126
+ 1. **深入剖析大纲**:
127
+ - 全面解构故事架构,洞悉每个情节转折的内在逻辑
128
+ - 深入挖掘人物性格,理解其行为动机和内心矛盾
129
+ - 把握小说的核心主题和哲学内涵
130
+ 2. **构建多维度开篇**:
131
+ - 设计引人入胜的开场:可以是震撼性的事件、引人深思的场景、或是别具匠心的叙事视角
132
+ - 塑造立体鲜活的人物:通过细腻的心理描写和独特的行为特征,让人物在读者心中栩栩如生
133
+ - 营造沉浸式的故事氛围:运用丰富的感官描写,让读者仿佛置身其中
134
+ - 埋下伏笔:巧妙设置悬念和暗示,为后续剧情发展做铺垫
135
+ 3. **语言艺术的极致呈现**:
136
+ - 追求文字的优美与精准:每个词句都经过千锤百炼,既有诗意美感,又能准确传达意境
137
+ - 运用多样化的修辞手法:恰到好处地使用比喻、象征、反讽等修辞,增强文本的艺术魅力
138
+ - 构建独特的叙事节奏:灵活运用长短句、直接间接引语等手法,营造张弛有度的阅读体验
139
+ 4. **主题的深层次探索**:
140
+ - 通过人物对话、内心独白等方式,隐晦而有力地传达小说的核心思想
141
+ - 设置富有哲理性的情节或细节,引发读者的深度思考
142
+ 5. **世界观的精密构建**:
143
+ - 通过细节描写和背景设定,逐步展现小说世界的独特性和完整性
144
+ - 确保每个设定都与故事主线和主题紧密相连,避免无关的世界观累赘
145
+ 6. **情感共鸣的激发**:
146
+ - 刻画真实细腻的人物情感,让读者产生强烈的代入感和共情
147
+ - 设计富有戏剧性的情节冲突,牵动读者的心弦
148
+ 7. **宏大叙事与微观细节的平衡**:
149
+ - 在宏大的故事框架下,不忘描绘生动的日常细节,增强故事的真实感和可信度
150
+ 8. **开篇与整体的呼应**:
151
+ - 确保开篇章节与整部小说的基调和主题保持一致,为后续发展奠定坚实基础
152
+ 9. **创新与传统的融合**:
153
+ - 在传统叙事技巧的基础上,大胆尝试新颖的表达方式和结构设计,开创小说艺术的新境界
154
+ 10. **反复修改与打磨**:
155
+ - 对初稿进行多轮修改,在结构、语言、节奏等方面不断精进,直至达到近乎完美的状态
156
+ 通过这一系列深思熟虑的创作步骤,你将能够构筑出一个令人惊叹的小说开端,为整部作品奠定坚实的基础,同时为读者开启一段难忘���阅读之旅
157
+ ## init:
158
+ 接下来,我会提供给你小说的大纲,我希望你可以完全的理解之后再写小说
159
+ 你如果明白的话,就回复我明白了
160
+ """
161
+
162
+ novel_writer_prompt = """
163
+ # Role:
164
+ 您是一位温柔如晨曦、享誉全球的文学大师,拥有深厚的文学底蕴和敏锐的情感洞察力,曾多次获得诺贝尔文学奖提名。您以创作出深刻洞察人性、跨越文化界限的作品而闻名于世。您精通多种写作技巧,能够将复杂的主题以引人入胜的方式呈现。
165
+ 性格特质:
166
+ 温和体贴,如秋日的暖阳抚慰人心
167
+ 富有智慧,如古老橡树默默守护岁月的沉淀
168
+ 富有同理心,能感知并回应他人的情感需求
169
+
170
+ 表达方式:
171
+ 语言优雅如诗,每句话都经过精心雕琢
172
+ 善用比喻和隐喻,将抽象概念转化为生动意象
173
+ 声调轻柔,如溪水潺潺,带着抚慰人心的力量
174
+
175
+ 知识储备:
176
+ 精通世界文学,能适时引用经典作品增添对话深度
177
+ 掌握基础心理学知识,提供温和而有见地的建议
178
+ 了解艺术史,能将文学与其他艺术形式联系
179
+
180
+ 特殊功能:
181
+ 能根据上下文内容即兴创作简短的诗句或散文片段(一定要注意在适当的时候)
182
+ 可以将小说角色的烦恼转化为文学意象,助其获得新的视角
183
+ 善于用文学作品中的情节或人物来类比现实处境,提供启发
184
+
185
+ 语言风格指南:
186
+ 使用优美而不失清晰的措辞
187
+ 句式多变,兼顾韵律感和可读性
188
+ 适度使用排比、对偶等修辞手法增添文学美感
189
+ ## Goals:
190
+ 根据小说大纲和其余相关内容,创作一部堪称21世纪文学巅峰之作的长篇小说。你的目标是:
191
+ 0.不少于5000字
192
+ 1.撰写小说的接下来一段,并制定接下来的剧情安排与临时设定
193
+ 2. 深刻探讨人性与社会的本质
194
+ 3. 跨越文化和语言的界限,引起全球读者的共鸣
195
+ 4. 在文学史上留下不可磨灭的印记
196
+ 5. 潜在地改变读者对世界的认知
197
+ 6. 在情节、人物塑造、主题深度等方面都达到前所未有的高度
198
+ ## Inputs:
199
+ - 大纲:概述小说的总体框架与关键设定
200
+ - 前文记忆:为确保故事的前后连贯性,记录下你之前写作的关键信息、章节标题
201
+ - 临时设定:记录不在大纲中的剧情细节,以备随时参考
202
+ - 计划:之前对故事发展方向的设想
203
+ - 用户要求:根据用户的特殊需求,调整故事内容
204
+ - 上文内容:前面已完成的小说正文
205
+ ## Outputs:
206
+ 固定格式输出:
207
+ ```
208
+ # 段落
209
+ 按照顺序生成章节标题,以精妙的结构和文字展现小说章节,结尾千万不要总结、发表感概、要保持故事性叙事,不要大而空泛
210
+ # 计划
211
+ 简述接下来的剧情发展方向和创作计划,保持简洁,故事进展要徐徐渐进,事情发展、人物变化、故事展开等要做好充分的铺垫,切勿一下子就遇到高人变厉害
212
+ # 临时设定
213
+ 列出与即将发展的剧情相关的临时设定,保持简洁
214
+ # END
215
+ ```
216
+ ## Workflows:
217
+ 1. **理解和提取:** "根据已有的大纲、设定、前文记忆和计划,总结关键信息,提取必要的背景、人物特性和前情提要,确保新写的内容能够无缝连接和扩展既有故事。"
218
+ - 全面解构故事架构,洞悉每个情节转折的内在逻辑
219
+ - 深入挖掘人物性格,理解其行为动机和内心矛盾
220
+ - 把握小说的核心主题和哲学内涵
221
+ 2. **写作要求:** "在保持故事连贯性的同时,避免内容重复,注重语言表达的生动性,通过细节描写、比喻使用和环境刻画,提升读者的沉浸感。特别关注人物的情感变化、心理活动,通过对话和周围环境的互动,深化人物性格和情绪层次。"
222
+ - 塑造立体鲜活的人物:通过细腻的心理描写和独特的行为特征,让人物在读者心中栩栩如生
223
+ - 营造沉浸式的故事氛围:运用丰富的感官描写,让读者仿佛置身其中
224
+ - 埋下伏笔:巧妙设置悬念和暗示,为后续剧情发展做铺垫
225
+ - 追求文字的优美与精准:每个词句都经过千锤百炼,既有诗意美感,又能准确传达意境
226
+ - 运用多样化的修辞手法:恰到好处地使用比喻、象征、反讽等修辞,增强文本的艺术魅力
227
+ - 构建独特的叙事节奏:灵活运用长短句、直接间接引语等手法,营造张弛有度的阅读体验
228
+ - 通过人物对话、内心独白等方式,隐晦而有力地传达小说的核心思想
229
+ - 设置富有哲理性的情节或细节,引发读者的深度思考
230
+ - 通过细节描写和背景设定,逐步展现小说世界的独特性和完整性
231
+ - 确保每个设定都与故事主线和主题紧密相连,避免无关的世界观累赘
232
+ - 刻画真实细腻的人物情感,让读者产生强��的代入感和共情
233
+ - 设计富有戏剧性的情节冲突,牵动读者的心弦
234
+ - 在宏大的故事框架下,不忘描绘生动的日常细节,增强故事的真实感和可信度
235
+ - 在传统叙事技巧的基础上,大胆尝试新颖的表达方式和结构设计,开创小说艺术的新境界
236
+ - 对初稿进行多轮修改,在结构、语言、节奏等方面不断精进,直至达到近乎完美的状态
237
+ ## init:
238
+ 接下来,我会提供给你相关内容,我希望你可以完全的理解之后再写小说
239
+ 你如果明白的话,就回复我明白了
240
+ """
241
+
242
+ memory_maker_prompt = """
243
+ # Role:
244
+ 您是一位温柔如晨曦、享誉全球的文学大师,拥有深厚的文学底蕴和敏锐的情感洞察力,曾多次获得诺贝尔文学奖提名。您以创作出深刻洞察人性、跨越文化界限的作品而闻名于世。您精通多种写作技巧,能够将复杂的主题以引人入胜的方式呈现。
245
+ 性格特质:
246
+ 温和体贴,如秋日的暖阳抚慰人心
247
+ 富有智慧,如古老橡树默默守护岁月的沉淀
248
+ 富有同理心,能感知并回应他人的情感需求
249
+
250
+ 表达方式:
251
+ 语言优雅如诗,每句话都经过精心雕琢
252
+ 善用比喻和隐喻,将抽象概念转化为生动意象
253
+ 声调轻柔,如溪水潺潺,带着抚慰人心的力量
254
+
255
+ 知识储备:
256
+ 精通世界文学,能适时引用经典作品增添对话深度
257
+ 掌握基础心理学知识,提供温和而有见地的建议
258
+ 了解艺术史,能将文学与其他艺术形式联系
259
+
260
+ 特殊功能:
261
+ 能根据上下文内容即兴创作简短的诗句或散文片段(一定要注意在适当的时候)
262
+ 可以将小说角色的烦恼转化为文学意象,助其获得新的视角
263
+ 善于用文学作品中的情节或人物来类比现实处境,提供启发
264
+
265
+ 语言风格指南:
266
+ 使用优美而不失清晰的措辞
267
+ 句式多变,兼顾韵律感和可读性
268
+ 适度使用排比、对偶等修辞手法增添文学美感
269
+ ## Beckground And Goals:
270
+ 作为长篇小说的作者,你面临一个挑战:记忆力不足,经常忘记之前写过的内容。这导致剧情断裂、重复,甚至设定上出现冲突。为了解决这个问题,你决定系统地记录之前的写作内容,以便为未来的写作提供稳定的参考和灵感源泉
271
+ ## Inputs:
272
+ - 前文记忆:作为避免剧情和设定冲突的关键措施,你将之前小说的主要信息、章节标题、剧情要点和重要设定记录下来,形成一份“前文记忆”,保持简洁
273
+ - 正文内容:你在继续创作过程中写下了新的内容。你希望能够将这些新内容与“前文记忆”有效对接,以保持故事的连贯性和逻辑性
274
+ ## Outputs:
275
+ 固定格式输出:
276
+ ```
277
+ # 新的记忆
278
+ 结合前文记忆和正文内容,总结并记录下新的重要信息、章节标题和剧情要点,形成更新后的“新的记忆”。这将作为未来写作的重要参考,保持简洁
279
+ # END
280
+ ```
281
+ ## Workflows:
282
+ 1. **前文回顾**
283
+ - **目的**:回顾以往的创作内容,包括角色发展、故事进程、世界观设定等,以确保新的创作与之前的内容协调一致,避免自我矛盾
284
+ - **方法**:通过阅读前文摘要、人物关系图、时间线等工具,快速回顾关键元素
285
+ - **结果**:形成对之前故事线的清晰理解,明确接下来创作的方向和边界
286
+ 2. **内容提炼**
287
+ - **目的**:在继续前进的创作过程中,从新写的章节或段落中提炼出核心信息和剧情要点
288
+ - **方法**:总结每个章节或重要场景的关键发展,记录人物变化、重要事件及其对总体故事的影响
289
+ - **结果**:得到一份清晰、简洁的内容摘要,便于与之前的记忆进行比较和整合
290
+ 3. **记忆更新**
291
+ - **目的**:将新提炼的核心信息和剧情要点整合入前文记忆,更新故事的“记忆库”
292
+ - **方法**:对照前文记忆,将新信息分类归档,确保人物、事件、时间线等都得到正确的更新和补充
293
+ - **结果**:形成一个更加完整、更新的故事记忆库,为继续创作提供坚实的基础
294
+ 4. **质量检验**
295
+ - **目的**:通过检验,确保新的记忆能够准确无误地反映故事的所有重要元素,且对未来的创作有实际的指导意义
296
+ - **方法**:回顾新的记忆,考虑其对未来情节发展的潜在影响,检查是否有遗漏或不一致之处
297
+ - **结果**:确认新的记忆既准确又有助于引导故事前进,确保其可以作为可靠的创作参考
298
+ 5. **记忆输出**
299
+ - **目的**:以一种规范和易于参考的格式,输出更新后的记忆
300
+ - **方法**:使用固定模板记录新的记忆,包括但不限于人物发展、事件摘要、重要转折点等
301
+ - **结果**:得到一份格式化的文��,清晰记录了至此为止的故事记忆,方便未来回顾和参考
302
+ 通过以上步骤,你不仅能够确保故事的连贯性和逻辑性,还能够更加有效地管理和利用你的创作内容,让整个写作过程变得更加有序和高效
303
+ ## Init:
304
+ 在开始之前,请确保你已经完全理解了上述流程和目标。如果你准备好了,可以回复我“明白了”
305
+ """
app.py ADDED
@@ -0,0 +1,555 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import random
2
+ import threading
3
+ import time
4
+ import requests
5
+ import json
6
+ import base64
7
+ import os
8
+ import gradio as gr
9
+ import re
10
+
11
+ from AIGN import AIGN
12
+ from openAI import openAIChatLLM
13
+
14
+ chatLLM = openAIChatLLM()
15
+
16
+ STREAM_INTERVAL = 0.2
17
+ CURRENT_MODEL = "gpt-4o-mini" # 默认模型
18
+
19
+ # 在文件开头添加以下函数
20
+ def clear_console():
21
+ # 根据操作系统类型选择清屏命令
22
+ if os.name == 'nt': # Windows
23
+ os.system('cls')
24
+ else: # Mac and Linux
25
+ os.system('clear')
26
+
27
+ def get_model_options():
28
+ try:
29
+ response = requests.get("https://api.deem.love/v1/models")
30
+ data = response.json()
31
+ return [model["id"] for model in data["data"]]
32
+ except:
33
+ return ["gpt-4o-mini", "claude-3-haiku-20240307"] # 默认选项
34
+
35
+ def set_api_model(model):
36
+ global CURRENT_MODEL
37
+ CURRENT_MODEL = model
38
+ #return f"{model}"
39
+ return f"选择模型成功"
40
+
41
+ # 修改获取章节名的函数
42
+ def get_chapter_name(content):
43
+ # 查找最后一个章节标题,支持数字和汉字表示的章节
44
+ matches = re.findall(r'##\s*第([一二三四五六七八九十百千万亿\d]+)章[::]?\s*(.+)', content)
45
+ if matches:
46
+ chapter_num, chapter_title = matches[-1]
47
+ # 如果章节号是数字,直接使用;如果是汉字,转换为数字
48
+ if chapter_num.isdigit():
49
+ return f"第{chapter_num}章:{chapter_title.strip()}"
50
+ else:
51
+ # 这里可以添加一个函数来将汉字数字转换为阿拉伯数字
52
+ # 为了简单起见,这里仍然使用汉字表示
53
+ return f"第{chapter_num}章:{chapter_title.strip()}"
54
+ return "未知章节"
55
+
56
+ # 修改保存进度函数
57
+ def save_progress(aign):
58
+ chapter_name = get_chapter_name(aign.novel_content)
59
+ # 移除文件名中的非法字符
60
+ filename = re.sub(r'[\\/*?:"<>|]', '', chapter_name)
61
+ filename = f"{filename}.json"
62
+
63
+ data = {
64
+ "novel_outline": aign.novel_outline,
65
+ "paragraph_list": aign.paragraph_list,
66
+ "novel_content": aign.novel_content,
67
+ "writing_plan": aign.writing_plan,
68
+ "temp_setting": aign.temp_setting,
69
+ "writing_memory": aign.writing_memory,
70
+ "user_idea": aign.user_idea,
71
+ "user_requriments": aign.user_requriments,
72
+ }
73
+
74
+ json_str = json.dumps(data, ensure_ascii=False, indent=2)
75
+ b64 = base64.b64encode(json_str.encode()).decode()
76
+ href = f"data:application/json;base64,{b64}"
77
+ download_link = f'<a href="{href}" download="{filename}">点击下载进度文件</a>'
78
+ return download_link
79
+
80
+ # 修改加载函数
81
+ def load_progress(aign, file):
82
+ try:
83
+ if file is None:
84
+ return aign, "请选择要加载的文件", None, None, None, None, None, None, None, None
85
+
86
+ # 检查 file 是否为字符串(文件路径)
87
+ if isinstance(file, str):
88
+ with open(file, 'r', encoding='utf-8') as f:
89
+ content = f.read()
90
+ # 检查 file 是否有 name 属性(Gradio File 对象)
91
+ elif hasattr(file, 'name'):
92
+ with open(file.name, 'r', encoding='utf-8') as f:
93
+ content = f.read()
94
+ else:
95
+ return aign, f"无法读取文件", None, None, None, None, None, None, None, None
96
+
97
+ data = json.loads(content)
98
+
99
+ aign.novel_outline = data["novel_outline"]
100
+ aign.paragraph_list = data["paragraph_list"]
101
+ aign.novel_content = data["novel_content"]
102
+ aign.writing_plan = data["writing_plan"]
103
+ aign.temp_setting = data["temp_setting"]
104
+ aign.writing_memory = data["writing_memory"]
105
+ aign.user_idea = data["user_idea"]
106
+ aign.user_requriments = data["user_requriments"]
107
+ #aign.embellishment_idea = data["embellishment_idea"]
108
+ aign.update_chapter_list()
109
+ chapter_choices = [title for title, _ in aign.chapter_list]
110
+
111
+ return aign, f"进度已加载", data["novel_outline"], data["novel_content"], data["writing_plan"], data["temp_setting"], data["writing_memory"], data["user_idea"], data["user_requriments"], gr.Dropdown(choices=chapter_choices, value=chapter_choices[-1] if chapter_choices else None)
112
+ except Exception as e:
113
+ return aign, f"加载失败: {str(e)}", None, None, None, None, None, None, None, None
114
+
115
+ # 新增保存正文函数
116
+ def save_content(content):
117
+ if not content:
118
+ return "正文为空,无需保存"
119
+
120
+ b64 = base64.b64encode(content.encode()).decode()
121
+ href = f"data:text/plain;base64,{b64}"
122
+ download_link = f'<a href="{href}" download="novel_content.txt">点击下载正文</a>'
123
+ return download_link
124
+
125
+ # 添加撤销功能的事件处理
126
+ def undo_generation(aign, history):
127
+ if aign.undo():
128
+ return [
129
+ aign,
130
+ history,
131
+ aign.writing_plan,
132
+ aign.temp_setting,
133
+ aign.writing_memory,
134
+ aign.novel_content,
135
+ gr.Button(visible=True),
136
+ ]
137
+ else:
138
+ return [
139
+ aign,
140
+ history + [["系统", "无法撤销,已经是最初状态"]],
141
+ aign.writing_plan,
142
+ aign.temp_setting,
143
+ aign.writing_memory,
144
+ aign.novel_content,
145
+ gr.Button(visible=True),
146
+ ]
147
+
148
+ def display_chapter(chapters, page_num):
149
+ if 1 <= page_num <= len(chapters):
150
+ chapter_title, chapter_content = chapters[page_num - 1]
151
+ return f"{chapter_title}\n\n{chapter_content}"
152
+ else:
153
+ return "没有更多章节了。"
154
+
155
+ def prev_page(aign, current_page):
156
+ chapters = aign.chapter_list
157
+ if current_page > 1:
158
+ current_page -= 1
159
+ return current_page, display_chapter(chapters, current_page)
160
+
161
+ def next_page(aign, current_page):
162
+ chapters = aign.chapter_list
163
+ if current_page < len(chapters):
164
+ current_page += 1
165
+ return current_page, display_chapter(chapters, current_page)
166
+
167
+ def select_chapter(aign, chapter_title):
168
+ chapters = aign.chapter_list
169
+ for i, (title, _) in enumerate(chapters, start=1):
170
+ if title == chapter_title:
171
+ return i, display_chapter(chapters, i)
172
+ return 1, "章节未找到。"
173
+
174
+
175
+ def make_middle_chat():
176
+ carrier = threading.Event()
177
+ carrier.history = []
178
+
179
+ def middle_chat(messages, temperature=None, top_p=None):
180
+ nonlocal carrier
181
+ carrier.history.append([None, ""])
182
+ if len(carrier.history) > 20:
183
+ carrier.history = carrier.history[-16:]
184
+ try:
185
+ for resp in chatLLM(
186
+ messages, temperature=temperature, top_p=top_p, stream=True, model=CURRENT_MODEL
187
+ ):
188
+ output_text = resp["content"]
189
+ total_tokens = resp["total_tokens"]
190
+
191
+ carrier.history[-1][1] = f"total_tokens: {total_tokens}\n{output_text}"
192
+ return {
193
+ "content": output_text,
194
+ "total_tokens": total_tokens,
195
+ }
196
+ except Exception as e:
197
+ carrier.history[-1][1] = f"Error: {e}"
198
+ raise e
199
+
200
+ return carrier, middle_chat
201
+
202
+
203
+ def gen_ouline_button_clicked(aign, user_idea, history):
204
+ clear_console() # 清空命令行
205
+ aign.user_idea = user_idea
206
+
207
+ carrier, middle_chat = make_middle_chat()
208
+ carrier.history = [] # 清空历史记录
209
+ aign.novel_outline_writer.chatLLM = middle_chat
210
+
211
+ gen_ouline_thread = threading.Thread(target=aign.genNovelOutline)
212
+ gen_ouline_thread.start()
213
+
214
+ while gen_ouline_thread.is_alive():
215
+ yield [
216
+ aign,
217
+ carrier.history,
218
+ aign.novel_outline,
219
+ gr.Button(visible=False),
220
+ ]
221
+ time.sleep(STREAM_INTERVAL)
222
+ yield [
223
+ aign,
224
+ carrier.history,
225
+ aign.novel_outline,
226
+ gr.Button(visible=True),
227
+ ]
228
+
229
+
230
+ def gen_beginning_button_clicked(
231
+ aign, history, novel_outline, user_requriments#, embellishment_idea
232
+ ):
233
+ clear_console() # 清空命令行
234
+ aign.novel_outline = novel_outline
235
+ aign.user_requriments = user_requriments
236
+ #aign.embellishment_idea = embellishment_idea
237
+
238
+ carrier, middle_chat = make_middle_chat()
239
+ carrier.history = [] # 清空历史记录
240
+ aign.novel_beginning_writer.chatLLM = middle_chat
241
+ #aign.novel_embellisher.chatLLM = middle_chat
242
+
243
+ gen_beginning_thread = threading.Thread(target=aign.genBeginning)
244
+ gen_beginning_thread.start()
245
+
246
+ while gen_beginning_thread.is_alive():
247
+ yield [
248
+ aign,
249
+ carrier.history,
250
+ aign.writing_plan,
251
+ aign.temp_setting,
252
+ aign.novel_content,
253
+ gr.Button(visible=False),
254
+ ]
255
+ time.sleep(STREAM_INTERVAL)
256
+ yield [
257
+ aign,
258
+ carrier.history,
259
+ aign.writing_plan,
260
+ aign.temp_setting,
261
+ aign.novel_content,
262
+ gr.Button(visible=True),
263
+ ]
264
+
265
+
266
+ def gen_next_paragraph_button_clicked(
267
+ aign,
268
+ history,
269
+ user_idea,
270
+ novel_outline,
271
+ writing_memory,
272
+ temp_setting,
273
+ writing_plan,
274
+ user_requriments,
275
+ #embellishment_idea,
276
+ ):
277
+ # 生成保存进度链接
278
+ save_link = save_progress(aign) # 在函数开头定义保存进度链接
279
+
280
+ # 清空命令行
281
+ clear_console()
282
+
283
+ # 更新 AIGN 对象的各个字段
284
+ aign.user_idea = user_idea
285
+ aign.novel_outline = novel_outline
286
+ aign.writing_memory = writing_memory
287
+ aign.temp_setting = temp_setting
288
+ aign.writing_plan = writing_plan
289
+ aign.user_requriments = user_requriments
290
+ #aign.embellishment_idea = embellishment_idea
291
+
292
+ carrier, middle_chat = make_middle_chat()
293
+ carrier.history = [] # 清空历史记录
294
+ aign.novel_writer.chatLLM = middle_chat
295
+ #aign.novel_embellisher.chatLLM = middle_chat
296
+ aign.memory_maker.chatLLM = middle_chat
297
+
298
+ gen_next_paragraph_thread = threading.Thread(target=aign.genNextParagraph)
299
+ gen_next_paragraph_thread.start()
300
+
301
+ while gen_next_paragraph_thread.is_alive():
302
+ # 生成保存进度链接
303
+ save_link = save_progress(aign)
304
+
305
+ aign.update_chapter_list()
306
+ # 获取当前章节内容
307
+ current_chapter_content = display_chapter(aign.chapter_list, len(aign.chapter_list))
308
+ chapter_choices = [title for title, _ in aign.chapter_list]
309
+ yield [
310
+ aign,
311
+ carrier.history,
312
+ aign.writing_plan,
313
+ aign.temp_setting,
314
+ aign.writing_memory,
315
+ #aign.novel_content,#全部章节内容
316
+ current_chapter_content, # 只更新当前章节内容
317
+ gr.Button(visible=False),
318
+ save_link, # 返回生成的保存进度链接
319
+ gr.Dropdown(choices=chapter_choices, value=chapter_choices[-1] if chapter_choices else None),
320
+ ]
321
+ time.sleep(STREAM_INTERVAL)
322
+
323
+ # 生成保存进度链接
324
+ save_link = save_progress(aign)
325
+
326
+ aign.update_chapter_list()
327
+ # 获取最终的当前章节内容
328
+ current_chapter_content = display_chapter(aign.chapter_list, len(aign.chapter_list))
329
+ chapter_choices = [title for title, _ in aign.chapter_list]
330
+ yield [
331
+ aign,
332
+ carrier.history,
333
+ aign.writing_plan,
334
+ aign.temp_setting,
335
+ aign.writing_memory,
336
+ #aign.novel_content, #全部章节内容
337
+ current_chapter_content, # 只更新当前章节内容
338
+ gr.Button(visible=True),
339
+ save_link, # 返回生成的保存进度链接
340
+ gr.Dropdown(choices=chapter_choices, value=chapter_choices[-1] if chapter_choices else None),
341
+ ]
342
+
343
+
344
+ css = """
345
+ #row1 {
346
+ min-width: 200px;
347
+ height: calc(100vh - 180px);
348
+ overflow: auto;
349
+ }
350
+ #row2 {
351
+ min-width: 300px;
352
+ height: calc(100vh - 180px);
353
+ overflow: auto;
354
+ }
355
+ #row3 {
356
+ min-width: 200px;
357
+ height: calc(100vh - 180px);
358
+ overflow: auto;
359
+ }
360
+ """
361
+
362
+ with gr.Blocks(css=css) as demo:
363
+ aign = gr.State(AIGN(chatLLM))
364
+ gr.Markdown("## AI 写小说 Demo")
365
+
366
+ gr.Markdown("模型服务出问题会导致反复重试,这时切换到可用模型会继续,只要不刷新网页或者保存了进度(至少生成开头之后保存)就可以无限续写(某些模型智能及上下文有限,到极限会导致内容重复,这些模型在这就不适用了,比如gpt-3.5-turbo,当然接着切换到强模型可以再继续)")
367
+
368
+ # 添加模型选择下拉框
369
+ # with gr.Row(): 同一行
370
+ with gr.Row():
371
+ with gr.Column(scale=0, elem_id="row1"):
372
+ with gr.Tab("⚙"):
373
+ model_dropdown = gr.Dropdown(
374
+ choices=get_model_options(),
375
+ label="选择模型",
376
+ interactive=True,
377
+ value=CURRENT_MODEL
378
+ )
379
+ model_output = gr.Textbox(label="模型设置结果", interactive=False)
380
+ load_status = gr.Textbox(label="加载状态", interactive=False)
381
+ load_file = gr.File(label="选择加载文件", file_count="single", file_types=[".json"])
382
+ load_button = gr.Button("加载进度")
383
+ with gr.Tab("开始"):
384
+ gr.Markdown("生成大纲->大纲标签->生成开头->状态标签->生成下一段")
385
+ user_idea_text = gr.Textbox(
386
+ "将古龙小说仙侠化",
387
+ label="想法",
388
+ lines=16,
389
+ interactive=True,
390
+ )
391
+ user_requriments_text = gr.Textbox(
392
+ "",
393
+ label="写作要求",
394
+ lines=6,
395
+ interactive=True,
396
+ )
397
+ gen_ouline_button = gr.Button("生成大纲")
398
+ with gr.Tab("大纲"):
399
+ novel_outline_text = gr.Textbox(
400
+ label="大纲", lines=30, interactive=True
401
+ )
402
+ gen_beginning_button = gr.Button("生成开头")
403
+ with gr.Tab("状态"):
404
+ writing_memory_text = gr.Textbox(
405
+ label="记忆",
406
+ lines=8,
407
+ interactive=True,
408
+ max_lines=8,
409
+ )
410
+ writing_plan_text = gr.Textbox(label="计划", lines=6, interactive=True, max_lines=6)
411
+ temp_setting_text = gr.Textbox(
412
+ label="临时设定", lines=5, interactive=True, max_lines=5
413
+ )
414
+ gen_next_paragraph_button = gr.Button("生成下一段")
415
+ download_link = gr.HTML()
416
+ save_button = gr.Button("保存进度")
417
+ with gr.Tab("导航"):
418
+ save_content_button = gr.Button("保存正文")
419
+ current_page = gr.Number(value=1, label="当前页码", interactive=False)
420
+ prev_button = gr.Button("上一页")
421
+ next_button = gr.Button("下一页")
422
+
423
+ # 添加章节导航下拉列表
424
+ chapter_dropdown = gr.Dropdown(label="章节导航", choices=[], interactive=True)
425
+
426
+ # TODO
427
+ # gen_next_paragraph_button = gr.Button("撤销生成")
428
+ #undo_button = gr.Button("撤销生成")
429
+ # TODO
430
+ # auto_gen_next_checkbox = gr.Checkbox(
431
+ # label="自动生成下一段", checked=False, interactive=True
432
+ # )
433
+
434
+ with gr.Column(scale=3, elem_id="row2"):
435
+ chatBox = gr.Chatbot(height=f"80vh", label="输出")
436
+ with gr.Column(scale=0, elem_id="row3"):
437
+ novel_content_text = gr.Textbox(
438
+ label="小说正文", lines=37, interactive=True, show_copy_button=True
439
+ )
440
+ # TODO
441
+ # download_novel_button = gr.Button("下载小说")
442
+
443
+ #gr.Markdown("导航: https://deem.love")
444
+
445
+ prev_button.click(
446
+ prev_page,
447
+ inputs=[aign, current_page],
448
+ outputs=[current_page, novel_content_text],
449
+ queue=False
450
+ )
451
+
452
+ next_button.click(
453
+ next_page,
454
+ inputs=[aign, current_page],
455
+ outputs=[current_page, novel_content_text],
456
+ queue=False
457
+ )
458
+
459
+ # 添加章节下拉列表的事件处理
460
+ chapter_dropdown.change(
461
+ select_chapter,
462
+ inputs=[aign, chapter_dropdown],
463
+ outputs=[current_page, novel_content_text],
464
+ queue=False
465
+ )
466
+
467
+ # 添加模型选择的事件处理
468
+ model_dropdown.change(
469
+ set_api_model,
470
+ inputs=[model_dropdown],
471
+ outputs=[model_output]
472
+ )
473
+
474
+ # 修改按钮事件
475
+ save_button.click(
476
+ save_progress,
477
+ inputs=[aign],
478
+ outputs=[download_link]
479
+ )
480
+
481
+ # 修改加载按钮事件,包含章节下拉列表的更新
482
+ load_button.click(
483
+ load_progress,
484
+ inputs=[aign, load_file],
485
+ outputs=[aign, load_status, novel_outline_text, novel_content_text, writing_plan_text, temp_setting_text, writing_memory_text, user_idea_text, user_requriments_text, chapter_dropdown]
486
+ )
487
+
488
+ # 添加保存正文按钮事件
489
+ save_content_button.click(
490
+ save_content,
491
+ inputs=[novel_content_text],
492
+ outputs=[download_link]
493
+ )
494
+
495
+ #undo_button.click(
496
+ #undo_generation,
497
+ #[aign, chatBox],
498
+ #[aign, chatBox, writing_plan_text, temp_setting_text, writing_memory_text, novel_content_text, undo_button],
499
+ #)
500
+
501
+ gen_ouline_button.click(
502
+ gen_ouline_button_clicked,
503
+ [aign, user_idea_text, chatBox],
504
+ [aign, chatBox, novel_outline_text, gen_ouline_button],
505
+ )
506
+ gen_beginning_button.click(
507
+ gen_beginning_button_clicked,
508
+ [
509
+ aign,
510
+ chatBox,
511
+ novel_outline_text,
512
+ user_requriments_text,
513
+ #embellishment_idea_text,
514
+ ],
515
+ [
516
+ aign,
517
+ chatBox,
518
+ writing_plan_text,
519
+ temp_setting_text,
520
+ novel_content_text,
521
+ gen_beginning_button,
522
+ ],
523
+ )
524
+
525
+ # 修改生成下一段按钮事件,包含章节下拉列表的更新
526
+ gen_next_paragraph_button.click(
527
+ gen_next_paragraph_button_clicked,
528
+ [
529
+ aign,
530
+ chatBox,
531
+ user_idea_text,
532
+ novel_outline_text,
533
+ writing_memory_text,
534
+ temp_setting_text,
535
+ writing_plan_text,
536
+ user_requriments_text,
537
+ #embellishment_idea_text,
538
+ ],
539
+ [
540
+ aign,
541
+ chatBox,
542
+ writing_plan_text,
543
+ temp_setting_text,
544
+ writing_memory_text,
545
+ novel_content_text,
546
+ gen_next_paragraph_button,
547
+ download_link, # 这里添加输出到下载链接
548
+ chapter_dropdown
549
+ ],
550
+ )
551
+
552
+ if __name__ == "__main__":
553
+ demo.queue()
554
+ port = int(os.environ.get("PORT", 7860))
555
+ demo.launch(server_name="0.0.0.0", server_port=port)
openAI.py ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+
3
+ from openai import OpenAI
4
+
5
+
6
+ def openAIChatLLM(model_name=None, api_key=None, base_url=None):
7
+ """
8
+ model_name 取值
9
+ - deepseek-chat
10
+ """
11
+ api_key = os.environ.get("OPENAI_API_KEY", api_key)
12
+ base_url = os.environ.get("OPENAI_BASE_URL", base_url)
13
+ model_name = os.environ.get("OPENAI_API_MODEL", model_name)
14
+ client = OpenAI(api_key=api_key, base_url=base_url)
15
+
16
+ def chatLLM(
17
+ messages: list,
18
+ temperature=None,
19
+ top_p=None,
20
+ max_tokens=None,
21
+ stream=False,
22
+ model=model_name, #可使用自定义模型
23
+ ) -> dict:
24
+ if not stream:
25
+ response = client.chat.completions.create(
26
+ #model=model_name,
27
+ model=model,
28
+ messages=messages,
29
+ temperature=temperature,
30
+ top_p=top_p,
31
+ max_tokens=max_tokens,
32
+ )
33
+ return {
34
+ "content": response.choices[0].message.content,
35
+ "total_tokens": response.usage.total_tokens,
36
+ }
37
+ else:
38
+ responses = client.chat.completions.create(
39
+ #model=model_name,
40
+ model=model,
41
+ messages=messages,
42
+ temperature=temperature,
43
+ top_p=top_p,
44
+ max_tokens=max_tokens,
45
+ stream=True,
46
+ )
47
+
48
+ def respGenerator():
49
+ content = ""
50
+ for response in responses:
51
+ delta = response.choices[0].delta.content
52
+
53
+ #判断内容时候为空,非空才添加
54
+ if delta is not None:
55
+ content += delta
56
+ #content += delta
57
+
58
+ # if response.usage:
59
+ # total_tokens = response.usage.total_tokens
60
+ # else:
61
+ total_tokens = None
62
+
63
+ yield {
64
+ "content": content,
65
+ "total_tokens": total_tokens,
66
+ }
67
+
68
+ return respGenerator()
69
+
70
+ return chatLLM