mirror of
https://github.com/microsoft/semantic-kernel.git
synced 2026-10-12 02:19:06 +00:00
Python: Update sessions python plugin to work with the correct endpoints (#7070)
### Motivation and Context The Sessions Python Plugin was working properly, but some users reported that the endpoints needed updating. We had made previous updates to the plugin, but they didn't account for the update API endpoints and request body changes shown [here](https://learn.microsoft.com/en-us/azure/container-apps/sessions-code-interpreter). <!-- Thank you for your contribution to the semantic-kernel repo! Please help reviewers and future users, providing the following information: 1. Why is this change required? 2. What problem does it solve? 3. What scenario does it contribute to? 4. If it fixes an open issue, please link to the issue here. --> ### Description This PR updates the plugin tool to work with the documented endpoints and request bodies. The unit tests were also updated. The concept sample for the Azure Python sessions plugin was tested with all methods to: execute code, upload multiple files, list files, and download them. - Additionally, improve error handling for each of the function calls so the proper error message is surfaced to the user via SK exceptions. - Fix path building and making sure the sessions endpoint is converted to a string from the Pydantic https type. - Closes #6934 <!-- Describe your changes, the overall approach, the underlying design. These notes will help understanding how your code works. Thanks! --> ### Contribution Checklist <!-- Before submitting this PR, please make sure: --> - [X] The code builds clean without any errors or warnings - [X] The PR follows the [SK Contribution Guidelines](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md) and the [pre-submission formatting script](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md#development-scripts) raises no violations - [X] All unit tests pass, and I have added new tests where possible - [ ] I didn't break anyone 😄
This commit is contained in:
@@ -76,10 +76,22 @@ async def test_call_to_container_succeeds(mock_post, aca_python_sessions_unit_te
|
||||
"semantic_kernel.core_plugins.sessions_python_tool.sessions_python_plugin.SessionsPythonTool._ensure_auth_token",
|
||||
return_value="test_token",
|
||||
):
|
||||
mock_request = httpx.Request(method="POST", url="https://example.com/python/execute/")
|
||||
mock_request = httpx.Request(method="POST", url="https://example.com/code/execute/")
|
||||
|
||||
mock_response = httpx.Response(
|
||||
status_code=200, json={"result": "success", "stdout": "", "stderr": ""}, request=mock_request
|
||||
status_code=200,
|
||||
json={
|
||||
"$id": "1",
|
||||
"properties": {
|
||||
"$id": "2",
|
||||
"status": "Success",
|
||||
"stdout": "",
|
||||
"stderr": "",
|
||||
"result": "even_numbers = [2 * i for i in range(1, 11)]\\nprint(even_numbers)",
|
||||
"executionTimeInMilliseconds": 12,
|
||||
},
|
||||
},
|
||||
request=mock_request,
|
||||
)
|
||||
|
||||
mock_post.return_value = await async_return(mock_response)
|
||||
@@ -101,7 +113,7 @@ async def test_call_to_container_fails_raises_exception(mock_post, aca_python_se
|
||||
"semantic_kernel.core_plugins.sessions_python_tool.sessions_python_plugin.SessionsPythonTool._ensure_auth_token",
|
||||
return_value="test_token",
|
||||
):
|
||||
mock_request = httpx.Request(method="POST", url="https://example.com/python/execute/")
|
||||
mock_request = httpx.Request(method="POST", url="https://example.com/code/execute/")
|
||||
|
||||
mock_response = httpx.Response(status_code=500, request=mock_request)
|
||||
|
||||
@@ -135,19 +147,22 @@ async def test_upload_file_with_local_path(mock_post, aca_python_sessions_unit_t
|
||||
),
|
||||
patch("builtins.open", mock_open(read_data=b"file data")),
|
||||
):
|
||||
mock_request = httpx.Request(method="POST", url="https://example.com/python/uploadFile?identifier=None")
|
||||
mock_request = httpx.Request(method="POST", url="https://example.com/files/upload?identifier=None")
|
||||
|
||||
mock_response = httpx.Response(
|
||||
status_code=200,
|
||||
json={
|
||||
"$id": "1",
|
||||
"$values": [
|
||||
"value": [
|
||||
{
|
||||
"$id": "2",
|
||||
"filename": "test.txt",
|
||||
"size": 123,
|
||||
"last_modified_time": "2024-06-03T17:48:46.2672398Z",
|
||||
}
|
||||
"properties": {
|
||||
"$id": "3",
|
||||
"filename": "hello.py",
|
||||
"size": 123,
|
||||
"lastModifiedTime": "2024-07-02T19:29:23.4369699Z",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
request=mock_request,
|
||||
@@ -159,10 +174,10 @@ async def test_upload_file_with_local_path(mock_post, aca_python_sessions_unit_t
|
||||
env_file_path="test.env",
|
||||
)
|
||||
|
||||
result = await plugin.upload_file(local_file_path="test.txt", remote_file_path="uploaded_test.txt")
|
||||
assert result.filename == "test.txt"
|
||||
result = await plugin.upload_file(local_file_path="hello.py", remote_file_path="hello.py")
|
||||
assert result.filename == "hello.py"
|
||||
assert result.size_in_bytes == 123
|
||||
assert result.full_path == "/mnt/data/test.txt"
|
||||
assert result.full_path == "/mnt/data/hello.py"
|
||||
mock_post.assert_awaited_once()
|
||||
|
||||
|
||||
@@ -181,19 +196,22 @@ async def test_upload_file_with_local_path_and_no_remote(mock_post, aca_python_s
|
||||
),
|
||||
patch("builtins.open", mock_open(read_data=b"file data")),
|
||||
):
|
||||
mock_request = httpx.Request(method="POST", url="https://example.com/python/uploadFile?identifier=None")
|
||||
mock_request = httpx.Request(method="POST", url="https://example.com/files/upload?identifier=None")
|
||||
|
||||
mock_response = httpx.Response(
|
||||
status_code=200,
|
||||
json={
|
||||
"$id": "1",
|
||||
"$values": [
|
||||
"value": [
|
||||
{
|
||||
"$id": "2",
|
||||
"filename": "test.txt",
|
||||
"size": 123,
|
||||
"last_modified_time": "2024-06-03T17:00:00.0000000Z",
|
||||
}
|
||||
"properties": {
|
||||
"$id": "3",
|
||||
"filename": "hello.py",
|
||||
"size": 123,
|
||||
"lastModifiedTime": "2024-07-02T19:29:23.4369699Z",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
request=mock_request,
|
||||
@@ -205,8 +223,8 @@ async def test_upload_file_with_local_path_and_no_remote(mock_post, aca_python_s
|
||||
env_file_path="test.env",
|
||||
)
|
||||
|
||||
result = await plugin.upload_file(local_file_path="test.txt")
|
||||
assert result.filename == "test.txt"
|
||||
result = await plugin.upload_file(local_file_path="hello.py")
|
||||
assert result.filename == "hello.py"
|
||||
assert result.size_in_bytes == 123
|
||||
mock_post.assert_awaited_once()
|
||||
|
||||
@@ -235,19 +253,22 @@ async def test_upload_file_with_buffer(
|
||||
),
|
||||
patch("builtins.open", mock_open(read_data="print('hello, world~')")),
|
||||
):
|
||||
mock_request = httpx.Request(method="POST", url="https://example.com/python/uploadFile?identifier=None")
|
||||
mock_request = httpx.Request(method="POST", url="https://example.com/files/upload?identifier=None")
|
||||
|
||||
mock_response = httpx.Response(
|
||||
status_code=200,
|
||||
json={
|
||||
"$id": "1",
|
||||
"$values": [
|
||||
"value": [
|
||||
{
|
||||
"$id": "2",
|
||||
"filename": expected_remote_file_path,
|
||||
"size": 456,
|
||||
"last_modified_time": "2024-06-03T17:00:00.0000000Z",
|
||||
}
|
||||
"properties": {
|
||||
"$id": "3",
|
||||
"filename": expected_remote_file_path,
|
||||
"size": 456,
|
||||
"lastModifiedTime": "2024-07-02T19:29:23.4369699Z",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
request=mock_request,
|
||||
@@ -286,25 +307,31 @@ async def test_list_files(mock_get, aca_python_sessions_unit_test_env):
|
||||
"semantic_kernel.core_plugins.sessions_python_tool.sessions_python_plugin.SessionsPythonTool._ensure_auth_token",
|
||||
return_value="test_token",
|
||||
):
|
||||
mock_request = httpx.Request(method="GET", url="https://example.com/python/files?identifier=None")
|
||||
mock_request = httpx.Request(method="GET", url="https://example.com/files?identifier=None")
|
||||
|
||||
mock_response = httpx.Response(
|
||||
status_code=200,
|
||||
json={
|
||||
"$id": "1",
|
||||
"$values": [
|
||||
"value": [
|
||||
{
|
||||
"$id": "2",
|
||||
"filename": "test1.txt",
|
||||
"size": 123,
|
||||
"last_modified_time": "2024-06-03T17:00:00.0000000Z",
|
||||
}, # noqa: E501
|
||||
"properties": {
|
||||
"$id": "3",
|
||||
"filename": "hello.py",
|
||||
"size": 123,
|
||||
"lastModifiedTime": "2024-07-02T19:29:23.4369699Z",
|
||||
},
|
||||
},
|
||||
{
|
||||
"$id": "3",
|
||||
"filename": "test2.txt",
|
||||
"size": 456,
|
||||
"last_modified_time": "2024-06-03T18:00:00.0000000Z",
|
||||
}, # noqa: E501
|
||||
"$id": "4",
|
||||
"properties": {
|
||||
"$id": "5",
|
||||
"filename": "world.py",
|
||||
"size": 456,
|
||||
"lastModifiedTime": "2024-07-02T19:29:38.1329088Z",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
request=mock_request,
|
||||
@@ -315,9 +342,9 @@ async def test_list_files(mock_get, aca_python_sessions_unit_test_env):
|
||||
|
||||
files = await plugin.list_files()
|
||||
assert len(files) == 2
|
||||
assert files[0].filename == "test1.txt"
|
||||
assert files[0].filename == "hello.py"
|
||||
assert files[0].size_in_bytes == 123
|
||||
assert files[1].filename == "test2.txt"
|
||||
assert files[1].filename == "world.py"
|
||||
assert files[1].size_in_bytes == 456
|
||||
mock_get.assert_awaited_once()
|
||||
|
||||
@@ -341,7 +368,8 @@ async def test_download_file_to_local(mock_get, aca_python_sessions_unit_test_en
|
||||
patch("builtins.open", mock_open()) as mock_file,
|
||||
):
|
||||
mock_request = httpx.Request(
|
||||
method="GET", url="https://example.com/python/downloadFile?identifier=None&filename=remote_test.txt"
|
||||
method="GET",
|
||||
url="https://example.com/python/files/content/remote_text.txt?identifier=None&filename=remote_test.txt",
|
||||
)
|
||||
|
||||
mock_response = httpx.Response(status_code=200, content=b"file data", request=mock_request)
|
||||
@@ -352,7 +380,7 @@ async def test_download_file_to_local(mock_get, aca_python_sessions_unit_test_en
|
||||
env_file_path="test.env",
|
||||
)
|
||||
|
||||
await plugin.download_file(remote_file_path="remote_test.txt", local_file_path="local_test.txt")
|
||||
await plugin.download_file(remote_file_name="remote_test.txt", local_file_path="local_test.txt")
|
||||
mock_get.assert_awaited_once()
|
||||
mock_file.assert_called_once_with("local_test.txt", "wb")
|
||||
mock_file().write.assert_called_once_with(b"file data")
|
||||
@@ -374,7 +402,8 @@ async def test_download_file_to_buffer(mock_get, aca_python_sessions_unit_test_e
|
||||
return_value="test_token",
|
||||
):
|
||||
mock_request = httpx.Request(
|
||||
method="GET", url="https://example.com/python/downloadFile?identifier=None&filename=remote_test.txt"
|
||||
method="GET",
|
||||
url="https://example.com/files/content/remote_test.txt?identifier=None&filename=remote_test.txt",
|
||||
)
|
||||
|
||||
mock_response = httpx.Response(status_code=200, content=b"file data", request=mock_request)
|
||||
@@ -382,7 +411,7 @@ async def test_download_file_to_buffer(mock_get, aca_python_sessions_unit_test_e
|
||||
|
||||
plugin = SessionsPythonTool(auth_callback=mock_auth_callback)
|
||||
|
||||
buffer = await plugin.download_file(remote_file_path="remote_test.txt")
|
||||
buffer = await plugin.download_file(remote_file_name="remote_test.txt")
|
||||
assert buffer is not None
|
||||
assert buffer.read() == b"file data"
|
||||
mock_get.assert_awaited_once()
|
||||
|
||||
Reference in New Issue
Block a user