You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Adds Class Deep Dives Vol. 5 for the Microsoft Agent Framework Python SDK, source-verified against agent-framework==1.19.0 (latest PyPI release, up from 1.18.0 covered in Vol. 4).
New file: microsoft_agent_framework_python_class_deep_dives_v5.md
Covers 10 previously undocumented public APIs, chosen to span a broad range of the framework's functionality:
#
Class / Type
Area
1
WorkflowEvent[DataT]
Workflow event bus — all lifecycle, diagnostic, bookkeeping, and data events; typed factory methods; request_info JSON serialization
2
AgentContext
Agent middleware pipeline context; timing, message injection, and result-override examples
Add Class Deep Dives Vol. 5, source-verified against agent-framework
1.19.0 (latest PyPI release). Covers 10 previously undocumented public
APIs spanning a broad range of the framework:
1. WorkflowEvent[DataT] — unified event bus: all lifecycle, diagnostic,
bookkeeping, and data-plane events via typed factory methods, property
accessors for request_info, and JSON serialization round-trip.
2. AgentContext — mutable context through the agent middleware pipeline;
demonstrates timing middleware, message injection, and result override.
3. MiddlewareBundle — indivisible middleware group for features that only
uphold their contract when installed together (e.g. agent-hooks).
4. ConversationSplit / ConversationSplitter — built-in LAST_TURN/FULL
enum strategies plus the structural Protocol for custom splitters.
5. VectorStoreHistoryProvider — vector-backed conversation history with
multi-dimensional scoping, optional embeddings, compaction, and a
semantic search_history tool.
6. MemoryStore (ABC) / MemoryFileStore — abstract backing store interface
for the memory harness; concrete filesystem implementation; custom
in-memory implementation example for testing.
7. MemoryTopicRecord — topic memory file: constructor, markdown
round-trip, to_dict/from_dict, and search helpers.
8. WorkflowRunResult — list[WorkflowEvent] subclass; get_outputs(),
get_final_state(), status_timeline(), and pending request detection.
9. FunctionInvocationContext — function middleware context including
progressive tool exposure (add_tools / remove_tools) with all-or-nothing
batch semantics; argument sanitisation middleware example.
10. ChatOptions — TypedDict of cross-provider request parameters;
default_options, per-run overrides, structured output, and
Unpack[ChatOptions] type-safe forwarding.
Also updates the comprehensive guide to reference Vol. 5 and bumps
the latest version badge to 1.19.0.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J66Vy9idLUFLqJfgqkM5uq
The reason will be displayed to describe this comment to others. Learn more.
Construct workflows through the supported builder API
WorkflowBuilder requires a start_executor= argument and does not expose add_agent(), so this first advertised runnable example fails while constructing the builder; the same unsupported pattern is repeated in the streaming, basic-result, and multi-agent examples. Use WorkflowBuilder(start_executor=agent) for a single agent and connect multiple agents with the documented edge/chain methods.
The reason will be displayed to describe this comment to others. Learn more.
Stream through Workflow.run
Workflow does not provide a stream() method, so this example still raises AttributeError after its builder construction is corrected. The documented streaming API is workflow.run("Cherry blossoms fall", stream=True), whose returned stream can be used in the async for loop.
The reason will be displayed to describe this comment to others. Learn more.
Resume pending workflows with Workflow.run
Workflow has no respond() method, making this HITL example and the later pending-request example unusable. Responses must be supplied through workflow.run(responses={req.request_id: user_answer}, ...), with checkpoint information when required by the paused run.
The reason will be displayed to describe this comment to others. Learn more.
Pass checks rather than an agent to LocalEvaluator
LocalEvaluator accepts positional EvalCheck callables and has no judge_agent keyword, so this built-in-split example fails at construction. To demonstrate an LLM judge, wrap the judge call in an EvalCheck (for example with @evaluator) and pass that check to LocalEvaluator; its returned EvalResults should then be read through items, not the later result.results loop.
The reason will be displayed to describe this comment to others. Learn more.
Use the actual agent-hooks factory contract
This factory is publicly exported from agent_framework/implemented in _agent_hooks, not _harness._hooks, and its contract takes one or more interceptor objects rather than a hooks_endpoint keyword. Consequently the example fails either at import or at invocation; replace it with an interceptor-based create_agent_hooks_middleware(interceptors, ...) example.
The reason will be displayed to describe this comment to others. Learn more.
Pair MemoryFileStore with MemoryContextProvider
MemoryFileStore backs MemoryContextProvider, whereas FileMemoryProvider uses the separate AgentFileStore abstraction and does not accept memory_store or memory_agent. This example therefore raises a constructor error and also teaches the wrong memory model; instantiate MemoryContextProvider(store=store, ...) for the topic/index store shown here.
The reason will be displayed to describe this comment to others. Learn more.
Handle validated BaseModel tool arguments
For normally validated tool calls, context.arguments may be a Pydantic BaseModel, which has no .values() method, so this guard raises AttributeError instead of inspecting or executing the tool. Normalize models with model_dump() while retaining the mapping path before iterating over argument values.
The reason will be displayed to describe this comment to others. Learn more.
Forward ChatOptions through the options parameter
Agent.run applies per-call chat settings through its options= mapping, not by expanding ChatOptions into top-level keywords. As written, this guidance and the examples using temperature=, model=, max_tokens=, or **override do not perform the documented option merge and may be rejected or forwarded as unrelated runtime kwargs; use agent.run(prompt, options=options) instead.
All bugs were in code examples in the Vol. 5 class deep dives doc:
- WorkflowBuilder: use start_executor= kwarg; replace .add_agent()/.add_agent()
chain with .add_edge() since add_agent() does not exist
- Streaming: use workflow.run(..., stream=True) instead of workflow.stream()
- HITL resume: use workflow.run(responses={id: answer}, checkpoint_id=...)
instead of the non-existent workflow.respond()
- LocalEvaluator: pass EvalCheck callables positionally; access .items not
.results on EvalResults
- create_agent_hooks_middleware: fix import to agent_framework (not
_harness._hooks) and use interceptors= arg, not hooks_endpoint=
- MemoryFileStore: pair with MemoryContextProvider(store=...) not
FileMemoryProvider(memory_store=...) — MemoryFileStore backs
MemoryContextProvider, not FileMemoryProvider
- FunctionInvocationContext.arguments: handle BaseModel via model_dump()
before iterating values
- ChatOptions per-run: pass options={...} mapping, not bare **kwargs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J66Vy9idLUFLqJfgqkM5uq
Nine additional corrections to code examples and prose:
- include_status_events=True needed on workflow.run() for status/failed
events to appear in the iterable list; without it they are only in
status_timeline()
- result.usage_details not result.usage (AgentResponse has no .usage)
- AgentResponse(messages=...) not message= (wrong kwarg name)
- LocalEvaluator.evaluate() has no split= param; split_strategy belongs
on EvalItem(split_strategy=ConversationSplit.LAST_TURN)
- VectorStoreHistoryProvider method table: show state=None default
- Structured output: use result.value (validated model), not
result.text + model_validate_json()
- Fix mismatched quote in path-traversal note (``.."` → `..`)
- typing.Unpack requires 3.11+; use typing_extensions for 3.10 compat
- Update stale "See also" link version string to 1.19.0
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J66Vy9idLUFLqJfgqkM5uq
The reason will be displayed to describe this comment to others. Learn more.
Pass a sequence to AgentResponse
AgentResponse.messages expects a sequence of Message objects, but this passes a single Message. The mocked response therefore cannot be consumed through normal sequence operations such as the .text accessor; wrap the assistant message in a list.
The reason will be displayed to describe this comment to others. Learn more.
Implement the interceptor protocol
Although the earlier import and factory argument were corrected, the newly supplied plain function still does not implement the agent-hooks Interceptor protocol, which expects an object exposing intercept(context) and returning a verdict. The emitter cannot invoke this (event_type, payload) -> dict callable as documented; use an interceptor class such as the EgressGuard pattern already shown in Vol. 4.
The reason will be displayed to describe this comment to others. Learn more.
Treat EvalItem.response as text
After EvalItem applies its conversation split, item.response is already the normalized response string, not a list of message objects. This loop iterates over individual characters and then accesses m.text, so the advertised evaluator raises AttributeError; inspect item.response directly.
The reason will be displayed to describe this comment to others. Learn more.
Supply the required check name
CheckResult requires a check_name in addition to passed and reason. Because factual_check is passed directly to LocalEvaluator rather than wrapped by a decorator that could provide metadata, this constructor call raises before the check can return; add a stable name such as check_name="factual_check".
The reason will be displayed to describe this comment to others. Learn more.
Read pass/fail data from item scores
The fresh correction to iterate over results.items is incomplete: each entry is an evaluation item result with fields such as status and scores, not a CheckResult with passed and reason. Once the check itself succeeds, this print still raises AttributeError; print r.status and inspect the entries in r.scores for per-check results and reasons.
The reason will be displayed to describe this comment to others. Learn more.
Attach custom splitters to EvalItem
LocalEvaluator.evaluate() has no split parameter, as the corrected example immediately above also notes. Following this usage comment therefore raises an unexpected-keyword error; construct each EvalItem with split_strategy=split_before_tool_call instead.
The reason will be displayed to describe this comment to others. Learn more.
Remove the unsupported memory_agent argument
After switching this example from FileMemoryProvider to MemoryContextProvider, the unsupported memory_agent keyword remains. MemoryContextProvider accepts store and options such as consolidation_client, but not a separate agent, so this corrected example still fails during provider construction.
Seven additional API accuracy fixes to code examples:
- AgentResponse(messages=[...]) — wrap single Message in a list;
messages= accepts Message | Sequence[Message] but list is safer
- create_agent_hooks_middleware: replace plain function stub with a
proper Interceptor subclass (agent_hooks package); plain callables
do not satisfy the Interceptor protocol
- factual_check EvalCheck: item.response is already a str (joined
assistant text); was incorrectly iterating it as a list of messages
- CheckResult: add required check_name= field
- results.items loop: EvalItemResult has is_passed/scores, not
passed/reason; print r.status, r.is_passed, and r.scores entries
- Custom ConversationSplitter comment: split_strategy goes on EvalItem,
not as a split= kwarg on evaluate()
- MemoryContextProvider: remove unsupported memory_agent= kwarg;
use consolidation_client= instead
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J66Vy9idLUFLqJfgqkM5uq
The reason will be displayed to describe this comment to others. Learn more.
Construct EvalItem conversations from Message objects
EvalItem.conversation expects list[Message], and ConversationSplit.LAST_TURN inspects message attributes such as role; these raw dictionaries are not normalized by EvalItem, so accessing item.response in factual_check fails before the check runs. Import Message and construct the user and assistant entries as Message instances, as the later FULL example does.
The reason will be displayed to describe this comment to others. Learn more.
Install the distribution that provides agent_hooks
Fresh evidence after the earlier interceptor correction is this newly added install command: the agent_hooks import is supplied by the agent-hooks-sdk distribution (the verified Vol. 4 example uses pip install --pre agent-hooks-sdk), not agent-hooks. A reader following this advertised runnable example therefore cannot obtain the imported SDK; update both the command and package wording.
The reason will be displayed to describe this comment to others. Learn more.
Resume concurrent requests with one response map
When a paused workflow has multiple pending requests, this resumes once per request while repeatedly loading the same checkpoint_id. Each invocation therefore starts from the original checkpoint rather than retaining responses supplied by earlier loop iterations, so the returned result can remain pending or lose prior answers. The corrected code should first collect every answer into one {request_id: answer} mapping and then call workflow.run() once (and repeat against a fresh result only for later HITL rounds).
This non-streaming call leaves include_status_events at its default False, so the status and failure events handled in the loop below are not present in result. Pass include_status_events=True (or read result.status_timeline()) so this example can observe those events.
The store writes records under record.slug, but get_topic only looks up the caller's value as a dictionary key. A caller using the documented human-readable topic name will therefore get FileNotFoundError; resolve both the slug and record.topic (or use one consistent key).
Filter workflow events by agent ID, not display name
Agent.name is a display name; workflow event executor_id is the agent's stable id, which is auto-generated here because neither agent sets one. This filter will normally return no writer outputs. Compare with writer.id or assign id="writer" when constructing the agent.
The comprehensive guide's feature table is headed What's new in 1.14.0, not 1.18.0. This stale version reference makes the new volume's See also text inaccurate; either update the referenced table or describe the page without claiming it is an 1.18.0 table.
Update the Python guide index for the 1.19.0 release
This version bump is not reflected on the Python guide landing page: python/index.mdx still advertises core 1.18.0 and Vol. 4 as the latest deep-dive card. Update that index metadata/card with the 1.19.0 and Vol. 5 links so users can discover the release being advertised here.
This issue also appears on line 17 of the same file.
Three more corrections:
- EvalItem.conversation expects list[Message], not raw dicts;
replaced dict literals with Message('user', [...]) and
Message('assistant', [...]) constructors; added Message import
- agent-hooks-sdk install comment: correct package name is
agent-hooks-sdk (pip install --pre agent-hooks-sdk)
- HITL detecting-pending-requests example: collect all pending
answers into one responses={} map before calling workflow.run()
once; looping with one request per run() call restarts from the
same checkpoint on each iteration, dropping earlier answers
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J66Vy9idLUFLqJfgqkM5uq
The SqlInjectionGuard example incorrectly presented a substring blocklist
as SQL injection protection. A blocklist only covers a tiny subset of
payloads and must never be the primary defence; real SQL injection
protection requires parameterized queries.
Changes:
- Rename SqlInjectionGuard → ToolInputPatternGuard
- Add prominent security note explaining the example is illustrative
pattern-matching, not an injection guard
- Update the error message to not imply SQL injection semantics
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J66Vy9idLUFLqJfgqkM5uq
This claimed runnable example passes dictionaries where EvalItem and the splitter contract expect list[Message]; the splitters access message attributes such as role and contents. Use Message instances here, as the evaluation guide does, or this example fails when evaluated.
Normalize topic and slug keys across custom store operations
The custom store writes records under record.slug, but get_topic() and delete_topic() look up the caller's topic key. For a normal record such as topic='Travel Plans' and slug='travel-plans', the provider cannot read or delete the record it just wrote; normalize both forms consistently (or key the stub by record.topic).
Use owner-specific transcript directories or reject unsupported storage
This test store returns one fixed transcript directory for every owner. If MemoryContextProvider writes transcript archives through this method, sessions for different users share the same files and can collide; the existing in-memory backend instead raises NotImplementedError for unsupported transcript directories. Return an owner-specific path or explicitly reject transcript storage.
Clarify that store enables provider-side conversation storage
The store option is provider-side conversation storage, not persistence of a request for fine-tuning. The model-provider reference describes it as “Provider-side conversation storage (OpenAI Responses API, Foundry)” (microsoft_agent_framework_python_model_providers.md:410); the current note could lead users to enable it for the wrong purpose.
The reason will be displayed to describe this comment to others. Learn more.
Update the landing page for 1.19.0 and Vol. 5
This version bump leaves the Python landing page inconsistent: index.mdx still labels 1.18.0 as the stable release in its metadata, version card, and “What's shipped” section, and its reference cards stop at Vol. 4 (index.mdx lines 3, 22, and 117–125). Readers entering through the main overview therefore see a stale version and cannot discover the newly added Vol. 5 page; update that page and its revision history alongside this change.
Although this section says get_topic accepts a topic name or slug, the stub stores records by slug and only performs owner_topics.get(topic). Calling it with the public record.topic (for example, "Travel Plans") raises FileNotFoundError, so the advertised custom implementation does not satisfy the interface. Resolve both forms in the example.
The event table omits the deprecated data event produced by WorkflowEvent.emit(), even though this section says it covers all event kinds and explicitly mentions emit() above. The comprehensive guide documents .emit() as producing type "data"; add that entry (and any other variants covered by the claim) so readers can interpret the complete event stream.
ChatOptions.store is not a fine-tuning switch. The provider-neutral options documentation describes it as provider-side request/conversation storage (for example, the Responses API or Foundry), so this note can lead users to believe enabling it persists data for fine-tuning when it does not.
Vol. 5 has 10 numbered sections but exposes 12 distinct types because sections 4 and 6 each document two separate types. Therefore “50 APIs (48 classes and 2 functions)” is internally inconsistent: 50 is a grouped-section count, not the count implied by the class breakdown. Either count each exported type or describe these as 50 grouped API sections.
- Bump version references from 1.18.0 to 1.19.0 in frontmatter
description, Version card, and "What's shipped" section
- Add Vol. 5 (1.19.0) LinkCard to the Reference section after Vol. 4
- Add 2026-09-21 / 1.19.0 entry to the revision history table
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J66Vy9idLUFLqJfgqkM5uq
- Fix VectorStoreHistoryProvider comment: both runs use the same session,
not a "later session" (line 685)
- Fix MemoryTopicRecord custom-store stub: key write_topic by record.topic
(not record.slug) so get_topic/delete_topic lookups match (line 832)
- Fix WorkflowRunResult executor filter: add id="writer" to Agent so that
ev.executor_id == "writer" matches the assigned ID, not an auto-generated
one (line 1066)
- Fix ChatOptions store field description: it controls provider-side
conversation storage (OpenAI Responses API, Foundry), not fine-tuning
persistence (line 1306)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J66Vy9idLUFLqJfgqkM5uq
Each resumed workflow.run() saves a fresh checkpoint forming a
previous_checkpoint_id chain rather than updating in-place. Query
checkpoint_storage.get_latest(workflow_name=workflow.name) before
every iteration to obtain the current checkpoint_id.
Also pass checkpoint_storage to the initial workflow.run() call and
change the function signature to accept checkpoint_storage instead
of a raw checkpoint_id string.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J66Vy9idLUFLqJfgqkM5uq
The reason will be displayed to describe this comment to others. Learn more.
Keep HITL resumption on the current checkpoint chain
When two invocations of the same named workflow share checkpoint storage, get_latest(workflow_name=...) can return the other invocation's checkpoint because it selects globally by workflow name rather than by run chain. The fresh evidence is this new workflow-name-only lookup: the handler can then submit one user's request IDs and answers while restoring another user's state, potentially corrupting or exposing workflow data. Identify the checkpoint whose pending request IDs match the current result, then follow that checkpoint's descendant chain rather than selecting the globally latest checkpoint.
The reason will be displayed to describe this comment to others. Learn more.
Fixed in commit 18a39eef. The root cause is shared storage across concurrent runs rather than anything in the get_latest() call itself — get_latest(workflow_name=...) is correct when the store holds only one run's checkpoints.
The fix:
Added a comment at the top of run_and_handle() explaining that checkpoint_storage must be scoped to a single run, and why.
Added a handle_request() usage pattern that creates a fresh MemoryFileStore(path=f"./checkpoints/{run_id}") per invocation (where run_id = uuid.uuid4().hex), so get_latest() only ever sees checkpoints from that specific run even in multi-user deployments.
This is presented as the complete event-type table, but it omits the still-public deprecated WorkflowEvent.emit() factory and its data event. The note only says it is deprecated, so readers cannot learn its signature or distinguish it from output/intermediate; add a data row and the corresponding emit entry, or explicitly state that deprecated APIs are excluded.
The constructor is presented as complete, but it omits the inherited skip_excluded option. The repository documents this as a shared history-provider flag (microsoft_agent_framework_python_sessions.md:141-146; microsoft_agent_framework_python_comprehensive_guide.md:2823-2830), so users cannot discover how to exclude compaction-marked messages with this provider. Add skip_excluded: bool = False to the signature and parameter table.
get_latest(workflow_name=...) selects the newest checkpoint for that
name in a given store; two concurrent invocations sharing the same
store can pick up each other's checkpoints.
Add a comment explaining the single-run-per-storage requirement, and
add a handle_request() usage pattern that creates a fresh MemoryFileStore
scoped to a uuid run_id for each invocation.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J66Vy9idLUFLqJfgqkM5uq
The reason will be displayed to describe this comment to others. Learn more.
Use FileCheckpointStorage for HITL checkpoints
This runnable example imports a nonexistent checkpoint backend: workflow checkpoints use the top-level FileCheckpointStorage, while MemoryFileStore is the unrelated long-term-memory store and is not exported from agent_framework.checkpointing. Consequently the example fails at import before handle_request() can run; import FileCheckpointStorage from agent_framework and construct it with the run-scoped directory (positionally or via storage_path=).
The reason will be displayed to describe this comment to others. Learn more.
Fixed in commit 820264fe. MemoryFileStore (the long-term memory backend) has been replaced with FileCheckpointStorage (the correct workflow checkpoint backend, re-exported from agent_framework). The import is now from agent_framework import WorkflowRunState, FileCheckpointStorage and the constructor call is FileCheckpointStorage(f"./checkpoints/{run_id}").
MemoryFileStore is the long-term-memory backend; workflow checkpoints
use FileCheckpointStorage (re-exported from agent_framework).
Replace the incorrect import and constructor call in the HITL
handle_request() usage pattern.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J66Vy9idLUFLqJfgqkM5uq
This custom splitter will raise AttributeError as soon as it encounters a plain-text content item, because Message.contents commonly contains strings and strings have no .type. The repository's equivalent splitter guards with getattr(c, "type", None) (microsoft_agent_framework_python_evaluation.md:264-266); use the same guard so normal text before the first tool call is handled.
The experimental note is applied to MiddlewareBundle itself, but the preceding Vol. 4 reference presents MiddlewareBundle as a public root export and places the optional/experimental AGENT-HOOKS requirement on create_agent_hooks_middleware() instead (microsoft_agent_framework_python_class_deep_dives_v4.md:763-787). As written, the plain bundle example either unnecessarily requires an acknowledgement or contradicts the actual gate; scope the note to the factory usage.
The reason will be displayed to describe this comment to others. Learn more.
Apply line_limit when rendering the memory index
When this testing store contains more topics than the configured MemoryContextProvider.index_line_limit, get_index_text() joins every entry because neither this method nor rebuild_index() applies line_limit. Tests using the advertised store therefore miss the production provider's index bound and can construct an oversized memory prompt; truncate the rendered entries to the requested limit.
The reason will be displayed to describe this comment to others. Learn more.
Fixed in commit 57c0f47f. Both rebuild_index() and get_index_text() now respect line_limit: rebuild_index slices topics[:line_limit] before building the entry list, and get_index_text slices entries[:line_limit] before joining, matching the production MemoryContextProvider bound.
The reason will be displayed to describe this comment to others. Learn more.
Preserve empty transcript scope components
Fresh evidence after the earlier path-encoding fix is that URL-safe base64 maps an empty ID to an empty path component, which the filesystem discards. If empty owner/source IDs are used, scopes such as (source_id="", owner="a") and (source_id="a", owner="") both resolve beneath _tmp/YQ, allowing their transcript files to collide despite the claimed isolation; reject empty IDs or encode them with a non-empty tagged representation.
The reason will be displayed to describe this comment to others. Learn more.
Fixed in commit 57c0f47f. _safe() now prepends a constant "s" before the base64 output, so _safe("") → "s" and _safe("x") → "sYA" etc. — every input including the empty string maps to a unique, non-empty path component, and the (source_id="", owner="x") / (source_id="x", owner="") collision is eliminated.
…index
_safe("") returned "" causing (source_id="", owner="x") and
(source_id="x", owner="") to resolve to the same filesystem path.
Prefix the base64 output with "s" so every input including "" maps
to a non-empty, unique path component.
rebuild_index() and get_index_text() both ignored the line_limit
parameter, diverging from the production MemoryContextProvider's
index bound. Slice topics and entries to [:line_limit] in both.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J66Vy9idLUFLqJfgqkM5uq
This volume contains 10 numbered sections but 12 public types: ConversationSplit/ConversationSplitter and MemoryStore/MemoryFileStore each contribute two classes. That conflicts with the comprehensive guide's new total of 52 APIs (50 classes + 2 functions) and with the 10-API title/link/changelog labels; either describe this as 10 deep dives covering 12 APIs, or update the aggregate counts and labels consistently.
The reason will be displayed to describe this comment to others. Learn more.
Include the deprecated data event in the taxonomy
For workflows or custom executors that still use the supported-but-deprecated WorkflowEvent.emit(), the emitted event has type "data", yet this event table and the factory-method table skip directly from diagnostic events to "output". A consumer implementing event handling from this advertised complete taxonomy can therefore silently discard emitted payloads; add the "data" row and the deprecated emit() factory while retaining the migration note.
The reason will be displayed to describe this comment to others. Learn more.
Fixed in commit b8fbc0af. Added "data" to the event type table with WorkflowEvent.emit(data)(deprecated) and DataT data type, and added the emit row to the factory-method table with its signature and a note to prefer ctx.yield_output().
The reason will be displayed to describe this comment to others. Learn more.
Bound encoded transcript path components
When a source or owner ID exceeds roughly 190 UTF-8 bytes, this unbounded base64 value expands beyond the common 255-byte filesystem component limit, so get_transcripts_directory() raises ENAMETOOLONG before the provider can persist a transcript. Fresh evidence after the earlier path-encoding correction is that replacing truncation with base64 removed collision risk but added no length handling; use a tagged digest representation for long values while retaining the current encoding for short ones.
The reason will be displayed to describe this comment to others. Learn more.
Fixed in commit b8fbc0af. _safe() now uses a tagged two-scheme approach: short values get "s" + url-safe base64 (as before), and values whose encoded form would exceed 200 bytes get "d" + SHA-256 hex (64 chars, well within the 255-byte limit). The distinct "s"/"d" prefixes prevent any cross-scheme collision. A _MAX_COMPONENT = 200 constant documents the threshold.
The reason will be displayed to describe this comment to others. Learn more.
Correct the comprehensive guide verification claim
This link labels the comprehensive guide as verified against 1.19.0, but that guide's front matter and header explicitly state that its content is verified against 1.14.0 and only identify 1.19.0 as the latest PyPI release. Readers can therefore mistake older examples for source-verified 1.19 behavior; change this label to 1.14.0 or reverify and update the linked guide.
The reason will be displayed to describe this comment to others. Learn more.
Fixed in commit b8fbc0af. The label now reads "content verified against 1.14.0 (latest release badge: 1.19.0)", accurately reflecting that the guide's examples were validated against 1.14.0 while the version badge was updated to track the latest PyPI release.
…ide version
WorkflowEvent taxonomy was missing the deprecated 'data' event type
produced by WorkflowEvent.emit(). Add the row to both the event-type
table and the factory-method table with a deprecation note.
_safe() had no length bound; IDs > ~190 UTF-8 bytes would produce a
base64 component exceeding the 255-byte filesystem limit. Now falls
back to 'd' + SHA-256 hex (64 chars) for long values, keeping 's' +
base64 for short ones. Distinct prefixes prevent cross-scheme collisions.
The See Also link to the comprehensive guide incorrectly claimed it was
verified against 1.19.0; the guide's own header states 1.14.0. Updated
the label to reflect both: content verified against 1.14.0, latest
release badge 1.19.0.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J66Vy9idLUFLqJfgqkM5uq
The reason will be displayed to describe this comment to others. Learn more.
Isolate transcript storage per store instance
After the source/owner scoping fixes, every InMemoryMemoryStore instance still uses the same fixed /tmp root. When parallel tests or two providers create separate store instances with the same source, owner, and session ID, MemoryContextProvider writes both instances' transcripts into the same directory even though their in-memory topics and state are independent, allowing stale or concurrent transcript data to contaminate tests. Allocate a unique temporary root per instance and clean it up with the store.
The reason will be displayed to describe this comment to others. Learn more.
Fixed in commit 201c05b3. __init__ now calls tempfile.mkdtemp(prefix="in-memory-store-") so every store instance gets a unique temporary root, eliminating transcript directory sharing between parallel test instances. A cleanup() method using shutil.rmtree is added for teardown.
All instances previously shared a single /tmp/in-memory-store-transcripts
root, so parallel tests with matching source_id/owner/session IDs would
write into the same transcript directory despite having independent
in-memory state.
Use tempfile.mkdtemp(prefix="in-memory-store-") in __init__ to give
every instance a unique root, and add a cleanup() method that calls
shutil.rmtree for teardown.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J66Vy9idLUFLqJfgqkM5uq
The reason will be displayed to describe this comment to others. Learn more.
Copilot review overview
🔵 Needs a closer look
One or more issues must be addressed before approval.
Review effort: Lite Findings: None
This branch has not been deployed
No deployments
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds Class Deep Dives Vol. 5 for the Microsoft Agent Framework Python SDK, source-verified against
agent-framework==1.19.0(latest PyPI release, up from 1.18.0 covered in Vol. 4).New file:
microsoft_agent_framework_python_class_deep_dives_v5.mdCovers 10 previously undocumented public APIs, chosen to span a broad range of the framework's functionality:
WorkflowEvent[DataT]AgentContextMiddlewareBundleConversationSplit/ConversationSplitterLAST_TURN/FULLeval strategies; custom splitter ProtocolVectorStoreHistoryProviderMemoryStore(ABC) /MemoryFileStoreMemoryTopicRecordWorkflowRunResultlist[WorkflowEvent]subclass;get_outputs(),get_final_state(),status_timeline(), pending request detectionFunctionInvocationContextadd_tools/remove_tools); argument sanitisationChatOptionsTypedDict; default options, per-run overrides, structured output,Unpacktype-safe forwardingEach section includes:
Updated:
microsoft_agent_framework_python_comprehensive_guide.md1.19.0🤖 Generated with Claude Code
https://claude.ai/code/session_01J66Vy9idLUFLqJfgqkM5uq
Generated by Claude Code