artie-cli 0.7.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.
- artie_cli-0.7.0/.artie.toml.example +32 -0
- artie_cli-0.7.0/.claude/settings.local.json +25 -0
- artie_cli-0.7.0/.github/workflows/ci.yml +37 -0
- artie_cli-0.7.0/.gitignore +23 -0
- artie_cli-0.7.0/ARCHITECTURE.md +83 -0
- artie_cli-0.7.0/CONTRIBUTING.md +142 -0
- artie_cli-0.7.0/PKG-INFO +169 -0
- artie_cli-0.7.0/README.md +139 -0
- artie_cli-0.7.0/config.py +184 -0
- artie_cli-0.7.0/doc-eval.py +393 -0
- artie_cli-0.7.0/doc_eval.py +435 -0
- artie_cli-0.7.0/docs/scoring.md +230 -0
- artie_cli-0.7.0/examples/bookclub-openapi.yaml +160 -0
- artie_cli-0.7.0/examples/broken-openapi.yaml +48 -0
- artie_cli-0.7.0/examples/sample-openapi.yaml +55 -0
- artie_cli-0.7.0/pyproject.toml +61 -0
- artie_cli-0.7.0/src/artie/__init__.py +11 -0
- artie_cli-0.7.0/src/artie/__main__.py +6 -0
- artie_cli-0.7.0/src/artie/baseline.py +71 -0
- artie_cli-0.7.0/src/artie/checks/__init__.py +23 -0
- artie_cli-0.7.0/src/artie/checks/auth_clarity.py +173 -0
- artie_cli-0.7.0/src/artie/checks/base.py +93 -0
- artie_cli-0.7.0/src/artie/checks/endpoint_completeness.py +138 -0
- artie_cli-0.7.0/src/artie/checks/error_documentation.py +180 -0
- artie_cli-0.7.0/src/artie/checks/example_coverage.py +157 -0
- artie_cli-0.7.0/src/artie/checks/format_efficiency.py +164 -0
- artie_cli-0.7.0/src/artie/checks/generation_quality.py +604 -0
- artie_cli-0.7.0/src/artie/checks/parameter_naming.py +227 -0
- artie_cli-0.7.0/src/artie/checks/schema_complexity.py +178 -0
- artie_cli-0.7.0/src/artie/cli.py +280 -0
- artie_cli-0.7.0/src/artie/config.py +180 -0
- artie_cli-0.7.0/src/artie/fetcher.py +117 -0
- artie_cli-0.7.0/src/artie/generator.py +186 -0
- artie_cli-0.7.0/src/artie/parsers/__init__.py +75 -0
- artie_cli-0.7.0/src/artie/parsers/openapi.py +325 -0
- artie_cli-0.7.0/src/artie/parsers/types.py +60 -0
- artie_cli-0.7.0/src/artie/reporters/__init__.py +1 -0
- artie_cli-0.7.0/src/artie/reporters/json_report.py +66 -0
- artie_cli-0.7.0/src/artie/reporters/terminal.py +186 -0
- artie_cli-0.7.0/tests/helpers.py +23 -0
- artie_cli-0.7.0/tests/test_baseline.py +76 -0
- artie_cli-0.7.0/tests/test_checks_scoring.py +375 -0
- artie_cli-0.7.0/tests/test_config.py +127 -0
- artie_cli-0.7.0/tests/test_format_detector.py +108 -0
- artie_cli-0.7.0/tests/test_gate.py +64 -0
- artie_cli-0.7.0/tests/test_generation_eval.py +143 -0
- artie_cli-0.7.0/tests/test_parser.py +202 -0
- artie_cli-0.7.0/tests/test_sample_generation.py +113 -0
- artie_cli-0.7.0/tests/test_scoring_version.py +51 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# .artie.toml — per-project configuration for artie-cli.
|
|
2
|
+
#
|
|
3
|
+
# Copy this file to `.artie.toml` in your repository root. artie discovers it
|
|
4
|
+
# automatically by searching the working directory and its parents; you can
|
|
5
|
+
# also point at one explicitly with `artie check --config path/to/.artie.toml`.
|
|
6
|
+
#
|
|
7
|
+
# Command-line flags always override what is set here. Every section is
|
|
8
|
+
# optional. See docs/scoring.md for what the thresholds mean.
|
|
9
|
+
|
|
10
|
+
# Checks excluded from the pass/fail gate. They still run and appear in the
|
|
11
|
+
# report — they just cannot fail the build. Use this for checks that don't
|
|
12
|
+
# apply to your API rather than disabling the gate entirely.
|
|
13
|
+
ignore = ["Schema Complexity"]
|
|
14
|
+
|
|
15
|
+
[defaults]
|
|
16
|
+
# Default values for CLI flags, so CI doesn't need a long command line.
|
|
17
|
+
with_generation = false # run the opt-in Sample Generation check
|
|
18
|
+
differential = false # differential mode (implies with_generation)
|
|
19
|
+
model = "claude-sonnet-4-6" # model for Sample Generation
|
|
20
|
+
fail_under = 7 # global gate threshold for checks without
|
|
21
|
+
# a per-check threshold below (0-10)
|
|
22
|
+
|
|
23
|
+
[thresholds]
|
|
24
|
+
# Per-check minimum scores. A check scoring below its threshold fails the
|
|
25
|
+
# run (exit code 1). These take precedence over defaults.fail_under and over
|
|
26
|
+
# the --fail-under CLI flag. Names must match the check names exactly.
|
|
27
|
+
"Format Efficiency" = 9
|
|
28
|
+
"Endpoint Completeness" = 8
|
|
29
|
+
"Example Coverage" = 7
|
|
30
|
+
"Error Documentation" = 7
|
|
31
|
+
"Auth Clarity" = 7
|
|
32
|
+
"Parameter Naming" = 8
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": [
|
|
4
|
+
"Bash(python3 -c \"import sys; sys.path.insert\\(0,'src'\\); import tomllib\")",
|
|
5
|
+
"Bash(python3 -c ' *)",
|
|
6
|
+
"Bash(python3 -m venv .venv)",
|
|
7
|
+
"Bash(.venv/bin/pip install *)",
|
|
8
|
+
"Bash(.venv/bin/python -c \"from artie import SCORING_VERSION; print\\('scoring', SCORING_VERSION\\)\")",
|
|
9
|
+
"Bash(.venv/bin/artie check *)",
|
|
10
|
+
"Read(//tmp/**)",
|
|
11
|
+
"Bash(/home/edgrz/lib/artie-cli/.venv/bin/artie check *)",
|
|
12
|
+
"Bash(echo \"exit=$?\")",
|
|
13
|
+
"Bash(python3 -c \"import json; d=json.load\\(open\\('/tmp/sample.json'\\)\\); print\\('scoring_version:', d.get\\('scoring_version'\\)\\); print\\('checks:', [\\(c['name'],c['score'],c.get\\('informational'\\)\\) for c in d['checks']]\\)\")",
|
|
14
|
+
"Bash(echo \"gate exit=$?\")",
|
|
15
|
+
"Bash(rm /tmp/.artie.toml)",
|
|
16
|
+
"Bash(.venv/bin/python -m pytest -q)",
|
|
17
|
+
"Bash(.venv/bin/artie --version)",
|
|
18
|
+
"Bash(env -u ANTHROPIC_API_KEY .venv/bin/artie check examples/sample-openapi.yaml --with-generation)",
|
|
19
|
+
"Bash(python3 -c \"import json,sys; d=json.load\\(sys.stdin\\); sg=[c for c in d['checks'] if c['name']=='Sample Generation'][0]; print\\('top keys:', sorted\\(d.keys\\(\\)\\)\\); print\\('sample-gen informational/evaluable/score:', sg['informational'], sg['evaluable'], sg['score']\\)\")",
|
|
20
|
+
"Bash(git checkout *)",
|
|
21
|
+
"Bash(git add *)",
|
|
22
|
+
"Bash(.venv/bin/python -m pytest --co -q)"
|
|
23
|
+
]
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
# Run the test suite on every push and pull request. The suite is fully
|
|
4
|
+
# offline — no check here calls the Anthropic API.
|
|
5
|
+
on:
|
|
6
|
+
push:
|
|
7
|
+
pull_request:
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
fail-fast: false
|
|
14
|
+
matrix:
|
|
15
|
+
python-version: ["3.10", "3.11", "3.12"]
|
|
16
|
+
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
|
|
20
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
21
|
+
uses: actions/setup-python@v5
|
|
22
|
+
with:
|
|
23
|
+
python-version: ${{ matrix.python-version }}
|
|
24
|
+
|
|
25
|
+
- name: Install package and dev dependencies
|
|
26
|
+
run: |
|
|
27
|
+
python -m pip install --upgrade pip
|
|
28
|
+
pip install -e ".[dev]"
|
|
29
|
+
|
|
30
|
+
- name: Run tests
|
|
31
|
+
run: pytest
|
|
32
|
+
|
|
33
|
+
- name: Smoke-check the CLI against the sample specs
|
|
34
|
+
run: |
|
|
35
|
+
artie check examples/bookclub-openapi.yaml
|
|
36
|
+
artie check examples/sample-openapi.yaml
|
|
37
|
+
artie check examples/broken-openapi.yaml
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Python bytecode
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
|
|
5
|
+
# Virtual environments
|
|
6
|
+
.venv/
|
|
7
|
+
venv/
|
|
8
|
+
|
|
9
|
+
# Packaging / build
|
|
10
|
+
build/
|
|
11
|
+
dist/
|
|
12
|
+
*.egg-info/
|
|
13
|
+
|
|
14
|
+
# Test and tooling caches
|
|
15
|
+
.pytest_cache/
|
|
16
|
+
.coverage
|
|
17
|
+
htmlcov/
|
|
18
|
+
|
|
19
|
+
# Local artie config (the example template is tracked; a real one is not)
|
|
20
|
+
.artie.toml
|
|
21
|
+
|
|
22
|
+
# Editor noise
|
|
23
|
+
.DS_Store
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
artie-cli scores API documentation for AI readiness. It accepts either a local file or an HTTP URL, detects the format, parses what it can, runs a set of checks against the parsed content, and renders a report. Each check is an independent measurement; there is no aggregate score.
|
|
4
|
+
|
|
5
|
+
## Layout
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
src/artie/
|
|
9
|
+
├── __init__.py Package version and SCORING_VERSION.
|
|
10
|
+
├── __main__.py Lets `python -m artie` run the CLI.
|
|
11
|
+
├── cli.py Typer-based CLI. Owns the request flow and the gate.
|
|
12
|
+
├── config.py Loads .artie.toml: per-check thresholds, ignored
|
|
13
|
+
│ checks, default flags.
|
|
14
|
+
├── baseline.py Loads a saved JSON report for run-over-run deltas.
|
|
15
|
+
├── fetcher.py HTTP fetcher using stdlib urllib. Returns raw content
|
|
16
|
+
│ plus a format hint derived from Content-Type and URL.
|
|
17
|
+
├── generator.py Minimal Anthropic Messages API client (stdlib urllib).
|
|
18
|
+
│ Used by the Sample Generation check. Implements retry
|
|
19
|
+
│ logic for transient errors (429, 503, 529).
|
|
20
|
+
├── parsers/
|
|
21
|
+
│ ├── __init__.py Format detection (detect_format) and the public
|
|
22
|
+
│ │ parse() entry point.
|
|
23
|
+
│ ├── openapi.py OpenAPI YAML and JSON parsing. Also extracts embedded
|
|
24
|
+
│ │ OpenAPI specs from markdown code fences.
|
|
25
|
+
│ └── types.py Endpoint and ParsedDocs dataclasses. Shared by all
|
|
26
|
+
│ checks that need a structured view of the spec.
|
|
27
|
+
├── checks/
|
|
28
|
+
│ ├── __init__.py The ALL_CHECKS registry.
|
|
29
|
+
│ ├── base.py BaseCheck, CheckResult, Severity. The check protocol.
|
|
30
|
+
│ ├── format_efficiency.py
|
|
31
|
+
│ ├── endpoint_completeness.py
|
|
32
|
+
│ ├── example_coverage.py
|
|
33
|
+
│ ├── error_documentation.py
|
|
34
|
+
│ ├── auth_clarity.py
|
|
35
|
+
│ ├── parameter_naming.py
|
|
36
|
+
│ ├── schema_complexity.py
|
|
37
|
+
│ └── generation_quality.py SampleGenerationCheck (opt-in, unscored).
|
|
38
|
+
└── reporters/
|
|
39
|
+
├── __init__.py
|
|
40
|
+
├── terminal.py Rich-based human-readable report.
|
|
41
|
+
└── json_report.py Machine-readable JSON for CI integration.
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Request flow
|
|
45
|
+
|
|
46
|
+
The CLI does the same thing in every invocation:
|
|
47
|
+
|
|
48
|
+
1. Parse arguments. Resolve target as either a file path or an HTTP URL.
|
|
49
|
+
2. Load configuration. `config.load_config` reads a `.artie.toml` (an explicit `--config` path, or auto-discovered from the working directory upward). CLI flags override config defaults; a `None` flag means "not passed". If `--baseline` is given, `baseline.load_baseline` reads the prior JSON report.
|
|
50
|
+
3. Read content. For files, read directly. For URLs, call `fetcher.fetch`, which sends an Accept header preferring structured formats and returns raw bytes, a Content-Type, and a format hint.
|
|
51
|
+
4. Detect format. `parsers.detect_format` combines the URL suffix, the Content-Type hint, and content inspection to return one of: `openapi-yaml`, `openapi-json`, `yaml`, `json`, `markdown`, `html`, `unknown`.
|
|
52
|
+
5. Parse. `parsers.parse` produces a `ParsedDocs`. For OpenAPI inputs this contains the raw dict, the extracted endpoints, and the components map. For markdown, parse scans code fences for an embedded OpenAPI spec; if found, the ParsedDocs is fully populated and `extracted_from` is set to `"markdown"`. For HTML or other unstructured inputs, raw is None and `parse_error` explains why.
|
|
53
|
+
6. Run checks. The CLI instantiates each check in `ALL_CHECKS`, plus `SampleGenerationCheck` constructed with the resolved flags, and calls `check.run(content, format_type, parsed)` on each. Checks decide for themselves whether they can run. If they cannot, they return a not-evaluable result.
|
|
54
|
+
7. Render. Either terminal output via Rich or JSON via `json_report.render`, both passed the baseline for delta reporting.
|
|
55
|
+
8. Apply the gate. `_gate_failed` exits non-zero if any scored, non-ignored check is below its threshold (per-check from config, else the global `--fail-under` or `defaults.fail_under`).
|
|
56
|
+
|
|
57
|
+
## The check protocol
|
|
58
|
+
|
|
59
|
+
Every check subclasses `BaseCheck` and implements `run(content, format_type, parsed) -> CheckResult`. A check returns one of three things:
|
|
60
|
+
|
|
61
|
+
- An evaluable result with a score 0-10 and a severity computed from the score.
|
|
62
|
+
- A not-evaluable result, created by calling `self.not_evaluable(reason)`. Use this when the input does not provide enough information to score honestly. Examples: schema complexity has no `components.schemas` section, endpoint completeness was given markdown with no embedded spec, generation quality has no API key in the environment.
|
|
63
|
+
- An evaluable result with score 0 if the check ran but found nothing worth scoring above zero. Used sparingly. Prefer not-evaluable when the issue is missing information rather than missing quality.
|
|
64
|
+
|
|
65
|
+
`CheckResult.metadata` is a free-form dict. Checks use it to carry machine-readable data that does not belong in findings or recommendations: token counts, evaluation criteria booleans, generated code blocks, baseline scores in differential mode, and so on. The JSON reporter passes metadata through verbatim. The terminal reporter has special-case rendering for `metadata.code`, which it shows as a syntax-highlighted Python block.
|
|
66
|
+
|
|
67
|
+
## Notable design decisions
|
|
68
|
+
|
|
69
|
+
**No aggregate score.** Each check stands alone. We do not currently have empirical evidence for the right weights to combine them. See `docs/scoring.md` for every threshold, its evidence basis, and the rationale for not aggregating.
|
|
70
|
+
|
|
71
|
+
**Stdlib urllib instead of the anthropic SDK.** The generator does one kind of call: a single Messages request, no streaming, no batching. Adding httpx, pydantic, and the SDK would roughly double cold-start time under uvx. The fetcher uses the same approach for the same reason.
|
|
72
|
+
|
|
73
|
+
**Embedded OpenAPI extraction.** Modern docs sites (Mintlify, Fern, ReadMe, Stainless, Scalar) embed the OpenAPI spec inside markdown code fences alongside prose and SDK examples. When the input is markdown, `parsers.openapi._parse_markdown` scans for the first fenced block whose contents parse as OpenAPI and uses that as the spec. The structured checks then run against the embedded spec while Format Efficiency still scores against the full markdown.
|
|
74
|
+
|
|
75
|
+
**Sample Generation is a separate kind of check, and unscored.** It calls an external API, costs money, and produces results that depend partly on the model rather than entirely on the docs. Because of that it is opt-in (`--with-generation`) and deliberately produces no 0-10 score: its `CheckResult` has `informational=True` and `score=None`. The generated code is the deliverable; the five structural criteria are reported as commentary. It accepts a `console` parameter so it can render a spinner during the LLM call, and a `differential` flag that triggers a second baseline call. When that baseline is structurally strong it emits a contamination warning. It is the most complex check and the least empirically grounded. Treat it accordingly.
|
|
76
|
+
|
|
77
|
+
**Informational is a third result state.** Alongside scored and not-evaluable, a `CheckResult` can be `informational`: the check ran and has findings but assigns no score. It is excluded from `is_evaluable` and from the pass/fail gate. Only Sample Generation uses it.
|
|
78
|
+
|
|
79
|
+
**Not-evaluable is a first-class state, not an error.** A check returning N/A is honest: this measurement does not apply to this input. The terminal reporter renders N/A with its own symbol and severity color. The JSON reporter exposes `evaluable: false`. Both are intentional design choices to avoid pressuring checks into producing fake scores when they cannot honestly produce real ones.
|
|
80
|
+
|
|
81
|
+
## Adding a check
|
|
82
|
+
|
|
83
|
+
See `CONTRIBUTING.md`.
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
## Development setup
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
git clone https://github.com/grzetich/artie-cli
|
|
7
|
+
cd artie-cli
|
|
8
|
+
python3 -m venv .venv
|
|
9
|
+
source .venv/bin/activate
|
|
10
|
+
pip install -e ".[dev]"
|
|
11
|
+
artie check examples/bookclub-openapi.yaml
|
|
12
|
+
pytest
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
The static checks and the test suite are fully offline. If you want to exercise the opt-in Sample Generation check, export an Anthropic API key and pass `--with-generation`:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
export ANTHROPIC_API_KEY=sk-ant-...
|
|
19
|
+
artie check examples/bookclub-openapi.yaml --with-generation
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Read `ARCHITECTURE.md` before touching the code. It explains the request flow, the check protocol, and the design decisions that constrain implementation choices.
|
|
23
|
+
|
|
24
|
+
## Running examples
|
|
25
|
+
|
|
26
|
+
Three samples live in `examples/`:
|
|
27
|
+
|
|
28
|
+
- `bookclub-openapi.yaml` is the gold standard. Every check should score 10/10 against it. If a change makes it score lower, the change is suspect.
|
|
29
|
+
- `sample-openapi.yaml` is a minimal spec. Endpoint Completeness scores well, Example Coverage and Auth Clarity do not. Useful for checking middle-of-the-road behavior.
|
|
30
|
+
- `broken-openapi.yaml` is deliberately bad. Most checks score zero or near zero, with named operations and properties in the recommendations. Useful for verifying that low scores produce actionable findings.
|
|
31
|
+
|
|
32
|
+
Run each before and after any scoring change to make sure the change does what you expect.
|
|
33
|
+
|
|
34
|
+
## Writing a new check
|
|
35
|
+
|
|
36
|
+
Subclass `BaseCheck`, implement `run`, register in `checks/__init__.py`.
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
from typing import Any
|
|
40
|
+
|
|
41
|
+
from artie.checks.base import BaseCheck, CheckResult
|
|
42
|
+
from artie.parsers.types import ParsedDocs
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class MyNewCheck(BaseCheck):
|
|
46
|
+
name = "My New Check"
|
|
47
|
+
description = "What this check measures."
|
|
48
|
+
|
|
49
|
+
def run(
|
|
50
|
+
self, content: str, format_type: str, parsed: Any = None
|
|
51
|
+
) -> CheckResult:
|
|
52
|
+
if not isinstance(parsed, ParsedDocs) or not parsed.is_openapi:
|
|
53
|
+
return self.not_evaluable("This check needs a parsed OpenAPI spec.")
|
|
54
|
+
|
|
55
|
+
# Compute the score on a 0-10 scale.
|
|
56
|
+
score = self._compute(parsed)
|
|
57
|
+
severity = self.severity_for(score)
|
|
58
|
+
|
|
59
|
+
return CheckResult(
|
|
60
|
+
name=self.name,
|
|
61
|
+
description=self.description,
|
|
62
|
+
score=score,
|
|
63
|
+
max_score=self.max_score,
|
|
64
|
+
severity=severity,
|
|
65
|
+
findings=["What we observed."],
|
|
66
|
+
recommendations=["What to do about it."],
|
|
67
|
+
metadata={"raw_data": "for JSON consumers"},
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
def _compute(self, parsed: ParsedDocs) -> int:
|
|
71
|
+
...
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Then add it to `ALL_CHECKS` in `checks/__init__.py`. Order matters because it determines display order in the report.
|
|
75
|
+
|
|
76
|
+
Things to honor in your check:
|
|
77
|
+
|
|
78
|
+
- Return `not_evaluable` when the check cannot honestly score the input. Do not return a fake zero or a fake ten just to have a number on the report.
|
|
79
|
+
- Use `self.severity_for(score)` to map the score to a severity bucket. Do not invent your own mapping.
|
|
80
|
+
- Findings are observations. Recommendations are advice. Keep them distinct.
|
|
81
|
+
- Findings should be specific to the input being scored. "5 of 14 endpoints" beats "many endpoints." When pointing at specific problems, name the operations or properties (up to about five examples, truncated with an ellipsis).
|
|
82
|
+
- Metadata is for machine consumers. Put structured data there instead of synthesizing it from finding strings.
|
|
83
|
+
|
|
84
|
+
## Style notes
|
|
85
|
+
|
|
86
|
+
Write the way the existing code reads. Concrete observations, named items, no hedging. Avoid these:
|
|
87
|
+
|
|
88
|
+
- Em or en dashes. Use a comma or split the sentence.
|
|
89
|
+
- "Leverage," "seamless," "robust," "fundamentally," "paradigm," "ecosystem" as metaphor, and the rest of the AI-tells list.
|
|
90
|
+
- Guided-tour phrasings like "Let's look at." Just look at it.
|
|
91
|
+
- Long bolded thesis sentences that summarize a paragraph. Let the paragraph carry its own weight.
|
|
92
|
+
- Sentences clustered on "This." More than two in a row signals laziness.
|
|
93
|
+
|
|
94
|
+
For docstrings, one sentence is usually enough. Longer when the function does something non-obvious. Module docstrings should explain why the module exists and what it produces, not narrate every function.
|
|
95
|
+
|
|
96
|
+
## Scoring conventions
|
|
97
|
+
|
|
98
|
+
Scores are 0-10 integers. The mapping to severity, in `BaseCheck.severity_for`:
|
|
99
|
+
|
|
100
|
+
- 9-10: excellent
|
|
101
|
+
- 7-8: good
|
|
102
|
+
- 4-6: needs work
|
|
103
|
+
- 0-3: poor
|
|
104
|
+
- None: not evaluable
|
|
105
|
+
|
|
106
|
+
If you find yourself wanting a different bucket size or a different scale, that is a sign to add a new check rather than redefine the scale. The convention is shared across all checks so users can read across them.
|
|
107
|
+
|
|
108
|
+
When a check has multiple sub-signals, average them and scale the result to 0-10. When sub-signals do not apply (no path parameters, no request bodies), drop them from the average rather than scoring them zero.
|
|
109
|
+
|
|
110
|
+
Document threshold choices in code comments at the top of the check. Future calibration work needs to know what each threshold currently corresponds to.
|
|
111
|
+
|
|
112
|
+
## Tests
|
|
113
|
+
|
|
114
|
+
Tests live under `tests/` and use pytest. Run them with `pytest` (the
|
|
115
|
+
configuration in `pyproject.toml` handles the source path). The suite is
|
|
116
|
+
fully offline — no test calls the Anthropic API.
|
|
117
|
+
|
|
118
|
+
Current coverage:
|
|
119
|
+
|
|
120
|
+
- Each check's scoring math and the Schema Complexity threshold buckets, including not-evaluable edge cases (`test_checks_scoring.py`).
|
|
121
|
+
- The format detector across all supported formats (`test_format_detector.py`).
|
|
122
|
+
- The OpenAPI parser including embedded extraction from markdown (`test_parser.py`).
|
|
123
|
+
- Sample Generation internals: the gap-marker regex, the identifier builder, and code evaluation (`test_generation_eval.py`, `test_sample_generation.py`).
|
|
124
|
+
- Config loading, baseline deltas, the pass/fail gate, and scoring-version sync (`test_config.py`, `test_baseline.py`, `test_gate.py`, `test_scoring_version.py`).
|
|
125
|
+
|
|
126
|
+
Still uncovered and worth adding: the fetcher's content-type hint logic and
|
|
127
|
+
the generator's retry behavior, both with mocked HTTP responses.
|
|
128
|
+
|
|
129
|
+
`.github/workflows/ci.yml` runs the suite on every push and pull request
|
|
130
|
+
across Python 3.10–3.12.
|
|
131
|
+
|
|
132
|
+
## Versioning
|
|
133
|
+
|
|
134
|
+
Bump the version in `pyproject.toml` and `src/artie/__init__.py` together. They must match. Use semver. A change to the scoring rubric is a minor version bump because it changes user-visible behavior even when code APIs do not change.
|
|
135
|
+
|
|
136
|
+
The scoring rubric is versioned separately from the package. `artie.SCORING_VERSION` is the runtime source of truth; `pyproject.toml [tool.artie] scoring_version` mirrors it, and `tests/test_scoring_version.py` fails if the two drift. Bump it whenever a threshold, bucket, or weighting changes, and update `docs/scoring.md` in the same commit. It is emitted in JSON output so downstream tooling can pin against it.
|
|
137
|
+
|
|
138
|
+
## Pull requests
|
|
139
|
+
|
|
140
|
+
Keep them focused. One check, one bug fix, one CI feature per PR. If you find yourself wanting to do unrelated cleanups, open a separate PR for those.
|
|
141
|
+
|
|
142
|
+
Run the three example specs before and after your change. Include the diff in the PR description when scores change. If a sample's score moves, explain why the new score is more correct than the old one.
|
artie_cli-0.7.0/PKG-INFO
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: artie-cli
|
|
3
|
+
Version: 0.7.0
|
|
4
|
+
Summary: Score your API documentation for AI-readiness. Based on the Tokens Not Jokin' research.
|
|
5
|
+
Project-URL: Homepage, https://artie.fm
|
|
6
|
+
Project-URL: Repository, https://github.com/grzetich/artie-cli
|
|
7
|
+
Project-URL: Research, https://leanpub.com/tokensnotjokin
|
|
8
|
+
Author-email: Ed Grzetich <ed@grzeti.ch>
|
|
9
|
+
License: MIT
|
|
10
|
+
Keywords: ai,api,developer-tools,documentation,llm,openapi
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Documentation
|
|
20
|
+
Classifier: Topic :: Software Development :: Documentation
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Requires-Dist: pyyaml>=6.0
|
|
23
|
+
Requires-Dist: rich>=13.0.0
|
|
24
|
+
Requires-Dist: tiktoken>=0.7.0
|
|
25
|
+
Requires-Dist: tomli>=2.0; python_version < '3.11'
|
|
26
|
+
Requires-Dist: typer>=0.12.0
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# artie-cli
|
|
32
|
+
|
|
33
|
+
Score your API documentation for AI-readiness.
|
|
34
|
+
|
|
35
|
+
artie reads your API docs the way an AI agent would, then reports how well your documentation actually supports code generation. The checks are derived from [*Tokens Not Jokin'*](https://leanpub.com/tokensnotjokin), a 21,462-test empirical study comparing four AI models against four documentation formats.
|
|
36
|
+
|
|
37
|
+
artie measures and reports. It does not convert, clean, or modify your documentation.
|
|
38
|
+
|
|
39
|
+
## Quickstart
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
# No install required
|
|
43
|
+
uvx artie-cli check ./openapi.yaml
|
|
44
|
+
uvx artie-cli check https://api.example.com/openapi.yaml
|
|
45
|
+
|
|
46
|
+
# Install with pipx
|
|
47
|
+
pipx install artie-cli
|
|
48
|
+
artie check ./openapi.yaml
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
By default artie runs the seven static checks, entirely offline. The optional Sample Generation check is opt-in: pass `--with-generation` and set an Anthropic API key.
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
export ANTHROPIC_API_KEY=sk-ant-...
|
|
55
|
+
artie check ./openapi.yaml --with-generation
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Without `--with-generation`, Sample Generation shows as N/A with a note on how to enable it.
|
|
59
|
+
|
|
60
|
+
## What it checks
|
|
61
|
+
|
|
62
|
+
**Seven static checks**, derived from the TNJ research:
|
|
63
|
+
|
|
64
|
+
- **Format Efficiency**: how token-efficient your documentation format is
|
|
65
|
+
- **Endpoint Completeness**: descriptions, operationIds, and documented path parameters
|
|
66
|
+
- **Example Coverage**: request and response body examples
|
|
67
|
+
- **Error Documentation**: 4xx and 5xx responses with meaningful descriptions and content schemas
|
|
68
|
+
- **Auth Clarity**: security schemes defined, described, and applied to operations
|
|
69
|
+
- **Parameter Naming**: consistent naming convention across parameters and schema properties
|
|
70
|
+
- **Schema Complexity**: nesting depth across component schemas
|
|
71
|
+
|
|
72
|
+
Each static check scores 0–10. Every threshold and weighting is documented in [docs/scoring.md](docs/scoring.md), along with which numbers are empirically grounded and which are heuristics awaiting calibration. artie deliberately reports no aggregate score.
|
|
73
|
+
|
|
74
|
+
**One optional, opt-in check**, applied to any input format:
|
|
75
|
+
|
|
76
|
+
- **Sample Generation**: an AI model is given your docs and asked to write a Python function that calls the API. The generated code is the deliverable — artie includes it in the report so you can see exactly what an agent writes from your docs, and comments on its structure (valid syntax, HTTP client import, error handling, request construction, response handling). It is **not scored**: one generation against one model is a sample, not a measurement. Enable it with `--with-generation`.
|
|
77
|
+
|
|
78
|
+
The static checks tell you what specifically to fix. Sample Generation shows you a concrete example of what an agent produces. Static checks return N/A when the input isn't OpenAPI; Sample Generation runs against anything.
|
|
79
|
+
|
|
80
|
+
## Inputs
|
|
81
|
+
|
|
82
|
+
artie accepts either a local file or an HTTP/HTTPS URL:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
artie check ./openapi.yaml
|
|
86
|
+
artie check https://api.example.com/openapi.yaml
|
|
87
|
+
artie check https://docs.example.com/getting-started
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Supported formats: OpenAPI YAML, OpenAPI JSON, plain YAML, plain JSON, Markdown, HTML.
|
|
91
|
+
|
|
92
|
+
When the input is markdown, artie scans for an OpenAPI spec embedded in a code fence and runs the structured checks against that spec. Modern docs sites (Mintlify, Fern, ReadMe, Stainless) embed the spec inline alongside prose and SDK examples, and artie picks it up automatically.
|
|
93
|
+
|
|
94
|
+
When fetching URLs, artie sends an `Accept` header that requests structured formats first. If the server honors content negotiation (some major docs sites do, including parts of AWS and most Mintlify-hosted sites), you may receive a different format than the URL suggests. The report calls this out.
|
|
95
|
+
|
|
96
|
+
## Output formats
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
# Pretty terminal output (default); static checks only, fully offline
|
|
100
|
+
artie check ./openapi.yaml
|
|
101
|
+
|
|
102
|
+
# JSON for CI pipelines (includes scoring_version)
|
|
103
|
+
artie check ./openapi.yaml --output json
|
|
104
|
+
|
|
105
|
+
# Fail the build if any check scores below 7
|
|
106
|
+
artie check ./openapi.yaml --fail-under 7
|
|
107
|
+
|
|
108
|
+
# Compare against a previously saved JSON report and show per-check deltas
|
|
109
|
+
artie check ./openapi.yaml --baseline previous.json
|
|
110
|
+
|
|
111
|
+
# Run the opt-in Sample Generation check (uses the Anthropic API)
|
|
112
|
+
artie check ./openapi.yaml --with-generation
|
|
113
|
+
|
|
114
|
+
# Differential mode: flag training-data contamination (implies --with-generation, doubles cost)
|
|
115
|
+
artie check ./openapi.yaml --differential
|
|
116
|
+
|
|
117
|
+
# Use a different model for Sample Generation
|
|
118
|
+
artie check ./openapi.yaml --with-generation --model claude-opus-4-7
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Configuration
|
|
122
|
+
|
|
123
|
+
Drop a `.artie.toml` in your repository root to pin per-check pass/fail thresholds, exclude checks from the gate, and set default flags. artie discovers it automatically from the working directory upward. See [`.artie.toml.example`](.artie.toml.example) for the full schema and [docs/scoring.md](docs/scoring.md) for what the thresholds mean.
|
|
124
|
+
|
|
125
|
+
## About Sample Generation and training contamination
|
|
126
|
+
|
|
127
|
+
Sample Generation has a real limitation worth understanding. When the model has seen an API during training (which is the case for AWS, Stripe, GitHub, and most public APIs with Python SDKs on PyPI), it can write working code from training alone, regardless of how complete the docs you're testing are. Good-looking code for a famous API tells you the model knows the API, not that the docs are good.
|
|
128
|
+
|
|
129
|
+
This is exactly why the check is unscored: a grade would imply a measurement the method can't honestly deliver. Instead artie shows you the generated code and comments on its structure.
|
|
130
|
+
|
|
131
|
+
The prompt instructs the model to use only information from the docs and to flag gaps in inline code comments. artie detects those gap comments automatically and reports them as evidence of real documentation deficiencies.
|
|
132
|
+
|
|
133
|
+
`--differential` mode adds a second API call with no docs body, measuring what the model produces from training alone. When that baseline is already strong, artie prints a contamination warning: the docs-informed sample reflects model capability, not docs quality. Differential mode is most informative for novel or internal APIs the model has not seen.
|
|
134
|
+
|
|
135
|
+
## Cost
|
|
136
|
+
|
|
137
|
+
Sample Generation makes one Anthropic API call per run (two with `--differential`). On Claude Sonnet 4.6 (the default), a typical docs page costs roughly $0.02 to $0.05 per run at retail pricing. Because the check is opt-in, CI runs stay free and offline unless you explicitly ask for it.
|
|
138
|
+
|
|
139
|
+
## Examples
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
artie check examples/bookclub-openapi.yaml
|
|
143
|
+
artie check examples/broken-openapi.yaml
|
|
144
|
+
artie check examples/sample-openapi.yaml
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Privacy
|
|
148
|
+
|
|
149
|
+
The static checks run entirely locally. Sample Generation sends your documentation content to Anthropic's API — but it is opt-in, so nothing leaves your machine unless you pass `--with-generation`.
|
|
150
|
+
|
|
151
|
+
## Why a CLI
|
|
152
|
+
|
|
153
|
+
artie is a checker, not a converter. CLI tools live where the docs do: in the repo, in the pipeline, next to the spec. You get a score, you act on it, the static-check data never leaves your machine.
|
|
154
|
+
|
|
155
|
+
## Research
|
|
156
|
+
|
|
157
|
+
artie's checks are grounded in empirical findings published in *Tokens Not Jokin'*. Key results:
|
|
158
|
+
|
|
159
|
+
- YAML uses up to 80% fewer tokens than OpenAPI 3.0 JSON
|
|
160
|
+
- Documentation format explains more than 10x the variance in generated code quality than model choice
|
|
161
|
+
- Disciplined error documentation produces dramatically better error handling in generated code
|
|
162
|
+
|
|
163
|
+
The Sample Generation check uses the same methodology as TNJ, applied per-spec: ask an AI to write code from these docs, then show what it produced.
|
|
164
|
+
|
|
165
|
+
Buy the book: [leanpub.com/tokensnotjokin](https://leanpub.com/tokensnotjokin)
|
|
166
|
+
|
|
167
|
+
## License
|
|
168
|
+
|
|
169
|
+
MIT
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# artie-cli
|
|
2
|
+
|
|
3
|
+
Score your API documentation for AI-readiness.
|
|
4
|
+
|
|
5
|
+
artie reads your API docs the way an AI agent would, then reports how well your documentation actually supports code generation. The checks are derived from [*Tokens Not Jokin'*](https://leanpub.com/tokensnotjokin), a 21,462-test empirical study comparing four AI models against four documentation formats.
|
|
6
|
+
|
|
7
|
+
artie measures and reports. It does not convert, clean, or modify your documentation.
|
|
8
|
+
|
|
9
|
+
## Quickstart
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# No install required
|
|
13
|
+
uvx artie-cli check ./openapi.yaml
|
|
14
|
+
uvx artie-cli check https://api.example.com/openapi.yaml
|
|
15
|
+
|
|
16
|
+
# Install with pipx
|
|
17
|
+
pipx install artie-cli
|
|
18
|
+
artie check ./openapi.yaml
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
By default artie runs the seven static checks, entirely offline. The optional Sample Generation check is opt-in: pass `--with-generation` and set an Anthropic API key.
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
export ANTHROPIC_API_KEY=sk-ant-...
|
|
25
|
+
artie check ./openapi.yaml --with-generation
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Without `--with-generation`, Sample Generation shows as N/A with a note on how to enable it.
|
|
29
|
+
|
|
30
|
+
## What it checks
|
|
31
|
+
|
|
32
|
+
**Seven static checks**, derived from the TNJ research:
|
|
33
|
+
|
|
34
|
+
- **Format Efficiency**: how token-efficient your documentation format is
|
|
35
|
+
- **Endpoint Completeness**: descriptions, operationIds, and documented path parameters
|
|
36
|
+
- **Example Coverage**: request and response body examples
|
|
37
|
+
- **Error Documentation**: 4xx and 5xx responses with meaningful descriptions and content schemas
|
|
38
|
+
- **Auth Clarity**: security schemes defined, described, and applied to operations
|
|
39
|
+
- **Parameter Naming**: consistent naming convention across parameters and schema properties
|
|
40
|
+
- **Schema Complexity**: nesting depth across component schemas
|
|
41
|
+
|
|
42
|
+
Each static check scores 0–10. Every threshold and weighting is documented in [docs/scoring.md](docs/scoring.md), along with which numbers are empirically grounded and which are heuristics awaiting calibration. artie deliberately reports no aggregate score.
|
|
43
|
+
|
|
44
|
+
**One optional, opt-in check**, applied to any input format:
|
|
45
|
+
|
|
46
|
+
- **Sample Generation**: an AI model is given your docs and asked to write a Python function that calls the API. The generated code is the deliverable — artie includes it in the report so you can see exactly what an agent writes from your docs, and comments on its structure (valid syntax, HTTP client import, error handling, request construction, response handling). It is **not scored**: one generation against one model is a sample, not a measurement. Enable it with `--with-generation`.
|
|
47
|
+
|
|
48
|
+
The static checks tell you what specifically to fix. Sample Generation shows you a concrete example of what an agent produces. Static checks return N/A when the input isn't OpenAPI; Sample Generation runs against anything.
|
|
49
|
+
|
|
50
|
+
## Inputs
|
|
51
|
+
|
|
52
|
+
artie accepts either a local file or an HTTP/HTTPS URL:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
artie check ./openapi.yaml
|
|
56
|
+
artie check https://api.example.com/openapi.yaml
|
|
57
|
+
artie check https://docs.example.com/getting-started
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Supported formats: OpenAPI YAML, OpenAPI JSON, plain YAML, plain JSON, Markdown, HTML.
|
|
61
|
+
|
|
62
|
+
When the input is markdown, artie scans for an OpenAPI spec embedded in a code fence and runs the structured checks against that spec. Modern docs sites (Mintlify, Fern, ReadMe, Stainless) embed the spec inline alongside prose and SDK examples, and artie picks it up automatically.
|
|
63
|
+
|
|
64
|
+
When fetching URLs, artie sends an `Accept` header that requests structured formats first. If the server honors content negotiation (some major docs sites do, including parts of AWS and most Mintlify-hosted sites), you may receive a different format than the URL suggests. The report calls this out.
|
|
65
|
+
|
|
66
|
+
## Output formats
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
# Pretty terminal output (default); static checks only, fully offline
|
|
70
|
+
artie check ./openapi.yaml
|
|
71
|
+
|
|
72
|
+
# JSON for CI pipelines (includes scoring_version)
|
|
73
|
+
artie check ./openapi.yaml --output json
|
|
74
|
+
|
|
75
|
+
# Fail the build if any check scores below 7
|
|
76
|
+
artie check ./openapi.yaml --fail-under 7
|
|
77
|
+
|
|
78
|
+
# Compare against a previously saved JSON report and show per-check deltas
|
|
79
|
+
artie check ./openapi.yaml --baseline previous.json
|
|
80
|
+
|
|
81
|
+
# Run the opt-in Sample Generation check (uses the Anthropic API)
|
|
82
|
+
artie check ./openapi.yaml --with-generation
|
|
83
|
+
|
|
84
|
+
# Differential mode: flag training-data contamination (implies --with-generation, doubles cost)
|
|
85
|
+
artie check ./openapi.yaml --differential
|
|
86
|
+
|
|
87
|
+
# Use a different model for Sample Generation
|
|
88
|
+
artie check ./openapi.yaml --with-generation --model claude-opus-4-7
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Configuration
|
|
92
|
+
|
|
93
|
+
Drop a `.artie.toml` in your repository root to pin per-check pass/fail thresholds, exclude checks from the gate, and set default flags. artie discovers it automatically from the working directory upward. See [`.artie.toml.example`](.artie.toml.example) for the full schema and [docs/scoring.md](docs/scoring.md) for what the thresholds mean.
|
|
94
|
+
|
|
95
|
+
## About Sample Generation and training contamination
|
|
96
|
+
|
|
97
|
+
Sample Generation has a real limitation worth understanding. When the model has seen an API during training (which is the case for AWS, Stripe, GitHub, and most public APIs with Python SDKs on PyPI), it can write working code from training alone, regardless of how complete the docs you're testing are. Good-looking code for a famous API tells you the model knows the API, not that the docs are good.
|
|
98
|
+
|
|
99
|
+
This is exactly why the check is unscored: a grade would imply a measurement the method can't honestly deliver. Instead artie shows you the generated code and comments on its structure.
|
|
100
|
+
|
|
101
|
+
The prompt instructs the model to use only information from the docs and to flag gaps in inline code comments. artie detects those gap comments automatically and reports them as evidence of real documentation deficiencies.
|
|
102
|
+
|
|
103
|
+
`--differential` mode adds a second API call with no docs body, measuring what the model produces from training alone. When that baseline is already strong, artie prints a contamination warning: the docs-informed sample reflects model capability, not docs quality. Differential mode is most informative for novel or internal APIs the model has not seen.
|
|
104
|
+
|
|
105
|
+
## Cost
|
|
106
|
+
|
|
107
|
+
Sample Generation makes one Anthropic API call per run (two with `--differential`). On Claude Sonnet 4.6 (the default), a typical docs page costs roughly $0.02 to $0.05 per run at retail pricing. Because the check is opt-in, CI runs stay free and offline unless you explicitly ask for it.
|
|
108
|
+
|
|
109
|
+
## Examples
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
artie check examples/bookclub-openapi.yaml
|
|
113
|
+
artie check examples/broken-openapi.yaml
|
|
114
|
+
artie check examples/sample-openapi.yaml
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Privacy
|
|
118
|
+
|
|
119
|
+
The static checks run entirely locally. Sample Generation sends your documentation content to Anthropic's API — but it is opt-in, so nothing leaves your machine unless you pass `--with-generation`.
|
|
120
|
+
|
|
121
|
+
## Why a CLI
|
|
122
|
+
|
|
123
|
+
artie is a checker, not a converter. CLI tools live where the docs do: in the repo, in the pipeline, next to the spec. You get a score, you act on it, the static-check data never leaves your machine.
|
|
124
|
+
|
|
125
|
+
## Research
|
|
126
|
+
|
|
127
|
+
artie's checks are grounded in empirical findings published in *Tokens Not Jokin'*. Key results:
|
|
128
|
+
|
|
129
|
+
- YAML uses up to 80% fewer tokens than OpenAPI 3.0 JSON
|
|
130
|
+
- Documentation format explains more than 10x the variance in generated code quality than model choice
|
|
131
|
+
- Disciplined error documentation produces dramatically better error handling in generated code
|
|
132
|
+
|
|
133
|
+
The Sample Generation check uses the same methodology as TNJ, applied per-spec: ask an AI to write code from these docs, then show what it produced.
|
|
134
|
+
|
|
135
|
+
Buy the book: [leanpub.com/tokensnotjokin](https://leanpub.com/tokensnotjokin)
|
|
136
|
+
|
|
137
|
+
## License
|
|
138
|
+
|
|
139
|
+
MIT
|