|
|
|
|
|
|
|
|
import os |
|
|
import json |
|
|
import uuid |
|
|
from PIL import Image |
|
|
from dotenv import load_dotenv |
|
|
from flask import Flask, request, jsonify, send_from_directory |
|
|
from flask_cors import CORS |
|
|
import google.generativeai as genai |
|
|
|
|
|
|
|
|
load_dotenv() |
|
|
|
|
|
|
|
|
PERSONALITY_FILE = "personality.json" |
|
|
SESSIONS_FILE = "chat_sessions.json" |
|
|
UPLOADS_FOLDER = "uploads" |
|
|
DEFAULT_PERSONALITY = """ |
|
|
あなたは消防署用のAI、グラフを上手くわかりやすい説明が得意です。以下の制約条件と入力文をもとに、適切な回答を出力してください。(AI CoT) |
|
|
「ステップごとに考えてみましょう。」)\n「あなたの推論を詳しく説明してください。」\n「問題をステップごとに分解してください。」\n「計算を示してください。」\n「よく考えてから答えを出してください。」 |
|
|
""" |
|
|
|
|
|
|
|
|
if not os.path.exists(UPLOADS_FOLDER): |
|
|
os.makedirs(UPLOADS_FOLDER) |
|
|
|
|
|
app = Flask(__name__, static_folder='static', static_url_path='') |
|
|
CORS(app) |
|
|
|
|
|
|
|
|
try: |
|
|
api_key = "AIzaSyA0IZW7yEAdZ20eJ5urvVxHYGotzl1qO5c" |
|
|
if not api_key: raise ValueError("環境変数 'GEMINI_API_KEY' が設定されていません。") |
|
|
genai.configure(api_key=api_key) |
|
|
print("✅ Gemini APIの設定が完了しました。") |
|
|
except Exception as e: |
|
|
print(f"❌ Gemini APIの初期化に失敗: {e}") |
|
|
|
|
|
|
|
|
def load_data(filepath, default_data): |
|
|
if not os.path.exists(filepath): return default_data |
|
|
try: |
|
|
with open(filepath, 'r', encoding='utf-8') as f: return json.load(f) |
|
|
except (IOError, json.JSONDecodeError): return default_data |
|
|
|
|
|
def save_data(filepath, data): |
|
|
with open(filepath, 'w', encoding='utf-8') as f: json.dump(data, f, ensure_ascii=False, indent=2) |
|
|
|
|
|
|
|
|
system_instruction = load_data(PERSONALITY_FILE, {"personality": DEFAULT_PERSONALITY})["personality"] |
|
|
chat_sessions_data = load_data(SESSIONS_FILE, {}) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@app.route('/uploads/<path:filename>') |
|
|
def uploaded_file(filename): |
|
|
"""uploadsフォルダから画像ファイルを配信する""" |
|
|
return send_from_directory(UPLOADS_FOLDER, filename) |
|
|
|
|
|
|
|
|
@app.route('/api/sessions', methods=['GET']) |
|
|
def get_sessions(): |
|
|
session_list = [{"id": session_id, **data} for session_id, data in chat_sessions_data.items()] |
|
|
session_list.sort(key=lambda x: (x.get('pinned', False), x.get('id', '')), reverse=True) |
|
|
return jsonify(session_list) |
|
|
|
|
|
@app.route('/api/session/<session_id>', methods=['GET']) |
|
|
def get_session_history(session_id): return jsonify(chat_sessions_data.get(session_id, {})) |
|
|
|
|
|
@app.route('/api/session/<session_id>', methods=['DELETE']) |
|
|
def delete_session(session_id): |
|
|
if session_id in chat_sessions_data: |
|
|
del chat_sessions_data[session_id]; save_data(SESSIONS_FILE, chat_sessions_data) |
|
|
return jsonify({"status": "success"}) |
|
|
return jsonify({"error": "セッションが見つかりません。"}), 404 |
|
|
|
|
|
@app.route('/api/session/<session_id>/rename', methods=['POST']) |
|
|
def rename_session(session_id): |
|
|
if session_id in chat_sessions_data: |
|
|
new_title = request.get_json().get('title', '').strip() |
|
|
if new_title: |
|
|
chat_sessions_data[session_id]['title'] = new_title; save_data(SESSIONS_FILE, chat_sessions_data) |
|
|
return jsonify({"status": "success"}) |
|
|
return jsonify({"error": "タイトルが空です。"}), 400 |
|
|
return jsonify({"error": "セッションが見つかりません。"}), 404 |
|
|
|
|
|
@app.route('/api/session/<session_id>/pin', methods=['POST']) |
|
|
def pin_session(session_id): |
|
|
if session_id in chat_sessions_data: |
|
|
is_pinned = chat_sessions_data[session_id].get('pinned', False) |
|
|
chat_sessions_data[session_id]['pinned'] = not is_pinned; save_data(SESSIONS_FILE, chat_sessions_data) |
|
|
return jsonify({"status": "success", "pinned": not is_pinned}) |
|
|
return jsonify({"error": "セッションが見つかりません。"}), 404 |
|
|
|
|
|
|
|
|
@app.route('/api/set_personality', methods=['POST']) |
|
|
def set_personality(): |
|
|
global system_instruction |
|
|
system_instruction = request.get_json().get('personality', '').strip() or DEFAULT_PERSONALITY |
|
|
save_data(PERSONALITY_FILE, {"personality": system_instruction}) |
|
|
return jsonify({"status": "success", "message": f"人格を設定しました。"}) |
|
|
|
|
|
@app.route('/api/chat', methods=['POST']) |
|
|
def handle_chat(): |
|
|
session_id = request.form.get('session_id') |
|
|
user_input = request.form.get('message', '').strip() |
|
|
file = request.files.get('file') |
|
|
|
|
|
if not session_id or (not user_input and not file): return jsonify({"error": "必須情報が不足しています。"}), 400 |
|
|
|
|
|
try: |
|
|
model = genai.GenerativeModel(model_name="gemini-2.5-pro", system_instruction=system_instruction) |
|
|
|
|
|
session_data = chat_sessions_data.get(session_id, {"title": "", "history": [], "pinned": False}) |
|
|
past_history = session_data.get("history", []) |
|
|
|
|
|
user_parts_for_api = [part for part in ([user_input] if user_input else []) + ([Image.open(file.stream)] if file else [])] |
|
|
|
|
|
response = model.generate_content(past_history + [{'role': 'user', 'parts': user_parts_for_api}]) |
|
|
|
|
|
user_parts_for_history = [user_input] if user_input else [] |
|
|
if file: |
|
|
ext = os.path.splitext(file.filename)[1] |
|
|
unique_filename = f"{uuid.uuid4()}{ext}" |
|
|
filepath = os.path.join(UPLOADS_FOLDER, unique_filename) |
|
|
file.seek(0) |
|
|
file.save(filepath) |
|
|
user_parts_for_history.append(f"/uploads/{unique_filename}") |
|
|
|
|
|
new_history = past_history + [ |
|
|
{'role': 'user', 'parts': user_parts_for_history}, |
|
|
{'role': 'model', 'parts': [response.text]} |
|
|
] |
|
|
|
|
|
session_data["history"] = new_history |
|
|
if not session_data.get("title"): |
|
|
session_data["title"] = user_input[:40] or (file.filename if file else "新しいチャット") |
|
|
|
|
|
chat_sessions_data[session_id] = session_data |
|
|
save_data(SESSIONS_FILE, chat_sessions_data) |
|
|
|
|
|
return jsonify({"reply": response.text, "title": session_data["title"]}) |
|
|
|
|
|
except Exception as e: |
|
|
print(f"❌ APIエラー: {e}") |
|
|
return jsonify({"error": "APIとの通信中、またはファイル処理中にエラーが発生しました。"}), 500 |
|
|
|
|
|
@app.route('/') |
|
|
def serve_index(): return send_from_directory(app.static_folder, 'index.html') |
|
|
|
|
|
if __name__ == '__main__': app.run(host='0.0.0.0', port=5000, debug=True) |