root commited on
Commit
b9f174a
·
1 Parent(s): a6d56a1

✨ feat: おみくじ機能を拡張し、CIのリポジトリURLを修正

Browse files
Files changed (3) hide show
  1. .env.example +2 -1
  2. .github/workflows/sync-to-hf.yml +2 -2
  3. app.py +198 -72
.env.example CHANGED
@@ -9,4 +9,5 @@ GITHUB_TOKEN=your_github_token
9
 
10
  # Codex挙動の設定
11
  # 静粛モード: 1 に設定すると余分なログ出力を抑制
12
- CODEX_QUIET_MODE=1
 
 
9
 
10
  # Codex挙動の設定
11
  # 静粛モード: 1 に設定すると余分なログ出力を抑制
12
+ CODEX_QUIET_MODE=1
13
+
.github/workflows/sync-to-hf.yml CHANGED
@@ -18,5 +18,5 @@ jobs:
18
  env:
19
  HF_TOKEN: ${{ secrets.HF_TOKEN }}
20
  run: |
21
- git fetch https://MakiAi:[email protected]/spaces/MakiAi/gradio-mcp-template main || true
22
- git push --force https://MakiAi:[email protected]/spaces/MakiAi/gradio-mcp-template main
 
18
  env:
19
  HF_TOKEN: ${{ secrets.HF_TOKEN }}
20
  run: |
21
+ git fetch https://MakiAi:[email protected]/spaces/MakiAi/gradio-mcp-codex-template main || true
22
+ git push --force https://MakiAi:[email protected]/spaces/MakiAi/gradio-mcp-codex-template main
app.py CHANGED
@@ -1,107 +1,233 @@
1
- import numpy as np
2
- import gradio as gr
3
- import qrcode
4
- from PIL import Image
5
- import io # Added for parity with user-provided snippet (currently unused)
6
 
 
 
 
 
 
7
 
8
- def reverse_text(text):
9
- """
10
- テキストを反転する。
 
11
 
12
- Args:
13
- text (str): 反転したいテキスト。
14
 
15
- Returns:
16
- str: 反転されたテキスト。
17
- """
18
- return text[::-1]
19
 
 
 
 
20
 
21
- def generate_qr_code(text):
22
- """
23
- テキストからQRコードを生成する。
 
 
 
24
 
25
- Args:
26
- text (str): QRコードに埋め込むテキスト。
 
 
27
 
28
- Returns:
29
- numpy.ndarray: 生成されたQRコード画像 (RGB)。
30
- """
31
- qr = qrcode.QRCode(version=5, box_size=10, border=5)
32
- qr.add_data(text)
33
- qr.make(fit=True)
34
- img = qr.make_image(fill_color="black", back_color="white")
35
 
36
- # PILイメージをNumPy配列に変換
37
- img_array = np.array(img.convert("RGB"))
38
- return img_array
39
 
 
40
 
41
- def count_words(text):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  """
43
- テキストの単語数をカウントする。
44
 
45
- Args:
46
- text (str): カウントしたいテキスト。
47
 
48
- Returns:
49
- int: 単語数。
 
 
 
 
 
 
 
50
  """
51
- if not text.strip():
52
- return 0
53
- return len(text.split())
 
54
 
55
 
56
- def resize_image(image, width, height):
 
 
 
 
 
 
 
 
 
57
  """
58
- 画像をリサイズする。
59
 
60
- Args:
61
- image (numpy.ndarray): リサイズしたい画像。
62
- width (int): 新しい幅。
63
- height (int): 新しい高さ。
 
64
 
65
- Returns:
66
- numpy.ndarray: リサイズされた画像 (RGB)。
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
  """
68
- # NumPy配列からPILイメージに変換
69
- pil_image = Image.fromarray(image)
70
 
71
- resized_image = pil_image.resize((int(width), int(height)))
72
- return np.array(resized_image)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
 
74
 
75
- # --- Interface -----------------------------------------------------------
 
 
 
 
 
 
76
 
77
- resize_interface = gr.Interface(
78
- fn=resize_image,
79
- inputs=[
80
- gr.Image(),
81
- gr.Number(label="", value=300),
82
- gr.Number(label="高さ", value=300),
83
- ],
84
- outputs=gr.Image(),
85
- api_name="resize_image",
86
  )
87
 
88
 
 
89
  demo = gr.TabbedInterface(
90
- [
91
- gr.Interface(reverse_text, gr.Textbox(), gr.Textbox(), api_name="reverse_text"),
92
- gr.Interface(generate_qr_code, gr.Textbox(), gr.Image(), api_name="generate_qr_code"),
93
- gr.Interface(count_words, gr.Textbox(), gr.Number(), api_name="count_words"),
94
- resize_interface,
95
- ],
96
- [
97
- "テキスト反転",
98
- "QRコード生成",
99
- "単語数カウント",
100
- "画像リサイズ",
101
  ],
102
  )
103
 
104
 
105
  if __name__ == "__main__":
106
- # mcp_server=True starts the SSE endpoint at /gradio_api/mcp/sse
 
 
107
  demo.launch(mcp_server=True)
 
1
+ """app.py Gradio MCP: Omikuji (Fortune) App
 
 
 
 
2
 
3
+ This Gradio application exposes several "omikuji" (fortune-telling) tools as both a
4
+ visual web UI (tabbed) and Model Context Protocol (MCP) SSE endpoints. Each tool is
5
+ implemented as a standalone Python function with a rich docstring so that Gradio
6
+ can automatically generate a JSON schema for MCP clients (Claude Desktop, Cline,
7
+ etc.).
8
 
9
+ Patterns implemented
10
+ --------------------
11
+ 1. draw_omikuji_basic
12
+ └─ Returns a single traditional Japanese fortune such as "大吉", "凶" …
13
 
14
+ 2. draw_omikuji_lucky_item
15
+ └─ Adds a random lucky item (財布, 植物, …) to the basic fortune.
16
 
17
+ 3. draw_omikuji_lucky_color_number
18
+ └─ Adds a lucky colour (赤, 青 …) *and* lucky number (1-99).
 
 
19
 
20
+ 4. draw_omikuji_full
21
+ └─ Gives a comprehensive result (overall/love/money/health fortunes, lucky
22
+ colour, item and number). Optional `name` input personalises the message.
23
 
24
+ Running
25
+ -------
26
+ ```
27
+ uv venv # optional: create virtual-env with uv (or use pip)
28
+ source .venv/bin/activate
29
+ uv pip install -r requirements.txt
30
 
31
+ python app.py # UI http://127.0.0.1:7860/
32
+ # SSE http://127.0.0.1:7860/gradio_api/mcp/sse
33
+ ```
34
+ """
35
 
36
+ from __future__ import annotations
 
 
 
 
 
 
37
 
38
+ import random
39
+ from typing import Dict
 
40
 
41
+ import gradio as gr
42
 
43
+ # ---------------------------------------------------------------------------
44
+ # 🎴 Omikuji base data
45
+ # ---------------------------------------------------------------------------
46
+
47
+ FORTUNES = [
48
+ "大吉", # Excellent luck
49
+ "中吉", # Good luck
50
+ "小吉", # Little luck
51
+ "吉", # Luck
52
+ "末吉", # Future luck
53
+ "凶", # Bad luck
54
+ "大凶", # Terrible luck
55
+ ]
56
+
57
+ LUCKY_ITEMS = [
58
+ "財布",
59
+ "スマホ",
60
+ "手帳",
61
+ "時計",
62
+ "本",
63
+ "鉛筆",
64
+ "植物",
65
+ "マグカップ",
66
+ "イヤホン",
67
+ "傘",
68
+ ]
69
+
70
+ LUCKY_COLORS = [
71
+ "赤",
72
+ "青",
73
+ "緑",
74
+ "黄色",
75
+ "紫",
76
+ "オレンジ",
77
+ "ピンク",
78
+ "白",
79
+ "黒",
80
+ "金",
81
+ ]
82
+
83
+
84
+ # ---------------------------------------------------------------------------
85
+ # 🔮 Omikuji functions (MCP tools)
86
+ # ---------------------------------------------------------------------------
87
+
88
+
89
+ def draw_omikuji_basic() -> str:
90
+ """Draw a single omikuji fortune.
91
+
92
+ Returns
93
+ -------
94
+ str
95
+ A traditional Japanese fortune string such as "大吉" (excellent luck)
96
+ or "凶" (bad luck).
97
  """
 
98
 
99
+ return random.choice(FORTUNES)
 
100
 
101
+
102
+ def draw_omikuji_lucky_item() -> str:
103
+ """Draw an omikuji fortune **with a lucky item**.
104
+
105
+ Returns
106
+ -------
107
+ str
108
+ A sentence combining the fortune and a randomly chosen lucky item.
109
+ Example: "中吉 — 今日のラッキーアイテムは『時計』!".
110
  """
111
+
112
+ fortune = random.choice(FORTUNES)
113
+ item = random.choice(LUCKY_ITEMS)
114
+ return f"{fortune} — 今日のラッキーアイテムは『{item}』!"
115
 
116
 
117
+ def draw_omikuji_lucky_color_number() -> str:
118
+ """Draw an omikuji fortune **with lucky colour & number**.
119
+
120
+ Returns
121
+ -------
122
+ str
123
+ A formatted string that includes:
124
+ - Fortune (大吉, …)
125
+ - Lucky colour (赤, 青, …)
126
+ - Lucky number (1–99)
127
  """
 
128
 
129
+ fortune = random.choice(FORTUNES)
130
+ color = random.choice(LUCKY_COLORS)
131
+ number = random.randint(1, 99)
132
+ return f"{fortune} — ラッキーカラー: {color} / ラッキーナンバー: {number}"
133
+
134
 
135
+ def draw_omikuji_full(name: str | None = None) -> Dict[str, str]:
136
+ """Comprehensive omikuji result.
137
+
138
+ Parameters
139
+ ----------
140
+ name : str | None, optional
141
+ User's name. When provided the message becomes personalised.
142
+
143
+ Returns
144
+ -------
145
+ dict of str
146
+ Keys: "overall", "love", "money", "health", "lucky_color",
147
+ "lucky_item", "lucky_number".
148
+
149
+ Notes
150
+ -----
151
+ This function returns a dict so that MCP clients receive structured JSON.
152
+ In the Gradio UI the dictionary is rendered nicely as a JSON viewer.
153
  """
 
 
154
 
155
+ overall = random.choice(FORTUNES)
156
+
157
+ # Sub-category fortunes use a biased list (no 大凶) to avoid discouragement
158
+ sub_fortunes = FORTUNES[:-1] # remove 大凶
159
+ love = random.choice(sub_fortunes)
160
+ money = random.choice(sub_fortunes)
161
+ health = random.choice(sub_fortunes)
162
+
163
+ return {
164
+ "name": name or "あなた",
165
+ "overall": overall,
166
+ "love": love,
167
+ "money": money,
168
+ "health": health,
169
+ "lucky_color": random.choice(LUCKY_COLORS),
170
+ "lucky_item": random.choice(LUCKY_ITEMS),
171
+ "lucky_number": str(random.randint(1, 99)),
172
+ }
173
+
174
+
175
+ # ---------------------------------------------------------------------------
176
+ # 🖼️ Gradio UI — TabbedInterface
177
+ # ---------------------------------------------------------------------------
178
+
179
+ # Each tool is mapped to a separate Interface for clarity. The outputs are
180
+ # chosen so that both web UI and MCP behave nicely.
181
+
182
+ iface_basic = gr.Interface(
183
+ fn=draw_omikuji_basic,
184
+ inputs=[], # no user input
185
+ outputs=gr.Textbox(label="結果"),
186
+ api_name="draw_omikuji_basic",
187
+ description="最もシンプルな伝統おみくじです。クリックするだけで運勢が表示されます。",
188
+ )
189
+
190
+ iface_item = gr.Interface(
191
+ fn=draw_omikuji_lucky_item,
192
+ inputs=[],
193
+ outputs=gr.Textbox(label="結果"),
194
+ api_name="draw_omikuji_lucky_item",
195
+ description="運勢に加えてラッキーアイテムを教えてくれるおみくじです。",
196
+ )
197
 
198
 
199
+ iface_color_number = gr.Interface(
200
+ fn=draw_omikuji_lucky_color_number,
201
+ inputs=[],
202
+ outputs=gr.Textbox(label="結果"),
203
+ api_name="draw_omikuji_lucky_color_number",
204
+ description="運勢+ラッキーカラー&ナンバーのおみくじです。",
205
+ )
206
 
207
+
208
+ iface_full = gr.Interface(
209
+ fn=draw_omikuji_full,
210
+ inputs=[gr.Textbox(label="名前(任意)", placeholder="入力しなくてもOK")],
211
+ outputs=gr.JSON(label="総合結果"),
212
+ api_name="draw_omikuji_full",
213
+ description="総合運・恋愛運・金運・健康運とラッキー情報を一度に表示します。",
 
 
214
  )
215
 
216
 
217
+ # Combine into tabs
218
  demo = gr.TabbedInterface(
219
+ interface_list=[iface_basic, iface_item, iface_color_number, iface_full],
220
+ tab_names=[
221
+ "シンプルおみくじ",
222
+ "アイテム付きおみくじ",
223
+ "カラー&ナンバーおみくじ",
224
+ "総合おみくじ",
 
 
 
 
 
225
  ],
226
  )
227
 
228
 
229
  if __name__ == "__main__":
230
+ # `mcp_server=True` exposes the functions to SSE clients at
231
+ # /gradio_api/mcp/sse. Passing `share=True` is optional here but useful for
232
+ # quickly sharing the demo.
233
  demo.launch(mcp_server=True)