Skip to content
Merged
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
3 changes: 3 additions & 0 deletions python/packages/a2a/agent_framework_a2a/_a2a_executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@
Message,
SupportsAgentRun,
)
from agent_framework._telemetry import mark_feature_used
from typing_extensions import override

from ._feature_usage import FeatureIndex
from ._utils import get_uri_data

logger = logging.getLogger("agent_framework.a2a")
Expand Down Expand Up @@ -147,6 +149,7 @@ async def execute(self, context: RequestContext, event_queue: EventQueue) -> Non
if context.message is None:
raise ValueError("Message must be provided in the RequestContext")

mark_feature_used(FeatureIndex.A2A)
query = context.get_user_input()
task = context.current_task

Expand Down
4 changes: 4 additions & 0 deletions python/packages/a2a/agent_framework_a2a/_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,12 @@
normalize_messages,
prepend_agent_framework_to_user_agent,
)
from agent_framework._telemetry import mark_feature_used
from agent_framework._types import AgentRunInputs
from agent_framework.observability import AgentTelemetryLayer
from google.protobuf.json_format import MessageToDict

from ._feature_usage import FeatureIndex
from ._utils import get_uri_data

if sys.version_info >= (3, 11):
Expand Down Expand Up @@ -542,6 +544,8 @@ async def _map_a2a_stream(
session: The agent session for context providers.
session_context: The session context for context providers.
"""
mark_feature_used(FeatureIndex.A2A)

if session_context is None:
session_context = SessionContext(input_messages=[], options={})

Expand Down
9 changes: 9 additions & 0 deletions python/packages/a2a/agent_framework_a2a/_feature_usage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Copyright (c) Microsoft. All rights reserved.

from enum import IntEnum


class FeatureIndex(IntEnum):
"""A2A-owned feature-usage indexes."""

A2A = 71
3 changes: 3 additions & 0 deletions python/packages/ag-ui/agent_framework_ag_ui/_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@

from ag_ui.core import BaseEvent
from agent_framework import SupportsAgentRun
from agent_framework._telemetry import mark_feature_used

from ._agent_run import PendingApprovalEntry, PendingApprovalKey, run_agent_stream
from ._approval_state import InMemoryAGUIApprovalStateStore
from ._feature_usage import FeatureIndex
from ._snapshots import AGUIThreadSnapshotStore


Expand Down Expand Up @@ -142,6 +144,7 @@ async def run(
Yields:
AG-UI events
"""
mark_feature_used(FeatureIndex.AG_UI)
async for event in run_agent_stream(
input_data,
self.agent,
Expand Down
3 changes: 3 additions & 0 deletions python/packages/ag-ui/agent_framework_ag_ui/_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,12 @@
ResponseStream,
)
from agent_framework._middleware import ChatMiddlewareLayer
from agent_framework._telemetry import mark_feature_used
from agent_framework._tools import FunctionInvocationConfiguration, FunctionInvocationLayer
from agent_framework.observability import ChatTelemetryLayer

from ._event_converters import AGUIEventConverter
from ._feature_usage import FeatureIndex
from ._http_service import AGUIHttpService, _serialize_available_interrupts, _serialize_resume
from ._message_adapters import agent_framework_messages_to_agui
from ._utils import convert_tools_to_agui_format
Expand Down Expand Up @@ -398,6 +400,7 @@ async def _streaming_impl(
Yields:
ChatResponseUpdate objects
"""
mark_feature_used(FeatureIndex.AG_UI)
messages_to_send, state = self._extract_state_from_messages(messages)

thread_id = self._get_thread_id(options)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Copyright (c) Microsoft. All rights reserved.

from enum import IntEnum


class FeatureIndex(IntEnum):
"""AG-UI-owned feature-usage indexes."""

AG_UI = 72
3 changes: 3 additions & 0 deletions python/packages/ag-ui/agent_framework_ag_ui/_workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@
ToolCallStartEvent,
)
from agent_framework import Workflow
from agent_framework._telemetry import mark_feature_used

from ._feature_usage import FeatureIndex
from ._message_adapters import agui_messages_to_snapshot_format
from ._run_common import (
_build_run_finished_event,
Expand Down Expand Up @@ -298,6 +300,7 @@ async def run(self, input_data: dict[str, Any]) -> AsyncGenerator[BaseEvent]:

Subclasses may override this to provide custom AG-UI streams.
"""
mark_feature_used(FeatureIndex.AG_UI)
thread_id = self._thread_id_from_input(input_data)
run_id = str(input_data.get("run_id") or input_data.get("runId") or uuid.uuid4())
snapshot_scope = cast(str | None, input_data.get(_SNAPSHOT_SCOPE_INPUT_KEY))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
tool,
)
from agent_framework._settings import SecretString, load_settings
from agent_framework._telemetry import get_user_agent
from agent_framework._telemetry import get_user_agent, mark_feature_used
from agent_framework._tools import SHELL_TOOL_KIND_VALUE, normalize_tools
from agent_framework._types import _get_data_bytes_as_str # type: ignore
from agent_framework.observability import ChatTelemetryLayer
Expand All @@ -55,6 +55,8 @@
from anthropic.types.beta.beta_encrypted_code_execution_result_block import BetaEncryptedCodeExecutionResultBlock
from pydantic import BaseModel

from ._feature_usage import FeatureIndex

if sys.version_info >= (3, 11):
from typing import TypedDict # pragma: no cover
else:
Expand Down Expand Up @@ -550,6 +552,7 @@ async def _stream() -> AsyncIterable[ChatResponseUpdate]:
# each message_delta carries the running total), so thread a per-stream
# accumulator to _process_stream_event to emit increments instead.
emitted_usage: dict[str, int] = {}
mark_feature_used(FeatureIndex.ANTHROPIC)
async for chunk in await self.anthropic_client.beta.messages.create(**run_options, stream=True):
parsed_chunk = self._process_stream_event(chunk, emitted_usage)
if parsed_chunk:
Expand All @@ -559,6 +562,7 @@ async def _stream() -> AsyncIterable[ChatResponseUpdate]:

# Non-streaming mode
async def _get_response() -> ChatResponse:
mark_feature_used(FeatureIndex.ANTHROPIC)
message = await self.anthropic_client.beta.messages.create(**run_options, stream=False)
return self._process_message(message, options)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Copyright (c) Microsoft. All rights reserved.

from enum import IntEnum


class FeatureIndex(IntEnum):
"""Anthropic-owned feature-usage indexes."""

ANTHROPIC = 57
10 changes: 6 additions & 4 deletions python/packages/anthropic/tests/test_anthropic_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
FunctionInvocationLayer,
Message,
SupportsChatGetResponse,
UsageDetails,
tool,
)
from agent_framework._settings import load_settings
Expand All @@ -33,6 +32,7 @@

from agent_framework_anthropic import AnthropicClient, RawAnthropicClient
from agent_framework_anthropic._chat_client import AnthropicSettings
from agent_framework_anthropic._feature_usage import FeatureIndex

# Test constants
VALID_PNG_BASE64 = b"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="
Expand Down Expand Up @@ -1606,10 +1606,12 @@ async def test_inner_get_response(mock_anthropic_client: MagicMock) -> None:
messages = [Message(role="user", contents=["Hi"])]
chat_options = ChatOptions(max_tokens=10)

response = await client._inner_get_response( # type: ignore[attr-defined]
messages=messages, options=chat_options
)
with patch("agent_framework_anthropic._chat_client.mark_feature_used") as mark_feature_used:
response = await client._inner_get_response( # type: ignore[attr-defined]
messages=messages, options=chat_options
)

mark_feature_used.assert_called_once_with(FeatureIndex.ANTHROPIC)
assert response is not None
assert response.response_id == "msg_test"
assert len(response.messages) == 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
SupportsGetEmbeddings,
load_settings,
)
from agent_framework._telemetry import get_user_agent
from agent_framework._telemetry import get_user_agent, mark_feature_used
from agent_framework.exceptions import SettingNotFoundError
from azure.core.credentials import AzureKeyCredential, TokenCredential
from azure.core.credentials_async import AsyncTokenCredential
Expand All @@ -47,6 +47,8 @@
VectorizedQuery,
)

from ._feature_usage import FeatureIndex

if TYPE_CHECKING:
from agent_framework._agents import SupportsAgentRun
from azure.search.documents.knowledgebases.aio import KnowledgeBaseRetrievalClient
Expand Down Expand Up @@ -626,6 +628,7 @@ async def before_run(
state: dict[str, Any],
) -> None:
"""Retrieve relevant context from Azure AI Search and add to session context."""
mark_feature_used(FeatureIndex.AZURE_AI_SEARCH)
messages_list = list(context.input_messages)

filtered_messages = [
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Copyright (c) Microsoft. All rights reserved.

from enum import IntEnum


class FeatureIndex(IntEnum):
"""Azure AI Search-owned feature-usage indexes."""

AZURE_AI_SEARCH = 65
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
KnowledgeBaseOutputModeLiteral,
RetrievalReasoningEffortLiteral,
)
from agent_framework_azure_ai_search._feature_usage import FeatureIndex

# -- Helpers -------------------------------------------------------------------

Expand Down Expand Up @@ -54,6 +55,17 @@ async def __anext__(self):
return doc


async def test_before_run_marks_azure_ai_search_used() -> None:
provider = object.__new__(AzureAISearchContextProvider)
context = Mock(spec=SessionContext)
context.input_messages = []

with patch("agent_framework_azure_ai_search._context_provider.mark_feature_used") as mark_feature_used:
await provider.before_run(agent=Mock(), session=Mock(spec=AgentSession), context=context, state={})

mark_feature_used.assert_called_once_with(FeatureIndex.AZURE_AI_SEARCH)


def _make_mock_index(
fields: list[SimpleNamespace] | None = None,
profiles: list[SimpleNamespace] | None = None,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,15 @@
)
from agent_framework._sessions import AgentSession
from agent_framework._settings import load_settings
from agent_framework._telemetry import mark_feature_used
from azure.ai.contentunderstanding import to_llm_input
from azure.ai.contentunderstanding.aio import ContentUnderstandingClient
from azure.ai.contentunderstanding.models import AnalysisInput, AnalysisResult
from azure.core.credentials import AzureKeyCredential
from azure.core.credentials_async import AsyncTokenCredential

from ._feature_usage import FeatureIndex

if TYPE_CHECKING:
from agent_framework._agents import SupportsAgentRun

Expand Down Expand Up @@ -275,6 +278,7 @@ async def before_run(

This method is called automatically by the framework before each LLM invocation.
"""
mark_feature_used(FeatureIndex.AZURE_CONTENTUNDERSTANDING)
documents: dict[str, DocumentEntry] = state.setdefault("documents", {})

# Per-session mutable state — isolated per session to prevent cross-session leakage.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Copyright (c) Microsoft. All rights reserved.

from enum import IntEnum


class FeatureIndex(IntEnum):
"""Azure Content Understanding-owned feature-usage indexes."""

AZURE_CONTENTUNDERSTANDING = 67
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import json
import re
from typing import Any, cast
from unittest.mock import AsyncMock, MagicMock
from unittest.mock import AsyncMock, MagicMock, patch

from agent_framework import Content, Message, SessionContext
from agent_framework._sessions import AgentSession
Expand All @@ -18,6 +18,7 @@
DocumentStatus,
)
from agent_framework_azure_contentunderstanding._detection import SUPPORTED_MEDIA_TYPES, derive_doc_key
from agent_framework_azure_contentunderstanding._feature_usage import FeatureIndex

# ---------------------------------------------------------------------------
# Helpers
Expand Down Expand Up @@ -983,7 +984,12 @@ async def test_lazy_initialization_on_before_run(self) -> None:
state: dict[str, Any] = {}
session = AgentSession()

await provider.before_run(agent=_make_mock_agent(), session=session, context=context, state=state)
with patch(
"agent_framework_azure_contentunderstanding._context_provider.mark_feature_used"
) as mark_feature_used:
await provider.before_run(agent=_make_mock_agent(), session=session, context=context, state=state)

mark_feature_used.assert_called_once_with(FeatureIndex.AZURE_CONTENTUNDERSTANDING)
# Client should still be set
assert provider._client is not None

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@

from agent_framework import AgentSession, ContextProvider, Message, SessionContext
from agent_framework._settings import load_settings
from agent_framework._telemetry import mark_feature_used

from ._feature_usage import FeatureIndex

if sys.version_info >= (3, 11):
from typing import Self # pragma: no cover
Expand Down Expand Up @@ -344,6 +347,8 @@ async def before_run(
context: The invocation context to add memories to.
state: Provider-scoped mutable state.
"""
mark_feature_used(FeatureIndex.AZURE_COSMOS_MEMORY)

# Extract query from input messages
query_text = "\n".join(msg.text for msg in context.input_messages if msg.text and msg.text.strip())

Expand Down Expand Up @@ -424,6 +429,8 @@ async def after_run(
context: The invocation context with response populated.
state: Provider-scoped mutable state.
"""
mark_feature_used(FeatureIndex.AZURE_COSMOS_MEMORY)

# Get user_id and thread_id from provider-scoped state (falling back to the session id)
user_id = self._resolve_user_id(state, session)
thread_id = state.get("thread_id") or session.session_id or "default"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Copyright (c) Microsoft. All rights reserved.

from enum import IntEnum


class FeatureIndex(IntEnum):
"""Azure Cosmos DB memory-owned feature-usage indexes."""

AZURE_COSMOS_MEMORY = 82
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,29 @@
DEFAULT_CONTEXT_PROMPT,
CosmosMemoryContextProvider,
)
from agent_framework_azure_cosmos_memory._feature_usage import FeatureIndex

# The provider methods accept an ``agent`` implementing ``SupportsAgentRun`` but never
# use it in these tests, so a typed ``None`` stub keeps the call sites clean.
_STUB_AGENT: Any = None


async def test_before_run_marks_cosmos_memory_used_before_empty_return() -> None:
provider = object.__new__(CosmosMemoryContextProvider)
context = MagicMock(spec=SessionContext)
context.input_messages = []

with patch("agent_framework_azure_cosmos_memory._context_provider.mark_feature_used") as mark_feature_used:
await provider.before_run(
agent=_STUB_AGENT,
session=MagicMock(spec=AgentSession),
context=context,
state={},
)

mark_feature_used.assert_called_once_with(FeatureIndex.AZURE_COSMOS_MEMORY)


@pytest.fixture
def mock_memory_client() -> AsyncMock:
"""Create a mock AsyncCosmosMemoryClient."""
Expand Down
Loading
Loading