How to Connect Assistants to Your Data#

python-icon Download In-Memory Script

Python script/notebook for the In-Memory Datastore example in this guide.

In-memory datastore how-to script
python-icon Download Oracle Database Script

Python script/notebook for Oracle Database Datastore example in this guide.

Oracle Database datastore how-to script

Prerequisites

This guide assumes familiarity with

Agents rely on access to relevant data to function effectively. Without a steady stream of high-quality data, AI models are unable to learn, reason, or make informed decisions. Connecting an Agent or Flow to data sources is therefore a critical step in developing functional AI systems. This connection enables agents to perceive their environment, update their knowledge or the underlying data source, and adapt to changing conditions.

In this tutorial, you will:

  • Define Entities that an Agent or Flow can access and manipulate.

  • Populate a Datastore with entities for development and testing.

  • Use Datastores in Flows and Agents to create two types of inventory management assistants.

To ensure reproducibility of this tutorial, you will use an in-memory data source. Check out the section Using Oracle Database Datastore to see how to configure an Oracle Database connection for persistent storage.

Concepts shown in this guide#

Note

The InMemoryDatastore is mainly suitable for testing and development, or other use-cases where data persistence across assistants and conversations is not a requirement. For production use-cases, the OracleDatabaseDatastore provides a robust and scalable persistence layer in Oracle Database.

Note that there are a few key differences between an in-memory and a database Datastore:

  • With database Datastores, all tables relevant to the assistant must already be created in the database prior to connecting to it.

  • You may choose to only model a subset of the tables available in the database via the Entity construct.

  • Database Datastores offer an additional query method (and the corresponding DatastoreQueryStep), that enables flexible execution of SQL queries that cannot be modelled by the list operation on the in-memory datastore

Datastores in Flows#

In this section, you will build a simple Flow that performs operations on an inventory database based on user input. This Flow helps users keep product descriptions up to date by leveraging an LLM for the creative writing component.

Step 1. Add imports and LLM configuration#

Import the required packages:

 1from textwrap import dedent
 2
 3from wayflowcore.controlconnection import ControlFlowEdge
 4from wayflowcore.dataconnection import DataFlowEdge
 5from wayflowcore.datastore import Entity, InMemoryDatastore
 6from wayflowcore.datastore.entity import nullable
 7from wayflowcore.flow import Flow
 8from wayflowcore.property import (
 9    AnyProperty,
10    DictProperty,
11    FloatProperty,
12    IntegerProperty,
13    ObjectProperty,
14    StringProperty,
15)
16from wayflowcore.steps import InputMessageStep, OutputMessageStep, PromptExecutionStep
17from wayflowcore.steps.datastoresteps import (
18    DatastoreCreateStep,
19    DatastoreDeleteStep,
20    DatastoreListStep,
21    DatastoreUpdateStep,
22)

In this assistant, you need to use an LLM. WayFlow supports several LLM API providers. Select an LLM from the options below:

from wayflowcore.models import OCIGenAIModel, OCIClientConfigWithApiKey

llm = OCIGenAIModel(
    model_id="provider.model-id",
    compartment_id="compartment-id",
    client_config=OCIClientConfigWithApiKey(
        service_endpoint="https://url-to-service-endpoint.com",
    ),
)

Important

API keys should not be stored anywhere in the code. Use environment variables and/or tools such as python-dotenv.

Step 2. Define data#

Start by defining the schema for your data using the Entity construct. In this example, you will manage products in an inventory, so a single collection is sufficient. Datastores also support managing multiple collections at the same time if needed.

 1product = Entity(
 2    properties={
 3        "ID": IntegerProperty(description="Unique product identifier"),
 4        # Descriptions can be helpful if an LLM needs to fill these fields,
 5        # or generally disambiguate non-obvious property names
 6        "title": StringProperty(description="Brief summary of the product"),
 7        "description": StringProperty(),
 8        "price": FloatProperty(default_value=0.1),
 9        # Use nullable to define optional properties
10        "category": nullable(StringProperty()),
11    },
12)

Next, create an InMemoryDatastore. For simplicity, you will use dummy data:

 1datastore = InMemoryDatastore(schema={"products": product})
 2
 3dummy_data = [
 4    {
 5        "ID": 0,
 6        "title": "Broccoli",
 7        "description": "Healthy and delicious cruciferous vegetable!",
 8        "price": 1.5,
 9        "category": "Produce",
10    },
11    {
12        "ID": 1,
13        "title": "Oranges",
14        "description": "Vitamin C-filled citrus fruits",
15        "price": 1.8,
16        "category": "Produce",
17    },
18    {
19        "ID": 2,
20        "title": "Shampoo",
21        "description": "Shiny smooth hair in just 10 applications!",
22        "price": 4.5,
23        "category": "Personal hygiene",
24    },
25    {
26        "ID": 3,
27        "title": "Crushed ice",
28        "description": "Cool any drink in seconds!",
29        "price": 4.5,
30    },
31]
32
33# Create supports both bulk-creation, as well as single element creation
34datastore.create(collection_name="products", entities=dummy_data[:-1])
35datastore.create(collection_name="products", entities=dummy_data[-1])

Step 3. Create datastore steps#

Now that the Datastore is set up, create steps to perform different operations in the flow. In this case, the assistant only needs to retrieve the current description of products and update it. See the list of all available Datastore steps for more details.

 1datastore_list_step = DatastoreListStep(
 2    datastore,
 3    name="product_list_step",
 4    collection_name="products",
 5    where={"title": "{{user_requested_product}}"},
 6    limit=1,
 7    unpack_single_entity_from_list=True,
 8)
 9
10datastore_update_step = DatastoreUpdateStep(
11    datastore,
12    name="product_update_step",
13    collection_name="products",
14    where={"title": "{{user_requested_product}}"}
15)

Key points:

  • Use where to filter which product to list and update. Configure it with a variable so that the user can dynamically choose the product title.

  • In the DatastoreListStep, limit and unpack_single_entity_from_list are used to assume that product titles are unique, making the output concise and easy to handle.

The input to the DatastoreListStep is the title of the product to retrieve, and the output is a single object containing the corresponding product data. The input to the DatastoreUpdateStep includes both the title of the product to update and the updates to apply, in the form of a dictionary containing the properties and the corresponding values. The output will be the new properties that were updated.

Step 4. Create the Flow#

Now define the Flow for this assistant.

After the user enters which product they want to update, and how the description should be updated, the datastore is queried to find the matching product. The LLM is prompted with the product details and the user’s instructions. The output is used to update the data, and the new result is returned back to the user.

Click to see the rest of the code
  1# We create the steps needed by our flow
  2USER_INPUT_STEP = "user_product_input_step"
  3USER_TASK_INPUT_STEP = "user_task_input_step"
  4LLM_REWRITE_STEP = "llm_rewrite_step"
  5USER_OUTPUT_STEP = "user_output_step"
  6
  7user_input_message_template = dedent(
  8    """I am an inventory Assistant, designed to help you keep product descriptions up-to-date.
  9    What product would you like to update? Please provide its title.
 10    """
 11)
 12
 13user_task_message_template = "How would you like to update the description? I will help you rewrite it according to your instructions"
 14
 15rewrite_description_prompt_template = dedent(
 16    """You are an inventory assistant.
 17
 18    Your task:
 19        - Based on the product details given below, rewrite the description according to the user's request
 20    Important:
 21        - Be helpful and concise in your messages
 22        - Only provide the new description as an output, and nothing else
 23
 24    Here is the User's request:
 25    - {{ user_request }}
 26
 27    Here is the product description:
 28    - {{ product }}
 29    """
 30)
 31
 32user_input_step = InputMessageStep(
 33    name=USER_INPUT_STEP,
 34    message_template=user_input_message_template,
 35)
 36
 37user_task_input_step = InputMessageStep(
 38    name=USER_TASK_INPUT_STEP,
 39    message_template=user_task_message_template,
 40)
 41
 42llm_rewrite_step = PromptExecutionStep(
 43    name=LLM_REWRITE_STEP,
 44    prompt_template=rewrite_description_prompt_template,
 45    llm=llm,
 46    input_descriptors=[
 47        DictProperty("product", key_type=StringProperty(), value_type=AnyProperty())
 48    ],
 49    output_descriptors=[
 50        ObjectProperty(
 51            name=PromptExecutionStep.OUTPUT, properties={"description": StringProperty()}
 52        )
 53    ],
 54)
 55
 56user_output_step = OutputMessageStep(
 57    name=USER_OUTPUT_STEP,
 58    message_template="The product has been updated with the following description: {{ answer['description'] }}",
 59)
 60
 61assistant = Flow(
 62    begin_step=user_input_step,
 63    control_flow_edges=[
 64        ControlFlowEdge(source_step=user_input_step, destination_step=user_task_input_step),
 65        ControlFlowEdge(source_step=user_task_input_step, destination_step=datastore_list_step),
 66        ControlFlowEdge(source_step=datastore_list_step, destination_step=llm_rewrite_step),
 67        ControlFlowEdge(source_step=llm_rewrite_step, destination_step=datastore_update_step),
 68        ControlFlowEdge(source_step=datastore_update_step, destination_step=user_output_step),
 69        ControlFlowEdge(source_step=user_output_step, destination_step=None),
 70    ],
 71    data_flow_edges=[
 72        # The first title given by the user is mapped to the datastore steps for listing and updating
 73        DataFlowEdge(
 74            user_input_step,
 75            InputMessageStep.USER_PROVIDED_INPUT,
 76            datastore_list_step,
 77            "user_requested_product",
 78        ),
 79        DataFlowEdge(
 80            user_input_step,
 81            InputMessageStep.USER_PROVIDED_INPUT,
 82            datastore_update_step,
 83            "user_requested_product",
 84        ),
 85        # The task and product detail are given to the LLM in the prompt execution step
 86        DataFlowEdge(
 87            user_task_input_step,
 88            InputMessageStep.USER_PROVIDED_INPUT,
 89            llm_rewrite_step,
 90            "user_request",
 91        ),
 92        DataFlowEdge(datastore_list_step, DatastoreListStep.ENTITIES, llm_rewrite_step, "product"),
 93        # The generated update is applied on the datastore, and echoed back to the user
 94        DataFlowEdge(
 95            llm_rewrite_step,
 96            PromptExecutionStep.OUTPUT,
 97            datastore_update_step,
 98            DatastoreUpdateStep.UPDATE,
 99        ),
100        DataFlowEdge(llm_rewrite_step, PromptExecutionStep.OUTPUT, user_output_step, "answer"),
101    ],
102)

Note the use of structured generation in the PromptExecutionStep (via the output_descriptors parameter). This ensures that the LLM generates exactly the structure expected by the DatastoreUpdateStep.

Finally, verify that the Flow works:

 1conversation = assistant.start_conversation()
 2conversation.execute()
 3conversation.append_user_message("Broccoli")
 4
 5conversation.execute()
 6conversation.append_user_message(
 7    "Shoppers don't know what 'cruciferous' means, we should find a catchier description."
 8)
 9
10conversation.execute()
11print(conversation.get_last_message().content)

Datastores in Agents#

This section assumes you have completed the previous steps on using datastores in Flows.

The Flow you built earlier is quite helpful and reliable because it performs a single, specialized task. Next, you will see how to define an Agent for inventory management when the task is not defined in advance. This Agent will be able to interpret the user’s requests and autonomously decide which actions on the Datastore are required to fulfill each task.

Step 1. Add imports and LLM configuration#

Add the additional imports needed to use Agents:

1from wayflowcore.agent import Agent

Step 2. Create Datastore Flows for an Agent#

To use the Datastore in an agent, create flows for the different operations you want the agent to perform. In the simplest setup, you can define one flow per basic Datastore operation. The agent will then determine the correct sequence of actions to achieve the user’s goal.

Define flows for:

 1AGENT_PROMPT = dedent(
 2    """
 3    You are an inventory assistant. Your task is to help the user with their requests by using the available tools.
 4    If you are unsure about the action to take, or you don't have the right tool, simply tell the user so and follow their guidance.
 5    """
 6)
 7
 8create_product_flow = Flow.from_steps(
 9    [DatastoreCreateStep(datastore, "products")],
10    name="Create product",
11    description="Creates a new product in the data source",
12)
13list_products_flow = Flow.from_steps(
14    [DatastoreListStep(datastore, "products")],
15    name="List all products",
16    description="Lists all products in the data source.",
17)
18list_one_product_flow = Flow.from_steps(
19    [datastore_list_step],
20    name="List single product",
21    description="Lists a single product in the data source by its title.",
22)
23update_product_flow = Flow.from_steps(
24    [datastore_update_step],
25    name="Update product",
26    description="Updates a product in the data source by its title.",
27)
28delete_product_flow = Flow.from_steps(
29    [DatastoreDeleteStep(datastore, "products", where={"title": "{{product_title}}"})],
30    name="Delete product",
31    description="Delete a product in the data source by its title.",
32)

Notice the provided descriptions for the flows to help the agent understand the objective of each operation.

Additionally, you can incorporate more complex behaviors into these flows. For example, you could ask for user confirmation before deleting entities, or you could provide the user with an overview, and an editing option of the updates made by the agent before they are applied.

Step 3. Create the Agent#

Finally, create the inventory management agent by combining the LLM, the datastore flows, and a custom instruction:

 1agent = Agent(
 2    llm=llm,
 3    flows=[
 4        create_product_flow,
 5        list_products_flow,
 6        list_one_product_flow,
 7        update_product_flow,
 8        delete_product_flow,
 9    ],
10    custom_instruction=AGENT_PROMPT,
11)

This agent can now respond to the user and perform actions on the data on their behalf.

Refer to the WayFlow Agents Tutorial to see how to run this Agent.

Agent Spec Exporting/Loading#

You can export the assistant configuration to its Agent Spec configuration using the AgentSpecExporter.

from wayflowcore.agentspec import AgentSpecExporter

serialized_agent = AgentSpecExporter().to_json(agent)

Here is what the Agent Spec representation will look like ↓

Click here to see the assistant configuration.
{
  "component_type": "ExtendedAgent",
  "id": "99f1d854-0f10-473a-ad2a-3558b8977244",
  "name": "agent_d18c1f13__auto",
  "description": "",
  "metadata": {
    "__metadata_info__": {}
  },
  "inputs": [],
  "outputs": [],
  "llm_config": {
    "component_type": "VllmConfig",
    "id": "b10b47da-03bc-4bb3-a024-b2d8330a5be8",
    "name": "LLAMA_MODEL_ID",
    "description": null,
    "metadata": {
      "__metadata_info__": {}
    },
    "default_generation_parameters": null,
    "url": "LLAMA_API_URL",
    "model_id": "LLAMA_MODEL_ID"
  },
  "system_prompt": "\nYou are an inventory assistant. Your task is to help the user with their requests by using the available tools.\nIf you are unsure about the action to take, or you don't have the right tool, simply tell the user so and follow their guidance.\n",
  "tools": [],
  "toolboxes": [],
  "context_providers": null,
  "can_finish_conversation": false,
  "max_iterations": 10,
  "initial_message": "Hi! How can I help you?",
  "caller_input_mode": "always",
  "agents": [],
  "flows": [
    {
      "component_type": "Flow",
      "id": "96d3782c-552c-429c-bd7e-4f4af61ff713",
      "name": "Create product",
      "description": "Creates a new product in the data source",
      "metadata": {
        "__metadata_info__": {}
      },
      "inputs": [
        {
          "type": "object",
          "additionalProperties": {},
          "key_type": {
            "type": "string"
          },
          "title": "entity"
        }
      ],
      "outputs": [
        {
          "type": "object",
          "additionalProperties": {},
          "key_type": {
            "type": "string"
          },
          "title": "created_entity"
        }
      ],
      "start_node": {
        "$component_ref": "79b6331a-5543-4310-8d8b-1d9f1337b025"
      },
      "nodes": [
        {
          "$component_ref": "331ca41a-8738-4751-a87e-5cc48b8a3e23"
        },
        {
          "$component_ref": "79b6331a-5543-4310-8d8b-1d9f1337b025"
        },
        {
          "$component_ref": "ae2f0460-cba8-499e-a993-62b946e56d40"
        }
      ],
      "control_flow_connections": [
        {
          "component_type": "ControlFlowEdge",
          "id": "fe4ef560-e504-4dc8-a298-7dbb3c067a6f",
          "name": "__StartStep___to_step_0_control_flow_edge",
          "description": null,
          "metadata": {
            "__metadata_info__": {}
          },
          "from_node": {
            "$component_ref": "79b6331a-5543-4310-8d8b-1d9f1337b025"
          },
          "from_branch": null,
          "to_node": {
            "$component_ref": "331ca41a-8738-4751-a87e-5cc48b8a3e23"
          }
        },
        {
          "component_type": "ControlFlowEdge",
          "id": "a16d091c-0d0c-4d29-9104-7761bdbb4cd3",
          "name": "step_0_to_None End node_control_flow_edge",
          "description": null,
          "metadata": {},
          "from_node": {
            "$component_ref": "331ca41a-8738-4751-a87e-5cc48b8a3e23"
          },
          "from_branch": null,
          "to_node": {
            "$component_ref": "ae2f0460-cba8-499e-a993-62b946e56d40"
          }
        }
      ],
      "data_flow_connections": [
        {
          "component_type": "DataFlowEdge",
          "id": "58dbd841-e0a5-4c87-ac0c-08a34625323b",
          "name": "__StartStep___entity_to_step_0_entity_data_flow_edge",
          "description": null,
          "metadata": {
            "__metadata_info__": {}
          },
          "source_node": {
            "$component_ref": "79b6331a-5543-4310-8d8b-1d9f1337b025"
          },
          "source_output": "entity",
          "destination_node": {
            "$component_ref": "331ca41a-8738-4751-a87e-5cc48b8a3e23"
          },
          "destination_input": "entity"
        },
        {
          "component_type": "DataFlowEdge",
          "id": "92415266-d5a4-40ca-b8e6-57e01ec002a5",
          "name": "step_0_created_entity_to_None End node_created_entity_data_flow_edge",
          "description": null,
          "metadata": {},
          "source_node": {
            "$component_ref": "331ca41a-8738-4751-a87e-5cc48b8a3e23"
          },
          "source_output": "created_entity",
          "destination_node": {
            "$component_ref": "ae2f0460-cba8-499e-a993-62b946e56d40"
          },
          "destination_input": "created_entity"
        }
      ],
      "$referenced_components": {
        "331ca41a-8738-4751-a87e-5cc48b8a3e23": {
          "component_type": "PluginDatastoreCreateNode",
          "id": "331ca41a-8738-4751-a87e-5cc48b8a3e23",
          "name": "step_0",
          "description": "",
          "metadata": {
            "__metadata_info__": {}
          },
          "inputs": [
            {
              "type": "object",
              "additionalProperties": {},
              "key_type": {
                "type": "string"
              },
              "title": "entity"
            }
          ],
          "outputs": [
            {
              "type": "object",
              "additionalProperties": {},
              "key_type": {
                "type": "string"
              },
              "title": "created_entity"
            }
          ],
          "branches": [
            "next"
          ],
          "input_mapping": {},
          "output_mapping": {},
          "datastore": {
            "$component_ref": "4f8e1008-2f20-456c-9250-679146e88192"
          },
          "collection_name": "products",
          "ENTITY": "entity",
          "CREATED_ENTITY": "created_entity",
          "component_plugin_name": "DatastorePlugin",
          "component_plugin_version": "25.4.0.dev0"
        },
        "79b6331a-5543-4310-8d8b-1d9f1337b025": {
          "component_type": "StartNode",
          "id": "79b6331a-5543-4310-8d8b-1d9f1337b025",
          "name": "__StartStep__",
          "description": "",
          "metadata": {
            "__metadata_info__": {}
          },
          "inputs": [
            {
              "type": "object",
              "additionalProperties": {},
              "key_type": {
                "type": "string"
              },
              "title": "entity"
            }
          ],
          "outputs": [
            {
              "type": "object",
              "additionalProperties": {},
              "key_type": {
                "type": "string"
              },
              "title": "entity"
            }
          ],
          "branches": [
            "next"
          ]
        },
        "ae2f0460-cba8-499e-a993-62b946e56d40": {
          "component_type": "EndNode",
          "id": "ae2f0460-cba8-499e-a993-62b946e56d40",
          "name": "None End node",
          "description": "End node representing all transitions to None in the WayFlow flow",
          "metadata": {},
          "inputs": [
            {
              "type": "object",
              "additionalProperties": {},
              "key_type": {
                "type": "string"
              },
              "title": "created_entity"
            }
          ],
          "outputs": [
            {
              "type": "object",
              "additionalProperties": {},
              "key_type": {
                "type": "string"
              },
              "title": "created_entity"
            }
          ],
          "branches": [],
          "branch_name": "next"
        }
      }
    },
    {
      "component_type": "Flow",
      "id": "ba1cd504-bef6-4af3-9498-cbf6e50a0bca",
      "name": "List all products",
      "description": "Lists all products in the data source.",
      "metadata": {
        "__metadata_info__": {}
      },
      "inputs": [],
      "outputs": [
        {
          "type": "array",
          "items": {
            "type": "object",
            "additionalProperties": {},
            "key_type": {
              "type": "string"
            }
          },
          "title": "entities"
        }
      ],
      "start_node": {
        "$component_ref": "bb1b722a-0aae-4e09-b2a4-cbcdb93b2cff"
      },
      "nodes": [
        {
          "$component_ref": "0c924103-23f5-4698-b69e-c553250186ab"
        },
        {
          "$component_ref": "bb1b722a-0aae-4e09-b2a4-cbcdb93b2cff"
        },
        {
          "$component_ref": "098f2832-4672-44eb-a060-ea9514ceefc5"
        }
      ],
      "control_flow_connections": [
        {
          "component_type": "ControlFlowEdge",
          "id": "a0392cfe-bed3-44cb-84c0-20a30f1cbe18",
          "name": "__StartStep___to_step_0_control_flow_edge",
          "description": null,
          "metadata": {
            "__metadata_info__": {}
          },
          "from_node": {
            "$component_ref": "bb1b722a-0aae-4e09-b2a4-cbcdb93b2cff"
          },
          "from_branch": null,
          "to_node": {
            "$component_ref": "0c924103-23f5-4698-b69e-c553250186ab"
          }
        },
        {
          "component_type": "ControlFlowEdge",
          "id": "3290795e-59ca-46f8-8a96-5a265d707c13",
          "name": "step_0_to_None End node_control_flow_edge",
          "description": null,
          "metadata": {},
          "from_node": {
            "$component_ref": "0c924103-23f5-4698-b69e-c553250186ab"
          },
          "from_branch": null,
          "to_node": {
            "$component_ref": "098f2832-4672-44eb-a060-ea9514ceefc5"
          }
        }
      ],
      "data_flow_connections": [
        {
          "component_type": "DataFlowEdge",
          "id": "308b92b6-70f4-4e9c-b41e-1563bc449a24",
          "name": "step_0_entities_to_None End node_entities_data_flow_edge",
          "description": null,
          "metadata": {},
          "source_node": {
            "$component_ref": "0c924103-23f5-4698-b69e-c553250186ab"
          },
          "source_output": "entities",
          "destination_node": {
            "$component_ref": "098f2832-4672-44eb-a060-ea9514ceefc5"
          },
          "destination_input": "entities"
        }
      ],
      "$referenced_components": {
        "098f2832-4672-44eb-a060-ea9514ceefc5": {
          "component_type": "EndNode",
          "id": "098f2832-4672-44eb-a060-ea9514ceefc5",
          "name": "None End node",
          "description": "End node representing all transitions to None in the WayFlow flow",
          "metadata": {},
          "inputs": [
            {
              "type": "array",
              "items": {
                "type": "object",
                "additionalProperties": {},
                "key_type": {
                  "type": "string"
                }
              },
              "title": "entities"
            }
          ],
          "outputs": [
            {
              "type": "array",
              "items": {
                "type": "object",
                "additionalProperties": {},
                "key_type": {
                  "type": "string"
                }
              },
              "title": "entities"
            }
          ],
          "branches": [],
          "branch_name": "next"
        },
        "0c924103-23f5-4698-b69e-c553250186ab": {
          "component_type": "PluginDatastoreListNode",
          "id": "0c924103-23f5-4698-b69e-c553250186ab",
          "name": "step_0",
          "description": "",
          "metadata": {
            "__metadata_info__": {}
          },
          "inputs": [],
          "outputs": [
            {
              "type": "array",
              "items": {
                "type": "object",
                "additionalProperties": {},
                "key_type": {
                  "type": "string"
                }
              },
              "title": "entities"
            }
          ],
          "branches": [
            "next"
          ],
          "input_mapping": {},
          "output_mapping": {},
          "datastore": {
            "$component_ref": "4f8e1008-2f20-456c-9250-679146e88192"
          },
          "collection_name": "products",
          "where": null,
          "limit": null,
          "unpack_single_entity_from_list": false,
          "ENTITIES": "entities",
          "component_plugin_name": "DatastorePlugin",
          "component_plugin_version": "25.4.0.dev0"
        },
        "bb1b722a-0aae-4e09-b2a4-cbcdb93b2cff": {
          "component_type": "StartNode",
          "id": "bb1b722a-0aae-4e09-b2a4-cbcdb93b2cff",
          "name": "__StartStep__",
          "description": "",
          "metadata": {
            "__metadata_info__": {}
          },
          "inputs": [],
          "outputs": [],
          "branches": [
            "next"
          ]
        }
      }
    },
    {
      "component_type": "Flow",
      "id": "bad00138-48b3-4e7c-84a9-51bd385a38a2",
      "name": "List single product",
      "description": "Lists a single product in the data source by its title.",
      "metadata": {
        "__metadata_info__": {}
      },
      "inputs": [
        {
          "description": "\"user_requested_product\" input variable for the template",
          "type": "string",
          "title": "user_requested_product"
        }
      ],
      "outputs": [
        {
          "type": "object",
          "additionalProperties": {},
          "key_type": {
            "type": "string"
          },
          "title": "entities"
        }
      ],
      "start_node": {
        "$component_ref": "76d19c9a-241f-4a4d-bbac-2bcbd3359498"
      },
      "nodes": [
        {
          "$component_ref": "d1b1f436-b91f-435c-867c-ecf9b5b187a1"
        },
        {
          "$component_ref": "76d19c9a-241f-4a4d-bbac-2bcbd3359498"
        },
        {
          "$component_ref": "aa8090d6-2220-40ad-ab16-6adb1bdead7b"
        }
      ],
      "control_flow_connections": [
        {
          "component_type": "ControlFlowEdge",
          "id": "e37ae190-e7f6-4090-8091-c5eddadf7a9d",
          "name": "__StartStep___to_product_list_step_control_flow_edge",
          "description": null,
          "metadata": {
            "__metadata_info__": {}
          },
          "from_node": {
            "$component_ref": "76d19c9a-241f-4a4d-bbac-2bcbd3359498"
          },
          "from_branch": null,
          "to_node": {
            "$component_ref": "d1b1f436-b91f-435c-867c-ecf9b5b187a1"
          }
        },
        {
          "component_type": "ControlFlowEdge",
          "id": "80680849-e51b-435d-aee6-cdb7e6571ee7",
          "name": "product_list_step_to_None End node_control_flow_edge",
          "description": null,
          "metadata": {},
          "from_node": {
            "$component_ref": "d1b1f436-b91f-435c-867c-ecf9b5b187a1"
          },
          "from_branch": null,
          "to_node": {
            "$component_ref": "aa8090d6-2220-40ad-ab16-6adb1bdead7b"
          }
        }
      ],
      "data_flow_connections": [
        {
          "component_type": "DataFlowEdge",
          "id": "dab6eb95-057c-4adc-85dd-2a40e14713eb",
          "name": "__StartStep___user_requested_product_to_product_list_step_user_requested_product_data_flow_edge",
          "description": null,
          "metadata": {
            "__metadata_info__": {}
          },
          "source_node": {
            "$component_ref": "76d19c9a-241f-4a4d-bbac-2bcbd3359498"
          },
          "source_output": "user_requested_product",
          "destination_node": {
            "$component_ref": "d1b1f436-b91f-435c-867c-ecf9b5b187a1"
          },
          "destination_input": "user_requested_product"
        },
        {
          "component_type": "DataFlowEdge",
          "id": "82d0a256-6c3d-45a8-a6bf-c1c949069927",
          "name": "product_list_step_entities_to_None End node_entities_data_flow_edge",
          "description": null,
          "metadata": {},
          "source_node": {
            "$component_ref": "d1b1f436-b91f-435c-867c-ecf9b5b187a1"
          },
          "source_output": "entities",
          "destination_node": {
            "$component_ref": "aa8090d6-2220-40ad-ab16-6adb1bdead7b"
          },
          "destination_input": "entities"
        }
      ],
      "$referenced_components": {
        "d1b1f436-b91f-435c-867c-ecf9b5b187a1": {
          "component_type": "PluginDatastoreListNode",
          "id": "d1b1f436-b91f-435c-867c-ecf9b5b187a1",
          "name": "product_list_step",
          "description": "",
          "metadata": {
            "__metadata_info__": {}
          },
          "inputs": [
            {
              "description": "\"user_requested_product\" input variable for the template",
              "type": "string",
              "title": "user_requested_product"
            }
          ],
          "outputs": [
            {
              "type": "object",
              "additionalProperties": {},
              "key_type": {
                "type": "string"
              },
              "title": "entities"
            }
          ],
          "branches": [
            "next"
          ],
          "input_mapping": {},
          "output_mapping": {},
          "datastore": {
            "$component_ref": "4f8e1008-2f20-456c-9250-679146e88192"
          },
          "collection_name": "products",
          "where": {
            "title": "{{user_requested_product}}"
          },
          "limit": 1,
          "unpack_single_entity_from_list": true,
          "ENTITIES": "entities",
          "component_plugin_name": "DatastorePlugin",
          "component_plugin_version": "25.4.0.dev0"
        },
        "76d19c9a-241f-4a4d-bbac-2bcbd3359498": {
          "component_type": "StartNode",
          "id": "76d19c9a-241f-4a4d-bbac-2bcbd3359498",
          "name": "__StartStep__",
          "description": "",
          "metadata": {
            "__metadata_info__": {}
          },
          "inputs": [
            {
              "description": "\"user_requested_product\" input variable for the template",
              "type": "string",
              "title": "user_requested_product"
            }
          ],
          "outputs": [
            {
              "description": "\"user_requested_product\" input variable for the template",
              "type": "string",
              "title": "user_requested_product"
            }
          ],
          "branches": [
            "next"
          ]
        },
        "aa8090d6-2220-40ad-ab16-6adb1bdead7b": {
          "component_type": "EndNode",
          "id": "aa8090d6-2220-40ad-ab16-6adb1bdead7b",
          "name": "None End node",
          "description": "End node representing all transitions to None in the WayFlow flow",
          "metadata": {},
          "inputs": [
            {
              "type": "object",
              "additionalProperties": {},
              "key_type": {
                "type": "string"
              },
              "title": "entities"
            }
          ],
          "outputs": [
            {
              "type": "object",
              "additionalProperties": {},
              "key_type": {
                "type": "string"
              },
              "title": "entities"
            }
          ],
          "branches": [],
          "branch_name": "next"
        }
      }
    },
    {
      "component_type": "Flow",
      "id": "a3047067-dea3-4cc9-a922-c26d35d97c48",
      "name": "Update product",
      "description": "Updates a product in the data source by its title.",
      "metadata": {
        "__metadata_info__": {}
      },
      "inputs": [
        {
          "description": "\"user_requested_product\" input variable for the template",
          "type": "string",
          "title": "user_requested_product"
        },
        {
          "type": "object",
          "additionalProperties": {},
          "key_type": {
            "type": "string"
          },
          "title": "update"
        }
      ],
      "outputs": [
        {
          "type": "array",
          "items": {
            "type": "object",
            "additionalProperties": {},
            "key_type": {
              "type": "string"
            }
          },
          "title": "entities"
        }
      ],
      "start_node": {
        "$component_ref": "8c74a4c5-fdf0-447e-956f-59498da1dc09"
      },
      "nodes": [
        {
          "$component_ref": "487a708b-0553-4bb5-b015-948d2a97ede6"
        },
        {
          "$component_ref": "8c74a4c5-fdf0-447e-956f-59498da1dc09"
        },
        {
          "$component_ref": "9074c861-1cf4-4bd9-88dd-3fe209b2c218"
        }
      ],
      "control_flow_connections": [
        {
          "component_type": "ControlFlowEdge",
          "id": "64436400-797d-4d35-92df-78fc557006f2",
          "name": "__StartStep___to_product_update_step_control_flow_edge",
          "description": null,
          "metadata": {
            "__metadata_info__": {}
          },
          "from_node": {
            "$component_ref": "8c74a4c5-fdf0-447e-956f-59498da1dc09"
          },
          "from_branch": null,
          "to_node": {
            "$component_ref": "487a708b-0553-4bb5-b015-948d2a97ede6"
          }
        },
        {
          "component_type": "ControlFlowEdge",
          "id": "5e55b5d7-7bf8-435a-9044-f05f1b377671",
          "name": "product_update_step_to_None End node_control_flow_edge",
          "description": null,
          "metadata": {},
          "from_node": {
            "$component_ref": "487a708b-0553-4bb5-b015-948d2a97ede6"
          },
          "from_branch": null,
          "to_node": {
            "$component_ref": "9074c861-1cf4-4bd9-88dd-3fe209b2c218"
          }
        }
      ],
      "data_flow_connections": [
        {
          "component_type": "DataFlowEdge",
          "id": "43c29773-0a4b-4145-9c0d-ad5238410e9d",
          "name": "__StartStep___user_requested_product_to_product_update_step_user_requested_product_data_flow_edge",
          "description": null,
          "metadata": {
            "__metadata_info__": {}
          },
          "source_node": {
            "$component_ref": "8c74a4c5-fdf0-447e-956f-59498da1dc09"
          },
          "source_output": "user_requested_product",
          "destination_node": {
            "$component_ref": "487a708b-0553-4bb5-b015-948d2a97ede6"
          },
          "destination_input": "user_requested_product"
        },
        {
          "component_type": "DataFlowEdge",
          "id": "8e5f0ea8-8ea1-42ba-bdba-95a74082c908",
          "name": "__StartStep___update_to_product_update_step_update_data_flow_edge",
          "description": null,
          "metadata": {
            "__metadata_info__": {}
          },
          "source_node": {
            "$component_ref": "8c74a4c5-fdf0-447e-956f-59498da1dc09"
          },
          "source_output": "update",
          "destination_node": {
            "$component_ref": "487a708b-0553-4bb5-b015-948d2a97ede6"
          },
          "destination_input": "update"
        },
        {
          "component_type": "DataFlowEdge",
          "id": "1bd34870-1e52-4896-bfaf-5a71006da687",
          "name": "product_update_step_entities_to_None End node_entities_data_flow_edge",
          "description": null,
          "metadata": {},
          "source_node": {
            "$component_ref": "487a708b-0553-4bb5-b015-948d2a97ede6"
          },
          "source_output": "entities",
          "destination_node": {
            "$component_ref": "9074c861-1cf4-4bd9-88dd-3fe209b2c218"
          },
          "destination_input": "entities"
        }
      ],
      "$referenced_components": {
        "487a708b-0553-4bb5-b015-948d2a97ede6": {
          "component_type": "PluginDatastoreUpdateNode",
          "id": "487a708b-0553-4bb5-b015-948d2a97ede6",
          "name": "product_update_step",
          "description": "",
          "metadata": {
            "__metadata_info__": {}
          },
          "inputs": [
            {
              "description": "\"user_requested_product\" input variable for the template",
              "type": "string",
              "title": "user_requested_product"
            },
            {
              "type": "object",
              "additionalProperties": {},
              "key_type": {
                "type": "string"
              },
              "title": "update"
            }
          ],
          "outputs": [
            {
              "type": "array",
              "items": {
                "type": "object",
                "additionalProperties": {},
                "key_type": {
                  "type": "string"
                }
              },
              "title": "entities"
            }
          ],
          "branches": [
            "next"
          ],
          "input_mapping": {},
          "output_mapping": {},
          "datastore": {
            "$component_ref": "4f8e1008-2f20-456c-9250-679146e88192"
          },
          "collection_name": "products",
          "where": {
            "title": "{{user_requested_product}}"
          },
          "ENTITIES": "entities",
          "UPDATE": "update",
          "component_plugin_name": "DatastorePlugin",
          "component_plugin_version": "25.4.0.dev0"
        },
        "8c74a4c5-fdf0-447e-956f-59498da1dc09": {
          "component_type": "StartNode",
          "id": "8c74a4c5-fdf0-447e-956f-59498da1dc09",
          "name": "__StartStep__",
          "description": "",
          "metadata": {
            "__metadata_info__": {}
          },
          "inputs": [
            {
              "description": "\"user_requested_product\" input variable for the template",
              "type": "string",
              "title": "user_requested_product"
            },
            {
              "type": "object",
              "additionalProperties": {},
              "key_type": {
                "type": "string"
              },
              "title": "update"
            }
          ],
          "outputs": [
            {
              "description": "\"user_requested_product\" input variable for the template",
              "type": "string",
              "title": "user_requested_product"
            },
            {
              "type": "object",
              "additionalProperties": {},
              "key_type": {
                "type": "string"
              },
              "title": "update"
            }
          ],
          "branches": [
            "next"
          ]
        },
        "9074c861-1cf4-4bd9-88dd-3fe209b2c218": {
          "component_type": "EndNode",
          "id": "9074c861-1cf4-4bd9-88dd-3fe209b2c218",
          "name": "None End node",
          "description": "End node representing all transitions to None in the WayFlow flow",
          "metadata": {},
          "inputs": [
            {
              "type": "array",
              "items": {
                "type": "object",
                "additionalProperties": {},
                "key_type": {
                  "type": "string"
                }
              },
              "title": "entities"
            }
          ],
          "outputs": [
            {
              "type": "array",
              "items": {
                "type": "object",
                "additionalProperties": {},
                "key_type": {
                  "type": "string"
                }
              },
              "title": "entities"
            }
          ],
          "branches": [],
          "branch_name": "next"
        }
      }
    },
    {
      "component_type": "Flow",
      "id": "ebae9461-df3a-4544-9cfd-dd2a2e13e34e",
      "name": "Delete product",
      "description": "Delete a product in the data source by its title.",
      "metadata": {
        "__metadata_info__": {}
      },
      "inputs": [
        {
          "description": "\"product_title\" input variable for the template",
          "type": "string",
          "title": "product_title"
        }
      ],
      "outputs": [],
      "start_node": {
        "$component_ref": "13d561e4-1c49-4808-974c-d82311479998"
      },
      "nodes": [
        {
          "$component_ref": "9cd4c282-5290-477d-b9c1-f069a599aa68"
        },
        {
          "$component_ref": "13d561e4-1c49-4808-974c-d82311479998"
        },
        {
          "$component_ref": "49384f2b-1825-4c6b-a876-86e8803b0014"
        }
      ],
      "control_flow_connections": [
        {
          "component_type": "ControlFlowEdge",
          "id": "7b91ecf7-f24e-4ff3-8df4-f6769847e7a4",
          "name": "__StartStep___to_step_0_control_flow_edge",
          "description": null,
          "metadata": {
            "__metadata_info__": {}
          },
          "from_node": {
            "$component_ref": "13d561e4-1c49-4808-974c-d82311479998"
          },
          "from_branch": null,
          "to_node": {
            "$component_ref": "9cd4c282-5290-477d-b9c1-f069a599aa68"
          }
        },
        {
          "component_type": "ControlFlowEdge",
          "id": "e666dfe3-6041-4141-b21b-afedfa74f3ab",
          "name": "step_0_to_None End node_control_flow_edge",
          "description": null,
          "metadata": {},
          "from_node": {
            "$component_ref": "9cd4c282-5290-477d-b9c1-f069a599aa68"
          },
          "from_branch": null,
          "to_node": {
            "$component_ref": "49384f2b-1825-4c6b-a876-86e8803b0014"
          }
        }
      ],
      "data_flow_connections": [
        {
          "component_type": "DataFlowEdge",
          "id": "a731fb73-ad3b-4a22-9f0b-35e9f1d63131",
          "name": "__StartStep___product_title_to_step_0_product_title_data_flow_edge",
          "description": null,
          "metadata": {
            "__metadata_info__": {}
          },
          "source_node": {
            "$component_ref": "13d561e4-1c49-4808-974c-d82311479998"
          },
          "source_output": "product_title",
          "destination_node": {
            "$component_ref": "9cd4c282-5290-477d-b9c1-f069a599aa68"
          },
          "destination_input": "product_title"
        }
      ],
      "$referenced_components": {
        "9cd4c282-5290-477d-b9c1-f069a599aa68": {
          "component_type": "PluginDatastoreDeleteNode",
          "id": "9cd4c282-5290-477d-b9c1-f069a599aa68",
          "name": "step_0",
          "description": "",
          "metadata": {
            "__metadata_info__": {}
          },
          "inputs": [
            {
              "description": "\"product_title\" input variable for the template",
              "type": "string",
              "title": "product_title"
            }
          ],
          "outputs": [],
          "branches": [
            "next"
          ],
          "input_mapping": {},
          "output_mapping": {},
          "datastore": {
            "$component_ref": "4f8e1008-2f20-456c-9250-679146e88192"
          },
          "collection_name": "products",
          "where": {
            "title": "{{product_title}}"
          },
          "component_plugin_name": "DatastorePlugin",
          "component_plugin_version": "25.4.0.dev0"
        },
        "13d561e4-1c49-4808-974c-d82311479998": {
          "component_type": "StartNode",
          "id": "13d561e4-1c49-4808-974c-d82311479998",
          "name": "__StartStep__",
          "description": "",
          "metadata": {
            "__metadata_info__": {}
          },
          "inputs": [
            {
              "description": "\"product_title\" input variable for the template",
              "type": "string",
              "title": "product_title"
            }
          ],
          "outputs": [
            {
              "description": "\"product_title\" input variable for the template",
              "type": "string",
              "title": "product_title"
            }
          ],
          "branches": [
            "next"
          ]
        },
        "49384f2b-1825-4c6b-a876-86e8803b0014": {
          "component_type": "EndNode",
          "id": "49384f2b-1825-4c6b-a876-86e8803b0014",
          "name": "None End node",
          "description": "End node representing all transitions to None in the WayFlow flow",
          "metadata": {},
          "inputs": [],
          "outputs": [],
          "branches": [],
          "branch_name": "next"
        }
      }
    }
  ],
  "agent_template": {
    "component_type": "PluginPromptTemplate",
    "id": "ea261004-a6d8-4380-af2a-4e5adbaf7b9c",
    "name": "",
    "description": null,
    "metadata": {
      "__metadata_info__": {}
    },
    "messages": [
      {
        "role": "system",
        "contents": [
          {
            "type": "text",
            "content": "{%- if __TOOLS__ -%}\nEnvironment: ipython\nCutting Knowledge Date: December 2023\n\nYou are a helpful assistant with tool calling capabilities. Only reply with a tool call if the function exists in the library provided by the user. If it doesn't exist, just reply directly in natural language. When you receive a tool call response, use the output to format an answer to the original user question.\n\nYou have access to the following functions. To call a function, please respond with JSON for a function call.\nRespond in the format {\"name\": function name, \"parameters\": dictionary of argument name and its value}.\nDo not use variables.\n\n[{% for tool in __TOOLS__%}{{tool.to_openai_format() | tojson}}{{', ' if not loop.last}}{% endfor %}]\n{%- endif -%}\n"
          }
        ],
        "tool_requests": null,
        "tool_result": null,
        "display_only": false,
        "sender": null,
        "recipients": [],
        "time_created": "2025-09-02T15:58:42.673848+00:00",
        "time_updated": "2025-09-02T15:58:42.673852+00:00"
      },
      {
        "role": "system",
        "contents": [
          {
            "type": "text",
            "content": "{%- if custom_instruction -%}Additional instructions:\n{{custom_instruction}}{%- endif -%}"
          }
        ],
        "tool_requests": null,
        "tool_result": null,
        "display_only": false,
        "sender": null,
        "recipients": [],
        "time_created": "2025-09-02T15:58:42.673915+00:00",
        "time_updated": "2025-09-02T15:58:42.673916+00:00"
      },
      {
        "role": "user",
        "contents": [],
        "tool_requests": null,
        "tool_result": null,
        "display_only": false,
        "sender": null,
        "recipients": [],
        "time_created": "2025-09-02T15:58:42.660189+00:00",
        "time_updated": "2025-09-02T15:58:42.660446+00:00"
      },
      {
        "role": "system",
        "contents": [
          {
            "type": "text",
            "content": "{% if __PLAN__ %}The current plan you should follow is the following: \n{{__PLAN__}}{% endif %}"
          }
        ],
        "tool_requests": null,
        "tool_result": null,
        "display_only": false,
        "sender": null,
        "recipients": [],
        "time_created": "2025-09-02T15:58:42.673985+00:00",
        "time_updated": "2025-09-02T15:58:42.673986+00:00"
      }
    ],
    "output_parser": {
      "component_type": "PluginJsonToolOutputParser",
      "id": "8d8b00f0-2c13-4e0f-894c-a46ad91eccbc",
      "name": "jsontool_outputparser",
      "description": null,
      "metadata": {
        "__metadata_info__": {}
      },
      "tools": null,
      "component_plugin_name": "OutputParserPlugin",
      "component_plugin_version": "25.4.0.dev0"
    },
    "inputs": [
      {
        "description": "\"__TOOLS__\" input variable for the template",
        "title": "__TOOLS__"
      },
      {
        "description": "\"custom_instruction\" input variable for the template",
        "type": "string",
        "title": "custom_instruction"
      },
      {
        "description": "\"__PLAN__\" input variable for the template",
        "type": "string",
        "title": "__PLAN__",
        "default": ""
      },
      {
        "type": "array",
        "items": {},
        "title": "__CHAT_HISTORY__"
      }
    ],
    "pre_rendering_transforms": null,
    "post_rendering_transforms": [
      {
        "component_type": "PluginRemoveEmptyNonUserMessageTransform",
        "id": "f964b648-4850-43ca-983b-6bf05ec6fe59",
        "name": "removeemptynonusermessage_messagetransform",
        "description": null,
        "metadata": {
          "__metadata_info__": {}
        },
        "component_plugin_name": "MessageTransformPlugin",
        "component_plugin_version": "25.4.0.dev0"
      },
      {
        "component_type": "PluginCoalesceSystemMessagesTransform",
        "id": "b0c702f1-14a8-4692-b746-490f3cc2a406",
        "name": "coalescesystemmessage_messagetransform",
        "description": null,
        "metadata": {
          "__metadata_info__": {}
        },
        "component_plugin_name": "MessageTransformPlugin",
        "component_plugin_version": "25.4.0.dev0"
      },
      {
        "component_type": "PluginLlamaMergeToolRequestAndCallsTransform",
        "id": "466f3fef-697b-4e2e-804b-cc9788186b0d",
        "name": "llamamergetoolrequestandcalls_messagetransform",
        "description": null,
        "metadata": {
          "__metadata_info__": {}
        },
        "component_plugin_name": "MessageTransformPlugin",
        "component_plugin_version": "25.4.0.dev0"
      }
    ],
    "tools": null,
    "native_tool_calling": false,
    "response_format": null,
    "native_structured_generation": true,
    "generation_config": null,
    "component_plugin_name": "PromptTemplatePlugin",
    "component_plugin_version": "25.4.0.dev0"
  },
  "component_plugin_name": "AgentPlugin",
  "component_plugin_version": "25.4.0.dev0",
  "$referenced_components": {
    "4f8e1008-2f20-456c-9250-679146e88192": {
      "component_type": "PluginInMemoryDatastore",
      "id": "4f8e1008-2f20-456c-9250-679146e88192",
      "name": "PluginInMemoryDatastore",
      "description": null,
      "metadata": {},
      "datastore_schema": {
        "products": {
          "description": "",
          "title": "",
          "properties": {
            "description": {
              "type": "string"
            },
            "ID": {
              "description": "Unique product identifier",
              "type": "integer"
            },
            "title": {
              "description": "Brief summary of the product",
              "type": "string"
            },
            "price": {
              "type": "number",
              "default": 0.1
            },
            "category": {
              "anyOf": [
                {
                  "type": "null"
                },
                {
                  "type": "string"
                }
              ],
              "default": null
            }
          }
        }
      },
      "component_plugin_name": "DatastorePlugin",
      "component_plugin_version": "25.4.0.dev0"
    }
  },
  "agentspec_version": "25.4.1"
}

You can then load the configuration back to an assistant using the AgentSpecLoader.

from wayflowcore.agentspec import AgentSpecLoader

agent = AgentSpecLoader().load_json(serialized_agent)

Note

This guide uses the following extension/plugin Agent Spec components:

  • PluginPromptTemplate

  • PluginDatastoreCreateNode

  • PluginDatastoreUpdateNode

  • PluginDatastoreListNode

  • PluginDatastoreDeleteNode

  • PluginJsonToolOutputParser

  • PluginRemoveEmptyNonUserMessageTransform

  • PluginCoalesceSystemMessagesTransform

  • PluginLlamaMergeToolRequestAndCallsTransform

  • PluginInMemoryDatastore

  • ExtendedAgent

See the list of available Agent Spec extension/plugin components in the API Reference

Using Oracle Database Datastore#

This guide mirrors the earlier in-memory demo, but leverages Oracle Database (Autonomous Database on OCI or an on-prem instance) for persistent storage.

Prerequisites

  • An Oracle Database instance, reachable from your development machine

  • Wallet files and/or database credentials for this instance

  • A running LLM endpoint (the examples uses vLLM, but any provider works)

Step 1. Configure the connection to the Database#

WayFlow supports two secure transport mechanisms for connecting to Oracle databases.

When using TLS (one-way TLS) the database presents its certificate, the client verifies it, and the user authenticates with username and password.

1connection_config = TlsOracleDatabaseConnectionConfig(
2    user="<db user>",  # Replace with your DB user
3    password="<db password>",  # Replace with your DB password  # nosec: this is just a placeholder
4    dsn="<db connection string>",  # e.g. "(description=(retry_count=2)..."
5    # This is optional, but helpful to re-configure credentials when loading the AgentSpec config for this object
6    id="oracle_datastore_connection_config",
7)

Parameter

Meaning

user

Database username (e.g., ADMIN).

password

Password for user.

dsn

Easy-connect string or TNS alias identifying the service. (e.g., adb.us-ashburn-1.oraclecloud.com:1522/xyz_high)

When using mTLS (mutual TLS) both sides exchange certificates: the client proves its identity with a wallet (client cert + private key) in addition to the username and password. This gives stronger, certificate-based client authentication and is often required for Oracle Autonomous Database in “Require mTLS” mode.

 1from wayflowcore.datastore import MTlsOracleDatabaseConnectionConfig
 2
 3connection_config = MTlsOracleDatabaseConnectionConfig(
 4    config_dir="./oracle-wallet",
 5    dsn="<dbname>",  # Entry in tnsnames.ora
 6    wallet_location="./oracle-wallet",
 7    wallet_password="<your wallet password>",  # Replace with your wallet password  # nosec: this is just a placeholder
 8    user="<db user>",  # Replace with your DB user
 9    password="<db password>",  # Replace with your DB password  # nosec: this is just a placeholder
10    # This is optional, but helpful to re-configure credentials when loading the AgentSpec config for this object
11    id="oracle_datastore_connection_config",
12)

Parameter

Meaning

user

Database username (e.g., ADMIN).

password

Password for user.

dsn

Easy-connect string or TNS alias identifying the service. Example: adb.us-ashburn-1.oraclecloud.com:1522/xyz_high

config_dir

Directory containing sqlnet.ora / tnsnames.ora. when you want to reference a TNS alias (e.g. <dbname>_high) instead of a raw DSN.

wallet_location

Path to the Oracle Wallet directory that contains cwallet.sso / ewallet.p12.

wallet_password

Password that protects the wallet’s private key.

Important

Do not hard-code any database credentials or sensitive connection details directly in your code. Please refer to our Security Guidelines for more information.

Step 2. Define the data model and Datastore#

Warning

The following code snippet will create a new products table in the database. Ensure you are using a throwaway schema with no other table named “products” when running this example.

For this guide, we use the same data model as in the in-memory example. It manages products in an inventory, so a single collection is sufficient. Datastores also support managing multiple database tables at the same time if needed.

Entities map the relational schema to strongly-typed objects that Flows and Agents can validate at runtime. The key difference to the in-memory example is that we require the Entity of interest to be already defined as a table in the database.

Note that, if you have some columns in the database that are not relevant to your assistant, you may simply omit them from the Entity definition (as is done in this example with the external_system_id). However, it may not be possible for the datastore or assistant to create such entities if the omitted columns are required.

 1table_definition = """CREATE TABLE products (
 2    ID NUMBER PRIMARY KEY,
 3    title VARCHAR2(255) NOT NULL,
 4    description VARCHAR2(255) NOT NULL,
 5    price NUMBER NOT NULL,
 6    category VARCHAR2(255) DEFAULT NULL,
 7    external_system_id NUMBER DEFAULT NULL
 8)"""
 9
10with connection_config.get_connection() as connection:
11    connection.cursor().execute(table_definition)
12
13product = Entity(
14    properties={
15        "ID": IntegerProperty(description="Unique product identifier"),
16        # Descriptions can be helpful if an LLM needs to fill these fields,
17        # or generally disambiguate non-obvious property names
18        "title": StringProperty(description="Brief summary of the product"),
19        "description": StringProperty(),
20        "price": FloatProperty(default_value=0.1),
21        # Use nullable to define optional properties
22        "category": nullable(StringProperty()),
23    },
24)
25
26datastore = OracleDatabaseDatastore(
27    schema={"products": product}, connection_config=connection_config
28)
29
30dummy_data = [
31    {
32        "ID": 0,
33        "title": "Broccoli",
34        "description": "Healthy and delicious cruciferous vegetable!",
35        "price": 1.5,
36        "category": "Produce",
37    },
38    {
39        "ID": 1,
40        "title": "Oranges",
41        # We introduce a typo in this entity for the Assistant to fix
42        "description": "Vitamin C-filled cirus fruits",
43        "price": 1.8,
44        "category": "Produce",
45    },
46    {
47        "ID": 2,
48        "title": "Shampoo",
49        "description": "Shiny smooth hair in just 10 applications!",
50        "price": 4.5,
51        "category": "Personal hygiene",
52    },
53    {
54        "ID": 3,
55        "title": "Crushed ice",
56        "description": "Cool any drink in seconds!",
57        "price": 4.5,
58        "category": "Food",
59    },
60]
61
62# Create supports both bulk-creation, as well as single element creation
63datastore.create(collection_name="products", entities=dummy_data)

Step 3. Create datastore steps#

Now that the Datastore is set up, create steps to perform different operations in the flow. In this guide, your assistant will identify inconsistencies in product descriptions of the same category. To do so, you will use the DatastoreQueryStep to fetch product information, and a PromptExecutionStep to identify the issues.

In particular, the DatastoreQueryStep can be used with Database Datastores to execute developer-defined SQL queries. These queries can optionally be parametrized with bind variables. See the bind variables guide on the python-oracledb documentation for more information. See also the list of all available Datastore steps for additional operations that can be performed with Oracle Database Datastores.

 1datastore_query_step = DatastoreQueryStep(
 2    datastore,
 3    "SELECT title, description FROM products WHERE category = :product_category",
 4    name="product_selection_step",
 5    input_descriptors=[
 6        ObjectProperty("bind_variables", properties={"product_category": StringProperty("")})
 7    ],
 8)
 9
10detect_issues_prompt_template = dedent(
11    """You are an inventory assistant.
12
13    Your task:
14        - Summarize potential inconsistencies across descriptions of products in the same category.
15          For example, identify typos and highlight improvement opportunities
16    Important:
17        - Be helpful and concise in your messages
18
19    Here are the product descriptions:
20    {{ products }}
21    """
22)
23
24llm_issue_detection_step = PromptExecutionStep(
25    prompt_template=detect_issues_prompt_template,
26    llm=llm,
27)
28
29user_output_step = OutputMessageStep(
30    message_template="The following issues have been identified: {{ issues }}",
31)
32
33assistant = Flow(
34    begin_step=datastore_query_step,
35    control_flow_edges=[
36        ControlFlowEdge(
37            source_step=datastore_query_step, destination_step=llm_issue_detection_step
38        ),
39        ControlFlowEdge(source_step=llm_issue_detection_step, destination_step=user_output_step),
40        ControlFlowEdge(source_step=user_output_step, destination_step=None),
41    ],
42    data_flow_edges=[
43        DataFlowEdge(
44            datastore_query_step, DatastoreQueryStep.RESULT, llm_issue_detection_step, "products"
45        ),
46        DataFlowEdge(
47            llm_issue_detection_step, PromptExecutionStep.OUTPUT, user_output_step, "issues"
48        ),
49    ],
50)

Finally, verify that the Flow works as expected:

1conversation = assistant.start_conversation({"bind_variables": {"product_category": "Produce"}})
2conversation.execute()
3print(conversation.get_last_message().content)

Agent Spec Exporting/Loading#

This flow can be exported to Agent Spec using the AgentSpecExporter as you have seen in the previous in-memory example, using the AgentSpecExporter.

from wayflowcore.agentspec import AgentSpecExporter

serialized_flow = AgentSpecExporter().to_json(assistant)

Here is what the Agent Spec representation will look like ↓

Click here to see the assistant configuration.
{
    "component_type": "Flow",
    "id": "e1ce33a4-df88-4652-9714-6b51fea8871b",
    "name": "flow_2b8b504d__auto",
    "description": "",
    "metadata": {
        "__metadata_info__": {}
    },
    "inputs": [
        {
            "type": "object",
            "properties": {
                "product_category": {
                    "type": "string"
                }
            },
            "title": "bind_variables"
        }
    ],
    "outputs": [
        {
            "type": "array",
            "items": {
                "type": "object",
                "additionalProperties": {},
                "key_type": {
                    "type": "string"
                }
            },
            "title": "result"
        },
        {
            "description": "the generated text",
            "type": "string",
            "title": "output"
        },
        {
            "description": "the message added to the messages list",
            "type": "string",
            "title": "output_message"
        }
    ],
    "start_node": {
        "$component_ref": "9162d8ad-cf00-4fc2-bed7-a73ffed79b32"
    },
    "nodes": [
        {
            "$component_ref": "85190204-2604-4fd0-b94a-6b4eb8e37f4f"
        },
        {
            "$component_ref": "309c4bda-986e-4fd6-82b8-a83a91168d8d"
        },
        {
            "$component_ref": "9eab48d6-a51c-412e-a459-a7fd05283e1a"
        },
        {
            "$component_ref": "9162d8ad-cf00-4fc2-bed7-a73ffed79b32"
        },
        {
            "$component_ref": "992516a7-a3f8-4cac-ac0d-0b1cac4eb936"
        }
    ],
    "control_flow_connections": [
        {
            "component_type": "ControlFlowEdge",
            "id": "153fa298-b907-4453-84e4-7fa334fce680",
            "name": "product_selection_step_to_step_PromptExecutionStep_8f995292__auto_control_flow_edge",
            "description": null,
            "metadata": {
                "__metadata_info__": {}
            },
            "from_node": {
                "$component_ref": "85190204-2604-4fd0-b94a-6b4eb8e37f4f"
            },
            "from_branch": null,
            "to_node": {
                "$component_ref": "309c4bda-986e-4fd6-82b8-a83a91168d8d"
            }
        },
        {
            "component_type": "ControlFlowEdge",
            "id": "bbb9d505-4aa6-4b58-8e8d-d50b1f4cc659",
            "name": "step_PromptExecutionStep_8f995292__auto_to_step_OutputMessageStep_2c68bdae__auto_control_flow_edge",
            "description": null,
            "metadata": {
                "__metadata_info__": {}
            },
            "from_node": {
                "$component_ref": "309c4bda-986e-4fd6-82b8-a83a91168d8d"
            },
            "from_branch": null,
            "to_node": {
                "$component_ref": "9eab48d6-a51c-412e-a459-a7fd05283e1a"
            }
        },
        {
            "component_type": "ControlFlowEdge",
            "id": "8c4158f0-f48e-4a7b-8686-999efb70b7cb",
            "name": "__StartStep___to_product_selection_step_control_flow_edge",
            "description": null,
            "metadata": {
                "__metadata_info__": {}
            },
            "from_node": {
                "$component_ref": "9162d8ad-cf00-4fc2-bed7-a73ffed79b32"
            },
            "from_branch": null,
            "to_node": {
                "$component_ref": "85190204-2604-4fd0-b94a-6b4eb8e37f4f"
            }
        },
        {
            "component_type": "ControlFlowEdge",
            "id": "05d5f99c-be1a-4f70-b0e2-4e85331016e1",
            "name": "step_OutputMessageStep_2c68bdae__auto_to_None End node_control_flow_edge",
            "description": null,
            "metadata": {},
            "from_node": {
                "$component_ref": "9eab48d6-a51c-412e-a459-a7fd05283e1a"
            },
            "from_branch": null,
            "to_node": {
                "$component_ref": "992516a7-a3f8-4cac-ac0d-0b1cac4eb936"
            }
        }
    ],
    "data_flow_connections": [
        {
            "component_type": "DataFlowEdge",
            "id": "b34e1582-9c08-4ad2-871b-628edf98a0ee",
            "name": "product_selection_step_result_to_step_PromptExecutionStep_8f995292__auto_products_data_flow_edge",
            "description": null,
            "metadata": {
                "__metadata_info__": {}
            },
            "source_node": {
                "$component_ref": "85190204-2604-4fd0-b94a-6b4eb8e37f4f"
            },
            "source_output": "result",
            "destination_node": {
                "$component_ref": "309c4bda-986e-4fd6-82b8-a83a91168d8d"
            },
            "destination_input": "products"
        },
        {
            "component_type": "DataFlowEdge",
            "id": "d29d24f2-d0d1-469f-9ab4-fa12af5e76ee",
            "name": "step_PromptExecutionStep_8f995292__auto_output_to_step_OutputMessageStep_2c68bdae__auto_issues_data_flow_edge",
            "description": null,
            "metadata": {
                "__metadata_info__": {}
            },
            "source_node": {
                "$component_ref": "309c4bda-986e-4fd6-82b8-a83a91168d8d"
            },
            "source_output": "output",
            "destination_node": {
                "$component_ref": "9eab48d6-a51c-412e-a459-a7fd05283e1a"
            },
            "destination_input": "issues"
        },
        {
            "component_type": "DataFlowEdge",
            "id": "bc7a1760-8c23-4d0b-a985-b5b2cc14a90d",
            "name": "__StartStep___bind_variables_to_product_selection_step_bind_variables_data_flow_edge",
            "description": null,
            "metadata": {
                "__metadata_info__": {}
            },
            "source_node": {
                "$component_ref": "9162d8ad-cf00-4fc2-bed7-a73ffed79b32"
            },
            "source_output": "bind_variables",
            "destination_node": {
                "$component_ref": "85190204-2604-4fd0-b94a-6b4eb8e37f4f"
            },
            "destination_input": "bind_variables"
        },
        {
            "component_type": "DataFlowEdge",
            "id": "b3b92de4-10df-4abf-a9b0-149079c46408",
            "name": "product_selection_step_result_to_None End node_result_data_flow_edge",
            "description": null,
            "metadata": {},
            "source_node": {
                "$component_ref": "85190204-2604-4fd0-b94a-6b4eb8e37f4f"
            },
            "source_output": "result",
            "destination_node": {
                "$component_ref": "992516a7-a3f8-4cac-ac0d-0b1cac4eb936"
            },
            "destination_input": "result"
        },
        {
            "component_type": "DataFlowEdge",
            "id": "fa6df9aa-3e40-4923-a1d4-58cd7428c588",
            "name": "step_PromptExecutionStep_8f995292__auto_output_to_None End node_output_data_flow_edge",
            "description": null,
            "metadata": {},
            "source_node": {
                "$component_ref": "309c4bda-986e-4fd6-82b8-a83a91168d8d"
            },
            "source_output": "output",
            "destination_node": {
                "$component_ref": "992516a7-a3f8-4cac-ac0d-0b1cac4eb936"
            },
            "destination_input": "output"
        },
        {
            "component_type": "DataFlowEdge",
            "id": "aa996728-8289-48d2-a7b0-004d371b2d64",
            "name": "step_OutputMessageStep_2c68bdae__auto_output_message_to_None End node_output_message_data_flow_edge",
            "description": null,
            "metadata": {},
            "source_node": {
                "$component_ref": "9eab48d6-a51c-412e-a459-a7fd05283e1a"
            },
            "source_output": "output_message",
            "destination_node": {
                "$component_ref": "992516a7-a3f8-4cac-ac0d-0b1cac4eb936"
            },
            "destination_input": "output_message"
        }
    ],
    "$referenced_components": {
        "9162d8ad-cf00-4fc2-bed7-a73ffed79b32": {
            "component_type": "StartNode",
            "id": "9162d8ad-cf00-4fc2-bed7-a73ffed79b32",
            "name": "__StartStep__",
            "description": "",
            "metadata": {
                "__metadata_info__": {}
            },
            "inputs": [
                {
                    "type": "object",
                    "properties": {
                        "product_category": {
                            "type": "string"
                        }
                    },
                    "title": "bind_variables"
                }
            ],
            "outputs": [
                {
                    "type": "object",
                    "properties": {
                        "product_category": {
                            "type": "string"
                        }
                    },
                    "title": "bind_variables"
                }
            ],
            "branches": [
                "next"
            ]
        },
        "85190204-2604-4fd0-b94a-6b4eb8e37f4f": {
            "component_type": "PluginDatastoreQueryNode",
            "id": "85190204-2604-4fd0-b94a-6b4eb8e37f4f",
            "name": "product_selection_step",
            "description": "",
            "metadata": {
                "__metadata_info__": {}
            },
            "inputs": [
                {
                    "type": "object",
                    "properties": {
                        "product_category": {
                            "type": "string"
                        }
                    },
                    "title": "bind_variables"
                }
            ],
            "outputs": [
                {
                    "type": "array",
                    "items": {
                        "type": "object",
                        "additionalProperties": {},
                        "key_type": {
                            "type": "string"
                        }
                    },
                    "title": "result"
                }
            ],
            "branches": [
                "next"
            ],
            "input_mapping": {},
            "output_mapping": {},
            "datastore": {
                "component_type": "PluginOracleDatabaseDatastore",
                "id": "2515997a-1dbf-47cb-9dfb-d1dd2dca1cee",
                "name": "PluginOracleDatabaseDatastore",
                "description": null,
                "metadata": {},
                "datastore_schema": {
                    "products": {
                        "description": "",
                        "title": "",
                        "properties": {
                            "description": {
                                "type": "string"
                            },
                            "ID": {
                                "description": "Unique product identifier",
                                "type": "integer"
                            },
                            "title": {
                                "description": "Brief summary of the product",
                                "type": "string"
                            },
                            "price": {
                                "type": "number",
                                "default": 0.1
                            },
                            "category": {
                                "anyOf": [
                                    {
                                        "type": "null"
                                    },
                                    {
                                        "type": "string"
                                    }
                                ],
                                "default": null
                            }
                        }
                    }
                },
                "connection_config": {
                    "component_type": "PluginTlsOracleDatabaseConnectionConfig",
                    "id": "13f2fc32-f442-49fb-b7cc-142a240bb8bc",
                    "name": "PluginTlsOracleDatabaseConnectionConfig",
                    "description": null,
                    "metadata": {},
                    "user": {
                        "$component_ref": "13f2fc32-f442-49fb-b7cc-142a240bb8bc.user"
                    },
                    "password": {
                        "$component_ref": "13f2fc32-f442-49fb-b7cc-142a240bb8bc.password"
                    },
                    "dsn": {
                        "$component_ref": "13f2fc32-f442-49fb-b7cc-142a240bb8bc.dsn"
                    },
                    "config_dir": null,
                    "component_plugin_name": "DatastorePlugin",
                    "component_plugin_version": "26.1.0.dev0"
                },
                "component_plugin_name": "DatastorePlugin",
                "component_plugin_version": "26.1.0.dev0"
            },
            "query": "SELECT title, description FROM products WHERE category = :product_category",
            "RESULT": "result",
            "component_plugin_name": "DatastorePlugin",
            "component_plugin_version": "26.1.0.dev0"
        },
        "309c4bda-986e-4fd6-82b8-a83a91168d8d": {
            "component_type": "LlmNode",
            "id": "309c4bda-986e-4fd6-82b8-a83a91168d8d",
            "name": "step_PromptExecutionStep_8f995292__auto",
            "description": "",
            "metadata": {
                "__metadata_info__": {}
            },
            "inputs": [
                {
                    "description": "\"products\" input variable for the template",
                    "type": "string",
                    "title": "products"
                }
            ],
            "outputs": [
                {
                    "description": "the generated text",
                    "type": "string",
                    "title": "output"
                }
            ],
            "branches": [
                "next"
            ],
            "llm_config": {
                "component_type": "VllmConfig",
                "id": "115a6849-0e1b-4b1b-8bd2-be872c9bc64b",
                "name": "llm_41795d1f__auto",
                "description": null,
                "metadata": {
                    "__metadata_info__": {}
                },
                "default_generation_parameters": null,
                "url": "LLAMA_API_URL",
                "model_id": "LLAMA_MODEL_ID",
                "api_type": "chat_completions",
                "api_key": null
            },
            "prompt_template": "You are an inventory assistant.\n\n    Your task:\n        - Summarize potential inconsistencies across descriptions of products in the same category.\n          For example, identify typos and highlight improvement opportunities\n    Important:\n        - Be helpful and concise in your messages\n\n    Here are the product descriptions:\n    {{ products }}\n"
        },
        "9eab48d6-a51c-412e-a459-a7fd05283e1a": {
            "component_type": "PluginOutputMessageNode",
            "id": "9eab48d6-a51c-412e-a459-a7fd05283e1a",
            "name": "step_OutputMessageStep_2c68bdae__auto",
            "description": "",
            "metadata": {
                "__metadata_info__": {}
            },
            "inputs": [
                {
                    "description": "\"issues\" input variable for the template",
                    "type": "string",
                    "title": "issues"
                }
            ],
            "outputs": [
                {
                    "description": "the message added to the messages list",
                    "type": "string",
                    "title": "output_message"
                }
            ],
            "branches": [
                "next"
            ],
            "message": "The following issues have been identified: {{ issues }}",
            "input_mapping": {},
            "output_mapping": {},
            "message_type": "AGENT",
            "rephrase": false,
            "llm_config": null,
            "expose_message_as_output": true,
            "component_plugin_name": "NodesPlugin",
            "component_plugin_version": "26.1.0.dev0"
        },
        "992516a7-a3f8-4cac-ac0d-0b1cac4eb936": {
            "component_type": "EndNode",
            "id": "992516a7-a3f8-4cac-ac0d-0b1cac4eb936",
            "name": "None End node",
            "description": "End node representing all transitions to None in the WayFlow flow",
            "metadata": {},
            "inputs": [
                {
                    "type": "array",
                    "items": {
                        "type": "object",
                        "additionalProperties": {},
                        "key_type": {
                            "type": "string"
                        }
                    },
                    "title": "result"
                },
                {
                    "description": "the generated text",
                    "type": "string",
                    "title": "output"
                },
                {
                    "description": "the message added to the messages list",
                    "type": "string",
                    "title": "output_message"
                }
            ],
            "outputs": [
                {
                    "type": "array",
                    "items": {
                        "type": "object",
                        "additionalProperties": {},
                        "key_type": {
                            "type": "string"
                        }
                    },
                    "title": "result"
                },
                {
                    "description": "the generated text",
                    "type": "string",
                    "title": "output"
                },
                {
                    "description": "the message added to the messages list",
                    "type": "string",
                    "title": "output_message"
                }
            ],
            "branches": [],
            "branch_name": "next"
        }
    },
    "agentspec_version": "26.1.0"
}

Warning

The Oracle Database Connection Config objects contain several sensitive values (like username, password, wallet location) that will not be serialized by the AgentSpecExporter. These will be serialized as references that must be resolved at loading time, by specifying the values of these sensitive fields in the component_registry argument of the loader:

component_registry = {
    # We map the ID of the sensitive fields in the connection config to their values
    "oracle_datastore_connection_config.user": "<db user>",  # Replace with your DB user
    "oracle_datastore_connection_config.password": "<db password>",  # Replace with your DB password  # nosec: this is just a placeholder
    "oracle_datastore_connection_config.dsn": "<db connection string>",  # e.g. "(description=(retry_count=2)..."
}

You can then load the configuration back to an assistant using the AgentSpecLoader.

from wayflowcore.agentspec import AgentSpecLoader

agent = AgentSpecLoader().load_json(serialized_flow, components_registry=component_registry)

Note

This guide uses the following extension/plugin Agent Spec components:

  • PluginDatastoreQueryNode

  • PluginOracleDatabaseDatastore

  • PluginTlsOracleDatabaseConnectionConfig

  • PluginOutputMessageNode

See the list of available Agent Spec extension/plugin components in the API Reference

Next steps#

Having learned how to connect WayFlow assistants to data sources, you may now proceed to:

Full code#

In-memory datastore#

Click on the card at the top of this page to download the full code for this guide or copy the code below.

Connecting Assistants to User Data (full code)
  1# Copyright © 2025 Oracle and/or its affiliates.
  2#
  3# This software is under the Apache License 2.0
  4# %%[markdown]
  5# WayFlow Code Example - How to Connect to Your Data
  6# --------------------------------------------------
  7
  8# How to use:
  9# Create a new Python virtual environment and install the latest WayFlow version.
 10# ```bash
 11# python -m venv venv-wayflowcore
 12# source venv-wayflowcore/bin/activate
 13# pip install --upgrade pip
 14# pip install "wayflowcore==26.1" 
 15# ```
 16
 17# You can now run the script
 18# 1. As a Python file:
 19# ```bash
 20# python howto_datastores.py
 21# ```
 22# 2. As a Notebook (in VSCode):
 23# When viewing the file,
 24#  - press the keys Ctrl + Enter to run the selected cell
 25#  - or Shift + Enter to run the selected cell and move to the cell below# (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) or Universal Permissive License
 26# (UPL) 1.0 (LICENSE-UPL or https://oss.oracle.com/licenses/upl), at your option.
 27
 28
 29
 30# %%[markdown]
 31## Imports
 32
 33# %%
 34from textwrap import dedent
 35
 36from wayflowcore.controlconnection import ControlFlowEdge
 37from wayflowcore.dataconnection import DataFlowEdge
 38from wayflowcore.datastore import Entity, InMemoryDatastore
 39from wayflowcore.datastore.entity import nullable
 40from wayflowcore.flow import Flow
 41from wayflowcore.property import (
 42    AnyProperty,
 43    DictProperty,
 44    FloatProperty,
 45    IntegerProperty,
 46    ObjectProperty,
 47    StringProperty,
 48)
 49from wayflowcore.steps import InputMessageStep, OutputMessageStep, PromptExecutionStep
 50from wayflowcore.steps.datastoresteps import (
 51    DatastoreCreateStep,
 52    DatastoreDeleteStep,
 53    DatastoreListStep,
 54    DatastoreUpdateStep,
 55)
 56
 57# %%[markdown]
 58## Define the llm
 59
 60# %%
 61from wayflowcore.models import VllmModel
 62
 63llm = VllmModel(
 64    model_id="LLAMA_MODEL_ID",
 65    host_port="LLAMA_API_URL",
 66)
 67
 68# %%[markdown]
 69## Create entities
 70
 71# %%
 72product = Entity(
 73    properties={
 74        "ID": IntegerProperty(description="Unique product identifier"),
 75        # Descriptions can be helpful if an LLM needs to fill these fields,
 76        # or generally disambiguate non-obvious property names
 77        "title": StringProperty(description="Brief summary of the product"),
 78        "description": StringProperty(),
 79        "price": FloatProperty(default_value=0.1),
 80        # Use nullable to define optional properties
 81        "category": nullable(StringProperty()),
 82    },
 83)
 84
 85# %%[markdown]
 86## Create datastore
 87
 88# %%
 89datastore = InMemoryDatastore(schema={"products": product})
 90
 91dummy_data = [
 92    {
 93        "ID": 0,
 94        "title": "Broccoli",
 95        "description": "Healthy and delicious cruciferous vegetable!",
 96        "price": 1.5,
 97        "category": "Produce",
 98    },
 99    {
100        "ID": 1,
101        "title": "Oranges",
102        "description": "Vitamin C-filled citrus fruits",
103        "price": 1.8,
104        "category": "Produce",
105    },
106    {
107        "ID": 2,
108        "title": "Shampoo",
109        "description": "Shiny smooth hair in just 10 applications!",
110        "price": 4.5,
111        "category": "Personal hygiene",
112    },
113    {
114        "ID": 3,
115        "title": "Crushed ice",
116        "description": "Cool any drink in seconds!",
117        "price": 4.5,
118    },
119]
120
121# Create supports both bulk-creation, as well as single element creation
122datastore.create(collection_name="products", entities=dummy_data[:-1])
123datastore.create(collection_name="products", entities=dummy_data[-1])
124
125# %%[markdown]
126## Create Datastore step
127
128# %%
129datastore_list_step = DatastoreListStep(
130    datastore,
131    name="product_list_step",
132    collection_name="products",
133    where={"title": "{{user_requested_product}}"},
134    limit=1,
135    unpack_single_entity_from_list=True,
136)
137
138datastore_update_step = DatastoreUpdateStep(
139    datastore,
140    name="product_update_step",
141    collection_name="products",
142    where={"title": "{{user_requested_product}}"}
143)
144
145# %%[markdown]
146## Create flow
147
148# %%
149# We create the steps needed by our flow
150USER_INPUT_STEP = "user_product_input_step"
151USER_TASK_INPUT_STEP = "user_task_input_step"
152LLM_REWRITE_STEP = "llm_rewrite_step"
153USER_OUTPUT_STEP = "user_output_step"
154
155user_input_message_template = dedent(
156    """I am an inventory Assistant, designed to help you keep product descriptions up-to-date.
157    What product would you like to update? Please provide its title.
158    """
159)
160
161user_task_message_template = "How would you like to update the description? I will help you rewrite it according to your instructions"
162
163rewrite_description_prompt_template = dedent(
164    """You are an inventory assistant.
165
166    Your task:
167        - Based on the product details given below, rewrite the description according to the user's request
168    Important:
169        - Be helpful and concise in your messages
170        - Only provide the new description as an output, and nothing else
171
172    Here is the User's request:
173    - {{ user_request }}
174
175    Here is the product description:
176    - {{ product }}
177    """
178)
179
180user_input_step = InputMessageStep(
181    name=USER_INPUT_STEP,
182    message_template=user_input_message_template,
183)
184
185user_task_input_step = InputMessageStep(
186    name=USER_TASK_INPUT_STEP,
187    message_template=user_task_message_template,
188)
189
190llm_rewrite_step = PromptExecutionStep(
191    name=LLM_REWRITE_STEP,
192    prompt_template=rewrite_description_prompt_template,
193    llm=llm,
194    input_descriptors=[
195        DictProperty("product", key_type=StringProperty(), value_type=AnyProperty())
196    ],
197    output_descriptors=[
198        ObjectProperty(
199            name=PromptExecutionStep.OUTPUT, properties={"description": StringProperty()}
200        )
201    ],
202)
203
204user_output_step = OutputMessageStep(
205    name=USER_OUTPUT_STEP,
206    message_template="The product has been updated with the following description: {{ answer['description'] }}",
207)
208
209assistant = Flow(
210    begin_step=user_input_step,
211    control_flow_edges=[
212        ControlFlowEdge(source_step=user_input_step, destination_step=user_task_input_step),
213        ControlFlowEdge(source_step=user_task_input_step, destination_step=datastore_list_step),
214        ControlFlowEdge(source_step=datastore_list_step, destination_step=llm_rewrite_step),
215        ControlFlowEdge(source_step=llm_rewrite_step, destination_step=datastore_update_step),
216        ControlFlowEdge(source_step=datastore_update_step, destination_step=user_output_step),
217        ControlFlowEdge(source_step=user_output_step, destination_step=None),
218    ],
219    data_flow_edges=[
220        # The first title given by the user is mapped to the datastore steps for listing and updating
221        DataFlowEdge(
222            user_input_step,
223            InputMessageStep.USER_PROVIDED_INPUT,
224            datastore_list_step,
225            "user_requested_product",
226        ),
227        DataFlowEdge(
228            user_input_step,
229            InputMessageStep.USER_PROVIDED_INPUT,
230            datastore_update_step,
231            "user_requested_product",
232        ),
233        # The task and product detail are given to the LLM in the prompt execution step
234        DataFlowEdge(
235            user_task_input_step,
236            InputMessageStep.USER_PROVIDED_INPUT,
237            llm_rewrite_step,
238            "user_request",
239        ),
240        DataFlowEdge(datastore_list_step, DatastoreListStep.ENTITIES, llm_rewrite_step, "product"),
241        # The generated update is applied on the datastore, and echoed back to the user
242        DataFlowEdge(
243            llm_rewrite_step,
244            PromptExecutionStep.OUTPUT,
245            datastore_update_step,
246            DatastoreUpdateStep.UPDATE,
247        ),
248        DataFlowEdge(llm_rewrite_step, PromptExecutionStep.OUTPUT, user_output_step, "answer"),
249    ],
250)
251
252# %%[markdown]
253## Execute flow
254
255# %%
256conversation = assistant.start_conversation()
257conversation.execute()
258conversation.append_user_message("Broccoli")
259
260conversation.execute()
261conversation.append_user_message(
262    "Shoppers don't know what 'cruciferous' means, we should find a catchier description."
263)
264
265conversation.execute()
266print(conversation.get_last_message().content)
267
268# %%[markdown]
269## Import agents
270
271# %%
272from wayflowcore.agent import Agent
273
274# %%[markdown]
275## Create agent flows
276
277# %%
278AGENT_PROMPT = dedent(
279    """
280    You are an inventory assistant. Your task is to help the user with their requests by using the available tools.
281    If you are unsure about the action to take, or you don't have the right tool, simply tell the user so and follow their guidance.
282    """
283)
284
285create_product_flow = Flow.from_steps(
286    [DatastoreCreateStep(datastore, "products")],
287    name="Create product",
288    description="Creates a new product in the data source",
289)
290list_products_flow = Flow.from_steps(
291    [DatastoreListStep(datastore, "products")],
292    name="List all products",
293    description="Lists all products in the data source.",
294)
295list_one_product_flow = Flow.from_steps(
296    [datastore_list_step],
297    name="List single product",
298    description="Lists a single product in the data source by its title.",
299)
300update_product_flow = Flow.from_steps(
301    [datastore_update_step],
302    name="Update product",
303    description="Updates a product in the data source by its title.",
304)
305delete_product_flow = Flow.from_steps(
306    [DatastoreDeleteStep(datastore, "products", where={"title": "{{product_title}}"})],
307    name="Delete product",
308    description="Delete a product in the data source by its title.",
309)
310
311# %%[markdown]
312## Create the agent
313
314# %%
315agent = Agent(
316    llm=llm,
317    flows=[
318        create_product_flow,
319        list_products_flow,
320        list_one_product_flow,
321        update_product_flow,
322        delete_product_flow,
323    ],
324    custom_instruction=AGENT_PROMPT,
325)
326
327# %%[markdown]
328## Export config to Agent Spec
329
330# %%
331from wayflowcore.agentspec import AgentSpecExporter
332
333serialized_agent = AgentSpecExporter().to_json(agent)
334
335# %%[markdown]
336## Load Agent Spec config
337
338# %%
339from wayflowcore.agentspec import AgentSpecLoader
340
341agent = AgentSpecLoader().load_json(serialized_agent)

Oracle Database datastore#

Click on the card at the top of this page to download the full code for this guide, or copy the code below.

Connecting Assistants to Oracle Database (full code)
  1# Copyright © 2025 Oracle and/or its affiliates.
  2#
  3# This software is under the Apache License 2.0
  4# %%[markdown]
  5# WayFlow Code Example - How to Connect To Oracle Database
  6# --------------------------------------------------------
  7
  8# How to use:
  9# Create a new Python virtual environment and install the latest WayFlow version.
 10# ```bash
 11# python -m venv venv-wayflowcore
 12# source venv-wayflowcore/bin/activate
 13# pip install --upgrade pip
 14# pip install "wayflowcore==26.1" 
 15# ```
 16
 17# You can now run the script
 18# 1. As a Python file:
 19# ```bash
 20# python howto_connect_to_oracle_database.py
 21# ```
 22# 2. As a Notebook (in VSCode):
 23# When viewing the file,
 24#  - press the keys Ctrl + Enter to run the selected cell
 25#  - or Shift + Enter to run the selected cell and move to the cell below# (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) or Universal Permissive License
 26# (UPL) 1.0 (LICENSE-UPL or https://oss.oracle.com/licenses/upl), at your option.
 27
 28
 29
 30
 31
 32
 33# %%[markdown]
 34## Define the llm
 35
 36# %%
 37from wayflowcore.models import VllmModel
 38
 39llm = VllmModel(
 40    model_id="LLAMA_MODEL_ID",
 41    host_port="LLAMA_API_URL",
 42)
 43
 44# %%[markdown]
 45## imports:
 46
 47# %%
 48from textwrap import dedent
 49
 50
 51from wayflowcore.controlconnection import ControlFlowEdge
 52from wayflowcore.dataconnection import DataFlowEdge
 53
 54from wayflowcore.datastore import (
 55    Entity,
 56    MTlsOracleDatabaseConnectionConfig,
 57    OracleDatabaseDatastore,
 58    TlsOracleDatabaseConnectionConfig,
 59)
 60from wayflowcore.datastore.entity import nullable
 61from wayflowcore.flow import Flow
 62from wayflowcore.property import (
 63    FloatProperty,
 64    IntegerProperty,
 65    ObjectProperty,
 66    StringProperty,
 67)
 68from wayflowcore.steps import OutputMessageStep, PromptExecutionStep
 69from wayflowcore.steps.datastoresteps import DatastoreQueryStep
 70
 71# %%[markdown]
 72## TLS Connection:
 73
 74# %%
 75connection_config = TlsOracleDatabaseConnectionConfig(
 76    user="<db user>",  # Replace with your DB user
 77    password="<db password>",  # Replace with your DB password  # nosec: this is just a placeholder
 78    dsn="<db connection string>",  # e.g. "(description=(retry_count=2)..."
 79    # This is optional, but helpful to re-configure credentials when loading the AgentSpec config for this object
 80    id="oracle_datastore_connection_config",
 81)
 82
 83# %%[markdown]
 84## mTLS Connection:
 85
 86# %%
 87from wayflowcore.datastore import MTlsOracleDatabaseConnectionConfig
 88
 89connection_config = MTlsOracleDatabaseConnectionConfig(
 90    config_dir="./oracle-wallet",
 91    dsn="<dbname>",  # Entry in tnsnames.ora
 92    wallet_location="./oracle-wallet",
 93    wallet_password="<your wallet password>",  # Replace with your wallet password  # nosec: this is just a placeholder
 94    user="<db user>",  # Replace with your DB user
 95    password="<db password>",  # Replace with your DB password  # nosec: this is just a placeholder
 96    # This is optional, but helpful to re-configure credentials when loading the AgentSpec config for this object
 97    id="oracle_datastore_connection_config",
 98)
 99# Datastore and Entity definitions
100
101# %%[markdown]
102## Schema
103
104# %%
105table_definition = """CREATE TABLE products (
106    ID NUMBER PRIMARY KEY,
107    title VARCHAR2(255) NOT NULL,
108    description VARCHAR2(255) NOT NULL,
109    price NUMBER NOT NULL,
110    category VARCHAR2(255) DEFAULT NULL,
111    external_system_id NUMBER DEFAULT NULL
112)"""
113
114with connection_config.get_connection() as connection:
115    connection.cursor().execute(table_definition)
116
117product = Entity(
118    properties={
119        "ID": IntegerProperty(description="Unique product identifier"),
120        # Descriptions can be helpful if an LLM needs to fill these fields,
121        # or generally disambiguate non-obvious property names
122        "title": StringProperty(description="Brief summary of the product"),
123        "description": StringProperty(),
124        "price": FloatProperty(default_value=0.1),
125        # Use nullable to define optional properties
126        "category": nullable(StringProperty()),
127    },
128)
129
130datastore = OracleDatabaseDatastore(
131    schema={"products": product}, connection_config=connection_config
132)
133
134dummy_data = [
135    {
136        "ID": 0,
137        "title": "Broccoli",
138        "description": "Healthy and delicious cruciferous vegetable!",
139        "price": 1.5,
140        "category": "Produce",
141    },
142    {
143        "ID": 1,
144        "title": "Oranges",
145        # We introduce a typo in this entity for the Assistant to fix
146        "description": "Vitamin C-filled cirus fruits",
147        "price": 1.8,
148        "category": "Produce",
149    },
150    {
151        "ID": 2,
152        "title": "Shampoo",
153        "description": "Shiny smooth hair in just 10 applications!",
154        "price": 4.5,
155        "category": "Personal hygiene",
156    },
157    {
158        "ID": 3,
159        "title": "Crushed ice",
160        "description": "Cool any drink in seconds!",
161        "price": 4.5,
162        "category": "Food",
163    },
164]
165
166# Create supports both bulk-creation, as well as single element creation
167datastore.create(collection_name="products", entities=dummy_data)
168
169# %%[markdown]
170## Create Datastore step and flow
171
172# %%
173datastore_query_step = DatastoreQueryStep(
174    datastore,
175    "SELECT title, description FROM products WHERE category = :product_category",
176    name="product_selection_step",
177    input_descriptors=[
178        ObjectProperty("bind_variables", properties={"product_category": StringProperty("")})
179    ],
180)
181
182detect_issues_prompt_template = dedent(
183    """You are an inventory assistant.
184
185    Your task:
186        - Summarize potential inconsistencies across descriptions of products in the same category.
187          For example, identify typos and highlight improvement opportunities
188    Important:
189        - Be helpful and concise in your messages
190
191    Here are the product descriptions:
192    {{ products }}
193    """
194)
195
196llm_issue_detection_step = PromptExecutionStep(
197    prompt_template=detect_issues_prompt_template,
198    llm=llm,
199)
200
201user_output_step = OutputMessageStep(
202    message_template="The following issues have been identified: {{ issues }}",
203)
204
205assistant = Flow(
206    begin_step=datastore_query_step,
207    control_flow_edges=[
208        ControlFlowEdge(
209            source_step=datastore_query_step, destination_step=llm_issue_detection_step
210        ),
211        ControlFlowEdge(source_step=llm_issue_detection_step, destination_step=user_output_step),
212        ControlFlowEdge(source_step=user_output_step, destination_step=None),
213    ],
214    data_flow_edges=[
215        DataFlowEdge(
216            datastore_query_step, DatastoreQueryStep.RESULT, llm_issue_detection_step, "products"
217        ),
218        DataFlowEdge(
219            llm_issue_detection_step, PromptExecutionStep.OUTPUT, user_output_step, "issues"
220        ),
221    ],
222)
223
224# %%[markdown]
225## Execute flow
226
227# %%
228conversation = assistant.start_conversation({"bind_variables": {"product_category": "Produce"}})
229conversation.execute()
230print(conversation.get_last_message().content)
231
232# %%[markdown]
233## Export config to Agent Spec
234
235# %%
236from wayflowcore.agentspec import AgentSpecExporter
237
238serialized_flow = AgentSpecExporter().to_json(assistant)
239
240# %%[markdown]
241## Provide sensitive information when loading the Agent Spec config
242
243# %%
244component_registry = {
245    # We map the ID of the sensitive fields in the connection config to their values
246    "oracle_datastore_connection_config.user": "<db user>",  # Replace with your DB user
247    "oracle_datastore_connection_config.password": "<db password>",  # Replace with your DB password  # nosec: this is just a placeholder
248    "oracle_datastore_connection_config.dsn": "<db connection string>",  # e.g. "(description=(retry_count=2)..."
249}
250
251# %%[markdown]
252## Load Agent Spec config
253
254# %%
255from wayflowcore.agentspec import AgentSpecLoader
256
257agent = AgentSpecLoader().load_json(serialized_flow, components_registry=component_registry)