import streamlit as st import tempfile import git from core.file_scanner import FileScanner from pathlib import Path from datetime import datetime from services.llm_service import LLMService from core.file_scanner import FileInfo from typing import List # ページ設定 st.set_page_config( page_title="Repository Code Analysis", page_icon="🔍", layout="wide" ) # ダークテーマの設定 st.markdown(""" """, unsafe_allow_html=True) def clone_repository(repo_url: str) -> Path: """リポジトリをクローンして一時ディレクトリに保存""" temp_dir = Path(tempfile.mkdtemp()) git.Repo.clone_from(repo_url, temp_dir) return temp_dir def create_download_content(files: List[FileInfo]) -> str: content = "# スキャン結果\n\n" for file in files: content += f"## {file.path}\n" content += f"サイズ: {file.formatted_size}\n" content += f"エンコーディング: {file.encoding or '不明'}\n\n" if file.content: content += f"```{file.extension[1:] if file.extension else ''}\n" content += file.content content += "\n```\n\n" return content # セッション状態の初期化 if 'repo_content' not in st.session_state: st.session_state.repo_content = None if 'temp_dir' not in st.session_state: st.session_state.temp_dir = None if 'llm_service' not in st.session_state: try: st.session_state.llm_service = LLMService() except ValueError as e: st.error(str(e)) st.stop() # メインのUIレイアウト st.title("🔍 リポジトリ解析・質問システム") # サイドバーでモデル選択 available_models = st.session_state.llm_service.settings.get_available_models() if len(available_models) > 1: selected_model = st.sidebar.selectbox( "使用するモデル", available_models, index=available_models.index(st.session_state.llm_service.current_model) ) st.session_state.llm_service.switch_model(selected_model) # URLの入力 repo_url = st.text_input( "GitHubリポジトリのURLを入力", placeholder="https://github.com/username/repository.git" ) # スキャン実行ボタン if st.button("スキャン開始", disabled=not repo_url): try: with st.spinner('リポジトリをクローン中...'): temp_dir = clone_repository(repo_url) st.session_state.temp_dir = temp_dir with st.spinner('ファイルをスキャン中...'): scanner = FileScanner(temp_dir) files = scanner.scan_files() # List[FileInfo] を取得 st.session_state.repo_content = LLMService.format_code_content(files) st.success(f"スキャン完了: {len(files)}個のファイルを検出") # 新しいスキャン時に会話履歴をクリア st.session_state.llm_service.clear_history() except Exception as e: st.error(f"エラーが発生しました: {str(e)}") # スキャン完了後の質問セクション if st.session_state.repo_content: st.divider() st.subheader("💭 コードについて質問する") # スキャン結果のダウンロードボタン scan_result = create_download_content(files) # filesはスキャン結果 st.download_button( label="スキャン結果をダウンロード", data=scan_result, file_name=f"scan_result_{datetime.now().strftime('%Y%m%d_%H%M%S')}.md", mime="text/markdown" ) # 会話履歴の表示(アシスタントの回答のみ) for message in st.session_state.llm_service.conversation_history: if message.role == "assistant": # アシスタントの回答のみを表示 st.markdown(f'
', unsafe_allow_html=True) query = st.text_area( "質問を入力してください", placeholder="例: このコードの主な機能は何ですか?" ) col1, col2 = st.columns([1, 5]) with col1: if st.button("履歴クリア"): st.session_state.llm_service.clear_history() st.rerun() with col2: if st.button("質問する", disabled=not query): with st.spinner('回答を生成中...'): response, error = st.session_state.llm_service.get_response( st.session_state.repo_content, query ) if error: st.error(error) else: st.rerun() # 会話履歴を更新するために再表示 # セッション終了時のクリーンアップ if st.session_state.temp_dir and Path(st.session_state.temp_dir).exists(): try: import shutil shutil.rmtree(st.session_state.temp_dir) except: pass # サイドバー情報 with st.sidebar: st.subheader("📌 使い方") st.markdown(""" 1. GitHubリポジトリのURLを入力 2. スキャンを実行 3. コードについて質問(最大5ターンの会話が可能) """) st.subheader("🔍 スキャン対象") st.markdown(""" - Python (.py) - JavaScript (.js) - Java (.java) - C/C++ (.c, .h, .cpp, .hpp) - その他の主要なプログラミング言語 """)