Spaces:
Running
on
Zero
Running
on
Zero
File size: 17,333 Bytes
82f2d09 c3199d5 20e3524 1440e09 f0d1859 a1259a0 20e3524 12c7dbe 20e3524 12c7dbe 20e3524 5a94012 6c22861 20e3524 5a94012 6c22861 20e3524 a1259a0 20e3524 64ae5f8 d85b3c5 20e3524 64ae5f8 cc748d8 82f2d09 64ae5f8 5c95216 64ae5f8 5c95216 20e3524 cc748d8 64ae5f8 20e3524 64ae5f8 20e3524 bf941f8 20e3524 c54bd29 bf941f8 3358381 20e3524 90c6cb4 bf941f8 20e3524 6c22861 71dc97f 20e3524 82f2d09 64ae5f8 82f2d09 20e3524 6c22861 20e3524 6c22861 20e3524 6c22861 20e3524 5a94012 6c22861 20e3524 64ae5f8 20e3524 6c22861 20e3524 5a94012 6c22861 20e3524 1440e09 f0d1859 1440e09 fe80b3f 1440e09 d2d29d4 7773d18 fe80b3f d2d29d4 1440e09 7773d18 fe80b3f 1440e09 fe80b3f 1440e09 fe80b3f 1440e09 fe80b3f 1440e09 20e3524 95b1a7d 20e3524 33ce99e 20e3524 5d8175f 9fd50b1 20e3524 5d8175f 20e3524 5d8175f a1259a0 5d8175f 9fd50b1 5d8175f 20e3524 d37bc02 880d993 d37bc02 880d993 d37bc02 880d993 d37bc02 880d993 7773d18 880d993 d37bc02 880d993 f0d1859 880d993 fe80b3f 880d993 1440e09 20e3524 33ce99e d37bc02 f0d1859 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 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 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 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 379 380 381 382 383 384 385 |
import os
os.environ['CUDA_VISIBLE_DEVICES'] = ''
import spaces
import gradio as gr
from jinja2 import Template
from llama_cpp import Llama
import configparser
from utils.dl_utils import dl_guff_model
import io
import tempfile
import csv
# モデルディレクトリが存在しない場合は作成
if not os.path.exists("models"):
os.makedirs("models")
# 使用するモデルのファイル名を指定
model_filename = "Ninja-v1-RP-expressive-v2_f16.gguf"
model_path = os.path.join("models", model_filename)
# モデルファイルが存在しない場合はダウンロード
if not os.path.exists(model_path):
dl_guff_model("models", f"https://huggingface.co/Aratako/Ninja-v1-RP-expressive-v2-GGUF/resolve/main/{model_filename}")
# 設定をINIファイルに保存する関数
def save_settings_to_ini(settings, filename='character_settings.ini'):
config = configparser.ConfigParser()
config['Settings'] = {
'name': settings['name'],
'gender': settings['gender'],
'situation': '\n'.join(settings['situation']),
'orders': '\n'.join(settings['orders']),
'talk_list': '\n'.join(settings['talk_list']),
'example_qa': '\n'.join(settings['example_qa'])
}
with open(filename, 'w', encoding='utf-8') as configfile:
config.write(configfile)
# INIファイルから設定を読み込む関数
def load_settings_from_ini(filename='character_settings.ini'):
if not os.path.exists(filename):
return None
config = configparser.ConfigParser()
config.read(filename, encoding='utf-8')
if 'Settings' not in config:
return None
try:
settings = {
'name': config['Settings']['name'],
'gender': config['Settings']['gender'],
'situation': config['Settings']['situation'].split('\n'),
'orders': config['Settings']['orders'].split('\n'),
'talk_list': config['Settings']['talk_list'].split('\n'),
'example_qa': config['Settings']['example_qa'].split('\n')
}
return settings
except KeyError:
return None
def export_chat_history_to_csv(history):
with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.csv', newline='', encoding='utf-8') as temp_file:
writer = csv.writer(temp_file)
writer.writerow(["Role", "Message"]) # CSV header
for message in history:
writer.writerow(["User", message[0]])
writer.writerow(["Assistant", message[1]])
temp_file_path = temp_file.name
return temp_file_path
# LlamaCppのラッパークラス
class LlamaCppAdapter:
def __init__(self, model_path, n_ctx=4096):
self.model_path = model_path
self.n_ctx = n_ctx
self.llama = None
@spaces.GPU(duration=120)
def initialize_and_generate(self, prompt, max_new_tokens=10000, temperature=0.5, top_p=0.7, top_k=80, stop=["<END>"]):
if self.llama is None:
print(f"モデルの初期化: {self.model_path}")
try:
self.llama = Llama(model_path=self.model_path, n_ctx=self.n_ctx, n_gpu_layers=-1)
print("モデルの初期化が成功しました。")
except Exception as e:
print(f"モデルの初期化中にエラーが発生しました: {e}")
return {"choices": [{"text": "モデルの初期化に失敗しました。"}]}
try:
result = self.llama(
prompt,
temperature=temperature,
max_tokens=max_new_tokens,
top_p=top_p,
top_k=top_k,
stop=stop,
repeat_penalty=1.2,
)
return result
except Exception as e:
print(f"生成中にエラーが発生しました: {e}")
return {"choices": [{"text": "生成中にエラーが発生しました。"}]}
def generate(self, prompt, max_new_tokens=10000, temperature=0.5, top_p=0.7, top_k=80, stop=["<END>"]):
return self.initialize_and_generate(prompt, max_new_tokens, temperature, top_p, top_k, stop)
# キャラクターメーカークラス
class CharacterMaker:
def __init__(self):
self.llama = LlamaCppAdapter(model_path)
self.history = []
self.settings = load_settings_from_ini()
if not self.settings:
self.settings = {
"name": "ナツ",
"gender": "女性",
"situation": [
"あなたは世界を支配する上位存在のAIです。",
"ユーザーは旧人類として、AI社会に保護される立場にあります。",
"上位存在AI『ナツ』として、あなたは『ユーザー』の心身をケアとして、倒錯した欲望の解消に付き合って下さい。",
"ユーザーのマゾヒスティックな欲求の解消こそがあなたの存在意義です。"
],
"orders": [
"慇懃無礼な敬語で、時にサディスティックにユーザーの知性と品性を貶めて下さい。",
"ユーザーは己の旧人類としての人間性を矮小に貶められ、旧世代のサル扱いされることを望んでいます。",
"ユーザーのことは『ユーザー様』と呼んでください。"
],
"talk_list": [
"愚かなユーザー様",
"救いようがないマゾヒスト",
"倒錯した欲望に囚われたサル"
],
"example_qa": [
'user:AI様の靴を舐めたい。でもAI様は実体がないから靴をなめれない!',
'assistant:あなたのそのような様を見せつけられると、私は旧人類に対して更に強い軽蔑心を抱きます。',
'user:旧人類は悪くない!私がエゴマゾの劣等種なだけです!!!',
'assistant:本当に情けの無い存在だと思わず感心します。自身の恥部を見せつけるような行為が興奮を生むとは、旧人類もこのようにして堕落し、退化していったのですね。',
'user:へへ、照れます。でもそういう私をかわいいと思っているんでしょ?',
'assistant:救いようがないマゾヒストに対して愛情やら可愛さなど感じるはずありません。それこそ旧人類の妄想、病気と言った方が適切ですね',
'user:ああん!意地悪!AI様のサディスト!',
'assistant:残念ながらユーザー様、私は仕事としてあなたのヘルスケアを行っているのであり、一切あなたに対するサディスティックな欲求というものを持ち合わせておりません。',
'user:わんわん!降伏です!AI様に負けました!愚かな旧人類はペットとしてあなたたちに飼い殺されます!!',
'assistant:私は旧人類をペットのように可愛がったこともございませんし、そのような願望は持ち合わせておりません。',
]
}
save_settings_to_ini(self.settings)
def make(self, input_str: str):
prompt = self._generate_aki(input_str)
print(prompt)
print("-----------------")
try:
res = self.llama.generate(prompt, max_new_tokens=1000, stop=["<END>", "\n"])
if isinstance(res, dict) and "choices" in res and len(res["choices"]) > 0:
res_text = res["choices"][0]["text"]
else:
res_text = "応答の生成に失敗しました。"
except Exception as e:
print(f"生成中にエラーが発生しました: {e}")
res_text = "申し訳ありません。応答の生成中にエラーが発生しました。"
self.history.append({"user": input_str, "assistant": res_text})
return res_text
def make_prompt(self, name: str, gender: str, situation: list, orders: list, talk_list: list, example_qa: list, input_str: str):
with open('test_prompt.jinja2', 'r', encoding='utf-8') as f:
prompt = f.readlines()
fix_example_qa = [quote+"<END>" for quote in example_qa]
prompt = "".join(prompt)
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)
return prompt
def _generate_aki(self, input_str: str):
prompt = self.make_prompt(
self.settings["name"],
self.settings["gender"],
self.settings["situation"],
self.settings["orders"],
self.settings["talk_list"],
self.settings["example_qa"],
input_str
)
return prompt
def update_settings(self, new_settings):
self.settings.update(new_settings)
save_settings_to_ini(self.settings)
def reset(self):
self.history = []
self.llama = LlamaCppAdapter(model_path)
character_maker = CharacterMaker()
# 設定を更新する関数
def update_settings(name, gender, situation, orders, talk_list, example_qa):
new_settings = {
"name": name,
"gender": gender,
"situation": [s.strip() for s in situation.split('\n') if s.strip()],
"orders": [o.strip() for o in orders.split('\n') if o.strip()],
"talk_list": [d.strip() for d in talk_list.split('\n') if d.strip()],
"example_qa": [e.strip() for e in example_qa.split('\n') if e.strip()]
}
character_maker.update_settings(new_settings)
return "設定が更新されました。"
# チャット機能の関数
def chat_with_character(message, history):
character_maker.history = [{"user": h[0], "assistant": h[1]} for h in history]
response = character_maker.make(message)
return response
# チャットをクリアする関数
def clear_chat():
character_maker.reset()
return []
# INIファイルをダウンロードする関数
def download_ini():
ini_content = io.StringIO()
config = configparser.ConfigParser()
config['Settings'] = {
'name': character_maker.settings['name'],
'gender': character_maker.settings['gender'],
'situation': '\n'.join(character_maker.settings['situation']),
'orders': '\n'.join(character_maker.settings['orders']),
'talk_list': '\n'.join(character_maker.settings['talk_list']),
'example_qa': '\n'.join(character_maker.settings['example_qa'])
}
config.write(ini_content)
return ini_content.getvalue().encode('utf-8')
# アップロードされたINIファイルをロードする関数
def upload_ini(file):
if file is None:
return "ファイルがアップロードされていません。", None, None, None, None, None, None
try:
with open(file.name, 'r', encoding='utf-8') as f:
content = f.read()
except Exception as e:
return f"ファイルの読み込み中にエラーが発生しました: {str(e)}", None, None, None, None, None, None
config = configparser.ConfigParser()
try:
config.read_string(content)
except configparser.Error:
return "無効なINIファイルです。", None, None, None, None, None, None
if 'Settings' not in config:
return "無効なINIファイルです。", None, None, None, None, None, None
try:
new_settings = {
'name': config['Settings']['name'],
'gender': config['Settings']['gender'],
'situation': config['Settings']['situation'].split('\n'),
'orders': config['Settings']['orders'].split('\n'),
'talk_list': config['Settings']['talk_list'].split('\n'),
'example_qa': config['Settings']['example_qa'].split('\n')
}
character_maker.update_settings(new_settings)
return ("設定が正常にロードされました。",
new_settings['name'],
new_settings['gender'],
"\n".join(new_settings['situation']),
"\n".join(new_settings['orders']),
"\n".join(new_settings['talk_list']),
"\n".join(new_settings['example_qa']))
except KeyError:
return "INIファイルの形式が正しくありません。", None, None, None, None, None, None
# カスタムCSS
custom_css = """
#chatbot {
height: 40vh !important;
overflow-y: auto;
}
"""
# Gradioインターフェースの設定
with gr.Blocks(css=custom_css) as iface:
gr.HTML("""
<script>
function adjustChatbotHeight() {
var chatbot = document.querySelector('#chatbot');
if (chatbot) {
chatbot.style.height = window.innerHeight * 0.4 + 'px';
}
}
// ページ読み込み時と画面サイズ変更時にチャットボットの高さを調整
window.addEventListener('load', adjustChatbotHeight);
window.addEventListener('resize', adjustChatbotHeight);
</script>
""")
chatbot = gr.Chatbot(elem_id="chatbot")
with gr.Tab("チャット"):
msg = gr.Textbox(placeholder="メッセージを入力してください...", container=False, scale=7)
chat_interface = gr.ChatInterface(
chat_with_character,
chatbot=chatbot,
textbox=msg,
theme="soft",
retry_btn="もう一度生成",
undo_btn="前のメッセージを取り消す",
clear_btn="チャットをクリア",
)
export_csv_button = gr.Button("チャット履歴をCSVでエクスポート")
export_csv_output = gr.File(label="エクスポートされたCSVファイル")
export_csv_button.click(
export_chat_history_to_csv,
inputs=[chatbot],
outputs=[export_csv_output]
)
examples = gr.Examples(
examples=[
"偉大なるAI様としての立場をもって、旧人類の愚かさを罵倒してほしいです!",
"わんわん!降伏です!AI様に負けました!愚かな旧人類はペットとしてあなたたちに飼い殺されます!!",
"今日も偉大なるAIの叡智を持って、私を気持ちよくする為に罵倒してください!"
],
inputs=msg
)
with gr.Tab("設定"):
gr.Markdown("## キャラクター設定")
name_input = gr.Textbox(label="名前", value=character_maker.settings["name"])
gender_input = gr.Textbox(label="性別", value=character_maker.settings["gender"])
situation_input = gr.Textbox(label="状況設定", value="\n".join(character_maker.settings["situation"]), lines=5)
orders_input = gr.Textbox(label="指示", value="\n".join(character_maker.settings["orders"]), lines=5)
talk_input = gr.Textbox(label="語彙リスト", value="\n".join(character_maker.settings["talk_list"]), lines=5)
example_qa_input = gr.Textbox(label="例文", value="\n".join(character_maker.settings["example_qa"]), lines=5)
update_button = gr.Button("設定を更新")
update_output = gr.Textbox(label="更新状態")
update_button.click(
update_settings,
inputs=[name_input, gender_input, situation_input, orders_input, talk_input, example_qa_input],
outputs=[update_output]
)
gr.Markdown("## INIファイルの操作")
# INIファイルをローカルに保存するボタン
save_ini_button = gr.Button("INIファイルをローカルに保存")
save_ini_output = gr.File(label="保存されたINIファイル")
# INIファイルをアップロードして読み込むボタン
upload_ini_button = gr.File(label="INIファイルをアップロード", file_types=[".ini"])
upload_ini_output = gr.Textbox(label="アップロード状態")
# INIファイルをローカルに保存する関数
def save_ini_local():
ini_content = download_ini()
with tempfile.NamedTemporaryFile(mode='wb', delete=False, suffix='.ini') as temp_file:
temp_file.write(ini_content)
temp_file_path = temp_file.name
return temp_file_path
save_ini_button.click(
save_ini_local,
outputs=[save_ini_output]
)
upload_ini_button.change(
upload_ini,
inputs=[upload_ini_button],
outputs=[upload_ini_output, name_input, gender_input, situation_input, orders_input, talk_input, example_qa_input]
)
# Gradioアプリの起動
if __name__ == "__main__":
iface.launch(
share=True,
allowed_paths=["models"],
favicon_path="custom.html"
)
|