

Also from Kynth Studios


Also from Kynth Studios


Also from Kynth Studios
1# packages/sdk/python — Python SDK for the Caveman Cloud gateway23Stdlib-only (`urllib.request`, no third-party deps) Python package. Provides `Cave` (config +4entrypoint), `CaveTool` (tool descriptor), and `ToolSearchResult`. All HTTP calls POST to the5gateway with `x-cave-agent` / `x-cave-workflow` / `x-cave-retention` headers set from `Cave` fields.67## Layout89- `caveman_cloud/__init__.py` — re-exports `Cave`, `CaveTool`, `CompressResult`, `ContextPackItem`, `ContextPackOptions`, `ContextPackResult`, `ToolSearchResult`, …10- `caveman_cloud/core.py` — all implementation: `Cave`, `Trace`, `Provider`, `_Create`, `ToolSearchResult`, `CompressResult`, `ContextPack*`, `CaveTool`, `headers()`11- `tests/test_sdk.py` — pytest tests; mock `urllib.request.urlopen` with `patch()`12- `tests/test_parity.py` — cross-language conformance suite; drives `../../parity/fixtures.json` (shared with sdk-ts). Same fixtures, two languages → a field in one SDK and not the other fails CI.13- `tests/test_trace_continuity.py` — trace/span id minting + which requests carry `x-cave-trace-id` / `x-cave-parent-span-id`; mirrors the TS `tests/trace-continuity.runtime.mjs`14- `pyproject.toml` — distribution name `caveman-sdk` (import package stays `caveman_cloud`), `requires-python = ">=3.13"`, no runtime dependencies1516## Key API surface (`core.py`)1718- `Cave.trace(workflow, tags, *, trace_id=None, span_id=None)` → context manager yielding `Trace`; call `.model["openai"].responses.create(body)` inside. The trace mints `trace_id` (32 lowercase hex) + a root `span_id` (16 lowercase hex) with the exporter's RNG; `trace_id`/`span_id` continue an inbound trace and a value that isn't the exact hex shape is replaced rather than sent. Every provider call made **through the trace** carries `x-cave-trace-id` + `x-cave-parent-span-id`. Generic `/sdk/v1/*` calls and providers built off the `Cave` carry neither; the sole SDK-endpoint exception is `Trace.tool`, whose `/sdk/v1/events` call carries the trace id and root parent span id19- `Trace.exporter(service_name=None)` → an `OTelExporter` whose `default_trace_id` is the trace's, so SDK spans and the gateway's request rows join one trace. MIRRORS the TS `CaveTrace.exporter`20- `Cave.tools(catalog, *, strategy="all", initial_tool_count=8)` → builder handle with `.strategy`, `.initial` (`list[CaveTool]`), `.search(query, *, max_tools, context, workflow, ranker, session_id)`. `strategy="deferred"` includes every `always_load` tool exactly once, then fills remaining initial slots from non-mandatory tools; a cap below the mandatory count fails locally. `.search()` always hits the gateway with the FULL catalog. MIRRORS the TS `cave.tools({catalog, strategy})`21- `Cave.tool_search(tools, query, *, context, max_tools, workflow, ranker, session_id)` → flat variant: POSTs `[tools, query]` to `/sdk/v1/tool-search`; returns `ToolSearchResult` with `.saved_tokens` / `.reduction_pct` / `.session_id`. Schema-token counters are estimates; `.token_basis` discloses the counter and `.basis` is always `"inferred"`. `ranker` (`"bm25"`|`"embeddings"`) is passed through verbatim — the SDK never computes similarity22- `Cave.prompts.internal_brevity(*, style, preserve_errors_verbatim=False, preserve_code_verbatim=False)` → output-style snippet (`"none"` → `""`); booleans render lowercase to match the TS `cave.prompts.internalBrevity`23- `Cave.compress(payload, *, content_type=None)` → `CompressResult`; POSTs `/sdk/v1/compress`, maps the Engine report. **Byte-safe pass-through** on any transport/parse problem (original input, `ratio=0.0`, no handle); `.token_count_basis` discloses the counter and `basis` is always `"inferred"`. The SDK delegates — it never reimplements a compressor24- `Cave.context.pack(query, items, options)` → `ContextPackResult`; connected-only POST to `/sdk/v1/context/pack`. Lossy selector over caller-owned items, never CCR/ledger; returns exact `deferred_ids`. Transport or malformed-report failure returns all original items with zero inferred savings25- `Trace.expand(source_ref)` — the GET half of `checkpoint()`; `GET /sdk/v1/checkpoints/{ref}/expand` returns the stored `{source_ref, version, messages, checkpoint}`26- `Cave.openai/anthropic/gemini/vertex(upstream_key)` → `Provider` that proxies through gateway; `Provider.raw(path, body)` is the escape hatch (mirrors the TS provider-client `raw`)27- `Cave.bedrock(region, endpoint="runtime")` → no-network first-party route descriptor; Runtime defaults to `/bedrock`, explicit Mantle returns `/bedrock/anthropic`, and `sdk_only=False` mirrors TS `sdkOnly`28- `Trace.tool(name, options, fn)` — calls `fn()` then POSTs a `tool.call` event29- `Trace.page_artifact(value, options)` / `Trace.artifacts.page(value, options)` — send versioned `{value, options, workflow}`; gateway stores only JSON `value`. `artifacts.get(id)` performs authenticated retrieval. `page_artifact` remains backwards-compatible alias.30- `Trace.model["openai"].responses.create(body, *, latency_class=None, tool_session_id=None)` — when `latency_class` is set, sends the `x-cave-async` header (`"true"` unless `"interactive"`); when `tool_session_id` is set, sends `x-cave-tool-session`, mirroring the TS `trace.model.openai.responses.create(body, {cave:{latencyClass, toolSessionId}})`31- `Trace.checkpoint(messages, options)` — POSTs to `/sdk/v1/checkpoints`; the gateway persists it (Valkey) and returns a reversible `source_ref` you can later expand via `GET /sdk/v1/checkpoints/{ref}/expand`32- `Cave.exporter(service_name=None)` → `OTelExporter`; `record_span(...)` maps current GenAI fields to `gen_ai.*`, `export()` POSTs OTLP/JSON to standard `/v1/traces` (headers via `otlp_headers()`; legacy `/otlp/v1/traces` remains server-only compatibility)33- `Cave.retry_loop_breaker(threshold=3)` → `RetryLoopBreaker`; `.record(name, args)` raises `RetryLoopError` after `threshold` consecutive identical tool calls (interrupts a stuck loop). `.guard(name, args, fn)` records then runs `fn`34- `Cave.jobs` → reserved `JobsClient` surface. Every method fails locally with `cave_async_jobs_unavailable`; it performs no network request until durable encrypted request storage, credential custody, and a draining worker exist. MIRRORS the TS `Cave.jobs`3536## Conventions3738- Tests use `patch("urllib.request.urlopen", side_effect=fake_urlopen)` — never real network39- Add new gateway endpoints via `Trace._request(path, body)` or `Provider.create(path, body)`40- `headers()` is the single source for all outgoing headers; edit there, nowhere else41- Deferred tool-search session handoff uses request/result `session_id` plus provider header `x-cave-tool-session`; update sdk-ts + parity fixtures with any change42- Run tests: `pytest` from this directory (Python ≥ 3.13 required)4344## Gotchas4546- **No third-party deps** — do not add `requests`, `httpx`, or any library; keep `dependencies = []` in pyproject.toml47- **byte-safe**: SDK sends request bodies to the gateway unmodified; no rewriting. `compress()` delegates to the Engine and passes the original through on any problem48- **context packing is connected-only and intentionally lossy**: it sends item bytes to gateway, never runs in local wrap, and relies on caller retaining every item named by `deferred_ids`. It chooses what enters window; cache-optimal assembly chooses placement49- `sdk-python` and `sdk-ts` mirror the same field names and `/sdk/v1/*` contract — enforced by the shared parity suite (`tests/test_parity.py` + `../../parity/fixtures.json`), not just convention. A divergence is a CI failure. Change one SDK, change both **and** the fixtures5051See ../../../CLAUDE.md (root)52
One repository carrying more than one format is the comparison this product exists for: does anyone actually write different content in each file, or is one a copy of the other?
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| JuliusBrussee/cavemanCLAUDE.md · 98k | CLAUDE.md | archgitdo-notagent-behaviour+1 | 73/100 | today | |
| JuliusBrussee/cavemanagents/AGENTS.md · 98k | AGENTS.md | agent-behaviour | 16/100 | today | |
| JuliusBrussee/cavemanagents/CLAUDE.md · 98k | CLAUDE.md | stylearchsecurityagent-behaviour | 64/100 | today | |
| JuliusBrussee/cavemanbrowse/CLAUDE.md · 98k | CLAUDE.md | testarch | 59/100 | today | |
| JuliusBrussee/cavemancacheengine/CLAUDE.md · 98k | CLAUDE.md | testarch | 73/100 | today | |
| JuliusBrussee/cavemanengine/AGENTS.md · 98k | AGENTS.md | stylearch | 66/100 | today | |
| JuliusBrussee/cavemanengine/CLAUDE.md · 98k | CLAUDE.md | stylearch | 58/100 | today | |
| JuliusBrussee/cavemanextension/AGENTS.md · 98k | AGENTS.md | buildteststylearch+1 | 73/100 | today | |
| JuliusBrussee/cavemanextension/CLAUDE.md · 98k | CLAUDE.md | buildteststylearch+1 | 73/100 | today | |
| JuliusBrussee/cavemanintegrations/CLAUDE.md · 98k | CLAUDE.md | stylearch | 55/100 | today | |
| JuliusBrussee/cavemanmcp/AGENTS.md · 98k | AGENTS.md | stylearch | 79/100 | today | |
| JuliusBrussee/cavemanmcp/CLAUDE.md · 98k | CLAUDE.md | stylearch | 79/100 | today | |
| JuliusBrussee/cavemanmem/AGENTS.md · 98k | AGENTS.md | stylearchperformanceagent-behaviour | 67/100 | today | |
| JuliusBrussee/cavemanmem/CLAUDE.md · 98k | CLAUDE.md | stylearchperformanceagent-behaviour | 71/100 | today | |
| JuliusBrussee/cavemanpackages/agent/CLAUDE.md · 98k | CLAUDE.md | archtesting-strategysecuritydependencies+2 | 34/100 | today | |
| JuliusBrussee/cavemanpackages/cli/AGENTS.md · 98k | AGENTS.md | stylearchdependenciesmonorepo | 71/100 | today | |
| JuliusBrussee/cavemanpackages/cli/CLAUDE.md · 98k | CLAUDE.md | stylearchdependenciesmonorepo | 71/100 | today | |
| JuliusBrussee/cavemanpackages/create-caveman-agent/CLAUDE.md · 98k | CLAUDE.md | archdependenciesmonorepoagent-behaviour | 36/100 | today | |
| JuliusBrussee/cavemanpackages/graders/AGENTS.md · 98k | AGENTS.md | teststylearchtypes+3 | 75/100 | today | |
| JuliusBrussee/cavemanpackages/graders/CLAUDE.md · 98k | CLAUDE.md | teststylearchtypes+3 | 75/100 | today |
Same format, overlapping stack, ranked by quality.
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| n8n-io/n8npackages/@n8n/agents/AGENTS.md · 201k | AGENTS.md | buildteststylearch+3 | 100/100 | 14 days ago | |
| duckduckgo/content-scope-scriptsspecial-pages/AGENTS.md · 70 | AGENTS.md | buildteststylearch+3 | 100/100 | 14 days ago | |
| deepseek-ai/deepseek-harnessnative/landlock-run/AGENTS.md · 104k | AGENTS.md | setupteststylearch+3 | 100/100 | today | |
| code-yeongyu/oh-my-openagentpackages/web/AGENTS.md · 68k | AGENTS.md | setupbuildtestlint-format+6 | 100/100 | 13 days ago | |
| TryGhost/Ghoste2e/AGENTS.md · 55k | AGENTS.md | setupteststylearch+2 | 100/100 | today | |
| mui/material-uiAGENTS.md · 99k | AGENTS.md | setupbuildtestlint-format+9 | 100/100 | 14 days ago | |
| ethereum/go-ethereumAGENTS.md · 51k | AGENTS.md | buildtestlint-formatgit+1 | 100/100 | 14 days ago | |
| elastic/elasticsearchx-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/AGENTS.md · 78k | AGENTS.md | buildtestlint-formatstyle+2 | 100/100 | 14 days ago |
A badge carrying the measured quality of the strongest agent config file in this repository, out of 100. It reads from this index every time somebody loads your page, so it changes when the measurement changes and there is nothing to keep up to date. Free, no account, and the value is not something you or we can set by hand.
[](https://rulestack.kynth.studio/configs/juliusbrussee-caveman-packages-sdk-python-agents)Would rather not hotlink us? Every badge is also served in shields.io’s endpoint schema, so shields renders the image and your readers never talk to our domain:
Published by Toolproof, the masthead over this index and eight others. The method behind the number is at toolproof.kynth.studio/methodology, and the whole thing is readable as JSON with no key at /api.