Yasu777 commited on
Commit
c9d618e
·
verified ·
1 Parent(s): cafc4a3

Create chat.py

Browse files
Files changed (1) hide show
  1. chat.py +652 -0
chat.py ADDED
@@ -0,0 +1,652 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import re
3
+ from groq import Groq
4
+ from editor import save_generated_code_to_new_file
5
+ from utils import load_system_prompt, extract_all_code_blocks, is_physics_related
6
+
7
+ def setup_chat(api_key, mobile=False):
8
+ """チャット機能とその関連機能をセットアップする"""
9
+ # ヘッダー
10
+ st.header("AIアシスタント" if not mobile else "チャット")
11
+
12
+ # 過去のメッセージを表示
13
+ display_message_history()
14
+
15
+ # 思考プロセスの表示(開閉可能なエクスパンダー)
16
+ if st.session_state.thinking_process:
17
+ with st.expander("思考プロセスを表示"):
18
+ for i, thought in enumerate(st.session_state.thinking_process):
19
+ st.subheader(thought["title"])
20
+ st.markdown(thought["content"])
21
+ st.divider()
22
+
23
+ # 最後に生成されたコードを保存する機能
24
+ if st.session_state.last_generated_code and st.button("AIが生成したコードをエディタに保存", key="save_ai_code"):
25
+ with st.spinner("コードを保存中..."):
26
+ file_name = save_generated_code_to_new_file(st.session_state.last_generated_code)
27
+ st.success(f"コードを '{file_name}' として保存しました")
28
+ st.session_state.last_generated_code = None # 保存したのでクリア
29
+
30
+ # モバイルモードならエディタタブに切り替え
31
+ if mobile:
32
+ st.session_state.active_tab = "エディタ"
33
+
34
+ st.rerun()
35
+
36
+ # モバイルの場合は追加のナビゲーション
37
+ if mobile:
38
+ if st.button("コードエディタモードへ", key="goto_editor", use_container_width=True):
39
+ st.session_state.active_tab = "エディタ"
40
+ st.rerun()
41
+
42
+ # チャット入力
43
+ if prompt := st.chat_input("質問を入力してください"):
44
+ process_chat_input(prompt, api_key)
45
+
46
+ def display_message_history():
47
+ """会話履歴を表示する"""
48
+ for message in st.session_state.messages:
49
+ with st.chat_message(message["role"]):
50
+ st.markdown(message["content"])
51
+
52
+ def process_chat_input(prompt, api_key):
53
+ """ユーザー入力を処理し、応答を生成する"""
54
+ # ユーザーメッセージを表示
55
+ st.chat_message("user").markdown(prompt)
56
+
57
+ # ユーザーメッセージをセッション状態に追加
58
+ st.session_state.messages.append({"role": "user", "content": prompt})
59
+
60
+ # 編集コマンドを検出
61
+ if prompt.strip().startswith("/edit"):
62
+ process_edit_command(prompt, api_key)
63
+ return
64
+
65
+ # モード切替コマンドを検出
66
+ if prompt.strip().startswith("/mode"):
67
+ process_mode_command(prompt)
68
+ return
69
+
70
+ # 通常の質問処理
71
+ process_normal_query(prompt, api_key)
72
+
73
+ def process_edit_command(prompt, api_key):
74
+ """エディタのコード編集コマンドを処理する"""
75
+ # 現在開いているファイルがない場合
76
+ if not st.session_state.current_file:
77
+ response = "エディタに開いているファイルがありません。まずはファイルを作成するか開いてください。"
78
+ st.chat_message("assistant").markdown(response)
79
+ st.session_state.messages.append({"role": "assistant", "content": response})
80
+ return
81
+
82
+ # 編集指示を抽出
83
+ instructions = prompt.replace("/edit", "").strip()
84
+ if not instructions:
85
+ instructions = "コードを改善してください。可読性を高め、バグがあれば修正し、コメントを追加してください。"
86
+
87
+ current_code = st.session_state.files[st.session_state.current_file]
88
+ file_ext = os.path.splitext(st.session_state.current_file)[1].lower()
89
+
90
+ # 言語を特定
91
+ lang_map = {
92
+ ".py": "Python",
93
+ ".js": "JavaScript",
94
+ ".html": "HTML",
95
+ ".css": "CSS",
96
+ ".json": "JSON",
97
+ ".txt": "テキスト"
98
+ }
99
+ language = lang_map.get(file_ext, "コード")
100
+
101
+ # AIに編集を依頼
102
+ with st.spinner("AIによるコード編集中..."):
103
+ edited_code = request_ai_edit(
104
+ api_key,
105
+ current_code,
106
+ instructions,
107
+ language
108
+ )
109
+
110
+ if edited_code:
111
+ st.session_state.files[st.session_state.current_file] = edited_code
112
+ response = f"✅ {st.session_state.current_file} を編集しました。編集結果はエディタで確認できます。"
113
+
114
+ # モバイルモードならエディタタブに切り替え
115
+ if st.session_state.mobile_mode:
116
+ st.session_state.active_tab = "エディタ"
117
+ else:
118
+ response = "⚠️ コードの編集に失敗しました。もう一度お試しください。"
119
+
120
+ # 応答を表示して保存
121
+ st.chat_message("assistant").markdown(response)
122
+ st.session_state.messages.append({"role": "assistant", "content": response})
123
+
124
+ if st.session_state.mobile_mode and edited_code:
125
+ st.rerun()
126
+
127
+ def process_mode_command(prompt):
128
+ """モード切り替えコマンドを処理する"""
129
+ parts = prompt.strip().split()
130
+ if len(parts) < 2:
131
+ response = "モード指定が不完全です。使用方法: /mode [通常|実装|エラー修正]"
132
+ else:
133
+ mode = parts[1].lower()
134
+ valid_modes = {"通常": "通常モード", "実装": "実装モード", "エラー修正": "エラー修正モード"}
135
+
136
+ if mode not in valid_modes and mode not in valid_modes.values():
137
+ response = f"無効なモードです。使用可能なモード: {', '.join(valid_modes.keys())}"
138
+ else:
139
+ old_mode = st.session_state.current_mode
140
+ if mode in valid_modes:
141
+ st.session_state.current_mode = valid_modes[mode]
142
+ else:
143
+ st.session_state.current_mode = mode
144
+
145
+ st.session_state.is_first_message = True
146
+
147
+ # モード変更時の説明メッセージを用意
148
+ mode_descriptions = {
149
+ "通常モード": "通常の質問応答モードに切り替えました。",
150
+ "実装モード": "アイデアから設計と実装を生成するモードに切り替えました。",
151
+ "エラー修正モード": "エラーメッセージから問題を診断・修正するモードに切り替えました。"
152
+ }
153
+
154
+ instructions = {
155
+ "通常モード": "質問や議論したいトピックを入力してください。",
156
+ "実装モード": "実装したい機能やアイデアを入力してください。",
157
+ "エラー修正モード": "発生したエラーメッセージとコードを入力してください。"
158
+ }
159
+
160
+ response = f"モードを「{old_mode}」から「{st.session_state.current_mode}」に変更しました。\n\n{mode_descriptions[st.session_state.current_mode]}\n\n{instructions[st.session_state.current_mode]}"
161
+
162
+ # レスポンスを表示
163
+ st.chat_message("assistant").markdown(response)
164
+
165
+ # アシスタントメッセージをセッション状態に追加
166
+ st.session_state.messages.append({"role": "assistant", "content": response})
167
+
168
+ def process_normal_query(prompt, api_key):
169
+ """通常の質問を処理し、AIからの応答を生成する"""
170
+ # APIキーがある場合のみ実行
171
+ if api_key:
172
+ try:
173
+ # 最初のメッセージはチェーンを使用、それ以降はQwen Coderのみで応答
174
+ if st.session_state.is_first_message:
175
+ with st.spinner("回答を生成中...(複数のモデルによる分析を行っています)"):
176
+ response, thinking_results = generate_chain_response(prompt, api_key)
177
+ st.session_state.thinking_process = thinking_results
178
+ st.session_state.is_first_message = False
179
+ else:
180
+ with st.spinner("回答を生成中..."):
181
+ response = generate_direct_response(prompt, api_key)
182
+
183
+ # レスポンスを表示
184
+ st.chat_message("assistant").markdown(response)
185
+
186
+ # アシスタントメッセージをセッション状態に追加
187
+ st.session_state.messages.append({"role": "assistant", "content": response})
188
+
189
+ # 会話状態を更新
190
+ update_conversation_state(prompt, response)
191
+
192
+ # モード切替提案を追加(UIには表示しない)
193
+ mode_suggestions = {
194
+ "通常モード": "\n\n---\n*実装を生成するには、「/mode 実装」と入力して実装モードに切り替えてください。*",
195
+ "実装モード": "\n\n---\n*このコードを検証・修正するには、「/mode エラー修正」と入力して検証修正モードに切り替えてください。*",
196
+ "エラー修正モード": "\n\n---\n*新しい実装を生成するには、「/mode 実装」と入力して実装モードに切り替えてください。*"
197
+ }
198
+
199
+ # 提案はセッションステートには保存するが、UIには表示しない(次回のcontext生成で使用)
200
+ suggestion = mode_suggestions.get(st.session_state.current_mode, "")
201
+ st.session_state.messages[-1]["content"] += suggestion
202
+
203
+ # コードブロックを抽出して保存機能を提供
204
+ code_blocks = extract_all_code_blocks(response)
205
+ if code_blocks and len(code_blocks) > 0:
206
+ st.session_state.last_generated_code = code_blocks[0]
207
+
208
+ except Exception as e:
209
+ st.error(f"エラーが発生しました: {str(e)}")
210
+ else:
211
+ st.error("GROQ_API_KEYが設定されていません。")
212
+
213
+ def generate_chain_response(user_input, api_key):
214
+ """複数のモデルを使ったチェーンベースの応答生成"""
215
+ client = Groq(api_key=api_key)
216
+ thinking_results = []
217
+
218
+ # モデル名のマッピング
219
+ from config import MODELS
220
+
221
+ # ユーザー入力が物理関連かどうかをチェック
222
+ is_physics = is_physics_related(user_input)
223
+
224
+ # Step 1: Mistral Sabaによる入力分析
225
+ mistral_system_prompt = load_system_prompt(MODELS["mistral_saba"])
226
+ mistral_prompt = f"""
227
+ 以下のユーザー入力を分析してください。ユーザーの質問や要求の本質、重要なキーワード、技術的課題を抽出し、
228
+ どのようなアプローチで回答すべきかを詳細に検討してください。
229
+
230
+ ユーザー入力: {user_input}
231
+ """
232
+
233
+ mistral_response = client.chat.completions.create(
234
+ model=MODELS["mistral_saba"],
235
+ messages=[
236
+ {"role": "system", "content": mistral_system_prompt},
237
+ {"role": "user", "content": mistral_prompt}
238
+ ],
239
+ temperature=0.3,
240
+ max_tokens=2000
241
+ )
242
+
243
+ mistral_analysis = mistral_response.choices[0].message.content
244
+ thinking_results.append({
245
+ "title": "Mistral Sabaによる分析",
246
+ "content": mistral_analysis
247
+ })
248
+
249
+ # 物理関連の場合のみDeepSeekの意見を取得
250
+ deepseek_knowledge = ""
251
+ if is_physics:
252
+ deepseek_system_prompt = load_system_prompt(MODELS["deepseek"])
253
+ deepseek_prompt = f"""
254
+ 以下のユーザー入力は物理学に関連しています。物理学の観点から、この質問や課題に対する
255
+ 科学的知識、法則、公式、アプローチを提供してください。特に関連する物理概念と実装方法の
256
+ 関連性があれば説明してください。
257
+
258
+ ユーザー入力: {user_input}
259
+ """
260
+
261
+ deepseek_response = client.chat.completions.create(
262
+ model=MODELS["deepseek"],
263
+ messages=[
264
+ {"role": "system", "content": deepseek_system_prompt},
265
+ {"role": "user", "content": deepseek_prompt}
266
+ ],
267
+ temperature=0.3,
268
+ max_tokens=2000
269
+ )
270
+
271
+ deepseek_knowledge = deepseek_response.choices[0].message.content
272
+ thinking_results.append({
273
+ "title": "DeepSeekによる物理学的知見",
274
+ "content": deepseek_knowledge
275
+ })
276
+
277
+ # Step 2: モード別の応答生成
278
+ if st.session_state.current_mode == "通常モード":
279
+ # 物理関連の場合のみDeepSeekの情報を含める
280
+ deepseek_section = ""
281
+ physics_consideration = ""
282
+
283
+ if is_physics:
284
+ physics_consideration = " DeepSeekの物理学的知見を考慮して"
285
+ deepseek_section = f"DeepSeekの物理学的知見:\n{deepseek_knowledge}"
286
+
287
+ # 現在のエディタのコードを取得(コードエディタで何かが開かれている場合)
288
+ editor_code = ""
289
+ editor_reference = ""
290
+ if st.session_state.current_file and st.session_state.current_file in st.session_state.files:
291
+ editor_code = st.session_state.files[st.session_state.current_file]
292
+ editor_reference = f"現在エディタで開かれているファイル '{st.session_state.current_file}':\n```\n{editor_code}\n```"
293
+
294
+ # 通常モード用のシステムプロンプトをロード
295
+ system_prompt = load_system_prompt(MODELS["qwen_coder"], "通常モード")
296
+
297
+ qwen_prompt = f"""
298
+ 以下のユーザー入力に対してコーディングアシスタントとして回答してください。
299
+ Mistral Sabaの分析結果{physics_consideration}を考慮して、
300
+ 最適なソリューションを提供してください。
301
+
302
+ ユーザー入力: {user_input}
303
+
304
+ Mistral Sabaの分析:
305
+ {mistral_analysis}
306
+ """
307
+
308
+ # 物理関連の場合のみDeepSeekの情報を追加
309
+ if is_physics:
310
+ qwen_prompt += f"\n\n{deepseek_section}"
311
+
312
+ # エディタのコードが存在する場合は参照として追加
313
+ if editor_code:
314
+ qwen_prompt += f"\n\n{editor_reference}\n\nエディタで開かれているコードに関して、ユーザーの質問に答えるか、必要に応じてコードの改善を提案してください。"
315
+
316
+ qwen_response = client.chat.completions.create(
317
+ model=MODELS["qwen_coder"],
318
+ messages=[
319
+ {"role": "system", "content": system_prompt},
320
+ {"role": "user", "content": qwen_prompt}
321
+ ],
322
+ temperature=0.5,
323
+ max_tokens=4000
324
+ )
325
+
326
+ final_response = qwen_response.choices[0].message.content
327
+
328
+ elif st.session_state.current_mode == "実装モード":
329
+ # 物理関連の条件分岐を変数で処理
330
+ deepseek_section = ""
331
+ physics_note = ""
332
+
333
+ if is_physics:
334
+ deepseek_section = f"DeepSeekの物理学的知見:\n{deepseek_knowledge}"
335
+ physics_note = "この実装には物理シミュレーションが含まれているため、物理法則の正確性と数値的安定性を特に重視してください。"
336
+
337
+ # Step 2a: 設計フェーズ - 専用のプロンプトを使用
338
+ design_system_prompt = load_system_prompt(MODELS["qwen_coder"], "実装モード_設計")
339
+
340
+ design_prompt = f"""
341
+ 以下のユーザー入力と分析結果に基づいて、実装に必要な設計書を作成してください。
342
+
343
+ ユーザー入力: {user_input}
344
+
345
+ Mistral Sabaの分析:
346
+ {mistral_analysis}
347
+ """
348
+
349
+ # 物理関連の場合のみDeepSeekの情報を追加
350
+ if is_physics:
351
+ design_prompt += f"\n\n{deepseek_section}"
352
+
353
+ design_response = client.chat.completions.create(
354
+ model=MODELS["qwen_coder"],
355
+ messages=[
356
+ {"role": "system", "content": design_system_prompt},
357
+ {"role": "user", "content": design_prompt}
358
+ ],
359
+ temperature=0.4,
360
+ max_tokens=3000
361
+ )
362
+
363
+ design_doc = design_response.choices[0].message.content
364
+ thinking_results.append({
365
+ "title": "Qwen Coderによる設計書",
366
+ "content": design_doc
367
+ })
368
+
369
+ # Step 2b: 実装フェーズ - 専用のプロンプトを使用
370
+ implementation_system_prompt = load_system_prompt(MODELS["qwen_coder"], "実装モード_実装")
371
+
372
+ implementation_prompt = f"""
373
+ 以下の設計書に基づいて、完全な実装コードを提供してください。
374
+ コードは実行可能で、エラー処理が適切に行われているものにしてください。
375
+ 各コンポーネントには十分なコメントを付け、使用方法の例も含めてください。
376
+ {physics_note}
377
+
378
+ 設計書:
379
+ {design_doc}
380
+
381
+ ユーザーの元々の要求:
382
+ {user_input}
383
+ """
384
+
385
+ implementation_response = client.chat.completions.create(
386
+ model=MODELS["qwen_coder"],
387
+ messages=[
388
+ {"role": "system", "content": implementation_system_prompt},
389
+ {"role": "user", "content": implementation_prompt}
390
+ ],
391
+ temperature=0.5,
392
+ max_tokens=4000
393
+ )
394
+
395
+ implementation_result = implementation_response.choices[0].message.content
396
+
397
+ # 設計書と実装を組み合わせた最終結果
398
+ final_response = f"# 設計書\n\n{design_doc}\n\n# 実装コード\n\n{implementation_result}"
399
+
400
+ # コードの自動抽出と保存機能
401
+ code_blocks = extract_all_code_blocks(implementation_result)
402
+ if code_blocks and len(code_blocks) > 0:
403
+ # 抽出したコードブロックに対してボタンを作成するための識別子を付加
404
+ final_response += "\n\n**実装されたコードをエディタに保存できます。チャット上の「コードを保存」ボタンをクリックしてください。**"
405
+ # 最初のコードブロックを抽出してセッション状態に保存
406
+ st.session_state.last_generated_code = code_blocks[0]
407
+
408
+ elif st.session_state.current_mode == "エラー修正モード":
409
+ # エディタのコードを取得(存在する場合)
410
+ editor_code = ""
411
+ editor_reference = ""
412
+ if st.session_state.current_file and st.session_state.current_file in st.session_state.files:
413
+ editor_code = st.session_state.files[st.session_state.current_file]
414
+ editor_reference = f"現在エディタで開かれているファイル '{st.session_state.current_file}':\n```\n{editor_code}\n```\n\nこのコードにエラーがある可能性があります。ユーザーからの情報と合わせて分析してください。"
415
+
416
+ # 物理関連の条件分岐を変数で処理
417
+ deepseek_section = ""
418
+
419
+ if is_physics:
420
+ deepseek_section = f"DeepSeekの物理学的知見:\n{deepseek_knowledge}"
421
+
422
+ # エラー修正モード用のシステムプロンプトをロード
423
+ error_system_prompt = load_system_prompt(MODELS["qwen_coder"], "エラー修正モード")
424
+
425
+ error_prompt = f"""
426
+ 以下のユーザー入力はコードのエラーに関するものです。
427
+ エラーの原因を特定し、修正案を提供してください。
428
+ 説明では:
429
+ 1. エラーの根本原因
430
+ 2. 問題箇所の特定
431
+ 3. 修正方法の詳細な説明
432
+ 4. 修正されたコード
433
+ 5. 同様のエラーを防ぐためのベストプラクティス
434
+ を含めてください。
435
+
436
+ ユーザー入力: {user_input}
437
+
438
+ Mistral Sabaの分析:
439
+ {mistral_analysis}
440
+ """
441
+
442
+ # エディタのコードを追加
443
+ if editor_code:
444
+ error_prompt += f"\n\n{editor_reference}"
445
+
446
+ # 物理関連の場合のみDeepSeekの情報を追加
447
+ if is_physics:
448
+ error_prompt += f"\n\n{deepseek_section}"
449
+
450
+ error_response = client.chat.completions.create(
451
+ model=MODELS["qwen_coder"],
452
+ messages=[
453
+ {"role": "system", "content": error_system_prompt},
454
+ {"role": "user", "content": error_prompt}
455
+ ],
456
+ temperature=0.4,
457
+ max_tokens=4000
458
+ )
459
+
460
+ final_response = error_response.choices[0].message.content
461
+
462
+ return final_response, thinking_results
463
+
464
+ def generate_direct_response(user_input, api_key):
465
+ """会話の連続性を考慮した直接応答を生成する"""
466
+ # APIクライアントを初期化
467
+ client = Groq(api_key=api_key)
468
+
469
+ # モデル名のマッピング
470
+ from config import MODELS
471
+
472
+ # 現在のモードに合わせたシステムプロンプトをロード
473
+ current_system_prompt = load_system_prompt(MODELS["qwen_coder"], st.session_state.current_mode)
474
+
475
+ # 会話の連続性のための追加指示
476
+ current_system_prompt += """
477
+
478
+ 重要: これは継続中の会話です。前の質問や回答を踏まえて、会話の自然な流れを維持してください。
479
+ ユーザーの最新の質問だけに集中するのではなく、会話全体の文脈を考慮してください。
480
+ 繰り返しは避け、新しい情報や洞察を提供してください。
481
+ """
482
+
483
+ # 過去の会話履歴を完全に構築
484
+ context = ""
485
+ if len(st.session_state.messages) > 2:
486
+ # 最近の会話履歴(最大トークン制限を考慮して適切な数に制限)
487
+ recent_messages = st.session_state.messages[-10:] # 最近の10メッセージまで
488
+ for msg in recent_messages:
489
+ role = "ユーザー" if msg["role"] == "user" else "アシスタント"
490
+ # 全文を含める
491
+ context += f"{role}: {msg['content']}\n\n"
492
+
493
+ # 現在のエディタのコードを取得(存在する場合)
494
+ editor_code = ""
495
+ editor_reference = ""
496
+ if st.session_state.current_file and st.session_state.current_file in st.session_state.files:
497
+ editor_code = st.session_state.files[st.session_state.current_file]
498
+ editor_reference = f"\n\n現在エディタで開かれているファイル '{st.session_state.current_file}':\n```\n{editor_code}\n```\n\nエディタで開かれているコードに関して、ユーザーの質問に答えるか、必要に応じてコードの改善を提案してください。"
499
+
500
+ qwen_prompt = f"""
501
+ あなたはユーザーと継続的な会話を行っているアシスタントです。
502
+ これまでの会話の流れを踏まえて、最新のユーザー入力に適切に応答してください。
503
+
504
+ これまでの会話:
505
+ {context}
506
+
507
+ 最新のユーザー入力: {user_input}
508
+
509
+ 前の会話の文脈を考慮して、この最新の質問に直接回答してください。
510
+ 前回までの内容を繰り返すのではなく、会話を自然に進めてください。
511
+ """
512
+
513
+ # エディタのコードが存在する場合は参照として追加
514
+ if editor_code:
515
+ qwen_prompt += editor_reference
516
+
517
+ qwen_response = client.chat.completions.create(
518
+ model=MODELS["qwen_coder"],
519
+ messages=[
520
+ {"role": "system", "content": current_system_prompt},
521
+ {"role": "user", "content": qwen_prompt}
522
+ ],
523
+ temperature=0.5,
524
+ max_tokens=4000
525
+ )
526
+
527
+ return qwen_response.choices[0].message.content
528
+
529
+ def request_ai_edit(api_key, code, instructions, language):
530
+ """AIにコードの編集を依頼する"""
531
+ client = Groq(api_key=api_key)
532
+
533
+ # モデル名のマッピング
534
+ from config import MODELS
535
+
536
+ # 編集用のシステムプロンプトをロード
537
+ edit_system_prompt = """あなたは優秀なコードエディタアシスタントです。
538
+ ユーザーから提供されたコードを指示に従って編集する役割を担っています。
539
+
540
+ 以下のガイドラインに従ってください:
541
+ 1. コード全体を提供してください(一部だけではなく)
542
+ 2. 元のコードの機能を維持してください
543
+ 3. コメントを追加して読みやすくします
544
+ 4. ベストプラクティスに従ってコードを改善します
545
+ 5. パフォーマンスやセキュリ��ィの問題があれば修正します
546
+
547
+ 最終的な編集済みコードのみを返してください。説明や解説は含めないでください。
548
+ """
549
+
550
+ edit_prompt = f"""
551
+ 以下の{language}コードを編集してください。
552
+
553
+ 編集指示:
554
+ {instructions}
555
+
556
+ コード:
557
+ ```
558
+ {code}
559
+ ```
560
+
561
+ 編集後のコード全体を返してください。解説は不要です。
562
+ """
563
+
564
+ try:
565
+ response = client.chat.completions.create(
566
+ model=MODELS["qwen_coder"],
567
+ messages=[
568
+ {"role": "system", "content": edit_system_prompt},
569
+ {"role": "user", "content": edit_prompt}
570
+ ],
571
+ temperature=0.3,
572
+ max_tokens=4000
573
+ )
574
+
575
+ # 回答からコードを抽出
576
+ edited_code = response.choices[0].message.content
577
+
578
+ # コードブロックから実際のコードを抽出
579
+ if "```" in edited_code:
580
+ # コードブロックのマークアップを削除
581
+ code_pattern = r"```(?:\w+)?\n([\s\S]*?)\n```"
582
+ match = re.search(code_pattern, edited_code)
583
+ if match:
584
+ edited_code = match.group(1)
585
+
586
+ return edited_code
587
+
588
+ except Exception as e:
589
+ st.error(f"AIによるコード編集中にエラーが発生しました: {str(e)}")
590
+ return None
591
+
592
+ def update_conversation_state(user_input, ai_response):
593
+ """会話の状態を更新する"""
594
+ # 主要トピックの更新(最初のメッセージから)
595
+ if st.session_state.conversation_state["topic"] is None and user_input:
596
+ # 簡易的なトピック抽出(最初の文か最初の30単語)
597
+ first_sentence = user_input.split('.')[0]
598
+ topic = first_sentence[:100] + "..." if len(first_sentence) > 100 else first_sentence
599
+ st.session_state.conversation_state["topic"] = topic
600
+
601
+ # フォローアップカウントの更新
602
+ if "?" in user_input or "?" in user_input:
603
+ st.session_state.conversation_state["follow_up_count"] += 1
604
+
605
+ def summarize_chat():
606
+ """チャット履歴を要約する"""
607
+ # APIキーの取得
608
+ api_key = load_api_key()
609
+ if not api_key:
610
+ return "APIキーが設定されていないため、要約を生成できません。"
611
+
612
+ client = Groq(api_key=api_key)
613
+
614
+ # チャット履歴を文字列にまとめる
615
+ chat_history = ""
616
+ for msg in st.session_state.messages:
617
+ role = "ユーザー" if msg["role"] == "user" else "アシスタント"
618
+ chat_history += f"{role}: {msg['content']}\n\n"
619
+
620
+ # 要約を生成
621
+ from config import MODELS
622
+
623
+ summarize_prompt = f"""
624
+ 以下はユーザーとアシスタントの間の会話です。この会話を300単語以内で要約してください。
625
+ 特に重要な内容、技術的な詳細、解決した問題に焦点を当ててください。
626
+
627
+ {chat_history}
628
+ """
629
+
630
+ # Mistral Sabaモデルのシステムプロンプトをロード
631
+ summary_system_prompt = load_system_prompt(MODELS["mistral_saba"])
632
+
633
+ summary_response = client.chat.completions.create(
634
+ model=MODELS["mistral_saba"],
635
+ messages=[
636
+ {"role": "system", "content": summary_system_prompt},
637
+ {"role": "user", "content": summarize_prompt}
638
+ ],
639
+ temperature=0.3,
640
+ max_tokens=1000
641
+ )
642
+
643
+ summary = summary_response.choices[0].message.content
644
+ st.session_state.chat_summary = summary
645
+
646
+ # 要約をthinking_processに追加
647
+ st.session_state.thinking_process.append({
648
+ "title": "前回の会話要約",
649
+ "content": summary
650
+ })
651
+
652
+ return summary