Skip to content
Merged
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
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
prune tests
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -326,3 +326,9 @@ uv run pre-commit install # one-time setup
- httpx >= 0.23.0
- pydantic >= 2.0
- typing-extensions >= 4.7

## License

This project is licensed under the Apache License 2.0. See [LICENSE](./LICENSE).
For third-party open-source software notices, see
[THIRD_PARTY_NOTICES.md](./THIRD_PARTY_NOTICES.md).
38 changes: 38 additions & 0 deletions THIRD_PARTY_NOTICES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Third-Party Notices

This repository contains code that is derived from or structurally adapted
from third-party open-source projects.

## Anthropic self-hosted worker SDK

Portions of the self-hosted worker lifecycle and local agent tool
implementations under `src/arkruntime/selfhosted` are structurally adapted
from Anthropic's self-hosted worker SDK implementations:

- https://github.com/anthropics/anthropic-sdk-python
- https://github.com/anthropics/anthropic-sdk-go

The upstream projects are licensed under the MIT License. The MIT copyright
and permission notice is preserved below as required by that license.

```text
Copyright 2023 Anthropic, PBC.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
```
3 changes: 3 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ python examples/async_responses_create.py
| `environments.py` | Managed-Agents: Environment lifecycle — Create/Get/List/Update/Delete (cloud + unrestricted networking) |
| `sessions_loop.py` | Managed-Agents: end-to-end agent loop — Agent + Env + Session, send user.message, stream events until idle |
| `memory_stores.py` | Managed-Agents: MemoryStore + nested Memory CRUD |
| `self_hosted_worker.py` | Managed-Agents: self-hosted worker poll / handle loop |

`self_hosted_worker.py` uses the client's production default `https://ark.cn-beijing.volces.com/api/v3`.

The Managed-Agents examples additionally accept `ARK_MODEL_ID` for the model id (falls back to a `${YOUR_MODEL_ID}` placeholder that will 400 at runtime).

Expand Down
88 changes: 88 additions & 0 deletions examples/self_hosted_worker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Copyright (c) 2026 ByteDance Ltd. and/or its affiliates.
# SPDX-License-Identifier: Apache-2.0

# Prepare the Python environment from the repository root before running:
#
# python3 -m venv .venv
# source .venv/bin/activate
#
# python -m pip install -U pip
# python -m pip install -e .
# python examples/self_hosted_worker.py

from __future__ import annotations

import logging
import os
import signal
import sys

_SETUP_INSTRUCTIONS = """Run these commands from the repository root:

python3 -m venv .venv
source .venv/bin/activate

python -m pip install -U pip
python -m pip install -e .
python examples/self_hosted_worker.py"""

try:
from arkruntime import Ark
from arkruntime.selfhosted import ClientAPI, EnvironmentWorker, EnvironmentWorkerOptions
except ModuleNotFoundError as exc:
print(f"failed to import arkruntime: {exc}", file=sys.stderr)
print(_SETUP_INSTRUCTIONS, file=sys.stderr)
raise SystemExit(1) from exc

logger = logging.getLogger("arkruntime.selfhosted.example")


def configure_logging() -> None:
level_name = os.environ.get("ARK_LOG", "info").upper()
level = getattr(logging, level_name, logging.INFO)
logging.basicConfig(
level=level,
format="%(asctime)s %(levelname)s %(name)s %(message)s",
force=True,
)


def required_env(name: str) -> str:
value = os.environ.get(name, "")
if not value:
raise RuntimeError(f"{name} is required")
return value


def main() -> None:
configure_logging()
base_url = os.environ.get("ARK_BASE_URL", "")
environment_id = required_env("MA_ENVIRONMENT_ID")
options = EnvironmentWorkerOptions(
environment_id=environment_id,
worker_id=os.environ.get("MA_WORKER_ID", ""),
workdir=os.environ.get("MA_WORKDIR", "."),
)
client_options = {"api_key": required_env("ARK_API_KEY")}
if base_url:
client_options["base_url"] = base_url
client = Ark(**client_options)
worker = EnvironmentWorker(ClientAPI(client), options)
for sig in (signal.SIGINT, signal.SIGTERM):
signal.signal(sig, lambda _signum, _frame: worker.close())
logger.info(
"starting self-hosted worker base_url=%s environment_id=%s worker_id=%s workdir=%s",
base_url or "default",
environment_id,
options.worker_id,
options.workdir,
)
try:
worker.run()
finally:
worker.close()
client.close()


if __name__ == "__main__":
main()
3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ dev = [
"respx>=0.21",
]

[tool.setuptools]
license-files = ["LICENSE", "THIRD_PARTY_NOTICES.md"]

[tool.setuptools.packages.find]
where = ["src"]
include = ["arkruntime*"]
Expand Down
4 changes: 3 additions & 1 deletion src/arkruntime/resources/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from .chat import AsyncChat, Chat
from .content_generation import AsyncContentGeneration, ContentGeneration
from .embeddings import AsyncEmbeddings, Embeddings
from .environments import AsyncEnvironments, Environments
from .environments import AsyncEnvironments, AsyncEnvironmentWork, Environments, EnvironmentWork
from .files import AsyncFiles, Files
from .images import AsyncImages, Images
from .memory_stores import (
Expand Down Expand Up @@ -56,6 +56,8 @@
"AsyncAgents",
"Environments",
"AsyncEnvironments",
"EnvironmentWork",
"AsyncEnvironmentWork",
"MemoryStores",
"AsyncMemoryStores",
"Memories",
Expand Down
3 changes: 2 additions & 1 deletion src/arkruntime/resources/environments/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from arkruntime.resources.environments.environments import AsyncEnvironments, Environments
from arkruntime.resources.environments.work import AsyncEnvironmentWork, EnvironmentWork

__all__ = ["Environments", "AsyncEnvironments"]
__all__ = ["Environments", "AsyncEnvironments", "EnvironmentWork", "AsyncEnvironmentWork"]
10 changes: 10 additions & 0 deletions src/arkruntime/resources/environments/environments.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import httpx

from ..._base_client import make_request_options
from ..._compat import cached_property
from ..._managed_agents_serialize import dump_body
from ..._resource import AsyncAPIResource, SyncAPIResource
from ..._types import NOT_GIVEN, Body, Headers, NotGiven, Query
Expand All @@ -13,6 +14,7 @@
from ...types.environment.environment import Environment
from ...types.environment.environment_scope import EnvironmentScope
from ...types.environment.list_environments_response import ListEnvironmentsResponse
from .work import AsyncEnvironmentWork, EnvironmentWork

__all__ = ["Environments", "AsyncEnvironments"]

Expand All @@ -29,6 +31,10 @@ def _list_query(*, limit=NOT_GIVEN, page=NOT_GIVEN) -> dict:


class Environments(SyncAPIResource):
@cached_property
def work(self) -> EnvironmentWork:
return EnvironmentWork(self._client)

def create(
self,
*,
Expand Down Expand Up @@ -123,6 +129,10 @@ def delete(


class AsyncEnvironments(AsyncAPIResource):
@cached_property
def work(self) -> AsyncEnvironmentWork:
return AsyncEnvironmentWork(self._client)

async def create(
self,
*,
Expand Down
Loading
Loading