diff --git a/python/packages/foundry_hosting/agent_framework_foundry_hosting/_responses.py b/python/packages/foundry_hosting/agent_framework_foundry_hosting/_responses.py index ceaf8fc865..be04a1f397 100644 --- a/python/packages/foundry_hosting/agent_framework_foundry_hosting/_responses.py +++ b/python/packages/foundry_hosting/agent_framework_foundry_hosting/_responses.py @@ -1087,9 +1087,16 @@ def _convert_file_data(data_uri: str, filename: str | None = None) -> Content: header, encoded = data_uri.split(";base64,", 1) media_type = header[len("data:") :] if media_type.startswith("text/"): - decoded_text = base64.b64decode(encoded).decode("utf-8") - prefix = f"[File: {filename}]\n" if filename else "" - return Content.from_text(f"{prefix}{decoded_text}") + try: + decoded_text = base64.b64decode(encoded).decode("utf-8") + except (ValueError, UnicodeDecodeError): + logger.warning( + "Failed to decode text/* file_data as UTF-8, falling through to URI passthrough.", + exc_info=True, + ) + else: + prefix = f"[File: {filename}]\n" if filename else "" + return Content.from_text(f"{prefix}{decoded_text}") additional_properties = {"filename": filename} if filename else None return Content.from_uri(data_uri, additional_properties=additional_properties) @@ -1127,6 +1134,8 @@ def _convert_message_content(content: MessageContent) -> Content: if content.type == "input_image": image = cast(MessageContentInputImageContent, content) if image.image_url: + if image.image_url.startswith("data:"): + return Content.from_uri(image.image_url) return Content.from_uri(image.image_url, media_type="image/*") if image.file_id: return Content.from_hosted_file(image.file_id) diff --git a/python/packages/foundry_hosting/tests/test_responses.py b/python/packages/foundry_hosting/tests/test_responses.py index 60a3532ee2..e7c0599ad3 100644 --- a/python/packages/foundry_hosting/tests/test_responses.py +++ b/python/packages/foundry_hosting/tests/test_responses.py @@ -1548,6 +1548,80 @@ class TestMultiTurnMixedContent: assert messages[0].contents[1].type == "data" assert messages[0].contents[1].uri == "data:application/pdf;base64,JVBERi0xLjQ=" + async def test_text_mime_file_data_decoded(self) -> None: + """Agent receives a text/* file_data that is base64-decoded to plain text.""" + agent = _make_agent( + response=AgentResponse(messages=[Message(role="assistant", contents=[Content.from_text("Got it")])]) + ) + server = _make_server(agent) + + import base64 + + encoded = base64.b64encode(b"Hello, world!").decode() + + resp = await _post_json( + server, + { + "model": "test-model", + "input": [ + { + "type": "message", + "role": "user", + "content": [ + { + "type": "input_file", + "file_data": f"data:text/plain;base64,{encoded}", + "filename": "greeting.txt", + }, + ], + } + ], + "stream": False, + }, + ) + + assert resp.status_code == 200 + + messages = agent.run.call_args.kwargs["messages"] + assert len(messages) == 1 + assert messages[0].contents[0].type == "text" + assert messages[0].contents[0].text == "[File: greeting.txt]\nHello, world!" + + async def test_text_mime_file_data_invalid_base64_falls_through(self) -> None: + """Invalid base64 in a text/* file_data falls through to URI passthrough.""" + agent = _make_agent( + response=AgentResponse(messages=[Message(role="assistant", contents=[Content.from_text("Got it")])]) + ) + server = _make_server(agent) + + resp = await _post_json( + server, + { + "model": "test-model", + "input": [ + { + "type": "message", + "role": "user", + "content": [ + { + "type": "input_file", + "file_data": "data:text/plain;base64,!!!invalid!!!", + "filename": "bad.txt", + }, + ], + } + ], + "stream": False, + }, + ) + + assert resp.status_code == 200 + + messages = agent.run.call_args.kwargs["messages"] + assert len(messages) == 1 + assert messages[0].contents[0].type == "data" + assert messages[0].contents[0].uri == "data:text/plain;base64,!!!invalid!!!" + async def test_mixed_text_and_image_input(self) -> None: """Agent receives a single message with both text and image content.""" agent = _make_agent( diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/01_basic/README.md b/python/samples/04-hosting/foundry-hosted-agents/responses/01_basic/README.md index 7081a581e9..dc1883778f 100644 --- a/python/samples/04-hosting/foundry-hosted-agents/responses/01_basic/README.md +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/01_basic/README.md @@ -18,7 +18,7 @@ The agent is hosted using the [Agent Framework](https://github.com/microsoft/age > Depending on how you run the agent host, you can invoke the agent using `curl` (`Invoke-WebRequest` in PowerShell) or `azd`. Please refer to the [parent README](../../README.md) for more details. Use this README for sample queries you can send to the agent. -Send a POST request to the server with a JSON body containing a "message" field to interact with the agent. For example: +Send a POST request to the server with a JSON body containing an `"input"` field to interact with the agent. For example: ```bash curl -X POST http://localhost:8088/responses -H "Content-Type: application/json" -d '{"input": "Hi"}' diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/02_tools/README.md b/python/samples/04-hosting/foundry-hosted-agents/responses/02_tools/README.md index e08eaf98f3..e82296c966 100644 --- a/python/samples/04-hosting/foundry-hosted-agents/responses/02_tools/README.md +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/02_tools/README.md @@ -22,7 +22,7 @@ Follow the instructions in the [Running the Agent Host Locally](../../README.md# > Depending on how you run the agent host, you can invoke the agent using `curl` (`Invoke-WebRequest` in PowerShell) or `azd`. Please refer to the [parent README](../../README.md) for more details. Use this README for sample queries you can send to the agent. -Send a POST request to the server with a JSON body containing a "message" field to interact with the agent. For example: +Send a POST request to the server with a JSON body containing an `"input"` field to interact with the agent. For example: ```bash curl -X POST http://localhost:8088/responses -H "Content-Type: application/json" -d '{"input": "What is the weather in Seattle?"}' diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/03_mcp/README.md b/python/samples/04-hosting/foundry-hosted-agents/responses/03_mcp/README.md index ac43d4f1df..b8b2bc137d 100644 --- a/python/samples/04-hosting/foundry-hosted-agents/responses/03_mcp/README.md +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/03_mcp/README.md @@ -22,7 +22,7 @@ Follow the instructions in the [Running the Agent Host Locally](../../README.md# > Depending on how you run the agent host, you can invoke the agent using `curl` (`Invoke-WebRequest` in PowerShell) or `azd`. Please refer to the [parent README](../../README.md) for more details. Use this README for sample queries you can send to the agent. -Send a POST request to the server with a JSON body containing a "message" field to interact with the agent. For example: +Send a POST request to the server with a JSON body containing an `"input"` field to interact with the agent. For example: ```bash curl -X POST http://localhost:8088/responses -H "Content-Type: application/json" -d '{"input": "List all the repositories I own on GitHub."}' diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/04_foundry_toolbox/README.md b/python/samples/04-hosting/foundry-hosted-agents/responses/04_foundry_toolbox/README.md index d3358cdc04..c7f8721d5c 100644 --- a/python/samples/04-hosting/foundry-hosted-agents/responses/04_foundry_toolbox/README.md +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/04_foundry_toolbox/README.md @@ -32,7 +32,7 @@ Follow the instructions in the [Running the Agent Host Locally](../../README.md# > Depending on how you run the agent host, you can invoke the agent using `curl` (`Invoke-WebRequest` in PowerShell) or `azd`. Please refer to the [parent README](../../README.md) for more details. Use this README for sample queries you can send to the agent. -Send a POST request to the server with a JSON body containing a "message" field to interact with the agent. For example: +Send a POST request to the server with a JSON body containing an `"input"` field to interact with the agent. For example: ```bash curl -X POST http://localhost:8088/responses -H "Content-Type: application/json" -d '{"input": "What tools do you have?"}' diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/05_workflows/README.md b/python/samples/04-hosting/foundry-hosted-agents/responses/05_workflows/README.md index fe1228f5a1..608d1a564e 100644 --- a/python/samples/04-hosting/foundry-hosted-agents/responses/05_workflows/README.md +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/05_workflows/README.md @@ -26,7 +26,7 @@ Follow the instructions in the [Running the Agent Host Locally](../../README.md# > Depending on how you run the agent host, you can invoke the agent using `curl` (`Invoke-WebRequest` in PowerShell) or `azd`. Please refer to the [parent README](../../README.md) for more details. Use this README for sample queries you can send to the agent. -Send a POST request to the server with a JSON body containing a "message" field to interact with the agent. For example: +Send a POST request to the server with a JSON body containing an `"input"` field to interact with the agent. For example: ```bash curl -X POST http://localhost:8088/responses -H "Content-Type: application/json" -d '{"input": "Create a slogan for a new electric SUV that is affordable and fun to drive."}'