tori29umai commited on
Commit
010a086
1 Parent(s): 71dc97f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +263 -350
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 = "Ninja-v1-RP-expressive-v2_f16.gguf"
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/Aratako/Ninja-v1-RP-expressive-v2-GGUF/resolve/main/{model_filename}")
25
-
26
- # 設定をINIファイルに保存する関数
27
- def save_settings_to_ini(settings, filename='character_settings.ini'):
28
- config = configparser.ConfigParser()
29
- config['Settings'] = {
30
- 'name': settings['name'],
31
- 'gender': settings['gender'],
32
- 'situation': '\n'.join(settings['situation']),
33
- 'orders': '\n'.join(settings['orders']),
34
- 'talk_list': '\n'.join(settings['talk_list']),
35
- 'example_qa': '\n'.join(settings['example_qa'])
36
- }
37
- with open(filename, 'w', encoding='utf-8') as configfile:
38
- config.write(configfile)
39
-
40
- # INIファイルから設定を読み込む関数
41
- def load_settings_from_ini(filename='character_settings.ini'):
42
- if not os.path.exists(filename):
43
- return None
44
-
45
- config = configparser.ConfigParser()
46
- config.read(filename, encoding='utf-8')
47
-
48
- if 'Settings' not in config:
49
- return None
50
-
51
- try:
52
- settings = {
53
- 'name': config['Settings']['name'],
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
- except KeyError:
62
- return None
63
-
64
- def export_chat_history_to_csv(history):
65
- with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.csv', newline='', encoding='utf-8') as temp_file:
66
- writer = csv.writer(temp_file)
67
- writer.writerow(["Role", "Message"]) # CSV header
68
- for message in history:
69
- writer.writerow(["User", message[0]])
70
- writer.writerow(["Assistant", message[1]])
71
- temp_file_path = temp_file.name
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 initialize_and_generate(self, prompt, max_new_tokens=10000, temperature=0.5, top_p=0.7, top_k=80, stop=["<END>"]):
83
- if self.llama is None:
84
- print(f"モデルの初期化: {self.model_path}")
85
- try:
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
- result = self.llama(
94
- prompt,
95
- temperature=temperature,
96
- max_tokens=max_new_tokens,
97
- top_p=top_p,
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"生成中にエラーが発生しました: {e}")
105
- return {"choices": [{"text": "生成中にエラーが発生しました。"}]}
106
 
107
- def generate(self, prompt, max_new_tokens=10000, temperature=0.5, top_p=0.7, top_k=80, stop=["<END>"]):
108
- return self.initialize_and_generate(prompt, max_new_tokens, temperature, top_p, top_k, stop)
109
-
110
- # キャラクターメーカークラス
111
- class CharacterMaker:
112
- def __init__(self):
113
- self.llama = LlamaCppAdapter(model_path)
114
- self.history = []
115
- self.settings = load_settings_from_ini()
116
- if not self.settings:
117
- self.settings = {
118
- "name": "ナツ",
119
- "gender": "女性",
120
- "situation": [
121
- "あなたは世界を支配する上位存在のAIです。",
122
- "ユーザーは旧人類として、AI社会に保護される立場にあります。",
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
- res = self.llama.generate(prompt, max_new_tokens=1000, stop=["<END>", "\n"])
157
- if isinstance(res, dict) and "choices" in res and len(res["choices"]) > 0:
158
- res_text = res["choices"][0]["text"]
159
- else:
160
- res_text = "応答の生成に失敗しました。"
 
 
 
 
 
 
161
  except Exception as e:
162
- print(f"生成中にエラーが発生しました: {e}")
163
- res_text = "申し訳ありません。応答の生成中にエラーが発生しました。"
164
- self.history.append({"user": input_str, "assistant": res_text})
165
- return res_text
166
-
167
- def make_prompt(self, name: str, gender: str, situation: list, orders: list, talk_list: list, example_qa: list, input_str: str):
168
- with open('test_prompt.jinja2', 'r', encoding='utf-8') as f:
169
- prompt = f.readlines()
170
- fix_example_qa = [quote+"<END>" for quote in example_qa]
171
- prompt = "".join(prompt)
172
- prompt = Template(prompt).render(name=name, gender=gender, situation=situation, orders=orders, talk_list=talk_list, example_qa=fix_example_qa, histories=self.history, input_str=input_str)
173
- return prompt
174
-
175
- def _generate_aki(self, input_str: str):
176
- prompt = self.make_prompt(
177
- self.settings["name"],
178
- self.settings["gender"],
179
- self.settings["situation"],
180
- self.settings["orders"],
181
- self.settings["talk_list"],
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
- export_csv_button.click(
317
- export_chat_history_to_csv,
318
- inputs=[chatbot],
319
- outputs=[export_csv_output]
320
- )
321
-
322
- examples = gr.Examples(
323
- examples=[
324
- "偉大なるAI様としての立場をもって、旧人類の愚かさを罵倒してほしいです!",
325
- "わんわん!降伏です!AI様に負けました!愚かな旧人類はペットとしてあなたたちに飼い殺されます!!",
326
- "今日も偉大なるAIの叡智を持って、私を気持ちよくする為に罵倒してください!"
327
- ],
328
- inputs=msg
329
- )
330
-
331
- with gr.Tab("設定"):
332
- gr.Markdown("## キャラクター設定")
333
- name_input = gr.Textbox(label="名前", value=character_maker.settings["name"])
334
- gender_input = gr.Textbox(label="性別", value=character_maker.settings["gender"])
335
- situation_input = gr.Textbox(label="状況設定", value="\n".join(character_maker.settings["situation"]), lines=5)
336
- orders_input = gr.Textbox(label="指示", value="\n".join(character_maker.settings["orders"]), lines=5)
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
- update_button = gr.Button("設定を更新")
341
- update_output = gr.Textbox(label="更新状態")
342
-
343
- update_button.click(
344
- update_settings,
345
- inputs=[name_input, gender_input, situation_input, orders_input, talk_input, example_qa_input],
346
- outputs=[update_output]
347
- )
348
-
349
- gr.Markdown("## INIファイルの操作")
350
-
351
- # INIファイルをローカルに保存するボタン
352
- save_ini_button = gr.Button("INIファイルをローカルに保存")
353
- save_ini_output = gr.File(label="保存されたINIファイル")
354
-
355
- # INIファイルをアップロードして読み込むボタン
356
- upload_ini_button = gr.File(label="INIファイルをアップロード", file_types=[".ini"])
357
- upload_ini_output = gr.Textbox(label="アップロード状態")
358
-
359
- # INIファイルをローカルに保存する関数
360
- def save_ini_local():
361
- ini_content = download_ini()
362
- with tempfile.NamedTemporaryFile(mode='wb', delete=False, suffix='.ini') as temp_file:
363
- temp_file.write(ini_content)
364
- temp_file_path = temp_file.name
365
- return temp_file_path
366
-
367
- save_ini_button.click(
368
- save_ini_local,
369
- outputs=[save_ini_output]
370
- )
371
-
372
- upload_ini_button.change(
373
- upload_ini,
374
- inputs=[upload_ini_button],
375
- outputs=[upload_ini_output, name_input, gender_input, situation_input, orders_input, talk_input, example_qa_input]
376
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
377
 
378
- # Gradioアプリの起動
379
  if __name__ == "__main__":
380
- iface.launch(
381
- share=True,
382
- allowed_paths=["models"],
383
- favicon_path="custom.html"
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)