The foundation package containing all core abstractions, types, and built-in OpenAI/Azure OpenAI support.
agent_framework/
├── __init__.py # Public API exports
├── security.py # Public security primitives, middleware, and tools
├── _agents.py # Agent implementations
├── _clients.py # Chat client base classes and protocols
├── _types.py # Core types (Message, ChatResponse, Content, etc.)
├── _tools.py # Tool definitions and function invocation
├── _middleware.py # Middleware system for request/response interception
├── _sessions.py # AgentSession and context provider abstractions
├── _skills.py # Agent Skills system (models, executors, provider)
├── _mcp.py # Model Context Protocol support
├── _workflows/ # Workflow orchestration (sequential, concurrent, handoff, etc.)
├── openai/ # Built-in OpenAI client
├── azure/ # Lazy-loading entry point for Azure integrations
└── <provider>/ # Other lazy-loading provider folders
SupportsAgentRun- Protocol defining the agent interfaceBaseAgent- Abstract base class for agentsAgent- Main agent class wrapping a chat client with tools, instructions, and middleware
SupportsChatGetResponse- Protocol for chat client implementationsBaseChatClient- Abstract base class with middleware support; subclasses implement_inner_get_response()and_inner_get_streaming_response()
Message- Represents a chat message with role, content, and metadataChatResponse- Response from a chat client containing messages and usageChatResponseUpdate- Streaming response updateAgentResponse/AgentResponseUpdate- Agent-level response wrappersContent- Base class for message content (text, function calls, images, etc.)ChatOptions- TypedDict for chat request options
ToolProtocol- Protocol for tool definitionsFunctionTool- Wraps Python functions as tools with JSON schema generation@tooldecorator - Converts functions to toolsuse_function_invocation()- Decorator to add automatic function calling to chat clients
AgentMiddleware- Intercepts agentrun()callsChatMiddleware- Intercepts chat clientget_response()callsFunctionMiddleware- Intercepts function/tool invocationsAgentContext/ChatContext/FunctionInvocationContext- Context objects passed through middleware. A tool can declare aFunctionInvocationContextparameter to receive it;context.toolsis the live, mutable tools list for the run, andcontext.add_tools(...)/context.remove_tools(...)enable progressive tool exposure (changes apply on the next function-calling iteration).
AgentSession- Manages conversation state and session metadataServiceSessionId- Mapping alias for structured service-owned continuation handles used inAgentSession.service_session_idSessionContext- Context object for session-scoped data during agent runsContextProvider- Base class for context providers (RAG, memory systems)HistoryProvider- Base class for conversation history storageInMemoryHistoryProvider- Built-in session-state history provider for local runsFileHistoryProvider- JSON Lines file-backed history provider storing one file per session with one message record per line
Skill- Abstract base for a skill definition bundling instructions (content) with frontmatter metadata, resources, and scripts. Concrete subclasses (InlineSkill,FileSkill,ClassSkill) accept afrontmatter=SkillFrontmatter(...)argument carrying the spec fields. Adding new spec fields is done in one place — onSkillFrontmatter— keeping the subclass constructors stable.SkillFrontmatter- L1 discovery metadata for a skill (name,description,license,compatibility,allowed_tools,metadata). All fields are mutable plain attributes; the constructor validatesname,description, andcompatibilityagainst the spec but post-construction assignments are not re-validated. Spec fields are reachable on every skill viaskill.frontmatter.SkillResource- Named supplementary content attached to a skill; holds either staticcontentor a dynamicfunction(sync or async). Exactly one must be provided.SkillScript- An executable script attached to a skill; holds either an inlinefunction(code-defined, runs in-process) or apathto a file on disk (file-based, delegated to a runner). Exactly one must be provided.SkillScriptRunner- Protocol for file-based script execution. Any callable matching(skill, script, args) -> Anysatisfies it. Code-defined scripts do not use a runner.SkillScriptArgumentParser- Public type alias for an optional callable(raw args: dict | list[str] | str | None) -> dict | Nonethat converts the rawargsvalue before anInlineSkillScriptruns (applied before the inline list-args guard). It is an opt-in customization hook (port of .NET PR #6498) that lets callers support backends sending tool-call arguments in a non-conforming shape (e.g. vLLM JSON strings). The output is constrained to adict(named keyword arguments) orNone, because inline scripts bind arguments by keyword name. Supply it via theargument_parser=constructor arg onInlineSkillScript,InlineSkill(default for scripts added via@skill.script), orClassSkill(default for scripts discovered via@ClassSkill.script). WhenNone(the default), the raw value is used unchanged. File-based scripts are unaffected (their runner owns arg handling).SkillsProvider- Context provider (extendsContextProvider) that discovers file-based skills fromSKILL.mdfiles and/or accepts code-definedSkillinstances. Follows progressive disclosure: advertise → load → read resources / run scripts. All three tools it exposes (load_skill,read_skill_resource,run_skill_script) are registered withapproval_mode="always_require", so every skill operation needs approval. To run unattended, pass one of the static auto-approval rules toToolApprovalMiddleware(viaauto_approval_rules):SkillsProvider.read_only_tools_auto_approval_ruleapproves only the read-only tools (load_skill,read_skill_resource) while still prompting forrun_skill_script, andSkillsProvider.all_tools_auto_approval_ruleapproves every skill tool including script execution. Both rules reject any call carrying aserver_labelso they stay scoped to this provider's local tools and never auto-approve a same-named hosted tool. The tool names are also exposed as class constants (LOAD_SKILL_TOOL_NAME,READ_SKILL_RESOURCE_TOOL_NAME,RUN_SKILL_SCRIPT_TOOL_NAME).SkillsSourcedecorators - Skill sources are composable:SkillsSourceis the abstract base, with concrete sources (InMemorySkillsSource,FileSkillsSource,MCPSkillsSource) and decorators that wrap an inner source —AggregatingSkillsSource(concatenate several sources),FilteringSkillsSource(predicate filter),DeduplicatingSkillsSource(first-wins by name), andCachingSkillsSource(cache the inner source's skills list).DelegatingSkillsSourceis the abstract base for decorators. Caching lives in the source pipeline, not the provider:SkillsProviderwraps its resolved source in aCachingSkillsSourceby default (so expensive filesystem/network discovery runs once and is reused), and rebuilds instructions/tools from the cached skills each run. Passdisable_caching=TruetoSkillsProvider(...)/SkillsProvider.from_paths(...)to skip the wrapping and re-query the source on every run.CachingSkillsSourceshares a single in-flight fetch across concurrent callers and resets its cache on failure so the next call retries.
MCPTool- Base wrapper that owns the MCPClientSessionand exposes the remote server's tools asFunctionTools.MCPStdioTool/MCPStreamableHTTPTool/MCPWebsocketTool- Transport-specific subclasses.- Argument allowlist (
_prepare_call_kwargs) - Before eachtools/call, kwargs are filtered to an allowlist built from the tool's declared parameters (inputSchema.properties) plus any user-configured extras. Framework runtime kwargs injected through the function-invocation pipeline (e.g.thread,conversation_id,chat_options,options,response_format) are stripped by default rather than forwarded. A tool that declares no usableproperties(including schemas withadditionalProperties: true) forwards only the configured extras. The_MCP_FRAMEWORK_DENYLISTis a safety net for framework-named params a server declares in its schema (those are dropped); names explicitly opted in viaadditional_tool_argument_namesalways win. The reserved_metakey is never forwarded as an argument; trusted caller/runtime_metais validated as MCP request metadata, model-supplied_metais discarded in generated MCP functions, and metadata precedence is caller/runtime < OpenTelemetry < tools/list metadata. allowed_tools(constructor arg on allMCPToolsubclasses) - Restricts exposed MCP tools by raw remote MCP tool identity. Prefixed local names remain accepted only when the raw remote name already matches its normalized form; normalized/local aliases do not authorize a different raw remote name. If multiple raw remote tool names map to the same local function name, tool loading raisesToolExecutionExceptioninstead of first-one-wins shadowing.additional_tool_argument_names(constructor arg on allMCPToolsubclasses) - Opt extra argument names back into the allowlist. Accepts aSequence[str](applied to every tool) or aMapping[str, Sequence[str]]keyed by remote tool name, where the reserved key"*"denotes global extras. It is configured only in user code at construction; there is no per-call/runtime override, so a model-issued tool call cannot change which names pass through. To use a server that acceptsadditionalProperties: true, list the extra names here and then either (1) manually extend that tool'sinputSchema(via the.functionslist after connecting) so the model is prompted to supply them, or (2) supply the values yourself viafunction_invocation_kwargs. If a normal forwarded argument name is supplied by both the model andfunction_invocation_kwargs, the model-supplied value wins;_metais the exception and only trusted runtime/caller metadata is used.- Sampling guardrails (
sampling_callback) - Passingclient=advertisesSamplingCapabilityso the server can sendsampling/createMessage. Because remote servers are untrusted (confused-deputy risk), the defaultsampling_callbackis deny-by-default and applies, in order: a per-session rate limit (sampling_max_requests, default_DEFAULT_SAMPLING_MAX_REQUESTS), an approval gate (sampling_approval_callback), and amaxTokenscap (sampling_max_tokens, default_DEFAULT_SAMPLING_MAX_TOKENS). The approval callback (constructor arg on all subclasses; exported type aliasSamplingApprovalCallback) receives the rawCreateMessageRequestParams, may be sync or async, and must return truthy to approve. When it isNone(the default) every sampling request is denied; passlambda params: Trueto restore legacy auto-approve as an explicit opt-in. Requests and denials are logged at WARNING (content is not logged). The per-session counter resets in_reset_session_state. MCPTaskOptions(experimental,MCP_LONG_RUNNING_TASKSfeature, frozen) - Per-tool-instance options controlling the SEP-2663 long-running task lifecycle. When the server advertises a tool withexecution.taskSupport == "required",MCPTool.call_tooltransparently routes throughcall_tool_as_task, which sends an augmentedtools/call, pollstasks/getuntil terminal, and reinterpretstasks/resultas a normalCallToolResult. Instances are immutable; replace viaMCPTool.task_options = MCPTaskOptions(...). Fields:default_ttl: timedelta | None— forwarded to the server asparams.task.ttl(milliseconds). WhenNone, the server's default applies.cancel_remote_task_on_local_cancellation: bool = True— only gates theCancelledErrorpath. Abandonment paths (see below) always cancel.max_task_wait: timedelta | None— client-side deadline for the whole post-create lifecycle (poll + result fetch). When exceeded, raisesToolExecutionExceptionand fires a best-efforttasks/cancel.None(default) means no client-side bound. Bounds sleeps, sends, AND reconnects viaasyncio.wait_for.
- Permissive fallback: servers that ignore the augmentation (return
CallToolResultdirectly) or reject the unknowntaskfield withMETHOD_NOT_FOUND/INVALID_PARAMSfall back to the plainsession.call_tool(...)path so legacy servers keep working. An unparseable success response (server accepted the augmented call but returned a payload that is neitherCreateTaskResultnorCallToolResult) does not fall back — it raisesToolExecutionExceptionto avoid double-executing a side-effecting tool. - Submit-vs-track reconnect policy: a dropped connection before a
task_idis known raisesToolExecutionException("connection lost; task state unknown")without re-issuing the augmentedtools/call, so a server that accepted the request but lost the response cannot be made to start the same operation twice; once atask_idexists,tasks/get/tasks/resultreconnect once and retry against the same id (a shared_send_with_one_reconnecthelper). - Cancel-on-abandonment vs terminal failure: any path where the remote task may still be running (max-wait exceeded, hard
McpErrorin poll, malformedtasks/get, second connection loss in poll/fetch, reconnect failure) fires best-efforttasks/cancelbefore raising. Terminal failures (failed/cancelled/input_requiredserver-side,completed+isError, malformedtasks/resultafter server completed) do not cancel — the server is already done._MCPTaskAbandonedis the private marker distinguishing the two. - Transient poll retry: a slow
tasks/getthat surfaces asMcpError(code=408 REQUEST_TIMEOUT)is retried (bounded bymax_task_wait). All other non-connectionMcpErrors during poll are treated as abandonment.tasks/resultdoes not get transient retry — the server has already completed, so a slow payload fetch is anomalous.
AgentFileStore- Abstract async store backing the file-access harness. Implementations exposewrite,read,delete,list_children,file_exists,search, andcreate_directoryover forward-slash relative paths.list_childrenreturns the direct children (files and subdirectories, subdirectories first) asFileStoreEntryinstances;searchaccepts a keyword-onlyrecursiveflag (defaultFalse) and, whenrecursive=True, walks all descendants and returnsfile_namevalues relative to the search directory.InMemoryAgentFileStore- Dict-backed store suitable for tests and lightweight scenarios.FileSystemAgentFileStore- Disk-backed store rooted under a configurable directory. Enforces relative-path normalization, root containment, and rejects symlink/reparse-point segments to prevent escape.FileSearchResult/FileSearchMatch-SerializationMixinDTOs returned bysearch, carrying the matching file name, a context snippet, and the matching lines with 1-based line numbers.FileStoreEntry-SerializationMixinDTO returned bylist_children, carrying an entrynameandtype("file"or"directory").FileAccessProvider-ContextProviderthat adds shared file-access tools (file_access_write,file_access_read,file_access_delete,file_access_ls,file_access_grep,file_access_replace,file_access_replace_lines) plus default usage instructions to each invocation.file_access_lsenumerates direct children (both files and subdirectories) as{name, type}entries with an optionalglob_pattern, so the agent can walk the tree level by level;file_access_grepsearches recursively from an optional basedirectoryand returns relativefile_namepaths, scoped via anfnmatchglob_pattern(where*crosses/, e.g.*.md,reports/*).file_access_replacesubstitutesold_stringwithnew_string(failing if not found, or if multiple matches andreplace_allis false);file_access_replace_linesreplaces whole 1-based lines with literal text (eachnew_lineincludes its own trailing newline; an emptynew_linedeletes the line, including its line break). All tools are registered withapproval_mode="always_require"by default, so every file operation needs host approval. Passdisable_write_tools=Trueto advertise only the read-only tools. To run unattended you can disable approval at the source withdisable_readonly_tool_approval=True(read, ls, grep) and/ordisable_write_tool_approval=True(write, delete, replace, replace_lines), which register the affected tools withapproval_mode="never_require"; alternatively, keep approval on and pass one of the static auto-approval rules toToolApprovalMiddleware(viaauto_approval_rules):FileAccessProvider.read_only_tools_auto_approval_ruleapproves only the read-only tools (read, ls, grep), whileFileAccessProvider.all_tools_auto_approval_ruleapproves every file-access tool including the write tools. Both rules reject any call carrying aserver_labelso they stay scoped to this provider's local tools and never auto-approve a same-named hosted tool. The tool names are also exposed as class constants (WRITE_TOOL_NAME,READ_TOOL_NAME,DELETE_TOOL_NAME,LS_TOOL_NAME,GREP_TOOL_NAME,REPLACE_TOOL_NAME,REPLACE_LINES_TOOL_NAME). UnlikeMemoryContextProvider, the store is intentionally shared across sessions and agents.
FileMemoryProvider-ContextProviderthat gives an agent a session-scoped, file-based memory backed by the sameAgentFileStoreabstraction. Adds tools (file_memory_write,file_memory_read,file_memory_delete,file_memory_ls,file_memory_grep,file_memory_replace,file_memory_replace_lines) plus default usage instructions. Port of the .NETFileMemoryProvider.- Scoping - Memories are isolated per session by default: each session writes under a working folder derived from
context.session_id. Pass an explicitscope(e.g. a user id) to group memories across sessions, mirroringFoundryMemoryProvider'sscopearg. - Descriptions & index -
file_memory_writeaccepts an optionaldescription, stored in a companion<stem>_description.mdsidecar. After each write/delete the provider rebuilds a capped (50-entry)memories.mdindex, andbefore_runinjects that index as ausercontext message so the model knows what memories exist. Sidecars and the index are internal files hidden fromfile_memory_ls/file_memory_grepand rejected as write targets. DEFAULT_FILE_MEMORY_SOURCE_ID/DEFAULT_FILE_MEMORY_INSTRUCTIONS- Public defaults for the provider's source id and instruction banner.- Harness wiring -
create_harness_agentincludes bothFileMemoryProviderandFileAccessProviderby default. Disable viadisable_file_memory/disable_file_access; override the backing store viafile_memory_store/file_access_store. When no store is supplied, defaults areFileSystemAgentFileStorerooted at{cwd}/agent-file-memory(memory) and{cwd}/working(access), mirroring the .NETHarnessAgent.
ToolApprovalMiddleware- Experimental opt-in agent middleware that coordinates session-backed approval rules, heuristicauto_approval_rules, queued approval requests, collected approval responses, and streaming/non-streaming approval prompts. Heuristic callbacks receive the underlyingfunction_callcontent.ToolApprovalRule/ToolApprovalState- Serializable state models for standing approvals and queued approval flow.ToolApprovalRule.arguments is Nonemeans a tool-wide rule; an empty dict{}means an exact no-argument call forcreate_always_approve_tool_with_arguments_response.create_always_approve_tool_response/create_always_approve_tool_with_arguments_response- Helpers that return normalfunction_approval_responsecontent withadditional_propertiesmetadata consumed byToolApprovalMiddleware. Standing rules for hosted tools include theserver_labelboundary, so same-named tools on different hosted servers do not share approvals.- Mixed tool-call batches use a default .NET-style bypass in the function invocation loop: when a session is available, approval requests for known non-approval-required tools are treated as already approved, hidden, stored in session state keyed to the visible approval request ids from that batch, and reinjected only when that visible approval flow resumes.
AgentLoopMiddleware-AgentMiddlewarethat re-runs an agent in a loop by callingcall_next()repeatedly (the pipeline re-readscontext.messageseach time). One configurable class covers two patterns: a required usershould_continuepredicate (sync or async, the first positional/keyword arg), and a chat-client judge built via the.with_judge(...)factory (a second chat client decides whether the original request was answered; loops while it is not, using aJudgeVerdictstructured-output response — internally just an asyncshould_continuepredicate). The constructor covers the predicate pattern directly; only the judge has a convenience classmethod factory (.with_judge(judge_client, ...)) that forwards to__init__. Supports both streaming and non-streaming runs. By default a non-streaming run returns an aggregatedAgentResponsecontaining every iteration's messages plus the injectednext_message"nudge" messages (asusermessages); setreturn_final_only=Trueto return only the last iteration's response. Streaming runs always yield each iteration's updates and emit the injected nudge messages asuserupdates between iterations (thereturn_final_onlyflag has no effect on streaming, and the final response reflects the last iteration;MiddlewareTerminationis handled cleanly).should_continueis required; other constructor args are optional:max_iterations(safety cap; defaults toDEFAULT_MAX_ITERATIONS=10, explicitNone→unbounded, positive int caps;.with_judgeusesDEFAULT_JUDGE_MAX_ITERATIONS=5 as its default),next_message(defaults to a short "continue" nudge),return_final_only, andadditional_instructions(an extrasystemmessage injected ahead of the input before the agent runs — becomes part of the original messages so it survivesfresh_contextresets and persists via a session). The judge is configured only through.with_judge(judge_client/instructions/criteria), not the constructor, and itsreasoningis fed back to the agent as the next iteration's input; the judge forwards the original request messages and the agent's latest response messages verbatim so multi-modal content is preserved.criteria(alist[str]) is both injected as the agent'sadditional_instructionsand rendered into the judge instructions wherever the{{criteria}}placeholder (CRITERIA_PLACEHOLDER) appears (DEFAULT_JUDGE_INSTRUCTIONSends with it; custominstructionsmay include it, and it is stripped when no criteria are given). Theshould_continue/next_messagecallables are invoked with keyword args (iteration,last_result,messages,original_messages,session,agent,progress,feedback) and may be sync or async; declare only what you need plus**kwargs.should_continuemay return a plainboolor a(bool, str | None)tuple whose second item is feedback surfaced tonext_message/record_feedbackvia thefeedbackkwarg (the judge uses this to relay itsreasoning). Stop precedence per iteration ismax_iterations→should_continue, evaluated beforerecord_feedbackso the feedback is available to it.- Feedback tracking -
record_feedbackcaptures a per-iteration progress entry (called with the loop kwargs; if it returns a truthy string the entry is appended, otherwise the agent's response text is used as the fallback entry). The accumulated log is exposed to every callback via theprogresskeyword (a per-iteration copy of prior entries) and, wheninject_progress=True(default), injected into the next iteration's input as ausermessage (the full log without a session, only the latest entry with a session to avoid duplicating history).fresh_context=Truerestarts each iteration from the original task plus the progress log; when a session is attached it is snapshotted (to_dict()) before the loop and restored (from_dict+ field copy) between iterations so the local transcript and any service-side conversation id reset too (in-loop working-state is discarded, pre-loop state preserved, continuity carried only by the progress log).
- Feedback tracking -
todos_remaining(*, looping_modes=None)/todos_remaining_message- Helper factories for todo-driven loops (the Python counterpart of .NET'sTodoCompletionLoopEvaluator), designed forcreate_harness_agentbut usable with any agent that registers aTodoProviderviacontext_providers. They resolve theTodoProvider/AgentModeProviderfrom the running agent (agent.context_providers, via_resolve_context_provider) rather than taking the provider as an argument, so they can be wired directly intoloop_should_continue/loop_next_message.todos_remainingreturns ashould_continuepredicate that loops while any todo is open; passlooping_modes=[...]to gate looping to specific operating modes (case-insensitive; honors theAgentModeProvider'ssource_id/available_modes),looping_modes=None(default) applies in every mode, and an empty sequence raisesValueError.todos_remaining_messageis anext_messagecallable that lists the still-open todo titles and tells the agent to finish them, returningNonewhen the session/agent/provider is unavailable or nothing is open (in which case the middleware's defaultNonehandling applies: reuse the previous iteration's messages verbatim under the defaultfresh_context=False, orDEFAULT_NEXT_MESSAGEonly whenfresh_context=True).background_tasks_running()/background_tasks_running_message- Helper factories for background-agent-driven loops, mirroring thetodos_remainingpair. They resolve theBackgroundAgentsProviderfrom the running agent (agent.context_providers, via_resolve_context_provider) rather than taking the provider as an argument, so they can be wired directly intocreate_harness_agent'sloop_should_continue/loop_next_message.background_tasks_runningreturns ashould_continuepredicate that loops while the provider's persisted state shows any task withstatus == RUNNING(pair it withmax_iterationsso the loop is bounded even if a task's persisted status is never refreshed).background_tasks_running_messageis anext_messagecallable that lists the still-running tasks (#<id> (<agent_name>): <description>) and tells the agent to wait for them to finish and retrieve their results, returningNonewhen the session/agent/provider is unavailable or no task is running.- Approval escape hatch -
_has_pending_approval_request(result)checks whether an iteration's response carries a pending tool-approval request (any content withtype == "function_approval_request"). Both the streaming and non-streaming loops stop and return that response to the caller before evaluatingshould_continue/max_iterationsor injectingnext_message, so the loop is HITL-safe even when wrapped outermost around aToolApprovalMiddleware(mirrors the C#LoopAgent'sHasPendingApprovalRequests). - Harness integration -
create_harness_agentenables the loop when aloop_should_continuecallable is passed; it prependsAgentLoopMiddleware(loop_should_continue, max_iterations=loop_max_iterations, next_message=loop_next_message)ahead ofToolApprovalMiddlewareso the loop is the outermost middleware (each iteration is a full agent run including tool approval, and the escape hatch hands pending approvals back to the caller).loop_next_messageandloop_max_iterationsonly take effect together withloop_should_continue(with noloop_should_continuethere is no loop, so they are ignored);loop_max_iterationsdefaults to the loop's default cap (None→ unbounded).
- Approval escape hatch -
Workflow- Graph-based workflow definitionWorkflowBuilder- Fluent API for building workflows, including explicitoutput_from/intermediate_output_fromselection for caller-facing emissions.output_fromis an allow-list for Workflow Output; unselected executor payloads are hidden unlessintermediate_output_fromselects them as Intermediate Output. Useoutput_from="all"for explicit all-output behavior andintermediate_output_from="all_other"for visible progress from every output-capable executor not selected byoutput_from.WorkflowRunResult- Non-streaming workflow result with Workflow Outputget_outputs()and Intermediate Outputget_intermediate_outputs()accessors- Orchestrators:
SequentialOrchestrator,ConcurrentOrchestrator,GroupChatOrchestrator,MagenticOrchestrator,HandoffOrchestrator
OpenAIChatClient- Chat client for the OpenAI Responses APIOpenAIChatCompletionClient- Chat client for the OpenAI Chat Completions API
FoundryChatClient- Chat client for Azure AI Foundry project endpoints
from agent_framework import Agent
from agent_framework.openai import OpenAIChatClient
agent = Agent(
client=OpenAIChatClient(),
instructions="You are helpful.",
tools=[my_function],
)
response = await agent.run("Hello")agent = OpenAIChatClient().as_agent(
name="Assistant",
instructions="You are helpful.",
)from agent_framework import Agent, AgentMiddleware, AgentContext
class LoggingMiddleware(AgentMiddleware):
async def process(self, context: AgentContext, call_next) -> None:
print(f"Input: {context.messages}")
await call_next()
print(f"Output: {context.result}")
agent = Agent(..., middleware=[LoggingMiddleware()])from agent_framework import BaseChatClient, ChatResponse, Message
class MyClient(BaseChatClient):
async def _inner_get_response(self, *, messages, options, **kwargs) -> ChatResponse:
# Call your LLM here
return ChatResponse(messages=[Message(role="assistant", contents=["Hi!"])])
async def _inner_get_streaming_response(self, *, messages, options, **kwargs):
yield ChatResponseUpdate(...)