Metadata-Version: 2.4
Name: seekapi
Version: 1.0.5
Summary: SDK for SeekAPI workers: input/output handling, push data and files, success/failure.
Author: SeekAPI
License-Expression: MIT
Project-URL: Repository, https://github.com/seekapi/workers
Keywords: seekapi,worker,lambda,sdk
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Requires-Python: >=3.10
Description-Content-Type: text/markdown

# SeekAPI Python SDK

SDK for building SeekAPI workers in Python. Handles **input** (fetch from presigned URL) and **output** (push JSON and files), with a minimal Lambda return contract.

- **Zero dependencies** — uses only the standard library.
- **Python 3.10+** — works in AWS Lambda runtimes.

## Install

```bash
pip install seekapi
```

## Quick start (recommended)

Use `run()` so the SDK handles input, output, and errors. You only implement a function from input → output:

```python
from seekapi import run

def handler(event, context):
    return run(event, context, lambda input: {
        "message": f"hello {input.get('name', 'world')}"
    })
```

## Low-level API

When you need more control (e.g. multiple `push_data` calls or `push_file`):

```python
from seekapi import (
    get_input,
    create_context,
    push_data,
    push_file,
    register_request_id,
    success,
    failure,
)

def handler(event, context):
    request_id = getattr(context, "aws_request_id", None) if context else None
    job_uuid = (event.get("job_uuid") or "").strip()
    if request_id and job_uuid:
        register_request_id(job_uuid, request_id)

    ctx = create_context(event)
    try:
        input_data = get_input(event)   # fetches and parses JSON from input_presigned_url
        # ... your logic ...
        push_data(ctx, {"result": "ok"})
        return success(request_id=request_id)
    except Exception as e:
        return failure("WORKER_ERROR", str(e), request_id=request_id)
```

### API reference

| Function | Description |
|----------|-------------|
| `get_input(event, timeout=10)` | Fetch and parse job input JSON from `event["input_presigned_url"]`. Raises `MissingInputError` if URL missing, `ValueError` on HTTP/JSON errors. |
| `create_context(event)` | Build context dict for push_data/push_file (job_uuid, execution_token, api_base, secret from env). |
| `push_data(context, data, type="json", timeout=8)` | Push JSON to the job (overwrites response_json). Returns the API JSON response, usually `{ "ok": true }`. |
| `push_file(context, name, local_path, content_type=None, timeout=15)` | Push a file to the job temp_files. Returns the API JSON response, usually `{ "ok": true }`. |
| `register_request_id(job_uuid, request_id, timeout=3)` | Register Lambda request_id for live logs (no-op if env not set). |
| `get_job_fixture()` | In local mode, returns `fixtures["get_job"]` from `SEEKAPI_LOCAL_FIXTURES`, or `{}` if absent. |
| `success(request_id=None)` | Return `{ "ok": true, "request_id"? }`. |
| `failure(code, message, request_id=None)` | Return `{ "ok": false, "error": { "code", "message" }, "request_id"? }`. |
| `run(event, context, user_fn)` | Get input → call `user_fn(input)` → push result → return success; on exception return failure. `run()` adapte les timeouts réseau au temps restant Lambda pour éviter les timeouts `Sandbox.Timedout`. |

### Environment (set by the platform)

- `WORKER_API_BASE_URL` — backend base URL for push and register_request_id.
- `WORKER_INTERNAL_SECRET` — secret for internal API auth.
- `SEEKAPI_LOCAL_MODE=1` — activate local mode for `push_data`, `push_file`, `charge`, and `get_job_fixture()`.
- `SEEKAPI_LOCAL_DIR` — output directory for local artifacts (default: `.seekapi-local` in the current working directory).
- `SEEKAPI_LOCAL_FIXTURES` — optional JSON file with local responses, for example `{ "charge": { ... }, "get_job": { ... } }`.

In production, `WORKER_API_BASE_URL` should target `https://api.seek-api.com` (injected automatically by the SeekAPI deployment flow).

### Local mode

Use local mode to run a worker in Docker or on your machine without a real backend:

```bash
SEEKAPI_LOCAL_MODE=1 \
SEEKAPI_LOCAL_DIR=/tmp/seekapi-out \
SEEKAPI_LOCAL_FIXTURES=/tmp/fixtures.json \
python worker.py
```

Artifacts are written under `SEEKAPI_LOCAL_DIR/<job_uuid>/`:

- `response_json.json` — latest `push_data(...)` payload
- `data_pushes.jsonl` — all JSON pushes with metadata
- `file_pushes.jsonl` — file upload metadata
- `files/` — copied files from `push_file(...)`

Example fixtures file:

```json
{
  "charge": {
    "ok": true,
    "metered_event_count": 3,
    "limit_reached": false
  },
  "get_job": {
    "status": "RUNNING",
    "response_json": {
      "status": "finished"
    }
  }
}
```

When local mode is enabled:

- `push_data(...)` and `push_file(...)` do not call the network and return `{ "ok": true }`
- `charge(...)` returns `fixtures["charge"]` if provided, otherwise `{ "ok": true, "metered_event_count": 0, "limit_reached": false }`
- `get_job_fixture()` returns `fixtures["get_job"]` for local assertions or Docker smoke tests

Do not enable `SEEKAPI_LOCAL_MODE` in deployed Lambdas.

### Errors

- **MissingInputError** — `event` has no `input_presigned_url`.
- **ValueError** — Input fetch failed (HTTP error, timeout, invalid JSON). Message is descriptive.

## Publishing to PyPI

From the package directory:

```bash
pip install build twine
# Après avoir changé version dans pyproject.toml : vider dist/ sinon d’anciens .whl restent utilisés
rm -rf dist build
python -m build
ls dist/   # tu dois voir seekapi-<version>-... correspondant à pyproject.toml
twine check dist/*
twine upload dist/*
```

Bump `version` in `pyproject.toml` before each release (PyPI rejects re-uploading the same version).

**Version dans le build** : `python -m build` lit uniquement `pyproject.toml`, mais les **fichiers** créés vont dans `dist/`. Si tu ne fais pas `rm -rf dist` avant un nouveau build, `twine upload dist/*` peut encore uploader un vieux `seekapi-1.0.1-...` généré avant le bump. En cas de doute : `rm -rf dist build src/seekapi.egg-info` puis rebuild.

### If `twine upload` returns HTTP 400

- **Cause la plus fréquente** : le fichier est **déjà sur PyPI** (même numéro de version qu’une release passée). Incrémente la version, reconstruis avec un `dist/` vide (`rm -rf dist` avant `python -m build`).
- **Ne mélange pas** plusieurs versions dans `dist/` : `twine upload dist/*` enverra tout ; si `1.0.1` existe déjà en ligne, l’upload de `seekapi-1.0.1-*.whl` échouera en 400 même si `1.0.2` serait valide. D’où l’intérêt de vider `dist/` à chaque build.
- Pour le détail exact : `twine upload dist/seekapi-<version>* --verbose`.

Use a version bump and tag before each release.
