update architecture docs for split MCP files, new clusters, Langfuse, and EC2 deploy

This commit is contained in:
2026-04-16 13:04:44 -03:00
parent 69441fa180
commit a8e55a4a8d
5 changed files with 530 additions and 469 deletions

View File

@@ -5,50 +5,46 @@ digraph deployment {
node [fontname="Helvetica" fontsize=10 style=filled color="#1e2a4a" fontcolor="#e8eaf0"] node [fontname="Helvetica" fontsize=10 style=filled color="#1e2a4a" fontcolor="#e8eaf0"]
edge [fontname="Helvetica" fontsize=9 fontcolor="#8892a8" color="#4a5568"] edge [fontname="Helvetica" fontsize=9 fontcolor="#8892a8" color="#4a5568"]
label="Deployment — Kind Cluster (dev) / EC2 (prod)" label="Deployment — Kind Clusters (dev) / EC2 (prod)"
labelloc=t labelloc=t
fontsize=14 fontsize=14
fontcolor="#0066ff" fontcolor="#0066ff"
user [label="Browser\nlocalhost:8040" fillcolor="#243056" shape=box] user [label="Browser" fillcolor="#243056" shape=box]
subgraph cluster_kind { subgraph cluster_ec2 {
label="Kind Cluster: unt (namespace: unt)" label="EC2 (mcrn.ar)"
color="#ff9800"
fontcolor="#ff9800"
style=rounded
nginx_edge [label="nginx (gateway container)\n443 · SSL\nstellarair.mcrn.ar\nlangfuse.mcrn.ar\nnova.mcrn.ar (Kong backend)" fillcolor="#121829" shape=box]
nova_ui [label="nova-ui\nnginx + Vue build" fillcolor="#0d1a33" shape=box]
nova_api [label="nova-api\nuvicorn · FastAPI\nMCP clients (stdio)" fillcolor="#0d1a33" shape=box]
}
subgraph cluster_kind_unt {
label="Kind Cluster: unt (local dev)"
color="#0066ff" color="#0066ff"
fontcolor="#0066ff" fontcolor="#0066ff"
style=rounded style=rounded
subgraph cluster_frontend_pod { unt_ui [label="ui pod\nnginx + Vue\nNodePort 30040" fillcolor="#121829" shape=box]
label="Pod: ui" unt_api [label="api pod\nuvicorn · FastAPI" fillcolor="#121829" shape=box]
color="#1e2a4a"
fontcolor="#4a5568"
ui [label="nginx\n:80\n\nVue SPA\nProxy → api:8000" fillcolor="#121829" shape=box]
ui_svc [label="Service: ui\nNodePort 30040" fillcolor="#0d1a33" shape=diamond fontsize=9]
} }
subgraph cluster_api_pod { subgraph cluster_kind_lng {
label="Pod: api" label="Kind Cluster: lng (shared observability)"
color="#1e2a4a" color="#00c853"
fontcolor="#4a5568" fontcolor="#00c853"
api [label="uvicorn\n:8000\n\nFastAPI\nMCP clients (stdio)\nLangGraph agents" fillcolor="#121829" shape=box] style=rounded
api_svc [label="Service: api\nClusterIP" fillcolor="#0d1a33" shape=diamond fontsize=9]
}
subgraph cluster_langfuse_pod { lf_web [label="langfuse-web\nNext.js\nNodePort 30030" fillcolor="#121829" shape=box]
label="Pod: langfuse" lf_worker [label="langfuse-worker\nClickHouse writer" fillcolor="#121829" shape=box]
color="#1e2a4a" lf_ch [label="ClickHouse\ntraces · spans" fillcolor="#0d1a33" shape=cylinder]
fontcolor="#4a5568" lf_pg [label="Postgres\nmetadata" fillcolor="#0d1a33" shape=cylinder]
langfuse [label="Langfuse\n:3000\n\nTrace viewer" fillcolor="#121829" shape=box] lf_redis [label="Redis\nqueue · cache" fillcolor="#0d1a33" shape=cylinder]
langfuse_svc [label="Service: langfuse\nNodePort 30030" fillcolor="#0d1a33" shape=diamond fontsize=9] lf_minio [label="MinIO\nS3 events" fillcolor="#0d1a33" shape=cylinder]
}
subgraph cluster_pg_pod {
label="Pod: postgres"
color="#1e2a4a"
fontcolor="#4a5568"
pg [label="PostgreSQL\n:5432\n\nLangfuse data" fillcolor="#121829" shape=cylinder]
pg_svc [label="Service: postgres\nClusterIP" fillcolor="#0d1a33" shape=diamond fontsize=9]
}
} }
subgraph cluster_external { subgraph cluster_external {
@@ -58,25 +54,33 @@ digraph deployment {
style=dashed style=dashed
ext_weather [label="OpenMeteo" fillcolor="#0d2a0d" shape=octagon fontcolor="#00c853"] ext_weather [label="OpenMeteo" fillcolor="#0d2a0d" shape=octagon fontcolor="#00c853"]
ext_faa [label="FAA" fillcolor="#0d2a0d" shape=octagon fontcolor="#00c853"] ext_faa [label="FAA" fillcolor="#0d2a0d" shape=octagon fontcolor="#00c853"]
ext_bedrock [label="AWS Bedrock" fillcolor="#243056" shape=octagon] ext_bedrock [label="Bedrock / Groq\nAnthropic / OpenAI" fillcolor="#243056" shape=octagon]
ext_kong [label="Kong Konnect\n(optional)" fillcolor="#243056" shape=octagon style="filled,dashed"] ext_kong [label="Kong Konnect\n(optional gateway)" fillcolor="#243056" shape=octagon style="filled,dashed"]
} }
// Port mappings // Prod path
user -> ui_svc [label="host:8040 → 30040" color="#0066ff"] user -> nginx_edge [label="stellarair.mcrn.ar" color="#ff9800"]
ui_svc -> ui user -> ext_kong [label="(API governance)" style=dashed color="#4a5568"]
ui -> api_svc [label="proxy"] ext_kong -> nginx_edge [label="X-Gateway-Secret" style=dashed color="#4a5568"]
api_svc -> api nginx_edge -> nova_ui
nova_ui -> nova_api [label="/agents /scenarios"]
nova_api -> ext_weather [label="HTTP" color="#00c853"]
nova_api -> ext_faa [label="HTTP" color="#00c853"]
nova_api -> ext_bedrock [label="LLM calls" style=dashed]
api -> ext_weather [label="HTTP" color="#00c853"] // Dev path
api -> ext_faa [label="HTTP" color="#00c853"] user -> unt_ui [label="localhost:8040" color="#0066ff"]
api -> ext_bedrock [label="Converse API" style=dashed] unt_ui -> unt_api
api -> langfuse_svc [label="traces" style=dotted] unt_api -> ext_weather [style=dashed color="#00c853"]
unt_api -> ext_faa [style=dashed color="#00c853"]
unt_api -> ext_bedrock [style=dashed]
langfuse_svc -> langfuse // Observability (shared by both dev and prod API)
langfuse -> pg_svc unt_api -> lf_web [label="OTLP traces" style=dotted color="#00c853"]
pg_svc -> pg nova_api -> lf_web [label="OTLP traces" style=dotted color="#00c853"]
lf_web -> lf_redis
user -> ext_kong [style=dashed label="(optional)" color="#4a5568"] lf_worker -> lf_redis
ext_kong -> ui_svc [style=dashed color="#4a5568"] lf_worker -> lf_ch
lf_web -> lf_pg
lf_web -> lf_minio
} }

View File

@@ -4,222 +4,280 @@
<!-- Generated by graphviz version 14.1.2 (0) <!-- Generated by graphviz version 14.1.2 (0)
--> -->
<!-- Title: deployment Pages: 1 --> <!-- Title: deployment Pages: 1 -->
<svg style="background:#0a0e17" width="1496pt" height="2167pt" <svg width="947pt" height="632pt"
viewBox="0.00 0.00 1496.00 2167.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> viewBox="0.00 0.00 947.00 632.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(2 2) rotate(0) translate(4 1079.34)"> <g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 628.34)">
<title>deployment</title> <title>deployment</title>
<polygon fill="#0a0e17" stroke="none" points="-4,4 -4,-1079.34 744,-1079.34 744,4 -4,4"/> <polygon fill="#0a0e17" stroke="none" points="-4,4 -4,-628.34 943,-628.34 943,4 -4,4"/>
<text xml:space="preserve" text-anchor="middle" x="370" y="-1058.04" font-family="Helvetica,sans-Serif" font-size="14.00" fill="#0066ff">Deployment — Kind Cluster (dev) / EC2 (prod)</text> <text xml:space="preserve" text-anchor="middle" x="469.5" y="-607.04" font-family="Helvetica,sans-Serif" font-size="14.00" fill="#0066ff">Deployment — Kind Clusters (dev) / EC2 (prod)</text>
<g id="clust1" class="cluster"> <g id="clust1" class="cluster">
<title>cluster_kind</title> <title>cluster_ec2</title>
<path fill="#0a0e17" stroke="#0066ff" d="M20,-8C20,-8 260,-8 260,-8 266,-8 272,-14 272,-20 272,-20 272,-964.84 272,-964.84 272,-970.84 266,-976.84 260,-976.84 260,-976.84 20,-976.84 20,-976.84 14,-976.84 8,-970.84 8,-964.84 8,-964.84 8,-20 8,-20 8,-14 14,-8 20,-8"/> <path fill="#0a0e17" stroke="#ff9800" d="M536,-212.84C536,-212.84 686,-212.84 686,-212.84 692,-212.84 698,-218.84 698,-224.84 698,-224.84 698,-513.84 698,-513.84 698,-519.84 692,-525.84 686,-525.84 686,-525.84 536,-525.84 536,-525.84 530,-525.84 524,-519.84 524,-513.84 524,-513.84 524,-224.84 524,-224.84 524,-218.84 530,-212.84 536,-212.84"/>
<text xml:space="preserve" text-anchor="middle" x="140" y="-959.54" font-family="Helvetica,sans-Serif" font-size="14.00" fill="#0066ff">Kind Cluster: unt &#160;(namespace: unt)</text> <text xml:space="preserve" text-anchor="middle" x="611" y="-508.54" font-family="Helvetica,sans-Serif" font-size="14.00" fill="#ff9800">EC2 (mcrn.ar)</text>
</g> </g>
<g id="clust2" class="cluster"> <g id="clust2" class="cluster">
<title>cluster_frontend_pod</title> <title>cluster_kind_unt</title>
<path fill="#0a0e17" stroke="#1e2a4a" d="M80,-733.34C80,-733.34 252,-733.34 252,-733.34 258,-733.34 264,-739.34 264,-745.34 264,-745.34 264,-931.59 264,-931.59 264,-937.59 258,-943.59 252,-943.59 252,-943.59 80,-943.59 80,-943.59 74,-943.59 68,-937.59 68,-931.59 68,-931.59 68,-745.34 68,-745.34 68,-739.34 74,-733.34 80,-733.34"/> <path fill="#0a0e17" stroke="#0066ff" d="M280,-217.97C280,-217.97 465,-217.97 465,-217.97 471,-217.97 477,-223.97 477,-229.97 477,-229.97 477,-381.84 477,-381.84 477,-387.84 471,-393.84 465,-393.84 465,-393.84 280,-393.84 280,-393.84 274,-393.84 268,-387.84 268,-381.84 268,-381.84 268,-229.97 268,-229.97 268,-223.97 274,-217.97 280,-217.97"/>
<text xml:space="preserve" text-anchor="middle" x="166" y="-926.29" font-family="Helvetica,sans-Serif" font-size="14.00" fill="#4a5568">Pod: ui</text> <text xml:space="preserve" text-anchor="middle" x="372.5" y="-376.54" font-family="Helvetica,sans-Serif" font-size="14.00" fill="#0066ff">Kind Cluster: unt (local dev)</text>
</g> </g>
<g id="clust3" class="cluster"> <g id="clust3" class="cluster">
<title>cluster_api_pod</title> <title>cluster_kind_lng</title>
<path fill="#0a0e17" stroke="#1e2a4a" d="M122,-481.09C122,-481.09 252,-481.09 252,-481.09 258,-481.09 264,-487.09 264,-493.09 264,-493.09 264,-692.09 264,-692.09 264,-698.09 258,-704.09 252,-704.09 252,-704.09 122,-704.09 122,-704.09 116,-704.09 110,-698.09 110,-692.09 110,-692.09 110,-493.09 110,-493.09 110,-487.09 116,-481.09 122,-481.09"/> <path fill="#0a0e17" stroke="#00c853" d="M20,-8C20,-8 361,-8 361,-8 367,-8 373,-14 373,-20 373,-20 373,-169.08 373,-169.08 373,-175.08 367,-181.08 361,-181.08 361,-181.08 20,-181.08 20,-181.08 14,-181.08 8,-175.08 8,-169.08 8,-169.08 8,-20 8,-20 8,-14 14,-8 20,-8"/>
<text xml:space="preserve" text-anchor="middle" x="187" y="-686.79" font-family="Helvetica,sans-Serif" font-size="14.00" fill="#4a5568">Pod: api</text> <text xml:space="preserve" text-anchor="middle" x="190.5" y="-163.78" font-family="Helvetica,sans-Serif" font-size="14.00" fill="#00c853">Kind Cluster: lng (shared observability)</text>
</g> </g>
<g id="clust4" class="cluster"> <g id="clust4" class="cluster">
<title>cluster_langfuse_pod</title>
<path fill="#0a0e17" stroke="#1e2a4a" d="M74,-254.34C74,-254.34 252,-254.34 252,-254.34 258,-254.34 264,-260.34 264,-266.34 264,-266.34 264,-439.84 264,-439.84 264,-445.84 258,-451.84 252,-451.84 252,-451.84 74,-451.84 74,-451.84 68,-451.84 62,-445.84 62,-439.84 62,-439.84 62,-266.34 62,-266.34 62,-260.34 68,-254.34 74,-254.34"/>
<text xml:space="preserve" text-anchor="middle" x="163" y="-434.54" font-family="Helvetica,sans-Serif" font-size="14.00" fill="#4a5568">Pod: langfuse</text>
</g>
<g id="clust5" class="cluster">
<title>cluster_pg_pod</title>
<path fill="#0a0e17" stroke="#1e2a4a" d="M72,-16C72,-16 252,-16 252,-16 258,-16 264,-22 264,-28 264,-28 264,-223.34 264,-223.34 264,-229.34 258,-235.34 252,-235.34 252,-235.34 72,-235.34 72,-235.34 66,-235.34 60,-229.34 60,-223.34 60,-223.34 60,-28 60,-28 60,-22 66,-16 72,-16"/>
<text xml:space="preserve" text-anchor="middle" x="162" y="-218.04" font-family="Helvetica,sans-Serif" font-size="14.00" fill="#4a5568">Pod: postgres</text>
</g>
<g id="clust6" class="cluster">
<title>cluster_external</title> <title>cluster_external</title>
<polygon fill="#0a0e17" stroke="#00c853" stroke-dasharray="5,2" points="280,-354.45 280,-446.98 732,-446.98 732,-354.45 280,-354.45"/> <polygon fill="#0a0e17" stroke="#00c853" stroke-dasharray="5,2" points="381,-91.06 381,-183.59 931,-183.59 931,-91.06 381,-91.06"/>
<text xml:space="preserve" text-anchor="middle" x="506" y="-429.68" font-family="Helvetica,sans-Serif" font-size="14.00" fill="#00c853">External APIs</text> <text xml:space="preserve" text-anchor="middle" x="656" y="-166.29" font-family="Helvetica,sans-Serif" font-size="14.00" fill="#00c853">External APIs</text>
</g> </g>
<!-- user --> <!-- user -->
<g id="node1" class="node"> <g id="node1" class="node">
<title>user</title> <title>user</title>
<polygon fill="#243056" stroke="#1e2a4a" points="465.62,-1050.09 378.38,-1050.09 378.38,-1014.09 465.62,-1014.09 465.62,-1050.09"/> <polygon fill="#243056" stroke="#1e2a4a" points="638.88,-599.09 583.12,-599.09 583.12,-563.09 638.88,-563.09 638.88,-599.09"/>
<text xml:space="preserve" text-anchor="middle" x="422" y="-1035.34" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">Browser</text> <text xml:space="preserve" text-anchor="middle" x="611" y="-577.97" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">Browser</text>
<text xml:space="preserve" text-anchor="middle" x="422" y="-1022.59" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">localhost:8040</text>
</g> </g>
<!-- ui_svc --> <!-- nginx_edge -->
<g id="node3" class="node"> <g id="node2" class="node">
<title>ui_svc</title> <title>nginx_edge</title>
<polygon fill="#0d1a33" stroke="#1e2a4a" points="166,-910.34 75.75,-879.84 166,-849.34 256.25,-879.84 166,-910.34"/> <polygon fill="#121829" stroke="#1e2a4a" points="690.25,-492.59 531.75,-492.59 531.75,-420.84 690.25,-420.84 690.25,-492.59"/>
<text xml:space="preserve" text-anchor="middle" x="166" y="-882.54" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#e8eaf0">Service: ui</text> <text xml:space="preserve" text-anchor="middle" x="611" y="-479.09" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">nginx (gateway container)</text>
<text xml:space="preserve" text-anchor="middle" x="166" y="-871.29" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#e8eaf0">NodePort 30040</text> <text xml:space="preserve" text-anchor="middle" x="611" y="-466.34" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">443 · SSL</text>
<text xml:space="preserve" text-anchor="middle" x="611" y="-453.59" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">stellarair.mcrn.ar</text>
<text xml:space="preserve" text-anchor="middle" x="611" y="-440.84" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">langfuse.mcrn.ar</text>
<text xml:space="preserve" text-anchor="middle" x="611" y="-428.09" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">nova.mcrn.ar (Kong backend)</text>
</g> </g>
<!-- user&#45;&gt;ui_svc --> <!-- user&#45;&gt;nginx_edge -->
<g id="edge1" class="edge"> <g id="edge1" class="edge">
<title>user&#45;&gt;ui_svc</title> <title>user&#45;&gt;nginx_edge</title>
<path fill="none" stroke="#0066ff" d="M392.38,-1013.71C346.98,-987.06 259.97,-936 208.05,-905.52"/> <path fill="none" stroke="#ff9800" d="M611,-562.82C611,-547.66 611,-524.89 611,-504.42"/>
<polygon fill="#0066ff" stroke="#0066ff" points="209.86,-902.53 199.46,-900.48 206.31,-908.56 209.86,-902.53"/> <polygon fill="#ff9800" stroke="#ff9800" points="614.5,-504.55 611,-494.55 607.5,-504.55 614.5,-504.55"/>
<text xml:space="preserve" text-anchor="middle" x="405.13" y="-987.54" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#8892a8">host:8040 → 30040</text> <text xml:space="preserve" text-anchor="middle" x="649.25" y="-536.54" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#8892a8">stellarair.mcrn.ar</text>
</g>
<!-- unt_ui -->
<g id="node5" class="node">
<title>unt_ui</title>
<polygon fill="#121829" stroke="#1e2a4a" points="469,-360.59 375,-360.59 375,-314.34 469,-314.34 469,-360.59"/>
<text xml:space="preserve" text-anchor="middle" x="422" y="-347.09" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">ui pod</text>
<text xml:space="preserve" text-anchor="middle" x="422" y="-334.34" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">nginx + Vue</text>
<text xml:space="preserve" text-anchor="middle" x="422" y="-321.59" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">NodePort 30040</text>
</g>
<!-- user&#45;&gt;unt_ui -->
<g id="edge9" class="edge">
<title>user&#45;&gt;unt_ui</title>
<path fill="none" stroke="#0066ff" d="M582.83,-578.8C546.98,-575.55 485.81,-564.31 453.25,-525.84 416.79,-482.77 415.11,-413.57 418,-372.41"/>
<polygon fill="#0066ff" stroke="#0066ff" points="421.48,-372.86 418.84,-362.6 414.5,-372.26 421.48,-372.86"/>
<text xml:space="preserve" text-anchor="middle" x="486.62" y="-453.79" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#8892a8">localhost:8040</text>
</g> </g>
<!-- ext_kong --> <!-- ext_kong -->
<g id="node13" class="node"> <g id="node16" class="node">
<title>ext_kong</title> <title>ext_kong</title>
<polygon fill="#243056" stroke="#1e2a4a" stroke-dasharray="5,2" points="723.76,-377.47 723.76,-398.71 687,-413.73 635,-413.73 598.24,-398.71 598.24,-377.47 635,-362.45 687,-362.45 723.76,-377.47"/> <polygon fill="#243056" stroke="#1e2a4a" stroke-dasharray="5,2" points="922.85,-114.08 922.85,-135.32 874.32,-150.34 805.68,-150.34 757.15,-135.32 757.15,-114.08 805.68,-99.06 874.32,-99.06 922.85,-114.08"/>
<text xml:space="preserve" text-anchor="middle" x="661" y="-391.34" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">Kong Konnect</text> <text xml:space="preserve" text-anchor="middle" x="840" y="-127.95" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">Kong Konnect</text>
<text xml:space="preserve" text-anchor="middle" x="661" y="-378.59" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">(optional)</text> <text xml:space="preserve" text-anchor="middle" x="840" y="-115.2" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">(optional gateway)</text>
</g> </g>
<!-- user&#45;&gt;ext_kong --> <!-- user&#45;&gt;ext_kong -->
<g id="edge12" class="edge">
<title>user&#45;&gt;ext_kong</title>
<path fill="none" stroke="#4a5568" stroke-dasharray="5,2" d="M465.88,-1022.95C532.38,-1007.78 651,-968.46 651,-880.84 651,-880.84 651,-880.84 651,-529.97 651,-494.35 654.22,-453.8 657,-425.38"/>
<polygon fill="#4a5568" stroke="#4a5568" points="660.48,-425.8 658.01,-415.5 653.52,-425.1 660.48,-425.8"/>
<text xml:space="preserve" text-anchor="middle" x="672.75" y="-714.79" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#8892a8">(optional)</text>
</g>
<!-- ui -->
<g id="node2" class="node">
<title>ui</title>
<polygon fill="#121829" stroke="#1e2a4a" points="229,-812.34 129,-812.34 129,-741.34 229,-741.34 229,-812.34"/>
<text xml:space="preserve" text-anchor="middle" x="179" y="-798.84" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">nginx</text>
<text xml:space="preserve" text-anchor="middle" x="179" y="-786.09" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">:80</text>
<text xml:space="preserve" text-anchor="middle" x="179" y="-761.34" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">Vue SPA</text>
<text xml:space="preserve" text-anchor="middle" x="179" y="-748.59" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">Proxy → api:8000</text>
</g>
<!-- api_svc -->
<g id="node5" class="node">
<title>api_svc</title>
<polygon fill="#0d1a33" stroke="#1e2a4a" points="187,-670.84 118.5,-640.34 187,-609.84 255.5,-640.34 187,-670.84"/>
<text xml:space="preserve" text-anchor="middle" x="187" y="-643.04" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#e8eaf0">Service: api</text>
<text xml:space="preserve" text-anchor="middle" x="187" y="-631.79" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#e8eaf0">ClusterIP</text>
</g>
<!-- ui&#45;&gt;api_svc -->
<g id="edge3" class="edge">
<title>ui&#45;&gt;api_svc</title>
<path fill="none" stroke="#4a5568" d="M181.08,-740.86C182.16,-722.68 183.49,-700.38 184.61,-681.5"/>
<polygon fill="#4a5568" stroke="#4a5568" points="188.09,-681.96 185.19,-671.77 181.1,-681.54 188.09,-681.96"/>
<text xml:space="preserve" text-anchor="middle" x="195.51" y="-714.79" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#8892a8">proxy</text>
</g>
<!-- ui_svc&#45;&gt;ui -->
<g id="edge2" class="edge"> <g id="edge2" class="edge">
<title>ui_svc&#45;&gt;ui</title> <title>user&#45;&gt;ext_kong</title>
<path fill="none" stroke="#4a5568" d="M169.69,-850.17C170.75,-841.97 171.92,-832.82 173.08,-823.86"/> <path fill="none" stroke="#4a5568" stroke-dasharray="5,2" d="M639.37,-576.2C694.78,-566.84 812,-537.59 812,-457.72 812,-457.72 812,-457.72 812,-242.97 812,-214.91 819.68,-184.08 826.99,-161.07"/>
<polygon fill="#4a5568" stroke="#4a5568" points="176.52,-824.56 174.32,-814.19 169.57,-823.66 176.52,-824.56"/> <polygon fill="#4a5568" stroke="#4a5568" points="830.21,-162.49 830.04,-151.89 823.57,-160.28 830.21,-162.49"/>
<text xml:space="preserve" text-anchor="middle" x="851" y="-334.54" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#8892a8">(API governance)</text>
</g> </g>
<!-- api --> <!-- nova_ui -->
<g id="node3" class="node">
<title>nova_ui</title>
<polygon fill="#0d1a33" stroke="#1e2a4a" points="660.12,-355.47 557.88,-355.47 557.88,-319.47 660.12,-319.47 660.12,-355.47"/>
<text xml:space="preserve" text-anchor="middle" x="609" y="-340.72" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">nova&#45;ui</text>
<text xml:space="preserve" text-anchor="middle" x="609" y="-327.97" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">nginx + Vue build</text>
</g>
<!-- nginx_edge&#45;&gt;nova_ui -->
<g id="edge4" class="edge">
<title>nginx_edge&#45;&gt;nova_ui</title>
<path fill="none" stroke="#4a5568" d="M610.4,-420.54C610.11,-403.44 609.76,-383.17 609.49,-367.05"/>
<polygon fill="#4a5568" stroke="#4a5568" points="612.99,-367.1 609.32,-357.16 605.99,-367.22 612.99,-367.1"/>
</g>
<!-- nova_api -->
<g id="node4" class="node"> <g id="node4" class="node">
<title>api</title> <title>nova_api</title>
<polygon fill="#121829" stroke="#1e2a4a" points="255.75,-572.84 148.25,-572.84 148.25,-489.09 255.75,-489.09 255.75,-572.84"/> <polygon fill="#0d1a33" stroke="#1e2a4a" points="662.75,-267.09 555.25,-267.09 555.25,-220.84 662.75,-220.84 662.75,-267.09"/>
<text xml:space="preserve" text-anchor="middle" x="202" y="-559.34" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">uvicorn</text> <text xml:space="preserve" text-anchor="middle" x="609" y="-253.59" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">nova&#45;api</text>
<text xml:space="preserve" text-anchor="middle" x="202" y="-546.59" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">:8000</text> <text xml:space="preserve" text-anchor="middle" x="609" y="-240.84" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">uvicorn · FastAPI</text>
<text xml:space="preserve" text-anchor="middle" x="202" y="-521.84" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">FastAPI</text> <text xml:space="preserve" text-anchor="middle" x="609" y="-228.09" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">MCP clients (stdio)</text>
<text xml:space="preserve" text-anchor="middle" x="202" y="-509.09" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">MCP clients (stdio)</text>
<text xml:space="preserve" text-anchor="middle" x="202" y="-496.34" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">LangGraph agents</text>
</g> </g>
<!-- langfuse_svc --> <!-- nova_ui&#45;&gt;nova_api -->
<g id="edge5" class="edge">
<title>nova_ui&#45;&gt;nova_api</title>
<path fill="none" stroke="#4a5568" d="M609,-319.26C609,-307.77 609,-292.29 609,-278.43"/>
<polygon fill="#4a5568" stroke="#4a5568" points="612.5,-278.81 609,-268.81 605.5,-278.81 612.5,-278.81"/>
<text xml:space="preserve" text-anchor="middle" x="649.88" y="-287.79" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#8892a8">/agents /scenarios</text>
</g>
<!-- lf_web -->
<g id="node7" class="node"> <g id="node7" class="node">
<title>langfuse_svc</title> <title>lf_web</title>
<polygon fill="#0d1a33" stroke="#1e2a4a" points="163,-418.59 69.75,-388.09 163,-357.59 256.25,-388.09 163,-418.59"/> <polygon fill="#121829" stroke="#1e2a4a" points="245,-147.83 151,-147.83 151,-101.58 245,-101.58 245,-147.83"/>
<text xml:space="preserve" text-anchor="middle" x="163" y="-390.79" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#e8eaf0">Service: langfuse</text> <text xml:space="preserve" text-anchor="middle" x="198" y="-134.33" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">langfuse&#45;web</text>
<text xml:space="preserve" text-anchor="middle" x="163" y="-379.54" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#e8eaf0">NodePort 30030</text> <text xml:space="preserve" text-anchor="middle" x="198" y="-121.58" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">Next.js</text>
<text xml:space="preserve" text-anchor="middle" x="198" y="-108.83" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">NodePort 30030</text>
</g> </g>
<!-- api&#45;&gt;langfuse_svc --> <!-- nova_api&#45;&gt;lf_web -->
<g id="edge8" class="edge"> <g id="edge15" class="edge">
<title>api&#45;&gt;langfuse_svc</title> <title>nova_api&#45;&gt;lf_web</title>
<path fill="none" stroke="#4a5568" stroke-dasharray="1,5" d="M190.61,-488.84C185.23,-469.38 178.83,-446.29 173.56,-427.22"/> <path fill="none" stroke="#00c853" stroke-dasharray="1,5" d="M555.01,-229.3C532.25,-223.79 505.42,-217.62 481,-212.84 420.96,-201.1 405.73,-199 345,-191.59 324.85,-189.13 272.32,-192.33 254,-183.59 241.18,-177.48 229.72,-167.11 220.55,-156.81"/>
<polygon fill="#4a5568" stroke="#4a5568" points="176.96,-426.4 170.92,-417.69 170.21,-428.27 176.96,-426.4"/> <polygon fill="#00c853" stroke="#00c853" points="223.49,-154.87 214.39,-149.44 218.12,-159.36 223.49,-154.87"/>
<text xml:space="preserve" text-anchor="middle" x="198.71" y="-462.54" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#8892a8">traces</text> <text xml:space="preserve" text-anchor="middle" x="453.63" y="-194.29" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#8892a8">OTLP traces</text>
</g> </g>
<!-- ext_weather --> <!-- ext_weather -->
<g id="node10" class="node"> <g id="node13" class="node">
<title>ext_weather</title> <title>ext_weather</title>
<polygon fill="#0d2a0d" stroke="#1e2a4a" points="383.85,-380.64 383.85,-395.55 355.82,-406.09 316.18,-406.09 288.15,-395.55 288.15,-380.64 316.18,-370.09 355.82,-370.09 383.85,-380.64"/> <polygon fill="#0d2a0d" stroke="#1e2a4a" points="738.85,-117.25 738.85,-132.16 710.82,-142.7 671.18,-142.7 643.15,-132.16 643.15,-117.25 671.18,-106.7 710.82,-106.7 738.85,-117.25"/>
<text xml:space="preserve" text-anchor="middle" x="336" y="-384.97" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#00c853">OpenMeteo</text> <text xml:space="preserve" text-anchor="middle" x="691" y="-121.58" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#00c853">OpenMeteo</text>
</g> </g>
<!-- api&#45;&gt;ext_weather --> <!-- nova_api&#45;&gt;ext_weather -->
<g id="edge5" class="edge"> <g id="edge6" class="edge">
<title>api&#45;&gt;ext_weather</title> <title>nova_api&#45;&gt;ext_weather</title>
<path fill="none" stroke="#00c853" d="M241.12,-488.84C263.97,-464.82 292.08,-435.27 311.75,-414.59"/> <path fill="none" stroke="#00c853" d="M662.06,-220.51C668.98,-215.59 675.29,-209.74 680,-202.84 689.6,-188.78 692.42,-169.89 692.81,-154.32"/>
<polygon fill="#00c853" stroke="#00c853" points="314.22,-417.07 318.58,-407.41 309.15,-412.24 314.22,-417.07"/> <polygon fill="#00c853" stroke="#00c853" points="696.31,-154.68 692.71,-144.71 689.31,-154.75 696.31,-154.68"/>
<text xml:space="preserve" text-anchor="middle" x="276.23" y="-462.54" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#8892a8">HTTP</text> <text xml:space="preserve" text-anchor="middle" x="697.13" y="-194.29" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#8892a8">HTTP</text>
</g> </g>
<!-- ext_faa --> <!-- ext_faa -->
<g id="node11" class="node"> <g id="node14" class="node">
<title>ext_faa</title> <title>ext_faa</title>
<polygon fill="#0d2a0d" stroke="#1e2a4a" points="456,-380.64 456,-395.55 440.18,-406.09 417.82,-406.09 402,-395.55 402,-380.64 417.82,-370.09 440.18,-370.09 456,-380.64"/> <polygon fill="#0d2a0d" stroke="#1e2a4a" points="625,-117.25 625,-132.16 609.18,-142.7 586.82,-142.7 571,-132.16 571,-117.25 586.82,-106.7 609.18,-106.7 625,-117.25"/>
<text xml:space="preserve" text-anchor="middle" x="429" y="-384.97" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#00c853">FAA</text> <text xml:space="preserve" text-anchor="middle" x="598" y="-121.58" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#00c853">FAA</text>
</g> </g>
<!-- api&#45;&gt;ext_faa --> <!-- nova_api&#45;&gt;ext_faa -->
<g id="edge6" class="edge"> <g id="edge7" class="edge">
<title>api&#45;&gt;ext_faa</title> <title>nova_api&#45;&gt;ext_faa</title>
<path fill="none" stroke="#00c853" d="M256.12,-518.06C297.44,-506.72 353.78,-486.18 393,-451.84 403.89,-442.31 412.27,-428.64 418.19,-416.5"/> <path fill="none" stroke="#00c853" d="M642.89,-220.59C651.8,-211.88 657.36,-201.61 651,-191.59 646.53,-184.54 640.24,-189.14 634,-183.59 624.52,-175.15 616.66,-163.63 610.73,-153.08"/>
<polygon fill="#00c853" stroke="#00c853" points="421.23,-418.29 422.15,-407.74 414.84,-415.41 421.23,-418.29"/> <polygon fill="#00c853" stroke="#00c853" points="613.9,-151.58 606.16,-144.35 607.7,-154.83 613.9,-151.58"/>
<text xml:space="preserve" text-anchor="middle" x="391.29" y="-462.54" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#8892a8">HTTP</text> <text xml:space="preserve" text-anchor="middle" x="664.99" y="-194.29" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#8892a8">HTTP</text>
</g> </g>
<!-- ext_bedrock --> <!-- ext_bedrock -->
<g id="node12" class="node"> <g id="node15" class="node">
<title>ext_bedrock</title> <title>ext_bedrock</title>
<polygon fill="#243056" stroke="#1e2a4a" points="580.31,-380.64 580.31,-395.55 549.08,-406.09 504.92,-406.09 473.69,-395.55 473.69,-380.64 504.92,-370.09 549.08,-370.09 580.31,-380.64"/> <polygon fill="#243056" stroke="#1e2a4a" points="552.7,-114.08 552.7,-135.32 504.84,-150.34 437.16,-150.34 389.3,-135.32 389.3,-114.08 437.16,-99.06 504.84,-99.06 552.7,-114.08"/>
<text xml:space="preserve" text-anchor="middle" x="527" y="-384.97" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">AWS Bedrock</text> <text xml:space="preserve" text-anchor="middle" x="471" y="-127.95" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">Bedrock / Groq</text>
<text xml:space="preserve" text-anchor="middle" x="471" y="-115.2" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">Anthropic / OpenAI</text>
</g> </g>
<!-- api&#45;&gt;ext_bedrock --> <!-- nova_api&#45;&gt;ext_bedrock -->
<g id="edge7" class="edge"> <g id="edge8" class="edge">
<title>api&#45;&gt;ext_bedrock</title> <title>nova_api&#45;&gt;ext_bedrock</title>
<path fill="none" stroke="#4a5568" stroke-dasharray="5,2" d="M256.09,-522.53C311.82,-512.97 399.56,-492.5 465,-451.84 480.6,-442.15 495.18,-427.78 506.29,-415.23"/> <path fill="none" stroke="#4a5568" stroke-dasharray="5,2" d="M603.93,-220.47C600.62,-210.42 595.33,-199.16 587,-191.59 578.37,-183.74 572.56,-188.54 562,-183.59 545.87,-176.03 529.15,-166.09 514.52,-156.59"/>
<polygon fill="#4a5568" stroke="#4a5568" points="508.91,-417.54 512.74,-407.66 503.59,-413 508.91,-417.54"/> <polygon fill="#4a5568" stroke="#4a5568" points="516.76,-153.88 506.49,-151.27 512.9,-159.71 516.76,-153.88"/>
<text xml:space="preserve" text-anchor="middle" x="475.05" y="-462.54" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#8892a8">Converse API</text> <text xml:space="preserve" text-anchor="middle" x="615.8" y="-194.29" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#8892a8">LLM calls</text>
</g> </g>
<!-- api_svc&#45;&gt;api --> <!-- unt_api -->
<g id="edge4" class="edge">
<title>api_svc&#45;&gt;api</title>
<path fill="none" stroke="#4a5568" d="M190.94,-611.13C192.09,-602.92 193.38,-593.66 194.67,-584.46"/>
<polygon fill="#4a5568" stroke="#4a5568" points="198.1,-585.21 196.01,-574.82 191.16,-584.24 198.1,-585.21"/>
</g>
<!-- langfuse -->
<g id="node6" class="node"> <g id="node6" class="node">
<title>langfuse</title> <title>unt_api</title>
<polygon fill="#121829" stroke="#1e2a4a" points="200.75,-320.59 123.25,-320.59 123.25,-262.34 200.75,-262.34 200.75,-320.59"/> <polygon fill="#121829" stroke="#1e2a4a" points="469.12,-261.97 372.88,-261.97 372.88,-225.97 469.12,-225.97 469.12,-261.97"/>
<text xml:space="preserve" text-anchor="middle" x="162" y="-307.09" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">Langfuse</text> <text xml:space="preserve" text-anchor="middle" x="421" y="-247.22" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">api pod</text>
<text xml:space="preserve" text-anchor="middle" x="162" y="-294.34" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">:3000</text> <text xml:space="preserve" text-anchor="middle" x="421" y="-234.47" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">uvicorn · FastAPI</text>
<text xml:space="preserve" text-anchor="middle" x="162" y="-269.59" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">Trace viewer</text>
</g> </g>
<!-- pg_svc --> <!-- unt_ui&#45;&gt;unt_api -->
<g id="node9" class="node">
<title>pg_svc</title>
<polygon fill="#0d1a33" stroke="#1e2a4a" points="162,-202.09 68,-171.59 162,-141.09 256,-171.59 162,-202.09"/>
<text xml:space="preserve" text-anchor="middle" x="162" y="-174.29" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#e8eaf0">Service: postgres</text>
<text xml:space="preserve" text-anchor="middle" x="162" y="-163.04" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#e8eaf0">ClusterIP</text>
</g>
<!-- langfuse&#45;&gt;pg_svc -->
<g id="edge10" class="edge"> <g id="edge10" class="edge">
<title>langfuse&#45;&gt;pg_svc</title> <title>unt_ui&#45;&gt;unt_api</title>
<path fill="none" stroke="#4a5568" d="M162,-261.93C162,-247.46 162,-229.62 162,-213.69"/> <path fill="none" stroke="#4a5568" d="M421.75,-313.85C421.62,-301.69 421.45,-286.58 421.31,-273.61"/>
<polygon fill="#4a5568" stroke="#4a5568" points="165.5,-214.02 162,-204.02 158.5,-214.02 165.5,-214.02"/> <polygon fill="#4a5568" stroke="#4a5568" points="424.82,-273.92 421.21,-263.96 417.82,-274 424.82,-273.92"/>
</g> </g>
<!-- langfuse_svc&#45;&gt;langfuse --> <!-- unt_api&#45;&gt;lf_web -->
<g id="edge9" class="edge"> <g id="edge14" class="edge">
<title>langfuse_svc&#45;&gt;langfuse</title> <title>unt_api&#45;&gt;lf_web</title>
<path fill="none" stroke="#4a5568" d="M162.69,-357.41C162.6,-349.53 162.51,-340.88 162.42,-332.55"/> <path fill="none" stroke="#00c853" stroke-dasharray="1,5" d="M372.64,-232.1C337.77,-222.81 290.61,-207.15 254,-183.59 242.63,-176.28 231.86,-166.23 222.86,-156.56"/>
<polygon fill="#4a5568" stroke="#4a5568" points="165.92,-332.55 162.32,-322.59 158.92,-332.62 165.92,-332.55"/> <polygon fill="#00c853" stroke="#00c853" points="225.71,-154.5 216.44,-149.36 220.49,-159.16 225.71,-154.5"/>
<text xml:space="preserve" text-anchor="middle" x="314.52" y="-194.29" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#8892a8">OTLP traces</text>
</g> </g>
<!-- pg --> <!-- unt_api&#45;&gt;ext_weather -->
<g id="node8" class="node">
<title>pg</title>
<path fill="#121829" stroke="#1e2a4a" d="M204.5,-96.81C204.5,-100.83 185.45,-104.09 162,-104.09 138.55,-104.09 119.5,-100.83 119.5,-96.81 119.5,-96.81 119.5,-31.28 119.5,-31.28 119.5,-27.26 138.55,-24 162,-24 185.45,-24 204.5,-27.26 204.5,-31.28 204.5,-31.28 204.5,-96.81 204.5,-96.81"/>
<path fill="none" stroke="#1e2a4a" d="M204.5,-96.81C204.5,-92.79 185.45,-89.53 162,-89.53 138.55,-89.53 119.5,-92.79 119.5,-96.81"/>
<text xml:space="preserve" text-anchor="middle" x="162" y="-79.67" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">PostgreSQL</text>
<text xml:space="preserve" text-anchor="middle" x="162" y="-66.92" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">:5432</text>
<text xml:space="preserve" text-anchor="middle" x="162" y="-42.17" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">Langfuse data</text>
</g>
<!-- pg_svc&#45;&gt;pg -->
<g id="edge11" class="edge"> <g id="edge11" class="edge">
<title>pg_svc&#45;&gt;pg</title> <title>unt_api&#45;&gt;ext_weather</title>
<path fill="none" stroke="#4a5568" d="M162,-140.63C162,-132.92 162,-124.41 162,-115.96"/> <path fill="none" stroke="#00c853" stroke-dasharray="5,2" d="M437.98,-225.52C450.77,-213.62 469.41,-198.77 489,-191.59 519.3,-180.49 604.59,-196.87 634,-183.59 649.26,-176.7 662.63,-163.75 672.58,-151.84"/>
<polygon fill="#4a5568" stroke="#4a5568" points="165.5,-116.07 162,-106.07 158.5,-116.07 165.5,-116.07"/> <polygon fill="#00c853" stroke="#00c853" points="675.12,-154.27 678.55,-144.24 669.62,-149.94 675.12,-154.27"/>
</g> </g>
<!-- ext_kong&#45;&gt;ui_svc --> <!-- unt_api&#45;&gt;ext_faa -->
<g id="edge12" class="edge">
<title>unt_api&#45;&gt;ext_faa</title>
<path fill="none" stroke="#00c853" stroke-dasharray="5,2" d="M430.26,-225.5C437.49,-213.75 448.72,-199.08 463,-191.59 482.55,-181.34 543.16,-195.08 562,-183.59 573.33,-176.68 581.65,-164.72 587.41,-153.45"/>
<polygon fill="#00c853" stroke="#00c853" points="590.56,-154.98 591.54,-144.43 584.2,-152.06 590.56,-154.98"/>
</g>
<!-- unt_api&#45;&gt;ext_bedrock -->
<g id="edge13" class="edge"> <g id="edge13" class="edge">
<title>ext_kong&#45;&gt;ui_svc</title> <title>unt_api&#45;&gt;ext_bedrock</title>
<path fill="none" stroke="#4a5568" stroke-dasharray="5,2" d="M640.9,-413.88C621.16,-440.78 594,-485.82 594,-529.97 594,-777.84 594,-777.84 594,-777.84 594,-846.35 383.67,-868.44 257.9,-875.53"/> <path fill="none" stroke="#4a5568" stroke-dasharray="5,2" d="M425.14,-225.54C427.77,-215.45 431.52,-202.61 436,-191.59 440.2,-181.26 445.53,-170.43 450.76,-160.6"/>
<polygon fill="#4a5568" stroke="#4a5568" points="258.06,-872.01 248.27,-876.05 258.44,-879 258.06,-872.01"/> <polygon fill="#4a5568" stroke="#4a5568" points="453.68,-162.56 455.4,-152.11 447.53,-159.21 453.68,-162.56"/>
</g>
<!-- lf_pg -->
<g id="node10" class="node">
<title>lf_pg</title>
<path fill="#0d1a33" stroke="#1e2a4a" d="M161.62,-57.88C161.62,-60.19 147.45,-62.06 130,-62.06 112.55,-62.06 98.38,-60.19 98.38,-57.88 98.38,-57.88 98.38,-20.19 98.38,-20.19 98.38,-17.88 112.55,-16 130,-16 147.45,-16 161.62,-17.88 161.62,-20.19 161.62,-20.19 161.62,-57.88 161.62,-57.88"/>
<path fill="none" stroke="#1e2a4a" d="M161.62,-57.88C161.62,-55.56 147.45,-53.69 130,-53.69 112.55,-53.69 98.38,-55.56 98.38,-57.88"/>
<text xml:space="preserve" text-anchor="middle" x="130" y="-42.28" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">Postgres</text>
<text xml:space="preserve" text-anchor="middle" x="130" y="-29.53" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">metadata</text>
</g>
<!-- lf_web&#45;&gt;lf_pg -->
<g id="edge19" class="edge">
<title>lf_web&#45;&gt;lf_pg</title>
<path fill="none" stroke="#4a5568" d="M179.78,-101.28C172.25,-92.01 163.39,-81.12 155.25,-71.1"/>
<polygon fill="#4a5568" stroke="#4a5568" points="158.01,-68.95 148.99,-63.4 152.58,-73.37 158.01,-68.95"/>
</g>
<!-- lf_redis -->
<g id="node11" class="node">
<title>lf_redis</title>
<path fill="#0d1a33" stroke="#1e2a4a" d="M262.75,-57.88C262.75,-60.19 244.04,-62.06 221,-62.06 197.96,-62.06 179.25,-60.19 179.25,-57.88 179.25,-57.88 179.25,-20.19 179.25,-20.19 179.25,-17.88 197.96,-16 221,-16 244.04,-16 262.75,-17.88 262.75,-20.19 262.75,-20.19 262.75,-57.88 262.75,-57.88"/>
<path fill="none" stroke="#1e2a4a" d="M262.75,-57.88C262.75,-55.56 244.04,-53.69 221,-53.69 197.96,-53.69 179.25,-55.56 179.25,-57.88"/>
<text xml:space="preserve" text-anchor="middle" x="221" y="-42.28" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">Redis</text>
<text xml:space="preserve" text-anchor="middle" x="221" y="-29.53" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">queue · cache</text>
</g>
<!-- lf_web&#45;&gt;lf_redis -->
<g id="edge16" class="edge">
<title>lf_web&#45;&gt;lf_redis</title>
<path fill="none" stroke="#4a5568" d="M204.16,-101.28C206.51,-92.75 209.24,-82.83 211.8,-73.49"/>
<polygon fill="#4a5568" stroke="#4a5568" points="215.14,-74.54 214.42,-63.97 208.39,-72.69 215.14,-74.54"/>
</g>
<!-- lf_minio -->
<g id="node12" class="node">
<title>lf_minio</title>
<path fill="#0d1a33" stroke="#1e2a4a" d="M80,-57.88C80,-60.19 65.66,-62.06 48,-62.06 30.34,-62.06 16,-60.19 16,-57.88 16,-57.88 16,-20.19 16,-20.19 16,-17.88 30.34,-16 48,-16 65.66,-16 80,-17.88 80,-20.19 80,-20.19 80,-57.88 80,-57.88"/>
<path fill="none" stroke="#1e2a4a" d="M80,-57.88C80,-55.56 65.66,-53.69 48,-53.69 30.34,-53.69 16,-55.56 16,-57.88"/>
<text xml:space="preserve" text-anchor="middle" x="48" y="-42.28" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">MinIO</text>
<text xml:space="preserve" text-anchor="middle" x="48" y="-29.53" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">S3 events</text>
</g>
<!-- lf_web&#45;&gt;lf_minio -->
<g id="edge20" class="edge">
<title>lf_web&#45;&gt;lf_minio</title>
<path fill="none" stroke="#4a5568" d="M157.8,-101.28C136.92,-89.63 111.41,-75.4 90.24,-63.59"/>
<polygon fill="#4a5568" stroke="#4a5568" points="92,-60.57 81.56,-58.75 88.59,-66.68 92,-60.57"/>
</g>
<!-- lf_worker -->
<g id="node8" class="node">
<title>lf_worker</title>
<polygon fill="#121829" stroke="#1e2a4a" points="365.12,-142.7 262.88,-142.7 262.88,-106.7 365.12,-106.7 365.12,-142.7"/>
<text xml:space="preserve" text-anchor="middle" x="314" y="-127.95" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">langfuse&#45;worker</text>
<text xml:space="preserve" text-anchor="middle" x="314" y="-115.2" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">ClickHouse writer</text>
</g>
<!-- lf_ch -->
<g id="node9" class="node">
<title>lf_ch</title>
<path fill="#0d1a33" stroke="#1e2a4a" d="M364.75,-57.88C364.75,-60.19 346.04,-62.06 323,-62.06 299.96,-62.06 281.25,-60.19 281.25,-57.88 281.25,-57.88 281.25,-20.19 281.25,-20.19 281.25,-17.88 299.96,-16 323,-16 346.04,-16 364.75,-17.88 364.75,-20.19 364.75,-20.19 364.75,-57.88 364.75,-57.88"/>
<path fill="none" stroke="#1e2a4a" d="M364.75,-57.88C364.75,-55.56 346.04,-53.69 323,-53.69 299.96,-53.69 281.25,-55.56 281.25,-57.88"/>
<text xml:space="preserve" text-anchor="middle" x="323" y="-42.28" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">ClickHouse</text>
<text xml:space="preserve" text-anchor="middle" x="323" y="-29.53" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">traces · spans</text>
</g>
<!-- lf_worker&#45;&gt;lf_ch -->
<g id="edge18" class="edge">
<title>lf_worker&#45;&gt;lf_ch</title>
<path fill="none" stroke="#4a5568" d="M315.86,-106.37C316.88,-96.92 318.17,-84.89 319.38,-73.68"/>
<polygon fill="#4a5568" stroke="#4a5568" points="322.84,-74.26 320.43,-63.94 315.88,-73.51 322.84,-74.26"/>
</g>
<!-- lf_worker&#45;&gt;lf_redis -->
<g id="edge17" class="edge">
<title>lf_worker&#45;&gt;lf_redis</title>
<path fill="none" stroke="#4a5568" d="M294.73,-106.37C282.92,-95.74 267.5,-81.86 253.81,-69.55"/>
<polygon fill="#4a5568" stroke="#4a5568" points="256.46,-67.23 246.69,-63.14 251.78,-72.43 256.46,-67.23"/>
</g>
<!-- ext_kong&#45;&gt;nginx_edge -->
<g id="edge3" class="edge">
<title>ext_kong&#45;&gt;nginx_edge</title>
<path fill="none" stroke="#4a5568" stroke-dasharray="5,2" d="M809.27,-150.83C792.29,-163.15 770.19,-176.78 748,-183.59 735.57,-187.41 523.04,-182.24 514,-191.59 450.61,-257.19 503.77,-314.62 549,-393.84 552.65,-400.23 557.13,-406.41 562,-412.24"/>
<polygon fill="#4a5568" stroke="#4a5568" points="559.09,-414.24 568.36,-419.38 564.32,-409.58 559.09,-414.24"/>
<text xml:space="preserve" text-anchor="middle" x="534.04" y="-287.79" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#8892a8">X&#45;Gateway&#45;Secret</text>
</g> </g>
</g> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 21 KiB

View File

@@ -14,70 +14,67 @@ digraph repo_structure {
mcp [label="mcp_servers/" fillcolor="#121829"] mcp [label="mcp_servers/" fillcolor="#121829"]
agents [label="agents/" fillcolor="#121829"] agents [label="agents/" fillcolor="#121829"]
irrop [label="irrop/" fillcolor="#121829"]
api [label="api/" fillcolor="#121829"] api [label="api/" fillcolor="#121829"]
ui_root [label="ui/" fillcolor="#121829"] ui_root [label="ui/" fillcolor="#121829"]
ctrl [label="ctrl/" fillcolor="#121829"] ctrl [label="ctrl/" fillcolor="#121829"]
tests [label="tests/" fillcolor="#121829"]
woodpecker [label=".woodpecker/" fillcolor="#121829"]
docs [label="docs/" fillcolor="#121829"] docs [label="docs/" fillcolor="#121829"]
// MCP subtree // MCP subtree
mcp_shared [label="shared/\nserver.py\ntools/ resources/ prompts/" fillcolor="#0d1a33" shape=box] mcp_shared [label="shared/\nserver.py\ntools.py · resources.py · prompts.py" fillcolor="#0d1a33" shape=box]
mcp_ops [label="ops/\nserver.py\ntools/ resources/ prompts/" fillcolor="#0d1a33" shape=box] mcp_ops [label="ops/\nserver.py\ntools.py · resources.py · prompts.py" fillcolor="#0d1a33" shape=box]
mcp_pax [label="passenger/\nserver.py\ntools/ resources/ prompts/" fillcolor="#0d1a33" shape=box] mcp_pax [label="passenger/\nserver.py\ntools.py · resources.py · prompts.py" fillcolor="#0d1a33" shape=box]
mcp_data [label="data/\nmodels.py\nreal/ (openmeteo, faa)\nmock/\nscenarios/ (4 scenarios)" fillcolor="#0d1a33" shape=box] mcp_llm [label="shared_llm.py\nGroq · Anthropic\nBedrock · OpenAI" fillcolor="#0d1a33" shape=box]
mcp_data [label="data/\nmodels.py\nreal/ (openmeteo, faa)\nscenarios/ (4 scenarios)" fillcolor="#0d1a33" shape=box]
// Agents subtree // Agents subtree
ag_efhas [label="fce.py\nFCE agent" fillcolor="#1a1a3a" shape=box] ag_fce [label="fce.py\nFCE agent" fillcolor="#1a1a3a" shape=box]
ag_handover [label="handover.py\nHandover agent" fillcolor="#1a1a3a" shape=box] ag_handover [label="handover.py\nHandover agent" fillcolor="#1a1a3a" shape=box]
ag_shared [label="shared/\nmcp_client.py\nllm.py" fillcolor="#1a1a3a" shape=box] ag_shared [label="shared/\nmcp_client.py\nparser.py · tool_runner.py" fillcolor="#1a1a3a" shape=box]
// IRROP subtree
ir_models [label="models/\nflight, passenger\ncrew, recovery" fillcolor="#1a2a1a" shape=box]
ir_rules [label="rules/\nfaa_part117\nrebooking\ncompensation" fillcolor="#1a2a1a" shape=box]
ir_pipeline [label="pipeline/\ningest → triage →\nrebook → compensate" fillcolor="#1a2a1a" shape=box]
// API subtree // API subtree
api_main [label="main.py\nFastAPI + WebSocket" fillcolor="#2a1a1a" shape=box] api_main [label="main.py\nFastAPI + WebSocket\nhealth · runs · Langfuse" fillcolor="#2a1a1a" shape=box]
api_routes [label="routes/\nagents, scenarios, ws" fillcolor="#2a1a1a" shape=box] api_config [label="config.py\nPydantic Settings" fillcolor="#2a1a1a" shape=box]
// UI subtree // UI subtree
ui_fw [label="framework/\nsoleprint-ui\n(shared component lib)" fillcolor="#2a2a0d" shape=box] ui_fw [label="framework/\nsoleprint-ui\n(shared component lib)" fillcolor="#2a2a0d" shape=box]
ui_app [label="app/\nVue 3 SPA\npages/ components/\nmars-tokens.css" fillcolor="#2a2a0d" shape=box] ui_app [label="app/\nVue 3 SPA\nconfig.ts (Kong proxy)" fillcolor="#2a2a0d" shape=box]
// Ctrl subtree // Ctrl subtree
ctrl_docker [label="Dockerfile.api\nDockerfile.ui\nnginx.conf\ndocker-compose.yml" fillcolor="#1a1a2a" shape=box] ctrl_docker [label="Dockerfile.api\nDockerfile.ui\nnginx.conf" fillcolor="#1a1a2a" shape=box]
ctrl_k8s [label="k8s/\nbase/ overlays/dev/\nkind-config.yaml" fillcolor="#1a1a2a" shape=box] ctrl_k8s [label="k8s/\nbase/ overlays/dev/\nkind-config.yaml" fillcolor="#1a1a2a" shape=box]
ctrl_tilt [label="Tiltfile\ntilt_config.json" fillcolor="#1a1a2a" shape=box] ctrl_edge [label="edge/\ndocker-compose.yml\n(production)" fillcolor="#1a1a2a" shape=box]
ctrl_tilt [label="Tiltfile\ndeploy.sh" fillcolor="#1a1a2a" shape=box]
// Edges // Edges
root -> mcp root -> mcp
root -> agents root -> agents
root -> irrop
root -> api root -> api
root -> ui_root root -> ui_root
root -> ctrl root -> ctrl
root -> tests
root -> woodpecker
root -> docs root -> docs
mcp -> mcp_shared mcp -> mcp_shared
mcp -> mcp_ops mcp -> mcp_ops
mcp -> mcp_pax mcp -> mcp_pax
mcp -> mcp_llm
mcp -> mcp_data mcp -> mcp_data
agents -> ag_efhas agents -> ag_fce
agents -> ag_handover agents -> ag_handover
agents -> ag_shared agents -> ag_shared
irrop -> ir_models
irrop -> ir_rules
irrop -> ir_pipeline
api -> api_main api -> api_main
api -> api_routes api -> api_config
ui_root -> ui_fw ui_root -> ui_fw
ui_root -> ui_app ui_root -> ui_app
ctrl -> ctrl_docker ctrl -> ctrl_docker
ctrl -> ctrl_k8s ctrl -> ctrl_k8s
ctrl -> ctrl_edge
ctrl -> ctrl_tilt ctrl -> ctrl_tilt
} }

View File

@@ -4,339 +4,334 @@
<!-- Generated by graphviz version 14.1.2 (0) <!-- Generated by graphviz version 14.1.2 (0)
--> -->
<!-- Title: repo_structure Pages: 1 --> <!-- Title: repo_structure Pages: 1 -->
<svg style="background:#0a0e17" width="4414pt" height="498pt" <svg width="2239pt" height="236pt"
viewBox="0.00 0.00 4414.00 498.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> viewBox="0.00 0.00 2239.00 236.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(2 2) rotate(0) translate(4 245)"> <g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 232.25)">
<title>repo_structure</title> <title>repo_structure</title>
<polygon fill="#0a0e17" stroke="none" points="-4,4 -4,-245 2203,-245 2203,4 -4,4"/> <polygon fill="#0a0e17" stroke="none" points="-4,4 -4,-232.25 2235.25,-232.25 2235.25,4 -4,4"/>
<text xml:space="preserve" text-anchor="middle" x="1099.5" y="-223.7" font-family="Helvetica,sans-Serif" font-size="14.00" fill="#0066ff">Repository Structure</text> <text xml:space="preserve" text-anchor="middle" x="1115.62" y="-210.95" font-family="Helvetica,sans-Serif" font-size="14.00" fill="#0066ff">Repository Structure</text>
<!-- root --> <!-- root -->
<g id="node1" class="node"> <g id="node1" class="node">
<title>root</title> <title>root</title>
<polygon fill="#0066ff" stroke="#1e2a4a" points="1454.75,-215.75 1451.75,-219.75 1430.75,-219.75 1427.75,-215.75 1384,-215.75 1384,-179.75 1454.75,-179.75 1454.75,-215.75"/> <polygon fill="#0066ff" stroke="#1e2a4a" points="1794.75,-203 1791.75,-207 1770.75,-207 1767.75,-203 1724,-203 1724,-167 1794.75,-167 1794.75,-203"/>
<text xml:space="preserve" text-anchor="middle" x="1419.38" y="-194.62" font-family="Helvetica,sans-Serif" font-size="10.00" fill="white">stellar&#45;ops/</text> <text xml:space="preserve" text-anchor="middle" x="1759.38" y="-181.88" font-family="Helvetica,sans-Serif" font-size="10.00" fill="white">stellar&#45;ops/</text>
</g> </g>
<!-- mcp --> <!-- mcp -->
<g id="node2" class="node"> <g id="node2" class="node">
<title>mcp</title> <title>mcp</title>
<polygon fill="#121829" stroke="#1e2a4a" points="434,-143.75 431,-147.75 410,-147.75 407,-143.75 352.75,-143.75 352.75,-107.75 434,-107.75 434,-143.75"/> <polygon fill="#121829" stroke="#1e2a4a" points="636,-131 633,-135 612,-135 609,-131 554.75,-131 554.75,-95 636,-95 636,-131"/>
<text xml:space="preserve" text-anchor="middle" x="393.38" y="-122.62" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">mcp_servers/</text> <text xml:space="preserve" text-anchor="middle" x="595.38" y="-109.88" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">mcp_servers/</text>
</g> </g>
<!-- root&#45;&gt;mcp --> <!-- root&#45;&gt;mcp -->
<g id="edge1" class="edge"> <g id="edge1" class="edge">
<title>root&#45;&gt;mcp</title> <title>root&#45;&gt;mcp</title>
<path fill="none" stroke="#1e2a4a" d="M1383.76,-194.32C1229.15,-183.77 616.05,-141.94 440.54,-129.97"/> <path fill="none" stroke="#1e2a4a" d="M1723.58,-181.85C1554.39,-171.67 835.03,-128.41 642.78,-116.85"/>
<polygon fill="#1e2a4a" stroke="#1e2a4a" points="440.85,-128.24 435.75,-129.64 440.62,-131.73 440.85,-128.24"/> <polygon fill="#1e2a4a" stroke="#1e2a4a" points="643.04,-115.11 637.94,-116.56 642.83,-118.61 643.04,-115.11"/>
</g> </g>
<!-- agents --> <!-- agents -->
<g id="node3" class="node"> <g id="node3" class="node">
<title>agents</title> <title>agents</title>
<polygon fill="#121829" stroke="#1e2a4a" points="843.38,-143.75 840.38,-147.75 819.38,-147.75 816.38,-143.75 789.38,-143.75 789.38,-107.75 843.38,-107.75 843.38,-143.75"/> <polygon fill="#121829" stroke="#1e2a4a" points="1119.38,-131 1116.38,-135 1095.38,-135 1092.38,-131 1065.38,-131 1065.38,-95 1119.38,-95 1119.38,-131"/>
<text xml:space="preserve" text-anchor="middle" x="816.38" y="-122.62" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">agents/</text> <text xml:space="preserve" text-anchor="middle" x="1092.38" y="-109.88" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">agents/</text>
</g> </g>
<!-- root&#45;&gt;agents --> <!-- root&#45;&gt;agents -->
<g id="edge2" class="edge"> <g id="edge2" class="edge">
<title>root&#45;&gt;agents</title> <title>root&#45;&gt;agents</title>
<path fill="none" stroke="#1e2a4a" d="M1383.91,-192.63C1276.34,-180.15 954.77,-142.82 849.87,-130.64"/> <path fill="none" stroke="#1e2a4a" d="M1723.51,-180.24C1606.91,-168 1239.52,-129.44 1126.08,-117.54"/>
<polygon fill="#1e2a4a" stroke="#1e2a4a" points="850.24,-128.92 845.07,-130.08 849.84,-132.4 850.24,-128.92"/> <polygon fill="#1e2a4a" stroke="#1e2a4a" points="1126.26,-115.8 1121.11,-117.02 1125.9,-119.28 1126.26,-115.8"/>
</g>
<!-- irrop -->
<g id="node4" class="node">
<title>irrop</title>
<polygon fill="#121829" stroke="#1e2a4a" points="1177.38,-143.75 1174.38,-147.75 1153.38,-147.75 1150.38,-143.75 1123.38,-143.75 1123.38,-107.75 1177.38,-107.75 1177.38,-143.75"/>
<text xml:space="preserve" text-anchor="middle" x="1150.38" y="-122.62" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">irrop/</text>
</g>
<!-- root&#45;&gt;irrop -->
<g id="edge3" class="edge">
<title>root&#45;&gt;irrop</title>
<path fill="none" stroke="#1e2a4a" d="M1383.65,-187.45C1331.44,-173.87 1234.95,-148.76 1183.97,-135.49"/>
<polygon fill="#1e2a4a" stroke="#1e2a4a" points="1184.43,-133.8 1179.15,-134.24 1183.54,-137.19 1184.43,-133.8"/>
</g> </g>
<!-- api --> <!-- api -->
<g id="node5" class="node"> <g id="node4" class="node">
<title>api</title> <title>api</title>
<polygon fill="#121829" stroke="#1e2a4a" points="1446.38,-143.75 1443.38,-147.75 1422.38,-147.75 1419.38,-143.75 1392.38,-143.75 1392.38,-107.75 1446.38,-107.75 1446.38,-143.75"/> <polygon fill="#121829" stroke="#1e2a4a" points="1473.38,-131 1470.38,-135 1449.38,-135 1446.38,-131 1419.38,-131 1419.38,-95 1473.38,-95 1473.38,-131"/>
<text xml:space="preserve" text-anchor="middle" x="1419.38" y="-122.62" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">api/</text> <text xml:space="preserve" text-anchor="middle" x="1446.38" y="-109.88" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">api/</text>
</g> </g>
<!-- root&#45;&gt;api --> <!-- root&#45;&gt;api -->
<g id="edge4" class="edge"> <g id="edge3" class="edge">
<title>root&#45;&gt;api</title> <title>root&#45;&gt;api</title>
<path fill="none" stroke="#1e2a4a" d="M1419.38,-179.45C1419.38,-170.63 1419.38,-159.78 1419.38,-150.22"/> <path fill="none" stroke="#1e2a4a" d="M1723.77,-176.04C1662.86,-162.42 1539.48,-134.82 1479.97,-121.51"/>
<polygon fill="#1e2a4a" stroke="#1e2a4a" points="1421.13,-150.37 1419.38,-145.37 1417.63,-150.37 1421.13,-150.37"/> <polygon fill="#1e2a4a" stroke="#1e2a4a" points="1480.47,-119.83 1475.2,-120.45 1479.7,-123.25 1480.47,-119.83"/>
</g> </g>
<!-- ui_root --> <!-- ui_root -->
<g id="node6" class="node"> <g id="node5" class="node">
<title>ui_root</title> <title>ui_root</title>
<polygon fill="#121829" stroke="#1e2a4a" points="1658.38,-143.75 1655.38,-147.75 1634.38,-147.75 1631.38,-143.75 1604.38,-143.75 1604.38,-107.75 1658.38,-107.75 1658.38,-143.75"/> <polygon fill="#121829" stroke="#1e2a4a" points="1750.38,-131 1747.38,-135 1726.38,-135 1723.38,-131 1696.38,-131 1696.38,-95 1750.38,-95 1750.38,-131"/>
<text xml:space="preserve" text-anchor="middle" x="1631.38" y="-122.62" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">ui/</text> <text xml:space="preserve" text-anchor="middle" x="1723.38" y="-109.88" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">ui/</text>
</g> </g>
<!-- root&#45;&gt;ui_root --> <!-- root&#45;&gt;ui_root -->
<g id="edge5" class="edge"> <g id="edge4" class="edge">
<title>root&#45;&gt;ui_root</title> <title>root&#45;&gt;ui_root</title>
<path fill="none" stroke="#1e2a4a" d="M1454.86,-185.03C1494.69,-171.88 1558.84,-150.7 1597.85,-137.82"/> <path fill="none" stroke="#1e2a4a" d="M1750.48,-166.7C1745.81,-157.63 1740.04,-146.4 1735.02,-136.65"/>
<polygon fill="#1e2a4a" stroke="#1e2a4a" points="1598.25,-139.53 1602.45,-136.3 1597.15,-136.21 1598.25,-139.53"/> <polygon fill="#1e2a4a" stroke="#1e2a4a" points="1736.71,-136.1 1732.86,-132.45 1733.59,-137.7 1736.71,-136.1"/>
</g> </g>
<!-- ctrl --> <!-- ctrl -->
<g id="node7" class="node"> <g id="node6" class="node">
<title>ctrl</title> <title>ctrl</title>
<polygon fill="#121829" stroke="#1e2a4a" points="1932.38,-143.75 1929.38,-147.75 1908.38,-147.75 1905.38,-143.75 1878.38,-143.75 1878.38,-107.75 1932.38,-107.75 1932.38,-143.75"/> <polygon fill="#121829" stroke="#1e2a4a" points="1846.38,-131 1843.38,-135 1822.38,-135 1819.38,-131 1792.38,-131 1792.38,-95 1846.38,-95 1846.38,-131"/>
<text xml:space="preserve" text-anchor="middle" x="1905.38" y="-122.62" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">ctrl/</text> <text xml:space="preserve" text-anchor="middle" x="1819.38" y="-109.88" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">ctrl/</text>
</g> </g>
<!-- root&#45;&gt;ctrl --> <!-- root&#45;&gt;ctrl -->
<g id="edge6" class="edge"> <g id="edge5" class="edge">
<title>root&#45;&gt;ctrl</title> <title>root&#45;&gt;ctrl</title>
<path fill="none" stroke="#1e2a4a" d="M1455.11,-191.6C1545.68,-178.56 1783.28,-144.34 1871.64,-131.61"/> <path fill="none" stroke="#1e2a4a" d="M1774.21,-166.7C1782.2,-157.37 1792.14,-145.77 1800.66,-135.83"/>
<polygon fill="#1e2a4a" stroke="#1e2a4a" points="1871.79,-133.36 1876.49,-130.91 1871.29,-129.89 1871.79,-133.36"/> <polygon fill="#1e2a4a" stroke="#1e2a4a" points="1801.8,-137.19 1803.73,-132.25 1799.15,-134.91 1801.8,-137.19"/>
</g>
<!-- tests -->
<g id="node7" class="node">
<title>tests</title>
<polygon fill="#121829" stroke="#1e2a4a" points="1918.38,-131 1915.38,-135 1894.38,-135 1891.38,-131 1864.38,-131 1864.38,-95 1918.38,-95 1918.38,-131"/>
<text xml:space="preserve" text-anchor="middle" x="1891.38" y="-109.88" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">tests/</text>
</g>
<!-- root&#45;&gt;tests -->
<g id="edge6" class="edge">
<title>root&#45;&gt;tests</title>
<path fill="none" stroke="#1e2a4a" d="M1792.34,-166.52C1812.59,-155.78 1838.41,-142.09 1858.62,-131.37"/>
<polygon fill="#1e2a4a" stroke="#1e2a4a" points="1859.22,-133.03 1862.82,-129.14 1857.58,-129.94 1859.22,-133.03"/>
</g>
<!-- woodpecker -->
<g id="node8" class="node">
<title>woodpecker</title>
<polygon fill="#121829" stroke="#1e2a4a" points="2016.62,-131 2013.62,-135 1992.62,-135 1989.62,-131 1936.12,-131 1936.12,-95 2016.62,-95 2016.62,-131"/>
<text xml:space="preserve" text-anchor="middle" x="1976.38" y="-109.88" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">.woodpecker/</text>
</g>
<!-- root&#45;&gt;woodpecker -->
<g id="edge7" class="edge">
<title>root&#45;&gt;woodpecker</title>
<path fill="none" stroke="#1e2a4a" d="M1795.19,-173.02C1829.27,-162.5 1881.94,-146.05 1927.38,-131 1928.14,-130.75 1928.92,-130.49 1929.7,-130.23"/>
<polygon fill="#1e2a4a" stroke="#1e2a4a" points="1930.15,-131.92 1934.33,-128.67 1929.03,-128.61 1930.15,-131.92"/>
</g> </g>
<!-- docs --> <!-- docs -->
<g id="node8" class="node"> <g id="node9" class="node">
<title>docs</title> <title>docs</title>
<polygon fill="#121829" stroke="#1e2a4a" points="2004.38,-143.75 2001.38,-147.75 1980.38,-147.75 1977.38,-143.75 1950.38,-143.75 1950.38,-107.75 2004.38,-107.75 2004.38,-143.75"/> <polygon fill="#121829" stroke="#1e2a4a" points="2088.38,-131 2085.38,-135 2064.38,-135 2061.38,-131 2034.38,-131 2034.38,-95 2088.38,-95 2088.38,-131"/>
<text xml:space="preserve" text-anchor="middle" x="1977.38" y="-122.62" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">docs/</text> <text xml:space="preserve" text-anchor="middle" x="2061.38" y="-109.88" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">docs/</text>
</g> </g>
<!-- root&#45;&gt;docs --> <!-- root&#45;&gt;docs -->
<g id="edge7" class="edge"> <g id="edge8" class="edge">
<title>root&#45;&gt;docs</title> <title>root&#45;&gt;docs</title>
<path fill="none" stroke="#1e2a4a" d="M1454.96,-197.25C1540.48,-197.56 1763.66,-193.16 1941.38,-143.75 1942.29,-143.5 1943.21,-143.22 1944.14,-142.92"/> <path fill="none" stroke="#1e2a4a" d="M1794.94,-179.73C1846.63,-172.9 1945.11,-157.5 2025.38,-131 2026.28,-130.7 2027.19,-130.39 2028.1,-130.06"/>
<polygon fill="#1e2a4a" stroke="#1e2a4a" points="1944.69,-144.59 1948.8,-141.25 1943.51,-141.29 1944.69,-144.59"/> <polygon fill="#1e2a4a" stroke="#1e2a4a" points="2028.71,-131.7 2032.73,-128.25 2027.44,-128.44 2028.71,-131.7"/>
</g> </g>
<!-- mcp_shared --> <!-- mcp_shared -->
<g id="node9" class="node"> <g id="node10" class="node">
<title>mcp_shared</title> <title>mcp_shared</title>
<polygon fill="#0d1a33" stroke="#1e2a4a" points="142.75,-59 0,-59 0,-12.75 142.75,-12.75 142.75,-59"/> <polygon fill="#0d1a33" stroke="#1e2a4a" points="190.75,-52.62 0,-52.62 0,-6.38 190.75,-6.38 190.75,-52.62"/>
<text xml:space="preserve" text-anchor="middle" x="71.38" y="-45.5" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">shared/</text> <text xml:space="preserve" text-anchor="middle" x="95.38" y="-39.12" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">shared/</text>
<text xml:space="preserve" text-anchor="middle" x="71.38" y="-32.75" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">server.py</text> <text xml:space="preserve" text-anchor="middle" x="95.38" y="-26.38" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">server.py</text>
<text xml:space="preserve" text-anchor="middle" x="71.38" y="-20" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">tools/ resources/ prompts/</text> <text xml:space="preserve" text-anchor="middle" x="95.38" y="-13.62" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">tools.py · resources.py · prompts.py</text>
</g> </g>
<!-- mcp&#45;&gt;mcp_shared --> <!-- mcp&#45;&gt;mcp_shared -->
<g id="edge8" class="edge"> <g id="edge9" class="edge">
<title>mcp&#45;&gt;mcp_shared</title> <title>mcp&#45;&gt;mcp_shared</title>
<path fill="none" stroke="#1e2a4a" d="M352.67,-118.24C304.25,-109.93 221.29,-93.94 152.38,-71.75 143.53,-68.9 134.32,-65.44 125.43,-61.82"/> <path fill="none" stroke="#1e2a4a" d="M554.42,-108.3C482.36,-101.39 328.39,-84.84 200.38,-59 193.85,-57.68 187.14,-56.19 180.41,-54.59"/>
<polygon fill="#1e2a4a" stroke="#1e2a4a" points="126.32,-60.3 121.03,-60.01 124.98,-63.53 126.32,-60.3"/> <polygon fill="#1e2a4a" stroke="#1e2a4a" points="180.97,-52.93 175.7,-53.45 180.15,-56.33 180.97,-52.93"/>
</g> </g>
<!-- mcp_ops --> <!-- mcp_ops -->
<g id="node10" class="node"> <g id="node11" class="node">
<title>mcp_ops</title> <title>mcp_ops</title>
<polygon fill="#0d1a33" stroke="#1e2a4a" points="303.75,-59 161,-59 161,-12.75 303.75,-12.75 303.75,-59"/> <polygon fill="#0d1a33" stroke="#1e2a4a" points="399.75,-52.62 209,-52.62 209,-6.38 399.75,-6.38 399.75,-52.62"/>
<text xml:space="preserve" text-anchor="middle" x="232.38" y="-45.5" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">ops/</text> <text xml:space="preserve" text-anchor="middle" x="304.38" y="-39.12" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">ops/</text>
<text xml:space="preserve" text-anchor="middle" x="232.38" y="-32.75" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">server.py</text> <text xml:space="preserve" text-anchor="middle" x="304.38" y="-26.38" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">server.py</text>
<text xml:space="preserve" text-anchor="middle" x="232.38" y="-20" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">tools/ resources/ prompts/</text> <text xml:space="preserve" text-anchor="middle" x="304.38" y="-13.62" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">tools.py · resources.py · prompts.py</text>
</g> </g>
<!-- mcp&#45;&gt;mcp_ops --> <!-- mcp&#45;&gt;mcp_ops -->
<g id="edge9" class="edge"> <g id="edge10" class="edge">
<title>mcp&#45;&gt;mcp_ops</title> <title>mcp&#45;&gt;mcp_ops</title>
<path fill="none" stroke="#1e2a4a" d="M361.57,-107.39C338.04,-94.55 305.62,-76.85 279.15,-62.4"/> <path fill="none" stroke="#1e2a4a" d="M554.3,-100.5C512.14,-88.69 445.24,-69.95 391.27,-54.84"/>
<polygon fill="#1e2a4a" stroke="#1e2a4a" points="280.15,-60.96 274.92,-60.1 278.47,-64.03 280.15,-60.96"/> <polygon fill="#1e2a4a" stroke="#1e2a4a" points="391.83,-53.18 386.55,-53.51 390.89,-56.55 391.83,-53.18"/>
</g> </g>
<!-- mcp_pax --> <!-- mcp_pax -->
<g id="node11" class="node"> <g id="node12" class="node">
<title>mcp_pax</title> <title>mcp_pax</title>
<polygon fill="#0d1a33" stroke="#1e2a4a" points="464.75,-59 322,-59 322,-12.75 464.75,-12.75 464.75,-59"/> <polygon fill="#0d1a33" stroke="#1e2a4a" points="608.75,-52.62 418,-52.62 418,-6.38 608.75,-6.38 608.75,-52.62"/>
<text xml:space="preserve" text-anchor="middle" x="393.38" y="-45.5" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">passenger/</text> <text xml:space="preserve" text-anchor="middle" x="513.38" y="-39.12" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">passenger/</text>
<text xml:space="preserve" text-anchor="middle" x="393.38" y="-32.75" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">server.py</text> <text xml:space="preserve" text-anchor="middle" x="513.38" y="-26.38" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">server.py</text>
<text xml:space="preserve" text-anchor="middle" x="393.38" y="-20" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">tools/ resources/ prompts/</text> <text xml:space="preserve" text-anchor="middle" x="513.38" y="-13.62" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">tools.py · resources.py · prompts.py</text>
</g> </g>
<!-- mcp&#45;&gt;mcp_pax --> <!-- mcp&#45;&gt;mcp_pax -->
<g id="edge10" class="edge"> <g id="edge11" class="edge">
<title>mcp&#45;&gt;mcp_pax</title> <title>mcp&#45;&gt;mcp_pax</title>
<path fill="none" stroke="#1e2a4a" d="M393.38,-107.39C393.38,-95.42 393.38,-79.23 393.38,-65.38"/> <path fill="none" stroke="#1e2a4a" d="M577.98,-94.72C567.1,-83.9 552.84,-69.73 540.49,-57.45"/>
<polygon fill="#1e2a4a" stroke="#1e2a4a" points="395.13,-65.72 393.38,-60.72 391.63,-65.72 395.13,-65.72"/> <polygon fill="#1e2a4a" stroke="#1e2a4a" points="541.89,-56.38 537.11,-54.09 539.43,-58.86 541.89,-56.38"/>
</g>
<!-- mcp_llm -->
<g id="node13" class="node">
<title>mcp_llm</title>
<polygon fill="#0d1a33" stroke="#1e2a4a" points="726.38,-52.62 626.38,-52.62 626.38,-6.38 726.38,-6.38 726.38,-52.62"/>
<text xml:space="preserve" text-anchor="middle" x="676.38" y="-39.12" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">shared_llm.py</text>
<text xml:space="preserve" text-anchor="middle" x="676.38" y="-26.38" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">Groq · Anthropic</text>
<text xml:space="preserve" text-anchor="middle" x="676.38" y="-13.62" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">Bedrock · OpenAI</text>
</g>
<!-- mcp&#45;&gt;mcp_llm -->
<g id="edge12" class="edge">
<title>mcp&#45;&gt;mcp_llm</title>
<path fill="none" stroke="#1e2a4a" d="M612.55,-94.72C623.31,-83.9 637.39,-69.73 649.59,-57.45"/>
<polygon fill="#1e2a4a" stroke="#1e2a4a" points="650.64,-58.88 652.92,-54.1 648.15,-56.41 650.64,-58.88"/>
</g> </g>
<!-- mcp_data --> <!-- mcp_data -->
<g id="node12" class="node"> <g id="node14" class="node">
<title>mcp_data</title> <title>mcp_data</title>
<polygon fill="#0d1a33" stroke="#1e2a4a" points="614.12,-71.75 482.62,-71.75 482.62,0 614.12,0 614.12,-71.75"/> <polygon fill="#0d1a33" stroke="#1e2a4a" points="876.12,-59 744.62,-59 744.62,0 876.12,0 876.12,-59"/>
<text xml:space="preserve" text-anchor="middle" x="548.38" y="-58.25" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">data/</text> <text xml:space="preserve" text-anchor="middle" x="810.38" y="-45.5" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">data/</text>
<text xml:space="preserve" text-anchor="middle" x="548.38" y="-45.5" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">models.py</text> <text xml:space="preserve" text-anchor="middle" x="810.38" y="-32.75" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">models.py</text>
<text xml:space="preserve" text-anchor="middle" x="548.38" y="-32.75" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">real/ (openmeteo, faa)</text> <text xml:space="preserve" text-anchor="middle" x="810.38" y="-20" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">real/ (openmeteo, faa)</text>
<text xml:space="preserve" text-anchor="middle" x="548.38" y="-20" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">mock/</text> <text xml:space="preserve" text-anchor="middle" x="810.38" y="-7.25" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">scenarios/ (4 scenarios)</text>
<text xml:space="preserve" text-anchor="middle" x="548.38" y="-7.25" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">scenarios/ (4 scenarios)</text>
</g> </g>
<!-- mcp&#45;&gt;mcp_data --> <!-- mcp&#45;&gt;mcp_data -->
<g id="edge11" class="edge"> <g id="edge13" class="edge">
<title>mcp&#45;&gt;mcp_data</title> <title>mcp&#45;&gt;mcp_data</title>
<path fill="none" stroke="#1e2a4a" d="M424,-107.39C440.21,-98.2 460.8,-86.52 480.54,-75.33"/> <path fill="none" stroke="#1e2a4a" d="M636.42,-96.48C664.33,-85.92 702.08,-71.63 735.38,-59 736.4,-58.61 737.44,-58.22 738.49,-57.82"/>
<polygon fill="#1e2a4a" stroke="#1e2a4a" points="481.28,-76.93 484.77,-72.94 479.55,-73.88 481.28,-76.93"/> <polygon fill="#1e2a4a" stroke="#1e2a4a" points="739.06,-59.47 743.11,-56.06 737.82,-56.2 739.06,-59.47"/>
</g> </g>
<!-- ag_efhas --> <!-- ag_fce -->
<g id="node13" class="node"> <g id="node15" class="node">
<title>ag_efhas</title> <title>ag_fce</title>
<polygon fill="#1a1a3a" stroke="#1e2a4a" points="698.12,-53.88 632.62,-53.88 632.62,-17.88 698.12,-17.88 698.12,-53.88"/> <polygon fill="#1a1a3a" stroke="#1e2a4a" points="960.12,-47.5 894.62,-47.5 894.62,-11.5 960.12,-11.5 960.12,-47.5"/>
<text xml:space="preserve" text-anchor="middle" x="665.38" y="-39.12" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">fce.py</text> <text xml:space="preserve" text-anchor="middle" x="927.38" y="-32.75" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">fce.py</text>
<text xml:space="preserve" text-anchor="middle" x="665.38" y="-26.38" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">FCE agent</text> <text xml:space="preserve" text-anchor="middle" x="927.38" y="-20" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">FCE agent</text>
</g> </g>
<!-- agents&#45;&gt;ag_efhas --> <!-- agents&#45;&gt;ag_fce -->
<g id="edge12" class="edge"> <g id="edge14" class="edge">
<title>agents&#45;&gt;ag_efhas</title> <title>agents&#45;&gt;ag_fce</title>
<path fill="none" stroke="#1e2a4a" d="M788.98,-113.62C766.33,-103.91 733.72,-88.74 707.38,-71.75 701.15,-67.74 694.82,-62.93 689.02,-58.18"/> <path fill="none" stroke="#1e2a4a" d="M1065.29,-102.04C1039.9,-92.31 1001.18,-76.44 969.38,-59 965.14,-56.68 960.79,-54.04 956.57,-51.33"/>
<polygon fill="#1e2a4a" stroke="#1e2a4a" points="690.4,-57.06 685.44,-55.19 688.16,-59.74 690.4,-57.06"/> <polygon fill="#1e2a4a" stroke="#1e2a4a" points="957.72,-49.99 952.58,-48.71 955.8,-52.92 957.72,-49.99"/>
</g> </g>
<!-- ag_handover --> <!-- ag_handover -->
<g id="node14" class="node"> <g id="node16" class="node">
<title>ag_handover</title> <title>ag_handover</title>
<polygon fill="#1a1a3a" stroke="#1e2a4a" points="810.38,-53.88 716.38,-53.88 716.38,-17.88 810.38,-17.88 810.38,-53.88"/> <polygon fill="#1a1a3a" stroke="#1e2a4a" points="1072.38,-47.5 978.38,-47.5 978.38,-11.5 1072.38,-11.5 1072.38,-47.5"/>
<text xml:space="preserve" text-anchor="middle" x="763.38" y="-39.12" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">handover.py</text> <text xml:space="preserve" text-anchor="middle" x="1025.38" y="-32.75" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">handover.py</text>
<text xml:space="preserve" text-anchor="middle" x="763.38" y="-26.38" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">Handover agent</text> <text xml:space="preserve" text-anchor="middle" x="1025.38" y="-20" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">Handover agent</text>
</g> </g>
<!-- agents&#45;&gt;ag_handover --> <!-- agents&#45;&gt;ag_handover -->
<g id="edge13" class="edge"> <g id="edge15" class="edge">
<title>agents&#45;&gt;ag_handover</title> <title>agents&#45;&gt;ag_handover</title>
<path fill="none" stroke="#1e2a4a" d="M805.9,-107.39C797.63,-93.67 786.01,-74.41 777.01,-59.48"/> <path fill="none" stroke="#1e2a4a" d="M1078.17,-94.72C1068.04,-82.4 1054.35,-65.74 1043.44,-52.48"/>
<polygon fill="#1e2a4a" stroke="#1e2a4a" points="778.69,-58.87 774.61,-55.5 775.69,-60.68 778.69,-58.87"/> <polygon fill="#1e2a4a" stroke="#1e2a4a" points="1045.05,-51.68 1040.52,-48.93 1042.35,-53.9 1045.05,-51.68"/>
</g> </g>
<!-- ag_shared --> <!-- ag_shared -->
<g id="node15" class="node"> <g id="node17" class="node">
<title>ag_shared</title> <title>ag_shared</title>
<polygon fill="#1a1a3a" stroke="#1e2a4a" points="912.5,-59 828.25,-59 828.25,-12.75 912.5,-12.75 912.5,-59"/> <polygon fill="#1a1a3a" stroke="#1e2a4a" points="1230.25,-52.62 1090.5,-52.62 1090.5,-6.38 1230.25,-6.38 1230.25,-52.62"/>
<text xml:space="preserve" text-anchor="middle" x="870.38" y="-45.5" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">shared/</text> <text xml:space="preserve" text-anchor="middle" x="1160.38" y="-39.12" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">shared/</text>
<text xml:space="preserve" text-anchor="middle" x="870.38" y="-32.75" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">mcp_client.py</text> <text xml:space="preserve" text-anchor="middle" x="1160.38" y="-26.38" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">mcp_client.py</text>
<text xml:space="preserve" text-anchor="middle" x="870.38" y="-20" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">llm.py</text> <text xml:space="preserve" text-anchor="middle" x="1160.38" y="-13.62" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">parser.py · tool_runner.py</text>
</g> </g>
<!-- agents&#45;&gt;ag_shared --> <!-- agents&#45;&gt;ag_shared -->
<g id="edge14" class="edge">
<title>agents&#45;&gt;ag_shared</title>
<path fill="none" stroke="#1e2a4a" d="M827.04,-107.39C834.53,-95.2 844.71,-78.64 853.32,-64.64"/>
<polygon fill="#1e2a4a" stroke="#1e2a4a" points="854.73,-65.68 855.86,-60.5 851.75,-63.84 854.73,-65.68"/>
</g>
<!-- ir_models -->
<g id="node16" class="node">
<title>ir_models</title>
<polygon fill="#1a2a1a" stroke="#1e2a4a" points="1027.88,-59 930.88,-59 930.88,-12.75 1027.88,-12.75 1027.88,-59"/>
<text xml:space="preserve" text-anchor="middle" x="979.38" y="-45.5" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">models/</text>
<text xml:space="preserve" text-anchor="middle" x="979.38" y="-32.75" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">flight, passenger</text>
<text xml:space="preserve" text-anchor="middle" x="979.38" y="-20" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">crew, recovery</text>
</g>
<!-- irrop&#45;&gt;ir_models -->
<g id="edge15" class="edge">
<title>irrop&#45;&gt;ir_models</title>
<path fill="none" stroke="#1e2a4a" d="M1123.23,-113.17C1099.86,-102.96 1065.42,-87.32 1036.38,-71.75 1031.08,-68.91 1025.6,-65.8 1020.22,-62.63"/>
<polygon fill="#1e2a4a" stroke="#1e2a4a" points="1021.38,-61.29 1016.19,-60.23 1019.59,-64.29 1021.38,-61.29"/>
</g>
<!-- ir_rules -->
<g id="node17" class="node">
<title>ir_rules</title>
<polygon fill="#1a2a1a" stroke="#1e2a4a" points="1130.88,-65.38 1045.88,-65.38 1045.88,-6.38 1130.88,-6.38 1130.88,-65.38"/>
<text xml:space="preserve" text-anchor="middle" x="1088.38" y="-51.88" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">rules/</text>
<text xml:space="preserve" text-anchor="middle" x="1088.38" y="-39.12" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">faa_part117</text>
<text xml:space="preserve" text-anchor="middle" x="1088.38" y="-26.38" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">rebooking</text>
<text xml:space="preserve" text-anchor="middle" x="1088.38" y="-13.62" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">compensation</text>
</g>
<!-- irrop&#45;&gt;ir_rules -->
<g id="edge16" class="edge"> <g id="edge16" class="edge">
<title>irrop&#45;&gt;ir_rules</title> <title>agents&#45;&gt;ag_shared</title>
<path fill="none" stroke="#1e2a4a" d="M1138.13,-107.39C1130.78,-96.97 1121.18,-83.36 1112.37,-70.88"/> <path fill="none" stroke="#1e2a4a" d="M1106.8,-94.72C1115.74,-83.99 1127.44,-69.98 1137.62,-57.78"/>
<polygon fill="#1e2a4a" stroke="#1e2a4a" points="1113.84,-69.93 1109.53,-66.85 1110.98,-71.95 1113.84,-69.93"/> <polygon fill="#1e2a4a" stroke="#1e2a4a" points="1138.75,-59.15 1140.61,-54.19 1136.06,-56.91 1138.75,-59.15"/>
</g>
<!-- ir_pipeline -->
<g id="node18" class="node">
<title>ir_pipeline</title>
<polygon fill="#1a2a1a" stroke="#1e2a4a" points="1273.38,-59 1149.38,-59 1149.38,-12.75 1273.38,-12.75 1273.38,-59"/>
<text xml:space="preserve" text-anchor="middle" x="1211.38" y="-45.5" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">pipeline/</text>
<text xml:space="preserve" text-anchor="middle" x="1211.38" y="-32.75" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">ingest → triage →</text>
<text xml:space="preserve" text-anchor="middle" x="1211.38" y="-20" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">rebook → compensate</text>
</g>
<!-- irrop&#45;&gt;ir_pipeline -->
<g id="edge17" class="edge">
<title>irrop&#45;&gt;ir_pipeline</title>
<path fill="none" stroke="#1e2a4a" d="M1162.43,-107.39C1170.96,-95.09 1182.58,-78.35 1192.36,-64.27"/>
<polygon fill="#1e2a4a" stroke="#1e2a4a" points="1193.59,-65.56 1195.01,-60.45 1190.72,-63.56 1193.59,-65.56"/>
</g> </g>
<!-- api_main --> <!-- api_main -->
<g id="node19" class="node"> <g id="node18" class="node">
<title>api_main</title> <title>api_main</title>
<polygon fill="#2a1a1a" stroke="#1e2a4a" points="1409.75,-53.88 1291,-53.88 1291,-17.88 1409.75,-17.88 1409.75,-53.88"/> <polygon fill="#2a1a1a" stroke="#1e2a4a" points="1378.38,-52.62 1248.38,-52.62 1248.38,-6.38 1378.38,-6.38 1378.38,-52.62"/>
<text xml:space="preserve" text-anchor="middle" x="1350.38" y="-39.12" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">main.py</text> <text xml:space="preserve" text-anchor="middle" x="1313.38" y="-39.12" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">main.py</text>
<text xml:space="preserve" text-anchor="middle" x="1350.38" y="-26.38" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">FastAPI + WebSocket</text> <text xml:space="preserve" text-anchor="middle" x="1313.38" y="-26.38" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">FastAPI + WebSocket</text>
<text xml:space="preserve" text-anchor="middle" x="1313.38" y="-13.62" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">health · runs · Langfuse</text>
</g> </g>
<!-- api&#45;&gt;api_main --> <!-- api&#45;&gt;api_main -->
<g id="edge18" class="edge"> <g id="edge17" class="edge">
<title>api&#45;&gt;api_main</title> <title>api&#45;&gt;api_main</title>
<path fill="none" stroke="#1e2a4a" d="M1405.74,-107.39C1394.87,-93.55 1379.58,-74.07 1367.82,-59.09"/> <path fill="none" stroke="#1e2a4a" d="M1419.14,-95.31C1400.86,-84.11 1376.42,-69.13 1355.63,-56.4"/>
<polygon fill="#1e2a4a" stroke="#1e2a4a" points="1369.38,-58.24 1364.91,-55.39 1366.63,-60.4 1369.38,-58.24"/> <polygon fill="#1e2a4a" stroke="#1e2a4a" points="1356.62,-54.95 1351.45,-53.83 1354.8,-57.93 1356.62,-54.95"/>
</g> </g>
<!-- api_routes --> <!-- api_config -->
<g id="node20" class="node"> <g id="node19" class="node">
<title>api_routes</title> <title>api_config</title>
<polygon fill="#2a1a1a" stroke="#1e2a4a" points="1548.88,-53.88 1427.88,-53.88 1427.88,-17.88 1548.88,-17.88 1548.88,-53.88"/> <polygon fill="#2a1a1a" stroke="#1e2a4a" points="1496.75,-47.5 1396,-47.5 1396,-11.5 1496.75,-11.5 1496.75,-47.5"/>
<text xml:space="preserve" text-anchor="middle" x="1488.38" y="-39.12" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">routes/</text> <text xml:space="preserve" text-anchor="middle" x="1446.38" y="-32.75" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">config.py</text>
<text xml:space="preserve" text-anchor="middle" x="1488.38" y="-26.38" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">agents, scenarios, ws</text> <text xml:space="preserve" text-anchor="middle" x="1446.38" y="-20" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">Pydantic Settings</text>
</g> </g>
<!-- api&#45;&gt;api_routes --> <!-- api&#45;&gt;api_config -->
<g id="edge19" class="edge"> <g id="edge18" class="edge">
<title>api&#45;&gt;api_routes</title> <title>api&#45;&gt;api_config</title>
<path fill="none" stroke="#1e2a4a" d="M1433.01,-107.39C1443.88,-93.55 1459.17,-74.07 1470.93,-59.09"/> <path fill="none" stroke="#1e2a4a" d="M1446.38,-94.72C1446.38,-82.94 1446.38,-67.19 1446.38,-54.24"/>
<polygon fill="#1e2a4a" stroke="#1e2a4a" points="1472.12,-60.4 1473.84,-55.39 1469.37,-58.24 1472.12,-60.4"/> <polygon fill="#1e2a4a" stroke="#1e2a4a" points="1448.13,-54.27 1446.38,-49.27 1444.63,-54.27 1448.13,-54.27"/>
</g> </g>
<!-- ui_fw --> <!-- ui_fw -->
<g id="node21" class="node"> <g id="node20" class="node">
<title>ui_fw</title> <title>ui_fw</title>
<polygon fill="#2a2a0d" stroke="#1e2a4a" points="1696,-59 1566.75,-59 1566.75,-12.75 1696,-12.75 1696,-59"/> <polygon fill="#2a2a0d" stroke="#1e2a4a" points="1644,-52.62 1514.75,-52.62 1514.75,-6.38 1644,-6.38 1644,-52.62"/>
<text xml:space="preserve" text-anchor="middle" x="1631.38" y="-45.5" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">framework/</text> <text xml:space="preserve" text-anchor="middle" x="1579.38" y="-39.12" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">framework/</text>
<text xml:space="preserve" text-anchor="middle" x="1631.38" y="-32.75" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">soleprint&#45;ui</text> <text xml:space="preserve" text-anchor="middle" x="1579.38" y="-26.38" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">soleprint&#45;ui</text>
<text xml:space="preserve" text-anchor="middle" x="1631.38" y="-20" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">(shared component lib)</text> <text xml:space="preserve" text-anchor="middle" x="1579.38" y="-13.62" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">(shared component lib)</text>
</g> </g>
<!-- ui_root&#45;&gt;ui_fw --> <!-- ui_root&#45;&gt;ui_fw -->
<g id="edge20" class="edge"> <g id="edge19" class="edge">
<title>ui_root&#45;&gt;ui_fw</title> <title>ui_root&#45;&gt;ui_fw</title>
<path fill="none" stroke="#1e2a4a" d="M1631.38,-107.39C1631.38,-95.42 1631.38,-79.23 1631.38,-65.38"/> <path fill="none" stroke="#1e2a4a" d="M1695.95,-96.48C1675.86,-85.11 1648.12,-69.41 1624.75,-56.18"/>
<polygon fill="#1e2a4a" stroke="#1e2a4a" points="1633.13,-65.72 1631.38,-60.72 1629.63,-65.72 1633.13,-65.72"/> <polygon fill="#1e2a4a" stroke="#1e2a4a" points="1625.75,-54.73 1620.53,-53.79 1624.02,-57.78 1625.75,-54.73"/>
</g> </g>
<!-- ui_app --> <!-- ui_app -->
<g id="node22" class="node"> <g id="node21" class="node">
<title>ui_app</title> <title>ui_app</title>
<polygon fill="#2a2a0d" stroke="#1e2a4a" points="1828.5,-65.38 1714.25,-65.38 1714.25,-6.38 1828.5,-6.38 1828.5,-65.38"/> <polygon fill="#2a2a0d" stroke="#1e2a4a" points="1784.62,-52.62 1662.12,-52.62 1662.12,-6.38 1784.62,-6.38 1784.62,-52.62"/>
<text xml:space="preserve" text-anchor="middle" x="1771.38" y="-51.88" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">app/</text> <text xml:space="preserve" text-anchor="middle" x="1723.38" y="-39.12" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">app/</text>
<text xml:space="preserve" text-anchor="middle" x="1771.38" y="-39.12" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">Vue 3 SPA</text> <text xml:space="preserve" text-anchor="middle" x="1723.38" y="-26.38" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">Vue 3 SPA</text>
<text xml:space="preserve" text-anchor="middle" x="1771.38" y="-26.38" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">pages/ components/</text> <text xml:space="preserve" text-anchor="middle" x="1723.38" y="-13.62" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">config.ts (Kong proxy)</text>
<text xml:space="preserve" text-anchor="middle" x="1771.38" y="-13.62" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">mars&#45;tokens.css</text>
</g> </g>
<!-- ui_root&#45;&gt;ui_app --> <!-- ui_root&#45;&gt;ui_app -->
<g id="edge21" class="edge"> <g id="edge20" class="edge">
<title>ui_root&#45;&gt;ui_app</title> <title>ui_root&#45;&gt;ui_app</title>
<path fill="none" stroke="#1e2a4a" d="M1658.7,-107.6C1676.09,-96.69 1699.15,-82.21 1719.88,-69.2"/> <path fill="none" stroke="#1e2a4a" d="M1723.38,-94.72C1723.38,-84.47 1723.38,-71.21 1723.38,-59.41"/>
<polygon fill="#1e2a4a" stroke="#1e2a4a" points="1720.79,-70.69 1724.1,-66.55 1718.93,-67.73 1720.79,-70.69"/> <polygon fill="#1e2a4a" stroke="#1e2a4a" points="1725.13,-59.54 1723.38,-54.54 1721.63,-59.54 1725.13,-59.54"/>
</g> </g>
<!-- ctrl_docker --> <!-- ctrl_docker -->
<g id="node23" class="node"> <g id="node22" class="node">
<title>ctrl_docker</title> <title>ctrl_docker</title>
<polygon fill="#1a1a2a" stroke="#1e2a4a" points="1964.38,-65.38 1846.38,-65.38 1846.38,-6.38 1964.38,-6.38 1964.38,-65.38"/> <polygon fill="#1a1a2a" stroke="#1e2a4a" points="1885.75,-52.62 1803,-52.62 1803,-6.38 1885.75,-6.38 1885.75,-52.62"/>
<text xml:space="preserve" text-anchor="middle" x="1905.38" y="-51.88" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">Dockerfile.api</text> <text xml:space="preserve" text-anchor="middle" x="1844.38" y="-39.12" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">Dockerfile.api</text>
<text xml:space="preserve" text-anchor="middle" x="1905.38" y="-39.12" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">Dockerfile.ui</text> <text xml:space="preserve" text-anchor="middle" x="1844.38" y="-26.38" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">Dockerfile.ui</text>
<text xml:space="preserve" text-anchor="middle" x="1905.38" y="-26.38" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">nginx.conf</text> <text xml:space="preserve" text-anchor="middle" x="1844.38" y="-13.62" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">nginx.conf</text>
<text xml:space="preserve" text-anchor="middle" x="1905.38" y="-13.62" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">docker&#45;compose.yml</text>
</g> </g>
<!-- ctrl&#45;&gt;ctrl_docker --> <!-- ctrl&#45;&gt;ctrl_docker -->
<g id="edge22" class="edge"> <g id="edge21" class="edge">
<title>ctrl&#45;&gt;ctrl_docker</title> <title>ctrl&#45;&gt;ctrl_docker</title>
<path fill="none" stroke="#1e2a4a" d="M1905.38,-107.39C1905.38,-97.25 1905.38,-84.09 1905.38,-71.89"/> <path fill="none" stroke="#1e2a4a" d="M1824.68,-94.72C1827.85,-84.37 1831.96,-70.96 1835.61,-59.08"/>
<polygon fill="#1e2a4a" stroke="#1e2a4a" points="1907.13,-72.13 1905.38,-67.13 1903.63,-72.13 1907.13,-72.13"/> <polygon fill="#1e2a4a" stroke="#1e2a4a" points="1837.23,-59.77 1837.02,-54.47 1833.88,-58.74 1837.23,-59.77"/>
</g> </g>
<!-- ctrl_k8s --> <!-- ctrl_k8s -->
<g id="node24" class="node"> <g id="node23" class="node">
<title>ctrl_k8s</title> <title>ctrl_k8s</title>
<polygon fill="#1a1a2a" stroke="#1e2a4a" points="2094,-59 1982.75,-59 1982.75,-12.75 2094,-12.75 2094,-59"/> <polygon fill="#1a1a2a" stroke="#1e2a4a" points="2015,-52.62 1903.75,-52.62 1903.75,-6.38 2015,-6.38 2015,-52.62"/>
<text xml:space="preserve" text-anchor="middle" x="2038.38" y="-45.5" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">k8s/</text> <text xml:space="preserve" text-anchor="middle" x="1959.38" y="-39.12" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">k8s/</text>
<text xml:space="preserve" text-anchor="middle" x="2038.38" y="-32.75" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">base/ overlays/dev/</text> <text xml:space="preserve" text-anchor="middle" x="1959.38" y="-26.38" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">base/ overlays/dev/</text>
<text xml:space="preserve" text-anchor="middle" x="2038.38" y="-20" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">kind&#45;config.yaml</text> <text xml:space="preserve" text-anchor="middle" x="1959.38" y="-13.62" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">kind&#45;config.yaml</text>
</g> </g>
<!-- ctrl&#45;&gt;ctrl_k8s --> <!-- ctrl&#45;&gt;ctrl_k8s -->
<g id="edge23" class="edge"> <g id="edge22" class="edge">
<title>ctrl&#45;&gt;ctrl_k8s</title> <title>ctrl&#45;&gt;ctrl_k8s</title>
<path fill="none" stroke="#1e2a4a" d="M1931.65,-107.39C1950.91,-94.66 1977.39,-77.17 1999.15,-62.79"/> <path fill="none" stroke="#1e2a4a" d="M1846.7,-96.09C1866.14,-84.78 1892.68,-69.33 1915.12,-56.26"/>
<polygon fill="#1e2a4a" stroke="#1e2a4a" points="2000.1,-64.26 2003.31,-60.04 1998.17,-61.34 2000.1,-64.26"/> <polygon fill="#1e2a4a" stroke="#1e2a4a" points="1915.98,-57.79 1919.42,-53.76 1914.22,-54.76 1915.98,-57.79"/>
</g>
<!-- ctrl_edge -->
<g id="node24" class="node">
<title>ctrl_edge</title>
<polygon fill="#1a1a2a" stroke="#1e2a4a" points="2151.38,-52.62 2033.38,-52.62 2033.38,-6.38 2151.38,-6.38 2151.38,-52.62"/>
<text xml:space="preserve" text-anchor="middle" x="2092.38" y="-39.12" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">edge/</text>
<text xml:space="preserve" text-anchor="middle" x="2092.38" y="-26.38" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">docker&#45;compose.yml</text>
<text xml:space="preserve" text-anchor="middle" x="2092.38" y="-13.62" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">(production)</text>
</g>
<!-- ctrl&#45;&gt;ctrl_edge -->
<g id="edge23" class="edge">
<title>ctrl&#45;&gt;ctrl_edge</title>
<path fill="none" stroke="#1e2a4a" d="M1846.62,-98.33C1849.54,-97.11 1852.49,-95.97 1855.38,-95 1928.14,-70.44 1951.09,-81.94 2024.38,-59 2028.06,-57.85 2031.83,-56.57 2035.6,-55.21"/>
<polygon fill="#1e2a4a" stroke="#1e2a4a" points="2036.02,-56.92 2040.1,-53.54 2034.8,-53.64 2036.02,-56.92"/>
</g> </g>
<!-- ctrl_tilt --> <!-- ctrl_tilt -->
<g id="node25" class="node"> <g id="node25" class="node">
<title>ctrl_tilt</title> <title>ctrl_tilt</title>
<polygon fill="#1a1a2a" stroke="#1e2a4a" points="2199,-53.88 2111.75,-53.88 2111.75,-17.88 2199,-17.88 2199,-53.88"/> <polygon fill="#1a1a2a" stroke="#1e2a4a" points="2231.25,-47.5 2169.5,-47.5 2169.5,-11.5 2231.25,-11.5 2231.25,-47.5"/>
<text xml:space="preserve" text-anchor="middle" x="2155.38" y="-39.12" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">Tiltfile</text> <text xml:space="preserve" text-anchor="middle" x="2200.38" y="-32.75" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">Tiltfile</text>
<text xml:space="preserve" text-anchor="middle" x="2155.38" y="-26.38" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">tilt_config.json</text> <text xml:space="preserve" text-anchor="middle" x="2200.38" y="-20" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e8eaf0">deploy.sh</text>
</g> </g>
<!-- ctrl&#45;&gt;ctrl_tilt --> <!-- ctrl&#45;&gt;ctrl_tilt -->
<g id="edge24" class="edge"> <g id="edge24" class="edge">
<title>ctrl&#45;&gt;ctrl_tilt</title> <title>ctrl&#45;&gt;ctrl_tilt</title>
<path fill="none" stroke="#1e2a4a" d="M1932.63,-111.11C1935.55,-109.88 1938.5,-108.74 1941.38,-107.75 2011.15,-83.86 2035.58,-100.79 2103.38,-71.75 2111.9,-68.1 2120.52,-63.01 2128.22,-57.84"/> <path fill="none" stroke="#1e2a4a" d="M1846.54,-98.06C1849.47,-96.89 1852.45,-95.84 1855.38,-95 1986.58,-57.37 2033.03,-108.14 2160.38,-59 2165.41,-57.06 2170.39,-54.34 2175.05,-51.34"/>
<polygon fill="#1e2a4a" stroke="#1e2a4a" points="2129.03,-59.41 2132.15,-55.13 2127.04,-56.53 2129.03,-59.41"/> <polygon fill="#1e2a4a" stroke="#1e2a4a" points="2175.96,-52.84 2179.1,-48.58 2173.98,-49.95 2175.96,-52.84"/>
</g> </g>
</g> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -193,7 +193,7 @@
<section id="system" class="graph-section active"> <section id="system" class="graph-section active">
<h2>SYSTEM ARCHITECTURE</h2> <h2>SYSTEM ARCHITECTURE</h2>
<p>End-to-end view: Vue UI → Kong gateway → FastAPI → MCP servers → live and scenario data sources. Langfuse traces every agent run.</p> <p>End-to-end view: Vue UI → Kong gateway (optional) → FastAPI → MCP servers → live and scenario data sources. Langfuse (separate shared cluster) traces every agent run and tool call.</p>
<div class="graph-container"> <div class="graph-container">
<a href="viewer.html?src=graphs/system_architecture.svg"><img src="graphs/system_architecture.svg" alt="System Architecture"></a> <a href="viewer.html?src=graphs/system_architecture.svg"><img src="graphs/system_architecture.svg" alt="System Architecture"></a>
</div> </div>
@@ -252,7 +252,7 @@
<section id="deploy" class="graph-section"> <section id="deploy" class="graph-section">
<h2>DEPLOYMENT</h2> <h2>DEPLOYMENT</h2>
<p>Kind cluster for dev (Tilt), docker-compose for quick start, EC2 for production demo. Entry point: localhost:8040.</p> <p>Kind cluster for dev (Tilt), docker-compose for EC2 production (nova-api + nova-ui on shared gateway network). Woodpecker CI builds images on push to main. EC2 nginx proxies stellarair.mcrn.ar → container; Kong Konnect available as optional governance layer.</p>
<div class="graph-container"> <div class="graph-container">
<a href="viewer.html?src=graphs/deployment.svg"><img src="graphs/deployment.svg" alt="Deployment"></a> <a href="viewer.html?src=graphs/deployment.svg"><img src="graphs/deployment.svg" alt="Deployment"></a>
</div> </div>
@@ -264,38 +264,45 @@
<div class="tree-container"> <div class="tree-container">
<pre class="repo-tree"><span class="t-root">stellar-ops/</span> <pre class="repo-tree"><span class="t-root">stellar-ops/</span>
├── <span class="t-dir">mcp_servers/</span> ├── <span class="t-dir">mcp_servers/</span>
│ ├── <span class="t-mcp">shared/</span> <span class="t-comment">server.py tools/ resources/ prompts/</span> │ ├── <span class="t-mcp">shared/</span> <span class="t-comment">server.py · tools.py · resources.py · prompts.py</span>
│ │ └── tools: <span class="t-live">get_route_weather</span> · <span class="t-live">get_hub_forecasts</span> · <span class="t-live">get_airport_status</span> │ │ └── tools: <span class="t-live">get_route_weather</span> · <span class="t-live">get_hub_forecasts</span> · <span class="t-live">get_airport_status</span>
│ │ get_flight_status · get_flight_details · get_irregular_ops │ │ get_flight_status · get_flight_details · get_irregular_ops
│ │ get_airport_congestion · get_maintenance_flags │ │ get_airport_congestion · get_maintenance_flags
│ ├── <span class="t-ops">ops/</span> <span class="t-comment">server.py tools/ resources/ prompts/</span> │ ├── <span class="t-ops">ops/</span> <span class="t-comment">server.py · tools.py · resources.py · prompts.py</span>
│ │ └── tools: get_crew_notes · get_crew_duty_status · get_pending_rebookings │ │ └── tools: get_crew_notes · get_crew_duty_status · get_pending_rebookings
│ │ generate_narrative │ │ generate_narrative
│ ├── <span class="t-pax">passenger/</span> <span class="t-comment">server.py tools/ resources/ prompts/</span> │ ├── <span class="t-pax">passenger/</span> <span class="t-comment">server.py · tools.py · resources.py · prompts.py</span>
│ │ └── tools: generate_notification │ │ └── tools: generate_notification
│ ├── shared_llm.py <span class="t-comment">multi-provider: Groq · Anthropic · Bedrock · OpenAI</span>
│ └── <span class="t-dir">data/</span> │ └── <span class="t-dir">data/</span>
│ ├── models.py <span class="t-comment">FlightData, CrewMember, Passenger, MELItem, HubInfo</span> │ ├── models.py <span class="t-comment">FlightData · CrewMember · Passenger · MELItem · HubInfo</span>
│ ├── real/ <span class="t-live">openmeteo.py · faa.py</span> │ ├── real/ <span class="t-live">openmeteo.py · faa.py</span>
│ └── scenarios/ <span class="t-comment">normal_ops · weather_disruption_ord</span> │ └── scenarios/ <span class="t-comment">normal_ops · weather_disruption_ord</span>
<span class="t-comment">maintenance_delay_sfo · crew_swap_ewr</span> <span class="t-comment">maintenance_delay_sfo · crew_swap_ewr</span>
├── <span class="t-dir">agents/</span> ├── <span class="t-dir">agents/</span>
│ ├── fce.py <span class="t-comment">FCE — "Behind Every Departure"</span> │ ├── fce.py <span class="t-comment">FCE — "Behind Every Departure" (passenger notifications)</span>
│ ├── handover.py <span class="t-comment">Shift Handover agent</span> │ ├── handover.py <span class="t-comment">Shift Handover (ops brief: IMMEDIATE / MONITOR / FYI)</span>
│ └── shared/ <span class="t-comment">mcp_client.py · llm.py (Bedrock/Anthropic)</span> │ └── shared/
├── <span class="t-dir">irrop/</span> <span class="t-comment">Disruption Recovery Engine (Project 3)</span> │ ├── mcp_client.py <span class="t-comment">MCPMultiClient + connect_servers context manager</span>
├── models/ <span class="t-comment">flight · passenger · crew · recovery</span> ├── parser.py <span class="t-comment">parse_tool_result · parse_resource_result · parse_prompt_result</span>
├── rules/ <span class="t-comment">faa_part117 · rebooking · compensation</span> └── tool_runner.py <span class="t-comment">build_tool_caller — timeout · Langfuse span · error collection</span>
│ └── pipeline/ <span class="t-comment">ingest → triage → rebook → compensate</span>
├── <span class="t-dir">api/</span> ├── <span class="t-dir">api/</span>
── main.py <span class="t-comment">FastAPI + WebSocket + scenario data API</span> ── main.py <span class="t-comment">FastAPI: agents, scenarios, WebSocket, /health, Langfuse traces</span>
│ └── config.py <span class="t-comment">Pydantic Settings — centralized env var reads</span>
├── <span class="t-dir">ui/</span> ├── <span class="t-dir">ui/</span>
│ ├── framework/ <span class="t-comment">soleprint-ui (shared component library)</span> │ ├── framework/ <span class="t-comment">soleprint-ui (shared component library)</span>
│ └── app/ <span class="t-comment">Vue 3 SPA — Operations · Internals · Data</span> │ └── app/ <span class="t-comment">Vue 3 SPA — Operations · Internals · Data · Settings</span>
│ └── src/config.ts <span class="t-comment">Kong proxy URL + API/WS base</span>
├── <span class="t-dir">ctrl/</span> ├── <span class="t-dir">ctrl/</span>
│ ├── Dockerfile.api/ui <span class="t-comment">Container builds</span> │ ├── Dockerfile.api/ui <span class="t-comment">Container builds</span>
│ ├── nginx.conf <span class="t-comment">UI nginx (proxies /agents /scenarios /config /health /ws)</span>
│ ├── k8s/ <span class="t-comment">base/ + overlays/dev/ (Kustomize)</span> │ ├── k8s/ <span class="t-comment">base/ + overlays/dev/ (Kustomize)</span>
│ ├── Tiltfile <span class="t-comment">Dev environment (Kind cluster: unt)</span> │ ├── Tiltfile <span class="t-comment">Dev environment (Kind cluster: unt)</span>
── docker-compose.yml <span class="t-comment">Simple alternative</span> ── edge/ <span class="t-comment">Production docker-compose (nova-api + nova-ui on gateway net)</span>
│ └── deploy.sh <span class="t-comment">rsync (bypass CI) · edge (pull registry images)</span>
├── <span class="t-dir">tests/</span> <span class="t-comment">69 tests: models · clients · MCP · scenarios · agents</span>
│ └── base.py <span class="t-comment">dual-mode: inprocess (default) · live (CONTRACT_TEST_MODE=live)</span>
├── <span class="t-dir">.woodpecker/</span> <span class="t-comment">CI pipeline — build API + UI, push to registry.mcrn.ar</span>
├── <span class="t-dir">docs/</span> <span class="t-comment">Architecture graphs (this page)</span> ├── <span class="t-dir">docs/</span> <span class="t-comment">Architecture graphs (this page)</span>
└── .mcp.json <span class="t-comment">Claude Code integration — 3 servers</span></pre> └── .mcp.json <span class="t-comment">Claude Code integration — 3 servers</span></pre>
</div> </div>