Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import tempfile | |
| class MarkdownGenerator: | |
| def generate_from_shot_data(shot_data): | |
| markdown = "" | |
| for i in range(0, len(shot_data), 3): | |
| if i + 2 < len(shot_data): | |
| title, content, visible = shot_data[i], shot_data[i + 1], shot_data[i + 2] | |
| if visible and content.strip(): | |
| markdown += f"# {title}\n{content}\n\n" | |
| return markdown.strip() | |
| def create_temporary_file(markdown_content): | |
| with tempfile.NamedTemporaryFile(mode='w', suffix='.md', delete=False, encoding='utf-8') as f: | |
| f.write(markdown_content) | |
| return f.name | |
| class ShotManager: | |
| def __init__(self): | |
| self.default_titles = ["命令書", "制約条件", "入力", "出力"] | |
| self.max_shots = 12 | |
| def add_shot(self, *shot_data): | |
| shot_data = list(shot_data) | |
| for i in range(12, len(shot_data), 3): | |
| if i + 2 < len(shot_data) and not shot_data[i + 2]: | |
| shot_data[i] = f"新しいショット {(i // 3) - 3}" | |
| shot_data[i + 2] = True | |
| break | |
| return shot_data + self._create_accordion_updates(shot_data) | |
| def delete_shot(self, shot_index, *shot_data): | |
| shot_data = list(shot_data) | |
| visible_count = sum(1 for i in range(2, len(shot_data), 3) if i < len(shot_data) and shot_data[i]) | |
| if visible_count > 1: | |
| data_index = shot_index * 3 | |
| if data_index + 2 < len(shot_data): | |
| shot_data[data_index:data_index + 3] = ["", "", False] | |
| return shot_data + self._create_accordion_updates(shot_data) | |
| def move_shot_up(self, shot_index, *shot_data): | |
| shot_data = list(shot_data) | |
| if shot_index > 0: | |
| self._swap_shots(shot_data, shot_index, shot_index - 1) | |
| return shot_data + self._create_accordion_updates(shot_data) | |
| def move_shot_down(self, shot_index, *shot_data): | |
| shot_data = list(shot_data) | |
| next_index = (shot_index + 1) * 3 | |
| if next_index + 2 < len(shot_data) and shot_data[next_index + 2]: | |
| self._swap_shots(shot_data, shot_index, shot_index + 1) | |
| return shot_data + self._create_accordion_updates(shot_data) | |
| def clear_all_shots(self, *shot_data): | |
| shot_data = list(shot_data) | |
| for i, title in enumerate(self.default_titles): | |
| data_index = i * 3 | |
| if data_index + 2 < len(shot_data): | |
| shot_data[data_index:data_index + 3] = [title, "", True] | |
| for i in range(4, len(shot_data) // 3): | |
| data_index = i * 3 | |
| if data_index + 2 < len(shot_data): | |
| shot_data[data_index:data_index + 3] = ["", "", False] | |
| return shot_data + self._create_accordion_updates(shot_data) | |
| def _swap_shots(self, shot_data, index1, index2): | |
| start1, start2 = index1 * 3, index2 * 3 | |
| if start1 + 2 < len(shot_data) and start2 + 2 < len(shot_data): | |
| for offset in range(3): | |
| shot_data[start1 + offset], shot_data[start2 + offset] = \ | |
| shot_data[start2 + offset], shot_data[start1 + offset] | |
| def _create_accordion_updates(self, shot_data): | |
| updates = [] | |
| for i in range(self.max_shots): | |
| data_index = i * 3 | |
| if data_index + 2 < len(shot_data): | |
| updates.append(gr.update(visible=shot_data[data_index + 2])) | |
| else: | |
| updates.append(gr.update()) | |
| return updates | |
| class PromptEngineering: | |
| def __init__(self): | |
| self.shot_manager = ShotManager() | |
| self.markdown_generator = MarkdownGenerator() | |
| def update_preview_and_stats(self, *shot_data): | |
| markdown = self.markdown_generator.generate_from_shot_data(shot_data) | |
| total_shots, filled_shots = self._calculate_stats(shot_data) | |
| stats = f"ショット数: {total_shots} | 入力済み: {filled_shots}" | |
| if markdown: | |
| temp_file = self.markdown_generator.create_temporary_file(markdown) | |
| return markdown, stats, markdown, gr.update(visible=True, value=temp_file) | |
| else: | |
| return "ショットに内容を入力すると、プレビューが表示されます。", stats, "", gr.update(visible=False) | |
| def _calculate_stats(self, shot_data): | |
| total_shots = filled_shots = 0 | |
| for i in range(0, len(shot_data), 3): | |
| if i + 2 < len(shot_data): | |
| title, content, visible = shot_data[i], shot_data[i + 1], shot_data[i + 2] | |
| if visible: | |
| total_shots += 1 | |
| if content.strip(): | |
| filled_shots += 1 | |
| return total_shots, filled_shots | |
| def create_interface(self): | |
| with gr.Blocks(title="🔧 プロンプトエンジニアリングツール", theme=gr.themes.Soft()) as demo: | |
| gr.Markdown("# 🔧 プロンプトエンジニアリングツール") | |
| gr.Markdown("各ショットを入力して、統合されたプロンプトを生成しましょう!") | |
| with gr.Row(): | |
| shot_components, shot_data_components = self._create_shot_input_section() | |
| preview_components = self._create_preview_section() | |
| self._setup_event_handlers(shot_components, shot_data_components, preview_components) | |
| return demo | |
| def _create_shot_input_section(self): | |
| with gr.Column(scale=2): | |
| gr.Markdown("## 📝 ショット入力") | |
| with gr.Row(): | |
| add_shot_btn = gr.Button("➕ ショットを追加", variant="primary") | |
| clear_all_btn = gr.Button("🗑️ 全てクリア", variant="stop") | |
| shot_components = [] | |
| shot_data_components = [] | |
| for i in range(12): | |
| component_data = self._create_single_shot(i) | |
| shot_components.append(component_data['components']) | |
| shot_data_components.extend(component_data['data']) | |
| return {'components': shot_components, 'add_btn': add_shot_btn, 'clear_btn': clear_all_btn}, shot_data_components | |
| def _create_single_shot(self, index): | |
| default_values = self._get_default_shot_values(index) | |
| with gr.Accordion(default_values['accordion_title'], | |
| open=(index == 0), | |
| visible=default_values['visible']) as accordion: | |
| with gr.Row(): | |
| with gr.Column(scale=3): | |
| title_input = gr.Textbox( | |
| label="ショットタイトル", | |
| value=default_values['title'], | |
| placeholder="ショットのタイトルを入力してください..." | |
| ) | |
| content_input = gr.Textbox( | |
| label="内容", | |
| value="", | |
| lines=5, | |
| placeholder="ショットの内容を入力してください..." | |
| ) | |
| with gr.Column(scale=1): | |
| gr.Markdown("**操作**") | |
| with gr.Row(): | |
| up_btn = gr.Button("⬆️", size="sm", variant="secondary") | |
| down_btn = gr.Button("⬇️", size="sm", variant="secondary") | |
| with gr.Row(): | |
| delete_btn = gr.Button("🗑️ 削除", size="sm", variant="stop") | |
| visible_state = gr.Checkbox(value=default_values['visible'], visible=False) | |
| return { | |
| 'components': { | |
| 'accordion': accordion, | |
| 'title': title_input, | |
| 'content': content_input, | |
| 'visible': visible_state, | |
| 'up': up_btn, | |
| 'down': down_btn, | |
| 'delete': delete_btn | |
| }, | |
| 'data': [title_input, content_input, visible_state] | |
| } | |
| def _get_default_shot_values(self, index): | |
| if index < 4: | |
| titles = ["命令書", "制約条件", "入力", "出力"] | |
| return { | |
| 'title': titles[index], | |
| 'visible': True, | |
| 'accordion_title': f"📋 {titles[index]}" | |
| } | |
| else: | |
| return { | |
| 'title': "", | |
| 'visible': False, | |
| 'accordion_title': f"📋 追加ショット {index - 3}" | |
| } | |
| def _create_preview_section(self): | |
| with gr.Column(scale=1): | |
| gr.Markdown("## 📄 生成されたプロンプト") | |
| stats_display = gr.Textbox( | |
| label="📊 統計", | |
| value="ショット数: 4 | 入力済み: 0", | |
| interactive=False | |
| ) | |
| gr.Markdown("### 👀 プレビュー") | |
| preview_area = gr.Textbox( | |
| label="生成されたMarkdown", | |
| value="ショットに内容を入力すると、プレビューが表示されます。", | |
| lines=15, | |
| max_lines=20, | |
| interactive=False | |
| ) | |
| gr.Markdown("### 📥 ダウンロード") | |
| download_file = gr.File(label="Markdownファイル", visible=False) | |
| gr.Markdown("### 📋 コピー用") | |
| copy_area = gr.Textbox( | |
| label="テキスト形式", | |
| lines=8, | |
| interactive=False, | |
| info="このテキストをコピーして使用できます" | |
| ) | |
| return [preview_area, stats_display, copy_area, download_file] | |
| def _setup_event_handlers(self, shot_components, shot_data_components, preview_components): | |
| accordion_components = [comp['accordion'] for comp in shot_components['components']] | |
| all_outputs = shot_data_components + accordion_components | |
| shot_components['add_btn'].click( | |
| fn=self.shot_manager.add_shot, | |
| inputs=shot_data_components, | |
| outputs=all_outputs | |
| ) | |
| shot_components['clear_btn'].click( | |
| fn=self.shot_manager.clear_all_shots, | |
| inputs=shot_data_components, | |
| outputs=all_outputs | |
| ) | |
| for i, component in enumerate(shot_components['components']): | |
| component['up'].click( | |
| fn=lambda *args, idx=i: self.shot_manager.move_shot_up(idx, *args), | |
| inputs=shot_data_components, | |
| outputs=all_outputs | |
| ) | |
| component['down'].click( | |
| fn=lambda *args, idx=i: self.shot_manager.move_shot_down(idx, *args), | |
| inputs=shot_data_components, | |
| outputs=all_outputs | |
| ) | |
| component['delete'].click( | |
| fn=lambda *args, idx=i: self.shot_manager.delete_shot(idx, *args), | |
| inputs=shot_data_components, | |
| outputs=all_outputs | |
| ) | |
| for component in shot_data_components: | |
| component.change( | |
| fn=self.update_preview_and_stats, | |
| inputs=shot_data_components, | |
| outputs=preview_components | |
| ) | |
| def launch(self): | |
| demo = self.create_interface() | |
| demo.launch() | |
| if __name__ == "__main__": | |
| app = PromptEngineering() | |
| app.launch() | |