Skip to content

feat: add Northflank provider#3448

Open
fr3fou wants to merge 2 commits into
openai:mainfrom
fr3fou:northflank-provider
Open

feat: add Northflank provider#3448
fr3fou wants to merge 2 commits into
openai:mainfrom
fr3fou:northflank-provider

Conversation

@fr3fou
Copy link
Copy Markdown

@fr3fou fr3fou commented May 18, 2026

Summary

Adds a Northflank provider integration behind the optional northflank extra.

This includes:

  • agents.extensions.northflank.northflank_tools(): a curated tool namespace for operating Northflank resources from agents.
  • agents.extensions.northflank.NorthflankShellExecutor: a ShellTool executor that runs commands inside a Northflank service container.
  • agents.extensions.sandbox.northflank.NorthflankSandboxClient: a hosted sandbox provider backed by Northflank services.

Supported features

Northflank tools support:

  • List/get projects and services.
  • Create deployment services from external Docker images.
  • Restart, pause, resume, scale, and wait for services.
  • Run one-shot exec commands in service, job, and addon containers.
  • Fetch and tail service logs.
  • Fetch service metrics.
  • Optional gated tools for secrets, volumes, domains, and deletes.

Mutating, exec, and destructive tools are marked with needs_approval=True. Delete, secret, volume, and domain tools are opt-in via northflank_tools(include_...).

The sandbox provider supports:

  • Creating ephemeral Northflank deployment services from an image.
  • Attaching to an existing Northflank service.
  • Shell exec through the sandbox session interface.
  • Workspace read/write.
  • Workspace persist/hydrate via tar.
  • Snapshot/resume state.
  • Cleanup of services created by the client.
  • Workspace persistence across stop/resume via either a Northflank volume (workspace_persistence="volume") or a tar embedded in session state (workspace_persistence="tar").

Usage

Install with the optional extra:

pip install "openai-agents[northflank]"

Use Northflank tools with an authenticated SDK client:

from northflank import AsyncApiClient

from agents import Agent, Runner
from agents.extensions.northflank import NorthflankCtx, northflank_tools

client = AsyncApiClient()

agent = Agent(
    name="northflank-ops",
    instructions="Help inspect and operate Northflank services.",
    tools=list(northflank_tools()),
)

result = await Runner.run(
    agent,
    "List the services in this project and summarize their status.",
    context=NorthflankCtx(client=client, project_id="my-project"),
)

Use the shell executor with ShellTool:

from northflank import AsyncApiClient

from agents import Agent, Runner, ShellTool
from agents.extensions.northflank import NorthflankCtx, NorthflankShellExecutor

client = AsyncApiClient()

agent = Agent(
    name="northflank-shell",
    tools=[
        ShellTool(
            executor=NorthflankShellExecutor(service_id="api"),
            needs_approval=True,
        )
    ],
)

result = await Runner.run(
    agent,
    "Check disk and memory usage in the service container.",
    context=NorthflankCtx(client=client, project_id="my-project"),
)

Use Northflank as a sandbox backend:

from northflank import AsyncApiClient

from agents import Runner
from agents.run import RunConfig
from agents.sandbox import SandboxAgent, SandboxRunConfig
from agents.extensions.sandbox.northflank import (
    NorthflankSandboxClient,
    NorthflankSandboxClientOptions,
)

client = AsyncApiClient()

agent = SandboxAgent(
    name="northflank-sandbox",
    instructions="Inspect the sandbox workspace and answer concisely.",
)

run_config = RunConfig(
    sandbox=SandboxRunConfig(
        client=NorthflankSandboxClient(client=client),
        options=NorthflankSandboxClientOptions(
            project_id="my-project",
            image_path="ubuntu:24.04",
            docker_command="sleep infinity",
        ),
    )
)

result = await Runner.run(agent, "What files are in the workspace?", run_config=run_config)

Workspace persistence

By default the sandbox workspace lives in the container filesystem and is dropped when the service is deleted. Two opt-in strategies let it survive stop/resume:

Volume mode — provisions a Northflank volume mounted at manifest.root, attached to the service. The volume survives stop/resume and is deleted by client.delete() if the client created it. Only valid with image_path (client-owned services).

NorthflankSandboxClientOptions(
    project_id="my-project",
    image_path="ubuntu:24.04",
    docker_command="sleep infinity",
    workspace_persistence="volume",
)

volume_spec defaults to a 5120 MiB nf-multi-rw volume in ReadWriteMany mode (5120 MiB is the smallest size Northflank accepts on that storage class). Override it to use a different size, storage class, or access mode:

NorthflankSandboxClientOptions(
    ...,
    workspace_persistence="volume",
    volume_spec={
        "storageSize": 20480,
        "accessMode": "ReadWriteOnce",
        "storageClassName": "nvme",
    },
)

Tar mode — at stop() the workspace is tarred via exec and embedded (base64) into the session state. On resume() the tar is uploaded and extracted back into the workspace.

NorthflankSandboxClientOptions(
    project_id="my-project",
    image_path="ubuntu:24.04",
    docker_command="sleep infinity",
    workspace_persistence="tar",
)

Examples

  • Listing Northflank services with northflank_tools().
  • Running a remote ShellTool against a Northflank service.
  • Running a sandbox agent on Northflank.

Tests

Unit coverage for the Northflank tools, shell executor, and sandbox client/session behavior.

fr3fou added 2 commits May 18, 2026 15:29
NorthflankSandboxClient + NorthflankSandboxSession (BaseSandboxClient /
BaseSandboxSession) run agent workspaces inside a Northflank service
container. Two modes:

* attach to an existing service (service_id) — client never deletes it.
* ephemeral deployment (image_path) — client creates the deployment and
  deletes it on cleanup. owned_by_client is recorded in session state so
  resume() preserves the ownership decision.

Exec goes through the V1 exec WebSocket; file IO uses the SDK's
file-copy API; workspace persistence is tar through /tmp staging. Tar
payloads are validated locally before extraction because tar -xf runs
remotely and doesn't apply Python's filter='data' policy.

NorthflankCtx + northflank_tools() expose curated @function_tool
wrappers around the Northflank REST API (list/get projects + services,
deploy + restart + pause + resume + scale + wait, exec in
service/job/addon containers, fetch/tail logs, fetch metrics, plus
gated delete/secret/volume/domain mutations). Mutating tools carry
needs_approval=True.

NorthflankShellExecutor wires the same exec channel into the
ShellTool executor interface for agents that don't need a full sandbox.

Adds the northflank optional extra (Python <3.14 since the Northflank
Python SDK targets 3.9-3.13) and the mypy module override.
Three example runners:
* examples/northflank/list_services.py — agent uses northflank_tools()
  to enumerate services in a project and summarise them.
* examples/northflank/remote_shell.py — agent uses ShellTool wired to
  NorthflankShellExecutor so every shell command runs inside a target
  Northflank service.
* examples/sandbox/extensions/northflank_runner.py — manual sandbox
  runner: small workspace, one shell tool, plus a stop/resume snapshot
  round-trip check against a real Northflank service.
@fr3fou fr3fou force-pushed the northflank-provider branch from c0da388 to c0dab52 Compare May 18, 2026 13:16
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c0dab526df

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread pyproject.toml
modal = ["modal==1.3.5"]
runloop = ["runloop_api_client>=1.16.0,<2.0.0"]
vercel = ["vercel>=0.5.6,<0.6"]
northflank = ["northflank>=1.0,<2; python_version < '3.14'"]
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Update the lockfile for the new extra

This adds the northflank optional dependency, but the commit does not update uv.lock, while CI installs from the frozen lock (UV_FROZEN=1 + make sync/uv sync --all-extras). With the current lock the package is not installed, so the new Northflank tests that import northflank at collection time fail with ModuleNotFoundError; please regenerate and commit the lockfile entry for this extra.

Useful? React with 👍 / 👎.

Comment thread pyproject.toml
modal = ["modal==1.3.5"]
runloop = ["runloop_api_client>=1.16.0,<2.0.0"]
vercel = ["vercel>=0.5.6,<0.6"]
northflank = ["northflank>=1.0,<2; python_version < '3.14'"]
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Do not exclude Northflank from the 3.14 test job

The dependency is guarded with python_version < '3.14', but the test workflow runs make sync/make tests on Python 3.14 (.github/workflows/tests.yml matrix includes 3.14). On that job --all-extras still omits northflank, while the new tests import northflank and agents.extensions.sandbox.northflank unconditionally during collection, so the 3.14 CI leg will fail unless the dependency is available there or those tests are skipped when it is not.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants