Metadata-Version: 2.4
Name: habermas-machine
Version: 1.0.2
Summary: AI-mediated deliberation and social-choice tooling for Habermas Machine experiments.
License-Expression: Apache-2.0
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
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 :: Scientific/Engineering :: Artificial Intelligence
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy>=1.26
Requires-Dist: scipy>=1.15.2
Requires-Dist: typing-extensions>=4.12
Provides-Extra: google
Requires-Dist: google-generativeai>=0.3.2; extra == "google"
Requires-Dist: python-dotenv>=1.0.1; extra == "google"
Provides-Extra: anthropic
Requires-Dist: anthropic<2,>=1; extra == "anthropic"
Requires-Dist: httpx2>=2; extra == "anthropic"
Requires-Dist: python-dotenv>=1.0.1; extra == "anthropic"
Provides-Extra: together
Requires-Dist: pandas>=2.2; extra == "together"
Requires-Dist: python-dotenv>=1.0.1; extra == "together"
Requires-Dist: together>=1.5.34; extra == "together"
Provides-Extra: modal
Requires-Dist: modal==1.4.1; extra == "modal"
Provides-Extra: openrouter
Requires-Dist: httpx>=0.27; extra == "openrouter"
Requires-Dist: openai>=1.40.0; extra == "openrouter"
Requires-Dist: python-dotenv>=1.0.1; extra == "openrouter"
Provides-Extra: all
Requires-Dist: anthropic<2,>=1; extra == "all"
Requires-Dist: google-generativeai>=0.3.2; extra == "all"
Requires-Dist: httpx>=0.27; extra == "all"
Requires-Dist: httpx2>=2; extra == "all"
Requires-Dist: modal==1.4.1; extra == "all"
Requires-Dist: openai>=1.40.0; extra == "all"
Requires-Dist: pandas>=2.2; extra == "all"
Requires-Dist: python-dotenv>=1.0.1; extra == "all"
Requires-Dist: together>=1.5.34; extra == "all"
Dynamic: license-file

# habermas-machine

[![PyPI](https://img.shields.io/pypi/v/habermas-machine.svg)](https://pypi.org/project/habermas-machine/)
[![CI](https://github.com/habermas-machine/habermas_machine/actions/workflows/ci.yml/badge.svg)](https://github.com/habermas-machine/habermas_machine/actions/workflows/ci.yml)
[![Python](https://img.shields.io/pypi/pyversions/habermas-machine.svg)](https://pypi.org/project/habermas-machine/)
[![License](https://img.shields.io/pypi/l/habermas-machine.svg)](https://github.com/habermas-machine/habermas_machine/blob/main/LICENSE)

`habermas-machine` packages the core deliberation, ranking, and social-choice components used to run Habermas Machine experiments.

## Install

Install the core package:

```bash
uv add habermas-machine
```

Install with provider extras when you need live model clients:

```bash
uv add "habermas-machine[all]"
```

Available extras:

- `google`
- `anthropic`
- `together`
- `modal`
- `openrouter`
- `all`

## Optional Anthropic structured output

The standard two-step generator can use Anthropic's native JSON-schema decoding:

```python
from habermas_machine import models

client = models.LLMCLient.ANTHROPIC.get_client("your-supported-claude-model")
statement_model = models.StatementModel.TWO_STEP_DIVERSE.get_model(
    llm_client=client,
    language="en",
    structured_output=True,
)
```

Use this model as the machine's `diverse_statement_model` (or register it in
`statement_models`) and call `HabermasMachine.mediate()` as usual. In
`habermas_api`, enable the option where `TWO_STEP_DIVERSE` is constructed after
updating the installed HM package. Parsed candidate fields keep their existing
shape, so the website's frontend does not need to change. Raw `model_response`
strings, including `candidates.all_text_model_responses` in the machine result,
contain JSON in structured mode instead of tagged text. Analysis scripts that
re-parse those responses must account for the selected format.

Both steps use the same drafting instructions as the tagged prompts, with JSON
format instructions for English and Arabic (other locales retain the English
fallback). Results remain `StatementResult` objects. Attempt limits and failed
response logging are retained; retries never switch formats. Structured mode
requires exactly the requested number of approach strings, each longer than five
characters after trimming, and rejects the whole list if any approach is invalid.
The tagged path retains its existing filtering of short approaches. Statement
length checks remain in place, and incomplete/refused output can still fail
validation.

The default token budget is 20,000, within the Anthropic SDK's default
non-streaming request limit. Unexpected response stop reasons (such as
`max_tokens` and `refusal`) are logged with raw text, token usage, and request ID
when available. Request/SDK errors are logged with the original exception and
traceback. Invalid requests and the SDK's `Streaming is required` errors propagate
immediately; other request failures retain their existing retry behavior.

The default is `False`. Structured mode currently requires an Anthropic model
supporting structured output and the stock two-step prompts/parsers. It supports
only `generate_diverse_statement`; single-statement and batch methods reject the
option rather than using tagged output. Other clients/generators remain unchanged.

## Development

Sync a local development environment with tests and release tooling:

```bash
uv sync --group dev --extra all
```

Run tests:

```bash
uv run pytest
```

Build distribution artifacts:

```bash
uv build
uv run twine check dist/*
```

## Releases

- GitHub Actions runs CI on pushes and pull requests.
  - Runs unit tests.
  - Builds the package.
  - Tests pacakge is installable.
  - Publishes a unique dev build to TestPyPI through trusted publishing.
- Creating a non-prerelease GitHub release publishes the tagged version to PyPI through trusted publishing.
