Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# Copyright © 2026 Oracle and/or its affiliates.
#
# This software is under the Apache License 2.0
# (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) or Universal Permissive License
# (UPL) 1.0 (LICENSE-UPL or https://oss.oracle.com/licenses/upl), at your option.

"""HTTP examples for the WayFlow Code Executor server."""

import time

import httpx

BASE_URL = "http://127.0.0.1:8765"

# .. start-##_Get_capabilities
response = httpx.get(f"{BASE_URL}/v1/code-executor")
response.raise_for_status()
print(response.json())
# .. end-##_Get_capabilities

# .. start-##_Run_script
response = httpx.post(
f"{BASE_URL}/v1/executions",
json={
"language_id": "python",
"input": [
{
"type": "script",
"source_code": "print('hello from the Code Executor server')",
}
],
"wait": True,
},
)
response.raise_for_status()
script_response = response.json()
print(script_response["output"])
# .. end-##_Run_script

# .. start-##_Run_function
response = httpx.post(
f"{BASE_URL}/v1/executions",
json={
"language_id": "python",
"input": [
{
"type": "function",
"source_code": "def multiply(a, b):\n return a * b",
"function_name": "multiply",
"arguments": {"a": 6, "b": 7},
}
],
"wait": True,
},
)
response.raise_for_status()
function_response = response.json()
print(function_response["output"][0]["structuredContent"])
# .. end-##_Run_function

# .. start-##_Poll_execution
response = httpx.post(
f"{BASE_URL}/v1/executions",
json={
"language_id": "python",
"input": [
{
"type": "script",
"source_code": "import time\ntime.sleep(1)\nprint('done')",
}
],
"wait": False,
},
)
response.raise_for_status()
execution = response.json()

while execution["status"] not in {"completed", "failed", "timed_out", "cancelled"}:
time.sleep(0.1)
response = httpx.get(f"{BASE_URL}/v1/executions/{execution['id']}")
response.raise_for_status()
execution = response.json()

print(execution)
# .. end-##_Poll_execution
171 changes: 171 additions & 0 deletions docs/wayflowcore/source/core/howtoguides/howto_serve_codeserver.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
.. _top-howtoservecodeserver:

=================================
How to Run a Code Executor Server
=================================

.. |python-icon| image:: ../../_static/icons/python-icon.svg
:width: 40px
:height: 40px

.. grid:: 2

.. grid-item-card:: |python-icon| Download Python Script
:link: ../code_examples/howto_serve_codeserver.py
:link-alt: Run a Code Executor Server how-to script

Python script for this guide.


WayFlow provides a Code Executor Protocol and a compatible server for running Python scripts and
functions. The server exposes a small HTTP API for checking capabilities, submitting executions,
and polling execution results.


Start the server
================

Start a local Python Code Executor server with the WayFlow CLI:

.. code-block:: bash

wayflow codeserver --host 127.0.0.1 --port 8765

The server is unauthenticated by default for local development. For deployments, put it behind
an authentication and TLS layer, and apply the resource limits appropriate for your environment.


Run the server in a container
=============================

You can also build and run the local Python Code Executor server with Podman or Docker. The
following is the container definition:

.. code-block:: Dockerfile

ARG PYTHON_BASE_IMAGE=python:3.11-slim
FROM ${PYTHON_BASE_IMAGE}

ENV PYTHONUNBUFFERED=1
WORKDIR /opt/wayflow

COPY wayflowcore /opt/wayflow/wayflowcore
COPY VERSION /opt/wayflow/VERSION

RUN python3 -m pip install --no-cache-dir --upgrade pip \
&& python3 -m pip install --no-cache-dir -e /opt/wayflow/wayflowcore

EXPOSE 8765

CMD ["wayflow", "codeserver", "--host", "0.0.0.0", "--port", "8765"]

Build the image from the directory containing ``Containerfile.local-python-codeserver``:

.. tabs::

.. tab:: Podman

.. code-block:: bash

podman build \
-f Containerfile.local-python-codeserver \
-t localhost/wayflow-code-server-local-python:dev .

If Podman encounters SELinux labeling issues on RHEL, you may want to look into
``--security-opt`` configuration parameters.

.. tab:: Docker

.. code-block:: bash

docker build \
-f Containerfile.local-python-codeserver \
-t wayflow-code-server-local-python:dev .

Run the container with an API key because it listens on all interfaces:

.. tabs::

.. tab:: Podman

.. code-block:: bash

podman run --rm \
--name wayflow-code-server \
-p 8765:8765 \
-e WAYFLOW_API_KEY='your-secret-key' \
localhost/wayflow-code-server-local-python:dev

.. tab:: Docker

.. code-block:: bash

docker run --rm \
--name wayflow-code-server \
-p 8765:8765 \
-e WAYFLOW_API_KEY='your-secret-key' \
wayflow-code-server-local-python:dev


Check server capabilities
=========================

The capabilities endpoint reports the languages and execution modes supported by the server.

.. literalinclude:: ../code_examples/howto_serve_codeserver.py
:language: python
:start-after: .. start-##_Get_capabilities
:end-before: .. end-##_Get_capabilities


Run a script
============

Submit a script and wait for it to complete. Captured output is returned in the execution result.

.. literalinclude:: ../code_examples/howto_serve_codeserver.py
:language: python
:start-after: .. start-##_Run_script
:end-before: .. end-##_Run_script


Run a function
==============

Submit source code containing one named function and pass JSON-compatible named arguments.

.. literalinclude:: ../code_examples/howto_serve_codeserver.py
:language: python
:start-after: .. start-##_Run_function
:end-before: .. end-##_Run_function


Submit and poll an execution
============================

Set ``wait`` to ``False`` to receive an execution identifier immediately. Poll the execution
endpoint until it reaches a terminal status.

.. literalinclude:: ../code_examples/howto_serve_codeserver.py
:language: python
:start-after: .. start-##_Poll_execution
:end-before: .. end-##_Poll_execution


Security considerations
=======================

The Code Executor server executes submitted source code. Do not expose an unauthenticated server
to an untrusted network. For production deployments, add authentication, TLS, rate limiting, and
resource controls through an API gateway, reverse proxy, or deployment-specific middleware.


Full code
=========

Click the card at the :ref:`top of this page <top-howtoservecodeserver>` to download the Python
example for this guide or copy the code below.

.. literalinclude:: ../code_examples/howto_serve_codeserver.py
:language: python
:linenos:
20 changes: 20 additions & 0 deletions wayflowcore/src/wayflowcore/_utils/notgiven.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Copyright © 2026 Oracle and/or its affiliates.
#
# This software is under the Apache License 2.0
# (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) or Universal Permissive License
# (UPL) 1.0 (LICENSE-UPL or https://oss.oracle.com/licenses/upl), at your option.

"""Marker types for optional values whose absence is meaningful."""

from typing import Literal


class NotGiven:
"""Marker for a value that was not supplied."""

def __bool__(self) -> Literal[False]:
"""Evaluate the marker as false."""
return False


NOT_GIVEN = NotGiven()
5 changes: 3 additions & 2 deletions wayflowcore/src/wayflowcore/agentserver/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ def __init__(
Config for the storage to save the conversations. If not provided, the default storage with `InMemoryDatastore` will be used.
"""

self.storage_config = ServerStorageConfig()
self.storage_config = storage_config or ServerStorageConfig()
self._storage = A2AStorage(self.storage_config)
self._broker = InMemoryBroker()

Expand Down Expand Up @@ -179,7 +179,8 @@ def __init__(
Datastore for server persistence. Needs to have the proper table and columns as specified
in the `storage_config`.
storage_config:
Cch will not guarantee persistence of data across runs.
Configuration for the datastore schema and retention behavior. When omitted,
the default in-memory storage configuration is used.
allowed_origins:
Origins allowed to make browser cross-origin requests to the server through
CORS (Cross-Origin Resource Sharing). CORS is a browser access-control
Expand Down
2 changes: 2 additions & 0 deletions wayflowcore/src/wayflowcore/cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import argparse
from typing import Optional, Sequence

from .codeserver import add_parser as add_codeserver_parser
from .serve import add_parser as add_serve_parser

__all__ = ["main"]
Expand All @@ -20,6 +21,7 @@ def build_parser() -> argparse.ArgumentParser:
subparsers = parser.add_subparsers(dest="command", required=True)

add_serve_parser(subparsers)
add_codeserver_parser(subparsers)
return parser


Expand Down
Loading