fortranspire 0.1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fortranspire-0.1.2/.claude/settings.local.json +62 -0
- fortranspire-0.1.2/.env.example +43 -0
- fortranspire-0.1.2/.github/workflows/analyze.yml +113 -0
- fortranspire-0.1.2/.github/workflows/deploy-azure.yml +78 -0
- fortranspire-0.1.2/.github/workflows/draft-paper.yml +35 -0
- fortranspire-0.1.2/.github/workflows/jax-translation.yml +54 -0
- fortranspire-0.1.2/.gitignore +27 -0
- fortranspire-0.1.2/.readthedocs.yaml +24 -0
- fortranspire-0.1.2/.zenodo.json +49 -0
- fortranspire-0.1.2/AZURE_DEPLOYMENT.md +56 -0
- fortranspire-0.1.2/Apptainer.analyze +78 -0
- fortranspire-0.1.2/CITATION.cff +64 -0
- fortranspire-0.1.2/CODE_OF_CONDUCT.md +60 -0
- fortranspire-0.1.2/CONTRIBUTING.md +117 -0
- fortranspire-0.1.2/Dockerfile +45 -0
- fortranspire-0.1.2/Dockerfile.ci +29 -0
- fortranspire-0.1.2/Dockerfile.hpc +31 -0
- fortranspire-0.1.2/LICENSE +201 -0
- fortranspire-0.1.2/OPTIMIZATION_REVIEW.md +134 -0
- fortranspire-0.1.2/PKG-INFO +1410 -0
- fortranspire-0.1.2/README.md +1324 -0
- fortranspire-0.1.2/RECETTE_SEISMIC.md +71 -0
- fortranspire-0.1.2/SECURITY.md +78 -0
- fortranspire-0.1.2/TUTORIAL_IDE.md +94 -0
- fortranspire-0.1.2/apptainer.def +21 -0
- fortranspire-0.1.2/batch_audit.sh +113 -0
- fortranspire-0.1.2/bencher_report.json +1 -0
- fortranspire-0.1.2/codemeta.json +114 -0
- fortranspire-0.1.2/docker-compose.yml +22 -0
- fortranspire-0.1.2/docs/api/index.md +20 -0
- fortranspire-0.1.2/docs/changelog.md +313 -0
- fortranspire-0.1.2/docs/code-of-conduct.md +4 -0
- fortranspire-0.1.2/docs/concepts/architecture.md +68 -0
- fortranspire-0.1.2/docs/concepts/fortran-patterns.md +26 -0
- fortranspire-0.1.2/docs/concepts/le-chat-connector.md +67 -0
- fortranspire-0.1.2/docs/concepts/legacy-documentation.md +132 -0
- fortranspire-0.1.2/docs/concepts/llm-endpoints.md +53 -0
- fortranspire-0.1.2/docs/concepts/mistral-integration.md +186 -0
- fortranspire-0.1.2/docs/conf.py +98 -0
- fortranspire-0.1.2/docs/contributing.md +4 -0
- fortranspire-0.1.2/docs/getting-started/configuration.md +51 -0
- fortranspire-0.1.2/docs/getting-started/installation.md +126 -0
- fortranspire-0.1.2/docs/getting-started/quickstart.md +92 -0
- fortranspire-0.1.2/docs/index.md +65 -0
- fortranspire-0.1.2/docs/requirements.txt +6 -0
- fortranspire-0.1.2/docs/security.md +4 -0
- fortranspire-0.1.2/examples/mistral_agents_api_smoke_test.py +160 -0
- fortranspire-0.1.2/fortranspire/__init__.py +14 -0
- fortranspire-0.1.2/fortranspire/agent/__init__.py +4 -0
- fortranspire-0.1.2/fortranspire/agent/analyze.py +579 -0
- fortranspire-0.1.2/fortranspire/agent/batch.py +254 -0
- fortranspire-0.1.2/fortranspire/agent/bench.py +368 -0
- fortranspire-0.1.2/fortranspire/agent/call_graph.py +306 -0
- fortranspire-0.1.2/fortranspire/agent/cli.py +300 -0
- fortranspire-0.1.2/fortranspire/agent/code_agent.py +69 -0
- fortranspire-0.1.2/fortranspire/agent/diff.py +308 -0
- fortranspire-0.1.2/fortranspire/agent/document.py +608 -0
- fortranspire-0.1.2/fortranspire/agent/explain.py +391 -0
- fortranspire-0.1.2/fortranspire/agent/format.py +136 -0
- fortranspire-0.1.2/fortranspire/agent/fortls_oracle.py +112 -0
- fortranspire-0.1.2/fortranspire/agent/nodes/__init__.py +38 -0
- fortranspire-0.1.2/fortranspire/agent/nodes/_common.py +79 -0
- fortranspire-0.1.2/fortranspire/agent/nodes/_state.py +54 -0
- fortranspire-0.1.2/fortranspire/agent/nodes/cython_wrapper.py +152 -0
- fortranspire-0.1.2/fortranspire/agent/nodes/equivalence_harness.py +231 -0
- fortranspire-0.1.2/fortranspire/agent/nodes/extractor.py +238 -0
- fortranspire-0.1.2/fortranspire/agent/nodes/init.py +29 -0
- fortranspire-0.1.2/fortranspire/agent/nodes/openacc.py +182 -0
- fortranspire-0.1.2/fortranspire/agent/nodes/parser.py +270 -0
- fortranspire-0.1.2/fortranspire/agent/nodes/pure_elemental.py +84 -0
- fortranspire-0.1.2/fortranspire/agent/nodes/validation.py +409 -0
- fortranspire-0.1.2/fortranspire/agent/report.py +227 -0
- fortranspire-0.1.2/fortranspire/agent/schemas.py +107 -0
- fortranspire-0.1.2/fortranspire/agent/toolchain.py +169 -0
- fortranspire-0.1.2/fortranspire/agent/translation_graph.py +1601 -0
- fortranspire-0.1.2/fortranspire/agent/translation_graph_phase1.py +81 -0
- fortranspire-0.1.2/fortranspire/brain/a7b8afb7-b6df-4d7c-9252-618f5c7ff174/scratch/inspect_loki.py +37 -0
- fortranspire-0.1.2/fortranspire/brain/a7b8afb7-b6df-4d7c-9252-618f5c7ff174/scratch/test_loki.py +39 -0
- fortranspire-0.1.2/fortranspire/cache/__init__.py +31 -0
- fortranspire-0.1.2/fortranspire/cache/install.py +92 -0
- fortranspire-0.1.2/fortranspire/cache/lru.py +188 -0
- fortranspire-0.1.2/fortranspire/cli.py +129 -0
- fortranspire-0.1.2/fortranspire/config.py +31 -0
- fortranspire-0.1.2/fortranspire/llm/__init__.py +154 -0
- fortranspire-0.1.2/fortranspire/main.py +62 -0
- fortranspire-0.1.2/fortranspire/observability/__init__.py +27 -0
- fortranspire-0.1.2/fortranspire/observability/llm_callback.py +51 -0
- fortranspire-0.1.2/fortranspire/observability/pricing.py +70 -0
- fortranspire-0.1.2/fortranspire/observability/tracer.py +158 -0
- fortranspire-0.1.2/fortranspire/prompts/__init__.py +31 -0
- fortranspire-0.1.2/fortranspire/prompts/cython_header/en/v1.md +1 -0
- fortranspire-0.1.2/fortranspire/prompts/cython_header/en/v2.md +1 -0
- fortranspire-0.1.2/fortranspire/prompts/cython_header/fr/v1.md +1 -0
- fortranspire-0.1.2/fortranspire/prompts/cython_header/fr/v2.md +10 -0
- fortranspire-0.1.2/fortranspire/prompts/cython_pyx/en/v1.md +1 -0
- fortranspire-0.1.2/fortranspire/prompts/cython_pyx/en/v2.md +1 -0
- fortranspire-0.1.2/fortranspire/prompts/cython_pyx/fr/v1.md +1 -0
- fortranspire-0.1.2/fortranspire/prompts/cython_pyx/fr/v2.md +11 -0
- fortranspire-0.1.2/fortranspire/prompts/doc_routine/en/v1.md +5 -0
- fortranspire-0.1.2/fortranspire/prompts/doc_routine/fr/v1.md +6 -0
- fortranspire-0.1.2/fortranspire/prompts/extractor/en/v1.md +53 -0
- fortranspire-0.1.2/fortranspire/prompts/extractor/en/v2.md +53 -0
- fortranspire-0.1.2/fortranspire/prompts/extractor/fr/v1.md +53 -0
- fortranspire-0.1.2/fortranspire/prompts/extractor/fr/v2.md +61 -0
- fortranspire-0.1.2/fortranspire/prompts/loader.py +120 -0
- fortranspire-0.1.2/fortranspire/prompts/openacc_driver/en/v1.md +12 -0
- fortranspire-0.1.2/fortranspire/prompts/openacc_driver/en/v2.md +12 -0
- fortranspire-0.1.2/fortranspire/prompts/openacc_driver/fr/v1.md +13 -0
- fortranspire-0.1.2/fortranspire/prompts/openacc_driver/fr/v2.md +21 -0
- fortranspire-0.1.2/fortranspire/prompts/openacc_kernel/en/v1.md +19 -0
- fortranspire-0.1.2/fortranspire/prompts/openacc_kernel/en/v2.md +19 -0
- fortranspire-0.1.2/fortranspire/prompts/openacc_kernel/fr/v1.md +22 -0
- fortranspire-0.1.2/fortranspire/prompts/openacc_kernel/fr/v2.md +30 -0
- fortranspire-0.1.2/fortranspire/prompts/openmp_driver/en/v2.md +27 -0
- fortranspire-0.1.2/fortranspire/prompts/openmp_driver/fr/v2.md +27 -0
- fortranspire-0.1.2/fortranspire/prompts/openmp_kernel/en/v2.md +34 -0
- fortranspire-0.1.2/fortranspire/prompts/openmp_kernel/fr/v2.md +35 -0
- fortranspire-0.1.2/fortranspire/requirements.txt +22 -0
- fortranspire-0.1.2/fortranspire/security/__init__.py +36 -0
- fortranspire-0.1.2/fortranspire/security/audit.py +107 -0
- fortranspire-0.1.2/fortranspire/security/auth.py +216 -0
- fortranspire-0.1.2/fortranspire/server.py +188 -0
- fortranspire-0.1.2/fortranspire/tools/__init__.py +8 -0
- fortranspire-0.1.2/fortranspire/tools/file_tools.py +59 -0
- fortranspire-0.1.2/fortranspire/tools/search_tools.py +38 -0
- fortranspire-0.1.2/fortranspire/tools/shell_tools.py +36 -0
- fortranspire-0.1.2/infrastructure/deploy.sh +66 -0
- fortranspire-0.1.2/infrastructure/main.tf +114 -0
- fortranspire-0.1.2/infrastructure/variables.tf +4 -0
- fortranspire-0.1.2/integration/le-chat-connector.json +106 -0
- fortranspire-0.1.2/paper.bib +105 -0
- fortranspire-0.1.2/paper.md +131 -0
- fortranspire-0.1.2/pyproject.toml +171 -0
- fortranspire-0.1.2/scripts/bench_gpu.sh +182 -0
- fortranspire-0.1.2/scripts/get_gpu_ip.sh +31 -0
- fortranspire-0.1.2/scripts/test_gpu.sh +139 -0
- fortranspire-0.1.2/tests/__init__.py +0 -0
- fortranspire-0.1.2/tests/cache/__init__.py +0 -0
- fortranspire-0.1.2/tests/cache/test_install.py +76 -0
- fortranspire-0.1.2/tests/cache/test_lru_cache.py +199 -0
- fortranspire-0.1.2/tests/fixtures/doc_kernel.f90 +38 -0
- fortranspire-0.1.2/tests/observability/__init__.py +0 -0
- fortranspire-0.1.2/tests/observability/test_llm_callback.py +56 -0
- fortranspire-0.1.2/tests/observability/test_tracer.py +145 -0
- fortranspire-0.1.2/tests/prompts/test_fr_i18n.py +81 -0
- fortranspire-0.1.2/tests/prompts/test_loader.py +88 -0
- fortranspire-0.1.2/tests/security/__init__.py +0 -0
- fortranspire-0.1.2/tests/security/test_audit.py +98 -0
- fortranspire-0.1.2/tests/security/test_auth.py +111 -0
- fortranspire-0.1.2/tests/security/test_middleware.py +106 -0
- fortranspire-0.1.2/tests/test_batch.py +260 -0
- fortranspire-0.1.2/tests/test_bench.py +215 -0
- fortranspire-0.1.2/tests/test_call_graph.py +155 -0
- fortranspire-0.1.2/tests/test_diff.py +157 -0
- fortranspire-0.1.2/tests/test_document.py +143 -0
- fortranspire-0.1.2/tests/test_equivalence_harness.py +126 -0
- fortranspire-0.1.2/tests/test_explain.py +116 -0
- fortranspire-0.1.2/tests/test_llm_backend.py +121 -0
- fortranspire-0.1.2/tests/test_openmp_target.py +217 -0
- fortranspire-0.1.2/tests/test_report.py +133 -0
- fortranspire-0.1.2/tests/test_schemas.py +76 -0
- fortranspire-0.1.2/tests/test_unified_cli.py +145 -0
- fortranspire-0.1.2/uv.lock +4771 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": [
|
|
4
|
+
"Bash(git add *)",
|
|
5
|
+
"Bash(git commit -m ' *)",
|
|
6
|
+
"Bash(git push *)",
|
|
7
|
+
"WebSearch",
|
|
8
|
+
"WebFetch(domain:mistral.ai)",
|
|
9
|
+
"WebFetch(domain:www.scaleway.com)",
|
|
10
|
+
"Bash(uv sync *)",
|
|
11
|
+
"Bash(curl -sL https://pypi.org/pypi/loki-frontend/json)",
|
|
12
|
+
"Bash(curl -sL https://pypi.org/pypi/loki/json)",
|
|
13
|
+
"Bash(curl -sL https://api.github.com/repos/ecmwf-ifs/loki/releases/latest)",
|
|
14
|
+
"Bash(python3 -c \"import sys, json; d=json.load\\(sys.stdin\\); print\\('tag:', d.get\\('tag_name'\\), 'name:', d.get\\('name'\\)\\)\")",
|
|
15
|
+
"Bash(curl -sL https://api.github.com/repos/ecmwf-ifs/loki/tags)",
|
|
16
|
+
"Bash(python3 -c \"import sys, json; tags=json.load\\(sys.stdin\\); print\\([t['name'] for t in tags[:10]]\\)\")",
|
|
17
|
+
"Bash(git ls-remote *)",
|
|
18
|
+
"Bash(uv run *)",
|
|
19
|
+
"Bash(.venv/bin/python *)",
|
|
20
|
+
"Bash(pip show *)",
|
|
21
|
+
"Bash(git rm *)",
|
|
22
|
+
"Bash(git checkout *)",
|
|
23
|
+
"Bash(git remote *)",
|
|
24
|
+
"Bash(gh pr create --title 'rename to fortranspire + JOSS scaffolding + agent-analyze / agent-doc / agent-format' --base main --head feature/fortranspire-rename-and-joss --body ' *)",
|
|
25
|
+
"Bash(git pull *)",
|
|
26
|
+
"Bash(awk 'NR==489,NR==733' fortranspire/agent/translation_graph_phase1.py)",
|
|
27
|
+
"Bash(awk 'NR==736,NR==807' fortranspire/agent/translation_graph_phase1.py)",
|
|
28
|
+
"Bash(awk 'NR==810,NR==967' fortranspire/agent/translation_graph_phase1.py)",
|
|
29
|
+
"Bash(awk 'NR==970,NR==1090' fortranspire/agent/translation_graph_phase1.py)",
|
|
30
|
+
"Bash(awk 'NR==1095,NR==1196' fortranspire/agent/translation_graph_phase1.py)",
|
|
31
|
+
"Bash(awk 'NR==1200,NR==1395' fortranspire/agent/translation_graph_phase1.py)",
|
|
32
|
+
"Bash(awk 'NR==1396,NR==1424' fortranspire/agent/translation_graph_phase1.py)",
|
|
33
|
+
"Bash(awk 'NR>=1085 && NR<=1095' fortranspire/agent/translation_graph_phase1.py.bak)",
|
|
34
|
+
"Bash(awk 'NR>=1085 && NR<=1095')",
|
|
35
|
+
"Bash(gh pr create --title '[#2] split monolithic LangGraph file into per-node modules' --base main --head refactor/issue-2-split-graph-nodes --body ' *)",
|
|
36
|
+
"Bash(gh pr create --title '[#3] externalize LLM prompts \\(versioning + i18n\\)' --base main --head refactor/issue-3-externalize-prompts --body ' *)",
|
|
37
|
+
"Bash(gh pr create --title '[#4] Pydantic schemas + documenter structured output' --base main --head refactor/issue-4-structured-outputs --body ' *)",
|
|
38
|
+
"Bash(gh pr *)",
|
|
39
|
+
"Bash(git fetch *)",
|
|
40
|
+
"Bash(git rebase *)",
|
|
41
|
+
"WebFetch(domain:meteofrance.com)",
|
|
42
|
+
"WebFetch(domain:www.bayfor.org)",
|
|
43
|
+
"WebFetch(domain:www.eurohpc-ju.europa.eu)",
|
|
44
|
+
"WebFetch(domain:topictree.ideal-ist.eu)",
|
|
45
|
+
"WebFetch(domain:www.marches-publics.gouv.fr)",
|
|
46
|
+
"WebFetch(domain:www.ecmwf.int)",
|
|
47
|
+
"WebFetch(domain:www.marchesonline.com)",
|
|
48
|
+
"WebFetch(domain:hpc-portal.eu)",
|
|
49
|
+
"Bash(uv build *)",
|
|
50
|
+
"Bash(git tag -a v0.1.0 -m ' *)",
|
|
51
|
+
"Bash(git tag *)",
|
|
52
|
+
"Bash(gh release *)",
|
|
53
|
+
"Bash(gh api *)",
|
|
54
|
+
"Bash(curl -sI https://github.com/maurinl26/loki)",
|
|
55
|
+
"Bash(curl -sI https://pypi.org/project/loki/)",
|
|
56
|
+
"Bash(curl -sI https://pypi.org/project/loki-fortranspire/)",
|
|
57
|
+
"Bash(curl -sI https://pypi.org/project/ecmwf-loki/)",
|
|
58
|
+
"Bash(curl -sI https://pypi.org/project/loki-ifs/)",
|
|
59
|
+
"Bash(curl -s -o /dev/null -w '%{http_code}' https://pypi.org/pypi/__TRACKED_VAR__/json)"
|
|
60
|
+
]
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Copier ce fichier en .env et adapter les valeurs
|
|
2
|
+
|
|
3
|
+
# ----------------------------------------------------
|
|
4
|
+
# Endpoint Mistral (OpenAI-compatible)
|
|
5
|
+
# ----------------------------------------------------
|
|
6
|
+
# Par défaut : La Plateforme Mistral (hébergement souverain en Europe).
|
|
7
|
+
# Alternatives : vLLM / TGI / Ollama auto-hébergés on-prem ou cloud privé.
|
|
8
|
+
MISTRAL_ENDPOINT="https://api.mistral.ai/v1"
|
|
9
|
+
MISTRAL_API_KEY="votre_clef_mistral_ici_xxxxxx"
|
|
10
|
+
|
|
11
|
+
# ── Modèle par étape du pipeline ────────────────────────────────────────────
|
|
12
|
+
# Le pipeline a deux types d'appels LLM :
|
|
13
|
+
# - reasoning (extractor, openacc) → besoin d'un gros modèle de raisonnement
|
|
14
|
+
# - code (cython_wrapper) → Codestral suffit, moins cher, plus rapide
|
|
15
|
+
#
|
|
16
|
+
# Si une variable spécifique est définie, elle prime. Sinon, fallback sur
|
|
17
|
+
# MISTRAL_MODEL (legacy, un seul modèle pour tout), puis sur les défauts.
|
|
18
|
+
MISTRAL_MODEL_REASONING="mistral-large-latest"
|
|
19
|
+
MISTRAL_MODEL_CODE="codestral-latest"
|
|
20
|
+
|
|
21
|
+
# Legacy : un seul modèle pour les deux étapes. Décommenter pour forcer.
|
|
22
|
+
# MISTRAL_MODEL="mistral-large-latest"
|
|
23
|
+
|
|
24
|
+
# Note : aucune variable AZURE_* n'est lue. L'agent appelle l'endpoint
|
|
25
|
+
# OpenAI-compatible défini ci-dessus directement, sans intermédiaire.
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# Paramètres de génération
|
|
29
|
+
LLM_TEMPERATURE=0.0
|
|
30
|
+
LLM_TOP_P=0.9
|
|
31
|
+
LLM_NUM_PREDICT=2048
|
|
32
|
+
|
|
33
|
+
# Agent
|
|
34
|
+
AGENT_MAX_ITERATIONS=15
|
|
35
|
+
AGENT_MEMORY_WINDOW=10
|
|
36
|
+
|
|
37
|
+
# Authentification (Optionnelle)
|
|
38
|
+
# Laissez vide pour un accès public.
|
|
39
|
+
# Renseignez une valeur pour forcer le header 'Authorization: Bearer <API_KEY>'
|
|
40
|
+
# API_KEY=ma_clef_secrete_123
|
|
41
|
+
|
|
42
|
+
# Répertoire de travail (laisser vide = racine du projet)
|
|
43
|
+
# AGENT_WORKSPACE=/path/to/your/project
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
name: Fortran static analysis
|
|
2
|
+
|
|
3
|
+
# Runs the deterministic Loki-based parser on every PR and push.
|
|
4
|
+
# No LLM is called, no token is consumed — safe on forks.
|
|
5
|
+
# Findings are uploaded as SARIF and surface as inline PR annotations
|
|
6
|
+
# via GitHub Code Scanning.
|
|
7
|
+
|
|
8
|
+
on:
|
|
9
|
+
pull_request:
|
|
10
|
+
paths:
|
|
11
|
+
- '**/*.f90'
|
|
12
|
+
- '**/*.F90'
|
|
13
|
+
- 'local_code_agent/**'
|
|
14
|
+
- '.github/workflows/analyze.yml'
|
|
15
|
+
push:
|
|
16
|
+
branches: [main]
|
|
17
|
+
paths:
|
|
18
|
+
- '**/*.f90'
|
|
19
|
+
- '**/*.F90'
|
|
20
|
+
- 'local_code_agent/**'
|
|
21
|
+
- '.github/workflows/analyze.yml'
|
|
22
|
+
workflow_dispatch:
|
|
23
|
+
inputs:
|
|
24
|
+
target_path:
|
|
25
|
+
description: 'Path to analyze (file or directory)'
|
|
26
|
+
required: false
|
|
27
|
+
default: '.'
|
|
28
|
+
|
|
29
|
+
permissions:
|
|
30
|
+
contents: read
|
|
31
|
+
security-events: write # required to upload SARIF
|
|
32
|
+
pull-requests: read
|
|
33
|
+
|
|
34
|
+
jobs:
|
|
35
|
+
analyze:
|
|
36
|
+
name: agent-analyze (Loki, no LLM)
|
|
37
|
+
runs-on: ubuntu-latest
|
|
38
|
+
timeout-minutes: 10
|
|
39
|
+
|
|
40
|
+
steps:
|
|
41
|
+
- name: Checkout
|
|
42
|
+
uses: actions/checkout@v4
|
|
43
|
+
with:
|
|
44
|
+
fetch-depth: 2 # need HEAD~1 to diff changed files on PR
|
|
45
|
+
|
|
46
|
+
- name: Install uv
|
|
47
|
+
uses: astral-sh/setup-uv@v3
|
|
48
|
+
with:
|
|
49
|
+
enable-cache: true
|
|
50
|
+
|
|
51
|
+
- name: Set up Python 3.12
|
|
52
|
+
run: uv python install 3.12
|
|
53
|
+
|
|
54
|
+
- name: Install project (no LLM extras needed)
|
|
55
|
+
run: uv sync --no-dev
|
|
56
|
+
|
|
57
|
+
- name: Pick paths to analyze
|
|
58
|
+
id: paths
|
|
59
|
+
run: |
|
|
60
|
+
set -euo pipefail
|
|
61
|
+
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
|
62
|
+
echo "targets=${{ github.event.inputs.target_path }}" >> "$GITHUB_OUTPUT"
|
|
63
|
+
exit 0
|
|
64
|
+
fi
|
|
65
|
+
if [ "${{ github.event_name }}" = "pull_request" ]; then
|
|
66
|
+
git fetch --no-tags --depth=1 origin "${{ github.base_ref }}"
|
|
67
|
+
changed=$(git diff --name-only --diff-filter=ACMR \
|
|
68
|
+
"origin/${{ github.base_ref }}...HEAD" \
|
|
69
|
+
| grep -E '\.(f90|F90)$' || true)
|
|
70
|
+
else
|
|
71
|
+
changed=$(git diff --name-only --diff-filter=ACMR HEAD~1 HEAD \
|
|
72
|
+
| grep -E '\.(f90|F90)$' || true)
|
|
73
|
+
fi
|
|
74
|
+
if [ -z "$changed" ]; then
|
|
75
|
+
echo "No Fortran files changed — analyzer will scan the whole repo."
|
|
76
|
+
echo "targets=." >> "$GITHUB_OUTPUT"
|
|
77
|
+
else
|
|
78
|
+
echo "Changed Fortran files:"
|
|
79
|
+
echo "$changed"
|
|
80
|
+
echo "targets<<EOF" >> "$GITHUB_OUTPUT"
|
|
81
|
+
echo "$changed" >> "$GITHUB_OUTPUT"
|
|
82
|
+
echo "EOF" >> "$GITHUB_OUTPUT"
|
|
83
|
+
fi
|
|
84
|
+
|
|
85
|
+
- name: Run analyzer (text — console)
|
|
86
|
+
continue-on-error: true
|
|
87
|
+
run: |
|
|
88
|
+
# shellcheck disable=SC2086
|
|
89
|
+
uv run agent-analyze --no-color ${{ steps.paths.outputs.targets }}
|
|
90
|
+
|
|
91
|
+
- name: Run analyzer (SARIF — for Code Scanning)
|
|
92
|
+
run: |
|
|
93
|
+
# shellcheck disable=SC2086
|
|
94
|
+
uv run agent-analyze \
|
|
95
|
+
--format sarif \
|
|
96
|
+
--fail-on error \
|
|
97
|
+
--output fortranspire.sarif \
|
|
98
|
+
${{ steps.paths.outputs.targets }}
|
|
99
|
+
|
|
100
|
+
- name: Upload SARIF to GitHub Code Scanning
|
|
101
|
+
if: always()
|
|
102
|
+
uses: github/codeql-action/upload-sarif@v3
|
|
103
|
+
with:
|
|
104
|
+
sarif_file: fortranspire.sarif
|
|
105
|
+
category: fortranspire
|
|
106
|
+
|
|
107
|
+
- name: Upload SARIF as build artifact
|
|
108
|
+
if: always()
|
|
109
|
+
uses: actions/upload-artifact@v4
|
|
110
|
+
with:
|
|
111
|
+
name: fortranspire-sarif
|
|
112
|
+
path: fortranspire.sarif
|
|
113
|
+
if-no-files-found: warn
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
name: 'Azure Infrastructure Control'
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
# Permet le lancement manuel depuis l'onglet "Actions" de GitHub
|
|
5
|
+
workflow_dispatch:
|
|
6
|
+
inputs:
|
|
7
|
+
action:
|
|
8
|
+
description: 'Action à effectuer (apply / destroy)'
|
|
9
|
+
required: true
|
|
10
|
+
default: 'apply'
|
|
11
|
+
type: choice
|
|
12
|
+
options:
|
|
13
|
+
- apply
|
|
14
|
+
- destroy
|
|
15
|
+
|
|
16
|
+
permissions:
|
|
17
|
+
contents: read
|
|
18
|
+
|
|
19
|
+
jobs:
|
|
20
|
+
terraform:
|
|
21
|
+
name: 'Terraform ${{ github.event.inputs.action }}'
|
|
22
|
+
runs-on: ubuntu-latest
|
|
23
|
+
env:
|
|
24
|
+
ARM_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
|
|
25
|
+
ARM_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
|
|
26
|
+
ARM_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
|
|
27
|
+
ARM_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
|
|
28
|
+
TF_VAR_ssh_public_key: ${{ secrets.DEPLOY_SSH_PUBKEY }}
|
|
29
|
+
|
|
30
|
+
steps:
|
|
31
|
+
- name: Checkout Code
|
|
32
|
+
uses: actions/checkout@v4
|
|
33
|
+
|
|
34
|
+
- name: Setup Terraform
|
|
35
|
+
uses: hashicorp/setup-terraform@v3
|
|
36
|
+
|
|
37
|
+
- name: Terraform Init
|
|
38
|
+
run: terraform init
|
|
39
|
+
working-directory: infrastructure
|
|
40
|
+
|
|
41
|
+
- name: Azure CLI Login
|
|
42
|
+
run: az login --service-principal -u $ARM_CLIENT_ID -p $ARM_CLIENT_SECRET --tenant $ARM_TENANT_ID
|
|
43
|
+
|
|
44
|
+
- name: Cleanup Stale Resources
|
|
45
|
+
if: github.event.inputs.action == 'apply'
|
|
46
|
+
run: |
|
|
47
|
+
# If RG exists in wrong region, delete it entirely — location is immutable so Terraform
|
|
48
|
+
# would try destroy+recreate anyway, but the orphaned NIC-ORCHESTRATOR blocks VNet deletion.
|
|
49
|
+
LOCATION=$(az group show --name rg-total-seismic-agent --query location -o tsv 2>/dev/null || echo "")
|
|
50
|
+
if [ -n "$LOCATION" ] && [ "$LOCATION" != "eastus" ]; then
|
|
51
|
+
echo "RG is in '$LOCATION', deleting so Terraform can recreate in eastus..."
|
|
52
|
+
az group delete --name rg-total-seismic-agent --yes
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
- name: Import Pre-existing Resources
|
|
56
|
+
if: github.event.inputs.action == 'apply'
|
|
57
|
+
working-directory: infrastructure
|
|
58
|
+
run: |
|
|
59
|
+
SUB="/subscriptions/${ARM_SUBSCRIPTION_ID}"
|
|
60
|
+
RG="rg-total-seismic-agent"
|
|
61
|
+
terraform import azurerm_resource_group.rg \
|
|
62
|
+
"${SUB}/resourceGroups/${RG}" 2>/dev/null || true
|
|
63
|
+
terraform import azurerm_virtual_network.vnet \
|
|
64
|
+
"${SUB}/resourceGroups/${RG}/providers/Microsoft.Network/virtualNetworks/vnet-seismic" 2>/dev/null || true
|
|
65
|
+
terraform import azurerm_subnet.subnet_compute \
|
|
66
|
+
"${SUB}/resourceGroups/${RG}/providers/Microsoft.Network/virtualNetworks/vnet-seismic/subnets/snet-compute" 2>/dev/null || true
|
|
67
|
+
terraform import azurerm_network_interface.nic_gpu \
|
|
68
|
+
"${SUB}/resourceGroups/${RG}/providers/Microsoft.Network/networkInterfaces/nic-gpu" 2>/dev/null || true
|
|
69
|
+
terraform import azurerm_ai_services.ai_studio \
|
|
70
|
+
"${SUB}/resourceGroups/${RG}/providers/Microsoft.CognitiveServices/accounts/ai-hub-seismic" 2>/dev/null || true
|
|
71
|
+
|
|
72
|
+
- name: Terraform Plan
|
|
73
|
+
run: terraform plan -input=false ${{ github.event.inputs.action == 'destroy' && '-destroy' || '' }}
|
|
74
|
+
working-directory: infrastructure
|
|
75
|
+
|
|
76
|
+
- name: Terraform Action
|
|
77
|
+
run: terraform ${{ github.event.inputs.action }} -auto-approve -input=false
|
|
78
|
+
working-directory: infrastructure
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
name: JOSS paper draft
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
paths:
|
|
6
|
+
- paper.md
|
|
7
|
+
- paper.bib
|
|
8
|
+
- .github/workflows/draft-paper.yml
|
|
9
|
+
pull_request:
|
|
10
|
+
paths:
|
|
11
|
+
- paper.md
|
|
12
|
+
- paper.bib
|
|
13
|
+
- .github/workflows/draft-paper.yml
|
|
14
|
+
workflow_dispatch:
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
paper:
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
name: Render paper.md via Open Journals Docker action
|
|
20
|
+
steps:
|
|
21
|
+
- name: Checkout
|
|
22
|
+
uses: actions/checkout@v4
|
|
23
|
+
|
|
24
|
+
- name: Build draft PDF
|
|
25
|
+
uses: openjournals/openjournals-draft-action@master
|
|
26
|
+
with:
|
|
27
|
+
journal: joss
|
|
28
|
+
paper-path: paper.md
|
|
29
|
+
|
|
30
|
+
- name: Upload rendered PDF as artifact
|
|
31
|
+
uses: actions/upload-artifact@v4
|
|
32
|
+
with:
|
|
33
|
+
name: paper
|
|
34
|
+
path: paper.pdf
|
|
35
|
+
if-no-files-found: error
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
name: Fortran to JAX Automated Translation
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
paths:
|
|
6
|
+
- '**/*.f90'
|
|
7
|
+
- '**/*.F90'
|
|
8
|
+
- '**/*.f'
|
|
9
|
+
workflow_dispatch:
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
translation-and-profiling:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
container:
|
|
15
|
+
image: ghcr.io/${{ github.repository_owner }}/jax-translation-agent:latest
|
|
16
|
+
steps:
|
|
17
|
+
- name: Checkout Code
|
|
18
|
+
uses: actions/checkout@v4
|
|
19
|
+
with:
|
|
20
|
+
fetch-depth: 0
|
|
21
|
+
|
|
22
|
+
- name: Install Bencher
|
|
23
|
+
uses: bencherdev/bencher@main
|
|
24
|
+
|
|
25
|
+
- name: Run Multi-Agent Translator and Bencher
|
|
26
|
+
env:
|
|
27
|
+
BENCHER_PROJECT: seismic-cpml
|
|
28
|
+
BENCHER_TESTBED: ubuntu-latest
|
|
29
|
+
BENCHER_ADAPTER: json
|
|
30
|
+
run: |
|
|
31
|
+
# Finds all specifically added/modified .f90 files in this PR
|
|
32
|
+
CHANGED_FILES=$(git diff --name-only origin/${{ github.base_ref }} HEAD | grep -E '\.f90$|\.F90$')
|
|
33
|
+
|
|
34
|
+
for file in $CHANGED_FILES; do
|
|
35
|
+
echo "Processing $file..."
|
|
36
|
+
|
|
37
|
+
# Exécution du pipeine englobé par Bencher
|
|
38
|
+
bencher run \
|
|
39
|
+
--project "seismic-cpml" \
|
|
40
|
+
--token "${{ secrets.BENCHER_API_TOKEN }}" \
|
|
41
|
+
--branch "${{ github.ref_name }}" \
|
|
42
|
+
--adapter json \
|
|
43
|
+
--file bencher_report.json \
|
|
44
|
+
--err \
|
|
45
|
+
"python -m local_code_agent.agent.cli translate \"$file\" && python -m local_code_agent.agent.cli profile \"$file\""
|
|
46
|
+
done
|
|
47
|
+
|
|
48
|
+
- name: Commit and Push Translated JAX Code
|
|
49
|
+
run: |
|
|
50
|
+
git config --global user.name "github-actions[bot]"
|
|
51
|
+
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
|
52
|
+
git add "**/*_jax.py"
|
|
53
|
+
git commit -m "chore: Auto-translated Fortran to JAX :robot:" || echo "No changes to commit"
|
|
54
|
+
git push
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
.env
|
|
2
|
+
.DS_Store
|
|
3
|
+
**/*.node
|
|
4
|
+
.venv/
|
|
5
|
+
|
|
6
|
+
# Node/Web
|
|
7
|
+
node_modules/
|
|
8
|
+
.next/
|
|
9
|
+
dist/
|
|
10
|
+
build/
|
|
11
|
+
|
|
12
|
+
# Python cache
|
|
13
|
+
__pycache__/
|
|
14
|
+
*.pyc
|
|
15
|
+
|
|
16
|
+
# HPC / Big data
|
|
17
|
+
*.sif
|
|
18
|
+
*.tar.gz
|
|
19
|
+
*.h5
|
|
20
|
+
*.nc
|
|
21
|
+
*.o
|
|
22
|
+
*.mod
|
|
23
|
+
*.so
|
|
24
|
+
|
|
25
|
+
#
|
|
26
|
+
output/
|
|
27
|
+
loki/
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Read the Docs configuration — see https://docs.readthedocs.io/en/stable/config-file/v2.html
|
|
2
|
+
version: 2
|
|
3
|
+
|
|
4
|
+
build:
|
|
5
|
+
os: ubuntu-24.04
|
|
6
|
+
tools:
|
|
7
|
+
python: "3.12"
|
|
8
|
+
jobs:
|
|
9
|
+
post_create_environment:
|
|
10
|
+
- pip install --upgrade pip
|
|
11
|
+
|
|
12
|
+
python:
|
|
13
|
+
install:
|
|
14
|
+
- requirements: docs/requirements.txt
|
|
15
|
+
- method: pip
|
|
16
|
+
path: .
|
|
17
|
+
|
|
18
|
+
sphinx:
|
|
19
|
+
configuration: docs/conf.py
|
|
20
|
+
fail_on_warning: false
|
|
21
|
+
|
|
22
|
+
formats:
|
|
23
|
+
- pdf
|
|
24
|
+
- epub
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"title": "fortranspire: LLM + MCP pipeline porting legacy Fortran HPC kernels to OpenACC GPU and differentiable JAX",
|
|
3
|
+
"description": "<p><strong>fortranspire</strong> is an open-source Model Context Protocol (MCP) server and CLI that incrementally transforms legacy Fortran 90 scientific code into GPU-accelerated (OpenACC), Python-callable (Cython / scikit-build), and optionally differentiable (JAX) form. It combines deterministic Loki AST analysis with targeted LLM calls against any OpenAI-compatible endpoint (Mistral, vLLM, TGI, Ollama).</p>",
|
|
4
|
+
"license": "Apache-2.0",
|
|
5
|
+
"upload_type": "software",
|
|
6
|
+
"access_right": "open",
|
|
7
|
+
"creators": [
|
|
8
|
+
{
|
|
9
|
+
"name": "Maurin, Loïc",
|
|
10
|
+
"affiliation": "External Lecturer, École Nationale de la Météorologie, Toulouse, France",
|
|
11
|
+
"orcid": "0009-0004-8117-4850"
|
|
12
|
+
}
|
|
13
|
+
],
|
|
14
|
+
"keywords": [
|
|
15
|
+
"Fortran",
|
|
16
|
+
"HPC",
|
|
17
|
+
"GPU",
|
|
18
|
+
"OpenACC",
|
|
19
|
+
"JAX",
|
|
20
|
+
"Cython",
|
|
21
|
+
"scikit-build",
|
|
22
|
+
"Loki",
|
|
23
|
+
"ECMWF",
|
|
24
|
+
"Model Context Protocol",
|
|
25
|
+
"MCP",
|
|
26
|
+
"large language models",
|
|
27
|
+
"Mistral",
|
|
28
|
+
"scientific computing",
|
|
29
|
+
"seismic imaging",
|
|
30
|
+
"numerical weather prediction",
|
|
31
|
+
"neural surrogates",
|
|
32
|
+
"code generation",
|
|
33
|
+
"agent",
|
|
34
|
+
"sovereign AI"
|
|
35
|
+
],
|
|
36
|
+
"communities": [
|
|
37
|
+
{
|
|
38
|
+
"identifier": "joss"
|
|
39
|
+
}
|
|
40
|
+
],
|
|
41
|
+
"related_identifiers": [
|
|
42
|
+
{
|
|
43
|
+
"identifier": "https://github.com/maurinl26/fortranspire",
|
|
44
|
+
"relation": "isSupplementTo",
|
|
45
|
+
"scheme": "url"
|
|
46
|
+
}
|
|
47
|
+
],
|
|
48
|
+
"language": "eng"
|
|
49
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Guide de Déploiement : Azure pour TotalEnergies
|
|
2
|
+
|
|
3
|
+
Ce document détaille l'architecture et les prérequis pour déployer l'Agent LangGraph de traduction Fortran ➔ JAX dans un environnement sécurisé Microsoft Azure (orienté Enterprise).
|
|
4
|
+
|
|
5
|
+
## 1. Architecture Cible
|
|
6
|
+
|
|
7
|
+
Pour ce cas d'usage traitant de physiques lourdes (EDP, C-PML) et d'intelligence artificielle générative, l'architecture se divise en trois composants :
|
|
8
|
+
|
|
9
|
+
1. **Serveur Orchestrateur (Compute)** : Exécute le code LangGraph (`translation_graph.py`), parse les arbres de syntaxe avec `loki-ifs` et héberge l'interface FastMCP.
|
|
10
|
+
- *Instance recommandée :* `Standard_D8s_v5` (Machine optimisée calcul à usage général).
|
|
11
|
+
2. **Nœud d'inférence Physique / JAX (GPU)** : Exécute la simulation JAX compilée (JIT) et entraîne le surrogate model de type Fourier Neural Operator (FNO).
|
|
12
|
+
- *Instance recommandée :* `Standard_NCads_A100_v4` (qui dispose d'un GPU Nvidia A100 80GB) pour supporter de grandes dimensions spatiales.
|
|
13
|
+
3. **Le Cerveau IA (Le LLM Mistral)** : Assure la compréhension mathématique et la traduction.
|
|
14
|
+
- *Service par défaut :* **La Plateforme Mistral** (`api.mistral.ai`) — endpoint souverain opéré en Europe par Mistral AI, sans intermédiaire hyperscaler.
|
|
15
|
+
- *Alternative on-prem :* vLLM/TGI hébergé sur la VM GPU ci-dessus, exposant la même API OpenAI-compatible.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## 2. Accès à Mistral : endpoint souverain
|
|
20
|
+
|
|
21
|
+
L'agent n'utilise pas Azure AI Studio pour le LLM. La connexion se fait **directement** vers un endpoint Mistral OpenAI-compatible :
|
|
22
|
+
|
|
23
|
+
1. Créer une clef sur [console.mistral.ai](https://console.mistral.ai/) → *API Keys*.
|
|
24
|
+
2. Renseigner `.env` :
|
|
25
|
+
|
|
26
|
+
```env
|
|
27
|
+
MISTRAL_ENDPOINT="https://api.mistral.ai/v1"
|
|
28
|
+
MISTRAL_API_KEY="<clef_mistral>"
|
|
29
|
+
MISTRAL_MODEL="mistral-large-latest"
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Pour une souveraineté complète (données + poids du modèle hors cloud public), pointer `MISTRAL_ENDPOINT` vers un serveur vLLM/TGI auto-hébergé sur la VM GPU listée au §1. Voir la section "Connecter un endpoint Mistral" du `README.md` pour la commande `vllm serve`.
|
|
33
|
+
|
|
34
|
+
> Les services restants documentés ici (orchestrateur D8s, inférence A100, ACR, CI/CD) concernent uniquement l'infrastructure de **compute** — le LLM, lui, ne dépend plus d'Azure.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## 3. Provisionnement et CI/CD (azure-pipelines.yml)
|
|
39
|
+
|
|
40
|
+
### Différence fondamentale entre CI/CD et Provisionnement (Infrastructure as Code)
|
|
41
|
+
**`azure-pipelines.yml`** ne sert PAS à instancier/louer la carte graphique ou le serveur en soi.
|
|
42
|
+
- Son rôle est purement logiciel (CI/CD) : À chaque "Push" sur votre dépôt Git, `azure-pipelines.yml` va demander à Azure de lancer des tests unitaires, vérifier la syntaxe Python, compiler le code, et éventuellement créer une image Docker.
|
|
43
|
+
|
|
44
|
+
**Pour *Provisionner* (allumer les machines `NC-A100` et `D8s`), on utilise de l'Infrastructure-as-Code (IaC) :**
|
|
45
|
+
- L'idéal est de créer un fichier **Bicep** ou **Terraform** (`main.tf`).
|
|
46
|
+
- Ce fichier déclare formellement "TotalEnergies désire X machines virtuelles et Y bases de données dans la région France-Central".
|
|
47
|
+
- Il est possible d'automatiser le déploiement de cette infrastructure via `azure-pipelines.yml`, mais c'est bien le langage *Terraform* ou le portail *Azure Resource Manager (ARM)* qui loue le matériel.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## 4. Workflow de Déploiement Recommandé pour le PoC
|
|
52
|
+
|
|
53
|
+
1. **Validation Locale** : Testez le script sur un ordinateur local/VSCode pour vous assurer de la syntaxe JAX (ce que nous avons fait).
|
|
54
|
+
2. **Setup Azure AI** : Allez sur le portail Azure AI, déployez Mistral et récupérez les clés pour votre fichier `.env`.
|
|
55
|
+
3. **Déploiement Docker** : Poussez votre code dans un Docker Registry Azure (`ACR - Azure Container Registry`).
|
|
56
|
+
4. **Execution GPU** : Démarrez une instance `NC` (A100), connectez-vous y en SSH, tirez l'image Docker, et lancez la boucle FWI Surrogate.
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
Bootstrap: docker
|
|
2
|
+
From: python:3.12-slim
|
|
3
|
+
|
|
4
|
+
# Lightweight Apptainer image for the analyze-only mode.
|
|
5
|
+
# - No NVIDIA HPC SDK (CPU-only, no GPU drivers required on the host)
|
|
6
|
+
# - No LLM dependencies pulled in (`uv sync --no-dev` skips dev extras)
|
|
7
|
+
# - Pullable on any HPC login node (Pangea, GENCI, OVH, on-prem)
|
|
8
|
+
#
|
|
9
|
+
# Build:
|
|
10
|
+
# apptainer build coding-agent-analyze.sif Apptainer.analyze
|
|
11
|
+
#
|
|
12
|
+
# Use in a Slurm step:
|
|
13
|
+
# srun apptainer run coding-agent-analyze.sif \
|
|
14
|
+
# --format sarif --output report.sarif --fail-on error src/
|
|
15
|
+
|
|
16
|
+
%files
|
|
17
|
+
pyproject.toml /opt/coding-agent/pyproject.toml
|
|
18
|
+
uv.lock /opt/coding-agent/uv.lock
|
|
19
|
+
README.md /opt/coding-agent/README.md
|
|
20
|
+
local_code_agent /opt/coding-agent/local_code_agent
|
|
21
|
+
|
|
22
|
+
%post
|
|
23
|
+
set -euo pipefail
|
|
24
|
+
export DEBIAN_FRONTEND=noninteractive
|
|
25
|
+
|
|
26
|
+
apt-get update
|
|
27
|
+
apt-get install -y --no-install-recommends \
|
|
28
|
+
git \
|
|
29
|
+
ca-certificates \
|
|
30
|
+
curl \
|
|
31
|
+
build-essential \
|
|
32
|
+
gfortran
|
|
33
|
+
rm -rf /var/lib/apt/lists/*
|
|
34
|
+
|
|
35
|
+
# uv — same tool used in the dev workflow, no surprises for contributors
|
|
36
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
37
|
+
install -m 0755 /root/.local/bin/uv /usr/local/bin/uv
|
|
38
|
+
|
|
39
|
+
# Project install — skip dev extras (no jax / langchain / fastmcp on this image)
|
|
40
|
+
cd /opt/coding-agent
|
|
41
|
+
uv sync --no-dev
|
|
42
|
+
|
|
43
|
+
# Smoke-test the entry point at build time so a broken image fails fast
|
|
44
|
+
uv run agent-analyze --help > /dev/null
|
|
45
|
+
|
|
46
|
+
%environment
|
|
47
|
+
export PYTHONUNBUFFERED=1
|
|
48
|
+
export PATH=/opt/coding-agent/.venv/bin:$PATH
|
|
49
|
+
|
|
50
|
+
%runscript
|
|
51
|
+
# `apptainer run ... <args>` → forwards args straight to agent-analyze.
|
|
52
|
+
cd /opt/coding-agent
|
|
53
|
+
exec uv run agent-analyze "$@"
|
|
54
|
+
|
|
55
|
+
%labels
|
|
56
|
+
Author "Loïc Maurin <maurin.loic.ac@gmail.com>"
|
|
57
|
+
Description "coding-agent — Fortran static analyzer (Loki, no LLM, no GPU)"
|
|
58
|
+
Mode "analyze-only"
|
|
59
|
+
License "Apache-2.0"
|
|
60
|
+
|
|
61
|
+
%help
|
|
62
|
+
Static analyzer for legacy Fortran 90 — detects COMMON blocks, SAVE,
|
|
63
|
+
I/O in kernels, missing IMPLICIT NONE, POINTER/derived-type patterns,
|
|
64
|
+
suspected loop-carried dependencies. No LLM call, no file rewrite.
|
|
65
|
+
|
|
66
|
+
Outputs SARIF (CI-uploadable), JSON, or human-readable text.
|
|
67
|
+
|
|
68
|
+
Examples
|
|
69
|
+
--------
|
|
70
|
+
Scan a single file:
|
|
71
|
+
apptainer run coding-agent-analyze.sif src/kernel.f90
|
|
72
|
+
|
|
73
|
+
Emit SARIF for a Jenkins / GitLab CI step:
|
|
74
|
+
apptainer run coding-agent-analyze.sif \\
|
|
75
|
+
--format sarif --output report.sarif --fail-on error src/
|
|
76
|
+
|
|
77
|
+
Run inside a Slurm job:
|
|
78
|
+
srun --cpus-per-task=2 apptainer run coding-agent-analyze.sif src/
|