Skip to content

Commit 7607acd

Browse files
CopilotTaoChenOSU
andauthored
Python: Add experimental decorator to all Evals pieces (microsoft#5040)
* Initial plan * feat(python): add experimental decorator to all Evals pieces Agent-Logs-Url: https://github.com/microsoft/agent-framework/sessions/99d71249-a5d6-4977-a5b5-6ffe0a3be2bc Co-authored-by: TaoChenOSU <12570346+TaoChenOSU@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: TaoChenOSU <12570346+TaoChenOSU@users.noreply.github.com>
1 parent 3d87cec commit 7607acd

3 files changed

Lines changed: 25 additions & 0 deletions

File tree

python/packages/core/agent_framework/_evaluation.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
runtime_checkable,
5454
)
5555

56+
from ._feature_stage import ExperimentalFeature, experimental
5657
from ._tools import FunctionTool
5758
from ._types import AgentResponse, Message
5859

@@ -64,13 +65,15 @@
6465
logger = logging.getLogger(__name__)
6566

6667

68+
@experimental(feature_id=ExperimentalFeature.EVALS)
6769
class EvalNotPassedError(Exception):
6870
"""Raised when evaluation results contain failures."""
6971

7072

7173
# region Core types
7274

7375

76+
@experimental(feature_id=ExperimentalFeature.EVALS)
7477
@runtime_checkable
7578
class ConversationSplitter(Protocol):
7679
"""Strategy for splitting a conversation into (query, response) messages.
@@ -103,6 +106,7 @@ def split_before_memory(conversation):
103106
def __call__(self, conversation: list[Message]) -> tuple[list[Message], list[Message]]: ...
104107

105108

109+
@experimental(feature_id=ExperimentalFeature.EVALS)
106110
class ConversationSplit(str, Enum):
107111
"""Built-in conversation split strategies.
108112
@@ -131,6 +135,7 @@ def __call__(self, conversation: list[Message]) -> tuple[list[Message], list[Mes
131135
return _BUILT_IN_SPLITTERS[self](conversation)
132136

133137

138+
@experimental(feature_id=ExperimentalFeature.EVALS)
134139
@dataclass
135140
class ExpectedToolCall:
136141
"""A tool call that an agent is expected to make.
@@ -173,6 +178,7 @@ def _split_full(conversation: list[Message]) -> tuple[list[Message], list[Messag
173178
}
174179

175180

181+
@experimental(feature_id=ExperimentalFeature.EVALS)
176182
class EvalItem:
177183
"""A single item to be evaluated.
178184
@@ -295,6 +301,7 @@ def per_turn_items(
295301
# region Score and result types
296302

297303

304+
@experimental(feature_id=ExperimentalFeature.EVALS)
298305
@dataclass
299306
class EvalScoreResult:
300307
"""Result from a single evaluator on a single item.
@@ -312,6 +319,7 @@ class EvalScoreResult:
312319
sample: dict[str, Any] | None = None
313320

314321

322+
@experimental(feature_id=ExperimentalFeature.EVALS)
315323
@dataclass
316324
class EvalItemResult:
317325
"""Per-item result from an evaluation run.
@@ -358,6 +366,7 @@ def is_failed(self) -> bool:
358366
return self.status == "fail"
359367

360368

369+
@experimental(feature_id=ExperimentalFeature.EVALS)
361370
class EvalResults:
362371
"""Results from an evaluation run by a single provider.
363372
@@ -493,6 +502,7 @@ def raise_for_status(self, msg: str | None = None) -> None:
493502
# region Evaluator protocol
494503

495504

505+
@experimental(feature_id=ExperimentalFeature.EVALS)
496506
@runtime_checkable
497507
class Evaluator(Protocol):
498508
"""Protocol for evaluation providers.
@@ -543,6 +553,7 @@ async def evaluate(
543553
# region Converter
544554

545555

556+
@experimental(feature_id=ExperimentalFeature.EVALS)
546557
class AgentEvalConverter:
547558
"""Converts agent-framework types to evaluation format.
548559
@@ -846,6 +857,7 @@ def _extract_overall_query(workflow_result: WorkflowRunResult) -> str | None:
846857
# region Local evaluation checks
847858

848859

860+
@experimental(feature_id=ExperimentalFeature.EVALS)
849861
@dataclass
850862
class CheckResult:
851863
"""Result of a single check on a single evaluation item.
@@ -870,6 +882,7 @@ class CheckResult:
870882
"""
871883

872884

885+
@experimental(feature_id=ExperimentalFeature.EVALS)
873886
def keyword_check(*keywords: str, case_sensitive: bool = False) -> EvalCheck:
874887
"""Check that the response contains all specified keywords.
875888
@@ -897,6 +910,7 @@ def _check(item: EvalItem) -> CheckResult:
897910
return _check
898911

899912

913+
@experimental(feature_id=ExperimentalFeature.EVALS)
900914
def tool_called_check(*tool_names: str, mode: Literal["all", "any"] = "all") -> EvalCheck:
901915
"""Check that specific tools were called during the conversation.
902916
@@ -979,6 +993,7 @@ def _extract_tool_calls(item: EvalItem) -> list[tuple[str, dict[str, Any] | None
979993
return calls
980994

981995

996+
@experimental(feature_id=ExperimentalFeature.EVALS)
982997
def tool_calls_present(item: EvalItem) -> CheckResult:
983998
"""Check that all expected tool calls were made (unordered, extras OK).
984999
@@ -1020,6 +1035,7 @@ def tool_calls_present(item: EvalItem) -> CheckResult:
10201035
)
10211036

10221037

1038+
@experimental(feature_id=ExperimentalFeature.EVALS)
10231039
def tool_call_args_match(item: EvalItem) -> CheckResult:
10241040
"""Check that expected tool calls match on name and arguments.
10251041
@@ -1220,6 +1236,7 @@ def evaluator(fn: Callable[..., Any], /) -> EvalCheck: ...
12201236
def evaluator(*, name: str | None = None) -> Callable[[Callable[..., Any]], EvalCheck]: ...
12211237

12221238

1239+
@experimental(feature_id=ExperimentalFeature.EVALS)
12231240
def evaluator(
12241241
fn: Callable[..., Any] | None = None,
12251242
*,
@@ -1322,6 +1339,7 @@ async def _run_check(check_fn: EvalCheck, item: EvalItem) -> CheckResult:
13221339
return result
13231340

13241341

1342+
@experimental(feature_id=ExperimentalFeature.EVALS)
13251343
class LocalEvaluator:
13261344
"""Evaluation provider that runs checks locally without API calls.
13271345
@@ -1431,6 +1449,7 @@ async def evaluate(
14311449
# region Public orchestration functions
14321450

14331451

1452+
@experimental(feature_id=ExperimentalFeature.EVALS)
14341453
async def evaluate_agent(
14351454
*,
14361455
agent: SupportsAgentRun | None = None,
@@ -1634,6 +1653,7 @@ async def evaluate_agent(
16341653
return await _run_evaluators(evaluators, items, eval_name=name)
16351654

16361655

1656+
@experimental(feature_id=ExperimentalFeature.EVALS)
16371657
async def evaluate_workflow(
16381658
*,
16391659
workflow: Workflow,

python/packages/core/agent_framework/_feature_stage.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ class ExperimentalFeature(str, Enum):
4646
on enum membership or attribute presence over time.
4747
"""
4848

49+
EVALS = "EVALS"
4950
SKILLS = "SKILLS"
5051

5152

python/packages/foundry/agent_framework_foundry/_foundry_evals.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
EvalResults,
4141
EvalScoreResult,
4242
)
43+
from agent_framework._feature_stage import ExperimentalFeature, experimental
4344
from openai import AsyncOpenAI
4445

4546
from ._chat_client import FoundryChatClient
@@ -491,6 +492,7 @@ async def _evaluate_via_responses_impl(
491492
# ---------------------------------------------------------------------------
492493

493494

495+
@experimental(feature_id=ExperimentalFeature.EVALS)
494496
class FoundryEvals:
495497
"""Evaluation provider backed by Microsoft Foundry.
496498
@@ -727,6 +729,7 @@ async def _evaluate_via_dataset(
727729
# ---------------------------------------------------------------------------
728730

729731

732+
@experimental(feature_id=ExperimentalFeature.EVALS)
730733
async def evaluate_traces(
731734
*,
732735
evaluators: Sequence[str] | None = None,
@@ -817,6 +820,7 @@ async def evaluate_traces(
817820
return await _poll_eval_run(oai_client, eval_obj.id, run.id, poll_interval, timeout)
818821

819822

823+
@experimental(feature_id=ExperimentalFeature.EVALS)
820824
async def evaluate_foundry_target(
821825
*,
822826
target: dict[str, Any],

0 commit comments

Comments
 (0)