tomo2chin2 commited on
Commit
672d01c
·
verified ·
1 Parent(s): f7515c9

Upload 9 files

Browse files
Files changed (3) hide show
  1. DEVELOPMENT_LOG.md +10 -1
  2. app.py +146 -101
  3. requirements.txt +2 -1
DEVELOPMENT_LOG.md CHANGED
@@ -83,16 +83,25 @@
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キー(必須)
 
83
  - **原因**: モデルは`IMAGE`と`TEXT`の両方を要求
84
  - **解決**: `response_modalities=["IMAGE", "TEXT"]`に変更
85
 
86
+ ### 2. 画像生成の不安定性 (部分的解決)
87
  - **症状**: 1回目は成功するが、2回目以降は画像データが含まれない
88
  - **対策実施**:
89
  - レスポンスのパーツを全て検査するように改善
90
  - テキストコンテンツも収集してエラー理由を把握
91
+ - システムインストラクションを追加して画像生成を強制
92
  - **推測される原因**:
93
  - レート制限
94
  - コンテンツフィルタリング
95
  - プロンプトの内容による拒否
96
 
97
+ ### 3. MCPエンドポイント認識問題 (調査中)
98
+ - **症状**: `/api/health`, `/api/mcp/*`エンドポイントがGradio UIを返す
99
+ - **原因**: Gradio 5.31.0内部FastAPIでのルート追加が正しく動作していない
100
+ - **対策検討**:
101
+ - FastAPIとGradioを完全分離
102
+ - プロキシ型アーキテクチャ
103
+ - Gradio External APIの利用
104
+
105
  ## 環境設定メモ
106
  - Hugging Face Spaces Secrets:
107
  - `GEMINI_API_KEY` - Gemini APIキー(必須)
app.py CHANGED
@@ -10,7 +10,9 @@ import logging
10
  import traceback
11
  from typing import Optional
12
  from fastapi import FastAPI, Request
13
- from fastapi.responses import JSONResponse
 
 
14
 
15
  # ログ設定
16
  logging.basicConfig(
@@ -19,6 +21,26 @@ logging.basicConfig(
19
  )
20
  logger = logging.getLogger(__name__)
21
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  # Gemini APIクライアントの初期化
23
  def get_gemini_client():
24
  api_key = os.environ.get("GEMINI_API_KEY")
@@ -151,6 +173,99 @@ def generate_image(prompt: str, previous_image: Optional[Image.Image] = None) ->
151
  logger.error(traceback.format_exc())
152
  return None, f"エラーが発生しました: {str(e)}"
153
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
154
  # Gradioインターフェースの作成
155
  def create_gradio_interface():
156
  with gr.Blocks(title="画像生成MCP - Gemini 2.0 Flash") as demo:
@@ -159,6 +274,11 @@ def create_gradio_interface():
159
 
160
  このアプリケーションは主にClaudeCodeから利用するためのMCPサーバーです。
161
  Gemini 2.0 Flash Previewを使用して画像を生成します。
 
 
 
 
 
162
  """)
163
 
164
  with gr.Tab("画像生成テスト"):
@@ -190,34 +310,25 @@ def create_gradio_interface():
190
 
191
  with gr.Tab("MCP API情報"):
192
  gr.Markdown("""
193
- ### MCP APIエンドポイント
194
-
195
- 以下のエンドポイントを使用してClaudeCodeから画像生成機能を利用できます:
196
-
197
- - **ツール一覧取得**: `POST https://[your-space-name].hf.space/api/mcp/list_tools`
198
- - **画像生成実行**: `POST https://[your-space-name].hf.space/api/mcp/call_tool`
199
- - **ヘルスチェック**: `GET https://[your-space-name].hf.space/api/health`
200
-
201
- ### リクエスト例
202
-
203
- #### ツール実行 (call_tool)
204
  ```json
205
  {
206
- "name": "generate_image",
207
- "arguments": {
208
- "prompt": "美しい夕日の風景"
209
  }
 
210
  }
211
  ```
212
 
213
- ### レスポンス例
214
- ```json
215
- {
216
- "success": true,
217
- "message": "画像生成に成功しました!",
218
- "image_base64": "iVBORw0KGgo..."
219
- }
220
- ```
221
  """)
222
 
223
  # イベントハンドラ
@@ -229,7 +340,6 @@ def create_gradio_interface():
229
 
230
  return demo
231
 
232
-
233
  # メイン実行部分
234
  if __name__ == "__main__":
235
  # APIキーチェック
@@ -245,88 +355,23 @@ if __name__ == "__main__":
245
  - Name: `GEMINI_API_KEY`
246
  - Value: あなたのGemini APIキー
247
  """)
248
- # Hugging Face Spaces用の設定
249
- demo.queue()
250
  demo.launch()
251
  else:
252
  # Gradioインターフェース作成
253
  logger.info("画像生成MCPサーバーを起動中...")
254
  demo = create_gradio_interface()
255
 
256
- # Gradio内部のFastAPIアプリを取得
257
- app = demo.app
258
 
259
- # MCPエンドポイントを直接追加
260
- @app.post("/api/mcp/list_tools")
261
- async def mcp_list_tools():
262
- """MCPツールのリストを返す"""
263
- return {
264
- "tools": [
265
- {
266
- "name": "generate_image",
267
- "description": "Gemini 2.0 Flash Previewを使用して画像を生成します",
268
- "inputSchema": {
269
- "type": "object",
270
- "properties": {
271
- "prompt": {
272
- "type": "string",
273
- "description": "生成したい画像の説明"
274
- }
275
- },
276
- "required": ["prompt"]
277
- }
278
- }
279
- ]
280
- }
281
-
282
- @app.post("/api/mcp/call_tool")
283
- async def mcp_call_tool(request: Request):
284
- """MCPツールを実行する"""
285
- try:
286
- data = await request.json()
287
- tool_name = data.get("name")
288
- arguments = data.get("arguments", {})
289
-
290
- if tool_name == "generate_image":
291
- prompt = arguments.get("prompt", "")
292
-
293
- # 画像生成を実行
294
- image, message = generate_image(prompt)
295
-
296
- if image:
297
- # 画像をbase64エンコード
298
- buffered = io.BytesIO()
299
- image.save(buffered, format="PNG")
300
- img_str = base64.b64encode(buffered.getvalue()).decode()
301
-
302
- return JSONResponse({
303
- "success": True,
304
- "message": message,
305
- "image_base64": img_str
306
- })
307
- else:
308
- return JSONResponse({
309
- "success": False,
310
- "message": message
311
- })
312
-
313
- return JSONResponse({
314
- "success": False,
315
- "message": f"Unknown tool: {tool_name}"
316
- }, status_code=400)
317
-
318
- except Exception as e:
319
- logger.error(f"MCPエラー: {str(e)}")
320
- return JSONResponse({
321
- "success": False,
322
- "message": f"Error: {str(e)}"
323
- }, status_code=500)
324
-
325
- @app.get("/api/health")
326
- async def health_check():
327
- """ヘルスチェックエンドポイント"""
328
- return {"status": "healthy", "service": "image-gen-mcp"}
329
 
330
- # Hugging Face Spaces用の設定
331
- demo.queue()
332
- demo.launch()
 
10
  import traceback
11
  from typing import Optional
12
  from fastapi import FastAPI, Request
13
+ from fastapi.responses import JSONResponse, RedirectResponse
14
+ from fastapi.middleware.cors import CORSMiddleware
15
+ import uvicorn
16
 
17
  # ログ設定
18
  logging.basicConfig(
 
21
  )
22
  logger = logging.getLogger(__name__)
23
 
24
+ # Gradio内部ログも取得
25
+ gradio_logger = logging.getLogger("gradio")
26
+ gradio_logger.setLevel(logging.INFO)
27
+
28
+ # FastAPIアプリケーション初期化
29
+ app = FastAPI(
30
+ title="ImageGenMCP Server",
31
+ version="1.0.0",
32
+ description="Gemini 2.0 Flash画像生成MCPサーバー"
33
+ )
34
+
35
+ # CORS設定の最適化
36
+ app.add_middleware(
37
+ CORSMiddleware,
38
+ allow_origins=["*"],
39
+ allow_credentials=True,
40
+ allow_methods=["*"],
41
+ allow_headers=["*"],
42
+ )
43
+
44
  # Gemini APIクライアントの初期化
45
  def get_gemini_client():
46
  api_key = os.environ.get("GEMINI_API_KEY")
 
173
  logger.error(traceback.format_exc())
174
  return None, f"エラーが発生しました: {str(e)}"
175
 
176
+ # MCPエンドポイント
177
+ @app.post("/mcp/list_tools")
178
+ async def mcp_list_tools():
179
+ """MCPツールのリストを返す"""
180
+ logger.info("MCP list_tools 呼び出し")
181
+ return {
182
+ "tools": [
183
+ {
184
+ "name": "generate_image",
185
+ "description": "Gemini 2.0 Flash Previewを使用して画像を生成します",
186
+ "inputSchema": {
187
+ "type": "object",
188
+ "properties": {
189
+ "prompt": {
190
+ "type": "string",
191
+ "description": "生成したい画像の説明"
192
+ }
193
+ },
194
+ "required": ["prompt"]
195
+ }
196
+ }
197
+ ]
198
+ }
199
+
200
+ @app.post("/mcp/call_tool")
201
+ async def mcp_call_tool(request: Request):
202
+ """MCPツールを実行する"""
203
+ try:
204
+ data = await request.json()
205
+ tool_name = data.get("name")
206
+ arguments = data.get("arguments", {})
207
+
208
+ logger.info(f"MCP call_tool 呼び出し: {tool_name}, args: {arguments}")
209
+
210
+ if tool_name == "generate_image":
211
+ prompt = arguments.get("prompt", "")
212
+
213
+ # 画像生成を実行
214
+ image, message = generate_image(prompt)
215
+
216
+ if image:
217
+ # 画像をbase64エンコード
218
+ buffered = io.BytesIO()
219
+ image.save(buffered, format="PNG")
220
+ img_str = base64.b64encode(buffered.getvalue()).decode()
221
+
222
+ logger.info("MCP画像生成成功")
223
+ return JSONResponse({
224
+ "success": True,
225
+ "message": message,
226
+ "image_base64": img_str
227
+ })
228
+ else:
229
+ logger.warning(f"MCP画像生成失敗: {message}")
230
+ return JSONResponse({
231
+ "success": False,
232
+ "message": message
233
+ })
234
+
235
+ return JSONResponse({
236
+ "success": False,
237
+ "message": f"Unknown tool: {tool_name}"
238
+ }, status_code=400)
239
+
240
+ except Exception as e:
241
+ logger.error(f"MCPエラー: {str(e)}")
242
+ return JSONResponse({
243
+ "success": False,
244
+ "message": f"Error: {str(e)}"
245
+ }, status_code=500)
246
+
247
+ # ヘルスチェックエンドポイント
248
+ @app.get("/health")
249
+ async def health_check():
250
+ """ヘルスチェックエンドポイント"""
251
+ return {
252
+ "status": "OK",
253
+ "version": "5.31.0",
254
+ "service": "image-gen-mcp",
255
+ "endpoints": ["/mcp/list_tools", "/mcp/call_tool", "/health"]
256
+ }
257
+
258
+ # プロキシエンドポイント(外部からのアクセス用)
259
+ @app.post("/gradio_api/mcp/list_tools")
260
+ async def proxy_list_tools():
261
+ """Gradio API経由でのlist_tools呼び出し"""
262
+ return await mcp_list_tools()
263
+
264
+ @app.post("/gradio_api/mcp/call_tool")
265
+ async def proxy_call_tool(request: Request):
266
+ """Gradio API経由でのcall_tool呼び出し"""
267
+ return await mcp_call_tool(request)
268
+
269
  # Gradioインターフェースの作成
270
  def create_gradio_interface():
271
  with gr.Blocks(title="画像生成MCP - Gemini 2.0 Flash") as demo:
 
274
 
275
  このアプリケーションは主にClaudeCodeから利用するためのMCPサーバーです。
276
  Gemini 2.0 Flash Previewを使用して画像を生成します。
277
+
278
+ ## APIエンドポイント
279
+ - `POST /mcp/list_tools` - ツール一覧
280
+ - `POST /mcp/call_tool` - 画像生成実行
281
+ - `GET /health` - ヘルスチェック
282
  """)
283
 
284
  with gr.Tab("画像生成テスト"):
 
310
 
311
  with gr.Tab("MCP API情報"):
312
  gr.Markdown("""
313
+ ### ClaudeCode設定例
 
 
 
 
 
 
 
 
 
 
314
  ```json
315
  {
316
+ "mcpServers": {
317
+ "image-gen": {
318
+ "url": "https://tomo2chin2-imagegenmcp.hf.space/mcp/"
319
  }
320
+ }
321
  }
322
  ```
323
 
324
+ ### プロキシエンドポイント
325
+ - `POST /gradio_api/mcp/list_tools`
326
+ - `POST /gradio_api/mcp/call_tool`
327
+
328
+ ### 直接エンドポイント
329
+ - `POST /mcp/list_tools`
330
+ - `POST /mcp/call_tool`
331
+ - `GET /health`
332
  """)
333
 
334
  # イベントハンドラ
 
340
 
341
  return demo
342
 
 
343
  # メイン実行部分
344
  if __name__ == "__main__":
345
  # APIキーチェック
 
355
  - Name: `GEMINI_API_KEY`
356
  - Value: あなたのGemini APIキー
357
  """)
 
 
358
  demo.launch()
359
  else:
360
  # Gradioインターフェース作成
361
  logger.info("画像生成MCPサーバーを起動中...")
362
  demo = create_gradio_interface()
363
 
364
+ # GradioFastAPIにマウント
365
+ app = gr.mount_gradio_app(app, demo, path="/")
366
 
367
+ # サーバー起動設定
368
+ config = uvicorn.Config(
369
+ app=app,
370
+ host="0.0.0.0",
371
+ port=7860,
372
+ log_level="info"
373
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
374
 
375
+ logger.info("FastAPI + Gradio統合サーバー起動")
376
+ server = uvicorn.Server(config)
377
+ server.run()
requirements.txt CHANGED
@@ -1,5 +1,6 @@
1
- gradio==5.31.0
2
  google-genai
3
  pillow
4
  fastapi
 
5
  python-multipart
 
1
+ gradio>=5.31.0,<5.32
2
  google-genai
3
  pillow
4
  fastapi
5
+ uvicorn[standard]
6
  python-multipart