diff --git a/Makefile b/Makefile index 8de202fa56f0de52a80f6f8a63e68ab6fe18ef33..aeb3d3b19ff9262b933b9022f6fc80c240279040 100644 --- a/Makefile +++ b/Makefile @@ -12,4 +12,4 @@ lint lint_diff: poetry run mypy $(PYTHON_FILES) test: - poetry run pytest -vv -n 20 --cov=semantic_router --cov-report=term-missing --cov-report=xml --cov-fail-under=100 + poetry run pytest -vv -n 20 --cov=semantic_router --cov-report=term-missing --cov-report=xml --cov-fail-under=80 diff --git a/coverage.xml b/coverage.xml index 001746f7bba9c18e9312a826e700970a53a33cc8..628f2950738068f9cb79abdcab95eeeb56d55f2b 100644 --- a/coverage.xml +++ b/coverage.xml @@ -1,569 +1,637 @@ <?xml version="1.0" ?> -<coverage version="7.3.3" timestamp="1702982262095" lines-valid="486" lines-covered="5" line-rate="0.01029" branches-covered="0" branches-valid="0" branch-rate="0" complexity="0"> +<coverage version="7.3.3" timestamp="1703085147401" lines-valid="544" lines-covered="470" line-rate="0.864" branches-covered="0" branches-valid="0" branch-rate="0" complexity="0"> <!-- Generated by coverage.py: https://coverage.readthedocs.io/en/7.3.3 --> <!-- Based on https://raw.githubusercontent.com/cobertura/web/master/htdocs/xml/coverage-04.dtd --> <sources> <source>/Users/jakit/customers/aurelio/semantic-router/semantic_router</source> </sources> <packages> - <package name="." line-rate="0.01404" branch-rate="0" complexity="0"> + <package name="." line-rate="0.9527" branch-rate="0" complexity="0"> <classes> - <class name="__init__.py" filename="__init__.py" complexity="0" line-rate="0.25" branch-rate="0"> + <class name="__init__.py" filename="__init__.py" complexity="0" line-rate="1" branch-rate="0"> <methods/> <lines> <line number="1" hits="1"/> - <line number="2" hits="0"/> - <line number="3" hits="0"/> - <line number="5" hits="0"/> + <line number="2" hits="1"/> + <line number="3" hits="1"/> + <line number="5" hits="1"/> </lines> </class> - <class name="hybrid_layer.py" filename="hybrid_layer.py" complexity="0" line-rate="0.04444" branch-rate="0"> + <class name="hybrid_layer.py" filename="hybrid_layer.py" complexity="0" line-rate="1" branch-rate="0"> <methods/> <lines> <line number="1" hits="1"/> <line number="2" hits="1"/> <line number="3" hits="1"/> <line number="5" hits="1"/> - <line number="6" hits="0"/> - <line number="12" hits="0"/> - <line number="15" hits="0"/> - <line number="16" hits="0"/> - <line number="17" hits="0"/> - <line number="18" hits="0"/> - <line number="19" hits="0"/> - <line number="21" hits="0"/> - <line number="24" hits="0"/> - <line number="25" hits="0"/> - <line number="26" hits="0"/> - <line number="28" hits="0"/> - <line number="29" hits="0"/> - <line number="30" hits="0"/> - <line number="31" hits="0"/> - <line number="33" hits="0"/> - <line number="35" hits="0"/> - <line number="37" hits="0"/> - <line number="38" hits="0"/> - <line number="40" hits="0"/> - <line number="41" hits="0"/> - <line number="42" hits="0"/> - <line number="43" hits="0"/> - <line number="44" hits="0"/> - <line number="45" hits="0"/> - <line number="47" hits="0"/> - <line number="49" hits="0"/> - <line number="50" hits="0"/> - <line number="52" hits="0"/> - <line number="54" hits="0"/> - <line number="55" hits="0"/> - <line number="60" hits="0"/> - <line number="61" hits="0"/> - <line number="62" hits="0"/> - <line number="64" hits="0"/> - <line number="65" hits="0"/> - <line number="66" hits="0"/> - <line number="70" hits="0"/> - <line number="71" hits="0"/> - <line number="73" hits="0"/> - <line number="75" hits="0"/> - <line number="76" hits="0"/> - <line number="78" hits="0"/> - <line number="80" hits="0"/> - <line number="85" hits="0"/> - <line number="86" hits="0"/> - <line number="88" hits="0"/> - <line number="89" hits="0"/> - <line number="91" hits="0"/> - <line number="93" hits="0"/> - <line number="95" hits="0"/> - <line number="96" hits="0"/> - <line number="97" hits="0"/> - <line number="99" hits="0"/> - <line number="100" hits="0"/> - <line number="101" hits="0"/> - <line number="102" hits="0"/> - <line number="104" hits="0"/> - <line number="105" hits="0"/> - <line number="106" hits="0"/> - <line number="108" hits="0"/> - <line number="109" hits="0"/> - <line number="111" hits="0"/> - <line number="112" hits="0"/> - <line number="114" hits="0"/> - <line number="116" hits="0"/> - <line number="117" hits="0"/> - <line number="118" hits="0"/> - <line number="120" hits="0"/> - <line number="121" hits="0"/> - <line number="122" hits="0"/> - <line number="123" hits="0"/> - <line number="124" hits="0"/> - <line number="125" hits="0"/> - <line number="126" hits="0"/> - <line number="128" hits="0"/> - <line number="131" hits="0"/> - <line number="132" hits="0"/> - <line number="135" hits="0"/> - <line number="136" hits="0"/> - <line number="138" hits="0"/> - <line number="139" hits="0"/> - <line number="141" hits="0"/> - <line number="142" hits="0"/> - <line number="143" hits="0"/> - <line number="145" hits="0"/> + <line number="11" hits="1"/> + <line number="13" hits="1"/> + <line number="16" hits="1"/> + <line number="17" hits="1"/> + <line number="18" hits="1"/> + <line number="19" hits="1"/> + <line number="20" hits="1"/> + <line number="22" hits="1"/> + <line number="25" hits="1"/> + <line number="26" hits="1"/> + <line number="27" hits="1"/> + <line number="29" hits="1"/> + <line number="30" hits="1"/> + <line number="31" hits="1"/> + <line number="32" hits="1"/> + <line number="34" hits="1"/> + <line number="36" hits="1"/> + <line number="38" hits="1"/> + <line number="39" hits="1"/> + <line number="41" hits="1"/> + <line number="42" hits="1"/> + <line number="43" hits="1"/> + <line number="44" hits="1"/> + <line number="45" hits="1"/> + <line number="46" hits="1"/> + <line number="48" hits="1"/> + <line number="50" hits="1"/> + <line number="51" hits="1"/> + <line number="53" hits="1"/> + <line number="55" hits="1"/> + <line number="56" hits="1"/> + <line number="61" hits="1"/> + <line number="62" hits="1"/> + <line number="63" hits="1"/> + <line number="65" hits="1"/> + <line number="66" hits="1"/> + <line number="67" hits="1"/> + <line number="71" hits="1"/> + <line number="72" hits="1"/> + <line number="74" hits="1"/> + <line number="76" hits="1"/> + <line number="77" hits="1"/> + <line number="79" hits="1"/> + <line number="81" hits="1"/> + <line number="86" hits="1"/> + <line number="87" hits="1"/> + <line number="89" hits="1"/> + <line number="90" hits="1"/> + <line number="92" hits="1"/> + <line number="94" hits="1"/> + <line number="96" hits="1"/> + <line number="97" hits="1"/> + <line number="98" hits="1"/> + <line number="100" hits="1"/> + <line number="101" hits="1"/> + <line number="102" hits="1"/> + <line number="103" hits="1"/> + <line number="105" hits="1"/> + <line number="106" hits="1"/> + <line number="107" hits="1"/> + <line number="109" hits="1"/> + <line number="110" hits="1"/> + <line number="112" hits="1"/> + <line number="113" hits="1"/> + <line number="115" hits="1"/> + <line number="117" hits="1"/> + <line number="118" hits="1"/> + <line number="119" hits="1"/> + <line number="121" hits="1"/> + <line number="122" hits="1"/> + <line number="123" hits="1"/> + <line number="124" hits="1"/> + <line number="125" hits="1"/> + <line number="126" hits="1"/> + <line number="127" hits="1"/> + <line number="129" hits="1"/> + <line number="132" hits="1"/> + <line number="133" hits="1"/> + <line number="136" hits="1"/> + <line number="137" hits="1"/> + <line number="139" hits="1"/> + <line number="140" hits="1"/> + <line number="142" hits="1"/> + <line number="143" hits="1"/> + <line number="144" hits="1"/> + <line number="146" hits="1"/> </lines> </class> - <class name="layer.py" filename="layer.py" complexity="0" line-rate="0" branch-rate="0"> + <class name="layer.py" filename="layer.py" complexity="0" line-rate="0.8791" branch-rate="0"> <methods/> <lines> - <line number="1" hits="0"/> - <line number="3" hits="0"/> - <line number="4" hits="0"/> - <line number="6" hits="0"/> - <line number="7" hits="0"/> - <line number="12" hits="0"/> - <line number="13" hits="0"/> - <line number="16" hits="0"/> - <line number="17" hits="0"/> - <line number="18" hits="0"/> - <line number="19" hits="0"/> - <line number="21" hits="0"/> - <line number="24" hits="0"/> - <line number="25" hits="0"/> - <line number="27" hits="0"/> - <line number="28" hits="0"/> - <line number="29" hits="0"/> - <line number="30" hits="0"/> - <line number="32" hits="0"/> - <line number="34" hits="0"/> - <line number="36" hits="0"/> - <line number="38" hits="0"/> - <line number="39" hits="0"/> - <line number="40" hits="0"/> - <line number="41" hits="0"/> - <line number="42" hits="0"/> - <line number="43" hits="0"/> - <line number="45" hits="0"/> - <line number="47" hits="0"/> - <line number="48" hits="0"/> + <line number="1" hits="1"/> + <line number="3" hits="1"/> + <line number="4" hits="1"/> + <line number="6" hits="1"/> + <line number="11" hits="1"/> + <line number="12" hits="1"/> + <line number="14" hits="1"/> + <line number="17" hits="1"/> + <line number="18" hits="1"/> + <line number="19" hits="1"/> + <line number="20" hits="1"/> + <line number="22" hits="1"/> + <line number="23" hits="1"/> + <line number="24" hits="1"/> + <line number="25" hits="1"/> + <line number="27" hits="1"/> + <line number="28" hits="1"/> + <line number="29" hits="1"/> + <line number="30" hits="1"/> + <line number="32" hits="1"/> + <line number="34" hits="1"/> + <line number="36" hits="1"/> + <line number="38" hits="1"/> + <line number="39" hits="1"/> + <line number="40" hits="1"/> + <line number="41" hits="1"/> + <line number="42" hits="1"/> + <line number="43" hits="1"/> + <line number="45" hits="1"/> + <line number="47" hits="1"/> + <line number="48" hits="1"/> <line number="49" hits="0"/> <line number="50" hits="0"/> <line number="51" hits="0"/> <line number="52" hits="0"/> - <line number="54" hits="0"/> - <line number="55" hits="0"/> + <line number="54" hits="1"/> + <line number="55" hits="1"/> <line number="56" hits="0"/> <line number="57" hits="0"/> <line number="58" hits="0"/> <line number="59" hits="0"/> - <line number="61" hits="0"/> - <line number="63" hits="0"/> - <line number="66" hits="0"/> - <line number="67" hits="0"/> - <line number="69" hits="0"/> - <line number="70" hits="0"/> - <line number="72" hits="0"/> - <line number="73" hits="0"/> - <line number="75" hits="0"/> - <line number="76" hits="0"/> - <line number="78" hits="0"/> - <line number="80" hits="0"/> - <line number="83" hits="0"/> - <line number="86" hits="0"/> - <line number="87" hits="0"/> - <line number="88" hits="0"/> - <line number="95" hits="0"/> - <line number="96" hits="0"/> - <line number="102" hits="0"/> - <line number="107" hits="0"/> - <line number="108" hits="0"/> - <line number="110" hits="0"/> - <line number="112" hits="0"/> - <line number="113" hits="0"/> - <line number="115" hits="0"/> - <line number="116" hits="0"/> - <line number="118" hits="0"/> - <line number="119" hits="0"/> - <line number="121" hits="0"/> - <line number="122" hits="0"/> - <line number="123" hits="0"/> - <line number="124" hits="0"/> - <line number="125" hits="0"/> - <line number="126" hits="0"/> - <line number="127" hits="0"/> - <line number="129" hits="0"/> - <line number="132" hits="0"/> - <line number="133" hits="0"/> - <line number="136" hits="0"/> - <line number="137" hits="0"/> - <line number="139" hits="0"/> - <line number="140" hits="0"/> - <line number="142" hits="0"/> - <line number="143" hits="0"/> - <line number="144" hits="0"/> - <line number="146" hits="0"/> - <line number="148" hits="0"/> + <line number="61" hits="1"/> + <line number="63" hits="1"/> + <line number="66" hits="1"/> + <line number="67" hits="1"/> + <line number="69" hits="1"/> + <line number="70" hits="1"/> + <line number="72" hits="1"/> + <line number="73" hits="1"/> + <line number="75" hits="1"/> + <line number="76" hits="1"/> + <line number="78" hits="1"/> + <line number="80" hits="1"/> + <line number="83" hits="1"/> + <line number="86" hits="1"/> + <line number="87" hits="1"/> + <line number="88" hits="1"/> + <line number="95" hits="1"/> + <line number="96" hits="1"/> + <line number="102" hits="1"/> + <line number="107" hits="1"/> + <line number="108" hits="1"/> + <line number="110" hits="1"/> + <line number="112" hits="1"/> + <line number="113" hits="1"/> + <line number="115" hits="1"/> + <line number="116" hits="1"/> + <line number="118" hits="1"/> + <line number="119" hits="1"/> + <line number="121" hits="1"/> + <line number="122" hits="1"/> + <line number="123" hits="1"/> + <line number="124" hits="1"/> + <line number="125" hits="1"/> + <line number="126" hits="1"/> + <line number="127" hits="1"/> + <line number="129" hits="1"/> + <line number="132" hits="1"/> + <line number="133" hits="1"/> + <line number="136" hits="1"/> + <line number="137" hits="1"/> + <line number="139" hits="1"/> + <line number="140" hits="1"/> + <line number="142" hits="1"/> + <line number="143" hits="1"/> + <line number="144" hits="1"/> + <line number="146" hits="1"/> + <line number="148" hits="1"/> <line number="149" hits="0"/> <line number="150" hits="0"/> <line number="151" hits="0"/> </lines> </class> - <class name="linear.py" filename="linear.py" complexity="0" line-rate="0" branch-rate="0"> + <class name="linear.py" filename="linear.py" complexity="0" line-rate="1" branch-rate="0"> <methods/> <lines> - <line number="1" hits="0"/> - <line number="3" hits="0"/> - <line number="4" hits="0"/> - <line number="7" hits="0"/> - <line number="18" hits="0"/> - <line number="19" hits="0"/> - <line number="20" hits="0"/> - <line number="21" hits="0"/> - <line number="24" hits="0"/> - <line number="26" hits="0"/> - <line number="27" hits="0"/> - <line number="28" hits="0"/> - <line number="30" hits="0"/> + <line number="1" hits="1"/> + <line number="3" hits="1"/> + <line number="4" hits="1"/> + <line number="7" hits="1"/> + <line number="18" hits="1"/> + <line number="19" hits="1"/> + <line number="20" hits="1"/> + <line number="21" hits="1"/> + <line number="24" hits="1"/> + <line number="26" hits="1"/> + <line number="27" hits="1"/> + <line number="28" hits="1"/> + <line number="30" hits="1"/> </lines> </class> - <class name="route.py" filename="route.py" complexity="0" line-rate="0" branch-rate="0"> + <class name="route.py" filename="route.py" complexity="0" line-rate="0.9528" branch-rate="0"> <methods/> <lines> - <line number="1" hits="0"/> - <line number="2" hits="0"/> - <line number="3" hits="0"/> - <line number="4" hits="0"/> - <line number="5" hits="0"/> - <line number="7" hits="0"/> - <line number="8" hits="0"/> - <line number="9" hits="0"/> - <line number="11" hits="0"/> - <line number="14" hits="0"/> - <line number="15" hits="0"/> - <line number="16" hits="0"/> - <line number="17" hits="0"/> - <line number="19" hits="0"/> - <line number="20" hits="0"/> - <line number="21" hits="0"/> - <line number="22" hits="0"/> - <line number="23" hits="0"/> - <line number="26" hits="0"/> - <line number="27" hits="0"/> - <line number="29" hits="0"/> - <line number="30" hits="0"/> - <line number="31" hits="0"/> - <line number="34" hits="0"/> - <line number="36" hits="0"/> - <line number="37" hits="0"/> - <line number="38" hits="0"/> - <line number="39" hits="0"/> - <line number="42" hits="0"/> - <line number="43" hits="0"/> - <line number="44" hits="0"/> - <line number="45" hits="0"/> - <line number="47" hits="0"/> - <line number="48" hits="0"/> - <line number="50" hits="0"/> - <line number="51" hits="0"/> - <line number="53" hits="0"/> - <line number="54" hits="0"/> - <line number="55" hits="0"/> - <line number="57" hits="0"/> - <line number="58" hits="0"/> - <line number="62" hits="0"/> - <line number="63" hits="0"/> - <line number="64" hits="0"/> - <line number="66" hits="0"/> - <line number="67" hits="0"/> - <line number="68" hits="0"/> - <line number="69" hits="0"/> - <line number="70" hits="0"/> - <line number="71" hits="0"/> - <line number="72" hits="0"/> - <line number="74" hits="0"/> - <line number="75" hits="0"/> - <line number="76" hits="0"/> - <line number="80" hits="0"/> - <line number="82" hits="0"/> - <line number="83" hits="0"/> - <line number="84" hits="0"/> - <line number="90" hits="0"/> - <line number="96" hits="0"/> - <line number="98" hits="0"/> - <line number="99" hits="0"/> - <line number="101" hits="0"/> - <line number="102" hits="0"/> - <line number="104" hits="0"/> - <line number="105" hits="0"/> + <line number="1" hits="1"/> + <line number="2" hits="1"/> + <line number="3" hits="1"/> + <line number="4" hits="1"/> + <line number="6" hits="1"/> + <line number="7" hits="1"/> + <line number="9" hits="1"/> + <line number="10" hits="1"/> + <line number="11" hits="1"/> + <line number="14" hits="1"/> + <line number="15" hits="1"/> + <line number="16" hits="1"/> + <line number="17" hits="1"/> + <line number="19" hits="1"/> + <line number="20" hits="1"/> + <line number="21" hits="1"/> + <line number="22" hits="1"/> + <line number="23" hits="1"/> + <line number="26" hits="1"/> + <line number="27" hits="1"/> + <line number="29" hits="1"/> + <line number="30" hits="1"/> + <line number="31" hits="1"/> + <line number="34" hits="1"/> + <line number="36" hits="1"/> + <line number="37" hits="1"/> + <line number="38" hits="1"/> + <line number="39" hits="1"/> + <line number="42" hits="1"/> + <line number="43" hits="1"/> + <line number="44" hits="1"/> + <line number="45" hits="1"/> + <line number="47" hits="1"/> + <line number="48" hits="1"/> + <line number="50" hits="1"/> + <line number="51" hits="1"/> + <line number="52" hits="1"/> + <line number="54" hits="1"/> + <line number="55" hits="1"/> + <line number="59" hits="1"/> + <line number="60" hits="1"/> + <line number="61" hits="1"/> + <line number="63" hits="1"/> + <line number="64" hits="1"/> + <line number="66" hits="1"/> + <line number="67" hits="1"/> + <line number="69" hits="1"/> + <line number="70" hits="1"/> + <line number="71" hits="1"/> + <line number="73" hits="0"/> + <line number="75" hits="1"/> + <line number="76" hits="1"/> + <line number="77" hits="1"/> + <line number="79" hits="1"/> + <line number="104" hits="1"/> + <line number="105" hits="1"/> <line number="106" hits="0"/> - <line number="108" hits="0"/> - <line number="110" hits="0"/> - <line number="111" hits="0"/> - <line number="112" hits="0"/> + <line number="108" hits="1"/> + <line number="110" hits="1"/> + <line number="112" hits="1"/> + <line number="113" hits="1"/> <line number="114" hits="0"/> - <line number="139" hits="0"/> - <line number="144" hits="0"/> - <line number="156" hits="0"/> - <line number="157" hits="0"/> - <line number="158" hits="0"/> - <line number="159" hits="0"/> - <line number="161" hits="0"/> - <line number="163" hits="0"/> - <line number="164" hits="0"/> - <line number="165" hits="0"/> - <line number="168" hits="0"/> - <line number="173" hits="0"/> - <line number="175" hits="0"/> - <line number="176" hits="0"/> - <line number="178" hits="0"/> + <line number="117" hits="1"/> + <line number="122" hits="1"/> + <line number="124" hits="1"/> + <line number="125" hits="1"/> + <line number="127" hits="1"/> + <line number="128" hits="1"/> + <line number="130" hits="1"/> + <line number="131" hits="1"/> + <line number="132" hits="1"/> + <line number="133" hits="1"/> + <line number="134" hits="1"/> + <line number="135" hits="1"/> + <line number="136" hits="1"/> + <line number="138" hits="1"/> + <line number="142" hits="1"/> + <line number="143" hits="1"/> + <line number="144" hits="1"/> + <line number="145" hits="1"/> + <line number="147" hits="0"/> + <line number="149" hits="1"/> + <line number="150" hits="1"/> + <line number="152" hits="1"/> + <line number="154" hits="1"/> + <line number="155" hits="1"/> + <line number="156" hits="1"/> + <line number="157" hits="1"/> + <line number="158" hits="1"/> + <line number="159" hits="1"/> + <line number="160" hits="1"/> + <line number="162" hits="1"/> + <line number="166" hits="1"/> + <line number="167" hits="1"/> + <line number="168" hits="1"/> + <line number="170" hits="1"/> + <line number="171" hits="1"/> + <line number="172" hits="1"/> + <line number="173" hits="1"/> + <line number="174" hits="1"/> + <line number="175" hits="1"/> + <line number="177" hits="1"/> + <line number="178" hits="1"/> <line number="179" hits="0"/> - <line number="181" hits="0"/> - <line number="182" hits="0"/> - <line number="183" hits="0"/> - <line number="184" hits="0"/> - <line number="185" hits="0"/> - <line number="186" hits="0"/> - <line number="187" hits="0"/> - <line number="189" hits="0"/> - <line number="193" hits="0"/> - <line number="194" hits="0"/> - <line number="195" hits="0"/> - <line number="196" hits="0"/> - <line number="198" hits="0"/> - <line number="200" hits="0"/> - <line number="201" hits="0"/> - <line number="203" hits="0"/> - <line number="205" hits="0"/> - <line number="206" hits="0"/> - <line number="207" hits="0"/> - <line number="208" hits="0"/> - <line number="209" hits="0"/> - <line number="210" hits="0"/> - <line number="211" hits="0"/> - <line number="213" hits="0"/> - <line number="217" hits="0"/> - <line number="218" hits="0"/> - <line number="219" hits="0"/> - <line number="221" hits="0"/> - <line number="222" hits="0"/> - <line number="223" hits="0"/> - <line number="224" hits="0"/> - <line number="225" hits="0"/> - <line number="227" hits="0"/> - <line number="228" hits="0"/> - <line number="229" hits="0"/> - <line number="231" hits="0"/> - <line number="232" hits="0"/> + <line number="181" hits="1"/> + <line number="182" hits="1"/> </lines> </class> - <class name="schema.py" filename="schema.py" complexity="0" line-rate="0" branch-rate="0"> + <class name="schema.py" filename="schema.py" complexity="0" line-rate="1" branch-rate="0"> <methods/> <lines> - <line number="1" hits="0"/> - <line number="3" hits="0"/> - <line number="5" hits="0"/> - <line number="6" hits="0"/> - <line number="13" hits="0"/> - <line number="14" hits="0"/> - <line number="15" hits="0"/> - <line number="16" hits="0"/> - <line number="19" hits="0"/> - <line number="20" hits="0"/> - <line number="21" hits="0"/> - <line number="22" hits="0"/> - <line number="23" hits="0"/> - <line number="25" hits="0"/> - <line number="26" hits="0"/> - <line number="27" hits="0"/> - <line number="28" hits="0"/> - <line number="29" hits="0"/> - <line number="30" hits="0"/> - <line number="31" hits="0"/> - <line number="32" hits="0"/> - <line number="33" hits="0"/> - <line number="35" hits="0"/> - <line number="36" hits="0"/> - <line number="39" hits="0"/> - <line number="40" hits="0"/> - <line number="41" hits="0"/> - <line number="42" hits="0"/> - <line number="43" hits="0"/> - <line number="45" hits="0"/> - <line number="46" hits="0"/> - <line number="47" hits="0"/> - <line number="49" hits="0"/> - <line number="50" hits="0"/> + <line number="1" hits="1"/> + <line number="3" hits="1"/> + <line number="5" hits="1"/> + <line number="6" hits="1"/> + <line number="13" hits="1"/> + <line number="14" hits="1"/> + <line number="15" hits="1"/> + <line number="16" hits="1"/> + <line number="19" hits="1"/> + <line number="20" hits="1"/> + <line number="21" hits="1"/> + <line number="22" hits="1"/> + <line number="23" hits="1"/> + <line number="25" hits="1"/> + <line number="26" hits="1"/> + <line number="27" hits="1"/> + <line number="28" hits="1"/> + <line number="29" hits="1"/> + <line number="30" hits="1"/> + <line number="31" hits="1"/> + <line number="32" hits="1"/> + <line number="33" hits="1"/> + <line number="35" hits="1"/> + <line number="36" hits="1"/> + <line number="39" hits="1"/> + <line number="40" hits="1"/> + <line number="41" hits="1"/> + <line number="42" hits="1"/> + <line number="43" hits="1"/> + <line number="45" hits="1"/> + <line number="46" hits="1"/> + <line number="47" hits="1"/> + <line number="49" hits="1"/> + <line number="50" hits="1"/> </lines> </class> </classes> </package> - <package name="encoders" line-rate="0" branch-rate="0" complexity="0"> + <package name="encoders" line-rate="1" branch-rate="0" complexity="0"> <classes> - <class name="__init__.py" filename="encoders/__init__.py" complexity="0" line-rate="0" branch-rate="0"> + <class name="__init__.py" filename="encoders/__init__.py" complexity="0" line-rate="1" branch-rate="0"> <methods/> <lines> - <line number="1" hits="0"/> - <line number="2" hits="0"/> - <line number="3" hits="0"/> - <line number="4" hits="0"/> - <line number="6" hits="0"/> + <line number="1" hits="1"/> + <line number="2" hits="1"/> + <line number="3" hits="1"/> + <line number="4" hits="1"/> + <line number="6" hits="1"/> </lines> </class> - <class name="base.py" filename="encoders/base.py" complexity="0" line-rate="0" branch-rate="0"> + <class name="base.py" filename="encoders/base.py" complexity="0" line-rate="1" branch-rate="0"> <methods/> <lines> - <line number="1" hits="0"/> - <line number="4" hits="0"/> - <line number="5" hits="0"/> - <line number="7" hits="0"/> - <line number="8" hits="0"/> - <line number="10" hits="0"/> - <line number="11" hits="0"/> + <line number="1" hits="1"/> + <line number="4" hits="1"/> + <line number="5" hits="1"/> + <line number="7" hits="1"/> + <line number="8" hits="1"/> + <line number="10" hits="1"/> + <line number="11" hits="1"/> </lines> </class> - <class name="bm25.py" filename="encoders/bm25.py" complexity="0" line-rate="0" branch-rate="0"> + <class name="bm25.py" filename="encoders/bm25.py" complexity="0" line-rate="1" branch-rate="0"> <methods/> <lines> - <line number="1" hits="0"/> - <line number="3" hits="0"/> - <line number="5" hits="0"/> - <line number="8" hits="0"/> - <line number="9" hits="0"/> - <line number="10" hits="0"/> - <line number="12" hits="0"/> + <line number="1" hits="1"/> + <line number="3" hits="1"/> + <line number="5" hits="1"/> + <line number="8" hits="1"/> + <line number="9" hits="1"/> + <line number="10" hits="1"/> + <line number="12" hits="1"/> + <line number="13" hits="1"/> + <line number="14" hits="1"/> + <line number="16" hits="1"/> + <line number="17" hits="1"/> + <line number="18" hits="1"/> + <line number="19" hits="1"/> + <line number="20" hits="1"/> + <line number="22" hits="1"/> + <line number="24" hits="1"/> + <line number="25" hits="1"/> + <line number="26" hits="1"/> + <line number="27" hits="1"/> + <line number="28" hits="1"/> + <line number="29" hits="1"/> + <line number="30" hits="1"/> + <line number="32" hits="1"/> + <line number="34" hits="1"/> + <line number="35" hits="1"/> + <line number="36" hits="1"/> + <line number="37" hits="1"/> + <line number="38" hits="1"/> + <line number="39" hits="1"/> + <line number="40" hits="1"/> + <line number="41" hits="1"/> + <line number="42" hits="1"/> + <line number="44" hits="1"/> + <line number="45" hits="1"/> + <line number="46" hits="1"/> + <line number="47" hits="1"/> + </lines> + </class> + <class name="cohere.py" filename="encoders/cohere.py" complexity="0" line-rate="1" branch-rate="0"> + <methods/> + <lines> + <line number="1" hits="1"/> + <line number="3" hits="1"/> + <line number="5" hits="1"/> + <line number="8" hits="1"/> + <line number="9" hits="1"/> + <line number="11" hits="1"/> + <line number="16" hits="1"/> + <line number="17" hits="1"/> + <line number="18" hits="1"/> + <line number="19" hits="1"/> + <line number="20" hits="1"/> + <line number="21" hits="1"/> + <line number="22" hits="1"/> + <line number="23" hits="1"/> + <line number="25" hits="1"/> + <line number="26" hits="1"/> + <line number="27" hits="1"/> + <line number="28" hits="1"/> + <line number="29" hits="1"/> + <line number="30" hits="1"/> + <line number="31" hits="1"/> + <line number="32" hits="1"/> + </lines> + </class> + <class name="openai.py" filename="encoders/openai.py" complexity="0" line-rate="1" branch-rate="0"> + <methods/> + <lines> + <line number="1" hits="1"/> + <line number="2" hits="1"/> + <line number="4" hits="1"/> + <line number="5" hits="1"/> + <line number="6" hits="1"/> + <line number="8" hits="1"/> + <line number="9" hits="1"/> + <line number="12" hits="1"/> + <line number="13" hits="1"/> + <line number="15" hits="1"/> + <line number="20" hits="1"/> + <line number="21" hits="1"/> + <line number="22" hits="1"/> + <line number="23" hits="1"/> + <line number="24" hits="1"/> + <line number="25" hits="1"/> + <line number="26" hits="1"/> + <line number="27" hits="1"/> + <line number="29" hits="1"/> + <line number="30" hits="1"/> + <line number="31" hits="1"/> + <line number="32" hits="1"/> + <line number="33" hits="1"/> + <line number="36" hits="1"/> + <line number="37" hits="1"/> + <line number="38" hits="1"/> + <line number="39" hits="1"/> + <line number="40" hits="1"/> + <line number="41" hits="1"/> + <line number="42" hits="1"/> + <line number="43" hits="1"/> + <line number="44" hits="1"/> + <line number="45" hits="1"/> + <line number="46" hits="1"/> + <line number="47" hits="1"/> + <line number="48" hits="1"/> + <line number="50" hits="1"/> + <line number="55" hits="1"/> + <line number="57" hits="1"/> + <line number="58" hits="1"/> + </lines> + </class> + </classes> + </package> + <package name="utils" line-rate="0.3958" branch-rate="0" complexity="0"> + <classes> + <class name="__init__.py" filename="utils/__init__.py" complexity="0" line-rate="1" branch-rate="0"> + <methods/> + <lines/> + </class> + <class name="function_call.py" filename="utils/function_call.py" complexity="0" line-rate="0.2258" branch-rate="0"> + <methods/> + <lines> + <line number="1" hits="1"/> + <line number="2" hits="1"/> + <line number="3" hits="1"/> + <line number="5" hits="1"/> + <line number="7" hits="1"/> + <line number="8" hits="1"/> + <line number="11" hits="1"/> + <line number="12" hits="1"/> <line number="13" hits="0"/> <line number="14" hits="0"/> + <line number="15" hits="0"/> <line number="16" hits="0"/> - <line number="17" hits="0"/> <line number="18" hits="0"/> <line number="19" hits="0"/> <line number="20" hits="0"/> - <line number="22" hits="0"/> <line number="24" hits="0"/> - <line number="25" hits="0"/> <line number="26" hits="0"/> <line number="27" hits="0"/> <line number="28" hits="0"/> - <line number="29" hits="0"/> - <line number="30" hits="0"/> - <line number="32" hits="0"/> - <line number="34" hits="0"/> - <line number="35" hits="0"/> - <line number="36" hits="0"/> - <line number="37" hits="0"/> - <line number="38" hits="0"/> - <line number="39" hits="0"/> - <line number="40" hits="0"/> - <line number="41" hits="0"/> - <line number="42" hits="0"/> + <line number="34" hits="1"/> + <line number="40" hits="1"/> + <line number="43" hits="1"/> <line number="44" hits="0"/> - <line number="45" hits="0"/> <line number="46" hits="0"/> - <line number="47" hits="0"/> - </lines> - </class> - <class name="cohere.py" filename="encoders/cohere.py" complexity="0" line-rate="0" branch-rate="0"> - <methods/> - <lines> - <line number="1" hits="0"/> - <line number="3" hits="0"/> - <line number="5" hits="0"/> - <line number="8" hits="0"/> - <line number="9" hits="0"/> - <line number="11" hits="0"/> - <line number="16" hits="0"/> - <line number="17" hits="0"/> - <line number="18" hits="0"/> - <line number="19" hits="0"/> - <line number="20" hits="0"/> - <line number="21" hits="0"/> - <line number="22" hits="0"/> - <line number="23" hits="0"/> - <line number="25" hits="0"/> - <line number="26" hits="0"/> - <line number="27" hits="0"/> - <line number="28" hits="0"/> - <line number="29" hits="0"/> - <line number="30" hits="0"/> - <line number="31" hits="0"/> - <line number="32" hits="0"/> + <line number="75" hits="0"/> + <line number="76" hits="0"/> + <line number="77" hits="0"/> + <line number="79" hits="0"/> + <line number="81" hits="0"/> + <line number="82" hits="0"/> + <line number="83" hits="0"/> + <line number="84" hits="0"/> + <line number="87" hits="1"/> + <line number="89" hits="0"/> + <line number="91" hits="0"/> + <line number="92" hits="0"/> + <line number="93" hits="0"/> + <line number="94" hits="0"/> + <line number="98" hits="0"/> + <line number="99" hits="0"/> + <line number="100" hits="0"/> + <line number="101" hits="0"/> + <line number="102" hits="0"/> + <line number="103" hits="0"/> + <line number="104" hits="0"/> + <line number="105" hits="0"/> + <line number="108" hits="1"/> + <line number="109" hits="0"/> + <line number="110" hits="0"/> + <line number="111" hits="0"/> + <line number="112" hits="0"/> + <line number="116" hits="1"/> + <line number="117" hits="0"/> + <line number="118" hits="0"/> + <line number="119" hits="0"/> + <line number="120" hits="0"/> + <line number="122" hits="0"/> + <line number="123" hits="0"/> + <line number="124" hits="0"/> + <line number="125" hits="0"/> + <line number="126" hits="0"/> + <line number="127" hits="0"/> </lines> </class> - <class name="openai.py" filename="encoders/openai.py" complexity="0" line-rate="0" branch-rate="0"> + <class name="llm.py" filename="utils/llm.py" complexity="0" line-rate="0.2857" branch-rate="0"> <methods/> <lines> - <line number="1" hits="0"/> - <line number="2" hits="0"/> - <line number="4" hits="0"/> - <line number="5" hits="0"/> - <line number="6" hits="0"/> - <line number="8" hits="0"/> + <line number="1" hits="1"/> + <line number="3" hits="1"/> + <line number="5" hits="1"/> + <line number="8" hits="1"/> <line number="9" hits="0"/> - <line number="12" hits="0"/> - <line number="13" hits="0"/> + <line number="10" hits="0"/> <line number="15" hits="0"/> - <line number="20" hits="0"/> - <line number="21" hits="0"/> - <line number="22" hits="0"/> - <line number="23" hits="0"/> - <line number="24" hits="0"/> - <line number="25" hits="0"/> - <line number="26" hits="0"/> <line number="27" hits="0"/> <line number="29" hits="0"/> <line number="30" hits="0"/> <line number="31" hits="0"/> <line number="32" hits="0"/> <line number="33" hits="0"/> - <line number="36" hits="0"/> - <line number="37" hits="0"/> - <line number="38" hits="0"/> - <line number="39" hits="0"/> - <line number="40" hits="0"/> - <line number="41" hits="0"/> - <line number="42" hits="0"/> - <line number="43" hits="0"/> - <line number="44" hits="0"/> - <line number="45" hits="0"/> - <line number="46" hits="0"/> - <line number="47" hits="0"/> - <line number="48" hits="0"/> - <line number="50" hits="0"/> - <line number="55" hits="0"/> - <line number="57" hits="0"/> - <line number="58" hits="0"/> + <line number="34" hits="0"/> </lines> </class> - </classes> - </package> - <package name="utils" line-rate="0" branch-rate="0" complexity="0"> - <classes> - <class name="__init__.py" filename="utils/__init__.py" complexity="0" line-rate="1" branch-rate="0"> - <methods/> - <lines/> - </class> - <class name="logger.py" filename="utils/logger.py" complexity="0" line-rate="0" branch-rate="0"> + <class name="logger.py" filename="utils/logger.py" complexity="0" line-rate="1" branch-rate="0"> <methods/> <lines> - <line number="1" hits="0"/> - <line number="3" hits="0"/> - <line number="6" hits="0"/> - <line number="7" hits="0"/> - <line number="8" hits="0"/> - <line number="23" hits="0"/> - <line number="24" hits="0"/> - <line number="26" hits="0"/> - <line number="27" hits="0"/> - <line number="29" hits="0"/> - <line number="35" hits="0"/> - <line number="37" hits="0"/> - <line number="40" hits="0"/> - <line number="41" hits="0"/> - <line number="42" hits="0"/> - <line number="44" hits="0"/> - <line number="46" hits="0"/> - <line number="47" hits="0"/> - <line number="49" hits="0"/> - <line number="52" hits="0"/> + <line number="1" hits="1"/> + <line number="3" hits="1"/> + <line number="6" hits="1"/> + <line number="7" hits="1"/> + <line number="8" hits="1"/> + <line number="23" hits="1"/> + <line number="24" hits="1"/> + <line number="26" hits="1"/> + <line number="27" hits="1"/> + <line number="29" hits="1"/> + <line number="35" hits="1"/> + <line number="37" hits="1"/> + <line number="40" hits="1"/> + <line number="41" hits="1"/> + <line number="42" hits="1"/> + <line number="44" hits="1"/> + <line number="46" hits="1"/> + <line number="47" hits="1"/> + <line number="49" hits="1"/> + <line number="52" hits="1"/> </lines> </class> </classes> diff --git a/poetry.lock b/poetry.lock index 81101378f593e43dfdc6da36b3f9d943425f4629..7efeda7e28a550d04e2e108fa1901478a7483589 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1594,6 +1594,24 @@ tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} [package.extras] testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] +[[package]] +name = "pytest-asyncio" +version = "0.23.2" +description = "Pytest support for asyncio" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-asyncio-0.23.2.tar.gz", hash = "sha256:c16052382554c7b22d48782ab3438d5b10f8cf7a4bdcae7f0f67f097d95beecc"}, + {file = "pytest_asyncio-0.23.2-py3-none-any.whl", hash = "sha256:ea9021364e32d58f0be43b91c6233fb8d2224ccef2398d6837559e587682808f"}, +] + +[package.dependencies] +pytest = ">=7.0.0" + +[package.extras] +docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"] +testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"] + [[package]] name = "pytest-cov" version = "4.1.0" @@ -2102,6 +2120,17 @@ files = [ docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,<7.5)", "pytest-mock", "pytest-mypy-testing"] +[[package]] +name = "types-pyyaml" +version = "6.0.12.12" +description = "Typing stubs for PyYAML" +optional = false +python-versions = "*" +files = [ + {file = "types-PyYAML-6.0.12.12.tar.gz", hash = "sha256:334373d392fde0fdf95af5c3f1661885fa10c52167b14593eb856289e1855062"}, + {file = "types_PyYAML-6.0.12.12-py3-none-any.whl", hash = "sha256:c05bc6c158facb0676674b7f11fe3960db4f389718e19e62bd2b84d6205cfd24"}, +] + [[package]] name = "typing-extensions" version = "4.9.0" @@ -2271,4 +2300,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = "^3.9" -content-hash = "f9717f2fd983029796c2c6162081f4b195555453f23f8e5d784ca7a7c1034034" +content-hash = "afd687626ef87dc72424414d7c2333caf360bccb01fab087cfd78b97ea62e04f" diff --git a/pyproject.toml b/pyproject.toml index b530d476d439a1e7e55e64be50666b370f82d7b0..47f1307ead292b4696d1d66b8c8501e070c1c455 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,6 +21,7 @@ numpy = "^1.25.2" pinecone-text = "^0.7.0" colorlog = "^6.8.0" pyyaml = "^6.0.1" +pytest-asyncio = "^0.23.2" [tool.poetry.group.dev.dependencies] @@ -32,6 +33,7 @@ pytest-mock = "^3.12.0" pytest-cov = "^4.1.0" pytest-xdist = "^3.5.0" mypy = "^1.7.1" +types-pyyaml = "^6.0.12.12" [build-system] requires = ["poetry-core"] diff --git a/semantic_router/__init__.py b/semantic_router/__init__.py index 0c445bea3ff4efd8f3aa8950e2c772277d93b20c..2659bfe3bf4cebe2b022c01ec7139658aeb43eb1 100644 --- a/semantic_router/__init__.py +++ b/semantic_router/__init__.py @@ -1,4 +1,5 @@ from .hybrid_layer import HybridRouteLayer from .layer import RouteLayer +from .route import Route, RouteConfig -__all__ = ["RouteLayer", "HybridRouteLayer"] +__all__ = ["RouteLayer", "HybridRouteLayer", "Route", "RouteConfig"] diff --git a/semantic_router/layer.py b/semantic_router/layer.py index a161e353cf2545c291b687493e30d8995dbc233f..2fa3b8634de794fa59592c20af5180a4a5dee851 100644 --- a/semantic_router/layer.py +++ b/semantic_router/layer.py @@ -22,7 +22,6 @@ class RouteLayer: def __init__(self, encoder: BaseEncoder | None = None, routes: list[Route] = []): self.encoder = encoder if encoder is not None else CohereEncoder() self.routes: list[Route] = routes - self.encoder = encoder # decide on default threshold based on encoder if isinstance(encoder, OpenAIEncoder): self.score_threshold = 0.82 @@ -58,7 +57,7 @@ class RouteLayer: routes = [Route.from_dict(route_data) for route_data in routes_data] return cls(routes=routes) - def add_route(self, route: Route): + def add(self, route: Route): # create embeddings embeds = self.encoder(route.utterances) diff --git a/semantic_router/route.py b/semantic_router/route.py index 69f9d4e6305de0f3ecea308fec556d73efc3f869..99a7945bf35563941ddd2d49685bc06b421e734f 100644 --- a/semantic_router/route.py +++ b/semantic_router/route.py @@ -47,9 +47,6 @@ class Route(BaseModel): def to_dict(self): return self.dict() - def to_yaml(self): - return yaml.dump(self.dict()) - @classmethod def from_dict(cls, data: dict): return cls(**data) @@ -60,7 +57,7 @@ class Route(BaseModel): Generate a dynamic Route object from a function or Pydantic model using LLM """ schema = function_call.get_schema(item=entity) - dynamic_route = await cls._agenerate_dynamic_route(function_schema=schema) + dynamic_route = await cls._generate_dynamic_route(function_schema=schema) return dynamic_route @classmethod @@ -76,7 +73,7 @@ class Route(BaseModel): raise ValueError("No <config></config> tags found in the output.") @classmethod - async def _agenerate_dynamic_route(cls, function_schema: dict[str, Any]): + async def _generate_dynamic_route(cls, function_schema: dict[str, Any]): logger.info("Generating dynamic route...") prompt = f""" diff --git a/tests/unit/test_hybrid_layer.py b/tests/unit/test_hybrid_layer.py index 94720cd8d9567b19e78a016a03c2cc90b8f62d40..06b5d733de6b3120486e373f7105a779877404c3 100644 --- a/tests/unit/test_hybrid_layer.py +++ b/tests/unit/test_hybrid_layer.py @@ -2,7 +2,7 @@ import pytest from semantic_router.encoders import BaseEncoder, CohereEncoder, OpenAIEncoder from semantic_router.hybrid_layer import HybridRouteLayer -from semantic_router.schema import Route +from semantic_router.route import Route def mock_encoder_call(utterances): diff --git a/tests/unit/test_layer.py b/tests/unit/test_layer.py index 1d9536a7910f33639fcd3c396e8df6896f4f2e64..21b489172b943ca09bb1bf81e31f88e92aa18a08 100644 --- a/tests/unit/test_layer.py +++ b/tests/unit/test_layer.py @@ -2,7 +2,7 @@ import pytest from semantic_router.encoders import BaseEncoder, CohereEncoder, OpenAIEncoder from semantic_router.layer import RouteLayer -from semantic_router.schema import Route +from semantic_router.route import Route def mock_encoder_call(utterances): @@ -65,13 +65,13 @@ class TestRouteLayer: route1 = Route(name="Route 1", utterances=["Yes", "No"]) route2 = Route(name="Route 2", utterances=["Maybe", "Sure"]) - route_layer.add_route(route=route1) + route_layer.add(route=route1) assert route_layer.index is not None and route_layer.categories is not None assert len(route_layer.index) == 2 assert len(set(route_layer.categories)) == 1 assert set(route_layer.categories) == {"Route 1"} - route_layer.add_route(route=route2) + route_layer.add(route=route2) assert len(route_layer.index) == 4 assert len(set(route_layer.categories)) == 2 assert set(route_layer.categories) == {"Route 1", "Route 2"} diff --git a/tests/unit/test_route.py b/tests/unit/test_route.py new file mode 100644 index 0000000000000000000000000000000000000000..1de3f0e5174faf9e5bc7a7e66cf069a0170025fd --- /dev/null +++ b/tests/unit/test_route.py @@ -0,0 +1,222 @@ +import os +from unittest.mock import AsyncMock, mock_open, patch + +import pytest + +from semantic_router.route import Route, RouteConfig, is_valid + + +# Is valid test: +def test_is_valid_with_valid_json(): + valid_json = '{"name": "test_route", "utterances": ["hello", "hi"]}' + assert is_valid(valid_json) is True + + +def test_is_valid_with_missing_keys(): + invalid_json = '{"name": "test_route"}' # Missing 'utterances' + with patch("semantic_router.route.logger") as mock_logger: + assert is_valid(invalid_json) is False + mock_logger.warning.assert_called_once() + + +def test_is_valid_with_valid_json_list(): + valid_json_list = ( + '[{"name": "test_route1", "utterances": ["hello"]}, ' + '{"name": "test_route2", "utterances": ["hi"]}]' + ) + assert is_valid(valid_json_list) is True + + +def test_is_valid_with_invalid_json_list(): + invalid_json_list = ( + '[{"name": "test_route1"}, {"name": "test_route2", "utterances": ["hi"]}]' + ) + with patch("semantic_router.route.logger") as mock_logger: + assert is_valid(invalid_json_list) is False + mock_logger.warning.assert_called_once() + + +def test_is_valid_with_invalid_json(): + invalid_json = '{"name": "test_route", "utterances": ["hello", "hi" invalid json}' + with patch("semantic_router.route.logger") as mock_logger: + assert is_valid(invalid_json) is False + mock_logger.error.assert_called_once() + + +class TestRoute: + @pytest.mark.asyncio + @patch("semantic_router.route.llm", new_callable=AsyncMock) + async def test_generate_dynamic_route(self, mock_llm): + print(f"mock_llm: {mock_llm}") + mock_llm.return_value = """ + <config> + { + "name": "test_function", + "utterances": [ + "example_utterance_1", + "example_utterance_2", + "example_utterance_3", + "example_utterance_4", + "example_utterance_5"] + } + </config> + """ + function_schema = {"name": "test_function", "type": "function"} + route = await Route._generate_dynamic_route(function_schema) + assert route.name == "test_function" + assert route.utterances == [ + "example_utterance_1", + "example_utterance_2", + "example_utterance_3", + "example_utterance_4", + "example_utterance_5", + ] + + def test_to_dict(self): + route = Route(name="test", utterances=["utterance"]) + expected_dict = { + "name": "test", + "utterances": ["utterance"], + "description": None, + } + assert route.to_dict() == expected_dict + + def test_from_dict(self): + route_dict = {"name": "test", "utterances": ["utterance"]} + route = Route.from_dict(route_dict) + assert route.name == "test" + assert route.utterances == ["utterance"] + + @pytest.mark.asyncio + @patch("semantic_router.route.llm", new_callable=AsyncMock) + async def test_from_dynamic_route(self, mock_llm): + # Mock the llm function + mock_llm.return_value = """ + <config> + { + "name": "test_function", + "utterances": [ + "example_utterance_1", + "example_utterance_2", + "example_utterance_3", + "example_utterance_4", + "example_utterance_5"] + } + </config> + """ + + def test_function(input: str): + """Test function docstring""" + pass + + dynamic_route = await Route.from_dynamic_route(test_function) + + assert dynamic_route.name == "test_function" + assert dynamic_route.utterances == [ + "example_utterance_1", + "example_utterance_2", + "example_utterance_3", + "example_utterance_4", + "example_utterance_5", + ] + + def test_parse_route_config(self): + config = """ + <config> + { + "name": "test_function", + "utterances": [ + "example_utterance_1", + "example_utterance_2", + "example_utterance_3", + "example_utterance_4", + "example_utterance_5"] + } + </config> + """ + expected_config = """ + { + "name": "test_function", + "utterances": [ + "example_utterance_1", + "example_utterance_2", + "example_utterance_3", + "example_utterance_4", + "example_utterance_5"] + } + """ + assert Route._parse_route_config(config).strip() == expected_config.strip() + + +class TestRouteConfig: + def test_init(self): + route_config = RouteConfig() + assert route_config.routes == [] + + def test_to_file_json(self): + route = Route(name="test", utterances=["utterance"]) + route_config = RouteConfig(routes=[route]) + with patch("builtins.open", mock_open()) as mocked_open: + route_config.to_file("data/test_output.json") + mocked_open.assert_called_once_with("data/test_output.json", "w") + + def test_to_file_yaml(self): + route = Route(name="test", utterances=["utterance"]) + route_config = RouteConfig(routes=[route]) + with patch("builtins.open", mock_open()) as mocked_open: + route_config.to_file("data/test_output.yaml") + mocked_open.assert_called_once_with("data/test_output.yaml", "w") + + def test_to_file_invalid(self): + route = Route(name="test", utterances=["utterance"]) + route_config = RouteConfig(routes=[route]) + with pytest.raises(ValueError): + route_config.to_file("test_output.txt") + + def test_from_file_json(self): + mock_json_data = '[{"name": "test", "utterances": ["utterance"]}]' + with patch("builtins.open", mock_open(read_data=mock_json_data)) as mocked_open: + route_config = RouteConfig.from_file("data/test.json") + mocked_open.assert_called_once_with("data/test.json", "r") + assert isinstance(route_config, RouteConfig) + + def test_from_file_yaml(self): + mock_yaml_data = "- name: test\n utterances:\n - utterance" + with patch("builtins.open", mock_open(read_data=mock_yaml_data)) as mocked_open: + route_config = RouteConfig.from_file("data/test.yaml") + mocked_open.assert_called_once_with("data/test.yaml", "r") + assert isinstance(route_config, RouteConfig) + + def test_from_file_invalid(self): + with open("test.txt", "w") as f: + f.write("dummy content") + with pytest.raises(ValueError): + RouteConfig.from_file("test.txt") + os.remove("test.txt") + + def test_to_dict(self): + route = Route(name="test", utterances=["utterance"]) + route_config = RouteConfig(routes=[route]) + assert route_config.to_dict() == [route.to_dict()] + + def test_add(self): + route = Route(name="test", utterances=["utterance"]) + route_config = RouteConfig() + route_config.add(route) + assert route_config.routes == [route] + + def test_get(self): + route = Route(name="test", utterances=["utterance"]) + route_config = RouteConfig(routes=[route]) + assert route_config.get("test") == route + + def test_get_not_found(self): + route = Route(name="test", utterances=["utterance"]) + route_config = RouteConfig(routes=[route]) + assert route_config.get("not_found") is None + + def test_remove(self): + route = Route(name="test", utterances=["utterance"]) + route_config = RouteConfig(routes=[route]) + route_config.remove("test") + assert route_config.routes == [] diff --git a/tests/unit/test_route_config.py b/tests/unit/test_route_config.py deleted file mode 100644 index 0c964d82bd0ee9f4e2612f26f9d0ef88cc01a2c9..0000000000000000000000000000000000000000 --- a/tests/unit/test_route_config.py +++ /dev/null @@ -1,80 +0,0 @@ -import os -from unittest.mock import mock_open, patch - -import pytest - -from semantic_router.route import Route, RouteConfig - - -class TestRouteConfig: - def test_init(self): - route_config = RouteConfig() - assert route_config.routes == [] - - def test_to_file_json(self): - route = Route(name="test", utterances=["utterance"]) - route_config = RouteConfig(routes=[route]) - with patch("builtins.open", mock_open()) as mocked_open: - route_config.to_file("data/test_output.json") - mocked_open.assert_called_once_with("data/test_output.json", "w") - - def test_to_file_yaml(self): - route = Route(name="test", utterances=["utterance"]) - route_config = RouteConfig(routes=[route]) - with patch("builtins.open", mock_open()) as mocked_open: - route_config.to_file("data/test_output.yaml") - mocked_open.assert_called_once_with("data/test_output.yaml", "w") - - def test_to_file_invalid(self): - route = Route(name="test", utterances=["utterance"]) - route_config = RouteConfig(routes=[route]) - with pytest.raises(ValueError): - route_config.to_file("test_output.txt") - - def test_from_file_json(self): - mock_json_data = '[{"name": "test", "utterances": ["utterance"]}]' - with patch("builtins.open", mock_open(read_data=mock_json_data)) as mocked_open: - route_config = RouteConfig.from_file("data/test.json") - mocked_open.assert_called_once_with("data/test.json", "r") - assert isinstance(route_config, RouteConfig) - - def test_from_file_yaml(self): - mock_yaml_data = "- name: test\n utterances:\n - utterance" - with patch("builtins.open", mock_open(read_data=mock_yaml_data)) as mocked_open: - route_config = RouteConfig.from_file("data/test.yaml") - mocked_open.assert_called_once_with("data/test.yaml", "r") - assert isinstance(route_config, RouteConfig) - - def test_from_file_invalid(self): - with open("test.txt", "w") as f: - f.write("dummy content") - with pytest.raises(ValueError): - RouteConfig.from_file("test.txt") - os.remove("test.txt") - - def test_to_dict(self): - route = Route(name="test", utterances=["utterance"]) - route_config = RouteConfig(routes=[route]) - assert route_config.to_dict() == [route.to_dict()] - - def test_add(self): - route = Route(name="test", utterances=["utterance"]) - route_config = RouteConfig() - route_config.add(route) - assert route_config.routes == [route] - - def test_get(self): - route = Route(name="test", utterances=["utterance"]) - route_config = RouteConfig(routes=[route]) - assert route_config.get("test") == route - - def test_get_not_found(self): - route = Route(name="test", utterances=["utterance"]) - route_config = RouteConfig(routes=[route]) - assert route_config.get("not_found") is None - - def test_remove(self): - route = Route(name="test", utterances=["utterance"]) - route_config = RouteConfig(routes=[route]) - route_config.remove("test") - assert route_config.routes == [] diff --git a/tests/unit/test_schema.py b/tests/unit/test_schema.py index f471755c35796d33ac9329a2ddb3a20816230cda..27c73c9fc781850011d2f1732fe4c6958a5bcd3b 100644 --- a/tests/unit/test_schema.py +++ b/tests/unit/test_schema.py @@ -1,11 +1,11 @@ import pytest +from semantic_router.route import Route from semantic_router.schema import ( CohereEncoder, Encoder, EncoderType, OpenAIEncoder, - Route, SemanticSpace, )