ForTriage is structured as a layered Streamlit application. The architecture separates data acquisition (uploads), analysis (Forti4D pipeline), visualization (views), and AI summarization (Gemini adapter) into distinct modules with clear boundaries. No layer reaches across another’s boundary: views never invoke Forti4D, and the AI adapter never reads DataFrames directly.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/acdeveloper-sci/fortriage/llms.txt
Use this file to discover all available pages before exploring further.
Module Layout
config.py is the only file allowed to define constants. gemini_client.py is the only file allowed to import google.genai. pipeline.py is the only file allowed to invoke forti4d.
Data Flow
The end-to-end flow from file upload to AI-generated insights follows these steps:File upload and staging
The user uploads Fortran source files via the Overview tab.
uploads.py’s merge_upload_sources() normalizes direct file uploads and ZIP archives into a deduplicated list of StagedFile objects, enforcing extension allowlists and per-file size limits.Pipeline invocation
On “Run analysis”,
pipeline.py’s run_pipeline() copies staged files to a temporary directory (fortriage_src_*) and invokes Forti4D as a subprocess, pointing it at a second temp directory (fortriage_out_*) for outputs.Output loading and cleanup
All six CSV and Markdown outputs are read into a
PipelineResult dataclass while both temp directories are still live. After loading, the temp directories are deleted. PipelineResult holds everything in memory — no paths escape the dataclass.Session state storage
PipelineResult is stored in st.session_state.pipeline_result. From this point, all views and AI modules read exclusively from session state — no further disk or subprocess access occurs.Dashboard views
The Overview, Drill-down, Executive Summary, and Report Explorer views read their data from
st.session_state.pipeline_result fields (prioritization, consolidated, migration_strategy, common_coupling, clones, project_summary).AI Insights context aggregation
insights_context.build_insights_context() aggregates PipelineResult into a bounded, LLM-safe context dict — scalars, small dicts of counts, and capped lists of top-N items. Raw DataFrames are never sent to the model.Gemini API call
gemini_client.generate_insights() sends the context dict to Gemini and returns structured JSON conforming to a response schema. The response is parsed and validated by insights_context.parse_insights_response().Forti4D Boundary
ForTriage is a consumer of Forti4D — it never modifies the analysis engine, its configuration, or its outputs. The integration boundary is preciselypipeline.py. run_pipeline() is the only place in the entire codebase where forti4d is invoked. This constraint is intentional: it makes the backing implementation fully swappable without touching any downstream code. For example, the subprocess call could be replaced with an HTTP call to a cloud Lambda function, a Docker container, or a mock for testing — and no view, component, or AI module would require any change.
Forti4D is installed as a Python package from its GitHub repository:
ForTriage intentionally does not expose Forti4D’s per-file diagnostic dumps (
audit/, blocks/). Only the six curated, corpus-level reports are surfaced: report_prioritization.csv, report_consolidated.csv, report_migration_strategy.csv, common_coupling.csv, report_clones.csv, and PROJECT_SUMMARY.md.Session State
state.py is the single source of truth for all st.session_state keys used by ForTriage. No module is permitted to write directly to session_state with ad-hoc keys.
| Function | Description |
|---|---|
init_session_state() | Sets defaults for all session state keys. Idempotent — called on every Streamlit rerun. Only populates keys that don’t already exist, so live values are never overwritten on rerun. |
reset_session_state() | Overwrites all keys to their defaults. Triggered by the “New Analysis” button to return the app to its initial state. |
| Key | Type | Description |
|---|---|---|
pipeline_result | PipelineResult | None | The result of the most recent analysis run, or None before any analysis. |
analysis_in_progress | bool | True while the background pipeline thread is running. Controls the spinner and disables the run button. |
current_view | str | Active tab/view name for navigation state. |
selected_unit | str | None | Unit name currently open in the Drill-down modal, if any. |
overview_filters | dict | Active filter state for the Overview table — keys: search (text query), type (unit type filter), tier (tier filter). |
excluded_filenames | set[str] | Filenames removed from staging via the chip removal control. Cleared when the ZIP identity changes. |
report_explorer_tab | str | Active tab in the Report Explorer view (default: "migration_strategy"). |
last_run_completed_at | datetime | None | Timestamp of the most recently completed analysis run, or None. |
run_id | str | None | Unique identifier for the current analysis run, used to key AI Insights results. |
modal_unit | str | None | Unit name for the currently open Drill-down modal (distinct from selected_unit for handoff coordination). |
overview_table_reset_token | int | Incremented to signal the Overview table to reset scroll and selection state. |
uploader_reset_token | int | Incremented to force a re-mount of the file uploader widget, clearing staged files. |
last_zip_identity | tuple[str, int] | None | (name, size) identity of the last uploaded ZIP, used to detect when the ZIP changes and clear excluded_filenames. |
sample_fixtures_loaded | bool | True after the “Load sample fixtures” button is clicked; cleared on reset. |
ai_insights_result | InsightsSections | None | Parsed AI Insights response for the current analysis, or None if not yet generated. |
ai_insights_kpis | dict | None | Pre-computed KPI dict for the current AI Insights run, or None. |
ai_insights_error | str | None | Error message from a failed AI Insights generation, or None on success. Exactly one of ai_insights_result / ai_insights_error is non-None after generation completes. |
chat_context | dict | None | Pre-built chat context dict (from build_chat_context()) for the current run, or None. |
chat_history | list[dict] | Ordered list of {"role": ..., "text": ...} dicts for the current chat session. |
AI Insights Adapter Pattern
gemini_client.py is the only module in the project that imports google.genai. All other modules — views/ai_insights.py, insights_context.py, state.py — depend only on the two public function signatures and the two public types this module exposes:
| Symbol | Kind | Description |
|---|---|---|
generate_insights(context, config) | Function | Sends the pre-aggregated context dict to Gemini and returns structured JSON text. |
send_chat_message(context, history, user_message, config) | Function | Sends a follow-up chat turn and returns the model’s free-text reply. |
LLMConfig | Dataclass | Provider credentials: api_key, model, timeout_seconds. Built by the caller from st.secrets — gemini_client.py never reads secrets itself. |
LLMError | Exception | Raised for any SDK-level failure: auth errors, rate limits, timeouts, transport failures, or empty responses. All SDK exceptions are normalized to this single type. |
gemini_client.py alone — the views, context aggregator, and session state require no changes.
LLMConfig is deliberately built by the Streamlit view layer (which has access to st.secrets) and passed into gemini_client.py — rather than having gemini_client.py read secrets directly. This keeps the adapter free of Streamlit imports and makes it testable without a Streamlit runtime.