Metadata-Version: 2.5
Name: plures
Version: 1.25.0
Summary: Human judgment as an API for AI agents. Send a decision to ID verified people and get their consensus back.
Project-URL: Homepage, https://plures.si/developers
Project-URL: Documentation, https://plures.si/docs
Project-URL: Source, https://github.com/plureslabs/plures
Author-email: Plures Labs LLC <hello@plures.si>
License-Expression: MIT
License-File: LICENSE
Keywords: agents,ai-agents,hitl,human-approval,human-in-the-loop,human-review,llm,plures
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Typing :: Typed
Requires-Python: >=3.8
Description-Content-Type: text/markdown

# plures

The official Python client for [Plures](https://plures.si): human judgment as an API for AI agents. Send a decision to ID verified people and get their consensus back before your agent acts. A human in the loop step you can call like any other API.

```bash
pip install plures
```

Python 3.8+, standard library only.

## Use it

```python
from plures import Plures

plures = Plures()  # reads PLURES_API_KEY, or Plures("pk_test_...")

task = plures.verify(
    "Does the invoice match the order?",
    ["Matches", "Does not match"],
    context="Order: 48 chairs, $4,218. Invoice: 48 chairs plus $180 freight, $4,398.",
    verifiers=3,
)
result = task["result"]
if result and result["agreement"] == 1:
    print("Act on:", result["answer"])
else:
    print("Not unanimous: send to the team")
```

Get a free test key at [plures.si](https://plures.si/?for=developers#join). Test keys complete at once with the first option (or `metadata["test_answer"]`) and never reach real people. Set `metadata["test_outcome"]` to `split`, `tie`, `expired` or `slow` to test the cases you route to your own team.

## Production: webhooks

```python
plures.create_task(
    "Should this customer get a refund?", ["Approve", "Decline"],
    context=ticket_text, webhook_url="https://your.app/plures",
    metadata={"order_id": order_id}, idempotency_key="refund-%s" % order_id,
)

# In your webhook handler (Flask shown), pass the raw body exactly as received
from plures import verify_webhook
event = verify_webhook(request.get_data(), request.headers.get("Plures-Signature"), WEBHOOK_SECRET)
```

## Pay per decision with x402 (preview)

Agents can buy a single decision with no API key, paying in USDC on Base through [x402](https://www.x402.org). It runs on the Base Sepolia test network for now; real USDC is coming. Pass a `payment` function that takes the payment requirements and returns the base64 `X-PAYMENT` header your x402 wallet signs:

```python
plures = Plures(payment=sign_x402_payment)  # your x402 signer
task = plures.create_task("Should this customer get a refund?", ["Approve", "Decline"])
plures.get_task(task["id"])  # the receipt is remembered
```

Without a `payment` function, a keyless request raises `PaymentRequiredError` with the requirements in `e.accepts`. See [Pay with x402](https://plures.si/docs#x402).

## API

- `log_turn(question, answer, intent=None, rerouted_to=None, retrieval=None, sources=None, session=None, channel=None)`: answer reviews for assistants. Plures picks the turns that went wrong plus a few at random for your team to grade. `turn_insights(app)` and `export_turns(app, kind)` (routes, qa, missed) turn the grades into routing examples and eval sets.
- `check(check_id, data=None, **overrides)`: run a check from your app's map, one line before the action it guards: `check = plures.check("refunds", {"order_total": total})`, then act only if `check["answer"] == "Approve"`. The check saved on Plures supplies the question, options, which fields people see (the rest come back in `kept_private`), who decides, how often, its conditions and what happens on a timeout. `checked` says whether a person looked; `skipped_because` says why not.
- `create_task(question, options, idempotency_key=None, **fields)`: fields are `context`, `image_url`, `audio_url` or `video_url` with `media_seconds` (a clip up to 180 seconds that people play through before answering), `sensitive`, `instructions`, `verifiers` (1, 3, 5, 7 or 9), `max_verifiers`, `presence_check` (default True), `webhook_url`, `metadata`, `expires_in`, `route`, `category`, `team`, `use_precedent`
- `use_precedent=True` on `create_task` or `verify`: look in your judgment graph first. When people already settled cases like this one (at least three close matches with the same options, all decided the same way with strong agreement), the task comes back completed with `decided_by` `"precedent"` for $0.05 live and people never see it. One in ten still goes to people as an audit. Not with `route="team"`.
- `fast=True` on `create_task` or `verify`: paid speed, served first and pushed at once with a bonus for answers within a minute; $0.10 more per answer live. Not with `route="team"`.
- `find_precedents(question, context=None, options=None, category=None, app=None, limit=None)`: the closest past decisions on cases like this one and, when the case is settled, a `suggestion`. Free.
- `get_task(id)`, `list_tasks(status=None, limit=None, starting_after=None, app=None)`, `cancel_task(id)`
- `Plures(app="support-agent")` or `PLURES_APP`: send everything to one of your apps. `list_apps()`, `create_app(name, id=None)`, `get_app(id)`, `update_app(id, name=None, id=None, default=None, archived=None)`, `archive_app(id)`, `restore_app(id)`, `delete_app(id)`. An archived app keeps its history but takes no new work.
- `map_guide()`, `get_map(app)`, `upload_map(app, agent_map, branch=None)` (a branch other than main gets its own map, compared with main), `set_checkpoint(app, id, status)`: the agent map, where your agent needs a person. Or run `npx plures map` in your repo.
- `create_map_key(app, label=None)`, `list_map_keys(app)`, `revoke_map_key(app, key_id)`: map keys for CI, which can only read and upload that app's map. `map_workflow(app, branch="main")` returns the GitHub Action that maps on every push. `start_map_run(app)`, `list_map_runs(app)`: a run where Plures runs Claude for CI with no Claude key, at cost. `map_estimate(app, kind="map")` shows what the next map or drift fix costs: the Plures engine fee, 5¢ a step of the app's map (the first map of each app is free), plus Claude at cost when Plures runs it. A drift fix is paid by outcome: each quality point it wins back costs 20¢ a step of the map ($3 to $15 a point), at most $500 a fix, with Claude included.
- `github_repos()`, `connect_github(app, "owner/repo")`, `turn_on_github(app)`, `run_github(app)` (map now), `github_status(app)`, `disconnect_github(app)`: Map with GitHub from code. Plures sets the repo's map key secret and opens a pull request adding the workflow; `turn_on_github` merges it. Installing the Plures GitHub App needs a browser once (`install_url`).
- `sample(rubric, output, input=None)`: send every output; a share is graded by people, with personal details taken out first. `create_rubric(name, question, options, sample_rate=0.05)`, `get_rubric(name)` (scores and drift), `list_rubrics()`, `rubric_suggestions(app)` (rubrics the Plures engine suggests from graded answers and the agent map), `update_rubric()`, `delete_rubric()`. `compare(release, a, b, input=None)` and `get_release(release)` for blind release checks. `export_graded(rubric)` for evals or fine tuning.
- `get_routing()`, `set_routing(team=None, groups=None, rules=None)`: named teams of your own people and rules by category, like `groups=[{"name": "Finance", "emails": ["ap@acme.com"]}], rules=[{"category": "refunds", "route": "team", "group": "Finance"}]`. Send a task with `route="team"` to skip verified people ($0.05 per internal vote live), or tag it with `category=` and let the rules decide. A team can vote: set `votes`, `unanimous`, `lead` and `passkey` on it, or `team_votes=3` on a task.
- `set_test_mix({"unanimous": 70, "split": 20, "tie": 5, "expired": 5})`, `get_test_mix()`, `clear_test_mix()`: with a test key, makes a share of test tasks split, tie or expire at random so you can test routing to your team without changing your agent. A task can override it with `metadata={"test_mix": {...}}`, and `metadata.test_outcome` beats both.
- `balance()`: your prepaid credit for live tasks, in cents. Live tasks cost $0.20 per answer (more for clips); a task that could cost more than your available credit raises `PluresError` with `type` `insufficient_credits` (status 402).
- `wait_for(id, timeout=600, interval=3)`: poll until settled
- `verify(question, options, timeout=600, **fields)`: create and wait
- `verify_webhook(raw_body, signature_header, secret)`: check a webhook and return the event

Errors raise `PluresError` with `.status` and `.type`. Server errors are retried twice.

Examples for LangGraph and the OpenAI Agents SDK: [github.com/plureslabs/plures](https://github.com/plureslabs/plures/tree/main/examples). Full reference: [plures.si/docs](https://plures.si/docs).
