forked from openai/openai-agents-python
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest_prompt_server.py
More file actions
322 lines (246 loc) · 11.2 KB
/
test_prompt_server.py
File metadata and controls
322 lines (246 loc) · 11.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
from typing import Any
import pytest
from mcp.types import ListResourcesResult, ListResourceTemplatesResult, ReadResourceResult
from agents import Agent, Runner
from agents.mcp import MCPServer, MCPToolMetaResolver
from ..fake_model import FakeModel
from ..test_responses import get_text_message
class FakeMCPPromptServer(MCPServer):
"""Fake MCP server for testing prompt functionality"""
def __init__(
self,
server_name: str = "fake_prompt_server",
tool_meta_resolver: MCPToolMetaResolver | None = None,
):
super().__init__(tool_meta_resolver=tool_meta_resolver)
self.prompts: list[Any] = []
self.prompt_results: dict[str, str] = {}
self._server_name = server_name
def add_prompt(self, name: str, description: str, arguments: dict[str, Any] | None = None):
"""Add a prompt to the fake server"""
from mcp.types import Prompt
prompt = Prompt(name=name, description=description, arguments=[])
self.prompts.append(prompt)
def set_prompt_result(self, name: str, result: str):
"""Set the result that should be returned for a prompt"""
self.prompt_results[name] = result
async def connect(self):
pass
async def cleanup(self):
pass
async def list_prompts(self, run_context=None, agent=None):
"""List available prompts"""
from mcp.types import ListPromptsResult
return ListPromptsResult(prompts=self.prompts)
async def get_prompt(self, name: str, arguments: dict[str, Any] | None = None):
"""Get a prompt with arguments"""
from mcp.types import GetPromptResult, PromptMessage, TextContent
if name not in self.prompt_results:
raise ValueError(f"Prompt '{name}' not found")
content = self.prompt_results[name]
# If it's a format string, try to format it with arguments
if arguments and "{" in content:
try:
content = content.format(**arguments)
except KeyError:
pass # Use original content if formatting fails
message = PromptMessage(role="user", content=TextContent(type="text", text=content))
return GetPromptResult(description=f"Generated prompt for {name}", messages=[message])
async def list_tools(self, run_context=None, agent=None):
return []
async def call_tool(
self,
tool_name: str,
arguments: dict[str, Any] | None = None,
meta: dict[str, Any] | None = None,
):
raise NotImplementedError("This fake server doesn't support tools")
async def list_resources(self) -> ListResourcesResult:
return ListResourcesResult(resources=[])
async def list_resource_templates(self) -> ListResourceTemplatesResult:
return ListResourceTemplatesResult(resourceTemplates=[])
async def read_resource(self, uri: str) -> ReadResourceResult:
return ReadResourceResult(contents=[])
@property
def name(self) -> str:
return self._server_name
@pytest.mark.asyncio
async def test_list_prompts():
"""Test listing available prompts"""
server = FakeMCPPromptServer()
server.add_prompt(
"generate_code_review_instructions", "Generate agent instructions for code review tasks"
)
result = await server.list_prompts()
assert len(result.prompts) == 1
assert result.prompts[0].name == "generate_code_review_instructions"
assert result.prompts[0].description is not None
assert "code review" in result.prompts[0].description
@pytest.mark.asyncio
async def test_get_prompt_without_arguments():
"""Test getting a prompt without arguments"""
server = FakeMCPPromptServer()
server.add_prompt("simple_prompt", "A simple prompt")
server.set_prompt_result("simple_prompt", "You are a helpful assistant.")
result = await server.get_prompt("simple_prompt")
assert len(result.messages) == 1
assert result.messages[0].content.text == "You are a helpful assistant."
@pytest.mark.asyncio
async def test_get_prompt_with_arguments():
"""Test getting a prompt with arguments"""
server = FakeMCPPromptServer()
server.add_prompt(
"generate_code_review_instructions", "Generate agent instructions for code review tasks"
)
server.set_prompt_result(
"generate_code_review_instructions",
"You are a senior {language} code review specialist. Focus on {focus}.",
)
result = await server.get_prompt(
"generate_code_review_instructions",
{"focus": "security vulnerabilities", "language": "python"},
)
assert len(result.messages) == 1
expected_text = (
"You are a senior python code review specialist. Focus on security vulnerabilities."
)
assert result.messages[0].content.text == expected_text
@pytest.mark.asyncio
async def test_get_prompt_not_found():
"""Test getting a prompt that doesn't exist"""
server = FakeMCPPromptServer()
with pytest.raises(ValueError, match="Prompt 'nonexistent' not found"):
await server.get_prompt("nonexistent")
@pytest.mark.asyncio
async def test_agent_with_prompt_instructions():
"""Test using prompt-generated instructions with an agent"""
server = FakeMCPPromptServer()
server.add_prompt(
"generate_code_review_instructions", "Generate agent instructions for code review tasks"
)
server.set_prompt_result(
"generate_code_review_instructions",
"You are a code reviewer. Analyze the provided code for security issues.",
)
# Get instructions from prompt
prompt_result = await server.get_prompt("generate_code_review_instructions")
instructions = prompt_result.messages[0].content.text
# Create agent with prompt-generated instructions
model = FakeModel()
agent = Agent(name="prompt_agent", instructions=instructions, model=model, mcp_servers=[server])
# Mock model response
model.add_multiple_turn_outputs(
[[get_text_message("Code analysis complete. Found security vulnerability.")]]
)
# Run the agent
result = await Runner.run(agent, input="Review this code: def unsafe_exec(cmd): os.system(cmd)")
assert "Code analysis complete" in result.final_output
assert (
agent.instructions
== "You are a code reviewer. Analyze the provided code for security issues."
)
@pytest.mark.asyncio
@pytest.mark.parametrize("streaming", [False, True])
async def test_agent_with_prompt_instructions_streaming(streaming: bool):
"""Test using prompt-generated instructions with streaming and non-streaming"""
server = FakeMCPPromptServer()
server.add_prompt(
"generate_code_review_instructions", "Generate agent instructions for code review tasks"
)
server.set_prompt_result(
"generate_code_review_instructions",
"You are a {language} code reviewer focusing on {focus}.",
)
# Get instructions from prompt with arguments
prompt_result = await server.get_prompt(
"generate_code_review_instructions", {"language": "Python", "focus": "security"}
)
instructions = prompt_result.messages[0].content.text
# Create agent
model = FakeModel()
agent = Agent(
name="streaming_prompt_agent", instructions=instructions, model=model, mcp_servers=[server]
)
model.add_multiple_turn_outputs([[get_text_message("Security analysis complete.")]])
if streaming:
streaming_result = Runner.run_streamed(agent, input="Review code")
async for _ in streaming_result.stream_events():
pass
final_result = streaming_result.final_output
else:
result = await Runner.run(agent, input="Review code")
final_result = result.final_output
assert "Security analysis complete" in final_result
assert agent.instructions == "You are a Python code reviewer focusing on security."
@pytest.mark.asyncio
async def test_multiple_prompts():
"""Test server with multiple prompts"""
server = FakeMCPPromptServer()
# Add multiple prompts
server.add_prompt(
"generate_code_review_instructions", "Generate agent instructions for code review tasks"
)
server.add_prompt(
"generate_testing_instructions", "Generate agent instructions for testing tasks"
)
server.set_prompt_result("generate_code_review_instructions", "You are a code reviewer.")
server.set_prompt_result("generate_testing_instructions", "You are a test engineer.")
# Test listing prompts
prompts_result = await server.list_prompts()
assert len(prompts_result.prompts) == 2
prompt_names = [p.name for p in prompts_result.prompts]
assert "generate_code_review_instructions" in prompt_names
assert "generate_testing_instructions" in prompt_names
# Test getting each prompt
review_result = await server.get_prompt("generate_code_review_instructions")
assert review_result.messages[0].content.text == "You are a code reviewer."
testing_result = await server.get_prompt("generate_testing_instructions")
assert testing_result.messages[0].content.text == "You are a test engineer."
@pytest.mark.asyncio
async def test_prompt_with_complex_arguments():
"""Test prompt with complex argument formatting"""
server = FakeMCPPromptServer()
server.add_prompt(
"generate_detailed_instructions", "Generate detailed instructions with multiple parameters"
)
server.set_prompt_result(
"generate_detailed_instructions",
"You are a {role} specialist. Your focus is on {focus}. "
+ "You work with {language} code. Your experience level is {level}.",
)
arguments = {
"role": "security",
"focus": "vulnerability detection",
"language": "Python",
"level": "senior",
}
result = await server.get_prompt("generate_detailed_instructions", arguments)
expected = (
"You are a security specialist. Your focus is on vulnerability detection. "
"You work with Python code. Your experience level is senior."
)
assert result.messages[0].content.text == expected
@pytest.mark.asyncio
async def test_prompt_with_missing_arguments():
"""Test prompt with missing arguments in format string"""
server = FakeMCPPromptServer()
server.add_prompt("incomplete_prompt", "Prompt with missing arguments")
server.set_prompt_result("incomplete_prompt", "You are a {role} working on {task}.")
# Only provide one of the required arguments
result = await server.get_prompt("incomplete_prompt", {"role": "developer"})
# Should return the original string since formatting fails
assert result.messages[0].content.text == "You are a {role} working on {task}."
@pytest.mark.asyncio
async def test_prompt_server_cleanup():
"""Test that prompt server cleanup works correctly"""
server = FakeMCPPromptServer()
server.add_prompt("test_prompt", "Test prompt")
server.set_prompt_result("test_prompt", "Test result")
# Test that server works before cleanup
result = await server.get_prompt("test_prompt")
assert result.messages[0].content.text == "Test result"
# Cleanup should not raise any errors
await server.cleanup()
# Server should still work after cleanup (in this fake implementation)
result = await server.get_prompt("test_prompt")
assert result.messages[0].content.text == "Test result"