Skip to content

Python API

extract and info are imported from the package root. annotate is also available there, and the import stays lazy: LangChain, LangGraph, and torch load only when annotation is enabled.

from figma_extractor import extract, info, annotate, __version__
from figma_extractor.llm.config import LlmConfig, LlmTasks

figma_extractor.llm exports annotate and LlmConfig. Importing that package does not import a provider SDK.

extract

Keyword-only. Pass exactly one of file or remote.

extract(file="./design.fig", output="./out")
extract(file="./design.fig", output="./out", clean=False, keep_intermediates=True)
extract(file="./design.fig", output="./out", write_toon=True)

extract(remote="ABC123", output="./out", api_key="figd_xxx")
extract(
    remote="https://www.figma.com/design/ABC123/My-Kit",
    output="./out",
    api_key="figd_xxx",
)
Argument Default Meaning
file None Local .fig path
remote None File key or Figma URL
output required Destination directory. Created if missing
api_key None Required when remote is set. The function does not read FIGMA_API_KEY
keep_intermediates False Keep source/ and extracted/
clean True Remove previous deliverables under output first
write_toon False Also write catalog/, toon/, and related transport files

The return value is a dict:

Key Meaning
source {"type": "local"\|"remote", "value": path or file key}
output Resolved output directory
design Same path as output (older name)
decode Local canvas summary, or remote node and image counts
tokens Token summary from build_tokens
structure Counts including pages, screens, components
images Image export summary
trees Tree summary (trees, and split.screensAfter when a board was split)
vectors Count of shared vector assets written
flow UI-flow summary
intermediatesKept The keep_intermediates flag you passed
transport Paths written by publish_transport when write_toon=True, else None
schemaVersion 2 for current extracts

Inside extract, the stages run in this order and can also be called on a directory that already has extracted/nodes.ndjson:

from figma_extractor.extract import (
    build_tokens,
    build_structure,
    build_images,
    build_screen_trees,
    build_ui_flow,
)
from figma_extractor.extract.vectors import publish_vector_assets

Call extract() for a full run. The stage functions expect the decode cache and write the same files extract writes. Vector assets and schema markers run after trees and before (or with) the UI-flow pass.

info

details = info("./out")
details = info()  # current working directory
print(details["summary"])
for screen in details["screens"]:
    print(screen["name"], screen.get("width"), screen.get("height"))

directory may be omitted. The resolver accepts the output root or a legacy design/ subfolder when pages.json or screens.json is there. Otherwise it raises FileNotFoundError.

Key Contents
directory Resolved extract path
summary Counts listed below
pages pages.json
screens screens.json
components components.json
componentSets component-sets.json
tokens variables, typography, effects
assets assets/manifest.json
text text-content.json
uiFlow ui-flow.json, or None when that file is absent
llm Compact annotation status from document/llm-summary.json or llm-annotations.json, or None

summary keys: pages, screens, trees, components, componentSets, variables, textStyles, effects, assets, uniqueTextStrings, suggestedRoutes. When annotations exist, llmEnabled and llmTasks are added.

figma_extractor.api.resolve_output_dir is the same directory check. resolve_design_dir is an alias.

annotate

from figma_extractor import annotate
from figma_extractor.llm.config import LlmConfig, LlmTasks

# Reads LLM_* from the environment when config is omitted.
result = annotate("./out")

# Deterministic. Does not import a provider SDK.
result = annotate("./out", LlmConfig(enabled=False))

# Calls the selected provider. Requires that extra to be installed.
result = annotate(
    "./out",
    LlmConfig(
        enabled=True,
        provider="ollama",
        model="llama3.2",
        tasks=LlmTasks(screen_classification=True),
    ),
)

# Build the object and skip the file write.
payload = annotate("./out", LlmConfig(enabled=False), write=False)
Argument Default Meaning
directory required Extract root. Must contain screens.json
config None LlmConfig. Omitted means LlmConfig.from_env()
write True Write llm-annotations.json

A disabled result has llmEnabled: false, empty llmResults, and screen rows copied from the extract. An enabled result is produced by the LangGraph workflow (prepare, optimize_toon, build_prompt, invoke, validate, repair, finalize). Validated task items are merged onto screens, nodes, and assets under an llm key without changing screens.json. Incremental runs merge new results with the previous llm-annotations.json and keep overlays for screens whose tree hash did not change.

LlmConfig

from figma_extractor.llm.config import LlmConfig, LlmTasks

config = LlmConfig.load(path="./llm.json", overrides={"enabled": True, "provider": "ollama"})
config = LlmConfig.from_env()
config.resolved_model()  # explicit model, or the provider default
config.check()           # raises LlmConfigError when an enabled config cannot run

Load order, later wins: built-in defaults, JSON file, environment, then overrides. None values in overrides are ignored. Keys named api_key, token, secret, password, authorization, or credentials are rejected in the file, in provider_options, and in overrides.

Field Default Meaning
enabled False Call a model
provider "openai" Provider id
model None Falls back to that provider's default model
temperature 0.0 0 to 2
max_tokens None Response token cap
timeout 60.0 Seconds, must be > 0
max_retries 2 Transport retries in the shared policy
streaming False Requires a provider with streaming
max_context_chars 12000 Context cap, at least 500
max_context_tokens None Optional token cap for packed TOON context
cache_results True Disk cache under .cache/llm
write_artifacts True Write prompts/ and synthesis proposals
toon_compact False Short TOON keys in prompts
max_concurrency 1 Recorded on the policy. Tasks still run sequentially
failure_limit 4 Consecutive failures before the run stops calling the provider
validation_attempts 2 One initial attempt plus one repair by default
backoff_seconds 0.5 Delay between retries
tasks all false LlmTasks
provider_options {} String map. See the table below

Enabled mode with no selected task raises LlmConfigError before LangGraph is imported.

LlmTasks fields match the task names. enabled_names() returns the names that are true. from_mapping rejects unknown names. Boolean strings accepted by the loader are 1, true, yes, on and 0, false, no, off.

JSON may nest settings under an llm object. That object is merged over the top-level keys. options is accepted as an alias of provider_options.

LlmConfig(
    enabled=True,
    provider="huggingface",
    tasks=LlmTasks(screen_classification=True),
    provider_options={
        "backend": "local",
        "model_path": "/models/Qwen3-0.6B",
        "quantization": "none",
    },
)

LlmConfig(
    enabled=True,
    provider="vertex",
    tasks=LlmTasks(semantic_classification=True),
    provider_options={"location": "us-central1"},
)
Provider Allowed provider_options Also read from the environment
ollama base_url OLLAMA_BASE_URL
huggingface see Hugging Face local. Default backend is local HF_TOKEN or HUGGINGFACEHUB_API_TOKEN only when HF_BACKEND=remote
vertex location GOOGLE_CLOUD_LOCATION, default us-central1
anthropic-vertex location GOOGLE_CLOUD_LOCATION, default us-east5
every other provider none credential variables only

Unknown option keys raise ProviderCapabilityError when a session is built.

Devices

from figma_extractor.llm.device import detect_device

status = detect_device()
status.kind       # cpu, cuda, rocm, mps, xpu, or unavailable
status.available
status.detail
status.torch_version
status.summary()  # same line the devices command prints

Importing detect_device does not import torch. The import happens inside the function.