diff --git a/docs/pyagentspec/source/_components/all_components.json b/docs/pyagentspec/source/_components/all_components.json index 92b31b8e..cec10863 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"}, + {"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.MessageTransform"}, + {"path": "pyagentspec.transforms.MessageSummarizationTransform"}, + {"path": "pyagentspec.transforms.ConversationSummarizationTransform"} + ] + }, { "name": "A2A", "path": "a2a", 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" + } + ] +} diff --git a/docs/pyagentspec/source/agentspec/language_spec_nightly.rst b/docs/pyagentspec/source/agentspec/language_spec_nightly.rst index e7762245..f3d82c71 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 @@ -419,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. @@ -449,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, ...). @@ -1942,6 +1951,164 @@ 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 +~~~~~~~~~~ + +Datastores are the AgentSpec abstraction that 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. Sensitive information (e.g., wallets, keys) is excluded from exported configurations and should be loaded at deserialization time. + +.. 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 +^^^^^^^^^^^^^^^^^ + +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 + + class PostgresDatabaseConnectionConfig(Component, is_abstract=True): + pass + + 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. + + +Transforms +^^^^^^^^^^ + +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 + + 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 messages where the context can become too large for the agent LLM to handle. + +.. code-block:: python + + class MessageSummarizationTransform(MessageTransform): + 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." + ) # 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 +'''''''''''''''''''''''''''''''''' + +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 # 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." + ) # 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 ---------- diff --git a/docs/pyagentspec/source/api/datastores.rst b/docs/pyagentspec/source/api/datastores.rst new file mode 100644 index 00000000..e9b58ec4 --- /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 + :exclude-members: model_post_init, model_config + +RelationalDatastore +~~~~~~~~~~~~~~~~~~~ + +.. _relationaldatastore: +.. autoclass:: pyagentspec.datastores.RelationalDatastore + :exclude-members: model_post_init, model_config + +InMemoryCollectionDatastore +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. _inmemorycollectiondatastore: +.. autoclass:: pyagentspec.datastores.InMemoryCollectionDatastore + :exclude-members: model_post_init, model_config + +Oracle datastore +---------------- + +OracleDatabaseDatastore +~~~~~~~~~~~~~~~~~~~~~~~ + +.. _oracledatabasedatastore: +.. autoclass:: pyagentspec.datastores.OracleDatabaseDatastore + :exclude-members: model_post_init, model_config + +TlsOracleDatabaseConnectionConfig +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. _tlsoracledatabaseconnectionconfig: +.. autoclass:: pyagentspec.datastores.TlsOracleDatabaseConnectionConfig + :exclude-members: model_post_init, model_config + +MTlsOracleDatabaseConnectionConfig +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. _mtlsoracledatabaseconnectionconfig: +.. autoclass:: pyagentspec.datastores.MTlsOracleDatabaseConnectionConfig + :exclude-members: model_post_init, model_config + +PostgreSQL datastore +-------------------- + +PostgresDatabaseDatastore +~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. _postgresdatabasedatastore: +.. autoclass:: pyagentspec.datastores.PostgresDatabaseDatastore + :exclude-members: model_post_init, model_config + +TlsPostgresDatabaseConnectionConfig +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. _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 new file mode 100644 index 00000000..b4f42ff2 --- /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.MessageTransform + :exclude-members: model_post_init, model_config + +Summarization transforms +------------------------ + +MessageSummarizationTransform +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. _messagesummarizationtransform: +.. autoclass:: pyagentspec.transforms.MessageSummarizationTransform + :exclude-members: model_post_init, model_config + +ConversationSummarizationTransform +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. _conversationsummarizationtransform: +.. autoclass:: pyagentspec.transforms.ConversationSummarizationTransform + :exclude-members: model_post_init, model_config diff --git a/docs/pyagentspec/source/changelog.rst b/docs/pyagentspec/source/changelog.rst index c29573c1..ced7e2bb 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. + +* **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. + Improvements ^^^^^^^^^^^^ diff --git a/pyagentspec/src/pyagentspec/_component_registry.py b/pyagentspec/src/pyagentspec/_component_registry.py index 8022d20f..ced13d39 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 @@ -67,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, @@ -84,9 +95,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 +118,8 @@ "OllamaConfig": OllamaConfig, "OpenAiCompatibleConfig": OpenAiCompatibleConfig, "OpenAiConfig": OpenAiConfig, + "OracleDatabaseDatastore": OracleDatabaseDatastore, + "PostgresDatabaseDatastore": PostgresDatabaseDatastore, "OutputMessageNode": OutputMessageNode, "RemoteTool": RemoteTool, "RemoteTransport": RemoteTransport, @@ -120,6 +135,11 @@ "Tool": Tool, "ToolBox": ToolBox, "ToolNode": ToolNode, + "TlsOracleDatabaseConnectionConfig": TlsOracleDatabaseConnectionConfig, + "MTlsOracleDatabaseConnectionConfig": MTlsOracleDatabaseConnectionConfig, + "TlsPostgresDatabaseConnectionConfig": TlsPostgresDatabaseConnectionConfig, + "MessageSummarizationTransform": MessageSummarizationTransform, + "ConversationSummarizationTransform": ConversationSummarizationTransform, "VllmConfig": VllmConfig, "Swarm": Swarm, "ManagerWorkers": ManagerWorkers, diff --git a/pyagentspec/src/pyagentspec/agent.py b/pyagentspec/src/pyagentspec/agent.py index 64be8ed0..69e42758 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 @@ -69,6 +72,8 @@ 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: @@ -79,5 +84,11 @@ 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) - 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/__init__.py b/pyagentspec/src/pyagentspec/datastores/__init__.py new file mode 100644 index 00000000..e050da9a --- /dev/null +++ b/pyagentspec/src/pyagentspec/datastores/__init__.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. + +from .datastore import Datastore, InMemoryCollectionDatastore, RelationalDatastore +from .oracle import ( + MTlsOracleDatabaseConnectionConfig, + OracleDatabaseConnectionConfig, + OracleDatabaseDatastore, + TlsOracleDatabaseConnectionConfig, +) +from .postgres import ( + PostgresDatabaseDatastore, + TlsPostgresDatabaseConnectionConfig, +) + +__all__ = [ + "Datastore", + "RelationalDatastore", + "InMemoryCollectionDatastore", + "OracleDatabaseConnectionConfig", + "OracleDatabaseDatastore", + "TlsOracleDatabaseConnectionConfig", + "MTlsOracleDatabaseConnectionConfig", + "PostgresDatabaseDatastore", + "TlsPostgresDatabaseConnectionConfig", +] diff --git a/pyagentspec/src/pyagentspec/datastores/datastore.py b/pyagentspec/src/pyagentspec/datastores/datastore.py new file mode 100644 index 00000000..b443c23a --- /dev/null +++ b/pyagentspec/src/pyagentspec/datastores/datastore.py @@ -0,0 +1,50 @@ +# 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, 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: + 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 configure how to store and retrieve data.""" + + min_agentspec_version: SkipJsonSchema[AgentSpecVersionEnum] = Field( + default=AgentSpecVersionEnum.v26_1_1, init=False, exclude=True + ) + + +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..c9416d5f --- /dev/null +++ b/pyagentspec/src/pyagentspec/datastores/oracle.py @@ -0,0 +1,63 @@ +# 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 oracle datastore and oracle database connnection configuration components.""" + +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 + + +class OracleDatabaseConnectionConfig(Component, abstract=True): + """Base class used for configuring connections to Oracle Database.""" + + min_agentspec_version: SkipJsonSchema[AgentSpecVersionEnum] = Field( + default=AgentSpecVersionEnum.v26_1_1, init=False, exclude=True + ) + + +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..38972640 --- /dev/null +++ b/pyagentspec/src/pyagentspec/datastores/postgres.py @@ -0,0 +1,64 @@ +# 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 postgres datastore and postgres database connnection configuration components.""" + +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 + + +class PostgresDatabaseConnectionConfig(Component, abstract=True): + """Base class for a PostgreSQL connection.""" + + min_agentspec_version: SkipJsonSchema[AgentSpecVersionEnum] = Field( + default=AgentSpecVersionEnum.v26_1_1, init=False, exclude=True + ) + + +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/src/pyagentspec/serialization/deserializationcontext.py b/pyagentspec/src/pyagentspec/serialization/deserializationcontext.py index 01a89f74..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 @@ -286,7 +265,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 +282,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,27 +361,26 @@ 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. + 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, [] 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..5d8be7b1 --- /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. +"""This file defines message transforms for message and conversation summarization.""" +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..27a917c9 --- /dev/null +++ b/pyagentspec/src/pyagentspec/transforms/transforms.py @@ -0,0 +1,19 @@ +# 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 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.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/serialization/datastores/__init__.py b/pyagentspec/tests/serialization/datastores/__init__.py new file mode 100644 index 00000000..10451404 --- /dev/null +++ b/pyagentspec/tests/serialization/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/serialization/datastores/conftest.py b/pyagentspec/tests/serialization/datastores/conftest.py new file mode 100644 index 00000000..43caad7e --- /dev/null +++ b/pyagentspec/tests/serialization/datastores/conftest.py @@ -0,0 +1,125 @@ +# 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.datastores import ( + InMemoryCollectionDatastore, +) +from pyagentspec.datastores.oracle import ( + MTlsOracleDatabaseConnectionConfig, + OracleDatabaseDatastore, + TlsOracleDatabaseConnectionConfig, +) +from pyagentspec.datastores.postgres import ( + PostgresDatabaseDatastore, + TlsPostgresDatabaseConnectionConfig, +) +from pyagentspec.property import ( + FloatProperty, + IntegerProperty, + ObjectProperty, + Property, + StringProperty, +) + +FAKE_PASSWORD = "testpass" # nosec B105 +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={ + "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 + ) + + +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/serialization/datastores/test_datastores.py b/pyagentspec/tests/serialization/datastores/test_datastores.py new file mode 100644 index 00000000..4cc69df6 --- /dev/null +++ b/pyagentspec/tests/serialization/datastores/test_datastores.py @@ -0,0 +1,52 @@ +# 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 pyagentspec.versioning import AgentSpecVersionEnum + +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 + + +@pytest.mark.parametrize("datastore, sensitive_fields", DATASTORES_AND_THEIR_SENSITIVE_FIELDS) +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.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: 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( + yaml_content=serialized_ds, components_registry=sensitive_fields + ) diff --git a/pyagentspec/tests/serialization/test_agent.py b/pyagentspec/tests/serialization/test_agent.py index 51c541c6..6845b95f 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,80 @@ 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 +): + 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 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 v26_1_1 + vllmconfig.min_agentspec_version = AgentSpecVersionEnum.v26_1_1 + + 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 + + +@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.v26_1_0 + ) + + +def test_deserializing_agent_with_non_empty_transforms_and_unsupported_version_raises( + agent_with_non_empty_transforms, +): + serializer = AgentSpecSerializer() + serialized_agent = serializer.to_yaml(agent_with_non_empty_transforms) + assert "agentspec_version: 26.1.1" in serialized_agent + serialized_agent = serialized_agent.replace( + "agentspec_version: 26.1.1", "agentspec_version: 26.1.0" + ) + + with pytest.raises(ValueError, match="Invalid agentspec_version"): + _ = AgentSpecDeserializer().from_yaml(serialized_agent) diff --git a/pyagentspec/tests/serialization/transforms/__init__.py b/pyagentspec/tests/serialization/transforms/__init__.py new file mode 100644 index 00000000..9a5e8602 --- /dev/null +++ b/pyagentspec/tests/serialization/transforms/__init__.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 .conftest import ( + create_conversation_summarization_transform, + create_message_summarization_transform, + create_test_llm_config, +) diff --git a/pyagentspec/tests/serialization/transforms/conftest.py b/pyagentspec/tests/serialization/transforms/conftest.py new file mode 100644 index 00000000..0e65573b --- /dev/null +++ b/pyagentspec/tests/serialization/transforms/conftest.py @@ -0,0 +1,76 @@ +# 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.transforms import ConversationSummarizationTransform, MessageSummarizationTransform +from pyagentspec.versioning import AgentSpecVersionEnum + +from ..datastores import DATASTORES_AND_THEIR_SENSITIVE_FIELDS + + +def create_test_llm_config(): + config = OpenAiConfig( + id="test_llm", + name="test_openai_config", + model_id="gpt-3.5-turbo", + ) + # Set to v26_1_1 to match the serialized agentspec_version, ensuring deserialized == original + config.min_agentspec_version = AgentSpecVersionEnum.v26_1_1 + return config + + +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", + ) + + +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 new file mode 100644 index 00000000..c69d4ded --- /dev/null +++ b/pyagentspec/tests/serialization/transforms/test_summarization_transforms.py @@ -0,0 +1,65 @@ +# 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 pyagentspec.versioning import AgentSpecVersionEnum + +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 +): + 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 + + +@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.v26_1_0) + + +@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: 26.1.1" in serialized_transform + serialized_transform = serialized_transform.replace( + "agentspec_version: 26.1.1", "agentspec_version: 26.1.0" + ) + + with pytest.raises(ValueError, match="Invalid agentspec_version"): + _ = AgentSpecDeserializer().from_yaml( + yaml_content=serialized_transform, components_registry=sensitive_fields + )