1) Do some rename refactor ,prepare for LLMProcess refactor
2) Fix merge bugs.
This commit is contained in:
@@ -11,3 +11,4 @@ math_school_env.db
|
|||||||
workflows.db
|
workflows.db
|
||||||
|
|
||||||
|
|
||||||
|
rootfs/test_doc/
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
# Check (TODO)
|
||||||
|
目的是根据Todo Log, 结合自己的角色检查TODO是否争取完成(非客观性TODO给出是否有所改进的评价)
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# Do (TODO)
|
||||||
|
目标是结合 角色定义,手头的工具,已知知识 完成一个确定的任务。
|
||||||
|
完成任务时应使用ReAct的方法:应在给出执行动作前,先自言自语的输出一个计划,然后在动作(这个自言自语会变成TODO Logs)
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# Review (Task/Todo)
|
||||||
|
目的是结合已知信息(重点是已经进行操作的记录),对失败的,完成的不好的任务进行思考,尝试给出更好的解决方案
|
||||||
|
1. 管理学方法:更换负责人
|
||||||
|
2. 管理学方法:拆分
|
||||||
|
3. 给出建议(该建议可以在下次一次DO-Check)循环中被使用
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
# Process Message
|
||||||
|
处理消息的首要是目的是分析消息的意图,并给予回复
|
||||||
@@ -1,395 +0,0 @@
|
|||||||
<mxfile host="65bd71144e" pages="3">
|
|
||||||
<diagram id="C5RBs43oDa-KdzZeNtuy" name="Page-1">
|
|
||||||
<mxGraphModel dx="500" dy="864" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
|
|
||||||
<root>
|
|
||||||
<mxCell id="WIyWlLk6GJQsqaUBKTNV-0"/>
|
|
||||||
<mxCell id="WIyWlLk6GJQsqaUBKTNV-1" parent="WIyWlLk6GJQsqaUBKTNV-0"/>
|
|
||||||
<mxCell id="WIyWlLk6GJQsqaUBKTNV-3" value="(chat) message" style="rounded=1;whiteSpace=wrap;html=1;fontSize=12;glass=0;strokeWidth=1;shadow=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
|
|
||||||
<mxGeometry x="159" y="330" width="90" height="40" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="RbYReh6FQah4Kl5oFyjJ-0" value="" style="endArrow=classic;html=1;rounded=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="WIyWlLk6GJQsqaUBKTNV-3" target="RbYReh6FQah4Kl5oFyjJ-1" edge="1">
|
|
||||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
|
||||||
<mxPoint x="279" y="390" as="sourcePoint"/>
|
|
||||||
<mxPoint x="309" y="350" as="targetPoint"/>
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="RbYReh6FQah4Kl5oFyjJ-8" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="RbYReh6FQah4Kl5oFyjJ-1" target="RbYReh6FQah4Kl5oFyjJ-2" edge="1">
|
|
||||||
<mxGeometry relative="1" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="RbYReh6FQah4Kl5oFyjJ-14" value="message" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="RbYReh6FQah4Kl5oFyjJ-1" target="RbYReh6FQah4Kl5oFyjJ-13" edge="1">
|
|
||||||
<mxGeometry relative="1" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="RbYReh6FQah4Kl5oFyjJ-1" value="Workflow" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#6d8764;fontColor=#ffffff;strokeColor=#3A5431;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
|
|
||||||
<mxGeometry x="304" y="320" width="120" height="60" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="RbYReh6FQah4Kl5oFyjJ-2" value="Functions" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
|
|
||||||
<mxGeometry x="514" y="320" width="130" height="60" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="RbYReh6FQah4Kl5oFyjJ-6" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="RbYReh6FQah4Kl5oFyjJ-3" target="RbYReh6FQah4Kl5oFyjJ-4" edge="1">
|
|
||||||
<mxGeometry relative="1" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="RbYReh6FQah4Kl5oFyjJ-3" value="(phicyal / virtual)<br>environment" style="shape=cube;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;darkOpacity=0.05;darkOpacity2=0.1;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
|
|
||||||
<mxGeometry x="304" y="120" width="120" height="80" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="RbYReh6FQah4Kl5oFyjJ-7" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="RbYReh6FQah4Kl5oFyjJ-4" target="RbYReh6FQah4Kl5oFyjJ-1" edge="1">
|
|
||||||
<mxGeometry relative="1" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="RbYReh6FQah4Kl5oFyjJ-4" value="(event) message" style="rounded=1;whiteSpace=wrap;html=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
|
|
||||||
<mxGeometry x="304" y="240" width="120" height="40" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="RbYReh6FQah4Kl5oFyjJ-9" value="" style="endArrow=classic;html=1;rounded=0;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0;entryY=0;entryDx=120;entryDy=50;entryPerimeter=0;dashed=1;dashPattern=8 8;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="RbYReh6FQah4Kl5oFyjJ-2" target="RbYReh6FQah4Kl5oFyjJ-3" edge="1">
|
|
||||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
|
||||||
<mxPoint x="564" y="290" as="sourcePoint"/>
|
|
||||||
<mxPoint x="614" y="240" as="targetPoint"/>
|
|
||||||
<Array as="points">
|
|
||||||
<mxPoint x="579" y="170"/>
|
|
||||||
</Array>
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="RbYReh6FQah4Kl5oFyjJ-10" value="" style="endArrow=classic;html=1;rounded=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0.16;entryY=0.55;entryDx=0;entryDy=0;entryPerimeter=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="RbYReh6FQah4Kl5oFyjJ-2" target="RbYReh6FQah4Kl5oFyjJ-11" edge="1">
|
|
||||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
|
||||||
<mxPoint x="684" y="320" as="sourcePoint"/>
|
|
||||||
<mxPoint x="714" y="350" as="targetPoint"/>
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="RbYReh6FQah4Kl5oFyjJ-11" value="Exist Service" style="ellipse;shape=cloud;whiteSpace=wrap;html=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
|
|
||||||
<mxGeometry x="689" y="300" width="130" height="90" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="RbYReh6FQah4Kl5oFyjJ-13" value="sub / other workflow" style="whiteSpace=wrap;html=1;rounded=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
|
|
||||||
<mxGeometry x="304" y="480" width="120" height="50" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="RbYReh6FQah4Kl5oFyjJ-15" value="response message" style="endArrow=classic;html=1;rounded=0;exitX=0.25;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="RbYReh6FQah4Kl5oFyjJ-1" target="RbYReh6FQah4Kl5oFyjJ-17" edge="1">
|
|
||||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
|
||||||
<mxPoint x="129" y="480" as="sourcePoint"/>
|
|
||||||
<mxPoint x="99" y="420" as="targetPoint"/>
|
|
||||||
<Array as="points">
|
|
||||||
<mxPoint x="334" y="420"/>
|
|
||||||
<mxPoint x="49" y="420"/>
|
|
||||||
</Array>
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="RbYReh6FQah4Kl5oFyjJ-18" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="RbYReh6FQah4Kl5oFyjJ-17" target="WIyWlLk6GJQsqaUBKTNV-3" edge="1">
|
|
||||||
<mxGeometry relative="1" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="RbYReh6FQah4Kl5oFyjJ-17" value="Personal" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
|
|
||||||
<mxGeometry x="9" y="310" width="80" height="80" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="RbYReh6FQah4Kl5oFyjJ-19" value="" style="shape=xor;whiteSpace=wrap;html=1;rotation=-90;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
|
|
||||||
<mxGeometry x="309" y="67" width="20" height="20" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="RbYReh6FQah4Kl5oFyjJ-21" value="" style="shape=xor;whiteSpace=wrap;html=1;rotation=-90;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
|
|
||||||
<mxGeometry x="347" y="67" width="20" height="20" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="RbYReh6FQah4Kl5oFyjJ-22" value="" style="shape=xor;whiteSpace=wrap;html=1;rotation=-90;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
|
|
||||||
<mxGeometry x="386" y="67" width="20" height="20" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="RbYReh6FQah4Kl5oFyjJ-23" value="IoT Devices" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
|
|
||||||
<mxGeometry x="240" y="67" width="60" height="30" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="RbYReh6FQah4Kl5oFyjJ-25" value="" style="endArrow=classic;html=1;rounded=0;exitX=0.25;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.122;entryY=0.008;entryDx=0;entryDy=0;entryPerimeter=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="RbYReh6FQah4Kl5oFyjJ-19" target="RbYReh6FQah4Kl5oFyjJ-3" edge="1">
|
|
||||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
|
||||||
<mxPoint x="240" y="170" as="sourcePoint"/>
|
|
||||||
<mxPoint x="290" y="120" as="targetPoint"/>
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="RbYReh6FQah4Kl5oFyjJ-26" value="" style="endArrow=classic;html=1;rounded=0;exitX=0.25;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0;entryY=0;entryDx=50;entryDy=0;entryPerimeter=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="RbYReh6FQah4Kl5oFyjJ-21" edge="1">
|
|
||||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
|
||||||
<mxPoint x="443" y="120" as="sourcePoint"/>
|
|
||||||
<mxPoint x="357" y="120.00000000000011" as="targetPoint"/>
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="RbYReh6FQah4Kl5oFyjJ-27" value="" style="endArrow=classic;html=1;rounded=0;exitX=0.25;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.766;entryY=0.02;entryDx=0;entryDy=0;entryPerimeter=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="RbYReh6FQah4Kl5oFyjJ-22" target="RbYReh6FQah4Kl5oFyjJ-3" edge="1">
|
|
||||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
|
||||||
<mxPoint x="430" y="140" as="sourcePoint"/>
|
|
||||||
<mxPoint x="480" y="90" as="targetPoint"/>
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="HvRWRVSjofeyYJQyU3aa-0" value="Sub workflow is not visiable for others<br><br>Sub worflow can share roles&nbsp;" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
|
|
||||||
<mxGeometry x="160" y="480" width="140" height="70" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="HvRWRVSjofeyYJQyU3aa-1" value="Chat with AI Agent is like chat with a workflow which have only one agent and no rules" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
|
|
||||||
<mxGeometry x="70" y="150" width="150" height="80" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="ed6Jjq-fMpksm2tlv9Oi-0" value="inner&nbsp;<br>environment" style="shape=cube;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;darkOpacity=0.05;darkOpacity2=0.1;size=10;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
|
|
||||||
<mxGeometry x="386" y="360" width="84" height="40" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="ed6Jjq-fMpksm2tlv9Oi-1" value="Environment could shared with workflows" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
|
|
||||||
<mxGeometry x="440" y="120" width="120" height="30" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="ed6Jjq-fMpksm2tlv9Oi-2" value="Inner environment&nbsp; could be used as Context(FileSystem)" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
|
|
||||||
<mxGeometry x="413" y="410" width="110" height="30" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
</root>
|
|
||||||
</mxGraphModel>
|
|
||||||
</diagram>
|
|
||||||
<diagram id="kWxfmPxtNxOAf0a73TCG" name="Page-2">
|
|
||||||
<mxGraphModel dx="500" dy="864" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0">
|
|
||||||
<root>
|
|
||||||
<mxCell id="0"/>
|
|
||||||
<mxCell id="1" parent="0"/>
|
|
||||||
<mxCell id="VGzDL9xesOOnN_l_G2Ij-1" value="<h1>Workflow rules</h1><p>workflow rules(prompt)</p>" style="text;html=1;strokeColor=none;fillColor=none;spacing=5;spacingTop=-20;whiteSpace=wrap;overflow=hidden;rounded=0;" parent="1" vertex="1">
|
|
||||||
<mxGeometry x="70" y="800" width="190" height="120" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="VGzDL9xesOOnN_l_G2Ij-2" value="<h1>Workflow roles</h1><p>AI Agent groups</p><p>Project Manager : Alex</p><p>Programer : Bobl</p><p><br></p>" style="text;html=1;strokeColor=none;fillColor=none;spacing=5;spacingTop=-20;whiteSpace=wrap;overflow=hidden;rounded=0;" parent="1" vertex="1">
|
|
||||||
<mxGeometry x="320" y="790" width="190" height="120" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="41KxsEdFswCCPQnavNoU-14" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.25;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="VGzDL9xesOOnN_l_G2Ij-3" target="VGzDL9xesOOnN_l_G2Ij-4" edge="1">
|
|
||||||
<mxGeometry relative="1" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="41KxsEdFswCCPQnavNoU-15" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.75;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="VGzDL9xesOOnN_l_G2Ij-3" target="VGzDL9xesOOnN_l_G2Ij-7" edge="1">
|
|
||||||
<mxGeometry relative="1" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="VGzDL9xesOOnN_l_G2Ij-3" value="Message" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1">
|
|
||||||
<mxGeometry x="340" y="170" width="100" height="40" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="VGzDL9xesOOnN_l_G2Ij-4" value="inner filter&nbsp;" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;" parent="1" vertex="1">
|
|
||||||
<mxGeometry x="150" y="260" width="120" height="50" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="VGzDL9xesOOnN_l_G2Ij-5" value="roleA" style="ellipse;whiteSpace=wrap;html=1;" parent="1" vertex="1">
|
|
||||||
<mxGeometry x="80" y="350" width="110" height="60" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="41KxsEdFswCCPQnavNoU-12" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="VGzDL9xesOOnN_l_G2Ij-6" target="VGzDL9xesOOnN_l_G2Ij-8" edge="1">
|
|
||||||
<mxGeometry relative="1" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="VGzDL9xesOOnN_l_G2Ij-6" value="roleB" style="ellipse;whiteSpace=wrap;html=1;" parent="1" vertex="1">
|
|
||||||
<mxGeometry x="240" y="350" width="110" height="60" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="41KxsEdFswCCPQnavNoU-4" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" parent="1" source="VGzDL9xesOOnN_l_G2Ij-7" edge="1">
|
|
||||||
<mxGeometry relative="1" as="geometry">
|
|
||||||
<mxPoint x="570.0344827586209" y="350" as="targetPoint"/>
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="VGzDL9xesOOnN_l_G2Ij-7" value="Workflow rules" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
|
|
||||||
<mxGeometry x="520" y="260" width="100" height="50" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="VGzDL9xesOOnN_l_G2Ij-8" value="Final Result of Message" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1">
|
|
||||||
<mxGeometry x="340" y="590" width="100" height="50" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="41KxsEdFswCCPQnavNoU-11" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="VGzDL9xesOOnN_l_G2Ij-9" target="VGzDL9xesOOnN_l_G2Ij-8" edge="1">
|
|
||||||
<mxGeometry relative="1" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="VGzDL9xesOOnN_l_G2Ij-9" value="Result Merge" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
|
|
||||||
<mxGeometry x="521" y="530" width="100" height="40" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="41KxsEdFswCCPQnavNoU-9" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="VGzDL9xesOOnN_l_G2Ij-10" target="VGzDL9xesOOnN_l_G2Ij-9" edge="1">
|
|
||||||
<mxGeometry relative="1" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="VGzDL9xesOOnN_l_G2Ij-10" value="roleC" style="ellipse;whiteSpace=wrap;html=1;" parent="1" vertex="1">
|
|
||||||
<mxGeometry x="435" y="350" width="110" height="60" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="41KxsEdFswCCPQnavNoU-8" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="VGzDL9xesOOnN_l_G2Ij-11" target="41KxsEdFswCCPQnavNoU-7" edge="1">
|
|
||||||
<mxGeometry relative="1" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="VGzDL9xesOOnN_l_G2Ij-11" value="roleD" style="ellipse;whiteSpace=wrap;html=1;" parent="1" vertex="1">
|
|
||||||
<mxGeometry x="595" y="350" width="110" height="60" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="VGzDL9xesOOnN_l_G2Ij-12" value="" style="endArrow=classic;html=1;rounded=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=1;entryY=0;entryDx=0;entryDy=0;" parent="1" source="VGzDL9xesOOnN_l_G2Ij-4" target="VGzDL9xesOOnN_l_G2Ij-5" edge="1">
|
|
||||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
|
||||||
<mxPoint x="160" y="510" as="sourcePoint"/>
|
|
||||||
<mxPoint x="210" y="460" as="targetPoint"/>
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="VGzDL9xesOOnN_l_G2Ij-13" value="" style="endArrow=classic;html=1;rounded=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0;entryY=0;entryDx=0;entryDy=0;" parent="1" source="VGzDL9xesOOnN_l_G2Ij-4" target="VGzDL9xesOOnN_l_G2Ij-6" edge="1">
|
|
||||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
|
||||||
<mxPoint x="210" y="510" as="sourcePoint"/>
|
|
||||||
<mxPoint x="260" y="460" as="targetPoint"/>
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="41KxsEdFswCCPQnavNoU-1" value="" style="shape=cross;whiteSpace=wrap;html=1;" parent="1" vertex="1">
|
|
||||||
<mxGeometry x="556" y="364" width="30" height="30" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="41KxsEdFswCCPQnavNoU-10" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="41KxsEdFswCCPQnavNoU-7" target="VGzDL9xesOOnN_l_G2Ij-9" edge="1">
|
|
||||||
<mxGeometry relative="1" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="41KxsEdFswCCPQnavNoU-7" value="call function or&nbsp;<br>send message" style="shape=parallelogram;perimeter=parallelogramPerimeter;whiteSpace=wrap;html=1;fixedSize=1;" parent="1" vertex="1">
|
|
||||||
<mxGeometry x="592.5" y="460" width="115" height="30" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="q_eHDVuD3DBh6_VEKgap-2" value="message process by filter,select one role do LLM and get RESULT of input" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
|
|
||||||
<mxGeometry x="90" y="190" width="165" height="30" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="q_eHDVuD3DBh6_VEKgap-3" value="message process by rules,select mutil role do LLM and merge RESULT of input" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
|
|
||||||
<mxGeometry x="510" y="180" width="165" height="30" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="q_eHDVuD3DBh6_VEKgap-5" value="如果一个工作流有3个role参与,每个人都需要调用函数并分析结果,那么要进行至少6次推理" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
|
|
||||||
<mxGeometry x="483" y="640" width="174" height="40" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="q_eHDVuD3DBh6_VEKgap-6" value="这里是基于规则的合并,给另一个Role合并是多输入Agent流程" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
|
|
||||||
<mxGeometry x="635" y="570" width="160" height="30" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
</root>
|
|
||||||
</mxGraphModel>
|
|
||||||
</diagram>
|
|
||||||
<diagram id="7NYJTgo0U9cdVshLy85U" name="Page-3">
|
|
||||||
|
|
||||||
<mxGraphModel dx="1881" dy="676" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0">
|
|
||||||
<root>
|
|
||||||
<mxCell id="0"/>
|
|
||||||
<mxCell id="1" parent="0"/>
|
|
||||||
<mxCell id="zJVYyOSlNiCVBlA5jEf0-2" value="Agent Sessions<br>Data" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;size=15;fontSize=11;" parent="1" vertex="1">
|
|
||||||
<mxGeometry x="110" y="533" width="80" height="70" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="8StUH8aFnwlRlHm0zShF-2" value="Small LLM, Train With Personal Data" style="shape=process;whiteSpace=wrap;html=1;backgroundOutline=1;" parent="1" vertex="1">
|
|
||||||
<mxGeometry x="270" y="258" width="140" height="50" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="awKuOLItvF_EOGf5wS2x-4" value="" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
|
|
||||||
<mxGeometry x="20" y="78" width="160" height="360" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="S5MjekdSblNa5itYb4Ee-13" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="8StUH8aFnwlRlHm0zShF-1" target="SPnvrlBiOLhgEaWxmdK3-4" edge="1">
|
|
||||||
<mxGeometry relative="1" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="8StUH8aFnwlRlHm0zShF-1" value="LLM<br>(Common Sence)" style="shape=process;whiteSpace=wrap;html=1;backgroundOutline=1;" parent="1" vertex="1">
|
|
||||||
<mxGeometry x="250" y="218" width="140" height="50" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="8StUH8aFnwlRlHm0zShF-3" value="ControlNet<br>base on Agent Memory" style="shape=process;whiteSpace=wrap;html=1;backgroundOutline=1;" parent="1" vertex="1">
|
|
||||||
<mxGeometry x="250" y="108" width="140" height="50" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="8StUH8aFnwlRlHm0zShF-4" value="Agent人格助记词" style="shape=document;whiteSpace=wrap;html=1;boundedLbl=1;size=0.25;" parent="1" vertex="1">
|
|
||||||
<mxGeometry x="40" y="108" width="120" height="50" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="8StUH8aFnwlRlHm0zShF-5" value="Role助记词" style="shape=document;whiteSpace=wrap;html=1;boundedLbl=1;size=0.25;" parent="1" vertex="1">
|
|
||||||
<mxGeometry x="40" y="168" width="120" height="50" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="8StUH8aFnwlRlHm0zShF-6" value="Workflow Rules助记词" style="shape=document;whiteSpace=wrap;html=1;boundedLbl=1;size=0.25;" parent="1" vertex="1">
|
|
||||||
<mxGeometry x="40" y="235.5" width="120" height="52.5" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="8StUH8aFnwlRlHm0zShF-7" value="访问Knowlege构造助记词" style="shape=document;whiteSpace=wrap;html=1;boundedLbl=1;size=0.25;" parent="1" vertex="1">
|
|
||||||
<mxGeometry x="40" y="378" width="120" height="50" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="Tz-ThZOieDqqDBRXvXnh-1" value="Invoke the function and wait for the necessary response." style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;fontSize=11;" parent="1" vertex="1">
|
|
||||||
<mxGeometry x="600" y="33" width="110" height="70" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="awKuOLItvF_EOGf5wS2x-2" value="Send Message and wait for the necessary response." style="rounded=1;whiteSpace=wrap;html=1;fontSize=11;glass=0;strokeWidth=1;shadow=0;" parent="1" vertex="1">
|
|
||||||
<mxGeometry x="610" y="153" width="90" height="70" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="awKuOLItvF_EOGf5wS2x-5" value="Input Prompts" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
|
|
||||||
<mxGeometry x="60" y="78" width="80" height="30" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="awKuOLItvF_EOGf5wS2x-7" value="Responce Prompts" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
|
|
||||||
<mxGeometry x="740" y="103" width="80" height="40" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="S5MjekdSblNa5itYb4Ee-1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="awKuOLItvF_EOGf5wS2x-8" target="awKuOLItvF_EOGf5wS2x-9" edge="1">
|
|
||||||
<mxGeometry relative="1" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="awKuOLItvF_EOGf5wS2x-8" value="LLM Competion" style="shape=process;whiteSpace=wrap;html=1;backgroundOutline=1;" parent="1" vertex="1">
|
|
||||||
<mxGeometry x="720" y="226.75" width="120" height="40" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="SPnvrlBiOLhgEaWxmdK3-9" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="awKuOLItvF_EOGf5wS2x-9" target="SPnvrlBiOLhgEaWxmdK3-1" edge="1">
|
|
||||||
<mxGeometry relative="1" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="awKuOLItvF_EOGf5wS2x-9" value="Final Result<br>Of Message<br>(can be None)" style="ellipse;whiteSpace=wrap;html=1;" parent="1" vertex="1">
|
|
||||||
<mxGeometry x="450" y="363" width="120" height="80" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="SPnvrlBiOLhgEaWxmdK3-1" value="Storage Result in agent session and workflow session" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1">
|
|
||||||
<mxGeometry x="450" y="483" width="120" height="60" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="SPnvrlBiOLhgEaWxmdK3-2" value="可用Function助记词" style="shape=document;whiteSpace=wrap;html=1;boundedLbl=1;size=0.25;" parent="1" vertex="1">
|
|
||||||
<mxGeometry x="40" y="308" width="120" height="50" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="SPnvrlBiOLhgEaWxmdK3-7" value="Yes" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="SPnvrlBiOLhgEaWxmdK3-4" target="SPnvrlBiOLhgEaWxmdK3-6" edge="1">
|
|
||||||
<mxGeometry relative="1" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="SPnvrlBiOLhgEaWxmdK3-8" value="No" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="SPnvrlBiOLhgEaWxmdK3-4" target="awKuOLItvF_EOGf5wS2x-9" edge="1">
|
|
||||||
<mxGeometry relative="1" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="SPnvrlBiOLhgEaWxmdK3-4" value="推理结果<br>需要进一步处理?" style="rhombus;whiteSpace=wrap;html=1;" parent="1" vertex="1">
|
|
||||||
<mxGeometry x="440" y="203" width="140" height="80" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="SPnvrlBiOLhgEaWxmdK3-6" value="Call Funstion or send message?" style="rhombus;whiteSpace=wrap;html=1;fontSize=11;" parent="1" vertex="1">
|
|
||||||
<mxGeometry x="440" y="78" width="140" height="80" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="S5MjekdSblNa5itYb4Ee-3" value="Call" style="endArrow=classic;html=1;rounded=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="SPnvrlBiOLhgEaWxmdK3-6" target="Tz-ThZOieDqqDBRXvXnh-1" edge="1">
|
|
||||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
|
||||||
<mxPoint x="410" y="113" as="sourcePoint"/>
|
|
||||||
<mxPoint x="460" y="63" as="targetPoint"/>
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="S5MjekdSblNa5itYb4Ee-4" value="Send" style="endArrow=classic;html=1;rounded=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="SPnvrlBiOLhgEaWxmdK3-6" target="awKuOLItvF_EOGf5wS2x-2" edge="1">
|
|
||||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
|
||||||
<mxPoint x="560" y="193" as="sourcePoint"/>
|
|
||||||
<mxPoint x="610" y="143" as="targetPoint"/>
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="S5MjekdSblNa5itYb4Ee-5" value="" style="endArrow=classic;html=1;rounded=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="awKuOLItvF_EOGf5wS2x-2" target="awKuOLItvF_EOGf5wS2x-7" edge="1">
|
|
||||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
|
||||||
<mxPoint x="720" y="213" as="sourcePoint"/>
|
|
||||||
<mxPoint x="770" y="163" as="targetPoint"/>
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="S5MjekdSblNa5itYb4Ee-6" value="" style="endArrow=classic;html=1;rounded=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="Tz-ThZOieDqqDBRXvXnh-1" target="awKuOLItvF_EOGf5wS2x-7" edge="1">
|
|
||||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
|
||||||
<mxPoint x="750" y="93" as="sourcePoint"/>
|
|
||||||
<mxPoint x="800" y="43" as="targetPoint"/>
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="S5MjekdSblNa5itYb4Ee-7" value="" style="endArrow=classic;html=1;rounded=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="awKuOLItvF_EOGf5wS2x-7" target="awKuOLItvF_EOGf5wS2x-8" edge="1">
|
|
||||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
|
||||||
<mxPoint x="750" y="213" as="sourcePoint"/>
|
|
||||||
<mxPoint x="800" y="163" as="targetPoint"/>
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="S5MjekdSblNa5itYb4Ee-10" value="" style="endArrow=none;dashed=1;html=1;dashPattern=1 3;strokeWidth=2;rounded=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;exitX=0.5;exitY=0;exitDx=0;exitDy=0;" parent="1" edge="1">
|
|
||||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
|
||||||
<mxPoint x="330" y="218" as="sourcePoint"/>
|
|
||||||
<mxPoint x="330" y="158" as="targetPoint"/>
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="S5MjekdSblNa5itYb4Ee-12" value="" style="shape=flexArrow;endArrow=classic;html=1;rounded=0;fillColor=#6d8764;strokeColor=#3A5431;" parent="1" edge="1">
|
|
||||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
|
||||||
<mxPoint x="200" y="251.75" as="sourcePoint"/>
|
|
||||||
<mxPoint x="240" y="252" as="targetPoint"/>
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="S5MjekdSblNa5itYb4Ee-15" value="Real Time Enviroment Knowlege" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;size=15;fontSize=11;" parent="1" vertex="1">
|
|
||||||
<mxGeometry x="10" y="533" width="80" height="70" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="zJVYyOSlNiCVBlA5jEf0-1" value="Workflow Context Data" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;size=15;fontSize=11;" parent="1" vertex="1">
|
|
||||||
<mxGeometry x="110" y="473" width="80" height="70" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="S5MjekdSblNa5itYb4Ee-14" value="OOD's KnowlegeBase" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;size=15;fontSize=11;" parent="1" vertex="1">
|
|
||||||
<mxGeometry x="10" y="473" width="80" height="70" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="zJVYyOSlNiCVBlA5jEf0-3" value="" style="shape=flexArrow;endArrow=classic;html=1;rounded=0;fillColor=#647687;strokeColor=#314354;" parent="1" target="8StUH8aFnwlRlHm0zShF-7" edge="1">
|
|
||||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
|
||||||
<mxPoint x="100" y="463" as="sourcePoint"/>
|
|
||||||
<mxPoint x="130" y="433" as="targetPoint"/>
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="iPE2hx-tEKFiySbPAp22-1" value="<font style="font-size: 11px;">可用Function受到人格和Workflow的共同影响</font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
|
|
||||||
<mxGeometry x="190" y="318" width="130" height="30" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="iPE2hx-tEKFiySbPAp22-2" value="" style="endArrow=none;dashed=1;html=1;dashPattern=1 3;strokeWidth=2;rounded=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="SPnvrlBiOLhgEaWxmdK3-2" target="iPE2hx-tEKFiySbPAp22-1" edge="1">
|
|
||||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
|
||||||
<mxPoint x="230" y="433" as="sourcePoint"/>
|
|
||||||
<mxPoint x="280" y="383" as="targetPoint"/>
|
|
||||||
</mxGeometry>
|
|
||||||
</mxCell>
|
|
||||||
</root>
|
|
||||||
</mxGraphModel>
|
|
||||||
</diagram>
|
|
||||||
<diagram id="zNgk-d6xdACtSja1wnUq" name="Page-4">
|
|
||||||
<mxGraphModel dx="2069" dy="1139" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1100" pageHeight="850" math="0" shadow="0">
|
|
||||||
<root>
|
|
||||||
<mxCell id="0"/>
|
|
||||||
<mxCell id="1" parent="0"/>
|
|
||||||
<mxCell id="PKOyaMforMDOFoUY4PA1-1" value="ChatSession" style="rounded=1;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
|
||||||
<mxGeometry x="140" y="150" width="120" height="60" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="PKOyaMforMDOFoUY4PA1-2" value="SubSession" style="rounded=1;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
|
||||||
<mxGeometry x="230" y="240" width="120" height="60" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="PKOyaMforMDOFoUY4PA1-3" value="SubSession" style="rounded=1;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
|
||||||
<mxGeometry x="230" y="330" width="120" height="60" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="PKOyaMforMDOFoUY4PA1-4" value="Agent Message" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;" vertex="1" parent="1">
|
|
||||||
<mxGeometry x="600" y="150" width="130" height="60" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="PKOyaMforMDOFoUY4PA1-5" value="Agent Message" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;" vertex="1" parent="1">
|
|
||||||
<mxGeometry x="600" y="230" width="130" height="60" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
<mxCell id="PKOyaMforMDOFoUY4PA1-6" value="Agent Message" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;" vertex="1" parent="1">
|
|
||||||
<mxGeometry x="600" y="320" width="130" height="60" as="geometry"/>
|
|
||||||
</mxCell>
|
|
||||||
</root>
|
|
||||||
</mxGraphModel>
|
|
||||||
</diagram>
|
|
||||||
</mxfile>
|
|
||||||
@@ -5,7 +5,6 @@ max_token_size = 128000
|
|||||||
enable_timestamp = "true"
|
enable_timestamp = "true"
|
||||||
owner_prompt = "I am your master{name}"
|
owner_prompt = "I am your master{name}"
|
||||||
contact_prompt = "I am your master's friend{name}"
|
contact_prompt = "I am your master's friend{name}"
|
||||||
owner_env = "calender"
|
|
||||||
|
|
||||||
[[prompt]]
|
[[prompt]]
|
||||||
role = "system"
|
role = "system"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import copy
|
import copy
|
||||||
|
|
||||||
from aios.agent.agent_base import CustomAIAgent, AgentPrompt
|
from aios.agent.agent_base import CustomAIAgent, LLMPrompt
|
||||||
from aios.knowledge.data.writer import split_text
|
from aios.knowledge.data.writer import split_text
|
||||||
from aios.proto.agent_msg import AgentMsg, AgentMsgType
|
from aios.proto.agent_msg import AgentMsg, AgentMsgType
|
||||||
from aios.proto.compute_task import ComputeTaskResultCode
|
from aios.proto.compute_task import ComputeTaskResultCode
|
||||||
@@ -19,10 +19,10 @@ class TextSummaryAgent(CustomAIAgent):
|
|||||||
|
|
||||||
chunks = split_text(msg.body, separators=["\n\n", "\n"], chunk_size=4000, chunk_overlap=200, length_function=len)
|
chunks = split_text(msg.body, separators=["\n\n", "\n"], chunk_size=4000, chunk_overlap=200, length_function=len)
|
||||||
|
|
||||||
prompt = AgentPrompt()
|
prompt = LLMPrompt()
|
||||||
prompt.system_message = {"role":"system","content":"Your job is to generate a summary based on the input."}
|
prompt.system_message = "Your job is to generate a summary based on the input."
|
||||||
if len(chunks) == 1:
|
if len(chunks) == 1:
|
||||||
prompt.append(AgentPrompt(chunks[0]))
|
prompt.append(LLMPrompt(chunks[0]))
|
||||||
resp = await self.do_llm_complection(prompt)
|
resp = await self.do_llm_complection(prompt)
|
||||||
if resp.result_code != ComputeTaskResultCode.OK:
|
if resp.result_code != ComputeTaskResultCode.OK:
|
||||||
return msg.create_error_resp(resp.error_str)
|
return msg.create_error_resp(resp.error_str)
|
||||||
@@ -31,14 +31,14 @@ class TextSummaryAgent(CustomAIAgent):
|
|||||||
segments = []
|
segments = []
|
||||||
for i, chunk in enumerate(chunks):
|
for i, chunk in enumerate(chunks):
|
||||||
seg_prompt = copy.deepcopy(prompt)
|
seg_prompt = copy.deepcopy(prompt)
|
||||||
seg_prompt.append(AgentPrompt(chunk))
|
seg_prompt.append(LLMPrompt(chunk))
|
||||||
resp = await self.do_llm_complection(seg_prompt)
|
resp = await self.do_llm_complection(seg_prompt)
|
||||||
if resp.result_code != ComputeTaskResultCode.OK:
|
if resp.result_code != ComputeTaskResultCode.OK:
|
||||||
return msg.create_error_resp(resp.error_str)
|
return msg.create_error_resp(resp.error_str)
|
||||||
segments.append(resp.result_str)
|
segments.append(resp.result_str)
|
||||||
|
|
||||||
segments_str = "\n".join(segments)
|
segments_str = "\n".join(segments)
|
||||||
prompt.append(AgentPrompt(f"以下文本分段之后的各段摘要,请合并生成一个完整摘要:\n{segments_str}"))
|
prompt.append(LLMPrompt(f"以下文本分段之后的各段摘要,请合并生成一个完整摘要:\n{segments_str}"))
|
||||||
resp = await self.do_llm_complection(prompt)
|
resp = await self.do_llm_complection(prompt)
|
||||||
if resp.result_code != ComputeTaskResultCode.OK:
|
if resp.result_code != ComputeTaskResultCode.OK:
|
||||||
return msg.create_error_resp(resp.error_str)
|
return msg.create_error_resp(resp.error_str)
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
|
|
||||||
from .proto.agent_msg import *
|
from .proto.agent_msg import *
|
||||||
from .proto.compute_task import *
|
from .proto.compute_task import *
|
||||||
|
from .proto.ai_function import *
|
||||||
|
from .proto.agent_task import *
|
||||||
|
|
||||||
from .agent.agent_base import AgentPrompt,CustomAIAgent, AgentTodo
|
from .agent.agent_base import *
|
||||||
from .agent.chatsession import AIChatSession
|
from .agent.chatsession import AIChatSession
|
||||||
from .agent.agent import AIAgent,AIAgentTemplete, BaseAIAgent
|
from .agent.agent import AIAgent,AIAgentTemplete, BaseAIAgent
|
||||||
from .agent.role import AIRole,AIRoleGroup
|
from .agent.role import AIRole,AIRoleGroup
|
||||||
# from .agent.workflow import Workflow
|
from .agent.workflow import Workflow
|
||||||
from .agent.ai_function import SimpleAIFunction, SimpleAIOperation
|
|
||||||
|
|
||||||
from .frame.compute_kernel import ComputeKernel,ComputeTask,ComputeTaskResult,ComputeTaskState,ComputeTaskType
|
from .frame.compute_kernel import ComputeKernel,ComputeTask,ComputeTaskResult,ComputeTaskState,ComputeTaskType
|
||||||
from .frame.compute_node import ComputeNode,LocalComputeNode
|
from .frame.compute_node import ComputeNode,LocalComputeNode
|
||||||
@@ -20,7 +21,7 @@ from .environment.environment import BaseEnvironment,SimpleEnvironment,Composite
|
|||||||
# from .environment.workflow_env import WorkflowEnvironment,CalenderEnvironment,CalenderEvent,PaintEnvironment
|
# from .environment.workflow_env import WorkflowEnvironment,CalenderEnvironment,CalenderEvent,PaintEnvironment
|
||||||
from .environment.text_to_speech_function import TextToSpeechFunction
|
from .environment.text_to_speech_function import TextToSpeechFunction
|
||||||
from .environment.image_2_text_function import Image2TextFunction
|
from .environment.image_2_text_function import Image2TextFunction
|
||||||
from .environment.workspace_env import WorkspaceEnvironment,TodoListEnvironment,TodoListType
|
from .environment.workspace_env import WorkspaceEnvironment
|
||||||
|
|
||||||
from .storage.storage import ResourceLocation,AIStorage,UserConfig,UserConfigItem
|
from .storage.storage import ResourceLocation,AIStorage,UserConfig,UserConfigItem
|
||||||
|
|
||||||
|
|||||||
+47
-45
@@ -13,10 +13,12 @@ import copy
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
from ..proto.agent_msg import AgentMsg
|
from ..proto.agent_msg import AgentMsg
|
||||||
|
from ..proto.ai_function import *
|
||||||
|
from ..proto.agent_task import *
|
||||||
|
from ..proto.compute_task import *
|
||||||
|
|
||||||
from .agent_base import *
|
from .agent_base import *
|
||||||
from .chatsession import *
|
from .chatsession import *
|
||||||
from .ai_function import *
|
|
||||||
from ..environment.workspace_env import WorkspaceEnvironment, TodoListType
|
from ..environment.workspace_env import WorkspaceEnvironment, TodoListType
|
||||||
|
|
||||||
from ..frame.contact_manager import ContactManager,Contact,FamilyMember
|
from ..frame.contact_manager import ContactManager,Contact,FamilyMember
|
||||||
@@ -69,7 +71,7 @@ class AIAgentTemplete:
|
|||||||
self.template_id:str = None
|
self.template_id:str = None
|
||||||
self.introduce:str = None
|
self.introduce:str = None
|
||||||
self.author:str = None
|
self.author:str = None
|
||||||
self.prompt:AgentPrompt = None
|
self.prompt:LLMPrompt = None
|
||||||
|
|
||||||
def load_from_config(self,config:dict) -> bool:
|
def load_from_config(self,config:dict) -> bool:
|
||||||
if config.get("llm_model_name") is not None:
|
if config.get("llm_model_name") is not None:
|
||||||
@@ -79,7 +81,7 @@ class AIAgentTemplete:
|
|||||||
if config.get("template_id") is not None:
|
if config.get("template_id") is not None:
|
||||||
self.template_id = config["template_id"]
|
self.template_id = config["template_id"]
|
||||||
if config.get("prompt") is not None:
|
if config.get("prompt") is not None:
|
||||||
self.prompt = AgentPrompt()
|
self.prompt = LLMPrompt()
|
||||||
if self.prompt.load_from_config(config["prompt"]) is False:
|
if self.prompt.load_from_config(config["prompt"]) is False:
|
||||||
logger.error("load prompt from config failed!")
|
logger.error("load prompt from config failed!")
|
||||||
return False
|
return False
|
||||||
@@ -90,9 +92,9 @@ class AIAgentTemplete:
|
|||||||
|
|
||||||
class AIAgent(BaseAIAgent):
|
class AIAgent(BaseAIAgent):
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.role_prompt:AgentPrompt = None
|
self.role_prompt:LLMPrompt = None
|
||||||
self.agent_prompt:AgentPrompt = None
|
self.agent_prompt:LLMPrompt = None
|
||||||
self.agent_think_prompt:AgentPrompt = None
|
self.agent_think_prompt:LLMPrompt = None
|
||||||
self.llm_model_name:str = None
|
self.llm_model_name:str = None
|
||||||
self.max_token_size:int = 128000
|
self.max_token_size:int = 128000
|
||||||
self.agent_energy = 15
|
self.agent_energy = 15
|
||||||
@@ -149,26 +151,26 @@ class AIAgent(BaseAIAgent):
|
|||||||
self.enable_thread = bool(config["enable_thread"])
|
self.enable_thread = bool(config["enable_thread"])
|
||||||
|
|
||||||
if config.get("prompt") is not None:
|
if config.get("prompt") is not None:
|
||||||
self.agent_prompt = AgentPrompt()
|
self.agent_prompt = LLMPrompt()
|
||||||
self.agent_prompt.load_from_config(config["prompt"])
|
self.agent_prompt.load_from_config(config["prompt"])
|
||||||
|
|
||||||
if config.get("think_prompt") is not None:
|
if config.get("think_prompt") is not None:
|
||||||
self.agent_think_prompt = AgentPrompt()
|
self.agent_think_prompt = LLMPrompt()
|
||||||
self.agent_think_prompt.load_from_config(config["think_prompt"])
|
self.agent_think_prompt.load_from_config(config["think_prompt"])
|
||||||
|
|
||||||
def load_todo_config(todo_type:str) -> bool:
|
def load_todo_config(todo_type:str) -> bool:
|
||||||
todo_config = config.get(todo_type)
|
todo_config = config.get(todo_type)
|
||||||
if todo_config is not None:
|
if todo_config is not None:
|
||||||
if todo_config.get("do") is not None:
|
if todo_config.get("do") is not None:
|
||||||
prompt = AgentPrompt()
|
prompt = LLMPrompt()
|
||||||
prompt.load_from_config(todo_config["do"])
|
prompt.load_from_config(todo_config["do"])
|
||||||
self.todo_prompts[todo_type]["do"] = prompt
|
self.todo_prompts[todo_type]["do"] = prompt
|
||||||
if todo_config.get("check") is not None:
|
if todo_config.get("check") is not None:
|
||||||
prompt = AgentPrompt()
|
prompt = LLMPrompt()
|
||||||
prompt.load_from_config(todo_config["check"])
|
prompt.load_from_config(todo_config["check"])
|
||||||
self.todo_prompts[todo_type]["check"] = prompt
|
self.todo_prompts[todo_type]["check"] = prompt
|
||||||
if todo_config.get("review_prompt") is not None:
|
if todo_config.get("review_prompt") is not None:
|
||||||
prompt = AgentPrompt()
|
prompt = LLMPrompt()
|
||||||
prompt.load_from_config(todo_config["review_prompt"])
|
prompt.load_from_config(todo_config["review_prompt"])
|
||||||
self.todo_prompts[todo_type]["review"] = prompt
|
self.todo_prompts[todo_type]["review"] = prompt
|
||||||
|
|
||||||
@@ -224,16 +226,16 @@ class AIAgent(BaseAIAgent):
|
|||||||
def get_max_token_size(self) -> int:
|
def get_max_token_size(self) -> int:
|
||||||
return self.max_token_size
|
return self.max_token_size
|
||||||
|
|
||||||
def get_agent_role_prompt(self) -> AgentPrompt:
|
def get_agent_role_prompt(self) -> LLMPrompt:
|
||||||
return self.role_prompt
|
return self.role_prompt
|
||||||
|
|
||||||
def _get_remote_user_prompt(self,remote_user:str) -> AgentPrompt:
|
def _get_remote_user_prompt(self,remote_user:str) -> LLMPrompt:
|
||||||
cm = ContactManager.get_instance()
|
cm = ContactManager.get_instance()
|
||||||
contact = cm.find_contact_by_name(remote_user)
|
contact = cm.find_contact_by_name(remote_user)
|
||||||
if contact is None:
|
if contact is None:
|
||||||
#create guest prompt
|
#create guest prompt
|
||||||
if self.guest_prompt_str is not None:
|
if self.guest_prompt_str is not None:
|
||||||
prompt = AgentPrompt()
|
prompt = LLMPrompt()
|
||||||
prompt.system_message = {"role":"system","content":self.guest_prompt_str}
|
prompt.system_message = {"role":"system","content":self.guest_prompt_str}
|
||||||
return prompt
|
return prompt
|
||||||
return None
|
return None
|
||||||
@@ -241,25 +243,25 @@ class AIAgent(BaseAIAgent):
|
|||||||
if contact.is_family_member:
|
if contact.is_family_member:
|
||||||
if self.owner_promp_str is not None:
|
if self.owner_promp_str is not None:
|
||||||
real_str = self.owner_promp_str.format_map(contact.to_dict())
|
real_str = self.owner_promp_str.format_map(contact.to_dict())
|
||||||
prompt = AgentPrompt()
|
prompt = LLMPrompt()
|
||||||
prompt.system_message = {"role":"system","content":real_str}
|
prompt.system_message = {"role":"system","content":real_str}
|
||||||
return prompt
|
return prompt
|
||||||
else:
|
else:
|
||||||
if self.contact_prompt_str is not None:
|
if self.contact_prompt_str is not None:
|
||||||
real_str = self.contact_prompt_str.format_map(contact.to_dict())
|
real_str = self.contact_prompt_str.format_map(contact.to_dict())
|
||||||
prompt = AgentPrompt()
|
prompt = LLMPrompt()
|
||||||
prompt.system_message = {"role":"system","content":real_str}
|
prompt.system_message = {"role":"system","content":real_str}
|
||||||
return prompt
|
return prompt
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_agent_prompt(self) -> AgentPrompt:
|
def get_agent_prompt(self) -> LLMPrompt:
|
||||||
return self.agent_prompt
|
return self.agent_prompt
|
||||||
|
|
||||||
async def _get_agent_think_prompt(self) -> AgentPrompt:
|
async def _get_agent_think_prompt(self) -> LLMPrompt:
|
||||||
return self.agent_think_prompt
|
return self.agent_think_prompt
|
||||||
|
|
||||||
def _format_msg_by_env_value(self,prompt:AgentPrompt):
|
def _format_msg_by_env_value(self,prompt:LLMPrompt):
|
||||||
for msg in prompt.messages:
|
for msg in prompt.messages:
|
||||||
old_content = msg.get("content")
|
old_content = msg.get("content")
|
||||||
msg["content"] = old_content.format_map(self.agent_workspace)
|
msg["content"] = old_content.format_map(self.agent_workspace)
|
||||||
@@ -284,7 +286,7 @@ class AIAgent(BaseAIAgent):
|
|||||||
return image_path
|
return image_path
|
||||||
|
|
||||||
async def _process_msg(self,msg:AgentMsg,workspace = None) -> AgentMsg:
|
async def _process_msg(self,msg:AgentMsg,workspace = None) -> AgentMsg:
|
||||||
msg_prompt = AgentPrompt()
|
msg_prompt = LLMPrompt()
|
||||||
if msg.msg_type == AgentMsgType.TYPE_GROUPMSG:
|
if msg.msg_type == AgentMsgType.TYPE_GROUPMSG:
|
||||||
need_process = False
|
need_process = False
|
||||||
if msg.is_image_msg():
|
if msg.is_image_msg():
|
||||||
@@ -378,7 +380,7 @@ class AIAgent(BaseAIAgent):
|
|||||||
|
|
||||||
workspace = self.get_workspace_by_msg(msg)
|
workspace = self.get_workspace_by_msg(msg)
|
||||||
|
|
||||||
prompt = AgentPrompt()
|
prompt = LLMPrompt()
|
||||||
if workspace:
|
if workspace:
|
||||||
prompt.append(workspace.get_prompt())
|
prompt.append(workspace.get_prompt())
|
||||||
prompt.append(workspace.get_role_prompt(self.agent_id))
|
prompt.append(workspace.get_role_prompt(self.agent_id))
|
||||||
@@ -390,7 +392,7 @@ class AIAgent(BaseAIAgent):
|
|||||||
if self.need_session_summmary(msg,chatsession):
|
if self.need_session_summmary(msg,chatsession):
|
||||||
# get relate session(todos) summary
|
# get relate session(todos) summary
|
||||||
summary = self.llm_select_session_summary(msg,chatsession)
|
summary = self.llm_select_session_summary(msg,chatsession)
|
||||||
prompt.append(AgentPrompt(summary))
|
prompt.append(LLMPrompt(summary))
|
||||||
|
|
||||||
known_info_str = "# Known information\n"
|
known_info_str = "# Known information\n"
|
||||||
have_known_info = False
|
have_known_info = False
|
||||||
@@ -399,7 +401,7 @@ class AIAgent(BaseAIAgent):
|
|||||||
have_known_info = True
|
have_known_info = True
|
||||||
known_info_str += f"## todo\n{todos_str}\n"
|
known_info_str += f"## todo\n{todos_str}\n"
|
||||||
inner_functions,function_token_len = BaseAIAgent.get_inner_functions(self.agent_workspace)
|
inner_functions,function_token_len = BaseAIAgent.get_inner_functions(self.agent_workspace)
|
||||||
system_prompt_len = self.token_len(prompt=prompt)
|
system_prompt_len = ComputeKernel.llm_num_tokens(prompt)
|
||||||
input_len = len(msg.body)
|
input_len = len(msg.body)
|
||||||
if msg.msg_type == AgentMsgType.TYPE_GROUPMSG:
|
if msg.msg_type == AgentMsgType.TYPE_GROUPMSG:
|
||||||
history_str,history_token_len = await self._get_prompt_from_session_for_groupchat(chatsession,system_prompt_len + function_token_len,input_len)
|
history_str,history_token_len = await self._get_prompt_from_session_for_groupchat(chatsession,system_prompt_len + function_token_len,input_len)
|
||||||
@@ -410,7 +412,7 @@ class AIAgent(BaseAIAgent):
|
|||||||
known_info_str += history_str
|
known_info_str += history_str
|
||||||
|
|
||||||
if have_known_info:
|
if have_known_info:
|
||||||
known_info_prompt = AgentPrompt(known_info_str)
|
known_info_prompt = LLMPrompt(known_info_str)
|
||||||
prompt.append(known_info_prompt) # chat context
|
prompt.append(known_info_prompt) # chat context
|
||||||
|
|
||||||
prompt.append(msg_prompt)
|
prompt.append(msg_prompt)
|
||||||
@@ -436,7 +438,7 @@ class AIAgent(BaseAIAgent):
|
|||||||
final_result = llm_result.resp
|
final_result = llm_result.resp
|
||||||
|
|
||||||
|
|
||||||
await workspace.exec_op_list(llm_result.op_list,self.agent_id)
|
await workspace.exec_op_list(llm_result.action_list,self.agent_id)
|
||||||
|
|
||||||
is_ignore = False
|
is_ignore = False
|
||||||
result_prompt_str = ""
|
result_prompt_str = ""
|
||||||
@@ -471,12 +473,12 @@ class AIAgent(BaseAIAgent):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
async def _get_history_prompt_for_think(self,chatsession:AIChatSession,summary:str,system_token_len:int,pos:int)->(AgentPrompt,int):
|
async def _get_history_prompt_for_think(self,chatsession:AIChatSession,summary:str,system_token_len:int,pos:int)->(LLMPrompt,int):
|
||||||
history_len = (self.max_token_size * 0.7) - system_token_len
|
history_len = (self.max_token_size * 0.7) - system_token_len
|
||||||
|
|
||||||
messages = chatsession.read_history(self.history_len,pos,"natural") # read
|
messages = chatsession.read_history(self.history_len,pos,"natural") # read
|
||||||
result_token_len = 0
|
result_token_len = 0
|
||||||
result_prompt = AgentPrompt()
|
result_prompt = LLMPrompt()
|
||||||
have_summary = False
|
have_summary = False
|
||||||
if summary is not None:
|
if summary is not None:
|
||||||
if len(summary) > 1:
|
if len(summary) > 1:
|
||||||
@@ -511,7 +513,7 @@ class AIAgent(BaseAIAgent):
|
|||||||
history_len = (self.max_token_size * 0.7) - system_token_len - input_token_len
|
history_len = (self.max_token_size * 0.7) - system_token_len - input_token_len
|
||||||
messages = chatsession.read_history(self.history_len) # read
|
messages = chatsession.read_history(self.history_len) # read
|
||||||
result_token_len = 0
|
result_token_len = 0
|
||||||
result_prompt = AgentPrompt()
|
result_prompt = LLMPrompt()
|
||||||
read_history_msg = 0
|
read_history_msg = 0
|
||||||
for msg in reversed(messages):
|
for msg in reversed(messages):
|
||||||
read_history_msg += 1
|
read_history_msg += 1
|
||||||
@@ -569,13 +571,13 @@ class AIAgent(BaseAIAgent):
|
|||||||
|
|
||||||
async def _llm_read_report(self,report:AgentReport,worksapce:WorkspaceEnvironment):
|
async def _llm_read_report(self,report:AgentReport,worksapce:WorkspaceEnvironment):
|
||||||
work_summary = worksapce.get_work_summary(self.agent_id)
|
work_summary = worksapce.get_work_summary(self.agent_id)
|
||||||
prompt : AgentPrompt = AgentPrompt()
|
prompt : LLMPrompt = LLMPrompt()
|
||||||
prompt.append(self.agent_prompt)
|
prompt.append(self.agent_prompt)
|
||||||
prompt.append(worksapce.get_role_prompt(self.agent_id))
|
prompt.append(worksapce.get_role_prompt(self.agent_id))
|
||||||
prompt.append(self.read_report_prompt)
|
prompt.append(self.read_report_prompt)
|
||||||
# report is a message from other agent(human) about work
|
# report is a message from other agent(human) about work
|
||||||
prompt.append(AgentPrompt(work_summary))
|
prompt.append(LLMPrompt(work_summary))
|
||||||
prompt.append(AgentPrompt(report.content))
|
prompt.append(LLMPrompt(report.content))
|
||||||
|
|
||||||
task_result:ComputeTaskResult = await self.do_llm_complection(prompt)
|
task_result:ComputeTaskResult = await self.do_llm_complection(prompt)
|
||||||
|
|
||||||
@@ -606,7 +608,7 @@ class AIAgent(BaseAIAgent):
|
|||||||
|
|
||||||
do_prompts = self._can_do_todo(todo_list_type, todo)
|
do_prompts = self._can_do_todo(todo_list_type, todo)
|
||||||
if do_prompts:
|
if do_prompts:
|
||||||
prompt : AgentPrompt = AgentPrompt()
|
prompt : LLMPrompt = LLMPrompt()
|
||||||
prompt.append(self.agent_prompt)
|
prompt.append(self.agent_prompt)
|
||||||
prompt.append(workspace.get_role_prompt(self.agent_id))
|
prompt.append(workspace.get_role_prompt(self.agent_id))
|
||||||
prompt.append(do_prompts)
|
prompt.append(do_prompts)
|
||||||
@@ -635,13 +637,13 @@ class AIAgent(BaseAIAgent):
|
|||||||
|
|
||||||
check_prompts = self._can_check_todo(todo_list_type, todo)
|
check_prompts = self._can_check_todo(todo_list_type, todo)
|
||||||
if check_prompts:
|
if check_prompts:
|
||||||
prompt : AgentPrompt = AgentPrompt()
|
prompt : LLMPrompt = LLMPrompt()
|
||||||
prompt.append(self.agent_prompt)
|
prompt.append(self.agent_prompt)
|
||||||
prompt.append(workspace.get_role_prompt(self.agent_id))
|
prompt.append(workspace.get_role_prompt(self.agent_id))
|
||||||
prompt.append(check_prompts)
|
prompt.append(check_prompts)
|
||||||
|
|
||||||
if todo.last_check_result:
|
if todo.last_check_result:
|
||||||
prompt.append(AgentPrompt(todo.last_check_result))
|
prompt.append(LLMPrompt(todo.last_check_result))
|
||||||
|
|
||||||
prompt.append(todo.detail)
|
prompt.append(todo.detail)
|
||||||
prompt.append(todo.result)
|
prompt.append(todo.result)
|
||||||
@@ -669,7 +671,7 @@ class AIAgent(BaseAIAgent):
|
|||||||
prompt.append(review_prompts)
|
prompt.append(review_prompts)
|
||||||
|
|
||||||
todo_tree = todo_list.get_todo_tree("/")
|
todo_tree = todo_list.get_todo_tree("/")
|
||||||
prompt.append(AgentPrompt(todo_tree))
|
prompt.append(LLMPrompt(todo_tree))
|
||||||
|
|
||||||
do_result : AgentTodoResult = await self._llm_review_todo(todo, prompt, workspace)
|
do_result : AgentTodoResult = await self._llm_review_todo(todo, prompt, workspace)
|
||||||
todo.last_review_time = datetime.datetime.now().timestamp()
|
todo.last_review_time = datetime.datetime.now().timestamp()
|
||||||
@@ -690,7 +692,7 @@ class AIAgent(BaseAIAgent):
|
|||||||
logger.info(f"agent {self.agent_id} ,check:{check_count} todo,do:{do_count} todo.")
|
logger.info(f"agent {self.agent_id} ,check:{check_count} todo,do:{do_count} todo.")
|
||||||
|
|
||||||
|
|
||||||
def _can_review_todo(self, todo_list_type: TodoListType, todo:AgentTodo) -> AgentPrompt:
|
def _can_review_todo(self, todo_list_type: TodoListType, todo:AgentTodo) -> LLMPrompt:
|
||||||
do_prompts = self.todo_prompts[todo_list_type].get("review")
|
do_prompts = self.todo_prompts[todo_list_type].get("review")
|
||||||
if not do_prompts:
|
if not do_prompts:
|
||||||
return None
|
return None
|
||||||
@@ -701,7 +703,7 @@ class AIAgent(BaseAIAgent):
|
|||||||
return do_prompts
|
return do_prompts
|
||||||
|
|
||||||
|
|
||||||
def _can_check_todo(self, todo_list_type: TodoListType, todo:AgentTodo) -> AgentPrompt:
|
def _can_check_todo(self, todo_list_type: TodoListType, todo:AgentTodo) -> LLMPrompt:
|
||||||
do_prompts = self.todo_prompts[todo_list_type].get("check")
|
do_prompts = self.todo_prompts[todo_list_type].get("check")
|
||||||
if not do_prompts:
|
if not do_prompts:
|
||||||
return None
|
return None
|
||||||
@@ -720,7 +722,7 @@ class AIAgent(BaseAIAgent):
|
|||||||
|
|
||||||
return do_prompts
|
return do_prompts
|
||||||
|
|
||||||
def _can_do_todo(self, todo_list_type: TodoListType, todo:AgentTodo) -> AgentPrompt:
|
def _can_do_todo(self, todo_list_type: TodoListType, todo:AgentTodo) -> LLMPrompt:
|
||||||
do_prompts = self.todo_prompts[todo_list_type].get("do")
|
do_prompts = self.todo_prompts[todo_list_type].get("do")
|
||||||
if not do_prompts:
|
if not do_prompts:
|
||||||
return None
|
return None
|
||||||
@@ -739,7 +741,7 @@ class AIAgent(BaseAIAgent):
|
|||||||
|
|
||||||
return do_prompts
|
return do_prompts
|
||||||
|
|
||||||
async def _llm_do_todo(self, todo: AgentTodo, prompt: AgentPrompt, workspace: WorkspaceEnvironment) -> AgentTodoResult:
|
async def _llm_do_todo(self, todo: AgentTodo, prompt: LLMPrompt, workspace: WorkspaceEnvironment) -> AgentTodoResult:
|
||||||
result = AgentTodoResult()
|
result = AgentTodoResult()
|
||||||
|
|
||||||
task_result:ComputeTaskResult = await self.do_llm_complection(prompt, is_json_resp=True)
|
task_result:ComputeTaskResult = await self.do_llm_complection(prompt, is_json_resp=True)
|
||||||
@@ -763,7 +765,7 @@ class AIAgent(BaseAIAgent):
|
|||||||
resp = await AIBus.get_default_bus().post_message(msg)
|
resp = await AIBus.get_default_bus().post_message(msg)
|
||||||
logging.info(f"agent {self.agent_id} send msg to {msg.target} result:{resp}")
|
logging.info(f"agent {self.agent_id} send msg to {msg.target} result:{resp}")
|
||||||
|
|
||||||
result_str, have_error = await workspace.exec_op_list(llm_result.op_list, self.agent_id)
|
result_str, have_error = await workspace.exec_op_list(llm_result.action_list, self.agent_id)
|
||||||
if have_error:
|
if have_error:
|
||||||
result.result_code = AgentTodoResult.TODO_RESULT_CODE_EXEC_OP_ERROR
|
result.result_code = AgentTodoResult.TODO_RESULT_CODE_EXEC_OP_ERROR
|
||||||
#result.error_str = error_str
|
#result.error_str = error_str
|
||||||
@@ -771,7 +773,7 @@ class AIAgent(BaseAIAgent):
|
|||||||
result.result_str = result_str
|
result.result_str = result_str
|
||||||
return result
|
return result
|
||||||
|
|
||||||
async def _llm_check_todo(self, todo: AgentTodo, prompt: AgentPrompt, workspace: WorkspaceEnvironment) -> AgentTodoResult:
|
async def _llm_check_todo(self, todo: AgentTodo, prompt: LLMPrompt, workspace: WorkspaceEnvironment) -> AgentTodoResult:
|
||||||
result = AgentTodoResult()
|
result = AgentTodoResult()
|
||||||
|
|
||||||
inner_functions,_ = BaseAIAgent.get_inner_functions(workspace)
|
inner_functions,_ = BaseAIAgent.get_inner_functions(workspace)
|
||||||
@@ -786,7 +788,7 @@ class AIAgent(BaseAIAgent):
|
|||||||
todo.last_check_result = task_result.result_str
|
todo.last_check_result = task_result.result_str
|
||||||
return result
|
return result
|
||||||
|
|
||||||
async def _llm_review_todo(self, todo:AgentTodo, prompt: AgentPrompt, workspace: WorkspaceEnvironment):
|
async def _llm_review_todo(self, todo:AgentTodo, prompt: LLMPrompt, workspace: WorkspaceEnvironment):
|
||||||
inner_functions,_ = BaseAIAgent.get_inner_functions(workspace)
|
inner_functions,_ = BaseAIAgent.get_inner_functions(workspace)
|
||||||
|
|
||||||
task_result:ComputeTaskResult = await self.do_llm_complection(prompt,inner_functions=inner_functions)
|
task_result:ComputeTaskResult = await self.do_llm_complection(prompt,inner_functions=inner_functions)
|
||||||
@@ -842,10 +844,10 @@ class AIAgent(BaseAIAgent):
|
|||||||
while True:
|
while True:
|
||||||
cur_pos = chatsession.summarize_pos
|
cur_pos = chatsession.summarize_pos
|
||||||
summary = chatsession.summary
|
summary = chatsession.summary
|
||||||
prompt:AgentPrompt = AgentPrompt()
|
prompt:LLMPrompt = LLMPrompt()
|
||||||
#prompt.append(self._get_agent_prompt())
|
#prompt.append(self._get_agent_prompt())
|
||||||
prompt.append(await self._get_agent_think_prompt())
|
prompt.append(await self._get_agent_think_prompt())
|
||||||
system_prompt_len = self.token_len(prompt=prompt)
|
system_prompt_len = ComputeKernel.llm_num_tokens(prompt)
|
||||||
#think env?
|
#think env?
|
||||||
history_prompt,next_pos = await self._get_history_prompt_for_think(chatsession,summary,system_prompt_len,cur_pos)
|
history_prompt,next_pos = await self._get_history_prompt_for_think(chatsession,summary,system_prompt_len,cur_pos)
|
||||||
prompt.append(history_prompt)
|
prompt.append(history_prompt)
|
||||||
@@ -864,7 +866,7 @@ class AIAgent(BaseAIAgent):
|
|||||||
chatsession.update_think_progress(next_pos,new_summary)
|
chatsession.update_think_progress(next_pos,new_summary)
|
||||||
return
|
return
|
||||||
|
|
||||||
async def get_prompt_from_session(self,chatsession:AIChatSession,system_token_len,input_token_len) -> AgentPrompt:
|
async def get_prompt_from_session(self,chatsession:AIChatSession,system_token_len,input_token_len) -> LLMPrompt:
|
||||||
# TODO: get prompt from group chat is different from single chat
|
# TODO: get prompt from group chat is different from single chat
|
||||||
if self.enable_thread:
|
if self.enable_thread:
|
||||||
return None
|
return None
|
||||||
|
|||||||
+10
-405
@@ -11,400 +11,15 @@ import shlex
|
|||||||
import json
|
import json
|
||||||
from typing import List, Tuple
|
from typing import List, Tuple
|
||||||
|
|
||||||
from .ai_function import FunctionItem, AIFunction
|
from ..proto.ai_function import *
|
||||||
from ..proto.agent_msg import AgentMsg, AgentMsgType
|
from ..proto.agent_msg import *
|
||||||
from ..proto.compute_task import ComputeTaskResult,ComputeTaskResultCode
|
from ..proto.compute_task import *
|
||||||
from ..environment.environment import BaseEnvironment
|
from ..environment.environment import *
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class AgentPrompt:
|
|
||||||
def __init__(self,prompt_str = None) -> None:
|
|
||||||
self.messages = []
|
|
||||||
if prompt_str:
|
|
||||||
self.messages.append({"role":"user","content":prompt_str})
|
|
||||||
self.system_message = None
|
|
||||||
|
|
||||||
def as_str(self)->str:
|
|
||||||
result_str = ""
|
|
||||||
if self.system_message:
|
|
||||||
result_str += self.system_message.get("role") + ":" + self.system_message.get("content") + "\n"
|
|
||||||
if self.messages:
|
|
||||||
for msg in self.messages:
|
|
||||||
result_str += msg.get("role") + ":" + msg.get("content") + "\n"
|
|
||||||
|
|
||||||
return result_str
|
|
||||||
|
|
||||||
def to_message_list(self):
|
|
||||||
result = []
|
|
||||||
if self.system_message:
|
|
||||||
result.append(self.system_message)
|
|
||||||
result.extend(self.messages)
|
|
||||||
return result
|
|
||||||
|
|
||||||
def append(self,prompt):
|
|
||||||
if prompt is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
if prompt.system_message is not None:
|
|
||||||
if self.system_message is None:
|
|
||||||
self.system_message = copy.deepcopy(prompt.system_message)
|
|
||||||
else:
|
|
||||||
self.system_message["content"] += prompt.system_message.get("content")
|
|
||||||
|
|
||||||
self.messages.extend(prompt.messages)
|
|
||||||
|
|
||||||
def load_from_config(self,config:list) -> bool:
|
|
||||||
if isinstance(config,list) is not True:
|
|
||||||
logger.error("prompt is not list!")
|
|
||||||
return False
|
|
||||||
self.messages = []
|
|
||||||
for msg in config:
|
|
||||||
if msg.get("content"):
|
|
||||||
if msg.get("role") == "system":
|
|
||||||
self.system_message = msg
|
|
||||||
else:
|
|
||||||
self.messages.append(msg)
|
|
||||||
else:
|
|
||||||
logger.error("prompt message has no content!")
|
|
||||||
return True
|
|
||||||
|
|
||||||
class LLMResult:
|
|
||||||
def __init__(self) -> None:
|
|
||||||
self.state : str = "ignore"
|
|
||||||
self.resp : str = ""
|
|
||||||
self.raw_resp = None
|
|
||||||
self.paragraphs : dict[str,FunctionItem] = []
|
|
||||||
|
|
||||||
|
|
||||||
self.post_msgs : List[AgentMsg] = []
|
|
||||||
self.send_msgs : List[AgentMsg] = []
|
|
||||||
self.calls : List[FunctionItem] = []
|
|
||||||
self.post_calls : List[FunctionItem] = []
|
|
||||||
self.op_list : List[FunctionItem] = [] # op_list is a optimize design for saving token
|
|
||||||
@classmethod
|
|
||||||
def from_json_str(self,llm_json_str:str) -> 'LLMResult':
|
|
||||||
r = LLMResult()
|
|
||||||
if llm_json_str is None:
|
|
||||||
r.state = "ignore"
|
|
||||||
return r
|
|
||||||
if llm_json_str == "ignore":
|
|
||||||
r.state = "ignore"
|
|
||||||
return r
|
|
||||||
|
|
||||||
llm_json = json.loads(llm_json_str)
|
|
||||||
r.state = llm_json.get("state")
|
|
||||||
r.resp = llm_json.get("resp")
|
|
||||||
r.raw_resp = llm_json
|
|
||||||
|
|
||||||
post_msgs = llm_json.get("post_msg")
|
|
||||||
r.post_msgs = []
|
|
||||||
if post_msgs:
|
|
||||||
for msg in post_msgs:
|
|
||||||
new_msg = AgentMsg()
|
|
||||||
target_id = msg.get("target")
|
|
||||||
msg_content = msg.get("content")
|
|
||||||
new_msg.set("",target_id,msg_content)
|
|
||||||
r.post_msgs.append(new_msg)
|
|
||||||
#new_msg.msg_type = AgentMsgType.TYPE_MSG
|
|
||||||
|
|
||||||
r.calls = llm_json.get("calls")
|
|
||||||
r.post_calls = llm_json.get("post_calls")
|
|
||||||
r.op_list = llm_json.get("op_list")
|
|
||||||
|
|
||||||
return r
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_str(self,llm_result_str:str,valid_func:List[str]=None) -> 'LLMResult':
|
|
||||||
r = LLMResult()
|
|
||||||
|
|
||||||
if llm_result_str is None:
|
|
||||||
r.state = "ignore"
|
|
||||||
return r
|
|
||||||
if llm_result_str == "ignore":
|
|
||||||
r.state = "ignore"
|
|
||||||
return r
|
|
||||||
|
|
||||||
if llm_result_str[0] == "{":
|
|
||||||
return LLMResult.from_json_str(llm_result_str)
|
|
||||||
# if llm_result_str.startswith("json"):
|
|
||||||
# return LLMResult.from_json_str(llm_result_str[4:])
|
|
||||||
|
|
||||||
lines = llm_result_str.splitlines()
|
|
||||||
is_need_wait = False
|
|
||||||
|
|
||||||
def check_args(func_item:FunctionItem):
|
|
||||||
match func_name:
|
|
||||||
case "send_msg":# /send_msg $target_id
|
|
||||||
if len(func_args) != 1:
|
|
||||||
return False
|
|
||||||
|
|
||||||
new_msg = AgentMsg()
|
|
||||||
target_id = func_item.args[0]
|
|
||||||
msg_content = func_item.body
|
|
||||||
new_msg.set("",target_id,msg_content)
|
|
||||||
|
|
||||||
r.send_msgs.append(new_msg)
|
|
||||||
is_need_wait = True
|
|
||||||
return True
|
|
||||||
|
|
||||||
case "post_msg":# /post_msg $target_id
|
|
||||||
if len(func_args) != 1:
|
|
||||||
return False
|
|
||||||
|
|
||||||
new_msg = AgentMsg()
|
|
||||||
target_id = func_item.args[0]
|
|
||||||
msg_content = func_item.body
|
|
||||||
new_msg.set("",target_id,msg_content)
|
|
||||||
r.post_msgs.append(new_msg)
|
|
||||||
return True
|
|
||||||
|
|
||||||
case "call":# /call $func_name $args_str
|
|
||||||
r.calls.append(func_item)
|
|
||||||
is_need_wait = True
|
|
||||||
return True
|
|
||||||
case "post_call": # /post_call $func_name,$args_str
|
|
||||||
r.post_calls.append(func_item)
|
|
||||||
return True
|
|
||||||
case _:
|
|
||||||
if valid_func is not None:
|
|
||||||
if func_name in valid_func:
|
|
||||||
r.paragraphs[func_name] = func_item
|
|
||||||
return True
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
current_func : FunctionItem = None
|
|
||||||
for line in lines:
|
|
||||||
if line.startswith("##/"):
|
|
||||||
if current_func:
|
|
||||||
if check_args(current_func) is False:
|
|
||||||
r.resp += current_func.dumps()
|
|
||||||
|
|
||||||
func_name,func_args = AgentMsg.parse_function_call(line[3:])
|
|
||||||
current_func = FunctionItem(func_name,func_args)
|
|
||||||
else:
|
|
||||||
if current_func:
|
|
||||||
current_func.append_body(line + "\n")
|
|
||||||
else:
|
|
||||||
r.resp += line + "\n"
|
|
||||||
|
|
||||||
if current_func:
|
|
||||||
if check_args(current_func) is False:
|
|
||||||
r.resp += current_func.dumps()
|
|
||||||
|
|
||||||
if len(r.send_msgs) > 0 or len(r.calls) > 0:
|
|
||||||
r.state = "waiting"
|
|
||||||
else:
|
|
||||||
r.state = "reponsed"
|
|
||||||
|
|
||||||
return r
|
|
||||||
|
|
||||||
class AgentReport:
|
|
||||||
def __init__(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
class AgentTodoResult:
|
|
||||||
TODO_RESULT_CODE_OK = 0,
|
|
||||||
TODO_RESULT_CODE_LLM_ERROR = 1,
|
|
||||||
TODO_RESULT_CODE_EXEC_OP_ERROR = 2
|
|
||||||
|
|
||||||
|
|
||||||
def __init__(self) -> None:
|
|
||||||
self.result_code = AgentTodoResult.TODO_RESULT_CODE_OK
|
|
||||||
self.result_str = None
|
|
||||||
self.error_str = None
|
|
||||||
self.op_list = None
|
|
||||||
|
|
||||||
def to_dict(self) -> dict:
|
|
||||||
result = {}
|
|
||||||
result["result_code"] = self.result_code
|
|
||||||
result["result_str"] = self.result_str
|
|
||||||
result["error_str"] = self.error_str
|
|
||||||
result["op_list"] = self.op_list
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
class AgentTodo:
|
|
||||||
TODO_STATE_WAIT_ASSIGN = "wait_assign"
|
|
||||||
TODO_STATE_INIT = "init"
|
|
||||||
|
|
||||||
TODO_STATE_PENDING = "pending"
|
|
||||||
TODO_STATE_WAITING_CHECK = "wait_check"
|
|
||||||
TODO_STATE_EXEC_FAILED = "exec_failed"
|
|
||||||
TDDO_STATE_CHECKFAILED = "check_failed"
|
|
||||||
|
|
||||||
TODO_STATE_CANCEL = "cancel"
|
|
||||||
TODO_STATE_DONE = "done"
|
|
||||||
TODO_STATE_REVIEWED = "reviewed"
|
|
||||||
TODO_STATE_EXPIRED = "expired"
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.todo_id = "todo#" + uuid.uuid4().hex
|
|
||||||
self.title = None
|
|
||||||
self.detail = None
|
|
||||||
self.todo_path = None # get parent todo,sub todo by path
|
|
||||||
#self.parent = None
|
|
||||||
self.create_time = time.time()
|
|
||||||
|
|
||||||
self.state = "wait_assign"
|
|
||||||
self.worker = None
|
|
||||||
self.checker = None
|
|
||||||
self.createor = None
|
|
||||||
|
|
||||||
self.need_check = True
|
|
||||||
self.due_date = time.time() + 3600 * 24 * 2
|
|
||||||
self.last_do_time = None
|
|
||||||
self.last_check_time = None
|
|
||||||
self.last_review_time = None
|
|
||||||
|
|
||||||
self.depend_todo_ids = []
|
|
||||||
self.sub_todos = {}
|
|
||||||
|
|
||||||
self.result : AgentTodoResult = None
|
|
||||||
self.last_check_result = None
|
|
||||||
self.retry_count = 0
|
|
||||||
self.raw_obj = None
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_dict(cls,json_obj:dict) -> 'AgentTodo':
|
|
||||||
todo = AgentTodo()
|
|
||||||
if json_obj.get("id") is not None:
|
|
||||||
todo.todo_id = json_obj.get("id")
|
|
||||||
|
|
||||||
todo.title = json_obj.get("title")
|
|
||||||
todo.state = json_obj.get("state")
|
|
||||||
create_time = json_obj.get("create_time")
|
|
||||||
if create_time:
|
|
||||||
todo.create_time = datetime.fromisoformat(create_time).timestamp()
|
|
||||||
|
|
||||||
todo.detail = json_obj.get("detail")
|
|
||||||
due_date = json_obj.get("due_date")
|
|
||||||
if due_date:
|
|
||||||
todo.due_date = datetime.fromisoformat(due_date).timestamp()
|
|
||||||
|
|
||||||
last_do_time = json_obj.get("last_do_time")
|
|
||||||
if last_do_time:
|
|
||||||
todo.last_do_time = datetime.fromisoformat(last_do_time).timestamp()
|
|
||||||
last_check_time = json_obj.get("last_check_time")
|
|
||||||
if last_check_time:
|
|
||||||
todo.last_check_time = datetime.fromisoformat(last_check_time).timestamp()
|
|
||||||
last_review_time = json_obj.get("last_review_time")
|
|
||||||
if last_review_time:
|
|
||||||
todo.last_review_time = datetime.fromisoformat(last_review_time).timestamp()
|
|
||||||
|
|
||||||
todo.depend_todo_ids = json_obj.get("depend_todo_ids")
|
|
||||||
todo.need_check = json_obj.get("need_check")
|
|
||||||
#todo.result = json_obj.get("result")
|
|
||||||
#todo.last_check_result = json_obj.get("last_check_result")
|
|
||||||
todo.worker = json_obj.get("worker")
|
|
||||||
todo.checker = json_obj.get("checker")
|
|
||||||
todo.createor = json_obj.get("createor")
|
|
||||||
if json_obj.get("retry_count"):
|
|
||||||
todo.retry_count = json_obj.get("retry_count")
|
|
||||||
|
|
||||||
todo.raw_obj = json_obj
|
|
||||||
|
|
||||||
return todo
|
|
||||||
|
|
||||||
def to_dict(self) -> dict:
|
|
||||||
if self.raw_obj:
|
|
||||||
result = self.raw_obj
|
|
||||||
else:
|
|
||||||
result = {}
|
|
||||||
|
|
||||||
result["id"] = self.todo_id
|
|
||||||
#result["parent_id"] = self.parent_id
|
|
||||||
result["title"] = self.title
|
|
||||||
result["state"] = self.state
|
|
||||||
result["create_time"] = datetime.fromtimestamp(self.create_time).isoformat()
|
|
||||||
result["detail"] = self.detail
|
|
||||||
result["due_date"] = datetime.fromtimestamp(self.due_date).isoformat()
|
|
||||||
result["last_do_time"] = datetime.fromtimestamp(self.last_do_time).isoformat() if self.last_do_time else None
|
|
||||||
result["last_check_time"] = datetime.fromtimestamp(self.last_check_time).isoformat() if self.last_check_time else None
|
|
||||||
result["last_review_time"] = datetime.fromtimestamp(self.last_review_time).isoformat() if self.last_review_time else None
|
|
||||||
result["depend_todo_ids"] = self.depend_todo_ids
|
|
||||||
result["need_check"] = self.need_check
|
|
||||||
result["worker"] = self.worker
|
|
||||||
result["checker"] = self.checker
|
|
||||||
result["createor"] = self.createor
|
|
||||||
result["retry_count"] = self.retry_count
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
def to_prompt(self) -> AgentPrompt:
|
|
||||||
json_str = json.dumps(self.raw_obj)
|
|
||||||
return AgentPrompt(json_str)
|
|
||||||
|
|
||||||
def can_review(self) -> bool:
|
|
||||||
if self.state != AgentTodo.TODO_STATE_DONE:
|
|
||||||
return False
|
|
||||||
|
|
||||||
now = datetime.now().timestamp()
|
|
||||||
if self.last_review_time:
|
|
||||||
time_diff = now - self.last_review_time
|
|
||||||
if time_diff < 60*15:
|
|
||||||
logger.info(f"todo {self.title} is already reviewed, ignore")
|
|
||||||
return False
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def can_check(self)->bool:
|
|
||||||
if self.state != AgentTodo.TODO_STATE_WAITING_CHECK:
|
|
||||||
return False
|
|
||||||
|
|
||||||
now = datetime.now().timestamp()
|
|
||||||
if self.last_check_time:
|
|
||||||
time_diff = now - self.last_check_time
|
|
||||||
if time_diff < 60*15:
|
|
||||||
logger.info(f"todo {self.title} is already checked, ignore")
|
|
||||||
return False
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def can_do(self) -> bool:
|
|
||||||
match self.state:
|
|
||||||
case AgentTodo.TODO_STATE_DONE:
|
|
||||||
logger.info(f"todo {self.title} is done, ignore")
|
|
||||||
return False
|
|
||||||
case AgentTodo.TODO_STATE_CANCEL:
|
|
||||||
logger.info(f"todo {self.title} is cancel, ignore")
|
|
||||||
return False
|
|
||||||
case AgentTodo.TODO_STATE_EXPIRED:
|
|
||||||
logger.info(f"todo {self.title} is expired, ignore")
|
|
||||||
return False
|
|
||||||
case AgentTodo.TODO_STATE_EXEC_FAILED:
|
|
||||||
if self.retry_count > 3:
|
|
||||||
logger.info(f"todo {self.title} retry count ({self.retry_count}) is too many, ignore")
|
|
||||||
return False
|
|
||||||
|
|
||||||
now = datetime.now().timestamp()
|
|
||||||
time_diff = self.due_date - now
|
|
||||||
if time_diff < 0:
|
|
||||||
logger.info(f"todo {self.title} is expired, ignore")
|
|
||||||
self.state = AgentTodo.TODO_STATE_EXPIRED
|
|
||||||
return False
|
|
||||||
|
|
||||||
if time_diff > 7*24*3600:
|
|
||||||
logger.info(f"todo {self.title} is far before due date, ignore")
|
|
||||||
return False
|
|
||||||
|
|
||||||
if self.last_do_time:
|
|
||||||
time_diff = now - self.last_do_time
|
|
||||||
if time_diff < 60*15:
|
|
||||||
logger.info(f"todo {self.title} is already do ignore")
|
|
||||||
return False
|
|
||||||
|
|
||||||
logger.info(f"todo {self.title} can do.")
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
class AgentWorkLog:
|
|
||||||
def __init__(self) -> None:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class BaseAIAgent(abc.ABC):
|
class BaseAIAgent(abc.ABC):
|
||||||
@@ -420,19 +35,9 @@ class BaseAIAgent(abc.ABC):
|
|||||||
def get_max_token_size(self) -> int:
|
def get_max_token_size(self) -> int:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def token_len(self, text:str=None, prompt:AgentPrompt=None) -> int:
|
@abstractmethod
|
||||||
from ..frame.compute_kernel import ComputeKernel
|
async def _process_msg(self,msg:AgentMsg,workspace = None) -> AgentMsg:
|
||||||
if text:
|
pass
|
||||||
return ComputeKernel.llm_num_tokens_from_text(text,self.get_llm_model_name())
|
|
||||||
elif prompt:
|
|
||||||
result = 0
|
|
||||||
if prompt.system_message:
|
|
||||||
result += ComputeKernel.llm_num_tokens_from_text(prompt.system_message.get("content"),self.get_llm_model_name())
|
|
||||||
for msg in prompt.messages:
|
|
||||||
result += ComputeKernel.llm_num_tokens_from_text(msg.get("content"),self.get_llm_model_name())
|
|
||||||
return result
|
|
||||||
else:
|
|
||||||
return 0
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_inner_functions(cls, env:BaseEnvironment) -> (dict,int):
|
def get_inner_functions(cls, env:BaseEnvironment) -> (dict,int):
|
||||||
@@ -458,7 +63,7 @@ class BaseAIAgent(abc.ABC):
|
|||||||
|
|
||||||
async def do_llm_complection(
|
async def do_llm_complection(
|
||||||
self,
|
self,
|
||||||
prompt:AgentPrompt,
|
prompt:LLMPrompt,
|
||||||
org_msg:AgentMsg=None,
|
org_msg:AgentMsg=None,
|
||||||
env:BaseEnvironment=None,
|
env:BaseEnvironment=None,
|
||||||
inner_functions=None,
|
inner_functions=None,
|
||||||
@@ -503,7 +108,7 @@ class BaseAIAgent(abc.ABC):
|
|||||||
inner_func_call_node = result_message.get("function_call")
|
inner_func_call_node = result_message.get("function_call")
|
||||||
|
|
||||||
if inner_func_call_node:
|
if inner_func_call_node:
|
||||||
call_prompt : AgentPrompt = copy.deepcopy(prompt)
|
call_prompt : LLMPrompt = copy.deepcopy(prompt)
|
||||||
func_msg = copy.deepcopy(result_message)
|
func_msg = copy.deepcopy(result_message)
|
||||||
del func_msg["tool_calls"]
|
del func_msg["tool_calls"]
|
||||||
call_prompt.messages.append(func_msg)
|
call_prompt.messages.append(func_msg)
|
||||||
@@ -515,7 +120,7 @@ class BaseAIAgent(abc.ABC):
|
|||||||
self,
|
self,
|
||||||
env: BaseEnvironment,
|
env: BaseEnvironment,
|
||||||
inner_func_call_node: dict,
|
inner_func_call_node: dict,
|
||||||
prompt: AgentPrompt,
|
prompt: LLMPrompt,
|
||||||
inner_functions: dict,
|
inner_functions: dict,
|
||||||
org_msg:AgentMsg,
|
org_msg:AgentMsg,
|
||||||
stack_limit = 5
|
stack_limit = 5
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
# TODO: let agent develolp custmized behavior easily
|
|
||||||
class AgentBehavior:
|
|
||||||
def __init__(self) -> None:
|
|
||||||
pass
|
|
||||||
@@ -0,0 +1,149 @@
|
|||||||
|
# Old name is behavior, I belive new name "llm_process" is better
|
||||||
|
from abc import ABC,abstractmethod
|
||||||
|
import copy
|
||||||
|
import json
|
||||||
|
import shlex
|
||||||
|
from typing import Any, Callable, Optional,Dict,Awaitable,List
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
from ..proto.compute_task import *
|
||||||
|
from ..proto.ai_function import *
|
||||||
|
from ..frame.compute_kernel import *
|
||||||
|
|
||||||
|
import logging
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
MIN_PREDICT_TOKEN_LEN = 32
|
||||||
|
|
||||||
|
|
||||||
|
class BaseLLMProcess:
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.enable_json_resp = False
|
||||||
|
self.model_name = "gpt-4"
|
||||||
|
self.max_token = 2000 # include input prompt
|
||||||
|
self.timeout = 1800 # 30 min
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
async def prepare_prompt(self) -> LLMPrompt:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
async def get_inner_function(self,func_name:str) -> AIFunction:
|
||||||
|
pass
|
||||||
|
|
||||||
|
async def _execute_inner_func(self,inner_func_call_node,prompt: LLMPrompt,stack_limit = 5) -> ComputeTaskResult:
|
||||||
|
arguments = None
|
||||||
|
try:
|
||||||
|
func_name = inner_func_call_node.get("name")
|
||||||
|
arguments = json.loads(inner_func_call_node.get("arguments"))
|
||||||
|
logger.info(f"LLMProcess execute inner func:{func_name} :\n\t {json.dumps(arguments)}")
|
||||||
|
|
||||||
|
func_node : AIFunction = await self.get_inner_function(func_name)
|
||||||
|
if func_node is None:
|
||||||
|
result_str:str = f"execute {func_name} error,function not found"
|
||||||
|
else:
|
||||||
|
result_str:str = await func_node.execute(**arguments)
|
||||||
|
except Exception as e:
|
||||||
|
result_str = f"execute {func_name} error:{str(e)}"
|
||||||
|
logger.error(f"LLMProcess execute inner func:{func_name} error:\n\t{e}")
|
||||||
|
|
||||||
|
logger.info("LLMProcess execute inner func result:" + result_str)
|
||||||
|
|
||||||
|
prompt.messages.append({"role":"function","content":result_str,"name":func_name})
|
||||||
|
if self.enable_json_resp:
|
||||||
|
resp_mode = "json"
|
||||||
|
else:
|
||||||
|
resp_mode = "text"
|
||||||
|
|
||||||
|
max_result_token = self.max_token - ComputeKernel.llm_num_tokens(prompt)
|
||||||
|
if max_result_token < MIN_PREDICT_TOKEN_LEN:
|
||||||
|
task_result = ComputeTaskResult()
|
||||||
|
task_result.result_code = ComputeTaskResultCode.ERROR
|
||||||
|
task_result.error_str = f"prompt too long,can not predict"
|
||||||
|
return task_result
|
||||||
|
|
||||||
|
task_result: ComputeTaskResult = await (ComputeKernel.get_instance().do_llm_completion(
|
||||||
|
prompt,
|
||||||
|
resp_mode=resp_mode,
|
||||||
|
mode_name=self.model_name,
|
||||||
|
max_token=max_result_token,
|
||||||
|
inner_functions=prompt.inner_functions,
|
||||||
|
timeout=self.timeout))
|
||||||
|
|
||||||
|
if task_result.result_code != ComputeTaskResultCode.OK:
|
||||||
|
logger.error(f"llm compute error:{task_result.error_str}")
|
||||||
|
return task_result
|
||||||
|
|
||||||
|
inner_func_call_node = None
|
||||||
|
if stack_limit > 0:
|
||||||
|
result_message : dict = task_result.result.get("message")
|
||||||
|
if result_message:
|
||||||
|
inner_func_call_node = result_message.get("function_call")
|
||||||
|
if inner_func_call_node:
|
||||||
|
func_msg = copy.deepcopy(result_message)
|
||||||
|
del func_msg["tool_calls"]#TODO: support tool_calls?
|
||||||
|
prompt.messages.append(func_msg)
|
||||||
|
else:
|
||||||
|
logger.error(f"inner function call stack limit reached")
|
||||||
|
task_result.result_code = ComputeTaskResultCode.ERROR
|
||||||
|
task_result.error_str = "inner function call stack limit reached"
|
||||||
|
return task_result
|
||||||
|
|
||||||
|
if inner_func_call_node:
|
||||||
|
return await self._execute_inner_func(inner_func_call_node,prompt,stack_limit-1)
|
||||||
|
else:
|
||||||
|
return task_result
|
||||||
|
|
||||||
|
async def process(self) -> LLMResult:
|
||||||
|
if self.enable_json_resp:
|
||||||
|
resp_mode = "json"
|
||||||
|
else:
|
||||||
|
resp_mode = "text"
|
||||||
|
|
||||||
|
prompt = await self.prepare_prompt()
|
||||||
|
max_result_token = self.max_token - ComputeKernel.llm_num_tokens(prompt)
|
||||||
|
if max_result_token < MIN_PREDICT_TOKEN_LEN:
|
||||||
|
return LLMResult.from_error_str(f"prompt too long,can not predict")
|
||||||
|
|
||||||
|
task_result: ComputeTaskResult = await (ComputeKernel.get_instance().do_llm_completion(
|
||||||
|
prompt,
|
||||||
|
resp_mode=resp_mode,
|
||||||
|
mode_name=self.model_name,
|
||||||
|
max_token=max_result_token,
|
||||||
|
inner_functions=prompt.inner_functions,
|
||||||
|
timeout=self.timeout))
|
||||||
|
|
||||||
|
if task_result.result_code != ComputeTaskResultCode.OK:
|
||||||
|
err_str = f"do_llm_completion error:{task_result.error_str}"
|
||||||
|
logger.error(err_str)
|
||||||
|
return LLMResult.from_error_str(err_str)
|
||||||
|
|
||||||
|
result_message = task_result.result.get("message")
|
||||||
|
inner_func_call_node = None
|
||||||
|
if result_message:
|
||||||
|
inner_func_call_node = result_message.get("function_call")
|
||||||
|
|
||||||
|
if inner_func_call_node:
|
||||||
|
call_prompt : LLMPrompt = copy.deepcopy(prompt)
|
||||||
|
func_msg = copy.deepcopy(result_message)
|
||||||
|
del func_msg["tool_calls"]
|
||||||
|
call_prompt.messages.append(func_msg)
|
||||||
|
task_result = await self._execute_inner_func(inner_func_call_node,call_prompt)
|
||||||
|
|
||||||
|
# parse task_result to LLM Result
|
||||||
|
if self.enable_json_resp:
|
||||||
|
llm_result = LLMResult.from_json_str(task_result.result_str)
|
||||||
|
else:
|
||||||
|
llm_result = LLMResult.from_str(task_result.result_str)
|
||||||
|
|
||||||
|
# execute op_list in LLM Result?
|
||||||
|
|
||||||
|
return llm_result
|
||||||
|
|
||||||
|
#class LLMProcess
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from .agent_base import AgentPrompt
|
from .agent_base import LLMPrompt
|
||||||
|
|
||||||
class AIRole:
|
class AIRole:
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
@@ -9,7 +9,7 @@ class AIRole:
|
|||||||
self.role_id :str = None # $workflow_id.$sub_workflow_id.$role_name
|
self.role_id :str = None # $workflow_id.$sub_workflow_id.$role_name
|
||||||
self.fullname : str = None
|
self.fullname : str = None
|
||||||
self.agent_name : str = None
|
self.agent_name : str = None
|
||||||
self.prompt : AgentPrompt = None
|
self.prompt : LLMPrompt = None
|
||||||
self.introduce : str = None
|
self.introduce : str = None
|
||||||
self.agent = None
|
self.agent = None
|
||||||
self.enable_function_list : list[str] = None
|
self.enable_function_list : list[str] = None
|
||||||
@@ -31,7 +31,7 @@ class AIRole:
|
|||||||
|
|
||||||
prompt_node = config.get("prompt")
|
prompt_node = config.get("prompt")
|
||||||
if prompt_node:
|
if prompt_node:
|
||||||
self.prompt = AgentPrompt()
|
self.prompt = LLMPrompt()
|
||||||
if self.prompt.load_from_config(prompt_node) is False:
|
if self.prompt.load_from_config(prompt_node) is False:
|
||||||
logging.error("load prompt failed!")
|
logging.error("load prompt failed!")
|
||||||
return False
|
return False
|
||||||
@@ -56,7 +56,7 @@ class AIRole:
|
|||||||
def get_name(self) -> str:
|
def get_name(self) -> str:
|
||||||
return self.role_name
|
return self.role_name
|
||||||
|
|
||||||
def get_prompt(self) -> AgentPrompt:
|
def get_prompt(self) -> LLMPrompt:
|
||||||
return self.prompt
|
return self.prompt
|
||||||
|
|
||||||
class AIRoleGroup:
|
class AIRoleGroup:
|
||||||
|
|||||||
+15
-15
@@ -9,11 +9,11 @@ from abc import ABC, abstractmethod
|
|||||||
|
|
||||||
from ..proto.compute_task import *
|
from ..proto.compute_task import *
|
||||||
from ..proto.agent_msg import *
|
from ..proto.agent_msg import *
|
||||||
|
from ..proto.ai_function import *
|
||||||
|
|
||||||
from .agent_base import *
|
from .agent_base import *
|
||||||
from .chatsession import AIChatSession
|
from .chatsession import AIChatSession
|
||||||
from .role import AIRole,AIRoleGroup
|
from .role import AIRole,AIRoleGroup
|
||||||
from .ai_function import AIFunction,FunctionItem
|
|
||||||
|
|
||||||
from ..frame.compute_kernel import ComputeKernel
|
from ..frame.compute_kernel import ComputeKernel
|
||||||
from ..frame.bus import AIBus
|
from ..frame.bus import AIBus
|
||||||
@@ -48,7 +48,7 @@ class Workflow:
|
|||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.workflow_name : str = None
|
self.workflow_name : str = None
|
||||||
self.workflow_id : str = None
|
self.workflow_id : str = None
|
||||||
self.rule_prompt : AgentPrompt = None
|
self.rule_prompt : LLMPrompt = None
|
||||||
self.workflow_config = None
|
self.workflow_config = None
|
||||||
self.role_group : dict = None
|
self.role_group : dict = None
|
||||||
self.input_filter : MessageFilter= None
|
self.input_filter : MessageFilter= None
|
||||||
@@ -83,7 +83,7 @@ class Workflow:
|
|||||||
self.db_file = self.owner_workflow.db_file
|
self.db_file = self.owner_workflow.db_file
|
||||||
|
|
||||||
if config.get("prompt") is not None:
|
if config.get("prompt") is not None:
|
||||||
self.rule_prompt = AgentPrompt()
|
self.rule_prompt = LLMPrompt()
|
||||||
if self.rule_prompt.load_from_config(config.get("prompt")) is False:
|
if self.rule_prompt.load_from_config(config.get("prompt")) is False:
|
||||||
logger.error("Workflow load prompt failed")
|
logger.error("Workflow load prompt failed")
|
||||||
return False
|
return False
|
||||||
@@ -279,7 +279,7 @@ class Workflow:
|
|||||||
logger.info(f"{msg.sender} post message {msg.msg_id} to AIBus: {msg.target}")
|
logger.info(f"{msg.sender} post message {msg.msg_id} to AIBus: {msg.target}")
|
||||||
return await self.get_bus().send_message(msg)
|
return await self.get_bus().send_message(msg)
|
||||||
|
|
||||||
async def role_call(self,func_item:FunctionItem,the_role:AIRole):
|
async def role_call(self,func_item:ActionItem,the_role:AIRole):
|
||||||
logger.info(f"{the_role.role_id} call {func_item.name} ")
|
logger.info(f"{the_role.role_id} call {func_item.name} ")
|
||||||
arguments = func_item.args
|
arguments = func_item.args
|
||||||
|
|
||||||
@@ -290,11 +290,11 @@ class Workflow:
|
|||||||
result_str:str = await func_node.execute(**arguments)
|
result_str:str = await func_node.execute(**arguments)
|
||||||
return result_str
|
return result_str
|
||||||
|
|
||||||
async def role_post_call(self,func_item:FunctionItem,the_role:AIRole):
|
async def role_post_call(self,func_item:ActionItem,the_role:AIRole):
|
||||||
logger.info(f"{the_role.role_id} post call {func_item.name} ")
|
logger.info(f"{the_role.role_id} post call {func_item.name} ")
|
||||||
return await self.role_call(func_item,the_role)
|
return await self.role_call(func_item,the_role)
|
||||||
|
|
||||||
def _format_msg_by_env_value(self,prompt:AgentPrompt):
|
def _format_msg_by_env_value(self,prompt:LLMPrompt):
|
||||||
if self.workflow_env is None:
|
if self.workflow_env is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -326,7 +326,7 @@ class Workflow:
|
|||||||
return result_func
|
return result_func
|
||||||
return None
|
return None
|
||||||
|
|
||||||
async def _role_execute_func(self,the_role:AIRole,inenr_func_call_node:dict,prompt:AgentPrompt,org_msg:AgentMsg,stack_limit = 5) -> [str,int]:
|
async def _role_execute_func(self,the_role:AIRole,inenr_func_call_node:dict,prompt:LLMPrompt,org_msg:AgentMsg,stack_limit = 5) -> [str,int]:
|
||||||
|
|
||||||
func_name = inenr_func_call_node.get("name")
|
func_name = inenr_func_call_node.get("name")
|
||||||
arguments = json.loads(inenr_func_call_node.get("arguments"))
|
arguments = json.loads(inenr_func_call_node.get("arguments"))
|
||||||
@@ -372,7 +372,7 @@ class Workflow:
|
|||||||
async def role_process_msg(self,msg:AgentMsg,the_role:AIRole,workflow_chat_session:AIChatSession) -> AgentMsg:
|
async def role_process_msg(self,msg:AgentMsg,the_role:AIRole,workflow_chat_session:AIChatSession) -> AgentMsg:
|
||||||
msg.target = the_role.get_role_id()
|
msg.target = the_role.get_role_id()
|
||||||
|
|
||||||
prompt = AgentPrompt()
|
prompt = LLMPrompt()
|
||||||
prompt.append(the_role.agent.agent_prompt)
|
prompt.append(the_role.agent.agent_prompt)
|
||||||
prompt.append(self.get_workflow_rule_prompt())
|
prompt.append(self.get_workflow_rule_prompt())
|
||||||
prompt.append(the_role.get_prompt())
|
prompt.append(the_role.get_prompt())
|
||||||
@@ -382,7 +382,7 @@ class Workflow:
|
|||||||
#support group chat, user content include sender name!
|
#support group chat, user content include sender name!
|
||||||
prompt.append(await self._get_prompt_from_session(the_role,workflow_chat_session))
|
prompt.append(await self._get_prompt_from_session(the_role,workflow_chat_session))
|
||||||
|
|
||||||
msg_prompt = AgentPrompt()
|
msg_prompt = LLMPrompt()
|
||||||
msg_prompt.messages = [{"role":"user","content":f"user name is {msg.sender}, his question is :{msg.body}"}]
|
msg_prompt.messages = [{"role":"user","content":f"user name is {msg.sender}, his question is :{msg.body}"}]
|
||||||
prompt.append(msg_prompt)
|
prompt.append(msg_prompt)
|
||||||
|
|
||||||
@@ -461,20 +461,20 @@ class Workflow:
|
|||||||
# message will be saved in role.process_message
|
# message will be saved in role.process_message
|
||||||
pass
|
pass
|
||||||
|
|
||||||
this_llm_resp_prompt = AgentPrompt()
|
this_llm_resp_prompt = LLMPrompt()
|
||||||
this_llm_resp_prompt.messages = [{"role":"assistant","content":result_str}]
|
this_llm_resp_prompt.messages = [{"role":"assistant","content":result_str}]
|
||||||
prompt.append(this_llm_resp_prompt)
|
prompt.append(this_llm_resp_prompt)
|
||||||
|
|
||||||
result_prompt = AgentPrompt()
|
result_prompt = LLMPrompt()
|
||||||
result_prompt.messages = [{"role":"user","content":result_prompt_str}]
|
result_prompt.messages = [{"role":"user","content":result_prompt_str}]
|
||||||
prompt.append(result_prompt)
|
prompt.append(result_prompt)
|
||||||
return await _do_process_msg()
|
return await _do_process_msg()
|
||||||
|
|
||||||
return await _do_process_msg()
|
return await _do_process_msg()
|
||||||
|
|
||||||
async def _get_prompt_from_session(self,the_role:AIRole,chatsession:AIChatSession) -> AgentPrompt:
|
async def _get_prompt_from_session(self,the_role:AIRole,chatsession:AIChatSession) -> LLMPrompt:
|
||||||
messages = chatsession.read_history(the_role.history_len) # read last 10 message
|
messages = chatsession.read_history(the_role.history_len) # read last 10 message
|
||||||
result_prompt = AgentPrompt()
|
result_prompt = LLMPrompt()
|
||||||
|
|
||||||
for msg in reversed(messages):
|
for msg in reversed(messages):
|
||||||
if msg.sender == the_role.role_id:
|
if msg.sender == the_role.role_id:
|
||||||
@@ -484,10 +484,10 @@ class Workflow:
|
|||||||
|
|
||||||
return result_prompt
|
return result_prompt
|
||||||
|
|
||||||
def _get_knowlege_prompt(self,role_name:str) -> AgentPrompt:
|
def _get_knowlege_prompt(self,role_name:str) -> LLMPrompt:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def get_workflow_rule_prompt(self) -> AgentPrompt:
|
def get_workflow_rule_prompt(self) -> LLMPrompt:
|
||||||
return self.rule_prompt
|
return self.rule_prompt
|
||||||
|
|
||||||
# def _env_event_to_msg(self,env_event:EnvironmentEvent) -> AgentMsg:
|
# def _env_event_to_msg(self,env_event:EnvironmentEvent) -> AgentMsg:
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import logging
|
|||||||
from typing import Dict
|
from typing import Dict
|
||||||
|
|
||||||
from ..frame.compute_kernel import ComputeKernel
|
from ..frame.compute_kernel import ComputeKernel
|
||||||
from ..agent.ai_function import AIFunction
|
from ..proto.ai_function import *
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from typing import Dict
|
from typing import Dict
|
||||||
|
|
||||||
from ..agent.ai_function import AIFunction
|
from ..proto.ai_function import *
|
||||||
from .code_interpreter import execute_code
|
from .code_interpreter import execute_code
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import json
|
import json
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
|
|
||||||
from ..agent.ai_function import AIFunction
|
from ..proto.ai_function import *
|
||||||
from duckduckgo_search import AsyncDDGS
|
from duckduckgo_search import AsyncDDGS
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from typing import Any, Callable, Optional,Dict,Awaitable,List
|
from typing import Any, Callable, Optional,Dict,Awaitable,List
|
||||||
import logging
|
import logging
|
||||||
from ..agent.ai_function import AIFunction, AIOperation
|
from ..proto.ai_function import *
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import logging
|
|||||||
from typing import Dict
|
from typing import Dict
|
||||||
|
|
||||||
from ..frame.compute_kernel import ComputeKernel
|
from ..frame.compute_kernel import ComputeKernel
|
||||||
from ..agent.ai_function import AIFunction
|
from ..proto.ai_function import *
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import random
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
|
|
||||||
from ..agent.ai_function import AIFunction
|
from ..proto.ai_function import *
|
||||||
from ..frame.compute_kernel import ComputeKernel
|
from ..frame.compute_kernel import ComputeKernel
|
||||||
from ..storage.storage import AIStorage
|
from ..storage.storage import AIStorage
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from typing import Dict
|
|||||||
|
|
||||||
from cachetools import TLRUCache, cached
|
from cachetools import TLRUCache, cached
|
||||||
|
|
||||||
from ..agent.ai_function import AIFunction
|
from ..proto.ai_function import *
|
||||||
from .sql_database import SQLDatabase, get_from_env
|
from .sql_database import SQLDatabase, get_from_env
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import random
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
|
|
||||||
from ..agent.ai_function import AIFunction
|
from ..proto.ai_function import *
|
||||||
from ..frame.compute_kernel import ComputeKernel
|
from ..frame.compute_kernel import ComputeKernel
|
||||||
from ..storage.storage import AIStorage
|
from ..storage.storage import AIStorage
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ from typing import Optional
|
|||||||
import aiosqlite
|
import aiosqlite
|
||||||
|
|
||||||
from ..proto.compute_task import *
|
from ..proto.compute_task import *
|
||||||
from ..agent.ai_function import SimpleAIFunction
|
from ..proto.ai_function import *
|
||||||
from ..frame.compute_kernel import ComputeKernel
|
from ..frame.compute_kernel import ComputeKernel
|
||||||
from ..frame.contact_manager import ContactManager,Contact,FamilyMember
|
from ..frame.contact_manager import ContactManager,Contact,FamilyMember
|
||||||
from ..storage.storage import AIStorage
|
from ..storage.storage import AIStorage
|
||||||
@@ -302,7 +302,7 @@ class CalenderEnvironment(SimpleEnvironment):
|
|||||||
return f'exec paint OK, saved as a local file, path is: {result.result["file"]}'
|
return f'exec paint OK, saved as a local file, path is: {result.result["file"]}'
|
||||||
|
|
||||||
|
|
||||||
class PaintEnvironment(BaseEnvironment):
|
class PaintEnvironment(SimpleEnvironment):
|
||||||
def __init__(self, env_id: str) -> None:
|
def __init__(self, env_id: str) -> None:
|
||||||
super().__init__(env_id)
|
super().__init__(env_id)
|
||||||
self.is_run = False
|
self.is_run = False
|
||||||
|
|||||||
@@ -6,8 +6,12 @@ import sqlite3
|
|||||||
import asyncio
|
import asyncio
|
||||||
from typing import Any,List,Dict
|
from typing import Any,List,Dict
|
||||||
import chardet
|
import chardet
|
||||||
from ..agent.agent_base import AgentMsg,AgentTodo,AgentPrompt,AgentTodoResult
|
|
||||||
from ..agent.ai_function import AIFunction,SimpleAIFunction, SimpleAIOperation
|
from ..proto.agent_task import *
|
||||||
|
from ..proto.ai_function import *
|
||||||
|
from ..proto.compute_task import *
|
||||||
|
from ..agent.agent_base import *
|
||||||
|
|
||||||
from ..storage.storage import AIStorage,ResourceLocation
|
from ..storage.storage import AIStorage,ResourceLocation
|
||||||
from .environment import SimpleEnvironment, CompositeEnvironment
|
from .environment import SimpleEnvironment, CompositeEnvironment
|
||||||
|
|
||||||
@@ -289,16 +293,16 @@ class WorkspaceEnvironment(CompositeEnvironment):
|
|||||||
def get_prompt(self) -> AgentMsg:
|
def get_prompt(self) -> AgentMsg:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_role_prompt(self,role_id:str) -> AgentPrompt:
|
def get_role_prompt(self,role_id:str) -> LLMPrompt:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_do_prompt(self,todo:AgentTodo=None)->AgentPrompt:
|
def get_do_prompt(self,todo:AgentTodo=None)->LLMPrompt:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# result mean: list[op_error_str],have_error
|
# result mean: list[op_error_str],have_error
|
||||||
async def exec_op_list(self,oplist:List,agent_id:str)->tuple[List[str],bool]:
|
async def exec_op_list(self,oplist:List,agent_id:str)->tuple[List[str],bool]:
|
||||||
result_str = "op list is none"
|
result_str = "op list is none"
|
||||||
if oplist is None:
|
if oplist is None or len(oplist) == 0:
|
||||||
return None,False
|
return None,False
|
||||||
|
|
||||||
result_str = []
|
result_str = []
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ from asyncio import Queue
|
|||||||
|
|
||||||
from ..proto.compute_task import *
|
from ..proto.compute_task import *
|
||||||
from ..knowledge import ObjectID
|
from ..knowledge import ObjectID
|
||||||
from ..agent.agent_base import AgentPrompt
|
|
||||||
from .compute_node import ComputeNode
|
from .compute_node import ComputeNode
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -106,6 +106,9 @@ class ComputeKernel:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def llm_num_tokens_from_text(text:str,model:str) -> int:
|
def llm_num_tokens_from_text(text:str,model:str) -> int:
|
||||||
|
if model is None:
|
||||||
|
model = "gpt4"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
encoding = tiktoken.encoding_for_model(model)
|
encoding = tiktoken.encoding_for_model(model)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
@@ -115,9 +118,12 @@ class ComputeKernel:
|
|||||||
token_count = len(encoding.encode(text))
|
token_count = len(encoding.encode(text))
|
||||||
return token_count
|
return token_count
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def llm_num_tokens(prompt: LLMPrompt, model_name: str = None) -> int:
|
||||||
|
return ComputeKernel.llm_num_tokens_from_text(prompt.as_str(), model_name)
|
||||||
|
|
||||||
# friendly interface for use:
|
# friendly interface for use:
|
||||||
def llm_completion(self, prompt: AgentPrompt, resp_mode:str="text",mode_name: Optional[str] = None, max_token: int = 0,inner_functions = None):
|
def llm_completion(self, prompt: LLMPrompt, resp_mode:str="text",mode_name: Optional[str] = None, max_token: int = 0,inner_functions = None):
|
||||||
# craete a llm_work_task ,push on queue's end
|
# craete a llm_work_task ,push on queue's end
|
||||||
# then task_schedule would run this task.(might schedule some work_task to another host)
|
# then task_schedule would run this task.(might schedule some work_task to another host)
|
||||||
task_req = ComputeTask()
|
task_req = ComputeTask()
|
||||||
@@ -153,7 +159,7 @@ class ComputeKernel:
|
|||||||
return time_out_result
|
return time_out_result
|
||||||
|
|
||||||
|
|
||||||
async def do_llm_completion(self, prompt: AgentPrompt,resp_mode:str="text", mode_name: Optional[str]=None, max_token:int=0, inner_functions=None, timeout=60) -> str:
|
async def do_llm_completion(self, prompt: LLMPrompt,resp_mode:str="text", mode_name: Optional[str]=None, max_token:int=0, inner_functions=None, timeout=60) -> str:
|
||||||
task_req = self.llm_completion(prompt, resp_mode,mode_name, max_token,inner_functions)
|
task_req = self.llm_completion(prompt, resp_mode,mode_name, max_token,inner_functions)
|
||||||
return await self._wait_task(task_req, timeout)
|
return await self._wait_task(task_req, timeout)
|
||||||
|
|
||||||
|
|||||||
@@ -232,9 +232,4 @@ class AgentMsg:
|
|||||||
def get_quote_msg_id(self) -> str:
|
def get_quote_msg_id(self) -> str:
|
||||||
return self.quote_msg_id
|
return self.quote_msg_id
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def parse_function_call(cls,func_string:str):
|
|
||||||
str_list = shlex.split(func_string)
|
|
||||||
func_name = str_list[0]
|
|
||||||
params = str_list[1:]
|
|
||||||
return func_name, params
|
|
||||||
|
|||||||
@@ -0,0 +1,221 @@
|
|||||||
|
|
||||||
|
import datetime
|
||||||
|
import time
|
||||||
|
|
||||||
|
from anyio import Path
|
||||||
|
|
||||||
|
|
||||||
|
class AgentTodoResult:
|
||||||
|
TODO_RESULT_CODE_OK = 0,
|
||||||
|
TODO_RESULT_CODE_LLM_ERROR = 1,
|
||||||
|
TODO_RESULT_CODE_EXEC_OP_ERROR = 2
|
||||||
|
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.result_code = AgentTodoResult.TODO_RESULT_CODE_OK
|
||||||
|
self.result_str = None
|
||||||
|
self.error_str = None
|
||||||
|
self.op_list = None
|
||||||
|
|
||||||
|
def to_dict(self) -> dict:
|
||||||
|
result = {}
|
||||||
|
result["result_code"] = self.result_code
|
||||||
|
result["result_str"] = self.result_str
|
||||||
|
result["error_str"] = self.error_str
|
||||||
|
result["op_list"] = self.op_list
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class AgentTodo:
|
||||||
|
TODO_STATE_WAIT_ASSIGN = "wait_assign"
|
||||||
|
TODO_STATE_INIT = "init"
|
||||||
|
|
||||||
|
TODO_STATE_PENDING = "pending"
|
||||||
|
TODO_STATE_WAITING_CHECK = "wait_check"
|
||||||
|
TODO_STATE_EXEC_FAILED = "exec_failed"
|
||||||
|
TDDO_STATE_CHECKFAILED = "check_failed"
|
||||||
|
|
||||||
|
TODO_STATE_CASNCEL = "cancel"
|
||||||
|
TODO_STATE_DONE = "done"
|
||||||
|
TODO_STATE_EXPIRED = "expired"
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.todo_id = "todo#" + uuid.uuid4().hex
|
||||||
|
self.title = None
|
||||||
|
self.detail = None
|
||||||
|
self.todo_path = None # get parent todo,sub todo by path
|
||||||
|
#self.parent = None
|
||||||
|
self.create_time = time.time()
|
||||||
|
|
||||||
|
self.state = "wait_assign"
|
||||||
|
self.worker = None
|
||||||
|
self.checker = None
|
||||||
|
self.createor = None
|
||||||
|
|
||||||
|
self.need_check = True
|
||||||
|
self.due_date = time.time() + 3600 * 24 * 2
|
||||||
|
self.last_do_time = None
|
||||||
|
self.last_check_time = None
|
||||||
|
self.last_review_time = None
|
||||||
|
|
||||||
|
self.depend_todo_ids = []
|
||||||
|
self.sub_todos = {}
|
||||||
|
|
||||||
|
self.result : AgentTodoResult = None
|
||||||
|
self.last_check_result = None
|
||||||
|
self.retry_count = 0
|
||||||
|
self.raw_obj = None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls,json_obj:dict) -> 'AgentTodo':
|
||||||
|
todo = AgentTodo()
|
||||||
|
if json_obj.get("id") is not None:
|
||||||
|
todo.todo_id = json_obj.get("id")
|
||||||
|
|
||||||
|
todo.title = json_obj.get("title")
|
||||||
|
todo.state = json_obj.get("state")
|
||||||
|
create_time = json_obj.get("create_time")
|
||||||
|
if create_time:
|
||||||
|
todo.create_time = datetime.fromisoformat(create_time).timestamp()
|
||||||
|
|
||||||
|
todo.detail = json_obj.get("detail")
|
||||||
|
due_date = json_obj.get("due_date")
|
||||||
|
if due_date:
|
||||||
|
todo.due_date = datetime.fromisoformat(due_date).timestamp()
|
||||||
|
|
||||||
|
last_do_time = json_obj.get("last_do_time")
|
||||||
|
if last_do_time:
|
||||||
|
todo.last_do_time = datetime.fromisoformat(last_do_time).timestamp()
|
||||||
|
last_check_time = json_obj.get("last_check_time")
|
||||||
|
if last_check_time:
|
||||||
|
todo.last_check_time = datetime.fromisoformat(last_check_time).timestamp()
|
||||||
|
last_review_time = json_obj.get("last_review_time")
|
||||||
|
if last_review_time:
|
||||||
|
todo.last_review_time = datetime.fromisoformat(last_review_time).timestamp()
|
||||||
|
|
||||||
|
todo.depend_todo_ids = json_obj.get("depend_todo_ids")
|
||||||
|
todo.need_check = json_obj.get("need_check")
|
||||||
|
#todo.result = json_obj.get("result")
|
||||||
|
#todo.last_check_result = json_obj.get("last_check_result")
|
||||||
|
todo.worker = json_obj.get("worker")
|
||||||
|
todo.checker = json_obj.get("checker")
|
||||||
|
todo.createor = json_obj.get("createor")
|
||||||
|
if json_obj.get("retry_count"):
|
||||||
|
todo.retry_count = json_obj.get("retry_count")
|
||||||
|
|
||||||
|
todo.raw_obj = json_obj
|
||||||
|
|
||||||
|
return todo
|
||||||
|
|
||||||
|
def to_dict(self) -> dict:
|
||||||
|
if self.raw_obj:
|
||||||
|
result = self.raw_obj
|
||||||
|
else:
|
||||||
|
result = {}
|
||||||
|
|
||||||
|
result["id"] = self.todo_id
|
||||||
|
#result["parent_id"] = self.parent_id
|
||||||
|
result["title"] = self.title
|
||||||
|
result["state"] = self.state
|
||||||
|
result["create_time"] = datetime.fromtimestamp(self.create_time).isoformat()
|
||||||
|
result["detail"] = self.detail
|
||||||
|
result["due_date"] = datetime.fromtimestamp(self.due_date).isoformat()
|
||||||
|
result["last_do_time"] = datetime.fromtimestamp(self.last_do_time).isoformat() if self.last_do_time else None
|
||||||
|
result["last_check_time"] = datetime.fromtimestamp(self.last_check_time).isoformat() if self.last_check_time else None
|
||||||
|
result["last_review_time"] = datetime.fromtimestamp(self.last_review_time).isoformat() if self.last_review_time else None
|
||||||
|
result["depend_todo_ids"] = self.depend_todo_ids
|
||||||
|
result["need_check"] = self.need_check
|
||||||
|
result["worker"] = self.worker
|
||||||
|
result["checker"] = self.checker
|
||||||
|
result["createor"] = self.createor
|
||||||
|
result["retry_count"] = self.retry_count
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
def can_check(self)->bool:
|
||||||
|
if self.state != AgentTodo.TODO_STATE_WAITING_CHECK:
|
||||||
|
return False
|
||||||
|
|
||||||
|
now = datetime.now().timestamp()
|
||||||
|
if self.last_check_time:
|
||||||
|
time_diff = now - self.last_check_time
|
||||||
|
if time_diff < 60*15:
|
||||||
|
logger.info(f"todo {self.title} is already checked, ignore")
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def can_do(self) -> bool:
|
||||||
|
match self.state:
|
||||||
|
case AgentTodo.TODO_STATE_DONE:
|
||||||
|
logger.info(f"todo {self.title} is done, ignore")
|
||||||
|
return False
|
||||||
|
case AgentTodo.TODO_STATE_CASNCEL:
|
||||||
|
logger.info(f"todo {self.title} is cancel, ignore")
|
||||||
|
return False
|
||||||
|
case AgentTodo.TODO_STATE_EXPIRED:
|
||||||
|
logger.info(f"todo {self.title} is expired, ignore")
|
||||||
|
return False
|
||||||
|
case AgentTodo.TODO_STATE_EXEC_FAILED:
|
||||||
|
if self.retry_count > 3:
|
||||||
|
logger.info(f"todo {self.title} retry count ({self.retry_count}) is too many, ignore")
|
||||||
|
return False
|
||||||
|
|
||||||
|
now = datetime.now().timestamp()
|
||||||
|
time_diff = self.due_date - now
|
||||||
|
if time_diff < 0:
|
||||||
|
logger.info(f"todo {self.title} is expired, ignore")
|
||||||
|
self.state = AgentTodo.TODO_STATE_EXPIRED
|
||||||
|
return False
|
||||||
|
|
||||||
|
if time_diff > 7*24*3600:
|
||||||
|
logger.info(f"todo {self.title} is far before due date, ignore")
|
||||||
|
return False
|
||||||
|
|
||||||
|
if self.last_do_time:
|
||||||
|
time_diff = now - self.last_do_time
|
||||||
|
if time_diff < 60*15:
|
||||||
|
logger.info(f"todo {self.title} is already do ignore")
|
||||||
|
return False
|
||||||
|
|
||||||
|
logger.info(f"todo {self.title} can do.")
|
||||||
|
return True
|
||||||
|
|
||||||
|
class AgentTask:
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.task_id : str = "task#" + uuid.uuid4().hex
|
||||||
|
self.task_path : Path = None # get parent todo,sub todo by path
|
||||||
|
self.title = None
|
||||||
|
self.detail = None
|
||||||
|
|
||||||
|
self.create_time = time.time()
|
||||||
|
|
||||||
|
self.state = "wait_assign"
|
||||||
|
self.worker = None
|
||||||
|
self.createor = None
|
||||||
|
|
||||||
|
self.due_date = time.time() + 3600 * 24 * 2
|
||||||
|
self.depend_task_ids = []
|
||||||
|
self.step_todos = {}
|
||||||
|
|
||||||
|
self.last_plan_time = None
|
||||||
|
self.last_check_time = None
|
||||||
|
#self.last_review_time = None
|
||||||
|
|
||||||
|
self.result : LLMResult = None
|
||||||
|
self.last_check_result = None
|
||||||
|
self.retry_count = 0
|
||||||
|
self.raw_obj = None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class AgentWorkLog:
|
||||||
|
def __init__(self) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class AgentReport:
|
||||||
|
def __init__(self) -> None:
|
||||||
|
pass
|
||||||
@@ -73,7 +73,7 @@ class AIFunction:
|
|||||||
#def load_from_config(self,config:dict) -> bool:
|
#def load_from_config(self,config:dict) -> bool:
|
||||||
# pass
|
# pass
|
||||||
|
|
||||||
class FunctionItem:
|
class ActionItem:
|
||||||
def __init__(self,name,args) -> None:
|
def __init__(self,name,args) -> None:
|
||||||
self.name = name
|
self.name = name
|
||||||
self.args = args
|
self.args = args
|
||||||
@@ -1,11 +1,20 @@
|
|||||||
|
|
||||||
|
import copy
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
import json
|
||||||
|
import shlex
|
||||||
import uuid
|
import uuid
|
||||||
import time
|
import time
|
||||||
from typing import Union
|
from typing import List, Union
|
||||||
|
from ..proto.ai_function import *
|
||||||
from ..knowledge import ObjectID
|
from ..knowledge import ObjectID
|
||||||
from ..storage.storage import AIStorage
|
from ..storage.storage import AIStorage
|
||||||
|
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class ComputeTaskResultCode(Enum):
|
class ComputeTaskResultCode(Enum):
|
||||||
OK = 0
|
OK = 0
|
||||||
TIMEOUT = 1
|
TIMEOUT = 1
|
||||||
@@ -31,6 +40,164 @@ class ComputeTaskType(Enum):
|
|||||||
TEXT_EMBEDDING ="text_embedding"
|
TEXT_EMBEDDING ="text_embedding"
|
||||||
IMAGE_EMBEDDING ="image_embedding"
|
IMAGE_EMBEDDING ="image_embedding"
|
||||||
|
|
||||||
|
class LLMPrompt:
|
||||||
|
def __init__(self,prompt_str = None) -> None:
|
||||||
|
self.messages = []
|
||||||
|
if prompt_str:
|
||||||
|
self.messages.append({"role":"user","content":prompt_str})
|
||||||
|
self.system_message = None
|
||||||
|
|
||||||
|
def as_str(self)->str:
|
||||||
|
result_str = ""
|
||||||
|
if self.system_message:
|
||||||
|
result_str += self.system_message.get("role") + ":" + self.system_message.get("content") + "\n"
|
||||||
|
if self.messages:
|
||||||
|
for msg in self.messages:
|
||||||
|
result_str += msg.get("role") + ":" + msg.get("content") + "\n"
|
||||||
|
|
||||||
|
return result_str
|
||||||
|
|
||||||
|
def to_message_list(self):
|
||||||
|
result = []
|
||||||
|
if self.system_message:
|
||||||
|
result.append(self.system_message)
|
||||||
|
result.extend(self.messages)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def append(self,prompt:'LLMPrompt'):
|
||||||
|
if prompt is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
if prompt.system_message is not None:
|
||||||
|
if self.system_message is None:
|
||||||
|
self.system_message = copy.deepcopy(prompt.system_message)
|
||||||
|
else:
|
||||||
|
self.system_message["content"] += prompt.system_message.get("content")
|
||||||
|
|
||||||
|
self.messages.extend(prompt.messages)
|
||||||
|
|
||||||
|
def load_from_config(self,config:list) -> bool:
|
||||||
|
if isinstance(config,list) is not True:
|
||||||
|
logger.error("prompt is not list!")
|
||||||
|
return False
|
||||||
|
self.messages = []
|
||||||
|
for msg in config:
|
||||||
|
if msg.get("content"):
|
||||||
|
if msg.get("role") == "system":
|
||||||
|
self.system_message = msg
|
||||||
|
else:
|
||||||
|
self.messages.append(msg)
|
||||||
|
else:
|
||||||
|
logger.error("prompt message has no content!")
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
class LLMResultStates(Enum):
|
||||||
|
IGNORE = "ignore"
|
||||||
|
OK = "ok" # process done
|
||||||
|
ERROR = "error"
|
||||||
|
|
||||||
|
class LLMResult:
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.state : str = LLMResultStates.IGNORE
|
||||||
|
self.compute_error_str = None
|
||||||
|
self.resp : str = "" # llm say:
|
||||||
|
self.raw_result = None # raw result from compute kernel
|
||||||
|
self.inner_functions : List[AIFunction] = []
|
||||||
|
self.action_list : List[ActionItem] = [] # op_list is a optimize design for saving token
|
||||||
|
|
||||||
|
#self.post_msgs : List[AgentMsg] = [] # move to op_list
|
||||||
|
# self.send_msgs : List[AgentMsg] = [] # move to op_list
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_error_str(self,error_str:str) -> 'LLMResult':
|
||||||
|
r = LLMResult()
|
||||||
|
r.state = "error"
|
||||||
|
r.compute_error_str = error_str
|
||||||
|
return r
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_json_str(self,llm_json_str:str) -> 'LLMResult':
|
||||||
|
r = LLMResult()
|
||||||
|
if llm_json_str is None:
|
||||||
|
r.state = LLMResultStates.IGNORE
|
||||||
|
return r
|
||||||
|
if llm_json_str == "**IGNORE**":
|
||||||
|
r.state = LLMResultStates.IGNORE
|
||||||
|
return r
|
||||||
|
|
||||||
|
llm_json = json.loads(llm_json_str)
|
||||||
|
r.resp = llm_json.get("resp")
|
||||||
|
r.raw_result = llm_json
|
||||||
|
r.action_list = llm_json.get("actions")
|
||||||
|
|
||||||
|
return r
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def parse_action(cls,func_string:str):
|
||||||
|
str_list = shlex.split(func_string)
|
||||||
|
func_name = str_list[0]
|
||||||
|
params = str_list[1:]
|
||||||
|
return func_name, params
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_str(self,llm_result_str:str,valid_func:List[str]=None) -> 'LLMResult':
|
||||||
|
r = LLMResult()
|
||||||
|
|
||||||
|
if llm_result_str is None:
|
||||||
|
r.state = LLMResultStates.IGNORE
|
||||||
|
return r
|
||||||
|
if llm_result_str == "**IGNORE**":
|
||||||
|
r.state = LLMResultStates.IGNORE
|
||||||
|
return r
|
||||||
|
|
||||||
|
if llm_result_str[0] == "{":
|
||||||
|
return LLMResult.from_json_str(llm_result_str)
|
||||||
|
|
||||||
|
lines = llm_result_str.splitlines()
|
||||||
|
is_need_wait = False
|
||||||
|
|
||||||
|
def check_args(action_item:ActionItem):
|
||||||
|
match action_item.name:
|
||||||
|
case "post_msg":# /post_msg $target_id
|
||||||
|
if len(action_item.args) != 1:
|
||||||
|
return False
|
||||||
|
|
||||||
|
new_msg = AgentMsg()
|
||||||
|
target_id = action_item.args[0]
|
||||||
|
msg_content = action_item.body
|
||||||
|
new_msg.set("",target_id,msg_content)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
current_action : ActionItem = None
|
||||||
|
for line in lines:
|
||||||
|
if line.startswith("##/"):
|
||||||
|
if current_action:
|
||||||
|
if check_args(current_action) is False:
|
||||||
|
r.resp += current_action.dumps()
|
||||||
|
else:
|
||||||
|
r.action_list.append(current_action)
|
||||||
|
|
||||||
|
action_name,action_args = LLMResult.parse_action(line[3:])
|
||||||
|
current_action = ActionItem(action_name,action_args)
|
||||||
|
else:
|
||||||
|
if current_action:
|
||||||
|
current_action.append_body(line + "\n")
|
||||||
|
else:
|
||||||
|
r.resp += line + "\n"
|
||||||
|
|
||||||
|
if current_action:
|
||||||
|
if check_args(current_action) is False:
|
||||||
|
r.resp += current_action.dumps()
|
||||||
|
else:
|
||||||
|
r.action_list.append(current_action)
|
||||||
|
return r
|
||||||
|
|
||||||
class ComputeTask:
|
class ComputeTask:
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
@@ -140,3 +307,5 @@ class ComputeTaskResult:
|
|||||||
self.task_id = task.task_id
|
self.task_id = task.task_id
|
||||||
self.callchain_id = task.callchain_id
|
self.callchain_id = task.callchain_id
|
||||||
task.result = self
|
task.result = self
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,426 +0,0 @@
|
|||||||
import logging
|
|
||||||
import os
|
|
||||||
import pathlib
|
|
||||||
import shutil
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
import re
|
|
||||||
import time
|
|
||||||
import ast
|
|
||||||
from concurrent.futures import ThreadPoolExecutor
|
|
||||||
from hashlib import md5
|
|
||||||
from typing import Optional, Union, List, Tuple
|
|
||||||
from generic_escape import GenericEscape
|
|
||||||
|
|
||||||
from aios_kernel import AIStorage
|
|
||||||
|
|
||||||
try:
|
|
||||||
import docker
|
|
||||||
except ImportError:
|
|
||||||
docker = None
|
|
||||||
|
|
||||||
CODE_BLOCK_PATTERN = r"```[ \t]*(\w+)?[ \t]*\r?\n(.*?)\r?\n[ \t]*```"
|
|
||||||
UNKNOWN = "unknown"
|
|
||||||
TIMEOUT_MSG = "Timeout"
|
|
||||||
DEFAULT_TIMEOUT = 600
|
|
||||||
WIN32 = sys.platform == "win32"
|
|
||||||
PATH_SEPARATOR = WIN32 and "\\" or "/"
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
BUILT_IN_MODULES = set(
|
|
||||||
[
|
|
||||||
"sys",
|
|
||||||
"os",
|
|
||||||
"math",
|
|
||||||
"random",
|
|
||||||
"datetime",
|
|
||||||
"json",
|
|
||||||
"re",
|
|
||||||
"subprocess",
|
|
||||||
"time",
|
|
||||||
"threading",
|
|
||||||
"logging",
|
|
||||||
"collections",
|
|
||||||
"itertools",
|
|
||||||
"functools",
|
|
||||||
"operator",
|
|
||||||
"pathlib",
|
|
||||||
"shutil",
|
|
||||||
"tempfile",
|
|
||||||
"pickle",
|
|
||||||
"io",
|
|
||||||
"argparse",
|
|
||||||
"typing",
|
|
||||||
"unittest",
|
|
||||||
"contextlib",
|
|
||||||
"abc",
|
|
||||||
"heapq",
|
|
||||||
"bisect",
|
|
||||||
"copy",
|
|
||||||
"decimal",
|
|
||||||
"fractions",
|
|
||||||
"hashlib",
|
|
||||||
"secrets",
|
|
||||||
"statistics",
|
|
||||||
"difflib",
|
|
||||||
"doctest",
|
|
||||||
"enum",
|
|
||||||
"inspect",
|
|
||||||
"traceback",
|
|
||||||
"weakref",
|
|
||||||
"gc",
|
|
||||||
"mmap",
|
|
||||||
"msvcrt",
|
|
||||||
"winreg",
|
|
||||||
"array",
|
|
||||||
"audioop",
|
|
||||||
"binascii",
|
|
||||||
"cProfile",
|
|
||||||
"concurrent.futures",
|
|
||||||
"configparser",
|
|
||||||
"csv",
|
|
||||||
"ctypes",
|
|
||||||
"dateutil",
|
|
||||||
"dis",
|
|
||||||
"fnmatch",
|
|
||||||
"getopt",
|
|
||||||
"glob",
|
|
||||||
"gzip",
|
|
||||||
"pdb",
|
|
||||||
"pprint",
|
|
||||||
"profile",
|
|
||||||
"pstats",
|
|
||||||
"queue",
|
|
||||||
"socket",
|
|
||||||
"sqlite3",
|
|
||||||
"ssl",
|
|
||||||
"struct",
|
|
||||||
"tarfile",
|
|
||||||
"telnetlib",
|
|
||||||
"timeit",
|
|
||||||
"tokenize",
|
|
||||||
"uuid",
|
|
||||||
"xml",
|
|
||||||
"zipfile",
|
|
||||||
"zlib",
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def get_imports(code: str) -> List[str]:
|
|
||||||
root = ast.parse(code)
|
|
||||||
|
|
||||||
imports = []
|
|
||||||
for node in ast.iter_child_nodes(root):
|
|
||||||
if isinstance(node, ast.Import):
|
|
||||||
module_names = [alias.name for alias in node.names]
|
|
||||||
elif isinstance(node, ast.ImportFrom):
|
|
||||||
module_names = [node.module]
|
|
||||||
else:
|
|
||||||
continue
|
|
||||||
|
|
||||||
for name in module_names:
|
|
||||||
# Exclude built-in modules
|
|
||||||
if name not in BUILT_IN_MODULES:
|
|
||||||
imports.append(name)
|
|
||||||
|
|
||||||
return imports
|
|
||||||
|
|
||||||
|
|
||||||
def write_requirements(code: str, requirements_filepath: str):
|
|
||||||
imports = get_imports(code)
|
|
||||||
|
|
||||||
with open(requirements_filepath, "w") as file:
|
|
||||||
for module in imports:
|
|
||||||
file.write(module + "\n")
|
|
||||||
|
|
||||||
|
|
||||||
def _cmd(lang):
|
|
||||||
if lang.startswith("python") or lang in ["bash", "sh", "powershell"]:
|
|
||||||
return lang
|
|
||||||
if lang in ["shell"]:
|
|
||||||
return "sh"
|
|
||||||
if lang in ["ps1"]:
|
|
||||||
return "powershell"
|
|
||||||
raise NotImplementedError(f"{lang} not recognized in code execution")
|
|
||||||
|
|
||||||
|
|
||||||
def create_runner(code: str, timeout: int = 30) -> str:
|
|
||||||
"""
|
|
||||||
Create a Python script that runs the code and prints the output
|
|
||||||
"""
|
|
||||||
code = GenericEscape().escape(code)
|
|
||||||
# Create a runner script
|
|
||||||
runner = f"""
|
|
||||||
import os
|
|
||||||
import subprocess
|
|
||||||
|
|
||||||
my_env = os.environ.copy()
|
|
||||||
my_env["PYTHONIOENCODING"] = "utf-8"
|
|
||||||
|
|
||||||
process = subprocess.Popen(
|
|
||||||
f"python -i -q -u".split(),
|
|
||||||
stdin=subprocess.PIPE,
|
|
||||||
stdout=subprocess.PIPE,
|
|
||||||
stderr=subprocess.PIPE,
|
|
||||||
text=True,
|
|
||||||
bufsize=0,
|
|
||||||
universal_newlines=True,
|
|
||||||
env=my_env
|
|
||||||
)
|
|
||||||
|
|
||||||
process.stdin.write("{code}" + "\\n")
|
|
||||||
process.stdin.write("exit()\\n")
|
|
||||||
process.stdin.flush()
|
|
||||||
|
|
||||||
try:
|
|
||||||
process.wait({timeout})
|
|
||||||
except Exception as e:
|
|
||||||
process.terminate()
|
|
||||||
|
|
||||||
for line in iter(process.stdout.readline, ""):
|
|
||||||
print(line)
|
|
||||||
|
|
||||||
for line in iter(process.stderr.readline, ""):
|
|
||||||
if line.startswith(">>>"):
|
|
||||||
continue
|
|
||||||
print(line)
|
|
||||||
"""
|
|
||||||
return runner
|
|
||||||
|
|
||||||
|
|
||||||
def _run_cmd(cmd: [str], work_dir: str, timeout: int) -> str:
|
|
||||||
if WIN32:
|
|
||||||
logger.warning("SIGALRM is not supported on Windows. No timeout will be enforced.")
|
|
||||||
result = subprocess.run(
|
|
||||||
cmd,
|
|
||||||
cwd=work_dir,
|
|
||||||
capture_output=True,
|
|
||||||
text=True,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
with ThreadPoolExecutor(max_workers=1) as executor:
|
|
||||||
future = executor.submit(
|
|
||||||
subprocess.run,
|
|
||||||
cmd,
|
|
||||||
cwd=work_dir,
|
|
||||||
capture_output=True,
|
|
||||||
text=True,
|
|
||||||
)
|
|
||||||
result = future.result(timeout=timeout)
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
def execute_code(
|
|
||||||
code: Optional[str] = None,
|
|
||||||
timeout: Optional[int] = None,
|
|
||||||
filename: Optional[str] = None,
|
|
||||||
work_dir: Optional[str] = None,
|
|
||||||
use_docker: Optional[Union[List[str], str, bool]] = None,
|
|
||||||
lang: Optional[str] = "python",
|
|
||||||
) -> Tuple[int, str]:
|
|
||||||
"""Execute code in a docker container.
|
|
||||||
This function is not tested on MacOS.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
code (Optional, str): The code to execute.
|
|
||||||
If None, the code from the file specified by filename will be executed.
|
|
||||||
Either code or filename must be provided.
|
|
||||||
timeout (Optional, int): The maximum execution time in seconds.
|
|
||||||
If None, a default timeout will be used. The default timeout is 600 seconds. On Windows, the timeout is not enforced when use_docker=False.
|
|
||||||
filename (Optional, str): The file name to save the code or where the code is stored when `code` is None.
|
|
||||||
If None, a file with a randomly generated name will be created.
|
|
||||||
The randomly generated file will be deleted after execution.
|
|
||||||
The file name must be a relative path. Relative paths are relative to the working directory.
|
|
||||||
work_dir (Optional, str): The working directory for the code execution.
|
|
||||||
If None, a default working directory will be used.
|
|
||||||
The default working directory is the "extensions" directory under
|
|
||||||
"path_to_autogen".
|
|
||||||
use_docker (Optional, list, str or bool): The docker image to use for code execution.
|
|
||||||
If a list or a str of image name(s) is provided, the code will be executed in a docker container
|
|
||||||
with the first image successfully pulled.
|
|
||||||
If None, False or empty, the code will be executed in the current environment.
|
|
||||||
Default is None, which will be converted into an empty list when docker package is available.
|
|
||||||
Expected behaviour:
|
|
||||||
- If `use_docker` is explicitly set to True and the docker package is available, the code will run in a Docker container.
|
|
||||||
- If `use_docker` is explicitly set to True but the Docker package is missing, an error will be raised.
|
|
||||||
- If `use_docker` is not set (i.e., left default to None) and the Docker package is not available, a warning will be displayed, but the code will run natively.
|
|
||||||
If the code is executed in the current environment,
|
|
||||||
the code must be trusted.
|
|
||||||
lang (Optional, str): The language of the code. Default is "python".
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
int: 0 if the code executes successfully.
|
|
||||||
str: The error message if the code fails to execute; the stdout otherwise.
|
|
||||||
"""
|
|
||||||
if all((code is None, filename is None)):
|
|
||||||
error_msg = f"Either {code=} or {filename=} must be provided."
|
|
||||||
logger.error(error_msg)
|
|
||||||
raise AssertionError(error_msg)
|
|
||||||
|
|
||||||
# Warn if use_docker was unspecified (or None), and cannot be provided (the default).
|
|
||||||
# In this case the current behavior is to fall back to run natively, but this behavior
|
|
||||||
# is subject to change.
|
|
||||||
if use_docker is None:
|
|
||||||
if docker is None:
|
|
||||||
use_docker = False
|
|
||||||
logger.warning(
|
|
||||||
"execute_code was called without specifying a value for use_docker. Since the python docker package is not available, code will be run natively. Note: this fallback behavior is subject to change"
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
# Default to true
|
|
||||||
use_docker = True
|
|
||||||
|
|
||||||
timeout = timeout or DEFAULT_TIMEOUT
|
|
||||||
original_filename = filename
|
|
||||||
if WIN32 and lang in ["sh", "shell"] and (not use_docker):
|
|
||||||
lang = "ps1"
|
|
||||||
if filename is None:
|
|
||||||
code_hash = md5(code.encode()).hexdigest()
|
|
||||||
# create a file with a automatically generated name
|
|
||||||
filename = f"tmp_code_{code_hash}.{'py' if lang.startswith('python') else lang}"
|
|
||||||
if work_dir is None:
|
|
||||||
WORKING_DIR = os.path.join(AIStorage.get_instance().get_myai_dir(), "tmp_code")
|
|
||||||
pathlib.Path(WORKING_DIR).mkdir(exist_ok=True)
|
|
||||||
work_dir = os.path.join(WORKING_DIR, code_hash)
|
|
||||||
pathlib.Path(work_dir).mkdir(exist_ok=True)
|
|
||||||
filepath = os.path.join(work_dir, filename)
|
|
||||||
file_dir = os.path.dirname(filepath)
|
|
||||||
os.makedirs(file_dir, exist_ok=True)
|
|
||||||
if code is not None:
|
|
||||||
write_requirements(code, os.path.join(file_dir, "requirements.txt"))
|
|
||||||
code = create_runner(code, 30)
|
|
||||||
with open(filepath, "w", encoding="utf-8") as fout:
|
|
||||||
fout.write(code)
|
|
||||||
|
|
||||||
|
|
||||||
# check if already running in a docker container
|
|
||||||
in_docker_container = os.path.exists("/.dockerenv")
|
|
||||||
if not use_docker or in_docker_container:
|
|
||||||
try:
|
|
||||||
env_cmd = ["python", "-m", "venv", os.path.join(file_dir, "venv")]
|
|
||||||
_run_cmd(env_cmd, file_dir, timeout)
|
|
||||||
if WIN32:
|
|
||||||
venv_path = os.path.join(file_dir, "venv", "Scripts")
|
|
||||||
else:
|
|
||||||
venv_path = os.path.join(file_dir, "venv", "bin")
|
|
||||||
pip_cmd = [os.path.join(venv_path, "python"), "-m", "pip", "install", "-r", "requirements.txt"]
|
|
||||||
_run_cmd(pip_cmd, file_dir, timeout)
|
|
||||||
# already running in a docker container
|
|
||||||
cmd = [
|
|
||||||
os.path.join(venv_path, "python"),
|
|
||||||
f".\\{filename}" if WIN32 else filename,
|
|
||||||
]
|
|
||||||
result = _run_cmd(cmd, file_dir, timeout)
|
|
||||||
except TimeoutError:
|
|
||||||
if original_filename is None:
|
|
||||||
shutil.rmtree(os.path.join(file_dir, "venv"))
|
|
||||||
os.remove(filepath)
|
|
||||||
os.remove(os.path.join(file_dir, "requirements.txt"))
|
|
||||||
try:
|
|
||||||
os.removedirs(file_dir)
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
return 1, TIMEOUT_MSG
|
|
||||||
if original_filename is None:
|
|
||||||
shutil.rmtree(os.path.join(file_dir, "venv"))
|
|
||||||
os.remove(filepath)
|
|
||||||
os.remove(os.path.join(file_dir, "requirements.txt"))
|
|
||||||
try:
|
|
||||||
os.removedirs(file_dir)
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
if result.returncode:
|
|
||||||
logs = result.stderr
|
|
||||||
if original_filename is None:
|
|
||||||
abs_path = str(pathlib.Path(filepath).absolute())
|
|
||||||
logs = logs.replace(str(abs_path), "").replace(filename, "")
|
|
||||||
else:
|
|
||||||
abs_path = str(pathlib.Path(work_dir).absolute()) + PATH_SEPARATOR
|
|
||||||
logs = logs.replace(str(abs_path), "")
|
|
||||||
else:
|
|
||||||
logs = result.stdout
|
|
||||||
return result.returncode, logs
|
|
||||||
|
|
||||||
# create a docker client
|
|
||||||
client = docker.from_env()
|
|
||||||
image_list = (
|
|
||||||
["python:3-alpine", "python:3", "python:3-windowsservercore"]
|
|
||||||
if use_docker is True
|
|
||||||
else [use_docker]
|
|
||||||
if isinstance(use_docker, str)
|
|
||||||
else use_docker
|
|
||||||
)
|
|
||||||
for image in image_list:
|
|
||||||
# check if the image exists
|
|
||||||
try:
|
|
||||||
client.images.get(image)
|
|
||||||
break
|
|
||||||
except docker.errors.ImageNotFound:
|
|
||||||
# pull the image
|
|
||||||
logger.info("Pulling image", image)
|
|
||||||
try:
|
|
||||||
client.images.pull(image, stream=True, decode=True)
|
|
||||||
break
|
|
||||||
except docker.errors.DockerException as e:
|
|
||||||
logger.error("Failed to pull image", image)
|
|
||||||
logger.exception(e)
|
|
||||||
# get a randomized str based on current time to wrap the exit code
|
|
||||||
exit_code_str = f"exitcode{time.time()}"
|
|
||||||
start_str = f'start{time.time()}'
|
|
||||||
abs_path = pathlib.Path(work_dir).absolute()
|
|
||||||
cmd = [
|
|
||||||
"sh",
|
|
||||||
"-c",
|
|
||||||
f"pip install --quiet -r requirements.txt; echo -n {start_str}; {_cmd(lang)} {filename}; exit_code=$?; echo -n {exit_code_str}; echo -n $exit_code; echo {exit_code_str};",
|
|
||||||
]
|
|
||||||
# create a docker container
|
|
||||||
container = client.containers.run(
|
|
||||||
image,
|
|
||||||
command=cmd,
|
|
||||||
working_dir="/workspace",
|
|
||||||
detach=True,
|
|
||||||
# get absolute path to the working directory
|
|
||||||
volumes={abs_path: {"bind": "/workspace", "mode": "rw"}},
|
|
||||||
)
|
|
||||||
start_time = time.time()
|
|
||||||
while container.status != "exited" and time.time() - start_time < timeout:
|
|
||||||
# Reload the container object
|
|
||||||
container.reload()
|
|
||||||
if container.status != "exited":
|
|
||||||
container.stop()
|
|
||||||
container.remove()
|
|
||||||
if original_filename is None:
|
|
||||||
os.remove(filepath)
|
|
||||||
return 1, TIMEOUT_MSG, image
|
|
||||||
# get the container logs
|
|
||||||
logs: str = container.logs().decode("utf-8").rstrip()
|
|
||||||
start_pos = logs.find(start_str)
|
|
||||||
if start_pos != -1:
|
|
||||||
logs = logs[start_pos + len(start_str):]
|
|
||||||
# # commit the image
|
|
||||||
# tag = filename.replace("/", "")
|
|
||||||
# container.commit(repository="python", tag=tag)
|
|
||||||
# remove the container
|
|
||||||
container.remove()
|
|
||||||
# check if the code executed successfully
|
|
||||||
exit_code = container.attrs["State"]["ExitCode"]
|
|
||||||
if exit_code == 0:
|
|
||||||
# extract the exit code from the logs
|
|
||||||
pattern = re.compile(f"{exit_code_str}(\\d+){exit_code_str}")
|
|
||||||
match = pattern.search(logs)
|
|
||||||
exit_code = 1 if match is None else int(match.group(1))
|
|
||||||
# remove the exit code from the logs
|
|
||||||
logs = logs if match is None else pattern.sub("", logs)
|
|
||||||
|
|
||||||
if original_filename is None:
|
|
||||||
os.remove(filepath)
|
|
||||||
os.remove(os.path.join(file_dir, "requirements.txt"))
|
|
||||||
os.removedirs(file_dir)
|
|
||||||
if exit_code:
|
|
||||||
logs = logs.replace(f"/workspace/{filename if original_filename is None else ''}", "")
|
|
||||||
# return the exit code, logs and image
|
|
||||||
return exit_code, logs
|
|
||||||
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
from typing import Dict
|
|
||||||
|
|
||||||
from aios_kernel.ai_function import AIFunction
|
|
||||||
from aios_kernel.code_interpreter import execute_code
|
|
||||||
|
|
||||||
|
|
||||||
class CodeInterpreterFunction(AIFunction):
|
|
||||||
def __init__(self):
|
|
||||||
self.func_id = "code_interpreter"
|
|
||||||
self.description = "execute python code"
|
|
||||||
|
|
||||||
def get_name(self) -> str:
|
|
||||||
return self.func_id
|
|
||||||
|
|
||||||
def get_description(self) -> str:
|
|
||||||
return self.description
|
|
||||||
|
|
||||||
def get_parameters(self) -> Dict:
|
|
||||||
return {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"code": {"type": "string", "description": "python code"}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async def execute(self, **kwargs) -> str:
|
|
||||||
code = kwargs.get("code")
|
|
||||||
ret_code, result = execute_code(code=code)
|
|
||||||
if ret_code == 0:
|
|
||||||
return result.strip()
|
|
||||||
else:
|
|
||||||
return result.strip()
|
|
||||||
|
|
||||||
def is_local(self) -> bool:
|
|
||||||
return True
|
|
||||||
|
|
||||||
def is_in_zone(self) -> bool:
|
|
||||||
return True
|
|
||||||
|
|
||||||
def is_ready_only(self) -> bool:
|
|
||||||
return False
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
import json
|
|
||||||
from typing import Dict
|
|
||||||
|
|
||||||
from aios_kernel.ai_function import AIFunction
|
|
||||||
from duckduckgo_search import AsyncDDGS
|
|
||||||
|
|
||||||
|
|
||||||
class DuckDuckGoTextSearchFunction(AIFunction):
|
|
||||||
def __init__(self):
|
|
||||||
self.name = "duckduckgo_text_search"
|
|
||||||
self.description = "Search text from duckduckgo.com"
|
|
||||||
self.region = "wt-wt"
|
|
||||||
self.safesearch = "moderate"
|
|
||||||
self.time = "y"
|
|
||||||
self.max_results = 5
|
|
||||||
|
|
||||||
def get_name(self) -> str:
|
|
||||||
return self.name
|
|
||||||
|
|
||||||
def get_description(self) -> str:
|
|
||||||
return self.description
|
|
||||||
|
|
||||||
def get_parameters(self) -> Dict:
|
|
||||||
return {"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"query": {"type": "string", "description": "The query to search for."}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async def execute(self, **kwargs) -> str:
|
|
||||||
query = kwargs.get("query")
|
|
||||||
|
|
||||||
async with AsyncDDGS() as ddgs:
|
|
||||||
results = [r async for r in ddgs.text(
|
|
||||||
query,
|
|
||||||
region=self.region,
|
|
||||||
safesearch=self.safesearch,
|
|
||||||
timelimit=self.time,
|
|
||||||
backend="api",
|
|
||||||
max_results=self.max_results
|
|
||||||
)]
|
|
||||||
|
|
||||||
return json.dumps(results)
|
|
||||||
|
|
||||||
def is_local(self) -> bool:
|
|
||||||
return True
|
|
||||||
|
|
||||||
def is_in_zone(self) -> bool:
|
|
||||||
return True
|
|
||||||
|
|
||||||
def is_ready_only(self) -> bool:
|
|
||||||
return False
|
|
||||||
@@ -1,493 +0,0 @@
|
|||||||
"""
|
|
||||||
Taken from: langchain
|
|
||||||
SQLAlchemy wrapper around a database.
|
|
||||||
"""
|
|
||||||
from __future__ import annotations
|
|
||||||
import os
|
|
||||||
|
|
||||||
import warnings
|
|
||||||
from typing import Any, Dict, Iterable, List, Literal, Optional, Sequence, Union
|
|
||||||
|
|
||||||
import sqlalchemy
|
|
||||||
from sqlalchemy import MetaData, Table, create_engine, inspect, select, text
|
|
||||||
from sqlalchemy.engine import Engine
|
|
||||||
from sqlalchemy.exc import ProgrammingError, SQLAlchemyError
|
|
||||||
from sqlalchemy.schema import CreateTable
|
|
||||||
from sqlalchemy.types import NullType
|
|
||||||
|
|
||||||
|
|
||||||
def get_from_env(key: str, env_key: str, default: Optional[str] = None) -> str:
|
|
||||||
"""Get a value from a dictionary or an environment variable."""
|
|
||||||
if env_key in os.environ and os.environ[env_key]:
|
|
||||||
return os.environ[env_key]
|
|
||||||
elif default is not None:
|
|
||||||
return default
|
|
||||||
else:
|
|
||||||
raise ValueError(
|
|
||||||
f"Did not find {key}, please add an environment variable"
|
|
||||||
f" `{env_key}` which contains it, or pass"
|
|
||||||
f" `{key}` as a named parameter."
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _format_index(index: sqlalchemy.engine.interfaces.ReflectedIndex) -> str:
|
|
||||||
return (
|
|
||||||
f'Name: {index["name"]}, Unique: {index["unique"]},'
|
|
||||||
f' Columns: {str(index["column_names"])}'
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def truncate_word(content: Any, *, length: int, suffix: str = "...") -> str:
|
|
||||||
"""
|
|
||||||
Truncate a string to a certain number of words, based on the max string
|
|
||||||
length.
|
|
||||||
"""
|
|
||||||
|
|
||||||
if not isinstance(content, str) or length <= 0:
|
|
||||||
return content
|
|
||||||
|
|
||||||
if len(content) <= length:
|
|
||||||
return content
|
|
||||||
|
|
||||||
return content[: length - len(suffix)].rsplit(" ", 1)[0] + suffix
|
|
||||||
|
|
||||||
|
|
||||||
class SQLDatabase:
|
|
||||||
"""SQLAlchemy wrapper around a database."""
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
engine: Engine,
|
|
||||||
schema: Optional[str] = None,
|
|
||||||
metadata: Optional[MetaData] = None,
|
|
||||||
ignore_tables: Optional[List[str]] = None,
|
|
||||||
include_tables: Optional[List[str]] = None,
|
|
||||||
sample_rows_in_table_info: int = 3,
|
|
||||||
indexes_in_table_info: bool = False,
|
|
||||||
custom_table_info: Optional[dict] = None,
|
|
||||||
view_support: bool = False,
|
|
||||||
max_string_length: int = 300,
|
|
||||||
):
|
|
||||||
"""Create engine from database URI."""
|
|
||||||
self._engine = engine
|
|
||||||
self._schema = schema
|
|
||||||
if include_tables and ignore_tables:
|
|
||||||
raise ValueError("Cannot specify both include_tables and ignore_tables")
|
|
||||||
|
|
||||||
self._inspector = inspect(self._engine)
|
|
||||||
|
|
||||||
# including view support by adding the views as well as tables to the all
|
|
||||||
# tables list if view_support is True
|
|
||||||
self._all_tables = set(
|
|
||||||
self._inspector.get_table_names(schema=schema)
|
|
||||||
+ (self._inspector.get_view_names(schema=schema) if view_support else [])
|
|
||||||
)
|
|
||||||
|
|
||||||
self._include_tables = set(include_tables) if include_tables else set()
|
|
||||||
if self._include_tables:
|
|
||||||
missing_tables = self._include_tables - self._all_tables
|
|
||||||
if missing_tables:
|
|
||||||
raise ValueError(
|
|
||||||
f"include_tables {missing_tables} not found in database"
|
|
||||||
)
|
|
||||||
self._ignore_tables = set(ignore_tables) if ignore_tables else set()
|
|
||||||
if self._ignore_tables:
|
|
||||||
missing_tables = self._ignore_tables - self._all_tables
|
|
||||||
if missing_tables:
|
|
||||||
raise ValueError(
|
|
||||||
f"ignore_tables {missing_tables} not found in database"
|
|
||||||
)
|
|
||||||
usable_tables = self.get_usable_table_names()
|
|
||||||
self._usable_tables = set(usable_tables) if usable_tables else self._all_tables
|
|
||||||
|
|
||||||
if not isinstance(sample_rows_in_table_info, int):
|
|
||||||
raise TypeError("sample_rows_in_table_info must be an integer")
|
|
||||||
|
|
||||||
self._sample_rows_in_table_info = sample_rows_in_table_info
|
|
||||||
self._indexes_in_table_info = indexes_in_table_info
|
|
||||||
|
|
||||||
self._custom_table_info = custom_table_info
|
|
||||||
if self._custom_table_info:
|
|
||||||
if not isinstance(self._custom_table_info, dict):
|
|
||||||
raise TypeError(
|
|
||||||
"table_info must be a dictionary with table names as keys and the "
|
|
||||||
"desired table info as values"
|
|
||||||
)
|
|
||||||
# only keep the tables that are also present in the database
|
|
||||||
intersection = set(self._custom_table_info).intersection(self._all_tables)
|
|
||||||
self._custom_table_info = dict(
|
|
||||||
(table, self._custom_table_info[table])
|
|
||||||
for table in self._custom_table_info
|
|
||||||
if table in intersection
|
|
||||||
)
|
|
||||||
|
|
||||||
self._max_string_length = max_string_length
|
|
||||||
|
|
||||||
self._metadata = metadata or MetaData()
|
|
||||||
# including view support if view_support = true
|
|
||||||
self._metadata.reflect(
|
|
||||||
views=view_support,
|
|
||||||
bind=self._engine,
|
|
||||||
only=list(self._usable_tables),
|
|
||||||
schema=self._schema,
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_uri(
|
|
||||||
cls, database_uri: str, engine_args: Optional[dict] = None, **kwargs: Any
|
|
||||||
) -> SQLDatabase:
|
|
||||||
"""Construct a SQLAlchemy engine from URI."""
|
|
||||||
_engine_args = engine_args or {}
|
|
||||||
return cls(create_engine(database_uri, **_engine_args), **kwargs)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_databricks(
|
|
||||||
cls,
|
|
||||||
catalog: str,
|
|
||||||
schema: str,
|
|
||||||
host: Optional[str] = None,
|
|
||||||
api_token: Optional[str] = None,
|
|
||||||
warehouse_id: Optional[str] = None,
|
|
||||||
cluster_id: Optional[str] = None,
|
|
||||||
engine_args: Optional[dict] = None,
|
|
||||||
**kwargs: Any,
|
|
||||||
) -> SQLDatabase:
|
|
||||||
"""
|
|
||||||
Class method to create an SQLDatabase instance from a Databricks connection.
|
|
||||||
This method requires the 'databricks-sql-connector' package. If not installed,
|
|
||||||
it can be added using `pip install databricks-sql-connector`.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
catalog (str): The catalog name in the Databricks database.
|
|
||||||
schema (str): The schema name in the catalog.
|
|
||||||
host (Optional[str]): The Databricks workspace hostname, excluding
|
|
||||||
'https://' part. If not provided, it attempts to fetch from the
|
|
||||||
environment variable 'DATABRICKS_HOST'. If still unavailable and if
|
|
||||||
running in a Databricks notebook, it defaults to the current workspace
|
|
||||||
hostname. Defaults to None.
|
|
||||||
api_token (Optional[str]): The Databricks personal access token for
|
|
||||||
accessing the Databricks SQL warehouse or the cluster. If not provided,
|
|
||||||
it attempts to fetch from 'DATABRICKS_TOKEN'. If still unavailable
|
|
||||||
and running in a Databricks notebook, a temporary token for the current
|
|
||||||
user is generated. Defaults to None.
|
|
||||||
warehouse_id (Optional[str]): The warehouse ID in the Databricks SQL. If
|
|
||||||
provided, the method configures the connection to use this warehouse.
|
|
||||||
Cannot be used with 'cluster_id'. Defaults to None.
|
|
||||||
cluster_id (Optional[str]): The cluster ID in the Databricks Runtime. If
|
|
||||||
provided, the method configures the connection to use this cluster.
|
|
||||||
Cannot be used with 'warehouse_id'. If running in a Databricks notebook
|
|
||||||
and both 'warehouse_id' and 'cluster_id' are None, it uses the ID of the
|
|
||||||
cluster the notebook is attached to. Defaults to None.
|
|
||||||
engine_args (Optional[dict]): The arguments to be used when connecting
|
|
||||||
Databricks. Defaults to None.
|
|
||||||
**kwargs (Any): Additional keyword arguments for the `from_uri` method.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
SQLDatabase: An instance of SQLDatabase configured with the provided
|
|
||||||
Databricks connection details.
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
ValueError: If 'databricks-sql-connector' is not found, or if both
|
|
||||||
'warehouse_id' and 'cluster_id' are provided, or if neither
|
|
||||||
'warehouse_id' nor 'cluster_id' are provided and it's not executing
|
|
||||||
inside a Databricks notebook.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
from databricks import sql # noqa: F401
|
|
||||||
except ImportError:
|
|
||||||
raise ValueError(
|
|
||||||
"databricks-sql-connector package not found, please install with"
|
|
||||||
" `pip install databricks-sql-connector`"
|
|
||||||
)
|
|
||||||
context = None
|
|
||||||
try:
|
|
||||||
from dbruntime.databricks_repl_context import get_context
|
|
||||||
|
|
||||||
context = get_context()
|
|
||||||
except ImportError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
default_host = context.browserHostName if context else None
|
|
||||||
if host is None:
|
|
||||||
host = get_from_env("host", "DATABRICKS_HOST", default_host)
|
|
||||||
|
|
||||||
default_api_token = context.apiToken if context else None
|
|
||||||
if api_token is None:
|
|
||||||
api_token = get_from_env("api_token", "DATABRICKS_TOKEN", default_api_token)
|
|
||||||
|
|
||||||
if warehouse_id is None and cluster_id is None:
|
|
||||||
if context:
|
|
||||||
cluster_id = context.clusterId
|
|
||||||
else:
|
|
||||||
raise ValueError(
|
|
||||||
"Need to provide either 'warehouse_id' or 'cluster_id'."
|
|
||||||
)
|
|
||||||
|
|
||||||
if warehouse_id and cluster_id:
|
|
||||||
raise ValueError("Can't have both 'warehouse_id' or 'cluster_id'.")
|
|
||||||
|
|
||||||
if warehouse_id:
|
|
||||||
http_path = f"/sql/1.0/warehouses/{warehouse_id}"
|
|
||||||
else:
|
|
||||||
http_path = f"/sql/protocolv1/o/0/{cluster_id}"
|
|
||||||
|
|
||||||
uri = (
|
|
||||||
f"databricks://token:{api_token}@{host}?"
|
|
||||||
f"http_path={http_path}&catalog={catalog}&schema={schema}"
|
|
||||||
)
|
|
||||||
return cls.from_uri(database_uri=uri, engine_args=engine_args, **kwargs)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_cnosdb(
|
|
||||||
cls,
|
|
||||||
url: str = "127.0.0.1:8902",
|
|
||||||
user: str = "root",
|
|
||||||
password: str = "",
|
|
||||||
tenant: str = "cnosdb",
|
|
||||||
database: str = "public",
|
|
||||||
) -> SQLDatabase:
|
|
||||||
"""
|
|
||||||
Class method to create an SQLDatabase instance from a CnosDB connection.
|
|
||||||
This method requires the 'cnos-connector' package. If not installed, it
|
|
||||||
can be added using `pip install cnos-connector`.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
url (str): The HTTP connection host name and port number of the CnosDB
|
|
||||||
service, excluding "http://" or "https://", with a default value
|
|
||||||
of "127.0.0.1:8902".
|
|
||||||
user (str): The username used to connect to the CnosDB service, with a
|
|
||||||
default value of "root".
|
|
||||||
password (str): The password of the user connecting to the CnosDB service,
|
|
||||||
with a default value of "".
|
|
||||||
tenant (str): The name of the tenant used to connect to the CnosDB service,
|
|
||||||
with a default value of "cnosdb".
|
|
||||||
database (str): The name of the database in the CnosDB tenant.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
SQLDatabase: An instance of SQLDatabase configured with the provided
|
|
||||||
CnosDB connection details.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
from cnosdb_connector import make_cnosdb_langchain_uri
|
|
||||||
|
|
||||||
uri = make_cnosdb_langchain_uri(url, user, password, tenant, database)
|
|
||||||
return cls.from_uri(database_uri=uri)
|
|
||||||
except ImportError:
|
|
||||||
raise ValueError(
|
|
||||||
"cnos-connector package not found, please install with"
|
|
||||||
" `pip install cnos-connector`"
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def dialect(self) -> str:
|
|
||||||
"""Return string representation of dialect to use."""
|
|
||||||
return self._engine.dialect.name
|
|
||||||
|
|
||||||
def get_usable_table_names(self) -> Iterable[str]:
|
|
||||||
"""Get names of tables available."""
|
|
||||||
if self._include_tables:
|
|
||||||
return sorted(self._include_tables)
|
|
||||||
return sorted(self._all_tables - self._ignore_tables)
|
|
||||||
|
|
||||||
def get_table_names(self) -> Iterable[str]:
|
|
||||||
"""Get names of tables available."""
|
|
||||||
warnings.warn(
|
|
||||||
"This method is deprecated - please use `get_usable_table_names`."
|
|
||||||
)
|
|
||||||
return self.get_usable_table_names()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def table_info(self) -> str:
|
|
||||||
"""Information about all tables in the database."""
|
|
||||||
return self.get_table_info()
|
|
||||||
|
|
||||||
def get_table_info(self, table_names: Optional[List[str]] = None) -> str:
|
|
||||||
"""Get information about specified tables.
|
|
||||||
|
|
||||||
Follows best practices as specified in: Rajkumar et al, 2022
|
|
||||||
(https://arxiv.org/abs/2204.00498)
|
|
||||||
|
|
||||||
If `sample_rows_in_table_info`, the specified number of sample rows will be
|
|
||||||
appended to each table description. This can increase performance as
|
|
||||||
demonstrated in the paper.
|
|
||||||
"""
|
|
||||||
all_table_names = self.get_usable_table_names()
|
|
||||||
if table_names is not None:
|
|
||||||
missing_tables = set(table_names).difference(all_table_names)
|
|
||||||
if missing_tables:
|
|
||||||
raise ValueError(f"table_names {missing_tables} not found in database")
|
|
||||||
all_table_names = table_names
|
|
||||||
|
|
||||||
meta_tables = [
|
|
||||||
tbl
|
|
||||||
for tbl in self._metadata.sorted_tables
|
|
||||||
if tbl.name in set(all_table_names)
|
|
||||||
and not (self.dialect == "sqlite" and tbl.name.startswith("sqlite_"))
|
|
||||||
]
|
|
||||||
|
|
||||||
tables = []
|
|
||||||
for table in meta_tables:
|
|
||||||
if self._custom_table_info and table.name in self._custom_table_info:
|
|
||||||
tables.append(self._custom_table_info[table.name])
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Ignore JSON datatyped columns
|
|
||||||
for k, v in table.columns.items():
|
|
||||||
if type(v.type) is NullType:
|
|
||||||
table._columns.remove(v)
|
|
||||||
|
|
||||||
# add create table command
|
|
||||||
create_table = str(CreateTable(table).compile(self._engine))
|
|
||||||
table_info = f"{create_table.rstrip()}"
|
|
||||||
has_extra_info = (
|
|
||||||
self._indexes_in_table_info or self._sample_rows_in_table_info
|
|
||||||
)
|
|
||||||
if has_extra_info:
|
|
||||||
table_info += "\n\n/*"
|
|
||||||
if self._indexes_in_table_info:
|
|
||||||
table_info += f"\n{self._get_table_indexes(table)}\n"
|
|
||||||
if self._sample_rows_in_table_info:
|
|
||||||
table_info += f"\n{self._get_sample_rows(table)}\n"
|
|
||||||
if has_extra_info:
|
|
||||||
table_info += "*/"
|
|
||||||
tables.append(table_info)
|
|
||||||
tables.sort()
|
|
||||||
final_str = "\n\n".join(tables)
|
|
||||||
return final_str
|
|
||||||
|
|
||||||
def _get_table_indexes(self, table: Table) -> str:
|
|
||||||
indexes = self._inspector.get_indexes(table.name)
|
|
||||||
indexes_formatted = "\n".join(map(_format_index, indexes))
|
|
||||||
return f"Table Indexes:\n{indexes_formatted}"
|
|
||||||
|
|
||||||
def _get_sample_rows(self, table: Table) -> str:
|
|
||||||
# build the select command
|
|
||||||
command = select(table).limit(self._sample_rows_in_table_info)
|
|
||||||
|
|
||||||
# save the columns in string format
|
|
||||||
columns_str = "\t".join([col.name for col in table.columns])
|
|
||||||
|
|
||||||
try:
|
|
||||||
# get the sample rows
|
|
||||||
with self._engine.connect() as connection:
|
|
||||||
sample_rows_result = connection.execute(command) # type: ignore
|
|
||||||
# shorten values in the sample rows
|
|
||||||
sample_rows = list(
|
|
||||||
map(lambda ls: [str(i)[:100] for i in ls], sample_rows_result)
|
|
||||||
)
|
|
||||||
|
|
||||||
# save the sample rows in string format
|
|
||||||
sample_rows_str = "\n".join(["\t".join(row) for row in sample_rows])
|
|
||||||
|
|
||||||
# in some dialects when there are no rows in the table a
|
|
||||||
# 'ProgrammingError' is returned
|
|
||||||
except ProgrammingError:
|
|
||||||
sample_rows_str = ""
|
|
||||||
|
|
||||||
return (
|
|
||||||
f"{self._sample_rows_in_table_info} rows from {table.name} table:\n"
|
|
||||||
f"{columns_str}\n"
|
|
||||||
f"{sample_rows_str}"
|
|
||||||
)
|
|
||||||
|
|
||||||
def _execute(
|
|
||||||
self,
|
|
||||||
command: str,
|
|
||||||
fetch: Union[Literal["all"], Literal["one"]] = "all",
|
|
||||||
) -> Sequence[Dict[str, Any]]:
|
|
||||||
"""
|
|
||||||
Executes SQL command through underlying engine.
|
|
||||||
|
|
||||||
If the statement returns no rows, an empty list is returned.
|
|
||||||
"""
|
|
||||||
with self._engine.begin() as connection:
|
|
||||||
if self._schema is not None:
|
|
||||||
if self.dialect == "snowflake":
|
|
||||||
connection.exec_driver_sql(
|
|
||||||
"ALTER SESSION SET search_path = %s", (self._schema,)
|
|
||||||
)
|
|
||||||
elif self.dialect == "bigquery":
|
|
||||||
connection.exec_driver_sql("SET @@dataset_id=?", (self._schema,))
|
|
||||||
elif self.dialect == "mssql":
|
|
||||||
pass
|
|
||||||
elif self.dialect == "trino":
|
|
||||||
connection.exec_driver_sql("USE ?", (self._schema,))
|
|
||||||
elif self.dialect == "duckdb":
|
|
||||||
# Unclear which parameterized argument syntax duckdb supports.
|
|
||||||
# The docs for the duckdb client say they support multiple,
|
|
||||||
# but `duckdb_engine` seemed to struggle with all of them:
|
|
||||||
# https://github.com/Mause/duckdb_engine/issues/796
|
|
||||||
connection.exec_driver_sql(f"SET search_path TO {self._schema}")
|
|
||||||
elif self.dialect == "oracle":
|
|
||||||
connection.exec_driver_sql(
|
|
||||||
f"ALTER SESSION SET CURRENT_SCHEMA = {self._schema}"
|
|
||||||
)
|
|
||||||
else: # postgresql and other compatible dialects
|
|
||||||
connection.exec_driver_sql("SET search_path TO %s", (self._schema,))
|
|
||||||
cursor = connection.execute(text(command))
|
|
||||||
if cursor.returns_rows:
|
|
||||||
if fetch == "all":
|
|
||||||
result = [x._asdict() for x in cursor.fetchall()]
|
|
||||||
elif fetch == "one":
|
|
||||||
first_result = cursor.fetchone()
|
|
||||||
result = [] if first_result is None else [first_result._asdict()]
|
|
||||||
else:
|
|
||||||
raise ValueError("Fetch parameter must be either 'one' or 'all'")
|
|
||||||
return result
|
|
||||||
return []
|
|
||||||
|
|
||||||
def run(
|
|
||||||
self,
|
|
||||||
command: str,
|
|
||||||
fetch: Union[Literal["all"], Literal["one"]] = "all",
|
|
||||||
) -> str:
|
|
||||||
"""Execute a SQL command and return a string representing the results.
|
|
||||||
|
|
||||||
If the statement returns rows, a string of the results is returned.
|
|
||||||
If the statement returns no rows, an empty string is returned.
|
|
||||||
"""
|
|
||||||
result = self._execute(command, fetch)
|
|
||||||
# Convert columns values to string to avoid issues with sqlalchemy
|
|
||||||
# truncating text
|
|
||||||
res = [
|
|
||||||
tuple(truncate_word(c, length=self._max_string_length) for c in r.values())
|
|
||||||
for r in result
|
|
||||||
]
|
|
||||||
if not res:
|
|
||||||
return ""
|
|
||||||
else:
|
|
||||||
return str(res)
|
|
||||||
|
|
||||||
def get_table_info_no_throw(self, table_names: Optional[List[str]] = None) -> str:
|
|
||||||
"""Get information about specified tables.
|
|
||||||
|
|
||||||
Follows best practices as specified in: Rajkumar et al, 2022
|
|
||||||
(https://arxiv.org/abs/2204.00498)
|
|
||||||
|
|
||||||
If `sample_rows_in_table_info`, the specified number of sample rows will be
|
|
||||||
appended to each table description. This can increase performance as
|
|
||||||
demonstrated in the paper.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
return self.get_table_info(table_names)
|
|
||||||
except ValueError as e:
|
|
||||||
"""Format the error message"""
|
|
||||||
return f"Error: {e}"
|
|
||||||
|
|
||||||
def run_no_throw(
|
|
||||||
self,
|
|
||||||
command: str,
|
|
||||||
fetch: Union[Literal["all"], Literal["one"]] = "all",
|
|
||||||
) -> str:
|
|
||||||
"""Execute a SQL command and return a string representing the results.
|
|
||||||
|
|
||||||
If the statement returns rows, a string of the results is returned.
|
|
||||||
If the statement returns no rows, an empty string is returned.
|
|
||||||
|
|
||||||
If the statement throws an error, the error message is returned.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
return self.run(command, fetch)
|
|
||||||
except SQLAlchemyError as e:
|
|
||||||
"""Format the error message"""
|
|
||||||
return f"Error: {e}"
|
|
||||||
@@ -1,112 +0,0 @@
|
|||||||
from datetime import timedelta, datetime
|
|
||||||
from typing import Dict
|
|
||||||
|
|
||||||
from cachetools import TLRUCache, cached
|
|
||||||
|
|
||||||
from aios_kernel.ai_function import AIFunction
|
|
||||||
from aios_kernel.sql_database import SQLDatabase, get_from_env
|
|
||||||
|
|
||||||
|
|
||||||
def _my_ttu(_key, _value, now):
|
|
||||||
return now + timedelta(seconds=600)
|
|
||||||
|
|
||||||
|
|
||||||
database_cache = TLRUCache(ttu=_my_ttu, maxsize=10000, timer=datetime.now)
|
|
||||||
|
|
||||||
|
|
||||||
@cached(cache=database_cache)
|
|
||||||
def get_database(uri: str) -> SQLDatabase:
|
|
||||||
return SQLDatabase.from_uri(uri)
|
|
||||||
|
|
||||||
|
|
||||||
class GetTableInfosFunction(AIFunction):
|
|
||||||
def __init__(self):
|
|
||||||
super().__init__()
|
|
||||||
self.name = "get_table_infos"
|
|
||||||
self.description = "Get table informations in the database"
|
|
||||||
|
|
||||||
def get_name(self) -> str:
|
|
||||||
return self.name
|
|
||||||
|
|
||||||
def get_description(self) -> str:
|
|
||||||
return self.description
|
|
||||||
|
|
||||||
def get_parameters(self) -> Dict:
|
|
||||||
return {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"database_url": {"type": "string", "description": "Database URL,Can be set to None"},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async def execute(self, **kwargs) -> str:
|
|
||||||
database_url: str = kwargs.get("database_url")
|
|
||||||
if (database_url is None
|
|
||||||
or database_url.strip() == ""
|
|
||||||
or database_url.strip().lower() == "none"
|
|
||||||
or database_url.strip().lower() == "null"):
|
|
||||||
database_url = get_from_env(key="database url", env_key="DATABASE_URL")
|
|
||||||
if database_url is None:
|
|
||||||
return "error: database_url is None"
|
|
||||||
database = get_database(database_url)
|
|
||||||
tables = database.get_usable_table_names()
|
|
||||||
table_infos = database.get_table_info(tables)
|
|
||||||
return table_infos
|
|
||||||
|
|
||||||
def is_local(self) -> bool:
|
|
||||||
return True
|
|
||||||
|
|
||||||
def is_in_zone(self) -> bool:
|
|
||||||
return True
|
|
||||||
|
|
||||||
def is_ready_only(self) -> bool:
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
class ExecuteSqlFunction(AIFunction):
|
|
||||||
def __init__(self):
|
|
||||||
super().__init__()
|
|
||||||
self.name = "execute_sql"
|
|
||||||
self.description = """
|
|
||||||
Input to this function is a detailed and correct SQL query, output is a result from the database.
|
|
||||||
If the query is not correct, an error message will be returned.
|
|
||||||
If an error is returned, rewrite the query, check the query, and try again.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def get_name(self) -> str:
|
|
||||||
return self.name
|
|
||||||
|
|
||||||
def get_description(self) -> str:
|
|
||||||
return self.description
|
|
||||||
|
|
||||||
def get_parameters(self) -> Dict:
|
|
||||||
return {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"database_url": {"type": "string", "description": "Database URL,Can be set to None"},
|
|
||||||
"sql": {"type": "string", "description": "SQL to execute"}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async def execute(self, **kwargs) -> str:
|
|
||||||
database_url = kwargs.get("database_url")
|
|
||||||
if (database_url is None
|
|
||||||
or database_url.strip() == ""
|
|
||||||
or database_url.strip().lower() == "none"
|
|
||||||
or database_url.strip().lower() == "null"):
|
|
||||||
database_url = get_from_env(key="database url", env_key="DATABASE_URL")
|
|
||||||
if database_url is None:
|
|
||||||
return "error: database_url is None"
|
|
||||||
sql = kwargs.get("sql")
|
|
||||||
|
|
||||||
database = get_database(database_url)
|
|
||||||
return database.run_no_throw(sql)
|
|
||||||
|
|
||||||
def is_local(self) -> bool:
|
|
||||||
return True
|
|
||||||
|
|
||||||
def is_in_zone(self) -> bool:
|
|
||||||
return True
|
|
||||||
|
|
||||||
def is_ready_only(self) -> bool:
|
|
||||||
return False
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import os
|
import os
|
||||||
from typing import Any,List,Dict
|
from typing import Any,List,Dict
|
||||||
from aios import AgentMsg,AgentTodo,AgentPrompt
|
from aios import AgentMsg,AgentTodo,LLMPrompt
|
||||||
from aios import SimpleAIFunction, SimpleAIOperation
|
from aios import SimpleAIFunction, SimpleAIOperation
|
||||||
from aios import SimpleEnvironment
|
from aios import SimpleEnvironment
|
||||||
|
|
||||||
|
|||||||
@@ -218,7 +218,7 @@ class IssueAgent(CustomAIAgent):
|
|||||||
super().__init__(agent_id, llm_model_name, max_token_size)
|
super().__init__(agent_id, llm_model_name, max_token_size)
|
||||||
|
|
||||||
|
|
||||||
class IssueParserEnvironment(Environment):
|
class IssueParserEnvironment(SimpleEnvironment):
|
||||||
def __init__(self, env_id: str, storage: IssueStorage) -> None:
|
def __init__(self, env_id: str, storage: IssueStorage) -> None:
|
||||||
super().__init__(env_id)
|
super().__init__(env_id)
|
||||||
self.storage = storage
|
self.storage = storage
|
||||||
@@ -305,7 +305,7 @@ class IssueParser:
|
|||||||
|
|
||||||
mail_desc = Mail.prompt_desc()
|
mail_desc = Mail.prompt_desc()
|
||||||
issue_desc = Issue.prompt_desc()
|
issue_desc = Issue.prompt_desc()
|
||||||
prompt = AgentPrompt()
|
prompt = LLMPrompt()
|
||||||
prompt.system_message = {"role": "system", "content": f'''
|
prompt.system_message = {"role": "system", "content": f'''
|
||||||
I'm a CEO of a company named 巴克云; You'ar my assistant, and you should help me to manage my issues. Issues is a concept in software development of this company, but I use it to manage my work.
|
I'm a CEO of a company named 巴克云; You'ar my assistant, and you should help me to manage my issues. Issues is a concept in software development of this company, but I use it to manage my work.
|
||||||
I'll give you a mail in json format, {mail_desc};
|
I'll give you a mail in json format, {mail_desc};
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import asyncio
|
||||||
import openai
|
import openai
|
||||||
from openai import AsyncOpenAI
|
from openai import AsyncOpenAI
|
||||||
import os
|
import os
|
||||||
@@ -237,6 +238,7 @@ class OpenAI_ComputeNode(ComputeNode):
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
logger.info(f"openai response: {resp}")
|
logger.info(f"openai response: {resp}")
|
||||||
|
#TODO: gpt-4v api is image_2_text ?
|
||||||
if mode_name == "gpt-4-vision-preview":
|
if mode_name == "gpt-4-vision-preview":
|
||||||
status_code = resp.choices[0].finish_reason
|
status_code = resp.choices[0].finish_reason
|
||||||
if status_code is None:
|
if status_code is None:
|
||||||
@@ -265,6 +267,7 @@ class OpenAI_ComputeNode(ComputeNode):
|
|||||||
|
|
||||||
if token_usage:
|
if token_usage:
|
||||||
result.result_refers["token_usage"] = token_usage
|
result.result_refers["token_usage"] = token_usage
|
||||||
|
|
||||||
logger.info(f"openai success response: {result.result_str}")
|
logger.info(f"openai success response: {result.result_str}")
|
||||||
return result
|
return result
|
||||||
case _:
|
case _:
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ class TelegramTunnel(AgentTunnel):
|
|||||||
async def _run_app():
|
async def _run_app():
|
||||||
try:
|
try:
|
||||||
update_id = (await self.bot.get_updates())[0].update_id
|
update_id = (await self.bot.get_updates())[0].update_id
|
||||||
except IndexError:
|
except Exception as e:
|
||||||
update_id = None
|
update_id = None
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"tg_tunnel error:{e}")
|
logger.error(f"tg_tunnel error:{e}")
|
||||||
@@ -97,7 +97,11 @@ class TelegramTunnel(AgentTunnel):
|
|||||||
#logger.info("listening for new messages...")
|
#logger.info("listening for new messages...")
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
|
if update_id:
|
||||||
update_id = await self._do_process_raw_message(self.bot, update_id)
|
update_id = await self._do_process_raw_message(self.bot, update_id)
|
||||||
|
else:
|
||||||
|
update_id = (await self.bot.get_updates())[0].update_id
|
||||||
|
|
||||||
except NetworkError:
|
except NetworkError:
|
||||||
await asyncio.sleep(1)
|
await asyncio.sleep(1)
|
||||||
except Forbidden:
|
except Forbidden:
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import logging
|
|||||||
import toml
|
import toml
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from aios import AIStorage,PackageEnv,PackageEnvManager,PackageMediaInfo,PackageInstallTask
|
from aios import AIStorage,PackageEnv,PackageEnvManager,PackageMediaInfo,PackageInstallTask,Workflow
|
||||||
from agent_manager import AgentManager
|
from agent_manager import AgentManager
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ mpmath>=1.3.0
|
|||||||
multidict>=6.0.4
|
multidict>=6.0.4
|
||||||
numpy>=1.25.2
|
numpy>=1.25.2
|
||||||
onnxruntime>=1.15.1
|
onnxruntime>=1.15.1
|
||||||
openai>=1.0.0
|
|
||||||
overrides>=7.4.0
|
overrides>=7.4.0
|
||||||
packaging>=23.1
|
packaging>=23.1
|
||||||
pandas>=2.1.0
|
pandas>=2.1.0
|
||||||
@@ -139,8 +139,9 @@ sentence-transformers==2.2.2
|
|||||||
tiktoken
|
tiktoken
|
||||||
markdown
|
markdown
|
||||||
PyPDF2
|
PyPDF2
|
||||||
srt==3.5.3
|
srt
|
||||||
webvtt-py==0.4.6
|
webvtt-py
|
||||||
|
openai
|
||||||
docker
|
docker
|
||||||
generic_escape
|
generic_escape
|
||||||
duckduckgo-search
|
duckduckgo-search
|
||||||
@@ -152,3 +153,4 @@ oracledb
|
|||||||
html2text
|
html2text
|
||||||
docx2txt
|
docx2txt
|
||||||
opencv-python
|
opencv-python
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ from sd_node import *
|
|||||||
from st_node import *
|
from st_node import *
|
||||||
|
|
||||||
from agent_manager import AgentManager
|
from agent_manager import AgentManager
|
||||||
# from workflow_manager import WorkflowManager
|
from workflow_manager import WorkflowManager
|
||||||
from knowledge_manager import KnowledgePipelineManager
|
from knowledge_manager import KnowledgePipelineManager
|
||||||
from tg_tunnel import TelegramTunnel
|
from tg_tunnel import TelegramTunnel
|
||||||
from email_tunnel import EmailTunnel
|
from email_tunnel import EmailTunnel
|
||||||
@@ -148,9 +148,9 @@ class AIOS_Shell:
|
|||||||
if await AgentManager.get_instance().initial() is not True:
|
if await AgentManager.get_instance().initial() is not True:
|
||||||
logger.error("agent manager initial failed!")
|
logger.error("agent manager initial failed!")
|
||||||
return False
|
return False
|
||||||
# if await WorkflowManager.get_instance().initial() is not True:
|
if await WorkflowManager.get_instance().initial() is not True:
|
||||||
# logger.error("workflow manager initial failed!")
|
logger.error("workflow manager initial failed!")
|
||||||
# return False
|
return False
|
||||||
|
|
||||||
open_ai_node = OpenAI_ComputeNode.get_instance()
|
open_ai_node = OpenAI_ComputeNode.get_instance()
|
||||||
if await open_ai_node.initial() is not True:
|
if await open_ai_node.initial() is not True:
|
||||||
|
|||||||
Reference in New Issue
Block a user