greennode-agentbase 1.0.2rc7
An SDK for building and deploying AI agents with GreenNode AgentBase
License: Apache-2.0
Requires Python: >=3.10

# GreenNode AgentBase SDK

A Python SDK for building and deploying AI agents with GreenNode AgentBase runtime.

## Overview

GreenNode AgentBase SDK provides a runtime framework for deploying AI agents with support for:
- **HTTP endpoint handling** for agent invocations
- **Identity:** workload identity, API keys, access tokens (and decorators `requires_api_key`, `requires_access_token`)
- **Memory:** memory, sessions, and events integration
- **Agent wrapper:** request context management, streaming responses

## Installation

```bash
pip install greennode-agentbase
```

## Quick Start

**Configuration (optional)**  
Values are resolved in order: environment variables → `.greennode.json`. Use `.greennode.json` for non-sensitive defaults and environment variables for secrets. Create `.greennode.json` in your project root with dummy values only; do not commit real credentials.

Example `.greennode.json`:

```json
{
    "client_id": "your-client-id-uuid",
    "client_secret": "your-client-secret",
    "agent_identity": "your-workload-name",
    "agent_user_id": "default-user-id"
}
```

```python
from greennode_agentbase import GreenNodeAgentBaseApp

# Create the application
app = GreenNodeAgentBaseApp()

# Define your agent handler
@app.entrypoint
def my_agent(payload: dict):
    """Your agent logic here."""
    query = payload.get("query", "")
    # Process the query with your agent
    return {"response": f"Processed: {query}"}

# Run the server
if __name__ == "__main__":
    app.run(port=8080)
```

## Usage

### Basic Agent Handler

```python
from greennode_agentbase import GreenNodeAgentBaseApp

app = GreenNodeAgentBaseApp()

@app.entrypoint
def simple_agent(payload: dict) -> dict:
    """Simple agent that echoes the input."""
    return {"echo": payload}
```


### Handler with Request Context

```python
from greennode_agentbase import GreenNodeAgentBaseApp, RequestContext

app = GreenNodeAgentBaseApp()

@app.entrypoint
def contextual_agent(payload: dict, context: RequestContext) -> dict:
    """Agent that uses request context."""
    session_id = context.session_id
    user_id = context.user_id  # from header or .greennode.json agent_user_id
    headers = context.request_headers or {}
    return {
        "result": payload,
        "session_id": session_id,
        "user_id": user_id,
        "authorization": headers.get("Authorization"),
    }
```

### Decorators (identity)

Use `requires_api_key` to inject an API key from a provider (e.g. for external APIs). Use `requires_access_token` to inject an OAuth2 access token (e.g. for user-scoped APIs like Gmail). Import from `greennode_agentbase.identity.auth`.

**`requires_api_key`** – the decorator fetches the API key and passes it into the named parameter (`into`):

```python
from greennode_agentbase.identity.auth import requires_api_key

@requires_api_key(
    provider_name="my-api-provider",
    into="api_key",
    auth_flow="USER_FEDERATION",
    callback_url="https://your-app/callback",
    force_delegation=True,
)
def call_external_api(query: str, api_key: str = None) -> str:
    # api_key is injected by the decorator
    response = requests.get("https://api.example.com/check", headers={"Key": api_key}, params={"q": query})
    return response.text
```

**`requires_access_token`** – the decorator obtains an access token (e.g. via user federation) and passes it into the named parameter (`into`):

```python
from greennode_agentbase.identity.auth import requires_access_token

SCOPES = ["https://www.googleapis.com/auth/gmail.readonly"]

@requires_access_token(
    provider_name="gmail-provider",
    scopes=SCOPES,
    auth_flow="USER_FEDERATION",
    callback_url="https://your-app/oauth2/callback",
    into="access_token",
    force_authentication=True,
)
def fetch_emails(days: int = 7, access_token: str = None) -> str:
    # access_token is injected by the decorator
    from google.oauth2.credentials import Credentials
    from googleapiclient.discovery import build
    creds = Credentials(token=access_token)
    service = build("gmail", "v1", credentials=creds)
    # ... use service
    return "summary"
```

For full examples (e.g. tools with signature surgery for agent frameworks), see the sdk-playground repository.

## Command-line interface (CLI)

The `greennode` CLI provides deploy, memory, runtime, and MCP server commands. Use `greennode --help` and `greennode <group> --help` for details.


### CLI hierarchy

```text
greennode
├── deploy
│   ├── run                    Deploy agent (build, push, create runtime). Use -o id to print only the runtime id.
│   └── update <runtime-id>    Build and push image, patch runtime, set default endpoint to new version (optional -i/--image-name, -v/--version).
├── identity
│   ├── whoami                  Show current workload identity and credential status
│   ├── login                   Configure IAM credentials, save config, and create workload identity if needed
│   ├── logout                  Clear workload and credentials from config
│   ├── workload
│   │   ├── create              Create a workload identity (optional --name, --set-current)
│   │   ├── use <name>          Set the current workload identity
│   │   └── list                List workload identities (optional --page, --size)
│   ├── outbound-auth           Manage outbound auth providers (prompt per field; empty or 'q' to cancel)
│   │   ├── static              API key (static) providers
│   │   │   ├── create          Create provider (prompt: name, apikey)
│   │   │   ├── list             List providers (prompt: page, size; or "not available" if API has no list)
│   │   │   ├── update          Update provider (prompt: name, apikey)
│   │   │   ├── get-key         Get API key for agent identity (prompt: provider_name, agent_identity_name)
│   │   │   └── delete          Delete provider (prompt: name)
│   │   ├── delegated           Delegated API key providers
│   │   │   ├── create          Create provider (prompt: name)
│   │   │   ├── list             List providers (same as static)
│   │   │   ├── get-key         Get delegated API key (prompt: provider_name, agent_identity_name, agent_user_id, return_url, optional fields)
│   │   │   └── delete          Delete provider (prompt: name)
│   │   └── oauth2              OAuth2 providers
│   │       ├── create          Create provider (prompt: name, client_id, client_secret, authorization_url, token_url)
│   │       ├── list             List providers (same as static)
│   │       ├── update          Update provider (prompt: name, client_id, client_secret, authorization_url, token_url)
│   │       ├── m2m-token       Get M2M token (prompt: provider_name, agent_identity_name, scopes)
│   │       ├── 3lo-token       Get 3LO token (prompt: provider_name, agent_identity_name, agent_user_id, scopes, return_url, optional)
│   │       └── delete          Delete provider (prompt: name)
│   └── config
│       └── show                Show config file path and non-sensitive values
├── mcp
│   ├── serve                   Start the GreenNode AgentBase helper MCP server (list_available_guides, read_guide_page)
│   └── run                     Alias for serve
├── memory
│   ├── create                  Create a memory (-n name, -d description, -S strategy JSON, optional --event-expiry-duration)
│   ├── list                    List memories (optional --page, --size)
│   ├── get <memory-id>         Get a memory by ID
│   ├── delete <memory-id>      Delete a memory (optional -y)
│   ├── strategies
│   │   └── list <memory-id>    List long-term memory strategies for a memory
│   ├── records
│   │   ├── list <memory-id> -n <namespace>           List memory records
│   │   ├── search <memory-id> -n <namespace> -q <query>   Search memory records
│   │   ├── insert <memory-id> -n <namespace> -c <content>... or -f <file>   Insert memory records
│   │   ├── delete <memory-id> <record-id>            Delete a memory record
│   │   └── generate <memory-id> --strategy-id <id>   Generate from session (--from-session, --actor-id, --session-id) or content (-c/--file)
│   ├── sessions
│   │   └── list <memory-id> <actor-id>               List sessions (optional --page, --size)
│   └── events
│       ├── list <memory-id> <actor-id> <session-id>  List events (optional --page, --size)
│       ├── create <memory-id> <actor-id> <session-id> -r <role> -c <content>   Create an event
│       └── delete <memory-id> <actor-id> <session-id> <event-id>   Delete an event
└── runtime
    ├── list                    List runtimes (optional --page, --size)
    ├── get <runtime-id>        Get a runtime by ID
    ├── patch <runtime-id>      Update a runtime (optional --image-url, --flavor-id, --command, --args, --env)
    └── endpoints
        ├── list <runtime-id>   List endpoints for a runtime (optional --page, --size)
        ├── create <runtime-id>  Create an endpoint (-n name, optional --version)
        ├── update <runtime-id> <endpoint-id>   Update endpoint (e.g. --version)
        └── delete <runtime-id> <endpoint-id>  Delete an endpoint
```

### Best practices

- **When to use which:** Use `greennode deploy run` for a full deploy (build, push, create runtime). Use `greennode deploy update <runtime-id>` to update an existing runtime with a new image and switch its default endpoint. Use `greennode runtime list`, `get`, `patch` and `greennode runtime endpoints ...` to manage runtimes and endpoints without redeploying.
- **Scripting:** After `greennode deploy run`, the runtime id is printed on its own line. Use `greennode deploy run -o id` to print only the runtime id for scripts.
- **Outbound auth:** Use `greennode identity outbound-auth <static|delegated|oauth2> <create|list|update|...>` to manage providers. Each command prompts for required fields; leave a prompt empty or type `q` to cancel without calling the API.

### MCP server (optional)

The MCP server exposes tools for retrieving docs: `list_available_guides` and `read_guide_page`. It requires Python 3.10+ and the optional `mcp` extra:

```bash
pip install greennode-agentbase[mcp]
greennode mcp serve
```

For local testing and sample running you can use a dedicated environment, e.g. `~/environments/agentic/env`:

```bash
~/environments/agentic/env/bin/pip install -e ".[mcp]"
~/environments/agentic/env/bin/greennode mcp serve
```

Edit `src/greennode_agentbase/mcp/guides.py` to set `DOCS_BASE_URL` and `AVAILABLE_GUIDES` for your docs site. Xem thêm [docs/MCP_AND_VKS.md](docs/MCP_AND_VKS.md) để biết cách dùng helper và tóm tắt cách VKS hoạt động.

## Development

This project uses [uv](https://docs.astral.sh/uv/) for dependency management and recommends it for local development.

### UV guide

**Prerequisites**

Install [uv](https://docs.astral.sh/uv/) (e.g. `curl -LsSf uv.run | sh` or `pip install uv`).

**Project setup**

Clone the repo, then run:

```bash
uv sync
```

This creates a virtual environment (if needed), installs all dependencies from the lockfile (including the `dev` and optional `build` groups), and uses the Python version from `.python-version` when present. The lockfile `uv.lock` pins exact versions for reproducible installs.

**Common commands**

```bash
uv run pytest                    # Run tests
uv run greennode --help          # Run the CLI
uv run hatch build               # Build the package
```

To add or update dependencies: `uv add <pkg>` or edit `pyproject.toml` and run `uv lock`.

**Installing from source (editable)**

After `uv sync`, the project is available in the environment. For an editable install in the current venv: `uv pip install -e .`

### Testing guide

**Run all tests**

```bash
uv run pytest
```

Or `pytest` if the venv is already activated.

**Run with coverage**

```bash
uv run pytest tests/ --cov=src/greennode_agentbase --cov-report=term-missing --cov-report=html
```

Coverage must stay at or above 80% (`--cov-fail-under=80` in CI).

**Test layout**

Tests live in `tests/`, with `conftest.py` providing shared fixtures. The structure mirrors `src/greennode_agentbase/` (e.g. `tests/core/`, `tests/runtime/`, `tests/identity/`).

**Run a subset**

```bash
uv run pytest tests/test_exceptions.py -v
uv run pytest tests/runtime/ -v
```

### Linting and type checking

```bash
ruff check src tests
mypy src
```

## License

Apache License 2.0 - see [LICENSE.txt](LICENSE.txt) for details.

## Contributing

Contributions are welcome! Please ensure:
- All tests pass
- Code follows the style guidelines (ruff, mypy)
- New features include tests
- Documentation is updated

## Support

For issues and questions, please use the project's issue tracker.