Compare commits

...
5 Commits
Author SHA1 Message Date
fabien 0a6ffab050 prise en charge des outils + weather 2026-10-04 21:03:51 +02:00
fabien a36f1231cd basic system promp 2026-09-23 00:09:26 +02:00
fabien b3be1a81e6 basic system prompt 2026-09-22 23:34:28 +02:00
fabien 1b5abb566e accès à un chat ouvert 2026-09-22 23:01:20 +02:00
fabien a92cc76a9a basic llm request stream 2026-09-22 21:15:23 +02:00
73 changed files with 2096 additions and 209 deletions

No files matched your search

+102
View File
@@ -0,0 +1,102 @@
<mxfile host="Electron">
<diagram name="Page-1" id="6ee6LexvoJe0i-CFFPA3">
<mxGraphModel dx="1067" dy="848" 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="0" />
<mxCell id="1" parent="0" />
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-29" parent="1" style="rounded=0;whiteSpace=wrap;html=1;fillColor=light-dark(#FFFFFF,transparent);strokeColor=#7D7D7D;" value="" vertex="1">
<mxGeometry height="270" width="220" x="540" y="140" as="geometry" />
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-15" edge="1" parent="1" source="Nf3PCM6Bg_LUHkPQi3Rq-1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" target="Nf3PCM6Bg_LUHkPQi3Rq-11">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-1" parent="1" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=30;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;" value="ChatService" vertex="1">
<mxGeometry height="120" width="140" x="290" y="425" as="geometry" />
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-2" parent="Nf3PCM6Bg_LUHkPQi3Rq-1" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="NewChat -&amp;gt; Chat" vertex="1">
<mxGeometry height="30" width="140" y="30" as="geometry" />
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-3" parent="Nf3PCM6Bg_LUHkPQi3Rq-1" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="AddMessage" vertex="1">
<mxGeometry height="30" width="140" y="60" as="geometry" />
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-22" parent="Nf3PCM6Bg_LUHkPQi3Rq-1" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="getChat" vertex="1">
<mxGeometry height="30" width="140" y="90" as="geometry" />
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-16" edge="1" parent="1" source="Nf3PCM6Bg_LUHkPQi3Rq-5" 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;" target="Nf3PCM6Bg_LUHkPQi3Rq-11">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-5" parent="1" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=30;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;" value="BrainHandler" vertex="1">
<mxGeometry height="150" width="140" x="590" y="450" as="geometry" />
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-6" parent="Nf3PCM6Bg_LUHkPQi3Rq-5" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="ProcessResponse" vertex="1">
<mxGeometry height="30" width="140" y="30" as="geometry" />
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-24" parent="Nf3PCM6Bg_LUHkPQi3Rq-5" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="subscribe" vertex="1">
<mxGeometry height="30" width="140" y="60" as="geometry" />
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-7" parent="Nf3PCM6Bg_LUHkPQi3Rq-5" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="Start" vertex="1">
<mxGeometry height="30" width="140" y="90" as="geometry" />
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-8" parent="Nf3PCM6Bg_LUHkPQi3Rq-5" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="Stop" vertex="1">
<mxGeometry height="30" width="140" y="120" as="geometry" />
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-10" edge="1" parent="1" source="Nf3PCM6Bg_LUHkPQi3Rq-2" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;" target="Nf3PCM6Bg_LUHkPQi3Rq-6">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-11" parent="1" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=30;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;" value="ChatRepository" vertex="1">
<mxGeometry height="90" width="140" x="400" y="710" as="geometry" />
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-12" parent="Nf3PCM6Bg_LUHkPQi3Rq-11" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="CreateChat" vertex="1">
<mxGeometry height="30" width="140" y="30" as="geometry" />
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-13" parent="Nf3PCM6Bg_LUHkPQi3Rq-11" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="CreateMessage" vertex="1">
<mxGeometry height="30" width="140" y="60" as="geometry" />
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-21" edge="1" parent="1" source="Nf3PCM6Bg_LUHkPQi3Rq-25" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" target="Nf3PCM6Bg_LUHkPQi3Rq-3">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-23" edge="1" parent="1" source="Nf3PCM6Bg_LUHkPQi3Rq-19" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" target="Nf3PCM6Bg_LUHkPQi3Rq-22">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-17" parent="1" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=30;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;" value="WebHandler" vertex="1">
<mxGeometry height="120" width="140" x="30" y="425" as="geometry" />
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-18" parent="Nf3PCM6Bg_LUHkPQi3Rq-17" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="NewChat" vertex="1">
<mxGeometry height="30" width="140" y="30" as="geometry" />
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-25" parent="Nf3PCM6Bg_LUHkPQi3Rq-17" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="NewMessage" vertex="1">
<mxGeometry height="30" width="140" y="60" as="geometry" />
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-19" parent="Nf3PCM6Bg_LUHkPQi3Rq-17" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="GetChatWithMessages" vertex="1">
<mxGeometry height="30" width="140" y="90" as="geometry" />
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-26" edge="1" parent="1" source="Nf3PCM6Bg_LUHkPQi3Rq-18" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" target="Nf3PCM6Bg_LUHkPQi3Rq-2">
<mxGeometry relative="1" as="geometry">
<mxPoint x="240" y="400" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-27" edge="1" parent="1" source="Nf3PCM6Bg_LUHkPQi3Rq-22" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" target="Nf3PCM6Bg_LUHkPQi3Rq-24">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-28" edge="1" parent="1" source="Nf3PCM6Bg_LUHkPQi3Rq-3" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" target="Nf3PCM6Bg_LUHkPQi3Rq-6">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-38" parent="1" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=30;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;" value="RunningSession" vertex="1">
<mxGeometry height="120" width="140" x="570" y="230" as="geometry" />
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-39" parent="Nf3PCM6Bg_LUHkPQi3Rq-38" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="Item 1" vertex="1">
<mxGeometry height="30" width="140" y="30" as="geometry" />
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-40" parent="Nf3PCM6Bg_LUHkPQi3Rq-38" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="Item 2" vertex="1">
<mxGeometry height="30" width="140" y="60" as="geometry" />
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-41" parent="Nf3PCM6Bg_LUHkPQi3Rq-38" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="Item 3" vertex="1">
<mxGeometry height="30" width="140" y="90" as="geometry" />
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-42" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="Goroutine" vertex="1">
<mxGeometry height="30" width="60" x="560" y="150" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>
+108
View File
@@ -0,0 +1,108 @@
<mxfile host="Electron">
<diagram name="Page-1" id="6ee6LexvoJe0i-CFFPA3">
<mxGraphModel dx="1067" dy="848" 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="0" />
<mxCell id="1" parent="0" />
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-43" parent="1" style="rounded=0;whiteSpace=wrap;html=1;" value="" vertex="1">
<mxGeometry height="250" width="510" x="260" y="380" as="geometry" />
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-29" parent="1" style="rounded=0;whiteSpace=wrap;html=1;fillColor=light-dark(#FFFFFF,transparent);strokeColor=#7D7D7D;" value="" vertex="1">
<mxGeometry height="270" width="220" x="520" y="50" as="geometry" />
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-15" edge="1" parent="1" source="Nf3PCM6Bg_LUHkPQi3Rq-1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" target="Nf3PCM6Bg_LUHkPQi3Rq-11">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-1" parent="1" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=30;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;" value="ChatService" vertex="1">
<mxGeometry height="120" width="140" x="290" y="425" as="geometry" />
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-2" parent="Nf3PCM6Bg_LUHkPQi3Rq-1" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="NewChat -&amp;gt; Chat" vertex="1">
<mxGeometry height="30" width="140" y="30" as="geometry" />
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-3" parent="Nf3PCM6Bg_LUHkPQi3Rq-1" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="AddMessage" vertex="1">
<mxGeometry height="30" width="140" y="60" as="geometry" />
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-22" parent="Nf3PCM6Bg_LUHkPQi3Rq-1" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="getChat" vertex="1">
<mxGeometry height="30" width="140" y="90" as="geometry" />
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-16" edge="1" parent="1" source="Nf3PCM6Bg_LUHkPQi3Rq-5" 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;" target="Nf3PCM6Bg_LUHkPQi3Rq-11">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-5" parent="1" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=30;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;" value="BrainHandler" vertex="1">
<mxGeometry height="150" width="140" x="590" y="450" as="geometry" />
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-6" parent="Nf3PCM6Bg_LUHkPQi3Rq-5" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="ProcessResponse" vertex="1">
<mxGeometry height="30" width="140" y="30" as="geometry" />
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-24" parent="Nf3PCM6Bg_LUHkPQi3Rq-5" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="subscribe" vertex="1">
<mxGeometry height="30" width="140" y="60" as="geometry" />
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-7" parent="Nf3PCM6Bg_LUHkPQi3Rq-5" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="Start" vertex="1">
<mxGeometry height="30" width="140" y="90" as="geometry" />
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-8" parent="Nf3PCM6Bg_LUHkPQi3Rq-5" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="Stop" vertex="1">
<mxGeometry height="30" width="140" y="120" as="geometry" />
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-10" edge="1" parent="1" source="Nf3PCM6Bg_LUHkPQi3Rq-2" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;" target="Nf3PCM6Bg_LUHkPQi3Rq-6">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-11" parent="1" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=30;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;" value="ChatRepository" vertex="1">
<mxGeometry height="90" width="140" x="400" y="710" as="geometry" />
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-12" parent="Nf3PCM6Bg_LUHkPQi3Rq-11" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="CreateChat" vertex="1">
<mxGeometry height="30" width="140" y="30" as="geometry" />
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-13" parent="Nf3PCM6Bg_LUHkPQi3Rq-11" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="CreateMessage" vertex="1">
<mxGeometry height="30" width="140" y="60" as="geometry" />
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-21" edge="1" parent="1" source="Nf3PCM6Bg_LUHkPQi3Rq-25" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" target="Nf3PCM6Bg_LUHkPQi3Rq-3">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-23" edge="1" parent="1" source="Nf3PCM6Bg_LUHkPQi3Rq-19" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" target="Nf3PCM6Bg_LUHkPQi3Rq-22">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-17" parent="1" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=30;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;" value="WebHandler" vertex="1">
<mxGeometry height="120" width="140" x="30" y="425" as="geometry" />
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-18" parent="Nf3PCM6Bg_LUHkPQi3Rq-17" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="NewChat" vertex="1">
<mxGeometry height="30" width="140" y="30" as="geometry" />
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-25" parent="Nf3PCM6Bg_LUHkPQi3Rq-17" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="NewMessage" vertex="1">
<mxGeometry height="30" width="140" y="60" as="geometry" />
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-19" parent="Nf3PCM6Bg_LUHkPQi3Rq-17" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="GetChatWithMessages" vertex="1">
<mxGeometry height="30" width="140" y="90" as="geometry" />
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-26" edge="1" parent="1" source="Nf3PCM6Bg_LUHkPQi3Rq-18" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" target="Nf3PCM6Bg_LUHkPQi3Rq-2">
<mxGeometry relative="1" as="geometry">
<mxPoint x="240" y="400" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-27" edge="1" parent="1" source="Nf3PCM6Bg_LUHkPQi3Rq-22" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" target="Nf3PCM6Bg_LUHkPQi3Rq-24">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-28" edge="1" parent="1" source="Nf3PCM6Bg_LUHkPQi3Rq-3" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" target="Nf3PCM6Bg_LUHkPQi3Rq-6">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-38" parent="1" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=30;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;" value="RunningSession" vertex="1">
<mxGeometry height="120" width="140" x="550" y="140" as="geometry" />
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-39" parent="Nf3PCM6Bg_LUHkPQi3Rq-38" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="Item 1" vertex="1">
<mxGeometry height="30" width="140" y="30" as="geometry" />
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-40" parent="Nf3PCM6Bg_LUHkPQi3Rq-38" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="Item 2" vertex="1">
<mxGeometry height="30" width="140" y="60" as="geometry" />
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-41" parent="Nf3PCM6Bg_LUHkPQi3Rq-38" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="Item 3" vertex="1">
<mxGeometry height="30" width="140" y="90" as="geometry" />
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-42" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="Goroutine" vertex="1">
<mxGeometry height="30" width="60" x="540" y="60" as="geometry" />
</mxCell>
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-44" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="Fusionner" vertex="1">
<mxGeometry height="30" width="60" x="480" y="380" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>
+105
View File
@@ -0,0 +1,105 @@
package cmd
import (
"time"
"trankilou.fr/lassistanoque/backend/internal/adapter/auth/password"
"trankilou.fr/lassistanoque/backend/internal/adapter/database"
"trankilou.fr/lassistanoque/backend/internal/adapter/file"
"trankilou.fr/lassistanoque/backend/internal/adapter/llm"
"trankilou.fr/lassistanoque/backend/internal/adapter/security"
"trankilou.fr/lassistanoque/backend/internal/adapter/tools/weather"
"trankilou.fr/lassistanoque/backend/internal/domain"
"trankilou.fr/lassistanoque/backend/internal/service/auth"
"trankilou.fr/lassistanoque/backend/internal/service/chat"
"trankilou.fr/lassistanoque/backend/internal/service/storage"
)
var db database.Database
var store storage.StorageProvider
var userRepository domain.UserRepository
var providerRepository domain.ProviderRepository
var agentRepository domain.AgentRepository
var chatRepository domain.ChatRepository
var settingsRepository domain.SettingsRepository
var authenticators = make(map[string]auth.Authenticator)
var tokenManager auth.TokenManager
var llmengine domain.LLMEngine
func init() {
var err error
db, err := database.GetDatabase()
if err != nil {
panic(err)
}
err = db.Migrate()
if err != nil {
panic(err)
}
// repositories
userRepository = db.UserRepository()
providerRepository = db.ProviderRepository()
agentRepository = db.AgentRepository()
chatRepository = db.ChatRepository()
settingsRepository = db.SettingsRepository()
// storage
store, err = file.GetStorageProvider(db.FileRepository())
if err != nil {
panic(err)
}
// authentication
tokenManager = security.NewJwtTokenManager(12*time.Hour, 7*24*time.Hour, "lassistanoque")
pwdAuth := password.NewPasswordAuthenticator(tokenManager, GetUserRepository())
authenticators["password"] = pwdAuth
// llm
llmengine = llm.NewAnyLLMEngine()
// llm tools
chat.RegisterTool(&weather.WeatherTool{})
}
func GetStorageProvider() storage.StorageProvider {
return store
}
func GetUserRepository() domain.UserRepository {
return userRepository
}
func GetProviderRepository() domain.ProviderRepository {
return providerRepository
}
func GetAgentRepository() domain.AgentRepository {
return agentRepository
}
func GetChatRepository() domain.ChatRepository {
return chatRepository
}
func GetSettingsRepository() domain.SettingsRepository {
return settingsRepository
}
func GetAuthenticators() map[string]auth.Authenticator {
return authenticators
}
func GetTokenManager() auth.TokenManager {
return tokenManager
}
func GetLLMEngine() domain.LLMEngine {
return llmengine
}
func Close() {
db.Close()
}
+21 -47
View File
@@ -11,15 +11,11 @@ import (
"time" "time"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"trankilou.fr/lassistanoque/backend/internal/adapter/auth/password" "trankilou.fr/lassistanoque/backend/internal/brain"
"trankilou.fr/lassistanoque/backend/internal/adapter/database"
"trankilou.fr/lassistanoque/backend/internal/adapter/file"
"trankilou.fr/lassistanoque/backend/internal/adapter/llm"
"trankilou.fr/lassistanoque/backend/internal/adapter/security"
"trankilou.fr/lassistanoque/backend/internal/http" "trankilou.fr/lassistanoque/backend/internal/http"
"trankilou.fr/lassistanoque/backend/internal/llmserver"
"trankilou.fr/lassistanoque/backend/internal/service/agent" "trankilou.fr/lassistanoque/backend/internal/service/agent"
"trankilou.fr/lassistanoque/backend/internal/service/auth" "trankilou.fr/lassistanoque/backend/internal/service/auth"
"trankilou.fr/lassistanoque/backend/internal/service/chat"
"trankilou.fr/lassistanoque/backend/internal/service/provider" "trankilou.fr/lassistanoque/backend/internal/service/provider"
"trankilou.fr/lassistanoque/backend/internal/service/storage" "trankilou.fr/lassistanoque/backend/internal/service/storage"
"trankilou.fr/lassistanoque/backend/internal/service/user" "trankilou.fr/lassistanoque/backend/internal/service/user"
@@ -45,67 +41,45 @@ func newServeCmd() *cobra.Command {
} }
func runServe() { func runServe() {
// database
db, err := database.GetDatabase()
if err != nil {
panic(err)
}
defer db.Close()
err = db.Migrate() defer Close()
if err != nil {
panic(err)
}
// storage
store, err := file.GetStorageProvider(db.FileRepository())
if err != nil {
panic(err)
}
// Adapters
tokenManager := security.NewJwtTokenManager(12*time.Hour, 7*24*time.Hour, "lassistanoque")
pwdAuth := password.NewPasswordAuthenticator(tokenManager, db.UserRepository())
llmEngine := llm.NewAnyLLMEngine()
// services // services
authService := auth.NewService( authService := auth.NewService(GetSettingsRepository(), GetUserRepository(), GetAuthenticators())
db.SettingsRepository(), userService := user.NewService(GetUserRepository())
db.UserRepository(), storageService := storage.NewService(GetStorageProvider())
map[string]auth.Authenticator{ providerService := provider.NewService(GetProviderRepository(), GetUserRepository(), GetLLMEngine())
"password": pwdAuth, agentService := agent.NewService(GetAgentRepository(), GetUserRepository())
}, chatService := chat.NewService(GetUserRepository(), GetAgentRepository(), GetProviderRepository(), GetChatRepository(), GetLLMEngine())
)
userService := user.NewService(db.UserRepository())
storageService := storage.NewService(store)
providerService := provider.NewService(db.ProviderRepository(), db.UserRepository(), llmEngine)
agentService := agent.NewService(db.AgentRepository(), db.UserRepository())
// http server // http server
router := http.NewRouter(http.Dependencies{ httpRouter := http.NewRouter(http.Dependencies{
StorageService: storageService, StorageService: storageService,
AuthService: authService, AuthService: authService,
UserService: userService, UserService: userService,
TokenManager: tokenManager,
ProviderService: providerService, ProviderService: providerService,
AgentService: agentService, AgentService: agentService,
ChatService: chatService,
TokenManager: GetTokenManager(),
}) })
llms := llmserver.NewLLMServer(llmserver.Dependencies{}) brainRouter := brain.NewRouter(brain.Dependencies{
ChatRepository: GetChatRepository(),
})
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop() defer stop()
// start LLM service // start Brain router
go func() { go func() {
if err := llms.Start(); err != nil && !errors.Is(err, nethttp.ErrServerClosed) { if err := brainRouter.Start(); err != nil && !errors.Is(err, nethttp.ErrServerClosed) {
slog.Error("failed to start llm server", "error", err) slog.Error("failed to start llm server", "error", err)
} }
}() }()
// start HTTP service // start HTTP router
go func() { go func() {
if err := router.Start(); err != nil && !errors.Is(err, nethttp.ErrServerClosed) { if err := httpRouter.Start(); err != nil && !errors.Is(err, nethttp.ErrServerClosed) {
slog.Error("failed to start http server", "error", err) slog.Error("failed to start http server", "error", err)
} }
}() }()
@@ -115,7 +89,7 @@ func runServe() {
// stop HTTP service // stop HTTP service
ctx, cancel1 := context.WithTimeout(context.Background(), 10*time.Second) ctx, cancel1 := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel1() defer cancel1()
if err := router.Stop(ctx); err != nil { if err := httpRouter.Stop(ctx); err != nil {
slog.Error("failed to stop http server", "error", err) slog.Error("failed to stop http server", "error", err)
} }
slog.Info("HTTP server stopped gracefully") slog.Info("HTTP server stopped gracefully")
@@ -123,7 +97,7 @@ func runServe() {
// stop LLM service // stop LLM service
ctx, cancel2 := context.WithTimeout(context.Background(), 10*time.Second) ctx, cancel2 := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel2() defer cancel2()
if err := llms.Stop(ctx); err != nil { if err := brainRouter.Stop(ctx); err != nil {
slog.Error("failed to stop llm server", "error", err) slog.Error("failed to stop llm server", "error", err)
} }
slog.Info("LLM server stopped gracefully") slog.Info("LLM server stopped gracefully")
@@ -14,6 +14,7 @@ type Database interface {
FileRepository() domain.FileRepository FileRepository() domain.FileRepository
ProviderRepository() domain.ProviderRepository ProviderRepository() domain.ProviderRepository
AgentRepository() domain.AgentRepository AgentRepository() domain.AgentRepository
ChatRepository() domain.ChatRepository
Migrate() error Migrate() error
Close() Close()
} }
+8 -6
View File
@@ -41,12 +41,14 @@ func NewTable[T any](db *sql.DB, dialect Dialect, tablename string) (Table[T], e
fields := make([]dbfield, 0) fields := make([]dbfield, 0)
for field := range t.Fields() { for field := range t.Fields() {
f := dbfield{ if field.Tag.Get("db") != "-" {
name: field.Name, f := dbfield{
stype: field.Type.String(), name: field.Name,
dbname: field.Tag.Get("db"), stype: field.Type.String(),
dbname: field.Tag.Get("db"),
}
fields = append(fields, f)
} }
fields = append(fields, f)
} }
return Table[T]{ return Table[T]{
@@ -188,7 +190,7 @@ func (t Table[T]) Select(options ...Option) ([]*T, error) {
if q.pagesize != 0 { if q.pagesize != 0 {
query += fmt.Sprintf(" limit %d", q.pagesize) query += fmt.Sprintf(" limit %d", q.pagesize)
if q.pagenum != 0 { if q.pagenum != 0 {
query += fmt.Sprintf(" offset %d", q.pagesize*q.pagenum) query += fmt.Sprintf(" offset %d", q.pagesize*(q.pagenum-1))
} }
} }
@@ -10,24 +10,24 @@ import (
type TursoAgentRepository struct { type TursoAgentRepository struct {
db *sql.DB db *sql.DB
AgentTable orm.Table[domain.Agent] agentTable orm.Table[domain.Agent]
} }
func NewTursoAgentRepository(db *sql.DB) *TursoAgentRepository { func NewTursoAgentRepository(db *sql.DB) *TursoAgentRepository {
dialect := orm.NewSqliteDialect() dialect := orm.NewSqliteDialect()
AgentTable, err := orm.NewTable[domain.Agent](db, dialect, "agents") agentTable, err := orm.NewTable[domain.Agent](db, dialect, "agents")
if err != nil { if err != nil {
log.Fatalf("error creating lasebuche agent table") log.Fatalf("error creating lasebuche agent table")
} }
return &TursoAgentRepository{ return &TursoAgentRepository{
db: db, db: db,
AgentTable: AgentTable, agentTable: agentTable,
} }
} }
func (r *TursoAgentRepository) ListAgents(userID string, teamID string) ([]*domain.Agent, error) { func (r *TursoAgentRepository) ListAgents(userID string, teamID string) ([]*domain.Agent, error) {
return r.AgentTable.Select( return r.agentTable.Select(
orm.WithWhere( orm.WithWhere(
"team_id=$1 and team_id in (select team_id from user_teams where user_id=$2)", "team_id=$1 and team_id in (select team_id from user_teams where user_id=$2)",
teamID, teamID,
@@ -37,7 +37,7 @@ func (r *TursoAgentRepository) ListAgents(userID string, teamID string) ([]*doma
} }
func (r *TursoAgentRepository) GetAgent(userID string, teamID string, id string) (*domain.Agent, error) { func (r *TursoAgentRepository) GetAgent(userID string, teamID string, id string) (*domain.Agent, error) {
return r.AgentTable.SelectOne( return r.agentTable.SelectOne(
"id=$1 and team_id=$2 and team_id in (select team_id from user_teams where user_id=$3)", "id=$1 and team_id=$2 and team_id in (select team_id from user_teams where user_id=$3)",
id, id,
teamID, teamID,
@@ -46,15 +46,15 @@ func (r *TursoAgentRepository) GetAgent(userID string, teamID string, id string)
} }
func (r *TursoAgentRepository) CreateAgent(userID string, agent *domain.Agent) (*domain.Agent, error) { func (r *TursoAgentRepository) CreateAgent(userID string, agent *domain.Agent) (*domain.Agent, error) {
return r.AgentTable.Insert(agent) return r.agentTable.Insert(agent)
} }
func (r *TursoAgentRepository) UpdateAgent(userID string, agent *domain.Agent) (*domain.Agent, error) { func (r *TursoAgentRepository) UpdateAgent(userID string, agent *domain.Agent) (*domain.Agent, error) {
return r.AgentTable.Update(agent) return r.agentTable.Update(agent)
} }
func (r *TursoAgentRepository) DeleteAgent(userID string, teamID string, id string) error { func (r *TursoAgentRepository) DeleteAgent(userID string, teamID string, id string) error {
return r.AgentTable.DeleteWhere( return r.agentTable.DeleteWhere(
"id=$1 and team_id=$2 and team_id in (select team_id from user_teams where user_id=$3)", "id=$1 and team_id=$2 and team_id in (select team_id from user_teams where user_id=$3)",
id, id,
teamID, teamID,
@@ -0,0 +1,109 @@
package turso
import (
"database/sql"
"log"
"trankilou.fr/lassistanoque/backend/internal/adapter/database/orm"
"trankilou.fr/lassistanoque/backend/internal/domain"
)
type TursoChatRepository struct {
db *sql.DB
chatTable orm.Table[domain.Chat]
messageTable orm.Table[domain.Message]
}
func NewTursoChatRepository(db *sql.DB) *TursoChatRepository {
dialect := orm.NewSqliteDialect()
chatTable, err := orm.NewTable[domain.Chat](db, dialect, "chats")
messageTable, err := orm.NewTable[domain.Message](db, dialect, "chat_messages")
if err != nil {
log.Fatalf("error creating lasebuche agent table")
}
return &TursoChatRepository{
db: db,
chatTable: chatTable,
messageTable: messageTable,
}
}
func (r *TursoChatRepository) ListChats(userID string, teamID string, page int) ([]*domain.Chat, error) {
return r.chatTable.Select(
orm.WithWhere(
"team_id=$1 and team_id in (select team_id from user_teams where user_id=$2)",
teamID,
userID,
),
orm.WithOrder("_date_created desc"),
orm.WithPagination(page, 50),
)
}
func (r *TursoChatRepository) GetChat(userID string, teamID string, id string) (*domain.Chat, error) {
return r.chatTable.SelectOne(
"id=$1 and team_id=$2 and team_id in (select team_id from user_teams where user_id=$3)",
id,
teamID,
userID,
)
}
func (r *TursoChatRepository) CreateChat(userID string, chat *domain.Chat) (*domain.Chat, error) {
return r.chatTable.Insert(chat)
}
func (r *TursoChatRepository) UpdateChat(userID string, chat *domain.Chat) (*domain.Chat, error) {
return r.chatTable.Update(chat)
}
func (r *TursoChatRepository) DeleteChat(userID string, teamID string, id string) error {
return r.chatTable.DeleteWhere(
"id=$1 and team_id=$2 and team_id in (select team_id from user_teams where user_id=$3)",
id,
teamID,
userID,
)
}
func (r *TursoChatRepository) GetChatMessages(userID string, teamID string, chatID string, withToolCallResponses bool) ([]*domain.Message, error) {
var messages []*domain.Message
var err error
if !withToolCallResponses {
messages, err = r.messageTable.Select(
orm.WithWhere(
`chat_id=$1
and team_id=$2
and team_id in (select team_id from user_teams where user_id=$3)
and (tool_call_id='' or tool_call_id is null)`,
chatID,
teamID,
userID,
),
orm.WithOrder("_date_created asc"),
)
} else {
messages, err = r.messageTable.Select(
orm.WithWhere(
"chat_id=$1 and team_id=$2 and team_id in (select team_id from user_teams where user_id=$3)",
chatID,
teamID,
userID,
),
orm.WithOrder("_date_created asc"),
)
}
if err != nil {
return nil, err
}
for _, m := range messages {
m.Json2toolCall()
}
return messages, nil
}
func (r *TursoChatRepository) CreateChatMessage(userID string, message *domain.Message) (*domain.Message, error) {
message.ToolCall2Json()
return r.messageTable.Insert(message)
}
@@ -75,3 +75,7 @@ func (db *TursoDB) ProviderRepository() domain.ProviderRepository {
func (db *TursoDB) AgentRepository() domain.AgentRepository { func (db *TursoDB) AgentRepository() domain.AgentRepository {
return NewTursoAgentRepository(db.DB) return NewTursoAgentRepository(db.DB)
} }
func (db *TursoDB) ChatRepository() domain.ChatRepository {
return NewTursoChatRepository(db.DB)
}
@@ -9,6 +9,7 @@ drop table user_teams;
drop table channels; drop table channels;
drop table tools; drop table tools;
drop table tasks; drop table tasks;
drop table history; drop table chats;
drop table chat_contents;
drop table files; drop table files;
drop table agents; drop table agents;
@@ -117,14 +117,30 @@ create table tasks (
_version text not null _version text not null
); );
create table history ( create table chats (
id text not null primary key, id text not null primary key,
task_id text not null, team_id text not null,
start_datetime numeric, start_datetime numeric not null,
end_datetime numeric, end_datetime numeric,
prompt text not null, agent_id text,
log text not null, user_id text,
response text not null, task_id text,
title text,
_date_created numeric not null default current_timestamp,
_date_updated numeric,
_version text not null
);
create table chat_messages (
id text not null primary key,
team_id text not null,
chat_id text not null,
content_datetime numeric,
role text not null,
content text,
agent_id text,
tool_call_id text,
tool_calls text,
_date_created numeric not null default current_timestamp, _date_created numeric not null default current_timestamp,
_date_updated numeric, _date_updated numeric,
_version text not null _version text not null
+105 -12
View File
@@ -3,8 +3,10 @@ package llm
import ( import (
"context" "context"
"fmt" "fmt"
"log/slog"
anyllm "github.com/mozilla-ai/any-llm-go" anyllm "github.com/mozilla-ai/any-llm-go"
"github.com/mozilla-ai/any-llm-go/providers"
"github.com/mozilla-ai/any-llm-go/providers/anthropic" "github.com/mozilla-ai/any-llm-go/providers/anthropic"
"github.com/mozilla-ai/any-llm-go/providers/ollama" "github.com/mozilla-ai/any-llm-go/providers/ollama"
"github.com/mozilla-ai/any-llm-go/providers/openai" "github.com/mozilla-ai/any-llm-go/providers/openai"
@@ -79,13 +81,16 @@ func (e *AnyLLMEngine) Stream(
ctx context.Context, ctx context.Context,
provider *domain.Provider, provider *domain.Provider,
modelID string, modelID string,
params *domain.StreamParams, params *domain.LLMParams,
messages []*domain.Message, messages []*domain.Message,
) error { ) *domain.Message {
p, err := providerFactory(provider) p, err := providerFactory(provider)
if err != nil { if err != nil {
return err return &domain.Message{
Role: string(domain.RoleAssistant),
Content: err.Error(),
}
} }
anyllmMessages := make([]anyllm.Message, 0) anyllmMessages := make([]anyllm.Message, 0)
@@ -96,26 +101,114 @@ func (e *AnyLLMEngine) Stream(
}) })
} }
if params.ReasoningEffort == "" {
params.ReasoningEffort = "none"
}
tools := make([]providers.Tool, len(params.Tools))
for i, t := range params.Tools {
tools[i] = toolConvert(t)
}
toolCalling := false
toolCalls := make([]domain.ToolCall, 0)
chunkChan, errChan := p.CompletionStream(ctx, anyllm.CompletionParams{ chunkChan, errChan := p.CompletionStream(ctx, anyllm.CompletionParams{
Model: modelID, Model: modelID,
Messages: anyllmMessages, Messages: anyllmMessages,
Stream: true, Stream: true,
ReasoningEffort: providers.ReasoningEffort(params.ReasoningEffort),
Tools: tools,
}) })
fullContent := ""
for chunk := range chunkChan { for chunk := range chunkChan {
if len(chunk.Choices) > 0 { if len(chunk.Choices) > 0 {
content := chunk.Choices[0].Delta.Content content := chunk.Choices[0].Delta.Content
reasoning := chunk.Choices[0].Delta.Reasoning
if params.OnChunk != nil { if params.OnChunk != nil {
params.OnChunk(&domain.Chunk{ if content != "" {
Content: content, fullContent += content
}) params.OnChunk(&domain.Chunk{
Done: false,
Role: anyllm.RoleAssistant,
Content: content,
Reasoning: false,
})
} else if reasoning != nil {
params.OnChunk(&domain.Chunk{
Done: false,
Role: anyllm.RoleAssistant,
Content: reasoning.Content,
Reasoning: true,
})
}
} }
for _, tc := range chunk.Choices[0].Delta.ToolCalls {
toolCalling = true
slog.Info("Model is calling tool", "tool", tc.Function.Name, "arguments", tc.Function.Arguments)
extra := make(map[string]map[string]any)
for k, v := range tc.Extra {
m := map[string]any(v)
extra[k] = m
}
toolCalls = append(toolCalls, domain.ToolCall{
ID: tc.ID,
Type: tc.Type,
Function: domain.ToolCallFunction{
Name: tc.Function.Name,
Arguments: tc.Function.Arguments,
},
Extra: extra,
})
params.OnChunk(&domain.Chunk{
Done: false,
ToolName: tc.Function.Name,
Role: anyllm.RoleAssistant,
Content: tc.Function.Arguments,
Reasoning: false,
})
}
} }
} }
if err := <-errChan; err != nil { if !toolCalling {
return err params.OnChunk(&domain.Chunk{
Done: true,
Role: anyllm.RoleAssistant,
Content: "",
})
} }
return nil if err := <-errChan; err != nil {
return &domain.Message{
Role: string(domain.RoleAssistant),
Content: err.Error(),
}
}
return &domain.Message{
Role: string(domain.RoleAssistant),
Content: fullContent,
ToolCalls: toolCalls,
}
}
func toolConvert(domainTool *domain.ToolDefinition) providers.Tool {
return providers.Tool{
Type: domainTool.Type,
Function: providers.Function{
Name: domainTool.Function.Name,
Description: domainTool.Function.Description,
Parameters: domainTool.Function.Parameters,
},
}
} }
@@ -0,0 +1,183 @@
package weather
import (
"context"
"encoding/json"
"fmt"
"net/http"
"net/url"
"strings"
"trankilou.fr/lassistanoque/backend/internal/domain"
)
type WeatherTool struct {
}
type WeatherConfig struct {
}
func (t *WeatherTool) Name() string {
return "weather"
}
func (t *WeatherTool) Description() string {
return "Get the current weather for a given location"
}
func (t *WeatherTool) Definition(ctx context.Context) *domain.ToolDefinition {
return &domain.ToolDefinition{
Type: "function",
Function: &domain.ToolFunction{
Name: t.Name(),
Description: t.Description(),
Parameters: map[string]any{
"type": "object",
"properties": map[string]any{
"location": map[string]any{
"type": "string",
"description": "The city name, e.g., 'Paris' or 'New York'",
},
},
"required": []string{"location"},
},
},
}
}
func (t *WeatherTool) Execute(ctx context.Context, input []byte) ([]byte, error) {
var params struct {
Location string `json:"location"`
}
err := json.Unmarshal(input, &params)
if err != nil {
return nil, err
}
resp, err := http.Get("https://geocoding-api.open-meteo.com/v1/search?name=" + url.QueryEscape(params.Location))
if err != nil {
return nil, err
}
defer resp.Body.Close()
var location struct {
Results []struct {
Latitude float64 `json:"latitude"`
Longitude float64 `json:"longitude"`
} `json:"results"`
}
err = json.NewDecoder(resp.Body).Decode(&location)
if err != nil {
return nil, err
}
if len(location.Results) == 0 {
return nil, fmt.Errorf("location not found : %s", input)
}
lon := location.Results[0].Longitude
lat := location.Results[0].Latitude
weatherResp, err := http.Get(fmt.Sprintf("https://api.open-meteo.com/v1/forecast?latitude=%f&longitude=%f&current_weather=true&daily=weather_code,temperature_2m_max,temperature_2m_min,rain_sum,wind_speed_10m_max&hourly=temperature_2m&forecast_days=15", lat, lon))
if err != nil {
return nil, err
}
defer weatherResp.Body.Close()
var weatherData struct {
CurrentWeatherUnits struct {
Temperature string `json:"temperature"`
WindSpeed string `json:"windspeed"`
} `json:"current_weather_units"`
CurrentWeather struct {
Time string `json:"time"`
Temperature float64 `json:"temperature"`
WeatherCode int `json:"weathercode"`
} `json:"current_weather"`
DailyUnits struct {
TemperatureMax string `json:"temperature_2m_max"`
TemperatureMin string `json:"temperature_2m_min"`
Rain string `json:"rain_sum"`
WindSpeed string `json:"wind_speed_10m_max"`
} `json:"daily_units"`
Daily struct {
Time []string `json:"time"`
TemperatureMax []float64 `json:"temperature_2m_max"`
TemperatureMin []float64 `json:"temperature_2m_min"`
Rain []float64 `json:"rain_sum"`
WindSpeed []float64 `json:"wind_speed_10m_max"`
} `json:"daily"`
}
err = json.NewDecoder(weatherResp.Body).Decode(&weatherData)
if err != nil {
return nil, err
}
var builder strings.Builder
fmt.Fprintf(&builder, "Current weather in %s\n", params.Location)
fmt.Fprintf(&builder, "- Time: %s\n", weatherData.CurrentWeather.Time)
fmt.Fprintf(&builder, "- Temperature: %.2f %s\n", weatherData.CurrentWeather.Temperature, weatherData.CurrentWeatherUnits.Temperature)
fmt.Fprintf(&builder, "- Weather code: %d - %s\n", weatherData.CurrentWeather.WeatherCode, wmoCodeToDescription(weatherData.CurrentWeather.WeatherCode))
if len(weatherData.Daily.WindSpeed) > 0 {
fmt.Fprintf(&builder, "- Wind speed: %.2f %s\n", weatherData.Daily.WindSpeed[0], weatherData.DailyUnits.WindSpeed)
}
if len(weatherData.Daily.TemperatureMax) > 0 {
fmt.Fprintf(&builder, "- Max temperature: %.2f %s\n", weatherData.Daily.TemperatureMax[0], weatherData.DailyUnits.TemperatureMax)
}
if len(weatherData.Daily.TemperatureMin) > 0 {
fmt.Fprintf(&builder, "- Min temperature: %.2f %s\n", weatherData.Daily.TemperatureMin[0], weatherData.DailyUnits.TemperatureMin)
}
if len(weatherData.Daily.Rain) > 0 {
fmt.Fprintf(&builder, "- Rainfall: %.2f %s\n", weatherData.Daily.Rain[0], weatherData.DailyUnits.Rain)
}
fmt.Fprintf(&builder, "\nForecast for the next days:\n")
// Iterate over the daily data and append to the repor
for i := 0; i < len(weatherData.Daily.Time); i++ {
fmt.Fprintf(&builder, "%s: Max Temp: %.2f %s, Min Temp: %.2f %s, Rain: %.2f %s, Wind Speed: %.2f %s\n",
weatherData.Daily.Time[i],
weatherData.Daily.TemperatureMax[i], weatherData.DailyUnits.TemperatureMax,
weatherData.Daily.TemperatureMin[i], weatherData.DailyUnits.TemperatureMin,
weatherData.Daily.Rain[i], weatherData.DailyUnits.Rain,
weatherData.Daily.WindSpeed[i], weatherData.DailyUnits.WindSpeed)
}
return []byte(builder.String()), nil
}
func wmoCodeToDescription(code int) string {
switch code {
case 0:
return "Clear sky"
case 1, 2, 3:
return "Mainly clear, partly cloudy, and overcast"
case 45, 48:
return "Fog and depositing rime fog"
case 51, 53, 55:
return "Drizzle: Light, moderate, and dense intensity"
case 56, 57:
return "Freezing Drizzle: Light and dense intensity"
case 61, 63, 65:
return "Rain: Slight, moderate and heavy intensity"
case 66, 67:
return "Freezing Rain: Light and heavy intensity"
case 71, 73, 75:
return "Snowfall: Slight, moderate, and heavy intensity"
case 77:
return "Snow grains"
case 80, 81, 82:
return "Rain showers: Slight, moderate, and violent"
case 85, 86:
return "Snow showers slight and heavy"
case 95:
return "Thunderstorm: Slight and heavy intensity"
case 96, 99:
return "Thunderstorm with slight and heavy hail"
default:
return "N/A"
}
}
@@ -1,17 +1,19 @@
package llmserver package brain
import ( import (
"context" "context"
"fmt" "fmt"
"sync" "sync"
"trankilou.fr/lassistanoque/backend/internal/domain"
"trankilou.fr/lassistanoque/backend/internal/utility" "trankilou.fr/lassistanoque/backend/internal/utility"
) )
type Dependencies struct { type Dependencies struct {
ChatRepository domain.ChatRepository
} }
type LLMServer struct { type Router struct {
started bool started bool
mu sync.Mutex mu sync.Mutex
sessions map[string]*Session // running sessions sessions map[string]*Session // running sessions
@@ -19,8 +21,8 @@ type LLMServer struct {
closeChan chan bool closeChan chan bool
} }
func NewLLMServer(deps Dependencies) *LLMServer { func NewRouter(deps Dependencies) *Router {
return &LLMServer{ return &Router{
started: true, started: true,
sessions: make(map[string]*Session), sessions: make(map[string]*Session),
newSessionChan: make(chan string), newSessionChan: make(chan string),
@@ -28,7 +30,7 @@ func NewLLMServer(deps Dependencies) *LLMServer {
} }
} }
func (s *LLMServer) Start() error { func (s *Router) Start() error {
for { for {
select { select {
@@ -40,13 +42,13 @@ func (s *LLMServer) Start() error {
} }
func (s *LLMServer) Stop(ctx context.Context) error { func (s *Router) Stop(ctx context.Context) error {
s.closeChan <- true s.closeChan <- true
s.started = false s.started = false
return nil return nil
} }
func (s *LLMServer) StartSession() (*Session, error) { func (s *Router) StartSession() (*Session, error) {
sid := utility.GenID() sid := utility.GenID()
session := &Session{} session := &Session{}
@@ -56,3 +58,9 @@ func (s *LLMServer) StartSession() (*Session, error) {
return session, nil return session, nil
} }
func (s *Router) Process(ctx context.Context, Chat *domain.ChatWithMessages) error {
s.closeChan <- true
s.started = false
return nil
}
+6
View File
@@ -0,0 +1,6 @@
package brain
type Session struct {
serverID string
chatID string
}
+109 -32
View File
@@ -1,49 +1,110 @@
package domain package domain
import "context" import (
"context"
"encoding/json"
"time"
)
type LLMModelDetail struct { type Chat struct {
ParentModel string `json:"parent_model"` ID string `db:"id" json:"id"`
Format string `json:"format"` TeamID string `db:"team_id" json:"teamId"`
Family string `json:"family"` StartDatetime *time.Time `db:"start_datetime" json:"startDatetime"`
Families []string `json:"families"` EndDatetime *time.Time `db:"end_datetime" json:"endDatetime"`
ParameterSize string `json:"parameter_size"` AgentID string `db:"agent_id" json:"agentId"`
QuantizationLevel string `json:"quantization_level"` UserID string `db:"user_id" json:"userId"`
} TaskID string `db:"task_id" json:"taskId,omitempty"`
Title string `db:"title" json:"title"`
type LLMModel struct { DateCreated time.Time `db:"_date_created" json:"_date_created"`
Name string `json:"name"` DateUpdated *time.Time `db:"_date_updated" json:"_date_updated"`
Model string `json:"model"` VersionId string `db:"_version" json:"_version"`
ModifiedAt int64 `json:"modified_at"`
Size int64 `json:"size"`
Digest string `json:"digest"`
Detail *LLMModelDetail `json:"detail"`
} }
type Message struct { type Message struct {
Role string ID string `db:"id" json:"id"`
Content string TeamID string `db:"team_id" json:"teamId"`
ToolCalls []ToolCall ChatID string `db:"chat_id" json:"chatId"`
ToolCallID string Role string `db:"role" json:"role"`
Content string `db:"content" json:"content"`
//Reasoning *Reasoning ToolCallID string `db:"tool_call_id" json:"toolCallId"`
ToolCalls []ToolCall `db:"-" json:"toolcalls"`
ToolCallsJson string `db:"tool_calls" json:"-"`
DateCreated time.Time `db:"_date_created" json:"_date_created"`
DateUpdated *time.Time `db:"_date_updated" json:"_date_updated"`
VersionId string `db:"_version" json:"_version"`
} }
type Tool struct { func (msg *Message) ToolCall2Json() error {
if msg.ToolCalls != nil {
toolCallJson, err := json.Marshal(&msg.ToolCalls)
if err != nil {
return err
}
msg.ToolCallsJson = string(toolCallJson)
}
return nil
}
func (msg *Message) Json2toolCall() error {
if msg.ToolCallsJson != "" {
err := json.Unmarshal([]byte(msg.ToolCallsJson), &msg.ToolCalls)
if err != nil {
return err
}
}
return nil
}
type ChatWithMessages struct {
Chat *Chat `json:"chat"`
Messages []*Message `json:"messages"`
}
type ChatParams struct {
ProviderID string
ModelID string
OnChunk StreamCallback
}
type Tool interface {
Name() string
Description() string
Definition(ctx context.Context) *ToolDefinition
Execute(ctx context.Context, input []byte) ([]byte, error)
}
type ToolDefinition struct {
Type string
Function *ToolFunction
}
type ToolFunction struct {
Name string
Description string
Parameters map[string]any
} }
type ToolCall struct { type ToolCall struct {
ID string
Type string
Function ToolCallFunction
Extra map[string]map[string]any
}
type ToolCallFunction struct {
Name string
Arguments string
} }
type Chunk struct { type Chunk struct {
Content string Done bool `json:"done"`
ToolName string `json:"toolName,omitempty"`
Role string `json:"role,omitempty"`
Content string `json:"content,omitempty"`
Reasoning bool `json:"reasoning,omitempty"`
} }
type StreamParams struct { type StreamCallback func(chunk *Chunk)
Tools []*Tool
OnChunk func(chunk *Chunk)
OnDebug func()
}
type Role string type Role string
@@ -54,6 +115,12 @@ const (
RoleTool Role = "tool" RoleTool Role = "tool"
) )
type LLMParams struct {
Tools []*ToolDefinition
OnChunk StreamCallback
ReasoningEffort string
}
type LLMEngine interface { type LLMEngine interface {
ListProviderTypes() []Item ListProviderTypes() []Item
ListModelsFromProvider(ctx context.Context, provider *Provider) ([]string, error) ListModelsFromProvider(ctx context.Context, provider *Provider) ([]string, error)
@@ -61,7 +128,17 @@ type LLMEngine interface {
ctx context.Context, ctx context.Context,
provider *Provider, provider *Provider,
modelID string, modelID string,
params *StreamParams, params *LLMParams,
messages []*Message, messages []*Message,
) error ) *Message
}
type ChatRepository interface {
ListChats(userID string, teamID string, page int) ([]*Chat, error)
GetChat(userID string, teamID string, id string) (*Chat, error)
CreateChat(userID string, chat *Chat) (*Chat, error)
UpdateChat(userID string, chat *Chat) (*Chat, error)
DeleteChat(userID string, teamID string, id string) error
GetChatMessages(userID string, teamID string, id string, withToolCallResponses bool) ([]*Message, error)
CreateChatMessage(userID string, message *Message) (*Message, error)
} }
+7 -7
View File
@@ -42,7 +42,7 @@ func (h *AgentHandler) ListAgents(c *echo.Context) error {
teamID := c.Param("space") teamID := c.Param("space")
agents, err := h.agentService.ListAgents(userID, teamID) agents, err := h.agentService.ListAgents(userID, teamID)
if err != nil { if err != nil {
c.Logger().Error("error listing agents", err) c.Logger().Error("error listing agents", "error", err)
return echo.NewHTTPError(http.StatusBadRequest, "error listing agents") return echo.NewHTTPError(http.StatusBadRequest, "error listing agents")
} }
return c.JSON(http.StatusOK, agents) return c.JSON(http.StatusOK, agents)
@@ -54,7 +54,7 @@ func (h *AgentHandler) GetAgent(c *echo.Context) error {
agentID := c.Param("agent") agentID := c.Param("agent")
agent, err := h.agentService.GetAgent(userID, teamID, agentID) agent, err := h.agentService.GetAgent(userID, teamID, agentID)
if err != nil { if err != nil {
c.Logger().Error("error getting agent: %s", err) c.Logger().Error("error getting agent", "error", err)
return echo.NewHTTPError(http.StatusBadRequest, "error getting agent") return echo.NewHTTPError(http.StatusBadRequest, "error getting agent")
} }
return c.JSON(http.StatusOK, agent) return c.JSON(http.StatusOK, agent)
@@ -66,13 +66,13 @@ func (h *AgentHandler) CreateAgent(c *echo.Context) error {
var agent domain.Agent var agent domain.Agent
err := c.Bind(&agent) err := c.Bind(&agent)
if err != nil { if err != nil {
c.Logger().Error("error binding agent : %s", err) c.Logger().Error("error binding agent", "error", err)
return echo.NewHTTPError(http.StatusBadRequest, "error binding agent") return echo.NewHTTPError(http.StatusBadRequest, "error binding agent")
} }
agent.TeamID = teamID agent.TeamID = teamID
updagent, err := h.agentService.CreateAgent(userID, &agent) updagent, err := h.agentService.CreateAgent(userID, &agent)
if err != nil { if err != nil {
c.Logger().Error("error creating agent: %s", err) c.Logger().Error("error creating agent", "error", err)
return echo.NewHTTPError(http.StatusBadRequest, "error creating agent") return echo.NewHTTPError(http.StatusBadRequest, "error creating agent")
} }
return c.JSON(http.StatusOK, updagent) return c.JSON(http.StatusOK, updagent)
@@ -84,13 +84,13 @@ func (h *AgentHandler) UpdateAgent(c *echo.Context) error {
var agent domain.Agent var agent domain.Agent
err := c.Bind(&agent) err := c.Bind(&agent)
if err != nil { if err != nil {
c.Logger().Error("error binding agent : %s", err) c.Logger().Error("error binding agent", "error", err)
return echo.NewHTTPError(http.StatusBadRequest, "error binding agent") return echo.NewHTTPError(http.StatusBadRequest, "error binding agent")
} }
agent.TeamID = teamID agent.TeamID = teamID
updagent, err := h.agentService.UpdateAgent(userID, &agent) updagent, err := h.agentService.UpdateAgent(userID, &agent)
if err != nil { if err != nil {
c.Logger().Error("error updating agent: %s", err) c.Logger().Error("error updating agent", "error", err)
return echo.NewHTTPError(http.StatusBadRequest, "error updating agent") return echo.NewHTTPError(http.StatusBadRequest, "error updating agent")
} }
return c.JSON(http.StatusOK, updagent) return c.JSON(http.StatusOK, updagent)
@@ -102,7 +102,7 @@ func (h *AgentHandler) DeleteAgent(c *echo.Context) error {
agentID := c.Param("agent") agentID := c.Param("agent")
err := h.agentService.DeleteAgent(userID, teamID, agentID) err := h.agentService.DeleteAgent(userID, teamID, agentID)
if err != nil { if err != nil {
c.Logger().Error("error creating agent: %s", err) c.Logger().Error("error creating agent", "error", err)
return echo.NewHTTPError(http.StatusBadRequest, "error creating agent") return echo.NewHTTPError(http.StatusBadRequest, "error creating agent")
} }
return c.JSON(http.StatusOK, agentID) return c.JSON(http.StatusOK, agentID)
+181
View File
@@ -0,0 +1,181 @@
package handlers
import (
"encoding/json"
"net/http"
"strconv"
"github.com/labstack/echo/v5"
"trankilou.fr/lassistanoque/backend/internal/domain"
"trankilou.fr/lassistanoque/backend/internal/service/auth"
"trankilou.fr/lassistanoque/backend/internal/service/chat"
)
func NewChatGroup(prefix string, e *echo.Group, service *chat.Service, middlewares ...echo.MiddlewareFunc) *echo.Group {
chatHandler := &ChatHandler{
chatService: service,
}
auth := e.Group(prefix, middlewares...)
auth.POST("/:space", chatHandler.NewChat)
auth.POST("/:space/:id", chatHandler.NewChatMessage)
auth.GET("/:space/all/:page", chatHandler.List)
auth.GET("/:space/:id", chatHandler.Get)
auth.GET("/:space/:id/subscribe", chatHandler.Subscribe)
auth.DELETE("/:space/:id", chatHandler.Delete)
return auth
}
type ChatHandler struct {
chatService *chat.Service
}
type ChatRequest struct {
Prompt string `json:"prompt"`
AgentID string `json:"agentID"`
ProviderID string `json:"providerID"`
ModelID string `json:"modelID"`
ReasoningLevel string `json:"reasoningLevel"`
}
func (h *ChatHandler) NewChat(c *echo.Context) error {
userID := c.Get(auth.ContextUserIDKey).(string)
teamID := c.Param("space")
var request ChatRequest
err := c.Bind(&request)
if err != nil {
c.Logger().Error("error binding request", "error", err.Error())
return echo.NewHTTPError(http.StatusBadRequest, "error binding request")
}
chat, err := h.chatService.NewChat(
userID,
teamID,
request.AgentID,
)
if err != nil {
c.Logger().Error("error creating chat", "error", err.Error())
return echo.NewHTTPError(http.StatusBadRequest, "error creating chat")
}
return c.JSON(http.StatusOK, chat)
}
func (h *ChatHandler) NewChatMessage(c *echo.Context) error {
userID := c.Get(auth.ContextUserIDKey).(string)
teamID := c.Param("space")
chatID := c.Param("id")
var request ChatRequest
err := c.Bind(&request)
if err != nil {
c.Logger().Error("error binding request", "error", err.Error())
return echo.NewHTTPError(http.StatusBadRequest, "error binding request")
}
enc := json.NewEncoder(c.Response())
chunkChan := make(chan *domain.Chunk)
_, err = h.chatService.AddChatMessage(
c.Request().Context(),
userID,
teamID,
chatID,
request.AgentID,
request.Prompt,
&domain.ChatParams{
OnChunk: func(chunk *domain.Chunk) {
chunkChan <- chunk
},
},
)
if err != nil {
c.Logger().Error("error creating chat message", "error", err.Error())
return echo.NewHTTPError(http.StatusBadRequest, "error creating chat message")
}
enc.Encode(&domain.Chunk{
Done: false,
Role: string(domain.RoleUser),
Content: request.Prompt,
})
http.NewResponseController(c.Response()).Flush()
for {
chunk := <-chunkChan
enc.Encode(chunk)
http.NewResponseController(c.Response()).Flush()
if chunk.Done {
return nil
}
}
}
func (h *ChatHandler) List(c *echo.Context) error {
userID := c.Get(auth.ContextUserIDKey).(string)
teamID := c.Param("space")
page := c.Param("page")
pageInt, _ := strconv.Atoi(page)
chats, err := h.chatService.ListChats(userID, teamID, pageInt)
if err != nil {
c.Logger().Error("error listing chats", "error", err)
return echo.NewHTTPError(http.StatusBadRequest, "error listing chats")
}
return c.JSON(http.StatusOK, chats)
}
func (h *ChatHandler) Get(c *echo.Context) error {
userID := c.Get(auth.ContextUserIDKey).(string)
teamID := c.Param("space")
chatID := c.Param("id")
chatWithMessages, err := h.chatService.GetChatWithMessages(userID, teamID, chatID)
if err != nil {
c.Logger().Error("error getting chat with messages", "error", err)
return echo.NewHTTPError(http.StatusBadRequest, "error getting chat")
}
return c.JSON(http.StatusOK, chatWithMessages)
}
func (h *ChatHandler) Delete(c *echo.Context) error {
userID := c.Get(auth.ContextUserIDKey).(string)
teamID := c.Param("space")
chatID := c.Param("id")
err := h.chatService.DeleteChat(userID, teamID, chatID)
if err != nil {
c.Logger().Error("error deleting chat", "error", err)
return echo.NewHTTPError(http.StatusBadRequest, "error deleting chat")
}
return c.JSON(http.StatusOK, "chat deleted")
}
func (h *ChatHandler) Subscribe(c *echo.Context) error {
userID := c.Get(auth.ContextUserIDKey).(string)
teamID := c.Param("space")
chatID := c.Param("id")
enc := json.NewEncoder(c.Response())
chunkChan := make(chan *domain.Chunk)
err := h.chatService.Subscribe(
userID,
teamID,
chatID,
func(chunk *domain.Chunk) {
chunkChan <- chunk
},
)
if err != nil {
c.Logger().Error("error subscribing to chat", "error", err.Error())
return echo.NewHTTPError(http.StatusBadRequest, "error creating chat")
}
for {
chunk := <-chunkChan
if chunk.Done {
return nil
}
enc.Encode(chunk)
http.NewResponseController(c.Response()).Flush()
}
}
+10 -10
View File
@@ -40,7 +40,7 @@ func (h *ModelHandler) ListProviders(c *echo.Context) error {
teamID := c.Param("space") teamID := c.Param("space")
providers, err := h.providerService.ListProviders(userID, teamID) providers, err := h.providerService.ListProviders(userID, teamID)
if err != nil { if err != nil {
c.Logger().Error("error listing providers: %s", err) c.Logger().Error("error listing providers", "error", err)
return echo.NewHTTPError(http.StatusBadRequest, "error listing providers") return echo.NewHTTPError(http.StatusBadRequest, "error listing providers")
} }
return c.JSON(http.StatusOK, providers) return c.JSON(http.StatusOK, providers)
@@ -52,7 +52,7 @@ func (h *ModelHandler) GetProvider(c *echo.Context) error {
providerID := c.Param("provider") providerID := c.Param("provider")
provider, err := h.providerService.GetProvider(userID, teamID, providerID) provider, err := h.providerService.GetProvider(userID, teamID, providerID)
if err != nil { if err != nil {
c.Logger().Error("error getting provider: %s", err) c.Logger().Error("error getting provider", "error", err)
return echo.NewHTTPError(http.StatusBadRequest, "error getting provider") return echo.NewHTTPError(http.StatusBadRequest, "error getting provider")
} }
return c.JSON(http.StatusOK, provider) return c.JSON(http.StatusOK, provider)
@@ -64,13 +64,13 @@ func (h *ModelHandler) CreateProvider(c *echo.Context) error {
var provider domain.Provider var provider domain.Provider
err := c.Bind(&provider) err := c.Bind(&provider)
if err != nil { if err != nil {
c.Logger().Error("error binding provider: %s", err) c.Logger().Error("error binding provider", "error", err)
return echo.NewHTTPError(http.StatusBadRequest, "error binding provider") return echo.NewHTTPError(http.StatusBadRequest, "error binding provider")
} }
provider.TeamID = teamID provider.TeamID = teamID
updprovider, err := h.providerService.CreateProvider(userID, &provider) updprovider, err := h.providerService.CreateProvider(userID, &provider)
if err != nil { if err != nil {
c.Logger().Error("error creating provider: %s", err) c.Logger().Error("error creating provider", "error", err)
return echo.NewHTTPError(http.StatusBadRequest, "error creating provider") return echo.NewHTTPError(http.StatusBadRequest, "error creating provider")
} }
return c.JSON(http.StatusOK, updprovider) return c.JSON(http.StatusOK, updprovider)
@@ -82,13 +82,13 @@ func (h *ModelHandler) UpdateProvider(c *echo.Context) error {
var provider domain.Provider var provider domain.Provider
err := c.Bind(&provider) err := c.Bind(&provider)
if err != nil { if err != nil {
c.Logger().Error("error binding provider: %s", err) c.Logger().Error("error binding provider", "error", err)
return echo.NewHTTPError(http.StatusBadRequest, "error binding provider") return echo.NewHTTPError(http.StatusBadRequest, "error binding provider")
} }
provider.TeamID = teamID provider.TeamID = teamID
updprovider, err := h.providerService.UpdateProvider(userID, &provider) updprovider, err := h.providerService.UpdateProvider(userID, &provider)
if err != nil { if err != nil {
c.Logger().Error("error updating provider: %s", err) c.Logger().Error("error updating provider", "error", err)
return echo.NewHTTPError(http.StatusBadRequest, "error creating provider") return echo.NewHTTPError(http.StatusBadRequest, "error creating provider")
} }
return c.JSON(http.StatusOK, updprovider) return c.JSON(http.StatusOK, updprovider)
@@ -100,7 +100,7 @@ func (h *ModelHandler) DeleteProvider(c *echo.Context) error {
providerID := c.Param("provider") providerID := c.Param("provider")
err := h.providerService.DeleteProvider(userID, teamID, providerID) err := h.providerService.DeleteProvider(userID, teamID, providerID)
if err != nil { if err != nil {
c.Logger().Error("error creating provider: %s", err) c.Logger().Error("error creating provider", "error", err)
return echo.NewHTTPError(http.StatusBadRequest, "error creating provider") return echo.NewHTTPError(http.StatusBadRequest, "error creating provider")
} }
return c.JSON(http.StatusOK, providerID) return c.JSON(http.StatusOK, providerID)
@@ -111,13 +111,13 @@ func (h *ModelHandler) ListAvailableModels(c *echo.Context) error {
var provider domain.Provider var provider domain.Provider
err := c.Bind(&provider) err := c.Bind(&provider)
if err != nil { if err != nil {
c.Logger().Error("error binding provider: %s", err) c.Logger().Error("error binding provider", "error", err)
return echo.NewHTTPError(http.StatusBadRequest, "error binding provider") return echo.NewHTTPError(http.StatusBadRequest, "error binding provider")
} }
provider.TeamID = teamID provider.TeamID = teamID
list, err := h.providerService.ListAvailableModels(c.Request().Context(), &provider) list, err := h.providerService.ListAvailableModels(c.Request().Context(), &provider)
if err != nil { if err != nil {
c.Logger().Error("error listing available models: %s", err) c.Logger().Error("error listing available models", "error", err)
return echo.NewHTTPError(http.StatusBadRequest, "error listing available models") return echo.NewHTTPError(http.StatusBadRequest, "error listing available models")
} }
return c.JSON(http.StatusOK, list) return c.JSON(http.StatusOK, list)
@@ -128,7 +128,7 @@ func (h *ModelHandler) ListProvidersModels(c *echo.Context) error {
teamID := c.Param("space") teamID := c.Param("space")
list, err := h.providerService.ListProvidersModels(userID, teamID) list, err := h.providerService.ListProvidersModels(userID, teamID)
if err != nil { if err != nil {
c.Logger().Error("error listing providers models: %s", err) c.Logger().Error("error listing providers models", "error", err)
return echo.NewHTTPError(http.StatusBadRequest, "error listing providers models") return echo.NewHTTPError(http.StatusBadRequest, "error listing providers models")
} }
return c.JSON(http.StatusOK, list) return c.JSON(http.StatusOK, list)
+3
View File
@@ -13,6 +13,7 @@ import (
"trankilou.fr/lassistanoque/backend/internal/http/handlers" "trankilou.fr/lassistanoque/backend/internal/http/handlers"
"trankilou.fr/lassistanoque/backend/internal/service/agent" "trankilou.fr/lassistanoque/backend/internal/service/agent"
"trankilou.fr/lassistanoque/backend/internal/service/auth" "trankilou.fr/lassistanoque/backend/internal/service/auth"
"trankilou.fr/lassistanoque/backend/internal/service/chat"
"trankilou.fr/lassistanoque/backend/internal/service/provider" "trankilou.fr/lassistanoque/backend/internal/service/provider"
"trankilou.fr/lassistanoque/backend/internal/service/storage" "trankilou.fr/lassistanoque/backend/internal/service/storage"
"trankilou.fr/lassistanoque/backend/internal/service/user" "trankilou.fr/lassistanoque/backend/internal/service/user"
@@ -40,6 +41,7 @@ type Dependencies struct {
UserService *user.Service UserService *user.Service
ProviderService *provider.Service ProviderService *provider.Service
AgentService *agent.Service AgentService *agent.Service
ChatService *chat.Service
TokenManager auth.TokenManager TokenManager auth.TokenManager
} }
@@ -81,6 +83,7 @@ func NewRouter(deps Dependencies) *Router {
_ = handlers.NewModelGroup("/provider", api, deps.ProviderService, deps.TokenManager.TokenMiddleware) _ = handlers.NewModelGroup("/provider", api, deps.ProviderService, deps.TokenManager.TokenMiddleware)
_ = handlers.NewAgentGroup("/agent", api, deps.AgentService, deps.UserService, deps.TokenManager.TokenMiddleware) _ = handlers.NewAgentGroup("/agent", api, deps.AgentService, deps.UserService, deps.TokenManager.TokenMiddleware)
_ = handlers.NewTeamGroup("/team", api, deps.UserService, deps.TokenManager.TokenMiddleware) _ = handlers.NewTeamGroup("/team", api, deps.UserService, deps.TokenManager.TokenMiddleware)
_ = handlers.NewChatGroup("/chat", api, deps.ChatService, deps.TokenManager.TokenMiddleware)
server := http.Server{ server := http.Server{
Addr: fmt.Sprintf(":%d", cfg.HttpPort), Addr: fmt.Sprintf(":%d", cfg.HttpPort),
@@ -1,2 +0,0 @@
/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style:solid;--tw-font-weight:initial;--tw-outline-style:solid;--tw-duration:initial;--tw-leading:initial;--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-ease:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000}}}@layer theme{:root,:host{--font-sans:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-red-300:oklch(80.8% .114 19.571);--color-red-400:oklch(70.4% .191 22.216);--color-red-500:oklch(63.7% .237 25.331);--color-red-700:oklch(50.5% .213 27.518);--color-red-900:oklch(39.6% .141 25.723);--color-green-300:oklch(87.1% .15 154.449);--color-green-700:oklch(52.7% .154 150.069);--color-cyan-600:oklch(60.9% .126 221.723);--color-sky-300:oklch(82.8% .111 230.318);--color-sky-700:oklch(50% .134 242.749);--color-blue-100:oklch(93.2% .032 255.585);--color-blue-600:oklch(54.6% .245 262.881);--color-blue-900:oklch(37.9% .146 265.522);--color-gray-100:oklch(96.7% .003 264.542);--color-gray-200:oklch(92.8% .006 264.531);--color-gray-300:oklch(87.2% .01 258.338);--color-gray-400:oklch(70.7% .022 261.325);--color-gray-500:oklch(55.1% .027 264.364);--color-gray-600:oklch(44.6% .03 256.802);--color-gray-800:oklch(27.8% .033 256.848);--color-stone-50:oklch(98.5% .001 106.423);--color-stone-100:oklch(97% .001 106.424);--color-stone-200:oklch(92.3% .003 48.717);--color-stone-300:oklch(86.9% .005 56.366);--color-stone-400:oklch(70.9% .01 56.259);--color-stone-500:oklch(55.3% .013 58.071);--color-stone-600:oklch(44.4% .011 73.639);--color-stone-700:oklch(37.4% .01 67.558);--color-stone-800:oklch(26.8% .007 34.298);--color-stone-900:oklch(21.6% .006 56.043);--color-stone-950:oklch(14.7% .004 49.25);--color-white:#fff;--spacing:.25rem;--container-3xl:48rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-3xl:1.875rem;--text-3xl--line-height:calc(2.25 / 1.875);--font-weight-medium:500;--font-weight-bold:700;--radius-sm:.25rem;--radius-lg:.5rem;--ease-in-out:cubic-bezier(.4, 0, .2, 1);--animate-spin:spin 1s linear infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring:where(:not(iframe)){outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variatLine truncated
@@ -0,0 +1,2 @@
/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style:solid;--tw-font-weight:initial;--tw-outline-style:solid;--tw-duration:initial;--tw-leading:initial;--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-ease:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000}}}@layer theme{:root,:host{--font-sans:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-red-300:oklch(80.8% .114 19.571);--color-red-400:oklch(70.4% .191 22.216);--color-red-500:oklch(63.7% .237 25.331);--color-red-700:oklch(50.5% .213 27.518);--color-red-900:oklch(39.6% .141 25.723);--color-green-300:oklch(87.1% .15 154.449);--color-green-700:oklch(52.7% .154 150.069);--color-cyan-400:oklch(78.9% .154 211.53);--color-cyan-600:oklch(60.9% .126 221.723);--color-sky-300:oklch(82.8% .111 230.318);--color-sky-700:oklch(50% .134 242.749);--color-blue-100:oklch(93.2% .032 255.585);--color-blue-600:oklch(54.6% .245 262.881);--color-blue-900:oklch(37.9% .146 265.522);--color-gray-100:oklch(96.7% .003 264.542);--color-gray-200:oklch(92.8% .006 264.531);--color-gray-300:oklch(87.2% .01 258.338);--color-gray-400:oklch(70.7% .022 261.325);--color-gray-500:oklch(55.1% .027 264.364);--color-gray-600:oklch(44.6% .03 256.802);--color-gray-800:oklch(27.8% .033 256.848);--color-stone-50:oklch(98.5% .001 106.423);--color-stone-100:oklch(97% .001 106.424);--color-stone-200:oklch(92.3% .003 48.717);--color-stone-300:oklch(86.9% .005 56.366);--color-stone-400:oklch(70.9% .01 56.259);--color-stone-500:oklch(55.3% .013 58.071);--color-stone-600:oklch(44.4% .011 73.639);--color-stone-700:oklch(37.4% .01 67.558);--color-stone-800:oklch(26.8% .007 34.298);--color-stone-900:oklch(21.6% .006 56.043);--color-stone-950:oklch(14.7% .004 49.25);--color-white:#fff;--spacing:.25rem;--container-3xl:48rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-3xl:1.875rem;--text-3xl--line-height:calc(2.25 / 1.875);--font-weight-medium:500;--font-weight-bold:700;--radius-sm:.25rem;--radius-lg:.5rem;--ease-in-out:cubic-bezier(.4, 0, .2, 1);--animate-spin:spin 1s linear infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring:where(:not(iframe)){outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inheritLine truncated
@@ -1 +1 @@
import{F as e,I as t,P as n,Q as r,X as i,nt as a,r as o,t as s}from"./39JK5HWF.js";var c=class{constructor(e,t){this.status=e,this.body=typeof t==`string`?{message:t}:t||{message:`Error: ${e}`}}toString(){return JSON.stringify(this.body)}},l=class{constructor(e,t){try{new Headers({location:t})}catch{throw Error(`Invalid redirect location ${JSON.stringify(t)}: this string contains characters that cannot be used in HTTP headers`)}this.status=e,this.location=t}},u=class extends Error{constructor(e,t,n){super(n),this.status=e,this.text=t}};new URL(`sveltekit-internal://`);function d(e,t){return e===`/`||t===`ignore`?e:t===`never`?e.endsWith(`/`)?e.slice(0,-1):e:t===`always`&&!e.endsWith(`/`)?e+`/`:e}function f(e){return e.split(`%25`).map(decodeURI).join(`%25`)}function p(e){for(let t in e)e[t]=decodeURIComponent(e[t]);return e}function m({href:e}){return e.split(`#`)[0]}function h(e,t,n,r=!1){let i=new URL(e);Object.defineProperty(i,"searchParams",{value:new Proxy(i.searchParams,{get(e,r){if(r===`get`||r===`getAll`||r===`has`)return(t,...i)=>(n(t),e[r](t,...i));t();let i=Reflect.get(e,r);return typeof i==`function`?i.bind(e):i}}),enumerable:!0,configurable:!0});let a=[`href`,`pathname`,`search`,`toString`,`toJSON`];r&&a.push(`hash`);for(let n of a)Object.defineProperty(i,n,{get(){return t(),e[n]},enumerable:!0,configurable:!0});return i}function g(){}function _(...e){let t=5381;for(let n of e)if(typeof n==`string`){let e=n.length;for(;e;)t=t*33^n.charCodeAt(--e)}else if(ArrayBuffer.isView(n)){let e=new Uint8Array(n.buffer,n.byteOffset,n.byteLength),r=e.length;for(;r;)t=t*33^e[--r]}else throw TypeError(`value must be a string or TypedArray`);return(t>>>0).toString(36)}new TextEncoder;function v(e){let t=atob(e),n=new Uint8Array(t.length);for(let e=0;e<t.length;e++)n[e]=t.charCodeAt(e);return n}var ee=window.fetch;window.fetch=(e,t)=>((e instanceof Request?e.method:t?.method||`GET`)!==`GET`&&y.delete(x(e)),ee(e,t));var y=new Map;function b(e,t){let n=x(e,t),r=document.querySelector(n);if(r?.textContent){r.remove();let{body:e,...t}=JSON.parse(r.textContent);r.getAttribute(`data-b64`)!==null&&(e=v(e));let i=r.getAttribute(`data-ttl`);return i&&y.set(n,{body:e,init:t,ttl:1e3*Number(i)}),Promise.resolve(new Response(e,t))}return window.fetch(e,t)}function te(e,t,n){if(y.size>0){let t=x(e,n),r=y.get(t);if(r){if(performance.now()<r.ttl&&[`default`,`force-cache`,`only-if-cached`,void 0].includes(n?.cache))return new Response(r.body,r.init);y.delete(t)}}return window.fetch(t,n)}function x(e,t){let n=`script[data-sveltekit-fetched][data-url=${JSON.stringify(e instanceof Request?e.url:e)}]`;if(t?.headers||t?.body){let e=[];t.headers&&e.push([...new Headers(t.headers)].join(`,`)),t.body&&(typeof t.body==`string`||ArrayBuffer.isView(t.body))&&e.push(t.body),n+=`[data-hash="${_(...e)}"]`}return n}var ne=/^(\[)?(\.\.\.)?(\w+)(?:=(\w+))?(\])?$/,re=/^\/\((?:[^)]+)\)$/;function ie(e){let t=[];return{pattern:e===`/`||re.test(e)?/^\/$/:RegExp(`^${oe(e).map(e=>{let n=/^\[\.\.\.(\w+)(?:=(\w+))?\]$/.exec(e);if(n)return t.push({name:n[1],matcher:n[2],optional:!1,rest:!0,chained:!0}),`(?:/([^]*))?`;let r=/^\[\[(\w+)(?:=(\w+))?\]\]$/.exec(e);if(r)return t.push({name:r[1],matcher:r[2],optional:!0,rest:!1,chained:!0}),`(?:/([^/]+))?`;if(!e)return;let i=e.split(/\[(.+?)\](?!\])/);return`/`+i.map((e,n)=>{if(n%2){if(e.startsWith(`x+`))return ce(String.fromCharCode(parseInt(e.slice(2),16)));if(e.startsWith(`u+`))return ce(String.fromCharCode(...e.slice(2).split(`-`).map(e=>parseInt(e,16))));let[,r,a,o,s]=ne.exec(e);return t.push({name:o,matcher:s,optional:!!r,rest:!!a,chained:a?n===1&&i[0]===``:!1}),a?`([^]*?)`:r?`([^/]*)?`:`([^/]+?)`}return ce(e)}).join(``)}).join(``)}/?$`),params:t}}function ae(e){return e!==``&&!/^\([^)]+\)$/.test(e)}function oe(e){return e.slice(1).split(`/`).filter(ae)}function se(e,t,n){let r={},i=e.slice(1),a=i.filter(e=>e!==void 0),o=0;for(let e=0;e<t.length;e+=1){let s=t[e],c=i[e-o];if(s.chained&&s.rest&&o&&(c=i.slice(e-o,e+1).filter(e=>e).join(`/`),o=0),c===void 0)if(s.rest)c=``;else continue;if(!s.matcher||n[s.matcher](c)){r[s.name]=c;let n=t[e+1],l=i[e+1];n&&!n.rest&&n.optional&&l&&s.chained&&(o=0),!n&&!l&&Object.keys(r).length===a.length&&(o=0);continue}if(s.optional&&s.chained){o++;continue}return}if(!o)return r}function ce(e){return e.normalize().replace(/[[\]]/g,`\\$&`).replace(/%/g,`%25`).replace(/\//g,`%2[Ff]`).replace(/\?/g,`%3[Ff]`).replace(/#/g,`%23`).replace(/[.*+?^${}()|\\]/g,`\\$&`)}function le({nodes:e,server_loads:t,dictionary:n,matchers:r}){let i=new Set(t);return Object.entries(n).map(([t,[n,i,s]])=>{let{pattern:c,params:l}=ie(t),u={id:t,exec:e=>{let t=c.exec(e);if(t)return se(t,l,r)},errors:[1,...s||[]].map(t=>e[t]),layouts:[0,...i||[]].map(o),leaf:a(n)};return u.errors.length=u.layouts.length=Math.max(u.errors.length,u.layouts.length),u});function a(t){let n=t<0;return n&&(t=~t),[n,e[t]]}function o(t){return t===void 0?t:[i.has(t),e[t]]}}function ue(e,t=JSON.parse){try{return t(sessionStorage[e Line truncated import{F as e,I as t,P as n,Q as r,X as i,nt as a,r as o,t as s}from"./39JK5HWF.js";var c=class{constructor(e,t){this.status=e,this.body=typeof t==`string`?{message:t}:t||{message:`Error: ${e}`}}toString(){return JSON.stringify(this.body)}},l=class{constructor(e,t){try{new Headers({location:t})}catch{throw Error(`Invalid redirect location ${JSON.stringify(t)}: this string contains characters that cannot be used in HTTP headers`)}this.status=e,this.location=t}},u=class extends Error{constructor(e,t,n){super(n),this.status=e,this.text=t}};new URL(`sveltekit-internal://`);function d(e,t){return e===`/`||t===`ignore`?e:t===`never`?e.endsWith(`/`)?e.slice(0,-1):e:t===`always`&&!e.endsWith(`/`)?e+`/`:e}function f(e){return e.split(`%25`).map(decodeURI).join(`%25`)}function p(e){for(let t in e)e[t]=decodeURIComponent(e[t]);return e}function m({href:e}){return e.split(`#`)[0]}function h(e,t,n,r=!1){let i=new URL(e);Object.defineProperty(i,"searchParams",{value:new Proxy(i.searchParams,{get(e,r){if(r===`get`||r===`getAll`||r===`has`)return(t,...i)=>(n(t),e[r](t,...i));t();let i=Reflect.get(e,r);return typeof i==`function`?i.bind(e):i}}),enumerable:!0,configurable:!0});let a=[`href`,`pathname`,`search`,`toString`,`toJSON`];r&&a.push(`hash`);for(let n of a)Object.defineProperty(i,n,{get(){return t(),e[n]},enumerable:!0,configurable:!0});return i}function g(){}function _(...e){let t=5381;for(let n of e)if(typeof n==`string`){let e=n.length;for(;e;)t=t*33^n.charCodeAt(--e)}else if(ArrayBuffer.isView(n)){let e=new Uint8Array(n.buffer,n.byteOffset,n.byteLength),r=e.length;for(;r;)t=t*33^e[--r]}else throw TypeError(`value must be a string or TypedArray`);return(t>>>0).toString(36)}new TextEncoder;function v(e){let t=atob(e),n=new Uint8Array(t.length);for(let e=0;e<t.length;e++)n[e]=t.charCodeAt(e);return n}var ee=window.fetch;window.fetch=(e,t)=>((e instanceof Request?e.method:t?.method||`GET`)!==`GET`&&y.delete(x(e)),ee(e,t));var y=new Map;function b(e,t){let n=x(e,t),r=document.querySelector(n);if(r?.textContent){r.remove();let{body:e,...t}=JSON.parse(r.textContent);r.getAttribute(`data-b64`)!==null&&(e=v(e));let i=r.getAttribute(`data-ttl`);return i&&y.set(n,{body:e,init:t,ttl:1e3*Number(i)}),Promise.resolve(new Response(e,t))}return window.fetch(e,t)}function te(e,t,n){if(y.size>0){let t=x(e,n),r=y.get(t);if(r){if(performance.now()<r.ttl&&[`default`,`force-cache`,`only-if-cached`,void 0].includes(n?.cache))return new Response(r.body,r.init);y.delete(t)}}return window.fetch(t,n)}function x(e,t){let n=`script[data-sveltekit-fetched][data-url=${JSON.stringify(e instanceof Request?e.url:e)}]`;if(t?.headers||t?.body){let e=[];t.headers&&e.push([...new Headers(t.headers)].join(`,`)),t.body&&(typeof t.body==`string`||ArrayBuffer.isView(t.body))&&e.push(t.body),n+=`[data-hash="${_(...e)}"]`}return n}var ne=/^(\[)?(\.\.\.)?(\w+)(?:=(\w+))?(\])?$/,re=/^\/\((?:[^)]+)\)$/;function ie(e){let t=[];return{pattern:e===`/`||re.test(e)?/^\/$/:RegExp(`^${oe(e).map(e=>{let n=/^\[\.\.\.(\w+)(?:=(\w+))?\]$/.exec(e);if(n)return t.push({name:n[1],matcher:n[2],optional:!1,rest:!0,chained:!0}),`(?:/([^]*))?`;let r=/^\[\[(\w+)(?:=(\w+))?\]\]$/.exec(e);if(r)return t.push({name:r[1],matcher:r[2],optional:!0,rest:!1,chained:!0}),`(?:/([^/]+))?`;if(!e)return;let i=e.split(/\[(.+?)\](?!\])/);return`/`+i.map((e,n)=>{if(n%2){if(e.startsWith(`x+`))return ce(String.fromCharCode(parseInt(e.slice(2),16)));if(e.startsWith(`u+`))return ce(String.fromCharCode(...e.slice(2).split(`-`).map(e=>parseInt(e,16))));let[,r,a,o,s]=ne.exec(e);return t.push({name:o,matcher:s,optional:!!r,rest:!!a,chained:a?n===1&&i[0]===``:!1}),a?`([^]*?)`:r?`([^/]*)?`:`([^/]+?)`}return ce(e)}).join(``)}).join(``)}/?$`),params:t}}function ae(e){return e!==``&&!/^\([^)]+\)$/.test(e)}function oe(e){return e.slice(1).split(`/`).filter(ae)}function se(e,t,n){let r={},i=e.slice(1),a=i.filter(e=>e!==void 0),o=0;for(let e=0;e<t.length;e+=1){let s=t[e],c=i[e-o];if(s.chained&&s.rest&&o&&(c=i.slice(e-o,e+1).filter(e=>e).join(`/`),o=0),c===void 0)if(s.rest)c=``;else continue;if(!s.matcher||n[s.matcher](c)){r[s.name]=c;let n=t[e+1],l=i[e+1];n&&!n.rest&&n.optional&&l&&s.chained&&(o=0),!n&&!l&&Object.keys(r).length===a.length&&(o=0);continue}if(s.optional&&s.chained){o++;continue}return}if(!o)return r}function ce(e){return e.normalize().replace(/[[\]]/g,`\\$&`).replace(/%/g,`%25`).replace(/\//g,`%2[Ff]`).replace(/\?/g,`%3[Ff]`).replace(/#/g,`%23`).replace(/[.*+?^${}()|\\]/g,`\\$&`)}function le({nodes:e,server_loads:t,dictionary:n,matchers:r}){let i=new Set(t);return Object.entries(n).map(([t,[n,i,s]])=>{let{pattern:c,params:l}=ie(t),u={id:t,exec:e=>{let t=c.exec(e);if(t)return se(t,l,r)},errors:[1,...s||[]].map(t=>e[t]),layouts:[0,...i||[]].map(o),leaf:a(n)};return u.errors.length=u.layouts.length=Math.max(u.errors.length,u.layouts.length),u});function a(t){let n=t<0;return n&&(t=~t),[n,e[t]]}function o(t){return t===void 0?t:[i.has(t),e[t]]}}function ue(e,t=JSON.parse){try{return t(sessionStorage[e Line truncated
@@ -1 +0,0 @@
import"./D3up1JC1.js";
@@ -1 +1 @@
import{J as e}from"./39JK5HWF.js";import{t}from"./D3up1JC1.js";import"./B5isPFOt.js";var n=e({user:null,status:`loading`}),r=()=>{localStorage.removeItem(`accessToken`),n.status=`unauthenticated`},i=()=>localStorage.getItem(`accessToken`),a=e=>{localStorage.setItem(`accessToken`,e)},o=`/api`,s=class extends Error{status;body;constructor(e,t){super(`API Error ${e}`),this.status=e,this.body=t}};async function c(e,n={}){let{auth:a=!0,headers:c,...u}=n,d={"Content-Type":`application/json`,...c},f=new Headers(d);if(a){let e=i();e&&f.append(`Authorization`,`Bearer ${e}`)}let p=await fetch(`${o}${e}`,{...u,headers:f});if(p.status===401&&(r(),t(`/login`)),!p.ok)throw new s(p.status,await l(p));if(p.status!==204)return p.json()}async function l(e){try{return await e.json()}catch{return null}}var u={get:(e,t)=>c(e,{...t,method:`GET`}),post:(e,t,n)=>c(e,{...n,method:`POST`,body:JSON.stringify(t)}),put:(e,t,n)=>c(e,{...n,method:`PUT`,body:JSON.stringify(t)}),delete:(e,t)=>c(e,{...t,method:`DELETE`})};export{a as i,n,r,u as t}; import{J as e}from"./39JK5HWF.js";import{t}from"./B0qrqti_.js";import"./DL7ZyBAF.js";var n=e({user:null,status:`loading`}),r=()=>{localStorage.removeItem(`accessToken`),n.status=`unauthenticated`},i=()=>localStorage.getItem(`accessToken`),a=e=>{localStorage.setItem(`accessToken`,e)},o=`/api`,s=class extends Error{status;body;constructor(e,t){super(`API Error ${e}`),this.status=e,this.body=t}};async function c(e,n={}){let{auth:a=!0,headers:c,...u}=n,d={"Content-Type":`application/json`,...c},f=new Headers(d);if(a){let e=i();e&&f.append(`Authorization`,`Bearer ${e}`)}let p=await fetch(`${o}${e}`,{...u,headers:f});if(p.status===401&&(r(),t(`/login`)),!p.ok)throw new s(p.status,await l(p));if(p.status!==204)return p.json()}async function l(e){try{return await e.json()}catch{return null}}var u={get:(e,t)=>c(e,{...t,method:`GET`}),post:(e,t,n)=>c(e,{...n,method:`POST`,body:JSON.stringify(t)}),put:(e,t,n)=>c(e,{...n,method:`PUT`,body:JSON.stringify(t)}),delete:(e,t)=>c(e,{...t,method:`DELETE`})};export{a as i,n,r,u as t};
@@ -1 +1 @@
import{t as e}from"./BqkZ0BZ5.js";var t={me:()=>e.get(`/user/me`),update:(t,n)=>e.put(`/user/${t}`,n),listAddresses:t=>e.get(`/user/${t}/address`),createAddress:(t,n)=>e.post(`/user/${t}/address`,n),updateAddress:(t,n)=>e.put(`/user/${t}/address`,n),deleteAddress:(t,n)=>e.delete(`/user/${t}/address/${n}`)};export{t}; import{t as e}from"./BC84wEQu.js";var t={me:()=>e.get(`/user/me`),update:(t,n)=>e.put(`/user/${t}`,n),listAddresses:t=>e.get(`/user/${t}/address`),createAddress:(t,n)=>e.post(`/user/${t}/address`,n),updateAddress:(t,n)=>e.put(`/user/${t}/address`,n),deleteAddress:(t,n)=>e.delete(`/user/${t}/address/${n}`)};export{t};
@@ -1,4 +1,4 @@
import{$ as e,A as t,B as n,D as r,E as i,G as a,I as o,J as s,K as c,L as l,M as u,N as d,O as f,P as p,Q as m,S as h,T as g,V as _,W as v,X as y,_ as b,a as x,at as ee,b as S,c as te,d as C,dt as w,f as ne,h as re,it as ie,j as ae,k as oe,l as se,lt as ce,m as le,n as ue,o as de,q as T,r as fe,u as pe,w as E,x as me}from"./39JK5HWF.js";import"./D3up1JC1.js";import"./xihTtKlq.js";var he=Math.min,D=Math.max,ge=Math.round,O=Math.floor,k=e=>({x:e,y:e}),A={left:`right`,right:`left`,bottom:`top`,top:`bottom`};function _e(e,t,n){return D(e,he(t,n))}function j(e,t){return typeof e==`function`?e(t):e}function M(e){return e.split(`-`)[0]}function N(e){return e.split(`-`)[1]}function P(e){return e===`x`?`y`:`x`}function ve(e){return e===`y`?`height`:`width`}function F(e){let t=e[0];return t===`t`||t===`b`?`y`:`x`}function I(e){return P(F(e))}function ye(e,t,n){n===void 0&&(n=!1);let r=N(e),i=I(e),a=ve(i),o=i===`x`?r===(n?`end`:`start`)?`right`:`left`:r===`start`?`bottom`:`top`;return t.reference[a]>t.floating[a]&&(o=Ee(o)),[o,Ee(o)]}function be(e){let t=Ee(e);return[L(e),t,L(t)]}function L(e){return e.includes(`start`)?e.replace(`start`,`end`):e.replace(`end`,`start`)}var xe=[`left`,`right`],Se=[`right`,`left`],R=[`top`,`bottom`],Ce=[`bottom`,`top`];function we(e,t,n){switch(e){case`top`:case`bottom`:return n?t?Se:xe:t?xe:Se;case`left`:case`right`:return t?R:Ce;default:return[]}}function Te(e,t,n,r){let i=N(e),a=we(M(e),n===`start`,r);return i&&(a=a.map(e=>e+`-`+i),t&&(a=a.concat(a.map(L)))),a}function Ee(e){let t=M(e);return A[t]+e.slice(t.length)}function De(e){return{top:e.top??0,right:e.right??0,bottom:e.bottom??0,left:e.left??0}}function Oe(e){return typeof e==`number`?{top:e,right:e,bottom:e,left:e}:De(e)}function ke(e){let{x:t,y:n,width:r,height:i}=e;return{width:r,height:i,top:n,left:t,right:t+r,bottom:n+i,x:t,y:n}}function Ae(e,t,n){let{reference:r,floating:i}=e,a=F(t),o=I(t),s=ve(o),c=M(t),l=a===`y`,u=r.x+r.width/2-i.width/2,d=r.y+r.height/2-i.height/2,f=r[s]/2-i[s]/2,p;switch(c){case`top`:p={x:u,y:r.y-i.height};break;case`bottom`:p={x:u,y:r.y+r.height};break;case`right`:p={x:r.x+r.width,y:d};break;case`left`:p={x:r.x-i.width,y:d};break;default:p={x:r.x,y:r.y}}let m=N(t);return m&&(p[o]+=f*(m===`end`?1:-1)*(n&&l?-1:1)),p}async function je(e,t){t===void 0&&(t={});let{x:n,y:r,platform:i,rects:a,elements:o,strategy:s}=e,{boundary:c=`clippingAncestors`,rootBoundary:l=`viewport`,elementContext:u=`floating`,altBoundary:d=!1,padding:f=0}=j(t,e),p=Oe(f),m=o[d?u===`floating`?`reference`:`floating`:u],h=ke(await i.getClippingRect({element:await(i.isElement==null?void 0:i.isElement(m))??!0?m:m.contextElement||await(i.getDocumentElement==null?void 0:i.getDocumentElement(o.floating)),boundary:c,rootBoundary:l,strategy:s})),g=u===`floating`?{x:n,y:r,width:a.floating.width,height:a.floating.height}:a.reference,_=await(i.getOffsetParent==null?void 0:i.getOffsetParent(o.floating)),v=await(i.isElement==null?void 0:i.isElement(_))&&await(i.getScale==null?void 0:i.getScale(_))||{x:1,y:1},y=ke(i.convertOffsetParentRelativeRectToViewportRelativeRect?await i.convertOffsetParentRelativeRectToViewportRelativeRect({elements:o,rect:g,offsetParent:_,strategy:s}):g);return{top:(h.top-y.top+p.top)/v.y,bottom:(y.bottom-h.bottom+p.bottom)/v.y,left:(h.left-y.left+p.left)/v.x,right:(y.right-h.right+p.right)/v.x}}var Me=50,Ne=async(e,t,n)=>{let{placement:r=`bottom`,strategy:i=`absolute`,middleware:a=[],platform:o}=n,s=o.detectOverflow?o:{...o,detectOverflow:je},c=await(o.isRTL==null?void 0:o.isRTL(t)),l=await o.getElementRects({reference:e,floating:t,strategy:i}),{x:u,y:d}=Ae(l,r,c),f=r,p=0,m={};for(let n=0;n<a.length;n++){let h=a[n];if(!h)continue;let{name:g,fn:_}=h,{x:v,y,data:b,reset:x}=await _({x:u,y:d,initialPlacement:r,placement:f,strategy:i,middlewareData:m,rects:l,platform:s,elements:{reference:e,floating:t}});u=v??u,d=y??d,m[g]={...m[g],...b},x&&p<Me&&(p++,typeof x==`object`&&(x.placement&&(f=x.placement),x.rects&&(l=x.rects===!0?await o.getElementRects({reference:e,floating:t,strategy:i}):x.rects),{x:u,y:d}=Ae(l,f,c)),n=-1)}return{x:u,y:d,placement:f,strategy:i,middlewareData:m}},Pe=function(e){return e===void 0&&(e={}),{name:`flip`,options:e,async fn(t){var n;let{placement:r,middlewareData:i,rects:a,initialPlacement:o,platform:s,elements:c}=t,{mainAxis:l=!0,crossAxis:u=!0,fallbackPlacements:d,fallbackStrategy:f=`bestFit`,fallbackAxisSideDirection:p=`none`,flipAlignment:m=!0,...h}=j(e,t);if((n=i.arrow)!=null&&n.alignmentOffset)return{};let g=M(r),_=F(o),v=M(o)===o,y=await(s.isRTL==null?void 0:s.isRTL(c.floating)),b=d||(v||!m?[Ee(o)]:be(o)),x=p!==`none`;!d&&x&&b.push(...Te(o,m,p,y));let ee=[o,...b],S=await s.detectOverflow(t,h),te=[],C=i.flip?.overflows||[];if(l&&te.push(S[g]),u){let e=ye(r,a,y);te.push(S[e[0]],S[e[1]])}if(C=[...C,{placement:r,overflows:te}],!te.every(e=>e<=0)){let e=(i.flip?.index||0)+1,t=ee[e];if(t&&(u!==`alignment`||_===F(t)||C.every(e=>F(e.placement)!==_||e.overflows[0]>0)))return{data:{index:e,overflows:C},reLine truncated import{$ as e,A as t,B as n,D as r,E as i,G as a,I as o,J as s,K as c,L as l,M as u,N as d,O as f,P as p,Q as m,S as h,T as g,V as _,W as v,X as y,_ as b,a as x,at as ee,b as S,c as te,d as C,dt as w,f as ne,h as re,it as ie,j as ae,k as oe,l as se,lt as ce,m as le,n as ue,o as de,q as T,r as fe,u as pe,w as E,x as me}from"./39JK5HWF.js";import"./B0qrqti_.js";import"./xihTtKlq.js";var he=Math.min,D=Math.max,ge=Math.round,O=Math.floor,k=e=>({x:e,y:e}),A={left:`right`,right:`left`,bottom:`top`,top:`bottom`};function _e(e,t,n){return D(e,he(t,n))}function j(e,t){return typeof e==`function`?e(t):e}function M(e){return e.split(`-`)[0]}function N(e){return e.split(`-`)[1]}function P(e){return e===`x`?`y`:`x`}function ve(e){return e===`y`?`height`:`width`}function F(e){let t=e[0];return t===`t`||t===`b`?`y`:`x`}function I(e){return P(F(e))}function ye(e,t,n){n===void 0&&(n=!1);let r=N(e),i=I(e),a=ve(i),o=i===`x`?r===(n?`end`:`start`)?`right`:`left`:r===`start`?`bottom`:`top`;return t.reference[a]>t.floating[a]&&(o=Ee(o)),[o,Ee(o)]}function be(e){let t=Ee(e);return[L(e),t,L(t)]}function L(e){return e.includes(`start`)?e.replace(`start`,`end`):e.replace(`end`,`start`)}var xe=[`left`,`right`],Se=[`right`,`left`],R=[`top`,`bottom`],Ce=[`bottom`,`top`];function we(e,t,n){switch(e){case`top`:case`bottom`:return n?t?Se:xe:t?xe:Se;case`left`:case`right`:return t?R:Ce;default:return[]}}function Te(e,t,n,r){let i=N(e),a=we(M(e),n===`start`,r);return i&&(a=a.map(e=>e+`-`+i),t&&(a=a.concat(a.map(L)))),a}function Ee(e){let t=M(e);return A[t]+e.slice(t.length)}function De(e){return{top:e.top??0,right:e.right??0,bottom:e.bottom??0,left:e.left??0}}function Oe(e){return typeof e==`number`?{top:e,right:e,bottom:e,left:e}:De(e)}function ke(e){let{x:t,y:n,width:r,height:i}=e;return{width:r,height:i,top:n,left:t,right:t+r,bottom:n+i,x:t,y:n}}function Ae(e,t,n){let{reference:r,floating:i}=e,a=F(t),o=I(t),s=ve(o),c=M(t),l=a===`y`,u=r.x+r.width/2-i.width/2,d=r.y+r.height/2-i.height/2,f=r[s]/2-i[s]/2,p;switch(c){case`top`:p={x:u,y:r.y-i.height};break;case`bottom`:p={x:u,y:r.y+r.height};break;case`right`:p={x:r.x+r.width,y:d};break;case`left`:p={x:r.x-i.width,y:d};break;default:p={x:r.x,y:r.y}}let m=N(t);return m&&(p[o]+=f*(m===`end`?1:-1)*(n&&l?-1:1)),p}async function je(e,t){t===void 0&&(t={});let{x:n,y:r,platform:i,rects:a,elements:o,strategy:s}=e,{boundary:c=`clippingAncestors`,rootBoundary:l=`viewport`,elementContext:u=`floating`,altBoundary:d=!1,padding:f=0}=j(t,e),p=Oe(f),m=o[d?u===`floating`?`reference`:`floating`:u],h=ke(await i.getClippingRect({element:await(i.isElement==null?void 0:i.isElement(m))??!0?m:m.contextElement||await(i.getDocumentElement==null?void 0:i.getDocumentElement(o.floating)),boundary:c,rootBoundary:l,strategy:s})),g=u===`floating`?{x:n,y:r,width:a.floating.width,height:a.floating.height}:a.reference,_=await(i.getOffsetParent==null?void 0:i.getOffsetParent(o.floating)),v=await(i.isElement==null?void 0:i.isElement(_))&&await(i.getScale==null?void 0:i.getScale(_))||{x:1,y:1},y=ke(i.convertOffsetParentRelativeRectToViewportRelativeRect?await i.convertOffsetParentRelativeRectToViewportRelativeRect({elements:o,rect:g,offsetParent:_,strategy:s}):g);return{top:(h.top-y.top+p.top)/v.y,bottom:(y.bottom-h.bottom+p.bottom)/v.y,left:(h.left-y.left+p.left)/v.x,right:(y.right-h.right+p.right)/v.x}}var Me=50,Ne=async(e,t,n)=>{let{placement:r=`bottom`,strategy:i=`absolute`,middleware:a=[],platform:o}=n,s=o.detectOverflow?o:{...o,detectOverflow:je},c=await(o.isRTL==null?void 0:o.isRTL(t)),l=await o.getElementRects({reference:e,floating:t,strategy:i}),{x:u,y:d}=Ae(l,r,c),f=r,p=0,m={};for(let n=0;n<a.length;n++){let h=a[n];if(!h)continue;let{name:g,fn:_}=h,{x:v,y,data:b,reset:x}=await _({x:u,y:d,initialPlacement:r,placement:f,strategy:i,middlewareData:m,rects:l,platform:s,elements:{reference:e,floating:t}});u=v??u,d=y??d,m[g]={...m[g],...b},x&&p<Me&&(p++,typeof x==`object`&&(x.placement&&(f=x.placement),x.rects&&(l=x.rects===!0?await o.getElementRects({reference:e,floating:t,strategy:i}):x.rects),{x:u,y:d}=Ae(l,f,c)),n=-1)}return{x:u,y:d,placement:f,strategy:i,middlewareData:m}},Pe=function(e){return e===void 0&&(e={}),{name:`flip`,options:e,async fn(t){var n;let{placement:r,middlewareData:i,rects:a,initialPlacement:o,platform:s,elements:c}=t,{mainAxis:l=!0,crossAxis:u=!0,fallbackPlacements:d,fallbackStrategy:f=`bestFit`,fallbackAxisSideDirection:p=`none`,flipAlignment:m=!0,...h}=j(e,t);if((n=i.arrow)!=null&&n.alignmentOffset)return{};let g=M(r),_=F(o),v=M(o)===o,y=await(s.isRTL==null?void 0:s.isRTL(c.floating)),b=d||(v||!m?[Ee(o)]:be(o)),x=p!==`none`;!d&&x&&b.push(...Te(o,m,p,y));let ee=[o,...b],S=await s.detectOverflow(t,h),te=[],C=i.flip?.overflows||[];if(l&&te.push(S[g]),u){let e=ye(r,a,y);te.push(S[e[0]],S[e[1]])}if(C=[...C,{placement:r,overflows:te}],!te.every(e=>e<=0)){let e=(i.flip?.index||0)+1,t=ee[e];if(t&&(u!==`alignment`||_===F(t)||C.every(e=>F(e.placement)!==_||e.overflows[0]>0)))return{data:{index:e,overflows:C},reLine truncated
3.908-3.747c0.533-0.481 1.141-0.446 1.574 0 0.436 0.445 0.408 1.197 0 3.908-3.747c0.533-0.481 1.141-0.446 1.574 0 0.436 0.445 0.408 1.197 0
1.615-0.406 0.418-4.695 4.502-4.695 4.502-0.217 0.223-0.502 1.615-0.406 0.418-4.695 4.502-4.695 4.502-0.217 0.223-0.502
0.335-0.787 0.335s-0.57-0.112-0.789-0.335c0 0.335-0.787 0.335s-0.57-0.112-0.789-0.335c0
@@ -1 +1 @@
import{t as e}from"./BqkZ0BZ5.js";var t={register:t=>e.post(`/auth/register`,t),status:()=>e.get(`/auth/status`),login:t=>e.post(`/auth/login`,t)};export{t}; import{t as e}from"./BC84wEQu.js";var t={register:t=>e.post(`/auth/register`,t),status:()=>e.get(`/auth/status`),login:t=>e.post(`/auth/login`,t)};export{t};
@@ -0,0 +1 @@
import"./B0qrqti_.js";
@@ -1 +1 @@
import{t as e}from"./BqkZ0BZ5.js";var t={providerTypes:()=>e.get(`/provider/providerTypes`),getProviders:t=>e.get(`/provider/${t}`),getProvider:(t,n)=>e.get(`/provider/${t}/${n}`),createProvider:(t,n)=>e.post(`/provider/${t}`,n),updateProvider:(t,n)=>e.put(`/provider/${t}`,n),deleteProvider:(t,n)=>e.delete(`/provider/${t}/${n}`),getAvailableModels:(t,n)=>e.post(`/provider/${t}/avalable-models`,n),getProvidersModels:t=>e.get(`/provider/${t}/models`)};export{t}; import{t as e}from"./BC84wEQu.js";var t={providerTypes:()=>e.get(`/provider/providerTypes`),getProviders:t=>e.get(`/provider/${t}`),getProvider:(t,n)=>e.get(`/provider/${t}/${n}`),createProvider:(t,n)=>e.post(`/provider/${t}`,n),updateProvider:(t,n)=>e.put(`/provider/${t}`,n),deleteProvider:(t,n)=>e.delete(`/provider/${t}/${n}`),getAvailableModels:(t,n)=>e.post(`/provider/${t}/avalable-models`,n),getProvidersModels:t=>e.get(`/provider/${t}/models`)};export{t};
@@ -1 +1 @@
import"./39JK5HWF.js";import{a as e,r as t}from"./D3up1JC1.js";var n={get data(){return e.data},get error(){return e.error},get form(){return e.form},get params(){return e.params},get route(){return e.route},get state(){return e.state},get status(){return e.status},get url(){return e.url}};t.updated.check;var r=n;export{r as t}; import"./39JK5HWF.js";import{a as e,r as t}from"./B0qrqti_.js";var n={get data(){return e.data},get error(){return e.error},get form(){return e.form},get params(){return e.params},get route(){return e.route},get state(){return e.state},get status(){return e.status},get url(){return e.url}};t.updated.check;var r=n;export{r as t};
@@ -1,2 +1,2 @@
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["../nodes/0.DNLpy_rd.js","../chunks/39JK5HWF.js","../chunks/xihTtKlq.js","../chunks/DTCALkcP.js","../assets/0.Bdga2HVs.css","../nodes/1.Cvrs0-W0.js","../chunks/D1d5-1oo.js","../chunks/D3up1JC1.js","../nodes/2.Bs3bPWro.js","../chunks/B5isPFOt.js","../chunks/BqkZ0BZ5.js","../chunks/BEgKbVuy.js","../chunks/D8dI9nx6.js","../chunks/BRXBpvt8.js","../nodes/3.BD_oZvMH.js","../nodes/4.Dw1ji0S9.js","../nodes/5.BV8wWprr.js","../nodes/6.iWRSAZEv.js","../nodes/7.CJ-_CrAo.js","../nodes/8.D-K-fxzc.js","../chunks/D35bQ8xD.js","../chunks/DRdGzrsF.js","../chunks/D4uBfDnr.js","../nodes/9.C_kOH_FX.js","../nodes/10.2kRbJUBr.js","../nodes/11.3PxmG5oJ.js","../nodes/12.B5iUSTUw.js","../chunks/D3MDK1zR.js","../nodes/13.CjlegDH_.js","../nodes/14.CFcHzpcm.js","../nodes/15.SpasLkf4.js","../nodes/16.TiX4VGhr.js","../nodes/17.wrIvaz1M.js","../nodes/18.BfPDxxm9.js","../chunks/DNQ6C3zq.js","../nodes/19.Bhx_NIDl.js"])))=>i.map(i=>d[i]); const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["../nodes/0.WTbeuaG5.js","../chunks/39JK5HWF.js","../chunks/xihTtKlq.js","../chunks/DTCALkcP.js","../assets/0.PdzDvXHc.css","../nodes/1.HToxGbTu.js","../chunks/DtPl1hOB.js","../chunks/B0qrqti_.js","../nodes/2.DJpmXDpd.js","../chunks/DL7ZyBAF.js","../chunks/BC84wEQu.js","../chunks/BDef5Ccs.js","../chunks/D8dI9nx6.js","../chunks/BRXBpvt8.js","../nodes/3.DQ-wAvIJ.js","../nodes/4.Dw1ji0S9.js","../nodes/5.BV8wWprr.js","../nodes/6.iWRSAZEv.js","../nodes/7.CJ-_CrAo.js","../nodes/8.TeTitTKA.js","../chunks/DOY2HMbt.js","../chunks/DRdGzrsF.js","../chunks/BorM6hBh.js","../nodes/9.C_kOH_FX.js","../nodes/10.2kRbJUBr.js","../nodes/11.3PxmG5oJ.js","../nodes/12.DXjVg3KD.js","../chunks/D3MDK1zR.js","../nodes/13.CjlegDH_.js","../nodes/14.CFcHzpcm.js","../nodes/15.SpasLkf4.js","../nodes/16.TiX4VGhr.js","../nodes/17.D_IcqSXn.js","../nodes/18.CljvOcq_.js","../chunks/DFI7Zp3t.js","../nodes/19.B7Etxitc.js"])))=>i.map(i=>d[i]);
import{$ as e,A as t,B as n,D as r,E as i,G as a,H as o,I as s,K as c,O as l,P as u,Q as d,T as f,V as p,X as m,a as h,at as g,dt as _,i as v,it as y,o as b,q as x,r as S,w as C,y as w}from"../chunks/39JK5HWF.js";import"../chunks/xihTtKlq.js";var T=`modulepreload`,E=function(e,t){return new URL(e,t).href},D={},O=function(e,t,n){let r=Promise.resolve();if(t&&t.length>0){let e=document.getElementsByTagName(`link`),i=document.querySelector(`meta[property=csp-nonce]`),a=i?.nonce||i?.getAttribute(`nonce`);function o(e){return Promise.all(e.map(e=>Promise.resolve(e).then(e=>({status:`fulfilled`,value:e}),e=>({status:`rejected`,reason:e}))))}function s(e){return import.meta.resolve?import.meta.resolve(e):new URL(e,import.meta.url).href}r=o(t.map(t=>{if(t=E(t,n),t=s(t),t in D)return;D[t]=!0;let r=t.endsWith(`.css`);for(let n=e.length-1;n>=0;n--){let i=e[n];if(i.href===t&&(!r||i.rel===`stylesheet`))return}let i=document.createElement(`link`);if(i.rel=r?`stylesheet`:T,r||(i.as=`script`),i.crossOrigin=``,i.href=t,a&&i.setAttribute(`nonce`,a),document.head.appendChild(i),r)return new Promise((e,n)=>{i.addEventListener(`load`,e),i.addEventListener(`error`,()=>n(Error(`Unable to preload CSS for ${t}`)))})}))}function i(e){let t=new Event(`vite:preloadError`,{cancelable:!0});if(t.payload=e,window.dispatchEvent(t),!t.defaultPrevented)throw e}return r.then(t=>{for(let e of t||[])e.status===`rejected`&&i(e.reason);return e().catch(i)})},k={},A=l(`<div id="svelte-announcer" aria-live="assertive" aria-atomic="true" style="position: absolute; left: 0; top: 0; clip: rect(0 0 0 0); clip-path: inset(50%); overflow: hidden; white-space: nowrap; width: 1px; height: 1px"><!></div>`),j=l(`<!> <!>`,1);function M(l,v){g(v,!0);let T=h(v,`components`,23,()=>[]),E=h(v,`data_0`,3,null),D=h(v,`data_1`,3,null),O=h(v,`data_2`,3,null);o(()=>v.stores.page.set(v.page)),p(()=>{v.stores,v.page,v.constructors,T(),v.form,E(),D(),O(),v.stores.page.notify()});let k=d(!1),M=d(!1),N=d(null);S(()=>{let e=v.stores.page.subscribe(()=>{u(k)&&(m(M,!0),s().then(()=>{m(N,document.title||`untitled page`,!0)}))});return m(k,!0),e});let P=e(()=>v.constructors[2]);var F=j(),I=c(F),L=t=>{let n=e(()=>v.constructors[0]);var a=r(),o=c(a);w(o,()=>u(n),(t,n)=>{b(n(t,{get data(){return E()},get form(){return v.form},get params(){return v.page.params},children:(t,n)=>{var a=r(),o=c(a),s=t=>{let n=e(()=>v.constructors[1]);var a=r(),o=c(a);w(o,()=>u(n),(e,t)=>{b(t(e,{get data(){return D()},get form(){return v.form},get params(){return v.page.params},children:(e,t)=>{var n=r(),a=c(n);w(a,()=>u(P),(e,t)=>{b(t(e,{get data(){return O()},get form(){return v.form},get params(){return v.page.params}}),e=>T()[2]=e,()=>T()?.[2])}),i(e,n)},$$slots:{default:!0}}),e=>T()[1]=e,()=>T()?.[1])}),i(t,a)},l=t=>{let n=e(()=>v.constructors[1]);var a=r(),o=c(a);w(o,()=>u(n),(e,t)=>{b(t(e,{get data(){return D()},get form(){return v.form},get params(){return v.page.params}}),e=>T()[1]=e,()=>T()?.[1])}),i(t,a)};C(o,e=>{v.constructors[2]?e(s):e(l,-1)}),i(t,a)},$$slots:{default:!0}}),e=>T()[0]=e,()=>T()?.[0])}),i(t,a)},R=t=>{let n=e(()=>v.constructors[0]);var a=r(),o=c(a);w(o,()=>u(n),(e,t)=>{b(t(e,{get data(){return E()},get form(){return v.form},get params(){return v.page.params}}),e=>T()[0]=e,()=>T()?.[0])}),i(t,a)};C(I,e=>{v.constructors[1]?e(L):e(R,-1)});var z=x(I,2),B=e=>{var r=A(),o=a(r),s=e=>{var r=t();n(()=>f(r,u(N))),i(e,r)};C(o,e=>{u(M)&&e(s)}),_(r),i(e,r)};C(z,e=>{u(k)&&e(B)}),i(l,F),y()}var N=v(M),P=[()=>O(()=>import(`../nodes/0.DNLpy_rd.js`),__vite__mapDeps([0,1,2,3,4]),import.meta.url),()=>O(()=>import(`../nodes/1.Cvrs0-W0.js`),__vite__mapDeps([5,1,2,6,7]),import.meta.url),()=>O(()=>import(`../nodes/2.Bs3bPWro.js`),__vite__mapDeps([8,1,7,2,3,6,9,10,11,12,13]),import.meta.url),()=>O(()=>import(`../nodes/3.BD_oZvMH.js`),__vite__mapDeps([14,1,7,2,9,12]),import.meta.url),()=>O(()=>import(`../nodes/4.Dw1ji0S9.js`),__vite__mapDeps([15,1,2]),import.meta.url),()=>O(()=>import(`../nodes/5.BV8wWprr.js`),__vite__mapDeps([16,1,2]),import.meta.url),()=>O(()=>import(`../nodes/6.iWRSAZEv.js`),__vite__mapDeps([17,1,2]),import.meta.url),()=>O(()=>import(`../nodes/7.CJ-_CrAo.js`),__vite__mapDeps([18,1,2]),import.meta.url),()=>O(()=>import(`../nodes/8.D-K-fxzc.js`),__vite__mapDeps([19,1,2,10,7,9,20,21,22]),import.meta.url),()=>O(()=>import(`../nodes/9.C_kOH_FX.js`),__vite__mapDeps([23,1,2]),import.meta.url),()=>O(()=>import(`../nodes/10.2kRbJUBr.js`),__vite__mapDeps([24,1,2]),import.meta.url),()=>O(()=>import(`../nodes/11.3PxmG5oJ.js`),__vite__mapDeps([25,1,2]),import.meta.url),()=>O(()=>import(`../nodes/12.B5iUSTUw.js`),__vite__mapDeps([26,1,2,20,10,7,9,21,22,27]),import.meta.url),()=>O(()=>import(`../nodes/13.CjlegDH_.js`),__vite__mapDeps([28,1,2]),import.meta.url),()=>O(()=>import(`../nodes/14.CFcHzpcm.js`),__vite__mapDeps([29,1,2]),import.meta.url),()=>O(()=>import(`../nodes/15.SpasLkf4.js`),__vite__mapDeps([30,1,2]),import.meta.url),()=>O(()=>import(`../nodes/16.TiX4VGhr.js`),__vite__mapDeps([31,1,Line truncated import{$ as e,A as t,B as n,D as r,E as i,G as a,H as o,I as s,K as c,O as l,P as u,Q as d,T as f,V as p,X as m,a as h,at as g,dt as _,i as v,it as y,o as b,q as x,r as S,w as C,y as w}from"../chunks/39JK5HWF.js";import"../chunks/xihTtKlq.js";var T=`modulepreload`,E=function(e,t){return new URL(e,t).href},D={},O=function(e,t,n){let r=Promise.resolve();if(t&&t.length>0){let e=document.getElementsByTagName(`link`),i=document.querySelector(`meta[property=csp-nonce]`),a=i?.nonce||i?.getAttribute(`nonce`);function o(e){return Promise.all(e.map(e=>Promise.resolve(e).then(e=>({status:`fulfilled`,value:e}),e=>({status:`rejected`,reason:e}))))}function s(e){return import.meta.resolve?import.meta.resolve(e):new URL(e,import.meta.url).href}r=o(t.map(t=>{if(t=E(t,n),t=s(t),t in D)return;D[t]=!0;let r=t.endsWith(`.css`);for(let n=e.length-1;n>=0;n--){let i=e[n];if(i.href===t&&(!r||i.rel===`stylesheet`))return}let i=document.createElement(`link`);if(i.rel=r?`stylesheet`:T,r||(i.as=`script`),i.crossOrigin=``,i.href=t,a&&i.setAttribute(`nonce`,a),document.head.appendChild(i),r)return new Promise((e,n)=>{i.addEventListener(`load`,e),i.addEventListener(`error`,()=>n(Error(`Unable to preload CSS for ${t}`)))})}))}function i(e){let t=new Event(`vite:preloadError`,{cancelable:!0});if(t.payload=e,window.dispatchEvent(t),!t.defaultPrevented)throw e}return r.then(t=>{for(let e of t||[])e.status===`rejected`&&i(e.reason);return e().catch(i)})},k={},A=l(`<div id="svelte-announcer" aria-live="assertive" aria-atomic="true" style="position: absolute; left: 0; top: 0; clip: rect(0 0 0 0); clip-path: inset(50%); overflow: hidden; white-space: nowrap; width: 1px; height: 1px"><!></div>`),j=l(`<!> <!>`,1);function M(l,v){g(v,!0);let T=h(v,`components`,23,()=>[]),E=h(v,`data_0`,3,null),D=h(v,`data_1`,3,null),O=h(v,`data_2`,3,null);o(()=>v.stores.page.set(v.page)),p(()=>{v.stores,v.page,v.constructors,T(),v.form,E(),D(),O(),v.stores.page.notify()});let k=d(!1),M=d(!1),N=d(null);S(()=>{let e=v.stores.page.subscribe(()=>{u(k)&&(m(M,!0),s().then(()=>{m(N,document.title||`untitled page`,!0)}))});return m(k,!0),e});let P=e(()=>v.constructors[2]);var F=j(),I=c(F),L=t=>{let n=e(()=>v.constructors[0]);var a=r(),o=c(a);w(o,()=>u(n),(t,n)=>{b(n(t,{get data(){return E()},get form(){return v.form},get params(){return v.page.params},children:(t,n)=>{var a=r(),o=c(a),s=t=>{let n=e(()=>v.constructors[1]);var a=r(),o=c(a);w(o,()=>u(n),(e,t)=>{b(t(e,{get data(){return D()},get form(){return v.form},get params(){return v.page.params},children:(e,t)=>{var n=r(),a=c(n);w(a,()=>u(P),(e,t)=>{b(t(e,{get data(){return O()},get form(){return v.form},get params(){return v.page.params}}),e=>T()[2]=e,()=>T()?.[2])}),i(e,n)},$$slots:{default:!0}}),e=>T()[1]=e,()=>T()?.[1])}),i(t,a)},l=t=>{let n=e(()=>v.constructors[1]);var a=r(),o=c(a);w(o,()=>u(n),(e,t)=>{b(t(e,{get data(){return D()},get form(){return v.form},get params(){return v.page.params}}),e=>T()[1]=e,()=>T()?.[1])}),i(t,a)};C(o,e=>{v.constructors[2]?e(s):e(l,-1)}),i(t,a)},$$slots:{default:!0}}),e=>T()[0]=e,()=>T()?.[0])}),i(t,a)},R=t=>{let n=e(()=>v.constructors[0]);var a=r(),o=c(a);w(o,()=>u(n),(e,t)=>{b(t(e,{get data(){return E()},get form(){return v.form},get params(){return v.page.params}}),e=>T()[0]=e,()=>T()?.[0])}),i(t,a)};C(I,e=>{v.constructors[1]?e(L):e(R,-1)});var z=x(I,2),B=e=>{var r=A(),o=a(r),s=e=>{var r=t();n(()=>f(r,u(N))),i(e,r)};C(o,e=>{u(M)&&e(s)}),_(r),i(e,r)};C(z,e=>{u(k)&&e(B)}),i(l,F),y()}var N=v(M),P=[()=>O(()=>import(`../nodes/0.WTbeuaG5.js`),__vite__mapDeps([0,1,2,3,4]),import.meta.url),()=>O(()=>import(`../nodes/1.HToxGbTu.js`),__vite__mapDeps([5,1,2,6,7]),import.meta.url),()=>O(()=>import(`../nodes/2.DJpmXDpd.js`),__vite__mapDeps([8,1,7,2,3,6,9,10,11,12,13]),import.meta.url),()=>O(()=>import(`../nodes/3.DQ-wAvIJ.js`),__vite__mapDeps([14,1,7,2,9,12]),import.meta.url),()=>O(()=>import(`../nodes/4.Dw1ji0S9.js`),__vite__mapDeps([15,1,2]),import.meta.url),()=>O(()=>import(`../nodes/5.BV8wWprr.js`),__vite__mapDeps([16,1,2]),import.meta.url),()=>O(()=>import(`../nodes/6.iWRSAZEv.js`),__vite__mapDeps([17,1,2]),import.meta.url),()=>O(()=>import(`../nodes/7.CJ-_CrAo.js`),__vite__mapDeps([18,1,2]),import.meta.url),()=>O(()=>import(`../nodes/8.TeTitTKA.js`),__vite__mapDeps([19,1,2,10,7,9,12,20,21,22]),import.meta.url),()=>O(()=>import(`../nodes/9.C_kOH_FX.js`),__vite__mapDeps([23,1,2]),import.meta.url),()=>O(()=>import(`../nodes/10.2kRbJUBr.js`),__vite__mapDeps([24,1,2]),import.meta.url),()=>O(()=>import(`../nodes/11.3PxmG5oJ.js`),__vite__mapDeps([25,1,2]),import.meta.url),()=>O(()=>import(`../nodes/12.DXjVg3KD.js`),__vite__mapDeps([26,1,2,20,10,7,9,21,22,27]),import.meta.url),()=>O(()=>import(`../nodes/13.CjlegDH_.js`),__vite__mapDeps([28,1,2]),import.meta.url),()=>O(()=>import(`../nodes/14.CFcHzpcm.js`),__vite__mapDeps([29,1,2]),import.meta.url),()=>O(()=>import(`../nodes/15.SpasLkf4.js`),__vite__mapDeps([30,1,2]),import.meta.url),()=>O(()=>import(`../nodes/16.TiX4VGhr.js`),__vite__mapDeps([31Line truncated
@@ -1 +0,0 @@
import{n as e,s as t}from"../chunks/D3up1JC1.js";export{t as load_css,e as start};
@@ -0,0 +1 @@
import{n as e,s as t}from"../chunks/B0qrqti_.js";export{t as load_css,e as start};
@@ -1 +1 @@
import{B as e,E as t,G as n,K as r,O as i,T as a,at as o,dt as s,it as c,q as l}from"../chunks/39JK5HWF.js";import"../chunks/xihTtKlq.js";import{t as u}from"../chunks/D1d5-1oo.js";var d=i(`<h1> </h1> <p> </p>`,1);function f(i,f){o(f,!0);var p=d(),m=r(p),h=n(m,!0);s(m);var g=l(m,2),_=n(g,!0);s(g),e(()=>{a(h,u.status),a(_,u.error?.message)}),t(i,p),c()}export{f as component}; import{B as e,E as t,G as n,K as r,O as i,T as a,at as o,dt as s,it as c,q as l}from"../chunks/39JK5HWF.js";import"../chunks/xihTtKlq.js";import{t as u}from"../chunks/DtPl1hOB.js";var d=i(`<h1> </h1> <p> </p>`,1);function f(i,f){o(f,!0);var p=d(),m=r(p),h=n(m,!0);s(m);var g=l(m,2),_=n(g,!0);s(g),e(()=>{a(h,u.status),a(_,u.error?.message)}),t(i,p),c()}export{f as component};
@@ -1 +0,0 @@
import{B as e,C as t,D as n,E as r,G as i,J as a,K as o,M as s,N as c,O as l,P as u,Q as d,T as f,X as p,_ as m,a as h,at as g,c as _,dt as v,ft as y,it as b,j as x,q as S,r as C,s as w,u as T,ut as E,w as D,x as O,z as k}from"../chunks/39JK5HWF.js";import"../chunks/xihTtKlq.js";import{t as A}from"../chunks/D35bQ8xD.js";import{n as j,t as M}from"../chunks/DRdGzrsF.js";import{t as ee}from"../chunks/D4uBfDnr.js";import{n as N}from"../chunks/D3MDK1zR.js";var P=y({load:()=>F}),F=async({params:e})=>({title:`Paramétrage : Modèles`,back:`../settings`,space:e.space,providerTypes:await A.providerTypes()}),I=l(`<li><label><input type="checkbox"/> </label></li>`),L=l(`<div><div class="formgroup my-2"><form><div class="formelement"><label for="name">Nom</label> <input id="name" type="text" class="textinput" placeholder="nom affiché"/> <!></div> <div class="formelement"><label for="type" class="block my-1 flex-1">Type</label> <!> <!></div> <div class="formelement"><label for="baseUrl">URL</label> <input id="baseUrl" type="text" class="textinput" placeholder="http://127.0.0.1:8080/v1" autocomplete="off"/> <!></div> <div class="formelement"><label for="apiKey">Clé API</label> <input id="apiKey" type="password" class="textinput" placeholder="clé API (optionnel)" autocomplete="new-password"/> <!></div> <div class="formelement"><label for="models">Models</label> <button class="bt small">Afficher les modèles disponibles</button> <ol class="ml-2"></ol> <div><!></div></div> <div class="text-right"><button type="button" class="bt">Annuler</button> <button type="submit" class="bt mx-4">Enregistrer</button></div></form></div></div>`);function R(t,l){g(l,!0);let y=h(l,`item`,7),x=d(a(y().models.split(`|`))),C=d(a(y().models.split(`|`).map(e=>({value:e,selected:!0})))),N=new j,P=e=>{e.focus()},F=e=>{e.preventDefault(),y().models=u(x).join(`|`),l.onSave(y())},R=e=>{e.preventDefault(),l.onCancel()},z=e=>{e.preventDefault(),N.clear(),A.getAvailableModels(y().teamId,y()).then(e=>{p(C,e.map(e=>({value:e,selected:u(x).includes(e)})),!0)}).catch(e=>{N.set(`models`,e)})},B=()=>{p(x,u(C).filter(e=>e.selected).map(e=>e.value),!0)};var V=L(),H=i(V),U=i(H),W=i(U),G=S(i(W),2);T(G),k(()=>_(G,()=>y().name,e=>y().name=e)),m(G,e=>P?.(e));var te=S(G,2);M(te,{key:`name`,get errors(){return l.errors}}),v(W);var K=S(W,2),q=S(i(K),2);ee(q,{get items(){return l.providerTypes},valueMode:`id`,get value(){return y().type},set value(e){y().type=e}});var ne=S(q,2);M(ne,{key:`type`,get errors(){return l.errors}}),v(K);var J=S(K,2),Y=S(i(J),2);T(Y);var re=S(Y,2);M(re,{key:`baseUrl`,get errors(){return l.errors}}),v(J);var X=S(J,2),Z=S(i(X),2);T(Z);var ie=S(Z,2);M(ie,{key:`apiKey`,get errors(){return l.errors}}),v(X);var Q=S(X,2),ae=S(i(Q),2),$=S(ae,2);O($,21,()=>u(C),e=>e.value,(t,a,c)=>{var l=n(),d=o(l),p=t=>{var n=I(),o=i(n),c=i(o);T(c);var l=S(c);v(o),v(n),e(()=>f(l,` ${u(a).value??``}`)),s(`change`,c,B),w(c,()=>u(a).selected,e=>u(a).selected=e),r(t,n)};D(d,e=>{u(a).value&&e(p)}),r(t,l)}),v($);var oe=S($,2),se=i(oe);M(se,{key:`models`,get errors(){return N}}),v(oe),v(Q);var ce=S(Q,2),le=i(ce);E(2),v(ce),v(U),v(H),v(V),c(`submit`,U,F),s(`change`,U,()=>{l.errors.clear()}),_(Y,()=>y().baseUrl,e=>y().baseUrl=e),_(Z,()=>y().apiKey,e=>y().apiKey=e),s(`click`,ae,z),s(`click`,le,R),r(t,V),b()}x([`change`,`click`]);var z=l(`<div class="rounded border border-dashed p-2 border-stone-500 cursor-pointer" tabindex="0"><i class="icon-plus"></i> Ajouter un fournisseur</div>`),B=l(`<div class="rounded border p-2 my-2 border-stone-500 flex"><div class="flex-1"><span> </span> <span class="text-stone-500"> </span></div> <button class="bt small mx-1"><i class="icon-edit"></i></button> <button class="bt small mx-1"><i class="icon-trash-empty"></i></button></div>`),V=l(`<div class="container"><!> <!></div>`);function H(c,l){g(l,!0);let m=d(a([])),h=d(!1),_=d(null),y=d(a({})),x=new j;C(async()=>{p(m,await A.getProviders(l.data.space),!0)});let w=()=>{u(_)||(x.clear(),p(y,{},!0),u(y).models=``,p(h,!0))},T=()=>{p(h,!1)},E=e=>{e.name||x.set(`name`,`Le nom est obligatoire !`),e.type||x.set(`type`,`Le type est obligatoire !`),e.baseUrl&&!N(e.baseUrl)&&x.set(`baseUrl`,`Mauvaise URL !`),x.size===0&&A.createProvider(l.data.space,e).then(e=>{u(m).push(e),p(h,!1),p(y,{},!0)}).catch(e=>{console.log(e)})},k=()=>{p(_,null),p(y,{},!0)},M=e=>{e.name||x.set(`name`,`Le nom est obligatoire !`),e.type||x.set(`type`,`Le type est obligatoire !`),e.baseUrl&&!N(e.baseUrl)&&x.set(`baseUrl`,`Mauvaise URL !`),x.size===0&&A.updateProvider(l.data.space,e).then(e=>{p(m,u(m).map(t=>e.id===t.id?e:t),!0),p(_,null),p(y,{},!0)}).catch(e=>{console.log(e)})},ee=async e=>{x.clear(),!u(h)&&A.getProvider(l.data.space,e).then(t=>{p(m,u(m).map(n=>n.id===e?t:n),!0),p(_,e,!0)})},P=e=>{A.deleteProvider(l.data.space,e).then(()=>{p(m,u(m).filter(t=>t.id!==e),!0)}).catch(e=>{console.log(e)})};var F=V(),I=i(F),L=e=>{var i=n(),a=o(i);t(a,()=>`new`,e=>{R(e,{get item(){return u(y)},get errors(){return x},get providerTypes(){rLine truncated
@@ -0,0 +1 @@
import{B as e,C as t,D as n,E as r,G as i,J as a,K as o,L as s,M as c,N as l,O as u,P as d,Q as f,T as p,X as m,_ as h,a as g,at as _,c as v,dt as y,ft as b,it as x,j as S,q as C,s as ee,u as w,ut as T,w as E,x as D,z as O}from"../chunks/39JK5HWF.js";import"../chunks/xihTtKlq.js";import{t as k}from"../chunks/DOY2HMbt.js";import{n as A,t as j}from"../chunks/DRdGzrsF.js";import{t as M}from"../chunks/BorM6hBh.js";import{n as N}from"../chunks/D3MDK1zR.js";var P=b({load:()=>F}),F=async({params:e})=>({title:`Paramétrage : Modèles`,back:`../settings`,space:e.space,providerTypes:await k.providerTypes(),providers:await k.getProviders(e.space)}),I=u(`<li><label><input type="checkbox"/> </label></li>`),L=u(`<div><div class="formgroup my-2"><form><div class="formelement"><label for="name">Nom</label> <input id="name" type="text" class="textinput" placeholder="nom affiché"/> <!></div> <div class="formelement"><label for="type" class="block my-1 flex-1">Type</label> <!> <!></div> <div class="formelement"><label for="baseUrl">URL</label> <input id="baseUrl" type="text" class="textinput" placeholder="http://127.0.0.1:8080/v1" autocomplete="off"/> <!></div> <div class="formelement"><label for="apiKey">Clé API</label> <input id="apiKey" type="password" class="textinput" placeholder="clé API (optionnel)" autocomplete="new-password"/> <!></div> <div class="formelement"><label for="models">Models</label> <button class="bt small">Afficher les modèles disponibles</button> <ol class="ml-2"></ol> <div><!></div></div> <div class="text-right"><button type="button" class="bt">Annuler</button> <button type="submit" class="bt mx-4">Enregistrer</button></div></form></div></div>`);function R(t,s){_(s,!0);let u=g(s,`item`,7),b=f(a(u().models.split(`|`))),S=f(a(u().models.split(`|`).map(e=>({value:e,selected:!0})))),N=new A,P=e=>{e.focus()},F=e=>{e.preventDefault(),u().models=d(b).join(`|`),s.onSave(u())},R=e=>{e.preventDefault(),s.onCancel()},z=e=>{e.preventDefault(),N.clear(),k.getAvailableModels(u().teamId,u()).then(e=>{m(S,e.map(e=>({value:e,selected:d(b).includes(e)})),!0)}).catch(e=>{N.set(`models`,e)})},B=()=>{m(b,d(S).filter(e=>e.selected).map(e=>e.value),!0)};var V=L(),H=i(V),U=i(H),W=i(U),G=C(i(W),2);w(G),O(()=>v(G,()=>u().name,e=>u().name=e)),h(G,e=>P?.(e));var te=C(G,2);j(te,{key:`name`,get errors(){return s.errors}}),y(W);var K=C(W,2),q=C(i(K),2);M(q,{get items(){return s.providerTypes},valueMode:`id`,get value(){return u().type},set value(e){u().type=e}});var ne=C(q,2);j(ne,{key:`type`,get errors(){return s.errors}}),y(K);var J=C(K,2),Y=C(i(J),2);w(Y);var re=C(Y,2);j(re,{key:`baseUrl`,get errors(){return s.errors}}),y(J);var X=C(J,2),Z=C(i(X),2);w(Z);var ie=C(Z,2);j(ie,{key:`apiKey`,get errors(){return s.errors}}),y(X);var Q=C(X,2),ae=C(i(Q),2),$=C(ae,2);D($,21,()=>d(S),e=>e.value,(t,a,s)=>{var l=n(),u=o(l),f=t=>{var n=I(),o=i(n),s=i(o);w(s);var l=C(s);y(o),y(n),e(()=>p(l,` ${d(a).value??``}`)),c(`change`,s,B),ee(s,()=>d(a).selected,e=>d(a).selected=e),r(t,n)};E(u,e=>{d(a).value&&e(f)}),r(t,l)}),y($);var oe=C($,2),se=i(oe);j(se,{key:`models`,get errors(){return N}}),y(oe),y(Q);var ce=C(Q,2),le=i(ce);T(2),y(ce),y(U),y(H),y(V),l(`submit`,U,F),c(`change`,U,()=>{s.errors.clear()}),v(Y,()=>u().baseUrl,e=>u().baseUrl=e),v(Z,()=>u().apiKey,e=>u().apiKey=e),c(`click`,ae,z),c(`click`,le,R),r(t,V),x()}S([`change`,`click`]);var z=u(`<button class="rounded w-full border border-dashed p-2 border-stone-500 cursor-pointer" tabindex="0"><i class="icon-plus"></i> Ajouter un fournisseur</button>`),B=u(`<div class="rounded border p-2 my-2 border-stone-500 flex"><div class="flex-1"><span> </span> <span class="text-stone-500"> </span></div> <button class="bt small mx-1" aria-label="Modifier"><i class="icon-edit"></i></button> <button class="bt small mx-1" aria-label="Supprimer"><i class="icon-trash-empty"></i></button></div>`),V=u(`<div class="container"><!> <!></div>`);function H(l,u){_(u,!0);let h=f(a(s(()=>u.data.providers))),g=f(!1),v=f(null),b=f(a({})),S=new A,ee=()=>{d(v)||(S.clear(),m(b,{},!0),d(b).models=``,m(g,!0))},w=()=>{m(g,!1)},T=e=>{e.name||S.set(`name`,`Le nom est obligatoire !`),e.type||S.set(`type`,`Le type est obligatoire !`),e.baseUrl&&!N(e.baseUrl)&&S.set(`baseUrl`,`Mauvaise URL !`),S.size===0&&k.createProvider(u.data.space,e).then(e=>{d(h).push(e),m(g,!1),m(b,{},!0)}).catch(e=>{console.log(e)})},O=()=>{m(v,null),m(b,{},!0)},j=e=>{e.name||S.set(`name`,`Le nom est obligatoire !`),e.type||S.set(`type`,`Le type est obligatoire !`),e.baseUrl&&!N(e.baseUrl)&&S.set(`baseUrl`,`Mauvaise URL !`),S.size===0&&k.updateProvider(u.data.space,e).then(e=>{m(h,d(h).map(t=>e.id===t.id?e:t),!0),m(v,null),m(b,{},!0)}).catch(e=>{console.log(e)})},M=async e=>{S.clear(),!d(g)&&k.getProvider(u.data.space,e).then(t=>{m(h,d(h).map(n=>n.id===e?t:n),!0),m(v,e,!0)})},P=e=>{k.deleteProvider(u.data.space,e).then(()=>{m(h,d(h).filter(t=>t.id!==e),!0)}).catch(e=>{console.log(e)})};var F=V(),I=i(F),L=e=>{var i=n(),a=o(i);t(a,()=>`new`,e=>{R(e,{get item()Line truncated
@@ -0,0 +1 @@
import{$ as e,A as t,B as n,E as r,G as i,J as a,K as o,L as s,M as c,N as l,O as u,P as d,Q as f,T as p,V as m,X as h,_ as ee,a as g,at as te,c as _,dt as v,ft as y,h as b,it as ne,j as x,p as S,q as C,r as re,u as w,ut as T,w as E,x as ie,z as ae}from"../chunks/39JK5HWF.js";import"../chunks/xihTtKlq.js";import{n as oe,t as se}from"../chunks/DTCALkcP.js";import{t as D}from"../chunks/BC84wEQu.js";import{t as O}from"../chunks/BDef5Ccs.js";import{n as ce,t as k}from"../chunks/DRdGzrsF.js";import{t as le}from"../chunks/BorM6hBh.js";import{t as ue}from"../chunks/D3MDK1zR.js";var de=y({load:()=>fe}),fe=async()=>({title:`Profil`,me:await O.me()}),A=function(e){return e[e.None=0]=`None`,e[e.Changed=1]=`Changed`,e[e.Saving=2]=`Saving`,e[e.Saved=3]=`Saved`,e[e.Error=4]=`Error`,e}({}),pe={listTimezones:()=>D.get(`/misc/timezones`)},me=u(`<i class="incon-spin1 anomate-spin"></i> Enregistrement en cours`,1),he=u(`<i class="incon-ok anomate-spin"></i> Enregistrement réussi`,1),ge=u(`<button><!></button>`);function _e(e,a){te(a,!0);let o=g(a,`state`,7),s=g(a,`disabled`,3,!1);m(()=>{(o()===A.Saved||o()===A.Error)&&setTimeout(()=>{o(A.None)},2e3)});let l=()=>{a.handleClick&&(o()===A.Changed||o()===A.None)&&a.handleClick()};var u=ge();let d;var f=i(u),h=e=>{var i=t();n(()=>p(i,a.label)),r(e,i)},ee=e=>{var t=me();T(),r(e,t)},_=e=>{var t=he();T(),r(e,t)},y=e=>{var n=t(`Echec`);r(e,n)};E(f,e=>{o()==A.None||o()==A.Changed?e(h):o()==A.Saving?e(ee,1):o()==A.Saved?e(_,2):o()==A.Error&&e(y,3)}),v(u),n(()=>{u.disabled=s(),d=b(u,1,`bt`,null,d,{disabled:s(),changed:o()===A.Changed,saving:o()===A.Saving,saved:o()===A.Saved,error:o()===A.Error})}),c(`click`,u,l),r(e,u),ne()}x([`click`]);var ve=u(`<div class="m-2"><div class="dark:bg-stone-700 bg-stone-300 rounded inline px-2 py-1 text-sm"><span class="inline-block w-20"> </span> <span> </span> <button class="ml-4 pointer" title="Annuler"><i class="icon-cancel"></i></button></div></div>`),ye=u(`<div class="mb-4 formgroup container"><h2>Mon compte</h2> <form><div class="formelement"><label for="email" class="my-1 flex-1">Email</label> <input id="email" type="text" class="textinput"/> <!></div> <div class="grid grid-cols-1 gap-x-4 md:grid-cols-2"><div class="formelement"><label for="firstname" class=" my-1 flex-1">Prénom</label> <input id="firstname" type="text" class="textinput"/> <!></div> <div class="formelement"><label for="lastname" class="my-1 flex-1">Nom</label> <input id="lastname" type="text" class="textinput"/> <!></div></div> <div class="grid grid-cols-1 gap-x-4 md:grid-cols-2"><div class="formelement"><label for="lang" class="block my-1 flex-1">Langue</label> <select class="textinput"><option>English</option><option>Français</option></select> <!></div> <div class="formelement"><label for="timezone" class="block my-1 flex-1">Timezone</label> <!> <!></div></div> <div class="formelement"><label for="theme" class="block my-1 flex-1">Thème</label> <select class="textinput"><option>Automatique</option><option>Sombre</option><option>Clair</option></select> <!></div> <!></form></div> <div class="mb-4 formgroup container"><h2>Canaux de communication</h2> <!> <div class="flex gap-2 mt-6"><div><select class="textinput"><option></option><option>Email</option><option>Signal</option><option>Telegram</option><option>Whatsapp</option></select></div> <div class="flex-1"><input type="text" class="textinput"/></div> <div><!></div></div></div>`,1);function j(t,u){te(u,!0);let g=a(s(()=>u.data.me.user)),y=f(a([])),b=f(a({type:``,address:``})),x=f(a([])),T=new ce,E=f(a(A.None)),D=f(a(A.None));re(async()=>{h(y,await O.listAddresses(g.id),!0),h(x,(await pe.listTimezones()).map(e=>({value:e,label:e})),!0)}),m(()=>{oe(g.theme),se()});let de=e=>{e.preventDefault(),h(E,A.Saving,!0),ue(g.email)||T.set(`email`,`Adresse email invalide !`),g.firstname||T.set(`firstname`,`Le prénom est obligatoire !`),g.lastname||T.set(`lastname`,`Le nom est obligatoire !`),T.size==0&&O.update(g.id,g).then(()=>{h(E,A.Saved,!0)}).catch(e=>{h(E,A.Saving,!0),console.error(e)})},fe=()=>{h(D,A.Saving,!0),O.createAddress(g.id,d(b)).then(e=>{d(y).push(e),h(b,{type:``,address:``},!0),h(D,A.Saved,!0)}).catch(e=>{console.log(e)})},me=e=>{O.deleteAddress(g.id,e).then(()=>{h(y,d(y).filter(t=>t.id!=e),!0)}).catch(e=>{console.log(e)})},he=()=>{T.clear(),h(E,A.Changed,!0)},ge=e=>{e.focus()};var j=ye(),M=o(j),N=C(i(M),2),P=i(N),F=C(i(P),2);w(F),ae(()=>_(F,()=>g.email,e=>g.email=e)),ee(F,e=>ge?.(e));var be=C(F,2);k(be,{key:`email`,get errors(){return T}}),v(P);var I=C(P,2),L=i(I),R=C(i(L),2);w(R);var xe=C(R,2);k(xe,{key:`firstname`,get errors(){return T}}),v(L);var Se=C(L,2),z=C(i(Se),2);w(z);var Ce=C(z,2);k(Ce,{key:`lastname`,get errors(){return T}}),v(Se),v(I);var B=C(I,2),V=i(B),H=C(i(V),2),U=i(H);U.value=U.__value=`en`;var we=C(U);we.value=we.__value=`fr`,v(H);var Te=C(H,2);k(Te,{key:`lang`,get errors(){return T}}),v(V);var Ee=C(V,2),De=C(i(Ee),2);le(De,{get items(){return d(x)},valueMode:`id`,get value(){return g.timezone},set valueLine truncated
@@ -1 +0,0 @@
import{$ as e,A as t,B as n,E as r,G as i,J as a,K as o,L as s,M as c,N as l,O as u,P as d,Q as f,T as p,V as m,X as h,_ as ee,a as g,at as te,c as _,dt as v,ft as y,h as b,it as ne,j as x,p as S,q as C,r as re,u as w,ut as T,w as E,x as ie,z as ae}from"../chunks/39JK5HWF.js";import"../chunks/xihTtKlq.js";import{n as oe,t as se}from"../chunks/DTCALkcP.js";import{t as D}from"../chunks/BqkZ0BZ5.js";import{t as O}from"../chunks/BEgKbVuy.js";import{n as ce,t as k}from"../chunks/DRdGzrsF.js";import{t as le}from"../chunks/D4uBfDnr.js";import{t as ue}from"../chunks/D3MDK1zR.js";var de=y({load:()=>fe}),fe=async()=>({title:`Profil`,me:await O.me()}),A=function(e){return e[e.None=0]=`None`,e[e.Changed=1]=`Changed`,e[e.Saving=2]=`Saving`,e[e.Saved=3]=`Saved`,e[e.Error=4]=`Error`,e}({}),pe={listTimezones:()=>D.get(`/misc/timezones`)},me=u(`<i class="incon-spin1 anomate-spin"></i> Enregistrement en cours`,1),he=u(`<i class="incon-ok anomate-spin"></i> Enregistrement réussi`,1),ge=u(`<button><!></button>`);function _e(e,a){te(a,!0);let o=g(a,`state`,7),s=g(a,`disabled`,3,!1);m(()=>{(o()===A.Saved||o()===A.Error)&&setTimeout(()=>{o(A.None)},2e3)});let l=()=>{a.handleClick&&(o()===A.Changed||o()===A.None)&&a.handleClick()};var u=ge();let d;var f=i(u),h=e=>{var i=t();n(()=>p(i,a.label)),r(e,i)},ee=e=>{var t=me();T(),r(e,t)},_=e=>{var t=he();T(),r(e,t)},y=e=>{var n=t(`Echec`);r(e,n)};E(f,e=>{o()==A.None||o()==A.Changed?e(h):o()==A.Saving?e(ee,1):o()==A.Saved?e(_,2):o()==A.Error&&e(y,3)}),v(u),n(()=>{u.disabled=s(),d=b(u,1,`bt`,null,d,{disabled:s(),changed:A.Changed,saving:A.Saving,saved:A.Saved,error:A.Error})}),c(`click`,u,l),r(e,u),ne()}x([`click`]);var ve=u(`<div class="m-2"><div class="dark:bg-stone-700 bg-stone-300 rounded inline px-2 py-1 text-sm"><span class="inline-block w-20"> </span> <span> </span> <button class="ml-4 pointer" title="Annuler"><i class="icon-cancel"></i></button></div></div>`),ye=u(`<div class="mb-4 formgroup container"><h2>Mon compte</h2> <form><div class="formelement"><label for="email" class="my-1 flex-1">Email</label> <input id="email" type="text" class="textinput"/> <!></div> <div class="grid grid-cols-1 gap-x-4 md:grid-cols-2"><div class="formelement"><label for="firstname" class=" my-1 flex-1">Prénom</label> <input id="firstname" type="text" class="textinput"/> <!></div> <div class="formelement"><label for="lastname" class="my-1 flex-1">Nom</label> <input id="lastname" type="text" class="textinput"/> <!></div></div> <div class="grid grid-cols-1 gap-x-4 md:grid-cols-2"><div class="formelement"><label for="lang" class="block my-1 flex-1">Langue</label> <select class="textinput"><option>English</option><option>Français</option></select> <!></div> <div class="formelement"><label for="timezone" class="block my-1 flex-1">Timezone</label> <!> <!></div></div> <div class="formelement"><label for="theme" class="block my-1 flex-1">Thème</label> <select class="textinput"><option>Automatique</option><option>Sombre</option><option>Clair</option></select> <!></div> <!></form></div> <div class="mb-4 formgroup container"><h2>Canaux de communication</h2> <!> <div class="flex gap-2 mt-6"><div><select class="textinput"><option></option><option>Email</option><option>Signal</option><option>Telegram</option><option>Whatsapp</option></select></div> <div class="flex-1"><input type="text" class="textinput"/></div> <div><!></div></div></div>`,1);function j(t,u){te(u,!0);let g=a(s(()=>u.data.me.user)),y=f(a([])),b=f(a({type:``,address:``})),x=f(a([])),T=new ce,E=f(a(A.None)),D=f(a(A.None));re(async()=>{h(y,await O.listAddresses(g.id),!0),h(x,(await pe.listTimezones()).map(e=>({value:e,label:e})),!0)}),m(()=>{oe(g.theme),se()});let de=e=>{e.preventDefault(),h(E,A.Saving,!0),ue(g.email)||T.set(`email`,`Adresse email invalide !`),g.firstname||T.set(`firstname`,`Le prénom est obligatoire !`),g.lastname||T.set(`lastname`,`Le nom est obligatoire !`),T.size==0&&O.update(g.id,g).then(()=>{h(E,A.Saved,!0)}).catch(e=>{h(E,A.Saving,!0),console.error(e)})},fe=()=>{h(D,A.Saving,!0),O.createAddress(g.id,d(b)).then(e=>{d(y).push(e),h(b,{type:``,address:``},!0),h(D,A.Saved,!0)}).catch(e=>{console.log(e)})},me=e=>{O.deleteAddress(g.id,e).then(()=>{h(y,d(y).filter(t=>t.id!=e),!0)}).catch(e=>{console.log(e)})},he=()=>{T.clear(),h(E,A.Changed,!0)},ge=e=>{e.focus()};var j=ye(),M=o(j),N=C(i(M),2),P=i(N),F=C(i(P),2);w(F),ae(()=>_(F,()=>g.email,e=>g.email=e)),ee(F,e=>ge?.(e));var be=C(F,2);k(be,{key:`email`,get errors(){return T}}),v(P);var I=C(P,2),L=i(I),R=C(i(L),2);w(R);var xe=C(R,2);k(xe,{key:`firstname`,get errors(){return T}}),v(L);var Se=C(L,2),z=C(i(Se),2);w(z);var Ce=C(z,2);k(Ce,{key:`lastname`,get errors(){return T}}),v(Se),v(I);var B=C(I,2),V=i(B),H=C(i(V),2),U=i(H);U.value=U.__value=`en`;var we=C(U);we.value=we.__value=`fr`,v(H);var Te=C(H,2);k(Te,{key:`lang`,get errors(){return T}}),v(V);var Ee=C(V,2),De=C(i(Ee),2);le(De,{get items(){return d(x)},valueMode:`id`,get value(){return g.timezone},set value(e){g.timezone=e}});var Line truncated
@@ -1 +1 @@
import{B as e,E as t,G as n,J as r,K as i,M as a,N as o,O as s,at as c,c as l,d as u,dt as d,et as f,it as p,j as m,q as h,u as g,ut as _,w as v}from"../chunks/39JK5HWF.js";import{t as y}from"../chunks/D3up1JC1.js";import"../chunks/xihTtKlq.js";import"../chunks/B5isPFOt.js";import{i as b,n as x}from"../chunks/BqkZ0BZ5.js";import{t as S}from"../chunks/BRXBpvt8.js";import{t as C}from"../chunks/DNQ6C3zq.js";var w=s(`<form><div class="flex"><label for="email" class="block mb-2 flex-1">Email</label> <a href="/recover-password" class="text-xs">Mot de passe oublié ?</a></div> <div class="flex flex-row"><input type="text" class="textinput mr-1"/> <button class="bt" title="page suivante"><i class="icon-right-big"></i></button></div></form> <div class="my-4 text-xs"><a href="/register">Pas encore de compte ? Incrivez-vous.</a></div>`,1),T=s(`<form><div class="flex"><label for="email" class="block mb-2 flex-1">Mot de passe</label></div> <div class="flex flex-row"><input type="password" class="textinput mr-1"/> <button class="bt">Entrer</button></div> <a href="/login" class="text-xs">← Retour</a></form>`),E=s(`<div class="h-full w-full flex items-center public"><div class="m-auto w-96 max-w-full p-4"><img class="h-10 mb-4" alt="logo"/> <h1>Identifiez-vous ...</h1> <!> <hr class="my-4"/> <div class="grid grid-cols-[repeat(auto-fit,minmax(150px,1fr))] gap-4"><button class="bt p-5 text-center" title="Connexion avec Google"><i class="icon-google"></i></button> <button class="bt p-5 text-center" title="Connexion avec Facebook"><i class="icon-facebook"></i></button> <button class="bt p-5 text-center" title="Connexion avec Github"><i class="icon-github"></i></button> <button class="bt p-5 text-center" title="Connexion avec Apple"><i class="icon-apple"></i></button></div></div></div>`);function D(s,m){c(m,!0);let D=r({email:``,password:``,validemail:!1}),O=()=>{D.validemail=!0},k=e=>{e.preventDefault(),C.login(D).then(e=>{x.user=e.user,b(e.accessToken),y(`/`)}).catch(e=>{console.log(e)})};var A=E(),j=n(A),M=n(j),N=h(M,4),P=e=>{var r=w(),a=i(r),s=h(n(a),2),c=n(s);g(c),f(c,!0),_(2),d(s),d(a),_(2),o(`submit`,a,O),l(c,()=>D.email,e=>D.email=e),t(e,r)},F=e=>{var r=T(),i=h(n(r),2),s=n(i);g(s),f(s,!0),_(2),d(i);var c=h(i,2);d(r),o(`submit`,r,k),l(s,()=>D.password,e=>D.password=e),a(`click`,c,()=>{D.validemail=!1}),t(e,r)};v(N,e=>{D.validemail?e(F,-1):e(P)}),_(4),d(j),d(A),e(()=>u(M,`src`,S)),t(s,A),p()}m([`click`]);export{D as component}; import{B as e,E as t,G as n,J as r,K as i,M as a,N as o,O as s,at as c,c as l,d as u,dt as d,et as f,it as p,j as m,q as h,u as g,ut as _,w as v}from"../chunks/39JK5HWF.js";import{t as y}from"../chunks/B0qrqti_.js";import"../chunks/xihTtKlq.js";import"../chunks/DL7ZyBAF.js";import{i as b,n as x}from"../chunks/BC84wEQu.js";import{t as S}from"../chunks/BRXBpvt8.js";import{t as C}from"../chunks/DFI7Zp3t.js";var w=s(`<form><div class="flex"><label for="email" class="block mb-2 flex-1">Email</label> <a href="/recover-password" class="text-xs">Mot de passe oublié ?</a></div> <div class="flex flex-row"><input type="text" class="textinput mr-1"/> <button class="bt" title="page suivante"><i class="icon-right-big"></i></button></div></form> <div class="my-4 text-xs"><a href="/register">Pas encore de compte ? Incrivez-vous.</a></div>`,1),T=s(`<form><div class="flex"><label for="email" class="block mb-2 flex-1">Mot de passe</label></div> <div class="flex flex-row"><input type="password" class="textinput mr-1"/> <button class="bt">Entrer</button></div> <a href="/login" class="text-xs">← Retour</a></form>`),E=s(`<div class="h-full w-full flex items-center public"><div class="m-auto w-96 max-w-full p-4"><img class="h-10 mb-4" alt="logo"/> <h1>Identifiez-vous ...</h1> <!> <hr class="my-4"/> <div class="grid grid-cols-[repeat(auto-fit,minmax(150px,1fr))] gap-4"><button class="bt p-5 text-center" title="Connexion avec Google"><i class="icon-google"></i></button> <button class="bt p-5 text-center" title="Connexion avec Facebook"><i class="icon-facebook"></i></button> <button class="bt p-5 text-center" title="Connexion avec Github"><i class="icon-github"></i></button> <button class="bt p-5 text-center" title="Connexion avec Apple"><i class="icon-apple"></i></button></div></div></div>`);function D(s,m){c(m,!0);let D=r({email:``,password:``,validemail:!1}),O=()=>{D.validemail=!0},k=e=>{e.preventDefault(),C.login(D).then(e=>{x.user=e.user,b(e.accessToken),y(`/`)}).catch(e=>{console.log(e)})};var A=E(),j=n(A),M=n(j),N=h(M,4),P=e=>{var r=w(),a=i(r),s=h(n(a),2),c=n(s);g(c),f(c,!0),_(2),d(s),d(a),_(2),o(`submit`,a,O),l(c,()=>D.email,e=>D.email=e),t(e,r)},F=e=>{var r=T(),i=h(n(r),2),s=n(i);g(s),f(s,!0),_(2),d(i);var c=h(i,2);d(r),o(`submit`,r,k),l(s,()=>D.password,e=>D.password=e),a(`click`,c,()=>{D.validemail=!1}),t(e,r)};v(N,e=>{D.validemail?e(F,-1):e(P)}),_(4),d(j),d(A),e(()=>u(M,`src`,S)),t(s,A),p()}m([`click`]);export{D as component};
@@ -1 +1 @@
import{B as e,E as t,G as n,J as r,M as i,N as a,O as o,at as s,c,d as l,dt as u,et as d,it as f,j as p,q as m,u as h}from"../chunks/39JK5HWF.js";import{t as g}from"../chunks/D3up1JC1.js";import"../chunks/xihTtKlq.js";import"../chunks/B5isPFOt.js";import{t as _}from"../chunks/BRXBpvt8.js";import{n as v,t as y}from"../chunks/DRdGzrsF.js";import{t as b}from"../chunks/D3MDK1zR.js";import{t as x}from"../chunks/DNQ6C3zq.js";var S=o(`<div class="h-full w-full flex items-center public"><div class="m-auto w-96 max-w-full p-4"><img class="h-10 mb-4" alt="logo"/> <h1>Inscription</h1> <form><label for="email" class="block my-1 flex-1">Email</label> <input id="email" type="text" class="textinput"/> <!> <label for="firstname" class="block my-1 flex-1">Prénom</label> <input id="firstname" type="text" class="textinput"/> <!> <label for="lastname" class="block my-1 flex-1">Nom</label> <input id="lastname" type="text" class="textinput"/> <!> <label for="password" class="block my-1 flex-1">Mot de passe</label> <input id="password" type="password" class="textinput"/> <!> <label for="verifpassword" class="block my-1 flex-1">Vérification du mot de passe</label> <input id="verifpassword" type="password" class="textinput"/> <!> <button class="bt my-2 w-full ">Enregistrer</button> <!></form></div></div>`);function C(o,p){s(p,!0);let C=new v,w=r({email:``,firstname:``,lastname:``,password:``,verifPassword:``}),T=e=>{if(e.preventDefault(),C.clear(),b(w.email)||C.set(`email`,`Adresse email invalide !`),w.firstname||C.set(`firstname`,`Le prénom est obligatoire !`),w.lastname||C.set(`lastname`,`Le nom est obligatoire !`),w.password!==w.verifPassword&&C.set(`verifPassword`,`Les mots de pass ne correspondent pas.`),C.size==0){let e={method:`password`,email:w.email,firstname:w.firstname,lastname:w.lastname,password:w.password};x.register(e).then(()=>{g(`/`)}).catch(e=>{C.set(`submit`,`Erreur: `+e)})}};var E=S(),D=n(E),O=n(D),k=m(O,4),A=m(n(k),2);h(A),d(A,!0);var j=m(A,2);y(j,{key:`email`,get errors(){return C}});var M=m(j,4);h(M);var N=m(M,2);y(N,{key:`firstname`,get errors(){return C}});var P=m(N,4);h(P);var F=m(P,2);y(F,{key:`lastname`,get errors(){return C}});var I=m(F,4);h(I);var L=m(I,2);y(L,{key:`password`,get errors(){return C}});var R=m(L,4);h(R);var z=m(R,2);y(z,{key:`verifPassword`,get errors(){return C}});var B=m(z,4);y(B,{key:`submit`,get errors(){return C}}),u(k),u(D),u(E),e(()=>l(O,`src`,_)),a(`submit`,k,T),i(`change`,k,()=>{C.clear()}),c(A,()=>w.email,e=>w.email=e),c(M,()=>w.firstname,e=>w.firstname=e),c(P,()=>w.lastname,e=>w.lastname=e),c(I,()=>w.password,e=>w.password=e),c(R,()=>w.verifPassword,e=>w.verifPassword=e),t(o,E),f()}p([`change`]);export{C as component}; import{B as e,E as t,G as n,J as r,M as i,N as a,O as o,at as s,c,d as l,dt as u,et as d,it as f,j as p,q as m,u as h}from"../chunks/39JK5HWF.js";import{t as g}from"../chunks/B0qrqti_.js";import"../chunks/xihTtKlq.js";import"../chunks/DL7ZyBAF.js";import{t as _}from"../chunks/BRXBpvt8.js";import{n as v,t as y}from"../chunks/DRdGzrsF.js";import{t as b}from"../chunks/D3MDK1zR.js";import{t as x}from"../chunks/DFI7Zp3t.js";var S=o(`<div class="h-full w-full flex items-center public"><div class="m-auto w-96 max-w-full p-4"><img class="h-10 mb-4" alt="logo"/> <h1>Inscription</h1> <form><label for="email" class="block my-1 flex-1">Email</label> <input id="email" type="text" class="textinput"/> <!> <label for="firstname" class="block my-1 flex-1">Prénom</label> <input id="firstname" type="text" class="textinput"/> <!> <label for="lastname" class="block my-1 flex-1">Nom</label> <input id="lastname" type="text" class="textinput"/> <!> <label for="password" class="block my-1 flex-1">Mot de passe</label> <input id="password" type="password" class="textinput"/> <!> <label for="verifpassword" class="block my-1 flex-1">Vérification du mot de passe</label> <input id="verifpassword" type="password" class="textinput"/> <!> <button class="bt my-2 w-full ">Enregistrer</button> <!></form></div></div>`);function C(o,p){s(p,!0);let C=new v,w=r({email:``,firstname:``,lastname:``,password:``,verifPassword:``}),T=e=>{if(e.preventDefault(),C.clear(),b(w.email)||C.set(`email`,`Adresse email invalide !`),w.firstname||C.set(`firstname`,`Le prénom est obligatoire !`),w.lastname||C.set(`lastname`,`Le nom est obligatoire !`),w.password!==w.verifPassword&&C.set(`verifPassword`,`Les mots de pass ne correspondent pas.`),C.size==0){let e={method:`password`,email:w.email,firstname:w.firstname,lastname:w.lastname,password:w.password};x.register(e).then(()=>{g(`/`)}).catch(e=>{C.set(`submit`,`Erreur: `+e)})}};var E=S(),D=n(E),O=n(D),k=m(O,4),A=m(n(k),2);h(A),d(A,!0);var j=m(A,2);y(j,{key:`email`,get errors(){return C}});var M=m(j,4);h(M);var N=m(M,2);y(N,{key:`firstname`,get errors(){return C}});var P=m(N,4);h(P);var F=m(P,2);y(F,{key:`lastname`,get errors(){return C}});var I=m(F,4);h(I);var L=m(I,2);y(L,{key:`password`,get errors(){return C}});var R=m(L,4);h(R);var z=m(R,2);y(z,{key:`verifPassword`,get errors(){return C}});var B=m(z,4);y(B,{key:`submit`,get errors(){return C}}),u(k),u(D),u(E),e(()=>l(O,`src`,_)),a(`submit`,k,T),i(`change`,k,()=>{C.clear()}),c(A,()=>w.email,e=>w.email=e),c(M,()=>w.firstname,e=>w.firstname=e),c(P,()=>w.lastname,e=>w.lastname=e),c(I,()=>w.password,e=>w.password=e),c(R,()=>w.verifPassword,e=>w.verifPassword=e),t(o,E),f()}p([`change`]);export{C as component};
@@ -1 +0,0 @@
import{$ as e,B as t,E as n,G as r,J as i,K as a,M as o,N as s,O as c,P as l,T as u,V as d,W as f,a as p,at as m,b as h,d as g,dt as _,ft as v,g as y,h as b,it as x,j as S,q as C,r as w,w as T,x as E}from"../chunks/39JK5HWF.js";import{t as D}from"../chunks/D3up1JC1.js";import"../chunks/xihTtKlq.js";import{n as O,t as k}from"../chunks/DTCALkcP.js";import{t as A}from"../chunks/D1d5-1oo.js";import"../chunks/B5isPFOt.js";import{n as j,r as M}from"../chunks/BqkZ0BZ5.js";import{t as N}from"../chunks/BEgKbVuy.js";import{t as P}from"../chunks/D8dI9nx6.js";import{t as F}from"../chunks/BRXBpvt8.js";var I=v({load:()=>L}),L=async()=>{let e=await N.me();return P.spaces=e.teams,P.current||(P.current=P.spaces[0].id),{spaceid:P.current,me:e}},R=i({sidebarDesktopOpen:!0,sidebarMobileOpen:!1,menuContentVisible:!0}),z=c(`<span class="ml-4"> </span>`),B=c(`<div class="my-2 block"><a class="p-2 w-full text-left menuitem block"><i></i> <!></a></div>`);function V(e,i){m(i,!0);let a=p(i,`label`,3,``),o=p(i,`icon`,3,``),s=p(i,`link`,3,``);var c=B(),l=r(c),d=r(l),f=C(d,2),h=e=>{var i=z(),o=r(i,!0);_(i),t(()=>u(o,a())),n(e,i)};T(f,e=>{R.menuContentVisible&&e(h)}),_(l),_(c),t(()=>{g(l,`href`,s()),b(d,1,y([o()]))}),n(e,c),x()}var H=c(`<div class="flex-1 p-3 font openonly"><img class="h-5" alt="logo"/></div>`),U=c(`<option> </option>`),W=c(`<div class="m-2"><div class="formelement"><select class="textinput"></select></div></div>`),G=c(`<div class="flex flex-row gap-2 p-2 "><div class="py-1 flex-1 flex flex-row text-xs"><div class="w-6 h-6 rounded-full bg-cyan-600 text-white flex items-center justify-center font-medium">FM</div> <div class="flex-1 ml-2 inline-flex items-center">Fabien Masson</div></div> <a href="/profile" class="bt small inline-flex items-center justify-center" title="Edition profil"><i class="icon-edit"></i></a> <button class="bt small" title="Déconnecter"><i class="icon-logout"></i></button></div>`),K=c(`<div class="flex flex-row italic mb-2"><!> <button id="hamburgerDesktop" title="Open menu"><i class="icon-menu pointer block p-2"></i></button> <button id="hamburgerMobile" title="Open menu"><i class="icon-menu pointer block p-2"></i></button></div> <!> <div id="menu" class="flex-1 overflow-y-scroll"><!> <!> <!> <!> <!></div> <div id="profile" class="w-full items-center border-t border-stone-200 dark:border-stone-800"><!></div>`,1);function q(i,s){m(s,!0);let c=()=>{M()};var d=K(),f=a(d),p=r(f),h=e=>{var i=H(),a=r(i);_(i),t(()=>g(a,`src`,F)),n(e,i)};T(p,e=>{R.menuContentVisible&&e(h)});var v=C(p,2),y=C(v,2);_(f);var b=C(f,2),S=e=>{var i=W(),a=r(i),o=r(a);E(o,21,()=>s.spaces,e=>e.id,(e,i)=>{var a=U(),o=r(a,!0);_(a);var s={};t(()=>{u(o,l(i).label),s!==(s=l(i).id)&&(a.value=(a.__value=l(i).id)??``)}),n(e,a)}),_(o),_(a),_(i),n(e,i)};T(b,e=>{R.menuContentVisible&&e(S)});var w=C(b,2),D=r(w);{let t=e(()=>`/${s.spaceid}`);V(D,{label:`Nouveau chat`,icon:`icon-plus`,get link(){return l(t)}})}var O=C(D,2);{let t=e(()=>`/${s.spaceid}/history`);V(O,{label:`Conversations`,icon:`icon-chat`,get link(){return l(t)}})}var k=C(O,2);{let t=e(()=>`/${s.spaceid}/tasks`);V(k,{label:`Planification`,icon:`icon-tasks`,get link(){return l(t)}})}var A=C(k,2);{let t=e(()=>`/${s.spaceid}/kb`);V(A,{label:`Connaissances`,icon:`icon-graduation-cap`,get link(){return l(t)}})}var j=C(A,2);{let t=e(()=>`/${s.spaceid}/settings`);V(j,{label:`Réglages`,icon:`icon-cog`,get link(){return l(t)}})}_(w);var N=C(w,2),P=r(N),I=e=>{var t=G(),i=C(r(t),4);_(t),o(`click`,i,c),n(e,t)},L=e=>{V(e,{label:`User`,icon:`icon-user`,link:`/profile`})};T(P,e=>{R.menuContentVisible?e(I):e(L,-1)}),_(N),o(`click`,v,function(...e){s.handleMenuDesktop?.apply(this,e)}),o(`click`,y,function(...e){s.handleMenuMobile?.apply(this,e)}),n(i,d),x()}S([`click`]);var J=c(`<span><a title="retour"><i class="icon-left-big"></i></a></span>`),Y=c(`<div id="outer"><div id="sidebar" class="flex flex-col z-10"><!></div> <div id="main"><div class="flex flex-col h-screen "><div class="px-2"><div class="header container"><h1 class="flex"><span class="flex-1"> </span> <!></h1></div></div> <div class="overflow-scroll flex-1 p-2"><!></div></div></div></div>`);function X(e,i){m(i,!0);let a=()=>{R.sidebarDesktopOpen=!R.sidebarDesktopOpen,R.sidebarDesktopOpen?setTimeout(()=>{R.menuContentVisible=R.sidebarDesktopOpen},200):R.menuContentVisible=R.sidebarDesktopOpen},o=()=>{R.sidebarMobileOpen=!R.sidebarMobileOpen,R.sidebarMobileOpen?setTimeout(()=>{R.menuContentVisible=R.sidebarMobileOpen},200):R.menuContentVisible=R.sidebarMobileOpen},c=()=>{R.menuContentVisible=window.innerWidth<640?R.sidebarMobileOpen:R.sidebarDesktopOpen};w(async()=>{c(),i.data.me?(j.user=i.data.me.user,O(j.user.theme),k(),j.status=`authenticated`):j.status=`unauthenticated`}),d(()=>{j.status==`unauthenticated`&&D(`/login`)});var l=Y();s(`resize`,f,c);var p=r(l);q(r(p),{get spaces(){return P.spaces},get spaceid(){return i.data.spaceid},handleMenuDesktop:a,handleMenuMobile:o}),_(p);var v=C(p,2),S=r(v),E=r(S),M=r(E),N=r(M),F=r(N),I=r(F,Line truncated
@@ -0,0 +1 @@
import{$ as e,B as t,E as n,G as r,J as i,K as a,M as o,N as s,O as c,P as l,T as u,V as d,W as f,a as p,at as m,b as h,d as g,dt as _,ft as v,g as y,h as b,it as x,j as S,q as C,r as w,w as T,x as E}from"../chunks/39JK5HWF.js";import{t as D}from"../chunks/B0qrqti_.js";import"../chunks/xihTtKlq.js";import{n as O,t as k}from"../chunks/DTCALkcP.js";import{t as A}from"../chunks/DtPl1hOB.js";import"../chunks/DL7ZyBAF.js";import{n as j,r as M}from"../chunks/BC84wEQu.js";import{t as N}from"../chunks/BDef5Ccs.js";import{t as P}from"../chunks/D8dI9nx6.js";import{t as F}from"../chunks/BRXBpvt8.js";var I=v({load:()=>L}),L=async()=>{let e=await N.me();return P.spaces=e.teams,P.current||(P.current=P.spaces[0].id),{spaceid:P.current,me:e}},R=i({sidebarDesktopOpen:!0,sidebarMobileOpen:!1,menuContentVisible:!0}),z=c(`<span class="ml-4"> </span>`),B=c(`<div class="my-2 block"><a class="p-2 w-full text-left menuitem block"><i></i> <!></a></div>`);function V(e,i){m(i,!0);let a=p(i,`label`,3,``),s=p(i,`icon`,3,``),c=p(i,`link`,3,``),l=e=>{e.preventDefault(),R.sidebarMobileOpen=!1,R.menuContentVisible=!1,D(c())};var d=B(),f=r(d),h=r(f),v=C(h,2),S=e=>{var i=z(),o=r(i,!0);_(i),t(()=>u(o,a())),n(e,i)};T(v,e=>{R.menuContentVisible&&e(S)}),_(f),_(d),t(()=>{g(f,`href`,c()),b(h,1,y([s()]))}),o(`click`,f,l),n(e,d),x()}S([`click`]);var H=c(`<div class="flex-1 p-3 font openonly"><img class="h-5" alt="logo"/></div>`),U=c(`<option> </option>`),W=c(`<div class="m-2"><div class="formelement"><select class="textinput"></select></div></div>`),G=c(`<div class="flex flex-row gap-2 p-2 "><div class="py-1 flex-1 flex flex-row text-xs"><div class="w-6 h-6 rounded-full bg-cyan-600 text-white flex items-center justify-center font-medium">FM</div> <div class="flex-1 ml-2 inline-flex items-center">Fabien Masson</div></div> <a href="/profile" class="bt small inline-flex items-center justify-center" title="Edition profil"><i class="icon-edit"></i></a> <button class="bt small" title="Déconnecter"><i class="icon-logout"></i></button></div>`),K=c(`<div class="flex flex-row italic mb-2"><!> <button id="hamburgerDesktop" title="Open menu"><i class="icon-menu pointer block p-2"></i></button> <button id="hamburgerMobile" title="Open menu"><i class="icon-menu pointer block p-2"></i></button></div> <!> <div id="menu" class="flex-1 overflow-y-scroll"><!> <!> <!> <!> <!></div> <div id="profile" class="w-full items-center border-t border-stone-200 dark:border-stone-800"><!></div>`,1);function q(i,s){m(s,!0);let c=()=>{M()};var d=K(),f=a(d),p=r(f),h=e=>{var i=H(),a=r(i);_(i),t(()=>g(a,`src`,F)),n(e,i)};T(p,e=>{R.menuContentVisible&&e(h)});var v=C(p,2),y=C(v,2);_(f);var b=C(f,2),S=e=>{var i=W(),a=r(i),o=r(a);E(o,21,()=>s.spaces,e=>e.id,(e,i)=>{var a=U(),o=r(a,!0);_(a);var s={};t(()=>{u(o,l(i).label),s!==(s=l(i).id)&&(a.value=(a.__value=l(i).id)??``)}),n(e,a)}),_(o),_(a),_(i),n(e,i)};T(b,e=>{R.menuContentVisible&&e(S)});var w=C(b,2),D=r(w);{let t=e(()=>`/${s.spaceid}`);V(D,{label:`Nouveau chat`,icon:`icon-plus`,get link(){return l(t)}})}var O=C(D,2);{let t=e(()=>`/${s.spaceid}/history`);V(O,{label:`Conversations`,icon:`icon-chat`,get link(){return l(t)}})}var k=C(O,2);{let t=e(()=>`/${s.spaceid}/tasks`);V(k,{label:`Planification`,icon:`icon-tasks`,get link(){return l(t)}})}var A=C(k,2);{let t=e(()=>`/${s.spaceid}/kb`);V(A,{label:`Connaissances`,icon:`icon-graduation-cap`,get link(){return l(t)}})}var j=C(A,2);{let t=e(()=>`/${s.spaceid}/settings`);V(j,{label:`Réglages`,icon:`icon-cog`,get link(){return l(t)}})}_(w);var N=C(w,2),P=r(N),I=e=>{var t=G(),i=C(r(t),4);_(t),o(`click`,i,c),n(e,t)},L=e=>{V(e,{label:`User`,icon:`icon-user`,link:`/profile`})};T(P,e=>{R.menuContentVisible?e(I):e(L,-1)}),_(N),o(`click`,v,function(...e){s.handleMenuDesktop?.apply(this,e)}),o(`click`,y,function(...e){s.handleMenuMobile?.apply(this,e)}),n(i,d),x()}S([`click`]);var J=c(`<span><a title="retour"><i class="icon-left-big"></i></a></span>`),Y=c(`<div id="outer"><div id="sidebar" class="flex flex-col z-10"><!></div> <div id="main"><div class="flex flex-col h-screen "><div class="px-2"><div class="header container"><h1 class="flex"><span class="flex-1"> </span> <!></h1></div></div> <div class="overflow-scroll flex-1 p-2"><!></div></div></div></div>`);function X(e,i){m(i,!0);let a=()=>{R.sidebarDesktopOpen=!R.sidebarDesktopOpen,R.sidebarDesktopOpen?setTimeout(()=>{R.menuContentVisible=R.sidebarDesktopOpen},200):R.menuContentVisible=R.sidebarDesktopOpen},o=()=>{R.sidebarMobileOpen=!R.sidebarMobileOpen,R.sidebarMobileOpen?setTimeout(()=>{R.menuContentVisible=R.sidebarMobileOpen},200):R.menuContentVisible=R.sidebarMobileOpen},c=()=>{R.menuContentVisible=window.innerWidth<640?R.sidebarMobileOpen:R.sidebarDesktopOpen};w(async()=>{c(),i.data.me?(j.user=i.data.me.user,O(j.user.theme),k(),j.status=`authenticated`):j.status=`unauthenticated`}),d(()=>{j.status==`unauthenticated`&&D(`/login`)});var l=Y();s(`resize`,f,c);var p=r(l);q(r(p),{get spaces(){return P.spaces},get spaceid(){return i.data.spacLine truncated
@@ -1 +1 @@
import{at as e,ft as t,it as n}from"../chunks/39JK5HWF.js";import{t as r}from"../chunks/D3up1JC1.js";import"../chunks/xihTtKlq.js";import"../chunks/B5isPFOt.js";import{t as i}from"../chunks/D8dI9nx6.js";var a=t({load:()=>o}),o=async({parent:e})=>{let{me:t}=await e();return{me:t}};function s(t,a){e(a,!0),r(`/${i.current}/`,{replaceState:!0}),n()}export{s as component,a as universal}; import{at as e,ft as t,it as n}from"../chunks/39JK5HWF.js";import{t as r}from"../chunks/B0qrqti_.js";import"../chunks/xihTtKlq.js";import"../chunks/DL7ZyBAF.js";import{t as i}from"../chunks/D8dI9nx6.js";var a=t({load:()=>o}),o=async({parent:e})=>{let{me:t}=await e();return{me:t}};function s(t,a){e(a,!0),r(`/${i.current}/`,{replaceState:!0}),n()}export{s as component,a as universal};
@@ -1 +0,0 @@
import{B as e,C as t,D as n,E as r,G as i,J as a,K as o,M as s,N as c,O as l,P as u,Q as d,T as f,X as p,_ as m,a as h,at as g,c as _,dt as v,ft as y,it as b,j as x,q as S,tt as C,u as w,ut as T,w as E,x as D,z as O}from"../chunks/39JK5HWF.js";import"../chunks/xihTtKlq.js";import{t as k}from"../chunks/BqkZ0BZ5.js";import{t as A}from"../chunks/D35bQ8xD.js";import{n as j,t as M}from"../chunks/DRdGzrsF.js";import{t as N}from"../chunks/D4uBfDnr.js";var P={getAgents:e=>k.get(`/agent/${e}`),getAgent:(e,t)=>k.get(`/agent/${e}/${t}`),createAgent:(e,t)=>k.post(`/agent/${e}`,t),updateAgent:(e,t)=>k.put(`/agent/${e}`,t),deleteAgent:(e,t)=>k.delete(`/agent/${e}/${t}`)},F=y({load:()=>I}),I=async({params:e})=>({title:`Paramétrage : Agents`,back:`../settings`,space:e.space,agents:await P.getAgents(e.space),providersModels:await A.getProvidersModels(e.space)}),L=l(`<div><div class="formgroup my-2"><form><div class="formelement"><label for="name">Nom</label> <input id="name" type="text" class="textinput" placeholder="nom affiché"/> <!></div> <div class="formelement"><label for="systemPrompt">Prompt système</label> <textarea rows="10" id="systemPrompt" class="areainput" placeholder="prompt système"></textarea> <!></div> <div class="formelement"><label for="type" class="block my-1 flex-1">Type</label> <!> <!></div> <div class="text-right"><button type="button" class="bt">Annuler</button> <button type="submit" class="bt mx-4">Enregistrer</button></div></form></div></div>`);function R(e,t){g(t,!0);let n=h(t,`item`,7),a=d(``),o=e=>e.group,l=e=>{e.focus()},f=e=>{e.preventDefault(),t.onSave(n())},y=e=>{e.preventDefault(),t.onCancel()};var x=L(),E=i(x),D=i(E),k=i(D),A=S(i(k),2);w(A),O(()=>_(A,()=>n().name,e=>n().name=e)),m(A,e=>l?.(e));var j=S(A,2);M(j,{key:`name`,get errors(){return t.errors}}),v(k);var P=S(k,2),F=S(i(P),2);C(F);var I=S(F,2);M(I,{key:`baseUrl`,get errors(){return t.errors}}),v(P);var R=S(P,2),z=S(i(R),2);N(z,{get items(){return t.providersModels},valueMode:`id`,groupBy:o,get value(){return u(a)},set value(e){p(a,e,!0)}});var B=S(z,2);M(B,{key:`type`,get errors(){return t.errors}}),v(R);var V=S(R,2),H=i(V);T(2),v(V),v(D),v(E),v(x),c(`submit`,D,f),s(`change`,D,()=>{t.errors.clear()}),_(F,()=>n().systemPrompt,e=>n().systemPrompt=e),s(`click`,H,y),r(e,x),b()}x([`change`,`click`]);var z=l(`<button class="rounded w-full border border-dashed p-2 border-stone-500 cursor-pointer" tabindex="0"><i class="icon-plus"></i> Ajouter un agent</button>`),B=l(`<div class="rounded border p-2 my-2 border-stone-500 flex"><div class="flex-1"><span> </span></div> <button class="bt small mx-1"><i class="icon-edit"></i></button> <button class="bt small mx-1"><i class="icon-trash-empty"></i></button></div>`),V=l(`<div class="container"><!> <!></div>`);function H(c,l){g(l,!0);let m=d(a(l.data.agents)),h=d(!1),_=d(null),y=d(a({})),x=new j,C=()=>{u(_)||(x.clear(),p(y,{},!0),p(h,!0))},w=()=>{p(h,!1)},T=e=>{e.name||x.set(`name`,`Le nom est obligatoire !`),x.size===0&&P.createAgent(l.data.space,e).then(e=>{u(m).push(e),p(h,!1),p(y,{},!0)}).catch(e=>{console.log(e)})},O=()=>{p(_,null),p(y,{},!0)},k=e=>{e.name||x.set(`name`,`Le nom est obligatoire !`),x.size===0&&P.updateAgent(l.data.space,e).then(e=>{p(m,u(m).map(t=>e.id===t.id?e:t),!0),p(_,null),p(y,{},!0)}).catch(e=>{console.log(e)})},A=async e=>{x.clear(),!u(h)&&P.getAgent(l.data.space,e).then(t=>{p(m,u(m).filter(e=>e.id===t.id?t:e),!0),p(_,e,!0)}).catch(e=>console.log(e))},M=e=>{P.deleteAgent(l.data.space,e).then(()=>{p(m,u(m).filter(t=>t.id!==e),!0)}).catch(e=>{console.log(e)})};var N=V(),F=i(N),I=e=>{var i=n(),a=o(i);t(a,()=>`new`,e=>{R(e,{get item(){return u(y)},get errors(){return x},onSave:T,onCancel:w,get providersModels(){return l.data.providersModels}})}),r(e,i)},L=e=>{var t=z();s(`click`,t,C),r(e,t)};E(F,e=>{u(h)?e(I):e(L,-1)});var H=S(F,2);D(H,17,()=>u(m),e=>e.id,(a,c)=>{var d=n(),p=o(d),m=e=>{var i=n(),a=o(i);t(a,()=>u(c).id,e=>{R(e,{get item(){return u(c)},get errors(){return x},onSave:k,onCancel:O,get providersModels(){return l.data.providersModels}})}),r(e,i)},h=t=>{var n=B(),a=i(n),o=i(a),l=i(o,!0);v(o),v(a);var d=S(a,2),p=S(d,2);v(n),e(()=>f(l,u(c).name)),s(`click`,d,()=>A(u(c).id)),s(`click`,p,()=>M(u(c).id)),r(t,n)};E(p,e=>{u(_)===u(c).id?e(m):e(h,-1)}),r(a,d)}),v(N),r(c,N),b()}x([`click`]);export{H as component,F as universal};
@@ -0,0 +1 @@
import{B as e,C as t,D as n,E as r,G as i,J as a,K as o,L as s,M as c,N as l,O as u,P as d,Q as f,T as p,X as m,_ as h,a as g,at as _,c as v,dt as y,ft as b,h as x,it as S,j as C,q as w,tt as T,u as E,ut as D,w as O,x as k,z as A}from"../chunks/39JK5HWF.js";import"../chunks/xihTtKlq.js";import{t as j}from"../chunks/BC84wEQu.js";import{t as M}from"../chunks/D8dI9nx6.js";import{t as N}from"../chunks/DOY2HMbt.js";import{n as P,t as F}from"../chunks/DRdGzrsF.js";import{t as I}from"../chunks/BorM6hBh.js";var L={getAgents:e=>j.get(`/agent/${e}`),getAgent:(e,t)=>j.get(`/agent/${e}/${t}`),createAgent:(e,t)=>j.post(`/agent/${e}`,t),updateAgent:(e,t)=>j.put(`/agent/${e}`,t),deleteAgent:(e,t)=>j.delete(`/agent/${e}/${t}`)},R={getSpace:e=>j.get(`/team/${e}`),updateSpace:(e,t)=>j.put(`/team/${e}`,t)},z=b({load:()=>B}),B=async({params:e})=>({title:`Paramétrage : Agents`,back:`../settings`,space:await R.getSpace(e.space),agents:await L.getAgents(e.space),providersModels:await N.getProvidersModels(e.space)}),V=u(`<div><div class="formgroup my-2"><form><div class="formelement"><label for="name">Nom</label> <input id="name" type="text" class="textinput" placeholder="nom affiché"/> <!></div> <div class="formelement"><label for="systemPrompt">Prompt système</label> <textarea rows="10" id="systemPrompt" class="areainput" placeholder="prompt système"></textarea> <!></div> <div class="formelement"><label for="type" class="block my-1 flex-1">Type</label> <!> <!></div> <div class="text-right"><button type="button" class="bt">Annuler</button> <button type="submit" class="bt mx-4">Enregistrer</button></div></form></div></div>`);function H(e,t){_(t,!0);let n=g(t,`item`,7),o=f(a(s(()=>n().defaultProviderId+`|`+n().defaultModelId))),u=e=>e.group,p=e=>{e.focus()},b=e=>{e.preventDefault();let r=d(o).split(`|`);d(o).length>1?(n().defaultProviderId=r[0],n().defaultModelId=r[1]):(n().defaultProviderId=``,n().defaultModelId=``),t.onSave(n())},x=e=>{e.preventDefault(),t.onCancel()};var C=V(),O=i(C),k=i(O),j=i(k),M=w(i(j),2);E(M),A(()=>v(M,()=>n().name,e=>n().name=e)),h(M,e=>p?.(e));var N=w(M,2);F(N,{key:`name`,get errors(){return t.errors}}),y(j);var P=w(j,2),L=w(i(P),2);T(L);var R=w(L,2);F(R,{key:`baseUrl`,get errors(){return t.errors}}),y(P);var z=w(P,2),B=w(i(z),2);I(B,{get items(){return t.providersModels},valueMode:`id`,groupBy:u,get value(){return d(o)},set value(e){m(o,e,!0)}});var H=w(B,2);F(H,{key:`model`,get errors(){return t.errors}}),y(z);var U=w(z,2),W=i(U);D(2),y(U),y(k),y(O),y(C),l(`submit`,k,b),c(`change`,k,()=>{t.errors.clear()}),v(L,()=>n().systemPrompt,e=>n().systemPrompt=e),c(`click`,W,x),r(e,C),S()}C([`change`,`click`]);var U=u(`<button class="rounded w-full border border-dashed p-2 border-stone-500 cursor-pointer" tabindex="0"><i class="icon-plus"></i> Ajouter un agent</button>`),W=u(`<div class="rounded border p-2 my-2 border-stone-500 flex"><div class="flex-1"><span> </span></div> <button title="Modifier">Agent par défaut</button> <button class="bt small mx-1" title="Modifier"><i class="icon-edit"></i></button> <button class="bt small mx-1" title="Supprimer"><i class="icon-trash-empty"></i></button></div>`),G=u(`<div class="container"><!> <!></div>`);function K(l,u){_(u,!0);let h=f(a(s(()=>u.data.space))),g=f(a(s(()=>u.data.agents))),v=f(!1),b=f(null),C=f(a({})),T=new P,E=()=>{d(b)||(T.clear(),m(C,{},!0),m(v,!0))},D=()=>{m(v,!1)},A=e=>{e.name||T.set(`name`,`Le nom est obligatoire !`),(e.defaultProviderId===``||e.defaultModelId===``)&&T.set(`model`,`Le modèle est obligatoire !`),T.size===0&&L.createAgent(u.data.space.id,e).then(e=>{d(g).push(e),m(v,!1),m(C,{},!0)}).catch(e=>{console.log(e)})},j=()=>{m(b,null),m(C,{},!0)},N=e=>{e.name||T.set(`name`,`Le nom est obligatoire !`),T.size===0&&L.updateAgent(u.data.space.id,e).then(e=>{m(g,d(g).map(t=>e.id===t.id?e:t),!0),m(b,null),m(C,{},!0)}).catch(e=>{console.log(e)})},F=async e=>{T.clear(),!d(v)&&L.getAgent(u.data.space.id,e).then(t=>{m(g,d(g).filter(e=>e.id===t.id?t:e),!0),m(b,e,!0)}).catch(e=>console.log(e))},I=e=>{L.deleteAgent(u.data.space.id,e).then(()=>{m(g,d(g).filter(t=>t.id!==e),!0)}).catch(e=>{console.log(e)})},z=e=>{let t=d(h);t.defaultAgentId=e,R.updateSpace(t.id,t).then(e=>{M.spaces.map(t=>t.id===M.current?e:t),m(h,e,!0)}).catch(e=>{console.log(e)})};var B=G(),V=i(B),K=e=>{var i=n(),a=o(i);t(a,()=>`new`,e=>{H(e,{get item(){return d(C)},get errors(){return T},onSave:A,onCancel:D,get providersModels(){return u.data.providersModels}})}),r(e,i)},q=e=>{var t=U();c(`click`,t,E),r(e,t)};O(V,e=>{d(v)?e(K):e(q,-1)});var J=w(V,2);k(J,17,()=>d(g),e=>e.id,(a,s)=>{var l=n(),f=o(l),m=e=>{var i=n(),a=o(i);t(a,()=>d(s).id,e=>{H(e,{get item(){return d(s)},get errors(){return T},onSave:N,onCancel:j,get providersModels(){return u.data.providersModels}})}),r(e,i)},g=t=>{var n=W(),a=i(n),o=i(a),l=i(o,!0);y(o),y(a);var u=w(a,2);let f;var m=w(u,2),g=w(m,2);y(n),e(()=>{p(l,d(s).name),f=x(u,1,`bt small mx-1`,null,f,{highlight:d(s).id===d(h).defaultAgentId})}),c(`click`,u,()=>z(dLine truncated
+1 -1
View File
@@ -1 +1 @@
{"version":"1788613178637"} {"version":"1788815336322"}
+8 -8
View File
@@ -4,29 +4,29 @@
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="text-scale" content="scale" /> <meta name="text-scale" content="scale" />
<link href="/_app/immutable/entry/start.Bxa_xdGb.js" rel="modulepreload"> <link href="/_app/immutable/entry/start.wXD-FaGY.js" rel="modulepreload">
<link href="/_app/immutable/chunks/D3up1JC1.js" rel="modulepreload"> <link href="/_app/immutable/chunks/B0qrqti_.js" rel="modulepreload">
<link href="/_app/immutable/chunks/39JK5HWF.js" rel="modulepreload"> <link href="/_app/immutable/chunks/39JK5HWF.js" rel="modulepreload">
<link href="/_app/immutable/entry/app.DHv_tZ2J.js" rel="modulepreload"> <link href="/_app/immutable/entry/app.DteMb5qC.js" rel="modulepreload">
<link href="/_app/immutable/chunks/xihTtKlq.js" rel="modulepreload"> <link href="/_app/immutable/chunks/xihTtKlq.js" rel="modulepreload">
<link href="/_app/immutable/nodes/0.DNLpy_rd.js" rel="modulepreload"> <link href="/_app/immutable/nodes/0.WTbeuaG5.js" rel="modulepreload">
<link href="/_app/immutable/chunks/DTCALkcP.js" rel="modulepreload"> <link href="/_app/immutable/chunks/DTCALkcP.js" rel="modulepreload">
<link href="/_app/immutable/assets/0.Bdga2HVs.css" rel="stylesheet"> <link href="/_app/immutable/assets/0.PdzDvXHc.css" rel="stylesheet">
</head> </head>
<body data-sveltekit-preload-data="hover"> <body data-sveltekit-preload-data="hover">
<div style="display: contents"> <div style="display: contents">
<script> <script>
{ {
__sveltekit_1oispqz = { __sveltekit_1vzpa2i = {
base: "" base: ""
}; };
const element = document.currentScript.parentElement; const element = document.currentScript.parentElement;
Promise.all([ Promise.all([
import("/_app/immutable/entry/start.Bxa_xdGb.js"), import("/_app/immutable/entry/start.wXD-FaGY.js"),
import("/_app/immutable/entry/app.DHv_tZ2J.js") import("/_app/immutable/entry/app.DteMb5qC.js")
]).then(([kit, app]) => { ]).then(([kit, app]) => {
kit.start(app, element); kit.start(app, element);
}); });
-5
View File
@@ -1,5 +0,0 @@
package llmserver
type Session struct {
server string
}
+246
View File
@@ -0,0 +1,246 @@
package chat
import (
"context"
"fmt"
"log/slog"
"sync"
"time"
"trankilou.fr/lassistanoque/backend/internal/domain"
)
type Service struct {
repoUser domain.UserRepository
repoAgent domain.AgentRepository
repoProvider domain.ProviderRepository
repoChat domain.ChatRepository
llmEngine domain.LLMEngine
runningSessions map[string]*ChatSession
mu sync.Mutex
}
func NewService(
repoUser domain.UserRepository,
repoAgent domain.AgentRepository,
repoProvider domain.ProviderRepository,
repoChat domain.ChatRepository,
llmEngine domain.LLMEngine,
) *Service {
return &Service{
repoUser: repoUser,
repoAgent: repoAgent,
repoProvider: repoProvider,
repoChat: repoChat,
llmEngine: llmEngine,
runningSessions: make(map[string]*ChatSession),
}
}
func (s *Service) NewChat(
userID string,
teamID string,
agentID string,
) (*domain.Chat, error) {
team, err := s.repoUser.FindTeam(userID, teamID)
if err != nil {
return nil, err
}
start := time.Now()
chat := &domain.Chat{
UserID: userID,
AgentID: agentID,
TeamID: team.ID,
StartDatetime: &start,
Title: "Nouvelle conversation",
}
chat, err = s.repoChat.CreateChat(userID, chat)
if err != nil {
return nil, err
}
return chat, nil
}
func (s *Service) AddChatMessage(
ctx context.Context,
userID string,
teamID string,
chatID string,
agentID string,
prompt string,
params *domain.ChatParams,
) (*domain.Message, error) {
team, err := s.repoUser.FindTeam(userID, teamID)
if err != nil {
return nil, err
}
var chatAgentID string
if team.DefaultAgentID != nil {
chatAgentID = *team.DefaultAgentID
} else if agentID != "" {
chatAgentID = agentID
} else {
return nil, fmt.Errorf("no agent defined")
}
agent, err := s.repoAgent.GetAgent(userID, team.ID, chatAgentID)
if err != nil {
return nil, err
}
chatProviderID := agent.DefaultProviderID
chatModelID := agent.DefaultModelID
if params != nil && params.ProviderID != "" {
chatProviderID = params.ProviderID
}
if params != nil && params.ModelID != "" {
chatModelID = params.ModelID
}
provider, err := s.repoProvider.GetProvider(userID, teamID, chatProviderID)
if err != nil {
return nil, err
}
message, err := s.repoChat.CreateChatMessage(userID, &domain.Message{
ChatID: chatID,
TeamID: teamID,
Role: string(domain.RoleUser),
Content: prompt,
})
if err != nil {
return nil, err
}
messages, err := s.repoChat.GetChatMessages(userID, teamID, chatID, true)
if err != nil {
return nil, err
}
var cb domain.StreamCallback
if params != nil && params.OnChunk != nil {
cb = params.OnChunk
}
s.runQuery(
ctx,
userID,
teamID,
chatID,
agent,
provider,
chatModelID,
messages,
cb,
)
return message, nil
}
func (s *Service) ListChats(userID string, teamID string, page int) ([]*domain.Chat, error) {
return s.repoChat.ListChats(userID, teamID, page)
}
func (s *Service) GetChat(userID string, teamID, chatID string) (*domain.Chat, error) {
return s.repoChat.GetChat(userID, teamID, chatID)
}
func (s *Service) GetChatWithMessages(userID string, teamID, chatID string) (*domain.ChatWithMessages, error) {
chat, err := s.repoChat.GetChat(userID, teamID, chatID)
if err != nil {
return nil, err
}
messages, err := s.repoChat.GetChatMessages(userID, teamID, chatID, false)
if err != nil {
return nil, err
}
return &domain.ChatWithMessages{
Chat: chat,
Messages: messages,
}, nil
}
func (s *Service) DeleteChat(userID string, teamID, chatID string) error {
return s.repoChat.DeleteChat(userID, teamID, chatID)
}
func (s *Service) runQuery(
ctx context.Context,
userID string,
teamID string,
chatID string,
agent *domain.Agent,
provider *domain.Provider,
modelID string,
messages []*domain.Message,
callback domain.StreamCallback,
) error {
chatSession := &ChatSession{
userID: userID,
teamID: teamID,
chatID: chatID,
agent: agent,
provider: provider,
modelID: modelID,
messages: messages,
llmEngine: s.llmEngine,
subscribers: make([]domain.StreamCallback, 0),
repoChat: s.repoChat,
}
if callback != nil {
chatSession.subscribers = append(chatSession.subscribers, callback)
}
s.mu.Lock()
s.runningSessions[chatID] = chatSession
s.mu.Unlock()
go func() {
err := chatSession.run(ctx)
if err != nil {
slog.Info("chat session error", "error", err)
}
delete(s.runningSessions, chatID)
}()
return nil
}
func (s *Service) Subscribe(
userID string,
teamID string,
chatID string,
callback domain.StreamCallback,
) error {
team, err := s.repoUser.FindTeam(userID, teamID)
if err != nil {
return err
}
_, err = s.repoChat.GetChat(userID, team.ID, chatID)
if err != nil {
return err
}
s.mu.Lock()
if chat, ok := s.runningSessions[chatID]; ok {
chat.subscribers = append(chat.subscribers, callback)
}
s.mu.Unlock()
return nil
}
+111
View File
@@ -0,0 +1,111 @@
package chat
import (
"context"
"sync"
"trankilou.fr/lassistanoque/backend/internal/domain"
)
type ChatSession struct {
userID string
teamID string
chatID string
agent *domain.Agent
provider *domain.Provider
modelID string
messages []*domain.Message
llmEngine domain.LLMEngine
repoChat domain.ChatRepository
subscribers []domain.StreamCallback
mu sync.Mutex
}
func (s *ChatSession) run(ctx context.Context) error {
params := &domain.LLMParams{}
tools := make([]*domain.ToolDefinition, 0)
for _, t := range allTools {
tools = append(tools, t.Definition(ctx))
}
params.Tools = tools
s.mu.Lock()
if len(s.subscribers) > 0 {
params.OnChunk = func(chunk *domain.Chunk) {
for _, cb := range s.subscribers {
cb(chunk)
}
}
}
s.mu.Unlock()
systemPrompt := &domain.Message{
Role: string(domain.RoleSystem),
Content: s.agent.SystemPrompt,
}
continue_loop := true
// LOOP on TOOLS
for continue_loop {
msg := s.llmEngine.Stream(
ctx,
s.provider,
s.modelID,
params,
append([]*domain.Message{systemPrompt}, s.messages...),
)
msg.ChatID = s.chatID
msg.TeamID = s.teamID
msg, err := s.repoChat.CreateChatMessage(s.userID, msg)
if err != nil {
return err
}
s.messages = append(s.messages, msg)
// TOOL
if len(msg.ToolCalls) > 0 {
continue_loop = true
for _, tc := range msg.ToolCalls {
tool := GetTool(tc.Function.Name)
output, err := tool.Execute(ctx, []byte(tc.Function.Arguments))
var toolResponse *domain.Message
if err != nil {
toolResponse = &domain.Message{
ChatID: s.chatID,
TeamID: s.teamID,
ToolCallID: tc.ID,
Role: string(domain.RoleTool),
Content: "ERROR: " + err.Error(),
}
} else {
toolResponse = &domain.Message{
ChatID: s.chatID,
TeamID: s.teamID,
ToolCallID: tc.ID,
Role: string(domain.RoleTool),
Content: string(output),
}
}
toolResponse, err = s.repoChat.CreateChatMessage(s.userID, toolResponse)
if err != nil {
return err
}
s.messages = append(s.messages, toolResponse)
}
} else {
continue_loop = false
}
// END TOOL
}
// END LOOP
return nil
}
+14
View File
@@ -0,0 +1,14 @@
package chat
import "trankilou.fr/lassistanoque/backend/internal/domain"
var allTools = make(map[string]domain.Tool)
func RegisterTool(tool domain.Tool) error {
allTools[tool.Name()] = tool
return nil
}
func GetTool(name string) domain.Tool {
return allTools[name]
}
Binary file not shown.
+165
View File
@@ -8,6 +8,7 @@
"name": "web", "name": "web",
"version": "0.0.1", "version": "0.0.1",
"dependencies": { "dependencies": {
"@humanspeak/svelte-markdown": "^1.9.2",
"oidc-client-ts": "^3.5.0", "oidc-client-ts": "^3.5.0",
"svelte-select": "^6.0.0" "svelte-select": "^6.0.0"
}, },
@@ -262,6 +263,51 @@
"node": ">=18.18.0" "node": ">=18.18.0"
} }
}, },
"node_modules/@humanspeak/memory-cache": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@humanspeak/memory-cache/-/memory-cache-1.2.0.tgz",
"integrity": "sha512-im7GIIpgjdD6Fx68xCeiXw3XiDv30/WMRAaodMm+ep0ptYfEUd0cdNeTAylHGIRTJIeByKFHdTmPjAktyDM/JQ==",
"license": "MIT",
"engines": {
"node": ">=20.19.0"
}
},
"node_modules/@humanspeak/svelte-markdown": {
"version": "1.9.2",
"resolved": "https://registry.npmjs.org/@humanspeak/svelte-markdown/-/svelte-markdown-1.9.2.tgz",
"integrity": "sha512-sczMB4oNSpADBWT6l9AkIErpJDDzGsamzl2/w3J5yAYBWWofLcmVDCti5KY5l0+Cec2p9xyb73qkkukoNmLPWQ==",
"license": "MIT",
"dependencies": {
"@humanspeak/memory-cache": "^1.1.2",
"github-slugger": "^2.0.0",
"htmlparser2": "^12.0.0",
"marked": "^18.0.9"
},
"engines": {
"node": ">=22"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/humanspeak"
},
"peerDependencies": {
"katex": ">=0.16.0",
"mermaid": ">=10.0.0",
"shiki": ">=4.0.0",
"svelte": "^5.0.0"
},
"peerDependenciesMeta": {
"katex": {
"optional": true
},
"mermaid": {
"optional": true
},
"shiki": {
"optional": true
}
}
},
"node_modules/@humanwhocodes/module-importer": { "node_modules/@humanwhocodes/module-importer": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
@@ -1722,6 +1768,73 @@
"integrity": "sha512-RWrqdArjvPbsATEhOPUo6Wndc/iWnkWKlhIrdlF3zMMYo/c3CVtoaVAyLtWxz5h8nSlkHzxnzV2uLydPXmtF+A==", "integrity": "sha512-RWrqdArjvPbsATEhOPUo6Wndc/iWnkWKlhIrdlF3zMMYo/c3CVtoaVAyLtWxz5h8nSlkHzxnzV2uLydPXmtF+A==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/dom-serializer": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-3.1.1.tgz",
"integrity": "sha512-4MEa38/QexBob6gFNwu+EGdWvhJ1OKuNwdYY3Y3NyeWDQfnGeDYQUDfIRzWu5B5gsv03so2Uxd28YC6zrsx3Lw==",
"license": "MIT",
"dependencies": {
"domelementtype": "^3.0.0",
"domhandler": "^6.0.0",
"entities": "^8.0.0"
},
"engines": {
"node": ">=20.19.0"
},
"funding": {
"type": "github",
"url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
}
},
"node_modules/domelementtype": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-3.0.0.tgz",
"integrity": "sha512-umCQid3jKbDmVjx8jGaW7uUykm4DEUeyV21hPxNMo2nV955DhUThwqyOIDtreepP31hl84X7G5U9ZfsWvIB3Pg==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/fb55"
}
],
"license": "BSD-2-Clause",
"engines": {
"node": ">=20.19.0"
}
},
"node_modules/domhandler": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-6.0.1.tgz",
"integrity": "sha512-gYzvtM72ZtxQO0T048kd6HWSbbGCNOUwcnfQ01cqIJ4X2IYKFFHZ5mKvrQETcFXxsRObZulDaKmy//R7TPtsBg==",
"license": "BSD-2-Clause",
"dependencies": {
"domelementtype": "^3.0.0"
},
"engines": {
"node": ">=20.19.0"
},
"funding": {
"type": "github",
"url": "https://github.com/fb55/domhandler?sponsor=1"
}
},
"node_modules/domutils": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-4.0.2.tgz",
"integrity": "sha512-qI4JLRKnSzqFqr7hAlS5xQDusBCjKSEG4t4+7aNrIQMHBcsC2TGEhuyABJdYkgSewL57PNLYEiibY2iPKhKpaA==",
"license": "BSD-2-Clause",
"dependencies": {
"dom-serializer": "^3.0.0",
"domelementtype": "^3.0.0",
"domhandler": "^6.0.0"
},
"engines": {
"node": ">=20.19.0"
},
"funding": {
"type": "github",
"url": "https://github.com/fb55/domutils?sponsor=1"
}
},
"node_modules/enhanced-resolve": { "node_modules/enhanced-resolve": {
"version": "5.24.5", "version": "5.24.5",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.24.5.tgz", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.24.5.tgz",
@@ -1736,6 +1849,18 @@
"node": ">=10.13.0" "node": ">=10.13.0"
} }
}, },
"node_modules/entities": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-8.1.0.tgz",
"integrity": "sha512-kxL7msIffSuh9aaFAMD7rxAIuTRMAHMeBtgHW2yUdWw732ZNh4MehkF2gdjvtdmikkaIP9bFDDJOPlsvm7avrA==",
"license": "BSD-2-Clause",
"engines": {
"node": ">=20.19.0"
},
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/escape-string-regexp": { "node_modules/escape-string-regexp": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
@@ -2095,6 +2220,12 @@
"node": "^8.16.0 || ^10.6.0 || >=11.0.0" "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
} }
}, },
"node_modules/github-slugger": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz",
"integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==",
"license": "ISC"
},
"node_modules/glob-parent": { "node_modules/glob-parent": {
"version": "6.0.2", "version": "6.0.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
@@ -2128,6 +2259,28 @@
"dev": true, "dev": true,
"license": "ISC" "license": "ISC"
}, },
"node_modules/htmlparser2": {
"version": "12.0.0",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-12.0.0.tgz",
"integrity": "sha512-Tz7u1i95/g2x2jz81+x0FBVhBhY5aRTvD3tXXdFaljuNdzDLJ8UGNRrTcj2cgQvAg3iW/h77Fz15nLW0L0CrZw==",
"funding": [
"https://github.com/fb55/htmlparser2?sponsor=1",
{
"type": "github",
"url": "https://github.com/sponsors/fb55"
}
],
"license": "MIT",
"dependencies": {
"domelementtype": "^3.0.0",
"domhandler": "^6.0.0",
"domutils": "^4.0.2",
"entities": "^8.0.0"
},
"engines": {
"node": ">=20.19.0"
}
},
"node_modules/ignore": { "node_modules/ignore": {
"version": "5.3.2", "version": "5.3.2",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
@@ -2632,6 +2785,18 @@
"@jridgewell/sourcemap-codec": "^1.5.5" "@jridgewell/sourcemap-codec": "^1.5.5"
} }
}, },
"node_modules/marked": {
"version": "18.0.14",
"resolved": "https://registry.npmjs.org/marked/-/marked-18.0.14.tgz",
"integrity": "sha512-mBHK6FBHuBAlhgRe88w9F0O1AbwwXJUcQibUbC/QcdTbVGAD7aWza+xt3N6oT/jCZx3/OMeS+8rnuiHZcQ9s7A==",
"license": "MIT",
"bin": {
"marked": "bin/marked.js"
},
"engines": {
"node": ">= 20"
}
},
"node_modules/mini-svg-data-uri": { "node_modules/mini-svg-data-uri": {
"version": "1.4.4", "version": "1.4.4",
"resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz", "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz",
+1
View File
@@ -48,6 +48,7 @@
"esbuild": "^0.25.0" "esbuild": "^0.25.0"
}, },
"dependencies": { "dependencies": {
"@humanspeak/svelte-markdown": "^1.9.2",
"oidc-client-ts": "^3.5.0", "oidc-client-ts": "^3.5.0",
"svelte-select": "^6.0.0" "svelte-select": "^6.0.0"
} }
+62
View File
@@ -48,15 +48,77 @@ async function request<T>(path: string, options: RequestOptions = {}): Promise<T
return res.json(); return res.json();
} }
async function* requestStream<T = unknown>(
path: string,
options: RequestOptions = {}
): AsyncGenerator<T, void, undefined> {
const { auth = true, headers, ...rest } = options;
const requestHeaders: HeadersInit = {
'Content-Type': 'application/json',
...headers,
};
const fetchHeaders = new Headers(requestHeaders)
if (auth) {
const token = getToken()
if (token) {
fetchHeaders.append('Authorization', `Bearer ${token}`)
}
}
const res = await fetch(`${BASE_URL}${path}`, {
...rest,
headers: fetchHeaders,
});
if (res.status === 401 ) {
logout()
goto("/login")
//throw new ApiError(401, await safeJson(res));
}
if (!res.ok || !res.body) throw new Error(`HTTP ${res.status}`);
const reader = res.body!.pipeThrough(new TextDecoderStream()).getReader();
let buffer = '';
try {
while (true) {
const { value, done } = await reader.read();
if (done) break;
buffer += value;
const lines = buffer.split('\n');
buffer = lines.pop() ?? ''; // dernière ligne potentiellement incomplète
for (const line of lines) {
if (line.trim()) yield JSON.parse(line) as T;
}
}
if (buffer.trim()) yield JSON.parse(buffer) as T; // reste éventuel
} finally {
reader.releaseLock();
}
return //res.json();
}
async function safeJson(res: Response) { async function safeJson(res: Response) {
try { return await res.json(); } catch { return null; } try { return await res.json(); } catch { return null; }
} }
export const api = { export const api = {
get: <T>(path: string, opts?: RequestOptions) => request<T>(path, { ...opts, method: 'GET' }), get: <T>(path: string, opts?: RequestOptions) => request<T>(path, { ...opts, method: 'GET' }),
post: <T>(path: string, body?: unknown, opts?: RequestOptions) => post: <T>(path: string, body?: unknown, opts?: RequestOptions) =>
request<T>(path, { ...opts, method: 'POST', body: JSON.stringify(body) }), request<T>(path, { ...opts, method: 'POST', body: JSON.stringify(body) }),
postStream: <T>(path: string, body?: unknown, opts?: RequestOptions) =>
requestStream<T>(path, {...opts, method: 'POST', body: JSON.stringify(body) }),
put: <T>(path: string, body?: unknown, opts?: RequestOptions) => put: <T>(path: string, body?: unknown, opts?: RequestOptions) =>
request<T>(path, { ...opts, method: 'PUT', body: JSON.stringify(body) }), request<T>(path, { ...opts, method: 'PUT', body: JSON.stringify(body) }),
delete: <T>(path: string, opts?: RequestOptions) => request<T>(path, { ...opts, method: 'DELETE' }), delete: <T>(path: string, opts?: RequestOptions) => request<T>(path, { ...opts, method: 'DELETE' }),
}; };
+11
View File
@@ -0,0 +1,11 @@
import { api } from '../client';
import type { Chat, Chunk, ChatRequest, ChatWithMessages } from "$lib/types/api"
export const chatApi = {
createChat: (space: string, data: Partial<ChatRequest>) => api.post<Chat>(`/chat/${space}`, data),
createChatMessage: (space: string, id: string, data: Partial<ChatRequest>) => api.postStream<Chunk>(`/chat/${space}/${id}`, data),
allChats: (space: string, pageNum: number) => api.get<Chat[]>(`/chat/${space}/all/${pageNum}`),
getChat: (space: string, id: string) => api.get<ChatWithMessages>(`/chat/${space}/${id}`),
subscribe: (space: string, id: string) => api.get<ChatWithMessages>(`/chat/${space}/${id}/subscribe`),
deleteChat: (space: string, id: string) => api.delete(`/chat/${space}/${id}`),
};
+4 -2
View File
@@ -5,8 +5,10 @@
const closeDrawer = (e: Event) => { const closeDrawer = (e: Event) => {
e.preventDefault() e.preventDefault()
uiState.sidebarMobileOpen=false if (uiState.sidebarMobileOpen) {
uiState.menuContentVisible=false uiState.sidebarMobileOpen=false
uiState.menuContentVisible=false
}
goto(link) goto(link)
} }
</script> </script>
+62
View File
@@ -78,6 +78,61 @@ export interface Agent {
_version: string _version: string
} }
export type Chat = {
id: string
teamId: string
startDatetime: string
endDatetime: string
agentId: string
userId: string
taskId: string
title: string
_date_created: string
_date_updated: string
_version: string
}
export interface Message {
id: string
role: string
content: string
reasoning?: string
toolCallId: string
toolcalls: Array<ToolCall>
_date_created: string
_date_updated: string
_version: string
}
export interface ToolCall {
ID: string
Type: string
Function: {
Name: string
Arguments: string
}
}
export type ChatWithMessages = {
chat: Chat
messages: Message[]
}
export type Chunk = {
toolName: string
done: boolean
role: string
content: string
reasoning: boolean
}
export type ChatRequest = {
agentId: string
providerId: string
modelId: string
prompt: string
}
export enum SaveState { export enum SaveState {
None, None,
@@ -86,3 +141,10 @@ export enum SaveState {
Saved, Saved,
Error, Error,
} }
export type PageRequest = {
pageNum: number
pageSize: number
filters: Map<string, string>
}
+3
View File
@@ -0,0 +1,3 @@
export const autofocus = (node: HTMLElement) => {
node.focus();
}
@@ -67,7 +67,7 @@
<div class="flex flex-col h-screen "> <div class="flex flex-col h-screen ">
<div class="px-2"> <div class="px-2">
<div class="header container"> <div class="header container">
<h1 class="flex"> <h1 class="flex mx-2">
<span class="flex-1">{page.data.title}</span> <span class="flex-1">{page.data.title}</span>
{#if page.data.back} {#if page.data.back}
<span><a href={page.data.back} title="retour"><i class="icon-left-big"></i></a></span> <span><a href={page.data.back} title="retour"><i class="icon-left-big"></i></a></span>
@@ -1,8 +1,35 @@
<div class="container"> <script lang="ts">
Lorem ipsum dolor sit amet consectetur, adipisicing elit. Asperiores, reiciendis quo. Quos animi nisi modi cumque hic illo doloremque totam! Distinctio molestias sint magnam illum nostrum eos, fugit aliquid consequuntur? import {autofocus} from "$lib/ui/autofocus"
Lorem ipsum dolor sit amet consectetur, adipisicing elit. Asperiores, reiciendis quo. Quos animi nisi modi cumque hic illo doloremque totam! Distinctio molestias sint magnam illum nostrum eos, fugit aliquid consequuntur? import {chatApi} from "$lib/api/endpoints/chat"
Lorem ipsum dolor sit amet consectetur, adipisicing elit. Asperiores, reiciendis quo. Quos animi nisi modi cumque hic illo doloremque totam! Distinctio molestias sint magnam illum nostrum eos, fugit aliquid consequuntur? import type {Chat} from "$lib/types/api"
Lorem ipsum dolor sit amet consectetur, adipisicing elit. Asperiores, reiciendis quo. Quos animi nisi modi cumque hic illo doloremque totam! Distinctio molestias sint magnam illum nostrum eos, fugit aliquid consequuntur? import type { PageProps } from './$types';
Lorem ipsum dolor sit amet consectetur, adipisicing elit. Asperiores, reiciendis quo. Quos animi nisi modi cumque hic illo doloremque totam! Distinctio molestias sint magnam illum nostrum eos, fugit aliquid consequuntur? import { goto } from "$app/navigation";
Lorem ipsum dolor sit amet consectetur, adipisicing elit. Asperiores, reiciendis quo. Quos animi nisi modi cumque hic illo doloremque totam! Distinctio molestias sint magnam illum nostrum eos, fugit aliquid consequuntur?
let { data }: PageProps = $props();
let chat = $state({} as Chat)
let prompt = $state("")
const handleOnSubmit = async () => {
chatApi.createChat(data.spaceId, chat)
.then((result)=>{
goto(`/${data.spaceId}/${result.id}`, {state: {prompt: prompt }})
}).catch((err)=>console.log(err))
}
</script>
<div class="h-full w-full flex items-center public">
<div class="container">
<form class="request flex w-full" onsubmit={handleOnSubmit}>
<input
class="chatinput w-full flex-1 focus:outline-hidden focus:shadow-none focus:ring-0"
type="text"
placeholder="Demande ce que tu veux"
bind:value={prompt}
use:autofocus
/>
<button class="chatbutton">Go</button>
</form>
</div>
</div> </div>
@@ -1,3 +1,8 @@
export function load() { import type { PageLoad } from "./$types";
return { title: 'Conversation' };
export const load: PageLoad = ({params}) => {
return {
title: 'Nouvelle conversation',
spaceId : params.space,
};
} }
@@ -0,0 +1,121 @@
<script lang="ts">
import {autofocus} from "$lib/ui/autofocus"
import { onMount } from "svelte"
import {page} from "$app/state"
import {chatApi} from "$lib/api/endpoints/chat"
import type { PageProps } from './$types';
import type { Message, ChatWithMessages } from "$lib/types/api"
import SvelteMarkdown from '@humanspeak/svelte-markdown'
let prompt = $state("")
let { data }: PageProps = $props();
let messages = $state([] as Message[])
let waiting = $state(false)
onMount(async ()=>{
if (page.state.prompt) {
console.log(page.state.prompt)
await sendRequest(page.state.prompt)
} else {
let chatWithMessage : ChatWithMessages = await chatApi.getChat(data.spaceId, data.chatId)
messages = chatWithMessage.messages
}
})
let historyDiv: HTMLDivElement;
const sendRequest = async(theprompt:string) => {
prompt = ""
let lastrole=""
let lastToolName=""
waiting = true
for await (const chunk of chatApi.createChatMessage(data.spaceId, data.chatId, { prompt: theprompt })) {
if (waiting && chunk.role==="assistant") {
waiting = false
}
if (chunk.role!=lastrole || chunk.toolName!=lastToolName) {
const currentDate = new Date()
let m = {
id: ''+currentDate.getTime(),
role: chunk.role,
content: "",
reasoning: "",
} as Message
if (chunk.toolName) {
m.toolcalls = [{
ID: "0",
Type: "Function",
Function: {
Name: chunk.toolName,
Arguments: ""
}
}]
}
if (chunk.reasoning) {
m.reasoning = chunk.content
} else {
m.content = chunk.content
}
messages.push(m)
lastrole=chunk.role
lastToolName=chunk.toolName
} else {
if (chunk.reasoning) {
messages[messages.length-1].reasoning += chunk.content
} else {
messages[messages.length-1].content += chunk.content
}
}
if (chunk.done) {
document
.querySelectorAll<HTMLDivElement>('div.reasoning:not(.hidden)')
.forEach((el) => el.classList.add('hidden'));
}
historyDiv.scrollTop = historyDiv.scrollHeight;
}
}
const handleOnSubmit = async ()=>{
await sendRequest(prompt)
}
</script>
<div class="h-full flex flex-col gap-2">
<div id="history" class="flex-1 container overflow-auto" bind:this={historyDiv}>
{#each messages as message (message.id)}
<div class={["message", "rounded", message.role]}>
{#if message.reasoning }
<div class="message-content reasoning">
<SvelteMarkdown source={message.reasoning} streaming={true}/>
</div>
{/if}
{#if message.toolcalls != null && message.toolcalls.length>0}
<div class="text-green-600">⚒️ Appel de l'outil {message.toolcalls[0].Function.Name}</div>
{:else}
<div class="message-content">
<SvelteMarkdown source={message.content} streaming={true}/>
</div>
{/if}
</div>
{/each}
{#if waiting}
<div class="animate-pulse">...</div>
{/if}
</div>
<div id="prompt" class="container">
<form class="request flex w-full" onsubmit={handleOnSubmit}>
<input
class="chatinput w-full flex-1 focus:outline-hidden focus:shadow-none focus:ring-0"
type="text"
placeholder="..."
bind:value={prompt}
use:autofocus
/>
<button class="chatbutton">Go</button>
</form>
</div>
</div>
@@ -0,0 +1,9 @@
import type { PageLoad } from "./$types";
export const load: PageLoad = ({params}) => {
return {
title: 'Nouvelle conversation',
spaceId: params.space,
chatId: params.conversation,
};
}
@@ -1,6 +1,15 @@
Lorem ipsum dolor sit amet consectetur, adipisicing elit. Asperiores, reiciendis quo. Quos animi nisi modi cumque hic illo doloremque totam! Distinctio molestias sint magnam illum nostrum eos, fugit aliquid consequuntur? <script lang="ts">
Lorem ipsum dolor sit amet consectetur, adipisicing elit. Asperiores, reiciendis quo. Quos animi nisi modi cumque hic illo doloremque totam! Distinctio molestias sint magnam illum nostrum eos, fugit aliquid consequuntur? import type { PageProps } from './$types';
Lorem ipsum dolor sit amet consectetur, adipisicing elit. Asperiores, reiciendis quo. Quos animi nisi modi cumque hic illo doloremque totam! Distinctio molestias sint magnam illum nostrum eos, fugit aliquid consequuntur? let { data }: PageProps = $props();
Lorem ipsum dolor sit amet consectetur, adipisicing elit. Asperiores, reiciendis quo. Quos animi nisi modi cumque hic illo doloremque totam! Distinctio molestias sint magnam illum nostrum eos, fugit aliquid consequuntur? </script>
Lorem ipsum dolor sit amet consectetur, adipisicing elit. Asperiores, reiciendis quo. Quos animi nisi modi cumque hic illo doloremque totam! Distinctio molestias sint magnam illum nostrum eos, fugit aliquid consequuntur?
Lorem ipsum dolor sit amet consectetur, adipisicing elit. Asperiores, reiciendis quo. Quos animi nisi modi cumque hic illo doloremque totam! Distinctio molestias sint magnam illum nostrum eos, fugit aliquid consequuntur? <div class="container">
{#each data.conversations as conversation (conversation.id)}
<div class="dark:bg-stone-800 rounded p-2 my-2">
<div class="font-bold text-base">
<a href={`/${data.space}/${conversation.id}`}>{conversation.title}</a>
</div>
<span class="text-xs">{conversation._date_created}</span>
</div>
{/each}
</div>
@@ -1,3 +1,10 @@
export function load() { import { chatApi } from "$lib/api/endpoints/chat"
return { title: 'Historique' }; import type { PageLoad } from './$types';
export const load: PageLoad = async ({ params }) => {
return {
title: 'Historique',
conversations: await chatApi.allChats(params.space, 0),
space: params.space,
};
} }
@@ -100,7 +100,6 @@
let updSpace = space let updSpace = space
updSpace.defaultAgentId = agentId updSpace.defaultAgentId = agentId
teamApi.updateSpace(updSpace.id, updSpace).then((newdata)=>{ teamApi.updateSpace(updSpace.id, updSpace).then((newdata)=>{
console.log(newdata)
spaceState.spaces.map((s)=>s.id===spaceState.current?newdata:s) spaceState.spaces.map((s)=>s.id===spaceState.current?newdata:s)
space=newdata space=newdata
}).catch((err)=>{ }).catch((err)=>{
@@ -108,8 +107,17 @@
}) })
} }
const handleKeydown = (event: KeyboardEvent) => {
event.preventDefault()
if (event.key==="Escape") {
handleCancelEditAgent()
}
}
</script> </script>
<svelte:window onkeydown={handleKeydown} />
<div class="container"> <div class="container">
{#if addingAgent} {#if addingAgent}
@@ -142,7 +150,7 @@
{/key} {/key}
{:else} {:else}
<div class="rounded border p-2 my-2 border-stone-500 flex"> <div class="rounded border p-2 my-2 border-stone-500 flex">
<div class="flex-1"> <div class="flex-1 p-2">
<span>{ agent.name }</span> <span>{ agent.name }</span>
</div> </div>
<button class="bt small mx-1" class:highlight={agent.id===space.defaultAgentId} onclick={()=>handleDefaultAgent(agent.id)} title="Modifier"> <button class="bt small mx-1" class:highlight={agent.id===space.defaultAgentId} onclick={()=>handleDefaultAgent(agent.id)} title="Modifier">
@@ -101,8 +101,17 @@
.catch((err) => {console.log(err)}) .catch((err) => {console.log(err)})
} }
const handleKeydown = (event: KeyboardEvent) => {
event.preventDefault()
if (event.key==="Escape") {
cancelEditProviderHandler()
}
}
</script> </script>
<svelte:window onkeydown={handleKeydown} />
<div class="container"> <div class="container">
{#if addingProvider} {#if addingProvider}
@@ -138,7 +147,7 @@
{/key} {/key}
{:else} {:else}
<div class="rounded border p-2 my-2 border-stone-500 flex"> <div class="rounded border p-2 my-2 border-stone-500 flex">
<div class="flex-1"> <div class="flex-1 p-2">
<span>{ p.name }</span> <span>{ p.name }</span>
<span class="text-stone-500">({ p.type })</span> <span class="text-stone-500">({ p.type })</span>
</div> </div>
@@ -3,6 +3,7 @@
import { auth, setToken } from '$lib/state/auth.svelte'; import { auth, setToken } from '$lib/state/auth.svelte';
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import { authApi } from "$lib/api/endpoints/auth" import { authApi } from "$lib/api/endpoints/auth"
import { validateEmail } from "$lib/utilities/validators"
type Credentials = { type Credentials = {
email: string email: string
@@ -17,7 +18,7 @@
}) })
const formSubmitEmail = () => { const formSubmitEmail = () => {
credentials.validemail = true credentials.validemail = validateEmail(credentials.email)
} }
const formSubmitPass = (e: Event) => { const formSubmitPass = (e: Event) => {
@@ -43,16 +44,16 @@
<form onsubmit={formSubmitEmail}> <form onsubmit={formSubmitEmail}>
<div class="flex"> <div class="flex">
<label for="email" class="block mb-2 flex-1">Email</label> <label for="email" class="block mb-2 flex-1">Email</label>
<a href="/recover-password" class="text-xs">Mot de passe oublié ?</a> <a href="/recover-password" class="text-xs ">Mot de passe oublié ?</a>
</div> </div>
<div class="flex flex-row"> <div class="flex flex-row">
<input <input
type="text" type="text"
bind:value={credentials.email} bind:value={credentials.email}
class="textinput mr-1" class="textinput mr-1 flex-1"
autofocus autofocus
/> />
<button class="bt" title="page suivante"> <button class="bt small" title="page suivante">
<i class="icon-right-big"></i> <i class="icon-right-big"></i>
</button> </button>
</div> </div>
@@ -72,10 +73,10 @@
<input <input
type="password" type="password"
bind:value={credentials.password} bind:value={credentials.password}
class="textinput mr-1" class="textinput mr-1 flex-1"
autofocus autofocus
/> />
<button class="bt"> <button class="bt small">
Entrer Entrer
</button> </button>
</div> </div>
+45 -9
View File
@@ -11,7 +11,7 @@ body {
@apply h-full bg-stone-100 text-stone-950 dark:bg-stone-950 dark:text-stone-100; @apply h-full bg-stone-100 text-stone-950 dark:bg-stone-950 dark:text-stone-100;
} }
.formelement > label { label {
@apply text-xs text-stone-600 uppercase dark:text-stone-400; @apply text-xs text-stone-600 uppercase dark:text-stone-400;
} }
hr { hr {
@@ -30,13 +30,13 @@ h2 {
} }
.bt { .bt {
appearance: button; appearance: button;
@apply min-w-40 cursor-pointer rounded bg-stone-300 px-4 py-1 transition-all duration-300 hover:bg-stone-200 dark:bg-stone-800 dark:hover:bg-stone-700; @apply min-w-40 cursor-pointer rounded bg-stone-300 px-4 py-1 transition-all duration-300 hover:bg-stone-400 dark:bg-stone-800 dark:hover:bg-stone-700;
&.small { &.small {
@apply min-w-0 px-2 py-1; @apply min-w-2 px-2 py-1;
} }
&.changed, &.changed,
&.saving { &.saving {
@apply bg-cyan-400 dark:bg-cyan-600; @apply bg-sky-400 dark:bg-sky-700;
} }
&.saved { &.saved {
@apply bg-green-300 dark:bg-green-700; @apply bg-green-300 dark:bg-green-700;
@@ -62,10 +62,12 @@ h2 {
.public h1 { .public h1 {
@apply mb-4 text-3xl font-bold; @apply mb-4 text-3xl font-bold;
} }
.profile a, .profile a {
.public a {
@apply bg-sky-300 dark:bg-sky-700; @apply bg-sky-300 dark:bg-sky-700;
} }
.public a {
@apply text-sky-500 dark:text-sky-700;
}
#outer { #outer {
@apply flex h-full flex-row; @apply flex h-full flex-row;
@@ -113,10 +115,18 @@ h2 {
} }
} }
.chatinput {
@apply w-full rounded-l border-stone-300 bg-stone-50 p-2 text-stone-900 dark:border-stone-700 dark:bg-stone-900 dark:text-stone-100;
}
.chatbutton {
@apply min-w-12 cursor-pointer rounded-r bg-stone-300 px-2 py-1 transition-all duration-300 hover:bg-stone-400 dark:bg-stone-700 dark:hover:bg-stone-600;
}
/* form */ /* form */
.formgroup { .formgroup {
@apply rounded border border-stone-300 bg-stone-100 p-2 text-sm dark:border-stone-700 dark:bg-stone-900; @apply rounded border border-stone-300 bg-stone-200 p-2 text-sm dark:border-stone-700 dark:bg-stone-900;
} }
.formelement { .formelement {
@@ -124,11 +134,11 @@ h2 {
} }
.textinput { .textinput {
@apply h-8 w-full rounded border-stone-700 bg-stone-50 px-2 py-1 text-sm text-stone-900 dark:bg-stone-950 dark:text-stone-100; @apply h-8 w-full rounded border-stone-300 bg-stone-50 px-2 py-1 text-sm text-stone-900 dark:border-stone-700 dark:bg-stone-950 dark:text-stone-100;
} }
.areainput { .areainput {
@apply w-full rounded border-stone-700 bg-stone-50 px-2 py-1 text-sm text-stone-900 dark:bg-stone-950 dark:text-stone-100; @apply w-full rounded border-stone-300 bg-stone-50 px-2 py-1 text-sm text-stone-900 dark:bg-stone-950 dark:text-stone-100;
} }
/* svelte select */ /* svelte select */
@@ -261,3 +271,29 @@ h2 {
.list-item { .list-item {
@apply list-none; @apply list-none;
} }
/* chat */
.message {
@apply rounded p-2;
&.user {
@apply bg-sky-700 dark:text-white;
}
}
.message-content {
@apply text-sm;
&.reasoning {
@apply text-white/50;
}
}
.message-content p {
@apply my-2;
}
.message-content pre {
@apply my-2 whitespace-pre dark:bg-stone-700;
}
+10
View File
@@ -0,0 +1,10 @@
/** @type {import('tailwindcss').Config} */
export default {
content: [
"./src/**/*.{html,js,svelte,ts}",
],
theme: {
extend: {},
},
plugins: [],
}