Skip to content

Commit 5aae018

Browse files
committed
chore(demo): add local cockpit demo setup without Docker
1 parent 57dc3c9 commit 5aae018

4 files changed

Lines changed: 350 additions & 1 deletion

File tree

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ ml/datasets/labeled/*.jsonl
4848
ml/models-src/*.labels.json
4949
ml/models-src/*.metrics.json
5050

51+
# Local demo runtime outputs
52+
runtime/demo-local/
53+
5154
# Private corpora / local document sets (never commit)
5255
PDF Permis/
5356
**/PDF Permis/
@@ -72,4 +75,4 @@ PDF Permis/
7275
!docs/screenshots/
7376
!docs/screenshots/*.png
7477

75-
.fake
78+
.fake
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"schema_version": "1.0",
3+
"workspace_path": "../../..",
4+
"runtime_directory": "../../runtime/demo-local/cockpit",
5+
"requests_directory": "requests",
6+
"results_directory": "results",
7+
"history_file": "history.jsonl",
8+
"tool_timeout_seconds": 120
9+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Le conseil municipal adopte une resolution concernant les travaux de voirie et la planification des investissements 2026. Le document mentionne les routes locales, les trottoirs, l'aqueduc et l'egout, puis precise le calendrier des chantiers prioritaires.

scripts/demo_local_cockpit.sh

Lines changed: 336 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,336 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5+
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
6+
SUITE_ROOT="$(cd "$REPO_ROOT/.." && pwd)"
7+
8+
DEMO_HOST="${ORCHIVISTE_DEMO_HOST:-127.0.0.1}"
9+
DEMO_PORT="${ORCHIVISTE_DEMO_PORT:-28780}"
10+
BASE_URL="http://${DEMO_HOST}:${DEMO_PORT}"
11+
12+
RUNTIME_ROOT="${ORCHIVISTE_DEMO_RUNTIME_DIR:-$REPO_ROOT/runtime/demo-local}"
13+
LOG_DIR="$RUNTIME_ROOT/logs"
14+
REQUEST_DIR="$RUNTIME_ROOT/requests"
15+
RESULT_DIR="$RUNTIME_ROOT/results"
16+
PID_FILE="$RUNTIME_ROOT/orchiviste-api.pid"
17+
API_LOG_FILE="$LOG_DIR/orchiviste-api.log"
18+
SQLITE_PATH="${ORCHIVISTE_DEMO_SQLITE_PATH:-$RUNTIME_ROOT/orchiviste-demo.sqlite}"
19+
COCKPIT_CONFIG_FILE="${ORCHIVISTE_DEMO_CONFIG_FILE:-$REPO_ROOT/OrchivisteAPI/configs/cockpit/demo.local.json}"
20+
FIXTURE_FILE="${ORCHIVISTE_DEMO_FIXTURE:-$REPO_ROOT/fixtures/demo/cockpit/input_document.txt}"
21+
22+
MUNI_ANALYSE_DIR="${MUNI_ANALYSE_DIR:-$SUITE_ROOT/MuniAnalyse}"
23+
MUNI_METADONNEES_DIR="${MUNI_METADONNEES_DIR:-$SUITE_ROOT/MuniMetadonnees}"
24+
MUNI_PRECLASSEMENT_DIR="${MUNI_PRECLASSEMENT_DIR:-$SUITE_ROOT/MuniPreclassement}"
25+
MUNI_CONTROLE_DIR="${MUNI_CONTROLE_DIR:-$SUITE_ROOT/MuniControle}"
26+
27+
need_cmd() {
28+
local cmd="$1"
29+
if ! command -v "$cmd" >/dev/null 2>&1; then
30+
echo "ECHEC: commande requise manquante: $cmd" >&2
31+
exit 1
32+
fi
33+
}
34+
35+
need_file() {
36+
local path="$1"
37+
local label="$2"
38+
if [[ ! -f "$path" ]]; then
39+
echo "ECHEC: $label introuvable: $path" >&2
40+
exit 1
41+
fi
42+
}
43+
44+
need_dir() {
45+
local path="$1"
46+
local label="$2"
47+
if [[ ! -d "$path" ]]; then
48+
echo "ECHEC: $label introuvable: $path" >&2
49+
exit 1
50+
fi
51+
}
52+
53+
require_prereqs() {
54+
need_cmd swift
55+
need_cmd curl
56+
need_cmd python3
57+
need_file "$COCKPIT_CONFIG_FILE" "configuration cockpit demo"
58+
need_file "$FIXTURE_FILE" "fixture demo"
59+
need_dir "$MUNI_ANALYSE_DIR" "repo MuniAnalyse"
60+
need_dir "$MUNI_METADONNEES_DIR" "repo MuniMetadonnees"
61+
need_dir "$MUNI_PRECLASSEMENT_DIR" "repo MuniPreclassement"
62+
need_dir "$MUNI_CONTROLE_DIR" "repo MuniControle"
63+
}
64+
65+
is_running() {
66+
if [[ ! -f "$PID_FILE" ]]; then
67+
return 1
68+
fi
69+
local pid
70+
pid="$(cat "$PID_FILE" 2>/dev/null || true)"
71+
[[ -n "$pid" ]] || return 1
72+
kill -0 "$pid" >/dev/null 2>&1
73+
}
74+
75+
wait_health() {
76+
local attempts=60
77+
while (( attempts > 0 )); do
78+
if curl -sS "$BASE_URL/v1/health" >/dev/null 2>&1; then
79+
return 0
80+
fi
81+
sleep 1
82+
attempts=$((attempts - 1))
83+
done
84+
return 1
85+
}
86+
87+
ensure_api_reachable() {
88+
if ! curl -sS "$BASE_URL/v1/health" >/dev/null 2>&1; then
89+
echo "ECHEC: API indisponible sur $BASE_URL. Lance d'abord: scripts/demo_local_cockpit.sh start" >&2
90+
exit 1
91+
fi
92+
}
93+
94+
build_demo_binaries() {
95+
echo "== Build OrchivisteAPI =="
96+
(cd "$REPO_ROOT/OrchivisteAPI" && swift build -c debug --product OrchivisteAPI)
97+
98+
echo "== Build outils actifs (Muni) =="
99+
(cd "$MUNI_ANALYSE_DIR" && swift build -c debug --product muni-analyse-cli)
100+
(cd "$MUNI_METADONNEES_DIR" && swift build -c debug --product muni-metadonnees-cli)
101+
(cd "$MUNI_PRECLASSEMENT_DIR" && swift build -c debug --product muni-preclassement-cli)
102+
(cd "$MUNI_CONTROLE_DIR" && swift build -c debug --product muni-controle-cli)
103+
}
104+
105+
cmd_start() {
106+
require_prereqs
107+
108+
if is_running; then
109+
echo "OrchivisteAPI est deja en cours (PID $(cat "$PID_FILE"))."
110+
echo "URL: $BASE_URL/ui/cockpit"
111+
exit 0
112+
fi
113+
114+
mkdir -p "$LOG_DIR" "$REQUEST_DIR" "$RESULT_DIR"
115+
116+
if [[ "${SKIP_BUILD:-0}" != "1" ]]; then
117+
build_demo_binaries
118+
fi
119+
120+
echo "== Demarrage OrchivisteAPI (sans Docker) =="
121+
local pid
122+
pushd "$REPO_ROOT/OrchivisteAPI" >/dev/null
123+
nohup env \
124+
ORCHIVISTE_API_HOST="$DEMO_HOST" \
125+
ORCHIVISTE_API_PORT="$DEMO_PORT" \
126+
ORCHIVISTE_AUTO_MIGRATE=1 \
127+
ORCHIVISTE_SQLITE_PATH="$SQLITE_PATH" \
128+
ORCHIVISTE_COCKPIT_CONFIG_FILE="$COCKPIT_CONFIG_FILE" \
129+
./.build/debug/OrchivisteAPI \
130+
>"$API_LOG_FILE" 2>&1 < /dev/null &
131+
pid="$!"
132+
popd >/dev/null
133+
134+
if [[ -z "$pid" ]]; then
135+
echo "ECHEC: impossible de recuperer le PID OrchivisteAPI." >&2
136+
[[ -f "$API_LOG_FILE" ]] && tail -n 80 "$API_LOG_FILE" >&2
137+
exit 1
138+
fi
139+
echo "$pid" > "$PID_FILE"
140+
141+
if ! wait_health; then
142+
echo "ECHEC: API non prete sur $BASE_URL (voir $API_LOG_FILE)" >&2
143+
cmd_stop >/dev/null 2>&1 || true
144+
exit 1
145+
fi
146+
147+
echo "API prete."
148+
echo "- URL cockpit: $BASE_URL/ui/cockpit"
149+
echo "- URL catalogue API: $BASE_URL/v1/cockpit/tools"
150+
echo "- PID file: $PID_FILE"
151+
echo "- Log API: $API_LOG_FILE"
152+
echo "- SQLite: $SQLITE_PATH"
153+
echo "- Runtime cockpit: $REPO_ROOT/runtime/demo-local/cockpit"
154+
}
155+
156+
cmd_catalog() {
157+
require_prereqs
158+
ensure_api_reachable
159+
local out="$RESULT_DIR/catalog.tools.json"
160+
mkdir -p "$RESULT_DIR"
161+
curl -sS "$BASE_URL/v1/cockpit/tools" > "$out"
162+
python3 - <<'PY' "$out"
163+
import json, sys
164+
with open(sys.argv[1], 'r', encoding='utf-8') as f:
165+
tools = json.load(f)
166+
print("Catalogue outils:")
167+
for item in tools:
168+
d = item.get("descriptor", {})
169+
print(f"- {d.get('id')} v{d.get('version')} status={d.get('integration_status')} available={item.get('is_available')}")
170+
PY
171+
echo "JSON complet: $out"
172+
}
173+
174+
cmd_sample_run() {
175+
require_prereqs
176+
ensure_api_reachable
177+
mkdir -p "$REQUEST_DIR" "$RESULT_DIR"
178+
179+
local payload="$REQUEST_DIR/demo-run-muni-analyse.payload.json"
180+
local response="$RESULT_DIR/demo-run-muni-analyse.response.json"
181+
local report_path="$RESULT_DIR/demo-run-muni-analyse.report.json"
182+
183+
cat > "$payload" <<JSON
184+
{
185+
"tool_id": "MuniAnalyse",
186+
"action": "run",
187+
"parameters": {
188+
"source_path": "$FIXTURE_FILE",
189+
"report_path": "$report_path"
190+
}
191+
}
192+
JSON
193+
194+
curl -sS -X POST "$BASE_URL/v1/cockpit/runs" \
195+
-H "Content-Type: application/json" \
196+
--data-binary "@$payload" \
197+
> "$response"
198+
199+
python3 - <<'PY' "$response" "$report_path"
200+
import json, sys, os
201+
resp = json.load(open(sys.argv[1], 'r', encoding='utf-8'))
202+
report_path = sys.argv[2]
203+
status = resp.get('status')
204+
if status not in {'succeeded', 'needs_review'}:
205+
raise SystemExit(f"ECHEC run: statut inattendu {status}")
206+
print(f"Run OK: status={status}")
207+
print(f"Execution ID: {resp.get('execution_id')}")
208+
print(f"Request file: {resp.get('request_file')}")
209+
print(f"Result file: {resp.get('result_file')}")
210+
print(f"History file: {resp.get('history_file')}")
211+
print(f"Report exists: {os.path.exists(report_path)} ({report_path})")
212+
PY
213+
214+
echo "Payload: $payload"
215+
echo "Reponse: $response"
216+
}
217+
218+
cmd_history() {
219+
require_prereqs
220+
ensure_api_reachable
221+
local out="$RESULT_DIR/history.latest.json"
222+
mkdir -p "$RESULT_DIR"
223+
curl -sS "$BASE_URL/v1/cockpit/history?limit=20" > "$out"
224+
python3 - <<'PY' "$out"
225+
import json, sys
226+
payload = json.load(open(sys.argv[1], 'r', encoding='utf-8'))
227+
entries = payload.get('entries', [])
228+
print(f"Historique ({len(entries)} entree(s))")
229+
for e in entries[:10]:
230+
print(f"- {e.get('started_at')} | {e.get('tool_id')} | {e.get('action')} | {e.get('status')} | {e.get('execution_id')}")
231+
print(f"history_file={payload.get('history_file')}")
232+
PY
233+
echo "JSON complet: $out"
234+
}
235+
236+
cmd_status() {
237+
if is_running; then
238+
local pid
239+
pid="$(cat "$PID_FILE")"
240+
echo "API en cours (PID $pid)."
241+
else
242+
echo "API arretee."
243+
fi
244+
245+
if curl -sS "$BASE_URL/v1/health" >/dev/null 2>&1; then
246+
echo "Health endpoint OK: $BASE_URL/v1/health"
247+
else
248+
echo "Health endpoint indisponible: $BASE_URL/v1/health"
249+
fi
250+
251+
echo "Cockpit UI: $BASE_URL/ui/cockpit"
252+
}
253+
254+
cmd_stop() {
255+
if ! is_running; then
256+
echo "API deja arretee."
257+
rm -f "$PID_FILE"
258+
return 0
259+
fi
260+
261+
local pid
262+
pid="$(cat "$PID_FILE")"
263+
echo "Arret API (PID $pid)..."
264+
kill "$pid" >/dev/null 2>&1 || true
265+
266+
local attempts=20
267+
while kill -0 "$pid" >/dev/null 2>&1 && (( attempts > 0 )); do
268+
sleep 0.5
269+
attempts=$((attempts - 1))
270+
done
271+
272+
if kill -0 "$pid" >/dev/null 2>&1; then
273+
echo "Force kill PID $pid"
274+
kill -9 "$pid" >/dev/null 2>&1 || true
275+
fi
276+
277+
rm -f "$PID_FILE"
278+
echo "API arretee."
279+
}
280+
281+
cmd_demo_once() {
282+
echo "== Demo locale Orchiviste (sans Docker) =="
283+
cmd_start
284+
trap 'cmd_stop >/dev/null 2>&1 || true' EXIT
285+
cmd_catalog
286+
cmd_sample_run
287+
cmd_history
288+
echo "Demo locale terminee."
289+
}
290+
291+
usage() {
292+
cat <<'TXT'
293+
Usage:
294+
scripts/demo_local_cockpit.sh start
295+
scripts/demo_local_cockpit.sh status
296+
scripts/demo_local_cockpit.sh catalog
297+
scripts/demo_local_cockpit.sh sample-run
298+
scripts/demo_local_cockpit.sh history
299+
scripts/demo_local_cockpit.sh stop
300+
scripts/demo_local_cockpit.sh demo-once
301+
302+
Variables utiles:
303+
ORCHIVISTE_DEMO_HOST (defaut: 127.0.0.1)
304+
ORCHIVISTE_DEMO_PORT (defaut: 28780)
305+
ORCHIVISTE_DEMO_RUNTIME_DIR (defaut: Orchiviste/runtime/demo-local)
306+
ORCHIVISTE_DEMO_SQLITE_PATH (defaut: runtime/demo-local/orchiviste-demo.sqlite)
307+
ORCHIVISTE_DEMO_CONFIG_FILE (defaut: OrchivisteAPI/configs/cockpit/demo.local.json)
308+
ORCHIVISTE_DEMO_FIXTURE (defaut: fixtures/demo/cockpit/input_document.txt)
309+
MUNI_ANALYSE_DIR (defaut: ../MuniAnalyse)
310+
MUNI_METADONNEES_DIR (defaut: ../MuniMetadonnees)
311+
MUNI_PRECLASSEMENT_DIR (defaut: ../MuniPreclassement)
312+
MUNI_CONTROLE_DIR (defaut: ../MuniControle)
313+
SKIP_BUILD=1 (skip des builds)
314+
TXT
315+
}
316+
317+
main() {
318+
local cmd="${1:-start}"
319+
case "$cmd" in
320+
start) cmd_start ;;
321+
status) cmd_status ;;
322+
catalog) cmd_catalog ;;
323+
sample-run) cmd_sample_run ;;
324+
history) cmd_history ;;
325+
stop) cmd_stop ;;
326+
demo-once) cmd_demo_once ;;
327+
-h|--help|help) usage ;;
328+
*)
329+
echo "Commande inconnue: $cmd" >&2
330+
usage
331+
exit 1
332+
;;
333+
esac
334+
}
335+
336+
main "$@"

0 commit comments

Comments
 (0)