You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Python openapi plugin is not working (Error while registering Rest function students./api/controller_get: KernelFunction failed to initialize: Failed to create KernelFunctionMetadata)
#10104
Closed
kkkumar2 opened this issue
Jan 7, 2025
· 3 comments
I am just trying out semantic kernel and i am testing the native function based plugin and OPENAPI based plugin , while just native function based plugin is working fine but OPENAPI based plugin is throwing an error . Upon debugging i found out that the issue was function name is having "/" which is not supported by the regex pattern in class KernelFunctionMetadata (name: str = Field(..., pattern=FUNCTION_NAME_REGEX)). API can have "/" in their paths and since i am directly using swagger.json function name is inferrred from paths only. for this scenario function name inferred is /api/controller_get.
FUNCTION_NAME_REGEX = r"^[0-9A-Za-z_]+$"
Code:
importasynciofromsemantic_kernelimportKernelfromsemantic_kernel.utils.loggingimportsetup_loggingfromsemantic_kernel.functionsimportkernel_functionfromsemantic_kernel.connectors.ai.open_aiimportAzureChatCompletionfromsemantic_kernel.connectors.ai.function_choice_behaviorimportFunctionChoiceBehaviorfromsemantic_kernel.connectors.ai.chat_completion_client_baseimportChatCompletionClientBasefromsemantic_kernel.contents.chat_historyimportChatHistoryfromsemantic_kernel.functions.kernel_argumentsimportKernelArgumentsfromsemantic_kernel.connectors.ai.open_ai.prompt_execution_settings.azure_chat_prompt_execution_settingsimport (
AzureChatPromptExecutionSettings,
)
importloggingfromsemantic_kernel.connectors.openapi_plugin.openapi_function_execution_parametersimportOpenAPIFunctionExecutionParametersfromtypingimportAnnotatedfromsemantic_kernel.functionsimportkernel_functionclassLightsPlugin:
lights= [
{"id": 1, "name": "Table Lamp", "is_on": False},
{"id": 2, "name": "Porch light", "is_on": False},
{"id": 3, "name": "Chandelier", "is_on": True},
]
@kernel_function(name="get_lights",description="Gets a list of lights and their current state", )defget_state(
self,
) ->Annotated[str, "the output is a string"]:
"""Gets a list of lights and their current state."""returnself.lights@kernel_function(name="change_state",description="Changes the state of the light", )defchange_state(
self,
id: int,
is_on: bool,
) ->Annotated[str, "the output is a string"]:
"""Changes the state of the light."""forlightinself.lights:
iflight["id"] ==id:
light["is_on"] =is_onreturnlightreturnNoneasyncdefmain():
# Initialize the kernelkernel=Kernel()
# Add Azure OpenAI chat completionchat_completion=AzureChatCompletion(
deployment_name="",
api_key="",
base_url="",
)
kernel.add_service(chat_completion)
# Set the logging level for semantic_kernel.kernel to DEBUG.setup_logging()
logging.getLogger("kernel").setLevel(logging.DEBUG)
# Add a plugin (the LightsPlugin class is defined below)# kernel.add_plugin(# LightsPlugin(),# plugin_name="Lights",# )kernel.add_plugin_from_openapi(
plugin_name="students",
openapi_document_path="./openapi.json",)
#openapi_parsed_spec=json_data,#execution_settings=OpenAPIFunctionExecutionParameters(enable_payload_namespacing=True,))# Enable planningexecution_settings=AzureChatPromptExecutionSettings()
execution_settings.function_choice_behavior=FunctionChoiceBehavior.Auto()
# Create a history of the conversationhistory=ChatHistory()
# Initiate a back-and-forth chatuserInput=NonewhileTrue:
# Collect user inputuserInput=input("User > ")
# Terminate the loop if the user says "exit"ifuserInput=="exit":
break# Add user input to the historyhistory.add_user_message(userInput)
# Get the response from the AIresult=awaitchat_completion.get_chat_message_content(
chat_history=history,
settings=execution_settings,
kernel=kernel,
)
# Print the resultsprint("Assistant > "+str(result))
# Add the message from the agent to the chat historyhistory.add_message(result)
# Run the main functionif__name__=="__main__":
asyncio.run(main())
OpenAPI.json
{"openapi": "3.0.1", "info": {"title": "WebApplication1", "version": "1.0"}, "paths": {"/api/controller": {"get": {"tags": ["Student"], "summary": "Gets the list of students.", "description": "This endpoint returns a list of student objects with their ID and name.", "responses": {"200": {"description": "OK", "content": {"text/plain": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/Student"}}}, "application/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/Student"}}}, "text/json": {"schema": {"type": "array", "items": {"$ref": "#/components/schemas/Student"}}}}}, "500": {"description": "Internal Server Error"}}}}}, "components": {"schemas": {"Student": {"type": "object", "properties": {"id": {"type": "integer", "format": "int32"}, "name": {"type": "string", "nullable": true}}, "additionalProperties": false}}}}
Hi @kkkumar2, thanks for filing the issue. As you've found, the function name violates our function name regex requirement. The default logic in OpenApiParser sets:
yes @moonbox3 , adding operationId in json file is working properly. how much weightage does function_name in kernel function has ? suppose if i set operationid (function_name) to GUID , does it have any impact while function calling ? I have tested it with smaller set of functions and it seems to be working fine when i set GUID to operationId
I’d recommend keeping that in mind as function calling may work with a small amount of APIs, but it’s probably better to make them more human readable (and thus semantically meaningful).
I am just trying out semantic kernel and i am testing the native function based plugin and OPENAPI based plugin , while just native function based plugin is working fine but OPENAPI based plugin is throwing an error . Upon debugging i found out that the issue was function name is having "/" which is not supported by the regex pattern in class KernelFunctionMetadata (name: str = Field(..., pattern=FUNCTION_NAME_REGEX)). API can have "/" in their paths and since i am directly using swagger.json function name is inferrred from paths only. for this scenario function name inferred is /api/controller_get.
FUNCTION_NAME_REGEX = r"^[0-9A-Za-z_]+$"
Code:
OpenAPI.json
Error:
The text was updated successfully, but these errors were encountered: