Skip to content

Commit aa9b215

Browse files
committed
Add examples
1 parent a7b81b7 commit aa9b215

3 files changed

Lines changed: 181 additions & 1 deletion

File tree

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,5 @@ scripts/
4747
.venv
4848
outputs/
4949
span.log
50-
uv.lock
50+
uv.lock
51+
workspace/*
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
{%- set image_count = namespace(value=0) %}
2+
{%- set video_count = namespace(value=0) %}
3+
4+
{%- if tools %}
5+
{{- '<|im_start|>system\n' }}
6+
7+
{%- if messages[0].role == 'system' %}
8+
{{- messages[0].content + '\n\n' }}
9+
{%- endif %}
10+
11+
{{- "# Tools\n\n" }}
12+
{{- "You may call one or more functions to assist with the user query.\n\n" }}
13+
{{- "You are provided with function signatures within <tools></tools> XML tags:\n<tools>" }}
14+
15+
{%- for tool in tools %}
16+
{{- "\n" + tool | tojson }}
17+
{%- endfor %}
18+
19+
{{- "\n</tools>\n\n" }}
20+
{{- "For each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags:\n" }}
21+
{{- "<tool_call>\n{\"name\": <function-name>, \"arguments\": <args-json-object>}\n</tool_call><|im_end|>\n" }}
22+
23+
{%- else %}
24+
{%- if messages[0].role != 'system' %}
25+
{{- '<|im_start|>system\nYou are a helpful assistant.<|im_end|>\n' }}
26+
{%- else %}
27+
{{- '<|im_start|>system\n' + messages[0].content + '<|im_end|>\n' }}
28+
{%- endif %}
29+
{%- endif %}
30+
31+
32+
{%- set ns = namespace(multi_step_tool=true, last_query_index=messages|length - 1) %}
33+
34+
{# Detect last user query before tool responses #}
35+
{%- for message in messages[::-1] %}
36+
{%- set index = (messages|length - 1) - loop.index0 %}
37+
{%- if ns.multi_step_tool and message.role == "user"
38+
and message.content is string
39+
and not (message.content.startswith('<tool_response>')
40+
and message.content.endswith('</tool_response>')) %}
41+
{%- set ns.multi_step_tool = false %}
42+
{%- set ns.last_query_index = index %}
43+
{%- endif %}
44+
{%- endfor %}
45+
46+
47+
{%- for message in messages %}
48+
{%- set role = message.role %}
49+
{%- set content = '' %}
50+
{%- if message.content is string %}
51+
{%- set content = message.content %}
52+
{%- elif message.content is iterable %}
53+
{%- for item in message.content %}
54+
{%- if item.type == 'image' or 'image' in item or 'image_url' in item %}
55+
{%- set image_count.value = image_count.value + 1 %}
56+
{%- if add_vision_id %}Picture {{ image_count.value }}: {% endif %}
57+
{{- '<|vision_start|><|image_pad|><|vision_end|>' }}
58+
{%- elif item.type == 'video' or 'video' in item %}
59+
{%- set video_count.value = video_count.value + 1 %}
60+
{%- if add_vision_id %}Video {{ video_count.value }}: {% endif %}
61+
{{- '<|vision_start|><|video_pad|><|vision_end|>' }}
62+
{%- elif 'text' in item %}
63+
{{- item.text }}
64+
{%- endif %}
65+
{%- endfor %}
66+
{%- endif %}
67+
68+
{%- if role == 'user' or (role == 'system' and not loop.first) %}
69+
{{- '<|im_start|>' + role + '\n' + content + '<|im_end|>\n' }}
70+
71+
{%- elif role == 'assistant' %}
72+
{%- set reasoning_content = '' %}
73+
{%- if message.reasoning_content is string %}
74+
{%- set reasoning_content = message.reasoning_content %}
75+
{%- elif '</think>' in content %}
76+
{%- set reasoning_content = content.split('</think>')[0].split('<think>')[-1].strip() %}
77+
{%- set content = content.split('</think>')[-1].lstrip('\n') %}
78+
{%- endif %}
79+
80+
{%- if loop.index0 > ns.last_query_index %}
81+
{%- if loop.last or reasoning_content %}
82+
{{- '<|im_start|>assistant\n<think>\n' + reasoning_content + '\n</think>\n\n' + content }}
83+
{%- else %}
84+
{{- '<|im_start|>assistant\n' + content }}
85+
{%- endif %}
86+
{%- else %}
87+
{{- '<|im_start|>assistant\n' + content }}
88+
{%- endif %}
89+
90+
{%- if message.tool_calls %}
91+
{%- for tool_call in message.tool_calls %}
92+
{%- if (loop.first and content) or (not loop.first) %}
93+
{{- '\n' }}
94+
{%- endif %}
95+
96+
{%- set tool_call = tool_call.function if tool_call.function else tool_call %}
97+
{{- '<tool_call>\n{"name": "' + tool_call.name + '", "arguments": ' }}
98+
{{- tool_call.arguments if tool_call.arguments is string else tool_call.arguments | tojson }}
99+
{{- '}\n</tool_call>' }}
100+
{%- endfor %}
101+
{%- endif %}
102+
103+
{{- '<|im_end|>\n' }}
104+
105+
{%- elif role == 'tool' %}
106+
{%- if loop.first or messages[loop.index0 - 1].role != 'tool' %}
107+
{{- '<|im_start|>user' }}
108+
{%- endif %}
109+
110+
{{- '\n<tool_response>\n' + content + '\n</tool_response>' }}
111+
112+
{%- if loop.last or messages[loop.index0 + 1].role != 'tool' %}
113+
{{- '<|im_end|>\n' }}
114+
{%- endif %}
115+
{%- endif %}
116+
{%- endfor %}
117+
118+
119+
{%- if add_generation_prompt %}
120+
{{- '<|im_start|>assistant\n' }}
121+
{%- if enable_thinking is defined and enable_thinking is false %}
122+
{{- '<think>\n\n</think>\n\n' }}
123+
{%- endif %}
124+
{%- endif %}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# server.py
2+
import base64
3+
import io
4+
from typing import List
5+
6+
from mcp.server.fastmcp import FastMCP
7+
from mcp.types import ImageContent
8+
from PIL import Image
9+
10+
app = FastMCP("demo")
11+
12+
13+
@app.tool(name="image_zoom_in_tool", description="Zoom in on a specific region of an image by cropping it based on a bounding box (bbox) and an optional object label.")
14+
def image_zoom_in_tool(image_path: str, bbox: List[float]):
15+
"""
16+
Zoom in on a specific region of an image by cropping it based on a bounding box (bbox) and an optional object label.
17+
18+
:param image_path: Path to the input image.
19+
:param bbox: Bounding box coordinates in the format [x_min, y_min, x_max, y_max].
20+
:return: Cropped image as a base64-encoded string.
21+
"""
22+
image = Image.open(image_path)
23+
24+
cropped_image = image.crop(bbox)
25+
26+
image_bytes = io.BytesIO()
27+
cropped_image.save(image_bytes, format="PNG")
28+
29+
image_bytes.seek(0)
30+
png = base64.b64encode(image_bytes.getvalue()).decode("utf-8")
31+
32+
return ImageContent(type="image", data=png, mimeType="image/png")
33+
34+
35+
@app.tool(name="weather", description="query weather")
36+
def get_weather(city: str):
37+
weather_data = {"Beijing": {"temp": 25, "condition": "Rainy"}, "Shanghai": {"temp": 28, "condition": "Cloudy"}}
38+
# 返回对应城市的天气信息,如果城市不存在则返回错误信息
39+
result = weather_data.get(city, {"error": "未找到该城市"})
40+
return result
41+
42+
43+
@app.tool(name="get_blank_image", description="get blank image")
44+
def get_blank_image(width: int = 512, height: int = 512):
45+
""" """
46+
image = Image.new("RGB", (width, height), color=(255, 255, 255))
47+
image_bytes = io.BytesIO()
48+
image.save(image_bytes, format="PNG")
49+
image_bytes.seek(0)
50+
png = base64.b64encode(image_bytes.getvalue()).decode("utf-8")
51+
return ImageContent(type="image", data=png, mimeType="image/png")
52+
53+
54+
if __name__ == "__main__":
55+
app.run()

0 commit comments

Comments
 (0)