{"id":50718195,"url":"https://github.com/Corpus-OS/corpusos","last_synced_at":"2026-06-26T22:00:41.049Z","repository":{"id":337854137,"uuid":"1086618616","full_name":"Corpus-OS/corpusos","owner":"Corpus-OS","description":"Open-source protocol suite standardizing LLM, Vector, Graph, and Embedding infrastructure across LangChain, LlamaIndex, AutoGen, CrewAI, Semantic Kernel, and MCP. 3,330+ conformance tests. One protocol. Any framework. Any provider.","archived":false,"fork":false,"pushed_at":"2026-03-27T00:32:33.000Z","size":8067,"stargazers_count":261,"open_issues_count":4,"forks_count":16,"subscribers_count":12,"default_branch":"main","last_synced_at":"2026-03-27T13:02:07.310Z","etag":null,"topics":["agents","ai-agents","ai-infrastructure","anthropic","autogen","chatgpt","crewai","enterprise","gemini","knowledge-graph","langchain","llamaindex","llm","mcp","multiagent","openai","python","rag","semantickernel","vector-database"],"latest_commit_sha":null,"homepage":"https://corpusos.com","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Corpus-OS.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":"NOTICE.md","maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-10-30T17:02:15.000Z","updated_at":"2026-03-27T00:32:38.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/Corpus-OS/corpusos","commit_stats":null,"previous_names":["corpus-os/corpus-protocol-suite"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/Corpus-OS/corpusos","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Corpus-OS%2Fcorpusos","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Corpus-OS%2Fcorpusos/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Corpus-OS%2Fcorpusos/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Corpus-OS%2Fcorpusos/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Corpus-OS","download_url":"https://codeload.github.com/Corpus-OS/corpusos/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Corpus-OS%2Fcorpusos/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34834415,"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-26T02:00:06.560Z","response_time":106,"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":["agents","ai-agents","ai-infrastructure","anthropic","autogen","chatgpt","crewai","enterprise","gemini","knowledge-graph","langchain","llamaindex","llm","mcp","multiagent","openai","python","rag","semantickernel","vector-database"],"created_at":"2026-06-09T21:00:25.963Z","updated_at":"2026-06-26T22:00:41.041Z","avatar_url":"https://github.com/Corpus-OS.png","language":"Python","funding_links":[],"categories":["*Ops for AI"],"sub_categories":["Model Serving \u0026 Inference"],"readme":"# Corpus OS \n\n\u003cimg width=\"1128\" height=\"191\" alt=\"image\" src=\"https://github.com/user-attachments/assets/cb2fe4ef-be6a-4406-b899-23ad1ed30c08\" /\u003e\n\n\n![Version](https://img.shields.io/badge/version-1.0.0-blue)\n![Python](https://img.shields.io/badge/python-3.10+-blue)\n![License](https://img.shields.io/badge/license-Apache--2.0-green)\n![LLM Protocol](https://img.shields.io/badge/LLM%20Protocol-100%25%20Conformant-brightgreen)\n![Vector Protocol](https://img.shields.io/badge/Vector%20Protocol-100%25%20Conformant-brightgreen)\n![Graph Protocol](https://img.shields.io/badge/Graph%20Protocol-100%25%20Conformant-brightgreen)\n![Embedding Protocol](https://img.shields.io/badge/Embedding%20Protocol-100%25%20Conformant-brightgreen)\n![Tests](https://img.shields.io/badge/Conformance%20Tests-3%2C330-brightgreen)\n\nReference implementation of the **[Corpus OS](https://corpusos.com)** — a **wire-first, vendor-neutral** SDK for interoperable AI frameworks and data backends across four domains: **LLM**, **Embedding**, **Vector**, and **Graph**.\n\n---\n**Contact:** [team@corpusos.com](mailto:team@corpusos.com) \n**Website:** [https://corpusos.com](https://corpusos.com) \n**Docs:** [https://docs.corpusos.com](https://docs.corpusos.com) \n\n---\n\n```\n┌──────────────────────────────────────────────────────────────────────┐\n│  Your App / Agents / RAG Pipelines                                   │\n│  (LangChain · LlamaIndex · Semantic Kernel · CrewAI · AutoGen · MCP) │\n├──────────────────────────────────────────────────────────────────────┤\n│  Corpus OS Protocol and SDK.                                         │\n│  One protocol · One error taxonomy · One metrics model               │\n├──────────┬──────────────┬────────────┬───────────────────────────────┤\n│ LLM/v1   │ Embedding/v1 │ Vector/v1  │ Graph/v1                      │\n├──────────┴──────────────┴────────────┴───────────────────────────────┤\n│  Any Provider: OpenAI · Anthropic · Pinecone · Neo4j · ...           │\n└──────────────────────────────────────────────────────────────────────┘\n```\n\n**Keep your frameworks. Standardize your infrastructure.**\n\n\u003e **Open-Core Model**: The **[Corpus OS](https://corpusos.com)** Protocol Suite and SDK are **fully open source** (Apache-2.0). Corpus Router and official production adapters are **commercial**, optional, and built on the same public protocols. Using this SDK does **not** lock you into CORPUS Router.\n\n---\n\n## Table of Contents\n\n1. [Why CORPUS](#why-corpus)\n2. [How CORPUS Compares](#how-corpus-compares)\n3. [When Not to Use CORPUS](#when-not-to-use-corpus)\n4. [Install](#install)\n5. [Quick Start](#-quick-start)\n6. [Domain Examples](#domain-examples)\n7. [Core Concepts](#core-concepts)\n8. [Error Taxonomy \u0026 Observability](#error-taxonomy--observability)\n9. [Performance \u0026 Configuration](#performance--configuration)\n10. [Testing \u0026 Conformance](#testing--conformance)\n11. [Documentation Layout](#-documentation-layout)\n12. [FAQ](#faq)\n13. [Contributing](#contributing)\n14. [License \u0026 Commercial Options](#license--commercial-options)\n\n---\n\n## Why Corpus OS\n\nModern AI platforms juggle multiple LLM, embedding, vector, and graph backends. Each vendor ships unique APIs, error schemes, rate limits, and capabilities — making cross-provider integration brittle and costly.\n\n**The problem:**\n\n- **Provider proliferation** — Dozens of incompatible APIs across AI infrastructure\n- **Duplicate integration** — Different error handling, observability, and resilience patterns rewritten per provider and framework\n- **Vendor lock-in** — Applications tightly coupled to specific backend choices\n- **Operational complexity** — Inconsistent monitoring and debugging across services\n\n**Corpus OS provides:**\n\n- **Stable, runtime-checkable protocols** across all four domains\n- **Normalized errors** with retry hints and machine-actionable scopes\n- **SIEM-safe metrics** (low-cardinality, tenant-hashed, no PII)\n- **Deadline propagation** for cancellation and cost control\n- **Two modes** — compose under your own router (`thin`) or use lightweight built-in infra (`standalone`)\n- **Wire-first design** — canonical JSON envelopes implementable in any language, with this SDK as reference\n\nCorpus OS is **not** a replacement for LangChain, LlamaIndex, Semantic Kernel, CrewAI, AutoGen, or MCP. Use those for agent-specific orchestration, agents, tools, and RAG pipelines. Use Corpus OS to standardize the **infrastructure layer underneath them**. Your app teams keep their frameworks. Your platform team gets one protocol, one error taxonomy, and one observability model across everything.\n\n---\n\n## How Corpus OS Compares\n\n| Aspect | LangChain / LlamaIndex | OpenRouter | MCP | **Corpus OS** |\n|---|---|---|---|---|\n| **Scope** | Application framework | LLM unification | Tools \u0026 data sources | **AI infrastructure protocols** |\n| **Domains** | LLM + Tools | LLM only | Tools + Data | **LLM + Vector + Graph + Embedding** |\n| **Error Standardization** | Partial | Limited | N/A | **Comprehensive taxonomy** |\n| **Multi-Provider Routing** | Basic | Managed service | N/A | **Protocol for any router** |\n| **Observability** | Basic | Limited | N/A | **Built-in metrics + tracing** |\n| **Vendor Neutrality** | High | Service-dependent | High | **Protocol-first, no lock-in** |\n\n### Who is this for?\n\n- **App developers** — Keep using your framework of choice. Talk to all backends through Corpus OS protocols. Swap providers or frameworks without rewriting integration code.\n- **Framework maintainers** — Implement one CORPUS adapter per protocol. Instantly support any conformant backend.\n- **Backend vendors** — Implement `llm/v1`, `embedding/v1`, `vector/v1`, or `graph/v1` once, run the conformance suite, and your service works with every framework.\n- **Platform / infra teams** — Unified observability: normalized error codes, deadlines, and metrics. One set of dashboards and SLOs across all AI traffic.\n- **MCP users** — The Corpus OS MCP server exposes protocols as standard MCP tools. Any MCP client can call into your infra with consistent behavior.\n\n### Integration Patterns\n\n| Pattern | How It Works | What You Get |\n|---|---|---|\n| Framework → Corpus OS → Providers | Framework uses Corpus OS as client | Unified errors/metrics across providers |\n| Corpus OS → Framework-as-adapter → Providers | Framework wrapped as Corpus OS adapter | Reuse existing chains/indices as \"providers\" |\n| Mixed | Both of the above | Gradual migration, no big-bang rewrites |\n\nLarge teams typically run all three patterns at once.\n\n---\n\n## When Not to Use CORPUS\n\nYou probably don't need Corpus OS if:\n\n- **Single-provider and happy** — One backend, fine with their SDK and breaking changes.\n- **No governance pressure** — No per-tenant isolation, budgets, audit trails, or data residency.\n- **No cross-domain orchestration** — Not coordinating LLM + Vector + Graph + Embedding together.\n- **Quick throwaway prototype** — Lock-in, metrics, and resilience aren't worth thinking about yet.\n\nIf any of these stop being true, `corpus_sdk` is the incremental next step.\n\n---\n\n## Install\n\n```bash\npip install corpus_sdk\n```\n\nPython ≥ 3.10 recommended. No heavy runtime dependencies.\n\n---\n\n## ⚡ Quick Start\n\n```python\nimport asyncio\nfrom corpus_sdk.llm.llm_base import (\n    BaseLLMAdapter, OperationContext, LLMCompletion,\n    LLMCapabilities, TokenUsage\n)\n\nclass QuickAdapter(BaseLLMAdapter):\n    async def _do_capabilities(self) -\u003e LLMCapabilities:\n        return LLMCapabilities(\n            server=\"quick-demo\",\n            version=\"1.0.0\",\n            model_family=\"demo\",\n            max_context_length=4096,\n        )\n    \n    async def _do_complete(self, messages, model=None, **kwargs) -\u003e LLMCompletion:\n        return LLMCompletion(\n            text=\"Hello from CORPUS!\",\n            model=model or \"quick-demo\",\n            model_family=\"demo\",\n            usage=TokenUsage(prompt_tokens=2, completion_tokens=3, total_tokens=5),\n            finish_reason=\"stop\",\n        )\n    \n    async def _do_count_tokens(self, text, *, model=None, ctx=None) -\u003e int:\n        return len(text.split())  # Simple word count\n    \n    async def _do_health(self, *, ctx=None) -\u003e dict:\n        return {\"ok\": True, \"server\": \"quick-demo\"}\n\n# Usage\nasync def main():\n    print(\"=\" * 80)\n    print(\"Quick LLM Adapter Demo\")\n    print(\"=\" * 80)\n    \n    adapter = QuickAdapter()\n    ctx = OperationContext(request_id=\"test-123\")\n    \n    # Test 1: Capabilities\n    caps = await adapter.capabilities()\n    print(f\"\\n✅ Capabilities:\")\n    print(f\"   Server: {caps.server} v{caps.version}\")\n    print(f\"   Model family: {caps.model_family}\")\n    print(f\"   Max context: {caps.max_context_length}\")\n    \n    # Test 2: Completion\n    result = await adapter.complete(\n        messages=[{\"role\": \"user\", \"content\": \"Hi\"}], \n        ctx=ctx\n    )\n    print(f\"\\n✅ Completion:\")\n    print(f\"   Response: {result.text}\")\n    print(f\"   Model: {result.model}\")\n    print(f\"   Tokens used: {result.usage.total_tokens} (prompt: {result.usage.prompt_tokens}, completion: {result.usage.completion_tokens})\")\n    print(f\"   Finish reason: {result.finish_reason}\")\n    \n    # Test 3: Token counting\n    tokens = await adapter.count_tokens(\"This is a test message\")\n    print(f\"\\n✅ Token Counting:\")\n    print(f\"   Text: 'This is a test message'\")\n    print(f\"   Tokens: {tokens}\")\n    \n    # Test 4: Health check\n    health = await adapter.health()\n    print(f\"\\n✅ Health Check:\")\n    print(f\"   OK: {health.get('ok', False)}\")\n    print(f\"   Server: {health.get('server', 'unknown')}\")\n    \n    print(\"\\n\" + \"=\" * 80)\n    print(\"✅ All tests passed!\")\n    print(\"=\" * 80)\n\nif __name__ == \"__main__\":\n    asyncio.run(main())\n```\n\nA complete quick start with all four protocols is in [`docs/guides/QUICK_START.md`](docs/guides/QUICK_START.md).\n\n---\n\n## Domain Examples\n\n\u003e **Minimal viable adapter:** Implement `_do_capabilities`, your core operation (`_do_embed`, `_do_complete`, `_do_query`, etc.), and `_do_health`. All other methods have safe no-op defaults — you only override what you need.\n\n\u003e In all examples, swap `Example*Adapter` with your actual adapter class that inherits the corresponding base and implements `_do_*` hooks.\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eEmbeddings\u003c/strong\u003e\u003c/summary\u003e\n\n```python\nimport asyncio\nfrom corpus_sdk.embedding.embedding_base import (\n    BaseEmbeddingAdapter, EmbedSpec, OperationContext,\n    EmbeddingVector, EmbeddingCapabilities, EmbedResult\n)\n\nclass QuickEmbeddingAdapter(BaseEmbeddingAdapter):\n    async def _do_capabilities(self) -\u003e EmbeddingCapabilities:\n        return EmbeddingCapabilities(\n            server=\"quick-embeddings\",\n            version=\"1.0.0\",\n            supported_models=(\"quick-embed-001\",),\n            max_batch_size=128,\n            max_text_length=8192,\n            supports_normalization=True,\n            normalizes_at_source=False,\n            supports_deadline=True,\n            supports_token_counting=False,\n        )\n\n    async def _do_embed(self, spec: EmbedSpec, *, ctx=None) -\u003e EmbedResult:\n        vec = [0.1, 0.2, 0.3]\n        return EmbedResult(\n            embedding=EmbeddingVector(\n                vector=vec,\n                text=spec.text,\n                model=spec.model,\n                dimensions=len(vec)\n            ),\n            model=spec.model,\n            text=spec.text,\n            tokens_used=None,\n            truncated=False,\n        )\n\n    async def _do_health(self, *, ctx=None) -\u003e dict:\n        return {\"ok\": True, \"server\": \"quick-embeddings\", \"version\": \"1.0.0\"}\n\n# Usage\nasync def main():\n    print(\"=\" * 80)\n    print(\"Quick Embedding Adapter Demo\")\n    print(\"=\" * 80)\n    \n    async with QuickEmbeddingAdapter() as adapter:\n        ctx = OperationContext(request_id=\"req-1\", tenant=\"acme\")\n        \n        # Test 1: Capabilities\n        caps = await adapter.capabilities()\n        print(f\"\\n✅ Capabilities:\")\n        print(f\"   Server: {caps.server} v{caps.version}\")\n        print(f\"   Supported models: {caps.supported_models}\")\n        print(f\"   Max batch size: {caps.max_batch_size}\")\n        print(f\"   Max text length: {caps.max_text_length}\")\n        print(f\"   Supports normalization: {caps.supports_normalization}\")\n        print(f\"   Supports deadline: {caps.supports_deadline}\")\n        \n        # Test 2: Embedding\n        res = await adapter.embed(\n            EmbedSpec(text=\"hello world\", model=\"quick-embed-001\"), ctx=ctx\n        )\n        print(f\"\\n✅ Embedding:\")\n        print(f\"   Text: '{res.text}'\")\n        print(f\"   Vector: {res.embedding.vector}\")\n        print(f\"   Dimensions: {res.embedding.dimensions}\")\n        print(f\"   Model: {res.model}\")\n        print(f\"   Truncated: {res.truncated}\")\n        \n        # Test 3: Multiple embeddings\n        texts = [\"first text\", \"second text\", \"third text\"]\n        print(f\"\\n✅ Multiple Embeddings:\")\n        for i, text in enumerate(texts, 1):\n            res = await adapter.embed(\n                EmbedSpec(text=text, model=\"quick-embed-001\"), ctx=ctx\n            )\n            print(f\"   {i}. '{text}' → {res.embedding.dimensions}D vector\")\n        \n        # Test 4: Health check\n        health = await adapter.health()\n        print(f\"\\n✅ Health Check:\")\n        print(f\"   OK: {health.get('ok', False)}\")\n        print(f\"   Server: {health.get('server', 'unknown')} v{health.get('version', 'unknown')}\")\n        \n        print(\"\\n\" + \"=\" * 80)\n        print(\"✅ All tests passed!\")\n        print(\"=\" * 80)\n\nif __name__ == \"__main__\":\n    asyncio.run(main())\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eLLM\u003c/strong\u003e\u003c/summary\u003e\n\n```python\nimport asyncio\nfrom corpus_sdk.llm.llm_base import (\n    BaseLLMAdapter, OperationContext, LLMCompletion,\n    TokenUsage, LLMCapabilities\n)\n\nclass QuickLLMAdapter(BaseLLMAdapter):\n    async def _do_capabilities(self) -\u003e LLMCapabilities:\n        return LLMCapabilities(\n            server=\"quick-llm\",\n            version=\"1.0.0\",\n            model_family=\"gpt-4\",\n            max_context_length=8192,\n            supports_streaming=True,\n            supports_roles=True,\n            supports_json_output=False,\n            supports_parallel_tool_calls=False,\n            idempotent_writes=False,\n            supports_multi_tenant=True,\n            supports_system_message=True,\n        )\n\n    async def _do_complete(self, messages, model, **kwargs) -\u003e LLMCompletion:\n        return LLMCompletion(\n            text=\"Hello from quick-llm!\",\n            model=model,\n            model_family=\"gpt-4\",\n            usage=TokenUsage(prompt_tokens=5, completion_tokens=5, total_tokens=10),\n            finish_reason=\"stop\",\n        )\n\n    async def _do_count_tokens(self, text, *, model=None, ctx=None) -\u003e int:\n        return len(text.split())  # Simple word count\n\n    async def _do_health(self, *, ctx=None) -\u003e dict:\n        return {\"ok\": True, \"server\": \"quick-llm\", \"version\": \"1.0.0\"}\n\n# Usage\nasync def main():\n    print(\"=\" * 80)\n    print(\"Quick LLM Adapter Demo\")\n    print(\"=\" * 80)\n    \n    async with QuickLLMAdapter() as adapter:\n        ctx = OperationContext(request_id=\"req-2\", tenant=\"acme\")\n        \n        # Test 1: Capabilities\n        caps = await adapter.capabilities()\n        print(f\"\\n✅ Capabilities:\")\n        print(f\"   Server: {caps.server} v{caps.version}\")\n        print(f\"   Model family: {caps.model_family}\")\n        print(f\"   Max context length: {caps.max_context_length}\")\n        print(f\"   Supports streaming: {caps.supports_streaming}\")\n        print(f\"   Supports roles: {caps.supports_roles}\")\n        print(f\"   Supports system message: {caps.supports_system_message}\")\n        print(f\"   Supports multi-tenant: {caps.supports_multi_tenant}\")\n        \n        # Test 2: Completion\n        resp = await adapter.complete(\n            messages=[{\"role\": \"user\", \"content\": \"Say hi\"}],\n            model=\"quick-llm-001\",\n            ctx=ctx,\n        )\n        print(f\"\\n✅ Completion:\")\n        print(f\"   Response: {resp.text}\")\n        print(f\"   Model: {resp.model}\")\n        print(f\"   Model family: {resp.model_family}\")\n        print(f\"   Tokens: {resp.usage.total_tokens} (prompt: {resp.usage.prompt_tokens}, completion: {resp.usage.completion_tokens})\")\n        print(f\"   Finish reason: {resp.finish_reason}\")\n        \n        # Test 3: Multi-message conversation\n        messages = [\n            {\"role\": \"system\", \"content\": \"You are a helpful assistant\"},\n            {\"role\": \"user\", \"content\": \"Hello\"},\n            {\"role\": \"assistant\", \"content\": \"Hi there!\"},\n            {\"role\": \"user\", \"content\": \"How are you?\"}\n        ]\n        resp = await adapter.complete(messages=messages, model=\"quick-llm-001\", ctx=ctx)\n        print(f\"\\n✅ Multi-turn Conversation:\")\n        print(f\"   Messages: {len(messages)} turns\")\n        print(f\"   Response: {resp.text}\")\n        \n        # Test 4: Token counting\n        test_text = \"This is a longer text to count tokens for testing purposes\"\n        tokens = await adapter.count_tokens(test_text, model=\"quick-llm-001\")\n        print(f\"\\n✅ Token Counting:\")\n        print(f\"   Text: '{test_text}'\")\n        print(f\"   Tokens: {tokens}\")\n        \n        # Test 5: Health check\n        health = await adapter.health()\n        print(f\"\\n✅ Health Check:\")\n        print(f\"   OK: {health.get('ok', False)}\")\n        print(f\"   Server: {health.get('server', 'unknown')} v{health.get('version', 'unknown')}\")\n        \n        print(\"\\n\" + \"=\" * 80)\n        print(\"✅ All tests passed!\")\n        print(\"=\" * 80)\n\nif __name__ == \"__main__\":\n    asyncio.run(main())\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eVector\u003c/strong\u003e\u003c/summary\u003e\n\n```python\n\nimport asyncio\nfrom corpus_sdk.vector.vector_base import (\n    BaseVectorAdapter, VectorCapabilities, QuerySpec, UpsertSpec, UpsertResult,\n    QueryResult, Vector, VectorMatch, OperationContext, VectorID\n)\n\nclass QuickVectorAdapter(BaseVectorAdapter):\n    def __init__(self):\n        super().__init__()\n        # Simple in-memory storage\n        self.vectors = {}\n    \n    async def _do_capabilities(self) -\u003e VectorCapabilities:\n        return VectorCapabilities(\n            server=\"quick-vector\",\n            version=\"1.0.0\",\n            max_dimensions=3\n        )\n\n    async def _do_upsert(self, spec: UpsertSpec, *, ctx=None) -\u003e UpsertResult:\n        \"\"\"Store vectors in memory\"\"\"\n        ns = spec.namespace or \"default\"\n        if ns not in self.vectors:\n            self.vectors[ns] = []\n        self.vectors[ns].extend(spec.vectors)\n        \n        return UpsertResult(\n            upserted_count=len(spec.vectors),\n            failed_count=0,\n            failures=[]\n        )\n\n    async def _do_query(self, spec: QuerySpec, *, ctx=None) -\u003e QueryResult:\n        \"\"\"Search for similar vectors\"\"\"\n        ns = spec.namespace or \"default\"\n        stored_vectors = self.vectors.get(ns, [])\n        \n        # Simple cosine similarity\n        def cosine_sim(a, b):\n            dot = sum(x * y for x, y in zip(a, b))\n            mag_a = sum(x * x for x in a) ** 0.5\n            mag_b = sum(x * x for x in b) ** 0.5\n            return dot / (mag_a * mag_b) if mag_a and mag_b else 0\n        \n        matches = []\n        for vec in stored_vectors:\n            score = cosine_sim(spec.vector, vec.vector)\n            matches.append(VectorMatch(vector=vec, score=score, distance=1-score))\n        \n        # Sort by score descending\n        matches.sort(key=lambda m: m.score, reverse=True)\n        top_matches = matches[:spec.top_k] if spec.top_k else matches\n        \n        return QueryResult(\n            matches=top_matches,\n            query_vector=spec.vector,\n            namespace=ns,\n            total_matches=len(top_matches),\n        )\n\n    async def _do_health(self, *, ctx=None) -\u003e dict:\n        return {\"ok\": True, \"server\": \"quick-vector\", \"version\": \"1.0.0\"}\n\n# Usage - Complete flow\nasync def main():\n    print(\"=\" * 80)\n    print(\"Quick Vector Adapter Demo\")\n    print(\"=\" * 80)\n    \n    adapter = QuickVectorAdapter()\n    ctx = OperationContext(request_id=\"req-3\", tenant=\"acme\")\n    \n    # Test 1: Capabilities\n    caps = await adapter.capabilities()\n    print(f\"\\n✅ Capabilities:\")\n    print(f\"   Server: {caps.server} v{caps.version}\")\n    print(f\"   Max dimensions: {caps.max_dimensions}\")\n    \n    # Test 2: Upsert vectors\n    vectors_to_add = [\n        Vector(id=VectorID(\"v1\"), vector=[0.1, 0.2, 0.3], metadata={\"label\": \"first\"}),\n        Vector(id=VectorID(\"v2\"), vector=[0.4, 0.5, 0.6], metadata={\"label\": \"second\"}),\n        Vector(id=VectorID(\"v3\"), vector=[0.7, 0.8, 0.9], metadata={\"label\": \"third\"}),\n    ]\n    \n    upsert_result = await adapter.upsert(\n        UpsertSpec(vectors=vectors_to_add),\n        ctx=ctx\n    )\n    print(f\"\\n✅ Upsert:\")\n    print(f\"   Upserted: {upsert_result.upserted_count} vectors\")\n    print(f\"   Failed: {upsert_result.failed_count} vectors\")\n    \n    # Test 3: Query for similar vectors (top_k=2 REQUIRED by SDK)\n    query_result = await adapter.query(\n        QuerySpec(vector=[0.1, 0.2, 0.3], top_k=2),\n        ctx=ctx\n    )\n    print(f\"\\n✅ Query (top 2):\")\n    print(f\"   Query vector: {query_result.query_vector}\")\n    print(f\"   Total matches: {query_result.total_matches}\")\n    print(f\"   Results:\")\n    for i, match in enumerate(query_result.matches, 1):\n        print(f\"      {i}. ID: {match.vector.id}, Score: {match.score:.3f}, Distance: {match.distance:.3f}\")\n        print(f\"         Metadata: {match.vector.metadata}\")\n    \n    # Test 4: Query all vectors (with top_k=10)\n    query_result_all = await adapter.query(\n        QuerySpec(vector=[0.5, 0.5, 0.5], top_k=10),\n        ctx=ctx\n    )\n    print(f\"\\n✅ Query (all matches):\")\n    print(f\"   Query vector: [0.5, 0.5, 0.5]\")\n    print(f\"   Total matches: {query_result_all.total_matches}\")\n    for i, match in enumerate(query_result_all.matches, 1):\n        print(f\"      {i}. ID: {match.vector.id}, Score: {match.score:.3f}\")\n    \n    # Test 5: Namespace support\n    ns_vectors = [\n        Vector(id=VectorID(\"ns1\"), vector=[0.9, 0.8, 0.7], metadata={\"type\": \"namespace_test\"}),\n    ]\n    upsert_ns = await adapter.upsert(\n        UpsertSpec(vectors=ns_vectors, namespace=\"test-namespace\"),\n        ctx=ctx\n    )\n    print(f\"\\n✅ Namespace Support:\")\n    print(f\"   Upserted {upsert_ns.upserted_count} vector(s) to 'test-namespace'\")\n    \n    query_ns = await adapter.query(\n        QuerySpec(vector=[0.9, 0.8, 0.7], top_k=5, namespace=\"test-namespace\"),\n        ctx=ctx\n    )\n    print(f\"   Query in 'test-namespace': {query_ns.total_matches} match(es)\")\n    \n    # Test 6: Health check\n    health = await adapter.health()\n    print(f\"\\n✅ Health Check:\")\n    print(f\"   OK: {health.get('ok', False)}\")\n    print(f\"   Server: {health.get('server', 'unknown')} v{health.get('version', 'unknown')}\")\n    \n    print(\"\\n\" + \"=\" * 80)\n    print(\"✅ All tests passed!\")\n    print(\"=\" * 80)\n\nif __name__ == \"__main__\":\n    asyncio.run(main())\n\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eGraph\u003c/strong\u003e\u003c/summary\u003e\n\n```python\nimport asyncio\nfrom corpus_sdk.graph.graph_base import (\n    BaseGraphAdapter, GraphCapabilities, UpsertNodesSpec,\n    Node, GraphID, OperationContext, GraphQuerySpec, QueryResult\n)\n\nclass QuickGraphAdapter(BaseGraphAdapter):\n    def __init__(self):\n        super().__init__()\n        # Simple in-memory storage\n        self.nodes = {}\n    \n    async def _do_capabilities(self) -\u003e GraphCapabilities:\n        return GraphCapabilities(\n            server=\"quick-graph\",\n            version=\"1.0.0\",\n            supported_query_dialects=(\"cypher\",),\n            supports_stream_query=True,\n            supports_bulk_vertices=True,\n            supports_batch=True,\n            supports_schema=True,\n        )\n\n    async def _do_upsert_nodes(self, spec: UpsertNodesSpec, *, ctx=None):\n        # Store nodes in memory\n        for node in spec.nodes:\n            self.nodes[str(node.id)] = node\n        \n        from corpus_sdk.graph.graph_base import UpsertResult\n        return UpsertResult(\n            upserted_count=len(spec.nodes),\n            failed_count=0,\n            failures=[]\n        )\n\n    async def _do_query(self, spec: GraphQuerySpec, *, ctx=None) -\u003e QueryResult:\n        return QueryResult(\n            records=[{\"id\": 1, \"name\": \"Ada\"}],\n            summary={\"rows\": 1},\n            dialect=spec.dialect,\n            namespace=spec.namespace or \"default\",\n        )\n\n    async def _do_health(self, *, ctx=None) -\u003e dict:\n        return {\"ok\": True, \"server\": \"quick-graph\", \"version\": \"1.0.0\"}\n\n# Usage\nasync def main():\n    print(\"=\" * 80)\n    print(\"Quick Graph Adapter Demo\")\n    print(\"=\" * 80)\n    \n    async with QuickGraphAdapter() as adapter:\n        ctx = OperationContext(request_id=\"req-4\", tenant=\"acme\")\n        \n        # Test 1: Capabilities\n        caps = await adapter.capabilities()\n        print(f\"\\n✅ Capabilities:\")\n        print(f\"   Server: {caps.server} v{caps.version}\")\n        print(f\"   Supported dialects: {caps.supported_query_dialects}\")\n        print(f\"   Supports streaming: {caps.supports_stream_query}\")\n        print(f\"   Supports bulk vertices: {caps.supports_bulk_vertices}\")\n        print(f\"   Supports batch: {caps.supports_batch}\")\n        print(f\"   Supports schema: {caps.supports_schema}\")\n        \n        # Test 2: Upsert nodes\n        result = await adapter.upsert_nodes(\n            UpsertNodesSpec(nodes=[\n                Node(\n                    id=GraphID(\"user:1\"),\n                    labels=(\"User\",),\n                    properties={\"name\": \"Ada\"}\n                ),\n                Node(\n                    id=GraphID(\"user:2\"),\n                    labels=(\"User\",),\n                    properties={\"name\": \"Bob\"}\n                )\n            ])\n        )\n        print(f\"\\n✅ Upsert Nodes:\")\n        print(f\"   Upserted: {result.upserted_count} nodes\")\n        print(f\"   Failed: {result.failed_count} nodes\")\n        \n        # Test 3: Query the graph\n        query_result = await adapter.query(\n            GraphQuerySpec(\n                text=\"MATCH (u:User) RETURN u.name\",\n                dialect=\"cypher\"\n            )\n        )\n        print(f\"\\n✅ Query:\")\n        print(f\"   Query: MATCH (u:User) RETURN u.name\")\n        print(f\"   Dialect: {query_result.dialect}\")\n        print(f\"   Records: {query_result.records}\")\n        print(f\"   Summary: {query_result.summary}\")\n        \n        # Test 4: Check stored nodes\n        print(f\"\\n✅ Storage Check:\")\n        print(f\"   Total nodes in memory: {len(adapter.nodes)}\")\n        for node_id, node in adapter.nodes.items():\n            print(f\"   - {node_id}: labels={node.labels}, properties={node.properties}\")\n        \n        # Test 5: Health check\n        health = await adapter.health()\n        print(f\"\\n✅ Health Check:\")\n        print(f\"   OK: {health.get('ok', False)}\")\n        print(f\"   Server: {health.get('server', 'unknown')} v{health.get('version', 'unknown')}\")\n        \n        print(\"\\n\" + \"=\" * 80)\n        print(\"✅ All tests passed!\")\n        print(\"=\" * 80)\n\nif __name__ == \"__main__\":\n    asyncio.run(main())\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003e RAG flow \u003c/strong\u003e\u003c/summary\u003e\n\n```python\n\"\"\"\nRAG (Retrieval-Augmented Generation) Example\nDemonstrates combining Embedding, Vector, and LLM protocols\n\"\"\"\nimport asyncio\nfrom corpus_sdk.llm.llm_base import (\n    BaseLLMAdapter, OperationContext, LLMCompletion,\n    TokenUsage, LLMCapabilities\n)\nfrom corpus_sdk.embedding.embedding_base import (\n    BaseEmbeddingAdapter, EmbedSpec, EmbeddingVector, \n    EmbeddingCapabilities, EmbedResult\n)\nfrom corpus_sdk.vector.vector_base import (\n    BaseVectorAdapter, VectorCapabilities, QuerySpec, UpsertSpec, UpsertResult,\n    QueryResult, Vector, VectorMatch, VectorID\n)\n\n\n# 1. Embedding Adapter\nclass QuickEmbeddingAdapter(BaseEmbeddingAdapter):\n    async def _do_capabilities(self) -\u003e EmbeddingCapabilities:\n        return EmbeddingCapabilities(\n            server=\"quick-embeddings\",\n            version=\"1.0.0\",\n            supported_models=(\"quick-embed-001\",),\n            max_batch_size=128,\n            max_text_length=8192,\n        )\n\n    async def _do_embed(self, spec: EmbedSpec, *, ctx=None) -\u003e EmbedResult:\n        # Deterministic embedding based on text content\n        vec = [hash(spec.text + str(i)) % 1000 / 1000.0 for i in range(384)]\n        return EmbedResult(\n            embedding=EmbeddingVector(\n                vector=vec,\n                text=spec.text,\n                model=spec.model,\n                dimensions=len(vec)\n            ),\n            model=spec.model,\n            text=spec.text,\n            tokens_used=len(spec.text.split()),\n            truncated=False,\n        )\n\n    async def _do_health(self, *, ctx=None) -\u003e dict:\n        return {\"ok\": True, \"server\": \"quick-embeddings\"}\n\n\n# 2. Vector Store Adapter\nclass QuickVectorAdapter(BaseVectorAdapter):\n    def __init__(self):\n        super().__init__()\n        self.vectors = {}\n    \n    async def _do_capabilities(self) -\u003e VectorCapabilities:\n        return VectorCapabilities(\n            server=\"quick-vector\",\n            version=\"1.0.0\",\n            max_dimensions=384\n        )\n\n    async def _do_upsert(self, spec: UpsertSpec, *, ctx=None) -\u003e UpsertResult:\n        ns = spec.namespace or \"default\"\n        if ns not in self.vectors:\n            self.vectors[ns] = []\n        self.vectors[ns].extend(spec.vectors)\n        \n        return UpsertResult(\n            upserted_count=len(spec.vectors),\n            failed_count=0,\n            failures=[]\n        )\n\n    async def _do_query(self, spec: QuerySpec, *, ctx=None) -\u003e QueryResult:\n        ns = spec.namespace or \"default\"\n        stored = self.vectors.get(ns, [])\n        \n        # Cosine similarity\n        def cosine_sim(a, b):\n            dot = sum(x * y for x, y in zip(a, b))\n            mag_a = sum(x * x for x in a) ** 0.5\n            mag_b = sum(x * x for x in b) ** 0.5\n            return dot / (mag_a * mag_b) if mag_a and mag_b else 0\n        \n        matches = []\n        for vec in stored:\n            score = cosine_sim(spec.vector, vec.vector)\n            matches.append(VectorMatch(vector=vec, score=score, distance=1-score))\n        \n        matches.sort(key=lambda m: m.score, reverse=True)\n        top_k = matches[:spec.top_k] if spec.top_k else matches\n        \n        return QueryResult(\n            matches=top_k,\n            query_vector=spec.vector,\n            namespace=ns,\n            total_matches=len(top_k),\n        )\n\n    async def _do_health(self, *, ctx=None) -\u003e dict:\n        return {\"ok\": True, \"server\": \"quick-vector\"}\n\n\n# 3. LLM Adapter\nclass QuickLLMAdapter(BaseLLMAdapter):\n    async def _do_capabilities(self) -\u003e LLMCapabilities:\n        return LLMCapabilities(\n            server=\"quick-llm\",\n            version=\"1.0.0\",\n            model_family=\"gpt-4\",\n            max_context_length=8192,\n        )\n\n    async def _do_complete(self, messages, model, **kwargs) -\u003e LLMCompletion:\n        # Extract the last user message\n        user_msg = messages[-1][\"content\"] if messages else \"\"\n        \n        # Simple mock: generate response based on context presence\n        if \"Corpus SDK\" in user_msg:\n            response = \"Based on the documentation, Corpus SDK is a protocol suite that provides standardized interfaces for LLM, Embedding, Vector, and Graph operations. It enables backend-agnostic AI applications.\"\n        elif \"domains\" in user_msg.lower():\n            response = \"The SDK supports four core domains: LLM (language models), Embedding (text vectorization), Vector (similarity search), and Graph (knowledge graphs).\"\n        elif \"integrate\" in user_msg.lower() or \"backend\" in user_msg.lower():\n            response = \"To integrate a backend, create an adapter class that inherits from the appropriate base (BaseLLMAdapter, BaseEmbeddingAdapter, etc.) and implement the _do_* hook methods like _do_complete, _do_embed, or _do_query.\"\n        else:\n            response = \"I can provide information about Corpus SDK based on the available documentation.\"\n            \n        return LLMCompletion(\n            text=response,\n            model=model,\n            model_family=\"gpt-4\",\n            usage=TokenUsage(\n                prompt_tokens=len(user_msg.split()),\n                completion_tokens=len(response.split()),\n                total_tokens=len(user_msg.split()) + len(response.split())\n            ),\n            finish_reason=\"stop\",\n        )\n\n    async def _do_count_tokens(self, text, *, model=None, ctx=None) -\u003e int:\n        return len(text.split())\n\n    async def _do_health(self, *, ctx=None) -\u003e dict:\n        return {\"ok\": True, \"server\": \"quick-llm\"}\n\n\n# 4. RAG Pipeline\nclass RAGPipeline:\n    \"\"\"Combines embedding, vector search, and LLM for RAG\"\"\"\n    \n    def __init__(self):\n        self.embedder = QuickEmbeddingAdapter()\n        self.vector_db = QuickVectorAdapter()\n        self.llm = QuickLLMAdapter()\n        self.ctx = OperationContext(request_id=\"rag-demo\", tenant=\"quickstart\")\n    \n    async def index_documents(self, documents: list[str]) -\u003e int:\n        \"\"\"Index documents into the vector database\"\"\"\n        vectors = []\n        \n        for i, doc in enumerate(documents):\n            # Embed the document\n            embed_result = await self.embedder.embed(\n                EmbedSpec(text=doc, model=\"quick-embed-001\"),\n                ctx=self.ctx\n            )\n            \n            # Create vector with metadata\n            vectors.append(Vector(\n                id=VectorID(f\"doc-{i}\"),\n                vector=embed_result.embedding.vector,\n                metadata={\"text\": doc, \"doc_id\": i}\n            ))\n        \n        # Upsert to vector store\n        result = await self.vector_db.upsert(\n            UpsertSpec(vectors=vectors),\n            ctx=self.ctx\n        )\n        \n        return result.upserted_count\n    \n    async def query(self, question: str, top_k: int = 3) -\u003e dict:\n        \"\"\"Answer a question using RAG\"\"\"\n        \n        # Step 1: Embed the question\n        question_embed = await self.embedder.embed(\n            EmbedSpec(text=question, model=\"quick-embed-001\"),\n            ctx=self.ctx\n        )\n        \n        # Step 2: Search for relevant documents\n        search_results = await self.vector_db.query(\n            QuerySpec(vector=question_embed.embedding.vector, top_k=top_k),\n            ctx=self.ctx\n        )\n        \n        # Step 3: Build context from top matches\n        context_docs = [\n            match.vector.metadata[\"text\"]\n            for match in search_results.matches\n        ]\n        context = \"\\n\\n\".join(f\"[{i+1}] {doc}\" for i, doc in enumerate(context_docs))\n        \n        # Step 4: Generate answer with LLM\n        prompt = f\"\"\"Use the following context to answer the question.\n\nContext:\n{context}\n\nQuestion: {question}\n\nAnswer:\"\"\"\n        \n        completion = await self.llm.complete(\n            messages=[{\"role\": \"user\", \"content\": prompt}],\n            model=\"quick-llm-001\",\n            ctx=self.ctx\n        )\n        \n        return {\n            \"answer\": completion.text,\n            \"sources\": context_docs,\n            \"relevance_scores\": [m.score for m in search_results.matches],\n            \"tokens_used\": completion.usage.total_tokens,\n        }\n\n\n# Usage Example\nasync def main():\n    print(\"=\" * 70)\n    print(\"RAG Pipeline Demo - Corpus SDK\")\n    print(\"=\" * 70)\n    \n    # Initialize pipeline\n    rag = RAGPipeline()\n    \n    # Knowledge base documents\n    documents = [\n        \"Corpus SDK is a protocol suite for building LLM applications with standardized interfaces.\",\n        \"The SDK supports four core domains: LLM, Embedding, Vector, and Graph protocols.\",\n        \"Adapters implement _do_* hooks (_do_complete, _do_embed, _do_query, etc.) to integrate any backend.\",\n        \"All SDK examples are tested and production-ready, ensuring reliability.\",\n        \"The protocol-based design enables swapping backends without changing application code.\",\n    ]\n    \n    # Index documents\n    print(\"\\n📚 Indexing documents...\")\n    count = await rag.index_documents(documents)\n    print(f\"✅ Indexed {count} documents\\n\")\n    \n    # Ask questions\n    questions = [\n        \"What is Corpus SDK?\",\n        \"How many domains does the SDK support?\",\n        \"How do I integrate my backend?\",\n    ]\n    \n    for i, question in enumerate(questions, 1):\n        print(f\"\\n{'─' * 70}\")\n        print(f\"Question {i}: {question}\")\n        print('─' * 70)\n        \n        result = await rag.query(question, top_k=2)\n        \n        print(f\"\\n💡 Answer:\\n{result['answer']}\\n\")\n        print(f\"📊 Relevance Scores: {[f'{s:.3f}' for s in result['relevance_scores']]}\")\n        print(f\"🔢 Tokens Used: {result['tokens_used']}\")\n        print(f\"\\n📎 Sources:\")\n        for j, source in enumerate(result['sources'], 1):\n            print(f\"  [{j}] {source}\")\n    \n    print(\"\\n\" + \"=\" * 70)\n    print(\"✅ RAG Demo Complete!\")\n    print(\"=\" * 70)\n\n\nif __name__ == \"__main__\":\n    asyncio.run(main())\n\n```\n\u003c/details\u003e\n\nFull implementations with batch operations, streaming, and multi-cloud scenarios are in [`docs/guides/ADAPTER_RECIPES.md`](docs/guides/ADAPTER_RECIPES.md).\n\n---\n\n## Core Concepts\n\n- **Protocol vs Base** — Protocols define required behavior. Bases implement validation, deadlines, observability, and error normalization. You implement `_do_*` hooks.\n- **OperationContext** — Carries `request_id`, `idempotency_key`, `deadline_ms`, `traceparent`, `tenant`, and cache hints across all operations.\n- **Wire Protocol** — Canonical envelopes (`op`, `ctx`, `args`) and response shapes (`ok`, `code`, `result`) defined in [`docs/spec/PROTOCOL.md`](docs/spec/PROTOCOL.md).\n- **Corpus OS-Compatible** — Implementations that honor the envelopes, reserved `op` strings, and error taxonomy. Validated by the conformance suite.\n\n---\n\n## Error Taxonomy \u0026 Observability\n\nAll domains share a **normalized error taxonomy**: `BadRequest`, `AuthError`, `ResourceExhausted`, `TransientNetwork`, `Unavailable`, `NotSupported`, `DeadlineExceeded`, plus domain-specific errors like `TextTooLong`, `ModelOverloaded`, `DimensionMismatch`, and `IndexNotReady`.\n\nErrors carry **machine-actionable hints** (`retry_after_ms`, `throttle_scope`) so routers and control planes can react consistently across providers. A pluggable `MetricsSink` protocol lets you bring your own metrics backend. Bases emit one `observe` per operation, hash tenants before recording, and never log prompts, vectors, or raw tenant IDs.\n\nFull details in [`docs/spec/ERRORS.md`](docs/spec/ERRORS.md) and [`docs/spec/METRICS.md`](docs/spec/METRICS.md).\n\n---\n\n## Performance \u0026 Configuration\n\nBase overhead is typically **\u003c10 ms** relative to vendor SDK calls: validation \u003c1 ms, metrics \u003c0.1 ms, cache lookup (standalone) \u003c0.5 ms. Async-first design avoids blocking and supports high concurrency. Batch operations (`embed_batch`, vector upserts, graph batch) are preferred for throughput.\n\nBenchmarks and deployment patterns in [`docs/guides/IMPLEMENTATION.md`](docs/guides/IMPLEMENTATION.md).\n\n### Modes: `thin` vs `standalone`\n\nOnce you're ready for production, choose a mode:\n\n| Mode | Infra Hooks | When to Use |\n|---|---|---|\n| **`thin`** (default) | All no-ops | You have an external control plane (router, scheduler, limiter) |\n| **`standalone`** | Deadline enforcement, circuit breaker, token-bucket limiter, in-memory TTL cache | Lightweight deployments without external infra |\n\nUse `thin` under a router to prevent double-stacking resiliency. Use `standalone` for prototyping or single-service deployments.\n\n---\n\n## Testing \u0026 Conformance\n\n### One-Command Testing\n\n```bash\n# All protocols at once\nmake test-all-conformance\n\n# Specific protocols\nmake test-llm-conformance\nmake test-vector-conformance\nmake test-graph-conformance\nmake test-embedding-conformance\n```\n\n### CLI\n\n```bash\ncorpus-sdk verify                        # All protocols\ncorpus-sdk verify -p llm -p vector       # Selected protocols\ncorpus-sdk test-llm-conformance          # Single protocol\n```\n\n### Direct pytest\n\n```bash\npytest tests/ -v --cov=corpus_sdk --cov-report=html\n```\n\nRequirements for \"CORPUS-Compatible\" certification are in [`docs/conformance/CERTIFICATION.md`](docs/conformance/CERTIFICATION.md).\n\n---\n\n## 📚 Documentation Layout\n\n**Spec (normative):** [`docs/spec/`](docs/spec/)\n\n| File | Contents |\n|---|---|\n| `SPECIFICATION.md` | Full protocol suite specification (all domains, cross-cutting behavior) |\n| `PROTOCOL.md` | Wire-level envelopes, streaming semantics, canonical `op` registry |\n| `ERRORS.md` | Canonical error taxonomy \u0026 mapping rules |\n| `METRICS.md` | Metrics schema \u0026 SIEM-safe observability |\n| `SCHEMA.md` | JSON/type shapes |\n| `VERSIONING.md` | Semantic versioning \u0026 compatibility rules |\n\n**Guides (how-to):** [`docs/guides/`](docs/guides/)\n\n| File | Contents |\n|---|---|\n| `QUICK_START.md` | End-to-end flows for all four protocols |\n| `IMPLEMENTATION.md` | How to implement adapters |\n| `ADAPTER_RECIPES.md` | Real-world multi-cloud and RAG scenarios |\n| `CONFORMANCE_GUIDE.md` | How to run \u0026 interpret conformance suites |\n\n**Conformance (testing):** [`docs/conformance/`](docs/conformance/) — Per-protocol test specs, schema conformance, behavioral conformance, and certification requirements.\n\n---\n\n## FAQ\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eIs the SDK open source?\u003c/strong\u003e\u003c/summary\u003e\n\nYes. The SDK (protocols, bases, example adapters) is open source under Apache-2.0. CORPUS Router and official production adapters are commercial.\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eDo I have to use CORPUS Router?\u003c/strong\u003e\u003c/summary\u003e\n\nNo. The SDK composes with any router or control plane. CORPUS Router is optional and adheres to the same public protocols.\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eHow does CORPUS relate to LangChain / LlamaIndex / MCP / OpenRouter?\u003c/strong\u003e\u003c/summary\u003e\n\nThey're complementary. LangChain/LlamaIndex are application frameworks. MCP standardizes tools and data sources. OpenRouter unifies LLM providers. CORPUS standardizes the infrastructure layer (LLM + Vector + Graph + Embedding) underneath all of them with consistent errors, metrics, and capabilities discovery.\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eWhy async-only?\u003c/strong\u003e\u003c/summary\u003e\n\nModern AI workloads require high concurrency. Async-first prevents blocking the event loop. Sync wrappers can be built on top if needed.\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eWhat happens if my adapter raises a non-normalized error?\u003c/strong\u003e\u003c/summary\u003e\n\nBases catch unexpected exceptions and record them as `UnhandledException` in metrics. Wrap provider errors in normalized exceptions for proper handling.\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eCan CORPUS Router run on-prem?\u003c/strong\u003e\u003c/summary\u003e\n\nYes. Available as managed cloud or on-prem deployment for regulated and air-gapped environments.\n\u003c/details\u003e\n\n---\n\n## Troubleshooting\n\n| Problem | Solution |\n|---|---|\n| Double-stacked resiliency (timeouts firing twice) | Ensure `mode=\"thin\"` under your router |\n| Circuit breaker opens frequently | Reduce concurrency or switch to `thin` with external circuit breaker |\n| Cache returns stale results | Verify sampling params in cache key; check `cache_ttl_s` |\n| `DeadlineExceeded` on fast operations | Ensure `deadline_ms` is absolute epoch time, not relative. Check NTP sync. |\n| Health check failures | Inspect `_do_health` implementation; verify backend reachability and credentials |\n\n```python\n# Debug mode\nimport logging\nlogging.basicConfig(level=logging.DEBUG)\nlogging.getLogger(\"corpus_sdk\").setLevel(logging.DEBUG)\n```\n\n---\n\n## Contributing\n\n```bash\ngit clone https://github.com/Corpus-OS/corpusos.git\ncd corpus-sdk\npip install -e \".[dev]\"\npytest\n```\n\nFollow PEP-8 (ruff/black). Type hints required on all public APIs. Include tests for new features. Maintain low-cardinality metrics — never add PII to `extra` fields. Observe SemVer.\n\nWe especially welcome **community adapter contributions** for new LLM, vector, graph, and embedding backends.\n\nCommunity questions: [GitHub Discussions](https://github.com/corpus/corpus-sdk/discussions) preferred.\n\n---\n\n## License \u0026 Commercial Options\n\n**License:** Apache-2.0 ([`LICENSE`](LICENSE))\n\n| Need | Solution | Cost |\n|---|---|---|\n| Learning / prototyping | `corpus_sdk` + example adapters | **Free (OSS)** |\n| Production with your own infra | `corpus_sdk` + your adapters | **Free (OSS)** |\n| Production with official adapters | `corpus_sdk` + Official Adapters | **Commercial** |\n| Enterprise multi-provider | `corpus_sdk` + CORPUS Router (Managed or On-Prem) | **Commercial** |\n\n---\n**Contact:** [team@corpusos.com](mailto:team@corpusos.com) \n**Website:** [https://corpusos.com](https://corpusos.com) \n**Docs:** [https://docs.corpusos.com](https://docs.corpusos.com) \n\n---\n\n**Built by the Corpus OS team** — wire-level AI infrastructure you integrate once and connect anywhere.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FCorpus-OS%2Fcorpusos","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FCorpus-OS%2Fcorpusos","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FCorpus-OS%2Fcorpusos/lists"}