{"id":50879159,"url":"https://github.com/cachly-dev/sdk-python","last_synced_at":"2026-06-15T12:03:55.276Z","repository":{"id":351888255,"uuid":"1212925853","full_name":"cachly-dev/sdk-python","owner":"cachly-dev","description":"Official Cachly sdk-python SDK","archived":false,"fork":false,"pushed_at":"2026-04-16T21:46:29.000Z","size":101,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-16T23:38:39.022Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cachly-dev.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-16T21:45:44.000Z","updated_at":"2026-04-16T21:46:32.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/cachly-dev/sdk-python","commit_stats":null,"previous_names":["cachly-dev/sdk-python"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/cachly-dev/sdk-python","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cachly-dev%2Fsdk-python","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cachly-dev%2Fsdk-python/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cachly-dev%2Fsdk-python/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cachly-dev%2Fsdk-python/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cachly-dev","download_url":"https://codeload.github.com/cachly-dev/sdk-python/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cachly-dev%2Fsdk-python/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34361404,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-15T02:00:07.085Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-15T12:03:55.138Z","updated_at":"2026-06-15T12:03:55.265Z","avatar_url":"https://github.com/cachly-dev.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# cachly Python SDK\n\n\u003e Official Python SDK for [cachly.dev](https://cachly.dev) —  \n\u003e Managed Valkey/Redis cache built for AI apps. **GDPR-compliant · German servers · Live in 30 seconds.**\n\n[![PyPI](https://img.shields.io/pypi/v/cachly?color=blue\u0026logo=pypi)](https://pypi.org/project/cachly/)\n[![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue?logo=python)](https://pypi.org/project/cachly/)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](../../LICENSE)\n[![GDPR: EU-only](https://img.shields.io/badge/GDPR-EU%20only-green)](https://cachly.dev/legal)\n\n---\n\n## Installation\n\n```bash\npip install cachly\n# or\nuv add cachly\n```\n\n\u003e Requires Python 3.10+. Uses `redis-py` and `numpy` (for semantic cache).\n\n---\n\n## Quick Start\n\n```python\nimport os\nfrom cachly import CachlyClient\n\ncache = CachlyClient(url=os.environ[\"CACHLY_URL\"])\n\n# Set / Get\ncache.set(\"user:42\", {\"name\": \"Alice\"}, ttl=300)\nuser = cache.get(\"user:42\")           # returns dict or None\n\n# Get-or-Set pattern\nreport = cache.get_or_set(\"report:monthly\", lambda: db.run_expensive_report(), ttl=3600)\n\n# Atomic counter\nviews = cache.incr(\"page:views\")\n\ncache.close()\n```\n\nCreate your free instance at **[cachly.dev](https://cachly.dev)** — no credit card required.\n\n---\n\n## Async Usage\n\n```python\nfrom cachly.asyncio import AsyncCachlyClient\n\nasync def main():\n    cache = AsyncCachlyClient(url=os.environ[\"CACHLY_URL\"])\n\n    await cache.set(\"session:abc\", session_data, ttl=1800)\n    data = await cache.get(\"session:abc\")\n\n    await cache.close()\n```\n\n---\n\n## Semantic AI Cache\n\nCache LLM responses **by meaning**, not exact text. The same prompt phrased differently returns the cached answer — cutting OpenAI costs by up to 60 %.\n\n```python\nfrom cachly import SemanticOptions\n\nresult = cache.semantic.get_or_set(\n    prompt=user_question,\n    fn=lambda: openai_client.chat.completions.create(\n        model=\"gpt-4o\",\n        messages=[{\"role\": \"user\", \"content\": user_question}]\n    ),\n    embed_fn=lambda text: openai_client.embeddings.create(\n        model=\"text-embedding-3-small\", input=text\n    ).data[0].embedding,\n    options=SemanticOptions(similarity_threshold=0.92, ttl_seconds=3600),\n)\n\nprint(\"hit\" if result.hit else \"miss\", result.value)\n```\n\n---\n\n## Batch API — Multiple Ops in One Round-Trip\n\nBundle GET/SET/DEL/EXISTS/TTL operations into **one** HTTP request (or Redis pipeline).\n\n```python\nfrom cachly import CachlyClient, BatchOp\n\ncache = CachlyClient(\n    url=os.environ[\"CACHLY_URL\"],\n    batch_url=os.environ.get(\"CACHLY_BATCH_URL\"),  # optional\n)\n\nresult = await cache.batch([\n    BatchOp(\"get\",    \"user:1\"),\n    BatchOp(\"get\",    \"config:app\"),\n    BatchOp(\"set\",    \"visits\", str(time.time()), ttl=86400),\n    BatchOp(\"exists\", \"session:xyz\"),\n    BatchOp(\"ttl\",    \"token:abc\"),\n])\nuser    = result[0]   # str | None\nconfig  = result[1]   # str | None\nok      = result[2]   # bool\npresent = result[3]   # bool\nsecs    = result[4]   # int (-1 = no TTL, -2 = key missing)\n```\n\nWithout `batch_url` the method falls back automatically to a **Redis pipeline** (one TCP round-trip).\n\n---\n\n## Django / FastAPI Integration\n\n```python\n# FastAPI\nfrom fastapi import FastAPI\nfrom cachly import CachlyClient\n\napp = FastAPI()\ncache = CachlyClient(url=os.environ[\"CACHLY_URL\"])\n\n@app.on_event(\"shutdown\")\nasync def shutdown():\n    cache.close()\n\n@app.get(\"/data/{key}\")\nasync def get_data(key: str):\n    return cache.get_or_set(key, lambda: fetch_from_db(key), ttl=60)\n```\n\n---\n\n## AI Dev Brain — Persistent Memory for Your Coding Assistant\n\ncachly ships a **30-tool MCP server** that gives Claude Code, Cursor, GitHub Copilot, and Windsurf a persistent memory across sessions — so they never forget your architecture, lessons learned, or last session context.\n\n```bash\n# One-time setup\nnpx @cachly-dev/init\n```\n\nOr configure manually in your editor (`~/.vscode/mcp.json` / `.cursor/mcp.json`):\n\n```json\n{\n  \"servers\": {\n    \"cachly\": {\n      \"type\": \"stdio\",\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"@cachly-dev/mcp-server\"],\n      \"env\": { \"CACHLY_JWT\": \"your-jwt-token\" }\n    }\n  }\n}\n```\n\nAdd to your AI assistant instructions (e.g. `.github/copilot-instructions.md`):\n\n```markdown\n## cachly AI Brain\n\nAt the START of every session:\nsession_start(instance_id = \"your-instance-id\", focus = \"what you're working on today\")\n\nAt the END of every session:\nsession_end(instance_id = \"your-instance-id\", summary = \"...\", files_changed = [...])\n\nAfter any bug fix or deploy:\nlearn_from_attempts(instance_id = \"your-instance-id\", topic = \"category:keyword\",\n  outcome = \"success\", what_worked = \"...\", what_failed = \"...\", severity = \"major\")\n```\n\n`session_start` returns a full briefing in **one call**: last session summary, relevant lessons, open failures, brain health. 60 % fewer file reads, instant context, zero re-discovery.\n\n→ Full docs: [cachly.dev/docs/ai-memory](https://cachly.dev/docs/ai-memory)\n\n---\n\n## LLM Response Caching Proxy\n\nUse cachly as a **drop-in caching proxy** for OpenAI or Anthropic — no SDK changes needed:\n\n```bash\n# Instead of https://api.openai.com — use your cachly proxy URL:\nOPENAI_BASE_URL=https://api.cachly.dev/v1/llm-proxy/YOUR_TOKEN/openai\n\n# Anthropic:\nANTHROPIC_BASE_URL=https://api.cachly.dev/v1/llm-proxy/YOUR_TOKEN/anthropic\n```\n\nIdentical requests are served from cache with `X-Cachly-Cache: HIT`. Check savings via `GET /v1/llm-proxy/YOUR_TOKEN/stats`.\n\n---\n\n## Agent Workflow Persistence\n\nCheckpoint agent workflow state so agents can resume from the last completed step on crash:\n\n```python\nimport httpx\n\nbase = f\"https://api.cachly.dev/v1/workflow/{token}\"\n\n# Save a checkpoint after each workflow step\nhttpx.post(f\"{base}/checkpoints\", json={\n    \"run_id\":     \"my-run-123\",\n    \"step_index\": 0,\n    \"step_name\":  \"research\",\n    \"agent_name\": \"researcher\",\n    \"status\":     \"completed\",\n    \"state\":      json.dumps({\"topic\": \"AI caching\", \"results\": []}),\n})\n\n# Resume: get the latest checkpoint for a run\ncheckpoint = httpx.get(f\"{base}/runs/my-run-123/latest\").json()\n# → {\"step_index\": 2, \"step_name\": \"write\", \"state\": \"...\", \"status\": \"completed\"}\n```\n\n---\n\n## Connection Pooling \u0026 Keep-Alive\n\n```python\nfrom cachly import CachlyClient, CachlyConfig, PoolConfig\n\ncache = CachlyClient(config=CachlyConfig(\n    url=os.environ[\"CACHLY_URL\"],\n    pool=PoolConfig(\n        keep_alive_s=30,          # PING every 30s (prevents firewall idle-disconnect)\n        max_retries=10,           # reconnect retries with exponential backoff\n        base_retry_delay_s=0.1,   # first retry delay\n        max_retry_delay_s=10,     # retry delay cap\n        idle_timeout_s=300,       # auto-disconnect after 5 min idle (0 = disabled)\n        on_error=lambda e: print(f\"cachly error: {e}\"),\n        on_reconnect=lambda: print(\"cachly reconnected\"),\n    ),\n))\n```\n\n---\n\n## Retry with Exponential Backoff\n\nEvery command is automatically retried on transient errors (ConnectionError, TimeoutError, BusyLoadingError, …) using AWS-style full-jitter backoff:\n\n```python\nfrom cachly import CachlyClient, RetryConfig\n\ncache = CachlyClient(\n    url=os.environ[\"CACHLY_URL\"],\n    retry=RetryConfig(\n        max_retries=3,       # retry up to 3× (default)\n        base_delay_s=0.05,   # first retry after ~50ms\n        max_delay_s=2.0,     # cap at 2s\n    ),\n)\n```\n\nDisable retries with `RetryConfig(max_retries=0)`.\n\n---\n\n## OpenTelemetry Tracing\n\n```python\nfrom opentelemetry import trace\n\ncache = CachlyClient(\n    url=os.environ[\"CACHLY_URL\"],\n    otel_tracer=trace.get_tracer(\"my-app\"),\n)\n\n# Every get/set/delete/incr produces OTEL spans:\n#   span: \"cache.get\"  attributes: { cache.key: \"user:42\" }\n#   span: \"cache.set\"  attributes: { cache.key: \"user:42\", cache.ttl: 300 }\n```\n\n---\n\n## API Reference\n\n| Method | Description |\n|---|---|\n| `CachlyClient(url, batch_url=None, pool=None)` | Create client from Redis URL |\n| `get(key)` | Get value (`None` if missing); auto-deserialises JSON |\n| `set(key, value, ttl=None)` | Set value, optional TTL in seconds |\n| `delete(*keys)` | Delete one or more keys |\n| `exists(key) → bool` | Check existence |\n| `expire(key, seconds)` | Update TTL |\n| `incr(key) → int` | Atomic increment |\n| `get_or_set(key, fn, ttl=None)` | Get-or-set pattern |\n| `batch(ops) → BatchResult` | Bulk ops in one round-trip |\n| `semantic` | `SemanticCache` for AI workloads |\n| `raw` | Direct `redis.Redis` access |\n| `close()` | Close connection pool and stop keep-alive |\n\n---\n\n## Environment Variables\n\n```bash\nCACHLY_URL=redis://:your-password@my-app.cachly.dev:30101\nCACHLY_BATCH_URL=https://api.cachly.dev/v1/cache/YOUR_TOKEN   # optional\n```\n\n---\n\n## Links\n\n- 📖 [cachly.dev docs](https://cachly.dev/docs)\n- 🧠 [AI Memory / MCP Server](https://cachly.dev/docs/ai-memory)\n- 🐛 [Issues](https://github.com/cachly-dev/sdk-python/issues)\n- 📦 [PyPI](https://pypi.org/project/cachly/)\n\n---\n\nMIT © [cachly.dev](https://cachly.dev)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcachly-dev%2Fsdk-python","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcachly-dev%2Fsdk-python","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcachly-dev%2Fsdk-python/lists"}