Merge pull request #185 from PenBo1/main

Add files via upload
This commit is contained in:
xianyun
2025-09-08 19:57:37 +08:00
committed by GitHub
29 changed files with 265 additions and 38 deletions
+10
View File
@@ -58,5 +58,15 @@
"architecture_llm": "Gemini 2.5 Pro", "architecture_llm": "Gemini 2.5 Pro",
"final_chapter_llm": "GPT 5", "final_chapter_llm": "GPT 5",
"consistency_review_llm": "DeepSeek V3" "consistency_review_llm": "DeepSeek V3"
},
"proxy_setting": {
"proxy_url": "127.0.0.1",
"proxy_port": "",
"enabled": false
},
"webdav_config": {
"webdav_url": "",
"webdav_username": "",
"webdav_password": ""
} }
} }
+86 -3
View File
@@ -8,15 +8,98 @@ from embedding_adapters import create_embedding_adapter
def load_config(config_file: str) -> dict: def load_config(config_file: str) -> dict:
"""从指定的 config_file 加载配置,若不存在则返回空字典""" """从指定的 config_file 加载配置,若不存在则创建一个默认配置文件"""
if os.path.exists(config_file):
# PenBo 修改代码,增加配置文件不存在则创建一个默认配置文件
if not os.path.exists(config_file):
create_config(config_file)
try: try:
with open(config_file, 'r', encoding='utf-8') as f: with open(config_file, 'r', encoding='utf-8') as f:
return json.load(f) return json.load(f)
except: except:
pass
return {} return {}
# PenBo 增加了创建默认配置文件函数
def create_config(config_file: str) -> dict:
"""创建一个创建默认配置文件。"""
config = {
"last_interface_format": "OpenAI",
"last_embedding_interface_format": "OpenAI",
"llm_configs": {
"DeepSeek V3": {
"api_key": "",
"base_url": "https://api.deepseek.com/v1",
"model_name": "deepseek-chat",
"temperature": 0.7,
"max_tokens": 8192,
"timeout": 600,
"interface_format": "OpenAI"
},
"GPT 5": {
"api_key": "",
"base_url": "https://api.openai.com/v1",
"model_name": "gpt-5",
"temperature": 0.7,
"max_tokens": 32768,
"timeout": 600,
"interface_format": "OpenAI"
},
"Gemini 2.5 Pro": {
"api_key": "",
"base_url": "https://generativelanguage.googleapis.com/v1beta/openai",
"model_name": "gemini-2.5-pro",
"temperature": 0.7,
"max_tokens": 32768,
"timeout": 600,
"interface_format": "OpenAI"
}
},
"embedding_configs": {
"OpenAI": {
"api_key": "",
"base_url": "https://api.openai.com/v1",
"model_name": "text-embedding-ada-002",
"retrieval_k": 4,
"interface_format": "OpenAI"
}
},
"other_params": {
"topic": "",
"genre": "",
"num_chapters": 0,
"word_number": 0,
"filepath": "",
"chapter_num": "120",
"user_guidance": "",
"characters_involved": "",
"key_items": "",
"scene_location": "",
"time_constraint": ""
},
"choose_configs": {
"prompt_draft_llm": "DeepSeek V3",
"chapter_outline_llm": "DeepSeek V3",
"architecture_llm": "Gemini 2.5 Pro",
"final_chapter_llm": "GPT 5",
"consistency_review_llm": "DeepSeek V3"
},
"proxy_setting": {
"proxy_url": "127.0.0.1",
"proxy_port": "",
"enabled": false
},
"webdav_config": {
"webdav_url": "",
"webdav_username": "",
"webdav_password": ""
}
}
save_config(config, config_file)
def save_config(config_data: dict, config_file: str) -> bool: def save_config(config_data: dict, config_file: str) -> bool:
"""将 config_data 保存到 config_file 中,返回 True/False 表示是否成功。""" """将 config_data 保存到 config_file 中,返回 True/False 表示是否成功。"""
try: try:
+17 -8
View File
@@ -100,26 +100,35 @@ class GeminiAdapter(BaseLLMAdapter):
""" """
适配 Google Gemini (Google Generative AI) 接口 适配 Google Gemini (Google Generative AI) 接口
""" """
# PenBo 修复新版本google-generativeai 不支持 Client 类问题;而是使用 GenerativeModel 类来访问API
def __init__(self, api_key: str, base_url: str, model_name: str, max_tokens: int, temperature: float = 0.7, timeout: Optional[int] = 600): def __init__(self, api_key: str, base_url: str, model_name: str, max_tokens: int, temperature: float = 0.7, timeout: Optional[int] = 600):
self.api_key = api_key self.api_key = api_key
self.model_name = model_name self.model_name = model_name
self.max_tokens = max_tokens self.max_tokens = max_tokens
self.temperature = temperature self.temperature = temperature
# gemini超时时间是毫秒 self.timeout = timeout
self.timeout = timeout * 1000
self._client = genai.Client(api_key=self.api_key,http_options=types.HttpOptions(base_url=base_url,timeout=self.timeout)) # 配置API密钥
genai.configure(api_key=self.api_key)
# 创建生成模型实例
self._model = genai.GenerativeModel(model_name=self.model_name)
def invoke(self, prompt: str) -> str: def invoke(self, prompt: str) -> str:
try: try:
response = self._client.models.generate_content( # 设置生成配置
model = self.model_name, generation_config = genai.types.GenerationConfig(
contents = prompt,
config = types.GenerateContentConfig(
max_output_tokens=self.max_tokens, max_output_tokens=self.max_tokens,
temperature=self.temperature, temperature=self.temperature,
),
) )
# 生成内容
response = self._model.generate_content(
prompt,
generation_config=generation_config
)
if response and response.text: if response and response.text:
return response.text return response.text
else: else:
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+84
View File
@@ -9,6 +9,8 @@ import customtkinter as ctk
from config_manager import load_config, save_config from config_manager import load_config, save_config
from tooltips import tooltips from tooltips import tooltips
import os
def create_label_with_help(self, parent, label_text, tooltip_key, row, column, def create_label_with_help(self, parent, label_text, tooltip_key, row, column,
font=None, sticky="e", padx=5, pady=5): font=None, sticky="e", padx=5, pady=5):
@@ -45,11 +47,17 @@ def build_config_tabview(self):
self.embeddings_config_tab = self.config_tabview.add("Embedding settings") self.embeddings_config_tab = self.config_tabview.add("Embedding settings")
self.config_choose = self.config_tabview.add("Config choose") self.config_choose = self.config_tabview.add("Config choose")
# PenBo 增加代理功能支持
self.proxy_setting_tab = self.config_tabview.add("Proxy setting")
build_ai_config_tab(self) build_ai_config_tab(self)
build_embeddings_config_tab(self) build_embeddings_config_tab(self)
build_config_choose_tab(self) build_config_choose_tab(self)
# PenBo 增加代理功能支持
build_proxy_setting_tab(self)
def build_ai_config_tab(self): def build_ai_config_tab(self):
def refresh_config_dropdown(): def refresh_config_dropdown():
"""刷新配置下拉菜单""" """刷新配置下拉菜单"""
@@ -607,8 +615,84 @@ def build_config_choose_tab(self):
# PenBo 增加代理功能支持
def build_proxy_setting_tab(self):
# 代理设置标签页布局
for i in range(5):
self.proxy_setting_tab.grid_rowconfigure(i, weight=0)
self.proxy_setting_tab.grid_columnconfigure(0, weight=0)
self.proxy_setting_tab.grid_columnconfigure(1, weight=1)
# 从配置文件加载代理设置
config_data = load_config(self.config_file)
proxy_setting = config_data.get("proxy_setting", {})
# 代理启用开关
create_label_with_help(self, self.proxy_setting_tab, "启用代理:", "proxy_enabled", 0, 0)
self.proxy_enabled_var = ctk.BooleanVar(value=proxy_setting.get("enabled", False))
proxy_enabled_switch = ctk.CTkSwitch(
self.proxy_setting_tab,
text="",
variable=self.proxy_enabled_var,
onvalue=True,
offvalue=False,
font=("Microsoft YaHei", 12)
)
proxy_enabled_switch.grid(row=0, column=1, padx=5, pady=5, sticky="w")
# 地址输入框
create_label_with_help(self, self.proxy_setting_tab, "地址:", "proxy_address", 1, 0)
self.proxy_address_var = ctk.StringVar(value=proxy_setting.get("proxy_url", "127.0.0.1"))
proxy_address_entry = ctk.CTkEntry(
self.proxy_setting_tab,
textvariable=self.proxy_address_var,
font=("Microsoft YaHei", 12)
)
proxy_address_entry.grid(row=1, column=1, padx=5, pady=5, sticky="nsew")
# 端口输入框
create_label_with_help(self, self.proxy_setting_tab, "端口:", "proxy_port", 2, 0)
self.proxy_port_var = ctk.StringVar(value=proxy_setting.get("proxy_port", "10809"))
proxy_port_entry = ctk.CTkEntry(
self.proxy_setting_tab,
textvariable=self.proxy_port_var,
font=("Microsoft YaHei", 12)
)
proxy_port_entry.grid(row=2, column=1, padx=5, pady=5, sticky="nsew")
def open_proxy(address, port):
"""启动代理"""
# 设置环境变量
os.environ['HTTP_PROXY'] = f"http://{address}:{port}"
os.environ['HTTPS_PROXY'] = f"http://{address}:{port}"
def save_proxy_setting():
config_data = load_config(self.config_file)
if "proxy_setting" not in config_data:
config_data["proxy_setting"] = {}
config_data["proxy_setting"]["enabled"] = self.proxy_enabled_var.get()
config_data["proxy_setting"]["proxy_url"] = self.proxy_address_var.get()
config_data["proxy_setting"]["proxy_port"] = self.proxy_port_var.get()
save_config(config_data, self.config_file)
messagebox.showinfo("提示", "代理配置已保存。")
if self.proxy_enabled_var.get():
open_proxy(self.proxy_address_var.get(), self.proxy_port_var.get())
else:
os.environ.pop('HTTP_PROXY', None)
os.environ.pop('HTTPS_PROXY', None)
# 添加保存按钮
save_btn = ctk.CTkButton(
self.proxy_setting_tab,
text="保存代理设置",
command=save_proxy_setting,
font=("Microsoft YaHei", 12)
)
save_btn.grid(row=3, column=0, columnspan=2, padx=5, pady=5, sticky="ew")
+48 -19
View File
@@ -445,38 +445,58 @@ def do_consistency_check(self):
self.enable_button_safe(self.btn_check_consistency) self.enable_button_safe(self.btn_check_consistency)
threading.Thread(target=task, daemon=True).start() threading.Thread(target=task, daemon=True).start()
def generate_batch_ui(self): def generate_batch_ui(self):
# PenBo 优化界面,使用customtkinter进行批量生成章节界面
def open_batch_dialog(): def open_batch_dialog():
dialog = tk.Toplevel() dialog = ctk.CTkToplevel()
dialog.title("批量生成章节")
chapter_file = os.path.join(self.filepath_var.get().strip(), "chapters") chapter_file = os.path.join(self.filepath_var.get().strip(), "chapters")
files = glob.glob(os.path.join(chapter_file, "chapter_*.txt")) files = glob.glob(os.path.join(chapter_file, "chapter_*.txt"))
if not files: if not files:
num = 1 num = 1
else: else:
num = max(int(os.path.basename(f).split('_')[1].split('.')[0]) for f in files) + 1 num = max(int(os.path.basename(f).split('_')[1].split('.')[0]) for f in files) + 1
dialog.geometry("+500+400")
tk.Label(dialog, text="起始章节").grid(row=0, column=0) dialog.geometry("400x200")
entry_start = tk.Entry(dialog) dialog.resizable(False, False)
entry_start.grid(row=0, column=1)
# 创建网格布局
dialog.grid_columnconfigure(0, weight=0)
dialog.grid_columnconfigure(1, weight=1)
dialog.grid_columnconfigure(2, weight=0)
dialog.grid_columnconfigure(3, weight=1)
# 起始章节
ctk.CTkLabel(dialog, text="起始章节:").grid(row=0, column=0, padx=10, pady=10, sticky="w")
entry_start = ctk.CTkEntry(dialog)
entry_start.grid(row=0, column=1, padx=10, pady=10, sticky="ew")
entry_start.insert(0, str(num)) entry_start.insert(0, str(num))
tk.Label(dialog, text="结束章节").grid(row=0, column=2)
entry_end = tk.Entry(dialog) # 结束章节
entry_end.grid(row=0, column=3) ctk.CTkLabel(dialog, text="结束章节:").grid(row=0, column=2, padx=10, pady=10, sticky="w")
tk.Label(dialog, text="期望字数").grid(row=1, column=0) entry_end = ctk.CTkEntry(dialog)
entry_word = tk.Entry(dialog) entry_end.grid(row=0, column=3, padx=10, pady=10, sticky="ew")
entry_word.grid(row=1, column=1)
# 期望字数
ctk.CTkLabel(dialog, text="期望字数:").grid(row=1, column=0, padx=10, pady=10, sticky="w")
entry_word = ctk.CTkEntry(dialog)
entry_word.grid(row=1, column=1, padx=10, pady=10, sticky="ew")
entry_word.insert(0, self.word_number_var.get()) entry_word.insert(0, self.word_number_var.get())
tk.Label(dialog, text="最低字数").grid(row=1, column=2)
entry_min = tk.Entry(dialog) # 最低字数
entry_min.grid(row=1, column=3) ctk.CTkLabel(dialog, text="最低字数:").grid(row=1, column=2, padx=10, pady=10, sticky="w")
entry_min = ctk.CTkEntry(dialog)
entry_min.grid(row=1, column=3, padx=10, pady=10, sticky="ew")
entry_min.insert(0, self.word_number_var.get()) entry_min.insert(0, self.word_number_var.get())
auto_enrich_bool = tk.BooleanVar() # 自动扩写选项
auto_enrich_bool_ck = tk.Checkbutton(dialog, text="低于最低字数时自动扩写", variable=auto_enrich_bool) auto_enrich_bool = ctk.BooleanVar()
auto_enrich_bool_ck.grid(row=2, column=0) auto_enrich_bool_ck = ctk.CTkCheckBox(dialog, text="低于最低字数时自动扩写", variable=auto_enrich_bool)
auto_enrich_bool_ck.grid(row=2, column=0, columnspan=2, padx=10, pady=10, sticky="w")
result = {"start": None, "end": None, "word": None, "min": None, "auto_enrich": None, "close": False} result = {"start": None, "end": None, "word": None, "min": None, "auto_enrich": None, "close": False}
def on_confirm(): def on_confirm():
nonlocal result nonlocal result
if not entry_start.get() or not entry_end.get() or not entry_word.get() or not entry_min.get(): if not entry_start.get() or not entry_end.get() or not entry_word.get() or not entry_min.get():
@@ -497,7 +517,16 @@ def generate_batch_ui(self):
nonlocal result nonlocal result
result["close"] = True result["close"] = True
dialog.destroy() dialog.destroy()
tk.Button(dialog, text="确认", command=on_confirm).grid(row=2, column=1)
# 按钮框架
button_frame = ctk.CTkFrame(dialog)
button_frame.grid(row=3, column=0, columnspan=4, padx=10, pady=10, sticky="ew")
button_frame.grid_columnconfigure(0, weight=1)
button_frame.grid_columnconfigure(1, weight=1)
ctk.CTkButton(button_frame, text="确认", command=on_confirm).grid(row=0, column=0, padx=10, pady=10, sticky="e")
ctk.CTkButton(button_frame, text="取消", command=on_cancel).grid(row=0, column=1, padx=10, pady=10, sticky="w")
dialog.protocol("WM_DELETE_WINDOW", on_cancel) dialog.protocol("WM_DELETE_WINDOW", on_cancel)
dialog.transient(self.master) dialog.transient(self.master)
dialog.grab_set() dialog.grab_set()
+12
View File
@@ -88,6 +88,18 @@ class NovelGeneratorGUI:
"retrieval_k": 4 "retrieval_k": 4
} }
# PenBo 增加代理功能支持
proxy_url = self.loaded_config["proxy_setting"]["proxy_url"]
proxy_port = self.loaded_config["proxy_setting"]["proxy_port"]
if self.loaded_config["proxy_setting"]["enabled"]:
os.environ['HTTP_PROXY'] = f"http://{proxy_url}:{proxy_port}"
os.environ['HTTPS_PROXY'] = f"http://{proxy_url}:{proxy_port}"
else:
os.environ.pop('HTTP_PROXY', None)
os.environ.pop('HTTPS_PROXY', None)
# -- LLM通用参数 -- # -- LLM通用参数 --
# self.llm_conf_name = next(iter(self.loaded_config["llm_configs"])) # self.llm_conf_name = next(iter(self.loaded_config["llm_configs"]))
self.api_key_var = ctk.StringVar(value=llm_conf.get("api_key", "")) self.api_key_var = ctk.StringVar(value=llm_conf.get("api_key", ""))