Metadata-Version: 2.4
Name: steamory-agent-kit-gum
Version: 0.1.0
Summary: Python SDK for the Gum Memory API.
Project-URL: Homepage, https://github.com/steamory-agent-kit/gum-sdk-python
Project-URL: Repository, https://github.com/steamory-agent-kit/gum-sdk-python
Project-URL: Issues, https://github.com/steamory-agent-kit/gum-sdk-python/issues
License-Expression: MIT
License-File: LICENSE
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Typing :: Typed
Requires-Python: >=3.9
Requires-Dist: httpx<1,>=0.27
Requires-Dist: typing-extensions>=4.8
Provides-Extra: dev
Requires-Dist: build>=1.2; extra == 'dev'
Requires-Dist: mypy>=1.10; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: respx>=0.21; extra == 'dev'
Requires-Dist: ruff>=0.6; extra == 'dev'
Requires-Dist: tomli>=2.0; (python_version < '3.11') and extra == 'dev'
Requires-Dist: twine>=5.0; extra == 'dev'
Description-Content-Type: text/markdown

# steamory-agent-kit-gum

Python SDK for the Gum Memory API. Use it to create conversation sessions,
append messages, retrieve contextual memory, and write user action events from
Python services.

## Features

- Synchronous and asynchronous clients.
- TypedDict-based request types that still accept normal Python dictionaries.
- Session object API aligned with the Node.js SDK.
- Automatic API key normalization, host normalization, path escaping, and
  datetime serialization.
- Typed API, connection, and timeout errors.

## Installation

```sh
pip install steamory-agent-kit-gum
```

## Quick Start

```python
from gum import GumClient

gum = GumClient(api_key="gum_api_key")

session = gum.sessions.create({
    "user_id": "user_123",
    "title": "Team scheduling session",
})

session.add_messages([
    {
        "role": "user",
        "content": "Use Berlin for Europe team scheduling.",
    },
])

memory = session.get_memory({
    "query": "which city should be used for Europe scheduling",
    "details": True,
})

print(memory.get("data"))
```

## Async Usage

```python
from gum import AsyncGumClient


async def main() -> None:
    async with AsyncGumClient(api_key="gum_api_key") as gum:
        session = await gum.sessions.create({"user_id": "user_123"})
        await session.add_message({"role": "user", "content": "hello"})
        memory = await session.get_memory({"query": "hello"})
        print(memory.get("data"))
```

## Configuration

```python
from gum import GumClient

gum = GumClient(
    api_key="gum_api_key",
    host="gum.asix.inc",
    timeout_ms=30_000,
)
```

| Option | Default | Description |
| --- | --- | --- |
| `api_key` | Required | Gum API key. The SDK sends `Authorization: Api-Key <api_key>`. Values already starting with `Api-Key ` are preserved. |
| `host` | `gum.asix.inc` | Plain hosts are normalized to HTTPS and trailing slashes are removed. Explicit `http://` or `https://` values are preserved. |
| `timeout_ms` | `30000` | Request timeout in milliseconds. Use `0` to disable the SDK timeout. |
| `headers` | `None` | Default headers merged into every request. |
| `client` | `None` | Optional `httpx.Client` or `httpx.AsyncClient` for custom transports, proxies, or tests. |

## API Reference

### Client

`GumClient` exposes synchronous resources:

- `gum.health(options=None)`
- `gum.sessions`
- `gum.user_actions`

`AsyncGumClient` exposes the same resources with awaitable network methods.

### Sessions

Create a session:

```python
session = gum.sessions.create({
    "user_id": "user_123",
    "metadata": {"source": "assistant-api"},
})

print(session.id)
print(session.raw_response)
```

Restore a local session helper from a stored id without a network request:

```python
session = gum.sessions.from_id("session_123")
```

Add messages:

```python
session.add_message({"role": "user", "content": "hello"})

session.add_messages([
    {"role": "user", "content": "hello"},
    {"role": "assistant", "content": "hi"},
])

gum.sessions.add_messages("session_123", {
    "messages": [{"role": "user", "content": "hello"}],
})
```

Retrieve memory:

```python
memory = session.get_memory({
    "query": "which city should be used for Europe scheduling",
    "details": True,
})
```

Pass `recall_config` to use the POST context endpoint:

```python
memory = session.get_memory({
    "query": "which city should be used for the user request",
    "details": True,
    "recall_config": {
        "message_recent_limit": 20,
        "message_semantic_top_k": 8,
        "query_router": "single_hop_parallel",
        "enable_long_term_recall": False,
    },
})
```

### User Actions

```python
from datetime import datetime, timezone

gum.user_actions.create({
    "user_id": "user_123",
    "timestamp": datetime(2026, 4, 22, 1, 2, 3, tzinfo=timezone.utc),
    "content": "User opened the Europe team scheduling page",
    "session_id": "session_123",
    "event_type": "page_view",
    "page": "team_scheduling",
    "anchors": {"region": "Europe", "city": "Berlin"},
    "metadata": {"source": "assistant-api"},
})
```

The Python SDK intentionally does not expose `threads`, `get_context`, or
`user_actions.query` in this first version. The public business surface is kept
aligned with the current Node.js SDK.

## Error Handling

```python
from gum import GumApiError, GumConnectionError, GumTimeoutError

try:
    gum.sessions.create({"user_id": "user_123"})
except GumApiError as error:
    print(error.status_code, error.detail, error.body)
except GumTimeoutError as error:
    print(f"Timed out after {error.timeout_ms}ms")
except GumConnectionError as error:
    print("Network failure", error.cause)
```

## Development

```sh
python -m pip install -e ".[dev]"
ruff check .
mypy src tests
pytest --cov=gum --cov-fail-under=95
```

Live smoke tests are optional and require a real API key:

```sh
GUM_API_KEY=your_gum_api_key_here pytest tests/live -m live
```

## GitLab CI

The GitLab pipeline verifies, builds, syncs to GitHub, and publishes the package:

- `verify`: installs the package with dev dependencies, then runs `ruff`, `mypy`,
  and coverage-gated tests.
- `build`: builds the source distribution and wheel, then runs `twine check`.
- `sync_github`: manual job on `main`, mirroring this repository to GitHub with
  `force-with-lease`.
- `publish`: automatic for tags and manual on the default branch; skips upload
  when the same package version already exists on PyPI.

Required GitLab CI/CD variables:

| Variable | Used by | Description |
| --- | --- | --- |
| `GITHUB_TOKEN` | `sync_github` | GitHub token with permission to push to the mirror repository. |
| `GITHUB_REPOSITORY_PYTHON_SDK` | `sync_github` | GitHub repository path, for example `steamory-agent-kit/gum-sdk-python`. |
| `PYPI_API_TOKEN` | `publish` | PyPI API token. The pipeline passes it to Twine as `__token__`. |

## License

MIT
