File size: 10,024 Bytes
7f508ac
 
 
 
 
acb1386
7f508ac
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2f424a5
 
 
 
 
 
 
 
 
 
 
 
 
7f508ac
2f424a5
7f508ac
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e81d0e0
7f508ac
 
 
 
 
 
e81d0e0
7f508ac
 
 
 
 
 
 
 
 
 
 
 
e81d0e0
 
 
 
 
 
 
 
 
 
7f508ac
 
 
 
 
 
 
 
 
 
 
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
import streamlit as st
import os

# 使用可能なモデルのリスト
MODELS = {
    "mistral_saba": "qwen-qwq-32b",
    "qwen_coder": "qwen-2.5-coder-32b",
    "deepseek": "deepseek-r1-distill-llama-70b"
}

# 動作モードの定義
MODES = ["通常モード", "実装モード", "エラー修正モード"]

# モード説明の表示
MODE_DESCRIPTIONS = {
    "通常モード": "Mistral Sabaがユーザー入力を分析し、必要に応じてDeepSeekの知識も活用した上で、Qwen Coderが回答を生成します。",
    "実装モード": "ユーザー入力の分析に基づき、Qwen Coderが設計書を作成した後、実装コードを提供します。",
    "エラー修正モード": "提示されたコードのエラーを分析し、Qwen Coderが修正案を提供します。"
}

# サイドバーの設定UI
def setup_config(compact=False):
    """サイドバーの設定UIをセットアップする"""
    st.title("コーディングアシスタント 💻" if not compact else "設定 ⚙️")
    
    # モード選択タブ
    selected_mode = st.radio(
        "モード選択",
        MODES,
        key="mode_selection"
    )
    
    # 選択されたモードの説明
    if not compact:
        st.markdown(f"**{selected_mode}**: {MODE_DESCRIPTIONS[selected_mode]}")
    
    # モード変更の検出
    if st.session_state.current_mode != selected_mode:
        st.session_state.current_mode = selected_mode
        st.session_state.is_first_message = True
        
        # モード変更時にこれまでのチャットを要約
        if not compact and len(st.session_state.messages) > 0:
            with st.spinner("前回の会話内容を要約しています..."):
                from chat import summarize_chat
                try:
                    summary = summarize_chat()
                    st.info(f"モードが変更されました。前回の会話の要約:\n\n{summary}")
                except Exception as e:
                    st.error(f"会話要約の生成中にエラーが発生しました: {str(e)}")
    
    # ファイル管理セクション
    if not compact:
        show_file_management()
    else:
        if st.button("ファイル管理", use_container_width=True):
            st.session_state.show_file_dialog = True
    
    # ファイル管理ダイアログ(コンパクトモードの場合)
    if compact and st.session_state.get("show_file_dialog", False):
        with st.expander("ファイル管理", expanded=True):
            show_file_management()
            if st.button("閉じる"):
                st.session_state.show_file_dialog = False
                st.rerun()
    
    # AI編集モード
    if not compact:
        st.checkbox("AIに編集を任せる", value=st.session_state.ai_edit_mode, key="ai_edit_mode_toggle")
        
        if st.session_state.ai_edit_mode:
            st.session_state.edit_instructions = st.text_area(
                "AI編集の指示",
                value=st.session_state.edit_instructions,
                placeholder="例: インデントを修正して、コメントを追加してください",
                height=100
            )
    
    # チャット履歴のクリア機能
    if st.button("会話履歴をクリア", use_container_width=True):
        st.session_state.messages = []
        st.session_state.thinking_process = []
        st.session_state.is_first_message = True
        st.session_state.conversation_state = {
            "topic": None,
            "last_question_type": None,
            "mentioned_entities": set(),
            "follow_up_count": 0
        }
        st.rerun()
    
    # 使い方ガイド(非コンパクトモードのみ)
    if not compact:
        show_help_guides()

def show_file_management():
    """ファイル管理UIを表示する"""
    st.header("ファイル管理")
    
    # ファイルアップロード機能を追加
    st.subheader("ファイルアップロード")
    uploaded_files = st.file_uploader("コードファイルをアップロード", 
                                    accept_multiple_files=True,
                                    type=["py", "js", "html", "css", "json", "txt"])
    
    if uploaded_files:
        for uploaded_file in uploaded_files:
            file_content = uploaded_file.getvalue().decode("utf-8")
            if uploaded_file.name not in st.session_state.files:
                st.session_state.files[uploaded_file.name] = file_content
                st.success(f"'{uploaded_file.name}'をアップロードしました")
    
    # 新規ファイル作成
    st.subheader("新規ファイル作成")
    new_file_name = st.text_input("新規ファイル名", value="", key="new_file")
    new_file_col1, new_file_col2 = st.columns([3, 1])
    
    with new_file_col1:
        file_extension = st.selectbox(
            "拡張子",
            options=[".py", ".js", ".html", ".css", ".json", ".txt"]
        )
    
    with new_file_col2:
        if st.button("作成", key="create_file_btn", use_container_width=True):
            full_filename = new_file_name + file_extension
            if full_filename not in st.session_state.files and new_file_name:
                st.session_state.files[full_filename] = ""
                st.session_state.current_file = full_filename
                st.success(f"ファイル '{full_filename}' を作成しました")
                st.rerun()
            elif not new_file_name:
                st.error("ファイル名を入力してください")
            else:
                st.error(f"ファイル '{full_filename}' は既に存在します")
    
    # 既存ファイル一覧
    if st.session_state.files:
        st.subheader("既存ファイル")
        for filename in st.session_state.files.keys():
            col1, col2, col3 = st.columns([3, 1, 1])
            with col1:
                if st.button(filename, key=f"open_{filename}"):
                    st.session_state.current_file = filename
                    st.rerun()
            with col2:
                if st.button("削除", key=f"delete_{filename}"):
                    del st.session_state.files[filename]
                    if st.session_state.current_file == filename:
                        st.session_state.current_file = None
                    st.rerun()
            with col3:
                if st.button("コピー", key=f"copy_{filename}"):
                    name, ext = os.path.splitext(filename)
                    new_name = f"{name}_copy{ext}"
                    counter = 1
                    while new_name in st.session_state.files:
                        new_name = f"{name}_copy_{counter}{ext}"
                        counter += 1
                    st.session_state.files[new_name] = st.session_state.files[filename]
                    st.rerun()

def show_help_guides():
    """使い方ガイドを表示する"""
    st.markdown("---")
    st.subheader("使い方ガイド")
    
    with st.expander("アプリの機能", expanded=False):
        st.markdown("""
        ### 主な機能
        1. **コードエディタ** - 複数のプログラミング言語に対応したシンタックスハイライト付きエディタ
        2. **Pythonコード実行** - エディタ上のPythonコードをその場で実行
        3. **AIアシスタント** - コーディングに関する質問や実装の提案
        4. **AIコード編集** - AIにコードの編集を依頼
        5. **ファイル管理** - 複数のファイルの作成・保存・編集
        6. **エラー修正** - コードのエラーを自動で診断・修正
        7. **複数ファイルの解析** - アップロードされた複数ファイルを解析
        
        ### 特殊コマンド
        - `/mode 通常` - 通常の質問応答モードに切り替え
        - `/mode 実装` - コード実装モードに切り替え
        - `/mode エラー修正` - エラー修正モードに切り替え
        - `/edit` - 現在のコードをAIに編集させる
        - `/analyse` - アップロードされたファイル群を解析
        """)
    
    with st.expander("モバイル利用のコツ", expanded=False):
        st.markdown("""
        ### モバイルでの使用Tips
        
        1. **タブ切り替え** - エディタとチャットを切り替えて使用します
        2. **横向き表示** - 横向きにすると画面が広く使えます
        3. **AIに編集を任せる** - コード入力が難しい場合はAIに編集を依頼できます
        4. **短いコード** - モバイルでは短く分割したコードが扱いやすいです
        5. **コード保存** - 作業中は定期的にコードを保存しましょう
        """)
    
    with st.expander("複数ファイル解析", expanded=False):
        st.markdown("""
        ### 複数ファイル解析の使い方
        
        1. **ファイルのアップロード** - 「ファイルアップロード」セクションから複数のファイルをアップロードします
        2. **解析コマンド** - チャットで `/analyse` を入力すると、アップロードされたファイル群の関係性を解析します
        3. **ファイル指定** - 特定のファイルについて質問する場合は、質問文中にファイル名を含めてください
        4. **コード理解** - 複数のファイルにまたがるコードの流れや依存関係も分析できます
        """)

def load_api_key():
    """APIキーを環境変数またはSecretsから取得する"""
    # 環境変数から取得
    api_key = os.getenv("GROQ_API_KEY", "")
    
    # secrets.tomlから取得(環境変数がない場合)
    if not api_key and hasattr(st, 'secrets') and 'GROQ_API_KEY' in st.secrets:
        api_key = st.secrets['GROQ_API_KEY']
    
    return api_key