data-olympus 0.3.1__tar.gz → 0.3.2__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.
- {data_olympus-0.3.1 → data_olympus-0.3.2}/CHANGELOG.md +18 -1
- {data_olympus-0.3.1 → data_olympus-0.3.2}/PKG-INFO +1 -1
- {data_olympus-0.3.1 → data_olympus-0.3.2}/pyproject.toml +1 -1
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/config.py +10 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/rest_api.py +30 -13
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/server.py +31 -10
- {data_olympus-0.3.1 → data_olympus-0.3.2}/.gitignore +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/LICENSE +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/NOTICE +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/README.md +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/SPEC.md +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/benchmarks/README.md +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/bin/_kb_detect_workspace.sh +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/bin/_kb_enforce.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/bin/_kb_fallback.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/bin/kb +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/bin/kb-enforce-hook +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/bin/opencode/data-olympus-gate.ts +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/deploy/k8s/README.md +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/deploy/k8s/read-replica/README.md +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/docs/releases/README.md +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/example-bundle/projects/acme-app/README.md +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/example-bundle/projects/acme-app/components/api/README.md +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/__init__.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/audit_log.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/audit_trailers.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/auth.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/cli/__init__.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/cli/import_cmd.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/cli/indexgen.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/cli/main.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/cli/report_cmd.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/cooccurrence.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/dedup.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/durable.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/embeddings.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/enforce_policy.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/format/__init__.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/format/document.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/format/frontmatter.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/format/lint.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/format/validate.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/git_ops.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/health.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/importer/__init__.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/importer/adr.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/importer/flat.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/importer/model.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/importer/okf.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/importer/run.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/importer/stamp.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/index.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/markdown_parse.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/models.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/onboarding.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/onboarding_inflight.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/onboarding_playbook.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/pending.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/principals.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/prompts.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/push_queue.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/query_expansion.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/rate_limit.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/refresh.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/report.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/safe_id.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/search_gate.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/search_shortcut.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/session_metrics.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/setup_wizard.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/thin_pointer.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/tools_audit.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/tools_enforce.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/tools_onboarding.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/tools_read.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/tools_write.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/trigram.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/viewer/__init__.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/viewer/generator.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/worktrees.py +0 -0
- {data_olympus-0.3.1 → data_olympus-0.3.2}/src/data_olympus/write_gate.py +0 -0
|
@@ -12,6 +12,22 @@ This project uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
12
12
|
|
|
13
13
|
## [Unreleased]
|
|
14
14
|
|
|
15
|
+
## [0.3.2] - 2026-07-05
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- **`gate/check` no longer self-DoSes multi-agent fleets with 429s.** The
|
|
20
|
+
enforcement hook calls `POST /api/v1/gate/check` (and `kb_gate_check`) once per
|
|
21
|
+
gated tool action for every agent, but it shared the write/consult quota
|
|
22
|
+
(`KB_RATE_LIMIT_PER_HOUR`, default 100). Behind an ingress with no auth, all
|
|
23
|
+
clients collapse to a single limiter bucket, so a few active agents exhausted
|
|
24
|
+
the hourly quota in minutes and everything after got `429 Too Many Requests`.
|
|
25
|
+
gate-check now has its own ceiling, `KB_GATE_CHECK_RATE_LIMIT_PER_HOUR`,
|
|
26
|
+
defaulting to `0` (unthrottled, consistent with the read routes) since it is a
|
|
27
|
+
mandatory per-action probe that does only cheap classification and no writes.
|
|
28
|
+
Set a positive value for an explicit backstop. `consult` and
|
|
29
|
+
`onboarding/cleanup-plan` stay throttled by `KB_RATE_LIMIT_PER_HOUR`.
|
|
30
|
+
|
|
15
31
|
## [0.3.1] - 2026-07-05
|
|
16
32
|
|
|
17
33
|
### Fixed
|
|
@@ -1013,7 +1029,8 @@ This project uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
1013
1029
|
- `docs/adoption.md`: bring-your-own-KB guide (author, lint, index, serve, wire an agent).
|
|
1014
1030
|
- `docs/comparison.md`: how data-olympus relates to OKF, enterprise catalogs, markdown KB tools, agent-context conventions, RAG, and ADR tooling.
|
|
1015
1031
|
|
|
1016
|
-
[Unreleased]: https://github.com/knaisoma/data-olympus/compare/v0.3.
|
|
1032
|
+
[Unreleased]: https://github.com/knaisoma/data-olympus/compare/v0.3.2...HEAD
|
|
1033
|
+
[0.3.2]: https://github.com/knaisoma/data-olympus/compare/v0.3.1...v0.3.2
|
|
1017
1034
|
[0.3.1]: https://github.com/knaisoma/data-olympus/compare/v0.3.0...v0.3.1
|
|
1018
1035
|
[0.3.0]: https://github.com/knaisoma/data-olympus/compare/v0.2.0...v0.3.0
|
|
1019
1036
|
[0.2.0]: https://github.com/knaisoma/data-olympus/compare/v0.1.1...v0.2.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: data-olympus
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: Governance-grade knowledge-base format (OKF-compatible) plus CLI and single-writer MCP server
|
|
5
5
|
Project-URL: Homepage, https://github.com/knaisoma/data-olympus
|
|
6
6
|
Project-URL: Repository, https://github.com/knaisoma/data-olympus
|
|
@@ -28,6 +28,14 @@ class Config:
|
|
|
28
28
|
write_block_paths: list[str] = field(default_factory=list)
|
|
29
29
|
rate_limit_per_hour: int = 100
|
|
30
30
|
rate_limit_per_ip_per_hour: int = 0
|
|
31
|
+
# Separate ceiling for the high-frequency /api/v1/gate/check route (the
|
|
32
|
+
# enforcement hook's per-tool-action freshness probe). 0 (default) disables
|
|
33
|
+
# throttling for that route: it is a mandatory, once-per-tool-call, read-only
|
|
34
|
+
# probe, so any fixed hourly quota self-DoSes an active multi-agent fleet
|
|
35
|
+
# (behind ingress all clients collapse to one limiter bucket). Reads are
|
|
36
|
+
# already unthrottled; this keeps gate/check consistent with them. Set a
|
|
37
|
+
# positive value only if you want an explicit backstop.
|
|
38
|
+
gate_check_rate_limit_per_hour: int = 0
|
|
31
39
|
max_text_bytes: int = 262144
|
|
32
40
|
max_postimage_bytes: int = 1048576
|
|
33
41
|
max_body_bytes: int = 2097152
|
|
@@ -155,6 +163,7 @@ def load_config() -> Config:
|
|
|
155
163
|
write_block_paths = _split_csv(os.getenv("KB_WRITE_BLOCK_PATHS", ""))
|
|
156
164
|
rate_limit_per_hour = int(os.getenv("KB_RATE_LIMIT_PER_HOUR", "100"))
|
|
157
165
|
rate_limit_per_ip_per_hour = int(os.getenv("KB_RATE_LIMIT_PER_IP_PER_HOUR", "0"))
|
|
166
|
+
gate_check_rate_limit_per_hour = int(os.getenv("KB_GATE_CHECK_RATE_LIMIT_PER_HOUR", "0"))
|
|
158
167
|
max_text_bytes = int(os.getenv("KB_MAX_TEXT_BYTES", "262144"))
|
|
159
168
|
max_postimage_bytes = int(os.getenv("KB_MAX_POSTIMAGE_BYTES", "1048576"))
|
|
160
169
|
max_body_bytes = int(os.getenv("KB_MAX_BODY_BYTES", "2097152"))
|
|
@@ -226,6 +235,7 @@ def load_config() -> Config:
|
|
|
226
235
|
write_block_paths=write_block_paths,
|
|
227
236
|
rate_limit_per_hour=rate_limit_per_hour,
|
|
228
237
|
rate_limit_per_ip_per_hour=rate_limit_per_ip_per_hour,
|
|
238
|
+
gate_check_rate_limit_per_hour=gate_check_rate_limit_per_hour,
|
|
229
239
|
max_text_bytes=max_text_bytes,
|
|
230
240
|
max_postimage_bytes=max_postimage_bytes,
|
|
231
241
|
max_body_bytes=max_body_bytes,
|
|
@@ -32,6 +32,7 @@ if TYPE_CHECKING:
|
|
|
32
32
|
from starlette.requests import Request
|
|
33
33
|
|
|
34
34
|
from data_olympus.models import HealthResponse
|
|
35
|
+
from data_olympus.rate_limit import SlidingWindowLimiter
|
|
35
36
|
from data_olympus.server import ServerState
|
|
36
37
|
|
|
37
38
|
|
|
@@ -285,22 +286,33 @@ async def _read_json_capped(
|
|
|
285
286
|
)
|
|
286
287
|
|
|
287
288
|
|
|
289
|
+
_USE_WRITE_LIMITER: Any = object()
|
|
290
|
+
|
|
291
|
+
|
|
288
292
|
def _rate_limited(
|
|
289
293
|
state: ServerState, request: Request, principal: Principal,
|
|
294
|
+
*, limiter: SlidingWindowLimiter | None = _USE_WRITE_LIMITER,
|
|
290
295
|
) -> JSONResponse | None:
|
|
291
|
-
"""Apply
|
|
292
|
-
|
|
293
|
-
The consult /
|
|
294
|
-
|
|
295
|
-
without bound.
|
|
296
|
-
(remote_addr, principal name)
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
296
|
+
"""Apply a sliding-window limiter to an enforcement-plane route.
|
|
297
|
+
|
|
298
|
+
The consult / cleanup-plan routes were previously unthrottled (item 6), so an
|
|
299
|
+
anonymous or authenticated caller could hammer the classifier and ledger
|
|
300
|
+
without bound. They reuse the write limiter (``state.rate_limiter``), keyed by
|
|
301
|
+
(remote_addr, principal name). Returns a 429 JSONResponse when over quota, else
|
|
302
|
+
None.
|
|
303
|
+
|
|
304
|
+
``limiter`` overrides which limiter to consult. The high-frequency
|
|
305
|
+
/api/v1/gate/check route passes ``state.gate_rate_limiter`` (None by default),
|
|
306
|
+
so it is unthrottled unless an operator sets an explicit ceiling: it is a
|
|
307
|
+
mandatory once-per-tool-action probe, and any fixed quota shared across a
|
|
308
|
+
fleet (all clients collapse to one bucket behind ingress) self-DoSes with 429s.
|
|
309
|
+
|
|
310
|
+
When the resolved limiter is absent (a read-only deployment, or gate/check with
|
|
311
|
+
no ceiling configured) throttling is skipped.
|
|
302
312
|
"""
|
|
303
|
-
limiter
|
|
313
|
+
# Distinguish "no limiter arg" (use the write limiter) from an explicit None
|
|
314
|
+
# (the gate-check limiter with no ceiling: throttling is genuinely off).
|
|
315
|
+
limiter = state.rate_limiter if limiter is _USE_WRITE_LIMITER else limiter
|
|
304
316
|
if limiter is None:
|
|
305
317
|
return None
|
|
306
318
|
remote_addr = request.client.host if request.client else "unknown"
|
|
@@ -662,7 +674,12 @@ def register_routes(
|
|
|
662
674
|
principal, denied = _authorize(request, registry)
|
|
663
675
|
if denied is not None:
|
|
664
676
|
return denied
|
|
665
|
-
|
|
677
|
+
# gate/check uses its own limiter (unthrottled by default): it is the
|
|
678
|
+
# hook's once-per-tool-action probe, so it must not share the write /
|
|
679
|
+
# consult quota (that self-DoSes a fleet, see _rate_limited).
|
|
680
|
+
if (throttled := _rate_limited(
|
|
681
|
+
state, request, principal, limiter=state.gate_rate_limiter
|
|
682
|
+
)) is not None:
|
|
666
683
|
return throttled
|
|
667
684
|
import time as _time
|
|
668
685
|
|
|
@@ -123,6 +123,7 @@ class ServerState:
|
|
|
123
123
|
push_queue: PushQueue | None = None,
|
|
124
124
|
pending: PendingQueue | None = None,
|
|
125
125
|
rate_limiter: SlidingWindowLimiter | None = None,
|
|
126
|
+
gate_rate_limiter: SlidingWindowLimiter | None = None,
|
|
126
127
|
blocklist: PathBlocklist | None = None,
|
|
127
128
|
audit_log: AuditLog | None = None,
|
|
128
129
|
classifier: IntentClassifier | None = None,
|
|
@@ -147,6 +148,10 @@ class ServerState:
|
|
|
147
148
|
self.push_queue: PushQueue | None = push_queue
|
|
148
149
|
self.pending: PendingQueue | None = pending
|
|
149
150
|
self.rate_limiter: SlidingWindowLimiter | None = rate_limiter
|
|
151
|
+
# Separate limiter for /api/v1/gate/check (None = unthrottled, the default).
|
|
152
|
+
# Kept distinct from `rate_limiter` so gate-check traffic and the write /
|
|
153
|
+
# consult / cleanup-plan quota never share a bucket.
|
|
154
|
+
self.gate_rate_limiter: SlidingWindowLimiter | None = gate_rate_limiter
|
|
150
155
|
self.blocklist: PathBlocklist | None = blocklist
|
|
151
156
|
self.audit_log: AuditLog | None = audit_log
|
|
152
157
|
# Process-wide write serializer (scope item 1): one instance shared by
|
|
@@ -232,6 +237,7 @@ def build_app(
|
|
|
232
237
|
confidence_threshold: float = 0.85,
|
|
233
238
|
rate_limit_per_hour: int = 100,
|
|
234
239
|
rate_limit_per_ip_per_hour: int = 0,
|
|
240
|
+
gate_check_rate_limit_per_hour: int = 0,
|
|
235
241
|
max_text_bytes: int = 262144,
|
|
236
242
|
max_postimage_bytes: int = 1048576,
|
|
237
243
|
max_body_bytes: int = 2097152,
|
|
@@ -279,6 +285,7 @@ def build_app(
|
|
|
279
285
|
write_block_paths=write_block_paths or [],
|
|
280
286
|
rate_limit_per_hour=rate_limit_per_hour,
|
|
281
287
|
rate_limit_per_ip_per_hour=rate_limit_per_ip_per_hour,
|
|
288
|
+
gate_check_rate_limit_per_hour=gate_check_rate_limit_per_hour,
|
|
282
289
|
max_text_bytes=max_text_bytes,
|
|
283
290
|
max_postimage_bytes=max_postimage_bytes,
|
|
284
291
|
max_body_bytes=max_body_bytes,
|
|
@@ -393,6 +400,13 @@ def build_app(
|
|
|
393
400
|
max_per_hour=config.rate_limit_per_hour,
|
|
394
401
|
max_per_ip_per_hour=config.rate_limit_per_ip_per_hour,
|
|
395
402
|
)
|
|
403
|
+
# gate/check gets its own limiter only when a positive ceiling is set;
|
|
404
|
+
# 0 (default) leaves it unthrottled (see Config.gate_check_rate_limit_per_hour).
|
|
405
|
+
gate_rate_limiter = (
|
|
406
|
+
SlidingWindowLimiter(max_per_hour=config.gate_check_rate_limit_per_hour)
|
|
407
|
+
if config.gate_check_rate_limit_per_hour > 0
|
|
408
|
+
else None
|
|
409
|
+
)
|
|
396
410
|
blocklist = PathBlocklist(
|
|
397
411
|
tier_blocks=config.write_block_tiers,
|
|
398
412
|
path_blocks=config.write_block_paths,
|
|
@@ -401,6 +415,7 @@ def build_app(
|
|
|
401
415
|
state.push_queue = push_queue
|
|
402
416
|
state.pending = pending
|
|
403
417
|
state.rate_limiter = rate_limiter
|
|
418
|
+
state.gate_rate_limiter = gate_rate_limiter
|
|
404
419
|
state.blocklist = blocklist
|
|
405
420
|
|
|
406
421
|
if config.kb_remote_url and not config.read_only:
|
|
@@ -545,17 +560,22 @@ def build_app(
|
|
|
545
560
|
)
|
|
546
561
|
return resp.model_dump()
|
|
547
562
|
|
|
548
|
-
def _mcp_rate_limited() -> dict[str, object] | None:
|
|
549
|
-
"""Apply
|
|
563
|
+
def _mcp_rate_limited(*, gate: bool = False) -> dict[str, object] | None:
|
|
564
|
+
"""Apply a sliding-window limiter to an MCP enforcement-plane tool
|
|
550
565
|
(WP0b item (b)).
|
|
551
566
|
|
|
552
|
-
The REST consult /
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
567
|
+
The REST consult / cleanup-plan routes throttle via ``_rate_limited``; the
|
|
568
|
+
MCP tool paths did not, so an agent could hammer the classifier/ledger
|
|
569
|
+
unbounded. Key on the resolved principal (there is no per-request remote
|
|
570
|
+
addr on the MCP transport, so the limiter's per-agent quota is the
|
|
571
|
+
meaningful dimension). Returns a rejection dict when over quota, else None.
|
|
572
|
+
|
|
573
|
+
``gate=True`` (kb_gate_check) consults ``state.gate_rate_limiter`` instead
|
|
574
|
+
of the write limiter; that limiter is None by default, so the
|
|
575
|
+
high-frequency freshness probe is unthrottled unless explicitly capped,
|
|
576
|
+
matching the REST /api/v1/gate/check route. Skipped when the resolved
|
|
577
|
+
limiter is None (read-only deploy, or gate-check with no ceiling set)."""
|
|
578
|
+
limiter = state.gate_rate_limiter if gate else state.rate_limiter
|
|
559
579
|
if limiter is None:
|
|
560
580
|
return None
|
|
561
581
|
principal_name = _current_principal.get().name
|
|
@@ -768,7 +788,7 @@ def build_app(
|
|
|
768
788
|
) -> dict[str, object]:
|
|
769
789
|
"""Return a verdict (allow | consult_required) for a pending code action.
|
|
770
790
|
Governed actions require a fresh consultation on record."""
|
|
771
|
-
if (throttled := _mcp_rate_limited()) is not None:
|
|
791
|
+
if (throttled := _mcp_rate_limited(gate=True)) is not None:
|
|
772
792
|
return throttled
|
|
773
793
|
import time as _time
|
|
774
794
|
|
|
@@ -852,6 +872,7 @@ def build_app_from_config(config: Config, *, bootstrap_now: bool = True) -> Fast
|
|
|
852
872
|
confidence_threshold=config.confidence_threshold,
|
|
853
873
|
rate_limit_per_hour=config.rate_limit_per_hour,
|
|
854
874
|
rate_limit_per_ip_per_hour=config.rate_limit_per_ip_per_hour,
|
|
875
|
+
gate_check_rate_limit_per_hour=config.gate_check_rate_limit_per_hour,
|
|
855
876
|
max_text_bytes=config.max_text_bytes,
|
|
856
877
|
max_postimage_bytes=config.max_postimage_bytes,
|
|
857
878
|
max_body_bytes=config.max_body_bytes,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{data_olympus-0.3.1 → data_olympus-0.3.2}/example-bundle/projects/acme-app/components/api/README.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|