Update coverage reports and fix Ollama client abort handling
This commit is contained in:
@@ -23,16 +23,16 @@
|
|||||||
<div class='clearfix'>
|
<div class='clearfix'>
|
||||||
|
|
||||||
<div class='fl pad1y space-right2'>
|
<div class='fl pad1y space-right2'>
|
||||||
<span class="strong">90.36% </span>
|
<span class="strong">92.55% </span>
|
||||||
<span class="quiet">Statements</span>
|
<span class="quiet">Statements</span>
|
||||||
<span class='fraction'>197/218</span>
|
<span class='fraction'>199/215</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class='fl pad1y space-right2'>
|
<div class='fl pad1y space-right2'>
|
||||||
<span class="strong">80.28% </span>
|
<span class="strong">85.71% </span>
|
||||||
<span class="quiet">Branches</span>
|
<span class="quiet">Branches</span>
|
||||||
<span class='fraction'>57/71</span>
|
<span class='fraction'>60/70</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
@@ -44,9 +44,9 @@
|
|||||||
|
|
||||||
|
|
||||||
<div class='fl pad1y space-right2'>
|
<div class='fl pad1y space-right2'>
|
||||||
<span class="strong">92.15% </span>
|
<span class="strong">94.52% </span>
|
||||||
<span class="quiet">Lines</span>
|
<span class="quiet">Lines</span>
|
||||||
<span class='fraction'>188/204</span>
|
<span class='fraction'>190/201</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
@@ -565,22 +565,22 @@
|
|||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">1x</span>
|
<span class="cline-any cline-yes">1x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">31x</span>
|
<span class="cline-any cline-yes">36x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">31x</span>
|
<span class="cline-any cline-yes">36x</span>
|
||||||
<span class="cline-any cline-yes">31x</span>
|
<span class="cline-any cline-yes">36x</span>
|
||||||
<span class="cline-any cline-yes">31x</span>
|
<span class="cline-any cline-yes">36x</span>
|
||||||
<span class="cline-any cline-yes">31x</span>
|
<span class="cline-any cline-yes">36x</span>
|
||||||
<span class="cline-any cline-yes">31x</span>
|
<span class="cline-any cline-yes">36x</span>
|
||||||
<span class="cline-any cline-yes">31x</span>
|
<span class="cline-any cline-yes">36x</span>
|
||||||
<span class="cline-any cline-yes">31x</span>
|
<span class="cline-any cline-yes">36x</span>
|
||||||
<span class="cline-any cline-yes">31x</span>
|
<span class="cline-any cline-yes">36x</span>
|
||||||
<span class="cline-any cline-yes">31x</span>
|
<span class="cline-any cline-yes">36x</span>
|
||||||
<span class="cline-any cline-yes">31x</span>
|
<span class="cline-any cline-yes">36x</span>
|
||||||
<span class="cline-any cline-yes">31x</span>
|
<span class="cline-any cline-yes">36x</span>
|
||||||
<span class="cline-any cline-yes">31x</span>
|
<span class="cline-any cline-yes">36x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
@@ -596,11 +596,11 @@
|
|||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">31x</span>
|
<span class="cline-any cline-yes">36x</span>
|
||||||
<span class="cline-any cline-yes">31x</span>
|
<span class="cline-any cline-yes">36x</span>
|
||||||
<span class="cline-any cline-yes">31x</span>
|
<span class="cline-any cline-yes">36x</span>
|
||||||
<span class="cline-any cline-yes">31x</span>
|
<span class="cline-any cline-yes">36x</span>
|
||||||
<span class="cline-any cline-yes">31x</span>
|
<span class="cline-any cline-yes">36x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
@@ -640,8 +640,8 @@
|
|||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">136x</span>
|
<span class="cline-any cline-yes">146x</span>
|
||||||
<span class="cline-any cline-yes">23x</span>
|
<span class="cline-any cline-yes">28x</span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-no"> </span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-no"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
@@ -649,71 +649,71 @@
|
|||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">33x</span>
|
<span class="cline-any cline-yes">43x</span>
|
||||||
<span class="cline-any cline-yes">33x</span>
|
<span class="cline-any cline-yes">43x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">33x</span>
|
<span class="cline-any cline-yes">43x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">33x</span>
|
<span class="cline-any cline-yes">43x</span>
|
||||||
<span class="cline-any cline-yes">4x</span>
|
<span class="cline-any cline-yes">4x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">33x</span>
|
<span class="cline-any cline-yes">43x</span>
|
||||||
<span class="cline-any cline-yes">4x</span>
|
<span class="cline-any cline-yes">4x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">4x</span>
|
<span class="cline-any cline-yes">4x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">33x</span>
|
<span class="cline-any cline-yes">43x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">22x</span>
|
<span class="cline-any cline-yes">27x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">22x</span>
|
<span class="cline-any cline-yes">27x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">22x</span>
|
<span class="cline-any cline-yes">27x</span>
|
||||||
<span class="cline-any cline-yes">22x</span>
|
<span class="cline-any cline-yes">27x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">33x</span>
|
<span class="cline-any cline-yes">43x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">282x</span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">43x</span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">43x</span>
|
||||||
<span class="cline-any cline-yes">262x</span>
|
<span class="cline-any cline-yes">262x</span>
|
||||||
|
<span class="cline-any cline-yes">262x</span>
|
||||||
|
<span class="cline-any cline-yes">109x</span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">153x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">33x</span>
|
<span class="cline-any cline-yes">153x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-yes">153x</span>
|
||||||
<span class="cline-any cline-yes">33x</span>
|
|
||||||
<span class="cline-any cline-yes">247x</span>
|
|
||||||
<span class="cline-any cline-yes">247x</span>
|
|
||||||
<span class="cline-any cline-yes">104x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">143x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">143x</span>
|
|
||||||
<span class="cline-any cline-yes">143x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">33x</span>
|
<span class="cline-any cline-yes">43x</span>
|
||||||
<span class="cline-any cline-yes">128x</span>
|
<span class="cline-any cline-yes">133x</span>
|
||||||
<span class="cline-any cline-yes">3656x</span>
|
<span class="cline-any cline-yes">3661x</span>
|
||||||
<span class="cline-any cline-yes">24x</span>
|
<span class="cline-any cline-yes">24x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">262x</span>
|
<span class="cline-any cline-yes">282x</span>
|
||||||
<span class="cline-any cline-yes">33x</span>
|
<span class="cline-any cline-yes">43x</span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-yes">1x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-yes">1x</span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-yes">1x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
@@ -787,59 +787,61 @@
|
|||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">138x</span>
|
<span class="cline-any cline-yes">292x</span>
|
||||||
<span class="cline-any cline-yes">9x</span>
|
<span class="cline-any cline-yes">26x</span>
|
||||||
<span class="cline-any cline-yes">9x</span>
|
<span class="cline-any cline-yes">26x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">9x</span>
|
<span class="cline-any cline-yes">26x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">146x</span>
|
<span class="cline-any cline-yes">158x</span>
|
||||||
<span class="cline-any cline-yes">13x</span>
|
<span class="cline-any cline-yes">19x</span>
|
||||||
<span class="cline-any cline-yes">8x</span>
|
<span class="cline-any cline-yes">12x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">8x</span>
|
<span class="cline-any cline-yes">12x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">13x</span>
|
<span class="cline-any cline-yes">19x</span>
|
||||||
<span class="cline-any cline-yes">13x</span>
|
<span class="cline-any cline-yes">19x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">16x</span>
|
<span class="cline-any cline-yes">21x</span>
|
||||||
<span class="cline-any cline-yes">16x</span>
|
<span class="cline-any cline-yes">21x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">16x</span>
|
<span class="cline-any cline-yes">21x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">16x</span>
|
<span class="cline-any cline-yes">21x</span>
|
||||||
<span class="cline-any cline-yes">16x</span>
|
<span class="cline-any cline-yes">21x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">14x</span>
|
<span class="cline-any cline-yes">19x</span>
|
||||||
<span class="cline-any cline-yes">14x</span>
|
<span class="cline-any cline-yes">19x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">14x</span>
|
<span class="cline-any cline-yes">19x</span>
|
||||||
<span class="cline-any cline-yes">14x</span>
|
<span class="cline-any cline-yes">19x</span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-no"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">14x</span>
|
<span class="cline-any cline-yes">19x</span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">19x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">14x</span>
|
<span class="cline-any cline-yes">19x</span>
|
||||||
<span class="cline-any cline-yes">14x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">14x</span>
|
<span class="cline-any cline-yes">19x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
@@ -852,7 +854,7 @@
|
|||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">14x</span>
|
<span class="cline-any cline-yes">19x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
@@ -873,20 +875,20 @@
|
|||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">14x</span>
|
<span class="cline-any cline-yes">19x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">14x</span>
|
<span class="cline-any cline-yes">19x</span>
|
||||||
<span class="cline-any cline-yes">14x</span>
|
<span class="cline-any cline-yes">19x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">14x</span>
|
<span class="cline-any cline-yes">19x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">14x</span>
|
<span class="cline-any cline-yes">19x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
@@ -895,109 +897,107 @@
|
|||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">14x</span>
|
<span class="cline-any cline-yes">19x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">14x</span>
|
<span class="cline-any cline-yes">19x</span>
|
||||||
<span class="cline-any cline-yes">14x</span>
|
<span class="cline-any cline-yes">19x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">14x</span>
|
<span class="cline-any cline-yes">19x</span>
|
||||||
<span class="cline-any cline-yes">14x</span>
|
<span class="cline-any cline-yes">19x</span>
|
||||||
<span class="cline-any cline-yes">14x</span>
|
<span class="cline-any cline-yes">19x</span>
|
||||||
<span class="cline-any cline-yes">14x</span>
|
<span class="cline-any cline-yes">19x</span>
|
||||||
<span class="cline-any cline-yes">14x</span>
|
<span class="cline-any cline-yes">19x</span>
|
||||||
<span class="cline-any cline-yes">12x</span>
|
<span class="cline-any cline-yes">17x</span>
|
||||||
<span class="cline-any cline-yes">12x</span>
|
<span class="cline-any cline-yes">17x</span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-no"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">12x</span>
|
<span class="cline-any cline-yes">17x</span>
|
||||||
<span class="cline-any cline-yes">12x</span>
|
<span class="cline-any cline-yes">17x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">12x</span>
|
<span class="cline-any cline-yes">17x</span>
|
||||||
<span class="cline-any cline-yes">1x</span>
|
<span class="cline-any cline-yes">4x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">12x</span>
|
<span class="cline-any cline-yes">17x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">8x</span>
|
<span class="cline-any cline-yes">13x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">8x</span>
|
<span class="cline-any cline-yes">13x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">1x</span>
|
<span class="cline-any cline-yes">4x</span>
|
||||||
<span class="cline-any cline-yes">1x</span>
|
<span class="cline-any cline-yes">4x</span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-no"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">1x</span>
|
<span class="cline-any cline-yes">4x</span>
|
||||||
<span class="cline-any cline-yes">1x</span>
|
<span class="cline-any cline-yes">6x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">1x</span>
|
<span class="cline-any cline-yes">4x</span>
|
||||||
<span class="cline-any cline-yes">1x</span>
|
<span class="cline-any cline-yes">4x</span>
|
||||||
<span class="cline-any cline-yes">1x</span>
|
<span class="cline-any cline-yes">6x</span>
|
||||||
<span class="cline-any cline-yes">1x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">1x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">1x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">1x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">1x</span>
|
|
||||||
<span class="cline-any cline-yes">1x</span>
|
|
||||||
<span class="cline-any cline-yes">1x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">1x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">8x</span>
|
|
||||||
<span class="cline-any cline-yes">7x</span>
|
|
||||||
<span class="cline-any cline-yes">7x</span>
|
|
||||||
<span class="cline-any cline-yes">7x</span>
|
|
||||||
<span class="cline-any cline-yes">7x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">8x</span>
|
|
||||||
<span class="cline-any cline-yes">2x</span>
|
<span class="cline-any cline-yes">2x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">8x</span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">4x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">14x</span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">4x</span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">2x</span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">2x</span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">2x</span>
|
||||||
|
<span class="cline-any cline-yes">2x</span>
|
||||||
|
<span class="cline-any cline-yes">2x</span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">2x</span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">2x</span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">13x</span>
|
||||||
|
<span class="cline-any cline-yes">9x</span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">13x</span>
|
||||||
|
<span class="cline-any cline-yes">2x</span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">13x</span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">19x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
@@ -1011,8 +1011,8 @@
|
|||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">6x</span>
|
<span class="cline-any cline-yes">6x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">16x</span>
|
<span class="cline-any cline-yes">21x</span>
|
||||||
<span class="cline-any cline-yes">16x</span>
|
<span class="cline-any cline-yes">21x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
@@ -1185,12 +1185,12 @@ export class ChatView extends ItemView {
|
|||||||
|
|
||||||
// Re-attach streaming message if it exists
|
// Re-attach streaming message if it exists
|
||||||
const streamingMessage = messagesSnapshot.find((msg) => msg.isStreaming);
|
const streamingMessage = messagesSnapshot.find((msg) => msg.isStreaming);
|
||||||
<span class="missing-if-branch" title="if path not taken" >I</span>if (streamingMessage && this.lastMessageEl) {
|
if (streamingMessage && this.lastMessageEl) {
|
||||||
const existingStreamingEl = <span class="cstat-no" title="statement not covered" >container.querySelector(</span>
|
const existingStreamingEl = container.querySelector(
|
||||||
`.ollama-message[data-msg-id="${streamingMessage.id}"]`
|
`.ollama-message[data-msg-id="${streamingMessage.id}"]`
|
||||||
);
|
);
|
||||||
<span class="cstat-no" title="statement not covered" > <span class="missing-if-branch" title="if path not taken" >I</span>if (!existingStreamingEl) {</span>
|
if (!existingStreamingEl) {
|
||||||
<span class="cstat-no" title="statement not covered" > container.appendChild(this.lastMessageEl);</span>
|
container.appendChild(this.lastMessageEl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1306,14 +1306,16 @@ export class ChatView extends ItemView {
|
|||||||
<span class="cstat-no" title="statement not covered" > context = context.substring(0, MAX_CONTEXT_LENGTH) + '\n\n... (truncated)';</span>
|
<span class="cstat-no" title="statement not covered" > context = context.substring(0, MAX_CONTEXT_LENGTH) + '\n\n... (truncated)';</span>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const systemContent = context
|
||||||
|
? <span class="branch-0 cbranch-no" title="branch not covered" >`You are a helpful assistant.\n\nRelevant vault context:\n${context}`</span>
|
||||||
|
: 'You are a helpful assistant.';
|
||||||
const systemMessage: OllamaMessage = {
|
const systemMessage: OllamaMessage = {
|
||||||
role: 'system',
|
role: 'system',
|
||||||
content: 'You are a helpful assistant.',
|
content: systemContent,
|
||||||
};
|
};
|
||||||
const userContent = context ? <span class="branch-0 cbranch-no" title="branch not covered" >`${context}\n\n${userMessage}` </span>: userMessage;
|
|
||||||
const userMessageWithContext: OllamaMessage = {
|
const userMessageWithContext: OllamaMessage = {
|
||||||
role: 'user',
|
role: 'user',
|
||||||
content: userContent,
|
content: userMessage,
|
||||||
};
|
};
|
||||||
|
|
||||||
const messages: OllamaMessage[] = [
|
const messages: OllamaMessage[] = [
|
||||||
@@ -1421,9 +1423,9 @@ export class ChatView extends ItemView {
|
|||||||
for (const result of settledResults) {
|
for (const result of settledResults) {
|
||||||
if (result.status === 'fulfilled') {
|
if (result.status === 'fulfilled') {
|
||||||
toolResults.push(result.value);
|
toolResults.push(result.value);
|
||||||
} else <span class="missing-if-branch" title="else path not taken" >E</span>{
|
} else {
|
||||||
// Use centralized error handler for tool errors
|
// Use centralized error handler for tool errors
|
||||||
<span class="cstat-no" title="statement not covered" > ErrorHandler.handleError(result.reason, 'ChatView.handleUserInput');</span>
|
ErrorHandler.handleError(result.reason, 'ChatView.handleUserInput');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1448,23 +1450,21 @@ export class ChatView extends ItemView {
|
|||||||
content: fullResponse,
|
content: fullResponse,
|
||||||
isStreaming: false,
|
isStreaming: false,
|
||||||
});
|
});
|
||||||
} else <span class="missing-if-branch" title="else path not taken" >E</span>{
|
} else {
|
||||||
// Even if no tool results were successful, mark streaming as complete
|
// Even if no tool results were successful, mark streaming as complete
|
||||||
// to prevent the assistant message from disappearing
|
// to prevent the assistant message from disappearing
|
||||||
<span class="cstat-no" title="statement not covered" > this.updateMessageById(assistantMessageId, {</span>
|
this.updateMessageById(assistantMessageId, {
|
||||||
content: fullResponse,
|
content: fullResponse,
|
||||||
isStreaming: false,
|
isStreaming: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update last message immutably — only if no tool calls were processed
|
// Update assistant message immutably — only if no tool calls were processed
|
||||||
if (toolCalls.length === 0) {
|
if (toolCalls.length === 0) {
|
||||||
const lastMessageIndex = this.messages.length - 1;
|
this.updateMessageById(assistantMessageId, {
|
||||||
if (lastMessageIndex >= 0) {
|
isStreaming: false,
|
||||||
const lastMessage = { ...this.messages[lastMessageIndex], isStreaming: false };
|
});
|
||||||
this.messages = [...this.messages.slice(0, lastMessageIndex), lastMessage];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Limit conversation history to prevent memory issues
|
// Limit conversation history to prevent memory issues
|
||||||
@@ -1501,7 +1501,7 @@ export class ChatView extends ItemView {
|
|||||||
<div class='footer quiet pad2 space-top1 center small'>
|
<div class='footer quiet pad2 space-top1 center small'>
|
||||||
Code coverage generated by
|
Code coverage generated by
|
||||||
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
||||||
at 2026-05-06T20:36:57.831Z
|
at 2026-05-06T21:42:52.284Z
|
||||||
</div>
|
</div>
|
||||||
<script src="prettify.js"></script>
|
<script src="prettify.js"></script>
|
||||||
<script>
|
<script>
|
||||||
|
|||||||
@@ -214,30 +214,30 @@
|
|||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">2x</span>
|
<span class="cline-any cline-yes">2x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">10x</span>
|
|
||||||
<span class="cline-any cline-yes">10x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">10x</span>
|
|
||||||
<span class="cline-any cline-yes">9x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">9x</span>
|
<span class="cline-any cline-yes">9x</span>
|
||||||
<span class="cline-any cline-yes">9x</span>
|
<span class="cline-any cline-yes">9x</span>
|
||||||
<span class="cline-any cline-yes">9x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">1x</span>
|
|
||||||
<span class="cline-any cline-yes">1x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">16x</span>
|
|
||||||
<span class="cline-any cline-yes">7x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">9x</span>
|
<span class="cline-any cline-yes">9x</span>
|
||||||
<span class="cline-any cline-yes">8x</span>
|
<span class="cline-any cline-yes">8x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">8x</span>
|
||||||
|
<span class="cline-any cline-yes">8x</span>
|
||||||
|
<span class="cline-any cline-yes">8x</span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">1x</span>
|
||||||
|
<span class="cline-any cline-yes">1x</span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">15x</span>
|
||||||
|
<span class="cline-any cline-yes">7x</span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">8x</span>
|
||||||
|
<span class="cline-any cline-yes">7x</span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">1x</span>
|
<span class="cline-any cline-yes">1x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
@@ -272,15 +272,15 @@
|
|||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">14x</span>
|
<span class="cline-any cline-yes">13x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">14x</span>
|
<span class="cline-any cline-yes">13x</span>
|
||||||
<span class="cline-any cline-yes">2x</span>
|
<span class="cline-any cline-yes">2x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">12x</span>
|
<span class="cline-any cline-yes">11x</span>
|
||||||
<span class="cline-any cline-yes">3x</span>
|
<span class="cline-any cline-yes">2x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">9x</span>
|
<span class="cline-any cline-yes">9x</span>
|
||||||
@@ -475,7 +475,7 @@ export class ErrorHandler {
|
|||||||
<div class='footer quiet pad2 space-top1 center small'>
|
<div class='footer quiet pad2 space-top1 center small'>
|
||||||
Code coverage generated by
|
Code coverage generated by
|
||||||
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
||||||
at 2026-05-06T20:36:57.831Z
|
at 2026-05-06T21:42:52.284Z
|
||||||
</div>
|
</div>
|
||||||
<script src="prettify.js"></script>
|
<script src="prettify.js"></script>
|
||||||
<script>
|
<script>
|
||||||
|
|||||||
@@ -23,30 +23,30 @@
|
|||||||
<div class='clearfix'>
|
<div class='clearfix'>
|
||||||
|
|
||||||
<div class='fl pad1y space-right2'>
|
<div class='fl pad1y space-right2'>
|
||||||
<span class="strong">74.47% </span>
|
<span class="strong">93.41% </span>
|
||||||
<span class="quiet">Statements</span>
|
<span class="quiet">Statements</span>
|
||||||
<span class='fraction'>499/670</span>
|
<span class='fraction'>624/668</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class='fl pad1y space-right2'>
|
<div class='fl pad1y space-right2'>
|
||||||
<span class="strong">59.06% </span>
|
<span class="strong">82.09% </span>
|
||||||
<span class="quiet">Branches</span>
|
<span class="quiet">Branches</span>
|
||||||
<span class='fraction'>176/298</span>
|
<span class='fraction'>243/296</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class='fl pad1y space-right2'>
|
<div class='fl pad1y space-right2'>
|
||||||
<span class="strong">78.21% </span>
|
<span class="strong">93.06% </span>
|
||||||
<span class="quiet">Functions</span>
|
<span class="quiet">Functions</span>
|
||||||
<span class='fraction'>79/101</span>
|
<span class='fraction'>94/101</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class='fl pad1y space-right2'>
|
<div class='fl pad1y space-right2'>
|
||||||
<span class="strong">75.31% </span>
|
<span class="strong">94.51% </span>
|
||||||
<span class="quiet">Lines</span>
|
<span class="quiet">Lines</span>
|
||||||
<span class='fraction'>482/640</span>
|
<span class='fraction'>603/638</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
@@ -61,7 +61,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div class='status-line medium'></div>
|
<div class='status-line high'></div>
|
||||||
<div class="pad1">
|
<div class="pad1">
|
||||||
<table class="coverage-summary">
|
<table class="coverage-summary">
|
||||||
<thead>
|
<thead>
|
||||||
@@ -80,17 +80,17 @@
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody><tr>
|
<tbody><tr>
|
||||||
<td class="file high" data-value="chat-view.ts"><a href="chat-view.ts.html">chat-view.ts</a></td>
|
<td class="file high" data-value="chat-view.ts"><a href="chat-view.ts.html">chat-view.ts</a></td>
|
||||||
<td data-value="90.36" class="pic high">
|
<td data-value="92.55" class="pic high">
|
||||||
<div class="chart"><div class="cover-fill" style="width: 90%"></div><div class="cover-empty" style="width: 10%"></div></div>
|
<div class="chart"><div class="cover-fill" style="width: 92%"></div><div class="cover-empty" style="width: 8%"></div></div>
|
||||||
</td>
|
</td>
|
||||||
<td data-value="90.36" class="pct high">90.36%</td>
|
<td data-value="92.55" class="pct high">92.55%</td>
|
||||||
<td data-value="218" class="abs high">197/218</td>
|
<td data-value="215" class="abs high">199/215</td>
|
||||||
<td data-value="80.28" class="pct high">80.28%</td>
|
<td data-value="85.71" class="pct high">85.71%</td>
|
||||||
<td data-value="71" class="abs high">57/71</td>
|
<td data-value="70" class="abs high">60/70</td>
|
||||||
<td data-value="82.85" class="pct high">82.85%</td>
|
<td data-value="82.85" class="pct high">82.85%</td>
|
||||||
<td data-value="35" class="abs high">29/35</td>
|
<td data-value="35" class="abs high">29/35</td>
|
||||||
<td data-value="92.15" class="pct high">92.15%</td>
|
<td data-value="94.52" class="pct high">94.52%</td>
|
||||||
<td data-value="204" class="abs high">188/204</td>
|
<td data-value="201" class="abs high">190/201</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
@@ -109,18 +109,18 @@
|
|||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td class="file low" data-value="ollama-client.ts"><a href="ollama-client.ts.html">ollama-client.ts</a></td>
|
<td class="file high" data-value="ollama-client.ts"><a href="ollama-client.ts.html">ollama-client.ts</a></td>
|
||||||
<td data-value="17.7" class="pic low">
|
<td data-value="91.91" class="pic high">
|
||||||
<div class="chart"><div class="cover-fill" style="width: 17%"></div><div class="cover-empty" style="width: 83%"></div></div>
|
<div class="chart"><div class="cover-fill" style="width: 91%"></div><div class="cover-empty" style="width: 9%"></div></div>
|
||||||
</td>
|
</td>
|
||||||
<td data-value="17.7" class="pct low">17.7%</td>
|
<td data-value="91.91" class="pct high">91.91%</td>
|
||||||
<td data-value="96" class="abs low">17/96</td>
|
<td data-value="99" class="abs high">91/99</td>
|
||||||
<td data-value="4" class="pct low">4%</td>
|
<td data-value="72" class="pct medium">72%</td>
|
||||||
<td data-value="50" class="abs low">2/50</td>
|
<td data-value="50" class="abs medium">36/50</td>
|
||||||
<td data-value="36.36" class="pct low">36.36%</td>
|
<td data-value="100" class="pct high">100%</td>
|
||||||
<td data-value="11" class="abs low">4/11</td>
|
<td data-value="11" class="abs high">11/11</td>
|
||||||
<td data-value="18.47" class="pct low">18.47%</td>
|
<td data-value="91.57" class="pct high">91.57%</td>
|
||||||
<td data-value="92" class="abs low">17/92</td>
|
<td data-value="95" class="abs high">87/95</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
@@ -154,33 +154,33 @@
|
|||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td class="file low" data-value="utils.ts"><a href="utils.ts.html">utils.ts</a></td>
|
<td class="file high" data-value="utils.ts"><a href="utils.ts.html">utils.ts</a></td>
|
||||||
<td data-value="44.32" class="pic low">
|
<td data-value="95.87" class="pic high">
|
||||||
<div class="chart"><div class="cover-fill" style="width: 44%"></div><div class="cover-empty" style="width: 56%"></div></div>
|
<div class="chart"><div class="cover-fill" style="width: 95%"></div><div class="cover-empty" style="width: 5%"></div></div>
|
||||||
</td>
|
</td>
|
||||||
<td data-value="44.32" class="pct low">44.32%</td>
|
<td data-value="95.87" class="pct high">95.87%</td>
|
||||||
<td data-value="97" class="abs low">43/97</td>
|
<td data-value="97" class="abs high">93/97</td>
|
||||||
<td data-value="22.91" class="pct low">22.91%</td>
|
<td data-value="85.41" class="pct high">85.41%</td>
|
||||||
<td data-value="48" class="abs low">11/48</td>
|
<td data-value="48" class="abs high">41/48</td>
|
||||||
<td data-value="43.75" class="pct low">43.75%</td>
|
<td data-value="93.75" class="pct high">93.75%</td>
|
||||||
<td data-value="16" class="abs low">7/16</td>
|
<td data-value="16" class="abs high">15/16</td>
|
||||||
<td data-value="43.01" class="pct low">43.01%</td>
|
<td data-value="96.77" class="pct high">96.77%</td>
|
||||||
<td data-value="93" class="abs low">40/93</td>
|
<td data-value="93" class="abs high">90/93</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td class="file high" data-value="vault-indexer.ts"><a href="vault-indexer.ts.html">vault-indexer.ts</a></td>
|
<td class="file high" data-value="vault-indexer.ts"><a href="vault-indexer.ts.html">vault-indexer.ts</a></td>
|
||||||
<td data-value="92.1" class="pic high">
|
<td data-value="92.85" class="pic high">
|
||||||
<div class="chart"><div class="cover-fill" style="width: 92%"></div><div class="cover-empty" style="width: 8%"></div></div>
|
<div class="chart"><div class="cover-fill" style="width: 92%"></div><div class="cover-empty" style="width: 8%"></div></div>
|
||||||
</td>
|
</td>
|
||||||
<td data-value="92.1" class="pct high">92.1%</td>
|
<td data-value="92.85" class="pct high">92.85%</td>
|
||||||
<td data-value="114" class="abs high">105/114</td>
|
<td data-value="112" class="abs high">104/112</td>
|
||||||
<td data-value="78.26" class="pct medium">78.26%</td>
|
<td data-value="80" class="pct high">80%</td>
|
||||||
<td data-value="46" class="abs medium">36/46</td>
|
<td data-value="45" class="abs high">36/45</td>
|
||||||
<td data-value="100" class="pct high">100%</td>
|
<td data-value="100" class="pct high">100%</td>
|
||||||
<td data-value="15" class="abs high">15/15</td>
|
<td data-value="15" class="abs high">15/15</td>
|
||||||
<td data-value="94.33" class="pct high">94.33%</td>
|
<td data-value="95.19" class="pct high">95.19%</td>
|
||||||
<td data-value="106" class="abs high">100/106</td>
|
<td data-value="104" class="abs high">99/104</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
</tbody>
|
</tbody>
|
||||||
@@ -191,7 +191,7 @@
|
|||||||
<div class='footer quiet pad2 space-top1 center small'>
|
<div class='footer quiet pad2 space-top1 center small'>
|
||||||
Code coverage generated by
|
Code coverage generated by
|
||||||
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
||||||
at 2026-05-06T20:36:57.831Z
|
at 2026-05-06T21:42:52.284Z
|
||||||
</div>
|
</div>
|
||||||
<script src="prettify.js"></script>
|
<script src="prettify.js"></script>
|
||||||
<script>
|
<script>
|
||||||
|
|||||||
@@ -23,30 +23,30 @@
|
|||||||
<div class='clearfix'>
|
<div class='clearfix'>
|
||||||
|
|
||||||
<div class='fl pad1y space-right2'>
|
<div class='fl pad1y space-right2'>
|
||||||
<span class="strong">17.7% </span>
|
<span class="strong">91.91% </span>
|
||||||
<span class="quiet">Statements</span>
|
<span class="quiet">Statements</span>
|
||||||
<span class='fraction'>17/96</span>
|
<span class='fraction'>91/99</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class='fl pad1y space-right2'>
|
<div class='fl pad1y space-right2'>
|
||||||
<span class="strong">4% </span>
|
<span class="strong">72% </span>
|
||||||
<span class="quiet">Branches</span>
|
<span class="quiet">Branches</span>
|
||||||
<span class='fraction'>2/50</span>
|
<span class='fraction'>36/50</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class='fl pad1y space-right2'>
|
<div class='fl pad1y space-right2'>
|
||||||
<span class="strong">36.36% </span>
|
<span class="strong">100% </span>
|
||||||
<span class="quiet">Functions</span>
|
<span class="quiet">Functions</span>
|
||||||
<span class='fraction'>4/11</span>
|
<span class='fraction'>11/11</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class='fl pad1y space-right2'>
|
<div class='fl pad1y space-right2'>
|
||||||
<span class="strong">18.47% </span>
|
<span class="strong">91.57% </span>
|
||||||
<span class="quiet">Lines</span>
|
<span class="quiet">Lines</span>
|
||||||
<span class='fraction'>17/92</span>
|
<span class='fraction'>87/95</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
@@ -61,7 +61,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div class='status-line low'></div>
|
<div class='status-line high'></div>
|
||||||
<pre><table class="coverage">
|
<pre><table class="coverage">
|
||||||
<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
|
<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
|
||||||
<a name='L2'></a><a href='#L2'>2</a>
|
<a name='L2'></a><a href='#L2'>2</a>
|
||||||
@@ -293,7 +293,15 @@
|
|||||||
<a name='L228'></a><a href='#L228'>228</a>
|
<a name='L228'></a><a href='#L228'>228</a>
|
||||||
<a name='L229'></a><a href='#L229'>229</a>
|
<a name='L229'></a><a href='#L229'>229</a>
|
||||||
<a name='L230'></a><a href='#L230'>230</a>
|
<a name='L230'></a><a href='#L230'>230</a>
|
||||||
<a name='L231'></a><a href='#L231'>231</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral"> </span>
|
<a name='L231'></a><a href='#L231'>231</a>
|
||||||
|
<a name='L232'></a><a href='#L232'>232</a>
|
||||||
|
<a name='L233'></a><a href='#L233'>233</a>
|
||||||
|
<a name='L234'></a><a href='#L234'>234</a>
|
||||||
|
<a name='L235'></a><a href='#L235'>235</a>
|
||||||
|
<a name='L236'></a><a href='#L236'>236</a>
|
||||||
|
<a name='L237'></a><a href='#L237'>237</a>
|
||||||
|
<a name='L238'></a><a href='#L238'>238</a>
|
||||||
|
<a name='L239'></a><a href='#L239'>239</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">2x</span>
|
<span class="cline-any cline-yes">2x</span>
|
||||||
@@ -306,19 +314,38 @@
|
|||||||
<span class="cline-any cline-yes">2x</span>
|
<span class="cline-any cline-yes">2x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">31x</span>
|
<span class="cline-any cline-yes">59x</span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">59x</span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">59x</span>
|
||||||
|
<span class="cline-any cline-yes">59x</span>
|
||||||
|
<span class="cline-any cline-yes">59x</span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">28x</span>
|
||||||
|
<span class="cline-any cline-no"> </span>
|
||||||
|
<span class="cline-any cline-no"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">20x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">31x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">31x</span>
|
|
||||||
<span class="cline-any cline-yes">31x</span>
|
|
||||||
<span class="cline-any cline-yes">31x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">3x</span>
|
<span class="cline-any cline-yes">3x</span>
|
||||||
|
<span class="cline-any cline-yes">3x</span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-no"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-no"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
@@ -327,29 +354,161 @@
|
|||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">25x</span>
|
||||||
|
<span class="cline-any cline-yes">25x</span>
|
||||||
|
<span class="cline-any cline-yes">25x</span>
|
||||||
|
<span class="cline-any cline-yes">5x</span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">25x</span>
|
||||||
|
<span class="cline-any cline-yes">25x</span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">25x</span>
|
||||||
|
<span class="cline-any cline-yes">8x</span>
|
||||||
|
<span class="cline-any cline-yes">5x</span>
|
||||||
|
<span class="cline-any cline-yes">5x</span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">5x</span>
|
||||||
|
<span class="cline-any cline-yes">5x</span>
|
||||||
|
<span class="cline-any cline-yes">2x</span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">3x</span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">13x</span>
|
||||||
|
<span class="cline-any cline-yes">1x</span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">12x</span>
|
||||||
|
<span class="cline-any cline-yes">12x</span>
|
||||||
|
<span class="cline-any cline-yes">1x</span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">11x</span>
|
||||||
|
<span class="cline-any cline-yes">11x</span>
|
||||||
|
<span class="cline-any cline-yes">11x</span>
|
||||||
|
<span class="cline-any cline-yes">11x</span>
|
||||||
|
<span class="cline-any cline-yes">11x</span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">11x</span>
|
||||||
|
<span class="cline-any cline-yes">11x</span>
|
||||||
|
<span class="cline-any cline-yes">19x</span>
|
||||||
|
<span class="cline-any cline-yes">19x</span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">10x</span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">10x</span>
|
||||||
|
<span class="cline-any cline-yes">10x</span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">10x</span>
|
||||||
|
<span class="cline-any cline-yes">68x</span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">67x</span>
|
||||||
|
<span class="cline-any cline-yes">67x</span>
|
||||||
|
<span class="cline-any cline-yes">14x</span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">13x</span>
|
||||||
|
<span class="cline-any cline-yes">13x</span>
|
||||||
|
<span class="cline-any cline-no"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">13x</span>
|
||||||
|
<span class="cline-any cline-yes">13x</span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">54x</span>
|
||||||
|
<span class="cline-any cline-yes">1x</span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">53x</span>
|
||||||
|
<span class="cline-any cline-yes">53x</span>
|
||||||
|
<span class="cline-any cline-yes">1x</span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">52x</span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">9x</span>
|
||||||
|
<span class="cline-any cline-yes">1x</span>
|
||||||
|
<span class="cline-any cline-yes">1x</span>
|
||||||
|
<span class="cline-any cline-yes">1x</span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">1x</span>
|
||||||
|
<span class="cline-any cline-yes">1x</span>
|
||||||
|
<span class="cline-any cline-yes">1x</span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-no"> </span>
|
||||||
|
<span class="cline-any cline-no"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-no"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">11x</span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">25x</span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">5x</span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">8x</span>
|
||||||
|
<span class="cline-any cline-yes">8x</span>
|
||||||
|
<span class="cline-any cline-yes">8x</span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">8x</span>
|
||||||
<span class="cline-any cline-yes">4x</span>
|
<span class="cline-any cline-yes">4x</span>
|
||||||
|
<span class="cline-any cline-yes">3x</span>
|
||||||
|
<span class="cline-any cline-yes">3x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">3x</span>
|
||||||
|
<span class="cline-any cline-yes">3x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-yes">1x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">4x</span>
|
|
||||||
<span class="cline-any cline-yes">4x</span>
|
|
||||||
<span class="cline-any cline-yes">4x</span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">4x</span>
|
<span class="cline-any cline-yes">4x</span>
|
||||||
@@ -357,166 +516,23 @@
|
|||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">8x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">15x</span>
|
||||||
|
<span class="cline-any cline-yes">1x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">18x</span>
|
||||||
|
<span class="cline-any cline-yes">1x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">4x</span>
|
<span class="cline-any cline-yes">17x</span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-yes">17x</span>
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">4x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
@@ -560,12 +576,12 @@ export class OllamaClient {
|
|||||||
yield* this.streamChatWithRetry(messages, tools, 0);
|
yield* this.streamChatWithRetry(messages, tools, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
<span class="fstat-no" title="function not covered" > async </span>streamChatAsPromise(
|
async streamChatAsPromise(
|
||||||
messages: OllamaMessage[],
|
messages: OllamaMessage[],
|
||||||
tools: OllamaTool[] = <span class="branch-0 cbranch-no" title="branch not covered" >[]</span>
|
tools: OllamaTool[] = <span class="branch-0 cbranch-no" title="branch not covered" >[]</span>
|
||||||
): Promise<OllamaMessage[]> {
|
): Promise<OllamaMessage[]> {
|
||||||
const chunks: OllamaMessage[] = <span class="cstat-no" title="statement not covered" >[];</span>
|
const chunks: OllamaMessage[] = [];
|
||||||
<span class="cstat-no" title="statement not covered" > for await (const chunk of this.streamChat(messages, tools)) {</span>
|
for await (const chunk of this.streamChat(messages, tools)) {
|
||||||
<span class="cstat-no" title="statement not covered" > chunks.push(chunk);</span>
|
<span class="cstat-no" title="statement not covered" > chunks.push(chunk);</span>
|
||||||
}
|
}
|
||||||
<span class="cstat-no" title="statement not covered" > return chunks;</span>
|
<span class="cstat-no" title="statement not covered" > return chunks;</span>
|
||||||
@@ -578,8 +594,8 @@ export class OllamaClient {
|
|||||||
): AsyncGenerator<OllamaMessage, void, unknown> {
|
): AsyncGenerator<OllamaMessage, void, unknown> {
|
||||||
const prevController = this.abortController;
|
const prevController = this.abortController;
|
||||||
this.abortController = new AbortController();
|
this.abortController = new AbortController();
|
||||||
<span class="missing-if-branch" title="if path not taken" >I</span>if (prevController) {
|
if (prevController) {
|
||||||
<span class="cstat-no" title="statement not covered" > prevController.abort();</span>
|
prevController.abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -597,70 +613,70 @@ export class OllamaClient {
|
|||||||
signal: this.abortController.signal,
|
signal: this.abortController.signal,
|
||||||
});
|
});
|
||||||
|
|
||||||
<span class="missing-if-branch" title="if path not taken" >I</span>if (!response.ok) {
|
if (!response.ok) {
|
||||||
<span class="cstat-no" title="statement not covered" > <span class="missing-if-branch" title="if path not taken" >I</span>if (response.status >= 500 && attempt < this.maxRetries) {</span>
|
if (response.status >= 500 && attempt < this.maxRetries) {
|
||||||
const retryDelay = <span class="cstat-no" title="statement not covered" >Math.pow(2, attempt) * 100;</span>
|
const retryDelay = Math.pow(2, attempt) * 100;
|
||||||
<span class="cstat-no" title="statement not covered" > Logger.warn(</span>
|
Logger.warn(
|
||||||
`Network error (status ${response.status}), retrying in ${retryDelay}ms (attempt ${attempt + 1}/${this.maxRetries})`,
|
`Network error (status ${response.status}), retrying in ${retryDelay}ms (attempt ${attempt + 1}/${this.maxRetries})`,
|
||||||
'ollama-client'
|
'ollama-client'
|
||||||
);
|
);
|
||||||
<span class="cstat-no" title="statement not covered" > await new Promise(<span class="fstat-no" title="function not covered" >(r</span>esolve) => <span class="cstat-no" title="statement not covered" >setTimeout(resolve, retryDelay))</span>;</span>
|
await new Promise((resolve) => setTimeout(resolve, retryDelay));
|
||||||
<span class="cstat-no" title="statement not covered" > yield* this.streamChatWithRetry(messages, tools, attempt + 1);</span>
|
yield* this.streamChatWithRetry(messages, tools, attempt + 1);
|
||||||
<span class="cstat-no" title="statement not covered" > return;</span>
|
return;
|
||||||
}
|
}
|
||||||
<span class="cstat-no" title="statement not covered" > throw new ApiError(`Ollama API error: ${response.status}`, response.status);</span>
|
throw new ApiError(`Ollama API error: ${response.status}`, response.status);
|
||||||
}
|
}
|
||||||
|
|
||||||
<span class="cstat-no" title="statement not covered" > <span class="missing-if-branch" title="if path not taken" >I</span>if (!response.body) {</span>
|
if (!response.body) {
|
||||||
<span class="cstat-no" title="statement not covered" > throw new Error('No response body');</span>
|
throw new Error('No response body');
|
||||||
}
|
}
|
||||||
|
|
||||||
const contentType = <span class="cstat-no" title="statement not covered" >response.headers.get('content-type');</span>
|
const contentType = response.headers.get('content-type');
|
||||||
<span class="cstat-no" title="statement not covered" > <span class="missing-if-branch" title="if path not taken" >I</span>if (!contentType || (!contentType.includes('ndjson') && !contentType.includes('json'))) {</span>
|
if (!contentType || (!contentType.includes('ndjson') && !contentType.includes('json'))) {
|
||||||
<span class="cstat-no" title="statement not covered" > throw new Error('Invalid response format');</span>
|
throw new Error('Invalid response format');
|
||||||
}
|
}
|
||||||
|
|
||||||
const reader = <span class="cstat-no" title="statement not covered" >response.body.getReader();</span>
|
const reader = response.body.getReader();
|
||||||
const decoder = <span class="cstat-no" title="statement not covered" >new TextDecoder();</span>
|
const decoder = new TextDecoder();
|
||||||
let buffer = <span class="cstat-no" title="statement not covered" >'';</span>
|
let buffer = '';
|
||||||
let malformedCount = <span class="cstat-no" title="statement not covered" >0;</span>
|
let malformedCount = 0;
|
||||||
const maxMalformed = <span class="cstat-no" title="statement not covered" >50;</span>
|
const maxMalformed = 50;
|
||||||
|
|
||||||
<span class="cstat-no" title="statement not covered" > try {</span>
|
try {
|
||||||
<span class="cstat-no" title="statement not covered" > while (true) {</span>
|
while (true) {
|
||||||
const { done, value } = <span class="cstat-no" title="statement not covered" >await reader.read();</span>
|
const { done, value } = await reader.read();
|
||||||
<span class="cstat-no" title="statement not covered" > <span class="missing-if-branch" title="if path not taken" >I</span>if (done) <span class="cstat-no" title="statement not covered" >break;</span></span>
|
if (done) break;
|
||||||
|
|
||||||
<span class="cstat-no" title="statement not covered" > buffer += decoder.decode(value, { stream: true });</span>
|
buffer += decoder.decode(value, { stream: true });
|
||||||
|
|
||||||
const lines = <span class="cstat-no" title="statement not covered" >buffer.split('\n');</span>
|
const lines = buffer.split('\n');
|
||||||
<span class="cstat-no" title="statement not covered" > buffer = lines.pop() ?? '';</span>
|
buffer = lines.pop() ?? <span class="branch-1 cbranch-no" title="branch not covered" >'';</span>
|
||||||
|
|
||||||
<span class="cstat-no" title="statement not covered" > for (const line of lines) {</span>
|
for (const line of lines) {
|
||||||
<span class="cstat-no" title="statement not covered" > <span class="missing-if-branch" title="if path not taken" >I</span>if (!line.trim()) <span class="cstat-no" title="statement not covered" >continue;</span></span>
|
if (!line.trim()) continue;
|
||||||
|
|
||||||
<span class="cstat-no" title="statement not covered" > try {</span>
|
try {
|
||||||
const parsed = <span class="cstat-no" title="statement not covered" >JSON.parse(line) as Record<string, unknown>;</span>
|
const parsed = JSON.parse(line) as Record<string, unknown>;
|
||||||
<span class="cstat-no" title="statement not covered" > this.throwIfOllamaError(parsed);</span>
|
this.throwIfOllamaError(parsed);
|
||||||
|
|
||||||
const message = <span class="cstat-no" title="statement not covered" >this.toOllamaMessage(parsed.message);</span>
|
const message = this.toOllamaMessage(parsed.message);
|
||||||
<span class="cstat-no" title="statement not covered" > <span class="missing-if-branch" title="if path not taken" >I</span>if (!message) {</span>
|
<span class="missing-if-branch" title="if path not taken" >I</span>if (!message) {
|
||||||
<span class="cstat-no" title="statement not covered" > continue;</span>
|
<span class="cstat-no" title="statement not covered" > continue;</span>
|
||||||
}
|
}
|
||||||
|
|
||||||
<span class="cstat-no" title="statement not covered" > malformedCount = 0;</span>
|
malformedCount = 0;
|
||||||
<span class="cstat-no" title="statement not covered" > yield message;</span>
|
yield message;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
<span class="cstat-no" title="statement not covered" > <span class="missing-if-branch" title="if path not taken" >I</span>if (error instanceof Error && error.message.startsWith('Ollama error:')) {</span>
|
if (error instanceof Error && error.message.startsWith('Ollama error:')) {
|
||||||
<span class="cstat-no" title="statement not covered" > throw error;</span>
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
<span class="cstat-no" title="statement not covered" > malformedCount++;</span>
|
malformedCount++;
|
||||||
<span class="cstat-no" title="statement not covered" > <span class="missing-if-branch" title="if path not taken" >I</span>if (malformedCount > maxMalformed) {</span>
|
if (malformedCount > maxMalformed) {
|
||||||
<span class="cstat-no" title="statement not covered" > throw new Error('Too many malformed chunks in stream');</span>
|
throw new Error('Too many malformed chunks in stream');
|
||||||
}
|
}
|
||||||
|
|
||||||
<span class="cstat-no" title="statement not covered" > Logger.warn(</span>
|
Logger.warn(
|
||||||
`Skipped malformed chunk: ${line.substring(0, 80)}... - ${(error as Error).message}`,
|
`Skipped malformed chunk: ${line.substring(0, 80)}... - ${(error as Error).message}`,
|
||||||
'ollama-client'
|
'ollama-client'
|
||||||
);
|
);
|
||||||
@@ -668,14 +684,14 @@ export class OllamaClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
<span class="cstat-no" title="statement not covered" > <span class="missing-if-branch" title="if path not taken" >I</span>if (buffer.trim()) {</span>
|
if (buffer.trim()) {
|
||||||
<span class="cstat-no" title="statement not covered" > try {</span>
|
try {
|
||||||
const parsed = <span class="cstat-no" title="statement not covered" >JSON.parse(buffer) as Record<string, unknown>;</span>
|
const parsed = JSON.parse(buffer) as Record<string, unknown>;
|
||||||
<span class="cstat-no" title="statement not covered" > this.throwIfOllamaError(parsed);</span>
|
this.throwIfOllamaError(parsed);
|
||||||
|
|
||||||
const message = <span class="cstat-no" title="statement not covered" >this.toOllamaMessage(parsed.message);</span>
|
const message = this.toOllamaMessage(parsed.message);
|
||||||
<span class="cstat-no" title="statement not covered" > <span class="missing-if-branch" title="if path not taken" >I</span>if (message) {</span>
|
if (message) {
|
||||||
<span class="cstat-no" title="statement not covered" > yield message;</span>
|
yield message;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
<span class="cstat-no" title="statement not covered" > <span class="missing-if-branch" title="if path not taken" >I</span>if (error instanceof Error && error.message.startsWith('Ollama error:')) {</span>
|
<span class="cstat-no" title="statement not covered" > <span class="missing-if-branch" title="if path not taken" >I</span>if (error instanceof Error && error.message.startsWith('Ollama error:')) {</span>
|
||||||
@@ -688,67 +704,75 @@ export class OllamaClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
<span class="cstat-no" title="statement not covered" > reader.releaseLock();</span>
|
reader.releaseLock();
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
this.abortController = null;
|
this.abortController = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
<span class="fstat-no" title="function not covered" > async </span>chat(messages: OllamaMessage[], tools: OllamaTool[] = <span class="branch-0 cbranch-no" title="branch not covered" >[])</span>: Promise<OllamaMessage> {
|
async chat(messages: OllamaMessage[], tools: OllamaTool[] = <span class="branch-0 cbranch-no" title="branch not covered" >[])</span>: Promise<OllamaMessage> {
|
||||||
<span class="cstat-no" title="statement not covered" > return this.chatWithRetry(messages, tools, 0);</span>
|
return this.chatWithRetry(messages, tools, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private <span class="fstat-no" title="function not covered" >async </span>chatWithRetry(
|
private async chatWithRetry(
|
||||||
messages: OllamaMessage[],
|
messages: OllamaMessage[],
|
||||||
tools: OllamaTool[] = <span class="branch-0 cbranch-no" title="branch not covered" >[],</span>
|
tools: OllamaTool[] = <span class="branch-0 cbranch-no" title="branch not covered" >[],</span>
|
||||||
attempt: number = <span class="branch-0 cbranch-no" title="branch not covered" >0</span>
|
attempt: number = <span class="branch-0 cbranch-no" title="branch not covered" >0</span>
|
||||||
): Promise<OllamaMessage> {
|
): Promise<OllamaMessage> {
|
||||||
const response = <span class="cstat-no" title="statement not covered" >await this.fetchFn(`${this.baseURL}/api/chat`, {</span>
|
this.abortController = new AbortController();
|
||||||
method: 'POST',
|
try {
|
||||||
headers: {
|
const response = await this.fetchFn(`${this.baseURL}/api/chat`, {
|
||||||
'Content-Type': 'application/json',
|
method: 'POST',
|
||||||
},
|
headers: {
|
||||||
body: JSON.stringify({
|
'Content-Type': 'application/json',
|
||||||
model: this.model,
|
},
|
||||||
messages,
|
body: JSON.stringify({
|
||||||
tools,
|
model: this.model,
|
||||||
stream: false,
|
messages,
|
||||||
}),
|
tools,
|
||||||
});
|
stream: false,
|
||||||
|
}),
|
||||||
|
signal: this.abortController.signal,
|
||||||
|
});
|
||||||
|
|
||||||
<span class="cstat-no" title="statement not covered" > <span class="missing-if-branch" title="if path not taken" >I</span>if (!response.ok) {</span>
|
if (!response.ok) {
|
||||||
<span class="cstat-no" title="statement not covered" > <span class="missing-if-branch" title="if path not taken" >I</span>if (response.status >= 500 && attempt < this.maxRetries) {</span>
|
if (response.status >= 500 && attempt < this.maxRetries) {
|
||||||
const retryDelay = <span class="cstat-no" title="statement not covered" >Math.pow(2, attempt) * 100;</span>
|
const retryDelay = Math.pow(2, attempt) * 100;
|
||||||
<span class="cstat-no" title="statement not covered" > Logger.warn(</span>
|
Logger.warn(
|
||||||
`Network error (status ${response.status}), retrying in ${retryDelay}ms (attempt ${attempt + 1}/${this.maxRetries})`,
|
`Network error (status ${response.status}), retrying in ${retryDelay}ms (attempt ${attempt + 1}/${this.maxRetries})`,
|
||||||
'ollama-client'
|
'ollama-client'
|
||||||
);
|
);
|
||||||
<span class="cstat-no" title="statement not covered" > await new Promise(<span class="fstat-no" title="function not covered" >(r</span>esolve) => <span class="cstat-no" title="statement not covered" >setTimeout(resolve, retryDelay))</span>;</span>
|
await new Promise((resolve) => setTimeout(resolve, retryDelay));
|
||||||
<span class="cstat-no" title="statement not covered" > return this.chatWithRetry(messages, tools, attempt + 1);</span>
|
return this.chatWithRetry(messages, tools, attempt + 1);
|
||||||
|
}
|
||||||
|
throw new ApiError(`Ollama API error: ${response.status}`, response.status);
|
||||||
}
|
}
|
||||||
<span class="cstat-no" title="statement not covered" > throw new ApiError(`Ollama API error: ${response.status}`, response.status);</span>
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = (<span class="cstat-no" title="statement not covered" >await response.json())</span> as OllamaChatResponse;
|
const data = (await response.json()) as OllamaChatResponse;
|
||||||
<span class="cstat-no" title="statement not covered" > return this.toOllamaMessage(data.message) ?? { role: 'assistant', content: '', tool_calls: [] };</span>
|
return (
|
||||||
}
|
this.toOllamaMessage(data.message) ?? { role: 'assistant', content: '', tool_calls: [] }
|
||||||
|
);
|
||||||
private <span class="fstat-no" title="function not covered" >throwIfOllamaError(</span>parsed: Record<string, unknown>): void {
|
} finally {
|
||||||
<span class="cstat-no" title="statement not covered" > <span class="missing-if-branch" title="if path not taken" >I</span>if (parsed.error) {</span>
|
this.abortController = null;
|
||||||
<span class="cstat-no" title="statement not covered" > throw new Error(`Ollama error: ${String(parsed.error)}`);</span>
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private <span class="fstat-no" title="function not covered" >toOllamaMessage(</span>value: unknown): OllamaMessage | null {
|
private throwIfOllamaError(parsed: Record<string, unknown>): void {
|
||||||
<span class="cstat-no" title="statement not covered" > <span class="missing-if-branch" title="if path not taken" >I</span>if (!value || typeof value !== 'object') {</span>
|
if (parsed.error) {
|
||||||
<span class="cstat-no" title="statement not covered" > return null;</span>
|
throw new Error(`Ollama error: ${String(parsed.error)}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private toOllamaMessage(value: unknown): OllamaMessage | null {
|
||||||
|
if (!value || typeof value !== 'object') {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const record = <span class="cstat-no" title="statement not covered" >value as Partial<OllamaMessage>;</span>
|
const record = value as Partial<OllamaMessage>;
|
||||||
<span class="cstat-no" title="statement not covered" > return {</span>
|
return {
|
||||||
role: record.role ?? 'assistant',
|
role: record.role ?? 'assistant',
|
||||||
content: typeof record.content === 'string' ? record.content : '',
|
content: typeof record.content === 'string' ? record.content : <span class="branch-1 cbranch-no" title="branch not covered" >'',</span>
|
||||||
tool_calls: record.tool_calls ?? [],
|
tool_calls: record.tool_calls ?? [],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -760,7 +784,7 @@ export class OllamaClient {
|
|||||||
<div class='footer quiet pad2 space-top1 center small'>
|
<div class='footer quiet pad2 space-top1 center small'>
|
||||||
Code coverage generated by
|
Code coverage generated by
|
||||||
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
||||||
at 2026-05-06T20:36:57.831Z
|
at 2026-05-06T21:42:52.284Z
|
||||||
</div>
|
</div>
|
||||||
<script src="prettify.js"></script>
|
<script src="prettify.js"></script>
|
||||||
<script>
|
<script>
|
||||||
|
|||||||
@@ -210,8 +210,8 @@
|
|||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">58x</span>
|
<span class="cline-any cline-yes">63x</span>
|
||||||
<span class="cline-any cline-yes">58x</span>
|
<span class="cline-any cline-yes">63x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
@@ -463,7 +463,7 @@ export class ToolExecutor {
|
|||||||
<div class='footer quiet pad2 space-top1 center small'>
|
<div class='footer quiet pad2 space-top1 center small'>
|
||||||
Code coverage generated by
|
Code coverage generated by
|
||||||
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
||||||
at 2026-05-06T20:36:57.831Z
|
at 2026-05-06T21:42:52.284Z
|
||||||
</div>
|
</div>
|
||||||
<script src="prettify.js"></script>
|
<script src="prettify.js"></script>
|
||||||
<script>
|
<script>
|
||||||
|
|||||||
@@ -261,9 +261,9 @@
|
|||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">15x</span>
|
<span class="cline-any cline-yes">19x</span>
|
||||||
<span class="cline-any cline-yes">15x</span>
|
<span class="cline-any cline-yes">19x</span>
|
||||||
<span class="cline-any cline-yes">15x</span>
|
<span class="cline-any cline-yes">19x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
@@ -281,9 +281,9 @@
|
|||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">1x</span>
|
<span class="cline-any cline-yes">5x</span>
|
||||||
<span class="cline-any cline-yes">1x</span>
|
<span class="cline-any cline-yes">5x</span>
|
||||||
<span class="cline-any cline-yes">1x</span>
|
<span class="cline-any cline-yes">5x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
@@ -604,7 +604,7 @@ export interface VaultIndexEntry {
|
|||||||
<div class='footer quiet pad2 space-top1 center small'>
|
<div class='footer quiet pad2 space-top1 center small'>
|
||||||
Code coverage generated by
|
Code coverage generated by
|
||||||
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
||||||
at 2026-05-06T20:36:57.831Z
|
at 2026-05-06T21:42:52.284Z
|
||||||
</div>
|
</div>
|
||||||
<script src="prettify.js"></script>
|
<script src="prettify.js"></script>
|
||||||
<script>
|
<script>
|
||||||
|
|||||||
+167
-179
@@ -23,30 +23,30 @@
|
|||||||
<div class='clearfix'>
|
<div class='clearfix'>
|
||||||
|
|
||||||
<div class='fl pad1y space-right2'>
|
<div class='fl pad1y space-right2'>
|
||||||
<span class="strong">44.32% </span>
|
<span class="strong">95.87% </span>
|
||||||
<span class="quiet">Statements</span>
|
<span class="quiet">Statements</span>
|
||||||
<span class='fraction'>43/97</span>
|
<span class='fraction'>93/97</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class='fl pad1y space-right2'>
|
<div class='fl pad1y space-right2'>
|
||||||
<span class="strong">22.91% </span>
|
<span class="strong">85.41% </span>
|
||||||
<span class="quiet">Branches</span>
|
<span class="quiet">Branches</span>
|
||||||
<span class='fraction'>11/48</span>
|
<span class='fraction'>41/48</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class='fl pad1y space-right2'>
|
<div class='fl pad1y space-right2'>
|
||||||
<span class="strong">43.75% </span>
|
<span class="strong">93.75% </span>
|
||||||
<span class="quiet">Functions</span>
|
<span class="quiet">Functions</span>
|
||||||
<span class='fraction'>7/16</span>
|
<span class='fraction'>15/16</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class='fl pad1y space-right2'>
|
<div class='fl pad1y space-right2'>
|
||||||
<span class="strong">43.01% </span>
|
<span class="strong">96.77% </span>
|
||||||
<span class="quiet">Lines</span>
|
<span class="quiet">Lines</span>
|
||||||
<span class='fraction'>40/93</span>
|
<span class='fraction'>90/93</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
@@ -61,7 +61,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div class='status-line low'></div>
|
<div class='status-line high'></div>
|
||||||
<pre><table class="coverage">
|
<pre><table class="coverage">
|
||||||
<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
|
<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
|
||||||
<a name='L2'></a><a href='#L2'>2</a>
|
<a name='L2'></a><a href='#L2'>2</a>
|
||||||
@@ -267,15 +267,7 @@
|
|||||||
<a name='L202'></a><a href='#L202'>202</a>
|
<a name='L202'></a><a href='#L202'>202</a>
|
||||||
<a name='L203'></a><a href='#L203'>203</a>
|
<a name='L203'></a><a href='#L203'>203</a>
|
||||||
<a name='L204'></a><a href='#L204'>204</a>
|
<a name='L204'></a><a href='#L204'>204</a>
|
||||||
<a name='L205'></a><a href='#L205'>205</a>
|
<a name='L205'></a><a href='#L205'>205</a></td><td class="line-coverage quiet"><span class="cline-any cline-yes">5x</span>
|
||||||
<a name='L206'></a><a href='#L206'>206</a>
|
|
||||||
<a name='L207'></a><a href='#L207'>207</a>
|
|
||||||
<a name='L208'></a><a href='#L208'>208</a>
|
|
||||||
<a name='L209'></a><a href='#L209'>209</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">5x</span>
|
|
||||||
<span class="cline-any cline-yes">5x</span>
|
<span class="cline-any cline-yes">5x</span>
|
||||||
<span class="cline-any cline-yes">5x</span>
|
<span class="cline-any cline-yes">5x</span>
|
||||||
<span class="cline-any cline-yes">5x</span>
|
<span class="cline-any cline-yes">5x</span>
|
||||||
@@ -293,35 +285,35 @@
|
|||||||
<span class="cline-any cline-yes">5x</span>
|
<span class="cline-any cline-yes">5x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-yes">23x</span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-yes">11x</span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-yes">11x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-yes">12x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-yes">4x</span>
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">1x</span>
|
|
||||||
<span class="cline-any cline-yes">1x</span>
|
<span class="cline-any cline-yes">1x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-yes">4x</span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-yes">2x</span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">64x</span>
|
||||||
|
<span class="cline-any cline-yes">63x</span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">2x</span>
|
||||||
|
<span class="cline-any cline-yes">2x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
@@ -329,71 +321,71 @@
|
|||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">5x</span>
|
<span class="cline-any cline-yes">5x</span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-yes">12x</span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-yes">2x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-yes">10x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-yes">10x</span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-yes">1x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-yes">9x</span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-yes">9x</span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-yes">6x</span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-no"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-yes">6x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-yes">3x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">5x</span>
|
<span class="cline-any cline-yes">5x</span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-yes">15x</span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-yes">1x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-yes">14x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-yes">14x</span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-yes">1x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-yes">13x</span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-yes">1x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-yes">12x</span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-yes">1x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-yes">11x</span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-yes">3x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-yes">8x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">5x</span>
|
<span class="cline-any cline-yes">5x</span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-yes">4x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-yes">4x</span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-yes">4x</span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-yes">2x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-yes">4x</span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-yes">4x</span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-yes">2x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-yes">4x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
@@ -402,67 +394,67 @@
|
|||||||
<span class="cline-any cline-yes">5x</span>
|
<span class="cline-any cline-yes">5x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">75x</span>
|
<span class="cline-any cline-yes">117x</span>
|
||||||
|
<span class="cline-any cline-yes">1x</span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">116x</span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-no"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">75x</span>
|
<span class="cline-any cline-yes">116x</span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-yes">58x</span>
|
||||||
|
<span class="cline-any cline-yes">58x</span>
|
||||||
|
<span class="cline-any cline-yes">83x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">75x</span>
|
<span class="cline-any cline-yes">58x</span>
|
||||||
<span class="cline-any cline-yes">26x</span>
|
|
||||||
<span class="cline-any cline-yes">26x</span>
|
|
||||||
<span class="cline-any cline-yes">49x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">49x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">5x</span>
|
<span class="cline-any cline-yes">5x</span>
|
||||||
<span class="cline-any cline-yes">27x</span>
|
<span class="cline-any cline-yes">41x</span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-yes">1x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">27x</span>
|
<span class="cline-any cline-yes">40x</span>
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">27x</span>
|
|
||||||
<span class="cline-any cline-yes">27x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">1x</span>
|
<span class="cline-any cline-yes">1x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">26x</span>
|
<span class="cline-any cline-yes">39x</span>
|
||||||
<span class="cline-any cline-yes">75x</span>
|
<span class="cline-any cline-yes">39x</span>
|
||||||
<span class="cline-any cline-yes">49x</span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">2x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">26x</span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">78x</span>
|
<span class="cline-any cline-yes">37x</span>
|
||||||
|
<span class="cline-any cline-yes">121x</span>
|
||||||
|
<span class="cline-any cline-yes">59x</span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">62x</span>
|
||||||
|
<span class="cline-any cline-yes">183x</span>
|
||||||
|
<span class="cline-any cline-yes">3x</span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">59x</span>
|
||||||
|
<span class="cline-any cline-yes">84x</span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-no"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">26x</span>
|
<span class="cline-any cline-yes">59x</span>
|
||||||
<span class="cline-any cline-yes">49x</span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
|
<span class="cline-any cline-yes">37x</span>
|
||||||
|
<span class="cline-any cline-yes">3x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">26x</span>
|
<span class="cline-any cline-yes">34x</span>
|
||||||
|
<span class="cline-any cline-yes">1x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">26x</span>
|
<span class="cline-any cline-yes">33x</span>
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">26x</span>
|
|
||||||
<span class="cline-any cline-no"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">26x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
@@ -470,20 +462,16 @@
|
|||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">5x</span>
|
<span class="cline-any cline-yes">5x</span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-yes">6x</span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-yes">6x</span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-yes">4x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-yes">2x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span></td><td class="text"><pre class="prettyprint lang-js">// src/utils.ts
|
<span class="cline-any cline-neutral"> </span></td><td class="text"><pre class="prettyprint lang-js">export enum LogLevel {
|
||||||
|
|
||||||
// ==================== Logger ====================
|
|
||||||
|
|
||||||
enum LogLevel {
|
|
||||||
DEBUG = 0,
|
DEBUG = 0,
|
||||||
INFO = 1,
|
INFO = 1,
|
||||||
WARN = 2,
|
WARN = 2,
|
||||||
@@ -500,24 +488,24 @@ const SEVERITY_ORDER: Record<string, number> = {
|
|||||||
export class Logger {
|
export class Logger {
|
||||||
private static minLevel: LogLevel = LogLevel.DEBUG;
|
private static minLevel: LogLevel = LogLevel.DEBUG;
|
||||||
|
|
||||||
<span class="fstat-no" title="function not covered" > static </span>setLevel(level: string | LogLevel): void {
|
static setLevel(level: string | LogLevel): void {
|
||||||
<span class="cstat-no" title="statement not covered" > if (typeof level === 'string') {</span>
|
if (typeof level === 'string') {
|
||||||
const lowerLevel = <span class="cstat-no" title="statement not covered" >level.toLowerCase();</span>
|
const lowerLevel = level.toLowerCase();
|
||||||
<span class="cstat-no" title="statement not covered" > Logger.minLevel = SEVERITY_ORDER[lowerLevel] ?? LogLevel.DEBUG;</span>
|
Logger.minLevel = SEVERITY_ORDER[lowerLevel] ?? LogLevel.DEBUG;
|
||||||
} else {
|
} else {
|
||||||
<span class="cstat-no" title="statement not covered" > Logger.minLevel = level;</span>
|
Logger.minLevel = level;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
<span class="fstat-no" title="function not covered" > static </span>debug(message: string, category: string = <span class="branch-0 cbranch-no" title="branch not covered" >'general')</span>: void {
|
static debug(message: string, category: string = <span class="branch-0 cbranch-no" title="branch not covered" >'general')</span>: void {
|
||||||
<span class="cstat-no" title="statement not covered" > <span class="missing-if-branch" title="if path not taken" >I</span>if (LogLevel.DEBUG >= Logger.minLevel) {</span>
|
if (LogLevel.DEBUG >= Logger.minLevel) {
|
||||||
<span class="cstat-no" title="statement not covered" > console.debug(`[${category}] DEBUG: ${message}`);</span>
|
console.debug(`[${category}] DEBUG: ${message}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
<span class="fstat-no" title="function not covered" > static </span>info(message: string, category: string = <span class="branch-0 cbranch-no" title="branch not covered" >'general')</span>: void {
|
static info(message: string, category: string = <span class="branch-0 cbranch-no" title="branch not covered" >'general')</span>: void {
|
||||||
<span class="cstat-no" title="statement not covered" > <span class="missing-if-branch" title="if path not taken" >I</span>if (LogLevel.INFO >= Logger.minLevel) {</span>
|
if (LogLevel.INFO >= Logger.minLevel) {
|
||||||
<span class="cstat-no" title="statement not covered" > console.info(`[${category}] INFO: ${message}`);</span>
|
console.info(`[${category}] INFO: ${message}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -527,81 +515,81 @@ export class Logger {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
<span class="fstat-no" title="function not covered" > static </span>error(message: string, category: string = <span class="branch-0 cbranch-no" title="branch not covered" >'general')</span>: void {
|
static error(message: string, category: string = <span class="branch-0 cbranch-no" title="branch not covered" >'general')</span>: void {
|
||||||
<span class="cstat-no" title="statement not covered" > <span class="missing-if-branch" title="if path not taken" >I</span>if (LogLevel.ERROR >= Logger.minLevel) {</span>
|
if (LogLevel.ERROR >= Logger.minLevel) {
|
||||||
<span class="cstat-no" title="statement not covered" > console.error(`[${category}] ERROR: ${message}`);</span>
|
console.error(`[${category}] ERROR: ${message}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== URL & Model Validation ====================
|
// ==================== URL & Model Validation ====================
|
||||||
|
|
||||||
export function <span class="fstat-no" title="function not covered" >validateOllamaUrl(</span>url: string): { valid: boolean; error?: string } {
|
export function validateOllamaUrl(url: string): { valid: boolean; error?: string } {
|
||||||
<span class="cstat-no" title="statement not covered" > <span class="missing-if-branch" title="if path not taken" >I</span>if (typeof url !== 'string' || !url.trim()) {</span>
|
if (typeof url !== 'string' || !url.trim()) {
|
||||||
<span class="cstat-no" title="statement not covered" > return { valid: false, error: 'URL cannot be empty' };</span>
|
return { valid: false, error: 'URL cannot be empty' };
|
||||||
}
|
}
|
||||||
|
|
||||||
const trimmedUrl = <span class="cstat-no" title="statement not covered" >url.trim();</span>
|
const trimmedUrl = url.trim();
|
||||||
|
|
||||||
<span class="cstat-no" title="statement not covered" > <span class="missing-if-branch" title="if path not taken" >I</span>if (trimmedUrl.endsWith('/')) {</span>
|
if (trimmedUrl.endsWith('/')) {
|
||||||
<span class="cstat-no" title="statement not covered" > return { valid: false, error: 'URL should not end with a slash' };</span>
|
return { valid: false, error: 'URL should not end with a slash' };
|
||||||
}
|
}
|
||||||
|
|
||||||
<span class="cstat-no" title="statement not covered" > try {</span>
|
try {
|
||||||
const parsed = <span class="cstat-no" title="statement not covered" >new URL(trimmedUrl);</span>
|
const parsed = new URL(trimmedUrl);
|
||||||
<span class="cstat-no" title="statement not covered" > <span class="missing-if-branch" title="if path not taken" >I</span>if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {</span>
|
<span class="missing-if-branch" title="if path not taken" >I</span>if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
|
||||||
<span class="cstat-no" title="statement not covered" > return { valid: false, error: 'Must be a valid HTTP or HTTPS URL' };</span>
|
<span class="cstat-no" title="statement not covered" > return { valid: false, error: 'Must be a valid HTTP or HTTPS URL' };</span>
|
||||||
}
|
}
|
||||||
<span class="cstat-no" title="statement not covered" > return { valid: true };</span>
|
return { valid: true };
|
||||||
} catch {
|
} catch {
|
||||||
<span class="cstat-no" title="statement not covered" > return { valid: false, error: 'Must be a valid HTTP or HTTPS URL' };</span>
|
return { valid: false, error: 'Must be a valid HTTP or HTTPS URL' };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function <span class="fstat-no" title="function not covered" >validateModelName(</span>model: string): { valid: boolean; error?: string } {
|
export function validateModelName(model: string): { valid: boolean; error?: string } {
|
||||||
<span class="cstat-no" title="statement not covered" > <span class="missing-if-branch" title="if path not taken" >I</span>if (typeof model !== 'string') {</span>
|
if (typeof model !== 'string') {
|
||||||
<span class="cstat-no" title="statement not covered" > return { valid: false, error: 'Model name must be a string' };</span>
|
return { valid: false, error: 'Model name must be a string' };
|
||||||
}
|
}
|
||||||
|
|
||||||
const trimmedModel = <span class="cstat-no" title="statement not covered" >model.trim();</span>
|
const trimmedModel = model.trim();
|
||||||
|
|
||||||
// Explicit check for empty string after trimming
|
// Explicit check for empty string after trimming
|
||||||
<span class="cstat-no" title="statement not covered" > <span class="missing-if-branch" title="if path not taken" >I</span>if (!trimmedModel || trimmedModel.length === 0) {</span>
|
if (!trimmedModel || trimmedModel.length === 0) {
|
||||||
<span class="cstat-no" title="statement not covered" > return { valid: false, error: 'Model name cannot be empty' };</span>
|
return { valid: false, error: 'Model name cannot be empty' };
|
||||||
}
|
}
|
||||||
|
|
||||||
<span class="cstat-no" title="statement not covered" > <span class="missing-if-branch" title="if path not taken" >I</span>if (trimmedModel.length < 2) {</span>
|
if (trimmedModel.length < 2) {
|
||||||
<span class="cstat-no" title="statement not covered" > return { valid: false, error: 'Model name must be at least 2 characters long' };</span>
|
return { valid: false, error: 'Model name must be at least 2 characters long' };
|
||||||
}
|
}
|
||||||
|
|
||||||
<span class="cstat-no" title="statement not covered" > <span class="missing-if-branch" title="if path not taken" >I</span>if (trimmedModel.length > 100) {</span>
|
if (trimmedModel.length > 100) {
|
||||||
<span class="cstat-no" title="statement not covered" > return { valid: false, error: 'Model name must be less than 100 characters long' };</span>
|
return { valid: false, error: 'Model name must be less than 100 characters long' };
|
||||||
}
|
}
|
||||||
|
|
||||||
<span class="cstat-no" title="statement not covered" > <span class="missing-if-branch" title="if path not taken" >I</span>if (!/^[a-zA-Z0-9._:-]+$/.test(trimmedModel)) {</span>
|
if (!/^[a-zA-Z0-9._:-]+$/.test(trimmedModel)) {
|
||||||
<span class="cstat-no" title="statement not covered" > return {</span>
|
return {
|
||||||
valid: false,
|
valid: false,
|
||||||
error: 'Model name can only contain letters, numbers, dots, dashes, underscores, and colons',
|
error: 'Model name can only contain letters, numbers, dots, dashes, underscores, and colons',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
<span class="cstat-no" title="statement not covered" > return { valid: true };</span>
|
return { valid: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
export function <span class="fstat-no" title="function not covered" >validatePluginSettings(</span>settings: { ollamaUrl: string; model: string }): string[] {
|
export function validatePluginSettings(settings: { ollamaUrl: string; model: string }): string[] {
|
||||||
const errors: string[] = <span class="cstat-no" title="statement not covered" >[];</span>
|
const errors: string[] = [];
|
||||||
|
|
||||||
const urlValidation = <span class="cstat-no" title="statement not covered" >validateOllamaUrl(settings.ollamaUrl);</span>
|
const urlValidation = validateOllamaUrl(settings.ollamaUrl);
|
||||||
<span class="cstat-no" title="statement not covered" > <span class="missing-if-branch" title="if path not taken" >I</span>if (!urlValidation.valid) {</span>
|
if (!urlValidation.valid) {
|
||||||
<span class="cstat-no" title="statement not covered" > errors.push(`Invalid Ollama URL: ${urlValidation.error}`);</span>
|
errors.push(`Invalid Ollama URL: ${urlValidation.error}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const modelValidation = <span class="cstat-no" title="statement not covered" >validateModelName(settings.model);</span>
|
const modelValidation = validateModelName(settings.model);
|
||||||
<span class="cstat-no" title="statement not covered" > <span class="missing-if-branch" title="if path not taken" >I</span>if (!modelValidation.valid) {</span>
|
if (!modelValidation.valid) {
|
||||||
<span class="cstat-no" title="statement not covered" > errors.push(`Invalid Model Name: ${modelValidation.error}`);</span>
|
errors.push(`Invalid Model Name: ${modelValidation.error}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
<span class="cstat-no" title="statement not covered" > return errors;</span>
|
return errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== Safe JSON Parsing ====================
|
// ==================== Safe JSON Parsing ====================
|
||||||
@@ -610,8 +598,8 @@ const MAX_JSON_SIZE = 1_000_000;
|
|||||||
const MAX_JSON_NESTING = 24;
|
const MAX_JSON_NESTING = 24;
|
||||||
|
|
||||||
function countNestingDepth(value: unknown, depth: number = 0): number {
|
function countNestingDepth(value: unknown, depth: number = 0): number {
|
||||||
<span class="missing-if-branch" title="if path not taken" >I</span>if (depth > MAX_JSON_NESTING) {
|
if (depth > MAX_JSON_NESTING) {
|
||||||
<span class="cstat-no" title="statement not covered" > return depth;</span>
|
return depth;
|
||||||
}
|
}
|
||||||
<span class="missing-if-branch" title="if path not taken" >I</span>if (Array.isArray(value)) {
|
<span class="missing-if-branch" title="if path not taken" >I</span>if (Array.isArray(value)) {
|
||||||
<span class="cstat-no" title="statement not covered" > return Math.max(...value.map(<span class="fstat-no" title="function not covered" >(i</span>tem) => <span class="cstat-no" title="statement not covered" >countNestingDepth(item, depth + 1))</span>, depth);</span>
|
<span class="cstat-no" title="statement not covered" > return Math.max(...value.map(<span class="fstat-no" title="function not covered" >(i</span>tem) => <span class="cstat-no" title="statement not covered" >countNestingDepth(item, depth + 1))</span>, depth);</span>
|
||||||
@@ -625,12 +613,12 @@ function countNestingDepth(value: unknown, depth: number = 0): number {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function safeParseJson(jsonString: string): unknown {
|
export function safeParseJson(jsonString: string): unknown {
|
||||||
<span class="missing-if-branch" title="if path not taken" >I</span>if (typeof jsonString !== 'string') {
|
if (typeof jsonString !== 'string') {
|
||||||
<span class="cstat-no" title="statement not covered" > throw new Error('Input must be a string');</span>
|
throw new Error('Input must be a string');
|
||||||
}
|
}
|
||||||
|
|
||||||
<span class="missing-if-branch" title="if path not taken" >I</span>if (jsonString.length > MAX_JSON_SIZE) {
|
if (jsonString.length > MAX_JSON_SIZE) {
|
||||||
<span class="cstat-no" title="statement not covered" > throw new Error('JSON input too large');</span>
|
throw new Error('JSON input too large');
|
||||||
}
|
}
|
||||||
|
|
||||||
let parsed: unknown;
|
let parsed: unknown;
|
||||||
@@ -647,8 +635,8 @@ export function safeParseJson(jsonString: string): unknown {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const dangerousKeys = ['constructor', 'prototype', '__proto__'];
|
const dangerousKeys = ['constructor', 'prototype', '__proto__'];
|
||||||
<span class="missing-if-branch" title="if path not taken" >I</span>if (dangerousKeys.some((key) => Object.keys(obj).includes(key))) {
|
if (dangerousKeys.some((key) => Object.keys(obj).includes(key))) {
|
||||||
<span class="cstat-no" title="statement not covered" > return true;</span>
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recursively check nested objects
|
// Recursively check nested objects
|
||||||
@@ -661,13 +649,13 @@ export function safeParseJson(jsonString: string): unknown {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
<span class="missing-if-branch" title="if path not taken" >I</span>if (checkDangerousPatterns(parsed)) {
|
if (checkDangerousPatterns(parsed)) {
|
||||||
<span class="cstat-no" title="statement not covered" > throw new Error('dangerous code pattern detected');</span>
|
throw new Error('dangerous code pattern detected');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check nesting depth
|
// Check nesting depth
|
||||||
<span class="missing-if-branch" title="if path not taken" >I</span>if (countNestingDepth(parsed) > MAX_JSON_NESTING) {
|
if (countNestingDepth(parsed) > MAX_JSON_NESTING) {
|
||||||
<span class="cstat-no" title="statement not covered" > throw new Error('JSON nesting too deep');</span>
|
throw new Error('JSON nesting too deep');
|
||||||
}
|
}
|
||||||
|
|
||||||
return parsed;
|
return parsed;
|
||||||
@@ -677,12 +665,12 @@ export function safeParseJson(jsonString: string): unknown {
|
|||||||
|
|
||||||
// ==================== HTTP Helpers ====================
|
// ==================== HTTP Helpers ====================
|
||||||
|
|
||||||
export function <span class="fstat-no" title="function not covered" >isValidHttpUrl(</span>url: string): boolean {
|
export function isValidHttpUrl(url: string): boolean {
|
||||||
<span class="cstat-no" title="statement not covered" > try {</span>
|
try {
|
||||||
const parsed = <span class="cstat-no" title="statement not covered" >new URL(url);</span>
|
const parsed = new URL(url);
|
||||||
<span class="cstat-no" title="statement not covered" > return parsed.protocol === 'http:' || parsed.protocol === 'https:';</span>
|
return parsed.protocol === 'http:' || parsed.protocol === 'https:';
|
||||||
} catch {
|
} catch {
|
||||||
<span class="cstat-no" title="statement not covered" > return false;</span>
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -694,7 +682,7 @@ export function <span class="fstat-no" title="function not covered" >isValidHttp
|
|||||||
<div class='footer quiet pad2 space-top1 center small'>
|
<div class='footer quiet pad2 space-top1 center small'>
|
||||||
Code coverage generated by
|
Code coverage generated by
|
||||||
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
||||||
at 2026-05-06T20:36:57.831Z
|
at 2026-05-06T21:42:52.284Z
|
||||||
</div>
|
</div>
|
||||||
<script src="prettify.js"></script>
|
<script src="prettify.js"></script>
|
||||||
<script>
|
<script>
|
||||||
|
|||||||
@@ -23,16 +23,16 @@
|
|||||||
<div class='clearfix'>
|
<div class='clearfix'>
|
||||||
|
|
||||||
<div class='fl pad1y space-right2'>
|
<div class='fl pad1y space-right2'>
|
||||||
<span class="strong">92.1% </span>
|
<span class="strong">92.85% </span>
|
||||||
<span class="quiet">Statements</span>
|
<span class="quiet">Statements</span>
|
||||||
<span class='fraction'>105/114</span>
|
<span class='fraction'>104/112</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class='fl pad1y space-right2'>
|
<div class='fl pad1y space-right2'>
|
||||||
<span class="strong">78.26% </span>
|
<span class="strong">80% </span>
|
||||||
<span class="quiet">Branches</span>
|
<span class="quiet">Branches</span>
|
||||||
<span class='fraction'>36/46</span>
|
<span class='fraction'>36/45</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
@@ -44,9 +44,9 @@
|
|||||||
|
|
||||||
|
|
||||||
<div class='fl pad1y space-right2'>
|
<div class='fl pad1y space-right2'>
|
||||||
<span class="strong">94.33% </span>
|
<span class="strong">95.19% </span>
|
||||||
<span class="quiet">Lines</span>
|
<span class="quiet">Lines</span>
|
||||||
<span class='fraction'>100/106</span>
|
<span class='fraction'>99/104</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
@@ -333,11 +333,7 @@
|
|||||||
<a name='L268'></a><a href='#L268'>268</a>
|
<a name='L268'></a><a href='#L268'>268</a>
|
||||||
<a name='L269'></a><a href='#L269'>269</a>
|
<a name='L269'></a><a href='#L269'>269</a>
|
||||||
<a name='L270'></a><a href='#L270'>270</a>
|
<a name='L270'></a><a href='#L270'>270</a>
|
||||||
<a name='L271'></a><a href='#L271'>271</a>
|
<a name='L271'></a><a href='#L271'>271</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral"> </span>
|
||||||
<a name='L272'></a><a href='#L272'>272</a>
|
|
||||||
<a name='L273'></a><a href='#L273'>273</a>
|
|
||||||
<a name='L274'></a><a href='#L274'>274</a>
|
|
||||||
<a name='L275'></a><a href='#L275'>275</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">2x</span>
|
<span class="cline-any cline-yes">2x</span>
|
||||||
@@ -370,27 +366,27 @@
|
|||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">62x</span>
|
<span class="cline-any cline-yes">67x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">62x</span>
|
<span class="cline-any cline-yes">67x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">28x</span>
|
<span class="cline-any cline-yes">33x</span>
|
||||||
<span class="cline-any cline-yes">2x</span>
|
<span class="cline-any cline-yes">2x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">26x</span>
|
<span class="cline-any cline-yes">31x</span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-no"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">26x</span>
|
<span class="cline-any cline-yes">31x</span>
|
||||||
<span class="cline-any cline-yes">26x</span>
|
<span class="cline-any cline-yes">31x</span>
|
||||||
<span class="cline-any cline-yes">26x</span>
|
<span class="cline-any cline-yes">31x</span>
|
||||||
<span class="cline-any cline-yes">26x</span>
|
<span class="cline-any cline-yes">31x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">26x</span>
|
<span class="cline-any cline-yes">31x</span>
|
||||||
<span class="cline-any cline-yes">50x</span>
|
<span class="cline-any cline-yes">50x</span>
|
||||||
<span class="cline-any cline-yes">38x</span>
|
<span class="cline-any cline-yes">38x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
@@ -401,11 +397,11 @@
|
|||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">26x</span>
|
<span class="cline-any cline-yes">31x</span>
|
||||||
<span class="cline-any cline-yes">26x</span>
|
<span class="cline-any cline-yes">31x</span>
|
||||||
<span class="cline-any cline-yes">26x</span>
|
<span class="cline-any cline-yes">31x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">26x</span>
|
<span class="cline-any cline-yes">31x</span>
|
||||||
<span class="cline-any cline-yes">15x</span>
|
<span class="cline-any cline-yes">15x</span>
|
||||||
<span class="cline-any cline-yes">15x</span>
|
<span class="cline-any cline-yes">15x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
@@ -442,16 +438,12 @@
|
|||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">15x</span>
|
<span class="cline-any cline-yes">15x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">15x</span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-no"> </span>
|
<span class="cline-any cline-yes">31x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">26x</span>
|
<span class="cline-any cline-yes">100x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
|
||||||
<span class="cline-any cline-yes">95x</span>
|
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
@@ -478,10 +470,10 @@
|
|||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">95x</span>
|
<span class="cline-any cline-yes">100x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-yes">362x</span>
|
<span class="cline-any cline-yes">367x</span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
<span class="cline-any cline-neutral"> </span>
|
<span class="cline-any cline-neutral"> </span>
|
||||||
@@ -715,10 +707,6 @@ class VaultIndexer {
|
|||||||
(result): result is NonNullable<typeof result> => result !== null
|
(result): result is NonNullable<typeof result> => result !== null
|
||||||
);
|
);
|
||||||
results.push(...validResults);
|
results.push(...validResults);
|
||||||
|
|
||||||
<span class="missing-if-branch" title="if path not taken" >I</span>if (results.length >= 50) {
|
|
||||||
<span class="cstat-no" title="statement not covered" > break;</span>
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
@@ -892,7 +880,7 @@ export { VaultIndexer };
|
|||||||
<div class='footer quiet pad2 space-top1 center small'>
|
<div class='footer quiet pad2 space-top1 center small'>
|
||||||
Code coverage generated by
|
Code coverage generated by
|
||||||
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
||||||
at 2026-05-06T20:36:57.831Z
|
at 2026-05-06T21:42:52.284Z
|
||||||
</div>
|
</div>
|
||||||
<script src="prettify.js"></script>
|
<script src="prettify.js"></script>
|
||||||
<script>
|
<script>
|
||||||
|
|||||||
+656
-660
File diff suppressed because it is too large
Load Diff
+6
-6
@@ -4,15 +4,15 @@ module.exports = {
|
|||||||
testMatch: ['**/tests/**/*.test.ts'],
|
testMatch: ['**/tests/**/*.test.ts'],
|
||||||
moduleFileExtensions: ['ts', 'js', 'json', 'node'],
|
moduleFileExtensions: ['ts', 'js', 'json', 'node'],
|
||||||
transform: {
|
transform: {
|
||||||
'^.+\\.ts$': 'ts-jest',
|
'^.+\\.ts$': [
|
||||||
|
'ts-jest',
|
||||||
|
{
|
||||||
|
tsconfig: 'tsconfig.test.json',
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
coverageDirectory: 'coverage',
|
coverageDirectory: 'coverage',
|
||||||
collectCoverage: true,
|
collectCoverage: true,
|
||||||
coverageReporters: ['text', 'lcov'],
|
coverageReporters: ['text', 'lcov'],
|
||||||
setupFiles: ['./jest.setup.js'],
|
setupFiles: ['./jest.setup.js'],
|
||||||
globals: {
|
|
||||||
'ts-jest': {
|
|
||||||
tsconfig: 'tsconfig.test.json',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|||||||
+33
-25
@@ -179,34 +179,42 @@ export class OllamaClient {
|
|||||||
tools: OllamaTool[] = [],
|
tools: OllamaTool[] = [],
|
||||||
attempt: number = 0
|
attempt: number = 0
|
||||||
): Promise<OllamaMessage> {
|
): Promise<OllamaMessage> {
|
||||||
const response = await this.fetchFn(`${this.baseURL}/api/chat`, {
|
this.abortController = new AbortController();
|
||||||
method: 'POST',
|
try {
|
||||||
headers: {
|
const response = await this.fetchFn(`${this.baseURL}/api/chat`, {
|
||||||
'Content-Type': 'application/json',
|
method: 'POST',
|
||||||
},
|
headers: {
|
||||||
body: JSON.stringify({
|
'Content-Type': 'application/json',
|
||||||
model: this.model,
|
},
|
||||||
messages,
|
body: JSON.stringify({
|
||||||
tools,
|
model: this.model,
|
||||||
stream: false,
|
messages,
|
||||||
}),
|
tools,
|
||||||
});
|
stream: false,
|
||||||
|
}),
|
||||||
|
signal: this.abortController.signal,
|
||||||
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
if (response.status >= 500 && attempt < this.maxRetries) {
|
if (response.status >= 500 && attempt < this.maxRetries) {
|
||||||
const retryDelay = Math.pow(2, attempt) * 100;
|
const retryDelay = Math.pow(2, attempt) * 100;
|
||||||
Logger.warn(
|
Logger.warn(
|
||||||
`Network error (status ${response.status}), retrying in ${retryDelay}ms (attempt ${attempt + 1}/${this.maxRetries})`,
|
`Network error (status ${response.status}), retrying in ${retryDelay}ms (attempt ${attempt + 1}/${this.maxRetries})`,
|
||||||
'ollama-client'
|
'ollama-client'
|
||||||
);
|
);
|
||||||
await new Promise((resolve) => setTimeout(resolve, retryDelay));
|
await new Promise((resolve) => setTimeout(resolve, retryDelay));
|
||||||
return this.chatWithRetry(messages, tools, attempt + 1);
|
return this.chatWithRetry(messages, tools, attempt + 1);
|
||||||
|
}
|
||||||
|
throw new ApiError(`Ollama API error: ${response.status}`, response.status);
|
||||||
}
|
}
|
||||||
throw new ApiError(`Ollama API error: ${response.status}`, response.status);
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = (await response.json()) as OllamaChatResponse;
|
const data = (await response.json()) as OllamaChatResponse;
|
||||||
return this.toOllamaMessage(data.message) ?? { role: 'assistant', content: '', tool_calls: [] };
|
return (
|
||||||
|
this.toOllamaMessage(data.message) ?? { role: 'assistant', content: '', tool_calls: [] }
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
this.abortController = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private throwIfOllamaError(parsed: Record<string, unknown>): void {
|
private throwIfOllamaError(parsed: Record<string, unknown>): void {
|
||||||
|
|||||||
+1
-5
@@ -1,8 +1,4 @@
|
|||||||
// src/utils.ts
|
export enum LogLevel {
|
||||||
|
|
||||||
// ==================== Logger ====================
|
|
||||||
|
|
||||||
enum LogLevel {
|
|
||||||
DEBUG = 0,
|
DEBUG = 0,
|
||||||
INFO = 1,
|
INFO = 1,
|
||||||
WARN = 2,
|
WARN = 2,
|
||||||
|
|||||||
+209
-3
@@ -1,5 +1,6 @@
|
|||||||
import { ChatView } from '../src/chat-view';
|
import { ChatView } from '../src/chat-view';
|
||||||
import { PluginSettings, OllamaMessage, ChatMessage, OllamaTool, ToolCall } from '../src/types';
|
import { PluginSettings, OllamaMessage, ChatMessage, OllamaTool, ToolCall } from '../src/types';
|
||||||
|
import { ErrorHandler } from '../src/error-handler';
|
||||||
|
|
||||||
// Mock Obsidian types
|
// Mock Obsidian types
|
||||||
interface MockVault {
|
interface MockVault {
|
||||||
@@ -198,6 +199,68 @@ describe('ChatView', () => {
|
|||||||
expect(lastMessage.isStreaming).toBe(false);
|
expect(lastMessage.isStreaming).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should handle streaming re-attach when existing streaming element is found', async () => {
|
||||||
|
view['sendButton'] = document.createElement('button');
|
||||||
|
view['inputEl'] = document.createElement('textarea');
|
||||||
|
(view['inputEl'] as HTMLTextAreaElement).value = 'test';
|
||||||
|
(view as any).lastMessageEl = document.createElement('div');
|
||||||
|
(view as any).lastMessageEl.setAttribute('data-msg-id', 'streaming-123');
|
||||||
|
|
||||||
|
// Mock querySelector to return existing element
|
||||||
|
const container = document.createElement('div');
|
||||||
|
const existingEl = document.createElement('div');
|
||||||
|
existingEl.setAttribute('data-msg-id', 'streaming-123');
|
||||||
|
container.appendChild(existingEl);
|
||||||
|
|
||||||
|
// Mock the contentEl to return our container
|
||||||
|
const originalContentEl = (view as any).contentEl;
|
||||||
|
(view as any).contentEl = container;
|
||||||
|
|
||||||
|
const chatSpy = jest.spyOn(view['ollamaClient'], 'streamChat').mockReturnValue(
|
||||||
|
(async function* () {
|
||||||
|
yield { role: 'assistant', content: 'response' };
|
||||||
|
})()
|
||||||
|
);
|
||||||
|
|
||||||
|
await (view as any).handleUserInput('test');
|
||||||
|
|
||||||
|
expect(chatSpy).toHaveBeenCalled();
|
||||||
|
expect(container.contains((view as any).lastMessageEl)).toBe(true);
|
||||||
|
|
||||||
|
// Restore original contentEl
|
||||||
|
(view as any).contentEl = originalContentEl;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should append streaming element to container when no existing element found', async () => {
|
||||||
|
view['sendButton'] = document.createElement('button');
|
||||||
|
view['inputEl'] = document.createElement('textarea');
|
||||||
|
(view['inputEl'] as HTMLTextAreaElement).value = 'test';
|
||||||
|
|
||||||
|
// Mock querySelector to return null (no existing element)
|
||||||
|
const container = document.createElement('div');
|
||||||
|
const originalQuerySelector = container.querySelector;
|
||||||
|
container.querySelector = jest.fn().mockReturnValue(null);
|
||||||
|
|
||||||
|
// Mock the contentEl to return our container
|
||||||
|
const originalContentEl = (view as any).contentEl;
|
||||||
|
(view as any).contentEl = container;
|
||||||
|
|
||||||
|
const chatSpy = jest.spyOn(view['ollamaClient'], 'streamChat').mockReturnValue(
|
||||||
|
(async function* () {
|
||||||
|
yield { role: 'assistant', content: 'response' };
|
||||||
|
})()
|
||||||
|
);
|
||||||
|
|
||||||
|
await (view as any).handleUserInput('test');
|
||||||
|
|
||||||
|
expect(chatSpy).toHaveBeenCalled();
|
||||||
|
expect(container.childElementCount).toBeGreaterThan(0);
|
||||||
|
|
||||||
|
// Restore original contentEl and querySelector
|
||||||
|
(view as any).contentEl = originalContentEl;
|
||||||
|
container.querySelector = originalQuerySelector;
|
||||||
|
});
|
||||||
|
|
||||||
it('should limit conversation history to maxMessageHistory', async () => {
|
it('should limit conversation history to maxMessageHistory', async () => {
|
||||||
view['sendButton'] = document.createElement('button');
|
view['sendButton'] = document.createElement('button');
|
||||||
view['inputEl'] = document.createElement('textarea');
|
view['inputEl'] = document.createElement('textarea');
|
||||||
@@ -325,6 +388,147 @@ describe('ChatView', () => {
|
|||||||
expect((view as any).messages.length).toBeGreaterThan(1);
|
expect((view as any).messages.length).toBeGreaterThan(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should handle tool call errors gracefully and continue with partial results', async () => {
|
||||||
|
view['sendButton'] = document.createElement('button');
|
||||||
|
view['inputEl'] = document.createElement('textarea');
|
||||||
|
(view['inputEl'] as HTMLTextAreaElement).value = 'test';
|
||||||
|
const chatSpy = jest.spyOn(view['ollamaClient'], 'streamChat').mockReturnValue(
|
||||||
|
(async function* () {
|
||||||
|
yield {
|
||||||
|
role: 'assistant',
|
||||||
|
content: 'Response with tools',
|
||||||
|
tool_calls: [
|
||||||
|
{
|
||||||
|
id: 'call_1',
|
||||||
|
type: 'function',
|
||||||
|
function: { name: 'create_file', arguments: '{"path":"test.md","content":"test"}' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'call_2',
|
||||||
|
type: 'function',
|
||||||
|
function: { name: 'nonexistent_tool', arguments: '{}' },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
})()
|
||||||
|
);
|
||||||
|
const followUpSpy = jest.spyOn(view['ollamaClient'], 'chat').mockResolvedValue({
|
||||||
|
role: 'assistant',
|
||||||
|
content: 'Follow-up response',
|
||||||
|
tool_calls: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Mock tool executor to return mixed results
|
||||||
|
const toolExecutor = view['toolExecutor'];
|
||||||
|
jest.spyOn(toolExecutor, 'handleToolCall').mockImplementation(async (call) => {
|
||||||
|
if (call.function.name === 'create_file') {
|
||||||
|
return { success: true, message: 'File created successfully' };
|
||||||
|
} else {
|
||||||
|
throw new Error('Tool not found');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||||
|
const errorHandlerSpy = jest.spyOn(ErrorHandler, 'handleError').mockImplementation(() => {});
|
||||||
|
|
||||||
|
await (view as any).handleUserInput('test');
|
||||||
|
|
||||||
|
expect(chatSpy).toHaveBeenCalled();
|
||||||
|
expect(followUpSpy).toHaveBeenCalled(); // Should still call follow-up with partial results
|
||||||
|
expect(errorHandlerSpy).toHaveBeenCalledWith(expect.any(Error), 'ChatView.handleUserInput');
|
||||||
|
expect((view as any).messages.length).toBeGreaterThan(1);
|
||||||
|
consoleSpy.mockRestore();
|
||||||
|
errorHandlerSpy.mockRestore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should continue processing even when all tool calls fail', async () => {
|
||||||
|
view['sendButton'] = document.createElement('button');
|
||||||
|
view['inputEl'] = document.createElement('textarea');
|
||||||
|
(view['inputEl'] as HTMLTextAreaElement).value = 'test';
|
||||||
|
const chatSpy = jest.spyOn(view['ollamaClient'], 'streamChat').mockReturnValue(
|
||||||
|
(async function* () {
|
||||||
|
yield {
|
||||||
|
role: 'assistant',
|
||||||
|
content: 'Response with tools',
|
||||||
|
tool_calls: [
|
||||||
|
{
|
||||||
|
id: 'call_1',
|
||||||
|
type: 'function',
|
||||||
|
function: { name: 'nonexistent_tool_1', arguments: '{}' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'call_2',
|
||||||
|
type: 'function',
|
||||||
|
function: { name: 'nonexistent_tool_2', arguments: '{}' },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
})()
|
||||||
|
);
|
||||||
|
const followUpSpy = jest.spyOn(view['ollamaClient'], 'chat').mockResolvedValue({
|
||||||
|
role: 'assistant',
|
||||||
|
content: 'Final response despite tool failures',
|
||||||
|
tool_calls: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Mock tool executor to fail for all calls
|
||||||
|
const toolExecutor = view['toolExecutor'];
|
||||||
|
jest.spyOn(toolExecutor, 'handleToolCall').mockImplementation(async () => {
|
||||||
|
throw new Error('Tool not found');
|
||||||
|
});
|
||||||
|
|
||||||
|
const errorHandlerSpy = jest.spyOn(ErrorHandler, 'handleError').mockImplementation(() => {});
|
||||||
|
|
||||||
|
await (view as any).handleUserInput('test');
|
||||||
|
|
||||||
|
expect(chatSpy).toHaveBeenCalled();
|
||||||
|
expect(followUpSpy).not.toHaveBeenCalled(); // Should skip follow-up when no tool results
|
||||||
|
expect(errorHandlerSpy).toHaveBeenCalledTimes(2); // Should be called for each failed tool call
|
||||||
|
expect((view as any).messages.length).toBeGreaterThan(1);
|
||||||
|
errorHandlerSpy.mockRestore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should skip follow-up when tool calls result in no successful results', async () => {
|
||||||
|
view['sendButton'] = document.createElement('button');
|
||||||
|
view['inputEl'] = document.createElement('textarea');
|
||||||
|
(view['inputEl'] as HTMLTextAreaElement).value = 'test';
|
||||||
|
const chatSpy = jest.spyOn(view['ollamaClient'], 'streamChat').mockReturnValue(
|
||||||
|
(async function* () {
|
||||||
|
yield {
|
||||||
|
role: 'assistant',
|
||||||
|
content: 'Response with tools',
|
||||||
|
tool_calls: [
|
||||||
|
{
|
||||||
|
id: 'call_1',
|
||||||
|
type: 'function',
|
||||||
|
function: { name: 'nonexistent_tool', arguments: '{}' },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
})()
|
||||||
|
);
|
||||||
|
const followUpSpy = jest.spyOn(view['ollamaClient'], 'chat').mockResolvedValue({
|
||||||
|
role: 'assistant',
|
||||||
|
content: 'Final response',
|
||||||
|
tool_calls: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Mock tool executor to fail for the call
|
||||||
|
const toolExecutor = view['toolExecutor'];
|
||||||
|
jest.spyOn(toolExecutor, 'handleToolCall').mockImplementation(async () => {
|
||||||
|
throw new Error('Tool not found');
|
||||||
|
});
|
||||||
|
|
||||||
|
const errorHandlerSpy = jest.spyOn(ErrorHandler, 'handleError').mockImplementation(() => {});
|
||||||
|
|
||||||
|
await (view as any).handleUserInput('test');
|
||||||
|
|
||||||
|
expect(chatSpy).toHaveBeenCalled();
|
||||||
|
expect(followUpSpy).not.toHaveBeenCalled(); // Should skip follow-up when no tool results
|
||||||
|
expect(errorHandlerSpy).toHaveBeenCalledTimes(1); // Should be called for each failed tool call
|
||||||
|
errorHandlerSpy.mockRestore();
|
||||||
|
});
|
||||||
|
|
||||||
it('should limit conversation history to maxMessageHistory', async () => {
|
it('should limit conversation history to maxMessageHistory', async () => {
|
||||||
view['sendButton'] = document.createElement('button');
|
view['sendButton'] = document.createElement('button');
|
||||||
view['inputEl'] = document.createElement('textarea');
|
view['inputEl'] = document.createElement('textarea');
|
||||||
@@ -383,14 +587,16 @@ describe('ChatView', () => {
|
|||||||
})()
|
})()
|
||||||
);
|
);
|
||||||
|
|
||||||
const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
const errorHandlerSpy = jest.spyOn(ErrorHandler, 'handleError').mockImplementation(() => {});
|
||||||
|
|
||||||
await (view as any).handleUserInput('test');
|
await (view as any).handleUserInput('test');
|
||||||
|
|
||||||
expect(chatSpy).toHaveBeenCalled();
|
expect(chatSpy).toHaveBeenCalled();
|
||||||
expect(consoleSpy).toHaveBeenCalledWith(
|
expect(errorHandlerSpy).toHaveBeenCalledWith(
|
||||||
'Ollama Plugin Error [ChatView.handleUserInput]: Network error'
|
expect.objectContaining({ message: 'Network error' }),
|
||||||
|
'ChatView.handleUserInput'
|
||||||
);
|
);
|
||||||
|
errorHandlerSpy.mockRestore();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('event handlers', () => {
|
describe('event handlers', () => {
|
||||||
|
|||||||
+172
-16
@@ -5,6 +5,22 @@ describe('OllamaClient', () => {
|
|||||||
let client: OllamaClient;
|
let client: OllamaClient;
|
||||||
let mockFetch: jest.Mock;
|
let mockFetch: jest.Mock;
|
||||||
|
|
||||||
|
function createMockReader(data: string) {
|
||||||
|
const encoder = new TextEncoder();
|
||||||
|
const encoded = encoder.encode(data);
|
||||||
|
let called = false;
|
||||||
|
return {
|
||||||
|
read: () => {
|
||||||
|
if (!called) {
|
||||||
|
called = true;
|
||||||
|
return Promise.resolve({ done: false, value: encoded });
|
||||||
|
}
|
||||||
|
return Promise.resolve({ done: true, value: new Uint8Array(0) });
|
||||||
|
},
|
||||||
|
releaseLock: jest.fn(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const mockMessages: OllamaMessage[] = [
|
const mockMessages: OllamaMessage[] = [
|
||||||
{ role: 'system', content: 'You are helpful.' },
|
{ role: 'system', content: 'You are helpful.' },
|
||||||
{ role: 'user', content: 'Hello' },
|
{ role: 'user', content: 'Hello' },
|
||||||
@@ -107,22 +123,6 @@ describe('OllamaClient', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('streamChat', () => {
|
describe('streamChat', () => {
|
||||||
function createMockReader(data: string) {
|
|
||||||
const encoder = new TextEncoder();
|
|
||||||
const encoded = encoder.encode(data);
|
|
||||||
let called = false;
|
|
||||||
return {
|
|
||||||
read: () => {
|
|
||||||
if (!called) {
|
|
||||||
called = true;
|
|
||||||
return Promise.resolve({ done: false, value: encoded });
|
|
||||||
}
|
|
||||||
return Promise.resolve({ done: true, value: new Uint8Array(0) });
|
|
||||||
},
|
|
||||||
releaseLock: jest.fn(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
it('should send a streaming request and yield chunks', async () => {
|
it('should send a streaming request and yield chunks', async () => {
|
||||||
const streamData = [
|
const streamData = [
|
||||||
JSON.stringify({ message: { content: 'He' } }),
|
JSON.stringify({ message: { content: 'He' } }),
|
||||||
@@ -357,6 +357,71 @@ describe('OllamaClient', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('streamChat with retry logic', () => {
|
||||||
|
it('should retry on 5xx errors and eventually succeed', async () => {
|
||||||
|
let callCount = 0;
|
||||||
|
mockFetch.mockImplementation(async () => {
|
||||||
|
callCount++;
|
||||||
|
if (callCount === 1) {
|
||||||
|
return { ok: false, status: 500 };
|
||||||
|
}
|
||||||
|
if (callCount === 2) {
|
||||||
|
return { ok: false, status: 502 };
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
ok: true,
|
||||||
|
body: {
|
||||||
|
getReader: () => ({
|
||||||
|
read: () => Promise.resolve({ done: true, value: new Uint8Array(0) }),
|
||||||
|
releaseLock: () => {},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
headers: {
|
||||||
|
get: (name: string) => (name === 'content-type' ? 'application/x-ndjson' : null),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const stream = await client.streamChat(mockMessages, mockTools);
|
||||||
|
const chunks: string[] = [];
|
||||||
|
|
||||||
|
for await (const _ of stream) {
|
||||||
|
chunks.push('chunk');
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(callCount).toBe(3);
|
||||||
|
expect(chunks.length).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should give up after maxRetries attempts', async () => {
|
||||||
|
mockFetch.mockResolvedValue({ ok: false, status: 500 });
|
||||||
|
|
||||||
|
const stream = await client.streamChat(mockMessages, mockTools);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
(async () => {
|
||||||
|
for await (const _ of stream) {
|
||||||
|
/* consume */
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
).rejects.toThrow('Ollama API error: 500');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not retry on 4xx errors', async () => {
|
||||||
|
mockFetch.mockResolvedValue({ ok: false, status: 404 });
|
||||||
|
|
||||||
|
const stream = await client.streamChat(mockMessages, mockTools);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
(async () => {
|
||||||
|
for await (const _ of stream) {
|
||||||
|
/* consume */
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
).rejects.toThrow('Ollama API error: 404');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('cancelStream', () => {
|
describe('cancelStream', () => {
|
||||||
it('should abort the current request', () => {
|
it('should abort the current request', () => {
|
||||||
client.cancelStream();
|
client.cancelStream();
|
||||||
@@ -368,4 +433,95 @@ describe('OllamaClient', () => {
|
|||||||
expect(client['abortController']).toBeNull();
|
expect(client['abortController']).toBeNull();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('streamChat final buffer parsing', () => {
|
||||||
|
it('should parse final buffer content when stream ends with partial line', async () => {
|
||||||
|
const streamData = [
|
||||||
|
JSON.stringify({ message: { content: 'First' } }),
|
||||||
|
JSON.stringify({ message: { content: 'Second' } }),
|
||||||
|
JSON.stringify({ message: { content: 'Third' } }),
|
||||||
|
'', // Final line should be empty to signal end
|
||||||
|
].join('\n');
|
||||||
|
|
||||||
|
const mockReader = createMockReader(streamData);
|
||||||
|
|
||||||
|
mockFetch.mockResolvedValue({
|
||||||
|
ok: true,
|
||||||
|
body: { getReader: () => mockReader },
|
||||||
|
headers: {
|
||||||
|
get: (name: string) => (name === 'content-type' ? 'application/x-ndjson' : null),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const stream = await client.streamChat(mockMessages, mockTools);
|
||||||
|
const chunks: string[] = [];
|
||||||
|
|
||||||
|
for await (const chunk of stream) {
|
||||||
|
chunks.push(chunk.content);
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(chunks).toEqual(['First', 'Second', 'Third']);
|
||||||
|
expect(mockReader.releaseLock).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle malformed final buffer content gracefully', async () => {
|
||||||
|
const streamData = [
|
||||||
|
JSON.stringify({ message: { content: 'Valid' } }),
|
||||||
|
'malformed json',
|
||||||
|
'', // Final line should be empty to signal end
|
||||||
|
].join('\n');
|
||||||
|
|
||||||
|
const mockReader = createMockReader(streamData);
|
||||||
|
const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation();
|
||||||
|
|
||||||
|
mockFetch.mockResolvedValue({
|
||||||
|
ok: true,
|
||||||
|
body: { getReader: () => mockReader },
|
||||||
|
headers: {
|
||||||
|
get: (name: string) => (name === 'content-type' ? 'application/x-ndjson' : null),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const stream = await client.streamChat(mockMessages, mockTools);
|
||||||
|
const chunks: string[] = [];
|
||||||
|
|
||||||
|
for await (const chunk of stream) {
|
||||||
|
chunks.push(chunk.content);
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(chunks).toEqual(['Valid']);
|
||||||
|
expect(consoleWarnSpy).toHaveBeenCalledWith(
|
||||||
|
expect.stringContaining('Skipped malformed chunk')
|
||||||
|
);
|
||||||
|
consoleWarnSpy.mockRestore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse final buffer content even when it contains message data', async () => {
|
||||||
|
const streamData = [
|
||||||
|
JSON.stringify({ message: { content: 'First' } }),
|
||||||
|
'', // Final line should be empty to signal end
|
||||||
|
JSON.stringify({ message: { content: 'Final' } }),
|
||||||
|
].join('\n');
|
||||||
|
|
||||||
|
const mockReader = createMockReader(streamData);
|
||||||
|
|
||||||
|
mockFetch.mockResolvedValue({
|
||||||
|
ok: true,
|
||||||
|
body: { getReader: () => mockReader },
|
||||||
|
headers: {
|
||||||
|
get: (name: string) => (name === 'content-type' ? 'application/x-ndjson' : null),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const stream = await client.streamChat(mockMessages, mockTools);
|
||||||
|
const chunks: string[] = [];
|
||||||
|
|
||||||
|
for await (const chunk of stream) {
|
||||||
|
chunks.push(chunk.content);
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(chunks).toEqual(['First', 'Final']);
|
||||||
|
expect(mockReader.releaseLock).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* Unit tests for utility functions, particularly validation functions
|
* Unit tests for utility functions, particularly validation functions
|
||||||
*/
|
*/
|
||||||
|
import { LogLevel, Logger } from '../src/utils';
|
||||||
import {
|
import {
|
||||||
validateOllamaUrl,
|
validateOllamaUrl,
|
||||||
validateModelName,
|
validateModelName,
|
||||||
@@ -202,3 +203,116 @@ describe('Validation Functions', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Logger', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
// Reset logger level before each test
|
||||||
|
Logger.setLevel(LogLevel.DEBUG);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('setLevel', () => {
|
||||||
|
it('should set level using string values', () => {
|
||||||
|
Logger.setLevel('debug');
|
||||||
|
expect((Logger as any).minLevel).toBe(LogLevel.DEBUG);
|
||||||
|
|
||||||
|
Logger.setLevel('info');
|
||||||
|
expect((Logger as any).minLevel).toBe(LogLevel.INFO);
|
||||||
|
|
||||||
|
Logger.setLevel('warn');
|
||||||
|
expect((Logger as any).minLevel).toBe(LogLevel.WARN);
|
||||||
|
|
||||||
|
Logger.setLevel('error');
|
||||||
|
expect((Logger as any).minLevel).toBe(LogLevel.ERROR);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set level using numeric values', () => {
|
||||||
|
Logger.setLevel(LogLevel.DEBUG);
|
||||||
|
expect((Logger as any).minLevel).toBe(LogLevel.DEBUG);
|
||||||
|
|
||||||
|
Logger.setLevel(LogLevel.INFO);
|
||||||
|
expect((Logger as any).minLevel).toBe(LogLevel.INFO);
|
||||||
|
|
||||||
|
Logger.setLevel(LogLevel.WARN);
|
||||||
|
expect((Logger as any).minLevel).toBe(LogLevel.WARN);
|
||||||
|
|
||||||
|
Logger.setLevel(LogLevel.ERROR);
|
||||||
|
expect((Logger as any).minLevel).toBe(LogLevel.ERROR);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should default to DEBUG for unknown string levels', () => {
|
||||||
|
Logger.setLevel('unknown-level');
|
||||||
|
expect((Logger as any).minLevel).toBe(LogLevel.DEBUG);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be case insensitive for string levels', () => {
|
||||||
|
Logger.setLevel('DEBUG');
|
||||||
|
expect((Logger as any).minLevel).toBe(LogLevel.DEBUG);
|
||||||
|
|
||||||
|
Logger.setLevel('Info');
|
||||||
|
expect((Logger as any).minLevel).toBe(LogLevel.INFO);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('logging methods with different levels', () => {
|
||||||
|
it('should log debug messages when level is DEBUG', () => {
|
||||||
|
const consoleSpy = jest.spyOn(console, 'debug').mockImplementation();
|
||||||
|
Logger.setLevel('debug');
|
||||||
|
|
||||||
|
Logger.debug('test message', 'test-category');
|
||||||
|
Logger.info('test message', 'test-category');
|
||||||
|
Logger.warn('test message', 'test-category');
|
||||||
|
Logger.error('test message', 'test-category');
|
||||||
|
|
||||||
|
expect(consoleSpy).toHaveBeenCalledWith('[test-category] DEBUG: test message');
|
||||||
|
consoleSpy.mockRestore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not log debug messages when level is INFO', () => {
|
||||||
|
const consoleSpy = jest.spyOn(console, 'debug').mockImplementation();
|
||||||
|
Logger.setLevel('info');
|
||||||
|
|
||||||
|
Logger.debug('test message', 'test-category');
|
||||||
|
Logger.info('test message', 'test-category');
|
||||||
|
|
||||||
|
expect(consoleSpy).not.toHaveBeenCalled();
|
||||||
|
consoleSpy.mockRestore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not log debug or info messages when level is WARN', () => {
|
||||||
|
const consoleDebugSpy = jest.spyOn(console, 'debug').mockImplementation();
|
||||||
|
const consoleInfoSpy = jest.spyOn(console, 'info').mockImplementation();
|
||||||
|
Logger.setLevel('warn');
|
||||||
|
|
||||||
|
Logger.debug('test message', 'test-category');
|
||||||
|
Logger.info('test message', 'test-category');
|
||||||
|
Logger.warn('test message', 'test-category');
|
||||||
|
|
||||||
|
expect(consoleDebugSpy).not.toHaveBeenCalled();
|
||||||
|
expect(consoleInfoSpy).not.toHaveBeenCalled();
|
||||||
|
consoleDebugSpy.mockRestore();
|
||||||
|
consoleInfoSpy.mockRestore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should only log error messages when level is ERROR', () => {
|
||||||
|
const consoleDebugSpy = jest.spyOn(console, 'debug').mockImplementation();
|
||||||
|
const consoleInfoSpy = jest.spyOn(console, 'info').mockImplementation();
|
||||||
|
const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation();
|
||||||
|
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
|
||||||
|
Logger.setLevel('error');
|
||||||
|
|
||||||
|
Logger.debug('test message', 'test-category');
|
||||||
|
Logger.info('test message', 'test-category');
|
||||||
|
Logger.warn('test message', 'test-category');
|
||||||
|
Logger.error('test message', 'test-category');
|
||||||
|
|
||||||
|
expect(consoleDebugSpy).not.toHaveBeenCalled();
|
||||||
|
expect(consoleInfoSpy).not.toHaveBeenCalled();
|
||||||
|
expect(consoleWarnSpy).not.toHaveBeenCalled();
|
||||||
|
expect(consoleErrorSpy).toHaveBeenCalledWith('[test-category] ERROR: test message');
|
||||||
|
consoleDebugSpy.mockRestore();
|
||||||
|
consoleInfoSpy.mockRestore();
|
||||||
|
consoleWarnSpy.mockRestore();
|
||||||
|
consoleErrorSpy.mockRestore();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user