Skip to content

feat(serialization): add Component serialization convenience APIs#126

Open
pullely-samuel wants to merge 15 commits intooracle:mainfrom
pullely-samuel:112-simplified-export-api
Open

feat(serialization): add Component serialization convenience APIs#126
pullely-samuel wants to merge 15 commits intooracle:mainfrom
pullely-samuel:112-simplified-export-api

Conversation

@pullely-samuel
Copy link

feat(serialization): add Component serialization convenience APIs

Closes #112

Summary

This PR adds convenience serialization and deserialization APIs directly on Component subclasses, so users can call methods like agent.to_json() and Agent.from_yaml(...) without manually instantiating serializer/deserializer classes.

What Changed

  • Added convenience instance methods on Component:
    • to_yaml(...)
    • to_json(...)
    • to_dict(...)
  • Added convenience class methods on Component:
    • from_yaml(...)
    • from_json(...)
    • from_dict(...)
  • Kept implementation thin by delegating to:
    • AgentSpecSerializer
    • AgentSpecDeserializer
  • Preserved support for:
    • disaggregated component export/import
    • component registry loading paths
    • serialization/deserialization plugins
  • Added runtime type guard for class-based deserialization mismatch handling.

Typing Alignment

  • Updated serializer overload typing so signatures match runtime behavior:
    • AgentSpecSerializer.to_json(...) overloads include indent.
    • AgentSpecSerializer.to_dict(...) bool-overload return type includes tuple form when exporting disaggregated components.

Documentation and Examples

  • Updated how-to docs to show convenience method usage.
  • Updated code example to use agent.to_json().
  • Added changelog entry for the new convenience API.

Tests

Extended serialization tests to cover convenience APIs and key edge cases, including:

  • roundtrip serialization/deserialization via direct component methods
  • class-based deserialization and type mismatch behavior
  • base Component deserialization returning concrete types
  • disaggregated loading/export paths (including custom ids)
  • plugin forwarding through convenience methods
  • JSON indentation behavior (including disaggregated output path)
  • agentspec_version forwarding behavior

Validation run:

  • pre-commit run --files <changed files>
  • pytest pyagentspec/tests/serialization/test_serialization.py -q (50 passed)
  • SKIP_LLM_TESTS=1 pytest -q pyagentspec/tests (935 passed, 511 skipped)

Notes

  • Root-level pytest -q includes tests_fuzz and fails in this local environment because optional pythonfuzz is not installed; CI test workflow runs pyagentspec/tests.
  • This PR intentionally focuses on issue feat: simplified agent spec export #112 scope (convenience API + directly related typing/docs/tests).

@pullely-samuel pullely-samuel requested a review from a team March 8, 2026 22:21
@oracle-contributor-agreement
Copy link

Thank you for your pull request and welcome to our community! To contribute, please sign the Oracle Contributor Agreement (OCA).
The following contributors of this PR have not signed the OCA:

To sign the OCA, please create an Oracle account and sign the OCA in Oracle's Contributor Agreement Application.

When signing the OCA, please provide your GitHub username. After signing the OCA and getting an OCA approval from Oracle, this PR will be automatically updated.

If you are an Oracle employee, please make sure that you are a member of the main Oracle GitHub organization, and your membership in this organization is public.

@oracle-contributor-agreement oracle-contributor-agreement bot added the OCA Required At least one contributor does not have an approved Oracle Contributor Agreement. label Mar 8, 2026
@pullely-samuel
Copy link
Author

I signed the Oracle Contributor Agreement (OCA) and received DocuSign confirmation. It is now pending Oracle approver review.

@dhilloulinoracle
Copy link
Contributor

Thank you @pullely-samuel for the contribution!
We will have a look and try to get it merged :)

@oracle-contributor-agreement
Copy link

Thank you for signing the OCA.

@oracle-contributor-agreement oracle-contributor-agreement bot added OCA Verified All contributors have signed the Oracle Contributor Agreement. and removed OCA Required At least one contributor does not have an approved Oracle Contributor Agreement. labels Mar 9, 2026
@pullely-samuel
Copy link
Author

Update since the initial PR commit d244a2e:

Summary

Following review, the main behavioral change is that Component.from_* no longer exposes import_only_referenced_components. That advanced referenced-component import path now stays on AgentSpecDeserializer.from_*, while constructor-like convenience calls such as Agent.from_yaml(...) always return the requested component type.

What Changed

  • Kept the convenience API surface on Component, but narrowed from_yaml(), from_json(), and from_dict() to main-component deserialization only.
  • Preserved the advanced disaggregated loading workflow through AgentSpecDeserializer(..., import_only_referenced_components=True) plus Agent.from_* (..., components_registry=...) for the main typed load.
  • Aligned the Component.to_* and Component.from_* overloads/signatures and docstrings more closely with the underlying serializer/deserializer surfaces.
  • Kept support for components_registry and serialization/deserialization plugins on the convenience APIs.
  • Updated the serialization tests to cover the intended split workflow and cleaned up some repeated test setup/helpers.
  • Updated the docs/examples so the simple agent guide shows the convenience roundtrip Agent.to_* / Agent.from_*, while the disaggregated-config guide now shows the intended split between AgentSpecDeserializer and Agent.from_yaml(...).

Validation

  • pytest pyagentspec/tests/serialization/test_serialization.py -q (50 passed)
  • pytest pyagentspec/tests/test_disaggregatedconfig.py -q (23 passed)

Relevant Examples

# .. start-export-config-to-agentspec
serialized_assistant = agent.to_json()
# .. end-export-config-to-agentspec
# .. start-import-config-from-agentspec
loaded_agent = Agent.from_json(serialized_assistant)
# .. end-import-config-from-agentspec

# .. start-export-deserialization:
deserializer = AgentSpecDeserializer()
component_registry = deserializer.from_yaml(
disagg_yaml,
import_only_referenced_components=True,
)
# Change the components dynamically
# For example, in this case we want to use a different LLM from the one we built the agent with
llm_config_prod = OpenAiCompatibleConfig(
name="llm-prod",
model_id="llm_model_2",
url="http://prod.llm.url",
)
component_registry["llm_config"] = llm_config_prod
# The `client_weather_tool` remains the one that was deserialized from `disagg_yaml`
# Load the agent with the updated component registry
loaded_agent = Agent.from_yaml(
main_yaml,
components_registry=component_registry,
)
# .. end-export-deserialization:

Copy link
Member

@cesarebernardis cesarebernardis left a comment

Choose a reason for hiding this comment

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

Thank you for the contribution!

@dhilloulinoracle
Copy link
Contributor

Internal regression failed: Build ID #378

pullely-samuel and others added 13 commits March 19, 2026 09:37
Add convenience methods on Component for serialization and deserialization:

- to_yaml(), to_json(), to_dict()
- from_yaml(), from_json(), from_dict()

Keep behavior consistent by delegating to AgentSpecSerializer and
AgentSpecDeserializer, including disaggregated configuration support and
plugin forwarding.

Update documentation and examples to show direct component methods, and add
serialization tests for roundtrips, type checks, disaggregated loading,
plugin integration, and formatting behavior.

Also align serializer overload typing with runtime behavior for to_json()
and to_dict().

Signed-off-by: Samuel Pullely <236219770+pullely-samuel@users.noreply.github.com>
Mirror the serializer-style overload coverage for Component.to_yaml(), Component.to_json(), and Component.to_dict().

Move the wrapper-specific `plugins` argument to the end and make it keyword-only in the runtime signatures, while keeping `indent` positional-or-keyword for `to_json()` to match serializer behavior.

This improves completeness and consistency of the convenience API while keeping the implementation thin and delegated to AgentSpecSerializer.

Signed-off-by: Samuel Pullely <236219770+pullely-samuel@users.noreply.github.com>
Add serializer-style example pointers to Component.to_yaml() and
Component.to_json(), and update the Component.to_dict() examples to
mirror the serializer docs while using the convenience API.

This keeps the convenience-method documentation consistent with the
underlying serializer docs and makes the two surfaces easier to compare.

Signed-off-by: Samuel Pullely <236219770+pullely-samuel@users.noreply.github.com>
Remove the import_only_referenced_components parameter from
Component.from_yaml(), Component.from_json(), and Component.from_dict(),
and always deserialize the main component in the inner
AgentSpecDeserializer call.

Referenced-component imports remain available through
AgentSpecDeserializer, which is the appropriate advanced API for
loading disaggregated configurations into a components registry.

This keeps convenience APIs such as Agent.from_yaml() and
Flow.from_dict() predictable by ensuring they always return the
requested component type.

Update the serialization tests accordingly.

Signed-off-by: Samuel Pullely <236219770+pullely-samuel@users.noreply.github.com>
…ializer

Mirror the deserializer-style overload coverage for Component.from_yaml(),
Component.from_json(), and Component.from_dict() after removing the
import_only_referenced_components convenience path.

Keep the convenience API focused on main-component deserialization while
preserving consistent overload coverage and keyword-only handling for the
wrapper-specific plugins argument.

This improves parity between Component and AgentSpecDeserializer without
reintroducing the unstable dict-returning behavior on the classmethods.

Signed-off-by: Samuel Pullely <236219770+pullely-samuel@users.noreply.github.com>
Align the Component.from_yaml(), Component.from_json(), and
Component.from_dict() docstrings more closely with the corresponding
AgentSpecDeserializer docstrings where that still fits the convenience
API.

Add example pointers to the YAML and JSON helpers, and update the
from_dict() examples to use the convenience API for the normal path
while keeping AgentSpecDeserializer for the advanced referenced-component
import workflow.

Keep the return-value documentation convenience-specific so it still
reflects the typed ComponentT/cls contract.

Signed-off-by: Samuel Pullely <236219770+pullely-samuel@users.noreply.github.com>
Add a simplest_agent fixture to the serialization test conftest and use
it in the existing convenience-API serialization tests that previously
repeated the same inline Agent construction.

This reduces duplication in test_serialization.py while keeping the
current test coverage and behavior unchanged.

Signed-off-by: Samuel Pullely <236219770+pullely-samuel@users.noreply.github.com>
Rename the temporary loaded_references variable in the serialization
tests to referenced_components for consistency with the
disaggregated-loading terminology.

Signed-off-by: Samuel Pullely <236219770+pullely-samuel@users.noreply.github.com>
Rename the custom plugin-only helper components used in the
serialization tests to make their nested/container roles easier to
understand when reading the plugin and disaggregation roundtrip cases.

Signed-off-by: Samuel Pullely <236219770+pullely-samuel@users.noreply.github.com>
Add Agent.from_* convenience deserialization examples to the simple
agent how-to, and update the disaggregated configuration guide to show
the intended split workflow: AgentSpecDeserializer for importing
referenced components and Agent.from_yaml() for loading the main agent.

This keeps the docs aligned with the convenience API surface and the
advanced disaggregated loading workflow.

Signed-off-by: Samuel Pullely <236219770+pullely-samuel@users.noreply.github.com>
Move the Agent import back inside the Sphinx snippet boundaries in the
disaggregated configuration example and mark the file to skip isort so
future formatting does not pull shared imports above the snippet
markers.

This keeps the rendered documentation examples complete and easier to
read on their own.

Signed-off-by: Samuel Pullely <236219770+pullely-samuel@users.noreply.github.com>
Update the convenience serialization API changelog entry to reflect the
final API split for disaggregated configurations: convenience
deserialization uses `components_registry`, while referenced-component
imports remain available through
`AgentSpecDeserializer(..., import_only_referenced_components=True)`.

Signed-off-by: Samuel Pullely <236219770+pullely-samuel@users.noreply.github.com>
@cesarebernardis cesarebernardis force-pushed the 112-simplified-export-api branch from e348c09 to 80777b3 Compare March 19, 2026 08:46
@dhilloulinoracle
Copy link
Contributor

Internal regression failed: Build ID #379

@cesarebernardis cesarebernardis force-pushed the 112-simplified-export-api branch from 53c5fc5 to 6c3ecd6 Compare March 20, 2026 08:52
@dhilloulinoracle
Copy link
Contributor

Internal regression failed: Build ID #383

@dhilloulinoracle
Copy link
Contributor

Internal regression succeeded 🍏: Build ID #384

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

OCA Verified All contributors have signed the Oracle Contributor Agreement.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: simplified agent spec export

3 participants