649 lines
25 KiB
Python
649 lines
25 KiB
Python
# Copyright (c) Microsoft. All rights reserved.
|
|
|
|
import os
|
|
import tempfile
|
|
from copy import deepcopy
|
|
from unittest.mock import patch
|
|
|
|
import pytest
|
|
|
|
from semantic_kernel.connectors.ai.open_ai.services.open_ai_chat_completion import OpenAIChatCompletion
|
|
from semantic_kernel.connectors.ai.open_ai.services.open_ai_text_completion import OpenAITextCompletion
|
|
from semantic_kernel.connectors.ai.prompt_execution_settings import PromptExecutionSettings
|
|
from semantic_kernel.const import METADATA_EXCEPTION_KEY
|
|
from semantic_kernel.contents import AuthorRole
|
|
from semantic_kernel.contents.chat_message_content import ChatMessageContent
|
|
from semantic_kernel.contents.streaming_chat_message_content import StreamingChatMessageContent
|
|
from semantic_kernel.contents.text_content import TextContent
|
|
from semantic_kernel.exceptions import FunctionInitializationError
|
|
from semantic_kernel.filters.functions.function_invocation_context import FunctionInvocationContext
|
|
from semantic_kernel.filters.kernel_filters_extension import _rebuild_function_invocation_context
|
|
from semantic_kernel.filters.prompts.prompt_render_context import PromptRenderContext
|
|
from semantic_kernel.functions.kernel_arguments import KernelArguments
|
|
from semantic_kernel.functions.kernel_function_from_prompt import KernelFunctionFromPrompt
|
|
from semantic_kernel.kernel import Kernel
|
|
from semantic_kernel.prompt_template.input_variable import InputVariable
|
|
from semantic_kernel.prompt_template.kernel_prompt_template import KernelPromptTemplate
|
|
from semantic_kernel.prompt_template.prompt_template_config import PromptTemplateConfig
|
|
|
|
|
|
def test_init_minimal_prompt():
|
|
function = KernelFunctionFromPrompt(
|
|
function_name="test",
|
|
plugin_name="test",
|
|
prompt="test",
|
|
)
|
|
|
|
assert function.name == "test"
|
|
assert function.plugin_name == "test"
|
|
assert function.description is None
|
|
assert function.prompt_template.prompt_template_config.template == "test"
|
|
|
|
|
|
def test_init_minimal_prompt_template():
|
|
function = KernelFunctionFromPrompt(
|
|
function_name="test",
|
|
plugin_name="test",
|
|
prompt_template=KernelPromptTemplate(prompt_template_config=PromptTemplateConfig(template="test")),
|
|
)
|
|
|
|
assert function.name == "test"
|
|
assert function.plugin_name == "test"
|
|
assert function.description is None
|
|
assert function.prompt_template.prompt_template_config.template == "test"
|
|
|
|
|
|
def test_init_minimal_prompt_template_config():
|
|
function = KernelFunctionFromPrompt(
|
|
function_name="test", plugin_name="test", prompt_template_config=PromptTemplateConfig(template="test")
|
|
)
|
|
|
|
assert function.name == "test"
|
|
assert function.plugin_name == "test"
|
|
assert function.description is None
|
|
assert function.prompt_template.prompt_template_config.template == "test"
|
|
|
|
|
|
def test_init_no_prompt():
|
|
with pytest.raises(FunctionInitializationError):
|
|
KernelFunctionFromPrompt(
|
|
function_name="test",
|
|
plugin_name="test",
|
|
)
|
|
|
|
|
|
def test_init_invalid_name():
|
|
with pytest.raises(FunctionInitializationError):
|
|
KernelFunctionFromPrompt(function_name="test func", plugin_name="test", prompt="test")
|
|
|
|
|
|
def test_init_prompt_execution_settings_none():
|
|
function = KernelFunctionFromPrompt(
|
|
function_name="test",
|
|
plugin_name="test",
|
|
prompt="test",
|
|
prompt_execution_settings=None,
|
|
)
|
|
|
|
assert function.name == "test"
|
|
assert function.plugin_name == "test"
|
|
assert function.description is None
|
|
assert function.prompt_template.prompt_template_config.template == "test"
|
|
|
|
|
|
def test_init_prompt_execution_settings_none_with_prompt_template():
|
|
function = KernelFunctionFromPrompt(
|
|
function_name="test",
|
|
plugin_name="test",
|
|
prompt_template=KernelPromptTemplate(
|
|
prompt_template_config=PromptTemplateConfig(template="test", execution_settings={})
|
|
),
|
|
prompt_execution_settings=None,
|
|
)
|
|
|
|
assert function.name == "test"
|
|
assert function.plugin_name == "test"
|
|
assert function.description is None
|
|
assert function.prompt_template.prompt_template_config.template == "test"
|
|
|
|
|
|
def test_init_prompt_execution_settings():
|
|
function = KernelFunctionFromPrompt(
|
|
function_name="test",
|
|
plugin_name="test",
|
|
prompt="test",
|
|
prompt_execution_settings=PromptExecutionSettings(service_id="test"),
|
|
)
|
|
|
|
assert function.name == "test"
|
|
assert function.plugin_name == "test"
|
|
assert function.description is None
|
|
assert function.prompt_template.prompt_template_config.template == "test"
|
|
|
|
|
|
def test_init_prompt_execution_settings_list():
|
|
function = KernelFunctionFromPrompt(
|
|
function_name="test",
|
|
plugin_name="test",
|
|
prompt="test",
|
|
prompt_execution_settings=[PromptExecutionSettings(service_id="test")],
|
|
)
|
|
|
|
assert function.name == "test"
|
|
assert function.plugin_name == "test"
|
|
assert function.description is None
|
|
assert function.prompt_template.prompt_template_config.template == "test"
|
|
|
|
|
|
def test_init_prompt_execution_settings_dict():
|
|
function = KernelFunctionFromPrompt(
|
|
function_name="test",
|
|
plugin_name="test",
|
|
prompt="test",
|
|
prompt_execution_settings={"test": PromptExecutionSettings(service_id="test")},
|
|
)
|
|
|
|
assert function.name == "test"
|
|
assert function.plugin_name == "test"
|
|
assert function.description is None
|
|
assert function.prompt_template.prompt_template_config.template == "test"
|
|
|
|
|
|
async def test_invoke_chat_stream(openai_unit_test_env):
|
|
kernel = Kernel()
|
|
kernel.add_service(OpenAIChatCompletion(service_id="test", ai_model_id="test"))
|
|
function = KernelFunctionFromPrompt(
|
|
function_name="test",
|
|
plugin_name="test",
|
|
prompt="test",
|
|
prompt_execution_settings=PromptExecutionSettings(service_id="test"),
|
|
)
|
|
|
|
# This part remains unchanged - for synchronous mocking example
|
|
with patch(
|
|
"semantic_kernel.connectors.ai.open_ai.services.open_ai_chat_completion.OpenAIChatCompletion.get_chat_message_contents"
|
|
) as mock:
|
|
mock.return_value = [ChatMessageContent(role=AuthorRole.ASSISTANT, content="test", metadata={})]
|
|
result = await function.invoke(kernel=kernel)
|
|
assert str(result) == "test"
|
|
|
|
with patch(
|
|
"semantic_kernel.connectors.ai.open_ai.services.open_ai_chat_completion.OpenAIChatCompletion.get_streaming_chat_message_contents"
|
|
) as mock:
|
|
mock.return_value = [
|
|
StreamingChatMessageContent(choice_index=0, role=AuthorRole.ASSISTANT, content="test", metadata={})
|
|
]
|
|
async for result in function.invoke_stream(kernel=kernel):
|
|
assert str(result) == "test"
|
|
|
|
|
|
async def test_invoke_exception(openai_unit_test_env):
|
|
kernel = Kernel()
|
|
kernel.add_service(OpenAIChatCompletion(service_id="test", ai_model_id="test"))
|
|
function = KernelFunctionFromPrompt(
|
|
function_name="test",
|
|
plugin_name="test",
|
|
prompt="test",
|
|
prompt_execution_settings=PromptExecutionSettings(service_id="test"),
|
|
)
|
|
with patch(
|
|
"semantic_kernel.connectors.ai.open_ai.services.open_ai_chat_completion.OpenAIChatCompletion.get_chat_message_contents",
|
|
side_effect=Exception,
|
|
) as mock:
|
|
mock.return_value = [ChatMessageContent(role=AuthorRole.ASSISTANT, content="test", metadata={})]
|
|
with pytest.raises(Exception, match="test"):
|
|
await function.invoke(kernel=kernel)
|
|
|
|
with patch(
|
|
"semantic_kernel.connectors.ai.open_ai.services.open_ai_chat_completion.OpenAIChatCompletion.get_streaming_chat_message_contents",
|
|
side_effect=Exception,
|
|
) as mock:
|
|
mock.return_value = [
|
|
StreamingChatMessageContent(choice_index=0, role=AuthorRole.ASSISTANT, content="test", metadata={})
|
|
]
|
|
with pytest.raises(Exception):
|
|
async for result in function.invoke_stream(kernel=kernel):
|
|
assert isinstance(result.metadata[METADATA_EXCEPTION_KEY], Exception)
|
|
|
|
|
|
async def test_invoke_text(openai_unit_test_env):
|
|
kernel = Kernel()
|
|
kernel.add_service(OpenAITextCompletion(service_id="test", ai_model_id="test"))
|
|
function = KernelFunctionFromPrompt(
|
|
function_name="test",
|
|
plugin_name="test",
|
|
prompt="test",
|
|
prompt_execution_settings=PromptExecutionSettings(service_id="test"),
|
|
)
|
|
with patch(
|
|
"semantic_kernel.connectors.ai.open_ai.services.open_ai_text_completion.OpenAITextCompletion.get_text_contents",
|
|
) as mock:
|
|
mock.return_value = [TextContent(text="test", metadata={})]
|
|
result = await function.invoke(kernel=kernel)
|
|
assert str(result) == "test"
|
|
|
|
with patch(
|
|
"semantic_kernel.connectors.ai.open_ai.services.open_ai_text_completion.OpenAITextCompletion.get_streaming_text_contents",
|
|
) as mock:
|
|
mock.return_value = [TextContent(text="test", metadata={})]
|
|
async for result in function.invoke_stream(kernel=kernel):
|
|
assert str(result) == "test"
|
|
|
|
|
|
async def test_invoke_exception_text(openai_unit_test_env):
|
|
kernel = Kernel()
|
|
kernel.add_service(OpenAITextCompletion(service_id="test", ai_model_id="test"))
|
|
function = KernelFunctionFromPrompt(
|
|
function_name="test",
|
|
plugin_name="test",
|
|
prompt="test",
|
|
prompt_execution_settings=PromptExecutionSettings(service_id="test"),
|
|
)
|
|
with patch(
|
|
"semantic_kernel.connectors.ai.open_ai.services.open_ai_text_completion.OpenAITextCompletion.get_text_contents",
|
|
side_effect=Exception,
|
|
) as mock:
|
|
mock.return_value = [TextContent(text="test", metadata={})]
|
|
with pytest.raises(Exception, match="test"):
|
|
await function.invoke(kernel=kernel)
|
|
|
|
with patch(
|
|
"semantic_kernel.connectors.ai.open_ai.services.open_ai_text_completion.OpenAITextCompletion.get_streaming_text_contents",
|
|
side_effect=Exception,
|
|
) as mock:
|
|
mock.return_value = []
|
|
with pytest.raises(Exception):
|
|
async for result in function.invoke_stream(kernel=kernel):
|
|
assert isinstance(result.metadata[METADATA_EXCEPTION_KEY], Exception)
|
|
|
|
|
|
async def test_invoke_defaults(openai_unit_test_env):
|
|
kernel = Kernel()
|
|
kernel.add_service(OpenAIChatCompletion(service_id="test", ai_model_id="test"))
|
|
function = KernelFunctionFromPrompt(
|
|
function_name="test",
|
|
plugin_name="test",
|
|
prompt_template_config=PromptTemplateConfig(
|
|
template="{{$input}}",
|
|
input_variables=[InputVariable(name="input", type="str", default="test", is_required=False)],
|
|
),
|
|
prompt_execution_settings=PromptExecutionSettings(service_id="test"),
|
|
)
|
|
with patch(
|
|
"semantic_kernel.connectors.ai.open_ai.services.open_ai_chat_completion.OpenAIChatCompletion.get_chat_message_contents"
|
|
) as mock:
|
|
mock.return_value = [ChatMessageContent(role=AuthorRole.ASSISTANT, content="test", metadata={})]
|
|
result = await function.invoke(kernel=kernel)
|
|
assert str(result) == "test"
|
|
|
|
|
|
def test_create_with_multiple_settings():
|
|
function = KernelFunctionFromPrompt(
|
|
function_name="test",
|
|
plugin_name="test",
|
|
prompt_template_config=PromptTemplateConfig(
|
|
template="test",
|
|
execution_settings=[
|
|
PromptExecutionSettings(service_id="test", temperature=0.0),
|
|
PromptExecutionSettings(service_id="test2", temperature=1.0),
|
|
],
|
|
),
|
|
)
|
|
assert (
|
|
function.prompt_template.prompt_template_config.execution_settings["test"].extension_data["temperature"] == 0.0
|
|
)
|
|
assert (
|
|
function.prompt_template.prompt_template_config.execution_settings["test2"].extension_data["temperature"] == 1.0
|
|
)
|
|
|
|
|
|
async def test_create_with_multiple_settings_one_service_registered(openai_unit_test_env):
|
|
kernel = Kernel()
|
|
kernel.add_service(OpenAIChatCompletion(service_id="test2", ai_model_id="test"))
|
|
function = KernelFunctionFromPrompt(
|
|
function_name="test",
|
|
plugin_name="test",
|
|
prompt_template_config=PromptTemplateConfig(
|
|
template="test",
|
|
execution_settings=[
|
|
PromptExecutionSettings(service_id="test", temperature=0.0),
|
|
PromptExecutionSettings(service_id="test2", temperature=1.0),
|
|
],
|
|
),
|
|
)
|
|
with patch(
|
|
"semantic_kernel.connectors.ai.open_ai.services.open_ai_chat_completion.OpenAIChatCompletion.get_chat_message_contents"
|
|
) as mock:
|
|
mock.return_value = [ChatMessageContent(role=AuthorRole.ASSISTANT, content="test", metadata={})]
|
|
result = await function.invoke(kernel=kernel)
|
|
assert str(result) == "test"
|
|
|
|
|
|
def test_from_yaml_fail():
|
|
with pytest.raises(FunctionInitializationError):
|
|
KernelFunctionFromPrompt.from_yaml("template_format: something_else")
|
|
|
|
|
|
def test_from_directory_prompt_only():
|
|
with pytest.raises(FunctionInitializationError):
|
|
KernelFunctionFromPrompt.from_directory(
|
|
path=os.path.join(
|
|
os.path.dirname(__file__),
|
|
"../../assets",
|
|
"test_plugins",
|
|
"TestPlugin",
|
|
"TestFunctionPromptOnly",
|
|
),
|
|
plugin_name="test",
|
|
)
|
|
|
|
|
|
def test_from_directory_config_only():
|
|
with pytest.raises(FunctionInitializationError):
|
|
KernelFunctionFromPrompt.from_directory(
|
|
path=os.path.join(
|
|
os.path.dirname(__file__),
|
|
"../../assets",
|
|
"test_plugins",
|
|
"TestPlugin",
|
|
"TestFunctionConfigOnly",
|
|
),
|
|
plugin_name="test",
|
|
)
|
|
|
|
|
|
async def test_prompt_render(kernel: Kernel, openai_unit_test_env):
|
|
kernel.add_service(OpenAIChatCompletion(service_id="default", ai_model_id="test"))
|
|
function = KernelFunctionFromPrompt(
|
|
function_name="test",
|
|
plugin_name="test",
|
|
prompt="test",
|
|
template_format="semantic-kernel",
|
|
)
|
|
_rebuild_function_invocation_context()
|
|
context = FunctionInvocationContext(function=function, kernel=kernel, arguments=KernelArguments())
|
|
prompt_render_result = await function._render_prompt(context)
|
|
assert prompt_render_result.rendered_prompt == "test"
|
|
|
|
|
|
async def test_prompt_render_with_filter(kernel: Kernel, openai_unit_test_env):
|
|
kernel.add_service(OpenAIChatCompletion(service_id="default", ai_model_id="test"))
|
|
|
|
@kernel.filter("prompt_rendering")
|
|
async def prompt_rendering_filter(context: PromptRenderContext, next):
|
|
await next(context)
|
|
context.rendered_prompt = f"preface {context.rendered_prompt or ''}"
|
|
|
|
function = KernelFunctionFromPrompt(
|
|
function_name="test",
|
|
plugin_name="test",
|
|
prompt="test",
|
|
template_format="semantic-kernel",
|
|
)
|
|
_rebuild_function_invocation_context()
|
|
context = FunctionInvocationContext(function=function, kernel=kernel, arguments=KernelArguments())
|
|
prompt_render_result = await function._render_prompt(context)
|
|
assert prompt_render_result.rendered_prompt == "preface test"
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("mode"),
|
|
[
|
|
("python"),
|
|
("json"),
|
|
],
|
|
)
|
|
def test_function_model_dump(mode: str):
|
|
function = KernelFunctionFromPrompt(
|
|
function_name="test",
|
|
plugin_name="test",
|
|
prompt="test",
|
|
template_format="semantic-kernel",
|
|
prompt_template_config=PromptTemplateConfig(
|
|
template="test",
|
|
input_variables=[InputVariable(name="input", type="str", default="test", is_required=False)],
|
|
),
|
|
)
|
|
model_dump = function.model_dump(mode=mode)
|
|
assert isinstance(model_dump, dict)
|
|
assert "metadata" in model_dump
|
|
assert len(model_dump["metadata"]["parameters"]) == 1
|
|
|
|
|
|
def test_function_model_dump_json():
|
|
function = KernelFunctionFromPrompt(
|
|
function_name="test",
|
|
plugin_name="test",
|
|
prompt="test",
|
|
template_format="semantic-kernel",
|
|
)
|
|
model_dump_json = function.model_dump_json()
|
|
assert isinstance(model_dump_json, str)
|
|
assert "test" in model_dump_json
|
|
|
|
|
|
def test_from_directory_utf8_encoding_default():
|
|
"""Test loading plugin with default UTF-8 encoding."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
prompt_path = os.path.join(temp_dir, "skprompt.txt")
|
|
config_path = os.path.join(temp_dir, "config.json")
|
|
|
|
# UTF-8 content with international characters
|
|
prompt_content = """Hello! I can help with questions in multiple languages:
|
|
English: Hello world!
|
|
Spanish: ¡Hola mundo!
|
|
Chinese: 你好世界!
|
|
Japanese: こんにちは世界!
|
|
|
|
Question: {{$input}}
|
|
"""
|
|
|
|
config_content = """{
|
|
"schema": 1,
|
|
"description": "A multilingual assistant function",
|
|
"input_variables": [
|
|
{
|
|
"name": "input",
|
|
"description": "User's question",
|
|
"required": true
|
|
}
|
|
]
|
|
}"""
|
|
|
|
# Write files with UTF-8 encoding
|
|
with open(prompt_path, "w", encoding="utf-8") as f:
|
|
f.write(prompt_content)
|
|
with open(config_path, "w", encoding="utf-8") as f:
|
|
f.write(config_content)
|
|
|
|
# Test default behavior (should use UTF-8)
|
|
function = KernelFunctionFromPrompt.from_directory(temp_dir)
|
|
assert function.name == os.path.basename(temp_dir)
|
|
assert function.description == "A multilingual assistant function"
|
|
assert "你好世界" in function.prompt_template.prompt_template_config.template
|
|
assert "こんにちは世界" in function.prompt_template.prompt_template_config.template
|
|
|
|
|
|
def test_from_directory_explicit_utf8_encoding():
|
|
"""Test loading plugin with explicit UTF-8 encoding."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
prompt_path = os.path.join(temp_dir, "skprompt.txt")
|
|
config_path = os.path.join(temp_dir, "config.json")
|
|
|
|
prompt_content = "Hello with UTF-8 characters: ñáéíóú {{$input}}"
|
|
config_content = '{"schema": 1, "description": "Test with UTF-8 characters"}'
|
|
|
|
with open(prompt_path, "w", encoding="utf-8") as f:
|
|
f.write(prompt_content)
|
|
with open(config_path, "w", encoding="utf-8") as f:
|
|
f.write(config_content)
|
|
|
|
# Test explicit UTF-8 encoding
|
|
function = KernelFunctionFromPrompt.from_directory(temp_dir, encoding="utf-8")
|
|
assert function.description == "Test with UTF-8 characters"
|
|
assert "ñáéíóú" in function.prompt_template.prompt_template_config.template
|
|
|
|
|
|
def test_from_directory_latin1_encoding():
|
|
"""Test loading plugin with Latin-1 encoding."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
prompt_path = os.path.join(temp_dir, "skprompt.txt")
|
|
config_path = os.path.join(temp_dir, "config.json")
|
|
|
|
# Content with Latin-1 characters (Western European)
|
|
prompt_content = """Assistant for Western European languages:
|
|
French: café, naïve, résumé
|
|
German: Müller, Größe, weiß
|
|
Spanish: niño, señora, años
|
|
|
|
Question: {{$input}}
|
|
"""
|
|
|
|
config_content = """{
|
|
"schema": 1,
|
|
"description": "Western European language assistant",
|
|
"input_variables": [
|
|
{
|
|
"name": "input",
|
|
"description": "User's question",
|
|
"required": true
|
|
}
|
|
]
|
|
}"""
|
|
|
|
# Write files with Latin-1 encoding
|
|
with open(prompt_path, "w", encoding="latin-1") as f:
|
|
f.write(prompt_content)
|
|
with open(config_path, "w", encoding="latin-1") as f:
|
|
f.write(config_content)
|
|
|
|
# Load with Latin-1 encoding
|
|
function = KernelFunctionFromPrompt.from_directory(temp_dir, encoding="latin-1")
|
|
assert function.description == "Western European language assistant"
|
|
assert "café" in function.prompt_template.prompt_template_config.template
|
|
assert "Müller" in function.prompt_template.prompt_template_config.template
|
|
assert "niño" in function.prompt_template.prompt_template_config.template
|
|
|
|
|
|
def test_from_directory_cp1252_encoding():
|
|
"""Test loading plugin with Windows-1252 encoding."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
prompt_path = os.path.join(temp_dir, "skprompt.txt")
|
|
config_path = os.path.join(temp_dir, "config.json")
|
|
|
|
# Content with Windows-1252 specific characters
|
|
prompt_content = """Windows text processing assistant:
|
|
Smart quotes: "Hello" and 'world'
|
|
Em dash: Yes—absolutely!
|
|
Ellipsis: Wait…
|
|
|
|
Question: {{$input}}
|
|
"""
|
|
|
|
config_content = """{
|
|
"schema": 1,
|
|
"description": "Windows text processing assistant",
|
|
"input_variables": [
|
|
{
|
|
"name": "input",
|
|
"description": "User's question about text processing",
|
|
"required": true
|
|
}
|
|
]
|
|
}"""
|
|
|
|
# Write files with Windows-1252 encoding
|
|
with open(prompt_path, "w", encoding="cp1252") as f:
|
|
f.write(prompt_content)
|
|
with open(config_path, "w", encoding="cp1252") as f:
|
|
f.write(config_content)
|
|
|
|
# Load with Windows-1252 encoding
|
|
function = KernelFunctionFromPrompt.from_directory(temp_dir, encoding="cp1252")
|
|
assert function.description == "Windows text processing assistant"
|
|
assert '"Hello"' in function.prompt_template.prompt_template_config.template
|
|
assert "Yes—absolutely" in function.prompt_template.prompt_template_config.template
|
|
assert "Wait…" in function.prompt_template.prompt_template_config.template
|
|
|
|
|
|
def test_from_directory_with_plugin_name_and_encoding():
|
|
"""Test loading plugin with both plugin name and encoding specified."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
prompt_path = os.path.join(temp_dir, "skprompt.txt")
|
|
config_path = os.path.join(temp_dir, "config.json")
|
|
|
|
prompt_content = "Simple assistant: {{$input}}"
|
|
config_content = '{"schema": 1, "description": "Simple assistant"}'
|
|
|
|
with open(prompt_path, "w", encoding="utf-8") as f:
|
|
f.write(prompt_content)
|
|
with open(config_path, "w", encoding="utf-8") as f:
|
|
f.write(config_content)
|
|
|
|
# Load with both plugin name and encoding specified
|
|
function = KernelFunctionFromPrompt.from_directory(
|
|
path=temp_dir, plugin_name="MyCustomPlugin", encoding="utf-8"
|
|
)
|
|
assert function.metadata.plugin_name == "MyCustomPlugin"
|
|
assert function.description == "Simple assistant"
|
|
assert function.prompt_template.prompt_template_config.template == "Simple assistant: {{$input}}"
|
|
|
|
|
|
def test_from_directory_encoding_error_handling():
|
|
"""Test that incorrect encoding raises appropriate error."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
prompt_path = os.path.join(temp_dir, "skprompt.txt")
|
|
config_path = os.path.join(temp_dir, "config.json")
|
|
|
|
# Write UTF-8 content
|
|
prompt_content = "Hello with UTF-8: 你好世界 {{$input}}"
|
|
config_content = '{"schema": 1, "description": "UTF-8 content"}'
|
|
|
|
with open(prompt_path, "w", encoding="utf-8") as f:
|
|
f.write(prompt_content)
|
|
with open(config_path, "w", encoding="utf-8") as f:
|
|
f.write(config_content)
|
|
|
|
# Try to read with ASCII encoding - should fail
|
|
with pytest.raises(UnicodeDecodeError):
|
|
KernelFunctionFromPrompt.from_directory(temp_dir, encoding="ascii")
|
|
|
|
|
|
def test_from_directory_backward_compatibility():
|
|
"""Test that existing code without encoding parameter still works."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
prompt_path = os.path.join(temp_dir, "skprompt.txt")
|
|
config_path = os.path.join(temp_dir, "config.json")
|
|
|
|
prompt_content = "Basic ASCII content: {{$input}}"
|
|
config_content = '{"schema": 1, "description": "Basic function"}'
|
|
|
|
with open(prompt_path, "w", encoding="utf-8") as f:
|
|
f.write(prompt_content)
|
|
with open(config_path, "w", encoding="utf-8") as f:
|
|
f.write(config_content)
|
|
|
|
# Test that old calling style still works
|
|
function = KernelFunctionFromPrompt.from_directory(temp_dir)
|
|
assert function.description == "Basic function"
|
|
assert function.prompt_template.prompt_template_config.template == "Basic ASCII content: {{$input}}"
|
|
|
|
|
|
def test_kernel_function_from_prompt_deepcopy():
|
|
"""Test deepcopying a KernelFunctionFromPrompt."""
|
|
function = KernelFunctionFromPrompt(
|
|
function_name="test_function",
|
|
plugin_name="test_plugin",
|
|
prompt="Hello, world!",
|
|
description="A test function.",
|
|
)
|
|
copied_function = deepcopy(function)
|
|
assert copied_function is not function
|
|
assert copied_function.name == function.name
|
|
assert copied_function.plugin_name == function.plugin_name
|
|
assert copied_function.description == function.description
|
|
assert copied_function.prompt_template.prompt_template_config.template == (
|
|
function.prompt_template.prompt_template_config.template
|
|
)
|
|
assert copied_function.prompt_template is not function.prompt_template
|