doloopio 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
doloop/__init__.py ADDED
@@ -0,0 +1 @@
1
+ __version__ = "0.1.0"
doloop/cli.py ADDED
@@ -0,0 +1,104 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ doloop — the resistance machine, from the CLI.
4
+
5
+ A thin client: it sends your AI's output to the doloop machine and gets back a
6
+ deterministic verdict (same input, same verdict, every time). The donkey rules
7
+ stay server-side; this client never holds them. Bring your own key.
8
+
9
+ Install: pip install doloopio
10
+ Auth: export DOLOOP_KEY=dlp_... (get one at https://api.doloop.io/dashboard)
11
+ Usage:
12
+ doloop loops # loops remaining on your key
13
+ doloop check "the answer your model produced"
14
+ doloop check -f answer.txt # check a file
15
+ echo "..." | doloop check # check stdin
16
+ doloop check --donkey conversations "User: ...\nBot: ..."
17
+ doloop design https://example.com # design review of a live URL (when live)
18
+ """
19
+ import sys, os, json, urllib.request, urllib.error
20
+
21
+ API = os.environ.get("DOLOOP_API", "https://api.doloop.io")
22
+ KEY = os.environ.get("DOLOOP_KEY", "")
23
+
24
+
25
+ def req(method, path, body=None):
26
+ data = json.dumps(body).encode() if body is not None else None
27
+ r = urllib.request.Request(API + path, data=data, method=method)
28
+ r.add_header("content-type", "application/json")
29
+ if KEY:
30
+ r.add_header("Authorization", "Bearer " + KEY)
31
+ try:
32
+ with urllib.request.urlopen(r, timeout=40) as resp:
33
+ return resp.status, json.loads(resp.read().decode() or "{}"), dict(resp.headers)
34
+ except urllib.error.HTTPError as e:
35
+ try:
36
+ return e.code, json.loads(e.read().decode() or "{}"), dict(e.headers)
37
+ except Exception:
38
+ return e.code, {}, {}
39
+ except Exception as e:
40
+ return 0, {"error": str(e)}, {}
41
+
42
+
43
+ def _text(args):
44
+ if "-f" in args:
45
+ return open(args[args.index("-f") + 1], encoding="utf-8").read()
46
+ pos = [a for i, a in enumerate(args)
47
+ if not a.startswith("-") and (i == 0 or args[i - 1] not in ("-f", "--donkey"))]
48
+ if pos:
49
+ return pos[-1]
50
+ if not sys.stdin.isatty():
51
+ return sys.stdin.read()
52
+ return None
53
+
54
+
55
+ def cmd_check(args):
56
+ donkey = args[args.index("--donkey") + 1] if "--donkey" in args else "writing"
57
+ text = _text(args)
58
+ if not text:
59
+ sys.exit("nothing to check. pass text, -f file, or pipe stdin.")
60
+ st, body, hdr = req("POST", "/v1/check", {"text": text, "donkey": donkey})
61
+ print(json.dumps(body, indent=2))
62
+ rem = hdr.get("x-doloop-loops-remaining") or hdr.get("X-Doloop-Loops-Remaining")
63
+ if rem:
64
+ print("loops remaining: " + rem, file=sys.stderr)
65
+ sys.exit(0 if body.get("verdict") == "pass" else 2)
66
+
67
+
68
+ def cmd_design(args):
69
+ url = next((a for a in args if a.startswith("http")), None)
70
+ if not url:
71
+ sys.exit("usage: doloop design https://your-url")
72
+ st, body, _ = req("POST", "/v1/check-design", {"url": url})
73
+ if st == 404:
74
+ sys.exit("the design endpoint is not live yet on this machine.")
75
+ print(json.dumps(body, indent=2))
76
+ sys.exit(0 if body.get("verdict") == "pass" else 2)
77
+
78
+
79
+ def cmd_loops(args):
80
+ if not KEY:
81
+ sys.exit("set DOLOOP_KEY=dlp_... (get one at https://api.doloop.io/dashboard)")
82
+ st, body, _ = req("GET", "/v1/balance")
83
+ if st == 401:
84
+ sys.exit("key not recognized. check DOLOOP_KEY.")
85
+ n = body.get("loops_remaining")
86
+ print(f"{n:,} loops remaining" if isinstance(n, int) else json.dumps(body))
87
+
88
+
89
+ def main():
90
+ args = sys.argv[1:]
91
+ cmd = args[0] if args else "help"
92
+ rest = args[1:]
93
+ if cmd == "check":
94
+ cmd_check(rest)
95
+ elif cmd == "design":
96
+ cmd_design(rest)
97
+ elif cmd in ("loops", "balance"):
98
+ cmd_loops(rest)
99
+ else:
100
+ print(__doc__.strip())
101
+
102
+
103
+ if __name__ == "__main__":
104
+ main()
@@ -0,0 +1,80 @@
1
+ Metadata-Version: 2.4
2
+ Name: doloopio
3
+ Version: 0.1.0
4
+ Summary: The doloop resistance machine: deterministic checks for AI output, from the CLI.
5
+ License: MIT
6
+ Requires-Python: >=3.8
7
+ Description-Content-Type: text/markdown
8
+ License-File: LICENSE
9
+ Dynamic: license-file
10
+
11
+ # doloop machine
12
+
13
+ An external, **deterministic** check you put between your AI and your users. Send it an output, get back an **objective verdict** — the same verdict every time. You bring your own model; the machine never touches it.
14
+
15
+ > Docs only. No product code lives here — just how to call the machine and copy-paste examples.
16
+
17
+ **Base URL:** `https://api.doloop.io`
18
+
19
+ ## Use doloop when…
20
+
21
+ | Your problem | Call | What you get |
22
+ |---|---|---|
23
+ | Your agent **loops** or repeats itself | `POST /v1/check` | findings flag the repetition; `verdict: fail` (a model in a loop can't see its own loop — doloop can) |
24
+ | Output **drifts** or slides back to a bug it just fixed | `POST /v1/check` | the drift caught from outside |
25
+ | The model pads with **slop**, hedges, jargon | `POST /v1/check` | each tell flagged with a line |
26
+ | A **number isn't in the source**, a total won't reconcile | `documents` donkey | tie-out failure |
27
+ | You need the **same verdict every time** (audit) | `POST /v1/check` | `input_sha256` — reproducible, provable |
28
+ | You want a human to review **only the failures** | gate on `verdict` | triage, not every line |
29
+
30
+ ## The four donkeys
31
+
32
+ A donkey is a deterministic check for one kind of output.
33
+
34
+ | Donkey | Catches | Surface |
35
+ |---|---|---|
36
+ | `writing` | de-slop: dead prose, jargon, self-management tics, flat cadence | https://writing.doloop.io |
37
+ | `conversations` | de-sycophant, de-loop: agreement drift, dialogue going in circles | https://conversations.doloop.io |
38
+ | `presentations` | land-the-finding: slides that bury the point or don't match the data | https://presentations.doloop.io |
39
+ | `documents` | tie-out: a number not in the source, a total that won't reconcile | https://documents.doloop.io |
40
+
41
+ ```bash
42
+ curl https://api.doloop.io/v1/donkeys
43
+ ```
44
+
45
+ ## Quickstart
46
+
47
+ ```bash
48
+ curl https://api.doloop.io/v1/check \
49
+ -H 'content-type: application/json' \
50
+ -d '{"text": "the answer your model just produced"}'
51
+ ```
52
+
53
+ ```json
54
+ { "verdict": "pass", "finding_count": 0, "findings": [], "input_sha256": "..." }
55
+ ```
56
+
57
+ Same text in, same verdict out, every time. `input_sha256` proves it: identical input gives an identical hash gives an identical verdict. **Objective, not subjective** — unlike an AI judge that scores the same answer 77% one run and 63% the next.
58
+
59
+ ## How people use it
60
+
61
+ 1. **Gate before ship** — check the output; if `verdict: fail`, don't ship it.
62
+ 2. **Self-heal loop** (the "doloop") — check → on fail, feed `findings` back to your model → re-check → ship on pass. See [`examples/loop.py`](examples/loop.py).
63
+ 3. **In-agent self-check** — the agent checks its own step output to catch the loop/drift it can't see itself. See [`examples/agent_check.py`](examples/agent_check.py).
64
+ 4. **BYOL proxy** — point an OpenAI-compatible `base_url` at the machine; the verdict rides back on every response. See [`examples/proxy.py`](examples/proxy.py).
65
+ 5. **CI gate** — run a corpus through the donkeys; fail the build on a regression. See [`examples/ci_check.sh`](examples/ci_check.sh).
66
+
67
+ ## Endpoints
68
+
69
+ | Method | Path | Does |
70
+ |---|---|---|
71
+ | `POST` | `/v1/check` | deterministic verdict on text |
72
+ | `GET` | `/v1/donkeys` | list the four donkeys |
73
+ | `POST` | `/v1/chat/completions` | OpenAI-compatible BYOL proxy with the verdict attached |
74
+ | `GET` | `/health` | liveness |
75
+
76
+ Machine-readable spec: [`openapi.json`](openapi.json). LLM index: [`llms.txt`](llms.txt). Pricing and the loop calculator: [`PRICING.md`](PRICING.md).
77
+
78
+ ## What it is
79
+
80
+ doloop is the **Deterministic Objective Loop**: the external checker a model can't be for itself, because a model in a loop can't see its own loop. Bring your model; doloop returns the verdict it can't give itself.
@@ -0,0 +1,8 @@
1
+ doloop/__init__.py,sha256=kUR5RAFc7HCeiqdlX36dZOHkUI5wI6V_43RpEcD8b-0,22
2
+ doloop/cli.py,sha256=lTkpS379-v7b13xIEwogw2DTBnJjSTEh8Vzr8WBFrkM,3703
3
+ doloopio-0.1.0.dist-info/licenses/LICENSE,sha256=kK6T4YXtbHYi3Ug0FvyXhzK8gS-9d-O_LOHBd5BbY2A,1071
4
+ doloopio-0.1.0.dist-info/METADATA,sha256=7l_OMElULeFXHtIpVX8KyHd4wjG1makWwUWYLC4MYAw,3976
5
+ doloopio-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
6
+ doloopio-0.1.0.dist-info/entry_points.txt,sha256=sGdmTntk1puGUriaeJOVoAzGUSPvv_qsu7e3GFGdSbs,43
7
+ doloopio-0.1.0.dist-info/top_level.txt,sha256=IugLGCJg-Xvdv0H09PgoYj6uljcBXuRL9kym9RrlG5g,7
8
+ doloopio-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ doloop = doloop.cli:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Gaurav Rastogi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ doloop