thinkless 0.2.0__tar.gz

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.
Files changed (85) hide show
  1. thinkless-0.2.0/.gitignore +33 -0
  2. thinkless-0.2.0/CHANGELOG.md +73 -0
  3. thinkless-0.2.0/LICENSE +202 -0
  4. thinkless-0.2.0/PKG-INFO +357 -0
  5. thinkless-0.2.0/README.md +271 -0
  6. thinkless-0.2.0/pyproject.toml +136 -0
  7. thinkless-0.2.0/src/thinkless/__init__.py +63 -0
  8. thinkless-0.2.0/src/thinkless/__main__.py +3 -0
  9. thinkless-0.2.0/src/thinkless/_hub.py +84 -0
  10. thinkless-0.2.0/src/thinkless/_json.py +33 -0
  11. thinkless-0.2.0/src/thinkless/_version.py +1 -0
  12. thinkless-0.2.0/src/thinkless/bench/__init__.py +22 -0
  13. thinkless-0.2.0/src/thinkless/bench/intents.py +406 -0
  14. thinkless-0.2.0/src/thinkless/bench/intents_report.py +87 -0
  15. thinkless-0.2.0/src/thinkless/bench/metrics.py +115 -0
  16. thinkless-0.2.0/src/thinkless/bench/report.py +123 -0
  17. thinkless-0.2.0/src/thinkless/bench/support.py +342 -0
  18. thinkless-0.2.0/src/thinkless/cli/__init__.py +1 -0
  19. thinkless-0.2.0/src/thinkless/cli/main.py +595 -0
  20. thinkless-0.2.0/src/thinkless/confidence.py +62 -0
  21. thinkless-0.2.0/src/thinkless/data/pricing.toml +56 -0
  22. thinkless-0.2.0/src/thinkless/data/viewer.html +411 -0
  23. thinkless-0.2.0/src/thinkless/decision.py +185 -0
  24. thinkless-0.2.0/src/thinkless/demo/__init__.py +1 -0
  25. thinkless-0.2.0/src/thinkless/demo/support/__init__.py +19 -0
  26. thinkless-0.2.0/src/thinkless/demo/support/agent.py +379 -0
  27. thinkless-0.2.0/src/thinkless/demo/support/data/calibration.jsonl +48 -0
  28. thinkless-0.2.0/src/thinkless/demo/support/data/scenarios.jsonl +53 -0
  29. thinkless-0.2.0/src/thinkless/demo/support/data/world.json +78 -0
  30. thinkless-0.2.0/src/thinkless/demo/support/questions.py +118 -0
  31. thinkless-0.2.0/src/thinkless/demo/support/stack.py +93 -0
  32. thinkless-0.2.0/src/thinkless/demo/support/world.py +165 -0
  33. thinkless-0.2.0/src/thinkless/engine.py +605 -0
  34. thinkless-0.2.0/src/thinkless/errors.py +13 -0
  35. thinkless-0.2.0/src/thinkless/llm/__init__.py +47 -0
  36. thinkless-0.2.0/src/thinkless/llm/anthropic.py +126 -0
  37. thinkless-0.2.0/src/thinkless/llm/base.py +82 -0
  38. thinkless-0.2.0/src/thinkless/llm/factory.py +91 -0
  39. thinkless-0.2.0/src/thinkless/llm/local.py +168 -0
  40. thinkless-0.2.0/src/thinkless/llm/openai_compat.py +165 -0
  41. thinkless-0.2.0/src/thinkless/llm/openrouter.py +78 -0
  42. thinkless-0.2.0/src/thinkless/llm/scripted.py +75 -0
  43. thinkless-0.2.0/src/thinkless/logs.py +68 -0
  44. thinkless-0.2.0/src/thinkless/pricing.py +100 -0
  45. thinkless-0.2.0/src/thinkless/providers/__init__.py +43 -0
  46. thinkless-0.2.0/src/thinkless/providers/base.py +108 -0
  47. thinkless-0.2.0/src/thinkless/providers/gliner.py +194 -0
  48. thinkless-0.2.0/src/thinkless/providers/hf.py +161 -0
  49. thinkless-0.2.0/src/thinkless/providers/laya.py +121 -0
  50. thinkless-0.2.0/src/thinkless/providers/llm.py +305 -0
  51. thinkless-0.2.0/src/thinkless/providers/rules.py +181 -0
  52. thinkless-0.2.0/src/thinkless/providers/systemone.py +147 -0
  53. thinkless-0.2.0/src/thinkless/providers/wire.py +108 -0
  54. thinkless-0.2.0/src/thinkless/py.typed +0 -0
  55. thinkless-0.2.0/src/thinkless/questions.py +246 -0
  56. thinkless-0.2.0/src/thinkless/settings.py +93 -0
  57. thinkless-0.2.0/src/thinkless/tracing/__init__.py +23 -0
  58. thinkless-0.2.0/src/thinkless/tracing/console.py +169 -0
  59. thinkless-0.2.0/src/thinkless/tracing/otel.py +139 -0
  60. thinkless-0.2.0/src/thinkless/tracing/sinks.py +142 -0
  61. thinkless-0.2.0/src/thinkless/tracing/span.py +98 -0
  62. thinkless-0.2.0/src/thinkless/tracing/summary.py +143 -0
  63. thinkless-0.2.0/src/thinkless/tracing/tracer.py +159 -0
  64. thinkless-0.2.0/src/thinkless/tracing/viewer.py +87 -0
  65. thinkless-0.2.0/tests/__init__.py +0 -0
  66. thinkless-0.2.0/tests/conftest.py +81 -0
  67. thinkless-0.2.0/tests/local/__init__.py +0 -0
  68. thinkless-0.2.0/tests/local/test_local_models.py +122 -0
  69. thinkless-0.2.0/tests/network/__init__.py +0 -0
  70. thinkless-0.2.0/tests/network/test_openrouter_live.py +49 -0
  71. thinkless-0.2.0/tests/unit/__init__.py +0 -0
  72. thinkless-0.2.0/tests/unit/test_bench.py +96 -0
  73. thinkless-0.2.0/tests/unit/test_cli_and_viewer.py +128 -0
  74. thinkless-0.2.0/tests/unit/test_confidence.py +44 -0
  75. thinkless-0.2.0/tests/unit/test_engine.py +375 -0
  76. thinkless-0.2.0/tests/unit/test_hf_classifier.py +70 -0
  77. thinkless-0.2.0/tests/unit/test_llm_decider.py +107 -0
  78. thinkless-0.2.0/tests/unit/test_openrouter.py +228 -0
  79. thinkless-0.2.0/tests/unit/test_pricing_and_llm.py +119 -0
  80. thinkless-0.2.0/tests/unit/test_questions.py +75 -0
  81. thinkless-0.2.0/tests/unit/test_rules.py +77 -0
  82. thinkless-0.2.0/tests/unit/test_support_demo.py +172 -0
  83. thinkless-0.2.0/tests/unit/test_systemone.py +126 -0
  84. thinkless-0.2.0/tests/unit/test_systemone_sdk_compat.py +79 -0
  85. thinkless-0.2.0/tests/unit/test_tracing.py +91 -0
@@ -0,0 +1,33 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ .eggs/
6
+ build/
7
+ dist/
8
+ .venv/
9
+ venv/
10
+
11
+ # Tooling
12
+ .pytest_cache/
13
+ .ruff_cache/
14
+ .mypy_cache/
15
+ .coverage
16
+ coverage.xml
17
+ htmlcov/
18
+ site/
19
+
20
+ # ThinkLess runtime output (traces, viewer exports, scratch benchmark runs)
21
+ .thinkless/
22
+
23
+ # Editors and OS
24
+ .idea/
25
+ .vscode/
26
+ *.swp
27
+ .DS_Store
28
+ Thumbs.db
29
+
30
+ # Secrets
31
+ .env
32
+ .env.*
33
+ !.env.example
@@ -0,0 +1,73 @@
1
+ # Changelog
2
+
3
+ All notable changes are recorded here. The format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project uses
5
+ [semantic versioning](https://semver.org/).
6
+
7
+ ## Unreleased
8
+
9
+ ## 0.2.0 - 2026-09-25
10
+
11
+ First release on PyPI.
12
+
13
+ ### Added
14
+
15
+ - `OpenRouterLLM` and the `openrouter:<slug>` spec: one key for models from
16
+ Anthropic, OpenAI, Google, Qwen, DeepSeek and others.
17
+ - Billed cost: `Completion.cost_usd` and `ProviderResult.cost_usd` carry the
18
+ cost a backend reports (OpenRouter does); the engine prefers it over the
19
+ price table and records `cost_source` on spans. Benchmark reports add a
20
+ billed cost row, and the intent benchmark reports cost per 1k per provider
21
+ and per cascade threshold.
22
+ - `--reasoning` for every CLI command that builds an LLM, and
23
+ `from_spec(..., reasoning=...)`, mapped to OpenRouter's `reasoning`,
24
+ Anthropic's `effort` and local thinking switches.
25
+ - `HFClassifier`: any Hugging Face text-classification model as a provider for
26
+ the questions it was trained for.
27
+ - `load_env()`, and the CLI reads `./.env` without overriding set variables.
28
+ - Escalation context: when a question escalates to the LLM decider, the
29
+ engine includes the decisions already settled in the same batch. It removed
30
+ false injection flags on requests for a human that appeared when an LLM saw
31
+ the injection question alone. On by default (`Engine(escalation_context=...)`).
32
+ - The System One wire format is tested against TypeSafe's official SDK models.
33
+
34
+ ### Fixed
35
+
36
+ - `LLMDecider` detects replies cut off at the token limit, logs why, and
37
+ retries once with a larger budget instead of abstaining silently.
38
+ - The OpenAI-compatible client retries once without `response_format` when an
39
+ endpoint rejects JSON mode.
40
+ - A redaction test that failed about one run in fifty when a random span id
41
+ contained its marker.
42
+
43
+ ## 0.1.0 - 2026-09-25
44
+
45
+ First public release.
46
+
47
+ ### Added
48
+
49
+ - Typed questions: `Choice`, `Score`, `YesNo` and `Extract`, following the
50
+ System One wire format.
51
+ - `Engine` with a confidence cascade across providers, batched provider calls,
52
+ `accepted`, `uncertain` and `abstained` statuses, per-question and
53
+ per-provider thresholds (`"question@provider"`), question-level provider
54
+ allowlists, and async variants of every call.
55
+ - Normalized confidence, `(k * p_max - 1) / (k - 1)`, applied uniformly across
56
+ providers.
57
+ - Decision providers: `Rules`, `GLiNER` (GLiNER 2.5), `Laya`, `SystemOne`
58
+ (TypeSafe Jev and compatible servers such as Kev and OpenJev) and
59
+ `LLMDecider`.
60
+ - LLM backends: `TransformersLLM`, `OpenAICompatibleLLM` (OpenAI, Ollama, vLLM
61
+ and others) and `AnthropicLLM` with server-side refusal fallbacks.
62
+ - Tracing: spans for runs, steps, decisions, provider attempts, generations,
63
+ tools and rules; JSONL, memory, console and OpenTelemetry sinks; run
64
+ summaries with decisions by plane, escalations, tokens, cost and time by
65
+ plane; content capture switch; a self-contained HTML viewer.
66
+ - A price table with sources, overridable with `THINKLESS_PRICING`.
67
+ - The support demo: an agent, a mock store, 53 labeled tickets and a separate
68
+ 48-row calibration set.
69
+ - Benchmarks: the support benchmark across `llm`, `hybrid` and `models` modes,
70
+ and intent benchmarks on Banking77, CLINC150 and Emotion with a simulated
71
+ cascade.
72
+ - `thinkless` CLI: `doctor`, `demo`, `bench support`, `bench intents`,
73
+ `calibrate`, `trace ls`, `trace show` and `trace view`.
@@ -0,0 +1,202 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "[]"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright 2026 Praveen Kumar
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
202
+
@@ -0,0 +1,357 @@
1
+ Metadata-Version: 2.5
2
+ Name: thinkless
3
+ Version: 0.2.0
4
+ Summary: The decision plane for AI agents. Rules and small calibrated models make the routine decisions, the LLM is reserved for the steps that need thought, and every step is traced.
5
+ Project-URL: Homepage, https://github.com/inboxpraveen/ThinkLess
6
+ Project-URL: Documentation, https://github.com/inboxpraveen/ThinkLess/tree/main/docs
7
+ Project-URL: Repository, https://github.com/inboxpraveen/ThinkLess
8
+ Project-URL: Issues, https://github.com/inboxpraveen/ThinkLess/issues
9
+ Project-URL: Changelog, https://github.com/inboxpraveen/ThinkLess/blob/main/CHANGELOG.md
10
+ Author: Praveen Kumar
11
+ License-Expression: Apache-2.0
12
+ License-File: LICENSE
13
+ Keywords: agents,classification,decision-models,gliner,jev,laya,llm,observability,routing,system-one,tracing
14
+ Classifier: Development Status :: 3 - Alpha
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Intended Audience :: Science/Research
17
+ Classifier: License :: OSI Approved :: Apache Software License
18
+ Classifier: Operating System :: OS Independent
19
+ Classifier: Programming Language :: Python :: 3
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Programming Language :: Python :: 3.13
24
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
25
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
26
+ Classifier: Typing :: Typed
27
+ Requires-Python: >=3.10
28
+ Requires-Dist: httpx<1,>=0.27
29
+ Requires-Dist: pydantic<3,>=2.6
30
+ Requires-Dist: rich>=13.7
31
+ Requires-Dist: tomli>=2.0; python_version < '3.11'
32
+ Requires-Dist: typer>=0.12
33
+ Provides-Extra: all
34
+ Requires-Dist: accelerate>=1.0; extra == 'all'
35
+ Requires-Dist: anthropic>=1.0; extra == 'all'
36
+ Requires-Dist: datasets>=3.0; extra == 'all'
37
+ Requires-Dist: gliner2[local]<3,>=2.0; extra == 'all'
38
+ Requires-Dist: laya<0.4,>=0.3.20; extra == 'all'
39
+ Requires-Dist: openai>=2.0; extra == 'all'
40
+ Requires-Dist: opentelemetry-api>=1.25; extra == 'all'
41
+ Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.25; extra == 'all'
42
+ Requires-Dist: opentelemetry-sdk>=1.25; extra == 'all'
43
+ Requires-Dist: protobuf>=4.25; extra == 'all'
44
+ Requires-Dist: sentencepiece>=0.2; extra == 'all'
45
+ Requires-Dist: torch>=2.1; extra == 'all'
46
+ Requires-Dist: transformers<5,>=4.48; extra == 'all'
47
+ Provides-Extra: anthropic
48
+ Requires-Dist: anthropic>=1.0; extra == 'anthropic'
49
+ Provides-Extra: bench
50
+ Requires-Dist: datasets>=3.0; extra == 'bench'
51
+ Provides-Extra: dev
52
+ Requires-Dist: mkdocs-material>=9.5; extra == 'dev'
53
+ Requires-Dist: mkdocstrings[python]>=0.26; extra == 'dev'
54
+ Requires-Dist: mypy>=1.10; extra == 'dev'
55
+ Requires-Dist: pytest-cov>=5; extra == 'dev'
56
+ Requires-Dist: pytest>=8; extra == 'dev'
57
+ Requires-Dist: ruff>=0.6; extra == 'dev'
58
+ Requires-Dist: typesafe-sdk>=0.7; extra == 'dev'
59
+ Provides-Extra: gliner
60
+ Requires-Dist: gliner2[local]<3,>=2.0; extra == 'gliner'
61
+ Requires-Dist: protobuf>=4.25; extra == 'gliner'
62
+ Requires-Dist: sentencepiece>=0.2; extra == 'gliner'
63
+ Requires-Dist: transformers<5,>=4.48; extra == 'gliner'
64
+ Provides-Extra: laya
65
+ Requires-Dist: laya<0.4,>=0.3.20; extra == 'laya'
66
+ Requires-Dist: transformers<5,>=4.48; extra == 'laya'
67
+ Provides-Extra: local
68
+ Requires-Dist: accelerate>=1.0; extra == 'local'
69
+ Requires-Dist: gliner2[local]<3,>=2.0; extra == 'local'
70
+ Requires-Dist: laya<0.4,>=0.3.20; extra == 'local'
71
+ Requires-Dist: protobuf>=4.25; extra == 'local'
72
+ Requires-Dist: sentencepiece>=0.2; extra == 'local'
73
+ Requires-Dist: torch>=2.1; extra == 'local'
74
+ Requires-Dist: transformers<5,>=4.48; extra == 'local'
75
+ Provides-Extra: local-llm
76
+ Requires-Dist: accelerate>=1.0; extra == 'local-llm'
77
+ Requires-Dist: torch>=2.1; extra == 'local-llm'
78
+ Requires-Dist: transformers<5,>=4.48; extra == 'local-llm'
79
+ Provides-Extra: openai
80
+ Requires-Dist: openai>=2.0; extra == 'openai'
81
+ Provides-Extra: otel
82
+ Requires-Dist: opentelemetry-api>=1.25; extra == 'otel'
83
+ Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.25; extra == 'otel'
84
+ Requires-Dist: opentelemetry-sdk>=1.25; extra == 'otel'
85
+ Description-Content-Type: text/markdown
86
+
87
+ <h1 align="center">ThinkLess</h1>
88
+
89
+ <p align="center"><b>Stop using an LLM for every decision.</b></p>
90
+
91
+ <p align="center">
92
+ <a href="https://pypi.org/project/thinkless/"><img alt="PyPI" src="https://img.shields.io/pypi/v/thinkless.svg"></a>
93
+ <a href="https://github.com/inboxpraveen/ThinkLess/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/inboxpraveen/ThinkLess/actions/workflows/ci.yml/badge.svg"></a>
94
+ <a href="https://github.com/inboxpraveen/ThinkLess/blob/main/LICENSE"><img alt="License: Apache 2.0" src="https://img.shields.io/badge/license-Apache%202.0-blue.svg"></a>
95
+ <img alt="Python 3.10+" src="https://img.shields.io/badge/python-3.10%2B-blue.svg">
96
+ <a href="https://github.com/inboxpraveen/ThinkLess/blob/main/docs/benchmarks.md"><img alt="Benchmarks" src="https://img.shields.io/badge/benchmarks-reproducible-brightgreen.svg"></a>
97
+ </p>
98
+
99
+ ThinkLess is an open-source decision plane for AI agents. The routine
100
+ judgments an agent makes (what the user wants, whether they asked for a
101
+ person, which order they mean, whether a message is trying to manipulate the
102
+ system) are answered by rules and small calibrated models in milliseconds. The
103
+ LLM is kept for the steps that need it: writing, planning, and the decisions
104
+ the small models are not sure about. Every step is traced with the plane that
105
+ handled it, its confidence, latency, tokens and cost.
106
+
107
+ It is model-neutral. Rules, [GLiNER 2.5](https://github.com/fastino-ai/GLiNER2),
108
+ [Laya](https://github.com/NandhaKishorM/laya), TypeSafe's
109
+ [Jev](https://typesafe.ai) (and any server speaking its System One API, such as
110
+ Kev and OpenJev), any Hugging Face classifier, and any LLM (local through
111
+ Transformers, Ollama or vLLM, or hosted through OpenRouter, Anthropic or
112
+ OpenAI) plug into the same cascade. The whole stack also runs offline on a
113
+ laptop GPU, with no API key.
114
+
115
+ <p align="center">
116
+ <img src="https://raw.githubusercontent.com/inboxpraveen/ThinkLess/main/docs/assets/trace-viewer.png" alt="The ThinkLess trace viewer comparing three decision planes on the support benchmark, with one ticket's waterfall: six decisions answered by rules, GLiNER and Laya in 126 ms, then a single LLM call for the reply" width="100%">
117
+ </p>
118
+
119
+ ## Why
120
+
121
+ A typical agent sends every branch of its workflow to one generative model:
122
+ classify the request, extract the arguments, pick the tool, check the result,
123
+ check the reply, then write the reply. Most of those are bounded questions with
124
+ a handful of possible answers. A generative model answers them slowly, at full
125
+ token cost, and without telling you how sure it is.
126
+
127
+ ThinkLess treats each of them as a typed question. It asks the cheapest
128
+ provider first and escalates only when the answer's confidence is below a
129
+ threshold you set per question and per provider, from data. Your code reads a
130
+ typed decision and never needs to know which model produced it.
131
+
132
+ ```python
133
+ intent = engine.decide(ticket, INTENT) # rules, then GLiNER, then Laya, then the LLM
134
+ if intent.is_("refund_duplicate_charge"): # only true when the answer is confident
135
+ ...
136
+ ```
137
+
138
+ ## Results
139
+
140
+ Same agent, same 53 labeled support tickets, two decision planes: `llm` sends
141
+ every decision to the LLM, `hybrid` asks rules, GLiNER and Laya first. Hosted
142
+ models ran through OpenRouter, and the dollar figures are what OpenRouter
143
+ billed ([full results](https://github.com/inboxpraveen/ThinkLess/blob/main/docs/benchmarks.md)):
144
+
145
+ | Fallback LLM | Success, `llm` | Success, `hybrid` | Billed per 1k tickets, `llm` | Billed per 1k tickets, `hybrid` | Decision time |
146
+ |---|---:|---:|---:|---:|---:|
147
+ | Qwen 3.7 Flash | 92.5% | **98.1%** | $0.034 | **$0.019** | 1.27 s to **0.58 s** |
148
+ | Gemini 2.5 Flash Lite | 98.1% | **98.1%** | $0.104 | **$0.059** | 1.17 s to **0.76 s** |
149
+ | GPT-5.6 Luna | 98.1% | **100%** | $0.235 | **$0.141** | 2.50 s to **1.18 s** |
150
+ | Claude Haiku 4.5 | 98.1% | **98.1%** | $1.338 | **$0.788** | 2.21 s to **1.34 s** |
151
+ | Qwen3-1.7B, local | 86.8% | **94.3%** | free | free | 1.84 s to **0.43 s** |
152
+
153
+ - **Hybrid matched or beat the LLM-only design on every model, at 40 to 44
154
+ percent lower billed cost** and 35 to 55 percent less time spent on
155
+ decisions. 87 percent of hybrid decisions never reached the LLM.
156
+ - **Small models are the most reliable extractors.** Rules and GLiNER found
157
+ every order number on every run. The larger hosted LLMs did too; Qwen 3.7
158
+ Flash and the local 1.7B model missed ones written without the word
159
+ "order".
160
+ - **On Banking77 (77 intents, 500 examples) the cascade matched or beat its
161
+ LLM at a quarter of the cost.** GLiNER alone 71.8 percent; Qwen 3.7 Flash
162
+ alone 73.8 percent, cascade 74.8 percent; Claude Haiku 4.5 alone 76.2
163
+ percent, cascade 76.2 percent. Each time a quarter of the calls reached the
164
+ LLM.
165
+ - **Nothing was tuned on the test tickets.** Thresholds and every fix were
166
+ validated on a separate calibration set first. The baseline is the
167
+ strongest form of the LLM design: all six triage questions in one prompt.
168
+ - The support set is 53 synthetic tickets: evidence of the mechanism and its
169
+ failure modes, not a leaderboard. The [benchmarks page](https://github.com/inboxpraveen/ThinkLess/blob/main/docs/benchmarks.md)
170
+ lists the weak areas found and what was done about each.
171
+
172
+ ## Quick start
173
+
174
+ ```bash
175
+ pip install "thinkless[local]" # GLiNER, Laya and a local LLM
176
+ thinkless doctor # check the environment
177
+ ```
178
+
179
+ ```python
180
+ from thinkless import Choice, Engine, Extract, JSONLSink, Tracer, YesNo
181
+ from thinkless.llm import TransformersLLM
182
+ from thinkless.providers import GLiNER, Laya, LLMDecider, Rules
183
+
184
+ rules = Rules()
185
+ rules.match("wants_human", r"\b(real person|a human|an agent|operator)\b", True, field="message")
186
+ rules.extract("order", field="message", order_id=r"#\s?(\d{4,5})\b")
187
+
188
+ llm = TransformersLLM("Qwen/Qwen3-1.7B")
189
+ engine = Engine(
190
+ [rules, GLiNER(), Laya(), LLMDecider(llm)], # cheapest first
191
+ llm=llm,
192
+ threshold=0.8,
193
+ tracer=Tracer([JSONLSink(".thinkless/traces")]),
194
+ )
195
+
196
+ with engine.run("ticket") as run:
197
+ decisions = engine.decide_many(
198
+ {"message": "I was charged twice for order #4471. Refund it or I'm cancelling."},
199
+ [
200
+ Choice("What does the customer want?", name="intent",
201
+ options={"refund": "wants money back", "order_status": "asks where an order is",
202
+ "cancel": "wants to cancel", "other": "anything else"}),
203
+ YesNo("Is the customer asking for a person?", name="wants_human"),
204
+ Extract(name="order", fields={"order_id": "the order number"}),
205
+ ],
206
+ )
207
+
208
+ for name, d in decisions.items():
209
+ print(f"{name:12} {d.value!s:24} {d.status.value:9} via {d.provider}")
210
+ print(run.summary().decisions_by_plane) # which plane answered what
211
+ ```
212
+
213
+ Prefer a hosted model? Put `OPENROUTER_API_KEY=...` in `.env` and swap one
214
+ line; OpenRouter reaches Qwen, Gemini, GPT and Claude models with one key:
215
+
216
+ ```python
217
+ from thinkless.llm import OpenRouterLLM
218
+
219
+ llm = OpenRouterLLM("qwen/qwen3.7-flash", reasoning={"enabled": False})
220
+ ```
221
+
222
+ Then open the trace:
223
+
224
+ ```bash
225
+ thinkless trace view
226
+ ```
227
+
228
+ No GPU? `pip install thinkless` and run
229
+ [`examples/01_rules_only.py`](https://github.com/inboxpraveen/ThinkLess/blob/main/examples/01_rules_only.py): the API, the cascade
230
+ and the traces with no model downloads.
231
+
232
+ ## Try the demo agent
233
+
234
+ A complete customer support agent ships with the package: a mock store with
235
+ orders, payments and a help center, 53 labeled tickets, and every hard case we
236
+ could think of (order numbers written in ways a regex misses, a customer
237
+ asking about someone else's order, prompt injections, requests for a human,
238
+ Spanish and German tickets).
239
+
240
+ ```bash
241
+ thinkless demo --ticket T-016 # one ticket, printed as a trace tree
242
+ thinkless demo --ticket T-051 --mode hybrid --mode llm # compare decision planes
243
+ thinkless bench support # every ticket in every mode
244
+ thinkless bench support --llm openrouter:qwen/qwen3.7-flash --reasoning off
245
+ thinkless bench intents --dataset banking77
246
+ ```
247
+
248
+ ## How it works
249
+
250
+ **Typed questions.** `Choice`, `Score`, `YesNo` and `Extract` declare what you
251
+ want to know and which answers are allowed. They follow the System One wire
252
+ format, so the same question works on every provider.
253
+ [Questions](https://github.com/inboxpraveen/ThinkLess/blob/main/docs/concepts/questions.md)
254
+
255
+ **A confidence cascade.** Providers are tried cheapest first. Each gets one
256
+ call with every open question it supports; answers that clear their threshold
257
+ close, the rest move on. Unresolved questions come back `uncertain` with the
258
+ best answer seen, and `decision.is_()` never matches an uncertain answer.
259
+ [The cascade](https://github.com/inboxpraveen/ThinkLess/blob/main/docs/concepts/cascade.md)
260
+
261
+ **One meaning of confidence.** Providers disagree about what "confidence"
262
+ means (Laya and TypeSafe compute it differently), so a threshold of 0.8 would
263
+ mean different things per backend. ThinkLess derives a single normalized
264
+ confidence from each provider's probabilities.
265
+ [Confidence](https://github.com/inboxpraveen/ThinkLess/blob/main/docs/concepts/confidence.md)
266
+
267
+ **Thresholds from data.** `thinkless calibrate` sweeps thresholds on labeled
268
+ examples and recommends the lowest one that meets your accuracy target, per
269
+ question and per provider (`thresholds={"intent@gliner": 0.9}`).
270
+ [Calibration](https://github.com/inboxpraveen/ThinkLess/blob/main/docs/guides/calibration.md)
271
+
272
+ **Traces for everything.** Decisions, provider attempts, generations, tool
273
+ calls and rule checks are spans with W3C-compatible ids. Write JSONL audit
274
+ files, export to any OpenTelemetry backend with GenAI semantic conventions, or
275
+ open the self-contained HTML viewer. Content capture can be switched off
276
+ without losing structure, timings or confidences.
277
+ [Tracing](https://github.com/inboxpraveen/ThinkLess/blob/main/docs/concepts/tracing.md)
278
+
279
+ ## Providers and backends
280
+
281
+ | Decision providers | Answers | Runs |
282
+ |---|---|---|
283
+ | `Rules` | every kind | in-process, under 1 ms |
284
+ | `GLiNER` (GLiNER 2.5) | choice, extract | local, 16 to 38 ms on a laptop GPU, about 85 ms on CPU |
285
+ | `Laya` | choice, score, yes/no | local, about 30 ms per batch on a laptop GPU |
286
+ | `SystemOne` (Jev, Kev, OpenJev) | choice, score, yes/no | hosted or self-hosted HTTP |
287
+ | `HFClassifier` | choice, yes/no, for the questions it was trained on | any Hugging Face text classifier, local |
288
+ | `LLMDecider` | every kind | any LLM backend below |
289
+
290
+ | LLM backends | Covers |
291
+ |---|---|
292
+ | `TransformersLLM` | any Hugging Face chat model, in-process |
293
+ | `OpenRouterLLM` | hundreds of hosted models with one key, billed cost recorded per call |
294
+ | `OpenAICompatibleLLM` | OpenAI, Ollama, vLLM, LM Studio, llama.cpp, Groq |
295
+ | `AnthropicLLM` | Claude, with server-side refusal fallbacks |
296
+
297
+ Writing your own provider is one class. [Providers](https://github.com/inboxpraveen/ThinkLess/blob/main/docs/guides/providers.md),
298
+ [LLM backends](https://github.com/inboxpraveen/ThinkLess/blob/main/docs/guides/llm-backends.md)
299
+
300
+ ## Documentation
301
+
302
+ - [Getting started](https://github.com/inboxpraveen/ThinkLess/blob/main/docs/getting-started.md)
303
+ - Concepts: [the four planes](https://github.com/inboxpraveen/ThinkLess/blob/main/docs/concepts/planes.md),
304
+ [questions](https://github.com/inboxpraveen/ThinkLess/blob/main/docs/concepts/questions.md),
305
+ [confidence](https://github.com/inboxpraveen/ThinkLess/blob/main/docs/concepts/confidence.md),
306
+ [the cascade](https://github.com/inboxpraveen/ThinkLess/blob/main/docs/concepts/cascade.md),
307
+ [tracing](https://github.com/inboxpraveen/ThinkLess/blob/main/docs/concepts/tracing.md)
308
+ - Guides: [the support agent](https://github.com/inboxpraveen/ThinkLess/blob/main/docs/guides/support-agent.md),
309
+ [calibration](https://github.com/inboxpraveen/ThinkLess/blob/main/docs/guides/calibration.md),
310
+ [providers](https://github.com/inboxpraveen/ThinkLess/blob/main/docs/guides/providers.md),
311
+ [LLM backends](https://github.com/inboxpraveen/ThinkLess/blob/main/docs/guides/llm-backends.md),
312
+ [production](https://github.com/inboxpraveen/ThinkLess/blob/main/docs/guides/production.md),
313
+ [troubleshooting](https://github.com/inboxpraveen/ThinkLess/blob/main/docs/guides/troubleshooting.md)
314
+ - [Benchmarks](https://github.com/inboxpraveen/ThinkLess/blob/main/docs/benchmarks.md), [CLI reference](https://github.com/inboxpraveen/ThinkLess/blob/main/docs/reference/cli.md),
315
+ [Python API](https://github.com/inboxpraveen/ThinkLess/blob/main/docs/reference/api.md), [roadmap](https://github.com/inboxpraveen/ThinkLess/blob/main/docs/roadmap.md)
316
+
317
+ ## Status
318
+
319
+ ThinkLess is at 0.1: the core API (questions, the engine, decisions and
320
+ traces) is meant to stay stable, and providers and benchmarks will grow.
321
+ Next up are shadow mode, calibration from traces, calibrated LLM confidence
322
+ from log probabilities, and live Jev runs. See the [roadmap](https://github.com/inboxpraveen/ThinkLess/blob/main/docs/roadmap.md).
323
+
324
+ ## Contributing
325
+
326
+ Issues, providers, benchmark runs on other hardware and models, and new demo
327
+ scenarios are all welcome. Start with [CONTRIBUTING.md](https://github.com/inboxpraveen/ThinkLess/blob/main/CONTRIBUTING.md). The
328
+ bar for changes that affect accuracy, latency or cost is evidence: a before
329
+ and after from `thinkless bench` or `thinkless calibrate`.
330
+
331
+ ## Sponsoring
332
+
333
+ If ThinkLess saves your team time or money, consider
334
+ [sponsoring its development](https://github.com/sponsors/inboxpraveen).
335
+ Sponsorship funds frontier-model benchmark runs, more providers and more
336
+ demos.
337
+
338
+ ## Citing
339
+
340
+ See [CITATION.cff](https://github.com/inboxpraveen/ThinkLess/blob/main/CITATION.cff).
341
+
342
+ ## Acknowledgements
343
+
344
+ ThinkLess builds on the work of the teams behind
345
+ [Jev and the System One API](https://typesafe.ai) (TypeSafe AI),
346
+ [GLiNER2](https://github.com/fastino-ai/GLiNER2) (Fastino),
347
+ [Laya](https://github.com/NandhaKishorM/laya) (Convai Innovations),
348
+ [Qwen](https://github.com/QwenLM) and
349
+ [Banking77](https://github.com/PolyAI-LDN/task-specific-datasets) (PolyAI).
350
+
351
+ ThinkLess is not affiliated with the NeurIPS 2025 paper
352
+ [Thinkless: LLM Learns When to Think](https://github.com/VainF/Thinkless),
353
+ which studies adaptive reasoning inside a single model.
354
+
355
+ ## License
356
+
357
+ [Apache License 2.0](https://github.com/inboxpraveen/ThinkLess/blob/main/LICENSE).