Metadata-Version: 2.4
Name: parsimony-core
Version: 0.2.0a1
Summary: Connector framework for financial data — typed fetch, hybrid-search catalogs, distribute via Hugging Face Hub or S3.
Project-URL: Homepage, https://parsimony.dev
Project-URL: Repository, https://github.com/ockham-sh/parsimony
Project-URL: Documentation, https://docs.parsimony.dev
Author-email: "Ockham.sh" <team@ockham.sh>
License-Expression: Apache-2.0
License-File: LICENSE
Keywords: catalog,connectors,data,finance,fred,sdmx
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Financial and Insurance Industry
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Office/Business :: Financial
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: httpx>=0.28.1
Requires-Dist: pandas<3,>=2.3.0
Requires-Dist: platformdirs<5,>=4.0.0
Requires-Dist: pyarrow>=23.0.1
Requires-Dist: pydantic<3,>=2.11.1
Requires-Dist: tenacity<10,>=9.0.0
Provides-Extra: all
Requires-Dist: faiss-cpu>=1.8.0; extra == 'all'
Requires-Dist: huggingface-hub>=0.25.0; extra == 'all'
Requires-Dist: litellm<2,>=1.59.0; extra == 'all'
Requires-Dist: rank-bm25>=0.2; extra == 'all'
Requires-Dist: s3fs>=2024.0; extra == 'all'
Requires-Dist: sentence-transformers>=3.0.0; extra == 'all'
Provides-Extra: dev
Requires-Dist: faiss-cpu>=1.8.0; extra == 'dev'
Requires-Dist: huggingface-hub>=0.25.0; extra == 'dev'
Requires-Dist: litellm<2,>=1.59.0; extra == 'dev'
Requires-Dist: mypy>=1.10; extra == 'dev'
Requires-Dist: pip-audit>=2.7; extra == 'dev'
Requires-Dist: pytest-asyncio>=1.3.0; extra == 'dev'
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
Requires-Dist: pytest>=9.0.3; extra == 'dev'
Requires-Dist: rank-bm25>=0.2; extra == 'dev'
Requires-Dist: ruff>=0.15.10; extra == 'dev'
Requires-Dist: sentence-transformers>=3.0.0; extra == 'dev'
Requires-Dist: types-requests>=2.31; extra == 'dev'
Provides-Extra: docs
Requires-Dist: mkdocs-material; extra == 'docs'
Provides-Extra: litellm
Requires-Dist: litellm<2,>=1.59.0; extra == 'litellm'
Provides-Extra: s3
Requires-Dist: s3fs>=2024.0; extra == 's3'
Provides-Extra: standard
Requires-Dist: faiss-cpu>=1.8.0; extra == 'standard'
Requires-Dist: huggingface-hub>=0.25.0; extra == 'standard'
Requires-Dist: rank-bm25>=0.2; extra == 'standard'
Requires-Dist: sentence-transformers>=3.0.0; extra == 'standard'
Description-Content-Type: text/markdown

# parsimony

[![PyPI version](https://img.shields.io/pypi/v/parsimony-core)](https://pypi.org/project/parsimony-core/)
[![License](https://img.shields.io/badge/license-Apache%202.0-blue)](LICENSE)
[![Python](https://img.shields.io/pypi/pyversions/parsimony-core)](https://pypi.org/project/parsimony-core/)
[![CI](https://github.com/ockham-sh/parsimony/actions/workflows/test.yml/badge.svg)](https://github.com/ockham-sh/parsimony/actions)
[![Docs](https://img.shields.io/badge/docs-parsimony.dev-blue)](https://docs.parsimony.dev)

Typed, composable data connectors for Python. A small kernel; every data source is a separate package discovered through the entry-point contract.

## Why parsimony

- **Light kernel.** `parsimony` ships as a small package (primitives, discovery, conformance, scaffolding). Data sources are separate PyPI distributions that plug in through a public contract.
- **One calling convention.** `await connectors["name"](params)` across every data source. Parameters are Pydantic models; results carry provenance.
- **Install what you need.** `pip install parsimony parsimony-fred parsimony-sdmx` — the kernel composes whatever is installed.
- **Private connectors as a first-class path.** Customer-private and vendor-published plugins use the same entry-point contract as official ones; the kernel cannot tell them apart.
- **MCP-ready.** Connectors are agent-addressable via the [Model Context Protocol](https://modelcontextprotocol.io/) — the server lives in the separate [`parsimony-mcp`](https://github.com/ockham-sh/parsimony-mcp) distribution.

## Install

Pick what you need. The kernel has no connectors of its own:

```bash
pip install parsimony-core                       # kernel only (tiny footprint)
pip install parsimony-core parsimony-fred        # + FRED
pip install parsimony-core parsimony-sdmx        # + SDMX (ECB, Eurostat, IMF, OECD, BIS, World Bank, ILO)
pip install 'parsimony-core[standard]'           # + canonical Catalog (FAISS + BM25 + sentence-transformers, hf:// loader)
pip install 'parsimony-core[standard,litellm]'   # + LiteLLMEmbeddingProvider (OpenAI, Gemini, Cohere, Voyage, Bedrock)
pip install parsimony-mcp                        # MCP server (separate distribution)
```

> Imports are always `from parsimony import ...`; the bare `parsimony` PyPI name is squatted, so the distribution ships as `parsimony-core`.

Full list of officially-maintained connectors: [ockham-sh/parsimony-connectors](https://github.com/ockham-sh/parsimony-connectors).

## 30-Second Example

Fetch US unemployment rate from FRED:

```python
import asyncio
from parsimony_fred import CONNECTORS as FRED

async def main():
    fred = FRED.bind_deps(api_key="your-fred-key")
    result = await fred["fred_fetch"](series_id="UNRATE")
    print(result.data.tail())
    print(result.provenance)

asyncio.run(main())
```

Or compose everything configured in the environment:

```python
from parsimony import build_connectors_from_env

connectors = build_connectors_from_env()
result = await connectors["fred_fetch"](series_id="UNRATE")
```

`build_connectors_from_env()` walks every installed `parsimony.providers` entry point, binds dependencies from environment variables, and returns a single flat `Connectors` surface.

## Core Primitives

Three decorators, one runtime type:

- `@connector` — typed fetch/search; the bread and butter.
- `@enumerator` — catalog population (KEY + TITLE + METADATA, no DATA).
- `@loader` — observation persistence into a `DataStore`.

Provenance on every result:

```python
result.provenance  # Provenance(source="fred", params={"series_id": "UNRATE"}, fetched_at=...)
```

## The Plugin Contract

Every data source is a separate distribution implementing one contract:

```toml
# your-connector/pyproject.toml
[project]
name = "parsimony-yourname"
dependencies = ["parsimony-core>=0.1.0,<0.3", "pydantic"]
classifiers = ["Framework :: Parsimony :: Contract 1"]

[project.entry-points."parsimony.providers"]
yourname = "parsimony_yourname"
```

Your module exports `CONNECTORS` (required), plus optional `ENV_VARS` and `PROVIDER_METADATA`. The full spec is in [`docs/contract.md`](docs/contract.md) — it's the framework's load-bearing surface.

Building a private connector for your organisation? See [`docs/building-a-private-connector.md`](docs/building-a-private-connector.md) for the customer-private path.

## Discovering Plugins

```python
from parsimony.discovery import discovered_providers

for provider in discovered_providers():
    print(provider.distribution_name, provider.version, provider.connectors.names())
```

Or from the command line:

```bash
parsimony list-plugins                  # what's installed
parsimony conformance verify <package>  # validate against the contract
```

`parsimony conformance verify` is the release-gate tool for every connector package and the security-review artefact for regulated-finance customers.

## Security

- **Allow-list by default.** Officially-maintained plugins (`parsimony-<name>` from [ockham-sh/parsimony-connectors](https://github.com/ockham-sh/parsimony-connectors)) load without opt-in. Non-official plugins require `PARSIMONY_TRUST_PLUGINS=<name1>,<name2>` to load, with a structured log entry for every decision.
- **ABI gate.** The kernel reads each plugin's contract-version classifier before importing the module. Mismatches fail loudly rather than mid-fetch.
- **Single trust root.** Official plugins publish from one signed manifest (`OFFICIAL_PLUGINS.json`) under one publishing identity.

See [`SECURITY.md`](SECURITY.md) for disclosure.

## MCP Server

The MCP server is a separate distribution, [`parsimony-mcp`](https://github.com/ockham-sh/parsimony-mcp):

```bash
pip install parsimony-mcp
parsimony-mcp
```

Every tool-tagged connector becomes an MCP tool.

## Documentation

Full docs at [docs.parsimony.dev](https://docs.parsimony.dev):

- [Quickstart](https://docs.parsimony.dev/quickstart/)
- [Plugin contract (authoritative)](docs/contract.md)
- [Building a private connector](docs/building-a-private-connector.md)
- [Architecture](https://docs.parsimony.dev/architecture/)
- [API Reference](https://docs.parsimony.dev/api-reference/)

## Contributing

- **Kernel changes** (this repo) — see [`CONTRIBUTING.md`](CONTRIBUTING.md).
- **New or updated connectors** — contribute to [ockham-sh/parsimony-connectors](https://github.com/ockham-sh/parsimony-connectors). The conformance suite is the merge gate.

The kernel does not accept provider-specific code. That's structurally enforced (see [`tests/test_kernel_purity.py`](tests/test_kernel_purity.py)).

## License

Apache 2.0.
