Skip to content

Commit 8b4713e

Browse files
[BREAKING] Python: Foundry hosting: parsed and durable Invocations agent runs (microsoft#8894)
* [BREAKING] Python: add parsed durable Foundry Invocations agent runs * Python: reject unsafe Invocations agent control options
1 parent e15c6dc commit 8b4713e

10 files changed

Lines changed: 1311 additions & 196 deletions

File tree

‎python/packages/foundry_hosting/README.md‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,41 @@ and user ID. Consumers must use it as a whole and must not parse it or depend on
268268
representation. Repeated requests for the same identifier pair restore the saved session.
269269
Locally, the platform session ID is used unchanged.
270270

271+
### Invocations agent requests and wire compatibility
272+
273+
By default, `POST /invocations` accepts `{"message": "Hi", "options": {}, "stream": false}`. Applications can supply
274+
a sync or async `parse_request(request)` returning a typed `InvocationRun(messages, options, stream)` to accept their
275+
own JSON shape and MAF `Message` inputs. A sync or async `prepare_options(request, options)` hook can filter or replace
276+
a **copy** of this turn's caller options without changing the agent's `default_options`. It must return a mapping
277+
with string keys. The host rejects reserved platform/session fields, `store`, `extra_body`, and private continuation
278+
fields after the hook; callers cannot select another sandbox or enable downstream service continuation through
279+
runtime options. It also rejects agent execution controls: `additional_function_arguments`, `function_invocation_kwargs`,
280+
`client_kwargs`, `middleware`, `session`, `tools`, `instructions`, `compaction_strategy`, and `tokenizer`. Trusted tool
281+
arguments belong in developer-configured agent defaults or middleware/factories, not in request options or hook output.
282+
A hook may strip denied caller fields before validation; allowed generation and provider-specific options remain
283+
available. These restrictions apply to both wire formats and every `unsupported_options` policy.
284+
When an agent cannot accept runtime options, `unsupported_options="warn"` (default) logs and ignores
285+
them; `"ignore"` silently drops them and `"error"` rejects them. For request-scoped factories, unsupported options
286+
discovered after streaming starts are reported as an SSE `error` event with `status: 400`.
287+
288+
**New default:** non-streaming success is JSON `{"response": "..."}`. Streaming success is real SSE
289+
`event: delta` with `{"text": "..."}`, followed by `event: done` with the platform sandbox `session_id`.
290+
The `done` event is sent only after the final MAF `ResponseStream` is finalized and the session is persisted.
291+
Client validation errors return JSON HTTP 400 before streaming where possible. Provider errors are logged and
292+
sanitized as JSON HTTP 500 or an SSE `error`; a cross-process ETag conflict is JSON HTTP 409 or an SSE `error`
293+
with `code: "session_conflict"` and `status: 409`. A stream may emit deltas before an error. The host serializes
294+
same-session requests in one process, but a CAS conflict can still follow external tool effects in separate
295+
processes; it does not guarantee exactly-once execution. See the
296+
[Invocations agent/parser example](../../samples/04-hosting/foundry-hosted-agents/invocations/basic/).
297+
298+
**Deprecated opt-in:** set `InvocationsHostServer(agent, legacy_wire_format=True)` only for existing clients
299+
that must keep the previous plain-text non-streaming response and raw text-chunk streaming format. The host logs
300+
and emits a deprecation warning once on construction. Errors are never returned as successful text: non-stream
301+
failures still use JSON error statuses; a post-start legacy stream failure terminates the stream rather than
302+
injecting unexpected SSE framing. Migrate all opted-in clients to JSON/SSE; remove the compatibility mode only
303+
after those callers have migrated and a separate, deliberate breaking-change decision, never by silently
304+
switching an opted-in deployment.
305+
271306
Both hosts accept `agent_session_store_provider` to select a `StoreProvider[SessionStore]`.
272307
Session state must support `AgentSession` serialization. Use `register_state_type()` codecs for
273308
custom types; unsupported live objects fail during persistence. Restored sessions preserve

‎python/packages/foundry_hosting/agent_framework_foundry_hosting/__init__.py‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
if TYPE_CHECKING:
77
from ._invocations import InvocationsHostServer
8-
from ._request import HostedResponseRequest
8+
from ._request import HostedResponseRequest, InvocationRun
99
from ._responses import ResponsesHostServer
1010
from ._scope import FoundryRequestScope
1111
from ._state_store import (
@@ -36,6 +36,7 @@
3636
"FoundryRequestScope": "._scope",
3737
"FoundryToolbox": "._toolbox",
3838
"HostedResponseRequest": "._request",
39+
"InvocationRun": "._request",
3940
"FunctionApprovalStore": "._state_store",
4041
"FunctionApprovalStoreProvider": "._state_store",
4142
"InvocationsHostServer": "._invocations",
@@ -55,6 +56,7 @@
5556
"FunctionApprovalStore",
5657
"FunctionApprovalStoreProvider",
5758
"HostedResponseRequest",
59+
"InvocationRun",
5860
"InvocationsHostServer",
5961
"ResponsesHostServer",
6062
"StoreProvider",

0 commit comments

Comments
 (0)