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での画像選択は無効にすることを推奨します。")