AI inference and per-node enrichment
The CLI can run an optional post-scan AI pipeline after structural detection. It proposes metadata patches for detected components and data flows, merges accepted proposals into the scan result, and records a summary in dataflow.json metadata.
The ai-per-node workstream focuses on third-party nodes: a bounded tool loop (tpAgent) reviews repository files, calls the configured model provider, and fills engineering/privacy/security fields. It can also emit a third-party data-flow insights block describing what categories of data appear to move to or from each vendor.
Structural scanning (Scan patterns) always runs first. AI inference is additive and can be disabled for reproducible Terraform-only output.
Mental model
End-to-end, a scan builds components and flows first, then optionally enriches them. Third-party data-flow insights are a separate metadata pass that runs only when AI ran and the data-flow flag is enabled.
Scan
├── Ingest + analyze + classify + data-flow detection
│ └── Detect components (classifiers, topology rules, property patterns, …)
├── [if AI on] Enrich node properties (provider + heuristics, tpAgent tool loop)
│ └── Merge accepted proposals into components / flows
├── Deterministic fallbacks (topology, third-party subType, cross-section flow trim, …)
├── Terraform minimal-service reduction (when applicable)
├── Stable component id assignment (cmp_1 … cmp_n)
└── [if AI on + data-flow flag] metadata.aiInference.thirdPartyDataFlow (per third party)
├── Final third_party list (same ids as graph nodes)
├── Remap post-merge proposal/trace ids via stableComponentKey
├── Collect vendor-relevant files (detection evidence + agentic trace paths)
├── Pattern scan on those files (main “what data”)
│ ├── Built-in pattern rules (financial, health, identifiers, …)
│ ├── PII signals — cli/patterns/pii-signals.rules.yaml
│ └── Non-PII signals — cli/patterns/non-pii-signals.rules.yaml
├── Merge integration_method / api_type → capabilities (rest, auth, storage, …)
└── Fallback from AI proposal evidence text when pattern hits are thinHow to read this
- Property enrichment changes what ends up on third-party (and other) nodes in
graph— integration fields, vendor metadata, and similar node properties. thirdPartyDataFlowdoes not add diagram edges; it adds read-only insights undermetadata.aiInference(and, after import,data._cliDataFlowInsightson third-party nodes). It is built after fallbacks and stablecmp_*assignment so eachentries[].componentIdmatches the final graph node id. Post-merge AI proposal and agentic-trace ids are bridged withstableComponentKey, notserviceNamealone (avoids collisions such as Google vs Google AI in one section). Pattern YAML is the primary source for data categories; provider proposals mainly raise confidence and supply evidence when file patterns are sparse.- With AI off, you still get structural detection and deterministic topology/subType passes, but no
thirdPartyDataFlowblock.
Enable inference
AI inference is on by default (anonymous platform proxy, workspace platform AI, or BYOK). To run a structural-only scan, opt out with one of:
- Flag:
--no-ai-inference - Environment:
SCAN_AI_INFERENCE=false - Config at scan root:
"enableAiInference": false
You must also configure either:
- BYOK provider + model + key (
--byok-provider/--byok-modelor--ai-provider/--ai-model, withSCAN_BYOK_PROVIDER,SCAN_BYOK_MODEL,SCAN_BYOK_API_KEY) — no workspace quota API, or - Platform AI with
DATAPARADE_WORKSPACE_API_KEY— requires a successful quota preflight and draws from workspace scan quotas (scan slots + platform AI tokens), or - Anonymous platform AI (no workspace key) — IP-capped session; real token usage is stored on the job and debited when you claim the preview after signup.
A workspace API key with SCAN_AI_INFERENCE=false / --no-ai-inference does not run inference and does not use quota (structural heuristics only; upload still works).
Security defaults
.env/.env.*files are excluded from default ingest and never embedded in provider prompts (even iflanguagesincludesenv). Scanning a single.envfile path is also skipped.dataparade config [path]printsaiApiKeyas<redacted>when set (loadsdataparade.config.jsonfrom the project at[path], same asscan).- Provider HTTP calls use a 120s default timeout (
SCAN_AI_HTTP_TIMEOUT_MS). --ai-endpoint/SCAN_AI_ENDPOINTcan target any URL; only run inference on code you would share with that endpoint.- LangSmith tracing (when enabled) uploads summary-only metadata; failures do not fail the scan.
- Proposal evidence must use exact repo-relative paths present in the scan (no fuzzy suffix matching).
- Unknown
SCAN_BYOK_PROVIDER/--ai-providervalues log a warning and are ignored (the scan uses default provider settings). - Cloud presets (
openai,anthropic,gemini,openrouter) withoutSCAN_BYOK_API_KEYskip network calls and emit a scan warning. - Only
.envpatterns are excluded by default; exclude other credential files with--excludeif needed.
Example:
SCAN_AI_INFERENCE=true \
SCAN_BYOK_PROVIDER=openai \
SCAN_BYOK_MODEL=gpt-4o-mini \
SCAN_BYOK_API_KEY=sk-... \
npx @dataparade/cli scan path/to/project --ai-inferenceOpenRouter (same API key env var; use your OpenRouter key):
SCAN_AI_INFERENCE=true \
SCAN_BYOK_PROVIDER=openrouter \
SCAN_BYOK_MODEL=openai/gpt-4o-mini \
SCAN_BYOK_API_KEY=sk-or-... \
npx @dataparade/cli scan path/to/project --ai-inferenceSee Environment variables and Scan arguments for the full flag and env surface.
Provider presets and API families
--ai-provider / aiProvider picks a preset (openai, anthropic, gemini, openrouter, local, mock). Presets are data, not one TypeScript file per vendor. Each preset points at an API family — the HTTP shape the CLI uses:
| Preset | API family | Role |
|---|---|---|
openai | Chat Completions | OpenAI-style messages + JSON output |
openrouter | Chat Completions | OpenRouter gateway (OpenAI-compatible); model ids like openai/gpt-4o-mini |
anthropic | Messages | Anthropic system + messages |
gemini | generateContent | Gemini with responseMimeType: application/json |
local | Ollama generate | Local POST /api/generate |
mock | — | No HTTP; empty proposals |
Regardless of preset, model output is normalized through one pipeline into component/flow patches with evidence. Changing provider does not change merge rules or dataflow.json graph shape — only which HTTP API is called and which model name you pass (--ai-model / SCAN_BYOK_MODEL).
Default endpoints
When SCAN_AI_ENDPOINT / aiEndpoint is unset, each preset uses a built-in default:
| Preset | Default endpoint |
|---|---|
openai | https://api.openai.com/v1/chat/completions |
openrouter | https://openrouter.ai/api/v1/chat/completions |
anthropic | https://api.anthropic.com/v1/messages |
gemini | https://generativelanguage.googleapis.com/v1beta/models/<model>:generateContent |
local | http://localhost:11434/api/generate (Ollama) |
For Gemini, if you provide a base models URL in SCAN_AI_ENDPOINT (for example .../v1beta/models), the CLI appends /<model>:generateContent.
Example local override:
SCAN_AI_INFERENCE=true
SCAN_BYOK_PROVIDER=local
SCAN_BYOK_MODEL="llama3.1"
SCAN_AI_ENDPOINT="http://YOUR_HOST:11434/api/generate"Override the default URL for any preset with SCAN_AI_ENDPOINT / aiEndpoint (interpretation is family-specific).
Inference scope
aiInferenceScope (flag --ai-inference-scope, env SCAN_AI_INFERENCE_SCOPE) controls which candidates enter the AI queues:
| Value | Behavior |
|---|---|
default | Full enrichment: third-party nodes and other supported candidate types. |
third_party_only | Only third_party components are enriched; assets, actors, and flow-only work are skipped in the AI step. Useful to limit cost and latency when you only need vendor metadata. |
Third-party tool loop (tpAgent)
For each third-party candidate, the orchestrator runs a bounded loop:
- Seed files from detection evidence and topology rules.
- Search the repo with deterministic keyword passes (capped).
- Provider infer — model proposes property patches with evidence references.
- Expand imports — follow import graph within file/review limits.
- Finalize when rounds exhaust or the agent completes.
Tune the loop with config file fields or environment variables (no dedicated CLI flags today):
| Setting | Config key | Environment variable | Default |
|---|---|---|---|
| Max provider rounds per candidate | aiToolLoopMaxRounds | SCAN_AI_TOOL_LOOP_MAX_ROUNDS | 3 |
| Max distinct files in candidate context | aiToolLoopMaxFiles | SCAN_AI_TOOL_LOOP_MAX_FILES | 48 |
| Max seeded search terms per candidate | aiToolLoopMaxSearches | SCAN_AI_TOOL_LOOP_MAX_SEARCHES | 12 |
Related cost controls (also apply to third-party batches):
aiMaxModelCalls/SCAN_AI_MAX_CALLS— cap total provider HTTP calls per queue.aiBudgetTokens/SCAN_AI_BUDGET_TOKENS— Working token budget for the inference planner (default 12000). Platform/anonymoussuggestedAiBudgetTokensis a server ceiling (anon default 100000, workspace up to remaining quota); the CLI takes the min of your working budget and that ceiling so a large cap cannot force a ~100k spend.aiProviderConcurrency/SCAN_AI_PROVIDER_CONCURRENCY— max in-flight provider requests fortpAgent(wave batching).aiMaxCandidatesPerAgent/SCAN_AI_MAX_CANDIDATES_PER_AGENT— per-agent queue cap (0= unlimited).
Example config fragment:
{
"enableAiInference": true,
"aiInferenceScope": "third_party_only",
"aiProvider": "openai",
"aiModel": "gpt-4o-mini",
"aiMaxModelCalls": 12,
"aiProviderConcurrency": 3,
"aiToolLoopMaxRounds": 2,
"aiToolLoopMaxFiles": 32,
"aiToolLoopMaxSearches": 8
}With --ai-verbose (or "aiVerbose": true in config), the CLI prints per-proposal apply/reject lines and token usage after the summary line.
Third-party data-flow insights
When AI inference runs and the data-flow flag is on, the CLI attaches metadata.aiInference.thirdPartyDataFlow: one entry per third-party component with direction, data categories, confidence band, capabilities, and file/line evidence (no raw source snippets in the written JSON). See Mental model for how pattern scan, capability merge, and evidence fallback fit together.
Per vendor, the builder:
- Gathers vendor-relevant files from structural evidence and tool-loop paths.
- Runs pattern scan (built-in rules plus PII / non-PII YAML) — primary source for
dataSharedcategories. - Adds capabilities from enriched
integration_method/api_typeon the component (e.g.rest,auth). - If patterns found little, infers categories from applied AI proposal evidence reason text.
When a single file is used by two or more AI providers (e.g. one API route calling OpenAI and Google), prompt/document assembly signals on that file are attributed to each AI vendor in the file—not only the vendor whose name appears on the same line. Evidence for that path is tagged (shared_ai_handler); entries may include the note shared_ai_handler_prompt_attribution. Non-AI third parties in the same file are unchanged.
Enable or disable the metadata block (requires AI to be on; default is off until you opt in):
- Config:
"aiThirdPartyDataFlowEnabled": true - Environment:
SCAN_AI_THIRD_PARTY_DATA_FLOW=true
When set to false, other AI enrichment still runs but thirdPartyDataFlow is omitted from metadata.
Entry shape (summary)
Each entries[] item includes:
componentId,componentName, optionalservicedirection:outbound_to_third_party,inbound_from_third_party,bidirectional, orunknowndataShared[]:{ category, labels[] }(categories such ascredentials,identifiers,profile_data,telemetry, …)confidence(0–1),confidenceBand(high|medium|low)source:heuristic,provider, orprovider_plus_heuristicevidence[]:{ filePath, startLine, endLine, reason }- optional
notes[](machine codes interpreted in the app UI, e.g.shared_ai_handler_prompt_attribution)
Totals: thirdPartiesAnalyzed, withDataShared.
The authoritative schema is aiInferenceSummarySchema in cli/src/core/schema/scan-result.schema.ts.
Agentic trace
When the tool loop runs, metadata.aiInference.agenticTrace lists per-candidate runs: candidate id, provider/model, round count, and toolCalls (tool name, arguments summary, outcome). The CLI import preview summary in the app shows run and tool-call counts when this block is present.
Console summary
With inference enabled, expect progress Running AI inference pipeline... and a line similar to:
[scan] ai-inference summary: candidates=… proposals=… applied=… rejected=… (provider/model)Heuristic vs provider proposal counts and token totals are included in metadata; verbose mode adds per-proposal detail on stderr.
Debugging toggles
OpenAI response-shape and debug logging (optional):
SCAN_AI_DEBUG— verbose provider loggingDATAPARADE_AI_OPENAI_JSON_SCHEMA— OpenAI JSON schema modeDATAPARADE_AI_OPENAI_JSON_SCHEMA_STRICT— strict JSON schema validation
Deterministic fallbacks (no AI)
Even without --ai-inference, the scan applies deterministic enrichment after the structural phases:
provider-topology.rules.yaml— managed service nodes and edges for cloud/SDK providers (Terraform resource types and SDK-backed vendors).- Third-party subtype heuristics — fill
subTypewhen the classifier left it empty.
PII / non-PII YAML is loaded during thirdPartyDataFlow construction (AI on + data-flow flag), not as a separate export path when AI is disabled.
For stable Terraform-only diagrams in CI, set SCAN_AI_INFERENCE=false so optional AI merge does not reshape components.
Privacy and output safety
- AI uses snippets and paths from scanned files; raw
sourceLocation.codeis stripped fromgraphnodes and edges indataflow.json(paths and line ranges remain). - Insights in metadata use the same evidence discipline (file + line range, no full file bodies in the export).
In the web app
After import, third-party nodes can carry _cliDataFlowInsights on node data (copied from metadata.aiInference.thirdPartyDataFlow during preview mapping). In Preview & Edit and on the diagram, open a third-party node and use the Data tab to review direction, categories, and evidence. See Creation and Imports for the import flow.
For the wrapper file layout and metadata fields, see Output and results.