Metadata-Version: 2.4
Name: asgard-sdk
Version: 0.1.0
Summary: Python SDK for Asgard Core API
Requires-Python: >=3.11
Requires-Dist: httpx>=0.27
Requires-Dist: pydantic>=2.0
Provides-Extra: dev
Requires-Dist: pytest-httpx>=0.30; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: respx>=0.21; extra == 'dev'
Requires-Dist: ruff>=0.9; extra == 'dev'
Description-Content-Type: text/markdown

# asgard-sdk-python

Python SDK for the Asgard Core API. Sync-first, with async extensibility in mind.

## Installation

```bash
uv add asgard-sdk-python
```

Or for development:

```bash
uv sync --extra dev
```

## Requirements

Python 3.11+

## Quick Start

### BotProviderClient

```python
from asgard import BotProviderClient, GenericBotMessage

client = BotProviderClient(
    host="http://localhost:8080",
    namespace="my-ns",
    bot_provider_name="my-bot",
    api_key="secret",
)

# Send a message (REST)
message = GenericBotMessage(custom_channel_id="ch-1", custom_message_id="m-1", text="hello")
reply = client.send_message(message)
for m in reply.messages:
    print(m.text)

# Stream a message (SSE)
with client.stream(message) as stream:
    for event in stream:
        if event.event_type.value == "asgard.message.delta":
            print(event.fact.message_delta.text, end="", flush=True)

# Upload a blob
with open("image.png", "rb") as f:
    blobs = client.upload_blob(custom_channel_id="ch-1", file=f, filename="image.png", mime="image/png")
```

### FunctionAgentClient

```python
from asgard import FunctionAgentClient

client = FunctionAgentClient(
    host="http://localhost:8080",
    namespace="my-ns",
    bot_provider_name="my-agent",
    api_key="secret",
)

result = client.trigger_json({"key": "value"})

with open("data.csv", "rb") as f:
    result = client.trigger_form({"key": "value"}, file=f, filename="data.csv", mime="text/csv")
```

## Environment Variables

| Variable | Description |
|----------|-------------|
| `EDGE_SERVER_HOST` | EdgeServer host URL |
| `NAMESPACE` | Asgard namespace |
| `BOT_PROVIDER_NAME` | Bot/agent name |
| `BOT_PROVIDER_API_KEY` | API key |

## CLI

```bash
# Bot interactive REPL (SSE by default)
asgard-cli --host http://localhost:8080 --namespace default --bot my-bot --apikey secret

# Switch to REST transport
asgard-cli --host ... --namespace ... --bot ... --apikey ... --transport rest

# Using env vars
export EDGE_SERVER_HOST=http://localhost:8080
export NAMESPACE=default
export BOT_PROVIDER_NAME=my-bot
export BOT_PROVIDER_API_KEY=secret
asgard-cli

# Function agent — JSON trigger
asgard-cli --agent function --json-trigger \
  --trigger-payload '{"key": "value"}' \
  --host ... --namespace ... --bot ... --apikey ...

# Function agent — Form trigger
asgard-cli --agent function --form-trigger \
  --trigger-payload '{"key": "value"}' \
  --form-file ./data.csv --form-mime text/csv \
  --host ... --namespace ... --bot ... --apikey ...
```

### Bot REPL commands

| Command | Description |
|---------|-------------|
| `/help` | Show help |
| `/exit` / `/quit` | Exit |
| `/transport sse\|rest` | Switch transport |
| `/debug on\|off` | Toggle debug mode |
| `/blob <path> [mime]` | Upload and attach a blob |
| `/blobs` | Show attached blob IDs |
| `/clear-blobs` | Clear attached blobs |
| `/channel [id]` | Show or switch channel |
| `/reset [text]` | Send RESET_CHANNEL action |

## Error Handling

```python
from asgard import AsgardError, AsgardStreamError

try:
    reply = client.send_message(message)
except AsgardError as e:
    print(e.detail.message)  # human-readable error
    print(e.detail.code)     # error code

try:
    with client.stream(message) as stream:
        for event in stream:
            ...
except AsgardStreamError as e:
    print(e.detail.message)
```

## Tool Call Consent

```python
from asgard import GenericBotMessage, ToolCallConsentResponseItem, ToolCallConsentResult, PostBackAction

# After receiving a tool_call.consent event:
consent_items = [
    ToolCallConsentResponseItem(
        tool_call_id="tc-1",
        result=ToolCallConsentResult.ALLOW_ONCE,
    )
]

response = GenericBotMessage(
    custom_channel_id="ch-1",
    custom_message_id="m-2",
    action=PostBackAction.RESPONSE_TOOL_CALL_CONSENT,
    tool_call_consents=consent_items,
)
reply = client.send_message(response)
```
