tomo2chin2 commited on
Commit
99760d3
·
verified ·
1 Parent(s): 5653c66

Upload 9 files

Browse files
Files changed (2) hide show
  1. DEVELOPMENT_LOG.md +107 -0
  2. app.py +25 -10
DEVELOPMENT_LOG.md ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ImageGenMCP 開発ログ
2
+
3
+ ## プロジェクト概要
4
+ - **目的**: ClaudeCode用の画像生成MCPサーバー
5
+ - **技術スタック**: Gradio 5.31.0, Gemini 2.0 Flash Preview, FastAPI
6
+ - **デプロイ先**: Hugging Face Spaces
7
+
8
+ ## 開発マイルストーン
9
+
10
+ ### 2025-06-01 - プロジェクト初期化と基本実装
11
+
12
+ #### 完了項目
13
+ 1. **プロジェクト構造の確立**
14
+ - `app.py` - メインアプリケーション
15
+ - `requirements.txt` - 依存関係
16
+ - `README.md` - Hugging Face Spaces仕様準拠
17
+ - `.gitignore` - Git設定
18
+ - `app.yaml` - HF Spaces設定
19
+ - `mcp_client.py` - MCPクライアントサンプル
20
+
21
+ 2. **Gemini 2.0 Flash統合**
22
+ - モデル名: `gemini-2.0-flash-preview-image-generation`
23
+ - 画像生成機能の実装
24
+ - 参考画像サポート
25
+ - エラーハンドリングとログ出力
26
+
27
+ 3. **UI崩れ問題の解決**
28
+ - **問題**: FastAPIとGradioの統合でUI崩れが発生
29
+ - **試行錯誤**:
30
+ - FastAPI root_path設定
31
+ - gr.mount_gradio_app()の使用
32
+ - パスマウント最適化
33
+ - **最終解決策**: Gradioの内部FastAPIアプリに直接ルートを追加するシンプルな構成
34
+
35
+ #### 技術的な学び
36
+ - Hugging Face SpacesでのFastAPI/Gradio統合は複雑になりがち
37
+ - Gradio 5.31.0は内部でFastAPIを使用しており、`demo.app`でアクセス可能
38
+ - `demo.queue()`と`demo.launch()`のシンプルな構成が最も安定
39
+
40
+ #### 現在のステータス
41
+ - ✅ 基本的なUI表示が正常動作
42
+ - ✅ 画像生成機能の実装完了
43
+ - ✅ MCPエンドポイントの定義完了
44
+ - ⏳ 実際の画像生成テスト待ち
45
+ - ⏳ MCPエンドポイントの動作確認待ち
46
+
47
+ ## APIエンドポイント仕様
48
+
49
+ ### MCPエンドポイント
50
+ - `POST /api/mcp/list_tools` - ツール一覧取得
51
+ - `POST /api/mcp/call_tool` - 画像生成実行
52
+ - `GET /api/health` - ヘルスチェック
53
+
54
+ ### リクエスト/レスポンス形式
55
+ ```json
56
+ // リクエスト (call_tool)
57
+ {
58
+ "name": "generate_image",
59
+ "arguments": {
60
+ "prompt": "生成したい画像の説明"
61
+ }
62
+ }
63
+
64
+ // レスポンス
65
+ {
66
+ "success": true,
67
+ "message": "画像生成に成功しました!",
68
+ "image_base64": "base64エンコードされた画像データ"
69
+ }
70
+ ```
71
+
72
+ ## 次のステップ
73
+ 1. 画像生成機能の実地テスト
74
+ 2. MCPエンドポイントの動作確認
75
+ 3. ClaudeCodeからの統合テスト
76
+ 4. パフォーマンス最適化
77
+ 5. エラーハンドリングの強化
78
+
79
+ ## 既知の問題と対策
80
+
81
+ ### 1. Gemini API レスポンスモダリティエラー (解決済み)
82
+ - **問題**: `response_modalities=["IMAGE"]`のみだとエラー
83
+ - **原因**: モデルは`IMAGE`と`TEXT`の両方を要求
84
+ - **解決**: `response_modalities=["IMAGE", "TEXT"]`に変更
85
+
86
+ ### 2. 画像生成の不安定性 (調査中)
87
+ - **症状**: 1回目は成功するが、2回目以降は画像データが含まれない
88
+ - **対策実施**:
89
+ - レスポンスのパーツを全て検査するように改善
90
+ - テキストコンテンツも収集してエラー理由を把握
91
+ - **推測される原因**:
92
+ - レート制限
93
+ - コンテンツフィルタリング
94
+ - プロンプトの内容による拒否
95
+
96
+ ## 環境設定メモ
97
+ - Hugging Face Spaces Secrets:
98
+ - `GEMINI_API_KEY` - Gemini APIキー(必須)
99
+
100
+ ## トラブルシューティング記録
101
+ 1. **UI崩れ問題** (解決済み)
102
+ - 症状: 画像アイコンのみ表示、UIコンポーネントが表示されない
103
+ - 原因: FastAPIとGradioのパス競合
104
+ - 解決: シンプルな統合方式に変更
105
+
106
+ ---
107
+ 最終更新: 2025-06-01
app.py CHANGED
@@ -94,19 +94,34 @@ def generate_image(prompt: str, previous_image: Optional[Image.Image] = None) ->
94
  logger.info("Gemini APIの呼び出し完了")
95
 
96
  # レスポンスから画像を取得
97
- if (response.candidates and
98
- response.candidates[0].content and
99
- response.candidates[0].content.parts and
100
- response.candidates[0].content.parts[0].inline_data):
101
 
102
- image_data = response.candidates[0].content.parts[0].inline_data.data
103
- image = Image.open(io.BytesIO(image_data))
 
 
 
 
 
 
 
 
104
 
105
- logger.info(f"画像生成成功: サイズ={image.size}")
106
- return image, "画像生成に成功しました!"
 
 
 
 
 
 
 
107
  else:
108
- logger.warning("レスポンスに画像データが含まれていません")
109
- return None, "画像の生成に失敗しました。レスポンスに画像データ含まれていません。"
110
 
111
  except Exception as e:
112
  logger.error(f"画像生成エラー: {str(e)}")
 
94
  logger.info("Gemini APIの呼び出し完了")
95
 
96
  # レスポンスから画像を取得
97
+ if response.candidates and response.candidates[0].content and response.candidates[0].content.parts:
98
+ # パーツを検査して画像を探す
99
+ image_found = False
100
+ text_content = ""
101
 
102
+ for part in response.candidates[0].content.parts:
103
+ if hasattr(part, 'inline_data') and part.inline_data:
104
+ # 画像データが見つかった
105
+ image_data = part.inline_data.data
106
+ image = Image.open(io.BytesIO(image_data))
107
+ logger.info(f"画像生成成功: サイズ={image.size}")
108
+ image_found = True
109
+ elif hasattr(part, 'text') and part.text:
110
+ # テキストコンテンツを収集
111
+ text_content += part.text
112
 
113
+ if image_found:
114
+ return image, "画像生成に成功しました!"
115
+ else:
116
+ # 画像が含まれていない場合の詳細ログ
117
+ logger.warning(f"レスポンスに画像データが含まれていません。テキスト: {text_content[:200]}")
118
+ if "I cannot" in text_content or "I can't" in text_content or "unable to" in text_content:
119
+ return None, f"画像生成が拒否されました: {text_content}"
120
+ else:
121
+ return None, "画像の生成に失敗しました。レスポンスに画像データが含まれていません。"
122
  else:
123
+ logger.warning("レスポンスに有効なコンテンツが含まれていません")
124
+ return None, "画像の生成に失敗しました。レスポンスが空です。"
125
 
126
  except Exception as e:
127
  logger.error(f"画像生成エラー: {str(e)}")