backstop-ai 0.6.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.
- backstop_ai-0.6.0/.github/ISSUE_TEMPLATE/benchmark_report.yml +26 -0
- backstop_ai-0.6.0/.github/ISSUE_TEMPLATE/bug_report.yml +42 -0
- backstop_ai-0.6.0/.github/ISSUE_TEMPLATE/feature_request.yml +37 -0
- backstop_ai-0.6.0/.github/ISSUE_TEMPLATE/provider_compatibility.yml +37 -0
- backstop_ai-0.6.0/.github/PULL_REQUEST_TEMPLATE.md +22 -0
- backstop_ai-0.6.0/.github/workflows/ci.yml +88 -0
- backstop_ai-0.6.0/.gitignore +31 -0
- backstop_ai-0.6.0/=0.40 +0 -0
- backstop_ai-0.6.0/CHANGELOG.md +190 -0
- backstop_ai-0.6.0/CODE_OF_CONDUCT.md +29 -0
- backstop_ai-0.6.0/CONTRIBUTING.md +43 -0
- backstop_ai-0.6.0/LICENSE.txt +21 -0
- backstop_ai-0.6.0/PKG-INFO +308 -0
- backstop_ai-0.6.0/README.md +263 -0
- backstop_ai-0.6.0/SECURITY.md +36 -0
- backstop_ai-0.6.0/benchmarks/local_overhead.py +150 -0
- backstop_ai-0.6.0/demo.gif +0 -0
- backstop_ai-0.6.0/docs/architecture.md +94 -0
- backstop_ai-0.6.0/docs/assets/backstop-logo.svg +15 -0
- backstop_ai-0.6.0/docs/benchmark-results-2026-07-20.md +30 -0
- backstop_ai-0.6.0/docs/benchmarks.md +46 -0
- backstop_ai-0.6.0/docs/compatibility.md +84 -0
- backstop_ai-0.6.0/docs/concurrency.md +82 -0
- backstop_ai-0.6.0/docs/dashboard.md +137 -0
- backstop_ai-0.6.0/docs/install.md +170 -0
- backstop_ai-0.6.0/docs/quickstart.md +68 -0
- backstop_ai-0.6.0/docs/sdk-matrix.md +51 -0
- backstop_ai-0.6.0/docs/threat-model.md +85 -0
- backstop_ai-0.6.0/examples/agent_loop_guard.py +77 -0
- backstop_ai-0.6.0/examples/anthropic_async.py +33 -0
- backstop_ai-0.6.0/examples/anthropic_budget.py +71 -0
- backstop_ai-0.6.0/examples/anthropic_sync.py +32 -0
- backstop_ai-0.6.0/examples/background_priority.py +38 -0
- backstop_ai-0.6.0/examples/basic.py +27 -0
- backstop_ai-0.6.0/examples/budget_blocking_demo.py +66 -0
- backstop_ai-0.6.0/examples/fastapi_tenants.py +56 -0
- backstop_ai-0.6.0/examples/openai_async.py +32 -0
- backstop_ai-0.6.0/examples/openai_sync.py +31 -0
- backstop_ai-0.6.0/examples/prometheus_metrics.py +13 -0
- backstop_ai-0.6.0/examples/wedge_basic.py +63 -0
- backstop_ai-0.6.0/examples/wedge_openai.py +62 -0
- backstop_ai-0.6.0/fix_wrapper.py +45 -0
- backstop_ai-0.6.0/install.sh +136 -0
- backstop_ai-0.6.0/llms.txt +79 -0
- backstop_ai-0.6.0/observability/prometheus-alerts.yml +38 -0
- backstop_ai-0.6.0/proofs/README.md +43 -0
- backstop_ai-0.6.0/proofs/proof_budget_exhaustion.py +110 -0
- backstop_ai-0.6.0/proofs/proof_budget_webhook.py +92 -0
- backstop_ai-0.6.0/proofs/proof_hierarchical.py +60 -0
- backstop_ai-0.6.0/proofs/proof_multi_agent_isolation.py +100 -0
- backstop_ai-0.6.0/proofs/proof_real_overhead.py +132 -0
- backstop_ai-0.6.0/proofs/proof_secrets.py +59 -0
- backstop_ai-0.6.0/proofs/proof_semantic_cache.py +153 -0
- backstop_ai-0.6.0/proofs/run_all.py +166 -0
- backstop_ai-0.6.0/proofs/wedge_e2e_demo.py +101 -0
- backstop_ai-0.6.0/pyproject.toml +72 -0
- backstop_ai-0.6.0/src/backstop/__init__.py +43 -0
- backstop_ai-0.6.0/src/backstop/__main__.py +4 -0
- backstop_ai-0.6.0/src/backstop/_httpcompat.py +119 -0
- backstop_ai-0.6.0/src/backstop/adapters/__init__.py +73 -0
- backstop_ai-0.6.0/src/backstop/admission.py +114 -0
- backstop_ai-0.6.0/src/backstop/agent_guard.py +54 -0
- backstop_ai-0.6.0/src/backstop/aimd.py +61 -0
- backstop_ai-0.6.0/src/backstop/audit.py +79 -0
- backstop_ai-0.6.0/src/backstop/audit_sink.py +212 -0
- backstop_ai-0.6.0/src/backstop/budget.py +79 -0
- backstop_ai-0.6.0/src/backstop/cache.py +157 -0
- backstop_ai-0.6.0/src/backstop/circuit.py +85 -0
- backstop_ai-0.6.0/src/backstop/cli.py +546 -0
- backstop_ai-0.6.0/src/backstop/config.py +313 -0
- backstop_ai-0.6.0/src/backstop/cost.py +36 -0
- backstop_ai-0.6.0/src/backstop/dashboard_app.py +381 -0
- backstop_ai-0.6.0/src/backstop/dashboard_demo.py +386 -0
- backstop_ai-0.6.0/src/backstop/dashboard_ui.py +1356 -0
- backstop_ai-0.6.0/src/backstop/demo.py +347 -0
- backstop_ai-0.6.0/src/backstop/exceptions.py +77 -0
- backstop_ai-0.6.0/src/backstop/extract.py +248 -0
- backstop_ai-0.6.0/src/backstop/forecast.py +62 -0
- backstop_ai-0.6.0/src/backstop/gateway.py +117 -0
- backstop_ai-0.6.0/src/backstop/harness.py +225 -0
- backstop_ai-0.6.0/src/backstop/hierarchical.py +167 -0
- backstop_ai-0.6.0/src/backstop/hooks.py +29 -0
- backstop_ai-0.6.0/src/backstop/latency.py +78 -0
- backstop_ai-0.6.0/src/backstop/ledger.py +112 -0
- backstop_ai-0.6.0/src/backstop/limiter.py +38 -0
- backstop_ai-0.6.0/src/backstop/metrics.py +154 -0
- backstop_ai-0.6.0/src/backstop/notifications.py +188 -0
- backstop_ai-0.6.0/src/backstop/otel.py +179 -0
- backstop_ai-0.6.0/src/backstop/pricing.py +299 -0
- backstop_ai-0.6.0/src/backstop/quotas.py +86 -0
- backstop_ai-0.6.0/src/backstop/real_anthropic.py +170 -0
- backstop_ai-0.6.0/src/backstop/real_openai.py +202 -0
- backstop_ai-0.6.0/src/backstop/retry.py +17 -0
- backstop_ai-0.6.0/src/backstop/rollout.py +106 -0
- backstop_ai-0.6.0/src/backstop/secrets.py +66 -0
- backstop_ai-0.6.0/src/backstop/state.py +73 -0
- backstop_ai-0.6.0/src/backstop/state_backends.py +201 -0
- backstop_ai-0.6.0/src/backstop/streaming.py +196 -0
- backstop_ai-0.6.0/src/backstop/telemetry.py +833 -0
- backstop_ai-0.6.0/src/backstop/transports.py +1092 -0
- backstop_ai-0.6.0/src/backstop/verify.py +705 -0
- backstop_ai-0.6.0/src/backstop/wrapper.py +314 -0
- backstop_ai-0.6.0/src/backstop/wrapper.py.backup +245 -0
- backstop_ai-0.6.0/src/backstop/wrapper.py.backup2 +253 -0
- backstop_ai-0.6.0/src/wedge/__init__.py +2 -0
- backstop_ai-0.6.0/src/wedge/cli.py +87 -0
- backstop_ai-0.6.0/src/wedge/diff_engine.py +126 -0
- backstop_ai-0.6.0/src/wedge/report.py +80 -0
- backstop_ai-0.6.0/src/wedge/runner.py +563 -0
- backstop_ai-0.6.0/tests/test_audit_sink.py +72 -0
- backstop_ai-0.6.0/tests/test_budget.py +43 -0
- backstop_ai-0.6.0/tests/test_budget_stress.py +111 -0
- backstop_ai-0.6.0/tests/test_cache_gzip_replay.py +79 -0
- backstop_ai-0.6.0/tests/test_cache_semantic.py +110 -0
- backstop_ai-0.6.0/tests/test_cli.py +97 -0
- backstop_ai-0.6.0/tests/test_config.py +26 -0
- backstop_ai-0.6.0/tests/test_dashboard_wsgi.py +273 -0
- backstop_ai-0.6.0/tests/test_deep_research.py +408 -0
- backstop_ai-0.6.0/tests/test_demo.py +105 -0
- backstop_ai-0.6.0/tests/test_extract.py +163 -0
- backstop_ai-0.6.0/tests/test_fallback_chain.py +291 -0
- backstop_ai-0.6.0/tests/test_guardrail_visibility.py +51 -0
- backstop_ai-0.6.0/tests/test_hierarchical.py +150 -0
- backstop_ai-0.6.0/tests/test_installer.py +49 -0
- backstop_ai-0.6.0/tests/test_ledger.py +227 -0
- backstop_ai-0.6.0/tests/test_notifications.py +97 -0
- backstop_ai-0.6.0/tests/test_otel.py +57 -0
- backstop_ai-0.6.0/tests/test_real_anthropic.py +52 -0
- backstop_ai-0.6.0/tests/test_real_openai.py +39 -0
- backstop_ai-0.6.0/tests/test_retry_aimd_circuit.py +60 -0
- backstop_ai-0.6.0/tests/test_secrets.py +92 -0
- backstop_ai-0.6.0/tests/test_streaming_budget.py +165 -0
- backstop_ai-0.6.0/tests/test_telemetry.py +499 -0
- backstop_ai-0.6.0/tests/test_transport.py +235 -0
- backstop_ai-0.6.0/tests/test_verify.py +149 -0
- backstop_ai-0.6.0/tests/test_wrapper.py +201 -0
- backstop_ai-0.6.0/tests/wedge/test_diff_engine.py +48 -0
- backstop_ai-0.6.0/tests/wedge/test_report.py +26 -0
- backstop_ai-0.6.0/tests/wedge/test_runner_offline.py +285 -0
- backstop_ai-0.6.0/ts/backstop/README.md +65 -0
- backstop_ai-0.6.0/ts/backstop/package-lock.json +604 -0
- backstop_ai-0.6.0/ts/backstop/package.json +37 -0
- backstop_ai-0.6.0/ts/backstop/src/admission.ts +162 -0
- backstop_ai-0.6.0/ts/backstop/src/agent.ts +38 -0
- backstop_ai-0.6.0/ts/backstop/src/audit.ts +70 -0
- backstop_ai-0.6.0/ts/backstop/src/budget.ts +104 -0
- backstop_ai-0.6.0/ts/backstop/src/cache.ts +123 -0
- backstop_ai-0.6.0/ts/backstop/src/circuit.ts +52 -0
- backstop_ai-0.6.0/ts/backstop/src/forecast.ts +18 -0
- backstop_ai-0.6.0/ts/backstop/src/index.ts +21 -0
- backstop_ai-0.6.0/ts/backstop/src/quotas.ts +33 -0
- backstop_ai-0.6.0/ts/backstop/src/types.ts +118 -0
- backstop_ai-0.6.0/ts/backstop/src/wrap.ts +250 -0
- backstop_ai-0.6.0/ts/backstop/tests/features.test.ts +110 -0
- backstop_ai-0.6.0/ts/backstop/tests/wrap.test.ts +134 -0
- backstop_ai-0.6.0/ts/backstop/tsconfig.json +17 -0
- backstop_ai-0.6.0/wedge-test-fixture/README.md +12 -0
- backstop_ai-0.6.0/wedge-test-fixture/src/main.py +25 -0
- backstop_ai-0.6.0/wedge-test-fixture/task.yaml +12 -0
- backstop_ai-0.6.0/wedge-test-fixture/tests/test_main.py +45 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
name: Benchmark report
|
|
2
|
+
description: Share benchmark results or report benchmark issues
|
|
3
|
+
title: "[Benchmark]: "
|
|
4
|
+
labels: ["benchmark"]
|
|
5
|
+
body:
|
|
6
|
+
- type: textarea
|
|
7
|
+
id: command
|
|
8
|
+
attributes:
|
|
9
|
+
label: Command
|
|
10
|
+
render: shell
|
|
11
|
+
validations:
|
|
12
|
+
required: true
|
|
13
|
+
- type: textarea
|
|
14
|
+
id: environment
|
|
15
|
+
attributes:
|
|
16
|
+
label: Environment
|
|
17
|
+
description: OS, Python version, CPU, provider SDK versions, and whether a local mock provider or real provider was used.
|
|
18
|
+
validations:
|
|
19
|
+
required: true
|
|
20
|
+
- type: textarea
|
|
21
|
+
id: results
|
|
22
|
+
attributes:
|
|
23
|
+
label: Results
|
|
24
|
+
render: text
|
|
25
|
+
validations:
|
|
26
|
+
required: true
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
name: Bug report
|
|
2
|
+
description: Report a Backstop bug
|
|
3
|
+
title: "[Bug]: "
|
|
4
|
+
labels: ["bug"]
|
|
5
|
+
body:
|
|
6
|
+
- type: textarea
|
|
7
|
+
id: summary
|
|
8
|
+
attributes:
|
|
9
|
+
label: Summary
|
|
10
|
+
description: What happened?
|
|
11
|
+
validations:
|
|
12
|
+
required: true
|
|
13
|
+
- type: textarea
|
|
14
|
+
id: reproduce
|
|
15
|
+
attributes:
|
|
16
|
+
label: Reproduction
|
|
17
|
+
description: Minimal code or commands that reproduce the issue.
|
|
18
|
+
render: python
|
|
19
|
+
validations:
|
|
20
|
+
required: true
|
|
21
|
+
- type: input
|
|
22
|
+
id: version
|
|
23
|
+
attributes:
|
|
24
|
+
label: Backstop version or commit
|
|
25
|
+
validations:
|
|
26
|
+
required: true
|
|
27
|
+
- type: input
|
|
28
|
+
id: provider
|
|
29
|
+
attributes:
|
|
30
|
+
label: Provider SDK and version
|
|
31
|
+
placeholder: openai 1.x, anthropic 0.x
|
|
32
|
+
- type: textarea
|
|
33
|
+
id: expected
|
|
34
|
+
attributes:
|
|
35
|
+
label: Expected behavior
|
|
36
|
+
validations:
|
|
37
|
+
required: true
|
|
38
|
+
- type: textarea
|
|
39
|
+
id: notes
|
|
40
|
+
attributes:
|
|
41
|
+
label: Notes
|
|
42
|
+
description: Do not include API keys, real prompts, real responses, or customer data.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
name: Feature request
|
|
2
|
+
description: Suggest an improvement to Backstop
|
|
3
|
+
title: "[Feature]: "
|
|
4
|
+
labels: ["enhancement"]
|
|
5
|
+
body:
|
|
6
|
+
- type: textarea
|
|
7
|
+
id: problem
|
|
8
|
+
attributes:
|
|
9
|
+
label: Problem
|
|
10
|
+
description: What LLM spend, latency, reliability, or provider-control problem does this solve?
|
|
11
|
+
validations:
|
|
12
|
+
required: true
|
|
13
|
+
- type: textarea
|
|
14
|
+
id: proposal
|
|
15
|
+
attributes:
|
|
16
|
+
label: Proposal
|
|
17
|
+
description: What should Backstop do?
|
|
18
|
+
validations:
|
|
19
|
+
required: true
|
|
20
|
+
- type: dropdown
|
|
21
|
+
id: area
|
|
22
|
+
attributes:
|
|
23
|
+
label: Area
|
|
24
|
+
options:
|
|
25
|
+
- Budget enforcement
|
|
26
|
+
- Distributed state
|
|
27
|
+
- Provider compatibility
|
|
28
|
+
- Metrics or telemetry
|
|
29
|
+
- Retry or circuit breaking
|
|
30
|
+
- Documentation
|
|
31
|
+
- Other
|
|
32
|
+
validations:
|
|
33
|
+
required: true
|
|
34
|
+
- type: textarea
|
|
35
|
+
id: alternatives
|
|
36
|
+
attributes:
|
|
37
|
+
label: Alternatives considered
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
name: Provider compatibility
|
|
2
|
+
description: Report a provider SDK compatibility issue
|
|
3
|
+
title: "[Provider]: "
|
|
4
|
+
labels: ["provider-compatibility"]
|
|
5
|
+
body:
|
|
6
|
+
- type: input
|
|
7
|
+
id: provider
|
|
8
|
+
attributes:
|
|
9
|
+
label: Provider
|
|
10
|
+
placeholder: OpenAI, Anthropic
|
|
11
|
+
validations:
|
|
12
|
+
required: true
|
|
13
|
+
- type: input
|
|
14
|
+
id: sdk_version
|
|
15
|
+
attributes:
|
|
16
|
+
label: SDK version
|
|
17
|
+
validations:
|
|
18
|
+
required: true
|
|
19
|
+
- type: textarea
|
|
20
|
+
id: code
|
|
21
|
+
attributes:
|
|
22
|
+
label: Minimal code
|
|
23
|
+
render: python
|
|
24
|
+
validations:
|
|
25
|
+
required: true
|
|
26
|
+
- type: textarea
|
|
27
|
+
id: behavior
|
|
28
|
+
attributes:
|
|
29
|
+
label: Behavior
|
|
30
|
+
description: What failed, and what did you expect?
|
|
31
|
+
validations:
|
|
32
|
+
required: true
|
|
33
|
+
- type: textarea
|
|
34
|
+
id: privacy
|
|
35
|
+
attributes:
|
|
36
|
+
label: Privacy note
|
|
37
|
+
description: Do not include API keys, real prompts, real responses, or customer data.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
## Summary
|
|
2
|
+
|
|
3
|
+
Describe the change and why it is needed.
|
|
4
|
+
|
|
5
|
+
## Tests
|
|
6
|
+
|
|
7
|
+
- [ ] `pytest`
|
|
8
|
+
- [ ] Real OpenAI smoke test, if OpenAI SDK behavior changed
|
|
9
|
+
- [ ] Real Anthropic smoke test, if Anthropic SDK behavior changed
|
|
10
|
+
- [ ] Benchmark run, if latency or throughput changed
|
|
11
|
+
|
|
12
|
+
## Compatibility
|
|
13
|
+
|
|
14
|
+
- Provider SDK impact:
|
|
15
|
+
- Public API impact:
|
|
16
|
+
- Migration notes:
|
|
17
|
+
|
|
18
|
+
## Security And Privacy
|
|
19
|
+
|
|
20
|
+
- [ ] Does not send prompt payloads to external services by default
|
|
21
|
+
- [ ] Does not log secrets, prompts, or responses
|
|
22
|
+
- [ ] Budget enforcement failure behavior is explicit
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
matrix:
|
|
14
|
+
python-version: ["3.10", "3.11", "3.12"]
|
|
15
|
+
# Pins must (a) be compatible with httpx>=0.28, which removed the
|
|
16
|
+
# `proxies` kwarg (openai<1.90 / anthropic<0.40 crash on it), and
|
|
17
|
+
# (b) stop relabelling transport exceptions: below openai 2.37 /
|
|
18
|
+
# anthropic 0.98 the SDK catches Backstop's BudgetExceededError and
|
|
19
|
+
# re-raises it as APIConnectionError, so the guardrail is invisible
|
|
20
|
+
# to user code. Floors in src/backstop/wrapper.py must match.
|
|
21
|
+
openai-version: ["2.37.0", "3.14.0"]
|
|
22
|
+
anthropic-version: ["0.99.0", "1.6.0"]
|
|
23
|
+
# Cross-check: newest openai against the oldest supported anthropic.
|
|
24
|
+
include:
|
|
25
|
+
- python-version: "3.12"
|
|
26
|
+
openai-version: "latest"
|
|
27
|
+
anthropic-version: "0.99.0"
|
|
28
|
+
fail-fast: false
|
|
29
|
+
|
|
30
|
+
steps:
|
|
31
|
+
- uses: actions/checkout@v4
|
|
32
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
33
|
+
uses: actions/setup-python@v5
|
|
34
|
+
with:
|
|
35
|
+
python-version: ${{ matrix.python-version }}
|
|
36
|
+
- name: Install dependencies
|
|
37
|
+
run: |
|
|
38
|
+
pip install -e ".[test,metrics,anthropic]"
|
|
39
|
+
if [ "${{ matrix.openai-version }}" != "latest" ]; then
|
|
40
|
+
pip install "openai==${{ matrix.openai-version }}"
|
|
41
|
+
fi
|
|
42
|
+
if [ "${{ matrix.anthropic-version }}" != "latest" ]; then
|
|
43
|
+
pip install "anthropic==${{ matrix.anthropic-version }}"
|
|
44
|
+
fi
|
|
45
|
+
- name: Run tests
|
|
46
|
+
run: pytest
|
|
47
|
+
- name: Run benchmark smoke
|
|
48
|
+
run: python -m backstop benchmark
|
|
49
|
+
|
|
50
|
+
build:
|
|
51
|
+
needs: test
|
|
52
|
+
runs-on: ubuntu-latest
|
|
53
|
+
permissions:
|
|
54
|
+
contents: write
|
|
55
|
+
id-token: write
|
|
56
|
+
|
|
57
|
+
steps:
|
|
58
|
+
- uses: actions/checkout@v4
|
|
59
|
+
- name: Set up Python
|
|
60
|
+
uses: actions/setup-python@v5
|
|
61
|
+
with:
|
|
62
|
+
python-version: "3.12"
|
|
63
|
+
- name: Install build tools
|
|
64
|
+
run: pip install build twine
|
|
65
|
+
- name: Build package
|
|
66
|
+
run: python -m build
|
|
67
|
+
- name: Validate package
|
|
68
|
+
run: python -m twine check dist/*
|
|
69
|
+
# Supply chain hardening: cosign signing, SLSA provenance (slsa-framework), SBOM generation
|
|
70
|
+
- name: Upload build artifacts
|
|
71
|
+
uses: actions/upload-artifact@v4
|
|
72
|
+
with:
|
|
73
|
+
name: dist
|
|
74
|
+
path: dist/
|
|
75
|
+
- name: Create GitHub Release
|
|
76
|
+
if: startsWith(github.ref, 'refs/tags/v')
|
|
77
|
+
run: |
|
|
78
|
+
gh release create "${{ github.ref_name }}" \
|
|
79
|
+
--repo "${{ github.repository }}" \
|
|
80
|
+
--notes "Backstop ${{ github.ref_name }}" \
|
|
81
|
+
dist/*
|
|
82
|
+
env:
|
|
83
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
84
|
+
- name: Publish to PyPI
|
|
85
|
+
if: startsWith(github.ref, 'refs/tags/v')
|
|
86
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
87
|
+
with:
|
|
88
|
+
password: ${{ secrets.PYPI_API_TOKEN }}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
.venv/
|
|
2
|
+
.pytest_cache/
|
|
3
|
+
__pycache__/
|
|
4
|
+
*.py[cod]
|
|
5
|
+
|
|
6
|
+
# Secrets / environment files — NEVER commit (see .agents/AGENTS.md)
|
|
7
|
+
.env
|
|
8
|
+
.env.*
|
|
9
|
+
*.env
|
|
10
|
+
!.env.example
|
|
11
|
+
.coverage
|
|
12
|
+
htmlcov/
|
|
13
|
+
dist/
|
|
14
|
+
build/
|
|
15
|
+
*.egg-info/
|
|
16
|
+
.kilo/
|
|
17
|
+
node_modules/
|
|
18
|
+
*.tsbuildinfo
|
|
19
|
+
ts/backstop/dist/
|
|
20
|
+
wedge_report.md
|
|
21
|
+
benchmarks/competitive/compose/
|
|
22
|
+
benchmarks/competitive/*.json
|
|
23
|
+
benchmarks/competitive/__pycache__/
|
|
24
|
+
|
|
25
|
+
# Internal working artifacts — never publish (see PLAN.md §0 rule 9)
|
|
26
|
+
artifacts/
|
|
27
|
+
agent/
|
|
28
|
+
.claude/
|
|
29
|
+
.agents/skills/
|
|
30
|
+
skills-lock.json
|
|
31
|
+
docs/internal/
|
backstop_ai-0.6.0/=0.40
ADDED
|
File without changes
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<p>
|
|
3
|
+
<a href="README.md">README</a> •
|
|
4
|
+
<a href="CODE_OF_CONDUCT.md">Code of Conduct</a>
|
|
5
|
+
</p>
|
|
6
|
+
</div>
|
|
7
|
+
|
|
8
|
+
# Changelog
|
|
9
|
+
|
|
10
|
+
All notable changes to Backstop should be documented in this file.
|
|
11
|
+
|
|
12
|
+
## 0.6.0 — Unreleased
|
|
13
|
+
|
|
14
|
+
- Renamed the Python distribution to `backstop-ai`; `import backstop` and the
|
|
15
|
+
`backstop` / `wedge` console commands are unchanged.
|
|
16
|
+
- Added repository metadata, Python classifiers, and package keywords; aligned
|
|
17
|
+
package and installer versions at `0.6.0`.
|
|
18
|
+
- Corrected install examples and removed unsupported PyPI verification claims.
|
|
19
|
+
Historical install strings below are retained as history, not current guidance;
|
|
20
|
+
see [the install guide](docs/install.md).
|
|
21
|
+
- Release preparation only: PyPI publication and clean-environment installation
|
|
22
|
+
from PyPI are pending. Provider SDK compatibility and enforcement-error
|
|
23
|
+
propagation remain unresolved; SDK dependency bounds are unchanged.
|
|
24
|
+
|
|
25
|
+
## Unreleased
|
|
26
|
+
|
|
27
|
+
- **Custom dashboard hardened (visual audit follow-up).** Fixed the topbar
|
|
28
|
+
collision between the mode badge and wordmark at phone widths (≤640px: the
|
|
29
|
+
brand row now wraps onto its own line); fixed keyboard focus being dropped to
|
|
30
|
+
`<body>` on every table re-render (row focus is restored across polls); grew
|
|
31
|
+
the refresh control and badges to comfortable touch-target sizes under
|
|
32
|
+
`pointer: coarse`.
|
|
33
|
+
- **Dashboard theme control.** `backstop dashboard --theme auto|dark|light`
|
|
34
|
+
sets the initial theme (default `auto` follows the OS), and a new **theme**
|
|
35
|
+
button in the topbar cycles auto → dark → light per browser, persisted in
|
|
36
|
+
`localStorage`.
|
|
37
|
+
- **Grafana export removed.** `backstop.dashboard` (the JSON spec module),
|
|
38
|
+
`tests/test_dashboard.py`, and `observability/grafana/` are gone — the
|
|
39
|
+
built-in dashboard is the single custom ops surface. Prometheus metrics and
|
|
40
|
+
`observability/prometheus-alerts.yml` are unchanged.
|
|
41
|
+
|
|
42
|
+
## v0.5.0 — 10× Better
|
|
43
|
+
|
|
44
|
+
Closes the gap between Backstop and proxy gateways (LiteLLM / BricksLLM) while
|
|
45
|
+
staying in-process and drop-in. All new infrastructure is opt-in; existing
|
|
46
|
+
installs keep their default behavior.
|
|
47
|
+
|
|
48
|
+
- **Competitive benchmark** — Firecrawl-sourced feature matrix vs LiteLLM /
|
|
49
|
+
BricksLLM and the "10× better" wedge (`docs/competitive-benchmark-2026-07-20.md`).
|
|
50
|
+
- **Deep Research: 10× Better** — exhaustive 6-agent Firecrawl synthesis
|
|
51
|
+
(gateways, observability, frameworks, clouds, in-process techniques, risks)
|
|
52
|
+
with a prioritized 13-item roadmap (`docs/deep-research-10x-better-2026-07-20.md`).
|
|
53
|
+
|
|
54
|
+
- **P0#1 Semantic cache (near-duplicate)** — opt-in pluggable embedder +
|
|
55
|
+
cosine `cache_similarity_threshold`. Exact match stays the zero-cost fast path;
|
|
56
|
+
on a miss, the prompt embedding is compared against cached entries and a
|
|
57
|
+
`>= threshold` match is short-circuited. Biggest single cost lever (50–80%
|
|
58
|
+
savings at typical hit rates) — `BackstopConfig(cache_enabled=True,
|
|
59
|
+
cache_semantic=True, cache_embedder=<callable>, cache_similarity_threshold=0.95)`.
|
|
60
|
+
- **P0#2 Fallback chain + priority routing** — the single `fallback_model` is
|
|
61
|
+
promoted to an ordered `fallback_chain` (list of `{model, base_url?}`) walked
|
|
62
|
+
in-process on circuit-open; `fallback_chain_for_priority` gives critical traffic
|
|
63
|
+
its own chain. Also fixes a latent bug where the single-model fallback silently
|
|
64
|
+
no-op'd (`httpx.Request` has no `copy()` in this httpx version).
|
|
65
|
+
- **P1 Shared (Redis) budget** — one token budget enforced across processes and
|
|
66
|
+
replicas via atomic Lua scripts. `BackstopConfig(shared_budget=True, redis_url=...)`
|
|
67
|
+
with `pip install "backstop[redis]"`.
|
|
68
|
+
- **P2 OpenTelemetry export** — mirrors the Prometheus series to a vendor-neutral
|
|
69
|
+
OTel meter. `BackstopConfig(otel_enabled=True)` with `pip install "backstop[otel]"`.
|
|
70
|
+
- **P3 In-process fallback** — retries once against a backup model/deployment when
|
|
71
|
+
the circuit opens, no proxy required. `BackstopConfig(fallback_model=...)`.
|
|
72
|
+
- **P4 Wedge semantic diff v2** — token/line-normalized similarity (identifier
|
|
73
|
+
Dice + line ratio) that is format/whitespace/removal-immune; report now carries
|
|
74
|
+
per-runner budget-isolation evidence and a cost estimate.
|
|
75
|
+
- **P5 Deterministic benchmarks** — seeded, reproducible `backstop benchmark`
|
|
76
|
+
(seed `0xC0FFEE`) with a `--publish` flag; published results in
|
|
77
|
+
`docs/benchmark-results-2026-07-20.md`.
|
|
78
|
+
- **P6 Accurate pricing** — maintained 2026 price table (Claude 4/3.5/3,
|
|
79
|
+
GPT 4.1/4o/o1/o3) with prefix+family resolution and offline-cached
|
|
80
|
+
`refresh_pricing()`.
|
|
81
|
+
- **P8 CLI ergonomics** — `backstop doctor` validates install/SDKs/keys and runs a
|
|
82
|
+
wrap smoke test; `backstop benchmark` produces reproducible proof.
|
|
83
|
+
- **P7 Doc drift fixes** — README benchmarks, features, and "what this is NOT"
|
|
84
|
+
corrected against current behavior.
|
|
85
|
+
- **P9 TypeScript SDK scaffold** — `@ravanish/backstop` mirrors `wrap()` (budget +
|
|
86
|
+
circuit breaker + retry + fallback) for Node.js agents (`ts/backstop`).
|
|
87
|
+
- **P10 Concurrency ceiling** — configurable `max_wrap_sessions` soft cap with a
|
|
88
|
+
warning, plus `docs/concurrency.md` guidance on the GIL ceiling and when to use
|
|
89
|
+
a proxy gateway.
|
|
90
|
+
- **Housekeeping** — deleted the superseded `docs/research` deep-research dump;
|
|
91
|
+
added `redis`/`otel` install extras.
|
|
92
|
+
|
|
93
|
+
### 10× Better — roadmap follow-up (this pass)
|
|
94
|
+
|
|
95
|
+
- **P1#3 Virtual keys + hierarchical budgets** — `virtual_keys` maps an API key
|
|
96
|
+
header (`virtual_key_header`) to a tenant, and `TenantBudget(parent=...)` rolls
|
|
97
|
+
spend up a team/org tree so a child budget can never exceed its parent.
|
|
98
|
+
- **P1#4 True per-tenant circuit breaker** — `per_tenant_circuit` keeps a separate
|
|
99
|
+
breaker per tenant (falls back to the global one); failures from one tenant no
|
|
100
|
+
longer trip the breaker for everyone.
|
|
101
|
+
- **P1#5 Cloud-quota-aware auto-tuning** — ingests provider `x-ratelimit-*` /
|
|
102
|
+
`anthropic-ratelimit-*` headers and proactively clamps the AIMD concurrency
|
|
103
|
+
limit (`apply_external_decrease`) before 429s hit, instead of reacting to them.
|
|
104
|
+
- **P1#6 Framework adapters** — `backstop.adapters` lazily bridges LangChain /
|
|
105
|
+
LlamaIndex callbacks to Backstop's hooks/metrics/tenant scoping so Backstop
|
|
106
|
+
becomes the guardrail *inside* the framework. Framework imports are deferred, so
|
|
107
|
+
importing the module never requires the framework installed.
|
|
108
|
+
- **P1#7 Cost forecasting + anomaly detection** — `backstop.forecast` projects
|
|
109
|
+
budget exhaustion from a measured burn rate and flags spend anomalies, turning
|
|
110
|
+
ledger data into an enforcement-triggering signal.
|
|
111
|
+
- **P1#12 Pluggable rate limiter + tiktoken pre-estimation** — `rate_limiter`
|
|
112
|
+
accepts any `allow(tokens) -> bool` object (e.g. `TokenBucketLimiter`, a
|
|
113
|
+
variable-cost token bucket charged by estimated tokens); `auto_token_count`
|
|
114
|
+
(opt-in) switches pre-dispatch estimates from the chars/4 heuristic to tiktoken;
|
|
115
|
+
`compress` is a pre-send `callable(body, model) -> body` hook.
|
|
116
|
+
- **P2#8 Tamper-evident audit log** — `audit_enabled` writes a chained,
|
|
117
|
+
HMAC-verifiable JSONL of every enforcement decision (`deny` / `fallback` /
|
|
118
|
+
`downgrade` / `shadow`) via `AuditLog.verify()`; the enterprise "escape hatch"
|
|
119
|
+
that makes in-process enforcement audit-ready.
|
|
120
|
+
- **P2#9 Secret provider** — `secret_provider` resolves virtual keys / tenant ids
|
|
121
|
+
to provider secrets at call time (env + static ships; cloud vaults implement the
|
|
122
|
+
same interface), avoiding plaintext key handling in-process.
|
|
123
|
+
- **P2#10 Gateway / sidecar mode** — `backstop gateway` (`backstop serve`) runs an
|
|
124
|
+
OpenAI-compatible reverse proxy so policy is non-bypassable for non-Python
|
|
125
|
+
services; `pip install "backstop[fastapi]"`.
|
|
126
|
+
- **P2#11 Agent guardrails** — `agent_guard` (`AgentGuard`) fences runaway agent
|
|
127
|
+
loops per agent id via sliding-window call/token ceilings.
|
|
128
|
+
- **P2#13 Safe rollout: shadow / canary** — `shadow_policy` (`ShadowPolicy` /
|
|
129
|
+
`CanaryRouter`) samples traffic to a candidate config/policy and records
|
|
130
|
+
reason-coded decisions before any hard cutover.
|
|
131
|
+
|
|
132
|
+
### v0.5.0 — Launch Readiness (2026-08-10)
|
|
133
|
+
|
|
134
|
+
Production-readiness pass: every claim is now backed by proof, every stale
|
|
135
|
+
doc is corrected, and the TypeScript SDK reaches full feature parity.
|
|
136
|
+
|
|
137
|
+
- **Proof infrastructure** — runnable proof scripts (`proofs/`) demonstrate
|
|
138
|
+
budget exhaustion prevention, multi-agent isolation, real-provider overhead,
|
|
139
|
+
and semantic cache savings against live providers.
|
|
140
|
+
- **Wedge fixed** — patch extraction now handles markdown code blocks, `FILE:`
|
|
141
|
+
headers, and multi-line LLM output (was unified-diff-only). Agent loop with
|
|
142
|
+
retry/fix on test failure. Per-runner test timeout.
|
|
143
|
+
- **Bundled test fixture** — `wedge-test-fixture/` gives `wedge run task.yaml`
|
|
144
|
+
a real repo to run against out of the box.
|
|
145
|
+
- **Docs corrected** — README benchmark table regenerated dynamically (was
|
|
146
|
+
stale), benchmark overhead measured live, version strings unified at `0.5.0`
|
|
147
|
+
across `pyproject.toml`, `wedge/__init__.py`, `install.sh`, and `docs/install.md`.
|
|
148
|
+
- **Gateway hardened** — auth (`--api-keys`), per-key rate limiting, 1 MB body
|
|
149
|
+
size guard, path traversal rejection, and error handling.
|
|
150
|
+
- **Cost forecasting → enforcement** — `forecast_horizon_seconds` proactively
|
|
151
|
+
tightens AIMD concurrency when burn rate projects exhaustion.
|
|
152
|
+
- **Secret provider wired** — virtual keys resolve to provider secrets at call
|
|
153
|
+
time in the transport, not just at wrap time.
|
|
154
|
+
- **TypeScript SDK v0.5.0** — full parity: priority admission, AIMD concurrency,
|
|
155
|
+
fallback chains, response cache (exact + semantic), hooks, audit log, agent
|
|
156
|
+
guardrails, quota-aware auto-tuning, cost forecasting, and `shadow/canary`.
|
|
157
|
+
- **Concurrent budget stress tests** — 20-thread hammer proves the no-overspend
|
|
158
|
+
invariant. CI matrix tests multiple SDK versions.
|
|
159
|
+
|
|
160
|
+
## v0.4.0
|
|
161
|
+
|
|
162
|
+
- Integrated the `wedge` tool directly into the `backstop` codebase as an executable package script (`wedge run task.yaml`).
|
|
163
|
+
- **Isolation Harness**: Built per-runner Git worktree simulation and task execution.
|
|
164
|
+
- **Diff Engine**: Added `difflib`-based patch similarity scoring (`CONVERGED`, `PARTIAL`, `DIVERGED`).
|
|
165
|
+
- **Reporting Engine**: Added terminal summary and Markdown report generation (`wedge_report.md`).
|
|
166
|
+
- Fully un-mocked and configured the Anthropic and OpenAI wrapper handlers for production-ready API integration.
|
|
167
|
+
- Added Phase 0 trust assets: security policy, contribution guide, code of conduct, public docs, examples, benchmark scaffold, and GitHub templates.
|
|
168
|
+
|
|
169
|
+
## v0.3.0
|
|
170
|
+
|
|
171
|
+
- Added per-tenant budget buckets.
|
|
172
|
+
- Added cost estimation helpers.
|
|
173
|
+
- Added downgrade behavior for exhausted tenant budgets.
|
|
174
|
+
|
|
175
|
+
## v0.2.0
|
|
176
|
+
|
|
177
|
+
- Added latency metadata.
|
|
178
|
+
- Added streaming handling.
|
|
179
|
+
- Added middleware hooks.
|
|
180
|
+
- Added request timeout support.
|
|
181
|
+
- Added response caching.
|
|
182
|
+
- Added token counting improvements.
|
|
183
|
+
- Removed dead `priority_weights` behavior.
|
|
184
|
+
|
|
185
|
+
## v0.1.0
|
|
186
|
+
|
|
187
|
+
- Added the initial Backstop package.
|
|
188
|
+
- Added OpenAI SDK wrapping.
|
|
189
|
+
- Added Anthropic SDK compatibility.
|
|
190
|
+
- Added budget enforcement, priority admission, AIMD concurrency, retry handling, circuit breaking, metrics, and CLI harness support.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Code Of Conduct
|
|
2
|
+
|
|
3
|
+
Backstop follows the Contributor Covenant Code of Conduct, version 2.1.
|
|
4
|
+
|
|
5
|
+
## Our Pledge
|
|
6
|
+
|
|
7
|
+
We pledge to make participation in this project a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation.
|
|
8
|
+
|
|
9
|
+
## Expected Behavior
|
|
10
|
+
|
|
11
|
+
- Be respectful and direct.
|
|
12
|
+
- Focus criticism on technical work and project outcomes.
|
|
13
|
+
- Assume maintainers and contributors are balancing tradeoffs.
|
|
14
|
+
- Keep security-sensitive details out of public threads.
|
|
15
|
+
|
|
16
|
+
## Unacceptable Behavior
|
|
17
|
+
|
|
18
|
+
- Harassment, insults, or personal attacks.
|
|
19
|
+
- Publishing private information without permission.
|
|
20
|
+
- Sexualized language or attention.
|
|
21
|
+
- Trolling or sustained disruption.
|
|
22
|
+
|
|
23
|
+
## Enforcement
|
|
24
|
+
|
|
25
|
+
Maintainers may remove comments, close issues, reject contributions, or ban participants who violate this code of conduct.
|
|
26
|
+
|
|
27
|
+
## Attribution
|
|
28
|
+
|
|
29
|
+
This Code of Conduct is adapted from the Contributor Covenant, version 2.1.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Thanks for helping improve Backstop. This project is optimized for practical LLM spend control: budgets, backpressure, retries, circuit breaking, fallbacks, metrics, and provider SDK compatibility.
|
|
4
|
+
|
|
5
|
+
## Local Setup
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
python3 -m venv .venv
|
|
9
|
+
. .venv/bin/activate
|
|
10
|
+
pip install -e ".[test,metrics,anthropic]"
|
|
11
|
+
pytest
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Development Guidelines
|
|
15
|
+
|
|
16
|
+
- Keep the local SDK path useful without a hosted service.
|
|
17
|
+
- Do not send prompt payloads to external services by default.
|
|
18
|
+
- Prefer provider SDK compatibility over custom HTTP API abstractions.
|
|
19
|
+
- Add tests for budget, retry, circuit, streaming, metrics, or wrapper behavior when changing those paths.
|
|
20
|
+
- Keep public APIs typed and documented.
|
|
21
|
+
- Avoid broad refactors in feature PRs.
|
|
22
|
+
|
|
23
|
+
## Pull Request Checklist
|
|
24
|
+
|
|
25
|
+
- Tests pass with `pytest`.
|
|
26
|
+
- New behavior has tests or a clear reason tests are not practical.
|
|
27
|
+
- Docs or examples are updated when public behavior changes.
|
|
28
|
+
- Security and privacy implications are called out.
|
|
29
|
+
- Provider SDK compatibility impact is described.
|
|
30
|
+
|
|
31
|
+
## Real Provider Tests
|
|
32
|
+
|
|
33
|
+
Real API tests are opt-in and require credentials:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
export OPENAI_API_KEY="..."
|
|
37
|
+
pytest -m real_openai
|
|
38
|
+
|
|
39
|
+
export ANTHROPIC_API_KEY="..."
|
|
40
|
+
pytest -m real_anthropic
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Do not include real API keys, prompts, responses, or customer data in issues, tests, fixtures, screenshots, or logs.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Backstop Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|