This package exposes LayoutGPT as a pydantic-ai agent for prompt-based text-to-layout generation, including the original prompt format, exemplar-selection modes, and output parser.
LayoutGPT is a prompt-based layout agent that turns natural-language scene descriptions and in-context examples into structured layout elements. It has no learned checkpoint; the package stores prompt, parser, and exemplar configuration while pydantic-ai supplies the model backend. Public outputs use normalized center xywh boxes in [0, 1], request-local integer labels, a valid-element mask, and id2label.
Developed by: Weixi Feng et al.
Shared by: creative-graphic-design.
Model type: content-agnostic; task: task-agnostic; conditioning: unconditional, text.
Language(s) (NLP): English prompts for prompt-only operation.
Use this package for research inference, prompt-serialization checks, and agreement checks against the original implementation for generated layouts.
LayoutGPT builds the same in-context prompts as the original 2D script, calls any pydantic-ai compatible chat model, and parses CSS-style layout lines into typed Python objects and the shared layout output schema. The supported settings are counting and spatial; exemplar selection supports fixed-random and k-similar.
Real provider calls require the credentials used by the selected pydantic-ai provider. For the default OpenAI provider, set OPENAI_API_KEY; for other providers, set the provider-specific API key before running run_sync() or the demo script.
Do not treat generated layouts as production accessibility annotations, OCR output, semantic scene understanding, or license-cleared design assets without separate review.
The packaged behavior follows the upstream prompt fixtures, exemplar metadata, parser rules, and datasets. Dataset coverage, label vocabularies, and layout quality inherit the limits of those sources.
Clone this repository and install the prompt-only workspace member. LayoutGPT has no learned checkpoints; the example uses saved prompt configuration and a parsed output object, so it runs without provider credentials.
fromlayout_gptimportLayoutGPTAgentfromlayout_gpt.schemaimportLayoutGPTConfig,LayoutGPTOutput,LayoutItem2Dagent=LayoutGPTAgent(config=LayoutGPTConfig(setting="counting",icl_type="fixed-random",k=0))agent.save_pretrained(".cache/layout-gpt/prompt-config")loaded=LayoutGPTAgent.from_pretrained(".cache/layout-gpt/prompt-config")output=LayoutGPTOutput(prompt="there is one clock in the image",canvas_size=64,items=[LayoutItem2D(label="clock",left=0.25,top=0.25,width=0.5,height=0.5)],raw_text="clock {height: 32px; width: 32px; top: 16px; left: 16px; }",id2label={0:"clock"},)layout_output=output.to_layout_generation_output()print(loaded.config.setting)print(layout_output.bbox)print(layout_output.id2label)
The example above is an offline parser/output demo; it does not call an LLM provider.
NSR-1K examples are loaded from the original dataset JSON files. layout_output.bbox is normalized center xywh in [0, 1]; layout_output.labels are request-local integer ids, and layout_output.id2label maps those ids back to object names.
Inputs and outputs are normalized to the public layout schema at package boundaries. Boxes, tokens, prompts, or analog bits that follow the original implementation stay inside package adapters and fixtures used for agreement checks.
Agreement checks use local-only generated prompt fixtures and parser-reference JSON. Provider outputs, generated artifacts, and downloaded datasets are not committed.
See REPRODUCING.md for the commands that prepare prompt assets, generate reference outputs, run agreement checks, save prompt configuration, and smoke-test local loading.
No new model training is performed by this prompt-only package. Agreement-check costs depend on the selected prompt fixtures, provider configuration, and local hardware.
LayoutGPT builds a structured prompt from task examples, calls a configured pydantic-ai model, and parses CSS-like layout text into boxes and labels. For k-similar, pass a query embedding function and candidate embeddings to run_sync() or build_prompt(); CLIP remains optional.
Repository wrapper code is Apache-2.0. The original LayoutGPT repository is MIT licensed; keep upstream prompt assets, datasets, and provider outputs under their own terms.
@article{feng2023layoutgpt,title={LayoutGPT: Compositional Visual Planning and Generation with Large Language Models},author={Feng, Weixi and Zhu, Wanrong and Fu, Tsu-Jui and Jampani, Varun and Akula, Arjun and He, Xuehai and Basu, Sugato and Wang, Xin Eric and Wang, William Yang},journal={arXiv preprint arXiv:2305.15393},year={2023}}
classLayoutGPTAgent(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=configself.token_counter=token_counterdefbuild_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,)ifself.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,)defrun_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_settingsorModelSettings(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:labelforindex,labelinenumerate(dict.fromkeys(item.labelforiteminitems))}returnLayoutGPTOutput(prompt=prompt,canvas_size=self.config.canvas_size,items=items,raw_text=response_text,id2label=id2label,selected_exemplar_ids=[example.idforexampleinexemplars],prompt_messages=model_promptifisinstance(model_prompt,list)elseNone,)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."""dellabels,bbox,mask,num_elements,normalized,num_inference_stepsnormalized_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)delnormalized_box_formatoutput=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()ifnotreturn_intermediates:output.intermediates=Noneifnormalized_output_typeisOutputType.dict:returncast(LayoutGPTOutputDict,self.output_to_dict(output))ifnormalized_output_typeisOutputType.dataclass:returnoutputassert_never(normalized_output_type)generate=__call__defsave_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",)@classmethoddeffrom_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"))returncls(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]:ifself.config.icl_typeisICLType.fixed_random:selection_seed=(int(generator.initial_seed())ifgeneratorisnotNoneelseseedifseedisnotNoneelseself.config.fixed_random_seed)returnselect_fixed_random(train_examples,k=self.config.k,seed=selection_seed)ifself.config.icl_typeisICLType.k_similar:ifquery_embeddingisNoneorexample_embeddingsisNone:msg="k-similar selection requires query_embedding and example_embeddings"raiseValueError(msg)returnselect_k_similar(train_examples,query=prompt,k=self.config.k,query_embedding=query_embedding,example_embeddings=example_embeddings,)assert_never(self.config.icl_type)
Initialize the runner with provider and prompt configuration.
Source code in models/layout-gpt/src/layout_gpt/agent.py
62636465666768697071727374757677
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=configself.token_counter=token_counter
defbuild_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,)ifself.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,)
defrun_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_settingsorModelSettings(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:labelforindex,labelinenumerate(dict.fromkeys(item.labelforiteminitems))}returnLayoutGPTOutput(prompt=prompt,canvas_size=self.config.canvas_size,items=items,raw_text=response_text,id2label=id2label,selected_exemplar_ids=[example.idforexampleinexemplars],prompt_messages=model_promptifisinstance(model_prompt,list)elseNone,)
__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
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."""dellabels,bbox,mask,num_elements,normalized,num_inference_stepsnormalized_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)delnormalized_box_formatoutput=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()ifnotreturn_intermediates:output.intermediates=Noneifnormalized_output_typeisOutputType.dict:returncast(LayoutGPTOutputDict,self.output_to_dict(output))ifnormalized_output_typeisOutputType.dataclass:returnoutputassert_never(normalized_output_type)
Persist prompt and parser configuration without provider state.
Source code in models/layout-gpt/src/layout_gpt/agent.py
227228229230231232233234235
defsave_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",)
classLayoutGPTOutput(BaseModel):"""Pydantic representation of a parsed LayoutGPT response."""prompt:strcanvas_size:intitems:list[LayoutItem2D]raw_text:strid2label:dict[int,str]selected_exemplar_ids:list[str|int]=Field(default_factory=list)prompt_messages:list[ChatMessage]|None=Nonemodel_config=ConfigDict(frozen=True)defto_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,}returnlayout_items_to_output(self.items,id2label=self.id2label,intermediates=cast(Mapping[str,LayoutAuxValue],intermediates),)
Convert to the shared public layout output schema.
Source code in models/layout-gpt/src/layout_gpt/schema.py
85868788899091929394959697
defto_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,}returnlayout_items_to_output(self.items,id2label=self.id2label,intermediates=cast(Mapping[str,LayoutAuxValue],intermediates),)
Build a Pydantic AI agent with provider selected by argument or env.
Source code in models/layout-gpt/src/layout_gpt/agent.py
4950515253545556
defbuild_agent(model:ModelLike=None)->Agent[None]:"""Build a Pydantic AI agent with provider selected by argument or env."""returnBaseLayoutAgent[RawLayoutResponse](model=model,model_env_var=DEFAULT_MODEL_ENV_VAR,raw_response_type=RawLayoutResponse,instructions=INSTRUCTIONS,).agent
classLayoutGPTAgent(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=configself.token_counter=token_counterdefbuild_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,)ifself.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,)defrun_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_settingsorModelSettings(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:labelforindex,labelinenumerate(dict.fromkeys(item.labelforiteminitems))}returnLayoutGPTOutput(prompt=prompt,canvas_size=self.config.canvas_size,items=items,raw_text=response_text,id2label=id2label,selected_exemplar_ids=[example.idforexampleinexemplars],prompt_messages=model_promptifisinstance(model_prompt,list)elseNone,)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."""dellabels,bbox,mask,num_elements,normalized,num_inference_stepsnormalized_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)delnormalized_box_formatoutput=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()ifnotreturn_intermediates:output.intermediates=Noneifnormalized_output_typeisOutputType.dict:returncast(LayoutGPTOutputDict,self.output_to_dict(output))ifnormalized_output_typeisOutputType.dataclass:returnoutputassert_never(normalized_output_type)generate=__call__defsave_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",)@classmethoddeffrom_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"))returncls(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]:ifself.config.icl_typeisICLType.fixed_random:selection_seed=(int(generator.initial_seed())ifgeneratorisnotNoneelseseedifseedisnotNoneelseself.config.fixed_random_seed)returnselect_fixed_random(train_examples,k=self.config.k,seed=selection_seed)ifself.config.icl_typeisICLType.k_similar:ifquery_embeddingisNoneorexample_embeddingsisNone:msg="k-similar selection requires query_embedding and example_embeddings"raiseValueError(msg)returnselect_k_similar(train_examples,query=prompt,k=self.config.k,query_embedding=query_embedding,example_embeddings=example_embeddings,)assert_never(self.config.icl_type)
Initialize the runner with provider and prompt configuration.
Source code in models/layout-gpt/src/layout_gpt/agent.py
62636465666768697071727374757677
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=configself.token_counter=token_counter
defbuild_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,)ifself.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,)
defrun_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_settingsorModelSettings(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:labelforindex,labelinenumerate(dict.fromkeys(item.labelforiteminitems))}returnLayoutGPTOutput(prompt=prompt,canvas_size=self.config.canvas_size,items=items,raw_text=response_text,id2label=id2label,selected_exemplar_ids=[example.idforexampleinexemplars],prompt_messages=model_promptifisinstance(model_prompt,list)elseNone,)
__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
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."""dellabels,bbox,mask,num_elements,normalized,num_inference_stepsnormalized_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)delnormalized_box_formatoutput=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()ifnotreturn_intermediates:output.intermediates=Noneifnormalized_output_typeisOutputType.dict:returncast(LayoutGPTOutputDict,self.output_to_dict(output))ifnormalized_output_typeisOutputType.dataclass:returnoutputassert_never(normalized_output_type)
Persist prompt and parser configuration without provider state.
Source code in models/layout-gpt/src/layout_gpt/agent.py
227228229230231232233234235
defsave_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",)
Build a Pydantic AI agent with provider selected by argument or env.
Source code in models/layout-gpt/src/layout_gpt/agent.py
4950515253545556
defbuild_agent(model:ModelLike=None)->Agent[None]:"""Build a Pydantic AI agent with provider selected by argument or env."""returnBaseLayoutAgent[RawLayoutResponse](model=model,model_env_var=DEFAULT_MODEL_ENV_VAR,raw_response_type=RawLayoutResponse,instructions=INSTRUCTIONS,).agent
Convert a string or enum value into the requested StrEnum.
Source code in models/layout-gpt/src/layout_gpt/enums.py
3031323334
defcoerce_enum(value:str|EnumT,enum_type:type[EnumT])->EnumT:"""Convert a string or enum value into the requested StrEnum."""ifisinstance(value,enum_type):returnvaluereturnenum_type(value)
@dataclass(frozen=True)classLayoutExample:"""One NSR-1K 2D LayoutGPT exemplar."""id:int|strprompt:strobjects:tuple[tuple[str,tuple[float,float,float,float]],...]metadata:dict[str,JsonValue]@classmethoddeffrom_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)ifnormalized_settingisLayoutGPTSetting.counting:raw_objects=cast(Sequence[tuple[str,Sequence[float]]],record["object_list"])elifnormalized_settingisLayoutGPTSetting.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]),),)forlabel,bboxinraw_objects)returncls(id=cast(int|str,record["id"]),prompt=str(record["prompt"]),objects=objects,metadata=cast(dict[str,JsonValue],dict(record)),)
@classmethoddeffrom_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)ifnormalized_settingisLayoutGPTSetting.counting:raw_objects=cast(Sequence[tuple[str,Sequence[float]]],record["object_list"])elifnormalized_settingisLayoutGPTSetting.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]),),)forlabel,bboxinraw_objects)returncls(id=cast(int|str,record["id"]),prompt=str(record["prompt"]),objects=objects,metadata=cast(dict[str,JsonValue],dict(record)),)
Load LayoutGPT NSR-1K examples from a reference-style JSON file.
Source code in models/layout-gpt/src/layout_gpt/exemplars.py
6869707172737475
defload_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)forrecordinrecords]
Select exemplars with the reference fixed random.seed(42) strategy.
Source code in models/layout-gpt/src/layout_gpt/exemplars.py
7879808182838485868788
defselect_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)returnshuffled[:k]
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
defselect_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. """iflen(examples)!=len(example_embeddings):msg="example_embeddings length must match examples"raiseValueError(msg)query_vector=_normalize(query_embedding(query))scores=[sum(q*eforq,einzip(query_vector,_normalize(embedding),strict=True))forembeddinginexample_embeddings]top_indices=sorted(range(len(scores)),key=scores.__getitem__,reverse=True)[:k]return[examples[index]forindexintop_indices]
CSS declaration keys emitted by the reference LayoutGPT prompts.
Source code in models/layout-gpt/src/layout_gpt/parser.py
13141516171819202122
classCSSProperty(StrEnum):"""CSS declaration keys emitted by the reference LayoutGPT prompts."""depth=auto()height=auto()left=auto()length=auto()orientation=auto()top=auto()width=auto()
defparse_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)ifmatchisNone:returnNonelabel=_strip_digits(match.group("label")).strip()declarations=_parse_declarations(match.group("body"))ifdeclarationsisNoneorset(declarations)!=_LAYOUT_2D_KEYS:returnLayoutItem2D(label=label,left=0,top=0,width=0,height=0)convert=floatifno_integerelse_parse_px_intleft=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)ifleft>=canvas_sizeortop>=canvas_size:returnNonereturnLayoutItem2D(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,)
defparse_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)ifmatchisNone:returnNonelabel=_strip_digits(match.group("label")).strip()declarations=_parse_declarations(match.group("body"))ifdeclarationsisNoneorset(declarations)!=_LAYOUT_3D_KEYS:returnNonereturnLayoutItem3D(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),)
Source code in models/layout-gpt/src/layout_gpt/parser.py
120121122123124125126127128
defparse_3d_layout_text(text:str,*,unit:str=DEFAULT_3D_UNIT)->list[LayoutItem3D]:"""Parse a multi-line 3D LayoutGPT response."""return[itemforlineintext.strip().splitlines()ifline.strip()and(item:=parse_3d_layout_line(line,unit=unit))isnotNone]
Source code in models/layout-gpt/src/layout_gpt/prompts.py
18192021222324252627282930
defsystem_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")
defform_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=""forexemplarinexemplars:current=create_exemplar_prompt(exemplar,canvas_size=canvas_size)current_length=token_counter(current)iftotal_length+current_length>input_length_limit:breakprompting_examples=current+prompting_examplestotal_length+=current_lengthreturnprompt+prompting_examples+last_example
classLayoutGPTOutput(BaseModel):"""Pydantic representation of a parsed LayoutGPT response."""prompt:strcanvas_size:intitems:list[LayoutItem2D]raw_text:strid2label:dict[int,str]selected_exemplar_ids:list[str|int]=Field(default_factory=list)prompt_messages:list[ChatMessage]|None=Nonemodel_config=ConfigDict(frozen=True)defto_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,}returnlayout_items_to_output(self.items,id2label=self.id2label,intermediates=cast(Mapping[str,LayoutAuxValue],intermediates),)
Convert to the shared public layout output schema.
Source code in models/layout-gpt/src/layout_gpt/schema.py
85868788899091929394959697
defto_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,}returnlayout_items_to_output(self.items,id2label=self.id2label,intermediates=cast(Mapping[str,LayoutAuxValue],intermediates),)
Runtime configuration for LayoutGPT prompt and provider behavior.
Source code in models/layout-gpt/src/layout_gpt/schema.py
100101102103104105106107108109110111112
classLayoutGPTConfig(BaseModel):"""Runtime configuration for LayoutGPT prompt and provider behavior."""setting:LayoutGPTSetting=LayoutGPTSetting.countingicl_type:ICLType=ICLType.k_similark: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=Truetemperature:float=DEFAULT_TEMPERATUREtop_p:float=DEFAULT_TOP_Pn_iter:int=Field(default=DEFAULT_N_ITER,ge=1)fixed_random_seed:int=DEFAULT_FIXED_RANDOM_SEED
Source code in models/layout-gpt/src/layout_gpt/types.py
2425262728293031323334
classLayoutGPTOutputDict(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,"..."]|Nonescores:object|Nonetrajectory:object|Noneintermediates:object|None