mirror of
https://github.com/microsoft/semantic-kernel.git
synced 2026-10-12 11:19:56 +00:00
### Motivation and Context <!-- Thank you for your contribution to the semantic-kernel repo! Please help reviewers and future users, providing the following information: 1. Why is this change required? 2. What problem does it solve? 3. What scenario does it contribute to? 4. If it fixes an open issue, please link to the issue here. --> We are adding two new attributes to the execute tool span according to this proposal to Otel Semantic Conventions: https://github.com/open-telemetry/semantic-conventions/pull/2528 ### Description <!-- Describe your changes, the overall approach, the underlying design. These notes will help understanding how your code works. Thanks! --> The following two attributes are added to the `execute_tool` span: 1. `gen_ai.tool.call.arguments` 2. `gen_ai.tool.call.result` ### Contribution Checklist <!-- Before submitting this PR, please make sure: --> - [x] The code builds clean without any errors or warnings - [x] The PR follows the [SK Contribution Guidelines](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md) and the [pre-submission formatting script](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md#development-scripts) raises no violations - [x] All unit tests pass, and I have added new tests where possible - [x] I didn't break anyone 😄
123 lines
4.9 KiB
Python
123 lines
4.9 KiB
Python
# Copyright (c) Microsoft. All rights reserved.
|
|
|
|
import json
|
|
from typing import TYPE_CHECKING, Any
|
|
|
|
from pydantic import BaseModel
|
|
|
|
from semantic_kernel.const import DEFAULT_SERVICE_NAME
|
|
|
|
if TYPE_CHECKING:
|
|
from collections.abc import Iterable
|
|
|
|
from _typeshed import SupportsKeysAndGetItem
|
|
|
|
from semantic_kernel.connectors.ai.prompt_execution_settings import PromptExecutionSettings
|
|
|
|
|
|
class KernelArguments(dict):
|
|
"""The arguments sent to the KernelFunction."""
|
|
|
|
def __init__(
|
|
self,
|
|
settings: (
|
|
"PromptExecutionSettings | list[PromptExecutionSettings] | dict[str, PromptExecutionSettings] | None"
|
|
) = None,
|
|
**kwargs: Any,
|
|
):
|
|
"""Initializes a new instance of the KernelArguments class.
|
|
|
|
This is a dict-like class with the additional field for the execution_settings.
|
|
|
|
This class is derived from a dict, hence behaves the same way,
|
|
just adds the execution_settings as a dict, with service_id and the settings.
|
|
|
|
Args:
|
|
settings (PromptExecutionSettings | List[PromptExecutionSettings] | None):
|
|
The settings for the execution.
|
|
If a list is given, make sure all items in the list have a unique service_id
|
|
as that is used as the key for the dict.
|
|
**kwargs (dict[str, Any]): The arguments for the function invocation, works similar to a regular dict.
|
|
"""
|
|
super().__init__(**kwargs)
|
|
settings_dict = None
|
|
if settings:
|
|
settings_dict = {}
|
|
if isinstance(settings, dict):
|
|
settings_dict = settings
|
|
elif isinstance(settings, list):
|
|
settings_dict = {s.service_id or DEFAULT_SERVICE_NAME: s for s in settings}
|
|
else:
|
|
settings_dict = {settings.service_id or DEFAULT_SERVICE_NAME: settings}
|
|
self.execution_settings: dict[str, "PromptExecutionSettings"] | None = settings_dict
|
|
|
|
def __bool__(self) -> bool:
|
|
"""Returns True if the arguments have any values."""
|
|
has_arguments = self.__len__() > 0
|
|
has_execution_settings = self.execution_settings is not None and len(self.execution_settings) > 0
|
|
return has_arguments or has_execution_settings
|
|
|
|
def __or__(self, value: dict) -> "KernelArguments":
|
|
"""Merges a KernelArguments with another KernelArguments or dict.
|
|
|
|
This implements the `|` operator for KernelArguments.
|
|
"""
|
|
if not isinstance(value, dict):
|
|
raise TypeError(
|
|
f"TypeError: unsupported operand type(s) for |: '{type(self).__name__}' and '{type(value).__name__}'"
|
|
)
|
|
|
|
# Merge execution settings
|
|
new_execution_settings = (self.execution_settings or {}).copy()
|
|
if isinstance(value, KernelArguments) and value.execution_settings:
|
|
new_execution_settings |= value.execution_settings
|
|
# Create a new KernelArguments with merged dict values
|
|
return KernelArguments(settings=new_execution_settings, **(dict(self) | dict(value)))
|
|
|
|
def __ror__(self, value: dict) -> "KernelArguments":
|
|
"""Merges a dict with a KernelArguments.
|
|
|
|
This implements the right-side `|` operator for KernelArguments.
|
|
"""
|
|
if not isinstance(value, dict):
|
|
raise TypeError(
|
|
f"TypeError: unsupported operand type(s) for |: '{type(value).__name__}' and '{type(self).__name__}'"
|
|
)
|
|
|
|
# Merge execution settings
|
|
new_execution_settings = {}
|
|
if isinstance(value, KernelArguments) and value.execution_settings:
|
|
new_execution_settings = value.execution_settings.copy()
|
|
if self.execution_settings:
|
|
new_execution_settings |= self.execution_settings
|
|
|
|
# Create a new KernelArguments with merged dict values
|
|
return KernelArguments(settings=new_execution_settings, **(dict(value) | dict(self)))
|
|
|
|
def __ior__(self, value: "SupportsKeysAndGetItem[Any, Any] | Iterable[tuple[Any, Any]]") -> "KernelArguments":
|
|
"""Merges into this KernelArguments with another KernelArguments or dict (in-place)."""
|
|
self.update(value)
|
|
|
|
# In-place merge execution settings
|
|
if isinstance(value, KernelArguments) and value.execution_settings:
|
|
if self.execution_settings:
|
|
self.execution_settings.update(value.execution_settings)
|
|
else:
|
|
self.execution_settings = value.execution_settings.copy()
|
|
|
|
return self
|
|
|
|
def dumps(self, include_execution_settings: bool = False) -> str:
|
|
"""Serializes the KernelArguments to a JSON string."""
|
|
data = dict(self)
|
|
if include_execution_settings and self.execution_settings:
|
|
data["execution_settings"] = self.execution_settings
|
|
|
|
def default(obj):
|
|
if isinstance(obj, BaseModel):
|
|
return obj.model_dump()
|
|
|
|
return str(obj)
|
|
|
|
return json.dumps(data, default=default)
|