From b7a4fe4aac7f936ee9320b9043d5c13452cf421f Mon Sep 17 00:00:00 2001 From: Youssef Boughizane Date: Wed, 10 Dec 2025 16:53:26 +0100 Subject: [PATCH 01/26] feat: datastores in agent-spec --- .../src/pyagentspec/_component_registry.py | 17 ++ .../src/pyagentspec/datastores/__init__.py | 23 +++ .../src/pyagentspec/datastores/datastore.py | 44 +++++ .../src/pyagentspec/datastores/oracle.py | 54 +++++++ .../src/pyagentspec/datastores/postgres.py | 55 +++++++ pyagentspec/tests/test_datastores.py | 151 ++++++++++++++++++ 6 files changed, 344 insertions(+) create mode 100644 pyagentspec/src/pyagentspec/datastores/__init__.py create mode 100644 pyagentspec/src/pyagentspec/datastores/datastore.py create mode 100644 pyagentspec/src/pyagentspec/datastores/oracle.py create mode 100644 pyagentspec/src/pyagentspec/datastores/postgres.py create mode 100644 pyagentspec/tests/test_datastores.py diff --git a/pyagentspec/src/pyagentspec/_component_registry.py b/pyagentspec/src/pyagentspec/_component_registry.py index 8022d20f..76c2d761 100644 --- a/pyagentspec/src/pyagentspec/_component_registry.py +++ b/pyagentspec/src/pyagentspec/_component_registry.py @@ -12,6 +12,16 @@ from pyagentspec.agent import Agent from pyagentspec.agenticcomponent import AgenticComponent from pyagentspec.component import Component, ComponentWithIO +from pyagentspec.datastores.datastore import Datastore, InMemoryCollectionDatastore +from pyagentspec.datastores.oracle import ( + MTlsOracleDatabaseConnectionConfig, + OracleDatabaseDatastore, + TlsOracleDatabaseConnectionConfig, +) +from pyagentspec.datastores.postgres import ( + PostgresDatabaseDatastore, + TlsPostgresDatabaseConnectionConfig, +) from pyagentspec.flows.edges.controlflowedge import ControlFlowEdge from pyagentspec.flows.edges.dataflowedge import DataFlowEdge from pyagentspec.flows.flow import Flow @@ -84,9 +94,11 @@ "BuiltinTool": BuiltinTool, "ControlFlowEdge": ControlFlowEdge, "DataFlowEdge": DataFlowEdge, + "Datastore": Datastore, "EndNode": EndNode, "Flow": Flow, "FlowNode": FlowNode, + "InMemoryCollectionDatastore": InMemoryCollectionDatastore, "InputMessageNode": InputMessageNode, "LlmConfig": LlmConfig, "LlmNode": LlmNode, @@ -105,6 +117,8 @@ "OllamaConfig": OllamaConfig, "OpenAiCompatibleConfig": OpenAiCompatibleConfig, "OpenAiConfig": OpenAiConfig, + "OracleDatabaseDatastore": OracleDatabaseDatastore, + "PostgresDatabaseDatastore": PostgresDatabaseDatastore, "OutputMessageNode": OutputMessageNode, "RemoteTool": RemoteTool, "RemoteTransport": RemoteTransport, @@ -120,6 +134,9 @@ "Tool": Tool, "ToolBox": ToolBox, "ToolNode": ToolNode, + "TlsOracleDatabaseConnectionConfig": TlsOracleDatabaseConnectionConfig, + "MTlsOracleDatabaseConnectionConfig": MTlsOracleDatabaseConnectionConfig, + "TlsPostgresDatabaseConnectionConfig": TlsPostgresDatabaseConnectionConfig, "VllmConfig": VllmConfig, "Swarm": Swarm, "ManagerWorkers": ManagerWorkers, diff --git a/pyagentspec/src/pyagentspec/datastores/__init__.py b/pyagentspec/src/pyagentspec/datastores/__init__.py new file mode 100644 index 00000000..44246f5e --- /dev/null +++ b/pyagentspec/src/pyagentspec/datastores/__init__.py @@ -0,0 +1,23 @@ +# Copyright © 2025 Oracle and/or its affiliates. +# +# This software is under the Apache License 2.0 +# (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) or Universal Permissive License +# (UPL) 1.0 (LICENSE-UPL or https://oss.oracle.com/licenses/upl), at your option. + +from .datastore import Datastore, InMemoryCollectionDatastore, RelationalDatastore +from .oracle import ( + MTlsOracleDatabaseConnectionConfig, + OracleDatabaseConnectionConfig, + OracleDatabaseDatastore, + TlsOracleDatabaseConnectionConfig, +) + +__all__ = [ + "Datastore", + "RelationalDatastore", + "InMemoryCollectionDatastore", + "OracleDatabaseConnectionConfig", + "OracleDatabaseDatastore", + "TlsOracleDatabaseConnectionConfig", + "MTlsOracleDatabaseConnectionConfig", +] diff --git a/pyagentspec/src/pyagentspec/datastores/datastore.py b/pyagentspec/src/pyagentspec/datastores/datastore.py new file mode 100644 index 00000000..ea339ac9 --- /dev/null +++ b/pyagentspec/src/pyagentspec/datastores/datastore.py @@ -0,0 +1,44 @@ +# Copyright © 2025 Oracle and/or its affiliates. +# +# This software is under the Apache License 2.0 +# (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) or Universal Permissive License +# (UPL) 1.0 (LICENSE-UPL or https://oss.oracle.com/licenses/upl), at your option. +"""This module defines the base, relational and in-memory datastore component.""" +from typing import Annotated, Dict + +from pydantic import AfterValidator + +from pyagentspec.component import Component +from pyagentspec.property import Property + + +def is_object_property(value: Property) -> Property: + schema = value.json_schema + if schema.get("type") == "object" and "properties" in schema: + return value + raise ValueError( + "Property is not a valid entity property. Entity properties must be objects with properties." + ) + + +Entity = Annotated[Property, AfterValidator(is_object_property)] + + +class Datastore(Component, abstract=True): + """Base class for Datastores. Datastores store and retrive data.""" + + +class RelationalDatastore(Datastore, abstract=True): + """A relational data store that supports querying data using SQL-like queries. + As a consequence, it has a fixed schema. + """ + + datastore_schema: Dict[str, Entity] + """Mapping of collection names to entity definitions used by this datastore.""" + + +class InMemoryCollectionDatastore(Datastore): + """In-memory datastore for testing and development purposes.""" + + datastore_schema: Dict[str, Entity] + """Mapping of collection names to entity definitions used by this datastore.""" diff --git a/pyagentspec/src/pyagentspec/datastores/oracle.py b/pyagentspec/src/pyagentspec/datastores/oracle.py new file mode 100644 index 00000000..749a7d5f --- /dev/null +++ b/pyagentspec/src/pyagentspec/datastores/oracle.py @@ -0,0 +1,54 @@ +# Copyright © 2025 Oracle and/or its affiliates. +# +# This software is under the Apache License 2.0 +# (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) or Universal Permissive License +# (UPL) 1.0 (LICENSE-UPL or https://oss.oracle.com/licenses/upl), at your option. + +from typing import Literal, Optional + +from pyagentspec.component import Component +from pyagentspec.sensitive_field import SensitiveField + +from .datastore import RelationalDatastore + + +class OracleDatabaseConnectionConfig(Component, abstract=True): + """Base class used for configuring connections to Oracle Database.""" + + +class OracleDatabaseDatastore(RelationalDatastore): + """Datastore that uses Oracle Database as the storage mechanism.""" + + connection_config: OracleDatabaseConnectionConfig + + +class TlsOracleDatabaseConnectionConfig(OracleDatabaseConnectionConfig): + """TLS Connection Configuration to Oracle Database.""" + + user: SensitiveField[str] + """User used to connect to the database""" + + password: SensitiveField[str] + """Password for the provided user""" + + dsn: SensitiveField[str] + """Connection string for the database (e.g., created using `oracledb.make_dsn`)""" + + config_dir: Optional[str] = None + """Configuration directory for the database connection. Set this if you are using an + alias from your tnsnames.ora files as a DSN. Make sure that the specified DSN is + appropriate for TLS connections (as the tnsnames.ora file in a downloaded wallet + will only include DSN entries for mTLS connections)""" + + protocol: Literal["tcp", "tcps"] = "tcps" + """'tcp' or 'tcps' indicating whether to use unencrypted network traffic or encrypted network traffic (TLS)""" + + +class MTlsOracleDatabaseConnectionConfig(TlsOracleDatabaseConnectionConfig): + """Mutual-TLS Connection Configuration to Oracle Database.""" + + wallet_location: SensitiveField[str] + """Location where the Oracle Database wallet is stored.""" + + wallet_password: SensitiveField[str] + """Password for the provided wallet.""" diff --git a/pyagentspec/src/pyagentspec/datastores/postgres.py b/pyagentspec/src/pyagentspec/datastores/postgres.py new file mode 100644 index 00000000..ae190b53 --- /dev/null +++ b/pyagentspec/src/pyagentspec/datastores/postgres.py @@ -0,0 +1,55 @@ +# Copyright © 2025 Oracle and/or its affiliates. +# +# This software is under the Apache License 2.0 +# (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) or Universal Permissive License +# (UPL) 1.0 (LICENSE-UPL or https://oss.oracle.com/licenses/upl), at your option. + +from typing import Literal, Optional + +from pyagentspec.component import Component +from pyagentspec.sensitive_field import SensitiveField + +from .datastore import RelationalDatastore + + +class PostgresDatabaseConnectionConfig(Component, abstract=True): + """Base class for a PostgreSQL connection.""" + + +class PostgresDatabaseDatastore(RelationalDatastore): + """Datastore that uses PostgreSQL Database as the storage mechanism.""" + + connection_config: PostgresDatabaseConnectionConfig + + +class TlsPostgresDatabaseConnectionConfig(PostgresDatabaseConnectionConfig): + """Configuration for a PostgreSQL connection with TLS/SSL support.""" + + user: SensitiveField[str] + """User of the postgres database""" + + password: SensitiveField[str] + """Password of the postgres database""" + + url: str + """URL to access the postgres database""" + + sslmode: Literal["disable", "allow", "prefer", "require", "verify-ca", "verify-full"] = ( + "require" + ) + """SSL mode for the PostgreSQL connection.""" + + sslcert: Optional[str] = None + """Path of the client SSL certificate, replacing the default `~/.postgresql/postgresql.crt`. + Ignored if an SSL connection is not made.""" + + sslkey: Optional[SensitiveField[str]] = None + """Path of the file containing the secret key used for the client certificate, replacing the default + `~/.postgresql/postgresql.key`. Ignored if an SSL connection is not made.""" + + sslrootcert: Optional[str] = None + """Path of the file containing SSL certificate authority (CA) certificate(s). Used to verify server identity.""" + + sslcrl: Optional[str] = None + """Path of the SSL server certificate revocation list (CRL). Certificates listed will be rejected + while attempting to authenticate the server's certificate.""" diff --git a/pyagentspec/tests/test_datastores.py b/pyagentspec/tests/test_datastores.py new file mode 100644 index 00000000..7ea06f44 --- /dev/null +++ b/pyagentspec/tests/test_datastores.py @@ -0,0 +1,151 @@ +# Copyright © 2025 Oracle and/or its affiliates. +# +# This software is under the Apache License 2.0 +# (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) or Universal Permissive License +# (UPL) 1.0 (LICENSE-UPL or https://oss.oracle.com/licenses/upl), at your option. + +from typing import Any + +import pytest + +from pyagentspec.datastores import ( + InMemoryCollectionDatastore, +) +from pyagentspec.datastores.datastore import Datastore +from pyagentspec.datastores.oracle import ( + MTlsOracleDatabaseConnectionConfig, + OracleDatabaseDatastore, + TlsOracleDatabaseConnectionConfig, +) +from pyagentspec.datastores.postgres import ( + PostgresDatabaseDatastore, + TlsPostgresDatabaseConnectionConfig, +) +from pyagentspec.property import ( + FloatProperty, + IntegerProperty, + ObjectProperty, + Property, + StringProperty, +) +from pyagentspec.serialization.deserializer import AgentSpecDeserializer +from pyagentspec.serialization.serializer import AgentSpecSerializer + +FAKE_PASSWORD = "testpass" # nosec B105 +FAKE_TESTUSER = "testuser" +FAKE_DSN = "testdsn" + +SCHEMA = { + "cache_collection": ObjectProperty( + properties={ + "content": StringProperty(), + "ttl": IntegerProperty(), + "created_at": FloatProperty(), + "last_used_at": FloatProperty(), + "metadata": Property(json_schema={}), + } + ) +} + + +def in_memory_datastore(schema): + return InMemoryCollectionDatastore(name="our_inmem_ds", datastore_schema=schema) + + +def oracle_datastore_tls(schema): + config = TlsOracleDatabaseConnectionConfig( + id="myconfig", + name="test_config", + user=FAKE_TESTUSER, + password=FAKE_PASSWORD, + dsn=FAKE_DSN, + protocol="tcps", + config_dir="/configdir", + ) + return OracleDatabaseDatastore( + id="my_ds", name="oracle_ds", datastore_schema=schema, connection_config=config + ) + + +def oracle_datastore_mtls(schema): + config = MTlsOracleDatabaseConnectionConfig( + id="myconfig_mtls", + name="test_config_mtls", + user=FAKE_TESTUSER, + password=FAKE_PASSWORD, + dsn=FAKE_DSN, + protocol="tcps", + config_dir="/configdir", + wallet_location="/wallet/location", + wallet_password="walletpass", # nosec B106 + ) + return OracleDatabaseDatastore( + id="my_ds_mtls", name="oracle_ds_mtls", datastore_schema=schema, connection_config=config + ) + + +def postgres_datastore_tls(schema): + config = TlsPostgresDatabaseConnectionConfig( + id="myconfig_postgres", + name="test_config_postgres", + user=FAKE_TESTUSER, + password=FAKE_PASSWORD, + url="postgresql://localhost:5432/testdb", + sslmode="require", + sslcert="/path/to/client.crt", + sslkey="/path/to/client.key", + sslrootcert="/path/to/ca.crt", + ) + return PostgresDatabaseDatastore( + id="my_ds_postgres", name="postgres_ds", datastore_schema=schema, connection_config=config + ) + + +@pytest.mark.parametrize( + "datastore, sensitive_fields", + [ + ( + oracle_datastore_tls(SCHEMA), + { + "myconfig.user": FAKE_TESTUSER, + "myconfig.password": FAKE_PASSWORD, + "myconfig.dsn": FAKE_DSN, + }, + ), + ( + oracle_datastore_mtls(SCHEMA), + { + "myconfig_mtls.user": FAKE_TESTUSER, + "myconfig_mtls.password": FAKE_PASSWORD, + "myconfig_mtls.dsn": FAKE_DSN, + "myconfig_mtls.wallet_location": "/wallet/location", + "myconfig_mtls.wallet_password": "walletpass", + }, + ), + ( + postgres_datastore_tls(SCHEMA), + { + "myconfig_postgres.user": FAKE_TESTUSER, + "myconfig_postgres.password": FAKE_PASSWORD, + "myconfig_postgres.sslkey": "/path/to/client.key", + }, + ), + (in_memory_datastore(SCHEMA), {}), + ], +) +def test_can_serialize_and_deserialize_datastore( + datastore: Datastore, sensitive_fields: dict[str, Any] +) -> None: + serialized_ds = AgentSpecSerializer().to_yaml(datastore) + print(serialized_ds) + assert len(serialized_ds.strip()) > 0 + deserialized_ds = AgentSpecDeserializer().from_yaml( + yaml_content=serialized_ds, components_registry=sensitive_fields + ) + assert deserialized_ds == datastore + serialized_ds = AgentSpecSerializer().to_json(datastore) + assert len(serialized_ds.strip()) > 0 + deserialized_ds = AgentSpecDeserializer().from_yaml( + yaml_content=serialized_ds, components_registry=sensitive_fields + ) + assert deserialized_ds == datastore From 6b604b7284ce6d6a5c2b3964c9ee2b9a3a602217 Mon Sep 17 00:00:00 2001 From: Youssef Boughizane Date: Thu, 18 Dec 2025 17:12:09 +0100 Subject: [PATCH 02/26] feat: MessageSummarizationTransforms --- .../src/pyagentspec/_component_registry.py | 3 + .../src/pyagentspec/datastores/__init__.py | 6 + .../src/pyagentspec/transforms/__init__.py | 14 +++ .../pyagentspec/transforms/summarization.py | 77 ++++++++++++ .../src/pyagentspec/transforms/transforms.py | 11 ++ pyagentspec/tests/test_datastores.py | 54 ++++----- pyagentspec/tests/transforms/__init__.py | 5 + .../test_summarization_transforms.py | 110 ++++++++++++++++++ 8 files changed, 253 insertions(+), 27 deletions(-) create mode 100644 pyagentspec/src/pyagentspec/transforms/__init__.py create mode 100644 pyagentspec/src/pyagentspec/transforms/summarization.py create mode 100644 pyagentspec/src/pyagentspec/transforms/transforms.py create mode 100644 pyagentspec/tests/transforms/__init__.py create mode 100644 pyagentspec/tests/transforms/test_summarization_transforms.py diff --git a/pyagentspec/src/pyagentspec/_component_registry.py b/pyagentspec/src/pyagentspec/_component_registry.py index 76c2d761..ced13d39 100644 --- a/pyagentspec/src/pyagentspec/_component_registry.py +++ b/pyagentspec/src/pyagentspec/_component_registry.py @@ -77,6 +77,7 @@ from pyagentspec.tools.servertool import ServerTool from pyagentspec.tools.tool import Tool from pyagentspec.tools.toolbox import ToolBox +from pyagentspec.transforms import ConversationSummarizationTransform, MessageSummarizationTransform BUILTIN_CLASS_MAP: Mapping[str, type[Component]] = { "A2AAgent": A2AAgent, @@ -137,6 +138,8 @@ "TlsOracleDatabaseConnectionConfig": TlsOracleDatabaseConnectionConfig, "MTlsOracleDatabaseConnectionConfig": MTlsOracleDatabaseConnectionConfig, "TlsPostgresDatabaseConnectionConfig": TlsPostgresDatabaseConnectionConfig, + "MessageSummarizationTransform": MessageSummarizationTransform, + "ConversationSummarizationTransform": ConversationSummarizationTransform, "VllmConfig": VllmConfig, "Swarm": Swarm, "ManagerWorkers": ManagerWorkers, diff --git a/pyagentspec/src/pyagentspec/datastores/__init__.py b/pyagentspec/src/pyagentspec/datastores/__init__.py index 44246f5e..e050da9a 100644 --- a/pyagentspec/src/pyagentspec/datastores/__init__.py +++ b/pyagentspec/src/pyagentspec/datastores/__init__.py @@ -11,6 +11,10 @@ OracleDatabaseDatastore, TlsOracleDatabaseConnectionConfig, ) +from .postgres import ( + PostgresDatabaseDatastore, + TlsPostgresDatabaseConnectionConfig, +) __all__ = [ "Datastore", @@ -20,4 +24,6 @@ "OracleDatabaseDatastore", "TlsOracleDatabaseConnectionConfig", "MTlsOracleDatabaseConnectionConfig", + "PostgresDatabaseDatastore", + "TlsPostgresDatabaseConnectionConfig", ] diff --git a/pyagentspec/src/pyagentspec/transforms/__init__.py b/pyagentspec/src/pyagentspec/transforms/__init__.py new file mode 100644 index 00000000..19f5998c --- /dev/null +++ b/pyagentspec/src/pyagentspec/transforms/__init__.py @@ -0,0 +1,14 @@ +# Copyright © 2025 Oracle and/or its affiliates. +# +# This software is under the Apache License 2.0 +# (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) or Universal Permissive License +# (UPL) 1.0 (LICENSE-UPL or https://oss.oracle.com/licenses/upl), at your option. + +from .summarization import ConversationSummarizationTransform, MessageSummarizationTransform +from .transforms import MessageTransform + +__all__ = [ + "MessageTransform", + "MessageSummarizationTransform", + "ConversationSummarizationTransform", +] diff --git a/pyagentspec/src/pyagentspec/transforms/summarization.py b/pyagentspec/src/pyagentspec/transforms/summarization.py new file mode 100644 index 00000000..d0bbfd3f --- /dev/null +++ b/pyagentspec/src/pyagentspec/transforms/summarization.py @@ -0,0 +1,77 @@ +# Copyright © 2025 Oracle and/or its affiliates. +# +# This software is under the Apache License 2.0 +# (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) or Universal Permissive License +# (UPL) 1.0 (LICENSE-UPL or https://oss.oracle.com/licenses/upl), at your option. + +from typing import Optional + +from pyagentspec.datastores import Datastore +from pyagentspec.llms import LlmConfig + +from .transforms import MessageTransform + + +class MessageSummarizationTransform(MessageTransform): + llm: LlmConfig + """LLM configuration for summarization.""" + + max_message_size: int = 20_000 + """Maximum message size in characters before triggering summarization.""" + + summarization_instructions: str = ( + "Please make a summary of this message. Include relevant information and keep it short. " + "Your response will replace the message, so just output the summary directly, no introduction needed." + ) + """Instructions for the LLM on how to summarize messages.""" + + summarized_message_template: str = "Summarized message: {{summary}}" + """Template for formatting the summarized message output.""" + + datastore: Optional[Datastore] = None + """ + Datastore on which to store the cache. If None, an in-memory Datastore will be created automatically. + """ + + max_cache_size: Optional[int] = 10_000 + """Maximum number of cache entries to keep.""" + + max_cache_lifetime: Optional[int] = 4 * 3600 + """Maximum lifetime of cache entries in seconds.""" + + cache_collection_name: str = "summarized_messages_cache" + """Name of the collection in the datastore for caching summarized messages.""" + + +class ConversationSummarizationTransform(MessageTransform): + llm: LlmConfig + """LLM configuration for conversation summarization.""" + + max_num_messages: int = 50 + """Maximum number of messages before triggering summarization.""" + + min_num_messages: int = 10 + """Minimum number of recent messages to keep unsummarized.""" + + summarization_instructions: str = ( + "Please make a summary of this conversation. Include relevant information and keep it short. " + "Your response will replace the messages, so just output the summary directly, no introduction needed." + ) + """Instructions for the LLM on how to summarize conversations.""" + + summarized_conversation_template: str = "Summarized conversation: {{summary}}" + """Template for formatting the summarized conversation output.""" + + datastore: Optional[Datastore] = None + """ + Datastore on which to store the cache. If None, an in-memory Datastore will be created automatically. + """ + + max_cache_size: Optional[int] = 10_000 + """Maximum number of cache entries to keep.""" + + max_cache_lifetime: Optional[int] = 4 * 3600 + """Maximum lifetime of cache entries in seconds.""" + + cache_collection_name: str = "summarized_conversations_cache" + """Name of the collection in the datastore for caching summarized conversations.""" diff --git a/pyagentspec/src/pyagentspec/transforms/transforms.py b/pyagentspec/src/pyagentspec/transforms/transforms.py new file mode 100644 index 00000000..e6a3bc6a --- /dev/null +++ b/pyagentspec/src/pyagentspec/transforms/transforms.py @@ -0,0 +1,11 @@ +# Copyright © 2025 Oracle and/or its affiliates. +# +# This software is under the Apache License 2.0 +# (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) or Universal Permissive License +# (UPL) 1.0 (LICENSE-UPL or https://oss.oracle.com/licenses/upl), at your option. + +from pyagentspec.component import Component + + +class MessageTransform(Component, abstract=True): + """Base class for message transformation components.""" diff --git a/pyagentspec/tests/test_datastores.py b/pyagentspec/tests/test_datastores.py index 7ea06f44..b8dca9c8 100644 --- a/pyagentspec/tests/test_datastores.py +++ b/pyagentspec/tests/test_datastores.py @@ -35,6 +35,29 @@ FAKE_TESTUSER = "testuser" FAKE_DSN = "testdsn" +# Sensitive fields registries for different datastore types +ORACLE_TLS_SENSITIVE_FIELDS = { + "myconfig.user": FAKE_TESTUSER, + "myconfig.password": FAKE_PASSWORD, + "myconfig.dsn": FAKE_DSN, +} + +ORACLE_MTLS_SENSITIVE_FIELDS = { + "myconfig_mtls.user": FAKE_TESTUSER, + "myconfig_mtls.password": FAKE_PASSWORD, + "myconfig_mtls.dsn": FAKE_DSN, + "myconfig_mtls.wallet_location": "/wallet/location", + "myconfig_mtls.wallet_password": "walletpass", +} + +POSTGRES_TLS_SENSITIVE_FIELDS = { + "myconfig_postgres.user": FAKE_TESTUSER, + "myconfig_postgres.password": FAKE_PASSWORD, + "myconfig_postgres.sslkey": "/path/to/client.key", +} + +IN_MEMORY_SENSITIVE_FIELDS = {} + SCHEMA = { "cache_collection": ObjectProperty( properties={ @@ -104,33 +127,10 @@ def postgres_datastore_tls(schema): @pytest.mark.parametrize( "datastore, sensitive_fields", [ - ( - oracle_datastore_tls(SCHEMA), - { - "myconfig.user": FAKE_TESTUSER, - "myconfig.password": FAKE_PASSWORD, - "myconfig.dsn": FAKE_DSN, - }, - ), - ( - oracle_datastore_mtls(SCHEMA), - { - "myconfig_mtls.user": FAKE_TESTUSER, - "myconfig_mtls.password": FAKE_PASSWORD, - "myconfig_mtls.dsn": FAKE_DSN, - "myconfig_mtls.wallet_location": "/wallet/location", - "myconfig_mtls.wallet_password": "walletpass", - }, - ), - ( - postgres_datastore_tls(SCHEMA), - { - "myconfig_postgres.user": FAKE_TESTUSER, - "myconfig_postgres.password": FAKE_PASSWORD, - "myconfig_postgres.sslkey": "/path/to/client.key", - }, - ), - (in_memory_datastore(SCHEMA), {}), + (oracle_datastore_tls(SCHEMA), ORACLE_TLS_SENSITIVE_FIELDS), + (oracle_datastore_mtls(SCHEMA), ORACLE_MTLS_SENSITIVE_FIELDS), + (postgres_datastore_tls(SCHEMA), POSTGRES_TLS_SENSITIVE_FIELDS), + (in_memory_datastore(SCHEMA), IN_MEMORY_SENSITIVE_FIELDS), ], ) def test_can_serialize_and_deserialize_datastore( diff --git a/pyagentspec/tests/transforms/__init__.py b/pyagentspec/tests/transforms/__init__.py new file mode 100644 index 00000000..b6a1c6eb --- /dev/null +++ b/pyagentspec/tests/transforms/__init__.py @@ -0,0 +1,5 @@ +# Copyright © 2025 Oracle and/or its affiliates. +# +# This software is under the Apache License 2.0 +# (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) or Universal Permissive License +# (UPL) 1.0 (LICENSE-UPL or https://oss.oracle.com/licenses/upl), at your option. diff --git a/pyagentspec/tests/transforms/test_summarization_transforms.py b/pyagentspec/tests/transforms/test_summarization_transforms.py new file mode 100644 index 00000000..0f3456db --- /dev/null +++ b/pyagentspec/tests/transforms/test_summarization_transforms.py @@ -0,0 +1,110 @@ +# Copyright © 2025 Oracle and/or its affiliates. +# +# This software is under the Apache License 2.0 +# (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) or Universal Permissive License +# (UPL) 1.0 (LICENSE-UPL or https://oss.oracle.com/licenses/upl), at your option. + + +import pytest + +from pyagentspec.llms import OpenAiConfig +from pyagentspec.serialization.deserializer import AgentSpecDeserializer +from pyagentspec.serialization.serializer import AgentSpecSerializer +from pyagentspec.transforms import ConversationSummarizationTransform, MessageSummarizationTransform + +from ..test_datastores import ( + IN_MEMORY_SENSITIVE_FIELDS, + ORACLE_MTLS_SENSITIVE_FIELDS, + ORACLE_TLS_SENSITIVE_FIELDS, + POSTGRES_TLS_SENSITIVE_FIELDS, + SCHEMA, + in_memory_datastore, + oracle_datastore_mtls, + oracle_datastore_tls, + postgres_datastore_tls, +) + + +def create_test_llm_config(): + return OpenAiConfig( + id="test_llm", + name="test_openai_config", + model_id="gpt-3.5-turbo", + ) + + +def create_message_summarization_transform(datastore): + # Use non-default values to ensure serialization/deserialization preserves exact values + return MessageSummarizationTransform( + id="test_msg_summarizer", + name="test_message_summarizer", + llm=create_test_llm_config(), + max_message_size=15_000, + summarization_instructions=( + "Create a concise summary of this message focusing on key points and actionable information." + ), + summarized_message_template="Message summary: {{summary}}", + datastore=datastore, + max_cache_size=2_500, + max_cache_lifetime=8 * 3600, + cache_collection_name="message_summaries_cache", + ) + + +def create_conversation_summarization_transform(datastore): + # Use non-default values to ensure serialization/deserialization preserves exact values + return ConversationSummarizationTransform( + id="test_conv_summarizer", + name="test_conversation_summarizer", + llm=create_test_llm_config(), + max_num_messages=75, + min_num_messages=25, + summarization_instructions=( + "Summarize this conversation thread, highlighting key decisions, action items, and important context." + ), + summarized_conversation_template="Conversation summary: {{summary}}", + datastore=datastore, + max_cache_size=5_000, + max_cache_lifetime=12 * 3600, + cache_collection_name="conversation_summaries_cache", + ) + + +@pytest.mark.parametrize( + "datastore_factory, sensitive_fields", + [ + (in_memory_datastore, IN_MEMORY_SENSITIVE_FIELDS), + (oracle_datastore_tls, ORACLE_TLS_SENSITIVE_FIELDS), + (oracle_datastore_mtls, ORACLE_MTLS_SENSITIVE_FIELDS), + (postgres_datastore_tls, POSTGRES_TLS_SENSITIVE_FIELDS), + ], +) +@pytest.mark.parametrize( + "transform_factory", + [ + create_message_summarization_transform, + create_conversation_summarization_transform, + ], +) +def test_can_serialize_and_deserialize_transform_with_all_datastores( + datastore_factory, sensitive_fields, transform_factory +): + datastore = datastore_factory(SCHEMA) + + transform = transform_factory(datastore) + + serialized_transform = AgentSpecSerializer().to_yaml(transform) + print(serialized_transform) + assert len(serialized_transform.strip()) > 0 + + deserialized_transform = AgentSpecDeserializer().from_yaml( + yaml_content=serialized_transform, components_registry=sensitive_fields + ) + assert deserialized_transform == transform + + serialized_transform = AgentSpecSerializer().to_json(transform) + assert len(serialized_transform.strip()) > 0 + deserialized_transform = AgentSpecDeserializer().from_yaml( + yaml_content=serialized_transform, components_registry=sensitive_fields + ) + assert deserialized_transform == transform From e8d5a06a85b73d58ffd837a63a0f636263d69c97 Mon Sep 17 00:00:00 2001 From: Youssef Boughizane Date: Thu, 18 Dec 2025 18:02:54 +0100 Subject: [PATCH 03/26] feat: add datastores and transforms with tests and docs --- .../source/_components/all_components.json | 23 +++++++ docs/pyagentspec/source/api/datastores.rst | 69 +++++++++++++++++++ docs/pyagentspec/source/api/transforms.rst | 31 +++++++++ docs/pyagentspec/source/changelog.rst | 10 +++ 4 files changed, 133 insertions(+) create mode 100644 docs/pyagentspec/source/api/datastores.rst create mode 100644 docs/pyagentspec/source/api/transforms.rst diff --git a/docs/pyagentspec/source/_components/all_components.json b/docs/pyagentspec/source/_components/all_components.json index 92b31b8e..e45dd0db 100644 --- a/docs/pyagentspec/source/_components/all_components.json +++ b/docs/pyagentspec/source/_components/all_components.json @@ -268,6 +268,29 @@ {"path": "pyagentspec.specialized_agent.AgentSpecializationParameters"} ] }, + { + "name": "Datastores", + "path": "datastores", + "classes": [ + {"path": "pyagentspec.datastores.datastore.Datastore"}, + {"path": "pyagentspec.datastores.datastore.RelationalDatastore"}, + {"path": "pyagentspec.datastores.datastore.InMemoryCollectionDatastore"}, + {"path": "pyagentspec.datastores.oracle.OracleDatabaseDatastore"}, + {"path": "pyagentspec.datastores.oracle.TlsOracleDatabaseConnectionConfig"}, + {"path": "pyagentspec.datastores.oracle.MTlsOracleDatabaseConnectionConfig"}, + {"path": "pyagentspec.datastores.postgres.PostgresDatabaseDatastore"}, + {"path": "pyagentspec.datastores.postgres.TlsPostgresDatabaseConnectionConfig"} + ] + }, + { + "name": "Transforms", + "path": "transforms", + "classes": [ + {"path": "pyagentspec.transforms.transforms.MessageTransform"}, + {"path": "pyagentspec.transforms.summarization.MessageSummarizationTransform"}, + {"path": "pyagentspec.transforms.summarization.ConversationSummarizationTransform"} + ] + }, { "name": "A2A", "path": "a2a", diff --git a/docs/pyagentspec/source/api/datastores.rst b/docs/pyagentspec/source/api/datastores.rst new file mode 100644 index 00000000..03b331de --- /dev/null +++ b/docs/pyagentspec/source/api/datastores.rst @@ -0,0 +1,69 @@ +Datastores +========== + +This page presents all APIs and classes related to Datastores in PyAgentSpec. + +Datastore base classes +---------------------- + +Datastore +~~~~~~~~~ + +.. _datastore: +.. autoclass:: pyagentspec.datastores.datastore.Datastore + :exclude-members: model_post_init, model_config + +RelationalDatastore +~~~~~~~~~~~~~~~~~~~ + +.. _relationaldatastore: +.. autoclass:: pyagentspec.datastores.datastore.RelationalDatastore + :exclude-members: model_post_init, model_config + +InMemoryCollectionDatastore +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. _inmemorycollectiondatastore: +.. autoclass:: pyagentspec.datastores.datastore.InMemoryCollectionDatastore + :exclude-members: model_post_init, model_config + +Oracle datastore +---------------- + +OracleDatabaseDatastore +~~~~~~~~~~~~~~~~~~~~~~~ + +.. _oracledatabasedatastore: +.. autoclass:: pyagentspec.datastores.oracle.OracleDatabaseDatastore + :exclude-members: model_post_init, model_config + +TlsOracleDatabaseConnectionConfig +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. _tlsoracledatabaseconnectionconfig: +.. autoclass:: pyagentspec.datastores.oracle.TlsOracleDatabaseConnectionConfig + :exclude-members: model_post_init, model_config + +MTlsOracleDatabaseConnectionConfig +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. _mtlsoracledatabaseconnectionconfig: +.. autoclass:: pyagentspec.datastores.oracle.MTlsOracleDatabaseConnectionConfig + :exclude-members: model_post_init, model_config + +PostgreSQL datastore +-------------------- + +PostgresDatabaseDatastore +~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. _postgresdatabasedatastore: +.. autoclass:: pyagentspec.datastores.postgres.PostgresDatabaseDatastore + :exclude-members: model_post_init, model_config + +TlsPostgresDatabaseConnectionConfig +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. _tlspostgresdatabaseconnectionconfig: +.. autoclass:: pyagentspec.datastores.postgres.TlsPostgresDatabaseConnectionConfig + :exclude-members: model_post_init, model_config diff --git a/docs/pyagentspec/source/api/transforms.rst b/docs/pyagentspec/source/api/transforms.rst new file mode 100644 index 00000000..23f66379 --- /dev/null +++ b/docs/pyagentspec/source/api/transforms.rst @@ -0,0 +1,31 @@ +Transforms +========== + +This page presents all APIs and classes related to Transforms in PyAgentSpec. + +Transform base classes +---------------------- + +MessageTransform +~~~~~~~~~~~~~~~~ + +.. _messagetransform: +.. autoclass:: pyagentspec.transforms.transforms.MessageTransform + :exclude-members: model_post_init, model_config + +Summarization transforms +------------------------ + +MessageSummarizationTransform +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. _messagesummarizationtransform: +.. autoclass:: pyagentspec.transforms.summarization.MessageSummarizationTransform + :exclude-members: model_post_init, model_config + +ConversationSummarizationTransform +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. _conversationsummarizationtransform: +.. autoclass:: pyagentspec.transforms.summarization.ConversationSummarizationTransform + :exclude-members: model_post_init, model_config diff --git a/docs/pyagentspec/source/changelog.rst b/docs/pyagentspec/source/changelog.rst index c29573c1..407dee3c 100644 --- a/docs/pyagentspec/source/changelog.rst +++ b/docs/pyagentspec/source/changelog.rst @@ -89,6 +89,16 @@ New features Introduced ManagerWorkers in the Agent Spec Language Specification. For more information check out the corresponding :doc:`managerworkers how-to guide ` or read the :ref:`API Reference `. +* **Datastores** + + Added support for datastores in Agent Spec through :ref:`OracleDatabaseDatastore ` and :ref:`PostgresDatabaseDatastore `. + Datastores enable persistent storage and caching capabilities for agent workflows. :ref:`InMemoryCollectionDatastore ` provides a drop-in replacement for development and prototyping. + +* **Transforms** + + Introduced transforms in Agent Spec that allow applying transformations on conversations before being passed to the underlying LLM. + We provide :ref:`MessageSummarizationTransform ` and :ref:`ConversationSummarizationTransform ` for handling long contexts through summarization. + Improvements ^^^^^^^^^^^^ From 5630bb9bf7bf00f440bd288085cb7d84d632e19c Mon Sep 17 00:00:00 2001 From: Youssef Boughizane Date: Fri, 19 Dec 2025 11:20:40 +0100 Subject: [PATCH 04/26] doc: correct ref for transforms and datastores --- .../source/_components/all_components.json | 22 +++++++++---------- docs/pyagentspec/source/api/datastores.rst | 16 +++++++------- docs/pyagentspec/source/api/transforms.rst | 6 ++--- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/docs/pyagentspec/source/_components/all_components.json b/docs/pyagentspec/source/_components/all_components.json index e45dd0db..cec10863 100644 --- a/docs/pyagentspec/source/_components/all_components.json +++ b/docs/pyagentspec/source/_components/all_components.json @@ -272,23 +272,23 @@ "name": "Datastores", "path": "datastores", "classes": [ - {"path": "pyagentspec.datastores.datastore.Datastore"}, - {"path": "pyagentspec.datastores.datastore.RelationalDatastore"}, - {"path": "pyagentspec.datastores.datastore.InMemoryCollectionDatastore"}, - {"path": "pyagentspec.datastores.oracle.OracleDatabaseDatastore"}, - {"path": "pyagentspec.datastores.oracle.TlsOracleDatabaseConnectionConfig"}, - {"path": "pyagentspec.datastores.oracle.MTlsOracleDatabaseConnectionConfig"}, - {"path": "pyagentspec.datastores.postgres.PostgresDatabaseDatastore"}, - {"path": "pyagentspec.datastores.postgres.TlsPostgresDatabaseConnectionConfig"} + {"path": "pyagentspec.datastores.Datastore"}, + {"path": "pyagentspec.datastores.RelationalDatastore"}, + {"path": "pyagentspec.datastores.InMemoryCollectionDatastore"}, + {"path": "pyagentspec.datastores.OracleDatabaseDatastore"}, + {"path": "pyagentspec.datastores.TlsOracleDatabaseConnectionConfig"}, + {"path": "pyagentspec.datastores.MTlsOracleDatabaseConnectionConfig"}, + {"path": "pyagentspec.datastores.PostgresDatabaseDatastore"}, + {"path": "pyagentspec.datastores.TlsPostgresDatabaseConnectionConfig"} ] }, { "name": "Transforms", "path": "transforms", "classes": [ - {"path": "pyagentspec.transforms.transforms.MessageTransform"}, - {"path": "pyagentspec.transforms.summarization.MessageSummarizationTransform"}, - {"path": "pyagentspec.transforms.summarization.ConversationSummarizationTransform"} + {"path": "pyagentspec.transforms.MessageTransform"}, + {"path": "pyagentspec.transforms.MessageSummarizationTransform"}, + {"path": "pyagentspec.transforms.ConversationSummarizationTransform"} ] }, { diff --git a/docs/pyagentspec/source/api/datastores.rst b/docs/pyagentspec/source/api/datastores.rst index 03b331de..e9b58ec4 100644 --- a/docs/pyagentspec/source/api/datastores.rst +++ b/docs/pyagentspec/source/api/datastores.rst @@ -10,21 +10,21 @@ Datastore ~~~~~~~~~ .. _datastore: -.. autoclass:: pyagentspec.datastores.datastore.Datastore +.. autoclass:: pyagentspec.datastores.Datastore :exclude-members: model_post_init, model_config RelationalDatastore ~~~~~~~~~~~~~~~~~~~ .. _relationaldatastore: -.. autoclass:: pyagentspec.datastores.datastore.RelationalDatastore +.. autoclass:: pyagentspec.datastores.RelationalDatastore :exclude-members: model_post_init, model_config InMemoryCollectionDatastore ~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. _inmemorycollectiondatastore: -.. autoclass:: pyagentspec.datastores.datastore.InMemoryCollectionDatastore +.. autoclass:: pyagentspec.datastores.InMemoryCollectionDatastore :exclude-members: model_post_init, model_config Oracle datastore @@ -34,21 +34,21 @@ OracleDatabaseDatastore ~~~~~~~~~~~~~~~~~~~~~~~ .. _oracledatabasedatastore: -.. autoclass:: pyagentspec.datastores.oracle.OracleDatabaseDatastore +.. autoclass:: pyagentspec.datastores.OracleDatabaseDatastore :exclude-members: model_post_init, model_config TlsOracleDatabaseConnectionConfig ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. _tlsoracledatabaseconnectionconfig: -.. autoclass:: pyagentspec.datastores.oracle.TlsOracleDatabaseConnectionConfig +.. autoclass:: pyagentspec.datastores.TlsOracleDatabaseConnectionConfig :exclude-members: model_post_init, model_config MTlsOracleDatabaseConnectionConfig ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. _mtlsoracledatabaseconnectionconfig: -.. autoclass:: pyagentspec.datastores.oracle.MTlsOracleDatabaseConnectionConfig +.. autoclass:: pyagentspec.datastores.MTlsOracleDatabaseConnectionConfig :exclude-members: model_post_init, model_config PostgreSQL datastore @@ -58,12 +58,12 @@ PostgresDatabaseDatastore ~~~~~~~~~~~~~~~~~~~~~~~~~ .. _postgresdatabasedatastore: -.. autoclass:: pyagentspec.datastores.postgres.PostgresDatabaseDatastore +.. autoclass:: pyagentspec.datastores.PostgresDatabaseDatastore :exclude-members: model_post_init, model_config TlsPostgresDatabaseConnectionConfig ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. _tlspostgresdatabaseconnectionconfig: -.. autoclass:: pyagentspec.datastores.postgres.TlsPostgresDatabaseConnectionConfig +.. autoclass:: pyagentspec.datastores.TlsPostgresDatabaseConnectionConfig :exclude-members: model_post_init, model_config diff --git a/docs/pyagentspec/source/api/transforms.rst b/docs/pyagentspec/source/api/transforms.rst index 23f66379..b4f42ff2 100644 --- a/docs/pyagentspec/source/api/transforms.rst +++ b/docs/pyagentspec/source/api/transforms.rst @@ -10,7 +10,7 @@ MessageTransform ~~~~~~~~~~~~~~~~ .. _messagetransform: -.. autoclass:: pyagentspec.transforms.transforms.MessageTransform +.. autoclass:: pyagentspec.transforms.MessageTransform :exclude-members: model_post_init, model_config Summarization transforms @@ -20,12 +20,12 @@ MessageSummarizationTransform ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. _messagesummarizationtransform: -.. autoclass:: pyagentspec.transforms.summarization.MessageSummarizationTransform +.. autoclass:: pyagentspec.transforms.MessageSummarizationTransform :exclude-members: model_post_init, model_config ConversationSummarizationTransform ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. _conversationsummarizationtransform: -.. autoclass:: pyagentspec.transforms.summarization.ConversationSummarizationTransform +.. autoclass:: pyagentspec.transforms.ConversationSummarizationTransform :exclude-members: model_post_init, model_config From 343d94a8439c7d1ebee9a38f8f4e5aa1080bd003 Mon Sep 17 00:00:00 2001 From: Youssef Boughizane Date: Fri, 19 Dec 2025 12:39:48 +0100 Subject: [PATCH 05/26] doc: add specs of datastore --- .../agentspec/language_spec_nightly.rst | 115 ++++++++++++++++++ .../src/pyagentspec/datastores/datastore.py | 2 +- 2 files changed, 116 insertions(+), 1 deletion(-) diff --git a/docs/pyagentspec/source/agentspec/language_spec_nightly.rst b/docs/pyagentspec/source/agentspec/language_spec_nightly.rst index e7762245..a7e69656 100644 --- a/docs/pyagentspec/source/agentspec/language_spec_nightly.rst +++ b/docs/pyagentspec/source/agentspec/language_spec_nightly.rst @@ -382,6 +382,11 @@ following goals : discovered from an MCP server. - * A ToolBox connecting to an MCP server and exposing its tools to an Agent + * - Datastore + - A component that provides storage capabilities for agentic systems, enabling them to store and access data. + - * An in-memory datastore for testing + * An Oracle Database datastore for production use + Agentic Components @@ -2431,6 +2436,116 @@ in the example code below: ) +Datastore Components + +~~~~~~~~~~~~~~~~~~~~ + +Datastore (abstract) Interface to enable agentic systems to store, and access data. + +.. code-block:: python + + class Datastore(Component, abstract=True): + pass + +All datastores currently defined in this version represent collections of objects defined by their type as an `Entity`, however the Datastore parent type is left without constraint to enable future extension or the development of plugins with different Datastore structure (e.g. key-value stores, or graph database, or unstructured data storage) + +Relational datastores & In-Memory datastore + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Relational datastores (OracleDatabaseDatastore) and their drop-in in-memory replacement (InMemoryCollectionDatastore) contain many collections (tables) where each collection is a set entities. They need to have a predefined fixed schema. A schema is a mapping from a collection name to an Entity object defining the entity(row). + +.. code-block:: python + + class InMemoryCollectionDatastore(Datastore): + datastore_schema: Dict[str, Entity] + +.. code-block:: python + + class RelationalDatastore(Datastore, is_abstract=True): + datastore_schema: Dict[str, Entity] + +OracleDatabaseDatastore backed by Oracle Database + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + class OracleDatabaseDatastore(RelationalDatastore): + connection_config: OracleDatabaseConnectionConfig + +OracleDatabaseConnectionConfig (abstract) Base class used for configuring connections to Oracle Database. + +.. code-block:: python + + class OracleDatabaseConnectionConfig(Component, is_abstract=True): + pass + +TlsOracleDatabaseConnectionConfig For standard TLS Oracle Database connections. + +.. code-block:: python + + class TlsOracleDatabaseConnectionConfig(OracleDatabaseConnectionConfig): + user: SensitiveField[str] + password: SensitiveField[str] + dsn: SensitiveField[str] + protocol: Literal["tcp", "tcps"] = "tcps" + config_dir: Optional[str] = None + +- **user**: User used to connect to the database +- **password**: Password for the provided user +- **dsn**: Connection string for the database (e.g., created using `oracledb.make_dsn`) +- **protocol**: 'tcp' or 'tcps' indicating whether to use unencrypted network traffic or encrypted network traffic (TLS) +- **config_dir**: Configuration directory for the database connection. Set this if you are using an alias from your tnsnames.ora files as a DSN. Make sure that the specified DSN is appropriate for TLS connections. + +MTlsOracleDatabaseConnectionConfig For Oracle DB connections using mutual TLS (wallet authentication). + +.. code-block:: python + + class MTlsOracleDatabaseConnectionConfig(TlsOracleDatabaseConnectionConfig): + + wallet_location: SensitiveField[str] + wallet_password: SensitiveField[str] + +- **wallet_location**: Location where the Oracle Database wallet is stored. +- **wallet_password**: Password for the provided wallet. + +PostgresDatastore backed by a Postgres Database + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + class PostgresDatabaseDatastore(RelationalDatastore): + connection_config: PostgresDatabaseConnectionConfig + +.. code-block:: python + + class PostgresDatabaseConnectionConfig(Component, is_abstract=True): + pass + +.. code-block:: python + + class TlsPostgresDatabaseConnectionConfig(PostgresDatabaseConnectionConfig): + user: SensitiveField[str] + password: SensitiveField[str] + url: str + sslmode: Literal["disable","allow", "prefer","require","verify-ca","verify-full"] = "require" + sslcert: Optional[str] = None + sslkey: Optional[SensitiveField[str]] = None + sslrootcert: Optional[str] = None + sslcrl: Optional[str] = None + +- **user**: User of the postgres database +- **password**: Password of the postgres database +- **url**: URL to access the postgres database +- **sslmode**: SSL mode for the PostgreSQL connection. +- **sslcert**: Path of the client SSL certificate, replacing the default `~/.postgresql/postgresql.crt`. Ignored if an SSL connection is not made. +- **sslkey**: Path of the file containing the secret key used for the client certificate, replacing the default `~/.postgresql/postgresql.key`. Ignored if an SSL connection is not made. +- **sslrootcert**: Path of the file containing SSL certificate authority (CA) certificate(s). Used to verify server identity. +- **sslcrl**: Path of the SSL server certificate revocation list (CRL). Certificates listed will be rejected while attempting to authenticate the server's certificate. + + Additional components for future versions ----------------------------------------- diff --git a/pyagentspec/src/pyagentspec/datastores/datastore.py b/pyagentspec/src/pyagentspec/datastores/datastore.py index ea339ac9..ce05e642 100644 --- a/pyagentspec/src/pyagentspec/datastores/datastore.py +++ b/pyagentspec/src/pyagentspec/datastores/datastore.py @@ -25,7 +25,7 @@ def is_object_property(value: Property) -> Property: class Datastore(Component, abstract=True): - """Base class for Datastores. Datastores store and retrive data.""" + """Base class for Datastores. Datastores store and retrieve data.""" class RelationalDatastore(Datastore, abstract=True): From dc5ed54fc495f1453cd4b0c214985023de4ec476 Mon Sep 17 00:00:00 2001 From: Youssef Boughizane Date: Fri, 19 Dec 2025 13:52:09 +0100 Subject: [PATCH 06/26] doc: add header comments --- pyagentspec/src/pyagentspec/datastores/oracle.py | 1 + pyagentspec/src/pyagentspec/datastores/postgres.py | 1 + pyagentspec/src/pyagentspec/transforms/summarization.py | 2 +- pyagentspec/src/pyagentspec/transforms/transforms.py | 2 +- 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/pyagentspec/src/pyagentspec/datastores/oracle.py b/pyagentspec/src/pyagentspec/datastores/oracle.py index 749a7d5f..dfe35eb8 100644 --- a/pyagentspec/src/pyagentspec/datastores/oracle.py +++ b/pyagentspec/src/pyagentspec/datastores/oracle.py @@ -3,6 +3,7 @@ # This software is under the Apache License 2.0 # (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) or Universal Permissive License # (UPL) 1.0 (LICENSE-UPL or https://oss.oracle.com/licenses/upl), at your option. +"""This module defines the oracle datastore and oracle database connnection configuration components.""" from typing import Literal, Optional diff --git a/pyagentspec/src/pyagentspec/datastores/postgres.py b/pyagentspec/src/pyagentspec/datastores/postgres.py index ae190b53..664a53c7 100644 --- a/pyagentspec/src/pyagentspec/datastores/postgres.py +++ b/pyagentspec/src/pyagentspec/datastores/postgres.py @@ -3,6 +3,7 @@ # This software is under the Apache License 2.0 # (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) or Universal Permissive License # (UPL) 1.0 (LICENSE-UPL or https://oss.oracle.com/licenses/upl), at your option. +"""This module defines the postgres datastore and postgres database connnection configuration components.""" from typing import Literal, Optional diff --git a/pyagentspec/src/pyagentspec/transforms/summarization.py b/pyagentspec/src/pyagentspec/transforms/summarization.py index d0bbfd3f..5d8be7b1 100644 --- a/pyagentspec/src/pyagentspec/transforms/summarization.py +++ b/pyagentspec/src/pyagentspec/transforms/summarization.py @@ -3,7 +3,7 @@ # This software is under the Apache License 2.0 # (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) or Universal Permissive License # (UPL) 1.0 (LICENSE-UPL or https://oss.oracle.com/licenses/upl), at your option. - +"""This file defines message transforms for message and conversation summarization.""" from typing import Optional from pyagentspec.datastores import Datastore diff --git a/pyagentspec/src/pyagentspec/transforms/transforms.py b/pyagentspec/src/pyagentspec/transforms/transforms.py index e6a3bc6a..716e663b 100644 --- a/pyagentspec/src/pyagentspec/transforms/transforms.py +++ b/pyagentspec/src/pyagentspec/transforms/transforms.py @@ -3,7 +3,7 @@ # This software is under the Apache License 2.0 # (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) or Universal Permissive License # (UPL) 1.0 (LICENSE-UPL or https://oss.oracle.com/licenses/upl), at your option. - +"""This file defines the base MessageTransform""" from pyagentspec.component import Component From 8351ad772d96ddddc2440f87b8bbc98267f5b019 Mon Sep 17 00:00:00 2001 From: Youssef Boughizane Date: Fri, 19 Dec 2025 14:29:25 +0100 Subject: [PATCH 07/26] doc: style improvement to datastore specs --- .../agentspec/language_spec_nightly.rst | 219 +++++++++--------- 1 file changed, 109 insertions(+), 110 deletions(-) diff --git a/docs/pyagentspec/source/agentspec/language_spec_nightly.rst b/docs/pyagentspec/source/agentspec/language_spec_nightly.rst index a7e69656..2e6e9db8 100644 --- a/docs/pyagentspec/source/agentspec/language_spec_nightly.rst +++ b/docs/pyagentspec/source/agentspec/language_spec_nightly.rst @@ -1947,6 +1947,115 @@ The ManagerWorkers has two main parameters: - Workers cannot interact with the end user directly. - When invoked, each worker can leverage its equipped tools to complete the assigned task and report the result back to the group manager. + +Datastores +~~~~~~~~~~ + +The core abstract is Datastore which enables agentic systems to store, and access data. + +.. code-block:: python + + class Datastore(Component, abstract=True): + pass + +All datastores currently defined represent collections of objects defined by their type as an ``Entity``, however the Datastore parent type is left without constraint to enable future extension or the development of plugins with different Datastore structure (e.g. key-value stores, or graph database, or unstructured data storage) + +An ``Entity`` is a property that has a JSON schema equivalent to an object property; an object property would work. + +Relational datastores & In-Memory datastore +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Relational datastores (``OracleDatabaseDatastore``, ``PostgresDatabaseDatastore``) and their drop-in in-memory replacement (``InMemoryCollectionDatastore``) contain many collections (tables) where each collection is a set entities. They need to have a predefined fixed schema. A schema is a mapping from a collection name to an Entity object defining the entity(row). + +.. code-block:: python + + class InMemoryCollectionDatastore(Datastore): + datastore_schema: Dict[str, Entity] + + class RelationalDatastore(Datastore, is_abstract=True): + datastore_schema: Dict[str, Entity] + +OracleDatabaseDatastore +^^^^^^^^^^^^^^^^^^^^^^^ + +The ``OracleDatabaseDatastore`` is a relational datastore that requires an ``OracleDatabaseConnectionConfig`` object for configuration. + +.. code-block:: python + + class OracleDatabaseDatastore(RelationalDatastore): + connection_config: OracleDatabaseConnectionConfig + +``OracleDatabaseConnectionConfig`` is the abstraction we use for configuring connections to Oracle Database. + +.. code-block:: python + + class OracleDatabaseConnectionConfig(Component, is_abstract=True): + pass + +``TlsOracleDatabaseConnectionConfig`` For standard TLS Oracle Database connections. + +.. code-block:: python + + class TlsOracleDatabaseConnectionConfig(OracleDatabaseConnectionConfig): + user: SensitiveField[str] + password: SensitiveField[str] + dsn: SensitiveField[str] + protocol: Literal["tcp", "tcps"] = "tcps" + config_dir: Optional[str] = None + +- **user**: User used to connect to the database +- **password**: Password for the provided user +- **dsn**: Connection string for the database (e.g., created using `oracledb.make_dsn`) +- **protocol**: 'tcp' or 'tcps' indicating whether to use unencrypted network traffic or encrypted network traffic (TLS) +- **config_dir**: Configuration directory for the database connection. Set this if you are using an alias from your tnsnames.ora files as a DSN. Make sure that the specified DSN is appropriate for TLS connections. + +``MTlsOracleDatabaseConnectionConfig`` For Oracle DB connections using mutual TLS (wallet authentication). + +.. code-block:: python + + class MTlsOracleDatabaseConnectionConfig(TlsOracleDatabaseConnectionConfig): + + wallet_location: SensitiveField[str] + wallet_password: SensitiveField[str] + +- **wallet_location**: Location where the Oracle Database wallet is stored. +- **wallet_password**: Password for the provided wallet. + +PostgresDatastore +^^^^^^^^^^^^^^^^^ + +.. code-block:: python + + class PostgresDatabaseDatastore(RelationalDatastore): + connection_config: PostgresDatabaseConnectionConfig + +.. code-block:: python + + class PostgresDatabaseConnectionConfig(Component, is_abstract=True): + pass + +.. code-block:: python + + class TlsPostgresDatabaseConnectionConfig(PostgresDatabaseConnectionConfig): + user: SensitiveField[str] + password: SensitiveField[str] + url: str + sslmode: Literal["disable","allow", "prefer","require","verify-ca","verify-full"] = "require" + sslcert: Optional[str] = None + sslkey: Optional[SensitiveField[str]] = None + sslrootcert: Optional[str] = None + sslcrl: Optional[str] = None + +- **user**: User of the postgres database +- **password**: Password of the postgres database +- **url**: URL to access the postgres database +- **sslmode**: SSL mode for the PostgreSQL connection. +- **sslcert**: Path of the client SSL certificate, replacing the default `~/.postgresql/postgresql.crt`. Ignored if an SSL connection is not made. +- **sslkey**: Path of the file containing the secret key used for the client certificate, replacing the default `~/.postgresql/postgresql.key`. Ignored if an SSL connection is not made. +- **sslrootcert**: Path of the file containing SSL certificate authority (CA) certificate(s). Used to verify server identity. +- **sslcrl**: Path of the SSL server certificate revocation list (CRL). Certificates listed will be rejected while attempting to authenticate the server's certificate. + + Versioning ---------- @@ -2436,116 +2545,6 @@ in the example code below: ) -Datastore Components - -~~~~~~~~~~~~~~~~~~~~ - -Datastore (abstract) Interface to enable agentic systems to store, and access data. - -.. code-block:: python - - class Datastore(Component, abstract=True): - pass - -All datastores currently defined in this version represent collections of objects defined by their type as an `Entity`, however the Datastore parent type is left without constraint to enable future extension or the development of plugins with different Datastore structure (e.g. key-value stores, or graph database, or unstructured data storage) - -Relational datastores & In-Memory datastore - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Relational datastores (OracleDatabaseDatastore) and their drop-in in-memory replacement (InMemoryCollectionDatastore) contain many collections (tables) where each collection is a set entities. They need to have a predefined fixed schema. A schema is a mapping from a collection name to an Entity object defining the entity(row). - -.. code-block:: python - - class InMemoryCollectionDatastore(Datastore): - datastore_schema: Dict[str, Entity] - -.. code-block:: python - - class RelationalDatastore(Datastore, is_abstract=True): - datastore_schema: Dict[str, Entity] - -OracleDatabaseDatastore backed by Oracle Database - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code-block:: python - - class OracleDatabaseDatastore(RelationalDatastore): - connection_config: OracleDatabaseConnectionConfig - -OracleDatabaseConnectionConfig (abstract) Base class used for configuring connections to Oracle Database. - -.. code-block:: python - - class OracleDatabaseConnectionConfig(Component, is_abstract=True): - pass - -TlsOracleDatabaseConnectionConfig For standard TLS Oracle Database connections. - -.. code-block:: python - - class TlsOracleDatabaseConnectionConfig(OracleDatabaseConnectionConfig): - user: SensitiveField[str] - password: SensitiveField[str] - dsn: SensitiveField[str] - protocol: Literal["tcp", "tcps"] = "tcps" - config_dir: Optional[str] = None - -- **user**: User used to connect to the database -- **password**: Password for the provided user -- **dsn**: Connection string for the database (e.g., created using `oracledb.make_dsn`) -- **protocol**: 'tcp' or 'tcps' indicating whether to use unencrypted network traffic or encrypted network traffic (TLS) -- **config_dir**: Configuration directory for the database connection. Set this if you are using an alias from your tnsnames.ora files as a DSN. Make sure that the specified DSN is appropriate for TLS connections. - -MTlsOracleDatabaseConnectionConfig For Oracle DB connections using mutual TLS (wallet authentication). - -.. code-block:: python - - class MTlsOracleDatabaseConnectionConfig(TlsOracleDatabaseConnectionConfig): - - wallet_location: SensitiveField[str] - wallet_password: SensitiveField[str] - -- **wallet_location**: Location where the Oracle Database wallet is stored. -- **wallet_password**: Password for the provided wallet. - -PostgresDatastore backed by a Postgres Database - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code-block:: python - - class PostgresDatabaseDatastore(RelationalDatastore): - connection_config: PostgresDatabaseConnectionConfig - -.. code-block:: python - - class PostgresDatabaseConnectionConfig(Component, is_abstract=True): - pass - -.. code-block:: python - - class TlsPostgresDatabaseConnectionConfig(PostgresDatabaseConnectionConfig): - user: SensitiveField[str] - password: SensitiveField[str] - url: str - sslmode: Literal["disable","allow", "prefer","require","verify-ca","verify-full"] = "require" - sslcert: Optional[str] = None - sslkey: Optional[SensitiveField[str]] = None - sslrootcert: Optional[str] = None - sslcrl: Optional[str] = None - -- **user**: User of the postgres database -- **password**: Password of the postgres database -- **url**: URL to access the postgres database -- **sslmode**: SSL mode for the PostgreSQL connection. -- **sslcert**: Path of the client SSL certificate, replacing the default `~/.postgresql/postgresql.crt`. Ignored if an SSL connection is not made. -- **sslkey**: Path of the file containing the secret key used for the client certificate, replacing the default `~/.postgresql/postgresql.key`. Ignored if an SSL connection is not made. -- **sslrootcert**: Path of the file containing SSL certificate authority (CA) certificate(s). Used to verify server identity. -- **sslcrl**: Path of the SSL server certificate revocation list (CRL). Certificates listed will be rejected while attempting to authenticate the server's certificate. - - Additional components for future versions ----------------------------------------- From 654f91d363a322aa5b4a60afa52e3264efc33947 Mon Sep 17 00:00:00 2001 From: Youssef Boughizane Date: Fri, 19 Dec 2025 15:02:37 +0100 Subject: [PATCH 08/26] doc: stylistic changes to spec --- .../agentspec/language_spec_nightly.rst | 38 +++++++++---------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/docs/pyagentspec/source/agentspec/language_spec_nightly.rst b/docs/pyagentspec/source/agentspec/language_spec_nightly.rst index 2e6e9db8..f332952b 100644 --- a/docs/pyagentspec/source/agentspec/language_spec_nightly.rst +++ b/docs/pyagentspec/source/agentspec/language_spec_nightly.rst @@ -1978,7 +1978,7 @@ Relational datastores (``OracleDatabaseDatastore``, ``PostgresDatabaseDatastore` OracleDatabaseDatastore ^^^^^^^^^^^^^^^^^^^^^^^ -The ``OracleDatabaseDatastore`` is a relational datastore that requires an ``OracleDatabaseConnectionConfig`` object for configuration. +The ``OracleDatabaseDatastore`` is a relational datastore that requires an ``OracleDatabaseConnectionConfig`` object for configuration. Sensitive information (e.g., wallets, keys) is excluded from exported configurations and should be loaded at deserialization time. .. code-block:: python @@ -2003,11 +2003,11 @@ The ``OracleDatabaseDatastore`` is a relational datastore that requires an ``Ora protocol: Literal["tcp", "tcps"] = "tcps" config_dir: Optional[str] = None -- **user**: User used to connect to the database -- **password**: Password for the provided user -- **dsn**: Connection string for the database (e.g., created using `oracledb.make_dsn`) -- **protocol**: 'tcp' or 'tcps' indicating whether to use unencrypted network traffic or encrypted network traffic (TLS) -- **config_dir**: Configuration directory for the database connection. Set this if you are using an alias from your tnsnames.ora files as a DSN. Make sure that the specified DSN is appropriate for TLS connections. +- ``user``: User used to connect to the database +- ``password``: Password for the provided user +- ``dsn``: Connection string for the database (e.g., created using `oracledb.make_dsn`) +- ``protocol``: 'tcp' or 'tcps' indicating whether to use unencrypted network traffic or encrypted network traffic (TLS) +- ``config_dir``: Configuration directory for the database connection. Set this if you are using an alias from your tnsnames.ora files as a DSN. Make sure that the specified DSN is appropriate for TLS connections. ``MTlsOracleDatabaseConnectionConfig`` For Oracle DB connections using mutual TLS (wallet authentication). @@ -2018,24 +2018,22 @@ The ``OracleDatabaseDatastore`` is a relational datastore that requires an ``Ora wallet_location: SensitiveField[str] wallet_password: SensitiveField[str] -- **wallet_location**: Location where the Oracle Database wallet is stored. -- **wallet_password**: Password for the provided wallet. +- ``wallet_location``: Location where the Oracle Database wallet is stored. +- ``wallet_password``: Password for the provided wallet. PostgresDatastore ^^^^^^^^^^^^^^^^^ +The ``PostgresDatabaseDatastore`` is a relational datastore intended to store entities in PostgreSQL databases. It requires a ``PostgresDatabaseConnectionConfig`` object for connection and authentication details. Sensitive information (e.g., user, password, SSL keys) is excluded from exported configurations and should be loaded at deserialization time. + .. code-block:: python class PostgresDatabaseDatastore(RelationalDatastore): connection_config: PostgresDatabaseConnectionConfig -.. code-block:: python - class PostgresDatabaseConnectionConfig(Component, is_abstract=True): pass -.. code-block:: python - class TlsPostgresDatabaseConnectionConfig(PostgresDatabaseConnectionConfig): user: SensitiveField[str] password: SensitiveField[str] @@ -2046,14 +2044,14 @@ PostgresDatastore sslrootcert: Optional[str] = None sslcrl: Optional[str] = None -- **user**: User of the postgres database -- **password**: Password of the postgres database -- **url**: URL to access the postgres database -- **sslmode**: SSL mode for the PostgreSQL connection. -- **sslcert**: Path of the client SSL certificate, replacing the default `~/.postgresql/postgresql.crt`. Ignored if an SSL connection is not made. -- **sslkey**: Path of the file containing the secret key used for the client certificate, replacing the default `~/.postgresql/postgresql.key`. Ignored if an SSL connection is not made. -- **sslrootcert**: Path of the file containing SSL certificate authority (CA) certificate(s). Used to verify server identity. -- **sslcrl**: Path of the SSL server certificate revocation list (CRL). Certificates listed will be rejected while attempting to authenticate the server's certificate. +- ``user``: User of the postgres database +- ``password``: Password of the postgres database +- ``url``: URL to access the postgres database +- ``sslmode``: SSL mode for the PostgreSQL connection. +- ``sslcert``: Path of the client SSL certificate, replacing the default `~/.postgresql/postgresql.crt`. Ignored if an SSL connection is not made. +- ``sslkey``: Path of the file containing the secret key used for the client certificate, replacing the default `~/.postgresql/postgresql.key`. Ignored if an SSL connection is not made. +- ``sslrootcert``: Path of the file containing SSL certificate authority (CA) certificate(s). Used to verify server identity. +- ``sslcrl``: Path of the SSL server certificate revocation list (CRL). Certificates listed will be rejected while attempting to authenticate the server's certificate. Versioning From 2bab05bf7077bc743f235f917ab8172c66cc3d78 Mon Sep 17 00:00:00 2001 From: Youssef Boughizane Date: Fri, 19 Dec 2025 15:26:34 +0100 Subject: [PATCH 09/26] doc: added transforms description in the specs --- .../agentspec/language_spec_nightly.rst | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/docs/pyagentspec/source/agentspec/language_spec_nightly.rst b/docs/pyagentspec/source/agentspec/language_spec_nightly.rst index f332952b..cd3a860d 100644 --- a/docs/pyagentspec/source/agentspec/language_spec_nightly.rst +++ b/docs/pyagentspec/source/agentspec/language_spec_nightly.rst @@ -2054,6 +2054,57 @@ The ``PostgresDatabaseDatastore`` is a relational datastore intended to store en - ``sslcrl``: Path of the SSL server certificate revocation list (CRL). Certificates listed will be rejected while attempting to authenticate the server's certificate. +Transforms +^^^^^^^^^^ + +Transforms extend the base ``MessageTransform`` component can be used in agents. they apply a transformation to the messages before it's passed to the LLM. + +.. code-block:: python + + class MessageTransform(Component, abstract=True): + pass + +MessageSummarizationTransform +''''''''''''''''''''''''''''' + +Summarizes messages exceeding a given number of characters using an LLM and caches the summaries in a ``Datastore``. This is useful for conversations with long messagwes where the context can become too large for the agent LLM to handle. + +.. code-block:: python + + class MessageSummarizationTransform(MessageTransform): + llm: LlmConfig + max_message_size: int = 20_000 + summarization_instructions: str = ( + "Please make a summary of this message. Include relevant information and keep it short. " + "Your response will replace the message, so just output the summary directly, no introduction needed." + ) + summarized_message_template: str = "Summarized message: {{summary}}" + datastore: Optional[Datastore] = None + max_cache_size: Optional[int] = 10_000 + max_cache_lifetime: Optional[int] = 4 * 3600 + cache_collection_name: str = "summarized_messages_cache" + +ConversationSummarizationTransform +'''''''''''''''''''''''''''''''''' + +Summarizes conversations exceeding a given number of messages using an LLM and caches conversation summaries in a ``Datastore``. This is useful to reduce long conversation history into a concise context for downstream LLM calls. + +.. code-block:: python + + class ConversationSummarizationTransform(MessageTransform): + llm: LlmConfig + max_num_messages: int = 50 + min_num_messages: int = 10 + summarization_instructions: str = ( + "Please make a summary of this conversation. Include relevant information and keep it short. " + "Your response will replace the messages, so just output the summary directly, no introduction needed." + ) + summarized_conversation_template: str = "Summarized conversation: {{summary}}" + datastore: Optional[Datastore] = None + max_cache_size: Optional[int] = 10_000 + max_cache_lifetime: Optional[int] = 4 * 3600 + cache_collection_name: str = "summarized_conversations_cache" + Versioning ---------- From 7d2d021c92c0be572e27ad3c9fbd5e6f3764bfc1 Mon Sep 17 00:00:00 2001 From: Youssef Boughizane Date: Fri, 19 Dec 2025 16:23:56 +0100 Subject: [PATCH 10/26] refactor: split tests files to conftests --- pyagentspec/tests/datastores/__init__.py | 7 ++ .../conftest.py} | 38 ++-------- .../tests/datastores/test_datastores.py | 29 ++++++++ pyagentspec/tests/transforms/conftest.py | 53 +++++++++++++ .../test_summarization_transforms.py | 74 ++----------------- 5 files changed, 102 insertions(+), 99 deletions(-) create mode 100644 pyagentspec/tests/datastores/__init__.py rename pyagentspec/tests/{test_datastores.py => datastores/conftest.py} (72%) create mode 100644 pyagentspec/tests/datastores/test_datastores.py create mode 100644 pyagentspec/tests/transforms/conftest.py diff --git a/pyagentspec/tests/datastores/__init__.py b/pyagentspec/tests/datastores/__init__.py new file mode 100644 index 00000000..10451404 --- /dev/null +++ b/pyagentspec/tests/datastores/__init__.py @@ -0,0 +1,7 @@ +# Copyright © 2025 Oracle and/or its affiliates. +# +# This software is under the Apache License 2.0 +# (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) or Universal Permissive License +# (UPL) 1.0 (LICENSE-UPL or https://oss.oracle.com/licenses/upl), at your option. + +from .conftest import DATASTORES_AND_THEIR_SENSITIVE_FIELDS diff --git a/pyagentspec/tests/test_datastores.py b/pyagentspec/tests/datastores/conftest.py similarity index 72% rename from pyagentspec/tests/test_datastores.py rename to pyagentspec/tests/datastores/conftest.py index b8dca9c8..43caad7e 100644 --- a/pyagentspec/tests/test_datastores.py +++ b/pyagentspec/tests/datastores/conftest.py @@ -4,14 +4,9 @@ # (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) or Universal Permissive License # (UPL) 1.0 (LICENSE-UPL or https://oss.oracle.com/licenses/upl), at your option. -from typing import Any - -import pytest - from pyagentspec.datastores import ( InMemoryCollectionDatastore, ) -from pyagentspec.datastores.datastore import Datastore from pyagentspec.datastores.oracle import ( MTlsOracleDatabaseConnectionConfig, OracleDatabaseDatastore, @@ -28,8 +23,6 @@ Property, StringProperty, ) -from pyagentspec.serialization.deserializer import AgentSpecDeserializer -from pyagentspec.serialization.serializer import AgentSpecSerializer FAKE_PASSWORD = "testpass" # nosec B105 FAKE_TESTUSER = "testuser" @@ -124,28 +117,9 @@ def postgres_datastore_tls(schema): ) -@pytest.mark.parametrize( - "datastore, sensitive_fields", - [ - (oracle_datastore_tls(SCHEMA), ORACLE_TLS_SENSITIVE_FIELDS), - (oracle_datastore_mtls(SCHEMA), ORACLE_MTLS_SENSITIVE_FIELDS), - (postgres_datastore_tls(SCHEMA), POSTGRES_TLS_SENSITIVE_FIELDS), - (in_memory_datastore(SCHEMA), IN_MEMORY_SENSITIVE_FIELDS), - ], -) -def test_can_serialize_and_deserialize_datastore( - datastore: Datastore, sensitive_fields: dict[str, Any] -) -> None: - serialized_ds = AgentSpecSerializer().to_yaml(datastore) - print(serialized_ds) - assert len(serialized_ds.strip()) > 0 - deserialized_ds = AgentSpecDeserializer().from_yaml( - yaml_content=serialized_ds, components_registry=sensitive_fields - ) - assert deserialized_ds == datastore - serialized_ds = AgentSpecSerializer().to_json(datastore) - assert len(serialized_ds.strip()) > 0 - deserialized_ds = AgentSpecDeserializer().from_yaml( - yaml_content=serialized_ds, components_registry=sensitive_fields - ) - assert deserialized_ds == datastore +DATASTORES_AND_THEIR_SENSITIVE_FIELDS = [ + (in_memory_datastore(SCHEMA), IN_MEMORY_SENSITIVE_FIELDS), + (oracle_datastore_tls(SCHEMA), ORACLE_TLS_SENSITIVE_FIELDS), + (oracle_datastore_mtls(SCHEMA), ORACLE_MTLS_SENSITIVE_FIELDS), + (postgres_datastore_tls(SCHEMA), POSTGRES_TLS_SENSITIVE_FIELDS), +] diff --git a/pyagentspec/tests/datastores/test_datastores.py b/pyagentspec/tests/datastores/test_datastores.py new file mode 100644 index 00000000..d3c32a5f --- /dev/null +++ b/pyagentspec/tests/datastores/test_datastores.py @@ -0,0 +1,29 @@ +# Copyright © 2025 Oracle and/or its affiliates. +# +# This software is under the Apache License 2.0 +# (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) or Universal Permissive License +# (UPL) 1.0 (LICENSE-UPL or https://oss.oracle.com/licenses/upl), at your option. + +import pytest + +from pyagentspec.serialization.deserializer import AgentSpecDeserializer +from pyagentspec.serialization.serializer import AgentSpecSerializer + +from .conftest import DATASTORES_AND_THEIR_SENSITIVE_FIELDS + + +@pytest.mark.parametrize("datastore, sensitive_fields", DATASTORES_AND_THEIR_SENSITIVE_FIELDS) +def test_can_serialize_and_deserialize_datastore(datastore, sensitive_fields) -> None: + serialized_ds = AgentSpecSerializer().to_yaml(datastore) + print(serialized_ds) + assert len(serialized_ds.strip()) > 0 + deserialized_ds = AgentSpecDeserializer().from_yaml( + yaml_content=serialized_ds, components_registry=sensitive_fields + ) + assert deserialized_ds == datastore + serialized_ds = AgentSpecSerializer().to_json(datastore) + assert len(serialized_ds.strip()) > 0 + deserialized_ds = AgentSpecDeserializer().from_yaml( + yaml_content=serialized_ds, components_registry=sensitive_fields + ) + assert deserialized_ds == datastore diff --git a/pyagentspec/tests/transforms/conftest.py b/pyagentspec/tests/transforms/conftest.py new file mode 100644 index 00000000..6658a25c --- /dev/null +++ b/pyagentspec/tests/transforms/conftest.py @@ -0,0 +1,53 @@ +# Copyright © 2025 Oracle and/or its affiliates. +# +# This software is under the Apache License 2.0 +# (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) or Universal Permissive License +# (UPL) 1.0 (LICENSE-UPL or https://oss.oracle.com/licenses/upl), at your option. + +from pyagentspec.llms import OpenAiConfig +from pyagentspec.transforms import ConversationSummarizationTransform, MessageSummarizationTransform + + +def create_test_llm_config(): + return OpenAiConfig( + id="test_llm", + name="test_openai_config", + model_id="gpt-3.5-turbo", + ) + + +def create_message_summarization_transform(datastore): + # Use non-default values to ensure serialization/deserialization preserves exact values + return MessageSummarizationTransform( + id="test_msg_summarizer", + name="test_message_summarizer", + llm=create_test_llm_config(), + max_message_size=15_000, + summarization_instructions=( + "Create a concise summary of this message focusing on key points and actionable information." + ), + summarized_message_template="Message summary: {{summary}}", + datastore=datastore, + max_cache_size=2_500, + max_cache_lifetime=8 * 3600, + cache_collection_name="message_summaries_cache", + ) + + +def create_conversation_summarization_transform(datastore): + # Use non-default values to ensure serialization/deserialization preserves exact values + return ConversationSummarizationTransform( + id="test_conv_summarizer", + name="test_conversation_summarizer", + llm=create_test_llm_config(), + max_num_messages=75, + min_num_messages=25, + summarization_instructions=( + "Summarize this conversation thread, highlighting key decisions, action items, and important context." + ), + summarized_conversation_template="Conversation summary: {{summary}}", + datastore=datastore, + max_cache_size=5_000, + max_cache_lifetime=12 * 3600, + cache_collection_name="conversation_summaries_cache", + ) diff --git a/pyagentspec/tests/transforms/test_summarization_transforms.py b/pyagentspec/tests/transforms/test_summarization_transforms.py index 0f3456db..ad676992 100644 --- a/pyagentspec/tests/transforms/test_summarization_transforms.py +++ b/pyagentspec/tests/transforms/test_summarization_transforms.py @@ -7,77 +7,19 @@ import pytest -from pyagentspec.llms import OpenAiConfig from pyagentspec.serialization.deserializer import AgentSpecDeserializer from pyagentspec.serialization.serializer import AgentSpecSerializer -from pyagentspec.transforms import ConversationSummarizationTransform, MessageSummarizationTransform -from ..test_datastores import ( - IN_MEMORY_SENSITIVE_FIELDS, - ORACLE_MTLS_SENSITIVE_FIELDS, - ORACLE_TLS_SENSITIVE_FIELDS, - POSTGRES_TLS_SENSITIVE_FIELDS, - SCHEMA, - in_memory_datastore, - oracle_datastore_mtls, - oracle_datastore_tls, - postgres_datastore_tls, +from ..datastores import DATASTORES_AND_THEIR_SENSITIVE_FIELDS +from .conftest import ( + create_conversation_summarization_transform, + create_message_summarization_transform, ) -def create_test_llm_config(): - return OpenAiConfig( - id="test_llm", - name="test_openai_config", - model_id="gpt-3.5-turbo", - ) - - -def create_message_summarization_transform(datastore): - # Use non-default values to ensure serialization/deserialization preserves exact values - return MessageSummarizationTransform( - id="test_msg_summarizer", - name="test_message_summarizer", - llm=create_test_llm_config(), - max_message_size=15_000, - summarization_instructions=( - "Create a concise summary of this message focusing on key points and actionable information." - ), - summarized_message_template="Message summary: {{summary}}", - datastore=datastore, - max_cache_size=2_500, - max_cache_lifetime=8 * 3600, - cache_collection_name="message_summaries_cache", - ) - - -def create_conversation_summarization_transform(datastore): - # Use non-default values to ensure serialization/deserialization preserves exact values - return ConversationSummarizationTransform( - id="test_conv_summarizer", - name="test_conversation_summarizer", - llm=create_test_llm_config(), - max_num_messages=75, - min_num_messages=25, - summarization_instructions=( - "Summarize this conversation thread, highlighting key decisions, action items, and important context." - ), - summarized_conversation_template="Conversation summary: {{summary}}", - datastore=datastore, - max_cache_size=5_000, - max_cache_lifetime=12 * 3600, - cache_collection_name="conversation_summaries_cache", - ) - - @pytest.mark.parametrize( - "datastore_factory, sensitive_fields", - [ - (in_memory_datastore, IN_MEMORY_SENSITIVE_FIELDS), - (oracle_datastore_tls, ORACLE_TLS_SENSITIVE_FIELDS), - (oracle_datastore_mtls, ORACLE_MTLS_SENSITIVE_FIELDS), - (postgres_datastore_tls, POSTGRES_TLS_SENSITIVE_FIELDS), - ], + "datastore, sensitive_fields", + DATASTORES_AND_THEIR_SENSITIVE_FIELDS, ) @pytest.mark.parametrize( "transform_factory", @@ -87,10 +29,8 @@ def create_conversation_summarization_transform(datastore): ], ) def test_can_serialize_and_deserialize_transform_with_all_datastores( - datastore_factory, sensitive_fields, transform_factory + transform_factory, datastore, sensitive_fields ): - datastore = datastore_factory(SCHEMA) - transform = transform_factory(datastore) serialized_transform = AgentSpecSerializer().to_yaml(transform) From ef880d5208e2e76568b1e47a32c2be40c746fb2a Mon Sep 17 00:00:00 2001 From: Youssef Boughizane Date: Fri, 19 Dec 2025 16:57:28 +0100 Subject: [PATCH 11/26] feat: agent can take list of transforms --- .../json_spec/agentspec_json_spec_26_1_0.json | 1230 +++++++++++++++-- pyagentspec/src/pyagentspec/agent.py | 3 + .../datastores/__init__.py | 0 .../datastores/conftest.py | 0 .../datastores/test_datastores.py | 0 pyagentspec/tests/serialization/test_agent.py | 35 + .../transforms/__init__.py | 6 + .../transforms/conftest.py | 0 .../test_summarization_transforms.py | 0 9 files changed, 1187 insertions(+), 87 deletions(-) rename pyagentspec/tests/{ => serialization}/datastores/__init__.py (100%) rename pyagentspec/tests/{ => serialization}/datastores/conftest.py (100%) rename pyagentspec/tests/{ => serialization}/datastores/test_datastores.py (100%) rename pyagentspec/tests/{ => serialization}/transforms/__init__.py (65%) rename pyagentspec/tests/{ => serialization}/transforms/conftest.py (100%) rename pyagentspec/tests/{ => serialization}/transforms/test_summarization_transforms.py (100%) diff --git a/docs/pyagentspec/source/agentspec/json_spec/agentspec_json_spec_26_1_0.json b/docs/pyagentspec/source/agentspec/json_spec/agentspec_json_spec_26_1_0.json index 978a28d7..565706c4 100644 --- a/docs/pyagentspec/source/agentspec/json_spec/agentspec_json_spec_26_1_0.json +++ b/docs/pyagentspec/source/agentspec/json_spec/agentspec_json_spec_26_1_0.json @@ -152,6 +152,16 @@ } ] }, + "ConversationSummarizationTransform": { + "anyOf": [ + { + "$ref": "#/$defs/BaseConversationSummarizationTransform" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, "DataFlowEdge": { "anyOf": [ { @@ -162,6 +172,16 @@ } ] }, + "Datastore": { + "anyOf": [ + { + "$ref": "#/$defs/BaseDatastore" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, "EndNode": { "anyOf": [ { @@ -202,6 +222,16 @@ "title": "HandoffMode", "type": "string" }, + "InMemoryCollectionDatastore": { + "anyOf": [ + { + "$ref": "#/$defs/BaseInMemoryCollectionDatastore" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, "InputMessageNode": { "anyOf": [ { @@ -306,6 +336,16 @@ } ] }, + "MTlsOracleDatabaseConnectionConfig": { + "anyOf": [ + { + "$ref": "#/$defs/BaseMTlsOracleDatabaseConnectionConfig" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, "ManagerWorkers": { "anyOf": [ { @@ -326,6 +366,16 @@ } ] }, + "MessageSummarizationTransform": { + "anyOf": [ + { + "$ref": "#/$defs/BaseMessageSummarizationTransform" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, "ModelProvider": { "description": "Provider of the model. It is used to ensure the requests to this model respect\nthe format expected by the provider.", "enum": [ @@ -466,6 +516,26 @@ } ] }, + "OracleDatabaseConnectionConfig": { + "anyOf": [ + { + "$ref": "#/$defs/BaseOracleDatabaseConnectionConfig" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "OracleDatabaseDatastore": { + "anyOf": [ + { + "$ref": "#/$defs/BaseOracleDatabaseDatastore" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, "OutputMessageNode": { "anyOf": [ { @@ -496,6 +566,26 @@ } ] }, + "PostgresDatabaseConnectionConfig": { + "anyOf": [ + { + "$ref": "#/$defs/BasePostgresDatabaseConnectionConfig" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "PostgresDatabaseDatastore": { + "anyOf": [ + { + "$ref": "#/$defs/BasePostgresDatabaseDatastore" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, "Property": { "description": "This object must be a valid JSON Schema", "type": "object" @@ -653,6 +743,26 @@ } ] }, + "TlsOracleDatabaseConnectionConfig": { + "anyOf": [ + { + "$ref": "#/$defs/BaseTlsOracleDatabaseConnectionConfig" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "TlsPostgresDatabaseConnectionConfig": { + "anyOf": [ + { + "$ref": "#/$defs/BaseTlsPostgresDatabaseConnectionConfig" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, "Tool": { "anyOf": [ { @@ -1881,6 +1991,119 @@ "type": "object", "x-abstract-component": false }, + "BaseConversationSummarizationTransform": { + "additionalProperties": false, + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "llm": { + "$ref": "#/$defs/LlmConfig" + }, + "max_num_messages": { + "default": 50, + "title": "Max Num Messages", + "type": "integer" + }, + "min_num_messages": { + "default": 10, + "title": "Min Num Messages", + "type": "integer" + }, + "summarization_instructions": { + "default": "Please make a summary of this conversation. Include relevant information and keep it short. Your response will replace the messages, so just output the summary directly, no introduction needed.", + "title": "Summarization Instructions", + "type": "string" + }, + "summarized_conversation_template": { + "default": "Summarized conversation: {{summary}}", + "title": "Summarized Conversation Template", + "type": "string" + }, + "datastore": { + "anyOf": [ + { + "type": "null" + }, + { + "$ref": "#/$defs/Datastore" + } + ], + "default": null + }, + "max_cache_size": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": 10000, + "title": "Max Cache Size" + }, + "max_cache_lifetime": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": 14400, + "title": "Max Cache Lifetime" + }, + "cache_collection_name": { + "default": "summarized_conversations_cache", + "title": "Cache Collection Name", + "type": "string" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "ConversationSummarizationTransform" + } + }, + "required": [ + "llm", + "name" + ], + "title": "ConversationSummarizationTransform", + "type": "object", + "x-abstract-component": false + }, "BaseDataFlowEdge": { "additionalProperties": false, "description": "A data flow edge specifies how the output of a node propagates as input of another node.\n\nAn outputs can be propagated as input of several nodes.", @@ -1949,6 +2172,20 @@ "type": "object", "x-abstract-component": false }, + "BaseDatastore": { + "anyOf": [ + { + "$ref": "#/$defs/InMemoryCollectionDatastore" + }, + { + "$ref": "#/$defs/OracleDatabaseDatastore" + }, + { + "$ref": "#/$defs/PostgresDatabaseDatastore" + } + ], + "x-abstract-component": true + }, "BaseEndNode": { "additionalProperties": false, "description": "End nodes denote the end of the execution of a flow.\n\nThere might be several end nodes in a flow, in which case the executor of the flow\nshould be able to track which one was reached and pass that back to the caller.\n\n- **Inputs**\n The list of inputs of the step. If both input and output properties are specified they\n must be an exact match\n\n If None is given, ``pyagentspec`` copies the outputs provided, if any. Otherwise, no input is exposed.\n- **Outputs**\n The list of outputs that should be exposed by the flow. If both input and output properties\n are specified they must be an exact match\n\n If None is given, ``pyagentspec`` copies the inputs provided, if any. Otherwise, no output is exposed.\n- **Branches**\n None.\n\nExamples\n--------\n>>> from pyagentspec.agent import Agent\n>>> from pyagentspec.flows.edges import ControlFlowEdge, DataFlowEdge\n>>> from pyagentspec.flows.flow import Flow\n>>> from pyagentspec.flows.nodes import AgentNode, BranchingNode, StartNode, EndNode\n>>> from pyagentspec.property import Property\n>>> languages_to_branch_name = {\n... \"english\": \"ENGLISH\",\n... \"spanish\": \"SPANISH\",\n... \"italian\": \"ITALIAN\",\n... }\n>>> language_property = Property(\n... json_schema={\"title\": \"language\", \"type\": \"string\"}\n... )\n>>> agent = Agent(\n... name=\"Language detector agent\",\n... llm_config=llm_config,\n... system_prompt=(\n... \"Your task is to understand the language spoken by the user.\"\n... \"Please output only the language in lowercase and submit.\"\n... ),\n... outputs=[language_property],\n... )\n>>> start_node = StartNode(name=\"start\")\n>>> english_end_node = EndNode(\n... name=\"english end\", branch_name=languages_to_branch_name[\"english\"]\n... )\n>>> spanish_end_node = EndNode(\n... name=\"spanish end\", branch_name=languages_to_branch_name[\"spanish\"]\n... )\n>>> italian_end_node = EndNode(\n... name=\"italian end\", branch_name=languages_to_branch_name[\"italian\"]\n... )\n>>> unknown_end_node = EndNode(name=\"unknown language end\", branch_name=\"unknown\")\n>>> branching_node = BranchingNode(\n... name=\"language check\",\n... mapping=languages_to_branch_name,\n... inputs=[language_property]\n... )\n>>> agent_node = AgentNode(\n... name=\"User input agent node\",\n... agent=agent,\n... )\n>>> assistant = Flow(\n... name=\"Check access flow\",\n... start_node=start_node,\n... nodes=[\n... start_node,\n... agent_node,\n... branching_node,\n... english_end_node,\n... spanish_end_node,\n... italian_end_node,\n... unknown_end_node,\n... ],\n... control_flow_connections=[\n... ControlFlowEdge(\n... name=\"start_to_agent\", from_node=start_node, to_node=agent_node\n... ),\n... ControlFlowEdge(\n... name=\"agent_to_branching\", from_node=agent_node, to_node=branching_node\n... ),\n... ControlFlowEdge(\n... name=\"branching_to_english_end\",\n... from_node=branching_node,\n... from_branch=languages_to_branch_name[\"english\"],\n... to_node=english_end_node,\n... ),\n... ControlFlowEdge(\n... name=\"branching_to_spanish_end\",\n... from_node=branching_node,\n... from_branch=languages_to_branch_name[\"spanish\"],\n... to_node=spanish_end_node,\n... ),\n... ControlFlowEdge(\n... name=\"branching_to_italian_end\",\n... from_node=branching_node,\n... from_branch=languages_to_branch_name[\"italian\"],\n... to_node=italian_end_node,\n... ),\n... ControlFlowEdge(\n... name=\"branching_to_unknown_end\",\n... from_node=branching_node,\n... from_branch=BranchingNode.DEFAULT_BRANCH,\n... to_node=unknown_end_node,\n... ),\n... ],\n... data_flow_connections=[\n... DataFlowEdge(\n... name=\"language_edge\",\n... source_node=agent_node,\n... source_output=\"language\",\n... destination_node=branching_node,\n... destination_input=\"language\",\n... ),\n... ],\n... )", @@ -2247,6 +2484,64 @@ "type": "object", "x-abstract-component": false }, + "BaseInMemoryCollectionDatastore": { + "additionalProperties": false, + "description": "In-memory datastore for testing and development purposes.", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "datastore_schema": { + "additionalProperties": { + "$ref": "#/$defs/Property" + }, + "title": "Datastore Schema", + "type": "object" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "InMemoryCollectionDatastore" + } + }, + "required": [ + "datastore_schema", + "name" + ], + "title": "InMemoryCollectionDatastore", + "type": "object", + "x-abstract-component": false + }, "BaseInputMessageNode": { "additionalProperties": false, "description": "This node interrupts the execution of the flow in order to wait for a user input, and restarts after receiving it.\nAn agent message, if given, is appended to the conversation before waiting for input.\nUser input is appended to the conversation as a user message, and it is returned as a string property from the node.\n\n- **Inputs**\n One per variable in the message\n- **Outputs**\n One string property that represents the content of the input user message.\n\n If None is given, ``pyagentspec`` infers a string property named ``user_input``.\n- **Branches**\n One, the default next.\n\nExamples\n--------\n>>> from pyagentspec.flows.edges import ControlFlowEdge, DataFlowEdge\n>>> from pyagentspec.flows.flow import Flow\n>>> from pyagentspec.flows.nodes import StartNode, EndNode, InputMessageNode, OutputMessageNode, LlmNode\n>>> from pyagentspec.property import StringProperty\n>>> start_node = StartNode(name=\"start\")\n>>> prompt_node = OutputMessageNode(name=\"ask_input\", message=\"What is the paragraph you want to rephrase?\")\n>>> input_node = InputMessageNode(name=\"user_input\", outputs=[StringProperty(title=\"user_input\")])\n>>> llm_node = LlmNode(\n... name=\"rephrase\",\n... llm_config=llm_config,\n... prompt_template=\"Rephrase {{user_input}}\",\n... outputs=[StringProperty(title=\"rephrased_user_input\")],\n... )\n>>> output_node = OutputMessageNode(name=\"ask_input\", message=\"{{rephrased_user_input}}\")\n>>> end_node = EndNode(name=\"end\")\n>>> flow = Flow(\n... name=\"rephrase_paragraph_flow\",\n... start_node=start_node,\n... nodes=[start_node, prompt_node, input_node, llm_node, output_node, end_node],\n... control_flow_connections=[\n... ControlFlowEdge(name=\"ce1\", from_node=start_node, to_node=prompt_node),\n... ControlFlowEdge(name=\"ce2\", from_node=prompt_node, to_node=input_node),\n... ControlFlowEdge(name=\"ce3\", from_node=input_node, to_node=llm_node),\n... ControlFlowEdge(name=\"ce4\", from_node=llm_node, to_node=output_node),\n... ControlFlowEdge(name=\"ce5\", from_node=output_node, to_node=end_node),\n... ],\n... data_flow_connections=[\n... DataFlowEdge(\n... name=\"de1\",\n... source_node=input_node,\n... source_output=\"user_input\",\n... destination_node=llm_node,\n... destination_input=\"user_input\",\n... ),\n... DataFlowEdge(\n... name=\"de2\",\n... source_node=llm_node,\n... source_output=\"rephrased_user_input\",\n... destination_node=output_node,\n... destination_input=\"rephrased_user_input\",\n... ),\n... ]\n... )", @@ -2712,9 +3007,9 @@ "type": "object", "x-abstract-component": false }, - "BaseManagerWorkers": { + "BaseMTlsOracleDatabaseConnectionConfig": { "additionalProperties": false, - "description": "Defines a ``ManagerWorkers`` conversational component.\n\nA ``ManagerWorkers`` is a multi-agent conversational component in which a group manager\nassigns tasks to the workers. The group manager and workers can be instantiated from\nany ``AgenticComponent`` type.\n\nExamples\n--------\n>>> from pyagentspec.agent import Agent\n>>> from pyagentspec.managerworkers import ManagerWorkers\n>>> manager_agent = Agent(\n... name=\"manager_agent\",\n... description=\"Agent that manages a group of math agents\",\n... llm_config=llm_config,\n... system_prompt=\"You are the manager of a group of math agents\"\n... )\n>>> multiplication_agent = Agent(\n... name=\"multiplication_agent\",\n... description=\"Agent that can do multiplication\",\n... llm_config=llm_config,\n... system_prompt=\"You can do multiplication.\"\n... )\n>>> division_agent = Agent(\n... name=\"division_agent\",\n... description=\"Agent that can do division\",\n... llm_config=llm_config,\n... system_prompt=\"You can do division.\"\n... )\n>>> group = ManagerWorkers(\n... name=\"managerworkers\",\n... group_manager=manager_agent,\n... workers=[multiplication_agent, division_agent],\n... )", + "description": "Mutual-TLS Connection Configuration to Oracle Database.", "properties": { "id": { "title": "Id", @@ -2748,29 +3043,125 @@ ], "title": "Metadata" }, - "inputs": { + "user": { + "title": "User", + "type": "string" + }, + "password": { + "title": "Password", + "type": "string" + }, + "dsn": { + "title": "Dsn", + "type": "string" + }, + "config_dir": { "anyOf": [ { - "items": { - "$ref": "#/$defs/Property" - }, - "type": "array" + "type": "string" }, { "type": "null" } ], "default": null, - "title": "Inputs" + "title": "Config Dir" }, - "outputs": { - "anyOf": [ - { - "items": { - "$ref": "#/$defs/Property" - }, - "type": "array" - }, + "protocol": { + "default": "tcps", + "enum": [ + "tcp", + "tcps" + ], + "title": "Protocol", + "type": "string" + }, + "wallet_location": { + "title": "Wallet Location", + "type": "string" + }, + "wallet_password": { + "title": "Wallet Password", + "type": "string" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "MTlsOracleDatabaseConnectionConfig" + } + }, + "required": [ + "dsn", + "name", + "password", + "user", + "wallet_location", + "wallet_password" + ], + "title": "MTlsOracleDatabaseConnectionConfig", + "type": "object", + "x-abstract-component": false + }, + "BaseManagerWorkers": { + "additionalProperties": false, + "description": "Defines a ``ManagerWorkers`` conversational component.\n\nA ``ManagerWorkers`` is a multi-agent conversational component in which a group manager\nassigns tasks to the workers. The group manager and workers can be instantiated from\nany ``AgenticComponent`` type.\n\nExamples\n--------\n>>> from pyagentspec.agent import Agent\n>>> from pyagentspec.managerworkers import ManagerWorkers\n>>> manager_agent = Agent(\n... name=\"manager_agent\",\n... description=\"Agent that manages a group of math agents\",\n... llm_config=llm_config,\n... system_prompt=\"You are the manager of a group of math agents\"\n... )\n>>> multiplication_agent = Agent(\n... name=\"multiplication_agent\",\n... description=\"Agent that can do multiplication\",\n... llm_config=llm_config,\n... system_prompt=\"You can do multiplication.\"\n... )\n>>> division_agent = Agent(\n... name=\"division_agent\",\n... description=\"Agent that can do division\",\n... llm_config=llm_config,\n... system_prompt=\"You can do division.\"\n... )\n>>> group = ManagerWorkers(\n... name=\"managerworkers\",\n... group_manager=manager_agent,\n... workers=[multiplication_agent, division_agent],\n... )", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "inputs": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Inputs" + }, + "outputs": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" + }, { "type": "null" } @@ -2910,6 +3301,114 @@ "type": "object", "x-abstract-component": false }, + "BaseMessageSummarizationTransform": { + "additionalProperties": false, + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "llm": { + "$ref": "#/$defs/LlmConfig" + }, + "max_message_size": { + "default": 20000, + "title": "Max Message Size", + "type": "integer" + }, + "summarization_instructions": { + "default": "Please make a summary of this message. Include relevant information and keep it short. Your response will replace the message, so just output the summary directly, no introduction needed.", + "title": "Summarization Instructions", + "type": "string" + }, + "summarized_message_template": { + "default": "Summarized message: {{summary}}", + "title": "Summarized Message Template", + "type": "string" + }, + "datastore": { + "anyOf": [ + { + "type": "null" + }, + { + "$ref": "#/$defs/Datastore" + } + ], + "default": null + }, + "max_cache_size": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": 10000, + "title": "Max Cache Size" + }, + "max_cache_lifetime": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": 14400, + "title": "Max Cache Lifetime" + }, + "cache_collection_name": { + "default": "summarized_messages_cache", + "title": "Cache Collection Name", + "type": "string" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "MessageSummarizationTransform" + } + }, + "required": [ + "llm", + "name" + ], + "title": "MessageSummarizationTransform", + "type": "object", + "x-abstract-component": false + }, "BaseNode": { "anyOf": [ { @@ -3696,6 +4195,79 @@ "type": "object", "x-abstract-component": false }, + "BaseOracleDatabaseConnectionConfig": { + "anyOf": [ + { + "$ref": "#/$defs/MTlsOracleDatabaseConnectionConfig" + }, + { + "$ref": "#/$defs/TlsOracleDatabaseConnectionConfig" + } + ], + "x-abstract-component": true + }, + "BaseOracleDatabaseDatastore": { + "additionalProperties": false, + "description": "Datastore that uses Oracle Database as the storage mechanism.", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "datastore_schema": { + "additionalProperties": { + "$ref": "#/$defs/Property" + }, + "title": "Datastore Schema", + "type": "object" + }, + "connection_config": { + "$ref": "#/$defs/OracleDatabaseConnectionConfig" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "OracleDatabaseDatastore" + } + }, + "required": [ + "connection_config", + "datastore_schema", + "name" + ], + "title": "OracleDatabaseDatastore", + "type": "object", + "x-abstract-component": false + }, "BaseOutputMessageNode": { "additionalProperties": false, "description": "This node appends an agent message to the ongoing flow conversation.\n\n- **Inputs**\n One per variable in the message.\n- **Outputs**\n No outputs.\n- **Branches**\n One, the default next.\n\nExamples\n--------\n>>> from pyagentspec.flows.edges import ControlFlowEdge, DataFlowEdge\n>>> from pyagentspec.flows.flow import Flow\n>>> from pyagentspec.flows.nodes import StartNode, EndNode, InputMessageNode, OutputMessageNode, LlmNode\n>>> from pyagentspec.property import StringProperty\n>>> start_node = StartNode(name=\"start\")\n>>> prompt_node = OutputMessageNode(name=\"ask_input\", message=\"What is the paragraph you want to rephrase?\")\n>>> input_node = InputMessageNode(name=\"user_input\", outputs=[StringProperty(title=\"user_input\")])\n>>> llm_node = LlmNode(\n... name=\"rephrase\",\n... llm_config=llm_config,\n... prompt_template=\"Rephrase {{user_input}}\",\n... outputs=[StringProperty(title=\"rephrased_user_input\")],\n... )\n>>> output_node = OutputMessageNode(name=\"ask_input\", message=\"{{rephrased_user_input}}\")\n>>> end_node = EndNode(name=\"end\")\n>>> flow = Flow(\n... name=\"rephrase_paragraph_flow\",\n... start_node=start_node,\n... nodes=[start_node, prompt_node, input_node, llm_node, output_node, end_node],\n... control_flow_connections=[\n... ControlFlowEdge(name=\"ce1\", from_node=start_node, to_node=prompt_node),\n... ControlFlowEdge(name=\"ce2\", from_node=prompt_node, to_node=input_node),\n... ControlFlowEdge(name=\"ce3\", from_node=input_node, to_node=llm_node),\n... ControlFlowEdge(name=\"ce4\", from_node=llm_node, to_node=output_node),\n... ControlFlowEdge(name=\"ce5\", from_node=output_node, to_node=end_node),\n... ],\n... data_flow_connections=[\n... DataFlowEdge(\n... name=\"de1\",\n... source_node=input_node,\n... source_output=\"user_input\",\n... destination_node=llm_node,\n... destination_input=\"user_input\",\n... ),\n... DataFlowEdge(\n... name=\"de2\",\n... source_node=llm_node,\n... source_output=\"rephrased_user_input\",\n... destination_node=output_node,\n... destination_input=\"rephrased_user_input\",\n... ),\n... ]\n... )", @@ -3988,6 +4560,76 @@ "type": "object", "x-abstract-component": false }, + "BasePostgresDatabaseConnectionConfig": { + "anyOf": [ + { + "$ref": "#/$defs/TlsPostgresDatabaseConnectionConfig" + } + ], + "x-abstract-component": true + }, + "BasePostgresDatabaseDatastore": { + "additionalProperties": false, + "description": "Datastore that uses PostgreSQL Database as the storage mechanism.", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "datastore_schema": { + "additionalProperties": { + "$ref": "#/$defs/Property" + }, + "title": "Datastore Schema", + "type": "object" + }, + "connection_config": { + "$ref": "#/$defs/PostgresDatabaseConnectionConfig" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "PostgresDatabaseDatastore" + } + }, + "required": [ + "connection_config", + "datastore_schema", + "name" + ], + "title": "PostgresDatabaseDatastore", + "type": "object", + "x-abstract-component": false + }, "BaseRemoteAgent": { "anyOf": [ { @@ -4858,42 +5500,249 @@ "type": "null" } ], - "default": null, - "title": "Sensitive Headers" - }, - "key_file": { - "title": "Key File", - "type": "string" - }, - "cert_file": { - "title": "Cert File", - "type": "string" - }, - "ca_file": { - "title": "Ca File", - "type": "string" - }, - "$referenced_components": { - "$ref": "#/$defs/ReferencedComponents" - }, - "component_type": { - "const": "StreamableHTTPmTLSTransport" + "default": null, + "title": "Sensitive Headers" + }, + "key_file": { + "title": "Key File", + "type": "string" + }, + "cert_file": { + "title": "Cert File", + "type": "string" + }, + "ca_file": { + "title": "Ca File", + "type": "string" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "StreamableHTTPmTLSTransport" + } + }, + "required": [ + "ca_file", + "cert_file", + "key_file", + "name", + "url" + ], + "title": "StreamableHTTPmTLSTransport", + "type": "object", + "x-abstract-component": false + }, + "BaseSwarm": { + "additionalProperties": false, + "description": "Defines a ``Swarm`` conversational component.\n\nA ``Swarm`` is a multi-agent conversational component in which each agent determines\nthe next agent to be executed, based on a list of pre-defined relationships.\nAgents in Swarm can be any ``AgenticComponent``.\n\nExamples\n--------\n>>> from pyagentspec.agent import Agent\n>>> from pyagentspec.swarm import Swarm\n>>> addition_agent = Agent(name=\"addition_agent\", description=\"Agent that can do additions\", llm_config=llm_config, system_prompt=\"You can do additions.\")\n>>> multiplication_agent = Agent(name=\"multiplication_agent\", description=\"Agent that can do multiplication\", llm_config=llm_config, system_prompt=\"You can do multiplication.\")\n>>> division_agent = Agent(name=\"division_agent\", description=\"Agent that can do division\", llm_config=llm_config, system_prompt=\"You can do division.\")\n>>>\n>>> swarm = Swarm(\n... name=\"swarm\",\n... first_agent=addition_agent,\n... relationships=[\n... (addition_agent, multiplication_agent),\n... (addition_agent, division_agent),\n... (multiplication_agent, division_agent),\n... ]\n... )", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "inputs": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Inputs" + }, + "outputs": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Outputs" + }, + "first_agent": { + "$ref": "#/$defs/AgenticComponent" + }, + "relationships": { + "items": { + "maxItems": 2, + "minItems": 2, + "prefixItems": [ + { + "$ref": "#/$defs/AgenticComponent" + }, + { + "$ref": "#/$defs/AgenticComponent" + } + ], + "type": "array" + }, + "title": "Relationships", + "type": "array" + }, + "handoff": { + "anyOf": [ + { + "type": "boolean" + }, + { + "$ref": "#/$defs/HandoffMode" + } + ], + "default": "optional", + "title": "Handoff" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "Swarm" + } + }, + "required": [ + "first_agent", + "name", + "relationships" + ], + "title": "Swarm", + "type": "object", + "x-abstract-component": false + }, + "BaseTlsOracleDatabaseConnectionConfig": { + "anyOf": [ + { + "$ref": "#/$defs/MTlsOracleDatabaseConnectionConfig" + }, + { + "additionalProperties": false, + "description": "TLS Connection Configuration to Oracle Database.", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "user": { + "title": "User", + "type": "string" + }, + "password": { + "title": "Password", + "type": "string" + }, + "dsn": { + "title": "Dsn", + "type": "string" + }, + "config_dir": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Config Dir" + }, + "protocol": { + "default": "tcps", + "enum": [ + "tcp", + "tcps" + ], + "title": "Protocol", + "type": "string" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "TlsOracleDatabaseConnectionConfig" + } + }, + "required": [ + "dsn", + "name", + "password", + "user" + ], + "title": "TlsOracleDatabaseConnectionConfig", + "type": "object" } - }, - "required": [ - "ca_file", - "cert_file", - "key_file", - "name", - "url" ], - "title": "StreamableHTTPmTLSTransport", - "type": "object", "x-abstract-component": false }, - "BaseSwarm": { + "BaseTlsPostgresDatabaseConnectionConfig": { "additionalProperties": false, - "description": "Defines a ``Swarm`` conversational component.\n\nA ``Swarm`` is a multi-agent conversational component in which each agent determines\nthe next agent to be executed, based on a list of pre-defined relationships.\nAgents in Swarm can be any ``AgenticComponent``.\n\nExamples\n--------\n>>> from pyagentspec.agent import Agent\n>>> from pyagentspec.swarm import Swarm\n>>> addition_agent = Agent(name=\"addition_agent\", description=\"Agent that can do additions\", llm_config=llm_config, system_prompt=\"You can do additions.\")\n>>> multiplication_agent = Agent(name=\"multiplication_agent\", description=\"Agent that can do multiplication\", llm_config=llm_config, system_prompt=\"You can do multiplication.\")\n>>> division_agent = Agent(name=\"division_agent\", description=\"Agent that can do division\", llm_config=llm_config, system_prompt=\"You can do division.\")\n>>>\n>>> swarm = Swarm(\n... name=\"swarm\",\n... first_agent=addition_agent,\n... relationships=[\n... (addition_agent, multiplication_agent),\n... (addition_agent, division_agent),\n... (multiplication_agent, division_agent),\n... ]\n... )", + "description": "Configuration for a PostgreSQL connection with TLS/SSL support.", "properties": { "id": { "title": "Id", @@ -4927,81 +5776,93 @@ ], "title": "Metadata" }, - "inputs": { + "user": { + "title": "User", + "type": "string" + }, + "password": { + "title": "Password", + "type": "string" + }, + "url": { + "title": "Url", + "type": "string" + }, + "sslmode": { + "default": "require", + "enum": [ + "allow", + "disable", + "prefer", + "require", + "verify-ca", + "verify-full" + ], + "title": "Sslmode", + "type": "string" + }, + "sslcert": { "anyOf": [ { - "items": { - "$ref": "#/$defs/Property" - }, - "type": "array" + "type": "string" }, { "type": "null" } ], "default": null, - "title": "Inputs" + "title": "Sslcert" }, - "outputs": { + "sslkey": { "anyOf": [ { - "items": { - "$ref": "#/$defs/Property" - }, - "type": "array" + "type": "string" }, { "type": "null" } ], "default": null, - "title": "Outputs" - }, - "first_agent": { - "$ref": "#/$defs/AgenticComponent" + "title": "Sslkey" }, - "relationships": { - "items": { - "maxItems": 2, - "minItems": 2, - "prefixItems": [ - { - "$ref": "#/$defs/AgenticComponent" - }, - { - "$ref": "#/$defs/AgenticComponent" - } - ], - "type": "array" - }, - "title": "Relationships", - "type": "array" + "sslrootcert": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Sslrootcert" }, - "handoff": { + "sslcrl": { "anyOf": [ { - "type": "boolean" + "type": "string" }, { - "$ref": "#/$defs/HandoffMode" + "type": "null" } ], - "default": "optional", - "title": "Handoff" + "default": null, + "title": "Sslcrl" }, "$referenced_components": { "$ref": "#/$defs/ReferencedComponents" }, "component_type": { - "const": "Swarm" + "const": "TlsPostgresDatabaseConnectionConfig" } }, "required": [ - "first_agent", "name", - "relationships" + "password", + "url", + "user" ], - "title": "Swarm", + "title": "TlsPostgresDatabaseConnectionConfig", "type": "object", "x-abstract-component": false }, @@ -5254,9 +6115,15 @@ { "$ref": "#/$defs/BaseControlFlowEdge" }, + { + "$ref": "#/$defs/BaseConversationSummarizationTransform" + }, { "$ref": "#/$defs/BaseDataFlowEdge" }, + { + "$ref": "#/$defs/BaseDatastore" + }, { "$ref": "#/$defs/BaseEndNode" }, @@ -5266,6 +6133,9 @@ { "$ref": "#/$defs/BaseFlowNode" }, + { + "$ref": "#/$defs/BaseInMemoryCollectionDatastore" + }, { "$ref": "#/$defs/BaseInputMessageNode" }, @@ -5284,12 +6154,18 @@ { "$ref": "#/$defs/BaseMCPToolSpec" }, + { + "$ref": "#/$defs/BaseMTlsOracleDatabaseConnectionConfig" + }, { "$ref": "#/$defs/BaseManagerWorkers" }, { "$ref": "#/$defs/BaseMapNode" }, + { + "$ref": "#/$defs/BaseMessageSummarizationTransform" + }, { "$ref": "#/$defs/BaseNode" }, @@ -5323,6 +6199,12 @@ { "$ref": "#/$defs/BaseOpenAiConfig" }, + { + "$ref": "#/$defs/BaseOracleDatabaseConnectionConfig" + }, + { + "$ref": "#/$defs/BaseOracleDatabaseDatastore" + }, { "$ref": "#/$defs/BaseOutputMessageNode" }, @@ -5332,6 +6214,12 @@ { "$ref": "#/$defs/BaseParallelMapNode" }, + { + "$ref": "#/$defs/BasePostgresDatabaseConnectionConfig" + }, + { + "$ref": "#/$defs/BasePostgresDatabaseDatastore" + }, { "$ref": "#/$defs/BaseRemoteAgent" }, @@ -5368,6 +6256,12 @@ { "$ref": "#/$defs/BaseSwarm" }, + { + "$ref": "#/$defs/BaseTlsOracleDatabaseConnectionConfig" + }, + { + "$ref": "#/$defs/BaseTlsPostgresDatabaseConnectionConfig" + }, { "$ref": "#/$defs/BaseTool" }, @@ -5618,6 +6512,21 @@ } } }, + "VersionedConversationSummarizationTransform": { + "anyOf": [ + { + "$ref": "#/$defs/BaseConversationSummarizationTransform" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, "VersionedDataFlowEdge": { "anyOf": [ { @@ -5633,6 +6542,21 @@ } } }, + "VersionedDatastore": { + "anyOf": [ + { + "$ref": "#/$defs/BaseDatastore" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, "VersionedEndNode": { "anyOf": [ { @@ -5678,6 +6602,21 @@ } } }, + "VersionedInMemoryCollectionDatastore": { + "anyOf": [ + { + "$ref": "#/$defs/BaseInMemoryCollectionDatastore" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, "VersionedInputMessageNode": { "anyOf": [ { @@ -5768,6 +6707,21 @@ } } }, + "VersionedMTlsOracleDatabaseConnectionConfig": { + "anyOf": [ + { + "$ref": "#/$defs/BaseMTlsOracleDatabaseConnectionConfig" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, "VersionedManagerWorkers": { "anyOf": [ { @@ -5798,6 +6752,21 @@ } } }, + "VersionedMessageSummarizationTransform": { + "anyOf": [ + { + "$ref": "#/$defs/BaseMessageSummarizationTransform" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, "VersionedNode": { "anyOf": [ { @@ -5963,6 +6932,21 @@ } } }, + "VersionedOracleDatabaseDatastore": { + "anyOf": [ + { + "$ref": "#/$defs/BaseOracleDatabaseDatastore" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, "VersionedOutputMessageNode": { "anyOf": [ { @@ -6008,6 +6992,21 @@ } } }, + "VersionedPostgresDatabaseDatastore": { + "anyOf": [ + { + "$ref": "#/$defs/BasePostgresDatabaseDatastore" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, "VersionedRemoteAgent": { "anyOf": [ { @@ -6188,6 +7187,36 @@ } } }, + "VersionedTlsOracleDatabaseConnectionConfig": { + "anyOf": [ + { + "$ref": "#/$defs/BaseTlsOracleDatabaseConnectionConfig" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedTlsPostgresDatabaseConnectionConfig": { + "anyOf": [ + { + "$ref": "#/$defs/BaseTlsPostgresDatabaseConnectionConfig" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, "VersionedTool": { "anyOf": [ { @@ -6311,9 +7340,15 @@ { "$ref": "#/$defs/VersionedControlFlowEdge" }, + { + "$ref": "#/$defs/VersionedConversationSummarizationTransform" + }, { "$ref": "#/$defs/VersionedDataFlowEdge" }, + { + "$ref": "#/$defs/VersionedDatastore" + }, { "$ref": "#/$defs/VersionedEndNode" }, @@ -6323,6 +7358,9 @@ { "$ref": "#/$defs/VersionedFlowNode" }, + { + "$ref": "#/$defs/VersionedInMemoryCollectionDatastore" + }, { "$ref": "#/$defs/VersionedInputMessageNode" }, @@ -6341,12 +7379,18 @@ { "$ref": "#/$defs/VersionedMCPToolSpec" }, + { + "$ref": "#/$defs/VersionedMTlsOracleDatabaseConnectionConfig" + }, { "$ref": "#/$defs/VersionedManagerWorkers" }, { "$ref": "#/$defs/VersionedMapNode" }, + { + "$ref": "#/$defs/VersionedMessageSummarizationTransform" + }, { "$ref": "#/$defs/VersionedNode" }, @@ -6380,6 +7424,9 @@ { "$ref": "#/$defs/VersionedOpenAiConfig" }, + { + "$ref": "#/$defs/VersionedOracleDatabaseDatastore" + }, { "$ref": "#/$defs/VersionedOutputMessageNode" }, @@ -6389,6 +7436,9 @@ { "$ref": "#/$defs/VersionedParallelMapNode" }, + { + "$ref": "#/$defs/VersionedPostgresDatabaseDatastore" + }, { "$ref": "#/$defs/VersionedRemoteAgent" }, @@ -6425,6 +7475,12 @@ { "$ref": "#/$defs/VersionedSwarm" }, + { + "$ref": "#/$defs/VersionedTlsOracleDatabaseConnectionConfig" + }, + { + "$ref": "#/$defs/VersionedTlsPostgresDatabaseConnectionConfig" + }, { "$ref": "#/$defs/VersionedTool" }, diff --git a/pyagentspec/src/pyagentspec/agent.py b/pyagentspec/src/pyagentspec/agent.py index 64be8ed0..ec75c3a0 100644 --- a/pyagentspec/src/pyagentspec/agent.py +++ b/pyagentspec/src/pyagentspec/agent.py @@ -16,6 +16,7 @@ from pyagentspec.templating import get_placeholder_properties_from_string from pyagentspec.tools.tool import Tool from pyagentspec.tools.toolbox import ToolBox +from pyagentspec.transforms import MessageTransform from pyagentspec.versioning import AgentSpecVersionEnum @@ -54,6 +55,8 @@ class Agent(AgenticComponent): """List of toolboxes that are passed to the agent.""" human_in_the_loop: bool = True """Flag that determines if the Agent can request input from the user.""" + transforms: List[MessageTransform] = [] + """Additional message transforms that the agent will use to modify its vision of the messages""" def _get_inferred_inputs(self) -> List[Property]: # Extract all the placeholders in the prompt and make them string inputs by default diff --git a/pyagentspec/tests/datastores/__init__.py b/pyagentspec/tests/serialization/datastores/__init__.py similarity index 100% rename from pyagentspec/tests/datastores/__init__.py rename to pyagentspec/tests/serialization/datastores/__init__.py diff --git a/pyagentspec/tests/datastores/conftest.py b/pyagentspec/tests/serialization/datastores/conftest.py similarity index 100% rename from pyagentspec/tests/datastores/conftest.py rename to pyagentspec/tests/serialization/datastores/conftest.py diff --git a/pyagentspec/tests/datastores/test_datastores.py b/pyagentspec/tests/serialization/datastores/test_datastores.py similarity index 100% rename from pyagentspec/tests/datastores/test_datastores.py rename to pyagentspec/tests/serialization/datastores/test_datastores.py diff --git a/pyagentspec/tests/serialization/test_agent.py b/pyagentspec/tests/serialization/test_agent.py index 51c541c6..427d111a 100644 --- a/pyagentspec/tests/serialization/test_agent.py +++ b/pyagentspec/tests/serialization/test_agent.py @@ -19,6 +19,11 @@ from ..conftest import read_agentspec_config_file from .conftest import assert_serialized_representations_are_equal +from .datastores import DATASTORES_AND_THEIR_SENSITIVE_FIELDS +from .transforms import ( + create_conversation_summarization_transform, + create_message_summarization_transform, +) @pytest.fixture() @@ -273,3 +278,33 @@ def test_deserializing_agent_with_builtin_tools_and_unsupported_version_raises( with pytest.raises(ValueError, match="Invalid agentspec_version"): _ = AgentSpecDeserializer().from_yaml(serialized_node) + + +@pytest.mark.parametrize( + "datastore, sensitive_fields", + DATASTORES_AND_THEIR_SENSITIVE_FIELDS, +) +def test_agent_with_non_empty_transforms_can_be_serialized_and_deserialized( + datastore, sensitive_fields, vllmconfig +): + transforms = [ + create_message_summarization_transform(datastore), + create_conversation_summarization_transform(datastore), + ] + + agent = Agent( + id="agent1", + name="Funny agent", + llm_config=vllmconfig, + system_prompt="No matter what the user asks, don't reply but make a joke instead", + transforms=transforms, + ) + + serializer = AgentSpecSerializer() + serialized_agent = serializer.to_yaml(agent) + assert len(serialized_agent.strip()) > 0 + + deserialized_agent = AgentSpecDeserializer().from_yaml( + yaml_content=serialized_agent, components_registry=sensitive_fields + ) + assert deserialized_agent == agent diff --git a/pyagentspec/tests/transforms/__init__.py b/pyagentspec/tests/serialization/transforms/__init__.py similarity index 65% rename from pyagentspec/tests/transforms/__init__.py rename to pyagentspec/tests/serialization/transforms/__init__.py index b6a1c6eb..9a5e8602 100644 --- a/pyagentspec/tests/transforms/__init__.py +++ b/pyagentspec/tests/serialization/transforms/__init__.py @@ -3,3 +3,9 @@ # This software is under the Apache License 2.0 # (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) or Universal Permissive License # (UPL) 1.0 (LICENSE-UPL or https://oss.oracle.com/licenses/upl), at your option. + +from .conftest import ( + create_conversation_summarization_transform, + create_message_summarization_transform, + create_test_llm_config, +) diff --git a/pyagentspec/tests/transforms/conftest.py b/pyagentspec/tests/serialization/transforms/conftest.py similarity index 100% rename from pyagentspec/tests/transforms/conftest.py rename to pyagentspec/tests/serialization/transforms/conftest.py diff --git a/pyagentspec/tests/transforms/test_summarization_transforms.py b/pyagentspec/tests/serialization/transforms/test_summarization_transforms.py similarity index 100% rename from pyagentspec/tests/transforms/test_summarization_transforms.py rename to pyagentspec/tests/serialization/transforms/test_summarization_transforms.py From caf127f239cc7f63a2dc0545a8b7d38b54ad88cf Mon Sep 17 00:00:00 2001 From: Youssef Boughizane Date: Fri, 19 Dec 2025 17:34:14 +0100 Subject: [PATCH 12/26] feat: agent raises with unsupported version --- pyagentspec/src/pyagentspec/agent.py | 2 +- ...le_serialized_agent_with_tools_25_4_2.yaml | 1 + pyagentspec/tests/serialization/test_agent.py | 41 +++++++++++++++++++ 3 files changed, 43 insertions(+), 1 deletion(-) diff --git a/pyagentspec/src/pyagentspec/agent.py b/pyagentspec/src/pyagentspec/agent.py index ec75c3a0..e6377279 100644 --- a/pyagentspec/src/pyagentspec/agent.py +++ b/pyagentspec/src/pyagentspec/agent.py @@ -77,7 +77,7 @@ def _versioned_model_fields_to_exclude( def _infer_min_agentspec_version_from_configuration(self) -> AgentSpecVersionEnum: parent_min_version = super()._infer_min_agentspec_version_from_configuration() current_object_min_version = self.min_agentspec_version - if self.toolboxes or not self.human_in_the_loop: + if self.toolboxes or not self.human_in_the_loop or self.transforms: # We first check if the component requires toolboxes) # If that's the case, we set the min version to 25.4.2, when toolboxes were introduced # Similarly, human_in_the_loop was only added in 25.4.2 (human_in_the_loop=True was diff --git a/pyagentspec/tests/agentspec_configs/example_serialized_agent_with_tools_25_4_2.yaml b/pyagentspec/tests/agentspec_configs/example_serialized_agent_with_tools_25_4_2.yaml index 4cffb5cb..c1bad396 100644 --- a/pyagentspec/tests/agentspec_configs/example_serialized_agent_with_tools_25_4_2.yaml +++ b/pyagentspec/tests/agentspec_configs/example_serialized_agent_with_tools_25_4_2.yaml @@ -74,4 +74,5 @@ tools: url: https://my.url/tool requires_confirmation: false human_in_the_loop: true +transforms: [] agentspec_version: 25.4.2 diff --git a/pyagentspec/tests/serialization/test_agent.py b/pyagentspec/tests/serialization/test_agent.py index 427d111a..d9af70c4 100644 --- a/pyagentspec/tests/serialization/test_agent.py +++ b/pyagentspec/tests/serialization/test_agent.py @@ -308,3 +308,44 @@ def test_agent_with_non_empty_transforms_can_be_serialized_and_deserialized( yaml_content=serialized_agent, components_registry=sensitive_fields ) assert deserialized_agent == agent + + +@pytest.fixture +def agent_with_non_empty_transforms(vllmconfig): + datastore, _ = DATASTORES_AND_THEIR_SENSITIVE_FIELDS[0] + transforms = [ + create_message_summarization_transform(datastore), + ] + + agent = Agent( + id="agent1", + name="Funny agent", + llm_config=vllmconfig, + system_prompt="No matter what the user asks, don't reply but make a joke instead", + transforms=transforms, + ) + return agent + + +def test_serializing_agent_with_non_empty_transforms_and_unsupported_version_raises( + agent_with_non_empty_transforms, +): + serializer = AgentSpecSerializer() + with pytest.raises(ValueError, match="Invalid agentspec_version"): + _ = serializer.to_yaml( + agent_with_non_empty_transforms, agentspec_version=AgentSpecVersionEnum.v25_4_1 + ) + + +def test_deserializing_agent_with_non_empty_transforms_and_unsupported_version_raises( + agent_with_non_empty_transforms, +): + serializer = AgentSpecSerializer() + serialized_node = serializer.to_yaml(agent_with_non_empty_transforms) + assert "agentspec_version: 25.4.2" in serialized_node + serialized_node = serialized_node.replace( + "agentspec_version: 25.4.2", "agentspec_version: 25.4.1" + ) + + with pytest.raises(ValueError, match="Invalid agentspec_version"): + _ = AgentSpecDeserializer().from_yaml(serialized_node) From 038817c879c4403287d1e20c99d4d3c0cda35e46 Mon Sep 17 00:00:00 2001 From: Youssef Boughizane Date: Mon, 22 Dec 2025 10:02:04 +0100 Subject: [PATCH 13/26] fix: added correct version for datastores, transforms, dbconfigs, llm_configs --- pyagentspec/src/pyagentspec/agent.py | 2 ++ .../src/pyagentspec/datastores/datastore.py | 8 +++++++- .../src/pyagentspec/datastores/oracle.py | 8 ++++++++ .../src/pyagentspec/datastores/postgres.py | 8 ++++++++ .../src/pyagentspec/transforms/transforms.py | 8 ++++++++ pyagentspec/tests/serialization/test_agent.py | 17 ++++++++++++++++- 6 files changed, 49 insertions(+), 2 deletions(-) diff --git a/pyagentspec/src/pyagentspec/agent.py b/pyagentspec/src/pyagentspec/agent.py index e6377279..1464dc70 100644 --- a/pyagentspec/src/pyagentspec/agent.py +++ b/pyagentspec/src/pyagentspec/agent.py @@ -72,6 +72,7 @@ def _versioned_model_fields_to_exclude( if agentspec_version < AgentSpecVersionEnum.v25_4_2: fields_to_exclude.add("toolboxes") fields_to_exclude.add("human_in_the_loop") + fields_to_exclude.add("transforms") return fields_to_exclude def _infer_min_agentspec_version_from_configuration(self) -> AgentSpecVersionEnum: @@ -82,5 +83,6 @@ def _infer_min_agentspec_version_from_configuration(self) -> AgentSpecVersionEnu # If that's the case, we set the min version to 25.4.2, when toolboxes were introduced # Similarly, human_in_the_loop was only added in 25.4.2 (human_in_the_loop=True was # the de-facto default before) + # Similary, transforms was only added in 25.4.2 current_object_min_version = AgentSpecVersionEnum.v25_4_2 return max(parent_min_version, current_object_min_version) diff --git a/pyagentspec/src/pyagentspec/datastores/datastore.py b/pyagentspec/src/pyagentspec/datastores/datastore.py index ce05e642..a299764b 100644 --- a/pyagentspec/src/pyagentspec/datastores/datastore.py +++ b/pyagentspec/src/pyagentspec/datastores/datastore.py @@ -6,10 +6,12 @@ """This module defines the base, relational and in-memory datastore component.""" from typing import Annotated, Dict -from pydantic import AfterValidator +from pydantic import AfterValidator, Field +from pydantic.json_schema import SkipJsonSchema from pyagentspec.component import Component from pyagentspec.property import Property +from pyagentspec.versioning import AgentSpecVersionEnum def is_object_property(value: Property) -> Property: @@ -27,6 +29,10 @@ def is_object_property(value: Property) -> Property: class Datastore(Component, abstract=True): """Base class for Datastores. Datastores store and retrieve data.""" + min_agentspec_version: SkipJsonSchema[AgentSpecVersionEnum] = Field( + default=AgentSpecVersionEnum.v25_4_2, init=False, exclude=True + ) + class RelationalDatastore(Datastore, abstract=True): """A relational data store that supports querying data using SQL-like queries. diff --git a/pyagentspec/src/pyagentspec/datastores/oracle.py b/pyagentspec/src/pyagentspec/datastores/oracle.py index dfe35eb8..36a1b89c 100644 --- a/pyagentspec/src/pyagentspec/datastores/oracle.py +++ b/pyagentspec/src/pyagentspec/datastores/oracle.py @@ -7,8 +7,12 @@ from typing import Literal, Optional +from pydantic import Field +from pydantic.json_schema import SkipJsonSchema + from pyagentspec.component import Component from pyagentspec.sensitive_field import SensitiveField +from pyagentspec.versioning import AgentSpecVersionEnum from .datastore import RelationalDatastore @@ -16,6 +20,10 @@ class OracleDatabaseConnectionConfig(Component, abstract=True): """Base class used for configuring connections to Oracle Database.""" + min_agentspec_version: SkipJsonSchema[AgentSpecVersionEnum] = Field( + default=AgentSpecVersionEnum.v25_4_2, init=False, exclude=True + ) + class OracleDatabaseDatastore(RelationalDatastore): """Datastore that uses Oracle Database as the storage mechanism.""" diff --git a/pyagentspec/src/pyagentspec/datastores/postgres.py b/pyagentspec/src/pyagentspec/datastores/postgres.py index 664a53c7..2151dfda 100644 --- a/pyagentspec/src/pyagentspec/datastores/postgres.py +++ b/pyagentspec/src/pyagentspec/datastores/postgres.py @@ -7,8 +7,12 @@ from typing import Literal, Optional +from pydantic import Field +from pydantic.json_schema import SkipJsonSchema + from pyagentspec.component import Component from pyagentspec.sensitive_field import SensitiveField +from pyagentspec.versioning import AgentSpecVersionEnum from .datastore import RelationalDatastore @@ -16,6 +20,10 @@ class PostgresDatabaseConnectionConfig(Component, abstract=True): """Base class for a PostgreSQL connection.""" + min_agentspec_version: SkipJsonSchema[AgentSpecVersionEnum] = Field( + default=AgentSpecVersionEnum.v25_4_2, init=False, exclude=True + ) + class PostgresDatabaseDatastore(RelationalDatastore): """Datastore that uses PostgreSQL Database as the storage mechanism.""" diff --git a/pyagentspec/src/pyagentspec/transforms/transforms.py b/pyagentspec/src/pyagentspec/transforms/transforms.py index 716e663b..04800458 100644 --- a/pyagentspec/src/pyagentspec/transforms/transforms.py +++ b/pyagentspec/src/pyagentspec/transforms/transforms.py @@ -4,8 +4,16 @@ # (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) or Universal Permissive License # (UPL) 1.0 (LICENSE-UPL or https://oss.oracle.com/licenses/upl), at your option. """This file defines the base MessageTransform""" +from pydantic import Field +from pydantic.json_schema import SkipJsonSchema + from pyagentspec.component import Component +from pyagentspec.versioning import AgentSpecVersionEnum class MessageTransform(Component, abstract=True): """Base class for message transformation components.""" + + min_agentspec_version: SkipJsonSchema[AgentSpecVersionEnum] = Field( + default=AgentSpecVersionEnum.v25_4_2, init=False, exclude=True + ) diff --git a/pyagentspec/tests/serialization/test_agent.py b/pyagentspec/tests/serialization/test_agent.py index d9af70c4..789299e8 100644 --- a/pyagentspec/tests/serialization/test_agent.py +++ b/pyagentspec/tests/serialization/test_agent.py @@ -15,6 +15,10 @@ from pyagentspec.serialization import AgentSpecSerializer from pyagentspec.serialization.deserializer import AgentSpecDeserializer from pyagentspec.tools import BuiltinTool, ClientTool, RemoteTool, ServerTool +from pyagentspec.transforms.summarization import ( + ConversationSummarizationTransform, + MessageSummarizationTransform, +) from pyagentspec.versioning import AgentSpecVersionEnum from ..conftest import read_agentspec_config_file @@ -287,10 +291,22 @@ def test_deserializing_agent_with_builtin_tools_and_unsupported_version_raises( def test_agent_with_non_empty_transforms_can_be_serialized_and_deserialized( datastore, sensitive_fields, vllmconfig ): + print(f"Testing with datastore type: {type(datastore).__name__}") transforms = [ create_message_summarization_transform(datastore), create_conversation_summarization_transform(datastore), ] + # The default min_agentspec_version for VllmConfig is v25_4_1. If we leave it unchanged, + # the agent with non-empty transforms would serialize to v25_4_2 (due to the transforms requiring that version). + # During deserialization, all fields including vllmconfig would be deserialized to v25_4_2, + # but vllmconfig's min_agentspec_version would still be v25_4_1, causing the test deserialized == original to fail. + # Therefore, we explicitly set the version to v25_4_2 for vllmconfig and the transforms' LLMs. + vllmconfig.min_agentspec_version = AgentSpecVersionEnum.v25_4_2 + for transform in transforms: + if isinstance( + transform, (ConversationSummarizationTransform, MessageSummarizationTransform) + ): + transform.llm.min_agentspec_version = AgentSpecVersionEnum.v25_4_2 agent = Agent( id="agent1", @@ -303,7 +319,6 @@ def test_agent_with_non_empty_transforms_can_be_serialized_and_deserialized( serializer = AgentSpecSerializer() serialized_agent = serializer.to_yaml(agent) assert len(serialized_agent.strip()) > 0 - deserialized_agent = AgentSpecDeserializer().from_yaml( yaml_content=serialized_agent, components_registry=sensitive_fields ) From a8f8a096f3859705d66ed78bb50e5695d371c4f6 Mon Sep 17 00:00:00 2001 From: Youssef Boughizane Date: Mon, 22 Dec 2025 10:09:18 +0100 Subject: [PATCH 14/26] fix: update .yaml with transforms=[] --- .../example_serialized_agent_with_tools_and_builtin_tools.yaml | 1 + .../example_serialized_agent_with_tools_and_toolboxes.yaml | 1 + ...example_serialized_agent_with_tools_no_human_in_the_loop.yaml | 1 + 3 files changed, 3 insertions(+) diff --git a/pyagentspec/tests/agentspec_configs/example_serialized_agent_with_tools_and_builtin_tools.yaml b/pyagentspec/tests/agentspec_configs/example_serialized_agent_with_tools_and_builtin_tools.yaml index c89d7089..e61f4c1e 100644 --- a/pyagentspec/tests/agentspec_configs/example_serialized_agent_with_tools_and_builtin_tools.yaml +++ b/pyagentspec/tests/agentspec_configs/example_serialized_agent_with_tools_and_builtin_tools.yaml @@ -87,4 +87,5 @@ tools: executor_name: demo_executor tool_version: '1.0' human_in_the_loop: true +transforms: [] agentspec_version: 25.4.2 diff --git a/pyagentspec/tests/agentspec_configs/example_serialized_agent_with_tools_and_toolboxes.yaml b/pyagentspec/tests/agentspec_configs/example_serialized_agent_with_tools_and_toolboxes.yaml index 2d464b0f..bc555462 100644 --- a/pyagentspec/tests/agentspec_configs/example_serialized_agent_with_tools_and_toolboxes.yaml +++ b/pyagentspec/tests/agentspec_configs/example_serialized_agent_with_tools_and_toolboxes.yaml @@ -73,4 +73,5 @@ $referenced_components: headers: null sensitive_headers: null human_in_the_loop: true +transforms: [] agentspec_version: 25.4.2 diff --git a/pyagentspec/tests/agentspec_configs/example_serialized_agent_with_tools_no_human_in_the_loop.yaml b/pyagentspec/tests/agentspec_configs/example_serialized_agent_with_tools_no_human_in_the_loop.yaml index ea3bd508..2cba6057 100644 --- a/pyagentspec/tests/agentspec_configs/example_serialized_agent_with_tools_no_human_in_the_loop.yaml +++ b/pyagentspec/tests/agentspec_configs/example_serialized_agent_with_tools_no_human_in_the_loop.yaml @@ -74,4 +74,5 @@ tools: url: https://my.url/tool requires_confirmation: false human_in_the_loop: false +transforms: [] agentspec_version: 25.4.2 From 9ede7231ba3dd3585494c6f5b4b5143db7229f96 Mon Sep 17 00:00:00 2001 From: Youssef Boughizane Date: Mon, 22 Dec 2025 10:15:55 +0100 Subject: [PATCH 15/26] refactor: correct typo in tests --- pyagentspec/tests/serialization/test_agent.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pyagentspec/tests/serialization/test_agent.py b/pyagentspec/tests/serialization/test_agent.py index 789299e8..7ed2ef17 100644 --- a/pyagentspec/tests/serialization/test_agent.py +++ b/pyagentspec/tests/serialization/test_agent.py @@ -356,11 +356,11 @@ def test_deserializing_agent_with_non_empty_transforms_and_unsupported_version_r agent_with_non_empty_transforms, ): serializer = AgentSpecSerializer() - serialized_node = serializer.to_yaml(agent_with_non_empty_transforms) - assert "agentspec_version: 25.4.2" in serialized_node - serialized_node = serialized_node.replace( + serialized_agent = serializer.to_yaml(agent_with_non_empty_transforms) + assert "agentspec_version: 25.4.2" in serialized_agent + serialized_agent = serialized_agent.replace( "agentspec_version: 25.4.2", "agentspec_version: 25.4.1" ) with pytest.raises(ValueError, match="Invalid agentspec_version"): - _ = AgentSpecDeserializer().from_yaml(serialized_node) + _ = AgentSpecDeserializer().from_yaml(serialized_agent) From dd2392ebe8568b78207fb42f1a721962bbff7bf5 Mon Sep 17 00:00:00 2001 From: Youssef Boughizane Date: Mon, 22 Dec 2025 10:39:26 +0100 Subject: [PATCH 16/26] fix: correct version of llm configs to ensure deserialized == original --- pyagentspec/tests/serialization/test_agent.py | 11 +---------- .../tests/serialization/transforms/conftest.py | 6 +++++- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/pyagentspec/tests/serialization/test_agent.py b/pyagentspec/tests/serialization/test_agent.py index 7ed2ef17..de849073 100644 --- a/pyagentspec/tests/serialization/test_agent.py +++ b/pyagentspec/tests/serialization/test_agent.py @@ -15,10 +15,6 @@ from pyagentspec.serialization import AgentSpecSerializer from pyagentspec.serialization.deserializer import AgentSpecDeserializer from pyagentspec.tools import BuiltinTool, ClientTool, RemoteTool, ServerTool -from pyagentspec.transforms.summarization import ( - ConversationSummarizationTransform, - MessageSummarizationTransform, -) from pyagentspec.versioning import AgentSpecVersionEnum from ..conftest import read_agentspec_config_file @@ -300,13 +296,8 @@ def test_agent_with_non_empty_transforms_can_be_serialized_and_deserialized( # the agent with non-empty transforms would serialize to v25_4_2 (due to the transforms requiring that version). # During deserialization, all fields including vllmconfig would be deserialized to v25_4_2, # but vllmconfig's min_agentspec_version would still be v25_4_1, causing the test deserialized == original to fail. - # Therefore, we explicitly set the version to v25_4_2 for vllmconfig and the transforms' LLMs. + # Therefore, we explicitly set the version to v25_4_2 vllmconfig.min_agentspec_version = AgentSpecVersionEnum.v25_4_2 - for transform in transforms: - if isinstance( - transform, (ConversationSummarizationTransform, MessageSummarizationTransform) - ): - transform.llm.min_agentspec_version = AgentSpecVersionEnum.v25_4_2 agent = Agent( id="agent1", diff --git a/pyagentspec/tests/serialization/transforms/conftest.py b/pyagentspec/tests/serialization/transforms/conftest.py index 6658a25c..f4665614 100644 --- a/pyagentspec/tests/serialization/transforms/conftest.py +++ b/pyagentspec/tests/serialization/transforms/conftest.py @@ -6,14 +6,18 @@ from pyagentspec.llms import OpenAiConfig from pyagentspec.transforms import ConversationSummarizationTransform, MessageSummarizationTransform +from pyagentspec.versioning import AgentSpecVersionEnum def create_test_llm_config(): - return OpenAiConfig( + config = OpenAiConfig( id="test_llm", name="test_openai_config", model_id="gpt-3.5-turbo", ) + # Set to v25_4_2 to match the serialized agentspec_version, ensuring deserialized == original + config.min_agentspec_version = AgentSpecVersionEnum.v25_4_2 + return config def create_message_summarization_transform(datastore): From 38d2c4e5cb20be684c8ce1873ddc5c2bf1d87a2a Mon Sep 17 00:00:00 2001 From: Youssef Boughizane Date: Mon, 22 Dec 2025 13:26:50 +0100 Subject: [PATCH 17/26] dev: add versioning tests --- .../json_spec/agentspec_json_spec_26_1_0.json | 32 +++++++++++++++++ .../datastores/test_datastores.py | 9 +++++ .../serialization/transforms/conftest.py | 19 +++++++++++ .../test_summarization_transforms.py | 34 +++++++++---------- 4 files changed, 76 insertions(+), 18 deletions(-) diff --git a/docs/pyagentspec/source/agentspec/json_spec/agentspec_json_spec_26_1_0.json b/docs/pyagentspec/source/agentspec/json_spec/agentspec_json_spec_26_1_0.json index 565706c4..cc9a719c 100644 --- a/docs/pyagentspec/source/agentspec/json_spec/agentspec_json_spec_26_1_0.json +++ b/docs/pyagentspec/source/agentspec/json_spec/agentspec_json_spec_26_1_0.json @@ -376,6 +376,16 @@ } ] }, + "MessageTransform": { + "anyOf": [ + { + "$ref": "#/$defs/BaseMessageTransform" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, "ModelProvider": { "description": "Provider of the model. It is used to ensure the requests to this model respect\nthe format expected by the provider.", "enum": [ @@ -1103,6 +1113,14 @@ "title": "Human In The Loop", "type": "boolean" }, + "transforms": { + "default": [], + "items": { + "$ref": "#/$defs/MessageTransform" + }, + "title": "Transforms", + "type": "array" + }, "$referenced_components": { "$ref": "#/$defs/ReferencedComponents" }, @@ -3409,6 +3427,17 @@ "type": "object", "x-abstract-component": false }, + "BaseMessageTransform": { + "anyOf": [ + { + "$ref": "#/$defs/ConversationSummarizationTransform" + }, + { + "$ref": "#/$defs/MessageSummarizationTransform" + } + ], + "x-abstract-component": true + }, "BaseNode": { "anyOf": [ { @@ -6166,6 +6195,9 @@ { "$ref": "#/$defs/BaseMessageSummarizationTransform" }, + { + "$ref": "#/$defs/BaseMessageTransform" + }, { "$ref": "#/$defs/BaseNode" }, diff --git a/pyagentspec/tests/serialization/datastores/test_datastores.py b/pyagentspec/tests/serialization/datastores/test_datastores.py index d3c32a5f..b3e440a6 100644 --- a/pyagentspec/tests/serialization/datastores/test_datastores.py +++ b/pyagentspec/tests/serialization/datastores/test_datastores.py @@ -8,6 +8,7 @@ from pyagentspec.serialization.deserializer import AgentSpecDeserializer from pyagentspec.serialization.serializer import AgentSpecSerializer +from pyagentspec.versioning import AgentSpecVersionEnum from .conftest import DATASTORES_AND_THEIR_SENSITIVE_FIELDS @@ -27,3 +28,11 @@ def test_can_serialize_and_deserialize_datastore(datastore, sensitive_fields) -> yaml_content=serialized_ds, components_registry=sensitive_fields ) assert deserialized_ds == datastore + + +@pytest.mark.parametrize("datastore, sensitive_fields", DATASTORES_AND_THEIR_SENSITIVE_FIELDS) +def test_serialization_with_unsupported_version_raises(datastore, sensitive_fields) -> None: + with pytest.raises( + ValueError, match="Invalid agentspec_version:.*but the minimum allowed version is.*" + ): + _ = AgentSpecSerializer().to_json(datastore, agentspec_version=AgentSpecVersionEnum.v25_4_1) diff --git a/pyagentspec/tests/serialization/transforms/conftest.py b/pyagentspec/tests/serialization/transforms/conftest.py index f4665614..a7daff4d 100644 --- a/pyagentspec/tests/serialization/transforms/conftest.py +++ b/pyagentspec/tests/serialization/transforms/conftest.py @@ -4,10 +4,14 @@ # (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) or Universal Permissive License # (UPL) 1.0 (LICENSE-UPL or https://oss.oracle.com/licenses/upl), at your option. +import pytest + from pyagentspec.llms import OpenAiConfig from pyagentspec.transforms import ConversationSummarizationTransform, MessageSummarizationTransform from pyagentspec.versioning import AgentSpecVersionEnum +from ..datastores import DATASTORES_AND_THEIR_SENSITIVE_FIELDS + def create_test_llm_config(): config = OpenAiConfig( @@ -55,3 +59,18 @@ def create_conversation_summarization_transform(datastore): max_cache_lifetime=12 * 3600, cache_collection_name="conversation_summaries_cache", ) + + +def parametrize_transform_and_datastore(f): + f = pytest.mark.parametrize( + "datastore, sensitive_fields", + DATASTORES_AND_THEIR_SENSITIVE_FIELDS, + )(f) + f = pytest.mark.parametrize( + "transform_factory", + [ + create_message_summarization_transform, + create_conversation_summarization_transform, + ], + )(f) + return f diff --git a/pyagentspec/tests/serialization/transforms/test_summarization_transforms.py b/pyagentspec/tests/serialization/transforms/test_summarization_transforms.py index ad676992..eeb8bbc5 100644 --- a/pyagentspec/tests/serialization/transforms/test_summarization_transforms.py +++ b/pyagentspec/tests/serialization/transforms/test_summarization_transforms.py @@ -9,25 +9,12 @@ from pyagentspec.serialization.deserializer import AgentSpecDeserializer from pyagentspec.serialization.serializer import AgentSpecSerializer +from pyagentspec.versioning import AgentSpecVersionEnum -from ..datastores import DATASTORES_AND_THEIR_SENSITIVE_FIELDS -from .conftest import ( - create_conversation_summarization_transform, - create_message_summarization_transform, -) - - -@pytest.mark.parametrize( - "datastore, sensitive_fields", - DATASTORES_AND_THEIR_SENSITIVE_FIELDS, -) -@pytest.mark.parametrize( - "transform_factory", - [ - create_message_summarization_transform, - create_conversation_summarization_transform, - ], -) +from .conftest import parametrize_transform_and_datastore + + +@parametrize_transform_and_datastore def test_can_serialize_and_deserialize_transform_with_all_datastores( transform_factory, datastore, sensitive_fields ): @@ -48,3 +35,14 @@ def test_can_serialize_and_deserialize_transform_with_all_datastores( yaml_content=serialized_transform, components_registry=sensitive_fields ) assert deserialized_transform == transform + + +@parametrize_transform_and_datastore +def test_transform_serialization_with_unsupported_version_raises( + transform_factory, datastore, sensitive_fields +): + transform = transform_factory(datastore) + with pytest.raises( + ValueError, match="Invalid agentspec_version:.*but the minimum allowed version is.*" + ): + _ = AgentSpecSerializer().to_json(transform, agentspec_version=AgentSpecVersionEnum.v25_4_1) From f8056e1019456a55b936971b5bba93642d80357d Mon Sep 17 00:00:00 2001 From: Youssef Boughizane Date: Mon, 22 Dec 2025 14:46:53 +0100 Subject: [PATCH 18/26] tests: add test for deserializing datastore/tranform of wrong version --- .../serialization/datastores/test_datastores.py | 16 +++++++++++++++- .../transforms/test_summarization_transforms.py | 17 +++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/pyagentspec/tests/serialization/datastores/test_datastores.py b/pyagentspec/tests/serialization/datastores/test_datastores.py index b3e440a6..44a6345f 100644 --- a/pyagentspec/tests/serialization/datastores/test_datastores.py +++ b/pyagentspec/tests/serialization/datastores/test_datastores.py @@ -31,8 +31,22 @@ def test_can_serialize_and_deserialize_datastore(datastore, sensitive_fields) -> @pytest.mark.parametrize("datastore, sensitive_fields", DATASTORES_AND_THEIR_SENSITIVE_FIELDS) -def test_serialization_with_unsupported_version_raises(datastore, sensitive_fields) -> None: +def test_datastore_serialization_with_unsupported_version_raises( + datastore, sensitive_fields +) -> None: with pytest.raises( ValueError, match="Invalid agentspec_version:.*but the minimum allowed version is.*" ): _ = AgentSpecSerializer().to_json(datastore, agentspec_version=AgentSpecVersionEnum.v25_4_1) + + +@pytest.mark.parametrize("datastore, sensitive_fields", DATASTORES_AND_THEIR_SENSITIVE_FIELDS) +def test_deserialization_with_unsupported_version_raises(datastore, sensitive_fields) -> None: + serialized_ds = AgentSpecSerializer().to_yaml(datastore) + assert "agentspec_version: 25.4.2" in serialized_ds + serialized_ds = serialized_ds.replace("agentspec_version: 25.4.2", "agentspec_version: 25.4.1") + + with pytest.raises(ValueError, match="Invalid agentspec_version"): + _ = AgentSpecDeserializer().from_yaml( + yaml_content=serialized_ds, components_registry=sensitive_fields + ) diff --git a/pyagentspec/tests/serialization/transforms/test_summarization_transforms.py b/pyagentspec/tests/serialization/transforms/test_summarization_transforms.py index eeb8bbc5..fc9243d0 100644 --- a/pyagentspec/tests/serialization/transforms/test_summarization_transforms.py +++ b/pyagentspec/tests/serialization/transforms/test_summarization_transforms.py @@ -46,3 +46,20 @@ def test_transform_serialization_with_unsupported_version_raises( ValueError, match="Invalid agentspec_version:.*but the minimum allowed version is.*" ): _ = AgentSpecSerializer().to_json(transform, agentspec_version=AgentSpecVersionEnum.v25_4_1) + + +@parametrize_transform_and_datastore +def test_transform_deserialization_with_unsupported_version_raises( + transform_factory, datastore, sensitive_fields +): + transform = transform_factory(datastore) + serialized_transform = AgentSpecSerializer().to_yaml(transform) + assert "agentspec_version: 25.4.2" in serialized_transform + serialized_transform = serialized_transform.replace( + "agentspec_version: 25.4.2", "agentspec_version: 25.4.1" + ) + + with pytest.raises(ValueError, match="Invalid agentspec_version"): + _ = AgentSpecDeserializer().from_yaml( + yaml_content=serialized_transform, components_registry=sensitive_fields + ) From e680794f2ada5ff50e292b24ef2128aedaddbbd0 Mon Sep 17 00:00:00 2001 From: Youssef Boughizane Date: Mon, 22 Dec 2025 16:38:57 +0100 Subject: [PATCH 19/26] fix: add transforms:[] to serialized yamls --- .../agentspec_configs/example_serialized_managerworkers.yaml | 3 +++ .../example_serialized_specialized_agent.yaml | 1 + .../tests/agentspec_configs/example_serialized_swarm.yaml | 3 +++ .../example_tools_with_confirmation_25_4_2.yaml | 1 + 4 files changed, 8 insertions(+) diff --git a/pyagentspec/tests/agentspec_configs/example_serialized_managerworkers.yaml b/pyagentspec/tests/agentspec_configs/example_serialized_managerworkers.yaml index 0f8331c0..2cf6f2e8 100644 --- a/pyagentspec/tests/agentspec_configs/example_serialized_managerworkers.yaml +++ b/pyagentspec/tests/agentspec_configs/example_serialized_managerworkers.yaml @@ -27,6 +27,7 @@ group_manager: customer interactions and orchestrating the resolution process efficiently. tools: [] toolboxes: [] + transforms: [] human_in_the_loop: true workers: - component_type: Agent @@ -43,6 +44,7 @@ workers: refund requests accurately and efficiently based on company policy. tools: [] toolboxes: [] + transforms: [] human_in_the_loop: true - component_type: Agent id: satisfaction_surveyor @@ -58,6 +60,7 @@ workers: customer interactions and orchestrating the resolution process efficiently. tools: [] toolboxes: [] + transforms: [] human_in_the_loop: true $referenced_components: vllm: diff --git a/pyagentspec/tests/agentspec_configs/example_serialized_specialized_agent.yaml b/pyagentspec/tests/agentspec_configs/example_serialized_specialized_agent.yaml index d631aeb8..939ea475 100644 --- a/pyagentspec/tests/agentspec_configs/example_serialized_specialized_agent.yaml +++ b/pyagentspec/tests/agentspec_configs/example_serialized_specialized_agent.yaml @@ -59,6 +59,7 @@ agent: - title: forecast type: string requires_confirmation: false + transforms: [] human_in_the_loop: true agent_specialization_parameters: component_type: AgentSpecializationParameters diff --git a/pyagentspec/tests/agentspec_configs/example_serialized_swarm.yaml b/pyagentspec/tests/agentspec_configs/example_serialized_swarm.yaml index 21279782..da5820cd 100644 --- a/pyagentspec/tests/agentspec_configs/example_serialized_swarm.yaml +++ b/pyagentspec/tests/agentspec_configs/example_serialized_swarm.yaml @@ -36,6 +36,7 @@ $referenced_components: system_prompt: You are a general practitioner tools: [] toolboxes: [] + transforms: [] human_in_the_loop: true vllm: component_type: VllmConfig @@ -61,6 +62,7 @@ $referenced_components: system_prompt: You are a pharmacist tools: [] toolboxes: [] + transforms: [] human_in_the_loop: true dermatologist: component_type: Agent @@ -75,5 +77,6 @@ $referenced_components: system_prompt: You are a dermatologist tools: [] toolboxes: [] + transforms: [] human_in_the_loop: true agentspec_version: 25.4.2 diff --git a/pyagentspec/tests/agentspec_configs/example_tools_with_confirmation_25_4_2.yaml b/pyagentspec/tests/agentspec_configs/example_tools_with_confirmation_25_4_2.yaml index 5de21a5b..ed011996 100644 --- a/pyagentspec/tests/agentspec_configs/example_tools_with_confirmation_25_4_2.yaml +++ b/pyagentspec/tests/agentspec_configs/example_tools_with_confirmation_25_4_2.yaml @@ -88,5 +88,6 @@ tools: executor_name: orch_executor tool_version: 1.0.0 toolboxes: [] +transforms: [] human_in_the_loop: true agentspec_version: 25.4.2 From 1263e44328ea47e4c33fa05d068740eb27212327 Mon Sep 17 00:00:00 2001 From: Youssef Boughizane Date: Mon, 22 Dec 2025 16:51:01 +0100 Subject: [PATCH 20/26] fix : fails with Optional[Datastore] --- .../pyagentspec/serialization/deserializationcontext.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pyagentspec/src/pyagentspec/serialization/deserializationcontext.py b/pyagentspec/src/pyagentspec/serialization/deserializationcontext.py index 01a89f74..2335c214 100644 --- a/pyagentspec/src/pyagentspec/serialization/deserializationcontext.py +++ b/pyagentspec/src/pyagentspec/serialization/deserializationcontext.py @@ -286,7 +286,6 @@ def _load_field( return self._load_reference(content["$component_ref"], annotation=annotation) origin_type = get_origin(annotation) - if origin_type is Annotated: inner_annotation, _ = get_args(annotation) return self._load_field(content, inner_annotation) @@ -304,6 +303,8 @@ def _load_field( and inspect.isclass(annotation) and issubclass(annotation, Property) ): + if isinstance(content, annotation): + return content, [] return Property(json_schema=content), [] elif self._is_pydantic_type(annotation): return self._load_pydantic_model_from_dict(content, annotation) @@ -381,6 +382,10 @@ def _load_field( return None, [] inner_annotations.remove(type(None)) + # This check is needed because `is_optional_type` accepts Union[T,G,None] + if len(inner_annotations) == 1: + return self._load_field(content, inner_annotations[0]) + # Try to deserialize components/pydantic models according to any of the annotations # If any of them works, we will proceed with that. This is our best effort. for inner_annotation in inner_annotations: From ef21f2b3491785132c5281352a35a5bfcb81c3ff Mon Sep 17 00:00:00 2001 From: Youssef Boughizane Date: Mon, 22 Dec 2025 17:00:48 +0100 Subject: [PATCH 21/26] fix: accumulate errors and remove _is_python_type --- .../serialization/deserializationcontext.py | 46 ++++--------------- 1 file changed, 10 insertions(+), 36 deletions(-) diff --git a/pyagentspec/src/pyagentspec/serialization/deserializationcontext.py b/pyagentspec/src/pyagentspec/serialization/deserializationcontext.py index 2335c214..51c6cc87 100644 --- a/pyagentspec/src/pyagentspec/serialization/deserializationcontext.py +++ b/pyagentspec/src/pyagentspec/serialization/deserializationcontext.py @@ -161,27 +161,6 @@ def _is_python_primitive_type(self, annotation: Optional[type]) -> bool: return False return issubclass(annotation, (bool, int, float, str)) - def _is_python_type(self, annotation: Optional[type]) -> bool: - origin_type = get_origin(annotation) - - if origin_type is None: - return True - if origin_type == dict: - dict_key_annotation, dict_value_annotation = get_args(annotation) - return self._is_python_type(dict_key_annotation) and self._is_python_type( - dict_value_annotation - ) - elif origin_type == list or origin_type == set: - (list_value_annotation,) = get_args(annotation) - return self._is_python_type(list_value_annotation) - elif origin_type == set: - (set_value_annotation,) = get_args(annotation) - return self._is_python_type(set_value_annotation) - elif origin_type == Union: - return all(self._is_python_type(t) for t in get_args(annotation)) - else: - return self._is_python_primitive_type(annotation) - def _is_pydantic_type(self, annotation: Optional[type]) -> TypeGuard[Type[BaseModel]]: try: return issubclass(annotation, BaseModel) if annotation is not None else False @@ -388,25 +367,20 @@ def _load_field( # Try to deserialize components/pydantic models according to any of the annotations # If any of them works, we will proceed with that. This is our best effort. + accumulated_errors = [] for inner_annotation in inner_annotations: try: return self._load_field(content, inner_annotation) - except Exception: # nosec + except Exception as e: # nosec # Something went wrong in deserialization, - # it's not the right type, we try the next one - pass - - # We tried all the components and pydantic models, and it did not work out, - # only python type is left. If it is only normal python types, just return the content - if self._is_python_type(annotation): - return content, [] - else: - # If even python type fails, then we do not support this, - # or there's an error in the representation - raise ValueError( - f"It looks like the annotation {annotation} is a mix of" - f" python and Agent Spec types which is not supported." - ) + # accumulate the error and try the next one + accumulated_errors.append(f"{inner_annotation}: {str(e)}") + + # If all attempts failed, raise ValueError with all accumulated errors + formatted_errors = "\n".join(f" - {error}" for error in accumulated_errors) + raise ValueError( + f"Failed to deserialize Union type {annotation} with content {content}.\nErrors:\n{formatted_errors}" + ) elif origin_type == Literal: return content, [] From c29c4ebcb35ad4b40e9a75f5282d3f95ac4e0471 Mon Sep 17 00:00:00 2001 From: Youssef Boughizane Date: Tue, 23 Dec 2025 11:38:07 +0100 Subject: [PATCH 22/26] fix: make the features belong to 26.1.1 --- pyagentspec/src/pyagentspec/agent.py | 12 +++++++++--- .../src/pyagentspec/datastores/datastore.py | 2 +- pyagentspec/src/pyagentspec/datastores/oracle.py | 2 +- pyagentspec/src/pyagentspec/datastores/postgres.py | 2 +- .../src/pyagentspec/transforms/transforms.py | 2 +- pyagentspec/src/pyagentspec/versioning.py | 3 ++- ...example_serialized_agent_with_tools_25_4_2.yaml | 1 - ...ialized_agent_with_tools_and_builtin_tools.yaml | 1 - ..._serialized_agent_with_tools_and_toolboxes.yaml | 1 - ...ized_agent_with_tools_no_human_in_the_loop.yaml | 1 - .../example_serialized_managerworkers.yaml | 3 --- .../example_serialized_specialized_agent.yaml | 1 - .../example_serialized_swarm.yaml | 3 --- .../example_tools_with_confirmation_25_4_2.yaml | 1 - .../serialization/datastores/test_datastores.py | 6 +++--- pyagentspec/tests/serialization/test_agent.py | 14 +++++++------- .../tests/serialization/transforms/conftest.py | 4 ++-- .../transforms/test_summarization_transforms.py | 6 +++--- 18 files changed, 30 insertions(+), 35 deletions(-) diff --git a/pyagentspec/src/pyagentspec/agent.py b/pyagentspec/src/pyagentspec/agent.py index 1464dc70..69e42758 100644 --- a/pyagentspec/src/pyagentspec/agent.py +++ b/pyagentspec/src/pyagentspec/agent.py @@ -72,17 +72,23 @@ def _versioned_model_fields_to_exclude( if agentspec_version < AgentSpecVersionEnum.v25_4_2: fields_to_exclude.add("toolboxes") fields_to_exclude.add("human_in_the_loop") + if agentspec_version < AgentSpecVersionEnum.v26_1_1: fields_to_exclude.add("transforms") return fields_to_exclude def _infer_min_agentspec_version_from_configuration(self) -> AgentSpecVersionEnum: parent_min_version = super()._infer_min_agentspec_version_from_configuration() current_object_min_version = self.min_agentspec_version - if self.toolboxes or not self.human_in_the_loop or self.transforms: + if self.toolboxes or not self.human_in_the_loop: # We first check if the component requires toolboxes) # If that's the case, we set the min version to 25.4.2, when toolboxes were introduced # Similarly, human_in_the_loop was only added in 25.4.2 (human_in_the_loop=True was # the de-facto default before) - # Similary, transforms was only added in 25.4.2 - current_object_min_version = AgentSpecVersionEnum.v25_4_2 + current_object_min_version = max( + current_object_min_version, AgentSpecVersionEnum.v25_4_2 + ) + if self.transforms: + current_object_min_version = max( + current_object_min_version, AgentSpecVersionEnum.v26_1_1 + ) return max(parent_min_version, current_object_min_version) diff --git a/pyagentspec/src/pyagentspec/datastores/datastore.py b/pyagentspec/src/pyagentspec/datastores/datastore.py index a299764b..7990ab82 100644 --- a/pyagentspec/src/pyagentspec/datastores/datastore.py +++ b/pyagentspec/src/pyagentspec/datastores/datastore.py @@ -30,7 +30,7 @@ class Datastore(Component, abstract=True): """Base class for Datastores. Datastores store and retrieve data.""" min_agentspec_version: SkipJsonSchema[AgentSpecVersionEnum] = Field( - default=AgentSpecVersionEnum.v25_4_2, init=False, exclude=True + default=AgentSpecVersionEnum.v26_1_1, init=False, exclude=True ) diff --git a/pyagentspec/src/pyagentspec/datastores/oracle.py b/pyagentspec/src/pyagentspec/datastores/oracle.py index 36a1b89c..c9416d5f 100644 --- a/pyagentspec/src/pyagentspec/datastores/oracle.py +++ b/pyagentspec/src/pyagentspec/datastores/oracle.py @@ -21,7 +21,7 @@ class OracleDatabaseConnectionConfig(Component, abstract=True): """Base class used for configuring connections to Oracle Database.""" min_agentspec_version: SkipJsonSchema[AgentSpecVersionEnum] = Field( - default=AgentSpecVersionEnum.v25_4_2, init=False, exclude=True + default=AgentSpecVersionEnum.v26_1_1, init=False, exclude=True ) diff --git a/pyagentspec/src/pyagentspec/datastores/postgres.py b/pyagentspec/src/pyagentspec/datastores/postgres.py index 2151dfda..38972640 100644 --- a/pyagentspec/src/pyagentspec/datastores/postgres.py +++ b/pyagentspec/src/pyagentspec/datastores/postgres.py @@ -21,7 +21,7 @@ class PostgresDatabaseConnectionConfig(Component, abstract=True): """Base class for a PostgreSQL connection.""" min_agentspec_version: SkipJsonSchema[AgentSpecVersionEnum] = Field( - default=AgentSpecVersionEnum.v25_4_2, init=False, exclude=True + default=AgentSpecVersionEnum.v26_1_1, init=False, exclude=True ) diff --git a/pyagentspec/src/pyagentspec/transforms/transforms.py b/pyagentspec/src/pyagentspec/transforms/transforms.py index 04800458..27a917c9 100644 --- a/pyagentspec/src/pyagentspec/transforms/transforms.py +++ b/pyagentspec/src/pyagentspec/transforms/transforms.py @@ -15,5 +15,5 @@ class MessageTransform(Component, abstract=True): """Base class for message transformation components.""" min_agentspec_version: SkipJsonSchema[AgentSpecVersionEnum] = Field( - default=AgentSpecVersionEnum.v25_4_2, init=False, exclude=True + default=AgentSpecVersionEnum.v26_1_1, init=False, exclude=True ) diff --git a/pyagentspec/src/pyagentspec/versioning.py b/pyagentspec/src/pyagentspec/versioning.py index b259053d..a54a40ed 100644 --- a/pyagentspec/src/pyagentspec/versioning.py +++ b/pyagentspec/src/pyagentspec/versioning.py @@ -30,7 +30,8 @@ class AgentSpecVersionEnum(Enum): v25_4_1 = "25.4.1" v25_4_2 = "25.4.2" v26_1_0 = "26.1.0" - current_version = "26.1.0" + v26_1_1 = "26.1.1" + current_version = "26.1.1" def __lt__(self, other: "AgentSpecVersionEnum") -> bool: return _version_lt(self.value, other.value) diff --git a/pyagentspec/tests/agentspec_configs/example_serialized_agent_with_tools_25_4_2.yaml b/pyagentspec/tests/agentspec_configs/example_serialized_agent_with_tools_25_4_2.yaml index c1bad396..4cffb5cb 100644 --- a/pyagentspec/tests/agentspec_configs/example_serialized_agent_with_tools_25_4_2.yaml +++ b/pyagentspec/tests/agentspec_configs/example_serialized_agent_with_tools_25_4_2.yaml @@ -74,5 +74,4 @@ tools: url: https://my.url/tool requires_confirmation: false human_in_the_loop: true -transforms: [] agentspec_version: 25.4.2 diff --git a/pyagentspec/tests/agentspec_configs/example_serialized_agent_with_tools_and_builtin_tools.yaml b/pyagentspec/tests/agentspec_configs/example_serialized_agent_with_tools_and_builtin_tools.yaml index e61f4c1e..c89d7089 100644 --- a/pyagentspec/tests/agentspec_configs/example_serialized_agent_with_tools_and_builtin_tools.yaml +++ b/pyagentspec/tests/agentspec_configs/example_serialized_agent_with_tools_and_builtin_tools.yaml @@ -87,5 +87,4 @@ tools: executor_name: demo_executor tool_version: '1.0' human_in_the_loop: true -transforms: [] agentspec_version: 25.4.2 diff --git a/pyagentspec/tests/agentspec_configs/example_serialized_agent_with_tools_and_toolboxes.yaml b/pyagentspec/tests/agentspec_configs/example_serialized_agent_with_tools_and_toolboxes.yaml index bc555462..2d464b0f 100644 --- a/pyagentspec/tests/agentspec_configs/example_serialized_agent_with_tools_and_toolboxes.yaml +++ b/pyagentspec/tests/agentspec_configs/example_serialized_agent_with_tools_and_toolboxes.yaml @@ -73,5 +73,4 @@ $referenced_components: headers: null sensitive_headers: null human_in_the_loop: true -transforms: [] agentspec_version: 25.4.2 diff --git a/pyagentspec/tests/agentspec_configs/example_serialized_agent_with_tools_no_human_in_the_loop.yaml b/pyagentspec/tests/agentspec_configs/example_serialized_agent_with_tools_no_human_in_the_loop.yaml index 2cba6057..ea3bd508 100644 --- a/pyagentspec/tests/agentspec_configs/example_serialized_agent_with_tools_no_human_in_the_loop.yaml +++ b/pyagentspec/tests/agentspec_configs/example_serialized_agent_with_tools_no_human_in_the_loop.yaml @@ -74,5 +74,4 @@ tools: url: https://my.url/tool requires_confirmation: false human_in_the_loop: false -transforms: [] agentspec_version: 25.4.2 diff --git a/pyagentspec/tests/agentspec_configs/example_serialized_managerworkers.yaml b/pyagentspec/tests/agentspec_configs/example_serialized_managerworkers.yaml index 2cf6f2e8..0f8331c0 100644 --- a/pyagentspec/tests/agentspec_configs/example_serialized_managerworkers.yaml +++ b/pyagentspec/tests/agentspec_configs/example_serialized_managerworkers.yaml @@ -27,7 +27,6 @@ group_manager: customer interactions and orchestrating the resolution process efficiently. tools: [] toolboxes: [] - transforms: [] human_in_the_loop: true workers: - component_type: Agent @@ -44,7 +43,6 @@ workers: refund requests accurately and efficiently based on company policy. tools: [] toolboxes: [] - transforms: [] human_in_the_loop: true - component_type: Agent id: satisfaction_surveyor @@ -60,7 +58,6 @@ workers: customer interactions and orchestrating the resolution process efficiently. tools: [] toolboxes: [] - transforms: [] human_in_the_loop: true $referenced_components: vllm: diff --git a/pyagentspec/tests/agentspec_configs/example_serialized_specialized_agent.yaml b/pyagentspec/tests/agentspec_configs/example_serialized_specialized_agent.yaml index 939ea475..d631aeb8 100644 --- a/pyagentspec/tests/agentspec_configs/example_serialized_specialized_agent.yaml +++ b/pyagentspec/tests/agentspec_configs/example_serialized_specialized_agent.yaml @@ -59,7 +59,6 @@ agent: - title: forecast type: string requires_confirmation: false - transforms: [] human_in_the_loop: true agent_specialization_parameters: component_type: AgentSpecializationParameters diff --git a/pyagentspec/tests/agentspec_configs/example_serialized_swarm.yaml b/pyagentspec/tests/agentspec_configs/example_serialized_swarm.yaml index da5820cd..21279782 100644 --- a/pyagentspec/tests/agentspec_configs/example_serialized_swarm.yaml +++ b/pyagentspec/tests/agentspec_configs/example_serialized_swarm.yaml @@ -36,7 +36,6 @@ $referenced_components: system_prompt: You are a general practitioner tools: [] toolboxes: [] - transforms: [] human_in_the_loop: true vllm: component_type: VllmConfig @@ -62,7 +61,6 @@ $referenced_components: system_prompt: You are a pharmacist tools: [] toolboxes: [] - transforms: [] human_in_the_loop: true dermatologist: component_type: Agent @@ -77,6 +75,5 @@ $referenced_components: system_prompt: You are a dermatologist tools: [] toolboxes: [] - transforms: [] human_in_the_loop: true agentspec_version: 25.4.2 diff --git a/pyagentspec/tests/agentspec_configs/example_tools_with_confirmation_25_4_2.yaml b/pyagentspec/tests/agentspec_configs/example_tools_with_confirmation_25_4_2.yaml index ed011996..5de21a5b 100644 --- a/pyagentspec/tests/agentspec_configs/example_tools_with_confirmation_25_4_2.yaml +++ b/pyagentspec/tests/agentspec_configs/example_tools_with_confirmation_25_4_2.yaml @@ -88,6 +88,5 @@ tools: executor_name: orch_executor tool_version: 1.0.0 toolboxes: [] -transforms: [] human_in_the_loop: true agentspec_version: 25.4.2 diff --git a/pyagentspec/tests/serialization/datastores/test_datastores.py b/pyagentspec/tests/serialization/datastores/test_datastores.py index 44a6345f..4cc69df6 100644 --- a/pyagentspec/tests/serialization/datastores/test_datastores.py +++ b/pyagentspec/tests/serialization/datastores/test_datastores.py @@ -37,14 +37,14 @@ def test_datastore_serialization_with_unsupported_version_raises( with pytest.raises( ValueError, match="Invalid agentspec_version:.*but the minimum allowed version is.*" ): - _ = AgentSpecSerializer().to_json(datastore, agentspec_version=AgentSpecVersionEnum.v25_4_1) + _ = AgentSpecSerializer().to_json(datastore, agentspec_version=AgentSpecVersionEnum.v26_1_0) @pytest.mark.parametrize("datastore, sensitive_fields", DATASTORES_AND_THEIR_SENSITIVE_FIELDS) def test_deserialization_with_unsupported_version_raises(datastore, sensitive_fields) -> None: serialized_ds = AgentSpecSerializer().to_yaml(datastore) - assert "agentspec_version: 25.4.2" in serialized_ds - serialized_ds = serialized_ds.replace("agentspec_version: 25.4.2", "agentspec_version: 25.4.1") + assert "agentspec_version: 26.1.1" in serialized_ds + serialized_ds = serialized_ds.replace("agentspec_version: 26.1.1", "agentspec_version: 26.1.0") with pytest.raises(ValueError, match="Invalid agentspec_version"): _ = AgentSpecDeserializer().from_yaml( diff --git a/pyagentspec/tests/serialization/test_agent.py b/pyagentspec/tests/serialization/test_agent.py index de849073..6845b95f 100644 --- a/pyagentspec/tests/serialization/test_agent.py +++ b/pyagentspec/tests/serialization/test_agent.py @@ -293,11 +293,11 @@ def test_agent_with_non_empty_transforms_can_be_serialized_and_deserialized( create_conversation_summarization_transform(datastore), ] # The default min_agentspec_version for VllmConfig is v25_4_1. If we leave it unchanged, - # the agent with non-empty transforms would serialize to v25_4_2 (due to the transforms requiring that version). - # During deserialization, all fields including vllmconfig would be deserialized to v25_4_2, + # the agent with non-empty transforms would serialize to v26_1_1 (due to the transforms requiring that version). + # During deserialization, all fields including vllmconfig would be deserialized to v26_1_1, # but vllmconfig's min_agentspec_version would still be v25_4_1, causing the test deserialized == original to fail. - # Therefore, we explicitly set the version to v25_4_2 - vllmconfig.min_agentspec_version = AgentSpecVersionEnum.v25_4_2 + # Therefore, we explicitly set the version to v26_1_1 + vllmconfig.min_agentspec_version = AgentSpecVersionEnum.v26_1_1 agent = Agent( id="agent1", @@ -339,7 +339,7 @@ def test_serializing_agent_with_non_empty_transforms_and_unsupported_version_rai serializer = AgentSpecSerializer() with pytest.raises(ValueError, match="Invalid agentspec_version"): _ = serializer.to_yaml( - agent_with_non_empty_transforms, agentspec_version=AgentSpecVersionEnum.v25_4_1 + agent_with_non_empty_transforms, agentspec_version=AgentSpecVersionEnum.v26_1_0 ) @@ -348,9 +348,9 @@ def test_deserializing_agent_with_non_empty_transforms_and_unsupported_version_r ): serializer = AgentSpecSerializer() serialized_agent = serializer.to_yaml(agent_with_non_empty_transforms) - assert "agentspec_version: 25.4.2" in serialized_agent + assert "agentspec_version: 26.1.1" in serialized_agent serialized_agent = serialized_agent.replace( - "agentspec_version: 25.4.2", "agentspec_version: 25.4.1" + "agentspec_version: 26.1.1", "agentspec_version: 26.1.0" ) with pytest.raises(ValueError, match="Invalid agentspec_version"): diff --git a/pyagentspec/tests/serialization/transforms/conftest.py b/pyagentspec/tests/serialization/transforms/conftest.py index a7daff4d..0e65573b 100644 --- a/pyagentspec/tests/serialization/transforms/conftest.py +++ b/pyagentspec/tests/serialization/transforms/conftest.py @@ -19,8 +19,8 @@ def create_test_llm_config(): name="test_openai_config", model_id="gpt-3.5-turbo", ) - # Set to v25_4_2 to match the serialized agentspec_version, ensuring deserialized == original - config.min_agentspec_version = AgentSpecVersionEnum.v25_4_2 + # Set to v26_1_1 to match the serialized agentspec_version, ensuring deserialized == original + config.min_agentspec_version = AgentSpecVersionEnum.v26_1_1 return config diff --git a/pyagentspec/tests/serialization/transforms/test_summarization_transforms.py b/pyagentspec/tests/serialization/transforms/test_summarization_transforms.py index fc9243d0..c69d4ded 100644 --- a/pyagentspec/tests/serialization/transforms/test_summarization_transforms.py +++ b/pyagentspec/tests/serialization/transforms/test_summarization_transforms.py @@ -45,7 +45,7 @@ def test_transform_serialization_with_unsupported_version_raises( with pytest.raises( ValueError, match="Invalid agentspec_version:.*but the minimum allowed version is.*" ): - _ = AgentSpecSerializer().to_json(transform, agentspec_version=AgentSpecVersionEnum.v25_4_1) + _ = AgentSpecSerializer().to_json(transform, agentspec_version=AgentSpecVersionEnum.v26_1_0) @parametrize_transform_and_datastore @@ -54,9 +54,9 @@ def test_transform_deserialization_with_unsupported_version_raises( ): transform = transform_factory(datastore) serialized_transform = AgentSpecSerializer().to_yaml(transform) - assert "agentspec_version: 25.4.2" in serialized_transform + assert "agentspec_version: 26.1.1" in serialized_transform serialized_transform = serialized_transform.replace( - "agentspec_version: 25.4.2", "agentspec_version: 25.4.1" + "agentspec_version: 26.1.1", "agentspec_version: 26.1.0" ) with pytest.raises(ValueError, match="Invalid agentspec_version"): From 8242b8397aa32ec08ff95ed8a0796b814b161c35 Mon Sep 17 00:00:00 2001 From: Youssef Boughizane Date: Tue, 23 Dec 2025 11:46:28 +0100 Subject: [PATCH 23/26] doc: stylistic (typos+improvement) changes to doc --- docs/pyagentspec/source/agentspec/language_spec_nightly.rst | 6 +++--- docs/pyagentspec/source/changelog.rst | 2 +- pyagentspec/src/pyagentspec/datastores/datastore.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/pyagentspec/source/agentspec/language_spec_nightly.rst b/docs/pyagentspec/source/agentspec/language_spec_nightly.rst index cd3a860d..e31df84d 100644 --- a/docs/pyagentspec/source/agentspec/language_spec_nightly.rst +++ b/docs/pyagentspec/source/agentspec/language_spec_nightly.rst @@ -1951,7 +1951,7 @@ The ManagerWorkers has two main parameters: Datastores ~~~~~~~~~~ -The core abstract is Datastore which enables agentic systems to store, and access data. +Datastores are the AgentSpec abstraction that enables agentic systems to store, and access data. .. code-block:: python @@ -2057,7 +2057,7 @@ The ``PostgresDatabaseDatastore`` is a relational datastore intended to store en Transforms ^^^^^^^^^^ -Transforms extend the base ``MessageTransform`` component can be used in agents. they apply a transformation to the messages before it's passed to the LLM. +Transforms extend the base ``MessageTransform`` component can be used in agents. They apply a transformation to the messages before they are passed to the LLM. .. code-block:: python @@ -2067,7 +2067,7 @@ Transforms extend the base ``MessageTransform`` component can be used in agents. MessageSummarizationTransform ''''''''''''''''''''''''''''' -Summarizes messages exceeding a given number of characters using an LLM and caches the summaries in a ``Datastore``. This is useful for conversations with long messagwes where the context can become too large for the agent LLM to handle. +Summarizes messages exceeding a given number of characters using an LLM and caches the summaries in a ``Datastore``. This is useful for conversations with long messages where the context can become too large for the agent LLM to handle. .. code-block:: python diff --git a/docs/pyagentspec/source/changelog.rst b/docs/pyagentspec/source/changelog.rst index 407dee3c..ced7e2bb 100644 --- a/docs/pyagentspec/source/changelog.rst +++ b/docs/pyagentspec/source/changelog.rst @@ -94,7 +94,7 @@ New features Added support for datastores in Agent Spec through :ref:`OracleDatabaseDatastore ` and :ref:`PostgresDatabaseDatastore `. Datastores enable persistent storage and caching capabilities for agent workflows. :ref:`InMemoryCollectionDatastore ` provides a drop-in replacement for development and prototyping. -* **Transforms** +* **Context Summarization Transforms** Introduced transforms in Agent Spec that allow applying transformations on conversations before being passed to the underlying LLM. We provide :ref:`MessageSummarizationTransform ` and :ref:`ConversationSummarizationTransform ` for handling long contexts through summarization. diff --git a/pyagentspec/src/pyagentspec/datastores/datastore.py b/pyagentspec/src/pyagentspec/datastores/datastore.py index 7990ab82..b443c23a 100644 --- a/pyagentspec/src/pyagentspec/datastores/datastore.py +++ b/pyagentspec/src/pyagentspec/datastores/datastore.py @@ -27,7 +27,7 @@ def is_object_property(value: Property) -> Property: class Datastore(Component, abstract=True): - """Base class for Datastores. Datastores store and retrieve data.""" + """Base class for Datastores. Datastores configure how to store and retrieve data.""" min_agentspec_version: SkipJsonSchema[AgentSpecVersionEnum] = Field( default=AgentSpecVersionEnum.v26_1_1, init=False, exclude=True From d480c8fb4a9ca150a50c7e796768c8dc39fe6557 Mon Sep 17 00:00:00 2001 From: Youssef Boughizane Date: Tue, 23 Dec 2025 13:40:44 +0100 Subject: [PATCH 24/26] doc: explain parameters for summarization transforms in specs --- .../agentspec/language_spec_nightly.rst | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/docs/pyagentspec/source/agentspec/language_spec_nightly.rst b/docs/pyagentspec/source/agentspec/language_spec_nightly.rst index e31df84d..d802a58a 100644 --- a/docs/pyagentspec/source/agentspec/language_spec_nightly.rst +++ b/docs/pyagentspec/source/agentspec/language_spec_nightly.rst @@ -2072,17 +2072,17 @@ Summarizes messages exceeding a given number of characters using an LLM and cach .. code-block:: python class MessageSummarizationTransform(MessageTransform): - llm: LlmConfig - max_message_size: int = 20_000 + llm: LlmConfig # LLM to use for the summarization. + max_message_size: int = 20_000 # The maximum size in number of characters for the content of a message. summarization_instructions: str = ( "Please make a summary of this message. Include relevant information and keep it short. " "Your response will replace the message, so just output the summary directly, no introduction needed." - ) - summarized_message_template: str = "Summarized message: {{summary}}" - datastore: Optional[Datastore] = None - max_cache_size: Optional[int] = 10_000 - max_cache_lifetime: Optional[int] = 4 * 3600 - cache_collection_name: str = "summarized_messages_cache" + ) # Instruction for the LLM on how to summarize the messages. + summarized_message_template: str = "Summarized message: {{summary}}" # Jinja2 template on how to present the summary (with variable `summary`) to the agent using the transform. + datastore: Optional[Datastore] = None # Datastore on which to store the cache. If None, an in-memory Datastore will be created automatically. The datastore needs to have a collection called `cache_collection_name`. + max_cache_size: Optional[int] = 10_000 # The number of cache entries (messages) kept in the cache. If None, there is no limit on cache size and no eviction occurs. + max_cache_lifetime: Optional[int] = 4 * 3600 # max lifetime of a message in the cache in seconds. If None, cached data persists indefinitely. + cache_collection_name: str = "summarized_messages_cache" # Name of the collection/table in the cache for storing summarized messages. ConversationSummarizationTransform '''''''''''''''''''''''''''''''''' @@ -2092,18 +2092,18 @@ Summarizes conversations exceeding a given number of messages using an LLM and c .. code-block:: python class ConversationSummarizationTransform(MessageTransform): - llm: LlmConfig - max_num_messages: int = 50 - min_num_messages: int = 10 + llm: LlmConfig # LLM to use for the summarization. + max_num_messages: int = 50 # Number of message after which we trigger summarization. + min_num_messages: int = 10 # Number of recent messages to keep from summarizing. summarization_instructions: str = ( "Please make a summary of this conversation. Include relevant information and keep it short. " "Your response will replace the messages, so just output the summary directly, no introduction needed." - ) - summarized_conversation_template: str = "Summarized conversation: {{summary}}" - datastore: Optional[Datastore] = None - max_cache_size: Optional[int] = 10_000 - max_cache_lifetime: Optional[int] = 4 * 3600 - cache_collection_name: str = "summarized_conversations_cache" + ) # Instruction for the LLM on how to summarize the conversation. + summarized_conversation_template: str = "Summarized conversation: {{summary}}" # Jinja2 template on how to present the summary (with variable `summary`) to the agent using the transform. + datastore: Optional[Datastore] = None # Datastore on which to store the cache. If None, an in-memory Datastore will be created automatically. + max_cache_size: Optional[int] = 10_000 # The maximum number of entries kept in the cache + max_cache_lifetime: Optional[int] = 4 * 3600 # max lifetime of an element in the cache in seconds + cache_collection_name: str = "summarized_conversations_cache" # the collection in the cache datastore where summarized conversations will be stored Versioning ---------- From 31f09adf7092894d1357c77c737df263f998ae3f Mon Sep 17 00:00:00 2001 From: Youssef Boughizane Date: Tue, 23 Dec 2025 14:11:52 +0100 Subject: [PATCH 25/26] doc: add agentspec_json...26.1.1 --- .../json_spec/agentspec_json_spec_26_1_0.json | 1236 +-- .../json_spec/agentspec_json_spec_26_1_1.json | 7530 +++++++++++++++++ 2 files changed, 7604 insertions(+), 1162 deletions(-) create mode 100644 docs/pyagentspec/source/agentspec/json_spec/agentspec_json_spec_26_1_1.json diff --git a/docs/pyagentspec/source/agentspec/json_spec/agentspec_json_spec_26_1_0.json b/docs/pyagentspec/source/agentspec/json_spec/agentspec_json_spec_26_1_0.json index cc9a719c..978a28d7 100644 --- a/docs/pyagentspec/source/agentspec/json_spec/agentspec_json_spec_26_1_0.json +++ b/docs/pyagentspec/source/agentspec/json_spec/agentspec_json_spec_26_1_0.json @@ -152,16 +152,6 @@ } ] }, - "ConversationSummarizationTransform": { - "anyOf": [ - { - "$ref": "#/$defs/BaseConversationSummarizationTransform" - }, - { - "$ref": "#/$defs/ComponentReference" - } - ] - }, "DataFlowEdge": { "anyOf": [ { @@ -172,16 +162,6 @@ } ] }, - "Datastore": { - "anyOf": [ - { - "$ref": "#/$defs/BaseDatastore" - }, - { - "$ref": "#/$defs/ComponentReference" - } - ] - }, "EndNode": { "anyOf": [ { @@ -222,16 +202,6 @@ "title": "HandoffMode", "type": "string" }, - "InMemoryCollectionDatastore": { - "anyOf": [ - { - "$ref": "#/$defs/BaseInMemoryCollectionDatastore" - }, - { - "$ref": "#/$defs/ComponentReference" - } - ] - }, "InputMessageNode": { "anyOf": [ { @@ -336,16 +306,6 @@ } ] }, - "MTlsOracleDatabaseConnectionConfig": { - "anyOf": [ - { - "$ref": "#/$defs/BaseMTlsOracleDatabaseConnectionConfig" - }, - { - "$ref": "#/$defs/ComponentReference" - } - ] - }, "ManagerWorkers": { "anyOf": [ { @@ -366,26 +326,6 @@ } ] }, - "MessageSummarizationTransform": { - "anyOf": [ - { - "$ref": "#/$defs/BaseMessageSummarizationTransform" - }, - { - "$ref": "#/$defs/ComponentReference" - } - ] - }, - "MessageTransform": { - "anyOf": [ - { - "$ref": "#/$defs/BaseMessageTransform" - }, - { - "$ref": "#/$defs/ComponentReference" - } - ] - }, "ModelProvider": { "description": "Provider of the model. It is used to ensure the requests to this model respect\nthe format expected by the provider.", "enum": [ @@ -526,26 +466,6 @@ } ] }, - "OracleDatabaseConnectionConfig": { - "anyOf": [ - { - "$ref": "#/$defs/BaseOracleDatabaseConnectionConfig" - }, - { - "$ref": "#/$defs/ComponentReference" - } - ] - }, - "OracleDatabaseDatastore": { - "anyOf": [ - { - "$ref": "#/$defs/BaseOracleDatabaseDatastore" - }, - { - "$ref": "#/$defs/ComponentReference" - } - ] - }, "OutputMessageNode": { "anyOf": [ { @@ -576,26 +496,6 @@ } ] }, - "PostgresDatabaseConnectionConfig": { - "anyOf": [ - { - "$ref": "#/$defs/BasePostgresDatabaseConnectionConfig" - }, - { - "$ref": "#/$defs/ComponentReference" - } - ] - }, - "PostgresDatabaseDatastore": { - "anyOf": [ - { - "$ref": "#/$defs/BasePostgresDatabaseDatastore" - }, - { - "$ref": "#/$defs/ComponentReference" - } - ] - }, "Property": { "description": "This object must be a valid JSON Schema", "type": "object" @@ -753,26 +653,6 @@ } ] }, - "TlsOracleDatabaseConnectionConfig": { - "anyOf": [ - { - "$ref": "#/$defs/BaseTlsOracleDatabaseConnectionConfig" - }, - { - "$ref": "#/$defs/ComponentReference" - } - ] - }, - "TlsPostgresDatabaseConnectionConfig": { - "anyOf": [ - { - "$ref": "#/$defs/BaseTlsPostgresDatabaseConnectionConfig" - }, - { - "$ref": "#/$defs/ComponentReference" - } - ] - }, "Tool": { "anyOf": [ { @@ -1113,14 +993,6 @@ "title": "Human In The Loop", "type": "boolean" }, - "transforms": { - "default": [], - "items": { - "$ref": "#/$defs/MessageTransform" - }, - "title": "Transforms", - "type": "array" - }, "$referenced_components": { "$ref": "#/$defs/ReferencedComponents" }, @@ -2009,119 +1881,6 @@ "type": "object", "x-abstract-component": false }, - "BaseConversationSummarizationTransform": { - "additionalProperties": false, - "properties": { - "id": { - "title": "Id", - "type": "string" - }, - "name": { - "title": "Name", - "type": "string" - }, - "description": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Description" - }, - "metadata": { - "anyOf": [ - { - "additionalProperties": true, - "type": "object" - }, - { - "type": "null" - } - ], - "title": "Metadata" - }, - "llm": { - "$ref": "#/$defs/LlmConfig" - }, - "max_num_messages": { - "default": 50, - "title": "Max Num Messages", - "type": "integer" - }, - "min_num_messages": { - "default": 10, - "title": "Min Num Messages", - "type": "integer" - }, - "summarization_instructions": { - "default": "Please make a summary of this conversation. Include relevant information and keep it short. Your response will replace the messages, so just output the summary directly, no introduction needed.", - "title": "Summarization Instructions", - "type": "string" - }, - "summarized_conversation_template": { - "default": "Summarized conversation: {{summary}}", - "title": "Summarized Conversation Template", - "type": "string" - }, - "datastore": { - "anyOf": [ - { - "type": "null" - }, - { - "$ref": "#/$defs/Datastore" - } - ], - "default": null - }, - "max_cache_size": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "default": 10000, - "title": "Max Cache Size" - }, - "max_cache_lifetime": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "default": 14400, - "title": "Max Cache Lifetime" - }, - "cache_collection_name": { - "default": "summarized_conversations_cache", - "title": "Cache Collection Name", - "type": "string" - }, - "$referenced_components": { - "$ref": "#/$defs/ReferencedComponents" - }, - "component_type": { - "const": "ConversationSummarizationTransform" - } - }, - "required": [ - "llm", - "name" - ], - "title": "ConversationSummarizationTransform", - "type": "object", - "x-abstract-component": false - }, "BaseDataFlowEdge": { "additionalProperties": false, "description": "A data flow edge specifies how the output of a node propagates as input of another node.\n\nAn outputs can be propagated as input of several nodes.", @@ -2190,20 +1949,6 @@ "type": "object", "x-abstract-component": false }, - "BaseDatastore": { - "anyOf": [ - { - "$ref": "#/$defs/InMemoryCollectionDatastore" - }, - { - "$ref": "#/$defs/OracleDatabaseDatastore" - }, - { - "$ref": "#/$defs/PostgresDatabaseDatastore" - } - ], - "x-abstract-component": true - }, "BaseEndNode": { "additionalProperties": false, "description": "End nodes denote the end of the execution of a flow.\n\nThere might be several end nodes in a flow, in which case the executor of the flow\nshould be able to track which one was reached and pass that back to the caller.\n\n- **Inputs**\n The list of inputs of the step. If both input and output properties are specified they\n must be an exact match\n\n If None is given, ``pyagentspec`` copies the outputs provided, if any. Otherwise, no input is exposed.\n- **Outputs**\n The list of outputs that should be exposed by the flow. If both input and output properties\n are specified they must be an exact match\n\n If None is given, ``pyagentspec`` copies the inputs provided, if any. Otherwise, no output is exposed.\n- **Branches**\n None.\n\nExamples\n--------\n>>> from pyagentspec.agent import Agent\n>>> from pyagentspec.flows.edges import ControlFlowEdge, DataFlowEdge\n>>> from pyagentspec.flows.flow import Flow\n>>> from pyagentspec.flows.nodes import AgentNode, BranchingNode, StartNode, EndNode\n>>> from pyagentspec.property import Property\n>>> languages_to_branch_name = {\n... \"english\": \"ENGLISH\",\n... \"spanish\": \"SPANISH\",\n... \"italian\": \"ITALIAN\",\n... }\n>>> language_property = Property(\n... json_schema={\"title\": \"language\", \"type\": \"string\"}\n... )\n>>> agent = Agent(\n... name=\"Language detector agent\",\n... llm_config=llm_config,\n... system_prompt=(\n... \"Your task is to understand the language spoken by the user.\"\n... \"Please output only the language in lowercase and submit.\"\n... ),\n... outputs=[language_property],\n... )\n>>> start_node = StartNode(name=\"start\")\n>>> english_end_node = EndNode(\n... name=\"english end\", branch_name=languages_to_branch_name[\"english\"]\n... )\n>>> spanish_end_node = EndNode(\n... name=\"spanish end\", branch_name=languages_to_branch_name[\"spanish\"]\n... )\n>>> italian_end_node = EndNode(\n... name=\"italian end\", branch_name=languages_to_branch_name[\"italian\"]\n... )\n>>> unknown_end_node = EndNode(name=\"unknown language end\", branch_name=\"unknown\")\n>>> branching_node = BranchingNode(\n... name=\"language check\",\n... mapping=languages_to_branch_name,\n... inputs=[language_property]\n... )\n>>> agent_node = AgentNode(\n... name=\"User input agent node\",\n... agent=agent,\n... )\n>>> assistant = Flow(\n... name=\"Check access flow\",\n... start_node=start_node,\n... nodes=[\n... start_node,\n... agent_node,\n... branching_node,\n... english_end_node,\n... spanish_end_node,\n... italian_end_node,\n... unknown_end_node,\n... ],\n... control_flow_connections=[\n... ControlFlowEdge(\n... name=\"start_to_agent\", from_node=start_node, to_node=agent_node\n... ),\n... ControlFlowEdge(\n... name=\"agent_to_branching\", from_node=agent_node, to_node=branching_node\n... ),\n... ControlFlowEdge(\n... name=\"branching_to_english_end\",\n... from_node=branching_node,\n... from_branch=languages_to_branch_name[\"english\"],\n... to_node=english_end_node,\n... ),\n... ControlFlowEdge(\n... name=\"branching_to_spanish_end\",\n... from_node=branching_node,\n... from_branch=languages_to_branch_name[\"spanish\"],\n... to_node=spanish_end_node,\n... ),\n... ControlFlowEdge(\n... name=\"branching_to_italian_end\",\n... from_node=branching_node,\n... from_branch=languages_to_branch_name[\"italian\"],\n... to_node=italian_end_node,\n... ),\n... ControlFlowEdge(\n... name=\"branching_to_unknown_end\",\n... from_node=branching_node,\n... from_branch=BranchingNode.DEFAULT_BRANCH,\n... to_node=unknown_end_node,\n... ),\n... ],\n... data_flow_connections=[\n... DataFlowEdge(\n... name=\"language_edge\",\n... source_node=agent_node,\n... source_output=\"language\",\n... destination_node=branching_node,\n... destination_input=\"language\",\n... ),\n... ],\n... )", @@ -2502,64 +2247,6 @@ "type": "object", "x-abstract-component": false }, - "BaseInMemoryCollectionDatastore": { - "additionalProperties": false, - "description": "In-memory datastore for testing and development purposes.", - "properties": { - "id": { - "title": "Id", - "type": "string" - }, - "name": { - "title": "Name", - "type": "string" - }, - "description": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Description" - }, - "metadata": { - "anyOf": [ - { - "additionalProperties": true, - "type": "object" - }, - { - "type": "null" - } - ], - "title": "Metadata" - }, - "datastore_schema": { - "additionalProperties": { - "$ref": "#/$defs/Property" - }, - "title": "Datastore Schema", - "type": "object" - }, - "$referenced_components": { - "$ref": "#/$defs/ReferencedComponents" - }, - "component_type": { - "const": "InMemoryCollectionDatastore" - } - }, - "required": [ - "datastore_schema", - "name" - ], - "title": "InMemoryCollectionDatastore", - "type": "object", - "x-abstract-component": false - }, "BaseInputMessageNode": { "additionalProperties": false, "description": "This node interrupts the execution of the flow in order to wait for a user input, and restarts after receiving it.\nAn agent message, if given, is appended to the conversation before waiting for input.\nUser input is appended to the conversation as a user message, and it is returned as a string property from the node.\n\n- **Inputs**\n One per variable in the message\n- **Outputs**\n One string property that represents the content of the input user message.\n\n If None is given, ``pyagentspec`` infers a string property named ``user_input``.\n- **Branches**\n One, the default next.\n\nExamples\n--------\n>>> from pyagentspec.flows.edges import ControlFlowEdge, DataFlowEdge\n>>> from pyagentspec.flows.flow import Flow\n>>> from pyagentspec.flows.nodes import StartNode, EndNode, InputMessageNode, OutputMessageNode, LlmNode\n>>> from pyagentspec.property import StringProperty\n>>> start_node = StartNode(name=\"start\")\n>>> prompt_node = OutputMessageNode(name=\"ask_input\", message=\"What is the paragraph you want to rephrase?\")\n>>> input_node = InputMessageNode(name=\"user_input\", outputs=[StringProperty(title=\"user_input\")])\n>>> llm_node = LlmNode(\n... name=\"rephrase\",\n... llm_config=llm_config,\n... prompt_template=\"Rephrase {{user_input}}\",\n... outputs=[StringProperty(title=\"rephrased_user_input\")],\n... )\n>>> output_node = OutputMessageNode(name=\"ask_input\", message=\"{{rephrased_user_input}}\")\n>>> end_node = EndNode(name=\"end\")\n>>> flow = Flow(\n... name=\"rephrase_paragraph_flow\",\n... start_node=start_node,\n... nodes=[start_node, prompt_node, input_node, llm_node, output_node, end_node],\n... control_flow_connections=[\n... ControlFlowEdge(name=\"ce1\", from_node=start_node, to_node=prompt_node),\n... ControlFlowEdge(name=\"ce2\", from_node=prompt_node, to_node=input_node),\n... ControlFlowEdge(name=\"ce3\", from_node=input_node, to_node=llm_node),\n... ControlFlowEdge(name=\"ce4\", from_node=llm_node, to_node=output_node),\n... ControlFlowEdge(name=\"ce5\", from_node=output_node, to_node=end_node),\n... ],\n... data_flow_connections=[\n... DataFlowEdge(\n... name=\"de1\",\n... source_node=input_node,\n... source_output=\"user_input\",\n... destination_node=llm_node,\n... destination_input=\"user_input\",\n... ),\n... DataFlowEdge(\n... name=\"de2\",\n... source_node=llm_node,\n... source_output=\"rephrased_user_input\",\n... destination_node=output_node,\n... destination_input=\"rephrased_user_input\",\n... ),\n... ]\n... )", @@ -3025,9 +2712,9 @@ "type": "object", "x-abstract-component": false }, - "BaseMTlsOracleDatabaseConnectionConfig": { + "BaseManagerWorkers": { "additionalProperties": false, - "description": "Mutual-TLS Connection Configuration to Oracle Database.", + "description": "Defines a ``ManagerWorkers`` conversational component.\n\nA ``ManagerWorkers`` is a multi-agent conversational component in which a group manager\nassigns tasks to the workers. The group manager and workers can be instantiated from\nany ``AgenticComponent`` type.\n\nExamples\n--------\n>>> from pyagentspec.agent import Agent\n>>> from pyagentspec.managerworkers import ManagerWorkers\n>>> manager_agent = Agent(\n... name=\"manager_agent\",\n... description=\"Agent that manages a group of math agents\",\n... llm_config=llm_config,\n... system_prompt=\"You are the manager of a group of math agents\"\n... )\n>>> multiplication_agent = Agent(\n... name=\"multiplication_agent\",\n... description=\"Agent that can do multiplication\",\n... llm_config=llm_config,\n... system_prompt=\"You can do multiplication.\"\n... )\n>>> division_agent = Agent(\n... name=\"division_agent\",\n... description=\"Agent that can do division\",\n... llm_config=llm_config,\n... system_prompt=\"You can do division.\"\n... )\n>>> group = ManagerWorkers(\n... name=\"managerworkers\",\n... group_manager=manager_agent,\n... workers=[multiplication_agent, division_agent],\n... )", "properties": { "id": { "title": "Id", @@ -3061,103 +2748,7 @@ ], "title": "Metadata" }, - "user": { - "title": "User", - "type": "string" - }, - "password": { - "title": "Password", - "type": "string" - }, - "dsn": { - "title": "Dsn", - "type": "string" - }, - "config_dir": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Config Dir" - }, - "protocol": { - "default": "tcps", - "enum": [ - "tcp", - "tcps" - ], - "title": "Protocol", - "type": "string" - }, - "wallet_location": { - "title": "Wallet Location", - "type": "string" - }, - "wallet_password": { - "title": "Wallet Password", - "type": "string" - }, - "$referenced_components": { - "$ref": "#/$defs/ReferencedComponents" - }, - "component_type": { - "const": "MTlsOracleDatabaseConnectionConfig" - } - }, - "required": [ - "dsn", - "name", - "password", - "user", - "wallet_location", - "wallet_password" - ], - "title": "MTlsOracleDatabaseConnectionConfig", - "type": "object", - "x-abstract-component": false - }, - "BaseManagerWorkers": { - "additionalProperties": false, - "description": "Defines a ``ManagerWorkers`` conversational component.\n\nA ``ManagerWorkers`` is a multi-agent conversational component in which a group manager\nassigns tasks to the workers. The group manager and workers can be instantiated from\nany ``AgenticComponent`` type.\n\nExamples\n--------\n>>> from pyagentspec.agent import Agent\n>>> from pyagentspec.managerworkers import ManagerWorkers\n>>> manager_agent = Agent(\n... name=\"manager_agent\",\n... description=\"Agent that manages a group of math agents\",\n... llm_config=llm_config,\n... system_prompt=\"You are the manager of a group of math agents\"\n... )\n>>> multiplication_agent = Agent(\n... name=\"multiplication_agent\",\n... description=\"Agent that can do multiplication\",\n... llm_config=llm_config,\n... system_prompt=\"You can do multiplication.\"\n... )\n>>> division_agent = Agent(\n... name=\"division_agent\",\n... description=\"Agent that can do division\",\n... llm_config=llm_config,\n... system_prompt=\"You can do division.\"\n... )\n>>> group = ManagerWorkers(\n... name=\"managerworkers\",\n... group_manager=manager_agent,\n... workers=[multiplication_agent, division_agent],\n... )", - "properties": { - "id": { - "title": "Id", - "type": "string" - }, - "name": { - "title": "Name", - "type": "string" - }, - "description": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Description" - }, - "metadata": { - "anyOf": [ - { - "additionalProperties": true, - "type": "object" - }, - { - "type": "null" - } - ], - "title": "Metadata" - }, - "inputs": { + "inputs": { "anyOf": [ { "items": { @@ -3319,125 +2910,6 @@ "type": "object", "x-abstract-component": false }, - "BaseMessageSummarizationTransform": { - "additionalProperties": false, - "properties": { - "id": { - "title": "Id", - "type": "string" - }, - "name": { - "title": "Name", - "type": "string" - }, - "description": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Description" - }, - "metadata": { - "anyOf": [ - { - "additionalProperties": true, - "type": "object" - }, - { - "type": "null" - } - ], - "title": "Metadata" - }, - "llm": { - "$ref": "#/$defs/LlmConfig" - }, - "max_message_size": { - "default": 20000, - "title": "Max Message Size", - "type": "integer" - }, - "summarization_instructions": { - "default": "Please make a summary of this message. Include relevant information and keep it short. Your response will replace the message, so just output the summary directly, no introduction needed.", - "title": "Summarization Instructions", - "type": "string" - }, - "summarized_message_template": { - "default": "Summarized message: {{summary}}", - "title": "Summarized Message Template", - "type": "string" - }, - "datastore": { - "anyOf": [ - { - "type": "null" - }, - { - "$ref": "#/$defs/Datastore" - } - ], - "default": null - }, - "max_cache_size": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "default": 10000, - "title": "Max Cache Size" - }, - "max_cache_lifetime": { - "anyOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ], - "default": 14400, - "title": "Max Cache Lifetime" - }, - "cache_collection_name": { - "default": "summarized_messages_cache", - "title": "Cache Collection Name", - "type": "string" - }, - "$referenced_components": { - "$ref": "#/$defs/ReferencedComponents" - }, - "component_type": { - "const": "MessageSummarizationTransform" - } - }, - "required": [ - "llm", - "name" - ], - "title": "MessageSummarizationTransform", - "type": "object", - "x-abstract-component": false - }, - "BaseMessageTransform": { - "anyOf": [ - { - "$ref": "#/$defs/ConversationSummarizationTransform" - }, - { - "$ref": "#/$defs/MessageSummarizationTransform" - } - ], - "x-abstract-component": true - }, "BaseNode": { "anyOf": [ { @@ -4224,79 +3696,6 @@ "type": "object", "x-abstract-component": false }, - "BaseOracleDatabaseConnectionConfig": { - "anyOf": [ - { - "$ref": "#/$defs/MTlsOracleDatabaseConnectionConfig" - }, - { - "$ref": "#/$defs/TlsOracleDatabaseConnectionConfig" - } - ], - "x-abstract-component": true - }, - "BaseOracleDatabaseDatastore": { - "additionalProperties": false, - "description": "Datastore that uses Oracle Database as the storage mechanism.", - "properties": { - "id": { - "title": "Id", - "type": "string" - }, - "name": { - "title": "Name", - "type": "string" - }, - "description": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Description" - }, - "metadata": { - "anyOf": [ - { - "additionalProperties": true, - "type": "object" - }, - { - "type": "null" - } - ], - "title": "Metadata" - }, - "datastore_schema": { - "additionalProperties": { - "$ref": "#/$defs/Property" - }, - "title": "Datastore Schema", - "type": "object" - }, - "connection_config": { - "$ref": "#/$defs/OracleDatabaseConnectionConfig" - }, - "$referenced_components": { - "$ref": "#/$defs/ReferencedComponents" - }, - "component_type": { - "const": "OracleDatabaseDatastore" - } - }, - "required": [ - "connection_config", - "datastore_schema", - "name" - ], - "title": "OracleDatabaseDatastore", - "type": "object", - "x-abstract-component": false - }, "BaseOutputMessageNode": { "additionalProperties": false, "description": "This node appends an agent message to the ongoing flow conversation.\n\n- **Inputs**\n One per variable in the message.\n- **Outputs**\n No outputs.\n- **Branches**\n One, the default next.\n\nExamples\n--------\n>>> from pyagentspec.flows.edges import ControlFlowEdge, DataFlowEdge\n>>> from pyagentspec.flows.flow import Flow\n>>> from pyagentspec.flows.nodes import StartNode, EndNode, InputMessageNode, OutputMessageNode, LlmNode\n>>> from pyagentspec.property import StringProperty\n>>> start_node = StartNode(name=\"start\")\n>>> prompt_node = OutputMessageNode(name=\"ask_input\", message=\"What is the paragraph you want to rephrase?\")\n>>> input_node = InputMessageNode(name=\"user_input\", outputs=[StringProperty(title=\"user_input\")])\n>>> llm_node = LlmNode(\n... name=\"rephrase\",\n... llm_config=llm_config,\n... prompt_template=\"Rephrase {{user_input}}\",\n... outputs=[StringProperty(title=\"rephrased_user_input\")],\n... )\n>>> output_node = OutputMessageNode(name=\"ask_input\", message=\"{{rephrased_user_input}}\")\n>>> end_node = EndNode(name=\"end\")\n>>> flow = Flow(\n... name=\"rephrase_paragraph_flow\",\n... start_node=start_node,\n... nodes=[start_node, prompt_node, input_node, llm_node, output_node, end_node],\n... control_flow_connections=[\n... ControlFlowEdge(name=\"ce1\", from_node=start_node, to_node=prompt_node),\n... ControlFlowEdge(name=\"ce2\", from_node=prompt_node, to_node=input_node),\n... ControlFlowEdge(name=\"ce3\", from_node=input_node, to_node=llm_node),\n... ControlFlowEdge(name=\"ce4\", from_node=llm_node, to_node=output_node),\n... ControlFlowEdge(name=\"ce5\", from_node=output_node, to_node=end_node),\n... ],\n... data_flow_connections=[\n... DataFlowEdge(\n... name=\"de1\",\n... source_node=input_node,\n... source_output=\"user_input\",\n... destination_node=llm_node,\n... destination_input=\"user_input\",\n... ),\n... DataFlowEdge(\n... name=\"de2\",\n... source_node=llm_node,\n... source_output=\"rephrased_user_input\",\n... destination_node=output_node,\n... destination_input=\"rephrased_user_input\",\n... ),\n... ]\n... )", @@ -4589,76 +3988,6 @@ "type": "object", "x-abstract-component": false }, - "BasePostgresDatabaseConnectionConfig": { - "anyOf": [ - { - "$ref": "#/$defs/TlsPostgresDatabaseConnectionConfig" - } - ], - "x-abstract-component": true - }, - "BasePostgresDatabaseDatastore": { - "additionalProperties": false, - "description": "Datastore that uses PostgreSQL Database as the storage mechanism.", - "properties": { - "id": { - "title": "Id", - "type": "string" - }, - "name": { - "title": "Name", - "type": "string" - }, - "description": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Description" - }, - "metadata": { - "anyOf": [ - { - "additionalProperties": true, - "type": "object" - }, - { - "type": "null" - } - ], - "title": "Metadata" - }, - "datastore_schema": { - "additionalProperties": { - "$ref": "#/$defs/Property" - }, - "title": "Datastore Schema", - "type": "object" - }, - "connection_config": { - "$ref": "#/$defs/PostgresDatabaseConnectionConfig" - }, - "$referenced_components": { - "$ref": "#/$defs/ReferencedComponents" - }, - "component_type": { - "const": "PostgresDatabaseDatastore" - } - }, - "required": [ - "connection_config", - "datastore_schema", - "name" - ], - "title": "PostgresDatabaseDatastore", - "type": "object", - "x-abstract-component": false - }, "BaseRemoteAgent": { "anyOf": [ { @@ -5529,249 +4858,42 @@ "type": "null" } ], - "default": null, - "title": "Sensitive Headers" - }, - "key_file": { - "title": "Key File", - "type": "string" - }, - "cert_file": { - "title": "Cert File", - "type": "string" - }, - "ca_file": { - "title": "Ca File", - "type": "string" - }, - "$referenced_components": { - "$ref": "#/$defs/ReferencedComponents" - }, - "component_type": { - "const": "StreamableHTTPmTLSTransport" - } - }, - "required": [ - "ca_file", - "cert_file", - "key_file", - "name", - "url" - ], - "title": "StreamableHTTPmTLSTransport", - "type": "object", - "x-abstract-component": false - }, - "BaseSwarm": { - "additionalProperties": false, - "description": "Defines a ``Swarm`` conversational component.\n\nA ``Swarm`` is a multi-agent conversational component in which each agent determines\nthe next agent to be executed, based on a list of pre-defined relationships.\nAgents in Swarm can be any ``AgenticComponent``.\n\nExamples\n--------\n>>> from pyagentspec.agent import Agent\n>>> from pyagentspec.swarm import Swarm\n>>> addition_agent = Agent(name=\"addition_agent\", description=\"Agent that can do additions\", llm_config=llm_config, system_prompt=\"You can do additions.\")\n>>> multiplication_agent = Agent(name=\"multiplication_agent\", description=\"Agent that can do multiplication\", llm_config=llm_config, system_prompt=\"You can do multiplication.\")\n>>> division_agent = Agent(name=\"division_agent\", description=\"Agent that can do division\", llm_config=llm_config, system_prompt=\"You can do division.\")\n>>>\n>>> swarm = Swarm(\n... name=\"swarm\",\n... first_agent=addition_agent,\n... relationships=[\n... (addition_agent, multiplication_agent),\n... (addition_agent, division_agent),\n... (multiplication_agent, division_agent),\n... ]\n... )", - "properties": { - "id": { - "title": "Id", - "type": "string" - }, - "name": { - "title": "Name", - "type": "string" - }, - "description": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Description" - }, - "metadata": { - "anyOf": [ - { - "additionalProperties": true, - "type": "object" - }, - { - "type": "null" - } - ], - "title": "Metadata" - }, - "inputs": { - "anyOf": [ - { - "items": { - "$ref": "#/$defs/Property" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Inputs" - }, - "outputs": { - "anyOf": [ - { - "items": { - "$ref": "#/$defs/Property" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Outputs" - }, - "first_agent": { - "$ref": "#/$defs/AgenticComponent" - }, - "relationships": { - "items": { - "maxItems": 2, - "minItems": 2, - "prefixItems": [ - { - "$ref": "#/$defs/AgenticComponent" - }, - { - "$ref": "#/$defs/AgenticComponent" - } - ], - "type": "array" - }, - "title": "Relationships", - "type": "array" - }, - "handoff": { - "anyOf": [ - { - "type": "boolean" - }, - { - "$ref": "#/$defs/HandoffMode" - } - ], - "default": "optional", - "title": "Handoff" - }, - "$referenced_components": { - "$ref": "#/$defs/ReferencedComponents" - }, - "component_type": { - "const": "Swarm" - } - }, - "required": [ - "first_agent", - "name", - "relationships" - ], - "title": "Swarm", - "type": "object", - "x-abstract-component": false - }, - "BaseTlsOracleDatabaseConnectionConfig": { - "anyOf": [ - { - "$ref": "#/$defs/MTlsOracleDatabaseConnectionConfig" - }, - { - "additionalProperties": false, - "description": "TLS Connection Configuration to Oracle Database.", - "properties": { - "id": { - "title": "Id", - "type": "string" - }, - "name": { - "title": "Name", - "type": "string" - }, - "description": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Description" - }, - "metadata": { - "anyOf": [ - { - "additionalProperties": true, - "type": "object" - }, - { - "type": "null" - } - ], - "title": "Metadata" - }, - "user": { - "title": "User", - "type": "string" - }, - "password": { - "title": "Password", - "type": "string" - }, - "dsn": { - "title": "Dsn", - "type": "string" - }, - "config_dir": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Config Dir" - }, - "protocol": { - "default": "tcps", - "enum": [ - "tcp", - "tcps" - ], - "title": "Protocol", - "type": "string" - }, - "$referenced_components": { - "$ref": "#/$defs/ReferencedComponents" - }, - "component_type": { - "const": "TlsOracleDatabaseConnectionConfig" - } - }, - "required": [ - "dsn", - "name", - "password", - "user" - ], - "title": "TlsOracleDatabaseConnectionConfig", - "type": "object" + "default": null, + "title": "Sensitive Headers" + }, + "key_file": { + "title": "Key File", + "type": "string" + }, + "cert_file": { + "title": "Cert File", + "type": "string" + }, + "ca_file": { + "title": "Ca File", + "type": "string" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "StreamableHTTPmTLSTransport" } + }, + "required": [ + "ca_file", + "cert_file", + "key_file", + "name", + "url" ], + "title": "StreamableHTTPmTLSTransport", + "type": "object", "x-abstract-component": false }, - "BaseTlsPostgresDatabaseConnectionConfig": { + "BaseSwarm": { "additionalProperties": false, - "description": "Configuration for a PostgreSQL connection with TLS/SSL support.", + "description": "Defines a ``Swarm`` conversational component.\n\nA ``Swarm`` is a multi-agent conversational component in which each agent determines\nthe next agent to be executed, based on a list of pre-defined relationships.\nAgents in Swarm can be any ``AgenticComponent``.\n\nExamples\n--------\n>>> from pyagentspec.agent import Agent\n>>> from pyagentspec.swarm import Swarm\n>>> addition_agent = Agent(name=\"addition_agent\", description=\"Agent that can do additions\", llm_config=llm_config, system_prompt=\"You can do additions.\")\n>>> multiplication_agent = Agent(name=\"multiplication_agent\", description=\"Agent that can do multiplication\", llm_config=llm_config, system_prompt=\"You can do multiplication.\")\n>>> division_agent = Agent(name=\"division_agent\", description=\"Agent that can do division\", llm_config=llm_config, system_prompt=\"You can do division.\")\n>>>\n>>> swarm = Swarm(\n... name=\"swarm\",\n... first_agent=addition_agent,\n... relationships=[\n... (addition_agent, multiplication_agent),\n... (addition_agent, division_agent),\n... (multiplication_agent, division_agent),\n... ]\n... )", "properties": { "id": { "title": "Id", @@ -5805,93 +4927,81 @@ ], "title": "Metadata" }, - "user": { - "title": "User", - "type": "string" - }, - "password": { - "title": "Password", - "type": "string" - }, - "url": { - "title": "Url", - "type": "string" - }, - "sslmode": { - "default": "require", - "enum": [ - "allow", - "disable", - "prefer", - "require", - "verify-ca", - "verify-full" - ], - "title": "Sslmode", - "type": "string" - }, - "sslcert": { + "inputs": { "anyOf": [ { - "type": "string" + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" }, { "type": "null" } ], "default": null, - "title": "Sslcert" + "title": "Inputs" }, - "sslkey": { + "outputs": { "anyOf": [ { - "type": "string" + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" }, { "type": "null" } ], "default": null, - "title": "Sslkey" + "title": "Outputs" }, - "sslrootcert": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Sslrootcert" + "first_agent": { + "$ref": "#/$defs/AgenticComponent" + }, + "relationships": { + "items": { + "maxItems": 2, + "minItems": 2, + "prefixItems": [ + { + "$ref": "#/$defs/AgenticComponent" + }, + { + "$ref": "#/$defs/AgenticComponent" + } + ], + "type": "array" + }, + "title": "Relationships", + "type": "array" }, - "sslcrl": { + "handoff": { "anyOf": [ { - "type": "string" + "type": "boolean" }, { - "type": "null" + "$ref": "#/$defs/HandoffMode" } ], - "default": null, - "title": "Sslcrl" + "default": "optional", + "title": "Handoff" }, "$referenced_components": { "$ref": "#/$defs/ReferencedComponents" }, "component_type": { - "const": "TlsPostgresDatabaseConnectionConfig" + "const": "Swarm" } }, "required": [ + "first_agent", "name", - "password", - "url", - "user" + "relationships" ], - "title": "TlsPostgresDatabaseConnectionConfig", + "title": "Swarm", "type": "object", "x-abstract-component": false }, @@ -6144,15 +5254,9 @@ { "$ref": "#/$defs/BaseControlFlowEdge" }, - { - "$ref": "#/$defs/BaseConversationSummarizationTransform" - }, { "$ref": "#/$defs/BaseDataFlowEdge" }, - { - "$ref": "#/$defs/BaseDatastore" - }, { "$ref": "#/$defs/BaseEndNode" }, @@ -6162,9 +5266,6 @@ { "$ref": "#/$defs/BaseFlowNode" }, - { - "$ref": "#/$defs/BaseInMemoryCollectionDatastore" - }, { "$ref": "#/$defs/BaseInputMessageNode" }, @@ -6183,21 +5284,12 @@ { "$ref": "#/$defs/BaseMCPToolSpec" }, - { - "$ref": "#/$defs/BaseMTlsOracleDatabaseConnectionConfig" - }, { "$ref": "#/$defs/BaseManagerWorkers" }, { "$ref": "#/$defs/BaseMapNode" }, - { - "$ref": "#/$defs/BaseMessageSummarizationTransform" - }, - { - "$ref": "#/$defs/BaseMessageTransform" - }, { "$ref": "#/$defs/BaseNode" }, @@ -6231,12 +5323,6 @@ { "$ref": "#/$defs/BaseOpenAiConfig" }, - { - "$ref": "#/$defs/BaseOracleDatabaseConnectionConfig" - }, - { - "$ref": "#/$defs/BaseOracleDatabaseDatastore" - }, { "$ref": "#/$defs/BaseOutputMessageNode" }, @@ -6246,12 +5332,6 @@ { "$ref": "#/$defs/BaseParallelMapNode" }, - { - "$ref": "#/$defs/BasePostgresDatabaseConnectionConfig" - }, - { - "$ref": "#/$defs/BasePostgresDatabaseDatastore" - }, { "$ref": "#/$defs/BaseRemoteAgent" }, @@ -6288,12 +5368,6 @@ { "$ref": "#/$defs/BaseSwarm" }, - { - "$ref": "#/$defs/BaseTlsOracleDatabaseConnectionConfig" - }, - { - "$ref": "#/$defs/BaseTlsPostgresDatabaseConnectionConfig" - }, { "$ref": "#/$defs/BaseTool" }, @@ -6544,21 +5618,6 @@ } } }, - "VersionedConversationSummarizationTransform": { - "anyOf": [ - { - "$ref": "#/$defs/BaseConversationSummarizationTransform" - }, - { - "$ref": "#/$defs/ComponentReference" - } - ], - "properties": { - "agentspec_version": { - "$ref": "#/$defs/AgentSpecVersionEnum" - } - } - }, "VersionedDataFlowEdge": { "anyOf": [ { @@ -6574,21 +5633,6 @@ } } }, - "VersionedDatastore": { - "anyOf": [ - { - "$ref": "#/$defs/BaseDatastore" - }, - { - "$ref": "#/$defs/ComponentReference" - } - ], - "properties": { - "agentspec_version": { - "$ref": "#/$defs/AgentSpecVersionEnum" - } - } - }, "VersionedEndNode": { "anyOf": [ { @@ -6634,21 +5678,6 @@ } } }, - "VersionedInMemoryCollectionDatastore": { - "anyOf": [ - { - "$ref": "#/$defs/BaseInMemoryCollectionDatastore" - }, - { - "$ref": "#/$defs/ComponentReference" - } - ], - "properties": { - "agentspec_version": { - "$ref": "#/$defs/AgentSpecVersionEnum" - } - } - }, "VersionedInputMessageNode": { "anyOf": [ { @@ -6739,21 +5768,6 @@ } } }, - "VersionedMTlsOracleDatabaseConnectionConfig": { - "anyOf": [ - { - "$ref": "#/$defs/BaseMTlsOracleDatabaseConnectionConfig" - }, - { - "$ref": "#/$defs/ComponentReference" - } - ], - "properties": { - "agentspec_version": { - "$ref": "#/$defs/AgentSpecVersionEnum" - } - } - }, "VersionedManagerWorkers": { "anyOf": [ { @@ -6784,21 +5798,6 @@ } } }, - "VersionedMessageSummarizationTransform": { - "anyOf": [ - { - "$ref": "#/$defs/BaseMessageSummarizationTransform" - }, - { - "$ref": "#/$defs/ComponentReference" - } - ], - "properties": { - "agentspec_version": { - "$ref": "#/$defs/AgentSpecVersionEnum" - } - } - }, "VersionedNode": { "anyOf": [ { @@ -6964,21 +5963,6 @@ } } }, - "VersionedOracleDatabaseDatastore": { - "anyOf": [ - { - "$ref": "#/$defs/BaseOracleDatabaseDatastore" - }, - { - "$ref": "#/$defs/ComponentReference" - } - ], - "properties": { - "agentspec_version": { - "$ref": "#/$defs/AgentSpecVersionEnum" - } - } - }, "VersionedOutputMessageNode": { "anyOf": [ { @@ -7024,21 +6008,6 @@ } } }, - "VersionedPostgresDatabaseDatastore": { - "anyOf": [ - { - "$ref": "#/$defs/BasePostgresDatabaseDatastore" - }, - { - "$ref": "#/$defs/ComponentReference" - } - ], - "properties": { - "agentspec_version": { - "$ref": "#/$defs/AgentSpecVersionEnum" - } - } - }, "VersionedRemoteAgent": { "anyOf": [ { @@ -7219,36 +6188,6 @@ } } }, - "VersionedTlsOracleDatabaseConnectionConfig": { - "anyOf": [ - { - "$ref": "#/$defs/BaseTlsOracleDatabaseConnectionConfig" - }, - { - "$ref": "#/$defs/ComponentReference" - } - ], - "properties": { - "agentspec_version": { - "$ref": "#/$defs/AgentSpecVersionEnum" - } - } - }, - "VersionedTlsPostgresDatabaseConnectionConfig": { - "anyOf": [ - { - "$ref": "#/$defs/BaseTlsPostgresDatabaseConnectionConfig" - }, - { - "$ref": "#/$defs/ComponentReference" - } - ], - "properties": { - "agentspec_version": { - "$ref": "#/$defs/AgentSpecVersionEnum" - } - } - }, "VersionedTool": { "anyOf": [ { @@ -7372,15 +6311,9 @@ { "$ref": "#/$defs/VersionedControlFlowEdge" }, - { - "$ref": "#/$defs/VersionedConversationSummarizationTransform" - }, { "$ref": "#/$defs/VersionedDataFlowEdge" }, - { - "$ref": "#/$defs/VersionedDatastore" - }, { "$ref": "#/$defs/VersionedEndNode" }, @@ -7390,9 +6323,6 @@ { "$ref": "#/$defs/VersionedFlowNode" }, - { - "$ref": "#/$defs/VersionedInMemoryCollectionDatastore" - }, { "$ref": "#/$defs/VersionedInputMessageNode" }, @@ -7411,18 +6341,12 @@ { "$ref": "#/$defs/VersionedMCPToolSpec" }, - { - "$ref": "#/$defs/VersionedMTlsOracleDatabaseConnectionConfig" - }, { "$ref": "#/$defs/VersionedManagerWorkers" }, { "$ref": "#/$defs/VersionedMapNode" }, - { - "$ref": "#/$defs/VersionedMessageSummarizationTransform" - }, { "$ref": "#/$defs/VersionedNode" }, @@ -7456,9 +6380,6 @@ { "$ref": "#/$defs/VersionedOpenAiConfig" }, - { - "$ref": "#/$defs/VersionedOracleDatabaseDatastore" - }, { "$ref": "#/$defs/VersionedOutputMessageNode" }, @@ -7468,9 +6389,6 @@ { "$ref": "#/$defs/VersionedParallelMapNode" }, - { - "$ref": "#/$defs/VersionedPostgresDatabaseDatastore" - }, { "$ref": "#/$defs/VersionedRemoteAgent" }, @@ -7507,12 +6425,6 @@ { "$ref": "#/$defs/VersionedSwarm" }, - { - "$ref": "#/$defs/VersionedTlsOracleDatabaseConnectionConfig" - }, - { - "$ref": "#/$defs/VersionedTlsPostgresDatabaseConnectionConfig" - }, { "$ref": "#/$defs/VersionedTool" }, diff --git a/docs/pyagentspec/source/agentspec/json_spec/agentspec_json_spec_26_1_1.json b/docs/pyagentspec/source/agentspec/json_spec/agentspec_json_spec_26_1_1.json new file mode 100644 index 00000000..96450488 --- /dev/null +++ b/docs/pyagentspec/source/agentspec/json_spec/agentspec_json_spec_26_1_1.json @@ -0,0 +1,7530 @@ +{ + "$defs": { + "A2AAgent": { + "anyOf": [ + { + "$ref": "#/$defs/BaseA2AAgent" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "A2AConnectionConfig": { + "anyOf": [ + { + "$ref": "#/$defs/BaseA2AConnectionConfig" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "A2ASessionParameters": { + "description": "Class to specify parameters of the A2A session.", + "properties": { + "timeout": { + "default": 60.0, + "title": "Timeout", + "type": "number" + }, + "poll_interval": { + "default": 2.0, + "title": "Poll Interval", + "type": "number" + }, + "max_retries": { + "default": 5, + "title": "Max Retries", + "type": "integer" + } + }, + "title": "A2ASessionParameters", + "type": "object" + }, + "Agent": { + "anyOf": [ + { + "$ref": "#/$defs/BaseAgent" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "AgentNode": { + "anyOf": [ + { + "$ref": "#/$defs/BaseAgentNode" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "AgentSpecializationParameters": { + "anyOf": [ + { + "$ref": "#/$defs/BaseAgentSpecializationParameters" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "AgenticComponent": { + "anyOf": [ + { + "$ref": "#/$defs/BaseAgenticComponent" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "ApiNode": { + "anyOf": [ + { + "$ref": "#/$defs/BaseApiNode" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "BranchingNode": { + "anyOf": [ + { + "$ref": "#/$defs/BaseBranchingNode" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "BuiltinTool": { + "anyOf": [ + { + "$ref": "#/$defs/BaseBuiltinTool" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "ClientTool": { + "anyOf": [ + { + "$ref": "#/$defs/BaseClientTool" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "ClientTransport": { + "anyOf": [ + { + "$ref": "#/$defs/BaseClientTransport" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "ComponentWithIO": { + "anyOf": [ + { + "$ref": "#/$defs/BaseComponentWithIO" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "ControlFlowEdge": { + "anyOf": [ + { + "$ref": "#/$defs/BaseControlFlowEdge" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "ConversationSummarizationTransform": { + "anyOf": [ + { + "$ref": "#/$defs/BaseConversationSummarizationTransform" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "DataFlowEdge": { + "anyOf": [ + { + "$ref": "#/$defs/BaseDataFlowEdge" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "Datastore": { + "anyOf": [ + { + "$ref": "#/$defs/BaseDatastore" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "EndNode": { + "anyOf": [ + { + "$ref": "#/$defs/BaseEndNode" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "Flow": { + "anyOf": [ + { + "$ref": "#/$defs/BaseFlow" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "FlowNode": { + "anyOf": [ + { + "$ref": "#/$defs/BaseFlowNode" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "HandoffMode": { + "description": "Controls how agents in a Swarm may delegate work to one another.\n\nThis setting determines whether an agent is equipped with:\n\n * *send_message* \u2014 a tool for asking another agent to perform a sub-task and reply back.\n\n * *handoff_conversation* \u2014 a tool for transferring the full user\u2013agent conversation to another agent.\n\nDepending on the selected mode, agents have different capabilities for delegation and collaboration.", + "enum": [ + "always", + "never", + "optional" + ], + "title": "HandoffMode", + "type": "string" + }, + "InMemoryCollectionDatastore": { + "anyOf": [ + { + "$ref": "#/$defs/BaseInMemoryCollectionDatastore" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "InputMessageNode": { + "anyOf": [ + { + "$ref": "#/$defs/BaseInputMessageNode" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "LlmConfig": { + "anyOf": [ + { + "$ref": "#/$defs/BaseLlmConfig" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "LlmGenerationConfig": { + "additionalProperties": true, + "description": "A configuration object defining LLM generation parameters.\n\nParameters include number of tokens, sampling parameters, etc.", + "properties": { + "max_tokens": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Max Tokens" + }, + "temperature": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Temperature" + }, + "top_p": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Top P" + } + }, + "title": "LlmGenerationConfig", + "type": "object" + }, + "LlmNode": { + "anyOf": [ + { + "$ref": "#/$defs/BaseLlmNode" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "MCPTool": { + "anyOf": [ + { + "$ref": "#/$defs/BaseMCPTool" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "MCPToolBox": { + "anyOf": [ + { + "$ref": "#/$defs/BaseMCPToolBox" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "MCPToolSpec": { + "anyOf": [ + { + "$ref": "#/$defs/BaseMCPToolSpec" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "MTlsOracleDatabaseConnectionConfig": { + "anyOf": [ + { + "$ref": "#/$defs/BaseMTlsOracleDatabaseConnectionConfig" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "ManagerWorkers": { + "anyOf": [ + { + "$ref": "#/$defs/BaseManagerWorkers" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "MapNode": { + "anyOf": [ + { + "$ref": "#/$defs/BaseMapNode" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "MessageSummarizationTransform": { + "anyOf": [ + { + "$ref": "#/$defs/BaseMessageSummarizationTransform" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "MessageTransform": { + "anyOf": [ + { + "$ref": "#/$defs/BaseMessageTransform" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "ModelProvider": { + "description": "Provider of the model. It is used to ensure the requests to this model respect\nthe format expected by the provider.", + "enum": [ + "COHERE", + "GROK", + "META", + "OTHER" + ], + "title": "ModelProvider", + "type": "string" + }, + "Node": { + "anyOf": [ + { + "$ref": "#/$defs/BaseNode" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "OciAPIType": { + "description": "Enumeration of API Types.", + "enum": [ + "oci", + "openai_chat_completions", + "openai_responses" + ], + "title": "OciAPIType", + "type": "string" + }, + "OciAgent": { + "anyOf": [ + { + "$ref": "#/$defs/BaseOciAgent" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "OciClientConfig": { + "anyOf": [ + { + "$ref": "#/$defs/BaseOciClientConfig" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "OciClientConfigWithApiKey": { + "anyOf": [ + { + "$ref": "#/$defs/BaseOciClientConfigWithApiKey" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "OciClientConfigWithInstancePrincipal": { + "anyOf": [ + { + "$ref": "#/$defs/BaseOciClientConfigWithInstancePrincipal" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "OciClientConfigWithResourcePrincipal": { + "anyOf": [ + { + "$ref": "#/$defs/BaseOciClientConfigWithResourcePrincipal" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "OciClientConfigWithSecurityToken": { + "anyOf": [ + { + "$ref": "#/$defs/BaseOciClientConfigWithSecurityToken" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "OciGenAiConfig": { + "anyOf": [ + { + "$ref": "#/$defs/BaseOciGenAiConfig" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "OllamaConfig": { + "anyOf": [ + { + "$ref": "#/$defs/BaseOllamaConfig" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "OpenAIAPIType": { + "description": "Enumeration of OpenAI API Types.\n\nchat completions: Chat Completions API\nresponses: Responses API", + "enum": [ + "chat_completions", + "responses" + ], + "title": "OpenAIAPIType", + "type": "string" + }, + "OpenAiCompatibleConfig": { + "anyOf": [ + { + "$ref": "#/$defs/BaseOpenAiCompatibleConfig" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "OpenAiConfig": { + "anyOf": [ + { + "$ref": "#/$defs/BaseOpenAiConfig" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "OracleDatabaseConnectionConfig": { + "anyOf": [ + { + "$ref": "#/$defs/BaseOracleDatabaseConnectionConfig" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "OracleDatabaseDatastore": { + "anyOf": [ + { + "$ref": "#/$defs/BaseOracleDatabaseDatastore" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "OutputMessageNode": { + "anyOf": [ + { + "$ref": "#/$defs/BaseOutputMessageNode" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "ParallelFlowNode": { + "anyOf": [ + { + "$ref": "#/$defs/BaseParallelFlowNode" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "ParallelMapNode": { + "anyOf": [ + { + "$ref": "#/$defs/BaseParallelMapNode" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "PostgresDatabaseConnectionConfig": { + "anyOf": [ + { + "$ref": "#/$defs/BasePostgresDatabaseConnectionConfig" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "PostgresDatabaseDatastore": { + "anyOf": [ + { + "$ref": "#/$defs/BasePostgresDatabaseDatastore" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "Property": { + "description": "This object must be a valid JSON Schema", + "type": "object" + }, + "ReductionMethod": { + "description": "Enumerator for the types of reduction available in the MapNode.", + "enum": [ + "append", + "average", + "max", + "min", + "sum" + ], + "title": "ReductionMethod", + "type": "string" + }, + "RemoteAgent": { + "anyOf": [ + { + "$ref": "#/$defs/BaseRemoteAgent" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "RemoteTool": { + "anyOf": [ + { + "$ref": "#/$defs/BaseRemoteTool" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "RemoteTransport": { + "anyOf": [ + { + "$ref": "#/$defs/BaseRemoteTransport" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "SSETransport": { + "anyOf": [ + { + "$ref": "#/$defs/BaseSSETransport" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "SSEmTLSTransport": { + "anyOf": [ + { + "$ref": "#/$defs/BaseSSEmTLSTransport" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "ServerTool": { + "anyOf": [ + { + "$ref": "#/$defs/BaseServerTool" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "ServingMode": { + "description": "Serving mode to use for the GenAI service", + "enum": [ + "DEDICATED", + "ON_DEMAND" + ], + "title": "ServingMode", + "type": "string" + }, + "SessionParameters": { + "description": "Class to specify parameters of the MCP client session.", + "properties": { + "read_timeout_seconds": { + "default": 60.0, + "title": "Read Timeout Seconds", + "type": "number" + } + }, + "title": "SessionParameters", + "type": "object" + }, + "SpecializedAgent": { + "anyOf": [ + { + "$ref": "#/$defs/BaseSpecializedAgent" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "StartNode": { + "anyOf": [ + { + "$ref": "#/$defs/BaseStartNode" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "StdioTransport": { + "anyOf": [ + { + "$ref": "#/$defs/BaseStdioTransport" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "StreamableHTTPTransport": { + "anyOf": [ + { + "$ref": "#/$defs/BaseStreamableHTTPTransport" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "StreamableHTTPmTLSTransport": { + "anyOf": [ + { + "$ref": "#/$defs/BaseStreamableHTTPmTLSTransport" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "Swarm": { + "anyOf": [ + { + "$ref": "#/$defs/BaseSwarm" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "TlsOracleDatabaseConnectionConfig": { + "anyOf": [ + { + "$ref": "#/$defs/BaseTlsOracleDatabaseConnectionConfig" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "TlsPostgresDatabaseConnectionConfig": { + "anyOf": [ + { + "$ref": "#/$defs/BaseTlsPostgresDatabaseConnectionConfig" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "Tool": { + "anyOf": [ + { + "$ref": "#/$defs/BaseTool" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "ToolBox": { + "anyOf": [ + { + "$ref": "#/$defs/BaseToolBox" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "ToolNode": { + "anyOf": [ + { + "$ref": "#/$defs/BaseToolNode" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "VllmConfig": { + "anyOf": [ + { + "$ref": "#/$defs/BaseVllmConfig" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ] + }, + "BaseA2AAgent": { + "additionalProperties": false, + "description": "Component which communicates with a remote server agent using the A2A Protocol.", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "inputs": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Inputs" + }, + "outputs": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Outputs" + }, + "agent_url": { + "title": "Agent Url", + "type": "string" + }, + "connection_config": { + "$ref": "#/$defs/A2AConnectionConfig" + }, + "session_parameters": { + "$ref": "#/$defs/A2ASessionParameters", + "default": { + "timeout": 60.0, + "poll_interval": 2.0, + "max_retries": 5 + } + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "A2AAgent" + } + }, + "required": [ + "agent_url", + "connection_config", + "name" + ], + "title": "A2AAgent", + "type": "object", + "x-abstract-component": false + }, + "BaseA2AConnectionConfig": { + "additionalProperties": false, + "description": "Class to specify configuration settings for establishing a connection in A2A communication.", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "timeout": { + "default": 600.0, + "title": "Timeout", + "type": "number" + }, + "headers": { + "anyOf": [ + { + "additionalProperties": { + "type": "string" + }, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Headers" + }, + "verify": { + "default": true, + "title": "Verify", + "type": "boolean" + }, + "key_file": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Key File" + }, + "cert_file": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Cert File" + }, + "ssl_ca_cert": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Ssl Ca Cert" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "A2AConnectionConfig" + } + }, + "required": [ + "name" + ], + "title": "A2AConnectionConfig", + "type": "object", + "x-abstract-component": false + }, + "BaseAgent": { + "additionalProperties": false, + "description": "An agent is a component that can do several rounds of conversation to solve a task.\n\nIt can be executed by itself, or be executed in a flow using an AgentNode.\n\n\nExamples\n--------\n>>> from pyagentspec.agent import Agent\n>>> from pyagentspec.property import Property\n>>> expertise_property=Property(\n... json_schema={\"title\": \"domain_of_expertise\", \"type\": \"string\"}\n... )\n>>> system_prompt = '''You are an expert in {{domain_of_expertise}}.\n... Please help the users with their requests.'''\n>>> agent = Agent(\n... name=\"Adaptive expert agent\",\n... system_prompt=system_prompt,\n... llm_config=llm_config,\n... inputs=[expertise_property],\n... )", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "inputs": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Inputs" + }, + "outputs": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Outputs" + }, + "llm_config": { + "$ref": "#/$defs/LlmConfig" + }, + "system_prompt": { + "title": "System Prompt", + "type": "string" + }, + "tools": { + "items": { + "$ref": "#/$defs/Tool" + }, + "title": "Tools", + "type": "array" + }, + "toolboxes": { + "items": { + "$ref": "#/$defs/ToolBox" + }, + "title": "Toolboxes", + "type": "array" + }, + "human_in_the_loop": { + "default": true, + "title": "Human In The Loop", + "type": "boolean" + }, + "transforms": { + "default": [], + "items": { + "$ref": "#/$defs/MessageTransform" + }, + "title": "Transforms", + "type": "array" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "Agent" + } + }, + "required": [ + "llm_config", + "name", + "system_prompt" + ], + "title": "Agent", + "type": "object", + "x-abstract-component": false + }, + "BaseAgentNode": { + "additionalProperties": false, + "description": "The agent execution node is a node that will execute an agent as part of a flow.\n\nIf branches are configured, the agent will be prompted to select a branch before the agent node\ncompletes to transition to another node of the Flow.\n\n- **Inputs**\n Inferred from the definition of the agent to execute.\n- **Outputs**\n Inferred from the definition of the agent to execute.\n- **Branches**\n One, the default next.\n\nExamples\n--------\n>>> from pyagentspec.flows.flow import Flow\n>>> from pyagentspec.flows.edges import ControlFlowEdge, DataFlowEdge\n>>> from pyagentspec.agent import Agent\n>>> from pyagentspec.property import Property\n>>> from pyagentspec.flows.nodes import AgentNode, StartNode, EndNode\n>>> from pyagentspec.tools import ServerTool\n>>> query_property = Property(json_schema={\"title\": \"query\", \"type\": \"string\"})\n>>> search_results_property = Property(\n... json_schema={\"title\": \"search_results\", \"type\": \"array\", \"items\": {\"type\": \"string\"}}\n... )\n>>> search_tool = ServerTool(\n... name=\"search_tool\",\n... description=(\n... \"This tool runs a web search with the given query \"\n... \"and returns the most relevant results\"\n... ),\n... inputs=[query_property],\n... outputs=[search_results_property],\n... )\n>>> agent = Agent(\n... name=\"Search agent\",\n... llm_config=llm_config,\n... system_prompt=(\n... \"Your task is to gather the required information for the user: {{query}}\"\n... ),\n... tools=[search_tool],\n... outputs=[search_results_property],\n... )\n>>> start_node = StartNode(name=\"start\", inputs=[query_property])\n>>> end_node = EndNode(name=\"end\", outputs=[search_results_property])\n>>> agent_node = AgentNode(\n... name=\"Search agent node\",\n... agent=agent,\n... )\n>>> flow = Flow(\n... name=\"Search agent flow\",\n... start_node=start_node,\n... nodes=[start_node, agent_node, end_node],\n... control_flow_connections=[\n... ControlFlowEdge(name=\"start_to_agent\", from_node=start_node, to_node=agent_node),\n... ControlFlowEdge(name=\"agent_to_end\", from_node=agent_node, to_node=end_node),\n... ],\n... data_flow_connections=[\n... DataFlowEdge(\n... name=\"query_edge\",\n... source_node=start_node,\n... source_output=\"query\",\n... destination_node=agent_node,\n... destination_input=\"query\",\n... ),\n... DataFlowEdge(\n... name=\"search_results_edge\",\n... source_node=agent_node,\n... source_output=\"search_results\",\n... destination_node=end_node,\n... destination_input=\"search_results\"\n... ),\n... ],\n... )", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "inputs": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Inputs" + }, + "outputs": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Outputs" + }, + "branches": { + "items": { + "type": "string" + }, + "title": "Branches", + "type": "array" + }, + "agent": { + "$ref": "#/$defs/AgenticComponent" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "AgentNode" + } + }, + "required": [ + "agent", + "name" + ], + "title": "AgentNode", + "type": "object", + "x-abstract-component": false + }, + "BaseAgentSpecializationParameters": { + "additionalProperties": false, + "description": "Parameters used to specialize an agent for a certain goal or task.", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "inputs": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Inputs" + }, + "outputs": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Outputs" + }, + "additional_instructions": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Additional Instructions" + }, + "additional_tools": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Tool" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Additional Tools" + }, + "human_in_the_loop": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Human In The Loop" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "AgentSpecializationParameters" + } + }, + "required": [ + "name" + ], + "title": "AgentSpecializationParameters", + "type": "object", + "x-abstract-component": false + }, + "BaseAgenticComponent": { + "anyOf": [ + { + "$ref": "#/$defs/A2AAgent" + }, + { + "$ref": "#/$defs/Agent" + }, + { + "$ref": "#/$defs/Flow" + }, + { + "$ref": "#/$defs/ManagerWorkers" + }, + { + "$ref": "#/$defs/OciAgent" + }, + { + "$ref": "#/$defs/RemoteAgent" + }, + { + "$ref": "#/$defs/SpecializedAgent" + }, + { + "$ref": "#/$defs/Swarm" + } + ], + "x-abstract-component": true + }, + "BaseApiNode": { + "additionalProperties": false, + "description": "Make an API call.\n\nThis node is intended to be a part of a Flow.\n\n- **Inputs**\n Inferred from the json spec retrieved from API Spec URI, if available and reachable.\n Otherwise, users have to manually specify them.\n- **Outputs**\n Inferred from the json spec retrieved from API Spec URI, if available and reachable.\n Otherwise, users should manually specify them.\n\n If None is given, ``pyagentspec`` infers a generic property of any type named ``response``.\n- **Branches**\n One, the default next.\n\n\nExamples\n--------\n>>> from pyagentspec.flows.nodes import ApiNode\n>>> from pyagentspec.property import Property\n>>> weather_result_property = Property(\n... json_schema={\n... \"title\": \"zurich_weather\",\n... \"type\": \"object\",\n... \"properties\": {\n... \"temperature\": {\n... \"type\": \"number\",\n... \"description\": \"Temperature in celsius degrees\",\n... },\n... \"weather\": {\"type\": \"string\"}\n... },\n... }\n... )\n>>> call_current_weather_step = ApiNode(\n... name=\"Weather API call node\",\n... url=\"https://example.com/weather\",\n... http_method = \"GET\",\n... query_params={\n... \"location\": \"zurich\",\n... },\n... outputs=[weather_result_property]\n... )\n>>>\n>>> item_id_property = Property(\n... json_schema={\"title\": \"item_id\", \"type\": \"string\"}\n... )\n>>> order_id_property = Property(\n... json_schema={\"title\": \"order_id\", \"type\": \"string\"}\n... )\n>>> store_id_property = Property(\n... json_schema={\"title\": \"store_id\", \"type\": \"string\"}\n... )\n>>> session_id_property = Property(\n... json_schema={\"title\": \"session_id\", \"type\": \"string\"}\n... )\n>>> create_order_step = ApiNode(\n... name=\"Orders api call node\",\n... url=\"https://example.com/orders/{{ order_id }}\",\n... http_method=\"POST\",\n... # sending an object which will automatically be transformed into JSON\n... data={\n... # define a static body parameter\n... \"topic_id\": 12345,\n... # define a templated body parameter.\n... # The value for {{ item_id }} will be taken from the IO system at runtime\n... \"item_id\": \"{{ item_id }}\",\n... },\n... query_params={\n... # provide one templated query parameter called \"store_id\"\n... # which will take its value from the IO system from key \"store_id\"\n... \"store_id\": \"{{ store_id }}\",\n... },\n... headers={\n... # set header session_id. the value is coming from the IO system\n... \"session_id\": \"{{ session_id }}\",\n... },\n... inputs=[item_id_property, order_id_property, store_id_property, session_id_property],\n... )", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "inputs": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Inputs" + }, + "outputs": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Outputs" + }, + "branches": { + "items": { + "type": "string" + }, + "title": "Branches", + "type": "array" + }, + "url": { + "title": "Url", + "type": "string" + }, + "http_method": { + "title": "Http Method", + "type": "string" + }, + "api_spec_uri": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Api Spec Uri" + }, + "data": { + "additionalProperties": true, + "title": "Data", + "type": "object" + }, + "query_params": { + "additionalProperties": true, + "title": "Query Params", + "type": "object" + }, + "headers": { + "additionalProperties": true, + "title": "Headers", + "type": "object" + }, + "sensitive_headers": { + "additionalProperties": true, + "title": "Sensitive Headers", + "type": "object" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "ApiNode" + } + }, + "required": [ + "http_method", + "name", + "url" + ], + "title": "ApiNode", + "type": "object", + "x-abstract-component": false + }, + "BaseBranchingNode": { + "additionalProperties": false, + "description": "Select the next node to transition to based on a mapping.\n\nThe input is used as key for the mapping. If the input does not correspond to any of the keys\nof the mapping the branch 'default' will be selected. This node is intended to be a part of a\nFlow.\n\n- **Inputs**\n The input value that should be used as key for the mapping.\n\n If None is given, ``pyagentspec`` infers a string property named ``branching_mapping_key``.\n- **Outputs**\n None.\n- **Branches**\n One for each value in the mapping, plus a branch called ``default``,\n which is the branch taken by the flow when mapping fails\n (i.e., the input does not match any key in the mapping).\n\nExamples\n--------\n>>> from pyagentspec.agent import Agent\n>>> from pyagentspec.flows.edges import ControlFlowEdge, DataFlowEdge\n>>> from pyagentspec.flows.flow import Flow\n>>> from pyagentspec.flows.nodes import AgentNode, BranchingNode, StartNode, EndNode\n>>> from pyagentspec.property import Property\n>>> CORRECT_PASSWORD_BRANCH = \"PASSWORD_OK\"\n>>> password_property = Property(\n... json_schema={\"title\": \"password\", \"type\": \"string\"}\n... )\n>>> agent = Agent(\n... name=\"User input agent\",\n... llm_config=llm_config,\n... system_prompt=(\n... \"Your task is to ask the password to the user. \"\n... \"Once you get it, submit it and end.\"\n... ),\n... outputs=[password_property],\n... )\n>>> start_node = StartNode(name=\"start\")\n>>> access_granted_end_node = EndNode(\n... name=\"access granted end\", branch_name=\"ACCESS_GRANTED\"\n... )\n>>> access_denied_end_node = EndNode(\n... name=\"access denied end\", branch_name=\"ACCESS_DENIED\"\n... )\n>>> branching_node = BranchingNode(\n... name=\"password check\",\n... mapping={\"123456\": CORRECT_PASSWORD_BRANCH},\n... inputs=[password_property]\n... )\n>>> agent_node = AgentNode(\n... name=\"User input agent node\",\n... agent=agent,\n... )\n>>> assistant = Flow(\n... name=\"Check access flow\",\n... start_node=start_node,\n... nodes=[\n... start_node,\n... agent_node,\n... branching_node,\n... access_granted_end_node,\n... access_denied_end_node\n... ],\n... control_flow_connections=[\n... ControlFlowEdge(\n... name=\"start_to_agent\",\n... from_node=start_node,\n... to_node=agent_node\n... ),\n... ControlFlowEdge(\n... name=\"agent_to_branching\",\n... from_node=agent_node,\n... to_node=branching_node\n... ),\n... ControlFlowEdge(\n... name=\"branching_to_access_granted\",\n... from_node=branching_node,\n... from_branch=CORRECT_PASSWORD_BRANCH,\n... to_node=access_granted_end_node,\n... ),\n... ControlFlowEdge(\n... name=\"branching_to_access_denied\",\n... from_node=branching_node,\n... from_branch=BranchingNode.DEFAULT_BRANCH,\n... to_node=access_denied_end_node,\n... ),\n... ],\n... data_flow_connections=[\n... DataFlowEdge(\n... name=\"password_edge\",\n... source_node=agent_node,\n... source_output=\"password\",\n... destination_node=branching_node,\n... destination_input=\"password\",\n... ),\n... ],\n... )", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "inputs": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Inputs" + }, + "outputs": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Outputs" + }, + "branches": { + "items": { + "type": "string" + }, + "title": "Branches", + "type": "array" + }, + "mapping": { + "additionalProperties": { + "type": "string" + }, + "title": "Mapping", + "type": "object" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "BranchingNode" + } + }, + "required": [ + "mapping", + "name" + ], + "title": "BranchingNode", + "type": "object", + "x-abstract-component": false + }, + "BaseBuiltinTool": { + "additionalProperties": false, + "description": "A tool that is built into and executed by the orchestrator", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "inputs": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Inputs" + }, + "outputs": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Outputs" + }, + "requires_confirmation": { + "default": false, + "title": "Requires Confirmation", + "type": "boolean" + }, + "tool_type": { + "title": "Tool Type", + "type": "string" + }, + "configuration": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Configuration" + }, + "executor_name": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Executor Name" + }, + "tool_version": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Tool Version" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "BuiltinTool" + } + }, + "required": [ + "name", + "tool_type" + ], + "title": "BuiltinTool", + "type": "object", + "x-abstract-component": false + }, + "BaseClientTool": { + "additionalProperties": false, + "description": "A tool that needs to be run by the client application.", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "inputs": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Inputs" + }, + "outputs": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Outputs" + }, + "requires_confirmation": { + "default": false, + "title": "Requires Confirmation", + "type": "boolean" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "ClientTool" + } + }, + "required": [ + "name" + ], + "title": "ClientTool", + "type": "object", + "x-abstract-component": false + }, + "BaseClientTransport": { + "anyOf": [ + { + "$ref": "#/$defs/RemoteTransport" + }, + { + "$ref": "#/$defs/SSETransport" + }, + { + "$ref": "#/$defs/SSEmTLSTransport" + }, + { + "$ref": "#/$defs/StdioTransport" + }, + { + "$ref": "#/$defs/StreamableHTTPTransport" + }, + { + "$ref": "#/$defs/StreamableHTTPmTLSTransport" + } + ], + "x-abstract-component": true + }, + "BaseComponentWithIO": { + "anyOf": [ + { + "$ref": "#/$defs/A2AAgent" + }, + { + "$ref": "#/$defs/Agent" + }, + { + "$ref": "#/$defs/AgentNode" + }, + { + "$ref": "#/$defs/AgentSpecializationParameters" + }, + { + "$ref": "#/$defs/AgenticComponent" + }, + { + "$ref": "#/$defs/ApiNode" + }, + { + "$ref": "#/$defs/BranchingNode" + }, + { + "$ref": "#/$defs/BuiltinTool" + }, + { + "$ref": "#/$defs/ClientTool" + }, + { + "$ref": "#/$defs/EndNode" + }, + { + "$ref": "#/$defs/Flow" + }, + { + "$ref": "#/$defs/FlowNode" + }, + { + "$ref": "#/$defs/InputMessageNode" + }, + { + "$ref": "#/$defs/LlmNode" + }, + { + "$ref": "#/$defs/MCPTool" + }, + { + "$ref": "#/$defs/MCPToolSpec" + }, + { + "$ref": "#/$defs/ManagerWorkers" + }, + { + "$ref": "#/$defs/MapNode" + }, + { + "$ref": "#/$defs/Node" + }, + { + "$ref": "#/$defs/OciAgent" + }, + { + "$ref": "#/$defs/OutputMessageNode" + }, + { + "$ref": "#/$defs/ParallelFlowNode" + }, + { + "$ref": "#/$defs/ParallelMapNode" + }, + { + "$ref": "#/$defs/RemoteAgent" + }, + { + "$ref": "#/$defs/RemoteTool" + }, + { + "$ref": "#/$defs/ServerTool" + }, + { + "$ref": "#/$defs/SpecializedAgent" + }, + { + "$ref": "#/$defs/StartNode" + }, + { + "$ref": "#/$defs/Swarm" + }, + { + "$ref": "#/$defs/Tool" + }, + { + "$ref": "#/$defs/ToolNode" + } + ], + "x-abstract-component": true + }, + "BaseControlFlowEdge": { + "additionalProperties": false, + "description": "A control flow edge specifies a possible transition from a node to another in a flow.\n\nA single node can have several potential next nodes, in which case several control flow edges\nshould be present in the control flow connections of that flow.", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "from_node": { + "$ref": "#/$defs/Node" + }, + "from_branch": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "From Branch" + }, + "to_node": { + "$ref": "#/$defs/Node" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "ControlFlowEdge" + } + }, + "required": [ + "from_node", + "name", + "to_node" + ], + "title": "ControlFlowEdge", + "type": "object", + "x-abstract-component": false + }, + "BaseConversationSummarizationTransform": { + "additionalProperties": false, + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "llm": { + "$ref": "#/$defs/LlmConfig" + }, + "max_num_messages": { + "default": 50, + "title": "Max Num Messages", + "type": "integer" + }, + "min_num_messages": { + "default": 10, + "title": "Min Num Messages", + "type": "integer" + }, + "summarization_instructions": { + "default": "Please make a summary of this conversation. Include relevant information and keep it short. Your response will replace the messages, so just output the summary directly, no introduction needed.", + "title": "Summarization Instructions", + "type": "string" + }, + "summarized_conversation_template": { + "default": "Summarized conversation: {{summary}}", + "title": "Summarized Conversation Template", + "type": "string" + }, + "datastore": { + "anyOf": [ + { + "type": "null" + }, + { + "$ref": "#/$defs/Datastore" + } + ], + "default": null + }, + "max_cache_size": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": 10000, + "title": "Max Cache Size" + }, + "max_cache_lifetime": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": 14400, + "title": "Max Cache Lifetime" + }, + "cache_collection_name": { + "default": "summarized_conversations_cache", + "title": "Cache Collection Name", + "type": "string" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "ConversationSummarizationTransform" + } + }, + "required": [ + "llm", + "name" + ], + "title": "ConversationSummarizationTransform", + "type": "object", + "x-abstract-component": false + }, + "BaseDataFlowEdge": { + "additionalProperties": false, + "description": "A data flow edge specifies how the output of a node propagates as input of another node.\n\nAn outputs can be propagated as input of several nodes.", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "source_node": { + "$ref": "#/$defs/Node" + }, + "source_output": { + "title": "Source Output", + "type": "string" + }, + "destination_node": { + "$ref": "#/$defs/Node" + }, + "destination_input": { + "title": "Destination Input", + "type": "string" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "DataFlowEdge" + } + }, + "required": [ + "destination_input", + "destination_node", + "name", + "source_node", + "source_output" + ], + "title": "DataFlowEdge", + "type": "object", + "x-abstract-component": false + }, + "BaseDatastore": { + "anyOf": [ + { + "$ref": "#/$defs/InMemoryCollectionDatastore" + }, + { + "$ref": "#/$defs/OracleDatabaseDatastore" + }, + { + "$ref": "#/$defs/PostgresDatabaseDatastore" + } + ], + "x-abstract-component": true + }, + "BaseEndNode": { + "additionalProperties": false, + "description": "End nodes denote the end of the execution of a flow.\n\nThere might be several end nodes in a flow, in which case the executor of the flow\nshould be able to track which one was reached and pass that back to the caller.\n\n- **Inputs**\n The list of inputs of the step. If both input and output properties are specified they\n must be an exact match\n\n If None is given, ``pyagentspec`` copies the outputs provided, if any. Otherwise, no input is exposed.\n- **Outputs**\n The list of outputs that should be exposed by the flow. If both input and output properties\n are specified they must be an exact match\n\n If None is given, ``pyagentspec`` copies the inputs provided, if any. Otherwise, no output is exposed.\n- **Branches**\n None.\n\nExamples\n--------\n>>> from pyagentspec.agent import Agent\n>>> from pyagentspec.flows.edges import ControlFlowEdge, DataFlowEdge\n>>> from pyagentspec.flows.flow import Flow\n>>> from pyagentspec.flows.nodes import AgentNode, BranchingNode, StartNode, EndNode\n>>> from pyagentspec.property import Property\n>>> languages_to_branch_name = {\n... \"english\": \"ENGLISH\",\n... \"spanish\": \"SPANISH\",\n... \"italian\": \"ITALIAN\",\n... }\n>>> language_property = Property(\n... json_schema={\"title\": \"language\", \"type\": \"string\"}\n... )\n>>> agent = Agent(\n... name=\"Language detector agent\",\n... llm_config=llm_config,\n... system_prompt=(\n... \"Your task is to understand the language spoken by the user.\"\n... \"Please output only the language in lowercase and submit.\"\n... ),\n... outputs=[language_property],\n... )\n>>> start_node = StartNode(name=\"start\")\n>>> english_end_node = EndNode(\n... name=\"english end\", branch_name=languages_to_branch_name[\"english\"]\n... )\n>>> spanish_end_node = EndNode(\n... name=\"spanish end\", branch_name=languages_to_branch_name[\"spanish\"]\n... )\n>>> italian_end_node = EndNode(\n... name=\"italian end\", branch_name=languages_to_branch_name[\"italian\"]\n... )\n>>> unknown_end_node = EndNode(name=\"unknown language end\", branch_name=\"unknown\")\n>>> branching_node = BranchingNode(\n... name=\"language check\",\n... mapping=languages_to_branch_name,\n... inputs=[language_property]\n... )\n>>> agent_node = AgentNode(\n... name=\"User input agent node\",\n... agent=agent,\n... )\n>>> assistant = Flow(\n... name=\"Check access flow\",\n... start_node=start_node,\n... nodes=[\n... start_node,\n... agent_node,\n... branching_node,\n... english_end_node,\n... spanish_end_node,\n... italian_end_node,\n... unknown_end_node,\n... ],\n... control_flow_connections=[\n... ControlFlowEdge(\n... name=\"start_to_agent\", from_node=start_node, to_node=agent_node\n... ),\n... ControlFlowEdge(\n... name=\"agent_to_branching\", from_node=agent_node, to_node=branching_node\n... ),\n... ControlFlowEdge(\n... name=\"branching_to_english_end\",\n... from_node=branching_node,\n... from_branch=languages_to_branch_name[\"english\"],\n... to_node=english_end_node,\n... ),\n... ControlFlowEdge(\n... name=\"branching_to_spanish_end\",\n... from_node=branching_node,\n... from_branch=languages_to_branch_name[\"spanish\"],\n... to_node=spanish_end_node,\n... ),\n... ControlFlowEdge(\n... name=\"branching_to_italian_end\",\n... from_node=branching_node,\n... from_branch=languages_to_branch_name[\"italian\"],\n... to_node=italian_end_node,\n... ),\n... ControlFlowEdge(\n... name=\"branching_to_unknown_end\",\n... from_node=branching_node,\n... from_branch=BranchingNode.DEFAULT_BRANCH,\n... to_node=unknown_end_node,\n... ),\n... ],\n... data_flow_connections=[\n... DataFlowEdge(\n... name=\"language_edge\",\n... source_node=agent_node,\n... source_output=\"language\",\n... destination_node=branching_node,\n... destination_input=\"language\",\n... ),\n... ],\n... )", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "inputs": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Inputs" + }, + "outputs": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Outputs" + }, + "branches": { + "items": { + "type": "string" + }, + "title": "Branches", + "type": "array" + }, + "branch_name": { + "default": "next", + "title": "Branch Name", + "type": "string" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "EndNode" + } + }, + "required": [ + "name" + ], + "title": "EndNode", + "type": "object", + "x-abstract-component": false + }, + "BaseFlow": { + "additionalProperties": false, + "description": "A flow is a component to model sequences of operations to do in a precised order.\n\nThe operations and sequence is defined by the nodes and transitions associated to the flow.\nSteps can be deterministic, or for some use LLMs.\n\nExample\n-------\n>>> from pyagentspec.property import Property\n>>> from pyagentspec.flows.flow import Flow\n>>> from pyagentspec.flows.edges import ControlFlowEdge, DataFlowEdge\n>>> from pyagentspec.flows.nodes import LlmNode, StartNode, EndNode\n>>> prompt_property = Property(\n... json_schema={\"title\": \"prompt\", \"type\": \"string\"}\n... )\n>>> llm_output_property = Property(\n... json_schema={\"title\": \"llm_output\", \"type\": \"string\"}\n... )\n>>> start_node = StartNode(name=\"start\", inputs=[prompt_property])\n>>> end_node = EndNode(name=\"end\", outputs=[llm_output_property])\n>>> llm_node = LlmNode(\n... name=\"simple llm node\",\n... llm_config=llm_config,\n... prompt_template=\"{{prompt}}\",\n... inputs=[prompt_property],\n... outputs=[llm_output_property],\n... )\n>>> flow = Flow(\n... name=\"Simple prompting flow\",\n... start_node=start_node,\n... nodes=[start_node, llm_node, end_node],\n... control_flow_connections=[\n... ControlFlowEdge(name=\"start_to_llm\", from_node=start_node, to_node=llm_node),\n... ControlFlowEdge(name=\"llm_to_end\", from_node=llm_node, to_node=end_node),\n... ],\n... data_flow_connections=[\n... DataFlowEdge(\n... name=\"prompt_edge\",\n... source_node=start_node,\n... source_output=\"prompt\",\n... destination_node=llm_node,\n... destination_input=\"prompt\",\n... ),\n... DataFlowEdge(\n... name=\"llm_output_edge\",\n... source_node=llm_node,\n... source_output=\"llm_output\",\n... destination_node=end_node,\n... destination_input=\"llm_output\"\n... ),\n... ],\n... )", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "inputs": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Inputs" + }, + "outputs": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Outputs" + }, + "start_node": { + "$ref": "#/$defs/Node" + }, + "nodes": { + "items": { + "$ref": "#/$defs/Node" + }, + "title": "Nodes", + "type": "array" + }, + "control_flow_connections": { + "items": { + "$ref": "#/$defs/ControlFlowEdge" + }, + "title": "Control Flow Connections", + "type": "array" + }, + "data_flow_connections": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/DataFlowEdge" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Data Flow Connections" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "Flow" + } + }, + "required": [ + "control_flow_connections", + "name", + "nodes", + "start_node" + ], + "title": "Flow", + "type": "object", + "x-abstract-component": false + }, + "BaseFlowNode": { + "additionalProperties": false, + "description": "The flow node executes a subflow as part of a flow.\n\n- **Inputs**\n Inferred from the inner structure. It's the sets of inputs\n required by the StartNode of the inner flow.\n- **Outputs**\n Inferred from the inner structure. It's the union of the\n sets of outputs exposed by the EndNodes of the inner flow.\n- **Branches**\n Inferred from the inner flow, one per each different value of the attribute\n ``branch_name`` of the nodes of type EndNode in the inner flow.\n\nExample\n-------\nThe ``FlowNode`` is particularly suitable when subflows can be reused inside a project.\nLet's see an example with a flow that estimates numerical value\nusing the \"wisdowm of the crowd\" effect:\n\n>>> from pyagentspec.property import Property\n>>> from pyagentspec.flows.flow import Flow\n>>> from pyagentspec.flows.edges import ControlFlowEdge, DataFlowEdge\n>>> from pyagentspec.flows.nodes import MapNode, LlmNode, ToolNode, StartNode, EndNode\n>>> from pyagentspec.tools import ServerTool\n>>> duplication_tool = ServerTool(\n... name=\"duplication_tool\",\n... description=\"\",\n... inputs=[\n... Property(\n... json_schema={\"title\": \"element\", \"description\": \"\", \"type\": \"string\"}\n... ),\n... Property(\n... json_schema={\"title\": \"n\", \"description\": \"\", \"type\": \"integer\"}\n... ),\n... ],\n... outputs=[\n... Property(\n... json_schema={\n... \"title\": \"flow_iterable_queries\",\n... \"type\": \"array\",\n... \"items\": {\"type\": \"string\"}\n... },\n... )\n... ],\n... )\n>>> reduce_tool = ServerTool(\n... name=\"reduce_tool\",\n... description=\"\",\n... inputs=[\n... Property(\n... json_schema={\"title\": \"elements\", \"type\": \"array\", \"items\": {\"type\": \"string\"}}\n... ),\n... ],\n... outputs=[Property(json_schema={\"title\": \"flow_processed_query\", \"type\": \"string\"})],\n... )\n>>> # Defining a simple prompt\n>>> REASONING_PROMPT_TEMPLATE = '''Provide your best numerical estimate for: {{user_input}}\n... Your answer should be a single number.\n... Do not include any units, reasoning, or extra text.'''\n>>> # Defining the subflow for the map step\n>>> user_input_property = Property(\n... json_schema={\"title\": \"user_input\", \"type\": \"string\"}\n... )\n>>> flow_processed_query_property = Property(\n... json_schema={\"title\": \"flow_processed_query\", \"type\": \"string\"}\n... )\n>>> start_node = StartNode(name=\"start\", inputs=[user_input_property])\n>>> end_node = EndNode(name=\"end\", outputs=[flow_processed_query_property])\n>>> llm_node = LlmNode(\n... name=\"reasoning llm node\",\n... llm_config=llm_config,\n... prompt_template=REASONING_PROMPT_TEMPLATE,\n... inputs=[user_input_property],\n... outputs=[flow_processed_query_property],\n... )\n>>> inner_map_flow = Flow(\n... name=\"Map flow\",\n... start_node=start_node,\n... nodes=[start_node, llm_node, end_node],\n... control_flow_connections=[\n... ControlFlowEdge(name=\"start_to_llm\", from_node=start_node, to_node=llm_node),\n... ControlFlowEdge(name=\"llm_to_end\", from_node=llm_node, to_node=end_node),\n... ],\n... data_flow_connections=[\n... DataFlowEdge(\n... name=\"query_edge\",\n... source_node=start_node,\n... source_output=\"user_input\",\n... destination_node=llm_node,\n... destination_input=\"user_input\",\n... ),\n... DataFlowEdge(\n... name=\"search_results_edge\",\n... source_node=llm_node,\n... source_output=\"flow_processed_query\",\n... destination_node=end_node,\n... destination_input=\"flow_processed_query\"\n... ),\n... ],\n... )\n>>> user_query_property = Property(\n... json_schema={\"title\": \"user_query\", \"type\": \"string\"}\n... )\n>>> n_repeat_property = Property(\n... json_schema={\"title\": \"n_repeat\", \"type\": \"integer\"}\n... )\n>>> flow_iterable_queries_property = Property(\n... json_schema={\n... \"title\": \"iterated_user_input\",\n... \"type\": \"array\",\n... \"items\": {\"type\": \"string\"},\n... }\n... )\n>>> flow_processed_queries_property = Property(\n... json_schema={\n... \"title\": \"collected_flow_processed_query\",\n... \"type\": \"array\",\n... \"items\": {\"type\": \"string\"},\n... }\n... )\n>>> start_node = StartNode(name=\"start\", inputs=[user_query_property, n_repeat_property])\n>>> end_node = EndNode(name=\"end\", outputs=[flow_processed_query_property])\n>>> duplication_node = ToolNode(\n... name=\"duplication_tool node\",\n... tool=duplication_tool,\n... )\n>>> reduce_node = ToolNode(\n... name=\"reduce_tool node\",\n... tool=reduce_tool,\n... )\n>>> map_node = MapNode(\n... name=\"map node\",\n... subflow=inner_map_flow,\n... inputs=[flow_iterable_queries_property],\n... outputs=[flow_processed_queries_property],\n... )\n>>> mapreduce_flow = Flow(\n... name=\"Map-reduce flow\",\n... start_node=start_node,\n... nodes=[start_node, duplication_node, map_node, reduce_node, end_node],\n... control_flow_connections=[\n... ControlFlowEdge(\n... name=\"start_to_duplication\", from_node=start_node, to_node=duplication_node\n... ),\n... ControlFlowEdge(\n... name=\"duplication_to_map\", from_node=duplication_node, to_node=map_node\n... ),\n... ControlFlowEdge(name=\"map_to_reduce\", from_node=map_node, to_node=reduce_node),\n... ControlFlowEdge(name=\"reduce_to_end\", from_node=reduce_node, to_node=end_node),\n... ],\n... data_flow_connections=[\n... DataFlowEdge(\n... name=\"query_edge\",\n... source_node=start_node,\n... source_output=\"user_query\",\n... destination_node=duplication_node,\n... destination_input=\"element\",\n... ),\n... DataFlowEdge(\n... name=\"n_repeat_edge\",\n... source_node=start_node,\n... source_output=\"n_repeat\",\n... destination_node=duplication_node,\n... destination_input=\"n\",\n... ),\n... DataFlowEdge(\n... name=\"flow_iterables_edge\",\n... source_node=duplication_node,\n... source_output=\"flow_iterable_queries\",\n... destination_node=map_node,\n... destination_input=\"iterated_user_input\",\n... ),\n... DataFlowEdge(\n... name=\"flow_processed_queries_edge\",\n... source_node=map_node,\n... source_output=\"collected_flow_processed_query\",\n... destination_node=reduce_node,\n... destination_input=\"elements\",\n... ),\n... DataFlowEdge(\n... name=\"flow_processed_query_edge\",\n... source_node=reduce_node,\n... source_output=\"flow_processed_query\",\n... destination_node=end_node,\n... destination_input=\"flow_processed_query\",\n... ),\n... ],\n... )\n\nOnce the subflow is created we can simply integrate it with the ``FlowNode``:\n\n>>> from pyagentspec.flows.nodes import FlowNode, AgentNode\n>>> from pyagentspec.agent import Agent\n>>> start_node = StartNode(name=\"start\")\n>>> end_node = EndNode(name=\"end\", outputs=[flow_processed_query_property])\n>>> flow_node = FlowNode(name=\"flow node\", subflow=mapreduce_flow)\n>>> agent = Agent(\n... name=\"User interaction agent\",\n... llm_config=llm_config,\n... system_prompt=(\n... \"Your task is to gather from the user the query and the number of times \"\n... \"it should be asked to an LLM. Once you have this information, submit and exit.\"\n... ),\n... outputs=[user_query_property, n_repeat_property],\n... )\n>>> agent_node = AgentNode(name=\"flow node\", agent=agent)\n>>> flow = Flow(\n... name=\"Map flow\",\n... start_node=start_node,\n... nodes=[start_node, agent_node, flow_node, end_node],\n... control_flow_connections=[\n... ControlFlowEdge(name=\"start_to_agent\", from_node=start_node, to_node=agent_node),\n... ControlFlowEdge(name=\"agent_to_flow\", from_node=agent_node, to_node=flow_node),\n... ControlFlowEdge(name=\"flow_to_end\", from_node=flow_node, to_node=end_node),\n... ],\n... data_flow_connections=[\n... DataFlowEdge(\n... name=\"query_edge\",\n... source_node=agent_node,\n... source_output=\"user_query\",\n... destination_node=flow_node,\n... destination_input=\"user_query\",\n... ),\n... DataFlowEdge(\n... name=\"n_rep_edge\",\n... source_node=agent_node,\n... source_output=\"n_repeat\",\n... destination_node=flow_node,\n... destination_input=\"n_repeat\"\n... ),\n... DataFlowEdge(\n... name=\"n_rep_edge\",\n... source_node=flow_node,\n... source_output=\"flow_processed_query\",\n... destination_node=end_node,\n... destination_input=\"flow_processed_query\"\n... ),\n... ],\n... )", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "inputs": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Inputs" + }, + "outputs": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Outputs" + }, + "branches": { + "items": { + "type": "string" + }, + "title": "Branches", + "type": "array" + }, + "subflow": { + "$ref": "#/$defs/Flow" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "FlowNode" + } + }, + "required": [ + "name", + "subflow" + ], + "title": "FlowNode", + "type": "object", + "x-abstract-component": false + }, + "BaseInMemoryCollectionDatastore": { + "additionalProperties": false, + "description": "In-memory datastore for testing and development purposes.", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "datastore_schema": { + "additionalProperties": { + "$ref": "#/$defs/Property" + }, + "title": "Datastore Schema", + "type": "object" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "InMemoryCollectionDatastore" + } + }, + "required": [ + "datastore_schema", + "name" + ], + "title": "InMemoryCollectionDatastore", + "type": "object", + "x-abstract-component": false + }, + "BaseInputMessageNode": { + "additionalProperties": false, + "description": "This node interrupts the execution of the flow in order to wait for a user input, and restarts after receiving it.\nAn agent message, if given, is appended to the conversation before waiting for input.\nUser input is appended to the conversation as a user message, and it is returned as a string property from the node.\n\n- **Inputs**\n One per variable in the message\n- **Outputs**\n One string property that represents the content of the input user message.\n\n If None is given, ``pyagentspec`` infers a string property named ``user_input``.\n- **Branches**\n One, the default next.\n\nExamples\n--------\n>>> from pyagentspec.flows.edges import ControlFlowEdge, DataFlowEdge\n>>> from pyagentspec.flows.flow import Flow\n>>> from pyagentspec.flows.nodes import StartNode, EndNode, InputMessageNode, OutputMessageNode, LlmNode\n>>> from pyagentspec.property import StringProperty\n>>> start_node = StartNode(name=\"start\")\n>>> prompt_node = OutputMessageNode(name=\"ask_input\", message=\"What is the paragraph you want to rephrase?\")\n>>> input_node = InputMessageNode(name=\"user_input\", outputs=[StringProperty(title=\"user_input\")])\n>>> llm_node = LlmNode(\n... name=\"rephrase\",\n... llm_config=llm_config,\n... prompt_template=\"Rephrase {{user_input}}\",\n... outputs=[StringProperty(title=\"rephrased_user_input\")],\n... )\n>>> output_node = OutputMessageNode(name=\"ask_input\", message=\"{{rephrased_user_input}}\")\n>>> end_node = EndNode(name=\"end\")\n>>> flow = Flow(\n... name=\"rephrase_paragraph_flow\",\n... start_node=start_node,\n... nodes=[start_node, prompt_node, input_node, llm_node, output_node, end_node],\n... control_flow_connections=[\n... ControlFlowEdge(name=\"ce1\", from_node=start_node, to_node=prompt_node),\n... ControlFlowEdge(name=\"ce2\", from_node=prompt_node, to_node=input_node),\n... ControlFlowEdge(name=\"ce3\", from_node=input_node, to_node=llm_node),\n... ControlFlowEdge(name=\"ce4\", from_node=llm_node, to_node=output_node),\n... ControlFlowEdge(name=\"ce5\", from_node=output_node, to_node=end_node),\n... ],\n... data_flow_connections=[\n... DataFlowEdge(\n... name=\"de1\",\n... source_node=input_node,\n... source_output=\"user_input\",\n... destination_node=llm_node,\n... destination_input=\"user_input\",\n... ),\n... DataFlowEdge(\n... name=\"de2\",\n... source_node=llm_node,\n... source_output=\"rephrased_user_input\",\n... destination_node=output_node,\n... destination_input=\"rephrased_user_input\",\n... ),\n... ]\n... )", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "inputs": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Inputs" + }, + "outputs": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Outputs" + }, + "branches": { + "items": { + "type": "string" + }, + "title": "Branches", + "type": "array" + }, + "message": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Message" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "InputMessageNode" + } + }, + "required": [ + "name" + ], + "title": "InputMessageNode", + "type": "object", + "x-abstract-component": false + }, + "BaseLlmConfig": { + "anyOf": [ + { + "$ref": "#/$defs/OciGenAiConfig" + }, + { + "$ref": "#/$defs/OllamaConfig" + }, + { + "$ref": "#/$defs/OpenAiCompatibleConfig" + }, + { + "$ref": "#/$defs/OpenAiConfig" + }, + { + "$ref": "#/$defs/VllmConfig" + } + ], + "x-abstract-component": true + }, + "BaseLlmNode": { + "additionalProperties": false, + "description": "Execute a prompt template with a given LLM.\n\nThis node is intended to be a part of a Flow.\n\n- **Inputs**\n One per placeholder in the prompt template.\n- **Outputs**\n The output text generated by the LLM.\n\n If None is given, ``pyagentspec`` infers a string property named ``generated_text``.\n- **Branches**\n One, the default next.\n\nExample\n-------\n>>> from pyagentspec.property import Property\n>>> from pyagentspec.flows.flow import Flow\n>>> from pyagentspec.flows.edges import ControlFlowEdge, DataFlowEdge\n>>> from pyagentspec.flows.nodes import LlmNode, StartNode, EndNode\n>>> country_property = Property(\n... json_schema={\"title\": \"country\", \"type\": \"string\"}\n... )\n>>> capital_property = Property(\n... json_schema={\"title\": \"capital\", \"type\": \"string\"}\n... )\n>>> start_node = StartNode(name=\"start\", inputs=[country_property])\n>>> end_node = EndNode(name=\"end\", outputs=[capital_property])\n>>> llm_node = LlmNode(\n... name=\"simple llm node\",\n... llm_config=llm_config,\n... prompt_template=\"What is the capital of {{ country }}?\",\n... inputs=[country_property],\n... outputs=[capital_property],\n... )\n>>> flow = Flow(\n... name=\"Get the country's capital flow\",\n... start_node=start_node,\n... nodes=[start_node, llm_node, end_node],\n... control_flow_connections=[\n... ControlFlowEdge(name=\"start_to_llm\", from_node=start_node, to_node=llm_node),\n... ControlFlowEdge(name=\"llm_to_end\", from_node=llm_node, to_node=end_node),\n... ],\n... data_flow_connections=[\n... DataFlowEdge(\n... name=\"country_edge\",\n... source_node=start_node,\n... source_output=\"country\",\n... destination_node=llm_node,\n... destination_input=\"country\",\n... ),\n... DataFlowEdge(\n... name=\"capital_edge\",\n... source_node=llm_node,\n... source_output=\"capital\",\n... destination_node=end_node,\n... destination_input=\"capital\"\n... ),\n... ],\n... )", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "inputs": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Inputs" + }, + "outputs": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Outputs" + }, + "branches": { + "items": { + "type": "string" + }, + "title": "Branches", + "type": "array" + }, + "llm_config": { + "$ref": "#/$defs/LlmConfig" + }, + "prompt_template": { + "title": "Prompt Template", + "type": "string" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "LlmNode" + } + }, + "required": [ + "llm_config", + "name", + "prompt_template" + ], + "title": "LlmNode", + "type": "object", + "x-abstract-component": false + }, + "BaseMCPTool": { + "additionalProperties": false, + "description": "Class for tools exposed by MCP servers", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "inputs": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Inputs" + }, + "outputs": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Outputs" + }, + "requires_confirmation": { + "default": false, + "title": "Requires Confirmation", + "type": "boolean" + }, + "client_transport": { + "$ref": "#/$defs/ClientTransport" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "MCPTool" + } + }, + "required": [ + "client_transport", + "name" + ], + "title": "MCPTool", + "type": "object", + "x-abstract-component": false + }, + "BaseMCPToolBox": { + "additionalProperties": false, + "description": "Class to dynamically expose a list of tools from a MCP Server.", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "client_transport": { + "$ref": "#/$defs/ClientTransport" + }, + "tool_filter": { + "anyOf": [ + { + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "$ref": "#/$defs/MCPToolSpec" + } + ] + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Tool Filter" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "MCPToolBox" + } + }, + "required": [ + "client_transport", + "name" + ], + "title": "MCPToolBox", + "type": "object", + "x-abstract-component": false + }, + "BaseMCPToolSpec": { + "additionalProperties": false, + "description": "Specification of MCP tool", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "inputs": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Inputs" + }, + "outputs": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Outputs" + }, + "requires_confirmation": { + "default": false, + "title": "Requires Confirmation", + "type": "boolean" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "MCPToolSpec" + } + }, + "required": [ + "name" + ], + "title": "MCPToolSpec", + "type": "object", + "x-abstract-component": false + }, + "BaseMTlsOracleDatabaseConnectionConfig": { + "additionalProperties": false, + "description": "Mutual-TLS Connection Configuration to Oracle Database.", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "user": { + "title": "User", + "type": "string" + }, + "password": { + "title": "Password", + "type": "string" + }, + "dsn": { + "title": "Dsn", + "type": "string" + }, + "config_dir": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Config Dir" + }, + "protocol": { + "default": "tcps", + "enum": [ + "tcp", + "tcps" + ], + "title": "Protocol", + "type": "string" + }, + "wallet_location": { + "title": "Wallet Location", + "type": "string" + }, + "wallet_password": { + "title": "Wallet Password", + "type": "string" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "MTlsOracleDatabaseConnectionConfig" + } + }, + "required": [ + "dsn", + "name", + "password", + "user", + "wallet_location", + "wallet_password" + ], + "title": "MTlsOracleDatabaseConnectionConfig", + "type": "object", + "x-abstract-component": false + }, + "BaseManagerWorkers": { + "additionalProperties": false, + "description": "Defines a ``ManagerWorkers`` conversational component.\n\nA ``ManagerWorkers`` is a multi-agent conversational component in which a group manager\nassigns tasks to the workers. The group manager and workers can be instantiated from\nany ``AgenticComponent`` type.\n\nExamples\n--------\n>>> from pyagentspec.agent import Agent\n>>> from pyagentspec.managerworkers import ManagerWorkers\n>>> manager_agent = Agent(\n... name=\"manager_agent\",\n... description=\"Agent that manages a group of math agents\",\n... llm_config=llm_config,\n... system_prompt=\"You are the manager of a group of math agents\"\n... )\n>>> multiplication_agent = Agent(\n... name=\"multiplication_agent\",\n... description=\"Agent that can do multiplication\",\n... llm_config=llm_config,\n... system_prompt=\"You can do multiplication.\"\n... )\n>>> division_agent = Agent(\n... name=\"division_agent\",\n... description=\"Agent that can do division\",\n... llm_config=llm_config,\n... system_prompt=\"You can do division.\"\n... )\n>>> group = ManagerWorkers(\n... name=\"managerworkers\",\n... group_manager=manager_agent,\n... workers=[multiplication_agent, division_agent],\n... )", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "inputs": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Inputs" + }, + "outputs": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Outputs" + }, + "group_manager": { + "$ref": "#/$defs/AgenticComponent" + }, + "workers": { + "items": { + "$ref": "#/$defs/AgenticComponent" + }, + "title": "Workers", + "type": "array" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "ManagerWorkers" + } + }, + "required": [ + "group_manager", + "name", + "workers" + ], + "title": "ManagerWorkers", + "type": "object", + "x-abstract-component": false + }, + "BaseMapNode": { + "additionalProperties": false, + "description": "The map node executes a subflow on each element of a given input as part of a flow.\n\n- **Inputs**\n Inferred from the inner structure. It's the sets of inputs\n required by the StartNode of the inner flow.\n The names of the inputs will be the ones of the inner flow,\n complemented with the ``iterated_`` prefix. Their type is\n ``Union[inner_type, List[inner_type]]``, where ``inner_type``\n is the type of the respective input in the inner flow.\n\n If None is given, ``pyagentspec`` infers input properties directly from the inner flow,\n specifying title and type according to the rules defined above.\n\n- **Outputs**\n Inferred from the inner structure. It's the union of the\n sets of outputs exposed by the EndNodes of the inner flow,\n combined with the reducer method of each output.\n The names of the outputs will be the ones of the inner flow,\n complemented with the ``collected_`` prefix. Their type depends\n on the ``reduce`` method specified for that output:\n\n - ``List`` of the respective output type in case of ``append``\n - same type of the respective output type in case of ``sum``, ``avg``\n\n If None is given, ``pyagentspec`` infers outputs by exposing\n an output property for each entry in the ``reducers`` dictionary, specifying title\n and type according to the rules defined above.\n\n- **Branches**\n One, the default next.\n\nExamples\n--------\nIn this example we will create a flow that returns\nan L2-normalized version of a given list of numbers.\n\n>>> from pyagentspec.property import Property\n>>> from pyagentspec.flows.edges import ControlFlowEdge, DataFlowEdge\n>>> from pyagentspec.flows.flow import Flow\n>>> from pyagentspec.flows.nodes import EndNode, StartNode, MapNode, ToolNode\n>>> from pyagentspec.tools import ServerTool\n\nFirst, we define a MapNode that returns the square of all the elements in a list.\nIt will be used to compute the L2-norm.\n\n>>> x_property = Property(json_schema={\"title\": \"x\", \"type\": \"number\"})\n>>> x_square_property = Property(\n... json_schema={\"title\": \"x_square\", \"type\": \"number\"}\n... )\n>>> square_tool = ServerTool(\n... name=\"compute_square_tool\",\n... description=\"Computes the square of a number\",\n... inputs=[x_property],\n... outputs=[x_square_property],\n... )\n>>> list_of_x_property = Property(\n... json_schema={\"title\": \"x_list\", \"type\": \"array\", \"items\": {\"type\": \"number\"}}\n... )\n>>> start_node = StartNode(name=\"start\", inputs=[x_property])\n>>> end_node = EndNode(name=\"end\", outputs=[x_square_property])\n>>> square_tool_node = ToolNode(name=\"square tool node\", tool=square_tool)\n>>> square_number_flow = Flow(\n... name=\"Square number flow\",\n... start_node=start_node,\n... nodes=[start_node, square_tool_node, end_node],\n... control_flow_connections=[\n... ControlFlowEdge(\n... name=\"start_to_tool\", from_node=start_node, to_node=square_tool_node\n... ),\n... ControlFlowEdge(\n... name=\"tool_to_end\", from_node=square_tool_node, to_node=end_node\n... ),\n... ],\n... data_flow_connections=[\n... DataFlowEdge(\n... name=\"x_edge\",\n... source_node=start_node,\n... source_output=\"x\",\n... destination_node=square_tool_node,\n... destination_input=\"x\",\n... ),\n... DataFlowEdge(\n... name=\"x_square_edge\",\n... source_node=square_tool_node,\n... source_output=\"x_square\",\n... destination_node=end_node,\n... destination_input=\"x_square\",\n... ),\n... ],\n... )\n>>> list_of_x_square_property = Property(\n... json_schema={\"title\": \"x_square_list\", \"type\": \"array\", \"items\": {\"type\": \"number\"}}\n... )\n>>> square_numbers_map_node = MapNode(\n... name=\"square number map node\",\n... subflow=square_number_flow,\n... )\n\nNow we define the MapNode responsible for normalizing the given list of input numbers.\nThe denominator is the same for all of the numbers,\nwe are going to map only the numerators (i.e., the input numbers).\n\n>>> numerator_property = Property(\n... json_schema={\"title\": \"numerator\", \"type\": \"number\"}\n... )\n>>> denominator_property = Property(\n... json_schema={\"title\": \"denominator\", \"type\": \"number\"}\n... )\n>>> result_property = Property(\n... json_schema={\"title\": \"result\", \"type\": \"number\"}\n... )\n>>> division_tool = ServerTool(\n... name=\"division_tool\",\n... description=\"Computes the ratio between two numbers\",\n... inputs=[numerator_property, denominator_property],\n... outputs=[result_property],\n... )\n>>> start_node = StartNode(name=\"start\", inputs=[numerator_property, denominator_property])\n>>> end_node = EndNode(name=\"end\", outputs=[result_property])\n>>> divide_node = ToolNode(name=\"divide node\", tool=division_tool)\n>>> normalize_flow = Flow(\n... name=\"Normalize flow\",\n... start_node=start_node,\n... nodes=[start_node, divide_node, end_node],\n... control_flow_connections=[\n... ControlFlowEdge(name=\"start_to_tool\", from_node=start_node, to_node=divide_node),\n... ControlFlowEdge(name=\"tool_to_end\", from_node=divide_node, to_node=end_node),\n... ],\n... data_flow_connections=[\n... DataFlowEdge(\n... name=\"numerator_edge\",\n... source_node=start_node,\n... source_output=\"numerator\",\n... destination_node=divide_node,\n... destination_input=\"numerator\",\n... ),\n... DataFlowEdge(\n... name=\"denominator_edge\",\n... source_node=start_node,\n... source_output=\"denominator\",\n... destination_node=divide_node,\n... destination_input=\"denominator\",\n... ),\n... DataFlowEdge(\n... name=\"result_edge\",\n... source_node=divide_node,\n... source_output=\"result\",\n... destination_node=end_node,\n... destination_input=\"result\",\n... ),\n... ],\n... )\n\nFinally, we define the overall flow:\n\n- The list of inputs is squared\n- The squared list is summed and root squared\n- The list of inputs is normalized based on the outcomes of the previous 2 steps\n\n>>> squared_sum_property = Property(\n... json_schema={\"title\": \"squared_sum\", \"type\": \"number\"}\n... )\n>>> normalized_list_of_x_property = Property(\n... json_schema={\n... \"title\": \"x_list_normalized\",\n... \"type\": \"array\",\n... \"items\": {\"type\": \"number\"},\n... }\n... )\n>>> normalize_map_node = MapNode(\n... name=\"normalize map node\",\n... subflow=normalize_flow,\n... )\n>>> squared_sum_tool = ServerTool(\n... name=\"squared_sum_tool\",\n... description=\"Computes the squared sum of a list of numbers\",\n... inputs=[list_of_x_property],\n... outputs=[squared_sum_property],\n... )\n>>> start_node = StartNode(name=\"start\", inputs=[list_of_x_property])\n>>> end_node = EndNode(name=\"end\", outputs=[normalized_list_of_x_property])\n>>> squared_sum_tool_node = ToolNode(name=\"squared sum tool node\", tool=squared_sum_tool)\n>>> flow = Flow(\n... name=\"L2 normalize flow\",\n... start_node=start_node,\n... nodes=[\n... start_node,\n... square_numbers_map_node,\n... squared_sum_tool_node,\n... normalize_map_node,\n... end_node,\n... ],\n... control_flow_connections=[\n... ControlFlowEdge(\n... name=\"start_to_square_numbers\",\n... from_node=start_node,\n... to_node=square_numbers_map_node\n... ),\n... ControlFlowEdge(\n... name=\"square_numbers_to_squared_sum_tool\",\n... from_node=square_numbers_map_node,\n... to_node=squared_sum_tool_node\n... ),\n... ControlFlowEdge(\n... name=\"squared_sum_tool_to_normalize\",\n... from_node=squared_sum_tool_node,\n... to_node=normalize_map_node\n... ),\n... ControlFlowEdge(\n... name=\"normalize_to_end\",\n... from_node=normalize_map_node,\n... to_node=end_node\n... ),\n... ],\n... data_flow_connections=[\n... DataFlowEdge(\n... name=\"list_of_x_edge\",\n... source_node=start_node,\n... source_output=\"x_list\",\n... destination_node=square_numbers_map_node,\n... destination_input=\"iterated_x\",\n... ),\n... DataFlowEdge(\n... name=\"x_square_list_edge\",\n... source_node=square_numbers_map_node,\n... source_output=\"collected_x_square\",\n... destination_node=squared_sum_tool_node,\n... destination_input=\"x_list\",\n... ),\n... DataFlowEdge(\n... name=\"numerator_edge\",\n... source_node=start_node,\n... source_output=\"x_list\",\n... destination_node=normalize_map_node,\n... destination_input=\"iterated_numerator\",\n... ),\n... DataFlowEdge(\n... name=\"denominator_edge\",\n... source_node=squared_sum_tool_node,\n... source_output=\"squared_sum\",\n... destination_node=normalize_map_node,\n... destination_input=\"iterated_denominator\",\n... ),\n... DataFlowEdge(\n... name=\"x_list_normalized_edge\",\n... source_node=normalize_map_node,\n... source_output=\"collected_result\",\n... destination_node=end_node,\n... destination_input=\"x_list_normalized\",\n... ),\n... ],\n... )", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "inputs": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Inputs" + }, + "outputs": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Outputs" + }, + "branches": { + "items": { + "type": "string" + }, + "title": "Branches", + "type": "array" + }, + "subflow": { + "$ref": "#/$defs/Flow" + }, + "reducers": { + "anyOf": [ + { + "additionalProperties": { + "$ref": "#/$defs/ReductionMethod" + }, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reducers" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "MapNode" + } + }, + "required": [ + "name", + "subflow" + ], + "title": "MapNode", + "type": "object", + "x-abstract-component": false + }, + "BaseMessageSummarizationTransform": { + "additionalProperties": false, + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "llm": { + "$ref": "#/$defs/LlmConfig" + }, + "max_message_size": { + "default": 20000, + "title": "Max Message Size", + "type": "integer" + }, + "summarization_instructions": { + "default": "Please make a summary of this message. Include relevant information and keep it short. Your response will replace the message, so just output the summary directly, no introduction needed.", + "title": "Summarization Instructions", + "type": "string" + }, + "summarized_message_template": { + "default": "Summarized message: {{summary}}", + "title": "Summarized Message Template", + "type": "string" + }, + "datastore": { + "anyOf": [ + { + "type": "null" + }, + { + "$ref": "#/$defs/Datastore" + } + ], + "default": null + }, + "max_cache_size": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": 10000, + "title": "Max Cache Size" + }, + "max_cache_lifetime": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": 14400, + "title": "Max Cache Lifetime" + }, + "cache_collection_name": { + "default": "summarized_messages_cache", + "title": "Cache Collection Name", + "type": "string" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "MessageSummarizationTransform" + } + }, + "required": [ + "llm", + "name" + ], + "title": "MessageSummarizationTransform", + "type": "object", + "x-abstract-component": false + }, + "BaseMessageTransform": { + "anyOf": [ + { + "$ref": "#/$defs/ConversationSummarizationTransform" + }, + { + "$ref": "#/$defs/MessageSummarizationTransform" + } + ], + "x-abstract-component": true + }, + "BaseNode": { + "anyOf": [ + { + "$ref": "#/$defs/AgentNode" + }, + { + "$ref": "#/$defs/ApiNode" + }, + { + "$ref": "#/$defs/BranchingNode" + }, + { + "$ref": "#/$defs/EndNode" + }, + { + "$ref": "#/$defs/FlowNode" + }, + { + "$ref": "#/$defs/InputMessageNode" + }, + { + "$ref": "#/$defs/LlmNode" + }, + { + "$ref": "#/$defs/MapNode" + }, + { + "$ref": "#/$defs/OutputMessageNode" + }, + { + "$ref": "#/$defs/ParallelFlowNode" + }, + { + "$ref": "#/$defs/ParallelMapNode" + }, + { + "$ref": "#/$defs/StartNode" + }, + { + "$ref": "#/$defs/ToolNode" + } + ], + "x-abstract-component": true + }, + "BaseOciAgent": { + "additionalProperties": false, + "description": "An agent is a component that can do several rounds of conversation to solve a task.\n\nThe agent is defined on the OCI console and this is only a wrapper to connect to it.\nIt can be executed by itself, or be executed in a flow using an AgentNode.", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "inputs": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Inputs" + }, + "outputs": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Outputs" + }, + "agent_endpoint_id": { + "title": "Agent Endpoint Id", + "type": "string" + }, + "client_config": { + "$ref": "#/$defs/OciClientConfig" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "OciAgent" + } + }, + "required": [ + "agent_endpoint_id", + "client_config", + "name" + ], + "title": "OciAgent", + "type": "object", + "x-abstract-component": false + }, + "BaseOciClientConfig": { + "anyOf": [ + { + "$ref": "#/$defs/OciClientConfigWithApiKey" + }, + { + "$ref": "#/$defs/OciClientConfigWithInstancePrincipal" + }, + { + "$ref": "#/$defs/OciClientConfigWithResourcePrincipal" + }, + { + "$ref": "#/$defs/OciClientConfigWithSecurityToken" + } + ], + "x-abstract-component": true + }, + "BaseOciClientConfigWithApiKey": { + "additionalProperties": false, + "description": "OCI client config class for authentication using API_KEY and a config file.", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "service_endpoint": { + "title": "Service Endpoint", + "type": "string" + }, + "auth_type": { + "const": "API_KEY", + "default": "API_KEY", + "title": "Auth Type", + "type": "string" + }, + "auth_profile": { + "title": "Auth Profile", + "type": "string" + }, + "auth_file_location": { + "title": "Auth File Location", + "type": "string" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "OciClientConfigWithApiKey" + } + }, + "required": [ + "auth_file_location", + "auth_profile", + "name", + "service_endpoint" + ], + "title": "OciClientConfigWithApiKey", + "type": "object", + "x-abstract-component": false + }, + "BaseOciClientConfigWithInstancePrincipal": { + "additionalProperties": false, + "description": "OCI client config class for authentication using INSTANCE_PRINCIPAL.", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "service_endpoint": { + "title": "Service Endpoint", + "type": "string" + }, + "auth_type": { + "const": "INSTANCE_PRINCIPAL", + "default": "INSTANCE_PRINCIPAL", + "title": "Auth Type", + "type": "string" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "OciClientConfigWithInstancePrincipal" + } + }, + "required": [ + "name", + "service_endpoint" + ], + "title": "OciClientConfigWithInstancePrincipal", + "type": "object", + "x-abstract-component": false + }, + "BaseOciClientConfigWithResourcePrincipal": { + "additionalProperties": false, + "description": "OCI client config class for authentication using RESOURCE_PRINCIPAL.", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "service_endpoint": { + "title": "Service Endpoint", + "type": "string" + }, + "auth_type": { + "const": "RESOURCE_PRINCIPAL", + "default": "RESOURCE_PRINCIPAL", + "title": "Auth Type", + "type": "string" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "OciClientConfigWithResourcePrincipal" + } + }, + "required": [ + "name", + "service_endpoint" + ], + "title": "OciClientConfigWithResourcePrincipal", + "type": "object", + "x-abstract-component": false + }, + "BaseOciClientConfigWithSecurityToken": { + "additionalProperties": false, + "description": "OCI client config class for authentication using SECURITY_TOKEN.", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "service_endpoint": { + "title": "Service Endpoint", + "type": "string" + }, + "auth_type": { + "const": "SECURITY_TOKEN", + "default": "SECURITY_TOKEN", + "title": "Auth Type", + "type": "string" + }, + "auth_profile": { + "title": "Auth Profile", + "type": "string" + }, + "auth_file_location": { + "title": "Auth File Location", + "type": "string" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "OciClientConfigWithSecurityToken" + } + }, + "required": [ + "auth_file_location", + "auth_profile", + "name", + "service_endpoint" + ], + "title": "OciClientConfigWithSecurityToken", + "type": "object", + "x-abstract-component": false + }, + "BaseOciGenAiConfig": { + "additionalProperties": false, + "description": "Class to configure a connection to a OCI GenAI hosted model.\n\nRequires to specify the model id and the client configuration to the OCI GenAI service.", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "default_generation_parameters": { + "anyOf": [ + { + "type": "null" + }, + { + "$ref": "#/$defs/LlmGenerationConfig" + } + ], + "default": null + }, + "model_id": { + "title": "Model Id", + "type": "string" + }, + "compartment_id": { + "title": "Compartment Id", + "type": "string" + }, + "serving_mode": { + "$ref": "#/$defs/ServingMode", + "default": "ON_DEMAND" + }, + "provider": { + "anyOf": [ + { + "type": "null" + }, + { + "$ref": "#/$defs/ModelProvider" + } + ], + "default": null + }, + "client_config": { + "$ref": "#/$defs/OciClientConfig" + }, + "api_type": { + "$ref": "#/$defs/OciAPIType", + "default": "oci" + }, + "conversation_store_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Conversation Store Id" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "OciGenAiConfig" + } + }, + "required": [ + "client_config", + "compartment_id", + "model_id", + "name" + ], + "title": "OciGenAiConfig", + "type": "object", + "x-abstract-component": false + }, + "BaseOllamaConfig": { + "additionalProperties": false, + "description": "Class to configure a connection to a local model ran with Ollama.\n\nRequires to specify the url and port at which the model is running.", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "default_generation_parameters": { + "anyOf": [ + { + "type": "null" + }, + { + "$ref": "#/$defs/LlmGenerationConfig" + } + ], + "default": null + }, + "url": { + "title": "Url", + "type": "string" + }, + "model_id": { + "title": "Model Id", + "type": "string" + }, + "api_type": { + "$ref": "#/$defs/OpenAIAPIType", + "default": "chat_completions" + }, + "api_key": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Api Key" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "OllamaConfig" + } + }, + "required": [ + "model_id", + "name", + "url" + ], + "title": "OllamaConfig", + "type": "object", + "x-abstract-component": false + }, + "BaseOpenAiCompatibleConfig": { + "anyOf": [ + { + "$ref": "#/$defs/OllamaConfig" + }, + { + "$ref": "#/$defs/VllmConfig" + }, + { + "additionalProperties": false, + "description": "Class to configure a connection to an LLM that is compatible with OpenAI completions APIs.\n\nRequires to specify the url of the APIs to contact.", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "default_generation_parameters": { + "anyOf": [ + { + "type": "null" + }, + { + "$ref": "#/$defs/LlmGenerationConfig" + } + ], + "default": null + }, + "url": { + "title": "Url", + "type": "string" + }, + "model_id": { + "title": "Model Id", + "type": "string" + }, + "api_type": { + "$ref": "#/$defs/OpenAIAPIType", + "default": "chat_completions" + }, + "api_key": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Api Key" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "OpenAiCompatibleConfig" + } + }, + "required": [ + "model_id", + "name", + "url" + ], + "title": "OpenAiCompatibleConfig", + "type": "object" + } + ], + "x-abstract-component": false + }, + "BaseOpenAiConfig": { + "additionalProperties": false, + "description": "Class to configure a connection to a OpenAI LLM.\n\nRequires to specify the identity of the model to use.", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "default_generation_parameters": { + "anyOf": [ + { + "type": "null" + }, + { + "$ref": "#/$defs/LlmGenerationConfig" + } + ], + "default": null + }, + "model_id": { + "title": "Model Id", + "type": "string" + }, + "api_type": { + "$ref": "#/$defs/OpenAIAPIType", + "default": "chat_completions" + }, + "api_key": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Api Key" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "OpenAiConfig" + } + }, + "required": [ + "model_id", + "name" + ], + "title": "OpenAiConfig", + "type": "object", + "x-abstract-component": false + }, + "BaseOracleDatabaseConnectionConfig": { + "anyOf": [ + { + "$ref": "#/$defs/MTlsOracleDatabaseConnectionConfig" + }, + { + "$ref": "#/$defs/TlsOracleDatabaseConnectionConfig" + } + ], + "x-abstract-component": true + }, + "BaseOracleDatabaseDatastore": { + "additionalProperties": false, + "description": "Datastore that uses Oracle Database as the storage mechanism.", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "datastore_schema": { + "additionalProperties": { + "$ref": "#/$defs/Property" + }, + "title": "Datastore Schema", + "type": "object" + }, + "connection_config": { + "$ref": "#/$defs/OracleDatabaseConnectionConfig" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "OracleDatabaseDatastore" + } + }, + "required": [ + "connection_config", + "datastore_schema", + "name" + ], + "title": "OracleDatabaseDatastore", + "type": "object", + "x-abstract-component": false + }, + "BaseOutputMessageNode": { + "additionalProperties": false, + "description": "This node appends an agent message to the ongoing flow conversation.\n\n- **Inputs**\n One per variable in the message.\n- **Outputs**\n No outputs.\n- **Branches**\n One, the default next.\n\nExamples\n--------\n>>> from pyagentspec.flows.edges import ControlFlowEdge, DataFlowEdge\n>>> from pyagentspec.flows.flow import Flow\n>>> from pyagentspec.flows.nodes import StartNode, EndNode, InputMessageNode, OutputMessageNode, LlmNode\n>>> from pyagentspec.property import StringProperty\n>>> start_node = StartNode(name=\"start\")\n>>> prompt_node = OutputMessageNode(name=\"ask_input\", message=\"What is the paragraph you want to rephrase?\")\n>>> input_node = InputMessageNode(name=\"user_input\", outputs=[StringProperty(title=\"user_input\")])\n>>> llm_node = LlmNode(\n... name=\"rephrase\",\n... llm_config=llm_config,\n... prompt_template=\"Rephrase {{user_input}}\",\n... outputs=[StringProperty(title=\"rephrased_user_input\")],\n... )\n>>> output_node = OutputMessageNode(name=\"ask_input\", message=\"{{rephrased_user_input}}\")\n>>> end_node = EndNode(name=\"end\")\n>>> flow = Flow(\n... name=\"rephrase_paragraph_flow\",\n... start_node=start_node,\n... nodes=[start_node, prompt_node, input_node, llm_node, output_node, end_node],\n... control_flow_connections=[\n... ControlFlowEdge(name=\"ce1\", from_node=start_node, to_node=prompt_node),\n... ControlFlowEdge(name=\"ce2\", from_node=prompt_node, to_node=input_node),\n... ControlFlowEdge(name=\"ce3\", from_node=input_node, to_node=llm_node),\n... ControlFlowEdge(name=\"ce4\", from_node=llm_node, to_node=output_node),\n... ControlFlowEdge(name=\"ce5\", from_node=output_node, to_node=end_node),\n... ],\n... data_flow_connections=[\n... DataFlowEdge(\n... name=\"de1\",\n... source_node=input_node,\n... source_output=\"user_input\",\n... destination_node=llm_node,\n... destination_input=\"user_input\",\n... ),\n... DataFlowEdge(\n... name=\"de2\",\n... source_node=llm_node,\n... source_output=\"rephrased_user_input\",\n... destination_node=output_node,\n... destination_input=\"rephrased_user_input\",\n... ),\n... ]\n... )", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "inputs": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Inputs" + }, + "outputs": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Outputs" + }, + "branches": { + "items": { + "type": "string" + }, + "title": "Branches", + "type": "array" + }, + "message": { + "title": "Message", + "type": "string" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "OutputMessageNode" + } + }, + "required": [ + "message", + "name" + ], + "title": "OutputMessageNode", + "type": "object", + "x-abstract-component": false + }, + "BaseParallelFlowNode": { + "additionalProperties": false, + "description": "The parallel flow node executes multiple subflows in parallel.\n\n- **Inputs**\n Inferred from the inner structure. It's the union of the sets of inputs of the inner flows.\n Inputs of different inner flows that have the same name are merged if they have the same type.\n\n- **Outputs**\n Inferred from the inner structure. It's the union of the outputs of the inner flows.\n Outputs of different inner flows that have the same name are not allowed.\n\n- **Branches**\n One, the default next.\n\nExamples\n--------", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "inputs": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Inputs" + }, + "outputs": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Outputs" + }, + "branches": { + "items": { + "type": "string" + }, + "title": "Branches", + "type": "array" + }, + "subflows": { + "items": { + "$ref": "#/$defs/Flow" + }, + "title": "Subflows", + "type": "array" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "ParallelFlowNode" + } + }, + "required": [ + "name" + ], + "title": "ParallelFlowNode", + "type": "object", + "x-abstract-component": false + }, + "BaseParallelMapNode": { + "additionalProperties": false, + "description": "The parallel map node executes a subflow on each element of a given input in a parallel manner.\n\n- **Inputs**\n Inferred from the inner structure. It's the sets of inputs\n required by the StartNode of the inner flow.\n The names of the inputs will be the ones of the inner flow,\n complemented with the ``iterated_`` prefix. Their type is\n ``Union[inner_type, List[inner_type]]``, where ``inner_type``\n is the type of the respective input in the inner flow.\n\n If None is given, ``pyagentspec`` infers input properties directly from the inner flow,\n specifying title and type according to the rules defined above.\n\n- **Outputs**\n Inferred from the inner structure. It's the union of the\n sets of outputs exposed by the EndNodes of the inner flow,\n combined with the reducer method of each output.\n The names of the outputs will be the ones of the inner flow,\n complemented with the ``collected_`` prefix. Their type depends\n on the ``reduce`` method specified for that output:\n\n - ``List`` of the respective output type in case of ``append``\n - same type of the respective output type in case of ``sum``, ``avg``\n\n If None is given, ``pyagentspec`` infers outputs by exposing\n an output property for each entry in the ``reducers`` dictionary, specifying title\n and type according to the rules defined above.\n\n- **Branches**\n One, the default next.\n\nExamples\n--------\nIn this example we create a flow that generates a summary of the given articles that talk about LLMs.\n\n>>> from pyagentspec.property import BooleanProperty, StringProperty, ListProperty\n>>> from pyagentspec.flows.edges import ControlFlowEdge, DataFlowEdge\n>>> from pyagentspec.flows.flow import Flow\n>>> from pyagentspec.flows.nodes import EndNode, StartNode, ParallelMapNode, LlmNode, BranchingNode\n>>> from pyagentspec.flows.node import Node\n\nFirst we define the flow that determines if an article talks about LLMs or not:\n\n- if it does, we return it, so that we will use it for our summary\n- if it does not, we don't return its text\n\n>>> def create_data_flow_edge(source_node: Node, destination_node: Node, property_name: str) -> DataFlowEdge:\n... return DataFlowEdge(\n... name=f\"{source_node.name}_{destination_node.name}_{property_name}_edge\",\n... source_node=source_node,\n... source_output=property_name,\n... destination_node=destination_node,\n... destination_input=property_name,\n... )\n>>>\n>>> article_property = StringProperty(title=\"article\")\n>>> is_llm_article_property = StringProperty(title=\"is_article\")\n>>> llm_node = LlmNode(\n... name=\"check_if_article_talks_about_llms_node\",\n... prompt_template=\"Look at this article: {{article}}. Does it talk about LLMs? Answer `yes` or `no`.\",\n... llm_config=llm_config,\n... inputs=[article_property],\n... outputs=[is_llm_article_property],\n... )\n>>>\n>>> branching_node = BranchingNode(\n... name=\"decide_if_we_should_return_the_article\",\n... mapping={\"yes\": \"yes\"},\n... inputs=[is_llm_article_property],\n... )\n>>>\n>>> start_node = StartNode(name=\"start\", inputs=[article_property])\n>>> end_node_with_output = EndNode(name=\"end_with_output\", outputs=[article_property])\n>>> end_node_without_output = EndNode(name=\"end_without_output\", outputs=[])\n>>>\n>>> check_if_article_is_about_llm_flow = Flow(\n... name=\"is_article_about_llm_flow\",\n... start_node=start_node,\n... nodes=[start_node, llm_node, end_node_with_output, end_node_without_output, branching_node],\n... control_flow_connections=[\n... ControlFlowEdge(name=\"start_to_llm\", from_node=start_node, to_node=llm_node),\n... ControlFlowEdge(name=\"llm_to_branching\", from_node=llm_node, to_node=branching_node),\n... ControlFlowEdge(name=\"branching_to_end_with\", from_node=branching_node, to_node=end_node_with_output),\n... ControlFlowEdge(name=\"branching_to_end_without\", from_node=branching_node, to_node=end_node_without_output),\n... ],\n... data_flow_connections=[\n... create_data_flow_edge(start_node, llm_node, article_property.title),\n... create_data_flow_edge(llm_node, branching_node, is_llm_article_property.title),\n... create_data_flow_edge(start_node, end_node_with_output, article_property.title),\n... ],\n... outputs=[StringProperty(title=article_property.title, default=\"\")],\n... )\n\nWe put this flow into a ``ParallelMapNode``, so that we can perform the check\nin parallel on multiple articles at the same time.\n\n>>> list_of_articles_property = ListProperty(title=\"iterated_article\", item_type=article_property)\n>>> list_of_articles_about_llm_property = ListProperty(title=\"collected_article\", item_type=article_property)\n>>> parallel_article_check_node = ParallelMapNode(\n... name=\"parallel_check_if_articles_talk_about_llms_node\",\n... subflow=check_if_article_is_about_llm_flow,\n... inputs=[list_of_articles_property],\n... outputs=[list_of_articles_about_llm_property],\n... )\n\nFinally, we create the flow that takes the list of articles as input, filters them through the\n``ParallelMapNode`` we have just created, and we generate the summary with the remaining articles.\n\n>>> summary_property = StringProperty(title=\"summary\")\n>>> summary_llm_node = LlmNode(\n... name=\"generate_summary_of_llm_articles_node\",\n... prompt_template=\"Summarize the following articles that talk about LLMs: {{collected_article}}\",\n... llm_config=llm_config,\n... inputs=[list_of_articles_about_llm_property],\n... outputs=[summary_property],\n... )\n>>> start_node = StartNode(name=\"start\", inputs=[list_of_articles_property])\n>>> end_node = EndNode(name=\"end\", outputs=[summary_property])\n>>> generate_summary_of_llm_articles_flow = Flow(\n... name=\"Generate summary of articles about LLMs flow\",\n... start_node=start_node,\n... nodes=[start_node, parallel_article_check_node, summary_llm_node, end_node],\n... control_flow_connections=[\n... ControlFlowEdge(name=\"start_to_parallelmap\", from_node=start_node, to_node=parallel_article_check_node),\n... ControlFlowEdge(name=\"parallelmap_to_llm\", from_node=parallel_article_check_node, to_node=summary_llm_node),\n... ControlFlowEdge(name=\"llm_to_end\", from_node=summary_llm_node, to_node=end_node),\n... ],\n... data_flow_connections=[\n... create_data_flow_edge(start_node, parallel_article_check_node, list_of_articles_property.title),\n... create_data_flow_edge(parallel_article_check_node, summary_llm_node, list_of_articles_about_llm_property.title),\n... create_data_flow_edge(summary_llm_node, end_node, summary_property.title),\n... ],\n... )", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "inputs": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Inputs" + }, + "outputs": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Outputs" + }, + "branches": { + "items": { + "type": "string" + }, + "title": "Branches", + "type": "array" + }, + "subflow": { + "$ref": "#/$defs/Flow" + }, + "reducers": { + "anyOf": [ + { + "additionalProperties": { + "$ref": "#/$defs/ReductionMethod" + }, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reducers" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "ParallelMapNode" + } + }, + "required": [ + "name", + "subflow" + ], + "title": "ParallelMapNode", + "type": "object", + "x-abstract-component": false + }, + "BasePostgresDatabaseConnectionConfig": { + "anyOf": [ + { + "$ref": "#/$defs/TlsPostgresDatabaseConnectionConfig" + } + ], + "x-abstract-component": true + }, + "BasePostgresDatabaseDatastore": { + "additionalProperties": false, + "description": "Datastore that uses PostgreSQL Database as the storage mechanism.", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "datastore_schema": { + "additionalProperties": { + "$ref": "#/$defs/Property" + }, + "title": "Datastore Schema", + "type": "object" + }, + "connection_config": { + "$ref": "#/$defs/PostgresDatabaseConnectionConfig" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "PostgresDatabaseDatastore" + } + }, + "required": [ + "connection_config", + "datastore_schema", + "name" + ], + "title": "PostgresDatabaseDatastore", + "type": "object", + "x-abstract-component": false + }, + "BaseRemoteAgent": { + "anyOf": [ + { + "$ref": "#/$defs/A2AAgent" + }, + { + "$ref": "#/$defs/OciAgent" + } + ], + "x-abstract-component": true + }, + "BaseRemoteTool": { + "additionalProperties": false, + "description": "A tool that is run remotely and called through REST.", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "inputs": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Inputs" + }, + "outputs": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Outputs" + }, + "requires_confirmation": { + "default": false, + "title": "Requires Confirmation", + "type": "boolean" + }, + "url": { + "title": "Url", + "type": "string" + }, + "http_method": { + "title": "Http Method", + "type": "string" + }, + "api_spec_uri": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Api Spec Uri" + }, + "data": { + "additionalProperties": true, + "title": "Data", + "type": "object" + }, + "query_params": { + "additionalProperties": true, + "title": "Query Params", + "type": "object" + }, + "headers": { + "additionalProperties": true, + "title": "Headers", + "type": "object" + }, + "sensitive_headers": { + "additionalProperties": true, + "title": "Sensitive Headers", + "type": "object" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "RemoteTool" + } + }, + "required": [ + "http_method", + "name", + "url" + ], + "title": "RemoteTool", + "type": "object", + "x-abstract-component": false + }, + "BaseRemoteTransport": { + "anyOf": [ + { + "$ref": "#/$defs/SSETransport" + }, + { + "$ref": "#/$defs/SSEmTLSTransport" + }, + { + "$ref": "#/$defs/StreamableHTTPTransport" + }, + { + "$ref": "#/$defs/StreamableHTTPmTLSTransport" + } + ], + "x-abstract-component": true + }, + "BaseSSETransport": { + "anyOf": [ + { + "$ref": "#/$defs/SSEmTLSTransport" + }, + { + "additionalProperties": false, + "description": "Transport implementation that connects to an MCP server via Server-Sent Events.", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "session_parameters": { + "$ref": "#/$defs/SessionParameters" + }, + "url": { + "title": "Url", + "type": "string" + }, + "headers": { + "anyOf": [ + { + "additionalProperties": { + "type": "string" + }, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Headers" + }, + "sensitive_headers": { + "anyOf": [ + { + "additionalProperties": { + "type": "string" + }, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Sensitive Headers" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "SSETransport" + } + }, + "required": [ + "name", + "url" + ], + "title": "SSETransport", + "type": "object" + } + ], + "x-abstract-component": false + }, + "BaseSSEmTLSTransport": { + "additionalProperties": false, + "description": "Transport layer for SSE with mTLS (mutual Transport Layer Security).\n\nThis transport establishes a secure, mutually authenticated TLS connection to the MCP server using client\ncertificates. Production deployments MUST use this transport to ensure both client and server identities\nare verified.\n\nNotes\n-----\n- Users MUST provide a valid client certificate (PEM format) and private key.\n- Users MUST provide (or trust) the correct certificate authority (CA) for the server they're connecting to.\n- The client certificate/key and CA certificate paths can be managed via secrets, config files, or secure\n environment variables in any production system.\n- Executors should ensure that these files are rotated and managed securely.", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "session_parameters": { + "$ref": "#/$defs/SessionParameters" + }, + "url": { + "title": "Url", + "type": "string" + }, + "headers": { + "anyOf": [ + { + "additionalProperties": { + "type": "string" + }, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Headers" + }, + "sensitive_headers": { + "anyOf": [ + { + "additionalProperties": { + "type": "string" + }, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Sensitive Headers" + }, + "key_file": { + "title": "Key File", + "type": "string" + }, + "cert_file": { + "title": "Cert File", + "type": "string" + }, + "ca_file": { + "title": "Ca File", + "type": "string" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "SSEmTLSTransport" + } + }, + "required": [ + "ca_file", + "cert_file", + "key_file", + "name", + "url" + ], + "title": "SSEmTLSTransport", + "type": "object", + "x-abstract-component": false + }, + "BaseServerTool": { + "additionalProperties": false, + "description": "A tool that is registered to and executed by the orchestrator.", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "inputs": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Inputs" + }, + "outputs": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Outputs" + }, + "requires_confirmation": { + "default": false, + "title": "Requires Confirmation", + "type": "boolean" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "ServerTool" + } + }, + "required": [ + "name" + ], + "title": "ServerTool", + "type": "object", + "x-abstract-component": false + }, + "BaseSpecializedAgent": { + "additionalProperties": false, + "description": "A specialized agent is an agent that uses an existing generic agent and\nspecializes it to solve a given task.\n\nIt can be executed anywhere an Agent can be executed.\n\nExamples\n--------\n>>> from pyagentspec.agent import Agent\n>>> from pyagentspec.property import StringProperty\n>>> from pyagentspec.specialized_agent import AgentSpecializationParameters, SpecializedAgent\n>>> from pyagentspec.tools import ServerTool\n>>> expertise_property = StringProperty(title=\"domain_of_expertise\")\n>>> system_prompt = '''You are an expert in {{domain_of_expertise}}.\n... Please help the users with their requests.'''\n>>> agent = Agent(\n... name=\"Adaptive expert agent\",\n... system_prompt=system_prompt,\n... llm_config=llm_config,\n... inputs=[expertise_property],\n... )\n>>> websearch_tool = ServerTool(\n... name=\"websearch_tool\",\n... description=\"Search the web for information\",\n... inputs=[StringProperty(title=\"query\")],\n... outputs=[StringProperty(title=\"search_result\")],\n... )\n>>> agent_specialization_parameters = AgentSpecializationParameters(\n... name=\"essay_agent\",\n... additional_instructions=\"Your goal is to help the user write an essay around the domain of expertise.\",\n... additional_tools=[websearch_tool]\n... )", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "inputs": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Inputs" + }, + "outputs": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Outputs" + }, + "agent": { + "$ref": "#/$defs/Agent" + }, + "agent_specialization_parameters": { + "$ref": "#/$defs/AgentSpecializationParameters" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "SpecializedAgent" + } + }, + "required": [ + "agent", + "agent_specialization_parameters", + "name" + ], + "title": "SpecializedAgent", + "type": "object", + "x-abstract-component": false + }, + "BaseStartNode": { + "additionalProperties": false, + "description": "Start nodes denote the start of the execution of a flow.\n\n- **Inputs**\n The list of inputs that should be the inputs of the flow. If both input and output\n properties are specified they must be an exact match\n\n If None is given, ``pyagentspec`` copies the outputs provided, if any. Otherwise, no input is exposed.\n- **Outputs**\n The list of outputs of the step. If both input and output properties are specified they\n must be an exact match\n\n If None is given, ``pyagentspec`` copies the inputs provided, if any. Otherwise, no output is exposed.\n- **Branches**\n One, the default next.\n\nExamples\n--------\n>>> from pyagentspec.property import Property\n>>> from pyagentspec.flows.edges import ControlFlowEdge, DataFlowEdge\n>>> from pyagentspec.flows.flow import Flow\n>>> from pyagentspec.flows.nodes import EndNode, LlmNode, StartNode\n>>> user_question_property = Property(\n... json_schema=dict(\n... title=\"user_question\",\n... description=\"The user question.\",\n... type=\"string\",\n... )\n... )\n>>> answer_property = Property(json_schema=dict(title=\"answer\", type=\"string\"))\n>>> start_node = StartNode(name=\"start\", inputs=[user_question_property])\n>>> end_node = EndNode(name=\"end\", outputs=[answer_property])\n>>> llm_node = LlmNode(\n... name=\"llm node\",\n... prompt_template=\"Answer the user question: {{user_question}}\",\n... llm_config=llm_config,\n... )\n>>> flow = Flow(\n... name=\"flow\",\n... start_node=start_node,\n... nodes=[start_node, llm_node, end_node],\n... control_flow_connections=[\n... ControlFlowEdge(name=\"start_to_llm\", from_node=start_node, to_node=llm_node),\n... ControlFlowEdge(name=\"llm_to_end\", from_node=llm_node, to_node=end_node),\n... ],\n... data_flow_connections=[\n... DataFlowEdge(\n... name=\"query_edge\",\n... source_node=start_node,\n... source_output=\"user_question\",\n... destination_node=llm_node,\n... destination_input=\"user_question\",\n... ),\n... DataFlowEdge(\n... name=\"answer_edge\",\n... source_node=llm_node,\n... source_output=\"generated_text\",\n... destination_node=end_node,\n... destination_input=\"answer\"\n... ),\n... ],\n... )", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "inputs": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Inputs" + }, + "outputs": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Outputs" + }, + "branches": { + "items": { + "type": "string" + }, + "title": "Branches", + "type": "array" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "StartNode" + } + }, + "required": [ + "name" + ], + "title": "StartNode", + "type": "object", + "x-abstract-component": false + }, + "BaseStdioTransport": { + "additionalProperties": false, + "description": "Base transport for connecting to an MCP server via subprocess with stdio.\n\nThis is a base class that can be subclassed for specific command-based\ntransports like Python, Node, Uvx, etc.\n\n.. warning::\n Stdio should be used for local prototyping only.", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "session_parameters": { + "$ref": "#/$defs/SessionParameters" + }, + "command": { + "title": "Command", + "type": "string" + }, + "args": { + "items": { + "type": "string" + }, + "title": "Args", + "type": "array" + }, + "env": { + "anyOf": [ + { + "additionalProperties": { + "type": "string" + }, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Env" + }, + "cwd": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Cwd" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "StdioTransport" + } + }, + "required": [ + "command", + "name" + ], + "title": "StdioTransport", + "type": "object", + "x-abstract-component": false + }, + "BaseStreamableHTTPTransport": { + "anyOf": [ + { + "$ref": "#/$defs/StreamableHTTPmTLSTransport" + }, + { + "additionalProperties": false, + "description": "Transport implementation that connects to an MCP server via Streamable HTTP.", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "session_parameters": { + "$ref": "#/$defs/SessionParameters" + }, + "url": { + "title": "Url", + "type": "string" + }, + "headers": { + "anyOf": [ + { + "additionalProperties": { + "type": "string" + }, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Headers" + }, + "sensitive_headers": { + "anyOf": [ + { + "additionalProperties": { + "type": "string" + }, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Sensitive Headers" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "StreamableHTTPTransport" + } + }, + "required": [ + "name", + "url" + ], + "title": "StreamableHTTPTransport", + "type": "object" + } + ], + "x-abstract-component": false + }, + "BaseStreamableHTTPmTLSTransport": { + "additionalProperties": false, + "description": "Transport layer for streamable HTTP with mTLS (mutual Transport Layer Security).\n\nThis transport establishes a secure, mutually authenticated TLS connection to the MCP server using client\ncertificates. Production deployments MUST use this transport to ensure both client and server identities\nare verified.\n\nNotes\n-----\n- Users MUST provide a valid client certificate (PEM format) and private key.\n- Users MUST provide (or trust) the correct certificate authority (CA) for the server they're connecting to.\n- The client certificate/key and CA certificate paths can be managed via secrets, config files, or secure\n environment variables in any production system.\n- Executors should ensure that these files are rotated and managed securely.", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "session_parameters": { + "$ref": "#/$defs/SessionParameters" + }, + "url": { + "title": "Url", + "type": "string" + }, + "headers": { + "anyOf": [ + { + "additionalProperties": { + "type": "string" + }, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Headers" + }, + "sensitive_headers": { + "anyOf": [ + { + "additionalProperties": { + "type": "string" + }, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Sensitive Headers" + }, + "key_file": { + "title": "Key File", + "type": "string" + }, + "cert_file": { + "title": "Cert File", + "type": "string" + }, + "ca_file": { + "title": "Ca File", + "type": "string" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "StreamableHTTPmTLSTransport" + } + }, + "required": [ + "ca_file", + "cert_file", + "key_file", + "name", + "url" + ], + "title": "StreamableHTTPmTLSTransport", + "type": "object", + "x-abstract-component": false + }, + "BaseSwarm": { + "additionalProperties": false, + "description": "Defines a ``Swarm`` conversational component.\n\nA ``Swarm`` is a multi-agent conversational component in which each agent determines\nthe next agent to be executed, based on a list of pre-defined relationships.\nAgents in Swarm can be any ``AgenticComponent``.\n\nExamples\n--------\n>>> from pyagentspec.agent import Agent\n>>> from pyagentspec.swarm import Swarm\n>>> addition_agent = Agent(name=\"addition_agent\", description=\"Agent that can do additions\", llm_config=llm_config, system_prompt=\"You can do additions.\")\n>>> multiplication_agent = Agent(name=\"multiplication_agent\", description=\"Agent that can do multiplication\", llm_config=llm_config, system_prompt=\"You can do multiplication.\")\n>>> division_agent = Agent(name=\"division_agent\", description=\"Agent that can do division\", llm_config=llm_config, system_prompt=\"You can do division.\")\n>>>\n>>> swarm = Swarm(\n... name=\"swarm\",\n... first_agent=addition_agent,\n... relationships=[\n... (addition_agent, multiplication_agent),\n... (addition_agent, division_agent),\n... (multiplication_agent, division_agent),\n... ]\n... )", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "inputs": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Inputs" + }, + "outputs": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Outputs" + }, + "first_agent": { + "$ref": "#/$defs/AgenticComponent" + }, + "relationships": { + "items": { + "maxItems": 2, + "minItems": 2, + "prefixItems": [ + { + "$ref": "#/$defs/AgenticComponent" + }, + { + "$ref": "#/$defs/AgenticComponent" + } + ], + "type": "array" + }, + "title": "Relationships", + "type": "array" + }, + "handoff": { + "anyOf": [ + { + "type": "boolean" + }, + { + "$ref": "#/$defs/HandoffMode" + } + ], + "default": "optional", + "title": "Handoff" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "Swarm" + } + }, + "required": [ + "first_agent", + "name", + "relationships" + ], + "title": "Swarm", + "type": "object", + "x-abstract-component": false + }, + "BaseTlsOracleDatabaseConnectionConfig": { + "anyOf": [ + { + "$ref": "#/$defs/MTlsOracleDatabaseConnectionConfig" + }, + { + "additionalProperties": false, + "description": "TLS Connection Configuration to Oracle Database.", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "user": { + "title": "User", + "type": "string" + }, + "password": { + "title": "Password", + "type": "string" + }, + "dsn": { + "title": "Dsn", + "type": "string" + }, + "config_dir": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Config Dir" + }, + "protocol": { + "default": "tcps", + "enum": [ + "tcp", + "tcps" + ], + "title": "Protocol", + "type": "string" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "TlsOracleDatabaseConnectionConfig" + } + }, + "required": [ + "dsn", + "name", + "password", + "user" + ], + "title": "TlsOracleDatabaseConnectionConfig", + "type": "object" + } + ], + "x-abstract-component": false + }, + "BaseTlsPostgresDatabaseConnectionConfig": { + "additionalProperties": false, + "description": "Configuration for a PostgreSQL connection with TLS/SSL support.", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "user": { + "title": "User", + "type": "string" + }, + "password": { + "title": "Password", + "type": "string" + }, + "url": { + "title": "Url", + "type": "string" + }, + "sslmode": { + "default": "require", + "enum": [ + "allow", + "disable", + "prefer", + "require", + "verify-ca", + "verify-full" + ], + "title": "Sslmode", + "type": "string" + }, + "sslcert": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Sslcert" + }, + "sslkey": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Sslkey" + }, + "sslrootcert": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Sslrootcert" + }, + "sslcrl": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Sslcrl" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "TlsPostgresDatabaseConnectionConfig" + } + }, + "required": [ + "name", + "password", + "url", + "user" + ], + "title": "TlsPostgresDatabaseConnectionConfig", + "type": "object", + "x-abstract-component": false + }, + "BaseTool": { + "anyOf": [ + { + "$ref": "#/$defs/BuiltinTool" + }, + { + "$ref": "#/$defs/ClientTool" + }, + { + "$ref": "#/$defs/MCPTool" + }, + { + "$ref": "#/$defs/RemoteTool" + }, + { + "$ref": "#/$defs/ServerTool" + } + ], + "x-abstract-component": true + }, + "BaseToolBox": { + "anyOf": [ + { + "$ref": "#/$defs/MCPToolBox" + } + ], + "x-abstract-component": true + }, + "BaseToolNode": { + "additionalProperties": false, + "description": "The tool execution node is a node that will execute a tool as part of a flow.\n\n- **Inputs**\n Inferred from the definition of the tool to execute.\n- **Outputs**\n Inferred from the definition of the tool to execute.\n- **Branches**\n One, the default next.\n\nExamples\n--------\n>>> from pyagentspec.flows.edges import ControlFlowEdge, DataFlowEdge\n>>> from pyagentspec.flows.flow import Flow\n>>> from pyagentspec.flows.nodes import ToolNode, StartNode, EndNode\n>>> from pyagentspec.tools import ServerTool\n>>> from pyagentspec.property import Property\n>>>\n>>> x_property = Property(json_schema={\"title\": \"x\", \"type\": \"number\"})\n>>> x_square_root_property = Property(\n... json_schema={\"title\": \"x_square_root\", \"type\": \"number\"}\n... )\n>>> square_root_tool = ServerTool(\n... name=\"compute_square_root\",\n... description=\"Computes the square root of a number\",\n... inputs=[x_property],\n... outputs=[x_square_root_property],\n... )\n>>> start_node = StartNode(name=\"start\", inputs=[x_property])\n>>> end_node = EndNode(name=\"end\", outputs=[x_square_root_property])\n>>> tool_node = ToolNode(name=\"\", tool=square_root_tool)\n>>> flow = Flow(\n... name=\"Compute square root flow\",\n... start_node=start_node,\n... nodes=[start_node, tool_node, end_node],\n... control_flow_connections=[\n... ControlFlowEdge(name=\"start_to_tool\", from_node=start_node, to_node=tool_node),\n... ControlFlowEdge(name=\"tool_to_end\", from_node=tool_node, to_node=end_node),\n... ],\n... data_flow_connections=[\n... DataFlowEdge(\n... name=\"x_edge\",\n... source_node=start_node,\n... source_output=\"x\",\n... destination_node=tool_node,\n... destination_input=\"x\",\n... ),\n... DataFlowEdge(\n... name=\"x_square_root_edge\",\n... source_node=tool_node,\n... source_output=\"x_square_root\",\n... destination_node=end_node,\n... destination_input=\"x_square_root\"\n... ),\n... ],\n... )", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "inputs": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Inputs" + }, + "outputs": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Property" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Outputs" + }, + "branches": { + "items": { + "type": "string" + }, + "title": "Branches", + "type": "array" + }, + "tool": { + "$ref": "#/$defs/Tool" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "ToolNode" + } + }, + "required": [ + "name", + "tool" + ], + "title": "ToolNode", + "type": "object", + "x-abstract-component": false + }, + "BaseVllmConfig": { + "additionalProperties": false, + "description": "Class to configure a connection to a vLLM-hosted LLM.\n\nRequires to specify the url at which the instance is running.", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata" + }, + "default_generation_parameters": { + "anyOf": [ + { + "type": "null" + }, + { + "$ref": "#/$defs/LlmGenerationConfig" + } + ], + "default": null + }, + "url": { + "title": "Url", + "type": "string" + }, + "model_id": { + "title": "Model Id", + "type": "string" + }, + "api_type": { + "$ref": "#/$defs/OpenAIAPIType", + "default": "chat_completions" + }, + "api_key": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Api Key" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "component_type": { + "const": "VllmConfig" + } + }, + "required": [ + "model_id", + "name", + "url" + ], + "title": "VllmConfig", + "type": "object", + "x-abstract-component": false + }, + "ReferencedComponents": { + "type": "object", + "additionalProperties": { + "anyOf": [ + { + "$ref": "#/$defs/BaseA2AAgent" + }, + { + "$ref": "#/$defs/BaseA2AConnectionConfig" + }, + { + "$ref": "#/$defs/BaseAgent" + }, + { + "$ref": "#/$defs/BaseAgentNode" + }, + { + "$ref": "#/$defs/BaseAgentSpecializationParameters" + }, + { + "$ref": "#/$defs/BaseAgenticComponent" + }, + { + "$ref": "#/$defs/BaseApiNode" + }, + { + "$ref": "#/$defs/BaseBranchingNode" + }, + { + "$ref": "#/$defs/BaseBuiltinTool" + }, + { + "$ref": "#/$defs/BaseClientTool" + }, + { + "$ref": "#/$defs/BaseClientTransport" + }, + { + "$ref": "#/$defs/BaseComponentWithIO" + }, + { + "$ref": "#/$defs/BaseControlFlowEdge" + }, + { + "$ref": "#/$defs/BaseConversationSummarizationTransform" + }, + { + "$ref": "#/$defs/BaseDataFlowEdge" + }, + { + "$ref": "#/$defs/BaseDatastore" + }, + { + "$ref": "#/$defs/BaseEndNode" + }, + { + "$ref": "#/$defs/BaseFlow" + }, + { + "$ref": "#/$defs/BaseFlowNode" + }, + { + "$ref": "#/$defs/BaseInMemoryCollectionDatastore" + }, + { + "$ref": "#/$defs/BaseInputMessageNode" + }, + { + "$ref": "#/$defs/BaseLlmConfig" + }, + { + "$ref": "#/$defs/BaseLlmNode" + }, + { + "$ref": "#/$defs/BaseMCPTool" + }, + { + "$ref": "#/$defs/BaseMCPToolBox" + }, + { + "$ref": "#/$defs/BaseMCPToolSpec" + }, + { + "$ref": "#/$defs/BaseMTlsOracleDatabaseConnectionConfig" + }, + { + "$ref": "#/$defs/BaseManagerWorkers" + }, + { + "$ref": "#/$defs/BaseMapNode" + }, + { + "$ref": "#/$defs/BaseMessageSummarizationTransform" + }, + { + "$ref": "#/$defs/BaseMessageTransform" + }, + { + "$ref": "#/$defs/BaseNode" + }, + { + "$ref": "#/$defs/BaseOciAgent" + }, + { + "$ref": "#/$defs/BaseOciClientConfig" + }, + { + "$ref": "#/$defs/BaseOciClientConfigWithApiKey" + }, + { + "$ref": "#/$defs/BaseOciClientConfigWithInstancePrincipal" + }, + { + "$ref": "#/$defs/BaseOciClientConfigWithResourcePrincipal" + }, + { + "$ref": "#/$defs/BaseOciClientConfigWithSecurityToken" + }, + { + "$ref": "#/$defs/BaseOciGenAiConfig" + }, + { + "$ref": "#/$defs/BaseOllamaConfig" + }, + { + "$ref": "#/$defs/BaseOpenAiCompatibleConfig" + }, + { + "$ref": "#/$defs/BaseOpenAiConfig" + }, + { + "$ref": "#/$defs/BaseOracleDatabaseConnectionConfig" + }, + { + "$ref": "#/$defs/BaseOracleDatabaseDatastore" + }, + { + "$ref": "#/$defs/BaseOutputMessageNode" + }, + { + "$ref": "#/$defs/BaseParallelFlowNode" + }, + { + "$ref": "#/$defs/BaseParallelMapNode" + }, + { + "$ref": "#/$defs/BasePostgresDatabaseConnectionConfig" + }, + { + "$ref": "#/$defs/BasePostgresDatabaseDatastore" + }, + { + "$ref": "#/$defs/BaseRemoteAgent" + }, + { + "$ref": "#/$defs/BaseRemoteTool" + }, + { + "$ref": "#/$defs/BaseRemoteTransport" + }, + { + "$ref": "#/$defs/BaseSSETransport" + }, + { + "$ref": "#/$defs/BaseSSEmTLSTransport" + }, + { + "$ref": "#/$defs/BaseServerTool" + }, + { + "$ref": "#/$defs/BaseSpecializedAgent" + }, + { + "$ref": "#/$defs/BaseStartNode" + }, + { + "$ref": "#/$defs/BaseStdioTransport" + }, + { + "$ref": "#/$defs/BaseStreamableHTTPTransport" + }, + { + "$ref": "#/$defs/BaseStreamableHTTPmTLSTransport" + }, + { + "$ref": "#/$defs/BaseSwarm" + }, + { + "$ref": "#/$defs/BaseTlsOracleDatabaseConnectionConfig" + }, + { + "$ref": "#/$defs/BaseTlsPostgresDatabaseConnectionConfig" + }, + { + "$ref": "#/$defs/BaseTool" + }, + { + "$ref": "#/$defs/BaseToolBox" + }, + { + "$ref": "#/$defs/BaseToolNode" + }, + { + "$ref": "#/$defs/BaseVllmConfig" + }, + { + "$ref": "#/$defs/ComponentReferenceWithNestedReferences" + } + ] + } + }, + "ComponentReference": { + "type": "object", + "properties": { + "$component_ref": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "$component_ref" + ] + }, + "ComponentReferenceWithNestedReferences": { + "type": "object", + "properties": { + "$component_ref": { + "type": "string" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + } + }, + "additionalProperties": false, + "required": [ + "$component_ref", + "$referenced_components" + ] + }, + "AgentSpecVersionEnum": { + "enum": [ + "25.4.1", + "25.4.2", + "26.1.0", + "26.1.1" + ], + "title": "AgentSpecVersionEnum", + "type": "string" + }, + "VersionedA2AAgent": { + "anyOf": [ + { + "$ref": "#/$defs/BaseA2AAgent" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedA2AConnectionConfig": { + "anyOf": [ + { + "$ref": "#/$defs/BaseA2AConnectionConfig" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedAgent": { + "anyOf": [ + { + "$ref": "#/$defs/BaseAgent" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedAgentNode": { + "anyOf": [ + { + "$ref": "#/$defs/BaseAgentNode" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedAgentSpecializationParameters": { + "anyOf": [ + { + "$ref": "#/$defs/BaseAgentSpecializationParameters" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedAgenticComponent": { + "anyOf": [ + { + "$ref": "#/$defs/BaseAgenticComponent" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedApiNode": { + "anyOf": [ + { + "$ref": "#/$defs/BaseApiNode" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedBranchingNode": { + "anyOf": [ + { + "$ref": "#/$defs/BaseBranchingNode" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedBuiltinTool": { + "anyOf": [ + { + "$ref": "#/$defs/BaseBuiltinTool" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedClientTool": { + "anyOf": [ + { + "$ref": "#/$defs/BaseClientTool" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedClientTransport": { + "anyOf": [ + { + "$ref": "#/$defs/BaseClientTransport" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedComponentWithIO": { + "anyOf": [ + { + "$ref": "#/$defs/BaseComponentWithIO" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedControlFlowEdge": { + "anyOf": [ + { + "$ref": "#/$defs/BaseControlFlowEdge" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedConversationSummarizationTransform": { + "anyOf": [ + { + "$ref": "#/$defs/BaseConversationSummarizationTransform" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedDataFlowEdge": { + "anyOf": [ + { + "$ref": "#/$defs/BaseDataFlowEdge" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedDatastore": { + "anyOf": [ + { + "$ref": "#/$defs/BaseDatastore" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedEndNode": { + "anyOf": [ + { + "$ref": "#/$defs/BaseEndNode" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedFlow": { + "anyOf": [ + { + "$ref": "#/$defs/BaseFlow" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedFlowNode": { + "anyOf": [ + { + "$ref": "#/$defs/BaseFlowNode" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedInMemoryCollectionDatastore": { + "anyOf": [ + { + "$ref": "#/$defs/BaseInMemoryCollectionDatastore" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedInputMessageNode": { + "anyOf": [ + { + "$ref": "#/$defs/BaseInputMessageNode" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedLlmConfig": { + "anyOf": [ + { + "$ref": "#/$defs/BaseLlmConfig" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedLlmNode": { + "anyOf": [ + { + "$ref": "#/$defs/BaseLlmNode" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedMCPTool": { + "anyOf": [ + { + "$ref": "#/$defs/BaseMCPTool" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedMCPToolBox": { + "anyOf": [ + { + "$ref": "#/$defs/BaseMCPToolBox" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedMCPToolSpec": { + "anyOf": [ + { + "$ref": "#/$defs/BaseMCPToolSpec" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedMTlsOracleDatabaseConnectionConfig": { + "anyOf": [ + { + "$ref": "#/$defs/BaseMTlsOracleDatabaseConnectionConfig" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedManagerWorkers": { + "anyOf": [ + { + "$ref": "#/$defs/BaseManagerWorkers" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedMapNode": { + "anyOf": [ + { + "$ref": "#/$defs/BaseMapNode" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedMessageSummarizationTransform": { + "anyOf": [ + { + "$ref": "#/$defs/BaseMessageSummarizationTransform" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedNode": { + "anyOf": [ + { + "$ref": "#/$defs/BaseNode" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedOciAgent": { + "anyOf": [ + { + "$ref": "#/$defs/BaseOciAgent" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedOciClientConfig": { + "anyOf": [ + { + "$ref": "#/$defs/BaseOciClientConfig" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedOciClientConfigWithApiKey": { + "anyOf": [ + { + "$ref": "#/$defs/BaseOciClientConfigWithApiKey" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedOciClientConfigWithInstancePrincipal": { + "anyOf": [ + { + "$ref": "#/$defs/BaseOciClientConfigWithInstancePrincipal" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedOciClientConfigWithResourcePrincipal": { + "anyOf": [ + { + "$ref": "#/$defs/BaseOciClientConfigWithResourcePrincipal" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedOciClientConfigWithSecurityToken": { + "anyOf": [ + { + "$ref": "#/$defs/BaseOciClientConfigWithSecurityToken" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedOciGenAiConfig": { + "anyOf": [ + { + "$ref": "#/$defs/BaseOciGenAiConfig" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedOllamaConfig": { + "anyOf": [ + { + "$ref": "#/$defs/BaseOllamaConfig" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedOpenAiCompatibleConfig": { + "anyOf": [ + { + "$ref": "#/$defs/BaseOpenAiCompatibleConfig" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedOpenAiConfig": { + "anyOf": [ + { + "$ref": "#/$defs/BaseOpenAiConfig" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedOracleDatabaseDatastore": { + "anyOf": [ + { + "$ref": "#/$defs/BaseOracleDatabaseDatastore" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedOutputMessageNode": { + "anyOf": [ + { + "$ref": "#/$defs/BaseOutputMessageNode" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedParallelFlowNode": { + "anyOf": [ + { + "$ref": "#/$defs/BaseParallelFlowNode" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedParallelMapNode": { + "anyOf": [ + { + "$ref": "#/$defs/BaseParallelMapNode" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedPostgresDatabaseDatastore": { + "anyOf": [ + { + "$ref": "#/$defs/BasePostgresDatabaseDatastore" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedRemoteAgent": { + "anyOf": [ + { + "$ref": "#/$defs/BaseRemoteAgent" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedRemoteTool": { + "anyOf": [ + { + "$ref": "#/$defs/BaseRemoteTool" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedRemoteTransport": { + "anyOf": [ + { + "$ref": "#/$defs/BaseRemoteTransport" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedSSETransport": { + "anyOf": [ + { + "$ref": "#/$defs/BaseSSETransport" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedSSEmTLSTransport": { + "anyOf": [ + { + "$ref": "#/$defs/BaseSSEmTLSTransport" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedServerTool": { + "anyOf": [ + { + "$ref": "#/$defs/BaseServerTool" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedSpecializedAgent": { + "anyOf": [ + { + "$ref": "#/$defs/BaseSpecializedAgent" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedStartNode": { + "anyOf": [ + { + "$ref": "#/$defs/BaseStartNode" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedStdioTransport": { + "anyOf": [ + { + "$ref": "#/$defs/BaseStdioTransport" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedStreamableHTTPTransport": { + "anyOf": [ + { + "$ref": "#/$defs/BaseStreamableHTTPTransport" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedStreamableHTTPmTLSTransport": { + "anyOf": [ + { + "$ref": "#/$defs/BaseStreamableHTTPmTLSTransport" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedSwarm": { + "anyOf": [ + { + "$ref": "#/$defs/BaseSwarm" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedTlsOracleDatabaseConnectionConfig": { + "anyOf": [ + { + "$ref": "#/$defs/BaseTlsOracleDatabaseConnectionConfig" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedTlsPostgresDatabaseConnectionConfig": { + "anyOf": [ + { + "$ref": "#/$defs/BaseTlsPostgresDatabaseConnectionConfig" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedTool": { + "anyOf": [ + { + "$ref": "#/$defs/BaseTool" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedToolBox": { + "anyOf": [ + { + "$ref": "#/$defs/BaseToolBox" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedToolNode": { + "anyOf": [ + { + "$ref": "#/$defs/BaseToolNode" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedVllmConfig": { + "anyOf": [ + { + "$ref": "#/$defs/BaseVllmConfig" + }, + { + "$ref": "#/$defs/ComponentReference" + } + ], + "properties": { + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + } + }, + "VersionedComponentReferenceWithNestedReferences": { + "type": "object", + "properties": { + "$component_ref": { + "type": "string" + }, + "$referenced_components": { + "$ref": "#/$defs/ReferencedComponents" + }, + "agentspec_version": { + "$ref": "#/$defs/AgentSpecVersionEnum" + } + }, + "additionalProperties": false, + "required": [ + "$component_ref", + "$referenced_components" + ] + } + }, + "anyOf": [ + { + "$ref": "#/$defs/VersionedA2AAgent" + }, + { + "$ref": "#/$defs/VersionedA2AConnectionConfig" + }, + { + "$ref": "#/$defs/VersionedAgent" + }, + { + "$ref": "#/$defs/VersionedAgentNode" + }, + { + "$ref": "#/$defs/VersionedAgentSpecializationParameters" + }, + { + "$ref": "#/$defs/VersionedAgenticComponent" + }, + { + "$ref": "#/$defs/VersionedApiNode" + }, + { + "$ref": "#/$defs/VersionedBranchingNode" + }, + { + "$ref": "#/$defs/VersionedBuiltinTool" + }, + { + "$ref": "#/$defs/VersionedClientTool" + }, + { + "$ref": "#/$defs/VersionedClientTransport" + }, + { + "$ref": "#/$defs/VersionedComponentReferenceWithNestedReferences" + }, + { + "$ref": "#/$defs/VersionedComponentWithIO" + }, + { + "$ref": "#/$defs/VersionedControlFlowEdge" + }, + { + "$ref": "#/$defs/VersionedConversationSummarizationTransform" + }, + { + "$ref": "#/$defs/VersionedDataFlowEdge" + }, + { + "$ref": "#/$defs/VersionedDatastore" + }, + { + "$ref": "#/$defs/VersionedEndNode" + }, + { + "$ref": "#/$defs/VersionedFlow" + }, + { + "$ref": "#/$defs/VersionedFlowNode" + }, + { + "$ref": "#/$defs/VersionedInMemoryCollectionDatastore" + }, + { + "$ref": "#/$defs/VersionedInputMessageNode" + }, + { + "$ref": "#/$defs/VersionedLlmConfig" + }, + { + "$ref": "#/$defs/VersionedLlmNode" + }, + { + "$ref": "#/$defs/VersionedMCPTool" + }, + { + "$ref": "#/$defs/VersionedMCPToolBox" + }, + { + "$ref": "#/$defs/VersionedMCPToolSpec" + }, + { + "$ref": "#/$defs/VersionedMTlsOracleDatabaseConnectionConfig" + }, + { + "$ref": "#/$defs/VersionedManagerWorkers" + }, + { + "$ref": "#/$defs/VersionedMapNode" + }, + { + "$ref": "#/$defs/VersionedMessageSummarizationTransform" + }, + { + "$ref": "#/$defs/VersionedNode" + }, + { + "$ref": "#/$defs/VersionedOciAgent" + }, + { + "$ref": "#/$defs/VersionedOciClientConfig" + }, + { + "$ref": "#/$defs/VersionedOciClientConfigWithApiKey" + }, + { + "$ref": "#/$defs/VersionedOciClientConfigWithInstancePrincipal" + }, + { + "$ref": "#/$defs/VersionedOciClientConfigWithResourcePrincipal" + }, + { + "$ref": "#/$defs/VersionedOciClientConfigWithSecurityToken" + }, + { + "$ref": "#/$defs/VersionedOciGenAiConfig" + }, + { + "$ref": "#/$defs/VersionedOllamaConfig" + }, + { + "$ref": "#/$defs/VersionedOpenAiCompatibleConfig" + }, + { + "$ref": "#/$defs/VersionedOpenAiConfig" + }, + { + "$ref": "#/$defs/VersionedOracleDatabaseDatastore" + }, + { + "$ref": "#/$defs/VersionedOutputMessageNode" + }, + { + "$ref": "#/$defs/VersionedParallelFlowNode" + }, + { + "$ref": "#/$defs/VersionedParallelMapNode" + }, + { + "$ref": "#/$defs/VersionedPostgresDatabaseDatastore" + }, + { + "$ref": "#/$defs/VersionedRemoteAgent" + }, + { + "$ref": "#/$defs/VersionedRemoteTool" + }, + { + "$ref": "#/$defs/VersionedRemoteTransport" + }, + { + "$ref": "#/$defs/VersionedSSETransport" + }, + { + "$ref": "#/$defs/VersionedSSEmTLSTransport" + }, + { + "$ref": "#/$defs/VersionedServerTool" + }, + { + "$ref": "#/$defs/VersionedSpecializedAgent" + }, + { + "$ref": "#/$defs/VersionedStartNode" + }, + { + "$ref": "#/$defs/VersionedStdioTransport" + }, + { + "$ref": "#/$defs/VersionedStreamableHTTPTransport" + }, + { + "$ref": "#/$defs/VersionedStreamableHTTPmTLSTransport" + }, + { + "$ref": "#/$defs/VersionedSwarm" + }, + { + "$ref": "#/$defs/VersionedTlsOracleDatabaseConnectionConfig" + }, + { + "$ref": "#/$defs/VersionedTlsPostgresDatabaseConnectionConfig" + }, + { + "$ref": "#/$defs/VersionedTool" + }, + { + "$ref": "#/$defs/VersionedToolBox" + }, + { + "$ref": "#/$defs/VersionedToolNode" + }, + { + "$ref": "#/$defs/VersionedVllmConfig" + } + ] +} From c144d54a4a933422ef153f67d293f952f82d186c Mon Sep 17 00:00:00 2001 From: Youssef Boughizane Date: Tue, 23 Dec 2025 14:19:27 +0100 Subject: [PATCH 26/26] doc: update agent doc in specs --- docs/pyagentspec/source/agentspec/language_spec_nightly.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/pyagentspec/source/agentspec/language_spec_nightly.rst b/docs/pyagentspec/source/agentspec/language_spec_nightly.rst index d802a58a..f3d82c71 100644 --- a/docs/pyagentspec/source/agentspec/language_spec_nightly.rst +++ b/docs/pyagentspec/source/agentspec/language_spec_nightly.rst @@ -424,6 +424,7 @@ for interactions with the agentic system. tools: List[Tool] toolboxes: List[ToolBox] human_in_the_loop: bool + transforms: List[MessageTransform] Its main goal is to accomplish any task assigned in the ``system_prompt`` and terminate, with or without any interaction with the user. @@ -454,6 +455,9 @@ registry for the Agent. In case of tool name collisions across different sources the exact behavior to the runtime. Runtimes MAY error on collisions. - Implementations SHOULD provide clear diagnostics when collisions occur. +Transforms, when present, apply transformations to the messages before they are passed to the LLM, +allowing for message summarization, conversation summarization, or other modifications to optimize the agent's context. + This Component will be expanded as more functionalities will be added to Agent Spec (e.g., memory, planners, ...).