This document covers the maintainer-facing machinery: the fixture/conformance system, the doc-drift checker, the provider adapter development guide, and the day-to-day commands.
lm15-python/
├── lm15/
│ ├── types.py # canonical dataclasses: Request, Response, Parts, tools, endpoints
│ ├── providers/ # OpenAI, Anthropic, Gemini adapters (+ compat, subscription, async)
│ ├── compat.py # typed API-dialect compatibility policies
│ ├── models.py # optional ModelInfo metadata + ModelRegistry
│ ├── profiles.py # ProviderProfile/EndpointProfile resolution helpers
│ ├── result.py # stream materialization + lazy Result helper
│ ├── serde.py # canonical JSON dictionaries
│ ├── errors.py # normalized lm15 error hierarchy
│ ├── live.py # websocket/live-session helpers
│ ├── sse.py # server-sent event parser
│ └── transports/ # stdlib HTTP/1.1 sync + async transports
├── conformance/ # fixture suite and reports
├── tests/ # unit + conformance tests wired into pytest
├── benchmarks/ # transport benchmarks
└── pyproject.toml
CI runs the language-neutral lm15-contract harness against this repo at the
exact contract commit recorded in CONTRACT_PIN (a full SHA of
lm15-dev/lm15-contract main). Discipline: when a change here requires a
contract change (new fixture, spec row, serde kind, protocol op), land the
contract commit first — with the evidence AUTHORITY.md demands — then bump
CONTRACT_PIN to that SHA in the same lm15-python commit as the code
change, so both repos stay green at every pinned pair of revisions. Never
bump the pin just to make CI green: the diff between the old and new pin is
part of your review.
The conformance suite checks that lm15's canonical model stays aligned with real provider APIs.
logical lm15 case
conformance/cross_sdk/test_cases.json
│
▼
lm15-python provider adapter builds HTTP request
│
▼
expected provider fixture
conformance/provider_requests/cases/<provider>/<feature>.json
│
├── check_request_fixtures.py compares request shape
├── validate_live.py can send the request to the real API
├── check_response_fixtures.py parses saved response bodies/SSE
├── check_error_fixtures.py normalizes provider error bodies
├── check_endpoint_fixtures.py checks embeddings/files/batch/image/audio/live
├── check_serde_fixtures.py checks canonical JSON round trips
└── check_doc_drift.py checks provider docs against features.yaml
Run everything:
python3 conformance/run_all.py --strictRun one check:
python3 conformance/check_doc_drift.py --strict
python3 conformance/check_response_fixtures.py --strictRun or preview one live provider fixture, if the relevant API key is set:
python3 conformance/provider_requests/validate_live.py --dry-run --task openai.basic_text
python3 conformance/provider_requests/validate_live.py --task openai.basic_textGenerated reports are written under conformance/reports/ and are ignored by
git.
-
Add or update the logical case in
conformance/cross_sdk/test_cases.json. -
Add the expected provider HTTP request in
conformance/provider_requests/cases/<provider>/<feature>.json. -
Add the feature to
conformance/provider_requests/features.yamlso doc drift can tell whether provider documentation is represented. -
If response parsing should be checked, add an
expect_lm15block to the provider case and save a real response body underconformance/provider_requests/results/bodies/<provider>.<feature>/. -
If the provider has a special error shape, add an error case under
conformance/errors/cases/<provider>.json. -
Run:
python3 conformance/run_all.py --strict pytest -q
Example expect_lm15 block:
{
"expect_lm15": {
"parts": {
"text": {"min": 1},
"citation": {"min": 1}
},
"finish_reason": "stop",
"usage": {"required": true}
}
}conformance/check_doc_drift.py parses snapshotted provider docs in
conformance/provider_docs/ and compares top-level request parameters with
conformance/provider_requests/features.yaml.
Some always-on lm15 request fields do not need separate feature entries:
IGNORE_PARAMS = {"model", "messages", "contents", "input"}Provider docs often use camelCase/PascalCase while lm15 feature names use snake_case, so the drift check normalizes names before deciding that a param is unmapped.
If check_doc_drift.py --strict reports an unmapped param, either:
- add a real feature entry to
features.yaml, or - add the param to
IGNORE_PARAMSonly if it is a core field that should never have a separate fixture.
Provider classes live in lm15/providers/ and inherit BaseProviderLM.
A provider adapter is responsible for:
build_request(request, stream)— map canonicalRequestto an HTTP request.parse_response(request, response)— map provider JSON to canonicalResponse.parse_stream_events(...)— map SSE chunks toStreamEvents (the single stream-parse path per provider).normalize_error(status, body)— map provider errors tolm15.errors.- Optional endpoint methods:
embeddings,file_upload,batch_submit,image_generate,audio_generate,live.
Keep provider-only options in Config.extensions rather than adding universal
fields unless the same concept is supported across providers.
The sibling lm15-contract repository is where cross-language conformance
lives — it is the authority, and this package is one implementation of it.
Start with its AUTHORITY.md (what is normative and what wins on conflict),
spec/ (the canonical rules: serde, mapping, errors), and harness/ (the
runner that executes the shared corpus of cases, goldens, bodies, and error
fixtures against each language implementation). Behavior changes here must be
reflected there first, then re-verified through the harness.
# Unit and conformance tests through pytest
pytest -q
# Full offline conformance suite
python3 conformance/run_all.py --strict
# Request fixture comparison only
python3 conformance/check_request_fixtures.py --strict
# Response/SSE fixture parser check only
python3 conformance/check_response_fixtures.py --strict
# Canonical JSON round trips only
python3 conformance/check_serde_fixtures.py --strict
# Provider-doc coverage only
python3 conformance/check_doc_drift.py --strict