refactor: use snake case instead of camel case in types (#1894)
This commit is contained in:
parent
7080fcf1e6
commit
e94b386a13
63 changed files with 1063 additions and 1057 deletions
|
|
@ -61,7 +61,7 @@ def run_unicode_server(port: int) -> None: # pragma: no cover
|
|||
Tool(
|
||||
name="echo_unicode",
|
||||
description="🔤 Echo Unicode text - Hello 👋 World 🌍 - Testing 🧪 Unicode ✨",
|
||||
inputSchema={
|
||||
input_schema={
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"text": {"type": "string", "description": "Text to echo back"},
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ async def test_list_roots_callback():
|
|||
async with create_session(server._mcp_server, list_roots_callback=list_roots_callback) as client_session:
|
||||
# Make a request to trigger sampling callback
|
||||
result = await client_session.call_tool("test_list_roots", {"message": "test message"})
|
||||
assert result.isError is False
|
||||
assert result.is_error is False
|
||||
assert isinstance(result.content[0], TextContent)
|
||||
assert result.content[0].text == "true"
|
||||
|
||||
|
|
@ -53,6 +53,6 @@ async def test_list_roots_callback():
|
|||
async with create_session(server._mcp_server) as client_session:
|
||||
# Make a request to trigger sampling callback
|
||||
result = await client_session.call_tool("test_list_roots", {"message": "test message"})
|
||||
assert result.isError is True
|
||||
assert result.is_error is True
|
||||
assert isinstance(result.content[0], TextContent)
|
||||
assert result.content[0].text == "Error executing tool test_list_roots: List roots not supported"
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ async def test_logging_callback():
|
|||
) as client_session:
|
||||
# First verify our test tool works
|
||||
result = await client_session.call_tool("test_tool", {})
|
||||
assert result.isError is False
|
||||
assert result.is_error is False
|
||||
assert isinstance(result.content[0], TextContent)
|
||||
assert result.content[0].text == "true"
|
||||
|
||||
|
|
@ -101,8 +101,8 @@ async def test_logging_callback():
|
|||
"extra_dict": {"a": 1, "b": 2, "c": 3},
|
||||
},
|
||||
)
|
||||
assert log_result.isError is False
|
||||
assert log_result_with_extra.isError is False
|
||||
assert log_result.is_error is False
|
||||
assert log_result_with_extra.is_error is False
|
||||
assert len(logging_collector.log_messages) == 2
|
||||
# Create meta object with related_request_id added dynamically
|
||||
log = logging_collector.log_messages[0]
|
||||
|
|
|
|||
|
|
@ -66,8 +66,8 @@ class TestClientOutputSchemaValidation:
|
|||
Tool(
|
||||
name="get_user",
|
||||
description="Get user data",
|
||||
inputSchema={"type": "object"},
|
||||
outputSchema=output_schema,
|
||||
input_schema={"type": "object"},
|
||||
output_schema=output_schema,
|
||||
)
|
||||
]
|
||||
|
||||
|
|
@ -105,8 +105,8 @@ class TestClientOutputSchemaValidation:
|
|||
Tool(
|
||||
name="calculate",
|
||||
description="Calculate something",
|
||||
inputSchema={"type": "object"},
|
||||
outputSchema=output_schema,
|
||||
input_schema={"type": "object"},
|
||||
output_schema=output_schema,
|
||||
)
|
||||
]
|
||||
|
||||
|
|
@ -136,8 +136,8 @@ class TestClientOutputSchemaValidation:
|
|||
Tool(
|
||||
name="get_scores",
|
||||
description="Get scores",
|
||||
inputSchema={"type": "object"},
|
||||
outputSchema=output_schema,
|
||||
input_schema={"type": "object"},
|
||||
output_schema=output_schema,
|
||||
)
|
||||
]
|
||||
|
||||
|
|
@ -171,8 +171,8 @@ class TestClientOutputSchemaValidation:
|
|||
Tool(
|
||||
name="get_person",
|
||||
description="Get person data",
|
||||
inputSchema={"type": "object"},
|
||||
outputSchema=output_schema,
|
||||
input_schema={"type": "object"},
|
||||
output_schema=output_schema,
|
||||
)
|
||||
]
|
||||
|
||||
|
|
@ -190,7 +190,7 @@ class TestClientOutputSchemaValidation:
|
|||
|
||||
@pytest.mark.anyio
|
||||
async def test_tool_not_listed_warning(self, caplog: pytest.LogCaptureFixture):
|
||||
"""Test that client logs warning when tool is not in list_tools but has outputSchema"""
|
||||
"""Test that client logs warning when tool is not in list_tools but has output_schema"""
|
||||
server = Server("test-server")
|
||||
|
||||
@server.list_tools()
|
||||
|
|
@ -210,8 +210,8 @@ class TestClientOutputSchemaValidation:
|
|||
async with client_session(server) as client:
|
||||
# Call a tool that wasn't listed
|
||||
result = await client.call_tool("mystery_tool", {})
|
||||
assert result.structuredContent == {"result": 42}
|
||||
assert result.isError is False
|
||||
assert result.structured_content == {"result": 42}
|
||||
assert result.is_error is False
|
||||
|
||||
# Check that warning was logged
|
||||
assert "Tool mystery_tool not listed" in caplog.text
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ async def test_sampling_callback():
|
|||
role="assistant",
|
||||
content=TextContent(type="text", text="This is a response from the sampling callback"),
|
||||
model="test-model",
|
||||
stopReason="endTurn",
|
||||
stop_reason="endTurn",
|
||||
)
|
||||
|
||||
async def sampling_callback(
|
||||
|
|
@ -47,7 +47,7 @@ async def test_sampling_callback():
|
|||
async with create_session(server._mcp_server, sampling_callback=sampling_callback) as client_session:
|
||||
# Make a request to trigger sampling callback
|
||||
result = await client_session.call_tool("test_sampling", {"message": "Test message for sampling"})
|
||||
assert result.isError is False
|
||||
assert result.is_error is False
|
||||
assert isinstance(result.content[0], TextContent)
|
||||
assert result.content[0].text == "true"
|
||||
|
||||
|
|
@ -55,7 +55,7 @@ async def test_sampling_callback():
|
|||
async with create_session(server._mcp_server) as client_session:
|
||||
# Make a request to trigger sampling callback
|
||||
result = await client_session.call_tool("test_sampling", {"message": "Test message for sampling"})
|
||||
assert result.isError is True
|
||||
assert result.is_error is True
|
||||
assert isinstance(result.content[0], TextContent)
|
||||
assert result.content[0].text == "Error executing tool test_sampling: Sampling not supported"
|
||||
|
||||
|
|
@ -72,7 +72,7 @@ async def test_create_message_backwards_compat_single_content():
|
|||
role="assistant",
|
||||
content=TextContent(type="text", text="Hello from LLM"),
|
||||
model="test-model",
|
||||
stopReason="endTurn",
|
||||
stop_reason="endTurn",
|
||||
)
|
||||
|
||||
async def sampling_callback(
|
||||
|
|
@ -99,7 +99,7 @@ async def test_create_message_backwards_compat_single_content():
|
|||
|
||||
async with create_session(server._mcp_server, sampling_callback=sampling_callback) as client_session:
|
||||
result = await client_session.call_tool("test_backwards_compat", {"message": "Test"})
|
||||
assert result.isError is False
|
||||
assert result.is_error is False
|
||||
assert isinstance(result.content[0], TextContent)
|
||||
assert result.content[0].text == "true"
|
||||
|
||||
|
|
@ -112,7 +112,7 @@ async def test_create_message_result_with_tools_type():
|
|||
role="assistant",
|
||||
content=ToolUseContent(type="tool_use", id="call_123", name="get_weather", input={"city": "SF"}),
|
||||
model="test-model",
|
||||
stopReason="toolUse",
|
||||
stop_reason="toolUse",
|
||||
)
|
||||
|
||||
# CreateMessageResultWithTools should have content_as_list
|
||||
|
|
@ -128,7 +128,7 @@ async def test_create_message_result_with_tools_type():
|
|||
ToolUseContent(type="tool_use", id="call_456", name="get_weather", input={"city": "NYC"}),
|
||||
],
|
||||
model="test-model",
|
||||
stopReason="toolUse",
|
||||
stop_reason="toolUse",
|
||||
)
|
||||
content_list_array = result_array.content_as_list
|
||||
assert len(content_list_array) == 2
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ async def test_client_session_initialize():
|
|||
|
||||
result = ServerResult(
|
||||
InitializeResult(
|
||||
protocolVersion=LATEST_PROTOCOL_VERSION,
|
||||
protocol_version=LATEST_PROTOCOL_VERSION,
|
||||
capabilities=ServerCapabilities(
|
||||
logging=None,
|
||||
resources=None,
|
||||
|
|
@ -57,7 +57,7 @@ async def test_client_session_initialize():
|
|||
experimental=None,
|
||||
prompts=None,
|
||||
),
|
||||
serverInfo=Implementation(name="mock-server", version="0.1.0"),
|
||||
server_info=Implementation(name="mock-server", version="0.1.0"),
|
||||
instructions="The server instructions.",
|
||||
)
|
||||
)
|
||||
|
|
@ -105,9 +105,9 @@ async def test_client_session_initialize():
|
|||
|
||||
# Assert the result
|
||||
assert isinstance(result, InitializeResult)
|
||||
assert result.protocolVersion == LATEST_PROTOCOL_VERSION
|
||||
assert result.protocol_version == LATEST_PROTOCOL_VERSION
|
||||
assert isinstance(result.capabilities, ServerCapabilities)
|
||||
assert result.serverInfo == Implementation(name="mock-server", version="0.1.0")
|
||||
assert result.server_info == Implementation(name="mock-server", version="0.1.0")
|
||||
assert result.instructions == "The server instructions."
|
||||
|
||||
# Check that the client sent the initialized notification
|
||||
|
|
@ -133,13 +133,13 @@ async def test_client_session_custom_client_info():
|
|||
jsonrpc_request.model_dump(by_alias=True, mode="json", exclude_none=True)
|
||||
)
|
||||
assert isinstance(request.root, InitializeRequest)
|
||||
received_client_info = request.root.params.clientInfo
|
||||
received_client_info = request.root.params.client_info
|
||||
|
||||
result = ServerResult(
|
||||
InitializeResult(
|
||||
protocolVersion=LATEST_PROTOCOL_VERSION,
|
||||
protocol_version=LATEST_PROTOCOL_VERSION,
|
||||
capabilities=ServerCapabilities(),
|
||||
serverInfo=Implementation(name="mock-server", version="0.1.0"),
|
||||
server_info=Implementation(name="mock-server", version="0.1.0"),
|
||||
)
|
||||
)
|
||||
|
||||
|
|
@ -194,13 +194,13 @@ async def test_client_session_default_client_info():
|
|||
jsonrpc_request.model_dump(by_alias=True, mode="json", exclude_none=True)
|
||||
)
|
||||
assert isinstance(request.root, InitializeRequest)
|
||||
received_client_info = request.root.params.clientInfo
|
||||
received_client_info = request.root.params.client_info
|
||||
|
||||
result = ServerResult(
|
||||
InitializeResult(
|
||||
protocolVersion=LATEST_PROTOCOL_VERSION,
|
||||
protocol_version=LATEST_PROTOCOL_VERSION,
|
||||
capabilities=ServerCapabilities(),
|
||||
serverInfo=Implementation(name="mock-server", version="0.1.0"),
|
||||
server_info=Implementation(name="mock-server", version="0.1.0"),
|
||||
)
|
||||
)
|
||||
|
||||
|
|
@ -254,14 +254,14 @@ async def test_client_session_version_negotiation_success():
|
|||
assert isinstance(request.root, InitializeRequest)
|
||||
|
||||
# Verify client sent the latest protocol version
|
||||
assert request.root.params.protocolVersion == LATEST_PROTOCOL_VERSION
|
||||
assert request.root.params.protocol_version == LATEST_PROTOCOL_VERSION
|
||||
|
||||
# Server responds with a supported older version
|
||||
result = ServerResult(
|
||||
InitializeResult(
|
||||
protocolVersion="2024-11-05",
|
||||
protocol_version="2024-11-05",
|
||||
capabilities=ServerCapabilities(),
|
||||
serverInfo=Implementation(name="mock-server", version="0.1.0"),
|
||||
server_info=Implementation(name="mock-server", version="0.1.0"),
|
||||
)
|
||||
)
|
||||
|
||||
|
|
@ -296,8 +296,8 @@ async def test_client_session_version_negotiation_success():
|
|||
|
||||
# Assert the result with negotiated version
|
||||
assert isinstance(result, InitializeResult)
|
||||
assert result.protocolVersion == "2024-11-05"
|
||||
assert result.protocolVersion in SUPPORTED_PROTOCOL_VERSIONS
|
||||
assert result.protocol_version == "2024-11-05"
|
||||
assert result.protocol_version in SUPPORTED_PROTOCOL_VERSIONS
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
|
|
@ -318,9 +318,9 @@ async def test_client_session_version_negotiation_failure():
|
|||
# Server responds with an unsupported version
|
||||
result = ServerResult(
|
||||
InitializeResult(
|
||||
protocolVersion="2020-01-01", # Unsupported old version
|
||||
protocol_version="2020-01-01", # Unsupported old version
|
||||
capabilities=ServerCapabilities(),
|
||||
serverInfo=Implementation(name="mock-server", version="0.1.0"),
|
||||
server_info=Implementation(name="mock-server", version="0.1.0"),
|
||||
)
|
||||
)
|
||||
|
||||
|
|
@ -377,9 +377,9 @@ async def test_client_capabilities_default():
|
|||
|
||||
result = ServerResult(
|
||||
InitializeResult(
|
||||
protocolVersion=LATEST_PROTOCOL_VERSION,
|
||||
protocol_version=LATEST_PROTOCOL_VERSION,
|
||||
capabilities=ServerCapabilities(),
|
||||
serverInfo=Implementation(name="mock-server", version="0.1.0"),
|
||||
server_info=Implementation(name="mock-server", version="0.1.0"),
|
||||
)
|
||||
)
|
||||
|
||||
|
|
@ -455,9 +455,9 @@ async def test_client_capabilities_with_custom_callbacks():
|
|||
|
||||
result = ServerResult(
|
||||
InitializeResult(
|
||||
protocolVersion=LATEST_PROTOCOL_VERSION,
|
||||
protocol_version=LATEST_PROTOCOL_VERSION,
|
||||
capabilities=ServerCapabilities(),
|
||||
serverInfo=Implementation(name="mock-server", version="0.1.0"),
|
||||
server_info=Implementation(name="mock-server", version="0.1.0"),
|
||||
)
|
||||
)
|
||||
|
||||
|
|
@ -503,7 +503,7 @@ async def test_client_capabilities_with_custom_callbacks():
|
|||
assert received_capabilities.roots is not None
|
||||
assert isinstance(received_capabilities.roots, types.RootsCapability)
|
||||
# Should be True for custom callback
|
||||
assert received_capabilities.roots.listChanged is True
|
||||
assert received_capabilities.roots.list_changed is True
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
|
|
@ -538,9 +538,9 @@ async def test_client_capabilities_with_sampling_tools():
|
|||
|
||||
result = ServerResult(
|
||||
InitializeResult(
|
||||
protocolVersion=LATEST_PROTOCOL_VERSION,
|
||||
protocol_version=LATEST_PROTOCOL_VERSION,
|
||||
capabilities=ServerCapabilities(),
|
||||
serverInfo=Implementation(name="mock-server", version="0.1.0"),
|
||||
server_info=Implementation(name="mock-server", version="0.1.0"),
|
||||
)
|
||||
)
|
||||
|
||||
|
|
@ -592,9 +592,9 @@ async def test_get_server_capabilities():
|
|||
|
||||
expected_capabilities = ServerCapabilities(
|
||||
logging=types.LoggingCapability(),
|
||||
prompts=types.PromptsCapability(listChanged=True),
|
||||
resources=types.ResourcesCapability(subscribe=True, listChanged=True),
|
||||
tools=types.ToolsCapability(listChanged=False),
|
||||
prompts=types.PromptsCapability(list_changed=True),
|
||||
resources=types.ResourcesCapability(subscribe=True, list_changed=True),
|
||||
tools=types.ToolsCapability(list_changed=False),
|
||||
)
|
||||
|
||||
async def mock_server():
|
||||
|
|
@ -608,9 +608,9 @@ async def test_get_server_capabilities():
|
|||
|
||||
result = ServerResult(
|
||||
InitializeResult(
|
||||
protocolVersion=LATEST_PROTOCOL_VERSION,
|
||||
protocol_version=LATEST_PROTOCOL_VERSION,
|
||||
capabilities=expected_capabilities,
|
||||
serverInfo=Implementation(name="mock-server", version="0.1.0"),
|
||||
server_info=Implementation(name="mock-server", version="0.1.0"),
|
||||
)
|
||||
)
|
||||
|
||||
|
|
@ -649,11 +649,11 @@ async def test_get_server_capabilities():
|
|||
assert capabilities == expected_capabilities
|
||||
assert capabilities.logging is not None
|
||||
assert capabilities.prompts is not None
|
||||
assert capabilities.prompts.listChanged is True
|
||||
assert capabilities.prompts.list_changed is True
|
||||
assert capabilities.resources is not None
|
||||
assert capabilities.resources.subscribe is True
|
||||
assert capabilities.tools is not None
|
||||
assert capabilities.tools.listChanged is False
|
||||
assert capabilities.tools.list_changed is False
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
|
|
@ -663,7 +663,7 @@ async def test_client_tool_call_with_meta(meta: dict[str, Any] | None):
|
|||
client_to_server_send, client_to_server_receive = anyio.create_memory_object_stream[SessionMessage](1)
|
||||
server_to_client_send, server_to_client_receive = anyio.create_memory_object_stream[SessionMessage](1)
|
||||
|
||||
mocked_tool = types.Tool(name="sample_tool", inputSchema={})
|
||||
mocked_tool = types.Tool(name="sample_tool", input_schema={})
|
||||
|
||||
async def mock_server():
|
||||
# Receive initialization request from client
|
||||
|
|
@ -677,9 +677,9 @@ async def test_client_tool_call_with_meta(meta: dict[str, Any] | None):
|
|||
|
||||
result = ServerResult(
|
||||
InitializeResult(
|
||||
protocolVersion=LATEST_PROTOCOL_VERSION,
|
||||
protocol_version=LATEST_PROTOCOL_VERSION,
|
||||
capabilities=ServerCapabilities(),
|
||||
serverInfo=Implementation(name="mock-server", version="0.1.0"),
|
||||
server_info=Implementation(name="mock-server", version="0.1.0"),
|
||||
)
|
||||
)
|
||||
|
||||
|
|
@ -712,7 +712,7 @@ async def test_client_tool_call_with_meta(meta: dict[str, Any] | None):
|
|||
assert jsonrpc_request.root.params["_meta"] == meta
|
||||
|
||||
result = ServerResult(
|
||||
CallToolResult(content=[TextContent(type="text", text="Called successfully")], isError=False)
|
||||
CallToolResult(content=[TextContent(type="text", text="Called successfully")], is_error=False)
|
||||
)
|
||||
|
||||
# Send the tools/call result
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ class TestClientSessionGroup:
|
|||
return f"{(server_info.name)}-{name}"
|
||||
|
||||
mcp_session_group = ClientSessionGroup(component_name_hook=hook)
|
||||
mcp_session_group._tools = {"server1-my_tool": types.Tool(name="my_tool", inputSchema={})}
|
||||
mcp_session_group._tools = {"server1-my_tool": types.Tool(name="my_tool", input_schema={})}
|
||||
mcp_session_group._tool_to_session = {"server1-my_tool": mock_session}
|
||||
text_content = types.TextContent(type="text", text="OK")
|
||||
mock_session.call_tool.return_value = types.CallToolResult(content=[text_content])
|
||||
|
|
@ -324,7 +324,7 @@ class TestClientSessionGroup:
|
|||
|
||||
# Mock session.initialize()
|
||||
mock_initialize_result = mock.AsyncMock(name="InitializeResult")
|
||||
mock_initialize_result.serverInfo = types.Implementation(name="foo", version="1")
|
||||
mock_initialize_result.server_info = types.Implementation(name="foo", version="1")
|
||||
mock_entered_session.initialize.return_value = mock_initialize_result
|
||||
|
||||
# --- Test Execution ---
|
||||
|
|
@ -381,5 +381,5 @@ class TestClientSessionGroup:
|
|||
mock_entered_session.initialize.assert_awaited_once()
|
||||
|
||||
# 3. Assert returned values
|
||||
assert returned_server_info is mock_initialize_result.serverInfo
|
||||
assert returned_server_info is mock_initialize_result.server_info
|
||||
assert returned_session is mock_entered_session
|
||||
|
|
|
|||
|
|
@ -45,9 +45,9 @@ async def test_client_capabilities_without_tasks():
|
|||
|
||||
result = ServerResult(
|
||||
InitializeResult(
|
||||
protocolVersion=LATEST_PROTOCOL_VERSION,
|
||||
protocol_version=LATEST_PROTOCOL_VERSION,
|
||||
capabilities=ServerCapabilities(),
|
||||
serverInfo=Implementation(name="mock-server", version="0.1.0"),
|
||||
server_info=Implementation(name="mock-server", version="0.1.0"),
|
||||
)
|
||||
)
|
||||
|
||||
|
|
@ -119,9 +119,9 @@ async def test_client_capabilities_with_tasks():
|
|||
|
||||
result = ServerResult(
|
||||
InitializeResult(
|
||||
protocolVersion=LATEST_PROTOCOL_VERSION,
|
||||
protocol_version=LATEST_PROTOCOL_VERSION,
|
||||
capabilities=ServerCapabilities(),
|
||||
serverInfo=Implementation(name="mock-server", version="0.1.0"),
|
||||
server_info=Implementation(name="mock-server", version="0.1.0"),
|
||||
)
|
||||
)
|
||||
|
||||
|
|
@ -203,9 +203,9 @@ async def test_client_capabilities_auto_built_from_handlers():
|
|||
|
||||
result = ServerResult(
|
||||
InitializeResult(
|
||||
protocolVersion=LATEST_PROTOCOL_VERSION,
|
||||
protocol_version=LATEST_PROTOCOL_VERSION,
|
||||
capabilities=ServerCapabilities(),
|
||||
serverInfo=Implementation(name="mock-server", version="0.1.0"),
|
||||
server_info=Implementation(name="mock-server", version="0.1.0"),
|
||||
)
|
||||
)
|
||||
|
||||
|
|
@ -283,9 +283,9 @@ async def test_client_capabilities_with_task_augmented_handlers():
|
|||
|
||||
result = ServerResult(
|
||||
InitializeResult(
|
||||
protocolVersion=LATEST_PROTOCOL_VERSION,
|
||||
protocol_version=LATEST_PROTOCOL_VERSION,
|
||||
capabilities=ServerCapabilities(),
|
||||
serverInfo=Implementation(name="mock-server", version="0.1.0"),
|
||||
server_info=Implementation(name="mock-server", version="0.1.0"),
|
||||
)
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -117,17 +117,17 @@ async def test_client_handles_get_task_request(client_streams: ClientTestStreams
|
|||
params: GetTaskRequestParams,
|
||||
) -> GetTaskResult | ErrorData:
|
||||
nonlocal received_task_id
|
||||
received_task_id = params.taskId
|
||||
task = await store.get_task(params.taskId)
|
||||
assert task is not None, f"Test setup error: task {params.taskId} should exist"
|
||||
received_task_id = params.task_id
|
||||
task = await store.get_task(params.task_id)
|
||||
assert task is not None, f"Test setup error: task {params.task_id} should exist"
|
||||
return GetTaskResult(
|
||||
taskId=task.taskId,
|
||||
task_id=task.task_id,
|
||||
status=task.status,
|
||||
statusMessage=task.statusMessage,
|
||||
createdAt=task.createdAt,
|
||||
lastUpdatedAt=task.lastUpdatedAt,
|
||||
status_message=task.status_message,
|
||||
created_at=task.created_at,
|
||||
last_updated_at=task.last_updated_at,
|
||||
ttl=task.ttl,
|
||||
pollInterval=task.pollInterval,
|
||||
poll_interval=task.poll_interval,
|
||||
)
|
||||
|
||||
await store.create_task(TaskMetadata(ttl=60000), task_id="test-task-123")
|
||||
|
|
@ -150,7 +150,7 @@ async def test_client_handles_get_task_request(client_streams: ClientTestStreams
|
|||
tg.start_soon(run_client)
|
||||
await client_ready.wait()
|
||||
|
||||
typed_request = GetTaskRequest(params=GetTaskRequestParams(taskId="test-task-123"))
|
||||
typed_request = GetTaskRequest(params=GetTaskRequestParams(task_id="test-task-123"))
|
||||
request = types.JSONRPCRequest(
|
||||
jsonrpc="2.0",
|
||||
id="req-1",
|
||||
|
|
@ -164,7 +164,7 @@ async def test_client_handles_get_task_request(client_streams: ClientTestStreams
|
|||
assert response.id == "req-1"
|
||||
|
||||
result = GetTaskResult.model_validate(response.result)
|
||||
assert result.taskId == "test-task-123"
|
||||
assert result.task_id == "test-task-123"
|
||||
assert result.status == "working"
|
||||
assert received_task_id == "test-task-123"
|
||||
|
||||
|
|
@ -183,8 +183,8 @@ async def test_client_handles_get_task_result_request(client_streams: ClientTest
|
|||
context: RequestContext[ClientSession, None],
|
||||
params: GetTaskPayloadRequestParams,
|
||||
) -> GetTaskPayloadResult | ErrorData:
|
||||
result = await store.get_result(params.taskId)
|
||||
assert result is not None, f"Test setup error: result for {params.taskId} should exist"
|
||||
result = await store.get_result(params.task_id)
|
||||
assert result is not None, f"Test setup error: result for {params.task_id} should exist"
|
||||
assert isinstance(result, types.CallToolResult)
|
||||
return GetTaskPayloadResult(**result.model_dump())
|
||||
|
||||
|
|
@ -213,7 +213,7 @@ async def test_client_handles_get_task_result_request(client_streams: ClientTest
|
|||
tg.start_soon(run_client)
|
||||
await client_ready.wait()
|
||||
|
||||
typed_request = GetTaskPayloadRequest(params=GetTaskPayloadRequestParams(taskId="test-task-456"))
|
||||
typed_request = GetTaskPayloadRequest(params=GetTaskPayloadRequestParams(task_id="test-task-456"))
|
||||
request = types.JSONRPCRequest(
|
||||
jsonrpc="2.0",
|
||||
id="req-2",
|
||||
|
|
@ -248,7 +248,7 @@ async def test_client_handles_list_tasks_request(client_streams: ClientTestStrea
|
|||
) -> ListTasksResult | ErrorData:
|
||||
cursor = params.cursor if params else None
|
||||
tasks_list, next_cursor = await store.list_tasks(cursor=cursor)
|
||||
return ListTasksResult(tasks=tasks_list, nextCursor=next_cursor)
|
||||
return ListTasksResult(tasks=tasks_list, next_cursor=next_cursor)
|
||||
|
||||
await store.create_task(TaskMetadata(ttl=60000), task_id="task-1")
|
||||
await store.create_task(TaskMetadata(ttl=60000), task_id="task-2")
|
||||
|
|
@ -301,16 +301,16 @@ async def test_client_handles_cancel_task_request(client_streams: ClientTestStre
|
|||
context: RequestContext[ClientSession, None],
|
||||
params: CancelTaskRequestParams,
|
||||
) -> CancelTaskResult | ErrorData:
|
||||
task = await store.get_task(params.taskId)
|
||||
assert task is not None, f"Test setup error: task {params.taskId} should exist"
|
||||
await store.update_task(params.taskId, status="cancelled")
|
||||
updated = await store.get_task(params.taskId)
|
||||
task = await store.get_task(params.task_id)
|
||||
assert task is not None, f"Test setup error: task {params.task_id} should exist"
|
||||
await store.update_task(params.task_id, status="cancelled")
|
||||
updated = await store.get_task(params.task_id)
|
||||
assert updated is not None
|
||||
return CancelTaskResult(
|
||||
taskId=updated.taskId,
|
||||
task_id=updated.task_id,
|
||||
status=updated.status,
|
||||
createdAt=updated.createdAt,
|
||||
lastUpdatedAt=updated.lastUpdatedAt,
|
||||
created_at=updated.created_at,
|
||||
last_updated_at=updated.last_updated_at,
|
||||
ttl=updated.ttl,
|
||||
)
|
||||
|
||||
|
|
@ -334,7 +334,7 @@ async def test_client_handles_cancel_task_request(client_streams: ClientTestStre
|
|||
tg.start_soon(run_client)
|
||||
await client_ready.wait()
|
||||
|
||||
typed_request = CancelTaskRequest(params=CancelTaskRequestParams(taskId="task-to-cancel"))
|
||||
typed_request = CancelTaskRequest(params=CancelTaskRequestParams(task_id="task-to-cancel"))
|
||||
request = types.JSONRPCRequest(
|
||||
jsonrpc="2.0",
|
||||
id="req-4",
|
||||
|
|
@ -347,7 +347,7 @@ async def test_client_handles_cancel_task_request(client_streams: ClientTestStre
|
|||
assert isinstance(response, types.JSONRPCResponse)
|
||||
|
||||
result = CancelTaskResult.model_validate(response.result)
|
||||
assert result.taskId == "task-to-cancel"
|
||||
assert result.task_id == "task-to-cancel"
|
||||
assert result.status == "cancelled"
|
||||
|
||||
tg.cancel_scope.cancel()
|
||||
|
|
@ -370,17 +370,17 @@ async def test_client_task_augmented_sampling(client_streams: ClientTestStreams)
|
|||
task_metadata: TaskMetadata,
|
||||
) -> CreateTaskResult:
|
||||
task = await store.create_task(task_metadata)
|
||||
created_task_id[0] = task.taskId
|
||||
created_task_id[0] = task.task_id
|
||||
|
||||
async def do_sampling() -> None:
|
||||
result = CreateMessageResult(
|
||||
role="assistant",
|
||||
content=TextContent(type="text", text="Sampled response"),
|
||||
model="test-model",
|
||||
stopReason="endTurn",
|
||||
stop_reason="endTurn",
|
||||
)
|
||||
await store.store_result(task.taskId, result)
|
||||
await store.update_task(task.taskId, status="completed")
|
||||
await store.store_result(task.task_id, result)
|
||||
await store.update_task(task.task_id, status="completed")
|
||||
sampling_completed.set()
|
||||
|
||||
assert background_tg[0] is not None
|
||||
|
|
@ -391,24 +391,24 @@ async def test_client_task_augmented_sampling(client_streams: ClientTestStreams)
|
|||
context: RequestContext[ClientSession, None],
|
||||
params: GetTaskRequestParams,
|
||||
) -> GetTaskResult | ErrorData:
|
||||
task = await store.get_task(params.taskId)
|
||||
assert task is not None, f"Test setup error: task {params.taskId} should exist"
|
||||
task = await store.get_task(params.task_id)
|
||||
assert task is not None, f"Test setup error: task {params.task_id} should exist"
|
||||
return GetTaskResult(
|
||||
taskId=task.taskId,
|
||||
task_id=task.task_id,
|
||||
status=task.status,
|
||||
statusMessage=task.statusMessage,
|
||||
createdAt=task.createdAt,
|
||||
lastUpdatedAt=task.lastUpdatedAt,
|
||||
status_message=task.status_message,
|
||||
created_at=task.created_at,
|
||||
last_updated_at=task.last_updated_at,
|
||||
ttl=task.ttl,
|
||||
pollInterval=task.pollInterval,
|
||||
poll_interval=task.poll_interval,
|
||||
)
|
||||
|
||||
async def get_task_result_handler(
|
||||
context: RequestContext[ClientSession, None],
|
||||
params: GetTaskPayloadRequestParams,
|
||||
) -> GetTaskPayloadResult | ErrorData:
|
||||
result = await store.get_result(params.taskId)
|
||||
assert result is not None, f"Test setup error: result for {params.taskId} should exist"
|
||||
result = await store.get_result(params.task_id)
|
||||
assert result is not None, f"Test setup error: result for {params.task_id} should exist"
|
||||
assert isinstance(result, CreateMessageResult)
|
||||
return GetTaskPayloadResult(**result.model_dump())
|
||||
|
||||
|
|
@ -439,7 +439,7 @@ async def test_client_task_augmented_sampling(client_streams: ClientTestStreams)
|
|||
typed_request = CreateMessageRequest(
|
||||
params=CreateMessageRequestParams(
|
||||
messages=[SamplingMessage(role="user", content=TextContent(type="text", text="Hello"))],
|
||||
maxTokens=100,
|
||||
max_tokens=100,
|
||||
task=TaskMetadata(ttl=60000),
|
||||
)
|
||||
)
|
||||
|
|
@ -456,14 +456,14 @@ async def test_client_task_augmented_sampling(client_streams: ClientTestStreams)
|
|||
assert isinstance(response, types.JSONRPCResponse)
|
||||
|
||||
task_result = CreateTaskResult.model_validate(response.result)
|
||||
task_id = task_result.task.taskId
|
||||
task_id = task_result.task.task_id
|
||||
assert task_id == created_task_id[0]
|
||||
|
||||
# Step 3: Wait for background sampling
|
||||
await sampling_completed.wait()
|
||||
|
||||
# Step 4: Server polls task status
|
||||
typed_poll = GetTaskRequest(params=GetTaskRequestParams(taskId=task_id))
|
||||
typed_poll = GetTaskRequest(params=GetTaskRequestParams(task_id=task_id))
|
||||
poll_request = types.JSONRPCRequest(
|
||||
jsonrpc="2.0",
|
||||
id="req-poll",
|
||||
|
|
@ -479,7 +479,7 @@ async def test_client_task_augmented_sampling(client_streams: ClientTestStreams)
|
|||
assert status.status == "completed"
|
||||
|
||||
# Step 5: Server gets result
|
||||
typed_result_req = GetTaskPayloadRequest(params=GetTaskPayloadRequestParams(taskId=task_id))
|
||||
typed_result_req = GetTaskPayloadRequest(params=GetTaskPayloadRequestParams(task_id=task_id))
|
||||
result_request = types.JSONRPCRequest(
|
||||
jsonrpc="2.0",
|
||||
id="req-result",
|
||||
|
|
@ -514,13 +514,13 @@ async def test_client_task_augmented_elicitation(client_streams: ClientTestStrea
|
|||
task_metadata: TaskMetadata,
|
||||
) -> CreateTaskResult | ErrorData:
|
||||
task = await store.create_task(task_metadata)
|
||||
created_task_id[0] = task.taskId
|
||||
created_task_id[0] = task.task_id
|
||||
|
||||
async def do_elicitation() -> None:
|
||||
# Simulate user providing elicitation response
|
||||
result = ElicitResult(action="accept", content={"name": "Test User"})
|
||||
await store.store_result(task.taskId, result)
|
||||
await store.update_task(task.taskId, status="completed")
|
||||
await store.store_result(task.task_id, result)
|
||||
await store.update_task(task.task_id, status="completed")
|
||||
elicitation_completed.set()
|
||||
|
||||
assert background_tg[0] is not None
|
||||
|
|
@ -531,24 +531,24 @@ async def test_client_task_augmented_elicitation(client_streams: ClientTestStrea
|
|||
context: RequestContext[ClientSession, None],
|
||||
params: GetTaskRequestParams,
|
||||
) -> GetTaskResult | ErrorData:
|
||||
task = await store.get_task(params.taskId)
|
||||
assert task is not None, f"Test setup error: task {params.taskId} should exist"
|
||||
task = await store.get_task(params.task_id)
|
||||
assert task is not None, f"Test setup error: task {params.task_id} should exist"
|
||||
return GetTaskResult(
|
||||
taskId=task.taskId,
|
||||
task_id=task.task_id,
|
||||
status=task.status,
|
||||
statusMessage=task.statusMessage,
|
||||
createdAt=task.createdAt,
|
||||
lastUpdatedAt=task.lastUpdatedAt,
|
||||
status_message=task.status_message,
|
||||
created_at=task.created_at,
|
||||
last_updated_at=task.last_updated_at,
|
||||
ttl=task.ttl,
|
||||
pollInterval=task.pollInterval,
|
||||
poll_interval=task.poll_interval,
|
||||
)
|
||||
|
||||
async def get_task_result_handler(
|
||||
context: RequestContext[ClientSession, None],
|
||||
params: GetTaskPayloadRequestParams,
|
||||
) -> GetTaskPayloadResult | ErrorData:
|
||||
result = await store.get_result(params.taskId)
|
||||
assert result is not None, f"Test setup error: result for {params.taskId} should exist"
|
||||
result = await store.get_result(params.task_id)
|
||||
assert result is not None, f"Test setup error: result for {params.task_id} should exist"
|
||||
assert isinstance(result, ElicitResult)
|
||||
return GetTaskPayloadResult(**result.model_dump())
|
||||
|
||||
|
|
@ -579,7 +579,7 @@ async def test_client_task_augmented_elicitation(client_streams: ClientTestStrea
|
|||
typed_request = ElicitRequest(
|
||||
params=ElicitRequestFormParams(
|
||||
message="What is your name?",
|
||||
requestedSchema={"type": "object", "properties": {"name": {"type": "string"}}},
|
||||
requested_schema={"type": "object", "properties": {"name": {"type": "string"}}},
|
||||
task=TaskMetadata(ttl=60000),
|
||||
)
|
||||
)
|
||||
|
|
@ -596,14 +596,14 @@ async def test_client_task_augmented_elicitation(client_streams: ClientTestStrea
|
|||
assert isinstance(response, types.JSONRPCResponse)
|
||||
|
||||
task_result = CreateTaskResult.model_validate(response.result)
|
||||
task_id = task_result.task.taskId
|
||||
task_id = task_result.task.task_id
|
||||
assert task_id == created_task_id[0]
|
||||
|
||||
# Step 3: Wait for background elicitation
|
||||
await elicitation_completed.wait()
|
||||
|
||||
# Step 4: Server polls task status
|
||||
typed_poll = GetTaskRequest(params=GetTaskRequestParams(taskId=task_id))
|
||||
typed_poll = GetTaskRequest(params=GetTaskRequestParams(task_id=task_id))
|
||||
poll_request = types.JSONRPCRequest(
|
||||
jsonrpc="2.0",
|
||||
id="req-poll",
|
||||
|
|
@ -619,7 +619,7 @@ async def test_client_task_augmented_elicitation(client_streams: ClientTestStrea
|
|||
assert status.status == "completed"
|
||||
|
||||
# Step 5: Server gets result
|
||||
typed_result_req = GetTaskPayloadRequest(params=GetTaskPayloadRequestParams(taskId=task_id))
|
||||
typed_result_req = GetTaskPayloadRequest(params=GetTaskPayloadRequestParams(task_id=task_id))
|
||||
result_request = types.JSONRPCRequest(
|
||||
jsonrpc="2.0",
|
||||
id="req-result",
|
||||
|
|
@ -661,7 +661,7 @@ async def test_client_returns_error_for_unhandled_task_request(client_streams: C
|
|||
tg.start_soon(run_client)
|
||||
await client_ready.wait()
|
||||
|
||||
typed_request = GetTaskRequest(params=GetTaskRequestParams(taskId="nonexistent"))
|
||||
typed_request = GetTaskRequest(params=GetTaskRequestParams(task_id="nonexistent"))
|
||||
request = types.JSONRPCRequest(
|
||||
jsonrpc="2.0",
|
||||
id="req-unhandled",
|
||||
|
|
@ -700,7 +700,7 @@ async def test_client_returns_error_for_unhandled_task_result_request(client_str
|
|||
tg.start_soon(run_client)
|
||||
await client_ready.wait()
|
||||
|
||||
typed_request = GetTaskPayloadRequest(params=GetTaskPayloadRequestParams(taskId="nonexistent"))
|
||||
typed_request = GetTaskPayloadRequest(params=GetTaskPayloadRequestParams(task_id="nonexistent"))
|
||||
request = types.JSONRPCRequest(
|
||||
jsonrpc="2.0",
|
||||
id="req-result",
|
||||
|
|
@ -772,7 +772,7 @@ async def test_client_returns_error_for_unhandled_cancel_task_request(client_str
|
|||
tg.start_soon(run_client)
|
||||
await client_ready.wait()
|
||||
|
||||
typed_request = CancelTaskRequest(params=CancelTaskRequestParams(taskId="nonexistent"))
|
||||
typed_request = CancelTaskRequest(params=CancelTaskRequestParams(task_id="nonexistent"))
|
||||
request = types.JSONRPCRequest(
|
||||
jsonrpc="2.0",
|
||||
id="req-cancel",
|
||||
|
|
@ -813,7 +813,7 @@ async def test_client_returns_error_for_unhandled_task_augmented_sampling(client
|
|||
typed_request = CreateMessageRequest(
|
||||
params=CreateMessageRequestParams(
|
||||
messages=[SamplingMessage(role="user", content=TextContent(type="text", text="Hello"))],
|
||||
maxTokens=100,
|
||||
max_tokens=100,
|
||||
task=TaskMetadata(ttl=60000),
|
||||
)
|
||||
)
|
||||
|
|
@ -859,7 +859,7 @@ async def test_client_returns_error_for_unhandled_task_augmented_elicitation(
|
|||
typed_request = ElicitRequest(
|
||||
params=ElicitRequestFormParams(
|
||||
message="What is your name?",
|
||||
requestedSchema={"type": "object", "properties": {"name": {"type": "string"}}},
|
||||
requested_schema={"type": "object", "properties": {"name": {"type": "string"}}},
|
||||
task=TaskMetadata(ttl=60000),
|
||||
)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -20,13 +20,13 @@ def make_task_result(
|
|||
"""Create GetTaskResult with sensible defaults."""
|
||||
now = datetime.now(timezone.utc)
|
||||
return GetTaskResult(
|
||||
taskId=task_id,
|
||||
task_id=task_id,
|
||||
status=status,
|
||||
statusMessage=status_message,
|
||||
createdAt=now,
|
||||
lastUpdatedAt=now,
|
||||
status_message=status_message,
|
||||
created_at=now,
|
||||
last_updated_at=now,
|
||||
ttl=60000,
|
||||
pollInterval=poll_interval,
|
||||
poll_interval=poll_interval,
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -117,5 +117,5 @@ async def test_poll_task_yields_full_result(features: ExperimentalClientFeatures
|
|||
|
||||
assert len(results) == 1
|
||||
assert results[0].status == "completed"
|
||||
assert results[0].statusMessage == "All done!"
|
||||
assert results[0].taskId == "test-task"
|
||||
assert results[0].status_message == "All done!"
|
||||
assert results[0].task_id == "test-task"
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ async def test_session_experimental_get_task() -> None:
|
|||
|
||||
@server.list_tools()
|
||||
async def list_tools():
|
||||
return [Tool(name="test_tool", description="Test", inputSchema={"type": "object"})]
|
||||
return [Tool(name="test_tool", description="Test", input_schema={"type": "object"})]
|
||||
|
||||
@server.call_tool()
|
||||
async def handle_call_tool(name: str, arguments: dict[str, Any]) -> list[TextContent] | CreateTaskResult:
|
||||
|
|
@ -70,10 +70,10 @@ async def test_session_experimental_get_task() -> None:
|
|||
task = await app.store.create_task(task_metadata)
|
||||
|
||||
done_event = Event()
|
||||
app.task_done_events[task.taskId] = done_event
|
||||
app.task_done_events[task.task_id] = done_event
|
||||
|
||||
async def do_work():
|
||||
async with task_execution(task.taskId, app.store) as task_ctx:
|
||||
async with task_execution(task.task_id, app.store) as task_ctx:
|
||||
await task_ctx.complete(CallToolResult(content=[TextContent(type="text", text="Done")]))
|
||||
done_event.set()
|
||||
|
||||
|
|
@ -85,16 +85,16 @@ async def test_session_experimental_get_task() -> None:
|
|||
@server.experimental.get_task()
|
||||
async def handle_get_task(request: GetTaskRequest) -> GetTaskResult:
|
||||
app = server.request_context.lifespan_context
|
||||
task = await app.store.get_task(request.params.taskId)
|
||||
assert task is not None, f"Test setup error: task {request.params.taskId} should exist"
|
||||
task = await app.store.get_task(request.params.task_id)
|
||||
assert task is not None, f"Test setup error: task {request.params.task_id} should exist"
|
||||
return GetTaskResult(
|
||||
taskId=task.taskId,
|
||||
task_id=task.task_id,
|
||||
status=task.status,
|
||||
statusMessage=task.statusMessage,
|
||||
createdAt=task.createdAt,
|
||||
lastUpdatedAt=task.lastUpdatedAt,
|
||||
status_message=task.status_message,
|
||||
created_at=task.created_at,
|
||||
last_updated_at=task.last_updated_at,
|
||||
ttl=task.ttl,
|
||||
pollInterval=task.pollInterval,
|
||||
poll_interval=task.poll_interval,
|
||||
)
|
||||
|
||||
# Set up streams
|
||||
|
|
@ -145,7 +145,7 @@ async def test_session_experimental_get_task() -> None:
|
|||
),
|
||||
CreateTaskResult,
|
||||
)
|
||||
task_id = create_result.task.taskId
|
||||
task_id = create_result.task.task_id
|
||||
|
||||
# Wait for task to complete
|
||||
await app_context.task_done_events[task_id].wait()
|
||||
|
|
@ -153,7 +153,7 @@ async def test_session_experimental_get_task() -> None:
|
|||
# Use session.experimental to get task status
|
||||
task_status = await client_session.experimental.get_task(task_id)
|
||||
|
||||
assert task_status.taskId == task_id
|
||||
assert task_status.task_id == task_id
|
||||
assert task_status.status == "completed"
|
||||
|
||||
tg.cancel_scope.cancel()
|
||||
|
|
@ -167,7 +167,7 @@ async def test_session_experimental_get_task_result() -> None:
|
|||
|
||||
@server.list_tools()
|
||||
async def list_tools():
|
||||
return [Tool(name="test_tool", description="Test", inputSchema={"type": "object"})]
|
||||
return [Tool(name="test_tool", description="Test", input_schema={"type": "object"})]
|
||||
|
||||
@server.call_tool()
|
||||
async def handle_call_tool(name: str, arguments: dict[str, Any]) -> list[TextContent] | CreateTaskResult:
|
||||
|
|
@ -179,10 +179,10 @@ async def test_session_experimental_get_task_result() -> None:
|
|||
task = await app.store.create_task(task_metadata)
|
||||
|
||||
done_event = Event()
|
||||
app.task_done_events[task.taskId] = done_event
|
||||
app.task_done_events[task.task_id] = done_event
|
||||
|
||||
async def do_work():
|
||||
async with task_execution(task.taskId, app.store) as task_ctx:
|
||||
async with task_execution(task.task_id, app.store) as task_ctx:
|
||||
await task_ctx.complete(
|
||||
CallToolResult(content=[TextContent(type="text", text="Task result content")])
|
||||
)
|
||||
|
|
@ -198,8 +198,8 @@ async def test_session_experimental_get_task_result() -> None:
|
|||
request: GetTaskPayloadRequest,
|
||||
) -> GetTaskPayloadResult:
|
||||
app = server.request_context.lifespan_context
|
||||
result = await app.store.get_result(request.params.taskId)
|
||||
assert result is not None, f"Test setup error: result for {request.params.taskId} should exist"
|
||||
result = await app.store.get_result(request.params.task_id)
|
||||
assert result is not None, f"Test setup error: result for {request.params.task_id} should exist"
|
||||
assert isinstance(result, CallToolResult)
|
||||
return GetTaskPayloadResult(**result.model_dump())
|
||||
|
||||
|
|
@ -251,7 +251,7 @@ async def test_session_experimental_get_task_result() -> None:
|
|||
),
|
||||
CreateTaskResult,
|
||||
)
|
||||
task_id = create_result.task.taskId
|
||||
task_id = create_result.task.task_id
|
||||
|
||||
# Wait for task to complete
|
||||
await app_context.task_done_events[task_id].wait()
|
||||
|
|
@ -275,7 +275,7 @@ async def test_session_experimental_list_tasks() -> None:
|
|||
|
||||
@server.list_tools()
|
||||
async def list_tools():
|
||||
return [Tool(name="test_tool", description="Test", inputSchema={"type": "object"})]
|
||||
return [Tool(name="test_tool", description="Test", input_schema={"type": "object"})]
|
||||
|
||||
@server.call_tool()
|
||||
async def handle_call_tool(name: str, arguments: dict[str, Any]) -> list[TextContent] | CreateTaskResult:
|
||||
|
|
@ -287,10 +287,10 @@ async def test_session_experimental_list_tasks() -> None:
|
|||
task = await app.store.create_task(task_metadata)
|
||||
|
||||
done_event = Event()
|
||||
app.task_done_events[task.taskId] = done_event
|
||||
app.task_done_events[task.task_id] = done_event
|
||||
|
||||
async def do_work():
|
||||
async with task_execution(task.taskId, app.store) as task_ctx:
|
||||
async with task_execution(task.task_id, app.store) as task_ctx:
|
||||
await task_ctx.complete(CallToolResult(content=[TextContent(type="text", text="Done")]))
|
||||
done_event.set()
|
||||
|
||||
|
|
@ -303,7 +303,7 @@ async def test_session_experimental_list_tasks() -> None:
|
|||
async def handle_list_tasks(request: ListTasksRequest) -> ListTasksResult:
|
||||
app = server.request_context.lifespan_context
|
||||
tasks_list, next_cursor = await app.store.list_tasks(cursor=request.params.cursor if request.params else None)
|
||||
return ListTasksResult(tasks=tasks_list, nextCursor=next_cursor)
|
||||
return ListTasksResult(tasks=tasks_list, next_cursor=next_cursor)
|
||||
|
||||
# Set up streams
|
||||
server_to_client_send, server_to_client_receive = anyio.create_memory_object_stream[SessionMessage](10)
|
||||
|
|
@ -354,7 +354,7 @@ async def test_session_experimental_list_tasks() -> None:
|
|||
),
|
||||
CreateTaskResult,
|
||||
)
|
||||
await app_context.task_done_events[create_result.task.taskId].wait()
|
||||
await app_context.task_done_events[create_result.task.task_id].wait()
|
||||
|
||||
# Use TaskClient to list tasks
|
||||
list_result = await client_session.experimental.list_tasks()
|
||||
|
|
@ -372,7 +372,7 @@ async def test_session_experimental_cancel_task() -> None:
|
|||
|
||||
@server.list_tools()
|
||||
async def list_tools():
|
||||
return [Tool(name="test_tool", description="Test", inputSchema={"type": "object"})]
|
||||
return [Tool(name="test_tool", description="Test", input_schema={"type": "object"})]
|
||||
|
||||
@server.call_tool()
|
||||
async def handle_call_tool(name: str, arguments: dict[str, Any]) -> list[TextContent] | CreateTaskResult:
|
||||
|
|
@ -390,32 +390,32 @@ async def test_session_experimental_cancel_task() -> None:
|
|||
@server.experimental.get_task()
|
||||
async def handle_get_task(request: GetTaskRequest) -> GetTaskResult:
|
||||
app = server.request_context.lifespan_context
|
||||
task = await app.store.get_task(request.params.taskId)
|
||||
assert task is not None, f"Test setup error: task {request.params.taskId} should exist"
|
||||
task = await app.store.get_task(request.params.task_id)
|
||||
assert task is not None, f"Test setup error: task {request.params.task_id} should exist"
|
||||
return GetTaskResult(
|
||||
taskId=task.taskId,
|
||||
task_id=task.task_id,
|
||||
status=task.status,
|
||||
statusMessage=task.statusMessage,
|
||||
createdAt=task.createdAt,
|
||||
lastUpdatedAt=task.lastUpdatedAt,
|
||||
status_message=task.status_message,
|
||||
created_at=task.created_at,
|
||||
last_updated_at=task.last_updated_at,
|
||||
ttl=task.ttl,
|
||||
pollInterval=task.pollInterval,
|
||||
poll_interval=task.poll_interval,
|
||||
)
|
||||
|
||||
@server.experimental.cancel_task()
|
||||
async def handle_cancel_task(request: CancelTaskRequest) -> CancelTaskResult:
|
||||
app = server.request_context.lifespan_context
|
||||
task = await app.store.get_task(request.params.taskId)
|
||||
assert task is not None, f"Test setup error: task {request.params.taskId} should exist"
|
||||
await app.store.update_task(request.params.taskId, status="cancelled")
|
||||
task = await app.store.get_task(request.params.task_id)
|
||||
assert task is not None, f"Test setup error: task {request.params.task_id} should exist"
|
||||
await app.store.update_task(request.params.task_id, status="cancelled")
|
||||
# CancelTaskResult extends Task, so we need to return the updated task info
|
||||
updated_task = await app.store.get_task(request.params.taskId)
|
||||
updated_task = await app.store.get_task(request.params.task_id)
|
||||
assert updated_task is not None
|
||||
return CancelTaskResult(
|
||||
taskId=updated_task.taskId,
|
||||
task_id=updated_task.task_id,
|
||||
status=updated_task.status,
|
||||
createdAt=updated_task.createdAt,
|
||||
lastUpdatedAt=updated_task.lastUpdatedAt,
|
||||
created_at=updated_task.created_at,
|
||||
last_updated_at=updated_task.last_updated_at,
|
||||
ttl=updated_task.ttl,
|
||||
)
|
||||
|
||||
|
|
@ -467,7 +467,7 @@ async def test_session_experimental_cancel_task() -> None:
|
|||
),
|
||||
CreateTaskResult,
|
||||
)
|
||||
task_id = create_result.task.taskId
|
||||
task_id = create_result.task.task_id
|
||||
|
||||
# Verify task is working
|
||||
status_before = await client_session.experimental.get_task(task_id)
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@ async def test_task_context_properties() -> None:
|
|||
task = await store.create_task(metadata=TaskMetadata(ttl=60000))
|
||||
ctx = TaskContext(task, store)
|
||||
|
||||
assert ctx.task_id == task.taskId
|
||||
assert ctx.task.taskId == task.taskId
|
||||
assert ctx.task_id == task.task_id
|
||||
assert ctx.task.task_id == task.task_id
|
||||
assert ctx.task.status == "working"
|
||||
assert ctx.is_cancelled is False
|
||||
|
||||
|
|
@ -33,9 +33,9 @@ async def test_task_context_update_status() -> None:
|
|||
await ctx.update_status("Processing step 1...")
|
||||
|
||||
# Check status message was updated
|
||||
updated = await store.get_task(task.taskId)
|
||||
updated = await store.get_task(task.task_id)
|
||||
assert updated is not None
|
||||
assert updated.statusMessage == "Processing step 1..."
|
||||
assert updated.status_message == "Processing step 1..."
|
||||
|
||||
store.cleanup()
|
||||
|
||||
|
|
@ -51,12 +51,12 @@ async def test_task_context_complete() -> None:
|
|||
await ctx.complete(result)
|
||||
|
||||
# Check task status
|
||||
updated = await store.get_task(task.taskId)
|
||||
updated = await store.get_task(task.task_id)
|
||||
assert updated is not None
|
||||
assert updated.status == "completed"
|
||||
|
||||
# Check result is stored
|
||||
stored_result = await store.get_result(task.taskId)
|
||||
stored_result = await store.get_result(task.task_id)
|
||||
assert stored_result is not None
|
||||
|
||||
store.cleanup()
|
||||
|
|
@ -72,10 +72,10 @@ async def test_task_context_fail() -> None:
|
|||
await ctx.fail("Something went wrong!")
|
||||
|
||||
# Check task status
|
||||
updated = await store.get_task(task.taskId)
|
||||
updated = await store.get_task(task.task_id)
|
||||
assert updated is not None
|
||||
assert updated.status == "failed"
|
||||
assert updated.statusMessage == "Something went wrong!"
|
||||
assert updated.status_message == "Something went wrong!"
|
||||
|
||||
store.cleanup()
|
||||
|
||||
|
|
@ -101,13 +101,13 @@ def test_create_task_state_generates_id() -> None:
|
|||
task1 = create_task_state(TaskMetadata(ttl=60000))
|
||||
task2 = create_task_state(TaskMetadata(ttl=60000))
|
||||
|
||||
assert task1.taskId != task2.taskId
|
||||
assert task1.task_id != task2.task_id
|
||||
|
||||
|
||||
def test_create_task_state_uses_provided_id() -> None:
|
||||
"""create_task_state uses the provided task ID."""
|
||||
task = create_task_state(TaskMetadata(ttl=60000), task_id="my-task-123")
|
||||
assert task.taskId == "my-task-123"
|
||||
assert task.task_id == "my-task-123"
|
||||
|
||||
|
||||
def test_create_task_state_null_ttl() -> None:
|
||||
|
|
@ -119,7 +119,7 @@ def test_create_task_state_null_ttl() -> None:
|
|||
def test_create_task_state_has_created_at() -> None:
|
||||
"""create_task_state sets createdAt timestamp."""
|
||||
task = create_task_state(TaskMetadata(ttl=60000))
|
||||
assert task.createdAt is not None
|
||||
assert task.created_at is not None
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
|
|
@ -148,7 +148,7 @@ async def test_task_execution_auto_fails_on_exception() -> None:
|
|||
failed_task = await store.get_task("exec-fail-1")
|
||||
assert failed_task is not None
|
||||
assert failed_task.status == "failed"
|
||||
assert "Oops!" in (failed_task.statusMessage or "")
|
||||
assert "Oops!" in (failed_task.status_message or "")
|
||||
|
||||
store.cleanup()
|
||||
|
||||
|
|
|
|||
|
|
@ -81,11 +81,11 @@ async def test_task_lifecycle_with_task_execution() -> None:
|
|||
Tool(
|
||||
name="process_data",
|
||||
description="Process data asynchronously",
|
||||
inputSchema={
|
||||
input_schema={
|
||||
"type": "object",
|
||||
"properties": {"input": {"type": "string"}},
|
||||
},
|
||||
execution=ToolExecution(taskSupport=TASK_REQUIRED),
|
||||
execution=ToolExecution(task_support=TASK_REQUIRED),
|
||||
)
|
||||
]
|
||||
|
||||
|
|
@ -101,11 +101,11 @@ async def test_task_lifecycle_with_task_execution() -> None:
|
|||
|
||||
# 2. Create event to signal completion (for testing)
|
||||
done_event = Event()
|
||||
app.task_done_events[task.taskId] = done_event
|
||||
app.task_done_events[task.task_id] = done_event
|
||||
|
||||
# 3. Define work function using task_execution for safety
|
||||
async def do_work():
|
||||
async with task_execution(task.taskId, app.store) as task_ctx:
|
||||
async with task_execution(task.task_id, app.store) as task_ctx:
|
||||
await task_ctx.update_status("Processing input...")
|
||||
# Simulate work
|
||||
input_value = arguments.get("input", "")
|
||||
|
|
@ -126,16 +126,16 @@ async def test_task_lifecycle_with_task_execution() -> None:
|
|||
@server.experimental.get_task()
|
||||
async def handle_get_task(request: GetTaskRequest) -> GetTaskResult:
|
||||
app = server.request_context.lifespan_context
|
||||
task = await app.store.get_task(request.params.taskId)
|
||||
assert task is not None, f"Test setup error: task {request.params.taskId} should exist"
|
||||
task = await app.store.get_task(request.params.task_id)
|
||||
assert task is not None, f"Test setup error: task {request.params.task_id} should exist"
|
||||
return GetTaskResult(
|
||||
taskId=task.taskId,
|
||||
task_id=task.task_id,
|
||||
status=task.status,
|
||||
statusMessage=task.statusMessage,
|
||||
createdAt=task.createdAt,
|
||||
lastUpdatedAt=task.lastUpdatedAt,
|
||||
status_message=task.status_message,
|
||||
created_at=task.created_at,
|
||||
last_updated_at=task.last_updated_at,
|
||||
ttl=task.ttl,
|
||||
pollInterval=task.pollInterval,
|
||||
poll_interval=task.poll_interval,
|
||||
)
|
||||
|
||||
@server.experimental.get_task_result()
|
||||
|
|
@ -143,8 +143,8 @@ async def test_task_lifecycle_with_task_execution() -> None:
|
|||
request: GetTaskPayloadRequest,
|
||||
) -> GetTaskPayloadResult:
|
||||
app = server.request_context.lifespan_context
|
||||
result = await app.store.get_result(request.params.taskId)
|
||||
assert result is not None, f"Test setup error: result for {request.params.taskId} should exist"
|
||||
result = await app.store.get_result(request.params.task_id)
|
||||
assert result is not None, f"Test setup error: result for {request.params.task_id} should exist"
|
||||
assert isinstance(result, CallToolResult)
|
||||
# Return as GetTaskPayloadResult (which accepts extra fields)
|
||||
return GetTaskPayloadResult(**result.model_dump())
|
||||
|
|
@ -205,22 +205,22 @@ async def test_task_lifecycle_with_task_execution() -> None:
|
|||
|
||||
assert isinstance(create_result, CreateTaskResult)
|
||||
assert create_result.task.status == "working"
|
||||
task_id = create_result.task.taskId
|
||||
task_id = create_result.task.task_id
|
||||
|
||||
# === Step 2: Wait for task to complete ===
|
||||
await app_context.task_done_events[task_id].wait()
|
||||
|
||||
task_status = await client_session.send_request(
|
||||
ClientRequest(GetTaskRequest(params=GetTaskRequestParams(taskId=task_id))),
|
||||
ClientRequest(GetTaskRequest(params=GetTaskRequestParams(task_id=task_id))),
|
||||
GetTaskResult,
|
||||
)
|
||||
|
||||
assert task_status.taskId == task_id
|
||||
assert task_status.task_id == task_id
|
||||
assert task_status.status == "completed"
|
||||
|
||||
# === Step 3: Retrieve the actual result ===
|
||||
task_result = await client_session.send_request(
|
||||
ClientRequest(GetTaskPayloadRequest(params=GetTaskPayloadRequestParams(taskId=task_id))),
|
||||
ClientRequest(GetTaskPayloadRequest(params=GetTaskPayloadRequestParams(task_id=task_id))),
|
||||
CallToolResult,
|
||||
)
|
||||
|
||||
|
|
@ -245,7 +245,7 @@ async def test_task_auto_fails_on_exception() -> None:
|
|||
Tool(
|
||||
name="failing_task",
|
||||
description="A task that fails",
|
||||
inputSchema={"type": "object", "properties": {}},
|
||||
input_schema={"type": "object", "properties": {}},
|
||||
)
|
||||
]
|
||||
|
||||
|
|
@ -260,10 +260,10 @@ async def test_task_auto_fails_on_exception() -> None:
|
|||
|
||||
# Create event to signal completion (for testing)
|
||||
done_event = Event()
|
||||
app.task_done_events[task.taskId] = done_event
|
||||
app.task_done_events[task.task_id] = done_event
|
||||
|
||||
async def do_failing_work():
|
||||
async with task_execution(task.taskId, app.store) as task_ctx:
|
||||
async with task_execution(task.task_id, app.store) as task_ctx:
|
||||
await task_ctx.update_status("About to fail...")
|
||||
raise RuntimeError("Something went wrong!")
|
||||
# Note: complete() is never called, but task_execution
|
||||
|
|
@ -279,16 +279,16 @@ async def test_task_auto_fails_on_exception() -> None:
|
|||
@server.experimental.get_task()
|
||||
async def handle_get_task(request: GetTaskRequest) -> GetTaskResult:
|
||||
app = server.request_context.lifespan_context
|
||||
task = await app.store.get_task(request.params.taskId)
|
||||
assert task is not None, f"Test setup error: task {request.params.taskId} should exist"
|
||||
task = await app.store.get_task(request.params.task_id)
|
||||
assert task is not None, f"Test setup error: task {request.params.task_id} should exist"
|
||||
return GetTaskResult(
|
||||
taskId=task.taskId,
|
||||
task_id=task.task_id,
|
||||
status=task.status,
|
||||
statusMessage=task.statusMessage,
|
||||
createdAt=task.createdAt,
|
||||
lastUpdatedAt=task.lastUpdatedAt,
|
||||
status_message=task.status_message,
|
||||
created_at=task.created_at,
|
||||
last_updated_at=task.last_updated_at,
|
||||
ttl=task.ttl,
|
||||
pollInterval=task.pollInterval,
|
||||
poll_interval=task.poll_interval,
|
||||
)
|
||||
|
||||
# Set up streams
|
||||
|
|
@ -340,18 +340,18 @@ async def test_task_auto_fails_on_exception() -> None:
|
|||
CreateTaskResult,
|
||||
)
|
||||
|
||||
task_id = create_result.task.taskId
|
||||
task_id = create_result.task.task_id
|
||||
|
||||
# Wait for task to complete (even though it fails)
|
||||
await app_context.task_done_events[task_id].wait()
|
||||
|
||||
# Check that task was auto-failed
|
||||
task_status = await client_session.send_request(
|
||||
ClientRequest(GetTaskRequest(params=GetTaskRequestParams(taskId=task_id))),
|
||||
ClientRequest(GetTaskRequest(params=GetTaskRequestParams(task_id=task_id))),
|
||||
GetTaskResult,
|
||||
)
|
||||
|
||||
assert task_status.status == "failed"
|
||||
assert task_status.statusMessage == "Something went wrong!"
|
||||
assert task_status.status_message == "Something went wrong!"
|
||||
|
||||
tg.cancel_scope.cancel()
|
||||
|
|
|
|||
|
|
@ -69,8 +69,8 @@ async def test_run_task_basic_flow() -> None:
|
|||
Tool(
|
||||
name="simple_task",
|
||||
description="A simple task",
|
||||
inputSchema={"type": "object", "properties": {"input": {"type": "string"}}},
|
||||
execution=ToolExecution(taskSupport=TASK_REQUIRED),
|
||||
input_schema={"type": "object", "properties": {"input": {"type": "string"}}},
|
||||
execution=ToolExecution(task_support=TASK_REQUIRED),
|
||||
)
|
||||
]
|
||||
|
||||
|
|
@ -119,7 +119,7 @@ async def test_run_task_basic_flow() -> None:
|
|||
)
|
||||
|
||||
# Should get CreateTaskResult
|
||||
task_id = result.task.taskId
|
||||
task_id = result.task.task_id
|
||||
assert result.task.status == "working"
|
||||
|
||||
# Wait for work to complete
|
||||
|
|
@ -157,8 +157,8 @@ async def test_run_task_auto_fails_on_exception() -> None:
|
|||
Tool(
|
||||
name="failing_task",
|
||||
description="A task that fails",
|
||||
inputSchema={"type": "object"},
|
||||
execution=ToolExecution(taskSupport=TASK_REQUIRED),
|
||||
input_schema={"type": "object"},
|
||||
execution=ToolExecution(task_support=TASK_REQUIRED),
|
||||
)
|
||||
]
|
||||
|
||||
|
|
@ -188,7 +188,7 @@ async def test_run_task_auto_fails_on_exception() -> None:
|
|||
await client_session.initialize()
|
||||
|
||||
result = await client_session.experimental.call_tool_as_task("failing_task", {})
|
||||
task_id = result.task.taskId
|
||||
task_id = result.task.task_id
|
||||
|
||||
# Wait for work to fail
|
||||
with anyio.fail_after(5):
|
||||
|
|
@ -201,7 +201,7 @@ async def test_run_task_auto_fails_on_exception() -> None:
|
|||
if task_status.status == "failed": # pragma: no branch
|
||||
break
|
||||
|
||||
assert "Something went wrong" in (task_status.statusMessage or "")
|
||||
assert "Something went wrong" in (task_status.status_message or "")
|
||||
|
||||
async with anyio.create_task_group() as tg:
|
||||
tg.start_soon(run_server)
|
||||
|
|
@ -363,8 +363,8 @@ async def test_run_task_with_model_immediate_response() -> None:
|
|||
Tool(
|
||||
name="task_with_immediate",
|
||||
description="A task with immediate response",
|
||||
inputSchema={"type": "object"},
|
||||
execution=ToolExecution(taskSupport=TASK_REQUIRED),
|
||||
input_schema={"type": "object"},
|
||||
execution=ToolExecution(task_support=TASK_REQUIRED),
|
||||
)
|
||||
]
|
||||
|
||||
|
|
@ -422,8 +422,8 @@ async def test_run_task_doesnt_complete_if_already_terminal() -> None:
|
|||
Tool(
|
||||
name="manual_complete_task",
|
||||
description="A task that manually completes",
|
||||
inputSchema={"type": "object"},
|
||||
execution=ToolExecution(taskSupport=TASK_REQUIRED),
|
||||
input_schema={"type": "object"},
|
||||
execution=ToolExecution(task_support=TASK_REQUIRED),
|
||||
)
|
||||
]
|
||||
|
||||
|
|
@ -457,7 +457,7 @@ async def test_run_task_doesnt_complete_if_already_terminal() -> None:
|
|||
await client_session.initialize()
|
||||
|
||||
result = await client_session.experimental.call_tool_as_task("manual_complete_task", {})
|
||||
task_id = result.task.taskId
|
||||
task_id = result.task.task_id
|
||||
|
||||
with anyio.fail_after(5):
|
||||
await work_completed.wait()
|
||||
|
|
@ -488,8 +488,8 @@ async def test_run_task_doesnt_fail_if_already_terminal() -> None:
|
|||
Tool(
|
||||
name="manual_cancel_task",
|
||||
description="A task that manually cancels then raises",
|
||||
inputSchema={"type": "object"},
|
||||
execution=ToolExecution(taskSupport=TASK_REQUIRED),
|
||||
input_schema={"type": "object"},
|
||||
execution=ToolExecution(task_support=TASK_REQUIRED),
|
||||
)
|
||||
]
|
||||
|
||||
|
|
@ -522,7 +522,7 @@ async def test_run_task_doesnt_fail_if_already_terminal() -> None:
|
|||
await client_session.initialize()
|
||||
|
||||
result = await client_session.experimental.call_tool_as_task("manual_cancel_task", {})
|
||||
task_id = result.task.taskId
|
||||
task_id = result.task.task_id
|
||||
|
||||
with anyio.fail_after(5):
|
||||
await work_completed.wait()
|
||||
|
|
@ -535,7 +535,7 @@ async def test_run_task_doesnt_fail_if_already_terminal() -> None:
|
|||
break
|
||||
|
||||
# Task should still be failed (from manual fail, not auto-fail from exception)
|
||||
assert status.statusMessage == "Manually failed" # Not "This error should not change status"
|
||||
assert status.status_message == "Manually failed" # Not "This error should not change status"
|
||||
|
||||
async with anyio.create_task_group() as tg:
|
||||
tg.start_soon(run_server)
|
||||
|
|
|
|||
|
|
@ -64,20 +64,20 @@ async def test_list_tasks_handler() -> None:
|
|||
now = datetime.now(timezone.utc)
|
||||
test_tasks = [
|
||||
Task(
|
||||
taskId="task-1",
|
||||
task_id="task-1",
|
||||
status="working",
|
||||
createdAt=now,
|
||||
lastUpdatedAt=now,
|
||||
created_at=now,
|
||||
last_updated_at=now,
|
||||
ttl=60000,
|
||||
pollInterval=1000,
|
||||
poll_interval=1000,
|
||||
),
|
||||
Task(
|
||||
taskId="task-2",
|
||||
task_id="task-2",
|
||||
status="completed",
|
||||
createdAt=now,
|
||||
lastUpdatedAt=now,
|
||||
created_at=now,
|
||||
last_updated_at=now,
|
||||
ttl=60000,
|
||||
pollInterval=1000,
|
||||
poll_interval=1000,
|
||||
),
|
||||
]
|
||||
|
||||
|
|
@ -92,8 +92,8 @@ async def test_list_tasks_handler() -> None:
|
|||
assert isinstance(result, ServerResult)
|
||||
assert isinstance(result.root, ListTasksResult)
|
||||
assert len(result.root.tasks) == 2
|
||||
assert result.root.tasks[0].taskId == "task-1"
|
||||
assert result.root.tasks[1].taskId == "task-2"
|
||||
assert result.root.tasks[0].task_id == "task-1"
|
||||
assert result.root.tasks[1].task_id == "task-2"
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
|
|
@ -105,24 +105,24 @@ async def test_get_task_handler() -> None:
|
|||
async def handle_get_task(request: GetTaskRequest) -> GetTaskResult:
|
||||
now = datetime.now(timezone.utc)
|
||||
return GetTaskResult(
|
||||
taskId=request.params.taskId,
|
||||
task_id=request.params.task_id,
|
||||
status="working",
|
||||
createdAt=now,
|
||||
lastUpdatedAt=now,
|
||||
created_at=now,
|
||||
last_updated_at=now,
|
||||
ttl=60000,
|
||||
pollInterval=1000,
|
||||
poll_interval=1000,
|
||||
)
|
||||
|
||||
handler = server.request_handlers[GetTaskRequest]
|
||||
request = GetTaskRequest(
|
||||
method="tasks/get",
|
||||
params=GetTaskRequestParams(taskId="test-task-123"),
|
||||
params=GetTaskRequestParams(task_id="test-task-123"),
|
||||
)
|
||||
result = await handler(request)
|
||||
|
||||
assert isinstance(result, ServerResult)
|
||||
assert isinstance(result.root, GetTaskResult)
|
||||
assert result.root.taskId == "test-task-123"
|
||||
assert result.root.task_id == "test-task-123"
|
||||
assert result.root.status == "working"
|
||||
|
||||
|
||||
|
|
@ -138,7 +138,7 @@ async def test_get_task_result_handler() -> None:
|
|||
handler = server.request_handlers[GetTaskPayloadRequest]
|
||||
request = GetTaskPayloadRequest(
|
||||
method="tasks/result",
|
||||
params=GetTaskPayloadRequestParams(taskId="test-task-123"),
|
||||
params=GetTaskPayloadRequestParams(task_id="test-task-123"),
|
||||
)
|
||||
result = await handler(request)
|
||||
|
||||
|
|
@ -155,23 +155,23 @@ async def test_cancel_task_handler() -> None:
|
|||
async def handle_cancel_task(request: CancelTaskRequest) -> CancelTaskResult:
|
||||
now = datetime.now(timezone.utc)
|
||||
return CancelTaskResult(
|
||||
taskId=request.params.taskId,
|
||||
task_id=request.params.task_id,
|
||||
status="cancelled",
|
||||
createdAt=now,
|
||||
lastUpdatedAt=now,
|
||||
created_at=now,
|
||||
last_updated_at=now,
|
||||
ttl=60000,
|
||||
)
|
||||
|
||||
handler = server.request_handlers[CancelTaskRequest]
|
||||
request = CancelTaskRequest(
|
||||
method="tasks/cancel",
|
||||
params=CancelTaskRequestParams(taskId="test-task-123"),
|
||||
params=CancelTaskRequestParams(task_id="test-task-123"),
|
||||
)
|
||||
result = await handler(request)
|
||||
|
||||
assert isinstance(result, ServerResult)
|
||||
assert isinstance(result.root, CancelTaskResult)
|
||||
assert result.root.taskId == "test-task-123"
|
||||
assert result.root.task_id == "test-task-123"
|
||||
assert result.root.status == "cancelled"
|
||||
|
||||
|
||||
|
|
@ -232,20 +232,20 @@ async def test_tool_with_task_execution_metadata() -> None:
|
|||
Tool(
|
||||
name="quick_tool",
|
||||
description="Fast tool",
|
||||
inputSchema={"type": "object", "properties": {}},
|
||||
execution=ToolExecution(taskSupport=TASK_FORBIDDEN),
|
||||
input_schema={"type": "object", "properties": {}},
|
||||
execution=ToolExecution(task_support=TASK_FORBIDDEN),
|
||||
),
|
||||
Tool(
|
||||
name="long_tool",
|
||||
description="Long running tool",
|
||||
inputSchema={"type": "object", "properties": {}},
|
||||
execution=ToolExecution(taskSupport=TASK_REQUIRED),
|
||||
input_schema={"type": "object", "properties": {}},
|
||||
execution=ToolExecution(task_support=TASK_REQUIRED),
|
||||
),
|
||||
Tool(
|
||||
name="flexible_tool",
|
||||
description="Can be either",
|
||||
inputSchema={"type": "object", "properties": {}},
|
||||
execution=ToolExecution(taskSupport=TASK_OPTIONAL),
|
||||
input_schema={"type": "object", "properties": {}},
|
||||
execution=ToolExecution(task_support=TASK_OPTIONAL),
|
||||
),
|
||||
]
|
||||
|
||||
|
|
@ -258,11 +258,11 @@ async def test_tool_with_task_execution_metadata() -> None:
|
|||
tools = result.root.tools
|
||||
|
||||
assert tools[0].execution is not None
|
||||
assert tools[0].execution.taskSupport == TASK_FORBIDDEN
|
||||
assert tools[0].execution.task_support == TASK_FORBIDDEN
|
||||
assert tools[1].execution is not None
|
||||
assert tools[1].execution.taskSupport == TASK_REQUIRED
|
||||
assert tools[1].execution.task_support == TASK_REQUIRED
|
||||
assert tools[2].execution is not None
|
||||
assert tools[2].execution.taskSupport == TASK_OPTIONAL
|
||||
assert tools[2].execution.task_support == TASK_OPTIONAL
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
|
|
@ -277,8 +277,8 @@ async def test_task_metadata_in_call_tool_request() -> None:
|
|||
Tool(
|
||||
name="long_task",
|
||||
description="A long running task",
|
||||
inputSchema={"type": "object", "properties": {}},
|
||||
execution=ToolExecution(taskSupport="optional"),
|
||||
input_schema={"type": "object", "properties": {}},
|
||||
execution=ToolExecution(task_support="optional"),
|
||||
)
|
||||
]
|
||||
|
||||
|
|
@ -361,7 +361,7 @@ async def test_task_metadata_is_task_property() -> None:
|
|||
Tool(
|
||||
name="test_tool",
|
||||
description="Test tool",
|
||||
inputSchema={"type": "object", "properties": {}},
|
||||
input_schema={"type": "object", "properties": {}},
|
||||
)
|
||||
]
|
||||
|
||||
|
|
@ -513,33 +513,35 @@ async def test_default_task_handlers_via_enable_tasks() -> None:
|
|||
ListTasksResult,
|
||||
)
|
||||
assert len(list_result.tasks) == 1
|
||||
assert list_result.tasks[0].taskId == task.taskId
|
||||
assert list_result.tasks[0].task_id == task.task_id
|
||||
|
||||
# Test get_task (default handler - found)
|
||||
get_result = await client_session.send_request(
|
||||
ClientRequest(GetTaskRequest(params=GetTaskRequestParams(taskId=task.taskId))),
|
||||
ClientRequest(GetTaskRequest(params=GetTaskRequestParams(task_id=task.task_id))),
|
||||
GetTaskResult,
|
||||
)
|
||||
assert get_result.taskId == task.taskId
|
||||
assert get_result.task_id == task.task_id
|
||||
assert get_result.status == "working"
|
||||
|
||||
# Test get_task (default handler - not found path)
|
||||
with pytest.raises(McpError, match="not found"):
|
||||
await client_session.send_request(
|
||||
ClientRequest(GetTaskRequest(params=GetTaskRequestParams(taskId="nonexistent-task"))),
|
||||
ClientRequest(GetTaskRequest(params=GetTaskRequestParams(task_id="nonexistent-task"))),
|
||||
GetTaskResult,
|
||||
)
|
||||
|
||||
# Create a completed task to test get_task_result
|
||||
completed_task = await store.create_task(TaskMetadata(ttl=60000))
|
||||
await store.store_result(
|
||||
completed_task.taskId, CallToolResult(content=[TextContent(type="text", text="Test result")])
|
||||
completed_task.task_id, CallToolResult(content=[TextContent(type="text", text="Test result")])
|
||||
)
|
||||
await store.update_task(completed_task.taskId, status="completed")
|
||||
await store.update_task(completed_task.task_id, status="completed")
|
||||
|
||||
# Test get_task_result (default handler)
|
||||
payload_result = await client_session.send_request(
|
||||
ClientRequest(GetTaskPayloadRequest(params=GetTaskPayloadRequestParams(taskId=completed_task.taskId))),
|
||||
ClientRequest(
|
||||
GetTaskPayloadRequest(params=GetTaskPayloadRequestParams(task_id=completed_task.task_id))
|
||||
),
|
||||
GetTaskPayloadResult,
|
||||
)
|
||||
# The result should have the related-task metadata
|
||||
|
|
@ -548,10 +550,10 @@ async def test_default_task_handlers_via_enable_tasks() -> None:
|
|||
|
||||
# Test cancel_task (default handler)
|
||||
cancel_result = await client_session.send_request(
|
||||
ClientRequest(CancelTaskRequest(params=CancelTaskRequestParams(taskId=task.taskId))),
|
||||
ClientRequest(CancelTaskRequest(params=CancelTaskRequestParams(task_id=task.task_id))),
|
||||
CancelTaskResult,
|
||||
)
|
||||
assert cancel_result.taskId == task.taskId
|
||||
assert cancel_result.task_id == task.task_id
|
||||
assert cancel_result.status == "cancelled"
|
||||
|
||||
tg.cancel_scope.cancel()
|
||||
|
|
@ -576,7 +578,7 @@ async def test_build_elicit_form_request() -> None:
|
|||
# Test without task_id
|
||||
request = server_session._build_elicit_form_request(
|
||||
message="Test message",
|
||||
requestedSchema={"type": "object", "properties": {"answer": {"type": "string"}}},
|
||||
requested_schema={"type": "object", "properties": {"answer": {"type": "string"}}},
|
||||
)
|
||||
assert request.method == "elicitation/create"
|
||||
assert request.params is not None
|
||||
|
|
@ -585,7 +587,7 @@ async def test_build_elicit_form_request() -> None:
|
|||
# Test with related_task_id (adds related-task metadata)
|
||||
request_with_task = server_session._build_elicit_form_request(
|
||||
message="Task message",
|
||||
requestedSchema={"type": "object"},
|
||||
requested_schema={"type": "object"},
|
||||
related_task_id="test-task-123",
|
||||
)
|
||||
assert request_with_task.method == "elicitation/create"
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ async def test_server_task_context_properties() -> None:
|
|||
)
|
||||
|
||||
assert ctx.task_id == "test-123"
|
||||
assert ctx.task.taskId == "test-123"
|
||||
assert ctx.task.task_id == "test-123"
|
||||
assert ctx.is_cancelled is False
|
||||
|
||||
store.cleanup()
|
||||
|
|
@ -181,7 +181,7 @@ async def test_elicit_raises_when_client_lacks_capability() -> None:
|
|||
)
|
||||
|
||||
with pytest.raises(McpError) as exc_info:
|
||||
await ctx.elicit(message="Test?", requestedSchema={"type": "object"})
|
||||
await ctx.elicit(message="Test?", requested_schema={"type": "object"})
|
||||
|
||||
assert "elicitation capability" in exc_info.value.error.message
|
||||
mock_session.check_client_capability.assert_called_once()
|
||||
|
|
@ -232,7 +232,7 @@ async def test_elicit_raises_without_handler() -> None:
|
|||
)
|
||||
|
||||
with pytest.raises(RuntimeError, match="handler is required"):
|
||||
await ctx.elicit(message="Test?", requestedSchema={"type": "object"})
|
||||
await ctx.elicit(message="Test?", requested_schema={"type": "object"})
|
||||
|
||||
store.cleanup()
|
||||
|
||||
|
|
@ -320,22 +320,22 @@ async def test_elicit_queues_request_and_waits_for_response() -> None:
|
|||
nonlocal elicit_result
|
||||
elicit_result = await ctx.elicit(
|
||||
message="Test?",
|
||||
requestedSchema={"type": "object"},
|
||||
requested_schema={"type": "object"},
|
||||
)
|
||||
|
||||
async with anyio.create_task_group() as tg:
|
||||
tg.start_soon(run_elicit)
|
||||
|
||||
# Wait for request to be queued
|
||||
await queue.wait_for_message(task.taskId)
|
||||
await queue.wait_for_message(task.task_id)
|
||||
|
||||
# Verify task is in input_required status
|
||||
updated_task = await store.get_task(task.taskId)
|
||||
updated_task = await store.get_task(task.task_id)
|
||||
assert updated_task is not None
|
||||
assert updated_task.status == "input_required"
|
||||
|
||||
# Dequeue and simulate response
|
||||
msg = await queue.dequeue(task.taskId)
|
||||
msg = await queue.dequeue(task.task_id)
|
||||
assert msg is not None
|
||||
assert msg.resolver is not None
|
||||
|
||||
|
|
@ -348,7 +348,7 @@ async def test_elicit_queues_request_and_waits_for_response() -> None:
|
|||
assert elicit_result.content == {"name": "Alice"}
|
||||
|
||||
# Verify task is back to working
|
||||
final_task = await store.get_task(task.taskId)
|
||||
final_task = await store.get_task(task.task_id)
|
||||
assert final_task is not None
|
||||
assert final_task.status == "working"
|
||||
|
||||
|
|
@ -396,15 +396,15 @@ async def test_elicit_url_queues_request_and_waits_for_response() -> None:
|
|||
tg.start_soon(run_elicit_url)
|
||||
|
||||
# Wait for request to be queued
|
||||
await queue.wait_for_message(task.taskId)
|
||||
await queue.wait_for_message(task.task_id)
|
||||
|
||||
# Verify task is in input_required status
|
||||
updated_task = await store.get_task(task.taskId)
|
||||
updated_task = await store.get_task(task.task_id)
|
||||
assert updated_task is not None
|
||||
assert updated_task.status == "input_required"
|
||||
|
||||
# Dequeue and simulate response
|
||||
msg = await queue.dequeue(task.taskId)
|
||||
msg = await queue.dequeue(task.task_id)
|
||||
assert msg is not None
|
||||
assert msg.resolver is not None
|
||||
|
||||
|
|
@ -416,7 +416,7 @@ async def test_elicit_url_queues_request_and_waits_for_response() -> None:
|
|||
assert elicit_result.action == "accept"
|
||||
|
||||
# Verify task is back to working
|
||||
final_task = await store.get_task(task.taskId)
|
||||
final_task = await store.get_task(task.task_id)
|
||||
assert final_task is not None
|
||||
assert final_task.status == "working"
|
||||
|
||||
|
|
@ -463,15 +463,15 @@ async def test_create_message_queues_request_and_waits_for_response() -> None:
|
|||
tg.start_soon(run_sampling)
|
||||
|
||||
# Wait for request to be queued
|
||||
await queue.wait_for_message(task.taskId)
|
||||
await queue.wait_for_message(task.task_id)
|
||||
|
||||
# Verify task is in input_required status
|
||||
updated_task = await store.get_task(task.taskId)
|
||||
updated_task = await store.get_task(task.task_id)
|
||||
assert updated_task is not None
|
||||
assert updated_task.status == "input_required"
|
||||
|
||||
# Dequeue and simulate response
|
||||
msg = await queue.dequeue(task.taskId)
|
||||
msg = await queue.dequeue(task.task_id)
|
||||
assert msg is not None
|
||||
assert msg.resolver is not None
|
||||
|
||||
|
|
@ -491,7 +491,7 @@ async def test_create_message_queues_request_and_waits_for_response() -> None:
|
|||
assert sampling_result.model == "test-model"
|
||||
|
||||
# Verify task is back to working
|
||||
final_task = await store.get_task(task.taskId)
|
||||
final_task = await store.get_task(task.task_id)
|
||||
assert final_task is not None
|
||||
assert final_task.status == "working"
|
||||
|
||||
|
|
@ -534,7 +534,7 @@ async def test_elicit_restores_status_on_cancellation() -> None:
|
|||
try:
|
||||
await ctx.elicit(
|
||||
message="Test?",
|
||||
requestedSchema={"type": "object"},
|
||||
requested_schema={"type": "object"},
|
||||
)
|
||||
except anyio.get_cancelled_exc_class():
|
||||
cancelled_error_raised = True
|
||||
|
|
@ -543,15 +543,15 @@ async def test_elicit_restores_status_on_cancellation() -> None:
|
|||
tg.start_soon(do_elicit)
|
||||
|
||||
# Wait for request to be queued
|
||||
await queue.wait_for_message(task.taskId)
|
||||
await queue.wait_for_message(task.task_id)
|
||||
|
||||
# Verify task is in input_required status
|
||||
updated_task = await store.get_task(task.taskId)
|
||||
updated_task = await store.get_task(task.task_id)
|
||||
assert updated_task is not None
|
||||
assert updated_task.status == "input_required"
|
||||
|
||||
# Get the queued message and set cancellation exception on its resolver
|
||||
msg = await queue.dequeue(task.taskId)
|
||||
msg = await queue.dequeue(task.task_id)
|
||||
assert msg is not None
|
||||
assert msg.resolver is not None
|
||||
|
||||
|
|
@ -559,7 +559,7 @@ async def test_elicit_restores_status_on_cancellation() -> None:
|
|||
msg.resolver.set_exception(asyncio.CancelledError())
|
||||
|
||||
# Verify task is back to working after cancellation
|
||||
final_task = await store.get_task(task.taskId)
|
||||
final_task = await store.get_task(task.task_id)
|
||||
assert final_task is not None
|
||||
assert final_task.status == "working"
|
||||
assert cancelled_error_raised
|
||||
|
|
@ -612,15 +612,15 @@ async def test_create_message_restores_status_on_cancellation() -> None:
|
|||
tg.start_soon(do_sampling)
|
||||
|
||||
# Wait for request to be queued
|
||||
await queue.wait_for_message(task.taskId)
|
||||
await queue.wait_for_message(task.task_id)
|
||||
|
||||
# Verify task is in input_required status
|
||||
updated_task = await store.get_task(task.taskId)
|
||||
updated_task = await store.get_task(task.task_id)
|
||||
assert updated_task is not None
|
||||
assert updated_task.status == "input_required"
|
||||
|
||||
# Get the queued message and set cancellation exception on its resolver
|
||||
msg = await queue.dequeue(task.taskId)
|
||||
msg = await queue.dequeue(task.task_id)
|
||||
assert msg is not None
|
||||
assert msg.resolver is not None
|
||||
|
||||
|
|
@ -628,7 +628,7 @@ async def test_create_message_restores_status_on_cancellation() -> None:
|
|||
msg.resolver.set_exception(asyncio.CancelledError())
|
||||
|
||||
# Verify task is back to working after cancellation
|
||||
final_task = await store.get_task(task.taskId)
|
||||
final_task = await store.get_task(task.task_id)
|
||||
assert final_task is not None
|
||||
assert final_task.status == "working"
|
||||
assert cancelled_error_raised
|
||||
|
|
@ -646,7 +646,7 @@ async def test_elicit_as_task_raises_without_handler() -> None:
|
|||
# Create mock session with proper client capabilities
|
||||
mock_session = Mock()
|
||||
mock_session.client_params = InitializeRequestParams(
|
||||
protocolVersion="2025-01-01",
|
||||
protocol_version="2025-01-01",
|
||||
capabilities=ClientCapabilities(
|
||||
tasks=ClientTasksCapability(
|
||||
requests=ClientTasksRequestsCapability(
|
||||
|
|
@ -654,7 +654,7 @@ async def test_elicit_as_task_raises_without_handler() -> None:
|
|||
)
|
||||
)
|
||||
),
|
||||
clientInfo=Implementation(name="test", version="1.0"),
|
||||
client_info=Implementation(name="test", version="1.0"),
|
||||
)
|
||||
|
||||
ctx = ServerTaskContext(
|
||||
|
|
@ -666,7 +666,7 @@ async def test_elicit_as_task_raises_without_handler() -> None:
|
|||
)
|
||||
|
||||
with pytest.raises(RuntimeError, match="handler is required for elicit_as_task"):
|
||||
await ctx.elicit_as_task(message="Test?", requestedSchema={"type": "object"})
|
||||
await ctx.elicit_as_task(message="Test?", requested_schema={"type": "object"})
|
||||
|
||||
store.cleanup()
|
||||
|
||||
|
|
@ -681,15 +681,15 @@ async def test_create_message_as_task_raises_without_handler() -> None:
|
|||
# Create mock session with proper client capabilities
|
||||
mock_session = Mock()
|
||||
mock_session.client_params = InitializeRequestParams(
|
||||
protocolVersion="2025-01-01",
|
||||
protocol_version="2025-01-01",
|
||||
capabilities=ClientCapabilities(
|
||||
tasks=ClientTasksCapability(
|
||||
requests=ClientTasksRequestsCapability(
|
||||
sampling=TasksSamplingCapability(createMessage=TasksCreateMessageCapability())
|
||||
sampling=TasksSamplingCapability(create_message=TasksCreateMessageCapability())
|
||||
)
|
||||
)
|
||||
),
|
||||
clientInfo=Implementation(name="test", version="1.0"),
|
||||
client_info=Implementation(name="test", version="1.0"),
|
||||
)
|
||||
|
||||
ctx = ServerTaskContext(
|
||||
|
|
|
|||
|
|
@ -24,13 +24,13 @@ async def test_create_and_get(store: InMemoryTaskStore) -> None:
|
|||
"""Test InMemoryTaskStore create and get operations."""
|
||||
task = await store.create_task(metadata=TaskMetadata(ttl=60000))
|
||||
|
||||
assert task.taskId is not None
|
||||
assert task.task_id is not None
|
||||
assert task.status == "working"
|
||||
assert task.ttl == 60000
|
||||
|
||||
retrieved = await store.get_task(task.taskId)
|
||||
retrieved = await store.get_task(task.task_id)
|
||||
assert retrieved is not None
|
||||
assert retrieved.taskId == task.taskId
|
||||
assert retrieved.task_id == task.task_id
|
||||
assert retrieved.status == "working"
|
||||
|
||||
|
||||
|
|
@ -42,12 +42,12 @@ async def test_create_with_custom_id(store: InMemoryTaskStore) -> None:
|
|||
task_id="my-custom-id",
|
||||
)
|
||||
|
||||
assert task.taskId == "my-custom-id"
|
||||
assert task.task_id == "my-custom-id"
|
||||
assert task.status == "working"
|
||||
|
||||
retrieved = await store.get_task("my-custom-id")
|
||||
assert retrieved is not None
|
||||
assert retrieved.taskId == "my-custom-id"
|
||||
assert retrieved.task_id == "my-custom-id"
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
|
|
@ -71,15 +71,15 @@ async def test_update_status(store: InMemoryTaskStore) -> None:
|
|||
"""Test InMemoryTaskStore status updates."""
|
||||
task = await store.create_task(metadata=TaskMetadata(ttl=60000))
|
||||
|
||||
updated = await store.update_task(task.taskId, status="completed", status_message="All done!")
|
||||
updated = await store.update_task(task.task_id, status="completed", status_message="All done!")
|
||||
|
||||
assert updated.status == "completed"
|
||||
assert updated.statusMessage == "All done!"
|
||||
assert updated.status_message == "All done!"
|
||||
|
||||
retrieved = await store.get_task(task.taskId)
|
||||
retrieved = await store.get_task(task.task_id)
|
||||
assert retrieved is not None
|
||||
assert retrieved.status == "completed"
|
||||
assert retrieved.statusMessage == "All done!"
|
||||
assert retrieved.status_message == "All done!"
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
|
|
@ -96,10 +96,10 @@ async def test_store_and_get_result(store: InMemoryTaskStore) -> None:
|
|||
|
||||
# Store result
|
||||
result = CallToolResult(content=[TextContent(type="text", text="Result data")])
|
||||
await store.store_result(task.taskId, result)
|
||||
await store.store_result(task.task_id, result)
|
||||
|
||||
# Retrieve result
|
||||
retrieved_result = await store.get_result(task.taskId)
|
||||
retrieved_result = await store.get_result(task.task_id)
|
||||
assert retrieved_result == result
|
||||
|
||||
|
||||
|
|
@ -114,7 +114,7 @@ async def test_get_result_nonexistent_returns_none(store: InMemoryTaskStore) ->
|
|||
async def test_get_result_no_result_returns_none(store: InMemoryTaskStore) -> None:
|
||||
"""Test that getting result when none stored returns None."""
|
||||
task = await store.create_task(metadata=TaskMetadata(ttl=60000))
|
||||
result = await store.get_result(task.taskId)
|
||||
result = await store.get_result(task.task_id)
|
||||
assert result is None
|
||||
|
||||
|
||||
|
|
@ -172,14 +172,14 @@ async def test_delete_task(store: InMemoryTaskStore) -> None:
|
|||
"""Test InMemoryTaskStore delete operation."""
|
||||
task = await store.create_task(metadata=TaskMetadata(ttl=60000))
|
||||
|
||||
deleted = await store.delete_task(task.taskId)
|
||||
deleted = await store.delete_task(task.task_id)
|
||||
assert deleted is True
|
||||
|
||||
retrieved = await store.get_task(task.taskId)
|
||||
retrieved = await store.get_task(task.task_id)
|
||||
assert retrieved is None
|
||||
|
||||
# Delete non-existent
|
||||
deleted = await store.delete_task(task.taskId)
|
||||
deleted = await store.delete_task(task.task_id)
|
||||
assert deleted is False
|
||||
|
||||
|
||||
|
|
@ -210,7 +210,7 @@ async def test_create_task_with_null_ttl(store: InMemoryTaskStore) -> None:
|
|||
assert task.ttl is None
|
||||
|
||||
# Task should persist (not expire)
|
||||
retrieved = await store.get_task(task.taskId)
|
||||
retrieved = await store.get_task(task.task_id)
|
||||
assert retrieved is not None
|
||||
|
||||
|
||||
|
|
@ -221,19 +221,19 @@ async def test_task_expiration_cleanup(store: InMemoryTaskStore) -> None:
|
|||
task = await store.create_task(metadata=TaskMetadata(ttl=1)) # 1ms TTL
|
||||
|
||||
# Manually force the expiry to be in the past
|
||||
stored = store._tasks.get(task.taskId)
|
||||
stored = store._tasks.get(task.task_id)
|
||||
assert stored is not None
|
||||
stored.expires_at = datetime.now(timezone.utc) - timedelta(seconds=10)
|
||||
|
||||
# Task should still exist in internal dict but be expired
|
||||
assert task.taskId in store._tasks
|
||||
assert task.task_id in store._tasks
|
||||
|
||||
# Any access operation should clean up expired tasks
|
||||
# list_tasks triggers cleanup
|
||||
tasks, _ = await store.list_tasks()
|
||||
|
||||
# Expired task should be cleaned up
|
||||
assert task.taskId not in store._tasks
|
||||
assert task.task_id not in store._tasks
|
||||
assert len(tasks) == 0
|
||||
|
||||
|
||||
|
|
@ -244,17 +244,17 @@ async def test_task_with_null_ttl_never_expires(store: InMemoryTaskStore) -> Non
|
|||
task = await store.create_task(metadata=TaskMetadata(ttl=None))
|
||||
|
||||
# Verify internal storage has no expiry
|
||||
stored = store._tasks.get(task.taskId)
|
||||
stored = store._tasks.get(task.task_id)
|
||||
assert stored is not None
|
||||
assert stored.expires_at is None
|
||||
|
||||
# Access operations should NOT remove this task
|
||||
await store.list_tasks()
|
||||
await store.get_task(task.taskId)
|
||||
await store.get_task(task.task_id)
|
||||
|
||||
# Task should still exist
|
||||
assert task.taskId in store._tasks
|
||||
retrieved = await store.get_task(task.taskId)
|
||||
assert task.task_id in store._tasks
|
||||
retrieved = await store.get_task(task.task_id)
|
||||
assert retrieved is not None
|
||||
|
||||
|
||||
|
|
@ -265,13 +265,13 @@ async def test_terminal_task_ttl_reset(store: InMemoryTaskStore) -> None:
|
|||
task = await store.create_task(metadata=TaskMetadata(ttl=60000)) # 60s
|
||||
|
||||
# Get the initial expiry
|
||||
stored = store._tasks.get(task.taskId)
|
||||
stored = store._tasks.get(task.task_id)
|
||||
assert stored is not None
|
||||
initial_expiry = stored.expires_at
|
||||
assert initial_expiry is not None
|
||||
|
||||
# Update to terminal state (completed)
|
||||
await store.update_task(task.taskId, status="completed")
|
||||
await store.update_task(task.task_id, status="completed")
|
||||
|
||||
# Expiry should be reset to a new time (from now + TTL)
|
||||
new_expiry = stored.expires_at
|
||||
|
|
@ -291,16 +291,16 @@ async def test_terminal_status_transition_rejected(store: InMemoryTaskStore) ->
|
|||
task = await store.create_task(metadata=TaskMetadata(ttl=60000))
|
||||
|
||||
# Move to terminal state
|
||||
await store.update_task(task.taskId, status=terminal_status)
|
||||
await store.update_task(task.task_id, status=terminal_status)
|
||||
|
||||
# Attempting to transition to any other status should raise
|
||||
with pytest.raises(ValueError, match="Cannot transition from terminal status"):
|
||||
await store.update_task(task.taskId, status="working")
|
||||
await store.update_task(task.task_id, status="working")
|
||||
|
||||
# Also test transitioning to another terminal state
|
||||
other_terminal = "failed" if terminal_status != "failed" else "completed"
|
||||
with pytest.raises(ValueError, match="Cannot transition from terminal status"):
|
||||
await store.update_task(task.taskId, status=other_terminal)
|
||||
await store.update_task(task.task_id, status=other_terminal)
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
|
|
@ -310,15 +310,15 @@ async def test_terminal_status_allows_same_status(store: InMemoryTaskStore) -> N
|
|||
This is not a transition, so it should be allowed (no-op).
|
||||
"""
|
||||
task = await store.create_task(metadata=TaskMetadata(ttl=60000))
|
||||
await store.update_task(task.taskId, status="completed")
|
||||
await store.update_task(task.task_id, status="completed")
|
||||
|
||||
# Setting the same status should not raise
|
||||
updated = await store.update_task(task.taskId, status="completed")
|
||||
updated = await store.update_task(task.task_id, status="completed")
|
||||
assert updated.status == "completed"
|
||||
|
||||
# Updating just the message should also work
|
||||
updated = await store.update_task(task.taskId, status_message="Updated message")
|
||||
assert updated.statusMessage == "Updated message"
|
||||
updated = await store.update_task(task.task_id, status_message="Updated message")
|
||||
assert updated.status_message == "Updated message"
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
|
|
@ -334,13 +334,13 @@ async def test_cancel_task_succeeds_for_working_task(store: InMemoryTaskStore) -
|
|||
task = await store.create_task(metadata=TaskMetadata(ttl=60000))
|
||||
assert task.status == "working"
|
||||
|
||||
result = await cancel_task(store, task.taskId)
|
||||
result = await cancel_task(store, task.task_id)
|
||||
|
||||
assert result.taskId == task.taskId
|
||||
assert result.task_id == task.task_id
|
||||
assert result.status == "cancelled"
|
||||
|
||||
# Verify store is updated
|
||||
retrieved = await store.get_task(task.taskId)
|
||||
retrieved = await store.get_task(task.task_id)
|
||||
assert retrieved is not None
|
||||
assert retrieved.status == "cancelled"
|
||||
|
||||
|
|
@ -359,10 +359,10 @@ async def test_cancel_task_rejects_nonexistent_task(store: InMemoryTaskStore) ->
|
|||
async def test_cancel_task_rejects_completed_task(store: InMemoryTaskStore) -> None:
|
||||
"""Test cancel_task raises McpError with INVALID_PARAMS for completed task."""
|
||||
task = await store.create_task(metadata=TaskMetadata(ttl=60000))
|
||||
await store.update_task(task.taskId, status="completed")
|
||||
await store.update_task(task.task_id, status="completed")
|
||||
|
||||
with pytest.raises(McpError) as exc_info:
|
||||
await cancel_task(store, task.taskId)
|
||||
await cancel_task(store, task.task_id)
|
||||
|
||||
assert exc_info.value.error.code == INVALID_PARAMS
|
||||
assert "terminal state 'completed'" in exc_info.value.error.message
|
||||
|
|
@ -372,10 +372,10 @@ async def test_cancel_task_rejects_completed_task(store: InMemoryTaskStore) -> N
|
|||
async def test_cancel_task_rejects_failed_task(store: InMemoryTaskStore) -> None:
|
||||
"""Test cancel_task raises McpError with INVALID_PARAMS for failed task."""
|
||||
task = await store.create_task(metadata=TaskMetadata(ttl=60000))
|
||||
await store.update_task(task.taskId, status="failed")
|
||||
await store.update_task(task.task_id, status="failed")
|
||||
|
||||
with pytest.raises(McpError) as exc_info:
|
||||
await cancel_task(store, task.taskId)
|
||||
await cancel_task(store, task.task_id)
|
||||
|
||||
assert exc_info.value.error.code == INVALID_PARAMS
|
||||
assert "terminal state 'failed'" in exc_info.value.error.message
|
||||
|
|
@ -385,10 +385,10 @@ async def test_cancel_task_rejects_failed_task(store: InMemoryTaskStore) -> None
|
|||
async def test_cancel_task_rejects_already_cancelled_task(store: InMemoryTaskStore) -> None:
|
||||
"""Test cancel_task raises McpError with INVALID_PARAMS for already cancelled task."""
|
||||
task = await store.create_task(metadata=TaskMetadata(ttl=60000))
|
||||
await store.update_task(task.taskId, status="cancelled")
|
||||
await store.update_task(task.task_id, status="cancelled")
|
||||
|
||||
with pytest.raises(McpError) as exc_info:
|
||||
await cancel_task(store, task.taskId)
|
||||
await cancel_task(store, task.task_id)
|
||||
|
||||
assert exc_info.value.error.code == INVALID_PARAMS
|
||||
assert "terminal state 'cancelled'" in exc_info.value.error.message
|
||||
|
|
@ -398,9 +398,9 @@ async def test_cancel_task_rejects_already_cancelled_task(store: InMemoryTaskSto
|
|||
async def test_cancel_task_succeeds_for_input_required_task(store: InMemoryTaskStore) -> None:
|
||||
"""Test cancel_task helper succeeds for a task in input_required status."""
|
||||
task = await store.create_task(metadata=TaskMetadata(ttl=60000))
|
||||
await store.update_task(task.taskId, status="input_required")
|
||||
await store.update_task(task.task_id, status="input_required")
|
||||
|
||||
result = await cancel_task(store, task.taskId)
|
||||
result = await cancel_task(store, task.task_id)
|
||||
|
||||
assert result.taskId == task.taskId
|
||||
assert result.task_id == task.task_id
|
||||
assert result.status == "cancelled"
|
||||
|
|
|
|||
|
|
@ -53,13 +53,13 @@ async def test_handle_returns_result_for_completed_task(
|
|||
"""Test that handle() returns the stored result for a completed task."""
|
||||
task = await store.create_task(TaskMetadata(ttl=60000), task_id="test-task")
|
||||
result = CallToolResult(content=[TextContent(type="text", text="Done!")])
|
||||
await store.store_result(task.taskId, result)
|
||||
await store.update_task(task.taskId, status="completed")
|
||||
await store.store_result(task.task_id, result)
|
||||
await store.update_task(task.task_id, status="completed")
|
||||
|
||||
mock_session = Mock()
|
||||
mock_session.send_message = AsyncMock()
|
||||
|
||||
request = GetTaskPayloadRequest(params=GetTaskPayloadRequestParams(taskId=task.taskId))
|
||||
request = GetTaskPayloadRequest(params=GetTaskPayloadRequestParams(task_id=task.task_id))
|
||||
response = await handler.handle(request, mock_session, "req-1")
|
||||
|
||||
assert response is not None
|
||||
|
|
@ -73,7 +73,7 @@ async def test_handle_raises_for_nonexistent_task(
|
|||
) -> None:
|
||||
"""Test that handle() raises McpError for nonexistent task."""
|
||||
mock_session = Mock()
|
||||
request = GetTaskPayloadRequest(params=GetTaskPayloadRequestParams(taskId="nonexistent"))
|
||||
request = GetTaskPayloadRequest(params=GetTaskPayloadRequestParams(task_id="nonexistent"))
|
||||
|
||||
with pytest.raises(McpError) as exc_info:
|
||||
await handler.handle(request, mock_session, "req-1")
|
||||
|
|
@ -87,12 +87,12 @@ async def test_handle_returns_empty_result_when_no_result_stored(
|
|||
) -> None:
|
||||
"""Test that handle() returns minimal result when task completed without stored result."""
|
||||
task = await store.create_task(TaskMetadata(ttl=60000), task_id="test-task")
|
||||
await store.update_task(task.taskId, status="completed")
|
||||
await store.update_task(task.task_id, status="completed")
|
||||
|
||||
mock_session = Mock()
|
||||
mock_session.send_message = AsyncMock()
|
||||
|
||||
request = GetTaskPayloadRequest(params=GetTaskPayloadRequestParams(taskId=task.taskId))
|
||||
request = GetTaskPayloadRequest(params=GetTaskPayloadRequestParams(task_id=task.task_id))
|
||||
response = await handler.handle(request, mock_session, "req-1")
|
||||
|
||||
assert response is not None
|
||||
|
|
@ -116,8 +116,8 @@ async def test_handle_delivers_queued_messages(
|
|||
params={},
|
||||
),
|
||||
)
|
||||
await queue.enqueue(task.taskId, queued_msg)
|
||||
await store.update_task(task.taskId, status="completed")
|
||||
await queue.enqueue(task.task_id, queued_msg)
|
||||
await store.update_task(task.task_id, status="completed")
|
||||
|
||||
sent_messages: list[SessionMessage] = []
|
||||
|
||||
|
|
@ -127,7 +127,7 @@ async def test_handle_delivers_queued_messages(
|
|||
mock_session = Mock()
|
||||
mock_session.send_message = track_send
|
||||
|
||||
request = GetTaskPayloadRequest(params=GetTaskPayloadRequestParams(taskId=task.taskId))
|
||||
request = GetTaskPayloadRequest(params=GetTaskPayloadRequestParams(task_id=task.task_id))
|
||||
await handler.handle(request, mock_session, "req-1")
|
||||
|
||||
assert len(sent_messages) == 1
|
||||
|
|
@ -143,7 +143,7 @@ async def test_handle_waits_for_task_completion(
|
|||
mock_session = Mock()
|
||||
mock_session.send_message = AsyncMock()
|
||||
|
||||
request = GetTaskPayloadRequest(params=GetTaskPayloadRequestParams(taskId=task.taskId))
|
||||
request = GetTaskPayloadRequest(params=GetTaskPayloadRequestParams(task_id=task.task_id))
|
||||
result_holder: list[GetTaskPayloadResult | None] = [None]
|
||||
|
||||
async def run_handle() -> None:
|
||||
|
|
@ -153,11 +153,11 @@ async def test_handle_waits_for_task_completion(
|
|||
tg.start_soon(run_handle)
|
||||
|
||||
# Wait for handler to start waiting (event gets created when wait starts)
|
||||
while task.taskId not in store._update_events:
|
||||
while task.task_id not in store._update_events:
|
||||
await anyio.sleep(0)
|
||||
|
||||
await store.store_result(task.taskId, CallToolResult(content=[TextContent(type="text", text="Done")]))
|
||||
await store.update_task(task.taskId, status="completed")
|
||||
await store.store_result(task.task_id, CallToolResult(content=[TextContent(type="text", text="Done")]))
|
||||
await store.update_task(task.task_id, status="completed")
|
||||
|
||||
assert result_holder[0] is not None
|
||||
|
||||
|
|
@ -248,12 +248,12 @@ async def test_deliver_registers_resolver_for_request_messages(
|
|||
resolver=resolver,
|
||||
original_request_id="inner-req-1",
|
||||
)
|
||||
await queue.enqueue(task.taskId, queued_msg)
|
||||
await queue.enqueue(task.task_id, queued_msg)
|
||||
|
||||
mock_session = Mock()
|
||||
mock_session.send_message = AsyncMock()
|
||||
|
||||
await handler._deliver_queued_messages(task.taskId, mock_session, "outer-req-1")
|
||||
await handler._deliver_queued_messages(task.task_id, mock_session, "outer-req-1")
|
||||
|
||||
assert "inner-req-1" in handler._pending_requests
|
||||
assert handler._pending_requests["inner-req-1"] is resolver
|
||||
|
|
@ -278,12 +278,12 @@ async def test_deliver_skips_resolver_registration_when_no_original_id(
|
|||
resolver=resolver,
|
||||
original_request_id=None, # No original request ID
|
||||
)
|
||||
await queue.enqueue(task.taskId, queued_msg)
|
||||
await queue.enqueue(task.task_id, queued_msg)
|
||||
|
||||
mock_session = Mock()
|
||||
mock_session.send_message = AsyncMock()
|
||||
|
||||
await handler._deliver_queued_messages(task.taskId, mock_session, "outer-req-1")
|
||||
await handler._deliver_queued_messages(task.task_id, mock_session, "outer-req-1")
|
||||
|
||||
# Resolver should NOT be registered since original_request_id is None
|
||||
assert len(handler._pending_requests) == 0
|
||||
|
|
@ -307,10 +307,10 @@ async def test_wait_for_task_update_handles_store_exception(
|
|||
# Queue a message to unblock the race via the queue path
|
||||
async def enqueue_later() -> None:
|
||||
# Wait for queue to start waiting (event gets created when wait starts)
|
||||
while task.taskId not in queue._events:
|
||||
while task.task_id not in queue._events:
|
||||
await anyio.sleep(0)
|
||||
await queue.enqueue(
|
||||
task.taskId,
|
||||
task.task_id,
|
||||
QueuedMessage(
|
||||
type="notification",
|
||||
message=JSONRPCRequest(
|
||||
|
|
@ -325,7 +325,7 @@ async def test_wait_for_task_update_handles_store_exception(
|
|||
async with anyio.create_task_group() as tg:
|
||||
tg.start_soon(enqueue_later)
|
||||
# This should complete via the queue path even though store raises
|
||||
await handler._wait_for_task_update(task.taskId)
|
||||
await handler._wait_for_task_update(task.task_id)
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
|
|
@ -344,11 +344,11 @@ async def test_wait_for_task_update_handles_queue_exception(
|
|||
# Update the store to unblock the race via the store path
|
||||
async def update_later() -> None:
|
||||
# Wait for store to start waiting (event gets created when wait starts)
|
||||
while task.taskId not in store._update_events:
|
||||
while task.task_id not in store._update_events:
|
||||
await anyio.sleep(0)
|
||||
await store.update_task(task.taskId, status="completed")
|
||||
await store.update_task(task.task_id, status="completed")
|
||||
|
||||
async with anyio.create_task_group() as tg:
|
||||
tg.start_soon(update_later)
|
||||
# This should complete via the store path even though queue raises
|
||||
await handler._wait_for_task_update(task.taskId)
|
||||
await handler._wait_for_task_update(task.task_id)
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ class TestCheckTasksCapability:
|
|||
"""When sampling.createMessage is required but client doesn't have it."""
|
||||
required = ClientTasksCapability(
|
||||
requests=ClientTasksRequestsCapability(
|
||||
sampling=TasksSamplingCapability(createMessage=TasksCreateMessageCapability())
|
||||
sampling=TasksSamplingCapability(create_message=TasksCreateMessageCapability())
|
||||
)
|
||||
)
|
||||
client = ClientTasksCapability(
|
||||
|
|
@ -96,12 +96,12 @@ class TestCheckTasksCapability:
|
|||
"""When sampling.createMessage is required and client has it."""
|
||||
required = ClientTasksCapability(
|
||||
requests=ClientTasksRequestsCapability(
|
||||
sampling=TasksSamplingCapability(createMessage=TasksCreateMessageCapability())
|
||||
sampling=TasksSamplingCapability(create_message=TasksCreateMessageCapability())
|
||||
)
|
||||
)
|
||||
client = ClientTasksCapability(
|
||||
requests=ClientTasksRequestsCapability(
|
||||
sampling=TasksSamplingCapability(createMessage=TasksCreateMessageCapability())
|
||||
sampling=TasksSamplingCapability(create_message=TasksCreateMessageCapability())
|
||||
)
|
||||
)
|
||||
assert check_tasks_capability(required, client) is True
|
||||
|
|
@ -111,13 +111,13 @@ class TestCheckTasksCapability:
|
|||
required = ClientTasksCapability(
|
||||
requests=ClientTasksRequestsCapability(
|
||||
elicitation=TasksElicitationCapability(create=TasksCreateElicitationCapability()),
|
||||
sampling=TasksSamplingCapability(createMessage=TasksCreateMessageCapability()),
|
||||
sampling=TasksSamplingCapability(create_message=TasksCreateMessageCapability()),
|
||||
)
|
||||
)
|
||||
client = ClientTasksCapability(
|
||||
requests=ClientTasksRequestsCapability(
|
||||
elicitation=TasksElicitationCapability(create=TasksCreateElicitationCapability()),
|
||||
sampling=TasksSamplingCapability(createMessage=TasksCreateMessageCapability()),
|
||||
sampling=TasksSamplingCapability(create_message=TasksCreateMessageCapability()),
|
||||
)
|
||||
)
|
||||
assert check_tasks_capability(required, client) is True
|
||||
|
|
@ -145,7 +145,7 @@ class TestCheckTasksCapability:
|
|||
)
|
||||
client = ClientTasksCapability(
|
||||
requests=ClientTasksRequestsCapability(
|
||||
sampling=TasksSamplingCapability(createMessage=TasksCreateMessageCapability())
|
||||
sampling=TasksSamplingCapability(create_message=TasksCreateMessageCapability())
|
||||
)
|
||||
)
|
||||
assert check_tasks_capability(required, client) is True
|
||||
|
|
@ -220,7 +220,7 @@ class TestHasTaskAugmentedSampling:
|
|||
caps = ClientCapabilities(
|
||||
tasks=ClientTasksCapability(
|
||||
requests=ClientTasksRequestsCapability(
|
||||
sampling=TasksSamplingCapability(createMessage=TasksCreateMessageCapability())
|
||||
sampling=TasksSamplingCapability(create_message=TasksCreateMessageCapability())
|
||||
)
|
||||
)
|
||||
)
|
||||
|
|
@ -276,7 +276,7 @@ class TestRequireTaskAugmentedSampling:
|
|||
caps = ClientCapabilities(
|
||||
tasks=ClientTasksCapability(
|
||||
requests=ClientTasksRequestsCapability(
|
||||
sampling=TasksSamplingCapability(createMessage=TasksCreateMessageCapability())
|
||||
sampling=TasksSamplingCapability(create_message=TasksCreateMessageCapability())
|
||||
)
|
||||
)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -61,13 +61,13 @@ def create_client_task_handlers(
|
|||
"""Handle task-augmented elicitation by creating a client-side task."""
|
||||
elicit_received.set()
|
||||
task = await client_task_store.create_task(task_metadata)
|
||||
task_complete_events[task.taskId] = Event()
|
||||
task_complete_events[task.task_id] = Event()
|
||||
|
||||
async def complete_task() -> None:
|
||||
# Store result before updating status to avoid race condition
|
||||
await client_task_store.store_result(task.taskId, elicit_response)
|
||||
await client_task_store.update_task(task.taskId, status="completed")
|
||||
task_complete_events[task.taskId].set()
|
||||
await client_task_store.store_result(task.task_id, elicit_response)
|
||||
await client_task_store.update_task(task.task_id, status="completed")
|
||||
task_complete_events[task.task_id].set()
|
||||
|
||||
context.session._task_group.start_soon(complete_task) # pyright: ignore[reportPrivateUsage]
|
||||
return CreateTaskResult(task=task)
|
||||
|
|
@ -77,16 +77,16 @@ def create_client_task_handlers(
|
|||
params: Any,
|
||||
) -> GetTaskResult:
|
||||
"""Handle tasks/get from server."""
|
||||
task = await client_task_store.get_task(params.taskId)
|
||||
assert task is not None, f"Task not found: {params.taskId}"
|
||||
task = await client_task_store.get_task(params.task_id)
|
||||
assert task is not None, f"Task not found: {params.task_id}"
|
||||
return GetTaskResult(
|
||||
taskId=task.taskId,
|
||||
task_id=task.task_id,
|
||||
status=task.status,
|
||||
statusMessage=task.statusMessage,
|
||||
createdAt=task.createdAt,
|
||||
lastUpdatedAt=task.lastUpdatedAt,
|
||||
status_message=task.status_message,
|
||||
created_at=task.created_at,
|
||||
last_updated_at=task.last_updated_at,
|
||||
ttl=task.ttl,
|
||||
pollInterval=100,
|
||||
poll_interval=100,
|
||||
)
|
||||
|
||||
async def handle_get_task_result(
|
||||
|
|
@ -94,11 +94,11 @@ def create_client_task_handlers(
|
|||
params: Any,
|
||||
) -> GetTaskPayloadResult | ErrorData:
|
||||
"""Handle tasks/result from server."""
|
||||
event = task_complete_events.get(params.taskId)
|
||||
assert event is not None, f"No completion event for task: {params.taskId}"
|
||||
event = task_complete_events.get(params.task_id)
|
||||
assert event is not None, f"No completion event for task: {params.task_id}"
|
||||
await event.wait()
|
||||
result = await client_task_store.get_result(params.taskId)
|
||||
assert result is not None, f"Result not found for task: {params.taskId}"
|
||||
result = await client_task_store.get_result(params.task_id)
|
||||
assert result is not None, f"Result not found for task: {params.task_id}"
|
||||
return GetTaskPayloadResult.model_validate(result.model_dump(by_alias=True))
|
||||
|
||||
return ExperimentalTaskHandlers(
|
||||
|
|
@ -129,13 +129,13 @@ def create_sampling_task_handlers(
|
|||
"""Handle task-augmented sampling by creating a client-side task."""
|
||||
sampling_received.set()
|
||||
task = await client_task_store.create_task(task_metadata)
|
||||
task_complete_events[task.taskId] = Event()
|
||||
task_complete_events[task.task_id] = Event()
|
||||
|
||||
async def complete_task() -> None:
|
||||
# Store result before updating status to avoid race condition
|
||||
await client_task_store.store_result(task.taskId, sampling_response)
|
||||
await client_task_store.update_task(task.taskId, status="completed")
|
||||
task_complete_events[task.taskId].set()
|
||||
await client_task_store.store_result(task.task_id, sampling_response)
|
||||
await client_task_store.update_task(task.task_id, status="completed")
|
||||
task_complete_events[task.task_id].set()
|
||||
|
||||
context.session._task_group.start_soon(complete_task) # pyright: ignore[reportPrivateUsage]
|
||||
return CreateTaskResult(task=task)
|
||||
|
|
@ -145,16 +145,16 @@ def create_sampling_task_handlers(
|
|||
params: Any,
|
||||
) -> GetTaskResult:
|
||||
"""Handle tasks/get from server."""
|
||||
task = await client_task_store.get_task(params.taskId)
|
||||
assert task is not None, f"Task not found: {params.taskId}"
|
||||
task = await client_task_store.get_task(params.task_id)
|
||||
assert task is not None, f"Task not found: {params.task_id}"
|
||||
return GetTaskResult(
|
||||
taskId=task.taskId,
|
||||
task_id=task.task_id,
|
||||
status=task.status,
|
||||
statusMessage=task.statusMessage,
|
||||
createdAt=task.createdAt,
|
||||
lastUpdatedAt=task.lastUpdatedAt,
|
||||
status_message=task.status_message,
|
||||
created_at=task.created_at,
|
||||
last_updated_at=task.last_updated_at,
|
||||
ttl=task.ttl,
|
||||
pollInterval=100,
|
||||
poll_interval=100,
|
||||
)
|
||||
|
||||
async def handle_get_task_result(
|
||||
|
|
@ -162,11 +162,11 @@ def create_sampling_task_handlers(
|
|||
params: Any,
|
||||
) -> GetTaskPayloadResult | ErrorData:
|
||||
"""Handle tasks/result from server."""
|
||||
event = task_complete_events.get(params.taskId)
|
||||
assert event is not None, f"No completion event for task: {params.taskId}"
|
||||
event = task_complete_events.get(params.task_id)
|
||||
assert event is not None, f"No completion event for task: {params.task_id}"
|
||||
await event.wait()
|
||||
result = await client_task_store.get_result(params.taskId)
|
||||
assert result is not None, f"Result not found for task: {params.taskId}"
|
||||
result = await client_task_store.get_result(params.task_id)
|
||||
assert result is not None, f"Result not found for task: {params.task_id}"
|
||||
return GetTaskPayloadResult.model_validate(result.model_dump(by_alias=True))
|
||||
|
||||
return ExperimentalTaskHandlers(
|
||||
|
|
@ -193,7 +193,7 @@ async def test_scenario1_normal_tool_normal_elicitation() -> None:
|
|||
Tool(
|
||||
name="confirm_action",
|
||||
description="Confirm an action",
|
||||
inputSchema={"type": "object"},
|
||||
input_schema={"type": "object"},
|
||||
)
|
||||
]
|
||||
|
||||
|
|
@ -204,7 +204,7 @@ async def test_scenario1_normal_tool_normal_elicitation() -> None:
|
|||
# Normal elicitation - expects immediate response
|
||||
result = await ctx.session.elicit(
|
||||
message="Please confirm the action",
|
||||
requestedSchema={"type": "object", "properties": {"confirm": {"type": "boolean"}}},
|
||||
requested_schema={"type": "object", "properties": {"confirm": {"type": "boolean"}}},
|
||||
)
|
||||
|
||||
confirmed = result.content.get("confirm", False) if result.content else False
|
||||
|
|
@ -278,7 +278,7 @@ async def test_scenario2_normal_tool_task_augmented_elicitation() -> None:
|
|||
Tool(
|
||||
name="confirm_action",
|
||||
description="Confirm an action",
|
||||
inputSchema={"type": "object"},
|
||||
input_schema={"type": "object"},
|
||||
)
|
||||
]
|
||||
|
||||
|
|
@ -289,7 +289,7 @@ async def test_scenario2_normal_tool_task_augmented_elicitation() -> None:
|
|||
# Task-augmented elicitation - server polls client
|
||||
result = await ctx.session.experimental.elicit_as_task(
|
||||
message="Please confirm the action",
|
||||
requestedSchema={"type": "object", "properties": {"confirm": {"type": "boolean"}}},
|
||||
requested_schema={"type": "object", "properties": {"confirm": {"type": "boolean"}}},
|
||||
ttl=60000,
|
||||
)
|
||||
|
||||
|
|
@ -358,8 +358,8 @@ async def test_scenario3_task_augmented_tool_normal_elicitation() -> None:
|
|||
Tool(
|
||||
name="confirm_action",
|
||||
description="Confirm an action",
|
||||
inputSchema={"type": "object"},
|
||||
execution=ToolExecution(taskSupport=TASK_REQUIRED),
|
||||
input_schema={"type": "object"},
|
||||
execution=ToolExecution(task_support=TASK_REQUIRED),
|
||||
)
|
||||
]
|
||||
|
||||
|
|
@ -372,7 +372,7 @@ async def test_scenario3_task_augmented_tool_normal_elicitation() -> None:
|
|||
# Normal elicitation within task - queued and delivered via tasks/result
|
||||
result = await task.elicit(
|
||||
message="Please confirm the action",
|
||||
requestedSchema={"type": "object", "properties": {"confirm": {"type": "boolean"}}},
|
||||
requested_schema={"type": "object", "properties": {"confirm": {"type": "boolean"}}},
|
||||
)
|
||||
|
||||
confirmed = result.content.get("confirm", False) if result.content else False
|
||||
|
|
@ -413,7 +413,7 @@ async def test_scenario3_task_augmented_tool_normal_elicitation() -> None:
|
|||
|
||||
# Call tool as task
|
||||
create_result = await client_session.experimental.call_tool_as_task("confirm_action", {})
|
||||
task_id = create_result.task.taskId
|
||||
task_id = create_result.task.task_id
|
||||
assert create_result.task.status == "working"
|
||||
|
||||
# Poll until input_required, then call tasks/result
|
||||
|
|
@ -472,8 +472,8 @@ async def test_scenario4_task_augmented_tool_task_augmented_elicitation() -> Non
|
|||
Tool(
|
||||
name="confirm_action",
|
||||
description="Confirm an action",
|
||||
inputSchema={"type": "object"},
|
||||
execution=ToolExecution(taskSupport=TASK_REQUIRED),
|
||||
input_schema={"type": "object"},
|
||||
execution=ToolExecution(task_support=TASK_REQUIRED),
|
||||
)
|
||||
]
|
||||
|
||||
|
|
@ -486,7 +486,7 @@ async def test_scenario4_task_augmented_tool_task_augmented_elicitation() -> Non
|
|||
# Task-augmented elicitation within task - server polls client
|
||||
result = await task.elicit_as_task(
|
||||
message="Please confirm the action",
|
||||
requestedSchema={"type": "object", "properties": {"confirm": {"type": "boolean"}}},
|
||||
requested_schema={"type": "object", "properties": {"confirm": {"type": "boolean"}}},
|
||||
ttl=60000,
|
||||
)
|
||||
|
||||
|
|
@ -522,7 +522,7 @@ async def test_scenario4_task_augmented_tool_task_augmented_elicitation() -> Non
|
|||
|
||||
# Call tool as task
|
||||
create_result = await client_session.experimental.call_tool_as_task("confirm_action", {})
|
||||
task_id = create_result.task.taskId
|
||||
task_id = create_result.task.task_id
|
||||
assert create_result.task.status == "working"
|
||||
|
||||
# Poll until input_required or terminal, then call tasks/result
|
||||
|
|
@ -572,7 +572,7 @@ async def test_scenario2_sampling_normal_tool_task_augmented_sampling() -> None:
|
|||
Tool(
|
||||
name="generate_text",
|
||||
description="Generate text using sampling",
|
||||
inputSchema={"type": "object"},
|
||||
input_schema={"type": "object"},
|
||||
)
|
||||
]
|
||||
|
||||
|
|
@ -658,8 +658,8 @@ async def test_scenario4_sampling_task_augmented_tool_task_augmented_sampling()
|
|||
Tool(
|
||||
name="generate_text",
|
||||
description="Generate text using sampling",
|
||||
inputSchema={"type": "object"},
|
||||
execution=ToolExecution(taskSupport=TASK_REQUIRED),
|
||||
input_schema={"type": "object"},
|
||||
execution=ToolExecution(task_support=TASK_REQUIRED),
|
||||
)
|
||||
]
|
||||
|
||||
|
|
@ -710,7 +710,7 @@ async def test_scenario4_sampling_task_augmented_tool_task_augmented_sampling()
|
|||
|
||||
# Call tool as task
|
||||
create_result = await client_session.experimental.call_tool_as_task("generate_text", {})
|
||||
task_id = create_result.task.taskId
|
||||
task_id = create_result.task.task_id
|
||||
assert create_result.task.status == "working"
|
||||
|
||||
# Poll until input_required or terminal
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue