chore: import upstream snapshot with attribution
CodeQL / Analyze (csharp) (push) Waiting to run
CodeQL / Analyze (python) (push) Waiting to run

This commit is contained in:
wehub-resource-sync
2026-07-13 13:21:23 +08:00
commit b957a53def
5423 changed files with 863745 additions and 0 deletions
@@ -0,0 +1,239 @@
# Copyright (c) Microsoft. All rights reserved.
import json
import logging
import re
from collections.abc import Mapping
from typing import TYPE_CHECKING, Any, ClassVar, Final, Literal, TypeVar
from xml.etree.ElementTree import Element # nosec
from pydantic import Field
from typing_extensions import deprecated
from semantic_kernel.const import DEFAULT_FULLY_QUALIFIED_NAME_SEPARATOR
from semantic_kernel.contents.const import FUNCTION_CALL_CONTENT_TAG, ContentTypes
from semantic_kernel.contents.kernel_content import KernelContent
from semantic_kernel.exceptions import (
ContentAdditionException,
ContentInitializationError,
FunctionCallInvalidArgumentsException,
FunctionCallInvalidNameException,
)
if TYPE_CHECKING:
from semantic_kernel.functions.kernel_arguments import KernelArguments
logger = logging.getLogger(__name__)
_T = TypeVar("_T", bound="FunctionCallContent")
EMPTY_VALUES: Final[list[str | None]] = ["", "{}", None]
class FunctionCallContent(KernelContent):
"""Class to hold a function call response."""
content_type: Literal[ContentTypes.FUNCTION_CALL_CONTENT] = Field(FUNCTION_CALL_CONTENT_TAG, init=False) # type: ignore
tag: ClassVar[str] = FUNCTION_CALL_CONTENT_TAG
id: str | None
call_id: str | None = None
index: int | None = None
name: str | None = None
function_name: str
plugin_name: str | None = None
arguments: str | Mapping[str, Any] | None = None
def __init__(
self,
inner_content: Any | None = None,
ai_model_id: str | None = None,
id: str | None = None,
index: int | None = None,
name: str | None = None,
function_name: str | None = None,
plugin_name: str | None = None,
arguments: str | Mapping[str, Any] | None = None,
metadata: dict[str, Any] | None = None,
call_id: str | None = None,
**kwargs: Any,
) -> None:
"""Create function call content.
Args:
inner_content (Any | None): The inner content.
ai_model_id (str | None): The id of the AI model.
id (str | None): The id of the function call.
index (int | None): The index of the function call.
name (str | None): The name of the function call.
When not supplied function_name and plugin_name should be supplied.
function_name (str | None): The function name.
Not used when 'name' is supplied.
plugin_name (str | None): The plugin name.
Not used when 'name' is supplied.
arguments (str | dict[str, Any] | None): The arguments of the function call.
metadata (dict[str, Any] | None): The metadata of the function call.
call_id (str | None): The id of the call.
kwargs (Any): Additional arguments.
"""
if function_name and plugin_name and not name:
name = f"{plugin_name}{DEFAULT_FULLY_QUALIFIED_NAME_SEPARATOR}{function_name}"
if name and not function_name and not plugin_name:
if DEFAULT_FULLY_QUALIFIED_NAME_SEPARATOR in name:
plugin_name, function_name = name.split(DEFAULT_FULLY_QUALIFIED_NAME_SEPARATOR, maxsplit=1)
else:
function_name = name
args = {
"inner_content": inner_content,
"ai_model_id": ai_model_id,
"id": id,
"call_id": call_id,
"index": index,
"name": name,
"function_name": function_name or "",
"plugin_name": plugin_name,
"arguments": arguments,
}
if metadata:
args["metadata"] = metadata
super().__init__(**args)
def __str__(self) -> str:
"""Return the function call as a string."""
if isinstance(self.arguments, dict):
return f"{self.name}({json.dumps(self.arguments)})"
return f"{self.name}({self.arguments})"
def __add__(self, other: "FunctionCallContent | None") -> "FunctionCallContent":
"""Add two function calls together, combines the arguments, ignores the name.
When both function calls have a dict as arguments, the arguments are merged,
which means that the arguments of the second function call
will overwrite the arguments of the first function call if the same key is present.
When one of the two arguments are a dict and the other a string, we raise a ContentAdditionException.
"""
if not other:
return self
if self.id and other.id and self.id != other.id:
raise ContentAdditionException("Function calls have different ids.")
if self.index != other.index:
raise ContentAdditionException("Function calls have different indexes.")
if self.call_id and other.call_id and self.call_id != other.call_id:
raise ContentAdditionException("Function calls have different call ids.")
return FunctionCallContent(
id=self.id or other.id,
call_id=self.call_id or other.call_id,
index=self.index or other.index,
name=self.name or other.name,
arguments=self.combine_arguments(self.arguments, other.arguments),
metadata=self.metadata | other.metadata,
)
def combine_arguments(
self, arg1: str | Mapping[str, Any] | None, arg2: str | Mapping[str, Any] | None
) -> str | Mapping[str, Any]:
"""Combine two arguments."""
if isinstance(arg1, Mapping) and isinstance(arg2, Mapping):
return {**arg1, **arg2}
# when one of the two is a dict, and the other isn't, we raise.
if isinstance(arg1, Mapping) or isinstance(arg2, Mapping):
raise ContentAdditionException("Cannot combine a dict with a string.")
if arg1 in EMPTY_VALUES and arg2 in EMPTY_VALUES:
return "{}"
if arg1 in EMPTY_VALUES:
return arg2 or "{}"
if arg2 in EMPTY_VALUES:
return arg1 or "{}"
return (arg1 or "") + (arg2 or "")
def parse_arguments(self) -> Mapping[str, Any] | None:
"""Parse the arguments into a dictionary."""
if not self.arguments:
return None
if isinstance(self.arguments, Mapping):
return self.arguments
try:
return json.loads(self.arguments)
except json.JSONDecodeError as exc:
logger.debug("Function Call arguments are not valid JSON. Trying to preprocess.")
try:
# Python strings can be single quoted, but JSON strings should be double quoted.
# JSON keys and values should be enclosed in double quotes.
# Replace single quotes with double quotes, but not if it's an escaped single quote.
return json.loads(re.sub(r"(?<!\\)'", '"', self.arguments).replace("\\'", "'"))
except json.JSONDecodeError:
raise FunctionCallInvalidArgumentsException(
"Function Call arguments are not valid JSON even after preprocessing."
) from exc
def to_kernel_arguments(self) -> "KernelArguments":
"""Return the arguments as a KernelArguments instance."""
from semantic_kernel.functions.kernel_arguments import KernelArguments
args = self.parse_arguments()
if not args:
return KernelArguments()
return KernelArguments(**args)
@deprecated("The function_name and plugin_name properties should be used instead.")
def split_name(self) -> list[str | None]:
"""Split the name into a plugin and function name."""
if not self.function_name:
raise FunctionCallInvalidNameException("Function name is not set.")
return [self.plugin_name or "", self.function_name]
@deprecated("The function_name and plugin_name properties should be used instead.")
def split_name_dict(self) -> dict:
"""Split the name into a plugin and function name."""
return {"plugin_name": self.plugin_name, "function_name": self.function_name}
def custom_fully_qualified_name(self, separator: str) -> str:
"""Get the fully qualified name of the function with a custom separator.
Args:
separator (str): The custom separator.
Returns:
The fully qualified name of the function with a custom separator.
"""
return f"{self.plugin_name}{separator}{self.function_name}" if self.plugin_name else self.function_name
def to_element(self) -> Element:
"""Convert the function call to an Element."""
element = Element(self.tag)
if self.id:
element.set("id", self.id)
if self.name:
element.set("name", self.name)
if self.arguments:
element.text = json.dumps(self.arguments) if isinstance(self.arguments, Mapping) else self.arguments
return element
@classmethod
def from_element(cls: type[_T], element: Element) -> _T:
"""Create an instance from an Element."""
if element.tag != cls.tag:
raise ContentInitializationError(f"Element tag is not {cls.tag}") # pragma: no cover
return cls(name=element.get("name"), id=element.get("id"), arguments=element.text or "")
def to_dict(self) -> dict[str, str | Any]:
"""Convert the instance to a dictionary."""
args = json.dumps(self.arguments) if isinstance(self.arguments, Mapping) else self.arguments
return {"id": self.id, "type": "function", "function": {"name": self.name, "arguments": args}}
def __hash__(self) -> int:
"""Return the hash of the function call content."""
args_hashable = frozenset(self.arguments.items()) if isinstance(self.arguments, Mapping) else None
return hash((
self.tag,
self.id,
self.call_id,
self.index,
self.name,
self.function_name,
self.plugin_name,
args_hashable,
))