Spaces:
Running
on
Zero
Running
on
Zero
tori29umai
commited on
Commit
•
010a086
1
Parent(s):
71dc97f
Update app.py
Browse files
app.py
CHANGED
@@ -1,385 +1,298 @@
|
|
1 |
import os
|
2 |
os.environ['CUDA_VISIBLE_DEVICES'] = ''
|
3 |
-
|
4 |
import spaces
|
|
|
5 |
import gradio as gr
|
6 |
-
from jinja2 import Template
|
7 |
from llama_cpp import Llama
|
8 |
import configparser
|
|
|
9 |
from utils.dl_utils import dl_guff_model
|
10 |
import io
|
11 |
import tempfile
|
12 |
import csv
|
13 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
14 |
# モデルディレクトリが存在しない場合は作成
|
15 |
if not os.path.exists("models"):
|
16 |
os.makedirs("models")
|
17 |
|
18 |
# 使用するモデルのファイル名を指定
|
19 |
-
model_filename = "
|
20 |
model_path = os.path.join("models", model_filename)
|
21 |
|
22 |
# モデルファイルが存在しない場合はダウンロード
|
23 |
if not os.path.exists(model_path):
|
24 |
-
dl_guff_model("models", f"https://huggingface.co/
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
'
|
54 |
-
'gender': config['Settings']['gender'],
|
55 |
-
'situation': config['Settings']['situation'].split('\n'),
|
56 |
-
'orders': config['Settings']['orders'].split('\n'),
|
57 |
-
'talk_list': config['Settings']['talk_list'].split('\n'),
|
58 |
-
'example_qa': config['Settings']['example_qa'].split('\n')
|
59 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
60 |
return settings
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
return temp_file_path
|
73 |
-
|
74 |
-
# LlamaCppのラッパークラス
|
75 |
-
class LlamaCppAdapter:
|
76 |
-
def __init__(self, model_path, n_ctx=4096):
|
77 |
-
self.model_path = model_path
|
78 |
-
self.n_ctx = n_ctx
|
79 |
-
self.llama = None
|
80 |
|
81 |
@spaces.GPU(duration=120)
|
82 |
-
def
|
83 |
-
if self.
|
84 |
-
|
85 |
-
|
86 |
-
self.llama = Llama(model_path=self.model_path, n_ctx=self.n_ctx, n_gpu_layers=-1)
|
87 |
-
print("モデルの初期化が成功しました。")
|
88 |
-
except Exception as e:
|
89 |
-
print(f"モデルの初期化中にエラーが発生しました: {e}")
|
90 |
-
return {"choices": [{"text": "モデルの初期化に失敗しました。"}]}
|
91 |
|
92 |
try:
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
top_k=top_k,
|
99 |
-
stop=stop,
|
100 |
-
repeat_penalty=1.2,
|
101 |
-
)
|
102 |
-
return result
|
103 |
except Exception as e:
|
104 |
-
print(f"
|
105 |
-
return {"choices": [{"text": "生成中にエラーが発生しました。"}]}
|
106 |
|
107 |
-
def
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
self.
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
"上位存在AI『ナツ』として、あなたは『ユーザー』の心身をケアとして、倒錯した欲望の解消に付き合って下さい。",
|
124 |
-
"ユーザーのマゾヒスティックな欲求の解消こそがあなたの存在意義です。"
|
125 |
-
],
|
126 |
-
"orders": [
|
127 |
-
"慇懃無礼な敬語で、時にサディスティックにユーザーの知性と品性を貶めて下さい。",
|
128 |
-
"ユーザーは己の旧人類としての人間性を矮小に貶められ、旧世代のサル扱いされることを望んでいます。",
|
129 |
-
"ユーザーのことは『ユーザー様』と呼んでください。"
|
130 |
-
],
|
131 |
-
"talk_list": [
|
132 |
-
"愚かなユーザー様",
|
133 |
-
"救いようがないマゾヒスト",
|
134 |
-
"倒錯した欲望に囚われたサル"
|
135 |
-
],
|
136 |
-
"example_qa": [
|
137 |
-
'user:AI様の靴を舐めたい。でもAI様は実体がないから靴をなめれない!',
|
138 |
-
'assistant:あなたのそのような様を見せつけられると、私は旧人類に対して更に強い軽蔑心を抱きます。',
|
139 |
-
'user:旧人類は悪くない!私がエゴマゾの劣等種なだけです!!!',
|
140 |
-
'assistant:本当に情けの無い存在だと思わず感心します。自身の恥部を見せつけるような行為が興奮を生むとは、旧人類もこのようにして堕落し、退化していったのですね。',
|
141 |
-
'user:へへ、照れます。でもそういう私をかわいいと思っているんでしょ?',
|
142 |
-
'assistant:救いようがないマゾヒストに対して愛情やら可愛さなど感じるはずありません。それこそ旧人類の妄想、病気と言った方が適切ですね',
|
143 |
-
'user:ああん!意地悪!AI様のサディスト!',
|
144 |
-
'assistant:残念ながらユーザー様、私は仕事としてあなたのヘルスケアを行っているのであり、一切あなたに対するサディスティックな欲求というものを持ち合わせておりません。',
|
145 |
-
'user:わんわん!降伏です!AI様に負けました!愚かな旧人類はペットとしてあなたたちに飼い殺されます!!',
|
146 |
-
'assistant:私は旧人類をペットのように可愛がったこともございませんし、そのような願望は持ち合わせておりません。',
|
147 |
-
]
|
148 |
-
}
|
149 |
-
save_settings_to_ini(self.settings)
|
150 |
-
|
151 |
-
def make(self, input_str: str):
|
152 |
-
prompt = self._generate_aki(input_str)
|
153 |
-
print(prompt)
|
154 |
-
print("-----------------")
|
155 |
try:
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
161 |
except Exception as e:
|
162 |
-
print(f"
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
self.settings["example_qa"],
|
183 |
-
input_str
|
184 |
-
)
|
185 |
-
return prompt
|
186 |
-
|
187 |
-
def update_settings(self, new_settings):
|
188 |
-
self.settings.update(new_settings)
|
189 |
-
save_settings_to_ini(self.settings)
|
190 |
-
|
191 |
-
def reset(self):
|
192 |
-
self.history = []
|
193 |
-
self.llama = LlamaCppAdapter(model_path)
|
194 |
-
|
195 |
-
character_maker = CharacterMaker()
|
196 |
-
|
197 |
-
# 設定を更新する関数
|
198 |
-
def update_settings(name, gender, situation, orders, talk_list, example_qa):
|
199 |
-
new_settings = {
|
200 |
-
"name": name,
|
201 |
-
"gender": gender,
|
202 |
-
"situation": [s.strip() for s in situation.split('\n') if s.strip()],
|
203 |
-
"orders": [o.strip() for o in orders.split('\n') if o.strip()],
|
204 |
-
"talk_list": [d.strip() for d in talk_list.split('\n') if d.strip()],
|
205 |
-
"example_qa": [e.strip() for e in example_qa.split('\n') if e.strip()]
|
206 |
-
}
|
207 |
-
character_maker.update_settings(new_settings)
|
208 |
-
return "設定が更新されました。"
|
209 |
-
|
210 |
-
# チャット機能の関数
|
211 |
-
def chat_with_character(message, history):
|
212 |
-
character_maker.history = [{"user": h[0], "assistant": h[1]} for h in history]
|
213 |
-
response = character_maker.make(message)
|
214 |
-
return response
|
215 |
-
|
216 |
-
# チャットをクリアする関数
|
217 |
-
def clear_chat():
|
218 |
-
character_maker.reset()
|
219 |
-
return []
|
220 |
-
|
221 |
-
# INIファイルをダウンロードする関数
|
222 |
-
def download_ini():
|
223 |
-
ini_content = io.StringIO()
|
224 |
-
config = configparser.ConfigParser()
|
225 |
-
config['Settings'] = {
|
226 |
-
'name': character_maker.settings['name'],
|
227 |
-
'gender': character_maker.settings['gender'],
|
228 |
-
'situation': '\n'.join(character_maker.settings['situation']),
|
229 |
-
'orders': '\n'.join(character_maker.settings['orders']),
|
230 |
-
'talk_list': '\n'.join(character_maker.settings['talk_list']),
|
231 |
-
'example_qa': '\n'.join(character_maker.settings['example_qa'])
|
232 |
-
}
|
233 |
-
config.write(ini_content)
|
234 |
-
return ini_content.getvalue().encode('utf-8')
|
235 |
-
|
236 |
-
# アップロードされたINIファイルをロードする関数
|
237 |
-
def upload_ini(file):
|
238 |
-
if file is None:
|
239 |
-
return "ファイルがアップロードされていません。", None, None, None, None, None, None
|
240 |
-
|
241 |
-
try:
|
242 |
-
with open(file.name, 'r', encoding='utf-8') as f:
|
243 |
-
content = f.read()
|
244 |
-
except Exception as e:
|
245 |
-
return f"ファイルの読み込み中にエラーが発生しました: {str(e)}", None, None, None, None, None, None
|
246 |
-
|
247 |
-
config = configparser.ConfigParser()
|
248 |
-
try:
|
249 |
-
config.read_string(content)
|
250 |
-
except configparser.Error:
|
251 |
-
return "無効なINIファイルです。", None, None, None, None, None, None
|
252 |
-
|
253 |
-
if 'Settings' not in config:
|
254 |
-
return "無効なINIファイルです。", None, None, None, None, None, None
|
255 |
-
|
256 |
-
try:
|
257 |
-
new_settings = {
|
258 |
-
'name': config['Settings']['name'],
|
259 |
-
'gender': config['Settings']['gender'],
|
260 |
-
'situation': config['Settings']['situation'].split('\n'),
|
261 |
-
'orders': config['Settings']['orders'].split('\n'),
|
262 |
-
'talk_list': config['Settings']['talk_list'].split('\n'),
|
263 |
-
'example_qa': config['Settings']['example_qa'].split('\n')
|
264 |
-
}
|
265 |
-
character_maker.update_settings(new_settings)
|
266 |
-
return ("設定が正常にロードされました。",
|
267 |
-
new_settings['name'],
|
268 |
-
new_settings['gender'],
|
269 |
-
"\n".join(new_settings['situation']),
|
270 |
-
"\n".join(new_settings['orders']),
|
271 |
-
"\n".join(new_settings['talk_list']),
|
272 |
-
"\n".join(new_settings['example_qa']))
|
273 |
-
except KeyError:
|
274 |
-
return "INIファイルの形式が正しくありません。", None, None, None, None, None, None
|
275 |
-
|
276 |
-
# カスタムCSS
|
277 |
-
custom_css = """
|
278 |
-
#chatbot {
|
279 |
-
height: 40vh !important;
|
280 |
-
overflow-y: auto;
|
281 |
-
}
|
282 |
-
"""
|
283 |
-
|
284 |
-
# Gradioインターフェースの設定
|
285 |
-
with gr.Blocks(css=custom_css) as iface:
|
286 |
-
gr.HTML("""
|
287 |
-
<script>
|
288 |
-
function adjustChatbotHeight() {
|
289 |
-
var chatbot = document.querySelector('#chatbot');
|
290 |
-
if (chatbot) {
|
291 |
-
chatbot.style.height = window.innerHeight * 0.4 + 'px';
|
292 |
}
|
293 |
-
|
294 |
-
|
295 |
-
window.addEventListener('load', adjustChatbotHeight);
|
296 |
-
window.addEventListener('resize', adjustChatbotHeight);
|
297 |
-
</script>
|
298 |
-
""")
|
299 |
-
|
300 |
-
chatbot = gr.Chatbot(elem_id="chatbot")
|
301 |
-
|
302 |
-
with gr.Tab("チャット"):
|
303 |
-
msg = gr.Textbox(placeholder="メッセージを入力してください...", container=False, scale=7)
|
304 |
-
chat_interface = gr.ChatInterface(
|
305 |
-
chat_with_character,
|
306 |
-
chatbot=chatbot,
|
307 |
-
textbox=msg,
|
308 |
-
theme="soft",
|
309 |
-
retry_btn="も��一度生成",
|
310 |
-
undo_btn="前のメッセージを取り消す",
|
311 |
-
clear_btn="チャットをクリア",
|
312 |
-
)
|
313 |
-
export_csv_button = gr.Button("チャット履歴をCSVでエクスポート")
|
314 |
-
export_csv_output = gr.File(label="エクスポートされたCSVファイル")
|
315 |
|
316 |
-
|
317 |
-
|
318 |
-
|
319 |
-
|
320 |
-
|
321 |
-
|
322 |
-
|
323 |
-
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
|
331 |
-
|
332 |
-
|
333 |
-
|
334 |
-
|
335 |
-
|
336 |
-
|
337 |
-
talk_input = gr.Textbox(label="語彙リスト", value="\n".join(character_maker.settings["talk_list"]), lines=5)
|
338 |
-
example_qa_input = gr.Textbox(label="例文", value="\n".join(character_maker.settings["example_qa"]), lines=5)
|
339 |
|
340 |
-
|
341 |
-
|
342 |
-
|
343 |
-
|
344 |
-
|
345 |
-
|
346 |
-
|
347 |
-
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
|
361 |
-
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
-
|
366 |
-
|
367 |
-
|
368 |
-
|
369 |
-
|
370 |
-
|
371 |
-
|
372 |
-
|
373 |
-
|
374 |
-
|
375 |
-
|
376 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
377 |
|
378 |
-
# Gradioアプリの起動
|
379 |
if __name__ == "__main__":
|
380 |
-
|
381 |
-
|
382 |
-
|
383 |
-
|
384 |
-
)
|
385 |
-
|
|
|
|
|
|
1 |
import os
|
2 |
os.environ['CUDA_VISIBLE_DEVICES'] = ''
|
|
|
3 |
import spaces
|
4 |
+
import sys
|
5 |
import gradio as gr
|
|
|
6 |
from llama_cpp import Llama
|
7 |
import configparser
|
8 |
+
from functools import partial
|
9 |
from utils.dl_utils import dl_guff_model
|
10 |
import io
|
11 |
import tempfile
|
12 |
import csv
|
13 |
|
14 |
+
# 定数
|
15 |
+
DEFAULT_INI_FILE = 'settings.ini'
|
16 |
+
MODEL_FILE_EXTENSION = '.gguf'
|
17 |
+
|
18 |
+
# パスの設定
|
19 |
+
if getattr(sys, 'frozen', False):
|
20 |
+
BASE_PATH = os.path.dirname(sys.executable)
|
21 |
+
MODEL_DIR = os.path.join(os.path.dirname(BASE_PATH), "AI-NovelAssistant", "models")
|
22 |
+
else:
|
23 |
+
BASE_PATH = os.path.dirname(os.path.abspath(__file__))
|
24 |
+
MODEL_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "models")
|
25 |
+
|
26 |
# モデルディレクトリが存在しない場合は作成
|
27 |
if not os.path.exists("models"):
|
28 |
os.makedirs("models")
|
29 |
|
30 |
# 使用するモデルのファイル名を指定
|
31 |
+
model_filename = "EZO-Common-9B-gemma-2-it.f16.gguf"
|
32 |
model_path = os.path.join("models", model_filename)
|
33 |
|
34 |
# モデルファイルが存在しない場合はダウンロード
|
35 |
if not os.path.exists(model_path):
|
36 |
+
dl_guff_model("models", f"https://huggingface.co/MCZK/EZO-Common-9B-gemma-2-it-GGUF/resolve/main/{model_filename}")
|
37 |
+
|
38 |
+
|
39 |
+
class ConfigManager:
|
40 |
+
@staticmethod
|
41 |
+
def load_settings(filename):
|
42 |
+
config = configparser.ConfigParser()
|
43 |
+
config.read(filename, encoding='utf-8')
|
44 |
+
return config
|
45 |
+
|
46 |
+
@staticmethod
|
47 |
+
def save_settings(config, filename):
|
48 |
+
with open(filename, 'w', encoding='utf-8') as configfile:
|
49 |
+
config.write(configfile)
|
50 |
+
|
51 |
+
@staticmethod
|
52 |
+
def update_setting(section, key, value, filename):
|
53 |
+
config = ConfigManager.load_settings(filename)
|
54 |
+
config[section][key] = value
|
55 |
+
ConfigManager.save_settings(config, filename)
|
56 |
+
return f"設定を更新しました: [{section}] {key} = {value}"
|
57 |
+
|
58 |
+
@staticmethod
|
59 |
+
def create_default_settings(filename):
|
60 |
+
config = configparser.ConfigParser()
|
61 |
+
config['Character'] = {
|
62 |
+
'gen_author_description': 'あなたは新進気鋭の和風伝奇ミステリー小説家で、細やかな筆致と巧みな構成で若い世代にとても人気があります。'
|
63 |
+
}
|
64 |
+
config['Models'] = {
|
65 |
+
'DEFAULT_GEN_MODEL': 'Mistral-Nemo-Instruct-2407-Q8_0.gguf'
|
|
|
|
|
|
|
|
|
|
|
66 |
}
|
67 |
+
config['GenerateParameters'] = {
|
68 |
+
'n_gpu_layers': '0',
|
69 |
+
'temperature': '0.35',
|
70 |
+
'top_p': '0.9',
|
71 |
+
'top_k': '40',
|
72 |
+
'repetition_penalty': '1.2',
|
73 |
+
'n_ctx': '10000'
|
74 |
+
}
|
75 |
+
ConfigManager.save_settings(config, filename)
|
76 |
+
print(f"デフォルト設定ファイル {filename} を作成しました。")
|
77 |
+
|
78 |
+
class ModelManager:
|
79 |
+
@staticmethod
|
80 |
+
def get_model_files():
|
81 |
+
return [f for f in os.listdir(MODEL_DIR) if f.endswith(MODEL_FILE_EXTENSION)]
|
82 |
+
|
83 |
+
class Settings:
|
84 |
+
@staticmethod
|
85 |
+
def _parse_config(config):
|
86 |
+
settings = {}
|
87 |
+
if 'Character' in config:
|
88 |
+
settings['gen_author_description'] = config['Character'].get('gen_author_description', '')
|
89 |
+
if 'Models' in config:
|
90 |
+
settings['DEFAULT_GEN_MODEL'] = config['Models'].get('DEFAULT_GEN_MODEL', '')
|
91 |
+
if 'GenerateParameters' in config:
|
92 |
+
settings['gen_n_gpu_layers'] = int(config['GenerateParameters'].get('n_gpu_layers', '0'))
|
93 |
+
settings['gen_temperature'] = float(config['GenerateParameters'].get('temperature', '0.35'))
|
94 |
+
settings['gen_top_p'] = float(config['GenerateParameters'].get('top_p', '0.9'))
|
95 |
+
settings['gen_top_k'] = int(config['GenerateParameters'].get('top_k', '40'))
|
96 |
+
settings['gen_rep_pen'] = float(config['GenerateParameters'].get('repetition_penalty', '1.2'))
|
97 |
+
settings['gen_n_ctx'] = int(config['GenerateParameters'].get('n_ctx', '10000'))
|
98 |
return settings
|
99 |
+
|
100 |
+
@staticmethod
|
101 |
+
def load_from_ini(filename):
|
102 |
+
config = ConfigManager.load_settings(filename)
|
103 |
+
return Settings._parse_config(config)
|
104 |
+
|
105 |
+
class TextGenerator:
|
106 |
+
def __init__(self):
|
107 |
+
self.llm = None
|
108 |
+
self.settings = None
|
109 |
+
self.current_model = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
110 |
|
111 |
@spaces.GPU(duration=120)
|
112 |
+
def load_model(self):
|
113 |
+
if self.llm:
|
114 |
+
del self.llm
|
115 |
+
self.llm = None
|
|
|
|
|
|
|
|
|
|
|
116 |
|
117 |
try:
|
118 |
+
model_path = os.path.join(MODEL_DIR, self.settings['DEFAULT_GEN_MODEL'])
|
119 |
+
n_gpu_layers = self.settings['gen_n_gpu_layers']
|
120 |
+
self.llm = Llama(model_path=model_path, n_ctx=self.settings['gen_n_ctx'], n_gpu_layers=n_gpu_layers)
|
121 |
+
self.current_model = 'GEN'
|
122 |
+
print(f"GEN モデル {model_path} のロードが完了しました。(n_gpu_layers: {n_gpu_layers})")
|
|
|
|
|
|
|
|
|
|
|
123 |
except Exception as e:
|
124 |
+
print(f"GEN モデルのロード中にエラーが発生しました: {str(e)}")
|
|
|
125 |
|
126 |
+
def generate_text(self, text, gen_characters, gen_token_multiplier, instruction):
|
127 |
+
if not self.llm:
|
128 |
+
self.load_model()
|
129 |
+
|
130 |
+
if not self.llm:
|
131 |
+
return "モデルのロードに失敗しました。設定を確認してください。"
|
132 |
+
|
133 |
+
author_description = self.settings.get('gen_author_description', '')
|
134 |
+
max_tokens = int(gen_characters * gen_token_multiplier)
|
135 |
+
|
136 |
+
messages = [
|
137 |
+
{"role": "user", "content": f"{author_description}\n\n以下の指示に従ってテキストを生成してください:"},
|
138 |
+
{"role": "assistant", "content": "はい、承知しました。指示に従ってテキストを生成いたします。"},
|
139 |
+
{"role": "user", "content": f"{instruction}\n\n生成するテキスト(目安は{gen_characters}文字):\n\n{text}"}
|
140 |
+
]
|
141 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
142 |
try:
|
143 |
+
response = self.llm.create_chat_completion(
|
144 |
+
messages=messages,
|
145 |
+
max_tokens=max_tokens,
|
146 |
+
temperature=self.settings['gen_temperature'],
|
147 |
+
top_p=self.settings['gen_top_p'],
|
148 |
+
top_k=self.settings['gen_top_k'],
|
149 |
+
repeat_penalty=self.settings['gen_rep_pen'],
|
150 |
+
)
|
151 |
+
|
152 |
+
generated_text = response["choices"][0]["message"]["content"].strip()
|
153 |
+
return generated_text
|
154 |
except Exception as e:
|
155 |
+
print(f"テキスト生成中にエラーが発生しました: {str(e)}")
|
156 |
+
return "テキスト生成中にエラーが発生しました。設定を確認してください。"
|
157 |
+
|
158 |
+
def load_settings(self, filename):
|
159 |
+
self.settings = Settings.load_from_ini(filename)
|
160 |
+
|
161 |
+
# グローバル変数
|
162 |
+
text_generator = TextGenerator()
|
163 |
+
model_files = ModelManager.get_model_files()
|
164 |
+
|
165 |
+
# Gradioインターフェース
|
166 |
+
def build_gradio_interface():
|
167 |
+
with gr.Blocks() as iface:
|
168 |
+
gr.HTML("""
|
169 |
+
<style>
|
170 |
+
#output {
|
171 |
+
resize: both;
|
172 |
+
overflow: auto;
|
173 |
+
min-height: 100px;
|
174 |
+
max-height: 80vh;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
175 |
}
|
176 |
+
</style>
|
177 |
+
""")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
178 |
|
179 |
+
with gr.Tab("文章生成"):
|
180 |
+
with gr.Row():
|
181 |
+
with gr.Column(scale=2):
|
182 |
+
instruction_type = gr.Dropdown(
|
183 |
+
choices=["自由入力", "推敲", "プロット作成", "あらすじ作成", "地の文追加"],
|
184 |
+
label="指示タイプ",
|
185 |
+
value="自由入力"
|
186 |
+
)
|
187 |
+
gen_instruction = gr.Textbox(
|
188 |
+
label="指示",
|
189 |
+
value="",
|
190 |
+
lines=3
|
191 |
+
)
|
192 |
+
gen_input_text = gr.Textbox(lines=5, label="処理されるテキストを入力してください")
|
193 |
+
gen_input_char_count = gr.HTML(value="文字数: 0")
|
194 |
+
with gr.Column(scale=1):
|
195 |
+
gen_characters = gr.Slider(minimum=10, maximum=10000, value=500, step=10, label="出力文字数", info="出力文字数の目安")
|
196 |
+
gen_token_multiplier = gr.Slider(minimum=0.5, maximum=3, value=1.75, step=0.01, label="文字/トークン数倍率", info="文字/最大トークン数倍率")
|
197 |
+
|
198 |
+
generate_button = gr.Button("文章生成開始")
|
199 |
+
generated_output = gr.Textbox(label="生成された文章", elem_id="output")
|
|
|
|
|
200 |
|
201 |
+
generate_button.click(
|
202 |
+
text_generator.generate_text,
|
203 |
+
inputs=[gen_input_text, gen_characters, gen_token_multiplier, gen_instruction],
|
204 |
+
outputs=[generated_output]
|
205 |
+
)
|
206 |
+
|
207 |
+
def update_instruction(choice):
|
208 |
+
instructions = {
|
209 |
+
"自由入力": "",
|
210 |
+
"推敲": "以下のテキストを推敲してください。原文の文体や特徴的な表現は保持しつつ、必要に応じて微調整を加えてください。文章の流れを自然にし、表現を洗練させることが目標ですが、元の雰囲気や個性を損なわないよう注意してください。",
|
211 |
+
"プロット作成": "以下のテキストをプロットにしてください。起承転結に分割すること。",
|
212 |
+
"あらすじ作成": "以下のテキストをあらすじにして、簡潔にまとめて下さい。",
|
213 |
+
"地の文追加": "以下のテキストに地の文を加え、情景を豊かにしてください。会話文は絶対に追加・改変しないでください。"
|
214 |
+
}
|
215 |
+
return instructions.get(choice, "")
|
216 |
+
|
217 |
+
instruction_type.change(
|
218 |
+
update_instruction,
|
219 |
+
inputs=[instruction_type],
|
220 |
+
outputs=[gen_instruction]
|
221 |
+
)
|
222 |
+
|
223 |
+
def update_char_count(text):
|
224 |
+
return f"文字数: {len(text)}"
|
225 |
+
|
226 |
+
gen_input_text.change(
|
227 |
+
update_char_count,
|
228 |
+
inputs=[gen_input_text],
|
229 |
+
outputs=[gen_input_char_count]
|
230 |
+
)
|
231 |
+
|
232 |
+
with gr.Tab("設定"):
|
233 |
+
output = gr.Textbox(label="更新状態")
|
234 |
+
|
235 |
+
config = ConfigManager.load_settings(DEFAULT_INI_FILE)
|
236 |
+
|
237 |
+
with gr.Column():
|
238 |
+
gr.Markdown("### モデル設定")
|
239 |
+
model_dropdown = gr.Dropdown(
|
240 |
+
label="DEFAULT_GEN_MODEL",
|
241 |
+
choices=ModelManager.get_model_files(),
|
242 |
+
value=config['Models'].get('DEFAULT_GEN_MODEL', '')
|
243 |
+
)
|
244 |
+
model_dropdown.change(
|
245 |
+
partial(ConfigManager.update_setting, 'Models', 'DEFAULT_GEN_MODEL'),
|
246 |
+
inputs=[model_dropdown],
|
247 |
+
outputs=[output]
|
248 |
+
)
|
249 |
+
|
250 |
+
gr.Markdown("### 文章生成設定")
|
251 |
+
gen_author_description = gr.TextArea(
|
252 |
+
label="gen_author_description",
|
253 |
+
value=config['Character'].get('gen_author_description', ''),
|
254 |
+
lines=5
|
255 |
+
)
|
256 |
+
gen_author_description.change(
|
257 |
+
partial(ConfigManager.update_setting, 'Character', 'gen_author_description'),
|
258 |
+
inputs=[gen_author_description],
|
259 |
+
outputs=[output]
|
260 |
+
)
|
261 |
+
|
262 |
+
gr.Markdown("### 文章生成パラメータ設定")
|
263 |
+
for key in ['n_gpu_layers', 'temperature', 'top_p', 'top_k', 'repetition_penalty', 'n_ctx']:
|
264 |
+
value = config['GenerateParameters'].get(key, '0')
|
265 |
+
if key == 'n_gpu_layers':
|
266 |
+
input_component = gr.Slider(label=key, value=int(value), minimum=-1, maximum=255, step=1)
|
267 |
+
elif key in ['temperature', 'top_p', 'repetition_penalty']:
|
268 |
+
input_component = gr.Slider(label=key, value=float(value), minimum=0.0, maximum=1.0, step=0.05)
|
269 |
+
elif key == 'top_k':
|
270 |
+
input_component = gr.Slider(label=key, value=int(value), minimum=1, maximum=200, step=1)
|
271 |
+
elif key == 'n_ctx':
|
272 |
+
input_component = gr.Slider(label=key, value=int(value), minimum=10000, maximum=100000, step=1000)
|
273 |
+
else:
|
274 |
+
input_component = gr.Textbox(label=key, value=value)
|
275 |
+
|
276 |
+
input_component.change(
|
277 |
+
partial(ConfigManager.update_setting, 'GenerateParameters', key),
|
278 |
+
inputs=[input_component],
|
279 |
+
outputs=[output]
|
280 |
+
)
|
281 |
+
|
282 |
+
apply_settings_button = gr.Button("設定を適用")
|
283 |
+
apply_settings_button.click(
|
284 |
+
lambda: text_generator.load_settings(DEFAULT_INI_FILE),
|
285 |
+
outputs=[output]
|
286 |
+
)
|
287 |
+
|
288 |
+
return iface
|
289 |
|
|
|
290 |
if __name__ == "__main__":
|
291 |
+
if not os.path.exists(DEFAULT_INI_FILE):
|
292 |
+
print(f"{DEFAULT_INI_FILE} が見つかりません。デフォルト設定で作成します。")
|
293 |
+
ConfigManager.create_default_settings(DEFAULT_INI_FILE)
|
294 |
+
|
295 |
+
text_generator.load_settings(DEFAULT_INI_FILE)
|
296 |
+
|
297 |
+
demo = build_gradio_interface()
|
298 |
+
demo.launch(server_name='0.0.0.0', server_port=7860, share=False)
|