Spaces:
Running
Running
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(""" | |
<style> | |
.stApp { | |
background-color: #0e1117; | |
color: #ffffff; | |
} | |
.chat-message { | |
padding: 1rem; | |
margin: 1rem 0; | |
border-radius: 0.5rem; | |
} | |
.assistant-message { | |
background-color: #1e2329; | |
color: #ffffff; | |
} | |
.stButton button { | |
background-color: #2ea44f; | |
color: #ffffff; | |
} | |
.stTextArea textarea { | |
background-color: #1e2329; | |
color: #ffffff; | |
} | |
</style> | |
""", 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'<div class="chat-message assistant-message">{message.content}</div>', | |
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: | |
model = st.radio( | |
"使用するモデル", | |
["Claude", "OpenAI"], | |
key="model_selection" | |
) | |
st.session_state.llm_service.switch_model(model.lower()) | |
st.divider() | |
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) | |
- その他の主要なプログラミング言語 | |
""") |