Skip to content

Layout gpt

LayoutGPT Pydantic AI agent package.

BoxFormat

Bases: StrEnum

Supported bounding-box coordinate formats.

Source code in lib/laygen/src/laygen/common/bbox.py
21
22
23
24
25
26
class BoxFormat(StrEnum):
    """Supported bounding-box coordinate formats."""

    xywh = auto()
    ltwh = auto()
    ltrb = auto()

ConditionType

Bases: StrEnum

Canonical condition names used by layout generation interfaces.

Source code in lib/laygen/src/laygen/common/conditions.py
 9
10
11
12
13
14
15
16
17
18
19
20
21
class ConditionType(StrEnum):
    """Canonical condition names used by layout generation interfaces."""

    unconditional = auto()
    label = auto()
    label_size = auto()
    completion = auto()
    refinement = auto()
    text = auto()
    content_image = auto()
    relation = auto()
    hierarchical = auto()
    retrieval = auto()

LayoutGPTAgent

Bases: BaseLayoutAgent[RawLayoutResponse]

High-level LayoutGPT runner that ports released prompt and parse strategy.

Source code in models/layout-gpt/src/layout_gpt/agent.py
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
class LayoutGPTAgent(BaseLayoutAgent[RawLayoutResponse]):
    """High-level LayoutGPT runner that ports released prompt and parse strategy."""

    def __init__(
        self,
        *,
        model: ModelLike = None,
        config: LayoutGPTConfig,
        token_counter: TokenCounter = default_token_counter,
    ) -> None:
        """Initialize the runner with provider and prompt configuration."""
        super().__init__(
            model=model,
            model_env_var=DEFAULT_MODEL_ENV_VAR,
            raw_response_type=RawLayoutResponse,
            instructions=INSTRUCTIONS,
        )
        self.config = config
        self.token_counter = token_counter

    def build_prompt(
        self,
        prompt: str,
        *,
        train_examples: Sequence[LayoutExample],
        seed: int | None = None,
        generator: torch.Generator | None = None,
        query_embedding: EmbeddingProvider | None = None,
        example_embeddings: Sequence[Sequence[float]] | None = None,
    ) -> tuple[str | list[ChatMessage], list[LayoutExample]]:
        """Select exemplars and serialize the prompt sent to the model."""
        exemplars = self._select_examples(
            prompt,
            train_examples=train_examples,
            seed=seed,
            generator=generator,
            query_embedding=query_embedding,
            example_embeddings=example_embeddings,
        )
        if self.config.chat:
            return (
                form_prompt_for_chatgpt(
                    prompt,
                    exemplars=exemplars,
                    canvas_size=self.config.canvas_size,
                    token_counter=self.token_counter,
                    input_length_limit=self.config.gpt_input_length_limit,
                ),
                exemplars,
            )
        return (
            form_prompt_for_gpt3(
                prompt,
                exemplars=exemplars,
                canvas_size=self.config.canvas_size,
                token_counter=self.token_counter,
                input_length_limit=self.config.gpt_input_length_limit,
            ),
            exemplars,
        )

    def run_sync(
        self,
        prompt: str,
        *,
        train_examples: Sequence[LayoutExample],
        model: ModelLike = None,
        seed: int | None = None,
        generator: torch.Generator | None = None,
        query_embedding: EmbeddingProvider | None = None,
        example_embeddings: Sequence[Sequence[float]] | None = None,
        model_settings: ModelSettings | None = None,
    ) -> LayoutGPTOutput:
        """Run LayoutGPT and parse the response into typed layout output."""
        model_prompt, exemplars = self.build_prompt(
            prompt,
            train_examples=train_examples,
            seed=seed,
            generator=generator,
            query_embedding=query_embedding,
            example_embeddings=example_embeddings,
        )
        raw = self.run_raw_sync(
            model_prompt,
            model=model,
            model_settings=model_settings
            or ModelSettings(
                temperature=self.config.temperature, top_p=self.config.top_p
            ),
        )
        response_text = self.repair_response_text(raw.text)
        items = parse_layout_text(response_text, canvas_size=self.config.canvas_size)
        id2label = {
            index: label
            for index, label in enumerate(dict.fromkeys(item.label for item in items))
        }
        return LayoutGPTOutput(
            prompt=prompt,
            canvas_size=self.config.canvas_size,
            items=items,
            raw_text=response_text,
            id2label=id2label,
            selected_exemplar_ids=[example.id for example in exemplars],
            prompt_messages=model_prompt if isinstance(model_prompt, list) else None,
        )

    def __call__(
        self,
        *,
        prompt: str,
        train_examples: Sequence[LayoutExample],
        batch_size: int = 1,
        seed: int | None = None,
        generator: torch.Generator | None = None,
        condition_type: str | ConditionType = ConditionType.text,
        labels: Int[torch.Tensor, "batch elements"]
        | list[ArrayLikeInput]
        | None = None,
        bbox: Float[torch.Tensor, "batch elements 4"]
        | list[ArrayLikeInput]
        | None = None,
        mask: Bool[torch.Tensor, "batch elements"] | list[ArrayLikeInput] | None = None,
        num_elements: int | list[int] | Int[torch.Tensor, "batch"] | None = None,
        box_format: str | BoxFormat = BoxFormat.xywh,
        normalized: bool = True,
        canvas_size: tuple[int, int] | None = None,
        num_inference_steps: int | None = None,
        output_type: str | OutputType = OutputType.dataclass,
        return_intermediates: bool = False,
        model: ModelLike = None,
        query_embedding: EmbeddingProvider | None = None,
        example_embeddings: Sequence[Sequence[float]] | None = None,
        model_settings: ModelSettings | None = None,
    ) -> LayoutGenerationOutput | LayoutGPTOutputDict:
        """Generate a layout through the common public generation surface."""
        del labels, bbox, mask, num_elements, normalized, num_inference_steps
        normalized_condition_type, normalized_box_format = (
            self.validate_generation_request(
                batch_size=batch_size,
                condition_type=condition_type,
                box_format=box_format,
                canvas_size=canvas_size,
                configured_canvas_size=self.config.canvas_size,
                supported_condition_types=SUPPORTED_CONDITION_TYPES,
            )
        )
        normalized_output_type = coerce_enum(output_type, OutputType)
        del normalized_box_format
        output = self.run_sync(
            prompt,
            train_examples=train_examples,
            model=model,
            seed=seed,
            generator=generator,
            query_embedding=query_embedding,
            example_embeddings=example_embeddings,
            model_settings=model_settings,
        ).to_layout_generation_output()
        if not return_intermediates:
            output.intermediates = None
        if normalized_output_type is OutputType.dict:
            return cast(LayoutGPTOutputDict, self.output_to_dict(output))
        if normalized_output_type is OutputType.dataclass:
            return output
        assert_never(normalized_output_type)

    generate = __call__

    def save_pretrained(self, save_directory: str | os.PathLike[str]) -> None:
        """Persist prompt and parser configuration without provider state."""
        path = Path(save_directory)
        path.mkdir(parents=True, exist_ok=True)
        (path / "layout_gpt_config.json").write_text(
            json.dumps(self.config.model_dump(mode="json"), indent=2, sort_keys=True)
            + "\n",
            encoding="utf-8",
        )

    @classmethod
    def from_pretrained(
        cls,
        pretrained_model_name_or_path: str | os.PathLike[str],
        *,
        model: ModelLike = None,
        token_counter: TokenCounter = default_token_counter,
    ) -> "LayoutGPTAgent":
        """Load saved LayoutGPT prompt and parser configuration."""
        path = Path(pretrained_model_name_or_path) / "layout_gpt_config.json"
        config_data = json.loads(path.read_text(encoding="utf-8"))
        return cls(
            model=model,
            config=LayoutGPTConfig(**config_data),
            token_counter=token_counter,
        )

    def _select_examples(
        self,
        prompt: str,
        *,
        train_examples: Sequence[LayoutExample],
        seed: int | None = None,
        generator: torch.Generator | None = None,
        query_embedding: EmbeddingProvider | None,
        example_embeddings: Sequence[Sequence[float]] | None,
    ) -> list[LayoutExample]:
        if self.config.icl_type is ICLType.fixed_random:
            selection_seed = (
                int(generator.initial_seed())
                if generator is not None
                else seed
                if seed is not None
                else self.config.fixed_random_seed
            )
            return select_fixed_random(
                train_examples, k=self.config.k, seed=selection_seed
            )
        if self.config.icl_type is ICLType.k_similar:
            if query_embedding is None or example_embeddings is None:
                msg = "k-similar selection requires query_embedding and example_embeddings"
                raise ValueError(msg)

            return select_k_similar(
                train_examples,
                query=prompt,
                k=self.config.k,
                query_embedding=query_embedding,
                example_embeddings=example_embeddings,
            )
        assert_never(self.config.icl_type)

__init__

__init__(
    *,
    model: ModelLike = None,
    config: LayoutGPTConfig,
    token_counter: TokenCounter = default_token_counter,
) -> None

Initialize the runner with provider and prompt configuration.

Source code in models/layout-gpt/src/layout_gpt/agent.py
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
def __init__(
    self,
    *,
    model: ModelLike = None,
    config: LayoutGPTConfig,
    token_counter: TokenCounter = default_token_counter,
) -> None:
    """Initialize the runner with provider and prompt configuration."""
    super().__init__(
        model=model,
        model_env_var=DEFAULT_MODEL_ENV_VAR,
        raw_response_type=RawLayoutResponse,
        instructions=INSTRUCTIONS,
    )
    self.config = config
    self.token_counter = token_counter

build_prompt

build_prompt(
    prompt: str,
    *,
    train_examples: Sequence[LayoutExample],
    seed: int | None = None,
    generator: Generator | None = None,
    query_embedding: EmbeddingProvider | None = None,
    example_embeddings: Sequence[Sequence[float]]
    | None = None,
) -> tuple[str | list[ChatMessage], list[LayoutExample]]

Select exemplars and serialize the prompt sent to the model.

Source code in models/layout-gpt/src/layout_gpt/agent.py
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
def build_prompt(
    self,
    prompt: str,
    *,
    train_examples: Sequence[LayoutExample],
    seed: int | None = None,
    generator: torch.Generator | None = None,
    query_embedding: EmbeddingProvider | None = None,
    example_embeddings: Sequence[Sequence[float]] | None = None,
) -> tuple[str | list[ChatMessage], list[LayoutExample]]:
    """Select exemplars and serialize the prompt sent to the model."""
    exemplars = self._select_examples(
        prompt,
        train_examples=train_examples,
        seed=seed,
        generator=generator,
        query_embedding=query_embedding,
        example_embeddings=example_embeddings,
    )
    if self.config.chat:
        return (
            form_prompt_for_chatgpt(
                prompt,
                exemplars=exemplars,
                canvas_size=self.config.canvas_size,
                token_counter=self.token_counter,
                input_length_limit=self.config.gpt_input_length_limit,
            ),
            exemplars,
        )
    return (
        form_prompt_for_gpt3(
            prompt,
            exemplars=exemplars,
            canvas_size=self.config.canvas_size,
            token_counter=self.token_counter,
            input_length_limit=self.config.gpt_input_length_limit,
        ),
        exemplars,
    )

run_sync

run_sync(
    prompt: str,
    *,
    train_examples: Sequence[LayoutExample],
    model: ModelLike = None,
    seed: int | None = None,
    generator: Generator | None = None,
    query_embedding: EmbeddingProvider | None = None,
    example_embeddings: Sequence[Sequence[float]]
    | None = None,
    model_settings: ModelSettings | None = None,
) -> LayoutGPTOutput

Run LayoutGPT and parse the response into typed layout output.

Source code in models/layout-gpt/src/layout_gpt/agent.py
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
def run_sync(
    self,
    prompt: str,
    *,
    train_examples: Sequence[LayoutExample],
    model: ModelLike = None,
    seed: int | None = None,
    generator: torch.Generator | None = None,
    query_embedding: EmbeddingProvider | None = None,
    example_embeddings: Sequence[Sequence[float]] | None = None,
    model_settings: ModelSettings | None = None,
) -> LayoutGPTOutput:
    """Run LayoutGPT and parse the response into typed layout output."""
    model_prompt, exemplars = self.build_prompt(
        prompt,
        train_examples=train_examples,
        seed=seed,
        generator=generator,
        query_embedding=query_embedding,
        example_embeddings=example_embeddings,
    )
    raw = self.run_raw_sync(
        model_prompt,
        model=model,
        model_settings=model_settings
        or ModelSettings(
            temperature=self.config.temperature, top_p=self.config.top_p
        ),
    )
    response_text = self.repair_response_text(raw.text)
    items = parse_layout_text(response_text, canvas_size=self.config.canvas_size)
    id2label = {
        index: label
        for index, label in enumerate(dict.fromkeys(item.label for item in items))
    }
    return LayoutGPTOutput(
        prompt=prompt,
        canvas_size=self.config.canvas_size,
        items=items,
        raw_text=response_text,
        id2label=id2label,
        selected_exemplar_ids=[example.id for example in exemplars],
        prompt_messages=model_prompt if isinstance(model_prompt, list) else None,
    )

__call__

__call__(
    *,
    prompt: str,
    train_examples: Sequence[LayoutExample],
    batch_size: int = 1,
    seed: int | None = None,
    generator: Generator | None = None,
    condition_type: str
    | ConditionType = ConditionType.text,
    labels: Int[Tensor, "batch elements"]
    | list[ArrayLikeInput]
    | None = None,
    bbox: Float[Tensor, "batch elements 4"]
    | list[ArrayLikeInput]
    | None = None,
    mask: Bool[Tensor, "batch elements"]
    | list[ArrayLikeInput]
    | None = None,
    num_elements: int
    | list[int]
    | Int[Tensor, "batch"]
    | None = None,
    box_format: str | BoxFormat = BoxFormat.xywh,
    normalized: bool = True,
    canvas_size: tuple[int, int] | None = None,
    num_inference_steps: int | None = None,
    output_type: str | OutputType = OutputType.dataclass,
    return_intermediates: bool = False,
    model: ModelLike = None,
    query_embedding: EmbeddingProvider | None = None,
    example_embeddings: Sequence[Sequence[float]]
    | None = None,
    model_settings: ModelSettings | None = None,
) -> LayoutGenerationOutput | LayoutGPTOutputDict

Generate a layout through the common public generation surface.

Source code in models/layout-gpt/src/layout_gpt/agent.py
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
def __call__(
    self,
    *,
    prompt: str,
    train_examples: Sequence[LayoutExample],
    batch_size: int = 1,
    seed: int | None = None,
    generator: torch.Generator | None = None,
    condition_type: str | ConditionType = ConditionType.text,
    labels: Int[torch.Tensor, "batch elements"]
    | list[ArrayLikeInput]
    | None = None,
    bbox: Float[torch.Tensor, "batch elements 4"]
    | list[ArrayLikeInput]
    | None = None,
    mask: Bool[torch.Tensor, "batch elements"] | list[ArrayLikeInput] | None = None,
    num_elements: int | list[int] | Int[torch.Tensor, "batch"] | None = None,
    box_format: str | BoxFormat = BoxFormat.xywh,
    normalized: bool = True,
    canvas_size: tuple[int, int] | None = None,
    num_inference_steps: int | None = None,
    output_type: str | OutputType = OutputType.dataclass,
    return_intermediates: bool = False,
    model: ModelLike = None,
    query_embedding: EmbeddingProvider | None = None,
    example_embeddings: Sequence[Sequence[float]] | None = None,
    model_settings: ModelSettings | None = None,
) -> LayoutGenerationOutput | LayoutGPTOutputDict:
    """Generate a layout through the common public generation surface."""
    del labels, bbox, mask, num_elements, normalized, num_inference_steps
    normalized_condition_type, normalized_box_format = (
        self.validate_generation_request(
            batch_size=batch_size,
            condition_type=condition_type,
            box_format=box_format,
            canvas_size=canvas_size,
            configured_canvas_size=self.config.canvas_size,
            supported_condition_types=SUPPORTED_CONDITION_TYPES,
        )
    )
    normalized_output_type = coerce_enum(output_type, OutputType)
    del normalized_box_format
    output = self.run_sync(
        prompt,
        train_examples=train_examples,
        model=model,
        seed=seed,
        generator=generator,
        query_embedding=query_embedding,
        example_embeddings=example_embeddings,
        model_settings=model_settings,
    ).to_layout_generation_output()
    if not return_intermediates:
        output.intermediates = None
    if normalized_output_type is OutputType.dict:
        return cast(LayoutGPTOutputDict, self.output_to_dict(output))
    if normalized_output_type is OutputType.dataclass:
        return output
    assert_never(normalized_output_type)

save_pretrained

save_pretrained(
    save_directory: str | PathLike[str],
) -> None

Persist prompt and parser configuration without provider state.

Source code in models/layout-gpt/src/layout_gpt/agent.py
227
228
229
230
231
232
233
234
235
def save_pretrained(self, save_directory: str | os.PathLike[str]) -> None:
    """Persist prompt and parser configuration without provider state."""
    path = Path(save_directory)
    path.mkdir(parents=True, exist_ok=True)
    (path / "layout_gpt_config.json").write_text(
        json.dumps(self.config.model_dump(mode="json"), indent=2, sort_keys=True)
        + "\n",
        encoding="utf-8",
    )

from_pretrained classmethod

from_pretrained(
    pretrained_model_name_or_path: str | PathLike[str],
    *,
    model: ModelLike = None,
    token_counter: TokenCounter = default_token_counter,
) -> "LayoutGPTAgent"

Load saved LayoutGPT prompt and parser configuration.

Source code in models/layout-gpt/src/layout_gpt/agent.py
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
@classmethod
def from_pretrained(
    cls,
    pretrained_model_name_or_path: str | os.PathLike[str],
    *,
    model: ModelLike = None,
    token_counter: TokenCounter = default_token_counter,
) -> "LayoutGPTAgent":
    """Load saved LayoutGPT prompt and parser configuration."""
    path = Path(pretrained_model_name_or_path) / "layout_gpt_config.json"
    config_data = json.loads(path.read_text(encoding="utf-8"))
    return cls(
        model=model,
        config=LayoutGPTConfig(**config_data),
        token_counter=token_counter,
    )

ICLType

Bases: StrEnum

Supported in-context exemplar selection modes.

Source code in models/layout-gpt/src/layout_gpt/enums.py
16
17
18
19
20
class ICLType(StrEnum):
    """Supported in-context exemplar selection modes."""

    fixed_random = "fixed-random"
    k_similar = "k-similar"

LayoutGPTSetting

Bases: StrEnum

Supported NSR-1K LayoutGPT settings.

Source code in models/layout-gpt/src/layout_gpt/enums.py
 9
10
11
12
13
class LayoutGPTSetting(StrEnum):
    """Supported NSR-1K LayoutGPT settings."""

    counting = auto()
    spatial = auto()

OutputType

Bases: StrEnum

Public return type names.

Source code in models/layout-gpt/src/layout_gpt/enums.py
23
24
25
26
27
class OutputType(StrEnum):
    """Public return type names."""

    dataclass = auto()
    dict = auto()

LayoutGPTOutput

Bases: BaseModel

Pydantic representation of a parsed LayoutGPT response.

Source code in models/layout-gpt/src/layout_gpt/schema.py
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
class LayoutGPTOutput(BaseModel):
    """Pydantic representation of a parsed LayoutGPT response."""

    prompt: str
    canvas_size: int
    items: list[LayoutItem2D]
    raw_text: str
    id2label: dict[int, str]
    selected_exemplar_ids: list[str | int] = Field(default_factory=list)
    prompt_messages: list[ChatMessage] | None = None

    model_config = ConfigDict(frozen=True)

    def to_layout_generation_output(self) -> LayoutGenerationOutput:
        """Convert to the shared public layout output schema."""
        intermediates: LayoutGPTIntermediates = {
            "prompt": self.prompt,
            "raw_text": self.raw_text,
            "selected_exemplar_ids": self.selected_exemplar_ids,
            "prompt_messages": self.prompt_messages,
        }
        return layout_items_to_output(
            self.items,
            id2label=self.id2label,
            intermediates=cast(Mapping[str, LayoutAuxValue], intermediates),
        )

to_layout_generation_output

to_layout_generation_output() -> LayoutGenerationOutput

Convert to the shared public layout output schema.

Source code in models/layout-gpt/src/layout_gpt/schema.py
85
86
87
88
89
90
91
92
93
94
95
96
97
def to_layout_generation_output(self) -> LayoutGenerationOutput:
    """Convert to the shared public layout output schema."""
    intermediates: LayoutGPTIntermediates = {
        "prompt": self.prompt,
        "raw_text": self.raw_text,
        "selected_exemplar_ids": self.selected_exemplar_ids,
        "prompt_messages": self.prompt_messages,
    }
    return layout_items_to_output(
        self.items,
        id2label=self.id2label,
        intermediates=cast(Mapping[str, LayoutAuxValue], intermediates),
    )

LayoutItem2D

Bases: BaseModel

A parsed 2D CSS layout item.

Source code in models/layout-gpt/src/layout_gpt/schema.py
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
class LayoutItem2D(BaseModel):
    """A parsed 2D CSS layout item."""

    label: str
    left: float = Field(ge=0.0)
    top: float = Field(ge=0.0)
    width: float = Field(ge=0.0)
    height: float = Field(ge=0.0)

    model_config = ConfigDict(frozen=True)

    @computed_field
    @property
    def bbox_ltrb(self) -> tuple[float, float, float, float]:
        """Normalized ``left, top, right, bottom`` box."""
        return (self.left, self.top, self.left + self.width, self.top + self.height)

    @computed_field
    @property
    def bbox_xywh(self) -> tuple[float, float, float, float]:
        """Normalized center ``xywh`` box."""
        return (
            self.left + self.width / 2,
            self.top + self.height / 2,
            self.width,
            self.height,
        )

bbox_ltrb property

bbox_ltrb: tuple[float, float, float, float]

Normalized left, top, right, bottom box.

bbox_xywh property

bbox_xywh: tuple[float, float, float, float]

Normalized center xywh box.

build_agent

build_agent(model: ModelLike = None) -> Agent[None]

Build a Pydantic AI agent with provider selected by argument or env.

Source code in models/layout-gpt/src/layout_gpt/agent.py
49
50
51
52
53
54
55
56
def build_agent(model: ModelLike = None) -> Agent[None]:
    """Build a Pydantic AI agent with provider selected by argument or env."""
    return BaseLayoutAgent[RawLayoutResponse](
        model=model,
        model_env_var=DEFAULT_MODEL_ENV_VAR,
        raw_response_type=RawLayoutResponse,
        instructions=INSTRUCTIONS,
    ).agent

agent

Provider-agnostic Pydantic AI wrapper for LayoutGPT.

LayoutGPTAgent

Bases: BaseLayoutAgent[RawLayoutResponse]

High-level LayoutGPT runner that ports released prompt and parse strategy.

Source code in models/layout-gpt/src/layout_gpt/agent.py
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
class LayoutGPTAgent(BaseLayoutAgent[RawLayoutResponse]):
    """High-level LayoutGPT runner that ports released prompt and parse strategy."""

    def __init__(
        self,
        *,
        model: ModelLike = None,
        config: LayoutGPTConfig,
        token_counter: TokenCounter = default_token_counter,
    ) -> None:
        """Initialize the runner with provider and prompt configuration."""
        super().__init__(
            model=model,
            model_env_var=DEFAULT_MODEL_ENV_VAR,
            raw_response_type=RawLayoutResponse,
            instructions=INSTRUCTIONS,
        )
        self.config = config
        self.token_counter = token_counter

    def build_prompt(
        self,
        prompt: str,
        *,
        train_examples: Sequence[LayoutExample],
        seed: int | None = None,
        generator: torch.Generator | None = None,
        query_embedding: EmbeddingProvider | None = None,
        example_embeddings: Sequence[Sequence[float]] | None = None,
    ) -> tuple[str | list[ChatMessage], list[LayoutExample]]:
        """Select exemplars and serialize the prompt sent to the model."""
        exemplars = self._select_examples(
            prompt,
            train_examples=train_examples,
            seed=seed,
            generator=generator,
            query_embedding=query_embedding,
            example_embeddings=example_embeddings,
        )
        if self.config.chat:
            return (
                form_prompt_for_chatgpt(
                    prompt,
                    exemplars=exemplars,
                    canvas_size=self.config.canvas_size,
                    token_counter=self.token_counter,
                    input_length_limit=self.config.gpt_input_length_limit,
                ),
                exemplars,
            )
        return (
            form_prompt_for_gpt3(
                prompt,
                exemplars=exemplars,
                canvas_size=self.config.canvas_size,
                token_counter=self.token_counter,
                input_length_limit=self.config.gpt_input_length_limit,
            ),
            exemplars,
        )

    def run_sync(
        self,
        prompt: str,
        *,
        train_examples: Sequence[LayoutExample],
        model: ModelLike = None,
        seed: int | None = None,
        generator: torch.Generator | None = None,
        query_embedding: EmbeddingProvider | None = None,
        example_embeddings: Sequence[Sequence[float]] | None = None,
        model_settings: ModelSettings | None = None,
    ) -> LayoutGPTOutput:
        """Run LayoutGPT and parse the response into typed layout output."""
        model_prompt, exemplars = self.build_prompt(
            prompt,
            train_examples=train_examples,
            seed=seed,
            generator=generator,
            query_embedding=query_embedding,
            example_embeddings=example_embeddings,
        )
        raw = self.run_raw_sync(
            model_prompt,
            model=model,
            model_settings=model_settings
            or ModelSettings(
                temperature=self.config.temperature, top_p=self.config.top_p
            ),
        )
        response_text = self.repair_response_text(raw.text)
        items = parse_layout_text(response_text, canvas_size=self.config.canvas_size)
        id2label = {
            index: label
            for index, label in enumerate(dict.fromkeys(item.label for item in items))
        }
        return LayoutGPTOutput(
            prompt=prompt,
            canvas_size=self.config.canvas_size,
            items=items,
            raw_text=response_text,
            id2label=id2label,
            selected_exemplar_ids=[example.id for example in exemplars],
            prompt_messages=model_prompt if isinstance(model_prompt, list) else None,
        )

    def __call__(
        self,
        *,
        prompt: str,
        train_examples: Sequence[LayoutExample],
        batch_size: int = 1,
        seed: int | None = None,
        generator: torch.Generator | None = None,
        condition_type: str | ConditionType = ConditionType.text,
        labels: Int[torch.Tensor, "batch elements"]
        | list[ArrayLikeInput]
        | None = None,
        bbox: Float[torch.Tensor, "batch elements 4"]
        | list[ArrayLikeInput]
        | None = None,
        mask: Bool[torch.Tensor, "batch elements"] | list[ArrayLikeInput] | None = None,
        num_elements: int | list[int] | Int[torch.Tensor, "batch"] | None = None,
        box_format: str | BoxFormat = BoxFormat.xywh,
        normalized: bool = True,
        canvas_size: tuple[int, int] | None = None,
        num_inference_steps: int | None = None,
        output_type: str | OutputType = OutputType.dataclass,
        return_intermediates: bool = False,
        model: ModelLike = None,
        query_embedding: EmbeddingProvider | None = None,
        example_embeddings: Sequence[Sequence[float]] | None = None,
        model_settings: ModelSettings | None = None,
    ) -> LayoutGenerationOutput | LayoutGPTOutputDict:
        """Generate a layout through the common public generation surface."""
        del labels, bbox, mask, num_elements, normalized, num_inference_steps
        normalized_condition_type, normalized_box_format = (
            self.validate_generation_request(
                batch_size=batch_size,
                condition_type=condition_type,
                box_format=box_format,
                canvas_size=canvas_size,
                configured_canvas_size=self.config.canvas_size,
                supported_condition_types=SUPPORTED_CONDITION_TYPES,
            )
        )
        normalized_output_type = coerce_enum(output_type, OutputType)
        del normalized_box_format
        output = self.run_sync(
            prompt,
            train_examples=train_examples,
            model=model,
            seed=seed,
            generator=generator,
            query_embedding=query_embedding,
            example_embeddings=example_embeddings,
            model_settings=model_settings,
        ).to_layout_generation_output()
        if not return_intermediates:
            output.intermediates = None
        if normalized_output_type is OutputType.dict:
            return cast(LayoutGPTOutputDict, self.output_to_dict(output))
        if normalized_output_type is OutputType.dataclass:
            return output
        assert_never(normalized_output_type)

    generate = __call__

    def save_pretrained(self, save_directory: str | os.PathLike[str]) -> None:
        """Persist prompt and parser configuration without provider state."""
        path = Path(save_directory)
        path.mkdir(parents=True, exist_ok=True)
        (path / "layout_gpt_config.json").write_text(
            json.dumps(self.config.model_dump(mode="json"), indent=2, sort_keys=True)
            + "\n",
            encoding="utf-8",
        )

    @classmethod
    def from_pretrained(
        cls,
        pretrained_model_name_or_path: str | os.PathLike[str],
        *,
        model: ModelLike = None,
        token_counter: TokenCounter = default_token_counter,
    ) -> "LayoutGPTAgent":
        """Load saved LayoutGPT prompt and parser configuration."""
        path = Path(pretrained_model_name_or_path) / "layout_gpt_config.json"
        config_data = json.loads(path.read_text(encoding="utf-8"))
        return cls(
            model=model,
            config=LayoutGPTConfig(**config_data),
            token_counter=token_counter,
        )

    def _select_examples(
        self,
        prompt: str,
        *,
        train_examples: Sequence[LayoutExample],
        seed: int | None = None,
        generator: torch.Generator | None = None,
        query_embedding: EmbeddingProvider | None,
        example_embeddings: Sequence[Sequence[float]] | None,
    ) -> list[LayoutExample]:
        if self.config.icl_type is ICLType.fixed_random:
            selection_seed = (
                int(generator.initial_seed())
                if generator is not None
                else seed
                if seed is not None
                else self.config.fixed_random_seed
            )
            return select_fixed_random(
                train_examples, k=self.config.k, seed=selection_seed
            )
        if self.config.icl_type is ICLType.k_similar:
            if query_embedding is None or example_embeddings is None:
                msg = "k-similar selection requires query_embedding and example_embeddings"
                raise ValueError(msg)

            return select_k_similar(
                train_examples,
                query=prompt,
                k=self.config.k,
                query_embedding=query_embedding,
                example_embeddings=example_embeddings,
            )
        assert_never(self.config.icl_type)

__init__

__init__(
    *,
    model: ModelLike = None,
    config: LayoutGPTConfig,
    token_counter: TokenCounter = default_token_counter,
) -> None

Initialize the runner with provider and prompt configuration.

Source code in models/layout-gpt/src/layout_gpt/agent.py
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
def __init__(
    self,
    *,
    model: ModelLike = None,
    config: LayoutGPTConfig,
    token_counter: TokenCounter = default_token_counter,
) -> None:
    """Initialize the runner with provider and prompt configuration."""
    super().__init__(
        model=model,
        model_env_var=DEFAULT_MODEL_ENV_VAR,
        raw_response_type=RawLayoutResponse,
        instructions=INSTRUCTIONS,
    )
    self.config = config
    self.token_counter = token_counter

build_prompt

build_prompt(
    prompt: str,
    *,
    train_examples: Sequence[LayoutExample],
    seed: int | None = None,
    generator: Generator | None = None,
    query_embedding: EmbeddingProvider | None = None,
    example_embeddings: Sequence[Sequence[float]]
    | None = None,
) -> tuple[str | list[ChatMessage], list[LayoutExample]]

Select exemplars and serialize the prompt sent to the model.

Source code in models/layout-gpt/src/layout_gpt/agent.py
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
def build_prompt(
    self,
    prompt: str,
    *,
    train_examples: Sequence[LayoutExample],
    seed: int | None = None,
    generator: torch.Generator | None = None,
    query_embedding: EmbeddingProvider | None = None,
    example_embeddings: Sequence[Sequence[float]] | None = None,
) -> tuple[str | list[ChatMessage], list[LayoutExample]]:
    """Select exemplars and serialize the prompt sent to the model."""
    exemplars = self._select_examples(
        prompt,
        train_examples=train_examples,
        seed=seed,
        generator=generator,
        query_embedding=query_embedding,
        example_embeddings=example_embeddings,
    )
    if self.config.chat:
        return (
            form_prompt_for_chatgpt(
                prompt,
                exemplars=exemplars,
                canvas_size=self.config.canvas_size,
                token_counter=self.token_counter,
                input_length_limit=self.config.gpt_input_length_limit,
            ),
            exemplars,
        )
    return (
        form_prompt_for_gpt3(
            prompt,
            exemplars=exemplars,
            canvas_size=self.config.canvas_size,
            token_counter=self.token_counter,
            input_length_limit=self.config.gpt_input_length_limit,
        ),
        exemplars,
    )

run_sync

run_sync(
    prompt: str,
    *,
    train_examples: Sequence[LayoutExample],
    model: ModelLike = None,
    seed: int | None = None,
    generator: Generator | None = None,
    query_embedding: EmbeddingProvider | None = None,
    example_embeddings: Sequence[Sequence[float]]
    | None = None,
    model_settings: ModelSettings | None = None,
) -> LayoutGPTOutput

Run LayoutGPT and parse the response into typed layout output.

Source code in models/layout-gpt/src/layout_gpt/agent.py
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
def run_sync(
    self,
    prompt: str,
    *,
    train_examples: Sequence[LayoutExample],
    model: ModelLike = None,
    seed: int | None = None,
    generator: torch.Generator | None = None,
    query_embedding: EmbeddingProvider | None = None,
    example_embeddings: Sequence[Sequence[float]] | None = None,
    model_settings: ModelSettings | None = None,
) -> LayoutGPTOutput:
    """Run LayoutGPT and parse the response into typed layout output."""
    model_prompt, exemplars = self.build_prompt(
        prompt,
        train_examples=train_examples,
        seed=seed,
        generator=generator,
        query_embedding=query_embedding,
        example_embeddings=example_embeddings,
    )
    raw = self.run_raw_sync(
        model_prompt,
        model=model,
        model_settings=model_settings
        or ModelSettings(
            temperature=self.config.temperature, top_p=self.config.top_p
        ),
    )
    response_text = self.repair_response_text(raw.text)
    items = parse_layout_text(response_text, canvas_size=self.config.canvas_size)
    id2label = {
        index: label
        for index, label in enumerate(dict.fromkeys(item.label for item in items))
    }
    return LayoutGPTOutput(
        prompt=prompt,
        canvas_size=self.config.canvas_size,
        items=items,
        raw_text=response_text,
        id2label=id2label,
        selected_exemplar_ids=[example.id for example in exemplars],
        prompt_messages=model_prompt if isinstance(model_prompt, list) else None,
    )

__call__

__call__(
    *,
    prompt: str,
    train_examples: Sequence[LayoutExample],
    batch_size: int = 1,
    seed: int | None = None,
    generator: Generator | None = None,
    condition_type: str
    | ConditionType = ConditionType.text,
    labels: Int[Tensor, "batch elements"]
    | list[ArrayLikeInput]
    | None = None,
    bbox: Float[Tensor, "batch elements 4"]
    | list[ArrayLikeInput]
    | None = None,
    mask: Bool[Tensor, "batch elements"]
    | list[ArrayLikeInput]
    | None = None,
    num_elements: int
    | list[int]
    | Int[Tensor, "batch"]
    | None = None,
    box_format: str | BoxFormat = BoxFormat.xywh,
    normalized: bool = True,
    canvas_size: tuple[int, int] | None = None,
    num_inference_steps: int | None = None,
    output_type: str | OutputType = OutputType.dataclass,
    return_intermediates: bool = False,
    model: ModelLike = None,
    query_embedding: EmbeddingProvider | None = None,
    example_embeddings: Sequence[Sequence[float]]
    | None = None,
    model_settings: ModelSettings | None = None,
) -> LayoutGenerationOutput | LayoutGPTOutputDict

Generate a layout through the common public generation surface.

Source code in models/layout-gpt/src/layout_gpt/agent.py
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
def __call__(
    self,
    *,
    prompt: str,
    train_examples: Sequence[LayoutExample],
    batch_size: int = 1,
    seed: int | None = None,
    generator: torch.Generator | None = None,
    condition_type: str | ConditionType = ConditionType.text,
    labels: Int[torch.Tensor, "batch elements"]
    | list[ArrayLikeInput]
    | None = None,
    bbox: Float[torch.Tensor, "batch elements 4"]
    | list[ArrayLikeInput]
    | None = None,
    mask: Bool[torch.Tensor, "batch elements"] | list[ArrayLikeInput] | None = None,
    num_elements: int | list[int] | Int[torch.Tensor, "batch"] | None = None,
    box_format: str | BoxFormat = BoxFormat.xywh,
    normalized: bool = True,
    canvas_size: tuple[int, int] | None = None,
    num_inference_steps: int | None = None,
    output_type: str | OutputType = OutputType.dataclass,
    return_intermediates: bool = False,
    model: ModelLike = None,
    query_embedding: EmbeddingProvider | None = None,
    example_embeddings: Sequence[Sequence[float]] | None = None,
    model_settings: ModelSettings | None = None,
) -> LayoutGenerationOutput | LayoutGPTOutputDict:
    """Generate a layout through the common public generation surface."""
    del labels, bbox, mask, num_elements, normalized, num_inference_steps
    normalized_condition_type, normalized_box_format = (
        self.validate_generation_request(
            batch_size=batch_size,
            condition_type=condition_type,
            box_format=box_format,
            canvas_size=canvas_size,
            configured_canvas_size=self.config.canvas_size,
            supported_condition_types=SUPPORTED_CONDITION_TYPES,
        )
    )
    normalized_output_type = coerce_enum(output_type, OutputType)
    del normalized_box_format
    output = self.run_sync(
        prompt,
        train_examples=train_examples,
        model=model,
        seed=seed,
        generator=generator,
        query_embedding=query_embedding,
        example_embeddings=example_embeddings,
        model_settings=model_settings,
    ).to_layout_generation_output()
    if not return_intermediates:
        output.intermediates = None
    if normalized_output_type is OutputType.dict:
        return cast(LayoutGPTOutputDict, self.output_to_dict(output))
    if normalized_output_type is OutputType.dataclass:
        return output
    assert_never(normalized_output_type)

save_pretrained

save_pretrained(
    save_directory: str | PathLike[str],
) -> None

Persist prompt and parser configuration without provider state.

Source code in models/layout-gpt/src/layout_gpt/agent.py
227
228
229
230
231
232
233
234
235
def save_pretrained(self, save_directory: str | os.PathLike[str]) -> None:
    """Persist prompt and parser configuration without provider state."""
    path = Path(save_directory)
    path.mkdir(parents=True, exist_ok=True)
    (path / "layout_gpt_config.json").write_text(
        json.dumps(self.config.model_dump(mode="json"), indent=2, sort_keys=True)
        + "\n",
        encoding="utf-8",
    )

from_pretrained classmethod

from_pretrained(
    pretrained_model_name_or_path: str | PathLike[str],
    *,
    model: ModelLike = None,
    token_counter: TokenCounter = default_token_counter,
) -> "LayoutGPTAgent"

Load saved LayoutGPT prompt and parser configuration.

Source code in models/layout-gpt/src/layout_gpt/agent.py
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
@classmethod
def from_pretrained(
    cls,
    pretrained_model_name_or_path: str | os.PathLike[str],
    *,
    model: ModelLike = None,
    token_counter: TokenCounter = default_token_counter,
) -> "LayoutGPTAgent":
    """Load saved LayoutGPT prompt and parser configuration."""
    path = Path(pretrained_model_name_or_path) / "layout_gpt_config.json"
    config_data = json.loads(path.read_text(encoding="utf-8"))
    return cls(
        model=model,
        config=LayoutGPTConfig(**config_data),
        token_counter=token_counter,
    )

build_agent

build_agent(model: ModelLike = None) -> Agent[None]

Build a Pydantic AI agent with provider selected by argument or env.

Source code in models/layout-gpt/src/layout_gpt/agent.py
49
50
51
52
53
54
55
56
def build_agent(model: ModelLike = None) -> Agent[None]:
    """Build a Pydantic AI agent with provider selected by argument or env."""
    return BaseLayoutAgent[RawLayoutResponse](
        model=model,
        model_env_var=DEFAULT_MODEL_ENV_VAR,
        raw_response_type=RawLayoutResponse,
        instructions=INSTRUCTIONS,
    ).agent

enums

Closed string modes that are specific to LayoutGPT.

LayoutGPTSetting

Bases: StrEnum

Supported NSR-1K LayoutGPT settings.

Source code in models/layout-gpt/src/layout_gpt/enums.py
 9
10
11
12
13
class LayoutGPTSetting(StrEnum):
    """Supported NSR-1K LayoutGPT settings."""

    counting = auto()
    spatial = auto()

ICLType

Bases: StrEnum

Supported in-context exemplar selection modes.

Source code in models/layout-gpt/src/layout_gpt/enums.py
16
17
18
19
20
class ICLType(StrEnum):
    """Supported in-context exemplar selection modes."""

    fixed_random = "fixed-random"
    k_similar = "k-similar"

OutputType

Bases: StrEnum

Public return type names.

Source code in models/layout-gpt/src/layout_gpt/enums.py
23
24
25
26
27
class OutputType(StrEnum):
    """Public return type names."""

    dataclass = auto()
    dict = auto()

coerce_enum

coerce_enum(
    value: str | EnumT, enum_type: type[EnumT]
) -> EnumT

Convert a string or enum value into the requested StrEnum.

Source code in models/layout-gpt/src/layout_gpt/enums.py
30
31
32
33
34
def coerce_enum(value: str | EnumT, enum_type: type[EnumT]) -> EnumT:
    """Convert a string or enum value into the requested StrEnum."""
    if isinstance(value, enum_type):
        return value
    return enum_type(value)

exemplars

LayoutGPT exemplar loading, selection, and serialization.

LayoutExample dataclass

One NSR-1K 2D LayoutGPT exemplar.

Source code in models/layout-gpt/src/layout_gpt/exemplars.py
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
@dataclass(frozen=True)
class LayoutExample:
    """One NSR-1K 2D LayoutGPT exemplar."""

    id: int | str
    prompt: str
    objects: tuple[tuple[str, tuple[float, float, float, float]], ...]
    metadata: dict[str, JsonValue]

    @classmethod
    def from_vendor_record(
        cls, record: VendorRecord, *, setting: str | LayoutGPTSetting
    ) -> LayoutExample:
        """Build an exemplar from the original NSR-1K JSON record."""
        normalized_setting = coerce_enum(setting, LayoutGPTSetting)
        if normalized_setting is LayoutGPTSetting.counting:
            raw_objects = cast(
                Sequence[tuple[str, Sequence[float]]], record["object_list"]
            )
        elif normalized_setting is LayoutGPTSetting.spatial:
            raw_objects = cast(
                Sequence[tuple[str, Sequence[float]]],
                [record["obj1"], record["obj2"]],
            )
        else:
            assert_never(normalized_setting)
        objects = tuple(
            (
                str(label),
                (
                    float(bbox[0]),
                    float(bbox[1]),
                    float(bbox[2]),
                    float(bbox[3]),
                ),
            )
            for label, bbox in raw_objects
        )
        return cls(
            id=cast(int | str, record["id"]),
            prompt=str(record["prompt"]),
            objects=objects,
            metadata=cast(dict[str, JsonValue], dict(record)),
        )

from_vendor_record classmethod

from_vendor_record(
    record: VendorRecord, *, setting: str | LayoutGPTSetting
) -> LayoutExample

Build an exemplar from the original NSR-1K JSON record.

Source code in models/layout-gpt/src/layout_gpt/exemplars.py
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
@classmethod
def from_vendor_record(
    cls, record: VendorRecord, *, setting: str | LayoutGPTSetting
) -> LayoutExample:
    """Build an exemplar from the original NSR-1K JSON record."""
    normalized_setting = coerce_enum(setting, LayoutGPTSetting)
    if normalized_setting is LayoutGPTSetting.counting:
        raw_objects = cast(
            Sequence[tuple[str, Sequence[float]]], record["object_list"]
        )
    elif normalized_setting is LayoutGPTSetting.spatial:
        raw_objects = cast(
            Sequence[tuple[str, Sequence[float]]],
            [record["obj1"], record["obj2"]],
        )
    else:
        assert_never(normalized_setting)
    objects = tuple(
        (
            str(label),
            (
                float(bbox[0]),
                float(bbox[1]),
                float(bbox[2]),
                float(bbox[3]),
            ),
        )
        for label, bbox in raw_objects
    )
    return cls(
        id=cast(int | str, record["id"]),
        prompt=str(record["prompt"]),
        objects=objects,
        metadata=cast(dict[str, JsonValue], dict(record)),
    )

load_nsr_examples

load_nsr_examples(
    path: str | Path, *, setting: str | LayoutGPTSetting
) -> list[LayoutExample]

Load LayoutGPT NSR-1K examples from a reference-style JSON file.

Source code in models/layout-gpt/src/layout_gpt/exemplars.py
68
69
70
71
72
73
74
75
def load_nsr_examples(
    path: str | Path, *, setting: str | LayoutGPTSetting
) -> list[LayoutExample]:
    """Load LayoutGPT NSR-1K examples from a reference-style JSON file."""
    records = cast(Sequence[VendorRecord], json.loads(Path(path).read_text()))
    return [
        LayoutExample.from_vendor_record(record, setting=setting) for record in records
    ]

select_fixed_random

select_fixed_random(
    examples: Sequence[LayoutExample],
    *,
    k: int,
    seed: int = DEFAULT_FIXED_RANDOM_SEED,
) -> list[LayoutExample]

Select exemplars with the reference fixed random.seed(42) strategy.

Source code in models/layout-gpt/src/layout_gpt/exemplars.py
78
79
80
81
82
83
84
85
86
87
88
def select_fixed_random(
    examples: Sequence[LayoutExample],
    *,
    k: int,
    seed: int = DEFAULT_FIXED_RANDOM_SEED,
) -> list[LayoutExample]:
    """Select exemplars with the reference fixed ``random.seed(42)`` strategy."""
    shuffled = list(examples)
    rng = random.Random(seed)
    rng.shuffle(shuffled)
    return shuffled[:k]

select_k_similar

select_k_similar(
    examples: Sequence[LayoutExample],
    *,
    query: str,
    k: int,
    query_embedding: EmbeddingProvider,
    example_embeddings: Sequence[Sequence[float]],
) -> list[LayoutExample]

Select top-k exemplars by CLIP-style cosine similarity.

The original code computes softmax(100 * query @ train.T) and then topk. Softmax preserves ranking, so this implementation keeps the same order without requiring torch or CLIP in the core package.

Source code in models/layout-gpt/src/layout_gpt/exemplars.py
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
def select_k_similar(
    examples: Sequence[LayoutExample],
    *,
    query: str,
    k: int,
    query_embedding: EmbeddingProvider,
    example_embeddings: Sequence[Sequence[float]],
) -> list[LayoutExample]:
    """Select top-k exemplars by CLIP-style cosine similarity.

    The original code computes ``softmax(100 * query @ train.T)`` and then
    ``topk``. Softmax preserves ranking, so this implementation keeps the same
    order without requiring torch or CLIP in the core package.
    """
    if len(examples) != len(example_embeddings):
        msg = "example_embeddings length must match examples"
        raise ValueError(msg)

    query_vector = _normalize(query_embedding(query))
    scores = [
        sum(q * e for q, e in zip(query_vector, _normalize(embedding), strict=True))
        for embedding in example_embeddings
    ]
    top_indices = sorted(range(len(scores)), key=scores.__getitem__, reverse=True)[:k]
    return [examples[index] for index in top_indices]

parser

Parse LayoutGPT CSS-like LLM output into typed layout items.

CSSProperty

Bases: StrEnum

CSS declaration keys emitted by the reference LayoutGPT prompts.

Source code in models/layout-gpt/src/layout_gpt/parser.py
13
14
15
16
17
18
19
20
21
22
class CSSProperty(StrEnum):
    """CSS declaration keys emitted by the reference LayoutGPT prompts."""

    depth = auto()
    height = auto()
    left = auto()
    length = auto()
    orientation = auto()
    top = auto()
    width = auto()

parse_layout_line

parse_layout_line(
    line: str,
    *,
    canvas_size: int = DEFAULT_CANVAS_SIZE,
    no_integer: bool = False,
) -> LayoutItem2D | None

Parse one 2D CSS line using the reference clamp/reject behavior.

Source code in models/layout-gpt/src/layout_gpt/parser.py
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
def parse_layout_line(
    line: str,
    *,
    canvas_size: int = DEFAULT_CANVAS_SIZE,
    no_integer: bool = False,
) -> LayoutItem2D | None:
    """Parse one 2D CSS line using the reference clamp/reject behavior."""
    match = _RULE_RE.match(line)
    if match is None:
        return None

    label = _strip_digits(match.group("label")).strip()
    declarations = _parse_declarations(match.group("body"))
    if declarations is None or set(declarations) != _LAYOUT_2D_KEYS:
        return LayoutItem2D(label=label, left=0, top=0, width=0, height=0)

    convert = float if no_integer else _parse_px_int
    left = convert(declarations[CSSProperty.left])
    top = convert(declarations[CSSProperty.top])
    width = convert(declarations[CSSProperty.width])
    height = convert(declarations[CSSProperty.height])
    right = min(left + width, canvas_size)
    bottom = min(top + height, canvas_size)
    if left >= canvas_size or top >= canvas_size:
        return None
    return LayoutItem2D(
        label=label,
        left=float(left) / canvas_size,
        top=float(top) / canvas_size,
        width=max(float(right - left), 0.0) / canvas_size,
        height=max(float(bottom - top), 0.0) / canvas_size,
    )

parse_layout_text

parse_layout_text(
    text: str, *, canvas_size: int = DEFAULT_CANVAS_SIZE
) -> list[LayoutItem2D]

Parse a multi-line 2D LayoutGPT response.

Source code in models/layout-gpt/src/layout_gpt/parser.py
85
86
87
88
89
90
91
92
93
94
def parse_layout_text(
    text: str, *, canvas_size: int = DEFAULT_CANVAS_SIZE
) -> list[LayoutItem2D]:
    """Parse a multi-line 2D LayoutGPT response."""
    return [
        item
        for line in text.strip().splitlines()
        if line.strip()
        and (item := parse_layout_line(line, canvas_size=canvas_size)) is not None
    ]

parse_3d_layout_line

parse_3d_layout_line(
    line: str, *, unit: str = DEFAULT_3D_UNIT
) -> LayoutItem3D | None

Parse one 3D CSS line from the reference scene-layout script.

Source code in models/layout-gpt/src/layout_gpt/parser.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
def parse_3d_layout_line(
    line: str, *, unit: str = DEFAULT_3D_UNIT
) -> LayoutItem3D | None:
    """Parse one 3D CSS line from the reference scene-layout script."""
    match = _RULE_RE.match(line)
    if match is None:
        return None
    label = _strip_digits(match.group("label")).strip()
    declarations = _parse_declarations(match.group("body"))
    if declarations is None or set(declarations) != _LAYOUT_3D_KEYS:
        return None
    return LayoutItem3D(
        label=label,
        length=_parse_unit_float(declarations[CSSProperty.length], unit=unit),
        width=_parse_unit_float(declarations[CSSProperty.width], unit=unit),
        height=_parse_unit_float(declarations[CSSProperty.height], unit=unit),
        orientation=_parse_orientation(declarations[CSSProperty.orientation]),
        left=_parse_unit_float(declarations[CSSProperty.left], unit=unit),
        top=_parse_unit_float(declarations[CSSProperty.top], unit=unit),
        depth=_parse_unit_float(declarations[CSSProperty.depth], unit=unit),
    )

parse_3d_layout_text

parse_3d_layout_text(
    text: str, *, unit: str = DEFAULT_3D_UNIT
) -> list[LayoutItem3D]

Parse a multi-line 3D LayoutGPT response.

Source code in models/layout-gpt/src/layout_gpt/parser.py
120
121
122
123
124
125
126
127
128
def parse_3d_layout_text(
    text: str, *, unit: str = DEFAULT_3D_UNIT
) -> list[LayoutItem3D]:
    """Parse a multi-line 3D LayoutGPT response."""
    return [
        item
        for line in text.strip().splitlines()
        if line.strip() and (item := parse_3d_layout_line(line, unit=unit)) is not None
    ]

prompts

Prompt serialization ported from the original LayoutGPT scripts.

default_token_counter

default_token_counter(text: str) -> int

Small dependency-free fallback token counter.

Source code in models/layout-gpt/src/layout_gpt/prompts.py
13
14
15
def default_token_counter(text: str) -> int:
    """Small dependency-free fallback token counter."""
    return len(text.split())

system_prompt_2d

system_prompt_2d(*, canvas_size: int) -> str

Return the reference 2D instruction prompt.

Source code in models/layout-gpt/src/layout_gpt/prompts.py
18
19
20
21
22
23
24
25
26
27
28
29
30
def system_prompt_2d(*, canvas_size: int) -> str:
    """Return the reference 2D instruction prompt."""
    return (
        "Instruction: Given a sentence prompt that will be used to generate an image, "
        "plan the layout of the image."
        "The generated layout should follow the CSS style, where each line starts "
        "with the object description and is followed by its absolute position. "
        'Formally, each line should be like "object {width: ?px; height: ?px; '
        'left: ?px; top: ?px; }". '
        f"The image is {canvas_size}px wide and {canvas_size}px high. "
        f"Therefore, all properties of the positions should not exceed {canvas_size}px, "
        "including the addition of left and width and the addition of top and height. \n"
    )

create_exemplar_prompt

create_exemplar_prompt(
    example: LayoutExample,
    *,
    canvas_size: int,
    is_chat: bool = False,
) -> str

Serialize one exemplar using reference CSS property order.

Source code in models/layout-gpt/src/layout_gpt/prompts.py
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
def create_exemplar_prompt(
    example: LayoutExample,
    *,
    canvas_size: int,
    is_chat: bool = False,
) -> str:
    """Serialize one exemplar using reference CSS property order."""
    prompt = "" if is_chat else f"\nPrompt: {example.prompt}\nLayout:\n"
    for category, bbox in example.objects:
        x, y, width, height = [int(value * canvas_size) for value in bbox]
        prompt += (
            f"{category} {{height: {height}px; width: {width}px; "
            f"top: {y}px; left: {x}px; }}\n"
        )
    return prompt

form_prompt_for_chatgpt

form_prompt_for_chatgpt(
    text_input: str,
    *,
    exemplars: Sequence[LayoutExample],
    canvas_size: int,
    token_counter: TokenCounter = default_token_counter,
    input_length_limit: int = DEFAULT_INPUT_LENGTH_LIMIT,
) -> list[ChatMessage]

Build chat messages with reference exemplar ordering and token truncation.

Source code in models/layout-gpt/src/layout_gpt/prompts.py
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
def form_prompt_for_chatgpt(
    text_input: str,
    *,
    exemplars: Sequence[LayoutExample],
    canvas_size: int,
    token_counter: TokenCounter = default_token_counter,
    input_length_limit: int = DEFAULT_INPUT_LENGTH_LIMIT,
) -> list[ChatMessage]:
    """Build chat messages with reference exemplar ordering and token truncation."""
    system_prompt = system_prompt_2d(canvas_size=canvas_size)
    final_prompt = f"Prompt: {text_input}\nLayout:"
    total_length = token_counter(system_prompt + final_prompt)
    messages: list[ChatMessage] = [{"role": "system", "content": system_prompt}]

    for exemplar in exemplars:
        user_prompt = f"Prompt: {exemplar.prompt}\nLayout:"
        answer = create_exemplar_prompt(exemplar, canvas_size=canvas_size, is_chat=True)
        current_length = token_counter(user_prompt + answer)
        if total_length + current_length > input_length_limit:
            break
        total_length += current_length
        current_messages: list[ChatMessage] = [
            {"role": "user", "content": user_prompt},
            {"role": "assistant", "content": answer},
        ]
        messages = messages[:1] + current_messages + messages[1:]

    messages.append({"role": "user", "content": final_prompt})
    return messages

form_prompt_for_gpt3

form_prompt_for_gpt3(
    text_input: str,
    *,
    exemplars: Sequence[LayoutExample],
    canvas_size: int,
    token_counter: TokenCounter = default_token_counter,
    input_length_limit: int = DEFAULT_INPUT_LENGTH_LIMIT,
) -> str

Build completion prompt with reference exemplar ordering and truncation.

Source code in models/layout-gpt/src/layout_gpt/prompts.py
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
def form_prompt_for_gpt3(
    text_input: str,
    *,
    exemplars: Sequence[LayoutExample],
    canvas_size: int,
    token_counter: TokenCounter = default_token_counter,
    input_length_limit: int = DEFAULT_INPUT_LENGTH_LIMIT,
) -> str:
    """Build completion prompt with reference exemplar ordering and truncation."""
    prompt = system_prompt_2d(canvas_size=canvas_size)
    last_example = f"\nPrompt: {text_input}\nLayout:"
    total_length = token_counter(prompt + last_example)
    prompting_examples = ""

    for exemplar in exemplars:
        current = create_exemplar_prompt(exemplar, canvas_size=canvas_size)
        current_length = token_counter(current)
        if total_length + current_length > input_length_limit:
            break
        prompting_examples = current + prompting_examples
        total_length += current_length

    return prompt + prompting_examples + last_example

schema

Typed LayoutGPT request and response schemas.

LayoutItem2D

Bases: BaseModel

A parsed 2D CSS layout item.

Source code in models/layout-gpt/src/layout_gpt/schema.py
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
class LayoutItem2D(BaseModel):
    """A parsed 2D CSS layout item."""

    label: str
    left: float = Field(ge=0.0)
    top: float = Field(ge=0.0)
    width: float = Field(ge=0.0)
    height: float = Field(ge=0.0)

    model_config = ConfigDict(frozen=True)

    @computed_field
    @property
    def bbox_ltrb(self) -> tuple[float, float, float, float]:
        """Normalized ``left, top, right, bottom`` box."""
        return (self.left, self.top, self.left + self.width, self.top + self.height)

    @computed_field
    @property
    def bbox_xywh(self) -> tuple[float, float, float, float]:
        """Normalized center ``xywh`` box."""
        return (
            self.left + self.width / 2,
            self.top + self.height / 2,
            self.width,
            self.height,
        )

bbox_ltrb property

bbox_ltrb: tuple[float, float, float, float]

Normalized left, top, right, bottom box.

bbox_xywh property

bbox_xywh: tuple[float, float, float, float]

Normalized center xywh box.

LayoutItem3D

Bases: BaseModel

A parsed 3D CSS layout item.

Source code in models/layout-gpt/src/layout_gpt/schema.py
51
52
53
54
55
56
57
58
59
60
61
62
63
class LayoutItem3D(BaseModel):
    """A parsed 3D CSS layout item."""

    label: str
    length: float
    width: float
    height: float
    orientation: float
    left: float
    top: float
    depth: float

    model_config = ConfigDict(frozen=True)

RawLayoutResponse

Bases: BaseModel

Structured model response before CSS parsing.

Source code in models/layout-gpt/src/layout_gpt/schema.py
66
67
68
69
class RawLayoutResponse(BaseModel):
    """Structured model response before CSS parsing."""

    text: str

LayoutGPTOutput

Bases: BaseModel

Pydantic representation of a parsed LayoutGPT response.

Source code in models/layout-gpt/src/layout_gpt/schema.py
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
class LayoutGPTOutput(BaseModel):
    """Pydantic representation of a parsed LayoutGPT response."""

    prompt: str
    canvas_size: int
    items: list[LayoutItem2D]
    raw_text: str
    id2label: dict[int, str]
    selected_exemplar_ids: list[str | int] = Field(default_factory=list)
    prompt_messages: list[ChatMessage] | None = None

    model_config = ConfigDict(frozen=True)

    def to_layout_generation_output(self) -> LayoutGenerationOutput:
        """Convert to the shared public layout output schema."""
        intermediates: LayoutGPTIntermediates = {
            "prompt": self.prompt,
            "raw_text": self.raw_text,
            "selected_exemplar_ids": self.selected_exemplar_ids,
            "prompt_messages": self.prompt_messages,
        }
        return layout_items_to_output(
            self.items,
            id2label=self.id2label,
            intermediates=cast(Mapping[str, LayoutAuxValue], intermediates),
        )

to_layout_generation_output

to_layout_generation_output() -> LayoutGenerationOutput

Convert to the shared public layout output schema.

Source code in models/layout-gpt/src/layout_gpt/schema.py
85
86
87
88
89
90
91
92
93
94
95
96
97
def to_layout_generation_output(self) -> LayoutGenerationOutput:
    """Convert to the shared public layout output schema."""
    intermediates: LayoutGPTIntermediates = {
        "prompt": self.prompt,
        "raw_text": self.raw_text,
        "selected_exemplar_ids": self.selected_exemplar_ids,
        "prompt_messages": self.prompt_messages,
    }
    return layout_items_to_output(
        self.items,
        id2label=self.id2label,
        intermediates=cast(Mapping[str, LayoutAuxValue], intermediates),
    )

LayoutGPTConfig

Bases: BaseModel

Runtime configuration for LayoutGPT prompt and provider behavior.

Source code in models/layout-gpt/src/layout_gpt/schema.py
100
101
102
103
104
105
106
107
108
109
110
111
112
class LayoutGPTConfig(BaseModel):
    """Runtime configuration for LayoutGPT prompt and provider behavior."""

    setting: LayoutGPTSetting = LayoutGPTSetting.counting
    icl_type: ICLType = ICLType.k_similar
    k: int = Field(default=DEFAULT_K, ge=0)
    canvas_size: int = Field(default=DEFAULT_CANVAS_SIZE, gt=0)
    gpt_input_length_limit: int = Field(default=DEFAULT_GPT_INPUT_LENGTH_LIMIT, gt=0)
    chat: bool = True
    temperature: float = DEFAULT_TEMPERATURE
    top_p: float = DEFAULT_TOP_P
    n_iter: int = Field(default=DEFAULT_N_ITER, ge=1)
    fixed_random_seed: int = DEFAULT_FIXED_RANDOM_SEED

types

Structured dictionary types used by LayoutGPT.

ChatMessage

Bases: TypedDict

Chat-style prompt message passed to Pydantic AI.

Source code in models/layout-gpt/src/layout_gpt/types.py
 8
 9
10
11
12
class ChatMessage(TypedDict):
    """Chat-style prompt message passed to Pydantic AI."""

    role: str
    content: str

LayoutGPTIntermediates

Bases: TypedDict

LayoutGPT-specific intermediate values attached to shared outputs.

Source code in models/layout-gpt/src/layout_gpt/types.py
15
16
17
18
19
20
21
class LayoutGPTIntermediates(TypedDict):
    """LayoutGPT-specific intermediate values attached to shared outputs."""

    prompt: str
    raw_text: str
    selected_exemplar_ids: list[str | int]
    prompt_messages: list[ChatMessage] | None

LayoutGPTOutputDict

Bases: TypedDict

Dictionary form returned when output_type='dict'.

Source code in models/layout-gpt/src/layout_gpt/types.py
24
25
26
27
28
29
30
31
32
33
34
class LayoutGPTOutputDict(TypedDict):
    """Dictionary form returned when ``output_type='dict'``."""

    bbox: Float[torch.Tensor, "batch elements 4"]
    labels: Int[torch.Tensor, "batch elements"]
    mask: Bool[torch.Tensor, "batch elements"]
    id2label: dict[int, str]
    sequences: Int[torch.Tensor, "..."] | None
    scores: object | None
    trajectory: object | None
    intermediates: object | None