Metadata-Version: 2.4
Name: embed-client
Version: 4.0.47
Summary: Async client for the Embedding Service API. Canonical class: EmbeddingClient (JSON-RPC via mcp-proxy-adapter >=8.10).
Author-email: Vasiliy Zdanovskiy <vasilyvz@gmail.com>
License-Expression: MIT
Project-URL: Homepage, https://github.com/vasilyvz/embed-client
Project-URL: Repository, https://github.com/vasilyvz/embed-client
Project-URL: Documentation, https://github.com/vasilyvz/embed-client#readme
Project-URL: Bug Tracker, https://github.com/vasilyvz/embed-client/issues
Project-URL: Configuration Reference, https://github.com/vasilyvz/embed-client/blob/main/CONFIGURATION.md
Keywords: embedding,async,client,api,authentication,ssl,tls,mtls,config,generator,validator,configuration
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
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: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
Classifier: Topic :: Security
Classifier: Topic :: System :: Networking
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: mcp-proxy-adapter>=8.11.1
Requires-Dist: mcp_security_framework>=2.3.2
Provides-Extra: scripts
Requires-Dist: pydantic>=2.0.0; extra == "scripts"
Provides-Extra: test
Requires-Dist: pytest; extra == "test"
Requires-Dist: pytest-asyncio; extra == "test"
Requires-Dist: pytest-xdist; extra == "test"
Requires-Dist: pytest-timeout; extra == "test"
Provides-Extra: dev
Requires-Dist: pytest; extra == "dev"
Requires-Dist: pytest-asyncio; extra == "dev"
Requires-Dist: pytest-xdist; extra == "dev"
Requires-Dist: pytest-timeout; extra == "dev"
Requires-Dist: black; extra == "dev"
Requires-Dist: flake8; extra == "dev"
Requires-Dist: mypy; extra == "dev"
Dynamic: license-file

# embed-client

Python **async** client for the Embedding Service API. The canonical public client is **`EmbeddingClient`**, aligned with the migrated server-side contract (JSON-RPC via **`mcp-proxy-adapter`**).

```python
from embed_client import EmbeddingClient
```

## Installation

```bash
pip install embed-client
```

Editable development checkout:

```bash
git clone <repository-url>
cd embed-client
pip install -e .
```

## Canonical real-server E2E (localhost:8001, mTLS)

The repository ships a **runnable** end-to-end tour of the public API against **localhost:8001** with **mTLS**, using certificates under **`mtls_certificates/`** (see **`mtls_certificates/README.md`** if present).

| Artifact | Path |
|----------|------|
| Example script | [`examples/canonical_mtls_localhost_8001.py`](examples/canonical_mtls_localhost_8001.py) |
| Live integration | Set **`RUN_CANONICAL_MTLS_E2E=1`** to perform real network calls (otherwise dry run / config check) |
| Optional `embed_file` | Set **`CANONICAL_EMBED_FILE_SERVER_PATH`** to a **server-visible** file path to include **`embed_file`** in the tour |
| Tests | [`tests/test_canonical_mtls_example.py`](tests/test_canonical_mtls_example.py) |

```bash
python examples/canonical_mtls_localhost_8001.py
RUN_CANONICAL_MTLS_E2E=1 python examples/canonical_mtls_localhost_8001.py
```

## Minimal usage

```python
import asyncio
from embed_client import EmbeddingClient

async def main() -> None:
    async with EmbeddingClient.from_base_url_port("http://localhost", 8001) as client:
        h = await client.health()
        print("health:", h)
        data = await client.embed(["hello"], error_policy="continue", wait=True)
        print("embed keys:", list(data.keys()) if isinstance(data, dict) else type(data))

if __name__ == "__main__":
    asyncio.run(main())
```

Public queue-oriented helpers include **`embed`**, **`wait_for_job`**, **`job_status`**, **`list_queued_commands`**, **`cmd`**, optional **`embed_file`**, plus introspection (**`list_commands`**, **`models`**, **`default_model`**, **`fetch_openapi_schema`**, **`http_get`**, **`timing_stats`**) and TLS helpers **`is_mtls_enabled`**, **`get_ssl_config`**. See **`embed_client/embedding_client.py`** for the full list.

**`embed_file` vs local JSON without shared server disk:** use **`embed_file`** with a server-visible **`input_file`** when the input already exists on the service host. For a **`json_array`** file that exists only on the client machine (no shared filesystem), use the CLI **`embed-file-json`** subcommand (or **`embed_file_from_local_json_file`**) so the client validates, uploads through the adapter, resolves **`storage_path`** via **`get_upload_session`**, then runs **`embed_file`** with that path and **`format=json_array`**. For Markdown sources (e.g. with YAML front matter), run **`prepare-embed-file-json`** first to emit a valid **`json_array`** (array of strings): the body is split **only** on blank lines (**`\n\n`**) into paragraph-sized strings, with per-paragraph UTF-8 size limits and **no** automatic oversize split — oversized paragraphs require editing the source or raising the byte cap; then **`embed-file-json`**. Add **`--show-progress`** to stream server WebSocket/status lines to stderr during the job wait (see **`CLI_README.md`**).

**Providers (server 4.0.46+):** the full model id is provider + model. **`embed`** and **`embed_file`** take an optional **`provider`** (`"local"` = the server's own model, or a live macsc provider id such as `"openai"`); it is sent only when not `None`. There is no `backend` argument any more (a `TypeError` is raised for `backend=`); the `backend` field of results and `models()` entries is still returned. **`default_model()`** returns exactly `{model, dimension, provider}`; **`models()`** returns `{use_own_model, default: {provider, model}, external: {configured, service}, models: [...]}` with entries `{name, provider, dimension, supported_dimensions, max_tokens, backend, residency (local)}`.

Resolution order (server): provider given -> must be known, model omitted -> the default model of that provider (it must be the default provider's, else `UNKNOWN_MODEL`); provider and model omitted -> the default pair; provider omitted and model given -> the default provider first (if it lists the model it is used and macsc is not consulted for a local default), else the other providers are searched: exactly one offers it -> used, several -> `PROVIDER_REQUIRED`, none -> `UNKNOWN_MODEL`. Dimension omitted -> the entry's default; given -> must satisfy `supported_dimensions` else `UNSUPPORTED_DIMENSION`.

Error codes (`error.data.code` / `error.data.kind`, surfaced unchanged): `-32602` `UNKNOWN_PROVIDER`, `UNKNOWN_MODEL`, `UNSUPPORTED_DIMENSION`, `PROVIDER_REQUIRED`; `-32003` `EXTERNAL_UNAVAILABLE`, `EXTERNAL_UPSTREAM_FAILURE`, `EXTERNAL_BAD_RESPONSE`, `EXTERNAL_INVALID_REQUEST`; `-32001` `MODEL_LOADING` (retry); `-32004` `OWN_MODEL_DISABLED`; `-32005` `GPU_DISABLED`; `-32603` `MODEL_LOAD_FAILED`, `NO_GPU_AVAILABLE`. **`model_offload()`** moves the resident local model VRAM -> RAM (pinned against the GPU watcher) and **`model_load(model=None, device=None, wait=None)`** moves it back (`wait=False` returns at once while the load continues, `wait=True` blocks until it is in VRAM); neither takes a provider. Refusals of `model_offload`/`model_load`/`default_model` arrive as **`EmbedAPIError`** carrying the server error object. Every embed result carries `provider`, `model`, `dimension` and `backend`; external results also carry `usage`.

## Documentation

- **[CONFIGURATION.md](CONFIGURATION.md)** — JSON config schema, protocols, validation
- **[CLI_README.md](CLI_README.md)** — `embed-config-generator`, `embed-config-validator`, `embed-vectorize`
- **[docs/api_format.md](docs/api_format.md)** — JSON-RPC shapes for `embed` and `embed_file` (server path vs upload)
- **[examples/README.md](examples/README.md)** — example index (canonical mTLS first)
- **[docs/SERVER_MODES_AND_CLIENT.md](docs/SERVER_MODES_AND_CLIENT.md)** — security modes vs client behavior (short summary; not full matrix)

## Security modes

The server supports multiple HTTP/HTTPS/mTLS and auth combinations. For deploy-time verification expectations, see the project deploy rule; for field-level mapping, see **CONFIGURATION.md** and **docs/SERVER_MODES_AND_CLIENT.md**.

## Development and tests

Run the test suite with **pytest**; configuration lives in **`pyproject.toml`** (`[tool.pytest.ini_options]`). There is **no** `pytest.ini` at the repository root.

```bash
pytest -q
```

### Release

The client is released together with the server (`embed`) as one unit and always carries the same
version. Releases are run **only** by the server script `docker/build.sh` in the `embed` repository,
which computes the next version, writes it into both trees, and calls this repository's scripts:

- `build.sh <release-version>` - client tests, sdist/wheel, `twine check`; never uploads
- `publish.sh <release-version>` - `twine upload` of the built files, run only after the Docker Hub push succeeded

Both scripts have no options and no skip switches, accept exactly one argument, and refuse a
version that differs from `pyproject.toml` / `embed_client/__init__.py` (exit code 3).

## License

MIT License

## Author

**Vasiliy Zdanovskiy** — vasilyvz@gmail.com
