Skip to content

Conversation

@PsiACE
Copy link
Member

@PsiACE PsiACE commented Nov 2, 2025

Fix: #18

image

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

Copilot AI left a comment

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
File Description
src/acp/schema.py Adds custom BaseModel class with populate_by_name=True configuration to support field aliases
src/acp/utils.py Updates serialize_params to use by_alias=True for proper JSON field name mapping
src/acp/connection.py Updates response serialization to use aliases and additional JSON-safe options
scripts/gen_schema.py Adds schema generation logic to automatically inject custom BaseModel configuration
tests/test_utils.py Adds test coverage for alias serialization behavior
examples/echo_agent.py Demonstrates 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 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
Copy link

Copilot AI Nov 2, 2025

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
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
if line.startswith("from pydantic import "):
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

Copilot uses AI. Check for mistakes.
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:
Copy link

Copilot AI Nov 2, 2025

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" 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 = []
for part in imports:
if part == "BaseModel":
new_imports.append("BaseModel as _BaseModel")
else:
new_imports.append(part)
# Ensure "BaseModel as _BaseModel" and "ConfigDict" are present
if "BaseModel as _BaseModel" not in new_imports:
new_imports.append("BaseModel as _BaseModel")
if "ConfigDict" not in new_imports:

Copilot uses AI. Check for mistakes.
@PsiACE PsiACE merged commit 893fcf4 into main Nov 2, 2025
12 checks passed
@PsiACE PsiACE deleted the fix-meta branch November 3, 2025 17:29
Sign up for free to 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