- Introduce dynamic variable definitions in AssistantConfig and Assistant models, allowing for flexible prompt customization. - Implement validation for dynamic variable names and types in the schema. - Update backend services and routes to handle dynamic variables in assistant configurations and runtime processing. - Enhance frontend components to support dynamic variable definitions, including a new editor for managing variables. - Add tests to ensure proper functionality and validation of dynamic variables in various scenarios.
100 lines
3.9 KiB
Python
100 lines
3.9 KiB
Python
from __future__ import annotations
|
|
|
|
import unittest
|
|
|
|
from models import AssistantConfig
|
|
from services.runtime_variables import (
|
|
DynamicVariableError,
|
|
DynamicVariableStore,
|
|
prepare_dynamic_config,
|
|
value_at_path,
|
|
)
|
|
|
|
|
|
class DynamicVariableTests(unittest.TestCase):
|
|
def test_defaults_client_values_and_system_values_are_merged(self):
|
|
cfg = AssistantConfig(
|
|
type="prompt",
|
|
runtimeMode="pipeline",
|
|
prompt="服务 {{user_name}} / {{tier}} / {{system__conversation_id}}",
|
|
greeting="您好 {{user_name}}",
|
|
dynamic_variable_definitions={
|
|
"user_name": {"type": "string", "required": True},
|
|
"tier": {"type": "string", "default": "普通"},
|
|
},
|
|
)
|
|
prepared = prepare_dynamic_config(
|
|
cfg, {"user_name": "王先生"}, assistant_id="asst_1"
|
|
)
|
|
store = DynamicVariableStore.from_config(prepared)
|
|
self.assertEqual(store.render(prepared.greeting), "您好 王先生")
|
|
self.assertIn("普通", store.render(prepared.prompt))
|
|
self.assertTrue(prepared.conversation_id.startswith("conv_"))
|
|
self.assertEqual(
|
|
prepared.dynamic_variables["system__conversation_id"],
|
|
prepared.conversation_id,
|
|
)
|
|
|
|
def test_realtime_prompt_supports_dynamic_variables(self):
|
|
cfg = AssistantConfig(
|
|
type="prompt",
|
|
runtimeMode="realtime",
|
|
prompt="请称呼用户为 {{user_name}}",
|
|
greeting="您好,{{user_name}}",
|
|
dynamic_variable_definitions={
|
|
"user_name": {"type": "string", "required": True}
|
|
},
|
|
)
|
|
prepared = prepare_dynamic_config(
|
|
cfg, {"user_name": "王先生"}, assistant_id="asst_realtime"
|
|
)
|
|
store = DynamicVariableStore.from_config(prepared)
|
|
self.assertEqual(store.render(prepared.greeting), "您好,王先生")
|
|
self.assertEqual(store.render(prepared.prompt), "请称呼用户为 王先生")
|
|
|
|
def test_client_cannot_set_reserved_or_wrong_typed_values(self):
|
|
cfg = AssistantConfig(
|
|
type="prompt",
|
|
runtimeMode="pipeline",
|
|
dynamic_variable_definitions={
|
|
"count": {"type": "number", "required": True}
|
|
},
|
|
)
|
|
with self.assertRaisesRegex(DynamicVariableError, "保留变量"):
|
|
prepare_dynamic_config(
|
|
cfg, {"system__agent_turns": 99}, assistant_id="asst_1"
|
|
)
|
|
with self.assertRaisesRegex(DynamicVariableError, "类型"):
|
|
prepare_dynamic_config(cfg, {"count": "1"}, assistant_id="asst_1")
|
|
|
|
def test_secret_is_header_only_and_expansion_is_single_pass(self):
|
|
store = DynamicVariableStore(
|
|
{"name": "{{secret__token}}"}, {"secret__token": "private"}
|
|
)
|
|
self.assertEqual(store.render("{{name}}"), "{{secret__token}}")
|
|
with self.assertRaisesRegex(DynamicVariableError, "只能用于 HTTP Header"):
|
|
store.render("{{secret__token}}")
|
|
self.assertEqual(
|
|
store.render("Bearer {{secret__token}}", allow_secrets=True),
|
|
"Bearer private",
|
|
)
|
|
|
|
def test_tool_assignment_and_system_history(self):
|
|
store = DynamicVariableStore(
|
|
{
|
|
"order_status": "unknown",
|
|
"system__agent_turns": 0,
|
|
"system__conversation_history": "",
|
|
}
|
|
)
|
|
store.assign("order_status", value_at_path({"order": {"status": "paid"}}, "order.status"))
|
|
store.record("user", "查订单")
|
|
store.record("agent", "已付款", completed_agent_turn=True)
|
|
self.assertEqual(store.values["order_status"], "paid")
|
|
self.assertEqual(store.values["system__agent_turns"], 1)
|
|
self.assertIn("查订单", store.values["system__conversation_history"])
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|