AI Agent:tools:image_check.py:ソースコード

from langchain_core.messages import HumanMessage
# from langchain_community.chat_models import ChatOllama # 古いインポート
from langchain_ollama import ChatOllama # 新しい推奨されるインポート (pip install -U langchain-ollama が必要)
import base64
import requests
import json # Ollama APIとの通信に必要

# 画像ファイルをbase64にエンコードするヘルパー関数
def encode_image(image_path_or_url):
    if image_path_or_url.startswith("http://") or image_path_or_url.startswith("https://"):
        response = requests.get(image_path_or_url)
        response.raise_for_status()
        image_data = response.content
        mime_type = response.headers.get('Content-Type', 'image/jpeg') # ヘッダーからMIMEタイプ取得
        base64_encoded_data = base64.b64encode(image_data).decode('utf-8')
        return f"data:{mime_type};base64,{base64_encoded_data}"
    else:
        with open(image_path_or_url, "rb") as image_file:
            image_data = image_file.read()
            if image_path_or_url.lower().endswith(".png"):
                mime_type = "image/png"
            elif image_path_or_url.lower().endswith((".jpg", ".jpeg")):
                mime_type = "image/jpeg"
            else:
                raise ValueError("Unsupported image type. Please use PNG or JPEG.")
            base64_encoded_data = base64.b64encode(image_data).decode('utf-8')
            return f"data:{mime_type};base64,{base64_encoded_data}"

def check_ollama_model_image_capability(model_name: str, ollama_base_url: str = "http://localhost:11434") -> bool:
    """
    指定されたOllamaモデルが画像入力をサポートしているかを確認します。
    モデルのmodelfileやファミリー情報、パラメータ等から判断します。
    """
    api_url = f"{ollama_base_url}/api/show"
    payload = {"name": model_name}
    headers = {'Content-Type': 'application/json'}

    print(f"モデル '{model_name}' の画像対応能力を {api_url} で確認中...")

    try:
        response = requests.post(api_url, data=json.dumps(payload), headers=headers)
        response.raise_for_status()
        model_info = response.json()

        # 1. modelfileの内容を確認 (最も直接的な手がかり)
        modelfile_content = model_info.get("modelfile", "")
        if "mmproj" in modelfile_content or \
           "<image>" in modelfile_content or \
           "{{.ImageData}}" in modelfile_content or \
           "CLIP Encoder" in modelfile_content: # CLIP関連のキーワードもチェック
            print(f"  情報源: modelfile (mmproj, <image>, ImageData, CLIP Encoder のいずれかを発見)")
            return True

        # 2. templateの内容を確認 (modelfileに情報がない場合のフォールバック)
        template_content = model_info.get("template", "")
        # Ollamaのバージョンやモデルによってtemplateの場所が異なる場合がある
        if not template_content and "details" in model_info and isinstance(model_info["details"], dict):
            template_content = model_info["details"].get("template","")
        if "<image>" in template_content or "{{.ImageData}}" in template_content:
            print(f"  情報源: template (<image> または ImageData を発見)")
            return True

        # 3. モデルファミリーを確認
        details = model_info.get("details", {})
        family = details.get("family", "").lower()
        families = details.get("families", [])
        if isinstance(families, list):
            families = [f.lower() for f in families if isinstance(f, str)]

        # 一般的なマルチモーダルモデルのファミリー名
        multimodal_families = ["llava", "clip", "vision", "idefics", "fuyu", "moondream", "paligemma"]
        if any(mf_family in family for mf_family in multimodal_families) or \
           any(mf_family in fam for fam in families for mf_family in multimodal_families):
            print(f"  情報源: モデルファミリー (例: {family}, {families})")
            return True
        
        print(f"  '{model_name}' のAPI応答からは明確な画像対応の兆候が見つかりませんでした。")
        return False

    except requests.exceptions.HTTPError as e:
        if e.response.status_code == 404:
            print(f"エラー: モデル '{model_name}' がOllamaサーバー({ollama_base_url})に見つかりません。`ollama pull {model_name}` を実行しましたか?")
        else:
            print(f"Ollama APIへのHTTPエラーが発生しました: {e} (URL: {api_url}, Status: {e.response.status_code})")
        return False
    except requests.exceptions.RequestException as e:
        print(f"Ollama API ({api_url}) への接続中にエラーが発生しました: {e}")
        return False
    except json.JSONDecodeError:
        print(f"Ollama API ({api_url}) からの応答が不正なJSON形式です。")
        return False
    except Exception as e:
        print(f"モデル '{model_name}' の画像対応チェック中に予期せぬエラー: {e}")
        return False

# --- メインの処理 ---

# 確認したいOllamaモデル名 (Ollamaサーバーで `ollama list` を実行して確認)
# model_to_check = "gemma3:12b-it-qat" # テキストモデルの例
model_to_check = "llava"             # 画像対応モデルの例 (事前に `ollama pull llava` が必要)
# model_to_check = "bakllava"        # 画像対応モデルの例 (事前に `ollama pull bakllava` が必要)
# model_to_check = "moondream"       # 画像対応モデルの例 (事前に `ollama pull moondream` が必要)
# model_to_check = "nonexistentmodel" # 存在しないモデルの例

ollama_server_url = "http://localhost:11434" # OllamaサーバーのURL

print(f"--- モデル '{model_to_check}' の画像対応チェック開始 ---")
supports_images = check_ollama_model_image_capability(model_to_check, ollama_server_url)
print(f"--- モデル '{model_to_check}' の画像対応チェック終了 ---")


if supports_images:
    print(f"\n[結果] モデル '{model_to_check}' は画像入力に対応している可能性が高いです。")
    print("UIで画像選択を有効にし、画像処理を試みます。")

    # LangchainのChatOllamaインスタンスを作成
    # base_urlを指定することで、デフォルト以外のOllamaサーバーにも接続可能
    llm = ChatOllama(model=model_to_check, base_url=ollama_server_url)

    # 画像のパスまたはURL
    image_path = "D:/Graph_AIs/StabilityMatrix-win-x64/data/Images/Inference/Eating ramen with chopsticks/2025-05-22_07-37-19-novaAnimeXL_ilV70-1605644680.png"
    # image_path = "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg" # URLの例

    try:
        print(f"\n'{model_to_check}' を使用して画像 '{image_path}' の説明を試みます...")
        base64_image = encode_image(image_path)

        message = HumanMessage(
            content=[
                {
                    "type": "text",
                    "text": "この画像について詳細に説明してください。"
                },
                {
                    "type": "image_url",
                    "image_url": {"url": base64_image}
                }
            ]
        )

        response = llm.invoke([message]) # メッセージのリストとして渡す
        print("\nモデルからの応答:")
        print(response.content)

    except FileNotFoundError:
        print(f"エラー: 画像ファイルが見つかりません: {image_path}")
    except ValueError as e: # encode_image でサポートされていない形式の場合など
        print(f"エラー: {e}")
    except requests.exceptions.RequestException as e: # 画像URLのダウンロード失敗など
        print(f"エラー: 画像URL処理中にエラーが発生しました: {e}")
    except Exception as e:
        print(f"画像送信・処理中に予期せぬエラーが発生しました: {e}")
        # LangChainからのOllama呼び出しエラーの場合、詳細が含まれることがある
        if "Ollama call failed" in str(e):
            if "404" in str(e):
                 print(f"  ヒント: モデル '{model_to_check}' がOllamaサーバーで正しくプルされ、利用可能か確認してください。")
            print(f"  Ollama呼び出しエラーの詳細: {str(e)}")

else:
    print(f"\n[結果] モデル '{model_to_check}' は画像入力に対応していないか、判定できませんでした。")
    print("UIでの画像選択は無効にすることを推奨します。")