Skip to content

[Python] Durable thread compaction and history providers (ADR 0032) - #59

Open
Ahmed Muhsin (ahmedmuhsin) wants to merge 69 commits into
mainfrom
python/durable-thread-compaction
Open

[Python] Durable thread compaction and history providers (ADR 0032)#59
Ahmed Muhsin (ahmedmuhsin) wants to merge 69 commits into
mainfrom
python/durable-thread-compaction

Conversation

@ahmedmuhsin

@ahmedmuhsin Ahmed Muhsin (ahmedmuhsin) commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Summary

Prototype only. This PR is not intended to merge as-is.

Integrated Python reference for the durable history, retention and workflow design under review in #88. It keeps the runtime changes, regression tests and samples together for end-to-end evaluation.

Design review belongs in ADR PR #88. After the ADR is approved, the agreed changes will be submitted as focused, stacked implementation PRs. This PR remains the integrated reference until that stack lands.

The prototype includes provisional breaking state and deployment changes. It is not a drop-in upgrade and does not implement .NET parity.

Related to #4.

Adds DurableHistoryProvider, a core HistoryProvider whose store is the agent's durable entity state. Because it is an ordinary provider, a CompactionProvider configured the normal way runs against durable history unchanged.

Compaction is reconciled by message id rather than by position, since strategies may insert messages (summaries) as well as annotate them. That required persisting message ids and making DurableAgentStateMessage serialization symmetric: extension_data was read on load but silently dropped on save, so compaction annotations were destroyed on every turn.

The ADR records the core interface gaps found while doing this.
The '!python/packages/**' negation earlier in the file un-ignored everything beneath it, so the integration test .env files holding endpoints and credentials were staged by a plain 'git add'. A trailing '**/.env' rule wins over that negation; .env.example templates stay tracked.
…session id

Two ways an agent that works in core could silently lose its conversation under the durable runtime, both failing without an error:

- Ownership of history was decided from the chat client's STORES_BY_DEFAULT alone. Core's rule is that an explicit 'store' in the agent's options wins, so an agent using the Responses API with store=False kept a plain in-memory provider that the durable runtime never persists.

- The entity built its per-operation session without an id, so core generated a fresh one each turn. External history providers (Cosmos, Redis, file) key their storage on session.session_id and were therefore reading and writing a different key on every turn.
…ADR 0032

Documents the two rules the fixes above depend on (store precedence over STORES_BY_DEFAULT, and stable session ids for external providers), and restores the entity lifetime/TTL section. TTL is the natural sibling of the retention setting this ADR introduces - the retention rationale already refers to it - and the .NET/Python parity gap it describes belongs in this repository.
…mpaction

Three samples, each showing an agent configured the ordinary core way running durably with no changes: compaction on the standalone worker (13) and on Azure Functions (14), and a user-owned external history store (14, Redis).

The Redis sample defines its own small provider rather than depending on agent-framework-redis, whose only release is a beta that no longer imports against current core.

Integration coverage asserts against real storage: compaction annotations and message ids survive entity serialization, an external provider keeps the whole conversation under one key, and a downstream workflow agent can reference the upstream conversation. Existing continuity tests were strengthened to assert recall rather than a bare 200.
Core documents the per-provider 'state' dict handed to before_run/after_run as durable for the life of the session and persists it through AgentSession.to_dict(). The entity built a fresh session per operation, so everything providers kept there was discarded at the end of every turn: tool approval rules and queued approval requests, todo lists, background-task state, memory extraction state. Nothing failed - agents just silently started over.

That is a poor fit for a runtime whose headline scenario is long-running human-in-the-loop, where an approval flow that spans turns cannot work if the pending requests are dropped between them.

The entity now persists the whole serialized session instead of individual fields, which also removes the hand-rolled serviceSessionId state field and its capture/restore helpers - that id is already part of AgentSession.to_dict(). The durable history provider's own slice is excluded, since it is derived from conversationHistory and would otherwise duplicate the transcript.

Restore applies the stored state onto a session built by the agent's own create_session(), preserving its session type.

Known limitation, recorded in the ADR: core's state type registry is process-local and only pre-registers Message, so to_dict-based values come back as plain data rather than their original class. Core's own state is mostly plain data and its tool-approval accessor takes either form, so this is latent; the fix belongs in core.
Core deserializes session state through a type registry it seeds with exactly one entry (Message); anything else must be registered explicitly, and the registry is process-local. to_dict-based types are never auto-registered - only Pydantic models are, and only as a side effect of serializing. A durable entity routinely restores in a process that never serialized the value, so provider state came back as plain dicts instead of its own classes.

Before restoring, the entity now registers the serializable types already loaded in the process. Nothing is imported from persisted data, so this cannot load code the application has not already loaded itself, and that is sufficient in practice: whoever put a value in the state bag had to import its class to construct it. The walk covers SerializationMixin subclasses and costs tens of microseconds.

Pydantic values in state remain uncovered (they are keyed by class name and walking every BaseModel subclass would be broad and collision-prone). Core seeding the registry with the types it ships would make this unnecessary - register_state_type() is already public and documents cold-start restore as its motivating case.
The gaps section claimed compaction 'bypasses the provider', which overstates it and would not survive review. Only one of CompactionProvider's two hooks is coupled to session state: before_strategy acts on the loaded invocation context and already works for every provider, so external stores do get in-run context bounding. What they do not get is the framework rewriting their store.

Whether that is a defect depends on who owns the store - not rewriting a user's Cosmos container is defensible, but durable entity state is framework-owned, which is what makes it a real problem here rather than a reasonable omission.

It is also unresolved rather than decided: ADR-0019 names three compaction points, scopes in Redis and Cosmos, and leaves the mechanism as an explicit open question that shipped unanswered. The languages then diverged - .NET put store reduction on the provider (IChatReducer, InMemory only; Cosmos has none), Python put it in CompactionProvider reaching into session state - and neither offers it to external providers.

Also corrects the knock-on claims elsewhere in the ADR that both core hooks apply 'unchanged', since L2 in fact carries workaround code, and cross-references the two gaps recorded in other sections.
These gaps are being followed up rather than fixed, so the ADR has to be the durable record. Three were under-captured:

- The per-service-call cadence split was only ever discussed, never written down. Added as gap 4: history providers move to per-model-call while CompactionProvider stays per-run, so compaction annotates after the last flush. Latent (HarnessAgent only), but the symptom would be missing annotations rather than an error.

- The .NET parity note said 'add extension data', which is misleading. .NET already has an ExtensionData property, but it is [JsonExtensionData] - the JSON overflow bucket, not a mapping of ChatMessage.AdditionalProperties. Annotations are lost at the conversion boundary, and MessageId does not exist at all. Anyone auditing for 'is extension data persisted?' would see the property and wrongly close the item.

- Recorded that the store-precedence rule is re-derived here because core does not expose it, that drift would present as silent conversation loss, and that the only real net is the compaction sample rather than the unit tests.
The ADR had grown into two documents in one hat: a forward-looking design decision in the present tense, followed by a retrospective implementation log, with no signal where one ended and the other began. Adds a short orientation note, marks the status accepted (Python implemented, .NET pending), and fixes the Context section's claim that the durable layer benefits from neither hook 'today' - no longer true.

Also notes once that .NET's ChatHistoryProvider and Python's HistoryProvider are the same concept, since the decision sections use one name and the implementation sections the other.

Deduplication: service-managed scope was stated four times and storage-capacity-is-separate five; each now has one home plus pointers. The per-option pros/cons lists restated Decision Outcome almost verbatim and are now one entry per option. Three Cross-Cutting bullets that repeated the drivers and the L1/L2 table are gone, as is the 'Why Option 6 over Option 2' paragraph now covered by the options summary.

Validation was written as intent; it now separates what is actually covered in Python from what is still outstanding, so the .NET gap is visible rather than implied.

549 -> 504 lines, 5267 -> 4877 words, with no information removed.
Punctuation pass over the material added on this branch: the ADR, the three new sample READMEs, and the docstrings, comments and log messages in the new provider, entity and sample code. Em dashes become commas, parentheses or sentence breaks, and semicolons joining independent clauses become separate sentences. Colons are kept only where they label something (Args, Returns, 'Chosen option', 'Workaround') rather than standing in for a conjunction.

Pre-existing text is left alone, so the em dashes still in _models.py, _workflows/context.py, _workflows/orchestrator.py, tests/test_app.py and samples/README.md are untouched - none of those lines are from this branch, and rewriting them would add unrelated churn.

Also fixes an indentation slip introduced while editing a comment in _history_provider.py.
The decision has not been accepted yet, so status goes back to proposed. Deciders and consulted are left blank rather than naming people who have not signed off.

Also reverts one word in the orientation note: it said the design was 'realized' in Python, which implied a settled decision. It is a prototype, which is how the rest of the ADR already describes it.
…hat could not fail

The session state bag was the one change on this branch verified only in-process. Its unit tests keep the session dict in memory, so they cannot show that the blob survives the entity's JSON encoding, that it carries the entity's own session id rather than a per-operation one, or that the durable history slice is really left out. A new test in test_13 reads the entity back from the scheduler and asserts all three against the real payload.

Two pre-existing tests were passing regardless of behavior:

- test_06 test_conditional_branching scheduled one spam email and asserted only that the orchestration COMPLETED, never which branch ran, so it would pass if the condition sent every email down the same path. It now asserts the branch-specific output and covers the legitimate branch too, which a stale comment implied was once intended.

- test_07 test_hitl_orchestration_timeout wrapped the wait in 'except (RuntimeError, TimeoutError): pass'. Since the shared helper raises on FAILED, its assert was unreachable and the test passed on every outcome including a hung orchestration. It now waits on the client directly and asserts the run failed with an approval timeout rather than for some other reason.
Copilot AI lite review requested due to automatic review settings July 31, 2026 12:16

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Implements ADR 0032 for Python by making durable agents/workflows reuse core conversation-history + compaction plumbing (via a durable-backed HistoryProvider), persisting per-session provider state across turns, and forwarding upstream workflow context to downstream agent nodes for parity with in-process execution.

Changes:

  • Add DurableHistoryProvider + automatic history-provider substitution so core compaction runs unchanged against durable entity-backed history (with opt-in prune_history retention).
  • Persist serialized AgentSession (provider state + service conversation id) in durable entity state across turns, excluding the durable history slice to avoid transcript duplication.
  • Add workflow context projection (context_mode / context_filter) into RunRequest.context_messages, plus new/updated unit + integration tests and samples.

Reviewed changes

Copilot reviewed 50 out of 51 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
schemas/durable-agent-entity-state.json Extends durable agent state schema to include persisted serialized session payload.
python/samples/README.md Documents new conversation-history/compaction samples.
python/samples/azure_functions/14_conversation_compaction/requirements.txt Dependencies for the new Azure Functions compaction sample.
python/samples/azure_functions/14_conversation_compaction/README.md Explains durable-backed history + compaction behavior for the Functions sample.
python/samples/azure_functions/14_conversation_compaction/local.settings.json.template Local settings template for the Functions compaction sample.
python/samples/azure_functions/14_conversation_compaction/host.json Durable Functions host configuration for the sample.
python/samples/azure_functions/14_conversation_compaction/function_app.py Functions sample wiring demonstrating durable-backed history + compaction.
python/samples/azure_functions/14_conversation_compaction/demo.http Ready-made HTTP sequence for exercising compaction behavior.
python/samples/14_external_history_redis/worker.py New sample worker hosting an agent whose history is stored in Redis.
python/samples/14_external_history_redis/sample.py Combined worker+client runner for the external Redis history sample.
python/samples/14_external_history_redis/requirements.txt Dependencies for the external Redis history sample.
python/samples/14_external_history_redis/redis_history_provider.py Minimal sample HistoryProvider implementation backed by Redis.
python/samples/14_external_history_redis/README.md Explains external-store history behavior under the durable runtime.
python/samples/14_external_history_redis/client.py Sample client that demonstrates recall via Redis-backed history.
python/samples/14_external_history_redis/.env.example Environment template for the Redis history sample.
python/samples/13_conversation_compaction/worker.py New durabletask sample worker for durable-backed history + compaction.
python/samples/13_conversation_compaction/sample.py Combined worker+client runner for the compaction sample.
python/samples/13_conversation_compaction/requirements.txt Dependencies for the durabletask compaction sample.
python/samples/13_conversation_compaction/README.md Explains durable-backed history + compaction semantics for durabletask.
python/samples/13_conversation_compaction/client.py Sample client validating bounded context + recent recall.
python/samples/13_conversation_compaction/.env.example Environment template for the durabletask compaction sample.
python/packages/durabletask/tests/test_workflow_context_parity.py Unit tests for projecting upstream workflow conversation into downstream runs.
python/packages/durabletask/tests/test_durable_history_provider.py Unit tests for durable-backed history provider + compaction persistence/pruning.
python/packages/durabletask/tests/test_durable_history_autoswap.py Unit tests for auto-swapping history providers without mutating the user agent.
python/packages/durabletask/tests/integration_tests/test_14_dt_external_history_redis.py Integration tests for external-store history continuity + stable session id.
python/packages/durabletask/tests/integration_tests/test_13_dt_conversation_compaction.py Integration tests for durable compaction behavior + session persistence shape.
python/packages/durabletask/tests/integration_tests/test_08_dt_workflow.py Adds assertion that downstream workflow agents receive upstream conversation.
python/packages/durabletask/tests/integration_tests/test_07_dt_single_agent_orchestration_hitl.py Fixes HITL timeout test to assert failure reason instead of swallowing errors.
python/packages/durabletask/tests/integration_tests/test_06_dt_multi_agent_orchestration_conditionals.py Strengthens conditional-branch assertions to validate correct branch output.
python/packages/durabletask/tests/integration_tests/test_01_dt_single_agent.py Makes conversation-continuity test actually depend on persisted history recall.
python/packages/durabletask/agent_framework_durabletask/_workflows/orchestrator.py Adds L3 context projection into agent tasks via context_messages.
python/packages/durabletask/agent_framework_durabletask/_workflows/dt_context.py Routes agent-task creation through shared build_agent_task helper.
python/packages/durabletask/agent_framework_durabletask/_workflows/context.py Extends orchestration-context protocol to accept optional context_messages.
python/packages/durabletask/agent_framework_durabletask/_worker.py Adds prune_history defaults/overrides when registering agents as entities.
python/packages/durabletask/agent_framework_durabletask/_shim.py Introduces build_agent_task + forwards optional context_messages to executors.
python/packages/durabletask/agent_framework_durabletask/_models.py Adds RunRequest.context_messages wire field with (de)serialization.
python/packages/durabletask/agent_framework_durabletask/_history_provider.py New durable-backed HistoryProvider + auto-swap logic and compaction reconciliation.
python/packages/durabletask/agent_framework_durabletask/_executors.py Extends run-request construction to carry orchestration id + optional context messages.
python/packages/durabletask/agent_framework_durabletask/_entities.py Switches entity execution to use core context pipeline + persists session + dedupes upstream context.
python/packages/durabletask/agent_framework_durabletask/_durable_agent_state.py Adds persisted session + message id + extension metadata to durable state entries/messages.
python/packages/durabletask/agent_framework_durabletask/_constants.py Adds new durable state field constants for message id + session.
python/packages/durabletask/agent_framework_durabletask/init.py Exports newly added durable history + task-building utilities.
python/packages/azurefunctions/tests/test_app.py Updates tests for entity creation signature to include prune_history.
python/packages/azurefunctions/tests/integration_tests/test_14_conversation_compaction.py New integration coverage for Functions-hosted durable compaction sample.
python/packages/azurefunctions/tests/integration_tests/test_01_single_agent.py Strengthens Functions continuity test to require history-based recall.
python/packages/azurefunctions/agent_framework_azurefunctions/_workflow_af_context.py Uses shared build_agent_task and updates protocol signature for context messages.
python/packages/azurefunctions/agent_framework_azurefunctions/_orchestration.py Aligns orchestration id propagation via executor hook instead of overriding get_run_request.
python/packages/azurefunctions/agent_framework_azurefunctions/_entities.py Adds prune_history option when creating agent entities.
python/packages/azurefunctions/agent_framework_azurefunctions/_app.py Adds app-level + per-agent prune_history plumbing through entity setup.
docs/decisions/0032-durable-thread-compaction.md Adds ADR 0032 describing the approach and Python prototype notes.
.gitignore Ignores .env files repo-wide while keeping .env.example tracked.

Comment thread python/packages/durabletask/agent_framework_durabletask/_entities.py Outdated
CI type-checks tests with mypy in addition to ruff and pyright. I ran the other two locally but not mypy, so all four Python jobs failed on the first push.

Most errors were stub clients and stub agents passed where the full client or agent protocol is expected. Rather than scattering per-call-site ignores, each affected test file now builds its agent through a small helper that relaxes the type once. That also removed some duplicated construction.

Two were real rather than cosmetic. test_durable_history_provider instantiated the abstract HistoryProvider directly, which now uses a concrete stub, and test_durabletask_workflow_initial_input had a context stub whose prepare_agent_task predated the context_messages parameter this branch adds to the protocol.

The remaining local mypy error is in integration_tests/conftest.py and comes from redis typing in my environment. CI does not report it, and the file is untouched here.
Copilot AI review requested due to automatic review settings July 31, 2026 12:50
redis-py types lrange differently depending on version, so annotating the result as list[str] passed locally and failed on CI with list[bytes | str]. The helper now takes the result loosely and coerces each entry, which holds either way.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 51 out of 52 changed files in this pull request and generated 1 comment.

Copilot AI review requested due to automatic review settings July 31, 2026 12:56

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 51 out of 52 changed files in this pull request and generated 1 comment.

Suppressed comments (2)

python/packages/durabletask/agent_framework_durabletask/_history_provider.py:270

  • The stored_by_id map stores (entry, index) positions, but _insert_new_message() mutates entry.messages during the same flush. After an insertion, indices for later messages in that entry become stale, so entry.messages[index] can point at the wrong message (potentially overwriting annotations or pruning the wrong item). Storing direct message references (or re-resolving by message_id after insertions) would avoid index drift.
            entry, index = position
            stored = entry.messages[index]
            stored.extension_data = annotations
            last_known = position
            if self.prune_excluded and annotations and annotations.get(EXCLUDED_KEY):

python/samples/azure_functions/14_conversation_compaction/function_app.py:81

  • This trailing triple-quoted string is an unused string literal (not a docstring), so it has no effect and adds dead code. Convert it to comments or fold it into the module docstring at the top of the file.

Comment thread python/packages/durabletask/agent_framework_durabletask/_history_provider.py Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

It introduces broad, cross-cutting runtime behavior changes (durable state schema, entity execution semantics, retention/eviction, and workflow context delivery) that warrant final human validation despite strong test coverage.

Review details

Suppressed comments (1)

Previously missed (1) — in code that hasn't changed since the last review.

python/packages/durabletask/agent_framework_durabletask/_history_provider.py:554

  • The warning message in the ensure_durable_history() exception path is misleading: returning the unmodified agent does not necessarily mean the entity will "replay persisted history" (e.g., agents with a context pipeline but without a durable history swap will continue using their existing providers, and may not get entity-replayed context at all). This makes troubleshooting harder because logs describe a fallback that may not actually occur.
  • Files reviewed: 58/59 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

@larohra Laveesh Rohra (larohra) left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fresh reassessment findings; see the six inline comments.

origins: list[tuple[DurableAgentStateEntry, DurableAgentStateMessage]] = []
evictable_bytes = 0
protected = _protected_entries(history, honor_delivery_window=honor_delivery_window)
for entry, index in replayable_entries(history):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we separate "replayable to the model" from "evictable from storage" here? replayable_entries() deliberately skips DurableAgentStateErrorResponse, so old failed responses never become retention candidates. With 80 one-hour-old error responses, enforce_budget(..., 12_000) removed 0 and left about 56 KB. Please allow expired failed-turn entries to be evicted while continuing to exclude them from model context.

state: dict[str, Any],
) -> None:
"""Load durable history into context, unless the service owns the conversation."""
if self._is_service_managed(session):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should follow the effective ownership decision for this run, not merely the presence of a service ID from an earlier run. A store=True turn leaves service_session_id persisted; a later store=False turn still takes this branch and skips durable history. I reproduced True -> False -> False against core 1.16: the latter turns kept sending the stale service ID and the first local turn never entered local history. Please clear or ignore the service ID when service_owns_history is false and define how ownership transitions preserve context.

# delivery window would otherwise protect everything and evict nothing, and state that
# cannot be persisted ends the session for every caller. Losing one response costs the
# caller a retry, so that is the cheaper failure.
forced = await _evict_once(history, serialized_size=size, target_bytes=target, honor_delivery_window=False)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once this forced pass evicts a completed response, there is no acknowledgement or durable idempotency record left behind. A repeated delivery with the same correlation ID then misses already_answered and can run the model and tools again, duplicating side effects. Please retain a lightweight correlation tombstone after response payload eviction, or add an explicit response mailbox/acknowledgement lifecycle rather than relying on a 60-second window.

return agent

provider_list = list(cast("Sequence[Any]", providers))
existing = next(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Selecting only the first load-enabled provider is not safe because the remaining load-enabled providers still participate in the core pipeline. With two InMemoryHistoryProviders, the second turn received the first request/response twice, and the second full transcript remained in persisted session state outside retention. Please reject configurations with more than one load-enabled primary provider, or implement explicit ordering and deduplication while continuing to allow additional store-only audit/evaluation providers.

)
updated = [replacement if p is existing else p for p in provider_list]
else:
# A deliberate storage choice (external or custom), so do not override it.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leaving an external provider in place is correct during normal execution, but the public entity reset operation only clears durable entity state and then reuses the same stable session ID. The next turn therefore reloads the supposedly reset transcript from the external provider; I reproduced q1/r1/q2/r2 returning after reset. Reset needs a provider clear/delete contract, a new session identity, or an explicit unsupported/error behavior for externally owned history.


if not selected:
return None
return [m.to_dict() for m in selected]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

full is still the default, and this serializes the entire accumulated conversation into the entity-call payload before target-side deduplication or retention can run. The ADR already measures 675,560 bytes at 800 turns, so this remains linear until it crosses the 1 MiB transport limit; fan-out repeats the payload per target. context_mode is a useful mitigation when semantics permit, but full-context workflows still need a bounded unseen suffix or an offloaded/reference-based transport plus a payload-limit regression test.

Copilot AI review requested due to automatic review settings September 10, 2026 15:35

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The PR introduces broad contract/runtime changes (schema v2, workflow protocol envelope, retention/budget enforcement, and response portability) across multiple packages and hosts, requiring careful human validation beyond targeted review comments.

Review details
  • Files reviewed: 65/126 changed files
  • Comments generated: 5
  • Review effort level: Lite

Comment on lines +194 to +198
messages = fields.get("messages")
if messages is not None and not isinstance(messages, Message):
if not isinstance(messages, Sequence) or isinstance(messages, (str, bytes, bytearray)):
raise TypeError("Agent response messages must be a sequence of messages")
fields["messages"] = [_load_message(message) for message in cast("Sequence[Any]", messages)]
Comment thread docs/features/durable-agents/README.md Outdated
Comment on lines +28 to +29
> [!WARNING]
> The local Python PR #59 implementation uses schema `2.0.0`, independent response/completion storage and an explicit `isolated_v2` deployment gate. It does not require a local mirror of external/service-owned history. Existing .NET readers do not support this layout. Do not mix these writers or replay old workflow histories through the new Python engine. See [ADR-0032](../../decisions/0032-durable-thread-compaction.md#state-evolution-and-compatibility) for migration, rollback and deployment boundaries.
Comment on lines +18 to +19
The settings below describe the local PR #59 implementation, not release readiness or the contents
of an already published package.
Comment thread python/packages/durabletask/README.md Outdated
Comment on lines +18 to +19
The settings below describe the local PR #59 implementation, not release readiness or the contents
of an already published package.
Comment thread python/samples/README.md Outdated
@@ -2,9 +2,34 @@

This directory contains samples for durable agent hosting using the Durable Task Scheduler. These samples demonstrate the worker-client architecture pattern, enabling distributed agent execution with persistent conversation state.

## Local PR #59 deployment contract
Copilot AI review requested due to automatic review settings September 10, 2026 16:34

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

It is a large prototype integrating breaking contract changes across runtime, tests, and samples, and is explicitly not intended to merge as-is.

Review details
  • Files reviewed: 65/125 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

Copilot AI review requested due to automatic review settings September 10, 2026 19:25

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

It’s a broad, prototype-scale change spanning runtime behavior, protocol versioning, and many tests/samples, and requires careful human validation of backward-compat and operational impact.

Review details

Suppressed comments (1)

Previously missed (1) — in code that hasn't changed since the last review.

python/packages/azurefunctions/agent_framework_azurefunctions/_entities.py:157

  • The success log runs even for unknown operations, because it’s emitted after the branch. That produces misleading logs ("completed successfully") for error responses. Return early in the unknown-operation branch so only known operations log success.
  • Files reviewed: 68/130 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment on lines +3 to +10
"Values": {
"FUNCTIONS_WORKER_RUNTIME": "python",
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"DURABLE_TASK_SCHEDULER_CONNECTION_STRING": "Endpoint=http://localhost:8080;TaskHub=default;Authentication=None",
"TASKHUB_NAME": "default",
"FOUNDRY_PROJECT_ENDPOINT": "<FOUNDRY_PROJECT_ENDPOINT>",
"FOUNDRY_MODEL": "<FOUNDRY_MODEL>"
}
Copilot AI review requested due to automatic review settings September 12, 2026 03:02

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

One or more issues must be addressed before approval.

Get a fresh assessment by requesting another Copilot review.

Review details
  • Files reviewed: 73/140 changed files
  • Comments generated: 3
  • Review effort level: Lite

Comment on lines +38 to +44
agent_client = get_client(log_handler=silent_handler)
try:
run_client(agent_client)
except Exception as e:
logger.exception(f"Error during agent interaction: {e}")

logger.debug("Sample completed. Worker shutting down...")
Returns:
DurableAIAgentWorker with agents registered
"""
agent_worker = DurableAIAgentWorker(worker)
Comment on lines +77 to +83
app = AgentFunctionApp(
agents=[_create_agent()],
enable_health_check=True,
max_poll_retries=50,
retention="keep_all",
max_state_bytes=None,
)
Copilot AI review requested due to automatic review settings September 12, 2026 03:15

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Several documented samples lack the required isolated deployment acknowledgement, and the Redis sample needs provider cleanup before approval.

Review details
  • Files reviewed: 73/140 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants