Skip to content

fix: preserve _meta during ACP serialization - #20

Merged
PsiACE merged 1 commit into
mainfrom
fix-meta
Nov 2, 2025
Merged

fix: preserve _meta during ACP serialization#20
PsiACE merged 1 commit into
mainfrom
fix-meta

Conversation

@PsiACE

Copy link
Copy Markdown
Member

Fix: #18

image

Signed-off-by: Chojan Shang <chojan.shang@vesoft.com>
@PsiACE
PsiACE requested a review from CopilotNovember 2, 2025 15:57

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds support for field aliases in Pydantic models, specifically to map the field_meta attribute to the _meta JSON field for RPC serialization. The change enables proper serialization/deserialization of metadata fields using the JSON-RPC convention.

Key changes:

  • Added populate_by_name=True configuration to allow models to accept both field names and their aliases during instantiation
  • Updated serialization to use by_alias=True to output JSON with aliased field names (_meta instead of field_meta)
  • Modified schema generation to automatically inject custom BaseModel configuration

Reviewed Changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
FileDescription
src/acp/schema.pyAdds custom BaseModel class with populate_by_name=True configuration to support field aliases
src/acp/utils.pyUpdates serialize_params to use by_alias=True for proper JSON field name mapping
src/acp/connection.pyUpdates response serialization to use aliases and additional JSON-safe options
scripts/gen_schema.pyAdds schema generation logic to automatically inject custom BaseModel configuration
tests/test_utils.pyAdds test coverage for alias serialization behavior
examples/echo_agent.pyDemonstrates usage of field_meta attribute with the new alias support

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadscripts/gen_schema.py
Comment on lines +229 to +251
if not line.startswith("from pydantic import "):
continue
imports = [part.strip() for part in line[len("from pydantic import ") :].split(",")]
has_alias = any(part == "BaseModel as _BaseModel" for part in imports)
has_config = any(part == "ConfigDict" for part in imports)
new_imports = []
for part in imports:
if part == "BaseModel":
new_imports.append("BaseModel as _BaseModel")
has_alias = True
else:
new_imports.append(part)
if not has_alias:
new_imports.append("BaseModel as _BaseModel")
if not has_config:
new_imports.append("ConfigDict")
lines[idx] = "from pydantic import " + ", ".join(new_imports)
insert_idx = idx + 1
lines.insert(insert_idx, "")
lines.insert(insert_idx + 1, "class BaseModel(_BaseModel):")
lines.insert(insert_idx + 2, " model_config = ConfigDict(populate_by_name=True)")
lines.insert(insert_idx + 3, "")
break

CopilotAINov 2, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The loop continues after finding the first from pydantic import line, but it should break after processing. This will cause issues if there are multiple pydantic import lines, as subsequent iterations will try to insert the BaseModel class definition multiple times at different positions.

Suggested change
ifnotline.startswith("from pydantic import "):
continue
imports= [part.strip() forpartinline[len("from pydantic import ") :].split(",")]
has_alias=any(part=="BaseModel as _BaseModel"forpartinimports)
has_config=any(part=="ConfigDict"forpartinimports)
new_imports= []
forpartinimports:
ifpart=="BaseModel":
new_imports.append("BaseModel as _BaseModel")
has_alias=True
else:
new_imports.append(part)
ifnothas_alias:
new_imports.append("BaseModel as _BaseModel")
ifnothas_config:
new_imports.append("ConfigDict")
lines[idx] ="from pydantic import "+", ".join(new_imports)
insert_idx=idx+1
lines.insert(insert_idx, "")
lines.insert(insert_idx+1, "class BaseModel(_BaseModel):")
lines.insert(insert_idx+2, " model_config = ConfigDict(populate_by_name=True)")
lines.insert(insert_idx+3, "")
break
ifline.startswith("from pydantic import "):
imports= [part.strip() forpartinline[len("from pydantic import ") :].split(",")]
has_alias=any(part=="BaseModel as _BaseModel"forpartinimports)
has_config=any(part=="ConfigDict"forpartinimports)
new_imports= []
forpartinimports:
ifpart=="BaseModel":
new_imports.append("BaseModel as _BaseModel")
has_alias=True
else:
new_imports.append(part)
ifnothas_alias:
new_imports.append("BaseModel as _BaseModel")
ifnothas_config:
new_imports.append("ConfigDict")
lines[idx] ="from pydantic import "+", ".join(new_imports)
insert_idx=idx+1
lines.insert(insert_idx, "")
lines.insert(insert_idx+1, "class BaseModel(_BaseModel):")
lines.insert(insert_idx+2, " model_config = ConfigDict(populate_by_name=True)")
lines.insert(insert_idx+3, "")
break

Copilot uses AI. Check for mistakes.
Comment threadscripts/gen_schema.py
Comment on lines +232 to +243
has_alias = any(part == "BaseModel as _BaseModel" for part in imports)
has_config = any(part == "ConfigDict" for part in imports)
new_imports = []
for part in imports:
if part == "BaseModel":
new_imports.append("BaseModel as _BaseModel")
has_alias = True
else:
new_imports.append(part)
if not has_alias:
new_imports.append("BaseModel as _BaseModel")
if not has_config:

CopilotAINov 2, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The conditions if not has_alias and if not has_config will always be False when the loop at line 235-240 already found BaseModel and converted it to the alias. Since line 232-233 check for existing alias/config, and lines 236-238 ensure the alias is added when BaseModel is found, these additional checks are redundant. The logic should either rely on the existing checks (lines 232-233) or simplify this section.

Suggested change
has_alias=any(part=="BaseModel as _BaseModel"forpartinimports)
has_config=any(part=="ConfigDict"forpartinimports)
new_imports= []
forpartinimports:
ifpart=="BaseModel":
new_imports.append("BaseModel as _BaseModel")
has_alias=True
else:
new_imports.append(part)
ifnothas_alias:
new_imports.append("BaseModel as _BaseModel")
ifnothas_config:
new_imports= []
forpartinimports:
ifpart=="BaseModel":
new_imports.append("BaseModel as _BaseModel")
else:
new_imports.append(part)
# Ensure "BaseModel as _BaseModel" and "ConfigDict" are present
if"BaseModel as _BaseModel"notinnew_imports:
new_imports.append("BaseModel as _BaseModel")
if"ConfigDict"notinnew_imports:

Copilot uses AI. Check for mistakes.
@PsiACE
PsiACE merged commit 893fcf4 into mainNov 2, 2025
12 checks passed
@PsiACE
PsiACE deleted the fix-meta branch November 3, 2025 17:29
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

_meta field is not included at serialization

2 participants

@PsiACE