Groxy2 / utils.py
Yasu777's picture
Update utils.py
93329be verified
import os
import re
import streamlit as st
# プロンプトディレクトリのパス
PROMPTS_DIR = "prompts"
# プロンプトディレクトリが存在しない場合は作成
def ensure_prompts_directory():
if not os.path.exists(PROMPTS_DIR):
os.makedirs(PROMPTS_DIR)
# モデル名とモードからプロンプトファイルのパスを取得
def get_prompt_path(model_name, mode=None):
if mode:
# モードが指定されている場合はモード別のプロンプトを返す
return os.path.join(PROMPTS_DIR, f"{model_name}_{mode}.txt")
else:
# モードが指定されていない場合は基本プロンプトを返す
return os.path.join(PROMPTS_DIR, f"{model_name}.txt")
# デフォルトのプロンプト内容 - モデル別(簡易版)
DEFAULT_MODEL_PROMPTS = {
"mistral-saba-24b": """あなたは入力分析に特化したAIアシスタントです。ユーザーの質問や要求を深く分析し、重要な要素や課題を抽出してください。""",
"qwen-2.5-coder-32b": """あなたはコーディングに特化した高性能AIアシスタントです。
プログラミング問題に対する効率的なソリューションを提供し、多様な言語に対応してください。""",
"deepseek-r1-distill-llama-70b": """あなたは知識豊富なAIアシスタントです。
特に物理シミュレーションや複雑なアルゴリズムについての深い理解を持っています。"""
}
# デフォルトのプロンプト内容 - モード別(簡易版)
DEFAULT_MODE_PROMPTS = {
"qwen-2.5-coder-32b_通常モード": """あなたはコーディングアシスタントとして、ユーザーの質問や課題に対して明確で実用的な回答を提供してください。""",
"qwen-2.5-coder-32b_実装モード_設計": """ユーザーの要件に基づいて詳細な設計書を作成してください。
システム構造、コンポーネント責任、技術的仕様を明確に定義してください。""",
"qwen-2.5-coder-32b_実装モード_実装": """提供された設計書に基づいて、高品質な実装コードを生成してください。
コードは実行可能で、適切なエラー処理とコメントを含むものにしてください。""",
"qwen-2.5-coder-32b_エラー修正モード": """ユーザーが提示したコードのエラーを診断し修正してください。
エラーの根本原因を特定し、修正方法と再発防止策を提案してください。"""
}
# すべてのデフォルトプロンプトを含む辞書
ALL_DEFAULT_PROMPTS = {**DEFAULT_MODEL_PROMPTS, **DEFAULT_MODE_PROMPTS}
# プロンプトファイルが存在しない場合にデフォルトのプロンプトを作成
def create_default_prompt_files():
ensure_prompts_directory()
for prompt_key, prompt_text in ALL_DEFAULT_PROMPTS.items():
prompt_path = os.path.join(PROMPTS_DIR, f"{prompt_key}.txt")
if not os.path.exists(prompt_path):
with open(prompt_path, "w", encoding="utf-8") as f:
f.write(prompt_text)
# プロンプトをロード(ファイルが存在しない場合はデフォルトを使用)
def load_system_prompt(model_name, mode=None):
ensure_prompts_directory()
if mode:
# モード指定がある場合、モード別プロンプトをロード
prompt_key = f"{model_name}_{mode}"
prompt_path = get_prompt_path(model_name, mode)
# モード別プロンプトが存在しなければデフォルトを作成
if not os.path.exists(prompt_path) and prompt_key in DEFAULT_MODE_PROMPTS:
with open(prompt_path, "w", encoding="utf-8") as f:
f.write(DEFAULT_MODE_PROMPTS[prompt_key])
# ファイルが存在すればロード
if os.path.exists(prompt_path):
with open(prompt_path, "r", encoding="utf-8") as f:
return f.read()
# モード別プロンプトがない場合は基本プロンプトを使用
# 基本プロンプトをロード
base_prompt_path = os.path.join(PROMPTS_DIR, f"{model_name}.txt")
if os.path.exists(base_prompt_path):
with open(base_prompt_path, "r", encoding="utf-8") as f:
return f.read()
# どのファイルも存在しない場合、デフォルトを返す
return DEFAULT_MODEL_PROMPTS.get(model_name, "あなたはコーディングの専門家です。ユーザーのプログラミングに関する質問に詳細かつ正確に回答してください。")
# テキストが物理学に関連しているかチェックする関数
def is_physics_related(text):
"""テキストが物理学に関連しているか判定する"""
lower_text = text.lower()
# 明確な物理関連キーワード
explicit_physics_keywords = [
"物理法則", "物理シミュレーション", "ニュートン力学", "電磁気学", "熱力学", "量子力学", "相対性理論",
"physics simulation", "newton's laws", "electromagnetism", "thermodynamics",
"quantum mechanics", "relativity theory"
]
# 単語境界を考慮すべきキーワード
word_boundary_keywords = [
r"\b力学\b", r"\b重力\b", r"\b摩擦\b", r"\b衝突\b", r"\b運動方程式\b", r"\b加速度\b", r"\b速度\b", r"\b質量\b",
r"\bgravity\b", r"\bfriction\b", r"\bcollision\b", r"\bvelocity\b", r"\bacceleration\b", r"\bmass\b"
]
# 明確な物理用語が含まれているか
explicit_match = any(keyword.lower() in lower_text for keyword in explicit_physics_keywords)
# 境界を考慮すべき単語が含まれているか
boundary_match = any(re.search(pattern, lower_text) for pattern in word_boundary_keywords)
# 物理シミュレーション関連のフレーズパターン
physics_phrases = [
r"物理.*シミュレ(ーション|ート)",
r"physics.*simulation",
r"ボールの.*衝突",
r"ball.*collision",
r"重力.*計算",
r"gravity.*calculation",
r"運動方程式",
r"equation.*motion",
r"力学.*モデル",
r"mechanics.*model"
]
phrase_match = any(re.search(pattern, lower_text) for pattern in physics_phrases)
# 誤検出しやすいコンテキスト
non_physics_contexts = [
"アプリの動作", "システムの動作", "プログラムの動作", "ソフトウェアの動作",
"app behavior", "system behavior", "program behavior", "software behavior",
"UIの動き", "インターフェースの動き", "ボタンの動き",
"UI movement", "interface movement", "button movement"
]
negative_context = any(context.lower() in lower_text for context in non_physics_contexts)
return explicit_match or (boundary_match and not negative_context) or phrase_match
# テキストからコードブロックを抽出する関数
def extract_all_code_blocks(text):
"""テキストから全てのコードブロックを抽出する"""
code_block_pattern = r"```(\w+)?\n([\s\S]*?)\n```"
matches = re.findall(code_block_pattern, text)
if not matches:
return []
code_blocks = []
for lang, code in matches:
lang = lang or "python" # 言語指定がない場合はPythonと仮定
code_blocks.append(f"```{lang}\n{code}\n```")
return code_blocks
# AIが生成したコードを新しいファイルに保存する関数
def save_generated_code_to_new_file(code_text, suggested_name=None):
"""AIが生成したコードを新しいファイルに保存する"""
# コードブロックのマークアップを削除
code_pattern = r"```(?:\w+)?\n([\s\S]*?)\n```"
match = re.search(code_pattern, code_text)
clean_code = match.group(1) if match else code_text
# 言語の推測
language = "py" # デフォルトはPython
lang_pattern = r"```(\w+)\n"
lang_match = re.search(lang_pattern, code_text)
if lang_match:
detected_lang = lang_match.group(1).lower()
lang_mapping = {
"python": "py",
"javascript": "js",
"html": "html",
"css": "css",
"json": "json",
"java": "java",
"cpp": "cpp",
"c++": "cpp",
"c": "c",
"go": "go",
"rust": "rs",
"typescript": "ts"
}
language = lang_mapping.get(detected_lang, detected_lang)
# ファイル名の生成
if not suggested_name:
import hashlib
import time
# タイムスタンプとコードの一部からハッシュを生成
timestamp = str(int(time.time()))
code_hash = hashlib.md5(clean_code[:100].encode()).hexdigest()[:6]
suggested_name = f"generated_{timestamp}_{code_hash}"
# 拡張子が含まれていなければ追加
if "." not in suggested_name:
suggested_name = f"{suggested_name}.{language}"
# 同名ファイルが存在する場合は連番を付ける
base_name, ext = os.path.splitext(suggested_name)
counter = 1
file_name = suggested_name
while file_name in st.session_state.files:
file_name = f"{base_name}_{counter}{ext}"
counter += 1
# ファイルを保存
st.session_state.files[file_name] = clean_code
st.session_state.current_file = file_name
return file_name
# ファイル間の依存関係を分析する関数
def get_file_dependencies(files):
"""アップロードされたファイル群の依存関係を分析する"""
dependencies = {}
file_summary = {}
# ファイル間の依存関係を検出
for filename, content in files.items():
dependencies[filename] = []
file_summary[filename] = []
# 主要な機能や役割を特定するためのキーワードパターン
keyword_patterns = {
"import": r"import\s+(\w+)", # Pythonのimport文
"from": r"from\s+(\w+(?:\.\w+)*)\s+import", # fromを使ったimport文
"require": r"require\(['\"](.+?)['\"]\)", # Node.jsのrequire
"include": r"include\(['\"](.+?)['\"]\)", # PHPやC/C++のinclude
"class": r"class\s+(\w+)", # クラス定義
"function": r"def\s+(\w+)|function\s+(\w+)", # 関数定義
"streamlit": r"st\.(\w+)", # Streamlit関数
"variable": r"(\w+)\s*=\s*" # 変数定義
}
# 他のファイル名への参照を検索
for other_filename in files.keys():
if other_filename != filename and other_filename in content:
# ファイル名への直接参照パターン
direct_ref_patterns = [
rf"import\s+{os.path.splitext(other_filename)[0]}", # Pythonのimport
rf"from\s+{os.path.splitext(other_filename)[0]}", # Pythonのfrom import
rf"require\(['\"]\./{other_filename}['\"]\)", # Node.jsのrequire
rf"include\(['\"]\./{other_filename}['\"]\)", # includeでの参照
rf"open\(['\"]\./{other_filename}['\"]\)", # ファイルを開く操作
rf"['\"]\./{other_filename}['\"]" # ファイルパスの文字列
]
# いずれかのパターンにマッチする場合、依存関係を追加
if any(re.search(pattern, content) for pattern in direct_ref_patterns):
dependencies[filename].append(other_filename)
# ファイルの主要なコンポーネントを抽出
for keyword, pattern in keyword_patterns.items():
matches = re.findall(pattern, content)
if matches:
flat_matches = []
for match in matches:
if isinstance(match, tuple):
# タプルの場合(複数のキャプチャグループがある場合)
flat_matches.extend([m for m in match if m])
else:
flat_matches.append(match)
# 重複を除去して最大10個までの項目を記録
unique_matches = list(set(flat_matches))[:10]
if unique_matches:
summary_item = f"{keyword}: {', '.join(unique_matches)}"
file_summary[filename].append(summary_item)
# 依存関係を文字列として整形
dependencies_str = "ファイル間の依存関係:\n"
for filename, deps in dependencies.items():
if deps:
dependencies_str += f"- {filename} -> {', '.join(deps)}\n"
else:
dependencies_str += f"- {filename}: 依存ファイルなし\n"
# ファイル概要を文字列として整形
summary_str = "各ファイルの概要:\n"
for filename, summary_items in file_summary.items():
summary_str += f"## {filename}\n"
if summary_items:
for item in summary_items:
summary_str += f"- {item}\n"
else:
summary_str += "- 主要コンポーネントを検出できませんでした\n"
summary_str += "\n"
return dependencies_str, summary_str