arcus-cli 0.1.0__tar.gz → 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.
- arcus_cli-0.2.0/.github/ISSUE_TEMPLATE/bug_report.yml +47 -0
- arcus_cli-0.2.0/.github/ISSUE_TEMPLATE/feature_request.yml +25 -0
- {arcus_cli-0.1.0 → arcus_cli-0.2.0}/.github/workflows/release.yml +13 -0
- arcus_cli-0.2.0/CONTRIBUTING.md +48 -0
- {arcus_cli-0.1.0 → arcus_cli-0.2.0}/PKG-INFO +213 -17
- arcus_cli-0.2.0/README.md +474 -0
- {arcus_cli-0.1.0 → arcus_cli-0.2.0}/pyproject.toml +1 -1
- {arcus_cli-0.1.0 → arcus_cli-0.2.0}/src/arcus/adapters/arc_adapter.py +45 -2
- arcus_cli-0.2.0/src/arcus/cli.py +1078 -0
- {arcus_cli-0.1.0 → arcus_cli-0.2.0}/src/arcus/config.py +19 -4
- {arcus_cli-0.1.0 → arcus_cli-0.2.0}/src/arcus/quality/gate.py +63 -9
- {arcus_cli-0.1.0 → arcus_cli-0.2.0}/src/arcus/routing/bandit.py +22 -2
- arcus_cli-0.2.0/src/arcus/routing/model_catalog.py +96 -0
- {arcus_cli-0.1.0 → arcus_cli-0.2.0}/src/arcus/routing/reward.py +9 -13
- {arcus_cli-0.1.0 → arcus_cli-0.2.0}/src/arcus/routing/warm_start.py +9 -6
- arcus_cli-0.2.0/tests/adapters/test_arc_adapter.py +167 -0
- arcus_cli-0.2.0/tests/quality/test_gate.py +402 -0
- {arcus_cli-0.1.0 → arcus_cli-0.2.0}/tests/routing/test_bandit.py +13 -2
- arcus_cli-0.2.0/tests/routing/test_model_catalog.py +116 -0
- {arcus_cli-0.1.0 → arcus_cli-0.2.0}/tests/routing/test_reward.py +10 -15
- {arcus_cli-0.1.0 → arcus_cli-0.2.0}/tests/routing/test_warm_start.py +31 -1
- arcus_cli-0.2.0/tests/test_cli.py +1846 -0
- {arcus_cli-0.1.0 → arcus_cli-0.2.0}/tests/test_config.py +30 -0
- {arcus_cli-0.1.0 → arcus_cli-0.2.0}/uv.lock +1 -1
- arcus_cli-0.1.0/README.md +0 -278
- arcus_cli-0.1.0/src/arcus/cli.py +0 -335
- arcus_cli-0.1.0/tests/adapters/test_arc_adapter.py +0 -70
- arcus_cli-0.1.0/tests/quality/test_gate.py +0 -230
- arcus_cli-0.1.0/tests/test_cli.py +0 -472
- {arcus_cli-0.1.0 → arcus_cli-0.2.0}/.github/workflows/ci.yml +0 -0
- {arcus_cli-0.1.0 → arcus_cli-0.2.0}/.gitignore +0 -0
- {arcus_cli-0.1.0 → arcus_cli-0.2.0}/.python-version +0 -0
- {arcus_cli-0.1.0 → arcus_cli-0.2.0}/LICENSE +0 -0
- {arcus_cli-0.1.0 → arcus_cli-0.2.0}/src/arcus/__init__.py +0 -0
- {arcus_cli-0.1.0 → arcus_cli-0.2.0}/src/arcus/adapters/__init__.py +0 -0
- {arcus_cli-0.1.0 → arcus_cli-0.2.0}/src/arcus/cache/__init__.py +0 -0
- {arcus_cli-0.1.0 → arcus_cli-0.2.0}/src/arcus/cache/benchmark.py +0 -0
- {arcus_cli-0.1.0 → arcus_cli-0.2.0}/src/arcus/cache/semantic_cache.py +0 -0
- {arcus_cli-0.1.0 → arcus_cli-0.2.0}/src/arcus/embeddings.py +0 -0
- {arcus_cli-0.1.0 → arcus_cli-0.2.0}/src/arcus/eval/__init__.py +0 -0
- {arcus_cli-0.1.0 → arcus_cli-0.2.0}/src/arcus/eval/offline.py +0 -0
- {arcus_cli-0.1.0 → arcus_cli-0.2.0}/src/arcus/eval/regret.py +0 -0
- {arcus_cli-0.1.0 → arcus_cli-0.2.0}/src/arcus/quality/__init__.py +0 -0
- {arcus_cli-0.1.0 → arcus_cli-0.2.0}/src/arcus/routing/__init__.py +0 -0
- {arcus_cli-0.1.0 → arcus_cli-0.2.0}/src/arcus/routing/context.py +0 -0
- {arcus_cli-0.1.0 → arcus_cli-0.2.0}/src/arcus/storage/__init__.py +0 -0
- {arcus_cli-0.1.0 → arcus_cli-0.2.0}/src/arcus/storage/db.py +0 -0
- {arcus_cli-0.1.0 → arcus_cli-0.2.0}/src/arcus/storage/stats.py +0 -0
- {arcus_cli-0.1.0 → arcus_cli-0.2.0}/tests/adapters/test_arc_adapter_live.py +0 -0
- {arcus_cli-0.1.0 → arcus_cli-0.2.0}/tests/cache/test_benchmark.py +0 -0
- {arcus_cli-0.1.0 → arcus_cli-0.2.0}/tests/cache/test_semantic_cache.py +0 -0
- {arcus_cli-0.1.0 → arcus_cli-0.2.0}/tests/eval/test_offline.py +0 -0
- {arcus_cli-0.1.0 → arcus_cli-0.2.0}/tests/eval/test_regret.py +0 -0
- {arcus_cli-0.1.0 → arcus_cli-0.2.0}/tests/routing/test_context.py +0 -0
- {arcus_cli-0.1.0 → arcus_cli-0.2.0}/tests/storage/test_db.py +0 -0
- {arcus_cli-0.1.0 → arcus_cli-0.2.0}/tests/storage/test_stats.py +0 -0
- {arcus_cli-0.1.0 → arcus_cli-0.2.0}/tests/test_embeddings.py +0 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
name: Bug report
|
|
2
|
+
description: Something in arcus isn't working as expected
|
|
3
|
+
labels: ["bug"]
|
|
4
|
+
body:
|
|
5
|
+
- type: textarea
|
|
6
|
+
id: what-happened
|
|
7
|
+
attributes:
|
|
8
|
+
label: What happened
|
|
9
|
+
description: What did you run, and what went wrong?
|
|
10
|
+
placeholder: |
|
|
11
|
+
Ran `arcus "..."` and got a traceback / wrong answer / hang, etc.
|
|
12
|
+
validations:
|
|
13
|
+
required: true
|
|
14
|
+
|
|
15
|
+
- type: textarea
|
|
16
|
+
id: expected
|
|
17
|
+
attributes:
|
|
18
|
+
label: What you expected instead
|
|
19
|
+
validations:
|
|
20
|
+
required: true
|
|
21
|
+
|
|
22
|
+
- type: input
|
|
23
|
+
id: version
|
|
24
|
+
attributes:
|
|
25
|
+
label: arcus-cli version
|
|
26
|
+
description: Output of `pip show arcus-cli` or `uv tool list`
|
|
27
|
+
validations:
|
|
28
|
+
required: true
|
|
29
|
+
|
|
30
|
+
- type: dropdown
|
|
31
|
+
id: mode
|
|
32
|
+
attributes:
|
|
33
|
+
label: Which mode were you using?
|
|
34
|
+
options:
|
|
35
|
+
- "arcus \"...\" (one-shot)"
|
|
36
|
+
- "arcus chat"
|
|
37
|
+
- "arcus stats"
|
|
38
|
+
- "arcus --random"
|
|
39
|
+
- Not sure / other
|
|
40
|
+
validations:
|
|
41
|
+
required: true
|
|
42
|
+
|
|
43
|
+
- type: textarea
|
|
44
|
+
id: logs
|
|
45
|
+
attributes:
|
|
46
|
+
label: Full error output, if any
|
|
47
|
+
render: shell
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
name: Feature request
|
|
2
|
+
description: Suggest something arcus should do
|
|
3
|
+
labels: ["enhancement"]
|
|
4
|
+
body:
|
|
5
|
+
- type: textarea
|
|
6
|
+
id: problem
|
|
7
|
+
attributes:
|
|
8
|
+
label: What problem does this solve?
|
|
9
|
+
description: What are you trying to do that arcus doesn't support today?
|
|
10
|
+
validations:
|
|
11
|
+
required: true
|
|
12
|
+
|
|
13
|
+
- type: textarea
|
|
14
|
+
id: proposal
|
|
15
|
+
attributes:
|
|
16
|
+
label: What you'd like to see
|
|
17
|
+
description: A rough idea of the behavior or interface is fine, doesn't need to be a full spec.
|
|
18
|
+
validations:
|
|
19
|
+
required: true
|
|
20
|
+
|
|
21
|
+
- type: textarea
|
|
22
|
+
id: alternatives
|
|
23
|
+
attributes:
|
|
24
|
+
label: Alternatives you've considered
|
|
25
|
+
description: Any workarounds you're using today, or other approaches you thought about.
|
|
@@ -48,3 +48,16 @@ jobs:
|
|
|
48
48
|
|
|
49
49
|
- name: Publish to PyPI
|
|
50
50
|
uses: pypa/gh-action-pypi-publish@release/v1
|
|
51
|
+
|
|
52
|
+
github_release:
|
|
53
|
+
needs: publish
|
|
54
|
+
runs-on: ubuntu-latest
|
|
55
|
+
permissions:
|
|
56
|
+
contents: write # required to create the GitHub Release
|
|
57
|
+
steps:
|
|
58
|
+
- uses: actions/checkout@v4
|
|
59
|
+
|
|
60
|
+
- name: Create GitHub Release
|
|
61
|
+
uses: softprops/action-gh-release@v2
|
|
62
|
+
with:
|
|
63
|
+
generate_release_notes: true
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Contributing to Arcus
|
|
2
|
+
|
|
3
|
+
## Setup
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
git clone https://github.com/ShakirFarhan/Arcus.git
|
|
7
|
+
cd Arcus
|
|
8
|
+
uv sync
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
You'll need an ARC API key to run the tool live (`llm.arc.vt.edu` > User
|
|
12
|
+
profile > Settings > Account > API keys). Most of the test suite runs
|
|
13
|
+
without one; a handful of live tests in `tests/adapters/test_arc_adapter_live.py`
|
|
14
|
+
only run if `ARC_API_KEY` is set in your shell:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
uv run pytest tests/ -q
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Before opening a PR
|
|
21
|
+
|
|
22
|
+
- Add a test for any new business logic (routing, reward calculation,
|
|
23
|
+
cache scoring, quality checks). Bug fixes should include a test that
|
|
24
|
+
fails without the fix.
|
|
25
|
+
- Keep PRs focused, one change per PR rather than bundling unrelated
|
|
26
|
+
fixes together.
|
|
27
|
+
- Run the full test suite locally before pushing; CI runs it again on
|
|
28
|
+
every PR but catching it early saves a round trip.
|
|
29
|
+
- Algorithmic functions (bandit logic, reward math, cache scoring)
|
|
30
|
+
should have a docstring explaining *why* a design choice was made,
|
|
31
|
+
not just what the code does.
|
|
32
|
+
|
|
33
|
+
## Code style
|
|
34
|
+
|
|
35
|
+
- No new dependencies without discussing them in the PR description
|
|
36
|
+
first, this project deliberately keeps its dependency footprint
|
|
37
|
+
small.
|
|
38
|
+
- Comments should explain non-obvious reasoning (a workaround, a
|
|
39
|
+
constraint, a subtle invariant), not restate what the code already
|
|
40
|
+
says.
|
|
41
|
+
- Prefer editing existing files and reusing existing patterns over
|
|
42
|
+
introducing new abstractions.
|
|
43
|
+
|
|
44
|
+
## Reporting bugs
|
|
45
|
+
|
|
46
|
+
Open an issue with what you ran, what you expected, and what actually
|
|
47
|
+
happened. If it's reproducible with `arcus --random`, mention that too,
|
|
48
|
+
it helps narrow down whether it's routing-specific.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: arcus-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Adaptive routing, a quality gate, and a correctness-aware cache on top of Virginia Tech ARC's LLM API
|
|
5
5
|
Project-URL: Homepage, https://github.com/ShakirFarhan/Arcus
|
|
6
6
|
Project-URL: Repository, https://github.com/ShakirFarhan/Arcus
|
|
@@ -30,6 +30,11 @@ Description-Content-Type: text/markdown
|
|
|
30
30
|
|
|
31
31
|
# Arcus
|
|
32
32
|
|
|
33
|
+
[](https://github.com/ShakirFarhan/Arcus/actions/workflows/ci.yml)
|
|
34
|
+
[](https://pypi.org/project/arcus-cli/)
|
|
35
|
+
[](https://pypi.org/project/arcus-cli/)
|
|
36
|
+
[](LICENSE)
|
|
37
|
+
|
|
33
38
|
A CLI that sits on top of Virginia Tech ARC's LLM API and makes it
|
|
34
39
|
smarter: it picks which of ARC's four open-weight models to route a
|
|
35
40
|
request to, checks the response before handing it back to you, and
|
|
@@ -40,11 +45,17 @@ machine with your own ARC key. Nothing goes through a shared server.
|
|
|
40
45
|
arcus "explain how binary search works"
|
|
41
46
|
```
|
|
42
47
|
|
|
48
|
+
**Contents:** [Why](#why) · [How it works](#how-it-works) · [Install](#install)
|
|
49
|
+
· [Usage](#usage) · [Status](#status) · [Security & privacy](#security--privacy)
|
|
50
|
+
|
|
43
51
|
## Why
|
|
44
52
|
|
|
45
53
|
ARC gives every VT user free access to four open-weight models
|
|
46
54
|
(gpt-oss-120b, GLM-5.3, Kimi-K3, DeepSeek-V4-Flash) through one
|
|
47
|
-
OpenAI-compatible endpoint
|
|
55
|
+
OpenAI-compatible endpoint ([ARC's own docs](https://www.docs.arc.vt.edu/ai/011_llm_api_arc_vt_edu.html)
|
|
56
|
+
cover the service itself, including its rate limits and data-handling
|
|
57
|
+
approval, arcus is a client built on top of it, not affiliated with
|
|
58
|
+
ARC). Picking a model by hand every time is
|
|
48
59
|
tedious, and a plain HTTP 200 doesn't tell you whether the response
|
|
49
60
|
inside it was actually any good, a truncated answer or a flat refusal
|
|
50
61
|
comes back looking the same as a correct one unless something reads the
|
|
@@ -127,6 +138,26 @@ by replaying the local request log, which works because a bandit's
|
|
|
127
138
|
`update()` is just an associative accumulation of pull counts and reward
|
|
128
139
|
sums.
|
|
129
140
|
|
|
141
|
+
The four model ids arcus routes to live in `ArcModel`, but ARC runs its
|
|
142
|
+
own model catalog independently and can rename or retire an entry at
|
|
143
|
+
any time. `src/arcus/routing/model_catalog.py` checks the configured
|
|
144
|
+
list against what ARC is actually serving (cached for a few hours so
|
|
145
|
+
this doesn't cost a network round trip on every call) and quietly drops
|
|
146
|
+
anything that's no longer live, rather than routing to a model
|
|
147
|
+
guaranteed to fail. Local history logged under a since-renamed model id
|
|
148
|
+
is skipped the same way when the bandit's state gets rebuilt.
|
|
149
|
+
|
|
150
|
+
Optionally, code, math, and long-document questions can also route
|
|
151
|
+
across ARC's `-thinking-*` reasoning-effort model variants, not just
|
|
152
|
+
the base four (`arcus config set enable_reasoning_variants true`,
|
|
153
|
+
default off). Everyday questions stay on the fast base four either way.
|
|
154
|
+
**This is built and unit tested but hasn't been run against a real ARC
|
|
155
|
+
key from this environment**, ARC's docs list these as separate catalog
|
|
156
|
+
model ids rather than a parameter on the base id, the same pattern
|
|
157
|
+
already confirmed for web search's legacy-tool-calling variants below,
|
|
158
|
+
but that specific assumption is unverified. Ask a code or math question
|
|
159
|
+
after turning it on and confirm it actually answers before trusting it.
|
|
160
|
+
|
|
130
161
|
### Quality gate
|
|
131
162
|
|
|
132
163
|
Five independent checks run over every response: truncation
|
|
@@ -137,6 +168,17 @@ negative reward for that model in that context and retries with a
|
|
|
137
168
|
different one, up to once per available arm, before giving up and
|
|
138
169
|
returning the last attempt. See `src/arcus/quality/gate.py`.
|
|
139
170
|
|
|
171
|
+
ARC caps concurrent requests per account rather than per model, so a
|
|
172
|
+
429 doesn't mean the model that was just called is bad, switching to a
|
|
173
|
+
different arm wouldn't help either. A rate limit gets a few short
|
|
174
|
+
retries against the same model before it's treated as a real failure,
|
|
175
|
+
so one busy moment doesn't unfairly tank that model's learned reward.
|
|
176
|
+
|
|
177
|
+
Similarly, ARC's access restriction (see Install below) applies to the
|
|
178
|
+
whole account, not one model, so hitting it stops the request
|
|
179
|
+
immediately with a clear message instead of cycling through every arm
|
|
180
|
+
against the same wall, and doesn't count against any model's reward.
|
|
181
|
+
|
|
140
182
|
### Semantic cache
|
|
141
183
|
|
|
142
184
|
Local `sentence-transformers` embeddings (`all-MiniLM-L6-v2`), cosine
|
|
@@ -202,6 +244,29 @@ All three real algorithms land well below the random baseline, which is
|
|
|
202
244
|
the actual point: they're spending far less time on worse-than-best
|
|
203
245
|
arms than picking blindly would.
|
|
204
246
|
|
|
247
|
+
### Document Q&A and web search
|
|
248
|
+
|
|
249
|
+
Both build directly on capabilities ARC's own API already provides,
|
|
250
|
+
rather than reimplementing them:
|
|
251
|
+
|
|
252
|
+
- **`arcus --doc <path> "question"`** uploads the file to ARC's RAG
|
|
253
|
+
endpoint, attaches it to the request, and deletes it from your ARC
|
|
254
|
+
account again once you have an answer. Works across all four core
|
|
255
|
+
models, confirmed live against the real API.
|
|
256
|
+
- **`arcus --web "question"`** routes to ARC's `server:websearch` tool
|
|
257
|
+
through three of its "legacy-tool-calling" model variants
|
|
258
|
+
(`gpt-oss-120b`, `Kimi-K3`, and the older `glm-52` variant) confirmed
|
|
259
|
+
to actually perform a real search and cite sources. A fourth,
|
|
260
|
+
DeepSeek's legacy variant, accepts the same request without erroring
|
|
261
|
+
but doesn't reliably act on it, live testing caught it answering a
|
|
262
|
+
time-sensitive question wrong with no citation, so it's left out.
|
|
263
|
+
|
|
264
|
+
Both skip the semantic cache: a cached answer keyed on question text
|
|
265
|
+
alone would risk answering about the wrong document, or serving a
|
|
266
|
+
web-search answer that's since gone stale. See `src/arcus/cli.py`
|
|
267
|
+
(`run_doc_ask`, `run_web_ask`) and `ArcAdapter.upload_file`/
|
|
268
|
+
`delete_file` in `src/arcus/adapters/arc_adapter.py`.
|
|
269
|
+
|
|
205
270
|
## Install
|
|
206
271
|
|
|
207
272
|
```bash
|
|
@@ -225,6 +290,23 @@ First run walks you through a one-time setup: it asks for your ARC key
|
|
|
225
290
|
`~/.config/arcus/config.toml` with `chmod 600`. No separate setup
|
|
226
291
|
command to remember.
|
|
227
292
|
|
|
293
|
+
ARC restricts the API to VT's campus network, so this (and every
|
|
294
|
+
`arcus` call after it) needs either an on-campus connection or VT's
|
|
295
|
+
VPN. Arcus surfaces this as a clear message rather than the generic
|
|
296
|
+
"no usable response" error when it happens.
|
|
297
|
+
|
|
298
|
+
For tab completion on the `chat`/`stats`/`eval`/`models`/`config`/
|
|
299
|
+
`--random`/`--model`/`--image`/`--doc`/`--web` words, add one of these
|
|
300
|
+
to your shell config:
|
|
301
|
+
|
|
302
|
+
```bash
|
|
303
|
+
# zsh, in ~/.zshrc
|
|
304
|
+
eval "$(arcus --completion zsh)"
|
|
305
|
+
|
|
306
|
+
# bash, in ~/.bashrc
|
|
307
|
+
eval "$(arcus --completion bash)"
|
|
308
|
+
```
|
|
309
|
+
|
|
228
310
|
## Usage
|
|
229
311
|
|
|
230
312
|
```bash
|
|
@@ -240,39 +322,145 @@ python broken.py 2>&1 | arcus "why is this failing"
|
|
|
240
322
|
# force the random-routing baseline instead of the learned bandit policy
|
|
241
323
|
arcus --random "explain how binary search works"
|
|
242
324
|
|
|
325
|
+
# skip the bandit entirely and pin a specific model for this one call
|
|
326
|
+
arcus --model GLM-5.3 "explain how binary search works"
|
|
327
|
+
|
|
243
328
|
# see how it's doing
|
|
244
329
|
arcus stats
|
|
245
330
|
|
|
331
|
+
# compare the routing policy actually run against offline alternatives
|
|
332
|
+
arcus eval
|
|
333
|
+
|
|
334
|
+
# see every model ARC is currently serving, and which ones arcus routes to
|
|
335
|
+
arcus models
|
|
336
|
+
|
|
246
337
|
# hold a multi-turn conversation instead of a single question
|
|
247
338
|
arcus chat
|
|
339
|
+
|
|
340
|
+
# inside chat, --doc/--web/--image/--model all work inline, one
|
|
341
|
+
# attachment per turn: "you: --doc paper.pdf summarize this"
|
|
342
|
+
|
|
343
|
+
# save the conversation to a file when you leave
|
|
344
|
+
arcus chat --save transcript.md
|
|
345
|
+
|
|
346
|
+
# ask about an image (routes to Kimi-K3, the one ARC model documented
|
|
347
|
+
# as vision-capable)
|
|
348
|
+
arcus --image screenshot.png "what's wrong with this code?"
|
|
349
|
+
|
|
350
|
+
# ask a question about a document, ARC handles the retrieval
|
|
351
|
+
arcus --doc syllabus.pdf "when is the midterm?"
|
|
352
|
+
|
|
353
|
+
# ask something that needs current information
|
|
354
|
+
arcus --web "what's the latest release of Python?"
|
|
355
|
+
|
|
356
|
+
# view or change local settings
|
|
357
|
+
arcus config
|
|
358
|
+
arcus config set bandit_algorithm ucb1
|
|
359
|
+
|
|
360
|
+
# check which version is installed
|
|
361
|
+
arcus --version
|
|
248
362
|
```
|
|
249
363
|
|
|
364
|
+
Quick reference, details for each are below:
|
|
365
|
+
|
|
366
|
+
| Command | What it does |
|
|
367
|
+
| --- | --- |
|
|
368
|
+
| `arcus "<question>"` | Ask something, routed through the bandit + quality gate. |
|
|
369
|
+
| `arcus --random "<question>"` | Same, but routes randomly instead of using the learned policy. |
|
|
370
|
+
| `arcus --model NAME "<question>"` | Skip routing, pin one specific model. |
|
|
371
|
+
| `arcus --image PATH "<question>"` | Ask about an image (vision-capable model only). |
|
|
372
|
+
| `arcus --doc PATH "<question>"` | Ask about an uploaded document (RAG). |
|
|
373
|
+
| `arcus --web "<question>"` | Ask something needing current information (web search). |
|
|
374
|
+
| `arcus chat [--save PATH]` | Multi-turn conversation; `--doc`/`--web`/`--image`/`--model` all work inline per turn. |
|
|
375
|
+
| `arcus stats` | Local routing performance so far. |
|
|
376
|
+
| `arcus eval` | Offline comparison of the routing policy against alternatives. |
|
|
377
|
+
| `arcus models` | ARC's live model catalog vs. what arcus routes to. |
|
|
378
|
+
| `arcus config [set ...]` | View or change local settings. |
|
|
379
|
+
| `arcus --version` | Installed version. |
|
|
380
|
+
|
|
250
381
|
`arcus chat` opens a REPL that remembers everything said earlier in that
|
|
251
382
|
session (resending the growing transcript each turn, since ARC's API has
|
|
252
383
|
no session concept of its own) and routes each turn through the same
|
|
253
384
|
bandit/quality-gate/logging pipeline as a one-shot `arcus "..."` call.
|
|
254
385
|
Type `exit` or press ctrl-d to leave. The conversation only lives for
|
|
255
|
-
that one run, closing the terminal loses it
|
|
386
|
+
that one run, closing the terminal loses it, unless you pass `--save
|
|
387
|
+
<path>`, which writes the full transcript (not just whatever's still in
|
|
388
|
+
the trimmed context window) to a markdown file when you exit.
|
|
389
|
+
|
|
390
|
+
`--doc PATH`, `--web`, `--image PATH`, and `--model NAME` all work
|
|
391
|
+
inline inside `arcus chat` too, typed as part of a turn (`you: --doc
|
|
392
|
+
paper.pdf summarize this`), one attachment per turn, the same rules as
|
|
393
|
+
below apply. The attachment only applies to that one turn, a later turn
|
|
394
|
+
that wants to keep asking about the same document attaches it again.
|
|
395
|
+
|
|
396
|
+
`arcus --image <path> "question"` attaches an image to a one-shot
|
|
397
|
+
question. It always goes to Kimi-K3 rather than through the usual
|
|
398
|
+
bandit comparison, confirmed directly against the API to be the only
|
|
399
|
+
one of the four models that can actually see an image, GLM-5.3 and
|
|
400
|
+
DeepSeek-V4-Flash both reject image content outright and gpt-oss-120b
|
|
401
|
+
accepts the request but reports it can't see anything. Skips the
|
|
402
|
+
semantic cache entirely too, matching on the question text alone would
|
|
403
|
+
risk serving back an answer about a completely different image.
|
|
404
|
+
|
|
405
|
+
`arcus --doc <path> "question"` and `arcus --web "question"` work the
|
|
406
|
+
same way as `--image`, cache skipped, see "Document Q&A and web search"
|
|
407
|
+
above for what each actually does. Only one of `--image`, `--doc`, or
|
|
408
|
+
`--web` can be used at a time.
|
|
409
|
+
|
|
410
|
+
`arcus --model NAME "question"` skips the bandit entirely and always
|
|
411
|
+
uses that model, checked against ARC's live catalog first. Since
|
|
412
|
+
there's only one arm, the quality gate's checks (empty, truncated,
|
|
413
|
+
repetitive, refusal) still run and still get reported, there's just no
|
|
414
|
+
other model left to fall back to if it fails, that's the point of an
|
|
415
|
+
explicit override. Combine with `--web` or `--image` and the name has
|
|
416
|
+
to be one of the models valid for that mode.
|
|
417
|
+
|
|
418
|
+
`arcus config` shows your current settings (the API key masked) and the
|
|
419
|
+
path to the config file. `arcus config set bandit_algorithm <algo>`
|
|
420
|
+
changes which bandit algorithm arcus uses without hand-editing the TOML
|
|
421
|
+
file. `arcus config set enable_reasoning_variants <true|false>` turns
|
|
422
|
+
the reasoning-effort routing described above on or off. Re-keying isn't
|
|
423
|
+
supported here on purpose, delete the config file and run `arcus` again
|
|
424
|
+
to go through setup fresh.
|
|
256
425
|
|
|
257
426
|
`arcus stats` reads your local SQLite log and prints a `rich`-formatted
|
|
258
427
|
table: request count, average reward, average latency, and cost score
|
|
259
428
|
per model per mode, plus your cache hit rate and how many attempts the
|
|
260
429
|
quality gate has caught and retried. Entirely local, no network call.
|
|
261
430
|
|
|
431
|
+
`arcus eval` runs the offline policy evaluation described above against
|
|
432
|
+
your own logged history and prints the comparison table (IPS and
|
|
433
|
+
doubly-robust estimates with 95% confidence intervals for the greedy
|
|
434
|
+
policy and each "always use model X" baseline, against what actually
|
|
435
|
+
ran). Below 30 logged bandit-mode requests it still prints the table but
|
|
436
|
+
flags the numbers as illustrative only, a bootstrap confidence interval
|
|
437
|
+
on a handful of rows isn't a reliable comparison yet.
|
|
438
|
+
|
|
262
439
|
## Status
|
|
263
440
|
|
|
264
441
|
Everything described above is implemented and working: the ARC adapter,
|
|
265
442
|
context classification, all three bandit algorithms with propensity
|
|
266
|
-
tracking, the reward function, the quality gate
|
|
267
|
-
|
|
268
|
-
|
|
443
|
+
tracking, the reward function, the quality gate (including rate-limit
|
|
444
|
+
backoff and VPN-restriction handling), the semantic cache and its
|
|
445
|
+
benchmark, the CLI (ask command with a manual `--model` override, chat
|
|
446
|
+
mode with inline attachments and transcript export, image input,
|
|
447
|
+
document Q&A, web search, a config command, first-run wizard, error
|
|
448
|
+
piping, stats, models, and offline eval), and the offline evaluation +
|
|
449
|
+
regret benchmarking layer.
|
|
269
450
|
|
|
270
451
|
Verified live against a real ARC key: all four models respond correctly
|
|
271
|
-
(`tests/adapters/test_arc_adapter_live.py`),
|
|
452
|
+
(`tests/adapters/test_arc_adapter_live.py`), a full end-to-end
|
|
272
453
|
`arcus "..."` run exercises the whole pipeline (context classification,
|
|
273
454
|
cache miss, bandit routing, a real ARC call, the quality gate, logging,
|
|
274
|
-
and caching the result) against real traffic
|
|
275
|
-
|
|
455
|
+
and caching the result) against real traffic, and image input, document
|
|
456
|
+
Q&A, and web search have each been run against real responses too, not
|
|
457
|
+
just unit tested. Test suite: 273 passing with a key set (269 plus 4
|
|
458
|
+
live-only tests), 4 skipped without one.
|
|
459
|
+
|
|
460
|
+
The one exception: reasoning-effort variant routing
|
|
461
|
+
(`enable_reasoning_variants`) is unit tested against a fake adapter only,
|
|
462
|
+
not yet confirmed against a real ARC key, and defaults off for exactly
|
|
463
|
+
that reason. See "Adaptive routing" above.
|
|
276
464
|
|
|
277
465
|
Worth knowing: ARC's models are reasoning models under the hood, they
|
|
278
466
|
write to a hidden `reasoning` field before `content`, so a small
|
|
@@ -284,19 +472,27 @@ budget of your own.
|
|
|
284
472
|
|
|
285
473
|
What's still open:
|
|
286
474
|
|
|
287
|
-
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
475
|
+
- Real logged usage is still thin (a handful of manual runs). `arcus
|
|
476
|
+
eval` runs the comparison today, it's just not resting on enough
|
|
477
|
+
data yet to trust the numbers, it says so when that's the case rather
|
|
478
|
+
than presenting a false-confidence table.
|
|
479
|
+
- Reasoning-effort variant routing needs a live-key run to confirm ARC
|
|
480
|
+
actually serves the `-thinking-*` ids the way its docs describe,
|
|
481
|
+
before it's safe to turn on by default.
|
|
292
482
|
|
|
293
483
|
## Security & privacy
|
|
294
484
|
|
|
295
485
|
- Each install uses its own user's ARC key. Keys are never shared,
|
|
296
486
|
bundled, or sent anywhere but ARC's own endpoint.
|
|
297
|
-
- No data leaves your machine
|
|
298
|
-
and stats are all local SQLite, nothing
|
|
299
|
-
anywhere.
|
|
487
|
+
- No data leaves your machine except to ARC itself, with your own key.
|
|
488
|
+
Request logs, cache entries, and stats are all local SQLite, nothing
|
|
489
|
+
is aggregated or reported anywhere else.
|
|
490
|
+
- `arcus --doc` uploads the whole file to your ARC account temporarily
|
|
491
|
+
(deleted again once you have an answer), and `arcus --web` sends your
|
|
492
|
+
question through ARC's own web search tool. Both stay within ARC,
|
|
493
|
+
same as every other request, but a document leaving your machine
|
|
494
|
+
entirely (even briefly, even to your own account) is worth knowing
|
|
495
|
+
about explicitly.
|
|
300
496
|
- This tool hasn't been through ARC's security review for regulated
|
|
301
497
|
data (FERPA records, health data, etc.) the way ARC's own web
|
|
302
498
|
interface has. Don't route sensitive regulated data through it.
|