{"id":50953467,"url":"https://github.com/outfox/loom","last_synced_at":"2026-06-18T04:01:31.719Z","repository":{"id":355637635,"uuid":"1155342605","full_name":"outfox/loom","owner":"outfox","description":"Layered Observation-Oriented Memory: LOOM is a context and cache manager for LLM and Agent conversations! 🪬💭","archived":false,"fork":false,"pushed_at":"2026-05-04T15:26:17.000Z","size":1001,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-04T16:36:46.215Z","etag":null,"topics":["ai","ai-agents","cache","context","context-engineering","context-management","language-model","large-language-models","llm"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/outfox.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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-02-11T12:04:31.000Z","updated_at":"2026-05-04T15:23:20.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/outfox/loom","commit_stats":null,"previous_names":["outfox/loom"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/outfox/loom","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/outfox%2Floom","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/outfox%2Floom/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/outfox%2Floom/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/outfox%2Floom/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/outfox","download_url":"https://codeload.github.com/outfox/loom/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/outfox%2Floom/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34475375,"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-18T02:00:06.871Z","response_time":128,"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":["ai","ai-agents","cache","context","context-engineering","context-management","language-model","large-language-models","llm"],"created_at":"2026-06-18T04:01:16.898Z","updated_at":"2026-06-18T04:01:31.695Z","avatar_url":"https://github.com/outfox.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"![loom logo](logo-loom.png)\n\n# LOOM\n\nA Python library for composing, interleaving, and compiling context from multiple sources into coherent prompts.\n\n## Installation\n\nLoom is not yet published to PyPI. Install from git:\n\n```bash\nuv add git+https://github.com/outfox/loom\n# or\npip install git+https://github.com/outfox/loom\n```\n\nA PyPI release under `context-loom` is planned.\n\n## Quick Start\n\n```python\nfrom loom import Context, StringEntry, FileEntry\n\nctx = Context(\"my-agent\")\n\n# Stable identity \u0026 rules\nctx.foundation.add(StringEntry(\"You are a helpful assistant.\"))\nctx.foundation.add(FileEntry(\"./SOUL.md\"))  # re-read on every compile\n\n# Current task\nctx.focus.add(StringEntry(\"Review PR #42 and suggest improvements.\"))\n\n# Pull in another context — its sections are interleaved with this one\nsecurity_ctx = Context(\"security\")\nsecurity_ctx.foundation.add(StringEntry(\"Always validate user input.\"))\nctx.include(security_ctx)\n\n# Render as Anthropic-style messages with explicit cache breakpoints\nmessages = ctx.to_messages(\n    cache_breakpoints=[\"foundation\", \"topic\"],\n    cache_ttl=3600,  # optional; sets Anthropic's max_age_seconds\n)\n\n# Drop straight into your LLM call\n# response = client.messages.create(model=\"claude-sonnet-4-6\", system=messages[0][\"content\"], ...)\n```\n\nFor plain-text output (e.g. dumping into a single system prompt), use `ctx.render()` instead.\n\n## Sections\n\nContext is organized into sections, compiled in this order:\n\n| Section | Purpose | Visitor Order |\n|---------|---------|---------------|\n| `foundation` | Core identity, rules | self → visitors |\n| `focus` | Current task/skill | visitors → self |\n| `topic` | What we're working on | self → visitors |\n| `convo` | Conversation history | visitors → self |\n| `step` | Current command output | self only (volatile) |\n| `attention` | Reinforcement/reminders | visitors → self |\n\n## Why this order? Prefix caching\n\nThe section order isn't arbitrary — it's optimized for **LLM prefix caching**:\n\n```text\n┌─────────────────────────────────┐\n│ FOUNDATION (stable)             │  ← Cached\n│ FOCUS (relatively stable)       │  ← Cached\n│ TOPIC (session-stable)          │  ← Cached\n│ CONVO (grows, but append-only)  │  ← Partially cached\n├─────────────────────────────────┤\n│ STEP (volatile, changes often)  │  ← Never cached\n│ ATTENTION (volatile)            │  ← Never cached\n└─────────────────────────────────┘\n```\n\nAll major LLM providers cache the beginning of your prompt and only recompute from where it changes. By putting stable content first and volatile content last, you get high cache hit rates, lower latency, and lower cost.\n\n`to_messages(cache_breakpoints=[...])` makes this explicit: each named section gets a `cache_control: {\"type\": \"ephemeral\"}` marker on its last block, and `cache_ttl=N` sets `max_age_seconds=N`. The `step` section is cleared after each compile, so everything before it stays byte-identical between turns.\n\n## Features\n\n### Cache-aware message rendering\n\n`to_messages()` produces Anthropic/OpenAI-style chat messages, with one content block per entry — cleaner section boundaries for the model than a single concatenated string.\n\n```python\nmessages = ctx.to_messages(\n    cache_breakpoints=[\"foundation\", \"topic\"],  # up to 4\n    cache_ttl=3600,                             # optional max_age_seconds\n    sections=[\"foundation\", \"focus\", \"topic\", \"convo\"],  # selective\n)\n```\n\n`sections=` lets you exclude volatile sections (e.g. when the caller manages `step` separately).\n\n### Visitors with deduplication\n\n`ctx.include(other)` interleaves another context's sections into this one's compile order. The same entry referenced from multiple contexts is compiled exactly once.\n\n```python\nshared = Context(\"shared-rules\")\nshared.foundation.add(FileEntry(\"./RULES.md\"))\n\nagent_a.include(shared)\nagent_b.include(shared)\n# RULES.md is compiled once per agent; if both feed into a parent context,\n# deduplication kicks in there too.\n```\n\n### File entries with frontmatter\n\n`FileEntry` re-reads the file on every compile, so edits show up without rebuilding the context. A YAML frontmatter block can promote the file out of the system prompt:\n\n```markdown\n---\nrole: assistant\n---\nHere is what I remember from our last session...\n```\n\nIn `to_messages()`, that file is emitted as its own `assistant` message after the system block, instead of being inlined into the system prompt.\n\n### Multimodal\n\n`ImageEntry` carries base64 image data and a media type. In `to_messages()` it's emitted inline as a vision content block (with a text caption so the model can refer back to it). In plain `render()` it falls back to `[Image: name]`.\n\n```python\nfrom loom import ImageEntry\n\nctx.topic.add(ImageEntry(data=b64, media_type=\"image/png\", name=\"screenshot\"))\n```\n\n### Lifecycle: `remember()`, `redact()`, entry IDs\n\nEvery entry gets a short, human-typeable id (e.g. `\"kvm\"`, `\"axr\"`) and a creation timestamp. IDs are recycled back into the pool when entries are removed, so long-lived processes don't exhaust them.\n\n```python\n# Promote a transient STEP entry into CONVO for long-term retention\nresult = ctx.step.add(StringEntry(tool_output))\nctx.remember(result)\n\n# Remove an entry by id, optionally leaving a tombstone\nctx.redact(\"kvm\", tombstone=\"[REDACTED: contained PII]\")\n```\n\n### Volatile `step`\n\nThe `step` section is cleared after every `compile()` / `to_messages()` call. Pass `clear_volatile=False` to keep it across calls.\n\n## Compaction (planned)\n\nA `Compactor` abstract base class lives in `loom.compactor`. The shipped implementation (`StubCompactor`) is a passthrough — it returns the compiled context unchanged. An LLM-backed compactor that summarizes older `convo` entries is the next milestone.\n\n## Development\n\nThe package layout is `src/loom/`; tests are in `tests/`.\n\n- **Test coverage:** run `pytest` after every change.\n- **String literals:** be careful with escaping — context compilation surfaces tricky edge cases. Tests help catch these.\n\n```bash\npytest\npytest --cov=loom  # with coverage\n```\n\n## License\n\nAGPL-3.0-or-later\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foutfox%2Floom","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Foutfox%2Floom","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foutfox%2Floom/lists"}