root
commited on
Commit
·
b9f174a
1
Parent(s):
a6d56a1
✨ feat: おみくじ機能を拡張し、CIのリポジトリURLを修正
Browse files- .env.example +2 -1
- .github/workflows/sync-to-hf.yml +2 -2
- 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 |
-
|
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 |
-
|
9 |
-
|
10 |
-
|
|
|
11 |
|
12 |
-
|
13 |
-
|
14 |
|
15 |
-
|
16 |
-
|
17 |
-
"""
|
18 |
-
return text[::-1]
|
19 |
|
|
|
|
|
|
|
20 |
|
21 |
-
|
22 |
-
|
23 |
-
|
|
|
|
|
|
|
24 |
|
25 |
-
|
26 |
-
|
|
|
|
|
27 |
|
28 |
-
|
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 |
-
|
37 |
-
|
38 |
-
return img_array
|
39 |
|
|
|
40 |
|
41 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
42 |
"""
|
43 |
-
テキストの単語数をカウントする。
|
44 |
|
45 |
-
|
46 |
-
text (str): カウントしたいテキスト。
|
47 |
|
48 |
-
|
49 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
50 |
"""
|
51 |
-
|
52 |
-
|
53 |
-
|
|
|
54 |
|
55 |
|
56 |
-
def
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
57 |
"""
|
58 |
-
画像をリサイズする。
|
59 |
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
|
|
64 |
|
65 |
-
|
66 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
67 |
"""
|
68 |
-
# NumPy配列からPILイメージに変換
|
69 |
-
pil_image = Image.fromarray(image)
|
70 |
|
71 |
-
|
72 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
73 |
|
74 |
|
75 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
76 |
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
outputs=gr.Image(),
|
85 |
-
api_name="resize_image",
|
86 |
)
|
87 |
|
88 |
|
|
|
89 |
demo = gr.TabbedInterface(
|
90 |
-
[
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
[
|
97 |
-
"テキスト反転",
|
98 |
-
"QRコード生成",
|
99 |
-
"単語数カウント",
|
100 |
-
"画像リサイズ",
|
101 |
],
|
102 |
)
|
103 |
|
104 |
|
105 |
if __name__ == "__main__":
|
106 |
-
# mcp_server=True
|
|
|
|
|
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)
|