Spaces:
Running
Running
| /** | |
| * prompt.js - プロンプト生成の中核機能を提供 | |
| * | |
| * 主な機能: | |
| * - Gemini APIを使用したプロンプト生成 | |
| * - モデルリストの取得と管理 | |
| * - プロンプトの生成と整形 | |
| * - 文字列置換機能 | |
| * | |
| * 重要な関数: | |
| * - getModelList(): 利用可能なGeminiモデルを取得 | |
| * - generatePrompt(): メインのプロンプト生成処理 | |
| * - applyReplacements(): 文字列置換を適用 | |
| */ | |
| async function getModelList() { | |
| const url = 'https://generativelanguage.googleapis.com/v1beta/models?key=' + document.getElementById('apiKey').value; | |
| const response = await fetch(url); | |
| const data = await response.json(); | |
| return data.models | |
| .filter(x => x.supportedGenerationMethods.includes("generateContent")) | |
| .filter(x => { | |
| const versionMatch = x.name.match(/gemini-(\d+\.\d+)/); | |
| return versionMatch && parseFloat(versionMatch[1]) >= 1.5; | |
| }); | |
| } | |
| function generatePrompt() { | |
| if (!document.getElementById('apiKey').value) { | |
| alert("Please enter your API key."); | |
| return; | |
| } | |
| let query = document.getElementById('query').value; | |
| let textFormat = 'str'; | |
| if (document.getElementById('splitStringsSwitch').checked) { | |
| query = Array.from(document.getElementById('query').value).join(":::"); | |
| //textFormat = 'array, # テキストは1character(not word)ずつ格納した配列にして返すこと。 Example: ["I", "t", " ", "i", "s", " ", "a", " ", "p", "e", "n", "."], ["こ", "れ", "は", " ", "ペ", "ン", "で", "す", "。"], '; | |
| } | |
| let anotherLanguage = ""; | |
| if (!["en", "ja"].includes(i18next.language)) { | |
| anotherLanguage = `, | |
| { | |
| "language": "${i18next.language}", | |
| "text": ${textFormat}, | |
| "title": str | |
| }`; | |
| } | |
| const selectedEndpoint = document.getElementById('endpointSelect').value; | |
| const url = `https://generativelanguage.googleapis.com/v1beta/${selectedEndpoint}:generateContent?key=` + document.getElementById('apiKey').value; | |
| const text = `「 ${query} 」をテーマに画像生成AIに送るプロンプトを考えてください。 | |
| 背景や小物のディテイール、構図、視覚効果など視覚的な情報のみに言及すること。 | |
| その上で長文を日本語と英語で返信してください。 | |
| 返答は以下のフォーマットのjson形式でのみ行う。json以外の内容をレスポンスに含めないこと。 | |
| \`\`\`json | |
| { | |
| "results": [ | |
| { | |
| "language": "en", | |
| "text": ${textFormat} # ${document.getElementById('characterCount').value}文字程度, | |
| "title": str | |
| }, | |
| { | |
| "language": "danbooru", | |
| "tags": [str], | |
| }, | |
| { | |
| "language": "ja", | |
| "text": ${textFormat}, | |
| "title": str | |
| }${anotherLanguage} | |
| ] | |
| } | |
| \`\`\` | |
| `; | |
| const payload = { | |
| contents: [ | |
| { | |
| parts: [ | |
| { text: text } | |
| ] | |
| } | |
| ], | |
| generation_config: { | |
| max_output_tokens: 4095, | |
| temperature: 1, | |
| top_p: 1, | |
| top_k: 32 | |
| }, | |
| safetySettings: [ | |
| { | |
| category: "HARM_CATEGORY_SEXUALLY_EXPLICIT", | |
| threshold: "OFF" | |
| } | |
| ] | |
| }; | |
| console.debug(text); | |
| // モーダルを表示 | |
| const loadingModal = new bootstrap.Modal(document.getElementById('loadingModal')); | |
| loadingModal.show(); | |
| // ローディングアイコンを表示 | |
| document.getElementById('loading').classList.remove('d-none'); | |
| // generatePromptボタンを無効化 | |
| document.getElementById('generatePromptButton').disabled = true; | |
| document.getElementById('generatePromptButton').classList.remove('btn-primary'); | |
| document.getElementById('generatePromptButton').classList.remove('btn-danger'); | |
| document.getElementById('generatePromptButton').classList.add('btn-secondary'); | |
| fetch(url, { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json' | |
| }, | |
| body: JSON.stringify(payload) | |
| }) | |
| .then(response => response.json()) | |
| .then(data => { | |
| console.debug(data.candidates[0].content); | |
| // レスポンスからテキストを抽出 | |
| const responseText = data.candidates[0].content.parts[0].text | |
| console.debug(responseText); | |
| let jsonString = responseText.replace(/```json|```/g, '').trim(); | |
| jsonString = jsonString.replace(/\n/g, ''); | |
| console.debug(jsonString); | |
| // JSONをパース | |
| const parsedData = JSON5.parse(jsonString); | |
| // 結果を表示 | |
| console.debug(parsedData); | |
| let promptEn = parsedData.results.find(x => x.language === 'en').text; | |
| let promptMyLanguage = parsedData.results.find(x => x.language === i18next.language).text; | |
| [promptEn, promptMyLanguage] = [promptEn, promptMyLanguage].map(x => { | |
| return Array.isArray(x) ? x.join("") : x; | |
| }); | |
| promptMyLanguage = promptMyLanguage.replace(/\。 ?/g, '。\n\n'); | |
| document.getElementById('promptMyLanguage').value = promptMyLanguage; | |
| promptEn = promptEn.replace(/\. ?/g, '.\n\n'); | |
| promptEn = promptEn.replace(/^ */g, ''); | |
| promptEn = applyReplacements(promptEn); | |
| document.getElementById('promptEn').value = promptEn; | |
| let danbooruTags = parsedData.results.find(x => x.language === 'danbooru'); | |
| if (danbooruTags.tags) { | |
| danbooruTags = danbooruTags.tags.map(x => applyReplacements(x.replace("_", " "))); | |
| document.getElementById('danbooruTags').value = danbooruTags.join(", "); | |
| } | |
| if (danbooruTags.text) { | |
| danbooruTags = danbooruTags.text.map(x => applyReplacements(x.replace("_", " "))); | |
| document.getElementById('danbooruTags').value = danbooruTags.join(", "); | |
| } | |
| const title = parsedData.results.find(x => x.language === i18next.language).title; | |
| // ローディングアイコンを非表示 | |
| document.getElementById('loading').classList.add('d-none'); | |
| document.getElementById('generatePromptButton').disabled = false; | |
| document.getElementById('generatePromptButton').classList.remove('btn-secondary'); | |
| document.getElementById('generatePromptButton').classList.add('btn-primary'); | |
| document.getElementById('query').value = query; | |
| saveToUserStorage(true); | |
| saveToHistory(title); // 履歴に保存 | |
| }) | |
| .catch(error => { | |
| console.error(error); | |
| // エラー時の処理 | |
| document.getElementById('generatePromptButton').classList.remove('btn-secondary'); | |
| document.getElementById('generatePromptButton').classList.add('btn-danger'); | |
| }) | |
| .finally(() => { | |
| document.getElementById('query').value = query; | |
| // ローディングアイコンを非表示 | |
| document.getElementById('loading').classList.add('d-none'); | |
| document.getElementById('generatePromptButton').disabled = false; | |
| document.getElementById('generatePromptButton').classList.remove('btn-secondary'); | |
| if (!document.getElementById('generatePromptButton').classList.contains('btn-danger')) { | |
| document.getElementById('generatePromptButton').classList.add('btn-primary'); | |
| } | |
| // モーダルを非表示 | |
| setTimeout(() => { | |
| loadingModal.hide(); | |
| }, 500); | |
| }); | |
| }; | |
| function applyReplacements(text) { | |
| const replacements = getReplacements(); | |
| for (const [search, replace] of replacements) { | |
| text = text.replace(new RegExp(search, 'g'), replace); | |
| } | |
| return text; | |
| } | |
| function getReplacements() { | |
| const replacementList = document.getElementById('replacementList'); | |
| const replacements = []; | |
| for (const set of replacementList.children) { | |
| const search = set.querySelector('.replacement-search').value; | |
| const replace = set.querySelector('.replacement-replace').value; | |
| if (search && replace) { | |
| replacements.push([search, replace]); | |
| } | |
| } | |
| return replacements; | |
| } | |
| // 置換セットの追加と削除の処理 | |
| document.getElementById('addReplacementButton').addEventListener('click', addReplacementSet); | |
| function addReplacementSet() { | |
| const replacementList = document.getElementById('replacementList'); | |
| const newSet = document.createElement('div'); | |
| newSet.className = 'replacement-set mb-2'; | |
| newSet.innerHTML = ` | |
| <input type="text" class="form-control form-control-sm replacement-search mb-1" placeholder="検索語"> | |
| <input type="text" class="form-control form-control-sm replacement-replace mb-1" placeholder="置換語"> | |
| <button class="btn btn-danger btn-sm remove-replacement"> | |
| <i class="fas fa-times"></i> | |
| </button> | |
| `; | |
| replacementList.appendChild(newSet); | |
| newSet.querySelector('.remove-replacement').addEventListener('click', function() { | |
| replacementList.removeChild(newSet); | |
| }); | |
| } | |
| // ページ読み込み時に保存された置換セットを復元 | |
| window.addEventListener('load', function() { | |
| const savedReplacements = JSON.parse(localStorage.getItem('replacements') || '[]'); | |
| for (const [search, replace] of savedReplacements) { | |
| addReplacementSet(); | |
| const lastSet = document.getElementById('replacementList').lastElementChild; | |
| lastSet.querySelector('.replacement-search').value = search; | |
| lastSet.querySelector('.replacement-replace').value = replace; | |
| } | |
| }); | |
| // ページを離れる前に置換セットを保存 | |
| window.addEventListener('beforeunload', function() { | |
| localStorage.setItem('replacements', JSON.stringify(getReplacements())); | |
| }); | |