Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions python/packages/core/agent_framework/_evaluation.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,14 @@

evals = FoundryEvals(project_client=client, model="gpt-4o")
results = await evaluate_agent(agent=agent, queries=["Hello"], evaluators=evals)
results.raise_for_status()
for result in results:
result.raise_for_status()

Local evaluator example:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Naveen Chatlapalli (@1aifanatic), this local example has another pre-existing problem: it calls tool_called_check without importing it, so copying it raises NameError before evaluation starts. Could you add tool_called_check to the agent_framework import here and in the LocalEvaluator basic-usage example, which has the same omission?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done — both local evaluator examples (module docstring and LocalEvaluator basic usage) now import tool_called_check. I ran each against an agent on a stub chat client: evaluation completes and reports the expected tool-check failure instead of a NameError.


.. code-block:: python

from agent_framework import LocalEvaluator, keyword_check, evaluate_agent
from agent_framework import LocalEvaluator, evaluate_agent, keyword_check, tool_called_check

local = LocalEvaluator(
keyword_check("weather", "temperature"),
Expand Down Expand Up @@ -1497,7 +1498,7 @@ class LocalEvaluator:

.. code-block:: python

from agent_framework import LocalEvaluator, keyword_check, evaluate_agent
from agent_framework import LocalEvaluator, evaluate_agent, keyword_check, tool_called_check

local = LocalEvaluator(
keyword_check("weather"),
Expand Down
26 changes: 19 additions & 7 deletions python/packages/core/agent_framework/_middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -661,7 +661,7 @@ class ChatContext:

class TokenCounterMiddleware(ChatMiddleware):
async def process(self, context: ChatContext, call_next):
print(f"Chat client: {context.chat_client.__class__.__name__}")
print(f"Chat client: {context.client.__class__.__name__}")
print(f"Messages: {len(context.messages)}")
print(f"Model: {context.options.get('model')}")

Expand Down Expand Up @@ -859,19 +859,31 @@ class AgentMiddleware(ABC):
Examples:
.. code-block:: python

from agent_framework import AgentMiddleware, AgentContext, Agent
from agent_framework import (
Agent,
AgentContext,
AgentMiddleware,
MiddlewareFailure,
MiddlewareTermination,
)


class RetryMiddleware(AgentMiddleware):
def __init__(self, max_retries: int = 3):
self.max_retries = max_retries

async def process(self, context: AgentContext, call_next):
for attempt in range(self.max_retries):
await call_next()
if context.result and not context.result.is_error:
break
print(f"Retry {attempt + 1}/{self.max_retries}")
for attempt in range(1, self.max_retries + 1):
try:
await call_next()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Naveen Chatlapalli (@1aifanatic) This retry example does not retry streaming failures — In streaming mode, await call_next() returns a lazy response stream before the provider request is consumed. The provider error happens later while the caller iterates the stream, after this middleware has already returned, so the try / except never sees it. Please either state that this example is for non-streaming calls only, or implement a stream-aware retry that stops retrying after the first update is emitted.

return
except (MiddlewareFailure, MiddlewareTermination):
# Abort and stop signals must reach the caller unchanged.
raise
except Exception:
Comment thread
eavanvalkenburg marked this conversation as resolved.
Resolved
if attempt == self.max_retries:
raise
print(f"Retry {attempt}/{self.max_retries}")


# Use with an agent
Expand Down
6 changes: 3 additions & 3 deletions python/packages/core/agent_framework/observability.py
Original file line number Diff line number Diff line change
Expand Up @@ -1058,7 +1058,7 @@ class ObservabilitySettings:
Examples:
.. code-block:: python

from agent_framework import ObservabilitySettings
from agent_framework.observability import ObservabilitySettings

# Using environment variables
# Instrumentation is enabled by default; set ENABLE_INSTRUMENTATION=false to disable.
Expand Down Expand Up @@ -1397,7 +1397,7 @@ def get_tracer(
Examples:
.. code-block:: python

from agent_framework import get_tracer
from agent_framework.observability import get_tracer

# Get default tracer
tracer = get_tracer()
Expand Down Expand Up @@ -1446,7 +1446,7 @@ def get_meter(
Examples:
.. code-block:: python

from agent_framework import get_meter
from agent_framework.observability import get_meter

# Get default meter
meter = get_meter()
Expand Down
13 changes: 7 additions & 6 deletions python/packages/core/agent_framework/security.py
Original file line number Diff line number Diff line change
Expand Up @@ -1343,7 +1343,8 @@ class LabelTrackingFunctionMiddleware(FunctionMiddleware, _SecurityScopeBinding)
Examples:
.. code-block:: python

from agent_framework import Agent, LabelTrackingFunctionMiddleware, tool
from agent_framework import Agent, tool

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Naveen Chatlapalli (@1aifanatic), the run call below was already broken before this PR: Agent.run leaves the OpenAI-style dictionary unchanged, and the OpenAI serializer then raises 'dict' object has no attribute 'role'. Could you import Message here and pass messages=[Message("user", ["What's the weather?"])], or simply use await agent.run("What's the weather?")?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done — the example now calls await agent.run("What's the weather?"). Ran it with LabelTrackingFunctionMiddleware on a stub chat client and it returns the response text.

from agent_framework.security import LabelTrackingFunctionMiddleware


@tool(additional_properties={"source_integrity": "trusted"})
Expand All @@ -1358,7 +1359,7 @@ async def get_weather(city: str) -> str:
agent = Agent(client=client, name="assistant", tools=[get_weather], middleware=[middleware])

# Run agent - untrusted tool results are automatically hidden
response = await agent.run(messages=[{"role": "user", "content": "What's the weather?"}])
response = await agent.run("What's the weather?")
"""

_requires_session_state = True
Expand Down Expand Up @@ -4314,7 +4315,7 @@ def get_security_tools() -> list[FunctionTool]:
from agent_framework.security import get_security_tools

agent = Agent(
chat_client=client,
client=client,
instructions="You are a helpful assistant.",
tools=[my_tool, *get_security_tools()],
)
Expand Down Expand Up @@ -4467,10 +4468,10 @@ async def apply_mcp_security_labels(

async with MCPStdioTool(name="github", command="gh-mcp", args=["stdio"]) as mcp:
await apply_mcp_security_labels(mcp)
agent = ChatCompletionAgent(
chat_client=client,
agent = Agent(
client=client,
tools=[mcp],
context_providers=[SecureAgentConfig(chat_client=client)],
context_providers=[SecureAgentConfig(quarantine_chat_client=client)],
)
"""
if not getattr(mcp_tool, "is_connected", False):
Expand Down
Loading