deeme commited on
Commit
f664d36
·
verified ·
1 Parent(s): 6311de0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +556 -556
app.py CHANGED
@@ -1,556 +1,556 @@
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
- "架空历史:\n\n1. 选择关键历史节点并改变\n2. 描述由此引发的历史走向变化\n3. 塑造新兴历史人物(可改编或原创)\n4. 构建独特社会结构、文化和思潮\n5. 想象可能出现的科技创新\n6. 概述世界格局重塑(国界、政体、国际关系)\n7. 用生动叙事呈现,让读者身临其境\n\n目标:创造合理又富想象力的平行宇宙,既有趣又引人深思。",
387
- label="想法",
388
- lines=16,
389
- interactive=True,
390
- )
391
- user_requriments_text = gr.Textbox(
392
- "1. 语言要求:\n - 不直白\n - 句式多变\n - 避免陈词滥调\n - 使用不寻常的词句\n - 运用隐喻和象征\n2. 创作风格:\n - 抽象\n - 富有意境和想象力\n - 具创意个性\n - 有力度\n - 画面感强\n - 音乐感佳\n - 浪漫气息浓厚\n - 语言深邃\n3. 表达目标:\n - 传达独特的神秘和魔幻感\n - 探索和反思自我与世界\n - 表达对自己和社会的孤独与关注\n4. 读者体验:有趣、惊奇、新鲜",
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=33, interactive=True
401
- )
402
- #gen_beginning_button = gr.Button("生成开头")
403
- #gen_next_paragraph_button = gr.Button("生成开头")
404
- with gr.Tab("状态"):
405
- writing_memory_text = gr.Textbox(
406
- label="记忆",
407
- lines=8,
408
- interactive=True,
409
- max_lines=8,
410
- )
411
- writing_plan_text = gr.Textbox(label="计划", lines=6, interactive=True, max_lines=6)
412
- temp_setting_text = gr.Textbox(
413
- label="临时设定", lines=5, interactive=True, max_lines=5
414
- )
415
- gen_next_paragraph_button = gr.Button("生成章节")
416
- download_link = gr.HTML()
417
- save_button = gr.Button("保存进度")
418
- with gr.Tab("导航"):
419
- save_content_button = gr.Button("保存正文")
420
- current_page = gr.Number(value=1, label="当前页码", interactive=False)
421
- prev_button = gr.Button("上一页")
422
- next_button = gr.Button("下一页")
423
-
424
- # 添加章节导航下拉列表
425
- chapter_dropdown = gr.Dropdown(label="章节导航", choices=[], interactive=True)
426
-
427
- # TODO
428
- # gen_next_paragraph_button = gr.Button("撤销生成")
429
- #undo_button = gr.Button("撤销生成")
430
- # TODO
431
- # auto_gen_next_checkbox = gr.Checkbox(
432
- # label="自动生成下一段", checked=False, interactive=True
433
- # )
434
-
435
- with gr.Column(scale=3, elem_id="row2"):
436
- chatBox = gr.Chatbot(height=f"80vh", label="输出")
437
- with gr.Column(scale=0, elem_id="row3"):
438
- novel_content_text = gr.Textbox(
439
- label="小说正文", lines=35, interactive=True, show_copy_button=True
440
- )
441
- # TODO
442
- # download_novel_button = gr.Button("下载小说")
443
-
444
- #gr.Markdown("导航: https://deem.love")
445
-
446
- prev_button.click(
447
- prev_page,
448
- inputs=[aign, current_page],
449
- outputs=[current_page, novel_content_text],
450
- queue=False
451
- )
452
-
453
- next_button.click(
454
- next_page,
455
- inputs=[aign, current_page],
456
- outputs=[current_page, novel_content_text],
457
- queue=False
458
- )
459
-
460
- # 添加章节下拉列表的事件处理
461
- chapter_dropdown.change(
462
- select_chapter,
463
- inputs=[aign, chapter_dropdown],
464
- outputs=[current_page, novel_content_text],
465
- queue=False
466
- )
467
-
468
- # 添加模型选择的事件处理
469
- model_dropdown.change(
470
- set_api_model,
471
- inputs=[model_dropdown],
472
- outputs=[model_output]
473
- )
474
-
475
- # 修改按钮事件
476
- save_button.click(
477
- save_progress,
478
- inputs=[aign],
479
- outputs=[download_link]
480
- )
481
-
482
- # 修改加载按钮事件,包含章节下拉列表的更新
483
- load_button.click(
484
- load_progress,
485
- inputs=[aign, load_file],
486
- 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]
487
- )
488
-
489
- # 添加保存正文按钮事件
490
- save_content_button.click(
491
- save_content,
492
- inputs=[novel_content_text],
493
- outputs=[download_link]
494
- )
495
-
496
- #undo_button.click(
497
- #undo_generation,
498
- #[aign, chatBox],
499
- #[aign, chatBox, writing_plan_text, temp_setting_text, writing_memory_text, novel_content_text, undo_button],
500
- #)
501
-
502
- gen_ouline_button.click(
503
- gen_ouline_button_clicked,
504
- [aign, user_idea_text, chatBox],
505
- [aign, chatBox, novel_outline_text, gen_ouline_button],
506
- )
507
- #gen_beginning_button.click(
508
- #gen_beginning_button_clicked,
509
- #[
510
- #aign,
511
- #chatBox,
512
- #novel_outline_text,
513
- #user_requriments_text,
514
- #embellishment_idea_text,
515
- #],
516
- #[
517
- #aign,
518
- #chatBox,
519
- #writing_plan_text,
520
- #temp_setting_text,
521
- #novel_content_text,
522
- #gen_beginning_button,
523
- #],
524
- #)
525
-
526
- # 修改生成下一段按钮事件,包含章节下拉列表的更新
527
- gen_next_paragraph_button.click(
528
- gen_next_paragraph_button_clicked,
529
- [
530
- aign,
531
- chatBox,
532
- user_idea_text,
533
- novel_outline_text,
534
- writing_memory_text,
535
- temp_setting_text,
536
- writing_plan_text,
537
- user_requriments_text,
538
- #embellishment_idea_text,
539
- ],
540
- [
541
- aign,
542
- chatBox,
543
- writing_plan_text,
544
- temp_setting_text,
545
- writing_memory_text,
546
- novel_content_text,
547
- gen_next_paragraph_button,
548
- download_link, # 这里添加输出到下载链接
549
- chapter_dropdown
550
- ],
551
- )
552
-
553
- if __name__ == "__main__":
554
- demo.queue()
555
- port = int(os.environ.get("PORT", 7860))
556
- demo.launch(server_name="127.0.0.1", server_port=port, share=True)
 
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.gen11月elOutline)
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.3月kdown("## AI 写小说 Demo")
365
+
366
+ #gr.3月kdown("模型服务出问题会导致反复重试,这时切换到可用模型会继续,只要不刷新网页或者保存了进度(至少生成开头之后保存)就可以无限续写(某些模型智能及上下文有限,到极限会导致内容重复,这些模型在这就不适用了,比如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.3月kdown("生成大纲->大纲标签->生成章节->状态标签->生成章节")
385
+ user_idea_text = gr.Textbox(
386
+ "架空历史:\n\n1. 选择关键历史节点并改变\n2. 描述由此引发的历史走向变化\n3. 塑造新兴历史人物(可改编或原创)\n4. 构建独特社会结构、文化和思潮\n5. 想象可能出现的科技创新\n6. 概述世界格局重塑(国界、政体、国际关系)\n7. 用生动叙事呈现,让读者身临其境\n\n目标:创造合理又富想象力的平行宇宙,既有趣又引人深思。",
387
+ label="想法",
388
+ lines=16,
389
+ interactive=True,
390
+ )
391
+ user_requriments_text = gr.Textbox(
392
+ "1. 语言要求:\n - 不直白\n - 句式多变\n - 避免陈词滥调\n - 使用不寻常的词句\n - 运用隐喻和象征\n2. 创作风格:\n - 抽象\n - 富有意境和想象力\n - 具创意个性\n - 有力度\n - 画面感强\n - 音乐感佳\n - 浪漫气息浓厚\n - 语言深邃\n3. 表达目标:\n - 传达独特的神秘和魔幻感\n - 探索和反思自我与世界\n - 表达对自己和社会的孤独与关注\n4. 读者体验:有趣、惊奇、新鲜",
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=33, interactive=True
401
+ )
402
+ #gen_beginning_button = gr.Button("生成开头")
403
+ #gen_next_paragraph_button = gr.Button("生成开头")
404
+ with gr.Tab("状态"):
405
+ writing_memory_text = gr.Textbox(
406
+ label="记忆",
407
+ lines=8,
408
+ interactive=True,
409
+ max_lines=8,
410
+ )
411
+ writing_plan_text = gr.Textbox(label="计划", lines=6, interactive=True, max_lines=6)
412
+ temp_setting_text = gr.Textbox(
413
+ label="临时设定", lines=5, interactive=True, max_lines=5
414
+ )
415
+ gen_next_paragraph_button = gr.Button("生成章节")
416
+ download_link = gr.HTML()
417
+ save_button = gr.Button("保存进度")
418
+ with gr.Tab("导航"):
419
+ save_content_button = gr.Button("保存正文")
420
+ current_page = gr.Number(value=1, label="当前页码", interactive=False)
421
+ prev_button = gr.Button("上一页")
422
+ next_button = gr.Button("下一页")
423
+
424
+ # 添加章节导航下拉列表
425
+ chapter_dropdown = gr.Dropdown(label="章节导航", choices=[], interactive=True)
426
+
427
+ # TODO
428
+ # gen_next_paragraph_button = gr.Button("撤销生成")
429
+ #undo_button = gr.Button("撤销生成")
430
+ # TODO
431
+ # auto_gen_next_checkbox = gr.Checkbox(
432
+ # label="自动生成下一段", checked=False, interactive=True
433
+ # )
434
+
435
+ with gr.Column(scale=3, elem_id="row2"):
436
+ chatBox = gr.Chatbot(height=f"80vh", label="输出")
437
+ with gr.Column(scale=0, elem_id="row3"):
438
+ novel_content_text = gr.Textbox(
439
+ label="小说正文", lines=35, interactive=True, show_copy_button=True
440
+ )
441
+ # TODO
442
+ # download_novel_button = gr.Button("下载小说")
443
+
444
+ #gr.3月kdown("导航: https://deem.love")
445
+
446
+ prev_button.click(
447
+ prev_page,
448
+ inputs=[aign, current_page],
449
+ outputs=[current_page, novel_content_text],
450
+ queue=False
451
+ )
452
+
453
+ next_button.click(
454
+ next_page,
455
+ inputs=[aign, current_page],
456
+ outputs=[current_page, novel_content_text],
457
+ queue=False
458
+ )
459
+
460
+ # 添加章节下拉列表的事件处理
461
+ chapter_dropdown.change(
462
+ select_chapter,
463
+ inputs=[aign, chapter_dropdown],
464
+ outputs=[current_page, novel_content_text],
465
+ queue=False
466
+ )
467
+
468
+ # 添加模型选择的事件处理
469
+ model_dropdown.change(
470
+ set_api_model,
471
+ inputs=[model_dropdown],
472
+ outputs=[model_output]
473
+ )
474
+
475
+ # 修改按钮事件
476
+ save_button.click(
477
+ save_progress,
478
+ inputs=[aign],
479
+ outputs=[download_link]
480
+ )
481
+
482
+ # 修改加载按钮事件,包含章节下拉列表的更新
483
+ load_button.click(
484
+ load_progress,
485
+ inputs=[aign, load_file],
486
+ 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]
487
+ )
488
+
489
+ # 添加保存正文按钮事件
490
+ save_content_button.click(
491
+ save_content,
492
+ inputs=[novel_content_text],
493
+ outputs=[download_link]
494
+ )
495
+
496
+ #undo_button.click(
497
+ #undo_generation,
498
+ #[aign, chatBox],
499
+ #[aign, chatBox, writing_plan_text, temp_setting_text, writing_memory_text, novel_content_text, undo_button],
500
+ #)
501
+
502
+ gen_ouline_button.click(
503
+ gen_ouline_button_clicked,
504
+ [aign, user_idea_text, chatBox],
505
+ [aign, chatBox, novel_outline_text, gen_ouline_button],
506
+ )
507
+ #gen_beginning_button.click(
508
+ #gen_beginning_button_clicked,
509
+ #[
510
+ #aign,
511
+ #chatBox,
512
+ #novel_outline_text,
513
+ #user_requriments_text,
514
+ #embellishment_idea_text,
515
+ #],
516
+ #[
517
+ #aign,
518
+ #chatBox,
519
+ #writing_plan_text,
520
+ #temp_setting_text,
521
+ #novel_content_text,
522
+ #gen_beginning_button,
523
+ #],
524
+ #)
525
+
526
+ # 修改生成下一段按钮事件,包含章节下拉列表的更新
527
+ gen_next_paragraph_button.click(
528
+ gen_next_paragraph_button_clicked,
529
+ [
530
+ aign,
531
+ chatBox,
532
+ user_idea_text,
533
+ novel_outline_text,
534
+ writing_memory_text,
535
+ temp_setting_text,
536
+ writing_plan_text,
537
+ user_requriments_text,
538
+ #embellishment_idea_text,
539
+ ],
540
+ [
541
+ aign,
542
+ chatBox,
543
+ writing_plan_text,
544
+ temp_setting_text,
545
+ writing_memory_text,
546
+ novel_content_text,
547
+ gen_next_paragraph_button,
548
+ download_link, # 这里添加输出到下载链接
549
+ chapter_dropdown
550
+ ],
551
+ )
552
+
553
+ if __name__ == "__main__":
554
+ demo.queue()
555
+ port = int(os.environ.get("PORT", 7860))
556
+ demo.launch(server_name="127.0.0.1", server_port=port)