{"id":47598118,"url":"https://github.com/aiperceivable/apcore-python","last_synced_at":"2026-04-19T11:01:11.856Z","repository":{"id":338230491,"uuid":"1156938533","full_name":"aiperceivable/apcore-python","owner":"aiperceivable","description":"A schema-driven module development framework for Python.","archived":false,"fork":false,"pushed_at":"2026-04-13T00:15:22.000Z","size":1470,"stargazers_count":3,"open_issues_count":0,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-13T02:21:42.944Z","etag":null,"topics":["agent-foundation","agent-observability","agent-schema","ai","ai-agent-skd","ai-agents","ai-api-specification","ai-executor","ai-plugin-architecture"],"latest_commit_sha":null,"homepage":"https://aipartnerup.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/aiperceivable.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":".github/CODEOWNERS","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-13T08:26:36.000Z","updated_at":"2026-04-13T00:15:25.000Z","dependencies_parsed_at":"2026-02-22T07:04:16.369Z","dependency_job_id":"8b02c5c6-214a-4a02-87a2-2483535e545f","html_url":"https://github.com/aiperceivable/apcore-python","commit_stats":null,"previous_names":["aipartnerup/apcore-python","aiperceivable/apcore-python"],"tags_count":21,"template":false,"template_full_name":null,"purl":"pkg:github/aiperceivable/apcore-python","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aiperceivable%2Fapcore-python","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aiperceivable%2Fapcore-python/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aiperceivable%2Fapcore-python/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aiperceivable%2Fapcore-python/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aiperceivable","download_url":"https://codeload.github.com/aiperceivable/apcore-python/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aiperceivable%2Fapcore-python/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32004043,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-18T20:23:30.271Z","status":"online","status_checked_at":"2026-04-19T02:00:07.110Z","response_time":55,"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":["agent-foundation","agent-observability","agent-schema","ai","ai-agent-skd","ai-agents","ai-api-specification","ai-executor","ai-plugin-architecture"],"created_at":"2026-04-01T18:31:08.291Z","updated_at":"2026-04-19T11:01:11.811Z","avatar_url":"https://github.com/aiperceivable.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/aiperceivable/apcore/main/apcore-logo.svg\" alt=\"apcore logo\" width=\"200\"/\u003e\n\u003c/div\u003e\n\n# apcore\n\n![Python](https://img.shields.io/badge/python-3.11+-blue.svg)\n![License](https://img.shields.io/badge/license-Apache%202.0-green.svg)\n[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/12294/badge)](https://www.bestpractices.dev/projects/12294)\n\n\u003e **Build once, invoke by Code or AI.**\n\nA schema-enforced module standard for the AI-Perceivable era.\n\n**apcore** is an AI-Perceivable module standard that makes every interface naturally perceivable and understandable by AI through enforced Schema definitions and behavioral annotations. It provides strict type safety, access control, middleware pipelines, and built-in observability — enabling you to define modules with structured input/output schemas that are easily consumed by both code and AI.\n\n## Features\n\n- **Schema-driven modules** -- Define input/output contracts using Pydantic models (with automatic validation) or plain JSON Schema dicts\n- **Execution Pipeline** -- Context creation, safety checks, ACL enforcement, approval gate, validation, middleware chains, and execution with timeout support\n- **`@module` decorator** -- Turn plain functions into fully schema-aware modules with zero boilerplate\n- **YAML bindings** -- Register modules declaratively without modifying source code\n- **Access control (ACL)** -- Pattern-based, first-match-wins rules with wildcard support\n- **Middleware system** -- Composable before/after hooks with error recovery\n- **Observability** -- Tracing (spans), metrics collection, and structured context logging\n- **Async support** -- Seamless sync and async module execution\n- **Safety guards** -- Call depth limits, circular call detection, frequency throttling\n- **Approval system** -- Pluggable approval gate (Step 5) with sync/async handlers, Phase B resume, and audit events\n- **Extension points** -- Unified extension management for discoverers, middleware, ACL, approval handlers, span exporters, and module validators\n- **Async task management** -- Background module execution with status tracking, cancellation, and concurrency limiting\n- **Behavioral annotations** -- Declare module traits (readonly, destructive, idempotent, cacheable, paginated, streaming) for AI-aware orchestration\n- **W3C Trace Context** -- traceparent header injection/extraction for distributed tracing interop\n\n## API Overview\n\n**Core**\n\n| Class | Description |\n|-------|-------------|\n| `APCore` | High-level client -- register modules, call, stream, validate |\n| `Registry` | Module storage -- discover, register, get, list, watch |\n| `Executor` | Execution engine -- call with middleware pipeline, ACL, approval |\n| `Context` | Request context -- trace ID, identity, call chain, cancel token |\n| `Config` | Configuration -- load from YAML, get/set values, namespace-partitioned Config Bus |\n| `Identity` | Caller identity -- id, type, roles, attributes |\n| `FunctionModule` | Wrapped function module created by `@module` decorator |\n\n**Access Control \u0026 Approval**\n\n| Class | Description |\n|-------|-------------|\n| `ACL` | Access control -- rule-based caller/target authorization |\n| `ApprovalHandler` | Pluggable approval gate protocol |\n| `AlwaysDenyHandler` / `AutoApproveHandler` / `CallbackApprovalHandler` | Built-in approval handlers |\n\n**Middleware**\n\n| Class | Description |\n|-------|-------------|\n| `Middleware` | Pipeline hooks -- before/after/on_error interception |\n| `BeforeMiddleware` / `AfterMiddleware` | Single-phase middleware adapters |\n| `LoggingMiddleware` | Structured logging middleware |\n| `RetryMiddleware` | Automatic retry with backoff |\n| `ErrorHistoryMiddleware` | Records errors into ErrorHistory |\n| `PlatformNotifyMiddleware` | Emits events on error rate/latency spikes |\n| `ObsLoggingMiddleware` | Observability-aware structured logging middleware |\n\n**Schema**\n\n| Class | Description |\n|-------|-------------|\n| `SchemaLoader` | Load schemas from YAML or native types |\n| `SchemaValidator` | Validate data against schemas |\n| `SchemaExporter` | Export schemas for MCP, OpenAI, Anthropic, generic |\n| `RefResolver` | Resolve `$ref` references in JSON Schema |\n\n**Observability**\n\n| Class | Description |\n|-------|-------------|\n| `TracingMiddleware` | Distributed tracing with span export |\n| `MetricsMiddleware` / `MetricsCollector` | Call count, latency, error rate metrics |\n| `ContextLogger` | Context-aware structured logging |\n| `ErrorHistory` | Ring buffer of recent errors with deduplication |\n| `UsageCollector` | Per-module usage statistics and trends |\n| `UsageMiddleware` | Per-call usage tracking middleware |\n| `TraceContext` | W3C Trace Context propagation (traceparent/tracestate) |\n| `InMemoryExporter` | Span exporter that stores spans in memory |\n| `StdoutExporter` | Span exporter that writes spans to stdout |\n| `OTLPExporter` | Span exporter using OpenTelemetry Protocol |\n\n**Events \u0026 Extensions**\n\n| Class | Description |\n|-------|-------------|\n| `EventEmitter` | Event system -- subscribe, emit, flush |\n| `WebhookSubscriber` / `A2ASubscriber` | Built-in event subscribers |\n| `ExtensionManager` | Unified extension point management |\n| `AsyncTaskManager` | Background module execution with status tracking |\n| `CancelToken` | Cooperative cancellation token |\n| `BindingLoader` | Load modules from YAML binding files |\n| `ErrorCodeRegistry` | Central registry for structured error codes |\n| `ErrorFormatterRegistry` | Surface-specific error formatter registry (MCP, A2A, CLI adapters) |\n\n## Configuration\n\n### Config Bus and Namespace Registration\n\n`Config` doubles as an ecosystem-level Config Bus. Any package can register a named namespace with optional JSON Schema validation, env prefix, and default values:\n\n```python\nfrom apcore import Config\n\n# Register a namespace (class-level, shared across all Config instances)\nConfig.register_namespace(\n    \"my_plugin\",\n    schema={\"type\": \"object\", \"properties\": {\"timeout_ms\": {\"type\": \"integer\"}}},\n    env_prefix=\"MY_PLUGIN__\",\n    defaults={\"timeout_ms\": 5000},\n)\n\n# Load config as usual\nconfig = Config.load(\"project.yaml\")\n\n# Namespace-aware access\ntimeout = config.get(\"my_plugin.timeout_ms\")   # dot-path with namespace resolution\nsubtree = config.namespace(\"my_plugin\")          # full subtree as dict\n\n# Typed access\nconfig.get_typed(\"my_plugin.timeout_ms\", int)\n\n# Mount an external source (no unified YAML required)\nconfig.mount(\"my_plugin\", from_dict={\"timeout_ms\": 3000})\n\n# Introspect registered namespaces\nnames = Config.registered_namespaces()\n```\n\n### Built-in Namespaces\n\napcore pre-registers two namespaces that promote its existing flat config keys:\n\n| Namespace | Env prefix | Keys |\n|-----------|-----------|------|\n| `observability` | `APCORE_OBSERVABILITY` | tracing, metrics, logging, error_history, platform_notify |\n| `sys_modules` | `APCORE_SYS` | thresholds.error_rate, thresholds.latency_p99_ms |\n\n### Environment Variable Conventions\n\n| Pattern | When to use | Example |\n|---------|------------|---------|\n| `APCORE_KEY_NAME` | Override a flat top-level apcore key (existing convention) | `APCORE_EXECUTOR_DEFAULT__TIMEOUT=5000` |\n| `APCORE_NAMESPACE` prefix | Override keys inside a registered namespace | `APCORE_OBSERVABILITY_TRACING_ENABLED=true` |\n| Custom prefix declared in `register_namespace` | Third-party packages with their own prefix | `MY_PLUGIN__TIMEOUT_MS=3000` |\n\nThe longest-prefix-match dispatch algorithm ensures that `APCORE_OBSERVABILITY_TRACING_ENABLED` routes to the `observability` namespace (not to a core flat key). Within each namespace, a single `_` maps to `.` and `__` maps to a literal `_`.\n\n### New Error Codes (0.15.0)\n\n| Code | Meaning |\n|------|---------|\n| `CONFIG_NAMESPACE_DUPLICATE` | A namespace with this name is already registered |\n| `CONFIG_NAMESPACE_RESERVED` | The namespace name is reserved (`_config`, `apcore`) |\n| `CONFIG_ENV_PREFIX_CONFLICT` | Two namespaces share the same env prefix |\n| `CONFIG_MOUNT_ERROR` | Failed to load or parse a mounted config source |\n| `CONFIG_BIND_ERROR` | Failed to deserialize a namespace subtree into the requested type |\n| `ERROR_FORMATTER_DUPLICATE` | A formatter for this surface is already registered |\n\n### Event Type Names\n\nCanonical event type names use dot-namespaced identifiers. `apcore.*` is reserved for core framework events; adapter packages use their own prefix (e.g., `apcore-mcp.*`).\n\n| Canonical name | Replaces | Emitted by |\n|---------------|---------|-----------|\n| `apcore.module.toggled` | `module_health_changed` | `system.control.toggle_feature` |\n| `apcore.health.recovered` | `module_health_changed` | `PlatformNotifyMiddleware` (error rate recovery) |\n| `apcore.config.updated` | `config_changed` | `system.control.update_config` |\n| `apcore.module.reloaded` | `config_changed` | `system.control.reload_module` |\n\nThe legacy short-form names are still emitted alongside the canonical names during the transition period.\n\n## Documentation\n\nFor full documentation, including Quick Start guides for both Python and TypeScript, visit:\n**[https://aiperceivable.github.io/apcore/getting-started.html](https://aiperceivable.github.io/apcore/getting-started.html)**\n\n## Requirements\n\n- Python \u003e= 3.11\n\n## Installation\n\n```bash\npip install apcore\n```\n\n### Development\n\n```bash\npip install -e \".[dev]\"\n```\n\n## Quick Start\n\n### Simple usage (Global Client)\n\nFor simple scripts or prototypes, you can use the global `apcore` functions:\n\n```python\nimport apcore\n\n@apcore.module(id=\"math.add\", description=\"Add two integers\")\ndef add(a: int, b: int) -\u003e int:\n    return a + b\n\n# Directly call it\nresult = apcore.call(\"math.add\", {\"a\": 10, \"b\": 5})\nprint(result)  # {'result': 15}\n```\n\n### Simplified Client (Recommended)\n\nThe `APCore` client provides a unified entry point that manages everything for you:\n\n```python\nfrom apcore import APCore\n\nclient = APCore()\n\n@client.module(id=\"math.add\", description=\"Add two integers\")\ndef add(a: int, b: int) -\u003e int:\n    return a + b\n\n# Call the module\nresult = client.call(\"math.add\", {\"a\": 10, \"b\": 5})\nprint(result)  # {'result': 15}\n```\n\n### Advanced: Define a module with a class\n\n```python\nfrom pydantic import BaseModel\nfrom apcore import Context, APCore\n\nclient = APCore()\n\nclass GreetInput(BaseModel):\n    name: str\n\nclass GreetOutput(BaseModel):\n    message: str\n\nclass GreetModule:\n    input_schema = GreetInput\n    output_schema = GreetOutput\n    description = \"Greet a user\"\n\n    def execute(self, inputs: dict, context: Context) -\u003e dict:\n        return {\"message\": f\"Hello, {inputs['name']}!\"}\n\nclient.register(\"greet\", GreetModule())\nresult = client.call(\"greet\", {\"name\": \"Alice\"})\n# {\"message\": \"Hello, Alice!\"}\n```\n\n### Alternative: Define schemas with plain dicts\n\nIf you prefer not to use Pydantic, pass raw JSON Schema dicts directly:\n\n```python\nfrom apcore import APCore\n\nclient = APCore()\n\nclass WeatherModule:\n    input_schema = {\"type\": \"object\", \"properties\": {\"city\": {\"type\": \"string\"}}}\n    output_schema = {\"type\": \"object\", \"properties\": {\"temp\": {\"type\": \"number\"}}}\n    description = \"Get current temperature\"\n\n    def execute(self, inputs: dict, context=None) -\u003e dict:\n        return {\"temp\": 22.5}\n\nclient.register(\"weather\", WeatherModule())\nresult = client.call(\"weather\", {\"city\": \"Tokyo\"})\n# {\"temp\": 22.5}\n```\n\n\u003e **Note:** Dict schemas skip Pydantic input validation. Use Pydantic models when you need automatic type coercion and validation, or validate inside `execute()`.\n\n### Add middleware\n\n```python\nfrom apcore import LoggingMiddleware, TracingMiddleware\n\nclient.use(LoggingMiddleware())\nclient.use(TracingMiddleware())\n```\n\n\n### Access control\n\n```python\nfrom apcore import ACL, ACLRule, Executor, Registry\n\nregistry = Registry()\nacl = ACL(rules=[\n    ACLRule(callers=[\"admin.*\"], targets=[\"*\"], effect=\"allow\", description=\"Admins can call anything\"),\n    ACLRule(callers=[\"*\"], targets=[\"admin.*\"], effect=\"deny\", description=\"Others cannot call admin modules\"),\n])\nexecutor = Executor(registry=registry, acl=acl)\n```\n\n## Examples\n\nThe `examples/` directory contains runnable demos:\n\n---\n\n### `simple_client` — APCore client with decorator-based modules\n\nInitializes an `APCore` client, registers modules with `@client.module()`, and calls them directly.\n\n```python\nfrom apcore import APCore\n\nclient = APCore()\n\n@client.module(id=\"math.add\", description=\"Add two integers\")\ndef add(a: int, b: int) -\u003e int:\n    return a + b\n\nresult = client.call(\"math.add\", {\"a\": 10, \"b\": 5})\nprint(result)  # {'result': 15}\n\n@client.module(id=\"greet\")\ndef greet(name: str, greeting: str = \"Hello\") -\u003e dict:\n    return {\"message\": f\"{greeting}, {name}!\"}\n\nresult = client.call(\"greet\", {\"name\": \"Alice\"})\nprint(result)  # {'message': 'Hello, Alice!'}\n```\n\n---\n\n### `global_client` — Minimal global client usage\n\nNo explicit initialization needed — use the default global client directly.\n\n```python\nimport apcore\n\n@apcore.module(id=\"math.add\")\ndef add(a: int, b: int) -\u003e int:\n    return a + b\n\nresult = apcore.call(\"math.add\", {\"a\": 10, \"b\": 5})\nprint(result)  # {'result': 15}\n```\n\n---\n\n### `greet` — Duck-typed module with Pydantic schemas\n\nDemonstrates the class-based module interface with Pydantic `BaseModel` for input/output schemas.\n\n```python\nfrom pydantic import BaseModel\n\nclass GreetInput(BaseModel):\n    name: str\n\nclass GreetOutput(BaseModel):\n    message: str\n\nclass GreetModule:\n    input_schema = GreetInput\n    output_schema = GreetOutput\n    description = \"Greet a user by name\"\n\n    def execute(self, inputs: dict, context) -\u003e dict:\n        name = inputs[\"name\"]\n        return {\"message\": f\"Hello, {name}!\"}\n```\n\n---\n\n### `get_user` — Readonly module with `ModuleAnnotations`\n\nDemonstrates behavioral annotations (`readonly`, `idempotent`) and simulated database lookup.\n\n```python\nfrom pydantic import BaseModel\nfrom apcore.module import ModuleAnnotations\n\nclass GetUserInput(BaseModel):\n    user_id: str\n\nclass GetUserOutput(BaseModel):\n    id: str\n    name: str\n    email: str\n\nclass GetUserModule:\n    input_schema = GetUserInput\n    output_schema = GetUserOutput\n    description = \"Get user details by ID\"\n    annotations = ModuleAnnotations(readonly=True, idempotent=True)\n\n    _users = {\n        \"user-1\": {\"id\": \"user-1\", \"name\": \"Alice\", \"email\": \"alice@example.com\"},\n        \"user-2\": {\"id\": \"user-2\", \"name\": \"Bob\", \"email\": \"bob@example.com\"},\n    }\n\n    def execute(self, inputs: dict, context) -\u003e dict:\n        user_id = inputs[\"user_id\"]\n        user = self._users.get(user_id)\n        if user is None:\n            return {\"id\": user_id, \"name\": \"Unknown\", \"email\": \"unknown@example.com\"}\n        return dict(user)\n```\n\n---\n\n### `send_email` — Destructive module with sensitive fields and ContextLogger\n\nShows `x-sensitive` on schema fields (for log redaction), `ModuleAnnotations` with metadata, `ModuleExample` for AI-perceivable documentation, and `ContextLogger` usage.\n\n```python\nfrom pydantic import BaseModel, Field\nfrom apcore.module import ModuleAnnotations, ModuleExample\nfrom apcore.observability import ContextLogger\n\nclass SendEmailInput(BaseModel):\n    to: str\n    subject: str\n    body: str\n    api_key: str = Field(..., json_schema_extra={\"x-sensitive\": True})\n\nclass SendEmailOutput(BaseModel):\n    status: str\n    message_id: str\n\nclass SendEmailModule:\n    input_schema = SendEmailInput\n    output_schema = SendEmailOutput\n    description = \"Send an email message\"\n    tags = [\"email\", \"communication\", \"external\"]\n    version = \"1.2.0\"\n    metadata = {\"provider\": \"example-smtp\", \"max_retries\": 3}\n    annotations = ModuleAnnotations(destructive=True, idempotent=False, open_world=True)\n    examples = [\n        ModuleExample(\n            title=\"Send a welcome email\",\n            inputs={\"to\": \"user@example.com\", \"subject\": \"Welcome!\", \"body\": \"...\", \"api_key\": \"sk-xxx\"},\n            output={\"status\": \"sent\", \"message_id\": \"msg-12345\"},\n            description=\"Sends a welcome email to a new user.\",\n        ),\n    ]\n\n    def execute(self, inputs: dict, context) -\u003e dict:\n        logger = ContextLogger.from_context(context, name=\"send_email\")\n        logger.info(\"Sending email\", extra={\"to\": inputs[\"to\"], \"subject\": inputs[\"subject\"]})\n        message_id = f\"msg-{hash(inputs['to']) % 100000:05d}\"\n        logger.info(\"Email sent successfully\", extra={\"message_id\": message_id})\n        return {\"status\": \"sent\", \"message_id\": message_id}\n```\n\n---\n\n### `decorated_add` — `@module` decorator for simple functions\n\n```python\nfrom apcore.decorator import module\n\n@module(description=\"Add two integers\", tags=[\"math\", \"utility\"])\ndef add(a: int, b: int) -\u003e int:\n    return a + b\n```\n\n## Development\n\n### Run tests\n\n```bash\npytest\n```\n\n### Run tests with coverage\n\n```bash\npytest --cov=src/apcore --cov-report=html\n```\n\n### Lint and format\n\n```bash\nruff check --fix src/ tests/\nruff format src/ tests/\n```\n\n### Type check\n\n```bash\nmypy src/ tests/\n```\n\n\n## 📄 License\n\nApache-2.0\n\n## 🔗 Links\n\n- **Documentation**: [docs/apcore](https://github.com/aiperceivable/apcore) - Complete documentation\n- **Website**: [aiperceivable.com](https://aiperceivable.com)\n- **GitHub**: [aiperceivable/apcore-python](https://github.com/aiperceivable/apcore-python)\n- **PyPI**: [apcore](https://pypi.org/project/apcore/)\n- **Issues**: [GitHub Issues](https://github.com/aiperceivable/apcore-python/issues)\n- **Discussions**: [GitHub Discussions](https://github.com/aiperceivable/apcore-python/discussions)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faiperceivable%2Fapcore-python","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faiperceivable%2Fapcore-python","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faiperceivable%2Fapcore-python/lists"}