pyagentic-core 2.15.1.dev1__tar.gz → 2.16.0a1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/PKG-INFO +1 -1
  2. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/docs/inheritance.md +28 -8
  3. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/mkdocs.yml +1 -0
  4. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic/_base/_agent/_agent.py +10 -5
  5. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic/_base/_agent/_agent_state.py +26 -6
  6. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic/_base/_exceptions.py +3 -2
  7. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic/_base/_metaclasses.py +30 -5
  8. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic_core.egg-info/PKG-INFO +1 -1
  9. pyagentic_core-2.16.0a1/pyagentic_core.egg-info/scm_version.json +8 -0
  10. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/tests/_base/test_agent_inheritance.py +131 -1
  11. pyagentic_core-2.15.1.dev1/pyagentic_core.egg-info/scm_version.json +0 -8
  12. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  13. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  14. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/.github/workflows/docs.yml +0 -0
  15. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/.github/workflows/release.yml +0 -0
  16. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/.github/workflows/testing.yml +0 -0
  17. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/.gitignore +0 -0
  18. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/CHANGELOG.md +0 -0
  19. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/LICENSE +0 -0
  20. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/README.md +0 -0
  21. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/docs/agent-linking.md +0 -0
  22. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/docs/api/creating-an-app.md +0 -0
  23. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/docs/api/deploying.md +0 -0
  24. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/docs/api/index.md +0 -0
  25. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/docs/api/jobs.md +0 -0
  26. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/docs/api/running.md +0 -0
  27. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/docs/diagrams/declaration.svg +0 -0
  28. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/docs/diagrams/instantiation.svg +0 -0
  29. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/docs/diagrams/runtime.svg +0 -0
  30. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/docs/diagrams/source/README.md +0 -0
  31. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/docs/diagrams/source/declaration.d2 +0 -0
  32. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/docs/diagrams/source/instantiation.d2 +0 -0
  33. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/docs/diagrams/source/runtime.d2 +0 -0
  34. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/docs/execution-modes.md +0 -0
  35. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/docs/getting-started.md +0 -0
  36. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/docs/images/langfuse.png +0 -0
  37. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/docs/index.md +0 -0
  38. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/docs/observability.md +0 -0
  39. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/docs/phases.md +0 -0
  40. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/docs/policies/built-in.md +0 -0
  41. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/docs/policies/custom.md +0 -0
  42. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/docs/policies/index.md +0 -0
  43. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/docs/prompts.md +0 -0
  44. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/docs/reference/architecture.md +0 -0
  45. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/docs/reference/modules.md +0 -0
  46. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/docs/responses.md +0 -0
  47. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/docs/states.md +0 -0
  48. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/docs/structured-output.md +0 -0
  49. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/docs/tools.md +0 -0
  50. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic/__init__.py +0 -0
  51. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic/_base/__init__.py +0 -0
  52. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic/_base/_agent/__init__.py +0 -0
  53. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic/_base/_agent/_agent_linking.py +0 -0
  54. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic/_base/_depends.py +0 -0
  55. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic/_base/_info.py +0 -0
  56. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic/_base/_mcp.py +0 -0
  57. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic/_base/_prompts.py +0 -0
  58. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic/_base/_ref.py +0 -0
  59. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic/_base/_spec.py +0 -0
  60. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic/_base/_state.py +0 -0
  61. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic/_base/_tool.py +0 -0
  62. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic/_base/_validation.py +0 -0
  63. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic/_utils/_typing.py +0 -0
  64. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic/_utils/_warnings.py +0 -0
  65. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic/_version_scheme.py +0 -0
  66. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic/api/__init__.py +0 -0
  67. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic/api/_app.py +0 -0
  68. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic/api/_build.py +0 -0
  69. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic/api/_config.py +0 -0
  70. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic/api/_docker.py +0 -0
  71. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic/api/_mcp_server.py +0 -0
  72. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic/api/_models.py +0 -0
  73. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic/api/_sessions.py +0 -0
  74. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic/api/jobs/__init__.py +0 -0
  75. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic/api/jobs/_models.py +0 -0
  76. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic/api/jobs/_orchestrator.py +0 -0
  77. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic/api/jobs/_routes.py +0 -0
  78. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic/api/jobs/backends/__init__.py +0 -0
  79. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic/api/jobs/backends/_base.py +0 -0
  80. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic/api/jobs/backends/_in_process.py +0 -0
  81. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic/api/jobs/store/__init__.py +0 -0
  82. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic/api/jobs/store/_base.py +0 -0
  83. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic/api/jobs/store/_sqlite.py +0 -0
  84. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic/llm/__init__.py +0 -0
  85. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic/llm/_anthropic.py +0 -0
  86. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic/llm/_gemini.py +0 -0
  87. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic/llm/_mock.py +0 -0
  88. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic/llm/_openai.py +0 -0
  89. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic/llm/_provider.py +0 -0
  90. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic/logging.py +0 -0
  91. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic/models/llm.py +0 -0
  92. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic/models/response.py +0 -0
  93. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic/models/tracing.py +0 -0
  94. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic/policies/__init__.py +0 -0
  95. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic/policies/_events.py +0 -0
  96. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic/policies/_list.py +0 -0
  97. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic/policies/_policy.py +0 -0
  98. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic/policies/messages.py +0 -0
  99. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic/tracing/__init__.py +0 -0
  100. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic/tracing/_basic.py +0 -0
  101. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic/tracing/_langfuse.py +0 -0
  102. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic/tracing/_tracer.py +0 -0
  103. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic/updates.py +0 -0
  104. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic_core.egg-info/SOURCES.txt +0 -0
  105. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic_core.egg-info/dependency_links.txt +0 -0
  106. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic_core.egg-info/requires.txt +0 -0
  107. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic_core.egg-info/scm_file_list.json +0 -0
  108. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyagentic_core.egg-info/top_level.txt +0 -0
  109. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/pyproject.toml +0 -0
  110. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/setup.cfg +0 -0
  111. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/setup.py +0 -0
  112. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/tests/__init__.py +0 -0
  113. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/tests/_base/__init__.py +0 -0
  114. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/tests/_base/test_agent.py +0 -0
  115. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/tests/_base/test_agent_forking.py +0 -0
  116. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/tests/_base/test_agent_linking.py +0 -0
  117. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/tests/_base/test_agent_message_policies.py +0 -0
  118. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/tests/_base/test_agent_provider.py +0 -0
  119. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/tests/_base/test_params.py +0 -0
  120. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/tests/_base/test_phases.py +0 -0
  121. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/tests/_base/test_policy_list.py +0 -0
  122. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/tests/_base/test_prompts.py +0 -0
  123. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/tests/_base/test_state.py +0 -0
  124. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/tests/_base/test_tool.py +0 -0
  125. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/tests/_base/test_validator.py +0 -0
  126. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/tests/api/__init__.py +0 -0
  127. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/tests/api/jobs/__init__.py +0 -0
  128. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/tests/api/jobs/test_orchestrator.py +0 -0
  129. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/tests/api/jobs/test_routes.py +0 -0
  130. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/tests/api/jobs/test_store_sqlite.py +0 -0
  131. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/tests/api/test_app.py +0 -0
  132. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/tests/api/test_build_agent.py +0 -0
  133. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/tests/api/test_config.py +0 -0
  134. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/tests/api/test_construct_model.py +0 -0
  135. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/tests/api/test_dependencies_integration.py +0 -0
  136. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/tests/api/test_docker.py +0 -0
  137. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/tests/api/test_mcp_server.py +0 -0
  138. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/tests/api/test_metaclass_models.py +0 -0
  139. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/tests/api/test_router.py +0 -0
  140. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/tests/api/test_sessions.py +0 -0
  141. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/tests/conftest.py +0 -0
  142. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/tests/llm/__init__.py +0 -0
  143. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/tests/llm/test_message_conversion.py +0 -0
  144. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/tests/models/test_messages.py +0 -0
  145. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/tests/models/test_responses.py +0 -0
  146. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/tests/policies/__init__.py +0 -0
  147. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/tests/policies/test_message_policies.py +0 -0
  148. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/tests/tracing/__init__.py +0 -0
  149. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/tests/tracing/test_basic_tracer.py +0 -0
  150. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/tests/tracing/test_models.py +0 -0
  151. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/tests/tracing/test_tracer.py +0 -0
  152. {pyagentic_core-2.15.1.dev1 → pyagentic_core-2.16.0a1}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyagentic-core
3
- Version: 2.15.1.dev1
3
+ Version: 2.16.0a1
4
4
  Summary: Build LLM Agents in a Pythonic way
5
5
  Author-email: Ryan Mikulec <rmikulec.dev@gmail.com>
6
6
  License: MIT
@@ -4,27 +4,47 @@ PyAgentic supports standard Python inheritance, allowing you to build agent hier
4
4
 
5
5
  ## How Inheritance Works
6
6
 
7
- Agent inheritance follows Python's standard rules with some PyAgentic-specific behaviors. When you inherit from an agent, you get all its tools, context items, and linked agents. However, each agent must define its own instructions to maintain clear identity and purpose.
7
+ Agent inheritance follows Python's standard rules with some PyAgentic-specific behaviors. When you inherit from an agent, you get all its tools, context items, linked agents, and instructions.
8
8
 
9
9
  PyAgentic builds the complete agent schema at class definition time, combining inherited elements with new ones to create a fully-typed, predictable agent interface.
10
10
 
11
11
  ### Inheritance Rules
12
12
 
13
- PyAgentic follows specific rules about what gets inherited and what must be redefined:
14
-
15
- #### What Gets Inherited ✅
13
+ Everything inherits by default:
16
14
 
17
15
  - **Tools** - All `@tool` methods are inherited and can be overridden
18
16
  - **State Fields** - `State` fields inherit with their defaults and types
19
17
  - **Linked Agents** - Agent references are inherited, child classes can add more
20
18
  - **Properties** - Dynamic properties are inherited and overridable
19
+ - **Instructions** - `__instructions__` is inherited from the nearest ancestor when not declared; somewhere in the hierarchy it must be declared
20
+
21
+ When a child declares its own `__instructions__`, it fully replaces the parent's — unless it opts back in with `{{ super }}` (see below).
22
+
23
+ ### Inheriting and Extending Instructions
24
+
25
+ A child that doesn't declare `__instructions__` simply uses its parent's:
26
+
27
+ ```python
28
+ class SupportAgent(BaseAgent):
29
+ __instructions__ = "You help customers troubleshoot {{ product }} issues."
21
30
 
22
- #### What Must Be Redefined ❌
31
+ product: State[str] = spec.State(default="our product")
23
32
 
24
- - **Instructions** - Each agent must define its own `__instructions__` to maintain clear identity
25
- - **Input Templates** - `__input_template__` is not inherited, allowing agent-specific formatting
33
+ class BillingSupportAgent(SupportAgent):
34
+ # No __instructions__ inherits SupportAgent's
35
+ ...
36
+ ```
37
+
38
+ A child that overrides can embed the parent's fully rendered instructions with `{{ super }}`, mirroring Jinja's template inheritance:
39
+
40
+ ```python
41
+ class BillingSupportAgent(SupportAgent):
42
+ __instructions__ = """{{ super }}
43
+
44
+ You specialize in billing: refunds, invoices, and subscription changes."""
45
+ ```
26
46
 
27
- This design ensures that while agents can share functionality, each maintains its own distinct purpose and behavior.
47
+ `{{ super }}` is the parent's instructions rendered with the *same* state, so templates like `{{ product }}` in the parent interpolate as usual. Chains fold through multiple levels — a grandchild's `{{ super }}` includes the parent's `{{ super }}` expansion, and so on. If there is no parent to inherit from, `{{ super }}` renders as an empty string.
28
48
 
29
49
  ## Basic Inheritance
30
50
 
@@ -10,6 +10,7 @@ nav:
10
10
  - Execution Modes: execution-modes.md
11
11
  - Tools: tools.md
12
12
  - State Management: states.md
13
+ - Prompt Management: prompts.md
13
14
  - Phases: phases.md
14
15
  - Policies:
15
16
  - Overview: policies/index.md
@@ -128,10 +128,12 @@ class BaseAgent(metaclass=AgentMeta):
128
128
 
129
129
  Agent definition requires the use of special decorators and class attributes:
130
130
  - @tool: Declares a method as a tool callable by the LLM
131
- - __instructions__: Required class attribute defining the agent's instructions,
132
- either as a string or a PromptRef from a PromptEngine. Rendered with state
133
- into the system message sent to the LLM. (__system_message__ is the
134
- deprecated spelling.)
131
+ - __instructions__: Class attribute defining the agent's instructions, either as
132
+ a string or a PromptRef from a PromptEngine. Rendered with state into the
133
+ system message sent to the LLM. Inherited from the nearest ancestor when
134
+ not declared; an overriding declaration can embed the parent's rendered
135
+ instructions with `{{ super }}`. Required somewhere in the hierarchy.
136
+ (__system_message__ is the deprecated spelling.)
135
137
  - __description__: Optional description used when agent is linked to another agent
136
138
  - __input_template__: Optional template for formatting user input
137
139
  - __response_format__: Optional Pydantic model for structured output
@@ -179,8 +181,11 @@ class BaseAgent(metaclass=AgentMeta):
179
181
  __dependencies__: ClassVar[dict[str, type]] # Depends[T] field -> dependency type
180
182
 
181
183
  # User-set Class Attributes (defined in subclass)
182
- __instructions__: ClassVar[Union[str, PromptRef]] # Required: the agent's instructions
184
+ __instructions__: ClassVar[Union[str, PromptRef]] # The agent's instructions (inheritable)
183
185
  __system_message__: ClassVar[str] # Deprecated: old spelling of __instructions__
186
+ # Overridden ancestor instructions, oldest first (set by metaclass); rendered
187
+ # into __instructions__ as `{{ super }}`
188
+ __parent_instructions__: ClassVar[tuple[Union[str, PromptRef], ...]] = ()
184
189
  __description__: ClassVar[str] # Optional: description for linked agents
185
190
  __input_template__: ClassVar[str] = None # Optional: template for user input
186
191
  __response_format__: ClassVar[Type[BaseModel]] = None # Optional: structured output format
@@ -36,6 +36,10 @@ class _AgentState(BaseModel):
36
36
  model_config = ConfigDict(arbitrary_types_allowed=True)
37
37
 
38
38
  instructions: str | PromptRef
39
+ # Instructions of overridden ancestors, oldest first; rendered into the
40
+ # template as `{{ super }}`. Excluded from dumps so it never leaks into
41
+ # template context or serialized state.
42
+ parent_instructions: tuple = Field(default=(), exclude=True)
39
43
  input_template: Optional[str] = "{{ user_message }}"
40
44
 
41
45
  _machine: Machine = PrivateAttr(default=None)
@@ -44,6 +48,7 @@ class _AgentState(BaseModel):
44
48
  _last_usage: Optional[UsageInfo] = PrivateAttr(default=None)
45
49
  _prompt_source: Optional[PromptSource] = PrivateAttr(default=None)
46
50
  _instructions_template: Template = PrivateAttr(default_factory=lambda: Template(source=""))
51
+ _parent_templates: list[Template] = PrivateAttr(default_factory=list)
47
52
  _input_template: Template = PrivateAttr(
48
53
  default_factory=lambda: Template(source="{{ user_message }}")
49
54
  )
@@ -91,6 +96,14 @@ class _AgentState(BaseModel):
91
96
  )
92
97
 
93
98
  self._instructions_template = Template(source=self.instructions)
99
+
100
+ # Templates for overridden ancestor instructions (oldest first); each may
101
+ # itself be a PromptRef, resolved here just like the main instructions
102
+ self._parent_templates = [
103
+ Template(source=parent.resolve().text if isinstance(parent, PromptRef) else parent)
104
+ for parent in self.parent_instructions
105
+ ]
106
+
94
107
  if self.input_template:
95
108
  self._input_template = Template(source=self.input_template)
96
109
 
@@ -417,16 +430,23 @@ class _AgentState(BaseModel):
417
430
  """
418
431
  Returns the current formatted system message with state fields interpolated.
419
432
 
433
+ When the agent overrides an ancestor's instructions, the ancestor chain is
434
+ rendered first (with the same state context) and exposed to each template
435
+ as `{{ super }}`, mirroring template inheritance in Jinja.
436
+
420
437
  Returns:
421
438
  str: The rendered system message with state values
422
439
  """
423
- # start with all the normal dataclass fields
424
-
425
- # now format your instruction template
440
+ context = self.model_dump()
426
441
  if self.phase:
427
- return self._instructions_template.render(phase=self.phase, **self.model_dump())
428
- else:
429
- return self._instructions_template.render(**self.model_dump())
442
+ context["phase"] = self.phase
443
+
444
+ # Fold the ancestor chain oldest-to-newest so each template's `{{ super }}`
445
+ # is its own parent's fully rendered instructions
446
+ rendered = ""
447
+ for template in (*self._parent_templates, self._instructions_template):
448
+ rendered = template.render(**{**context, "super": rendered})
449
+ return rendered
430
450
 
431
451
  @property
432
452
  def messages(self) -> list[Message]:
@@ -18,12 +18,13 @@ class InvalidToolDefinition(Exception):
18
18
  class InstructionsNotDeclared(Exception):
19
19
  """
20
20
  Exception raised when an Agent subclass is created without declaring __instructions__
21
- (or the deprecated __system_message__).
21
+ (or the deprecated __system_message__) and no ancestor declares one to inherit.
22
22
  """
23
23
 
24
24
  def __init__(self):
25
25
  super().__init__(
26
- "Instructions not declared on agent. Agent must be declared with `__instructions__`"
26
+ "Instructions not declared on agent. Agent must be declared with "
27
+ "`__instructions__` or inherit one from a parent agent"
27
28
  )
28
29
 
29
30
 
@@ -611,6 +611,7 @@ class AgentMeta(type):
611
611
  # Create the state object with instructions and state fields
612
612
  self.state = self.__state_class__(
613
613
  instructions=self.__instructions__,
614
+ parent_instructions=self.__parent_instructions__,
614
615
  input_template=self.__input_template__,
615
616
  **compiled,
616
617
  )
@@ -665,7 +666,10 @@ class AgentMeta(type):
665
666
 
666
667
  Inheritance is respected in MRO order. Tools, state attributes, and linked agents
667
668
  can all be inherited from other agents or mixins.
668
- __instructions__ and __input_template__ are *not* inherited.
669
+ __instructions__ is inherited from the nearest ancestor when not declared. When a
670
+ subclass declares its own __instructions__, the ancestor chain is recorded on
671
+ __parent_instructions__ so the template can embed the parent's rendered
672
+ instructions via `{{ super }}`.
669
673
 
670
674
  Args:
671
675
  name (str): Name of the new class
@@ -700,10 +704,11 @@ class AgentMeta(type):
700
704
  mcs.__BaseAgent__ = cls
701
705
  return cls
702
706
  cls.__abstract_base__ = False
703
- # Verify instructions are set (not inherited); __system_message__ is
704
- # the deprecated spelling and normalizes onto __instructions__
707
+ # Resolve instructions; __system_message__ is the deprecated spelling
708
+ # and normalizes onto __instructions__
709
+ declared = None
705
710
  if "__instructions__" in namespace:
706
- cls.__instructions__ = namespace["__instructions__"]
711
+ declared = namespace["__instructions__"]
707
712
  elif "__system_message__" in namespace:
708
713
  warnings.warn(
709
714
  f"`__system_message__` on {name} is deprecated; "
@@ -711,7 +716,27 @@ class AgentMeta(type):
711
716
  DeprecationWarning,
712
717
  stacklevel=2,
713
718
  )
714
- cls.__instructions__ = namespace["__system_message__"]
719
+ declared = namespace["__system_message__"]
720
+
721
+ inherited_instructions = inherited_namespace.get(
722
+ "__instructions__", inherited_namespace.get("__system_message__")
723
+ )
724
+ if declared is not None:
725
+ cls.__instructions__ = declared
726
+ # When overriding an ancestor's instructions, record the ancestor
727
+ # chain (oldest first) so the template can embed the parent's
728
+ # rendered instructions via `{{ super }}`
729
+ if inherited_instructions is not None:
730
+ cls.__parent_instructions__ = (
731
+ *inherited_namespace.get("__parent_instructions__", ()),
732
+ inherited_instructions,
733
+ )
734
+ else:
735
+ cls.__parent_instructions__ = ()
736
+ elif inherited_instructions is not None:
737
+ # Instructions are inherited from the nearest ancestor;
738
+ # __parent_instructions__ resolves to the declaring ancestor's chain
739
+ cls.__instructions__ = inherited_instructions
715
740
  else:
716
741
  raise InstructionsNotDeclared()
717
742
  # Keep the deprecated attribute readable for backwards compatibility
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyagentic-core
3
- Version: 2.15.1.dev1
3
+ Version: 2.16.0a1
4
4
  Summary: Build LLM Agents in a Pythonic way
5
5
  Author-email: Ryan Mikulec <rmikulec.dev@gmail.com>
6
6
  License: MIT
@@ -0,0 +1,8 @@
1
+ {
2
+ "tag": "2.15.2.dev2",
3
+ "distance": 1,
4
+ "node": "gadae32f31efb39fd485e1da58335278a0b1e6cd0",
5
+ "dirty": true,
6
+ "branch": "feat/inherited-instructions",
7
+ "node_date": "2026-07-09"
8
+ }
@@ -2,7 +2,9 @@ import pytest
2
2
  from pydantic import BaseModel
3
3
 
4
4
  from pyagentic import BaseAgent, AgentExtension, tool, spec, State
5
- from pyagentic._base._exceptions import SystemMessageNotDeclared
5
+ from pyagentic._base._exceptions import InstructionsNotDeclared, SystemMessageNotDeclared
6
+
7
+ _PROVIDER_KWARGS = {"model": "openai::validation", "api_key": "validation"}
6
8
 
7
9
 
8
10
  class TestBasicInheritance:
@@ -128,6 +130,134 @@ class TestAgentExtension:
128
130
  assert "cache" in MyAgent.__tool_defs__
129
131
 
130
132
 
133
+ class TestInstructionInheritance:
134
+ """Test that __instructions__ are inherited and composable via {{ super }}."""
135
+
136
+ def test_child_inherits_parent_instructions(self):
137
+ """Test that a child without __instructions__ uses the parent's."""
138
+
139
+ class ParentAgent(BaseAgent):
140
+ __instructions__ = "I am a parent agent"
141
+
142
+ class ChildAgent(ParentAgent):
143
+ pass
144
+
145
+ assert ChildAgent.__instructions__ == "I am a parent agent"
146
+ child = ChildAgent(**_PROVIDER_KWARGS)
147
+ assert child.state.system_message == "I am a parent agent"
148
+
149
+ def test_no_instructions_anywhere_raises(self):
150
+ """Test that an agent hierarchy with no instructions at all still raises."""
151
+
152
+ with pytest.raises(InstructionsNotDeclared):
153
+
154
+ class NoInstructionsAgent(BaseAgent):
155
+ pass
156
+
157
+ def test_override_injects_parent_via_super(self):
158
+ """Test that an overriding child can embed the parent's instructions."""
159
+
160
+ class ParentAgent(BaseAgent):
161
+ __instructions__ = "I am a parent agent."
162
+
163
+ class ChildAgent(ParentAgent):
164
+ __instructions__ = "{{ super }} I also review code."
165
+
166
+ child = ChildAgent(**_PROVIDER_KWARGS)
167
+ assert child.state.system_message == "I am a parent agent. I also review code."
168
+
169
+ def test_override_without_super_replaces(self):
170
+ """Test that overriding without {{ super }} fully replaces the parent's."""
171
+
172
+ class ParentAgent(BaseAgent):
173
+ __instructions__ = "I am a parent agent."
174
+
175
+ class ChildAgent(ParentAgent):
176
+ __instructions__ = "I am my own agent."
177
+
178
+ child = ChildAgent(**_PROVIDER_KWARGS)
179
+ assert child.state.system_message == "I am my own agent."
180
+
181
+ def test_super_renders_parent_with_state(self):
182
+ """Test that state fields interpolate inside the injected parent template."""
183
+
184
+ class ParentAgent(BaseAgent):
185
+ __instructions__ = "I help the {{ team }} team."
186
+
187
+ team: State[str] = spec.State(default="core")
188
+
189
+ class ChildAgent(ParentAgent):
190
+ __instructions__ = "{{ super }} I specialize in {{ language }}."
191
+
192
+ language: State[str] = spec.State(default="python")
193
+
194
+ child = ChildAgent(team="data", **_PROVIDER_KWARGS)
195
+ assert child.state.system_message == "I help the data team. I specialize in python."
196
+
197
+ def test_super_chains_through_grandparent(self):
198
+ """Test that {{ super }} folds through multiple overriding levels."""
199
+
200
+ class GrandparentAgent(BaseAgent):
201
+ __instructions__ = "Level 1."
202
+
203
+ class ParentAgent(GrandparentAgent):
204
+ __instructions__ = "{{ super }} Level 2."
205
+
206
+ class ChildAgent(ParentAgent):
207
+ __instructions__ = "{{ super }} Level 3."
208
+
209
+ child = ChildAgent(**_PROVIDER_KWARGS)
210
+ assert child.state.system_message == "Level 1. Level 2. Level 3."
211
+
212
+ def test_super_chain_skips_non_declaring_level(self):
213
+ """Test that a non-declaring middle class passes its parent's chain through."""
214
+
215
+ class GrandparentAgent(BaseAgent):
216
+ __instructions__ = "Level 1."
217
+
218
+ class ParentAgent(GrandparentAgent):
219
+ pass
220
+
221
+ class ChildAgent(ParentAgent):
222
+ __instructions__ = "{{ super }} Level 3."
223
+
224
+ child = ChildAgent(**_PROVIDER_KWARGS)
225
+ assert child.state.system_message == "Level 1. Level 3."
226
+
227
+ def test_super_empty_without_parent(self):
228
+ """Test that {{ super }} renders empty when there is nothing to inherit."""
229
+
230
+ class RootAgent(BaseAgent):
231
+ __instructions__ = "{{ super }}Root."
232
+
233
+ agent = RootAgent(**_PROVIDER_KWARGS)
234
+ assert agent.state.system_message == "Root."
235
+
236
+ def test_fork_preserves_inherited_instructions(self):
237
+ """Test that forked agents render the same composed system message."""
238
+
239
+ class ParentAgent(BaseAgent):
240
+ __instructions__ = "I am a parent agent."
241
+
242
+ class ChildAgent(ParentAgent):
243
+ __instructions__ = "{{ super }} I also review code."
244
+
245
+ child = ChildAgent(**_PROVIDER_KWARGS)
246
+ fork = child.fork()
247
+ assert fork.state.system_message == child.state.system_message
248
+
249
+ def test_deprecated_system_message_inherits(self):
250
+ """Test that the deprecated __system_message__ spelling still inherits."""
251
+
252
+ class ParentAgent(BaseAgent):
253
+ __system_message__ = "I am a parent agent"
254
+
255
+ class ChildAgent(ParentAgent):
256
+ pass
257
+
258
+ assert ChildAgent.__instructions__ == "I am a parent agent"
259
+
260
+
131
261
  class TestComplexInheritance:
132
262
  """Test complex inheritance scenarios."""
133
263
 
@@ -1,8 +0,0 @@
1
- {
2
- "tag": "2.15.0a1",
3
- "distance": 1,
4
- "node": "g26af3c3b63e03e136fae8fa6570e033dfc025e82",
5
- "dirty": true,
6
- "branch": "main",
7
- "node_date": "2026-07-09"
8
- }