liulab-llms 2026.11.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.
- liulab_llms-2026.11.0/.github/workflows/ci.yml +109 -0
- liulab_llms-2026.11.0/.github/workflows/docs.yml +47 -0
- liulab_llms-2026.11.0/.github/workflows/release.yml +72 -0
- liulab_llms-2026.11.0/.gitignore +27 -0
- liulab_llms-2026.11.0/.markdownlint-cli2.yaml +35 -0
- liulab_llms-2026.11.0/.vale.ini +54 -0
- liulab_llms-2026.11.0/AGENTS.md +132 -0
- liulab_llms-2026.11.0/CHANGELOG.md +28 -0
- liulab_llms-2026.11.0/CLAUDE.md +1 -0
- liulab_llms-2026.11.0/GLOSSARY.md +159 -0
- liulab_llms-2026.11.0/LICENSE +21 -0
- liulab_llms-2026.11.0/PKG-INFO +87 -0
- liulab_llms-2026.11.0/README.md +63 -0
- liulab_llms-2026.11.0/docs/adr/0001-the-call-layers-wire.md +36 -0
- liulab_llms-2026.11.0/docs/adr/0002-one-process-holds-a-task.md +34 -0
- liulab_llms-2026.11.0/docs/adr/0003-the-state-files-layout.md +37 -0
- liulab_llms-2026.11.0/docs/adr/0004-the-stamps-repo-version.md +29 -0
- liulab_llms-2026.11.0/docs/adr/0005-the-answer-schema-validator.md +25 -0
- liulab_llms-2026.11.0/docs/adr/0006-the-gateway-server.md +46 -0
- liulab_llms-2026.11.0/docs/adr/0007-a-pass-at-scale.md +42 -0
- liulab_llms-2026.11.0/docs/adr/0008-off-peak-only.md +39 -0
- liulab_llms-2026.11.0/docs/adr/0009-jev-through-typesafes-sdk.md +38 -0
- liulab_llms-2026.11.0/docs/adr/0010-reviews-in-json-lines-files.md +39 -0
- liulab_llms-2026.11.0/docs/adr/0011-rate-limits-and-retries.md +42 -0
- liulab_llms-2026.11.0/docs/agents/writing.md +66 -0
- liulab_llms-2026.11.0/docs/api.md +83 -0
- liulab_llms-2026.11.0/docs/gateway.md +94 -0
- liulab_llms-2026.11.0/docs/index.md +94 -0
- liulab_llms-2026.11.0/mkdocs.yml +85 -0
- liulab_llms-2026.11.0/pixi.lock +4468 -0
- liulab_llms-2026.11.0/pyproject.toml +243 -0
- liulab_llms-2026.11.0/scripts/check.sh +152 -0
- liulab_llms-2026.11.0/scripts/conformance.py +2095 -0
- liulab_llms-2026.11.0/skills/install.py +271 -0
- liulab_llms-2026.11.0/src/labllm/__init__.py +53 -0
- liulab_llms-2026.11.0/src/labllm/ceiling.py +76 -0
- liulab_llms-2026.11.0/src/labllm/client.py +343 -0
- liulab_llms-2026.11.0/src/labllm/clock.py +49 -0
- liulab_llms-2026.11.0/src/labllm/engine.py +748 -0
- liulab_llms-2026.11.0/src/labllm/errors.py +83 -0
- liulab_llms-2026.11.0/src/labllm/flights.py +35 -0
- liulab_llms-2026.11.0/src/labllm/model.py +35 -0
- liulab_llms-2026.11.0/src/labllm/peak.py +174 -0
- liulab_llms-2026.11.0/src/labllm/py.typed +0 -0
- liulab_llms-2026.11.0/src/labllm/retry.py +63 -0
- liulab_llms-2026.11.0/src/labllm/reviews.py +262 -0
- liulab_llms-2026.11.0/src/labllm/server/__init__.py +16 -0
- liulab_llms-2026.11.0/src/labllm/server/__main__.py +72 -0
- liulab_llms-2026.11.0/src/labllm/server/app.py +328 -0
- liulab_llms-2026.11.0/src/labllm/server/chat.py +154 -0
- liulab_llms-2026.11.0/src/labllm/server/jev.py +123 -0
- liulab_llms-2026.11.0/src/labllm/server/limits.py +157 -0
- liulab_llms-2026.11.0/src/labllm/server/prices.py +85 -0
- liulab_llms-2026.11.0/src/labllm/server/record.py +223 -0
- liulab_llms-2026.11.0/src/labllm/server/settings.py +91 -0
- liulab_llms-2026.11.0/src/labllm/server/throwaway.py +47 -0
- liulab_llms-2026.11.0/src/labllm/state.py +542 -0
- liulab_llms-2026.11.0/src/labllm/testing.py +303 -0
- liulab_llms-2026.11.0/src/labllm/wire.py +199 -0
- liulab_llms-2026.11.0/styles/Lab/Jargon.yml +49 -0
- liulab_llms-2026.11.0/styles/Lab/LengthAdr.yml +14 -0
- liulab_llms-2026.11.0/styles/Lab/LengthDoc.yml +16 -0
- liulab_llms-2026.11.0/styles/Lab/Readability.yml +16 -0
- liulab_llms-2026.11.0/tests/conftest.py +15 -0
- liulab_llms-2026.11.0/tests/data/layout-1.sql +47 -0
- liulab_llms-2026.11.0/tests/gateway_harness.py +153 -0
- liulab_llms-2026.11.0/tests/harness.py +98 -0
- liulab_llms-2026.11.0/tests/test_call_layer.py +571 -0
- liulab_llms-2026.11.0/tests/test_client.py +177 -0
- liulab_llms-2026.11.0/tests/test_engine.py +738 -0
- liulab_llms-2026.11.0/tests/test_purity.py +20 -0
- liulab_llms-2026.11.0/tests/test_reviews.py +269 -0
- liulab_llms-2026.11.0/tests/test_server.py +865 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# The gate, run on the pull request rather than on the branch.
|
|
2
|
+
#
|
|
3
|
+
# A job exists because a pixi ENVIRONMENT differs, not because a tool differs. The six
|
|
4
|
+
# static steps share one environment and are one job; `test` runs in the `test`
|
|
5
|
+
# environment; `docs` needs the heavy `docs` environment that nothing else installs, which
|
|
6
|
+
# is the whole reason it is a job of its own.
|
|
7
|
+
#
|
|
8
|
+
# Every step invokes `pixi run <task>` and never a command line. The step list lives in
|
|
9
|
+
# `pyproject.toml` — `check-static` names it once — so adding a step is one more word
|
|
10
|
+
# there and nothing here. Conformance rule `workflow-step-tasks` holds every workflow in
|
|
11
|
+
# the repo to it, and what it checks is that the command came from the task table: a step
|
|
12
|
+
# may pass arguments after `--`, and the rule prints them rather than forbidding them.
|
|
13
|
+
# It reads the `run:` line only, so `env:`, `working-directory:`, `shell:` and a local
|
|
14
|
+
# composite action can still make a step do something no `pixi run` does here. Keeping
|
|
15
|
+
# CI and a laptop the same is a habit this file keeps, not a property anything proves.
|
|
16
|
+
#
|
|
17
|
+
# Not shipped, deliberately: a `claude.yml`, because on a public repo anyone who can
|
|
18
|
+
# comment could spend the API budget; container and benchmark workflows, because nothing
|
|
19
|
+
# here needs them.
|
|
20
|
+
name: ci
|
|
21
|
+
|
|
22
|
+
on:
|
|
23
|
+
# `pull_request` is the trigger that matters. It tests the MERGE commit — the code that
|
|
24
|
+
# will exist after the merge, not the branch tip — and it is the only event that
|
|
25
|
+
# produces a run for a pull request from a fork. `push` is for `main` alone; on every
|
|
26
|
+
# branch it would run everything a second time for no new information.
|
|
27
|
+
push:
|
|
28
|
+
branches: [main]
|
|
29
|
+
pull_request:
|
|
30
|
+
|
|
31
|
+
# Keyed on the workflow and the ref, so pushing again to a pull request cancels the run it
|
|
32
|
+
# supersedes. `main` runs are never cancelled: a cancelled run on the branch everything
|
|
33
|
+
# merges into is an unreliable gate, and a red-or-missing history is worse than a slow one.
|
|
34
|
+
concurrency:
|
|
35
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
36
|
+
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
|
37
|
+
|
|
38
|
+
permissions:
|
|
39
|
+
contents: read
|
|
40
|
+
|
|
41
|
+
jobs:
|
|
42
|
+
# ruff, pyright, vale, markdownlint and conformance, all at once, all reported.
|
|
43
|
+
check:
|
|
44
|
+
runs-on: ubuntu-latest
|
|
45
|
+
steps:
|
|
46
|
+
# A real checkout, not a source archive: `conformance` shells out to `git ls-files`
|
|
47
|
+
# and exits 2 without a `.git` — its "could not run", spelled apart from the 1 a
|
|
48
|
+
# broken rule exits, so a bad checkout never reads as a rule this repo failed. The
|
|
49
|
+
# default shallow depth is enough — it needs the file list, not the history.
|
|
50
|
+
- uses: actions/checkout@v7
|
|
51
|
+
- uses: prefix-dev/setup-pixi@v0.10.2
|
|
52
|
+
with:
|
|
53
|
+
environments: default
|
|
54
|
+
# Fail if `pixi.lock` no longer matches `pyproject.toml`, rather than silently
|
|
55
|
+
# solving something else. This is what makes CI run the versions the laptop ran.
|
|
56
|
+
locked: true
|
|
57
|
+
# `check-static`, not `check`: `check` is the static steps plus pytest, and pytest is
|
|
58
|
+
# the `test` job below, in the environment that owns it.
|
|
59
|
+
- run: pixi run check-static
|
|
60
|
+
|
|
61
|
+
test:
|
|
62
|
+
runs-on: ubuntu-latest
|
|
63
|
+
steps:
|
|
64
|
+
- uses: actions/checkout@v7
|
|
65
|
+
- uses: prefix-dev/setup-pixi@v0.10.2
|
|
66
|
+
with:
|
|
67
|
+
environments: test
|
|
68
|
+
locked: true
|
|
69
|
+
# `-e test` is load-bearing: the `test` task exists in every environment, and without
|
|
70
|
+
# the flag pixi would run it in `default`, which this job did not install.
|
|
71
|
+
- run: pixi run -e test test
|
|
72
|
+
|
|
73
|
+
build:
|
|
74
|
+
runs-on: ubuntu-latest
|
|
75
|
+
steps:
|
|
76
|
+
- uses: actions/checkout@v7
|
|
77
|
+
with:
|
|
78
|
+
# The one job that needs history. The version is dynamic and hatch-vcs reads the
|
|
79
|
+
# newest tag; a shallow clone carries no tags, so the wheel would be stamped as a
|
|
80
|
+
# dev build even on a released commit.
|
|
81
|
+
fetch-depth: 0
|
|
82
|
+
- uses: prefix-dev/setup-pixi@v0.10.2
|
|
83
|
+
with:
|
|
84
|
+
# No `environments`: `build` is a plain task, so the default environment has it.
|
|
85
|
+
locked: true
|
|
86
|
+
- run: pixi run build
|
|
87
|
+
- uses: actions/upload-artifact@v7
|
|
88
|
+
with:
|
|
89
|
+
name: dist
|
|
90
|
+
path: dist/
|
|
91
|
+
# An empty upload is a green job that proved nothing.
|
|
92
|
+
if-no-files-found: error
|
|
93
|
+
|
|
94
|
+
docs:
|
|
95
|
+
runs-on: ubuntu-latest
|
|
96
|
+
steps:
|
|
97
|
+
- uses: actions/checkout@v7
|
|
98
|
+
- uses: prefix-dev/setup-pixi@v0.10.2
|
|
99
|
+
with:
|
|
100
|
+
environments: docs
|
|
101
|
+
locked: true
|
|
102
|
+
# `docs-build` is a task on the `docs` FEATURE, so pixi finds it in the one
|
|
103
|
+
# environment that has zensical and no `-e` flag is needed. The task carries
|
|
104
|
+
# `--strict`, so a dead link or a bad reference fails here — but not every kind of
|
|
105
|
+
# broken link, and not a `nav:` entry naming a missing file. The comment above
|
|
106
|
+
# `validation:` in `mkdocs.yml` lists what this job catches and what it does not, so
|
|
107
|
+
# a green run here is not a claim the site has no dead ends. This job builds the
|
|
108
|
+
# site and stops; publishing it is `docs.yml`, on `main`.
|
|
109
|
+
- run: pixi run docs-build
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Publishing the site to the `gh-pages` branch.
|
|
2
|
+
#
|
|
3
|
+
# Hand-written on purpose: zensical has no `gh-deploy` and none is planned, so the two
|
|
4
|
+
# halves are spelled out — build into `./site`, then force-push that directory to
|
|
5
|
+
# `gh-pages`.
|
|
6
|
+
#
|
|
7
|
+
# The pull request already builds the site (the `docs` job in `ci.yml`). This workflow adds
|
|
8
|
+
# only the publish, so it runs on `main` and nowhere else.
|
|
9
|
+
name: docs
|
|
10
|
+
|
|
11
|
+
on:
|
|
12
|
+
push:
|
|
13
|
+
branches: [main]
|
|
14
|
+
# For re-publishing without a commit — a Pages setting changed, a deploy was lost.
|
|
15
|
+
workflow_dispatch:
|
|
16
|
+
|
|
17
|
+
# The deploy force-pushes a branch of this repository. Nothing else here writes.
|
|
18
|
+
permissions:
|
|
19
|
+
contents: write
|
|
20
|
+
|
|
21
|
+
# NOT cancelled in flight, unlike `ci.yml`. This job replaces a published branch, and a run
|
|
22
|
+
# killed between the build and the push leaves `gh-pages` holding half a site with nothing
|
|
23
|
+
# queued to finish it. A superseded run is cheap; a half-written public site is not.
|
|
24
|
+
concurrency:
|
|
25
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
26
|
+
cancel-in-progress: false
|
|
27
|
+
|
|
28
|
+
jobs:
|
|
29
|
+
deploy:
|
|
30
|
+
runs-on: ubuntu-latest
|
|
31
|
+
steps:
|
|
32
|
+
- uses: actions/checkout@v7
|
|
33
|
+
- uses: prefix-dev/setup-pixi@v0.10.2
|
|
34
|
+
with:
|
|
35
|
+
environments: docs
|
|
36
|
+
locked: true
|
|
37
|
+
# Writes `./site`. `--clean --strict` live in the task, so this and a laptop build
|
|
38
|
+
# the same way.
|
|
39
|
+
- run: pixi run docs-build
|
|
40
|
+
- uses: peaceiris/actions-gh-pages@v4
|
|
41
|
+
with:
|
|
42
|
+
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
43
|
+
publish_dir: ./site
|
|
44
|
+
publish_branch: gh-pages
|
|
45
|
+
# `gh-pages` holds built output and no history anyone reads: one commit,
|
|
46
|
+
# replaced whole each deploy. This is what `mkdocs gh-deploy --force` did.
|
|
47
|
+
force_orphan: true
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Publishing to PyPI.
|
|
2
|
+
#
|
|
3
|
+
# Before the first release, PyPI needs a trusted publisher for this repository, this
|
|
4
|
+
# workflow filename and the environment named below. That step is outside the repo, so
|
|
5
|
+
# `init-repo` hands you the checklist rather than leaving a workflow that looks ready and
|
|
6
|
+
# is not. There is no API token here, and there must never be one.
|
|
7
|
+
#
|
|
8
|
+
# Two jobs on purpose. Only `publish` holds the OIDC token that PyPI trusts, and it runs no
|
|
9
|
+
# code from this repository — it downloads what `build` made and hands it over. A build
|
|
10
|
+
# step and a publishing credential in one job means every build dependency can reach that
|
|
11
|
+
# credential.
|
|
12
|
+
#
|
|
13
|
+
# A repo that does not publish deletes this file; `init-repo` does that for you.
|
|
14
|
+
name: release
|
|
15
|
+
|
|
16
|
+
on:
|
|
17
|
+
# NEVER a trigger that pushing `v0.0.0` can fire — a `push:` with no branch filter,
|
|
18
|
+
# `create`, or a `tags:` filter that admits it. `init-repo` pushes `v0.0.0` on a repo's
|
|
19
|
+
# first day so the version does not read as if a release already happened, and any of
|
|
20
|
+
# those would fire a PyPI publish right then, from a repo nobody has finished naming.
|
|
21
|
+
# Publishing a GitHub Release is a deliberate act by a person, so that is the trigger.
|
|
22
|
+
# This paragraph is the advice and conformance rule `release-trigger` is the enforcement;
|
|
23
|
+
# they say one thing, so they change together.
|
|
24
|
+
release:
|
|
25
|
+
types: [published]
|
|
26
|
+
# For a release whose publish failed on something outside the code — a missing trusted
|
|
27
|
+
# publisher, a PyPI outage. Choose the tag when you run it.
|
|
28
|
+
workflow_dispatch:
|
|
29
|
+
|
|
30
|
+
permissions:
|
|
31
|
+
contents: read
|
|
32
|
+
|
|
33
|
+
jobs:
|
|
34
|
+
build:
|
|
35
|
+
runs-on: ubuntu-latest
|
|
36
|
+
steps:
|
|
37
|
+
- uses: actions/checkout@v7
|
|
38
|
+
with:
|
|
39
|
+
# hatch-vcs reads the newest tag, and a shallow clone has none, so without this
|
|
40
|
+
# the release would be built and published as a dev version.
|
|
41
|
+
fetch-depth: 0
|
|
42
|
+
- uses: prefix-dev/setup-pixi@v0.10.2
|
|
43
|
+
with:
|
|
44
|
+
locked: true
|
|
45
|
+
# The same task the `build` job in `ci.yml` runs, so what is published was built the
|
|
46
|
+
# way every pull request already proved.
|
|
47
|
+
- run: pixi run build
|
|
48
|
+
- uses: actions/upload-artifact@v7
|
|
49
|
+
with:
|
|
50
|
+
name: dist
|
|
51
|
+
path: dist/
|
|
52
|
+
if-no-files-found: error
|
|
53
|
+
|
|
54
|
+
publish:
|
|
55
|
+
needs: build
|
|
56
|
+
runs-on: ubuntu-latest
|
|
57
|
+
# Name this environment in the PyPI trusted publisher too, if you set one there. Add
|
|
58
|
+
# required reviewers to it to put a person between a published Release and PyPI.
|
|
59
|
+
environment:
|
|
60
|
+
name: pypi
|
|
61
|
+
permissions:
|
|
62
|
+
# OIDC: GitHub mints a short-lived token, PyPI checks it came from this repository
|
|
63
|
+
# and this workflow. This is the only permission the job has.
|
|
64
|
+
id-token: write
|
|
65
|
+
steps:
|
|
66
|
+
- uses: actions/download-artifact@v8
|
|
67
|
+
with:
|
|
68
|
+
name: dist
|
|
69
|
+
path: dist/
|
|
70
|
+
# PyPA's own pointer, which is what their trusted-publishing guide tells you to use:
|
|
71
|
+
# it moves with security fixes to the publishing action. `dist/` is its default.
|
|
72
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
.pixi/
|
|
2
|
+
# Build output: `site/` is the docs, `dist/` the wheel. `.cache/` is mkdocs' download cache,
|
|
3
|
+
# which mkdocstrings fills with fetched inventories. mkdocs writes a `.gitignore` holding `*`
|
|
4
|
+
# inside it, so it already hides itself — named here anyway, because a rule this repo relies
|
|
5
|
+
# on should not live in a file a dependency happens to generate.
|
|
6
|
+
site/
|
|
7
|
+
.cache/
|
|
8
|
+
dist/
|
|
9
|
+
build/
|
|
10
|
+
__pycache__/
|
|
11
|
+
*.egg-info/
|
|
12
|
+
.coverage
|
|
13
|
+
.pytest_cache/
|
|
14
|
+
.ruff_cache/
|
|
15
|
+
.mypy_cache/
|
|
16
|
+
.DS_Store
|
|
17
|
+
|
|
18
|
+
# Agent products. Never a blanket `/.claude/`: the skill symlinks under `.claude/skills/`
|
|
19
|
+
# and `.agents/skills/` are TRACKED, because `init-repo` has to be discoverable the instant
|
|
20
|
+
# you clone — before anyone could run the thing that runs installers. So this names the two
|
|
21
|
+
# machine-local files inside `.claude/` and nothing else.
|
|
22
|
+
.claude/settings.local.json
|
|
23
|
+
.claude/worktrees/
|
|
24
|
+
# Installer output, from `python skills/install.py --target codex|gemini`. Nobody's clone
|
|
25
|
+
# needs another machine's copy.
|
|
26
|
+
/.codex/
|
|
27
|
+
/.gemini/
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# markdownlint reads structure; vale reads language. Both are steps of `check-static`.
|
|
2
|
+
#
|
|
3
|
+
# There is deliberately no `ignores:` list. It used to exist only to mirror a second list
|
|
4
|
+
# of excluded paths kept elsewhere, and two lists that must agree are a drift waiting to
|
|
5
|
+
# happen. Every markdown file in the repo is linted, agent-facing ones included.
|
|
6
|
+
#
|
|
7
|
+
# `gitignore: true` is not that list coming back: it delegates to the one exclusion list
|
|
8
|
+
# the repo already has, so `.pixi/`, `site/`, `dist/` and `build/` are skipped without
|
|
9
|
+
# anything new to keep in sync. It is load-bearing — without it the glob below walks
|
|
10
|
+
# `.pixi/envs/`, finds 376 markdown files shipped inside node_modules and reports 5010
|
|
11
|
+
# issues in other people's READMEs.
|
|
12
|
+
gitignore: true
|
|
13
|
+
|
|
14
|
+
# The glob lives here rather than on the pixi task line so that the file set does not
|
|
15
|
+
# depend on whether the task shell expands `**` before markdownlint-cli2 sees it.
|
|
16
|
+
globs:
|
|
17
|
+
- "**/*.md"
|
|
18
|
+
|
|
19
|
+
config:
|
|
20
|
+
default: true
|
|
21
|
+
# Scoped, not disabled. A hard tab in prose is an accident; inside a fenced block it is the
|
|
22
|
+
# content — `docs/research/` quotes a tab-indented record verbatim, the reason `Lab.Jargon`,
|
|
23
|
+
# `Lab.Readability` and the agent-docs table already exempt it.
|
|
24
|
+
MD010:
|
|
25
|
+
code_blocks: false
|
|
26
|
+
# The formatter owns line length in code and nothing owns it in prose. Wrapping is a
|
|
27
|
+
# matter of taste, and tables and long links routinely exceed any limit, so MD013 would
|
|
28
|
+
# fire on every one of them.
|
|
29
|
+
MD013: false
|
|
30
|
+
# Scoped, not disabled. A Keep a Changelog file gives every release the same section
|
|
31
|
+
# names, so `### Added` recurs once per release by design and the default fails a
|
|
32
|
+
# correct changelog on its SECOND release. `siblings_only` still fails one release
|
|
33
|
+
# repeating its own section, which is the duplicate that is a real mistake.
|
|
34
|
+
MD024:
|
|
35
|
+
siblings_only: true
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Rules live in styles/Lab/, tracked — so `styles/` is not in .gitignore.
|
|
2
|
+
#
|
|
3
|
+
# EVERY RULE IS NAMED IN EVERY SECTION. Vale's `*` matches `/`, so sections overlap and
|
|
4
|
+
# per-rule settings accumulate, later sections winning. An omitted rule keeps whatever an
|
|
5
|
+
# earlier section said, which once left ADRs checked by nothing on green CI. Broad section
|
|
6
|
+
# first. Each [tool.liulab.agent-docs] key appears verbatim in one header below; conformance
|
|
7
|
+
# rule 3 pairs them by substring. The caps are explained in docs/agents/writing.md.
|
|
8
|
+
|
|
9
|
+
StylesPath = styles
|
|
10
|
+
MinAlertLevel = error
|
|
11
|
+
|
|
12
|
+
# 1. Human-facing: the published site. Uncapped.
|
|
13
|
+
[{README.md,CHANGELOG.md,docs/**/*.md}]
|
|
14
|
+
BasedOnStyles = Lab
|
|
15
|
+
Lab.Jargon = YES
|
|
16
|
+
Lab.Readability = YES
|
|
17
|
+
Lab.LengthDoc = NO
|
|
18
|
+
Lab.LengthAdr = NO
|
|
19
|
+
|
|
20
|
+
# 2. Agent-facing: word budget only. GLOSSARY.md is not here — see section 5.
|
|
21
|
+
[{AGENTS.md,docs/agents/**/*.md,skills/**/SKILL.md}]
|
|
22
|
+
BasedOnStyles = Lab
|
|
23
|
+
Lab.Jargon = NO
|
|
24
|
+
Lab.Readability = NO
|
|
25
|
+
Lab.LengthDoc = YES
|
|
26
|
+
Lab.LengthAdr = NO
|
|
27
|
+
|
|
28
|
+
# 3. Decision records: the tighter cap.
|
|
29
|
+
[docs/adr/**/*.md]
|
|
30
|
+
BasedOnStyles = Lab
|
|
31
|
+
Lab.Jargon = NO
|
|
32
|
+
Lab.Readability = NO
|
|
33
|
+
Lab.LengthDoc = NO
|
|
34
|
+
Lab.LengthAdr = YES
|
|
35
|
+
|
|
36
|
+
# 4. Research notes: uncapped, and exempt from jargon because a note quotes what it argues
|
|
37
|
+
# against — the note defining the jargon map lists every banned phrase as evidence.
|
|
38
|
+
[docs/research/**/*.md]
|
|
39
|
+
BasedOnStyles = Lab
|
|
40
|
+
Lab.Jargon = NO
|
|
41
|
+
Lab.Readability = NO
|
|
42
|
+
Lab.LengthDoc = NO
|
|
43
|
+
Lab.LengthAdr = NO
|
|
44
|
+
|
|
45
|
+
# 5. The glossary: no file cap. It grows one term at a time and never shrinks, so a file-level
|
|
46
|
+
# word count measures the size of the domain, not the quality of the writing. Conformance caps
|
|
47
|
+
# each entry at 200 words instead — the unit that matches how the file grows. Last because it
|
|
48
|
+
# is the narrowest pattern here, and the later section wins.
|
|
49
|
+
[GLOSSARY.md]
|
|
50
|
+
BasedOnStyles = Lab
|
|
51
|
+
Lab.Jargon = NO
|
|
52
|
+
Lab.Readability = NO
|
|
53
|
+
Lab.LengthDoc = NO
|
|
54
|
+
Lab.LengthAdr = NO
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# liulab-llms
|
|
2
|
+
|
|
3
|
+
Lab repos call language models through one lab gateway, which holds the provider keys,
|
|
4
|
+
caches every response for the whole lab, and refuses a call that would cross its meter's
|
|
5
|
+
ceiling. This package is the client for it, the gateway itself under the `gateway` extra,
|
|
6
|
+
and an engine for model work over many units. seqforge is the first user, not the only one.
|
|
7
|
+
Distribution name **`liulab-llms`**, import name **`labllm`**.
|
|
8
|
+
|
|
9
|
+
Easy to get wrong: the cache key is the hash of the request form `labllm.wire` builds, so
|
|
10
|
+
nothing that labels a call (meter, tag, person, repo, ceiling) may enter the form. The plain
|
|
11
|
+
install never imports LiteLLM; a test pins it.
|
|
12
|
+
|
|
13
|
+
## Restraint
|
|
14
|
+
|
|
15
|
+
Generalizable, lightweight, uncustomized — in that order, and ahead of thorough.
|
|
16
|
+
|
|
17
|
+
Every gate, lint rule, cap and check is paid by everyone who works here afterwards. They
|
|
18
|
+
arrive one at a time, each reasonable alone, and nothing measures the sum. So before adding
|
|
19
|
+
one:
|
|
20
|
+
|
|
21
|
+
- **Does it generalize?** A rule shaped by one situation belongs where that situation is.
|
|
22
|
+
Evidence that it helps there is not evidence about here.
|
|
23
|
+
- **What is the total?** Count what someone must already satisfy before writing any code.
|
|
24
|
+
- **Would a narrower rule do?** Prefer narrowing to forbidding a legitimate practice.
|
|
25
|
+
|
|
26
|
+
**A measurement outranks a hypothesis.** When a rule is shown to fire on correct work, that
|
|
27
|
+
is evidence; a defect it might also catch is not. Declining a rule, or removing one that
|
|
28
|
+
misfires, is as much a contribution as adding one.
|
|
29
|
+
|
|
30
|
+
## Toolchain
|
|
31
|
+
|
|
32
|
+
- **pixi** is the only supported toolchain. Never bare pip, uv, or conda. `pyproject.toml`
|
|
33
|
+
is the single source of truth for dependencies, environments, and tasks.
|
|
34
|
+
- **Python 3.13**, declared by `requires-python` and the pixi pin. Write it anywhere else — a
|
|
35
|
+
ruff target, a pyright version — and `conformance` holds that copy to the floor.
|
|
36
|
+
- **hatchling + hatch-vcs**. The version comes from the newest git tag, CalVer
|
|
37
|
+
`vYYYY.M.PATCH`. Never hand-edit a version.
|
|
38
|
+
- Platforms: `osx-arm64` and `linux-64`.
|
|
39
|
+
|
|
40
|
+
## Layout
|
|
41
|
+
|
|
42
|
+
```text
|
|
43
|
+
src/labllm/ the package: client.py, wire.py (request form, cache key), testing.py,
|
|
44
|
+
engine.py, state.py (state file), reviews.py, server/ (the gateway)
|
|
45
|
+
tests/ pytest; test_call_layer.py runs against every gateway in conftest.py
|
|
46
|
+
docs/ the published site; docs/adr/ and docs/agents/ are agent-facing
|
|
47
|
+
skills/ repo-local agent skills; `python skills/install.py --help`
|
|
48
|
+
scripts/check.sh the gate runner
|
|
49
|
+
GLOSSARY.md the glossary — the words this repo uses
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Gates
|
|
53
|
+
|
|
54
|
+
`pixi run check` must be green before you commit. It is `check-static` plus `test`:
|
|
55
|
+
|
|
56
|
+
| Step | Tool |
|
|
57
|
+
| --- | --- |
|
|
58
|
+
| `lint`, `fmt-check` | ruff |
|
|
59
|
+
| `typecheck` | pyright, `standard` mode, plus annotated parameters outside `tests/` |
|
|
60
|
+
| `vale`, `markdownlint` | the writing rules |
|
|
61
|
+
| `conformance` | the repo still matches the template's rules |
|
|
62
|
+
| `test` | pytest |
|
|
63
|
+
|
|
64
|
+
`check.sh` runs every step and reports **all** failures, not just the first — so read to the
|
|
65
|
+
bottom before fixing anything. The docs build is not part of `check`; it needs its own pixi
|
|
66
|
+
environment and runs as its own CI job.
|
|
67
|
+
|
|
68
|
+
Work on a branch and merge through a pull request: `pull_request` tests the merge commit,
|
|
69
|
+
while a push to `main` tests it only once it has landed.
|
|
70
|
+
|
|
71
|
+
Four traps in the gate:
|
|
72
|
+
|
|
73
|
+
- **`--doctest-modules` runs every `Examples` block in `src/` as a test.** An example that
|
|
74
|
+
downloads, shells out or needs a large file wants `# doctest: +SKIP`, and the marker covers
|
|
75
|
+
only the example it sits on.
|
|
76
|
+
- **`filterwarnings = ["error"]`.** Each tolerated warning gets a targeted entry in
|
|
77
|
+
`pyproject.toml` with a comment saying why. Never a blanket ignore.
|
|
78
|
+
- **`src/` is collected**, so every module is imported at test time. Keep a heavy import
|
|
79
|
+
inside the method body that needs it, not at module scope.
|
|
80
|
+
- **A substring of `--help` output is not a substring of the help.** Typer prints it through
|
|
81
|
+
rich, which styles the first dash of an option on its own, so `--version` is several spans
|
|
82
|
+
and not one string. Colour is off under `ssh` and on in CI; reproduce CI with
|
|
83
|
+
`FORCE_COLOR=1`, and strip the styling before you assert.
|
|
84
|
+
|
|
85
|
+
## Writing rules
|
|
86
|
+
|
|
87
|
+
Three rules, all enforced by `vale`: be concise; agent-facing documents have word caps;
|
|
88
|
+
human-facing prose avoids jargon and stays readable. Read `docs/agents/writing.md` before
|
|
89
|
+
writing either kind — the caps are lower than you expect, and this file is subject to one.
|
|
90
|
+
|
|
91
|
+
### Comments and docstrings are short
|
|
92
|
+
|
|
93
|
+
Nothing checks these, so the rule is on you. A docstring says what a thing does and what it
|
|
94
|
+
promises. A comment says why a line is surprising. Neither is a notebook.
|
|
95
|
+
|
|
96
|
+
**Do not record:**
|
|
97
|
+
|
|
98
|
+
- **Measurements.** Sizes, row counts, timings, percentages, entry totals. They were true
|
|
99
|
+
once, on one machine, against one release. A number in a comment is wrong by the next
|
|
100
|
+
release and nothing checks it.
|
|
101
|
+
- **The environment.** Hostnames, absolute paths, package versions, which host something ran
|
|
102
|
+
on, what a tool printed that day.
|
|
103
|
+
- **Implementation detail a reader can see.** Restating the code below in prose, or narrating
|
|
104
|
+
how the current version happens to work inside.
|
|
105
|
+
- **History.** What a previous version did, what was tried, which ticket decided it, what the
|
|
106
|
+
alternative was.
|
|
107
|
+
|
|
108
|
+
Where a fact genuinely has to survive, it has a home that is checked. A comment is the one
|
|
109
|
+
place that holds none of them accountable, which is why it is the wrong place:
|
|
110
|
+
|
|
111
|
+
| The fact | Where it lives |
|
|
112
|
+
| --- | --- |
|
|
113
|
+
| What the code does | a test pins it |
|
|
114
|
+
| A decision and its trade-off | an ADR |
|
|
115
|
+
| Vocabulary | `GLOSSARY.md` |
|
|
116
|
+
| What is still open | an issue |
|
|
117
|
+
| A measurement | nowhere — delete it |
|
|
118
|
+
|
|
119
|
+
Keep a docstring to its contract — arguments, return, what it raises, one example if the
|
|
120
|
+
example earns its keep. Say the surprising thing in one sentence and stop.
|
|
121
|
+
|
|
122
|
+
## Read next
|
|
123
|
+
|
|
124
|
+
| When | Read |
|
|
125
|
+
| --- | --- |
|
|
126
|
+
| Before changing code | `GLOSSARY.md`, then any ADR covering the area |
|
|
127
|
+
| Before changing the wire or a gateway | `docs/adr/0001-the-call-layers-wire.md`, `tests/harness.py` |
|
|
128
|
+
| Writing anything | `docs/agents/writing.md` |
|
|
129
|
+
|
|
130
|
+
The issue tracker, triage labels and domain docs are the lab defaults, the page `/lab-setup`
|
|
131
|
+
points every agent at. A page of this repo's own under `docs/agents/` overrides only that one
|
|
132
|
+
default, so write one only where this repo differs.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
Every change worth knowing about, newest first. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Version numbers are CalVer tags
|
|
5
|
+
of the form `vYYYY.M.PATCH`, and the tag is where the version comes from — nothing here
|
|
6
|
+
sets one.
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [2026.11.0] - 2026-11-09
|
|
11
|
+
|
|
12
|
+
The first release.
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- The lab gateway, under the `gateway` extra: one service that holds the provider keys, sends
|
|
17
|
+
every model call, keeps every attempt in PostgreSQL and replays any request it has answered
|
|
18
|
+
before at no cost.
|
|
19
|
+
- The engine: declare a unit kind as one plain function, run a pass over many units from your
|
|
20
|
+
own command, stop cleanly at a ceiling, and resume by running the same command again.
|
|
21
|
+
- Off-peak-only passes, which send DeepSeek calls only in its half-price hours.
|
|
22
|
+
- The call layer: open a meter with a dollar ceiling and make blocking chat and Jev calls through
|
|
23
|
+
the lab gateway, with `labllm.testing.FakeGateway` standing in for it in tests.
|
|
24
|
+
- The review queue and reviews: list the units a person must look at, and save a person's
|
|
25
|
+
answer or drop to a file per kind that the repo commits to git.
|
|
26
|
+
- The gateway keeps every caller under each provider's rate limits, waiting rather than
|
|
27
|
+
refusing, and tries a busy provider again for a few minutes. Each reply says how many
|
|
28
|
+
attempts it took.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
AGENTS.md
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# Glossary
|
|
2
|
+
|
|
3
|
+
The words for model calls through the lab gateway, and for the work run over many units.
|
|
4
|
+
|
|
5
|
+
## Glossary
|
|
6
|
+
|
|
7
|
+
### Ceiling
|
|
8
|
+
|
|
9
|
+
A meter's limit in dollars, required and with no default. The gateway refuses a call whose
|
|
10
|
+
largest possible cost (estimated input plus the full output allowance), added to what the
|
|
11
|
+
meter has spent and holds for calls in flight, would cross it. A cache hit never counts.
|
|
12
|
+
_Avoid_: budget, cap, limit
|
|
13
|
+
|
|
14
|
+
### Dropped
|
|
15
|
+
|
|
16
|
+
An ending a person's review gives: the unit is taken out of the work, with a note giving the
|
|
17
|
+
reason. No answer is kept. Like an answer from a review, it holds until the unit's inputs tag
|
|
18
|
+
changes.
|
|
19
|
+
_Avoid_: skipped, excluded
|
|
20
|
+
|
|
21
|
+
### Exchange
|
|
22
|
+
|
|
23
|
+
One attempt at a provider, retries each their own, recorded whole in the gateway's record:
|
|
24
|
+
the request, the response or error, usage, cost, and the person, repo, meter and tag. A cache
|
|
25
|
+
hit adds an exchange with no bodies.
|
|
26
|
+
_Avoid_: transcript, log entry
|
|
27
|
+
|
|
28
|
+
### Gateway
|
|
29
|
+
|
|
30
|
+
The lab's one service that sends every model call: it holds the provider keys, keeps the
|
|
31
|
+
record, replays any request it has answered, and enforces each meter's ceiling. A call
|
|
32
|
+
identical to one in flight waits for it, so a request is paid once. `Gateway` is also the
|
|
33
|
+
library's client for it.
|
|
34
|
+
_Avoid_: proxy
|
|
35
|
+
|
|
36
|
+
### Held
|
|
37
|
+
|
|
38
|
+
A unit whose inputs are not there yet: its kind's loader raised `Held`, and the reason is
|
|
39
|
+
recorded. Not an ending, so it leaves the unit's current answer as it was.
|
|
40
|
+
_Avoid_: skipped, pending
|
|
41
|
+
|
|
42
|
+
### Inputs tag
|
|
43
|
+
|
|
44
|
+
A string the repo's loader returns with a unit's inputs, stamped on its answer. The engine
|
|
45
|
+
only compares it, so the repo chooses how fine it is.
|
|
46
|
+
_Avoid_: hash, checksum
|
|
47
|
+
|
|
48
|
+
### Meter
|
|
49
|
+
|
|
50
|
+
A group of calls under one ceiling, with its own identity on the gateway. Its label is for
|
|
51
|
+
people, so two meters with one label never share a ceiling. A pass opens one meter.
|
|
52
|
+
_Avoid_: session, budget
|
|
53
|
+
|
|
54
|
+
### Off-peak only
|
|
55
|
+
|
|
56
|
+
A meter and pass setting, off by default. Its calls go to a provider only outside the peak
|
|
57
|
+
hours of the model's price table entry: in peak the gateway answers with the time peak ends,
|
|
58
|
+
and the call waits and is sent again. New calls stop a set margin before peak starts. A cache
|
|
59
|
+
hit is answered at once, and a model with no off-peak hours is unaffected.
|
|
60
|
+
_Avoid_: night mode, discount mode
|
|
61
|
+
|
|
62
|
+
### Pass
|
|
63
|
+
|
|
64
|
+
One run of a unit kind over the keys it is given, in that order, under one meter. It records
|
|
65
|
+
each unit's outcome and returns counts per ending. Rerunning the same command starts a new
|
|
66
|
+
pass whose finished calls replay free, which is how a stopped pass resumes.
|
|
67
|
+
_Avoid_: run, batch, job, round
|
|
68
|
+
|
|
69
|
+
### Rate limit
|
|
70
|
+
|
|
71
|
+
A provider's limit, set in the gateway's settings for all callers together: requests a
|
|
72
|
+
second, input tokens a second, or calls at once, for the provider or for one of its models.
|
|
73
|
+
A per-minute limit is entered as its per-second rate. A call over one waits; none is refused.
|
|
74
|
+
_Avoid_: quota, throttle, ceiling
|
|
75
|
+
|
|
76
|
+
### Record
|
|
77
|
+
|
|
78
|
+
The gateway's one table of every exchange, which is also its response cache: the cache
|
|
79
|
+
entry for a request is its first successful exchange not retired. A bad entry is retired by
|
|
80
|
+
hand and stays in the record. Each meter's spend is rebuilt from it after a restart.
|
|
81
|
+
_Avoid_: log, transcript, cache table
|
|
82
|
+
|
|
83
|
+
### Refused
|
|
84
|
+
|
|
85
|
+
An ending: the kind's code refused the unit with a message. No answer is kept.
|
|
86
|
+
_Avoid_: rejected, failed
|
|
87
|
+
|
|
88
|
+
### Request form
|
|
89
|
+
|
|
90
|
+
What decides a call's answer, as `labllm.wire` builds it: provider, exact model name,
|
|
91
|
+
settings, the messages or Jev's description and questions, and the output schema. Its hash
|
|
92
|
+
is the cache key; labels, the tag and the ceiling are never in it.
|
|
93
|
+
_Avoid_: payload, prompt
|
|
94
|
+
|
|
95
|
+
### Review
|
|
96
|
+
|
|
97
|
+
One person's recorded decision on one unit: an answer, or a drop. It is a line in its kind's
|
|
98
|
+
review file, made for the unit's inputs tag of that moment, and outranks the models' answer
|
|
99
|
+
while the tag is unchanged. Of a unit's reviews for one tag, the latest-timed wins.
|
|
100
|
+
_Avoid_: correction, label, annotation
|
|
101
|
+
|
|
102
|
+
### Review queue
|
|
103
|
+
|
|
104
|
+
Every unit whose current answer is unsettled or refused, in key order: the units a person
|
|
105
|
+
must look at.
|
|
106
|
+
_Avoid_: inbox, backlog, triage list
|
|
107
|
+
|
|
108
|
+
### Settled
|
|
109
|
+
|
|
110
|
+
An ending: the unit has an answer and every check passed, or a person overrode a failed check
|
|
111
|
+
with a reason. Code alone may settle a unit, unless a check it recorded failed.
|
|
112
|
+
_Avoid_: done, accepted
|
|
113
|
+
|
|
114
|
+
### Stamp
|
|
115
|
+
|
|
116
|
+
What produced an answer, recorded with it: the kind and its version, the model for each
|
|
117
|
+
role, the inputs tag, the pass, and the calling repo's and liulab-llms's code versions.
|
|
118
|
+
_Avoid_: provenance, metadata
|
|
119
|
+
|
|
120
|
+
### Task
|
|
121
|
+
|
|
122
|
+
One body of work's state: one SQLite file in a folder the calling command names, held by one
|
|
123
|
+
process at a time. It keeps every pass and each unit's outcome, answer, stamp and check
|
|
124
|
+
results per pass. Deleting the file is safe: rerunning rebuilds the models' answers from the
|
|
125
|
+
cache.
|
|
126
|
+
_Avoid_: project, database
|
|
127
|
+
|
|
128
|
+
### Unfinished
|
|
129
|
+
|
|
130
|
+
A unit that reached no ending in a pass: its kind's code raised an error, its answer did not
|
|
131
|
+
fit the answer schema, or the pass stopped. The reason is recorded. Not an ending, so it
|
|
132
|
+
leaves the unit's current answer as it was.
|
|
133
|
+
_Avoid_: failed, errored
|
|
134
|
+
|
|
135
|
+
### Unit
|
|
136
|
+
|
|
137
|
+
One independent piece of work, named by a string key, such as one sequencing Series. Its
|
|
138
|
+
current answer is its outcome in the latest pass that reached an ending, unless a review made
|
|
139
|
+
for that outcome's inputs tag outranks it.
|
|
140
|
+
_Avoid_: item, row, sample
|
|
141
|
+
|
|
142
|
+
### Unit kind
|
|
143
|
+
|
|
144
|
+
A repo's declaration of one kind of unit: a name, a hand-set version, roles, a loader, one
|
|
145
|
+
plain function that does the work, an answer schema and answer checks.
|
|
146
|
+
_Avoid_: step list, pipeline
|
|
147
|
+
|
|
148
|
+
### Unsettled
|
|
149
|
+
|
|
150
|
+
An ending: the unit has an answer that fits its schema, but a check failed. The answer is
|
|
151
|
+
kept with each failed check's message.
|
|
152
|
+
_Avoid_: doubtful, partial
|
|
153
|
+
|
|
154
|
+
### Waiting call
|
|
155
|
+
|
|
156
|
+
A call the gateway holds back for a rate limit or a provider's pause after overload. Waiting
|
|
157
|
+
calls go first come, first served, holding nothing against their meter's ceiling. Waiting
|
|
158
|
+
for a rate limit never ends a call; only a provider still failing after the retry budget does.
|
|
159
|
+
_Avoid_: queued request, throttled call
|