arcus-cli 0.1.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.
- arcus_cli-0.1.0/.github/workflows/ci.yml +23 -0
- arcus_cli-0.1.0/.github/workflows/release.yml +50 -0
- arcus_cli-0.1.0/.gitignore +12 -0
- arcus_cli-0.1.0/.python-version +1 -0
- arcus_cli-0.1.0/LICENSE +21 -0
- arcus_cli-0.1.0/PKG-INFO +308 -0
- arcus_cli-0.1.0/README.md +278 -0
- arcus_cli-0.1.0/pyproject.toml +50 -0
- arcus_cli-0.1.0/src/arcus/__init__.py +0 -0
- arcus_cli-0.1.0/src/arcus/adapters/__init__.py +0 -0
- arcus_cli-0.1.0/src/arcus/adapters/arc_adapter.py +68 -0
- arcus_cli-0.1.0/src/arcus/cache/__init__.py +0 -0
- arcus_cli-0.1.0/src/arcus/cache/benchmark.py +159 -0
- arcus_cli-0.1.0/src/arcus/cache/semantic_cache.py +168 -0
- arcus_cli-0.1.0/src/arcus/cli.py +335 -0
- arcus_cli-0.1.0/src/arcus/config.py +46 -0
- arcus_cli-0.1.0/src/arcus/embeddings.py +34 -0
- arcus_cli-0.1.0/src/arcus/eval/__init__.py +0 -0
- arcus_cli-0.1.0/src/arcus/eval/offline.py +219 -0
- arcus_cli-0.1.0/src/arcus/eval/regret.py +96 -0
- arcus_cli-0.1.0/src/arcus/quality/__init__.py +0 -0
- arcus_cli-0.1.0/src/arcus/quality/gate.py +225 -0
- arcus_cli-0.1.0/src/arcus/routing/__init__.py +0 -0
- arcus_cli-0.1.0/src/arcus/routing/bandit.py +226 -0
- arcus_cli-0.1.0/src/arcus/routing/context.py +218 -0
- arcus_cli-0.1.0/src/arcus/routing/reward.py +91 -0
- arcus_cli-0.1.0/src/arcus/routing/warm_start.py +35 -0
- arcus_cli-0.1.0/src/arcus/storage/__init__.py +0 -0
- arcus_cli-0.1.0/src/arcus/storage/db.py +117 -0
- arcus_cli-0.1.0/src/arcus/storage/stats.py +51 -0
- arcus_cli-0.1.0/tests/adapters/test_arc_adapter.py +70 -0
- arcus_cli-0.1.0/tests/adapters/test_arc_adapter_live.py +42 -0
- arcus_cli-0.1.0/tests/cache/test_benchmark.py +21 -0
- arcus_cli-0.1.0/tests/cache/test_semantic_cache.py +142 -0
- arcus_cli-0.1.0/tests/eval/test_offline.py +172 -0
- arcus_cli-0.1.0/tests/eval/test_regret.py +42 -0
- arcus_cli-0.1.0/tests/quality/test_gate.py +230 -0
- arcus_cli-0.1.0/tests/routing/test_bandit.py +241 -0
- arcus_cli-0.1.0/tests/routing/test_context.py +81 -0
- arcus_cli-0.1.0/tests/routing/test_reward.py +85 -0
- arcus_cli-0.1.0/tests/routing/test_warm_start.py +117 -0
- arcus_cli-0.1.0/tests/storage/test_db.py +140 -0
- arcus_cli-0.1.0/tests/storage/test_stats.py +83 -0
- arcus_cli-0.1.0/tests/test_cli.py +472 -0
- arcus_cli-0.1.0/tests/test_config.py +55 -0
- arcus_cli-0.1.0/tests/test_embeddings.py +14 -0
- arcus_cli-0.1.0/uv.lock +1401 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main, master]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
|
|
14
|
+
- name: Install uv
|
|
15
|
+
uses: astral-sh/setup-uv@v3
|
|
16
|
+
with:
|
|
17
|
+
enable-cache: true
|
|
18
|
+
|
|
19
|
+
- name: Install dependencies
|
|
20
|
+
run: uv sync
|
|
21
|
+
|
|
22
|
+
- name: Run tests
|
|
23
|
+
run: uv run pytest tests/ -q
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
|
|
14
|
+
- name: Install uv
|
|
15
|
+
uses: astral-sh/setup-uv@v3
|
|
16
|
+
with:
|
|
17
|
+
enable-cache: true
|
|
18
|
+
|
|
19
|
+
- name: Install dependencies
|
|
20
|
+
run: uv sync
|
|
21
|
+
|
|
22
|
+
- name: Run tests
|
|
23
|
+
run: uv run pytest tests/ -q
|
|
24
|
+
|
|
25
|
+
publish:
|
|
26
|
+
needs: test
|
|
27
|
+
runs-on: ubuntu-latest
|
|
28
|
+
environment: pypi
|
|
29
|
+
permissions:
|
|
30
|
+
id-token: write # required for PyPI trusted publishing, no API token needed
|
|
31
|
+
steps:
|
|
32
|
+
- uses: actions/checkout@v4
|
|
33
|
+
|
|
34
|
+
- name: Install uv
|
|
35
|
+
uses: astral-sh/setup-uv@v3
|
|
36
|
+
|
|
37
|
+
- name: Check tag matches pyproject.toml version
|
|
38
|
+
run: |
|
|
39
|
+
TAG_VERSION="${GITHUB_REF_NAME#v}"
|
|
40
|
+
PYPROJECT_VERSION=$(grep -m1 '^version = ' pyproject.toml | sed -E 's/version = "(.*)"/\1/')
|
|
41
|
+
if [ "$TAG_VERSION" != "$PYPROJECT_VERSION" ]; then
|
|
42
|
+
echo "Tag $GITHUB_REF_NAME doesn't match pyproject.toml version $PYPROJECT_VERSION"
|
|
43
|
+
exit 1
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
- name: Build
|
|
47
|
+
run: uv build
|
|
48
|
+
|
|
49
|
+
- name: Publish to PyPI
|
|
50
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.13
|
arcus_cli-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Shakir Farhan
|
|
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.
|
arcus_cli-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: arcus-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Adaptive routing, a quality gate, and a correctness-aware cache on top of Virginia Tech ARC's LLM API
|
|
5
|
+
Project-URL: Homepage, https://github.com/ShakirFarhan/Arcus
|
|
6
|
+
Project-URL: Repository, https://github.com/ShakirFarhan/Arcus
|
|
7
|
+
Project-URL: Issues, https://github.com/ShakirFarhan/Arcus/issues
|
|
8
|
+
Author: Shakir Farhan
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: arc,bandit,cli,llm,routing,virginia-tech
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
|
+
Classifier: Topic :: Utilities
|
|
20
|
+
Requires-Python: >=3.13
|
|
21
|
+
Requires-Dist: openai
|
|
22
|
+
Requires-Dist: platformdirs>=4.11.3
|
|
23
|
+
Requires-Dist: pydantic-settings>=2.15.0
|
|
24
|
+
Requires-Dist: pydantic>=2.13.4
|
|
25
|
+
Requires-Dist: rich>=15.0.0
|
|
26
|
+
Requires-Dist: sentence-transformers>=6.0.0
|
|
27
|
+
Requires-Dist: sqlmodel>=0.0.39
|
|
28
|
+
Requires-Dist: typer>=0.27.1
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# Arcus
|
|
32
|
+
|
|
33
|
+
A CLI that sits on top of Virginia Tech ARC's LLM API and makes it
|
|
34
|
+
smarter: it picks which of ARC's four open-weight models to route a
|
|
35
|
+
request to, checks the response before handing it back to you, and
|
|
36
|
+
caches answers to questions it's already seen. Runs entirely on your own
|
|
37
|
+
machine with your own ARC key. Nothing goes through a shared server.
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
arcus "explain how binary search works"
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Why
|
|
44
|
+
|
|
45
|
+
ARC gives every VT user free access to four open-weight models
|
|
46
|
+
(gpt-oss-120b, GLM-5.3, Kimi-K3, DeepSeek-V4-Flash) through one
|
|
47
|
+
OpenAI-compatible endpoint. Picking a model by hand every time is
|
|
48
|
+
tedious, and a plain HTTP 200 doesn't tell you whether the response
|
|
49
|
+
inside it was actually any good, a truncated answer or a flat refusal
|
|
50
|
+
comes back looking the same as a correct one unless something reads the
|
|
51
|
+
content. Arcus adds three things on top of the raw API:
|
|
52
|
+
|
|
53
|
+
- **Adaptive routing** — a multi-armed bandit learns, per kind of
|
|
54
|
+
question, which model tends to give the best result for the least
|
|
55
|
+
latency and cost.
|
|
56
|
+
- **A quality gate** — validates every response (truncation, empty
|
|
57
|
+
output, repetition loops, refusal phrases, schema conformance) before
|
|
58
|
+
it reaches you, and silently retries with a different model if the
|
|
59
|
+
first one produced garbage.
|
|
60
|
+
- **A correctness-aware cache** — skips the API call entirely for
|
|
61
|
+
questions it's answered before, but only when it's actually confident
|
|
62
|
+
the new question means the same thing as the cached one.
|
|
63
|
+
|
|
64
|
+
## How it works
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
your question (CLI arg or piped stdin)
|
|
68
|
+
|
|
|
69
|
+
v
|
|
70
|
+
context classifier -- code / reasoning-math / writing / long-document / general
|
|
71
|
+
|
|
|
72
|
+
v
|
|
73
|
+
semantic cache check -- hit? return the cached answer, skip everything below
|
|
74
|
+
| miss
|
|
75
|
+
v
|
|
76
|
+
bandit router -- picks a model, one bandit instance per (task type, length) bucket
|
|
77
|
+
|
|
|
78
|
+
v
|
|
79
|
+
ARC API call (your own key, OpenAI-compatible endpoint)
|
|
80
|
+
|
|
|
81
|
+
v
|
|
82
|
+
quality gate -- validates the response, retries with a different model on failure
|
|
83
|
+
| pass
|
|
84
|
+
v
|
|
85
|
+
answer to you + write to cache + reward logged back to the bandit
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Everything after "your question" runs locally. The only network call
|
|
89
|
+
this tool ever makes is to ARC, with your own key.
|
|
90
|
+
|
|
91
|
+
### Context classification
|
|
92
|
+
|
|
93
|
+
Regex/keyword rules catch the obvious cases fast (a traceback is
|
|
94
|
+
obviously a code question, "write me a poem" is obviously a writing
|
|
95
|
+
request). Anything that doesn't match falls back to comparing the
|
|
96
|
+
prompt's embedding against a small set of labeled anchor examples per
|
|
97
|
+
category, so phrasing the regex rules never thought of still lands in
|
|
98
|
+
the right bucket instead of defaulting to "general." See
|
|
99
|
+
`src/arcus/routing/context.py`.
|
|
100
|
+
|
|
101
|
+
### Adaptive routing
|
|
102
|
+
|
|
103
|
+
Three interchangeable bandit algorithms, picked via config
|
|
104
|
+
(`bandit_algorithm` in `~/.config/arcus/config.toml`, default
|
|
105
|
+
`thompson`):
|
|
106
|
+
|
|
107
|
+
- **Epsilon-greedy** — simplest baseline, explores randomly a fixed
|
|
108
|
+
fraction of the time.
|
|
109
|
+
- **UCB1** — no tunable knob, explores under-tried arms automatically
|
|
110
|
+
via a confidence bound.
|
|
111
|
+
- **Thompson sampling** — Bayesian, samples from each arm's learned
|
|
112
|
+
`Beta` distribution, the default because it adapts fastest early on.
|
|
113
|
+
|
|
114
|
+
A random-selection baseline (`--random`) is also wired in as an A/B
|
|
115
|
+
comparison point, mostly useful for the offline evaluation report below.
|
|
116
|
+
The reward each arm is updated with is a weighted mix of quality (from
|
|
117
|
+
the gate below), normalized latency, and a simulated cost signal built
|
|
118
|
+
from real published hosting rates for these same open-weight models
|
|
119
|
+
(ARC itself is free, this exists to demonstrate cost-aware routing as a
|
|
120
|
+
practice). See `src/arcus/routing/bandit.py` and
|
|
121
|
+
`src/arcus/routing/reward.py`.
|
|
122
|
+
|
|
123
|
+
Since every `arcus` invocation is a fresh process, there's no daemon
|
|
124
|
+
holding the bandit's learned state in memory between runs. Instead,
|
|
125
|
+
`src/arcus/routing/warm_start.py` rebuilds it at the start of every call
|
|
126
|
+
by replaying the local request log, which works because a bandit's
|
|
127
|
+
`update()` is just an associative accumulation of pull counts and reward
|
|
128
|
+
sums.
|
|
129
|
+
|
|
130
|
+
### Quality gate
|
|
131
|
+
|
|
132
|
+
Five independent checks run over every response: truncation
|
|
133
|
+
(`finish_reason == "length"`), empty output, repetition (trigram
|
|
134
|
+
duplication ratio), refusal-phrase matching, and optional Pydantic
|
|
135
|
+
schema validation for structured-output requests. Any failure logs a
|
|
136
|
+
negative reward for that model in that context and retries with a
|
|
137
|
+
different one, up to once per available arm, before giving up and
|
|
138
|
+
returning the last attempt. See `src/arcus/quality/gate.py`.
|
|
139
|
+
|
|
140
|
+
### Semantic cache
|
|
141
|
+
|
|
142
|
+
Local `sentence-transformers` embeddings (`all-MiniLM-L6-v2`), cosine
|
|
143
|
+
similarity lookup against everything stored so far. Two things keep it
|
|
144
|
+
from just being a naive "similar enough, ship it" cache:
|
|
145
|
+
|
|
146
|
+
- **Volatility classification** — a query containing words like
|
|
147
|
+
"today," "current," or "latest" gets a TTL of zero (never actually
|
|
148
|
+
served stale), stable conceptual questions get a week.
|
|
149
|
+
- **Parameter-diff check** — before trusting a high-similarity match,
|
|
150
|
+
numbers and capitalized entities extracted from both queries are
|
|
151
|
+
compared. "when is project 2 due" and "when is project 3 due" read as
|
|
152
|
+
almost identical to a cosine similarity score, this check catches
|
|
153
|
+
that they're different questions.
|
|
154
|
+
|
|
155
|
+
Measured against a 62-pair labeled benchmark of true paraphrases and
|
|
156
|
+
near-duplicate-but-different prompts (`src/arcus/cache/benchmark.py`):
|
|
157
|
+
|
|
158
|
+
| approach | precision | recall |
|
|
159
|
+
| -------------------------- | --------- | ------ |
|
|
160
|
+
| naive cosine similarity | 0.306 | 0.688 |
|
|
161
|
+
| + parameter-diff check | 1.000 | 0.625 |
|
|
162
|
+
|
|
163
|
+
The param-diff check trades some recall (it rejects a few pairs it
|
|
164
|
+
shouldn't, "World War 1" vs "the First World War" gets flagged as a
|
|
165
|
+
conflicting parameter, a known and documented limitation) for a real
|
|
166
|
+
jump in precision, going from roughly 1-in-3 cache hits being wrong to
|
|
167
|
+
zero false hits in this benchmark.
|
|
168
|
+
|
|
169
|
+
### Offline policy evaluation and regret benchmarking
|
|
170
|
+
|
|
171
|
+
Every request logs the propensity (the probability the routing policy
|
|
172
|
+
assigned to whichever model it picked), which makes it possible to
|
|
173
|
+
estimate how a *different* policy would have performed without ever
|
|
174
|
+
running it live, using only the log that already exists. `arcus` logs
|
|
175
|
+
propensity from the very first request, this can't be added
|
|
176
|
+
retroactively to old data.
|
|
177
|
+
|
|
178
|
+
`src/arcus/eval/offline.py` implements inverse propensity scoring (IPS)
|
|
179
|
+
and doubly robust (DR) estimators plus percentile bootstrap confidence
|
|
180
|
+
intervals, and `evaluate_policies()` produces a comparison table:
|
|
181
|
+
the logged policy's actual average reward next to estimated values for
|
|
182
|
+
any alternative policies you want to compare it against (e.g. "what if
|
|
183
|
+
we'd always used the cheapest model").
|
|
184
|
+
|
|
185
|
+
Regret benchmarking is a different technique: it needs a *known*
|
|
186
|
+
ground-truth reward per arm to measure regret against, which real
|
|
187
|
+
traffic can't provide (a real request only ever explores one model per
|
|
188
|
+
round, so there's no way to know what the other three would have
|
|
189
|
+
scored). `src/arcus/eval/regret.py` simulates each algorithm against a
|
|
190
|
+
labeled synthetic reward environment instead, this is the standard way
|
|
191
|
+
to study a bandit algorithm's exploration behavior on its own, separate
|
|
192
|
+
from real-world model quality. A sample run (2000 rounds, seed 42):
|
|
193
|
+
|
|
194
|
+
| algorithm | final cumulative regret |
|
|
195
|
+
| -------------- | ------------------------ |
|
|
196
|
+
| epsilon-greedy | 7.9 |
|
|
197
|
+
| thompson | 25.4 |
|
|
198
|
+
| ucb1 | 58.6 |
|
|
199
|
+
| random | 76.9 |
|
|
200
|
+
|
|
201
|
+
All three real algorithms land well below the random baseline, which is
|
|
202
|
+
the actual point: they're spending far less time on worse-than-best
|
|
203
|
+
arms than picking blindly would.
|
|
204
|
+
|
|
205
|
+
## Install
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
pip install arcus-cli
|
|
209
|
+
# or, with uv
|
|
210
|
+
uv tool install arcus-cli
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Or run from source:
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
git clone https://github.com/ShakirFarhan/Arcus.git
|
|
217
|
+
cd Arcus
|
|
218
|
+
uv sync
|
|
219
|
+
uv run arcus "explain how binary search works"
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
First run walks you through a one-time setup: it asks for your ARC key
|
|
223
|
+
(get one from `llm.arc.vt.edu` under User profile > Settings > Account
|
|
224
|
+
> API keys), makes one live call to check it works, and saves it to
|
|
225
|
+
`~/.config/arcus/config.toml` with `chmod 600`. No separate setup
|
|
226
|
+
command to remember.
|
|
227
|
+
|
|
228
|
+
## Usage
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
# ask something directly
|
|
232
|
+
arcus "explain how binary search works"
|
|
233
|
+
|
|
234
|
+
# pipe an error straight in
|
|
235
|
+
python broken.py 2>&1 | arcus
|
|
236
|
+
|
|
237
|
+
# or combine piped context with an explicit instruction
|
|
238
|
+
python broken.py 2>&1 | arcus "why is this failing"
|
|
239
|
+
|
|
240
|
+
# force the random-routing baseline instead of the learned bandit policy
|
|
241
|
+
arcus --random "explain how binary search works"
|
|
242
|
+
|
|
243
|
+
# see how it's doing
|
|
244
|
+
arcus stats
|
|
245
|
+
|
|
246
|
+
# hold a multi-turn conversation instead of a single question
|
|
247
|
+
arcus chat
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
`arcus chat` opens a REPL that remembers everything said earlier in that
|
|
251
|
+
session (resending the growing transcript each turn, since ARC's API has
|
|
252
|
+
no session concept of its own) and routes each turn through the same
|
|
253
|
+
bandit/quality-gate/logging pipeline as a one-shot `arcus "..."` call.
|
|
254
|
+
Type `exit` or press ctrl-d to leave. The conversation only lives for
|
|
255
|
+
that one run, closing the terminal loses it.
|
|
256
|
+
|
|
257
|
+
`arcus stats` reads your local SQLite log and prints a `rich`-formatted
|
|
258
|
+
table: request count, average reward, average latency, and cost score
|
|
259
|
+
per model per mode, plus your cache hit rate and how many attempts the
|
|
260
|
+
quality gate has caught and retried. Entirely local, no network call.
|
|
261
|
+
|
|
262
|
+
## Status
|
|
263
|
+
|
|
264
|
+
Everything described above is implemented and working: the ARC adapter,
|
|
265
|
+
context classification, all three bandit algorithms with propensity
|
|
266
|
+
tracking, the reward function, the quality gate, the semantic cache and
|
|
267
|
+
its benchmark, the CLI (ask command, chat mode, first-run wizard, error
|
|
268
|
+
piping, stats), and the offline evaluation + regret benchmarking layer.
|
|
269
|
+
|
|
270
|
+
Verified live against a real ARC key: all four models respond correctly
|
|
271
|
+
(`tests/adapters/test_arc_adapter_live.py`), and a full end-to-end
|
|
272
|
+
`arcus "..."` run exercises the whole pipeline (context classification,
|
|
273
|
+
cache miss, bandit routing, a real ARC call, the quality gate, logging,
|
|
274
|
+
and caching the result) against real traffic. Test suite: 178 passing
|
|
275
|
+
with a key set (174 plus 4 live-only tests), 4 skipped without one.
|
|
276
|
+
|
|
277
|
+
Worth knowing: ARC's models are reasoning models under the hood, they
|
|
278
|
+
write to a hidden `reasoning` field before `content`, so a small
|
|
279
|
+
`max_tokens` budget can get entirely spent on reasoning before any real
|
|
280
|
+
answer comes out. The CLI itself never sets `max_tokens`, so normal
|
|
281
|
+
usage isn't affected, ARC's server-side default leaves plenty of room,
|
|
282
|
+
this only matters if you're calling the adapter directly with a tight
|
|
283
|
+
budget of your own.
|
|
284
|
+
|
|
285
|
+
What's still open:
|
|
286
|
+
|
|
287
|
+
- Not published to PyPI yet.
|
|
288
|
+
- Real logged usage is still thin (a handful of manual runs). Once
|
|
289
|
+
there's a real query history, `arcus/eval/offline.py`'s
|
|
290
|
+
`evaluate_policies()` is what turns it into the comparison table
|
|
291
|
+
described above.
|
|
292
|
+
|
|
293
|
+
## Security & privacy
|
|
294
|
+
|
|
295
|
+
- Each install uses its own user's ARC key. Keys are never shared,
|
|
296
|
+
bundled, or sent anywhere but ARC's own endpoint.
|
|
297
|
+
- No data leaves your machine by default. Request logs, cache entries,
|
|
298
|
+
and stats are all local SQLite, nothing is aggregated or reported
|
|
299
|
+
anywhere.
|
|
300
|
+
- This tool hasn't been through ARC's security review for regulated
|
|
301
|
+
data (FERPA records, health data, etc.) the way ARC's own web
|
|
302
|
+
interface has. Don't route sensitive regulated data through it.
|
|
303
|
+
- MIT licensed, source is fully readable, that's the actual trust
|
|
304
|
+
mechanism here rather than a policy document.
|
|
305
|
+
|
|
306
|
+
## License
|
|
307
|
+
|
|
308
|
+
MIT, see `LICENSE`.
|