133 lines
3.7 KiB
Python
133 lines
3.7 KiB
Python
import pytest
|
|
|
|
from apps.skills.models import SkillDefinition, SkillVersion
|
|
from apps.skills.services import (
|
|
SkillValidationError,
|
|
render_prompt,
|
|
validate_skill_version,
|
|
)
|
|
|
|
VALID_INPUT_SCHEMA = {
|
|
"type": "object",
|
|
"properties": {"problem": {"type": "string"}},
|
|
"required": ["problem"],
|
|
}
|
|
VALID_OUTPUT_SCHEMA = {
|
|
"type": "object",
|
|
"properties": {"summary": {"type": "string"}},
|
|
"required": ["summary"],
|
|
"additionalProperties": False,
|
|
}
|
|
|
|
|
|
def make_version(**overrides):
|
|
definition = SkillDefinition.objects.create(code="summarize", name="问题摘要")
|
|
values = {
|
|
"definition": definition,
|
|
"version": 1,
|
|
"prompt_template": "请总结:{{ input.problem }}",
|
|
"input_schema": VALID_INPUT_SCHEMA,
|
|
"output_schema": VALID_OUTPUT_SCHEMA,
|
|
"example_input": {"problem": "无法登录"},
|
|
}
|
|
values.update(overrides)
|
|
return SkillVersion.objects.create(**values)
|
|
|
|
|
|
def test_validates_and_renders_restricted_template(db):
|
|
version = make_version()
|
|
|
|
validate_skill_version(version)
|
|
|
|
assert render_prompt(version.prompt_template, {"problem": "无法登录"}) == "请总结:无法登录"
|
|
|
|
|
|
def test_rejects_expression_or_missing_variable(db):
|
|
version = make_version(prompt_template="{{ steps.previous.output }}")
|
|
|
|
with pytest.raises(SkillValidationError, match="不允许的模板变量"):
|
|
validate_skill_version(version)
|
|
|
|
with pytest.raises(SkillValidationError, match="模板变量不存在"):
|
|
render_prompt("{{ input.environment }}", {"problem": "无法登录"})
|
|
|
|
|
|
def test_rejects_example_that_does_not_match_schema(db):
|
|
version = make_version(example_input={"problem": 123})
|
|
|
|
with pytest.raises(SkillValidationError, match="示例输入不符合"):
|
|
validate_skill_version(version)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"output_schema",
|
|
[
|
|
{"type": "array", "items": {"type": "string"}},
|
|
{
|
|
"type": "object",
|
|
"properties": {"summary": {"type": "string"}},
|
|
"required": ["summary"],
|
|
},
|
|
{
|
|
"type": "object",
|
|
"properties": {"summary": {"type": "string"}},
|
|
"required": [],
|
|
"additionalProperties": False,
|
|
},
|
|
],
|
|
)
|
|
def test_rejects_incomplete_output_contracts_for_every_enforcement(db, output_schema):
|
|
version = make_version(
|
|
output_schema=output_schema,
|
|
output_enforcement="validated_json",
|
|
)
|
|
|
|
with pytest.raises(SkillValidationError):
|
|
validate_skill_version(version)
|
|
|
|
|
|
def test_native_enforcement_rejects_unsupported_schema_keyword_at_publish(db):
|
|
output_schema = {
|
|
"type": "object",
|
|
"properties": {
|
|
"missing_fields": {
|
|
"type": "array",
|
|
"items": {"type": "string"},
|
|
"uniqueItems": True,
|
|
}
|
|
},
|
|
"required": ["missing_fields"],
|
|
"additionalProperties": False,
|
|
}
|
|
version = make_version(
|
|
output_schema=output_schema,
|
|
output_enforcement="native_json_schema",
|
|
)
|
|
|
|
with pytest.raises(
|
|
SkillValidationError,
|
|
match=r"\$\.properties\.missing_fields\.uniqueItems",
|
|
):
|
|
validate_skill_version(version)
|
|
|
|
|
|
def test_validated_json_keeps_business_only_schema_constraints(db):
|
|
output_schema = {
|
|
"type": "object",
|
|
"properties": {
|
|
"values": {
|
|
"type": "array",
|
|
"items": {"type": "string"},
|
|
"uniqueItems": True,
|
|
}
|
|
},
|
|
"required": ["values"],
|
|
"additionalProperties": False,
|
|
}
|
|
version = make_version(
|
|
output_schema=output_schema,
|
|
output_enforcement="validated_json",
|
|
)
|
|
|
|
validate_skill_version(version)
|