{"id":50806707,"url":"https://github.com/prismal-ai/prismal","last_synced_at":"2026-06-13T02:02:26.578Z","repository":{"id":360821180,"uuid":"1246226072","full_name":"prismal-ai/prismal","owner":"prismal-ai","description":"Prism-inspired multi-agent orchestration framework built on LangGraph. Security-first, provider-agnostic, composable.","archived":false,"fork":false,"pushed_at":"2026-06-06T04:42:34.000Z","size":4337,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-06T06:14:02.806Z","etag":null,"topics":["agent-framework","agentic-ai-architecture","agentic-ai-development","human-in-the-loop","langchain-python","langgraph","llm-agents","llmops","mcp","multi-agent","rag","supervisor-pattern"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/prismal-ai.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":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-05-22T01:59:38.000Z","updated_at":"2026-05-28T01:15:43.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/prismal-ai/prismal","commit_stats":null,"previous_names":["prismal-ai/prismal"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/prismal-ai/prismal","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/prismal-ai%2Fprismal","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/prismal-ai%2Fprismal/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/prismal-ai%2Fprismal/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/prismal-ai%2Fprismal/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/prismal-ai","download_url":"https://codeload.github.com/prismal-ai/prismal/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/prismal-ai%2Fprismal/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34269364,"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-13T02:00:06.617Z","response_time":62,"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-framework","agentic-ai-architecture","agentic-ai-development","human-in-the-loop","langchain-python","langgraph","llm-agents","llmops","mcp","multi-agent","rag","supervisor-pattern"],"created_at":"2026-06-13T02:02:24.821Z","updated_at":"2026-06-13T02:02:26.571Z","avatar_url":"https://github.com/prismal-ai.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# prismal-ai\n\n[![PyPI version](https://badge.fury.io/py/prismal-ai.svg)](https://pypi.org/project/prismal-ai/)\n[![Python 3.13+](https://img.shields.io/badge/python-3.13+-blue.svg)](https://www.python.org/downloads/)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\n**Prismal AI Agent Framework** — the core engine powering multi-agent orchestration, security guardrails, RAG, MCP integration, and observability.\n\nThis package is the **agent framework layer** extracted from the larger monorepo as a standalone, publishable PyPI package. It provides everything needed to build and run AI agents without the web server, dashboard, or CLI. It was published as `lightagent-agents` through v2.x and **rebranded in v3.0.0**: the distribution is published on PyPI as `prismal-ai` while the import namespace is `prismal` (`lightagent.*` → `prismal.*`). End-user backward compatibility is provided by the deprecated `lightagent-agents` distribution, which now depends on `prismal-ai`. The sibling `lightagent` app package historically shared this import namespace and is rebranded/coordinated in tandem.\n\n---\n\n## Features\n\n- **26 specialized AI agents** built on [LangGraph](https://langchain-ai.github.io/langgraph/) — coder, researcher, planner, critic, data_analyst, rag_agent, codeact_agent, cua_agent, and more\n- **SUPERVISOR state machine** — central supervisor routes each turn to the right specialist, then back to `END`\n- **Security-first (5-layer defense)** — `InputSanitizer` → `GuardrailsEngine` (+ NeMo Guardrails L3) → `ActionInterceptor` → `AuditLogger` (hash-chained) + `SecurePromptBuilder` + `PermissionManager`\n- **Provider-agnostic** — Anthropic Claude, OpenAI GPT, Google Gemini, Ollama via LiteLLM (isolated in `prismal/providers/`)\n- **7 RAG engines** — standard + CRAG, HyDE, RAG-Fusion (RRF), Hybrid (BM25 + semantic), Self-RAG, Parent-Child hierarchical, Multi-Vector, and Adaptive facade\n- **7 agent reasoning patterns** — Tree of Thoughts, Debate, Constitutional AI, LATS (MCTS), LLM-Compiler (parallel DAG), Mixture of Agents, Swarm/Handoff\n- **5 domain subgraph pipelines** — Customer Service, Document Generation, Data ETL, Code Review, Debate/Consensus — on top of the existing dev/ml/financial pipelines\n- **Multimodal layer (implemented, opt-in)** — Vision / Audio / Video agents, modality router, multimodal fusion, multimodal subgraph, multimodal RAG engine with cross-modal embeddings, and `MediaValidator` security gate — gated by `settings.multimodal_enabled` (default `False`); see [`specs/multimodal-agents/`](./specs/multimodal-agents/)\n- **Kokoro deliberation (implemented, opt-in)** — three Markdown-authored persona souls (spirit 魂 / mind 知 / heart 情) deliberate toward agreement and a `KokoroJudgeAgent` renders the final, accountable verdict (optionally executing one `ActionInterceptor`-gated action) — gated by `settings.kokoro_enabled` (default `False`); see [`docs/kokoro.md`](./docs/kokoro.md) and [`specs/kokoro-deliberation/`](./specs/kokoro-deliberation/)\n- **Skynet swarm supervisor (implemented, opt-in)** — a meta-supervisor that decomposes one order into N sub-orders, dispatches a dynamically-sized worker swarm via LangGraph `Send` fan-out (supervisor-sized, hard-capped, overflow deferred), reduces the results, and re-plans unmet work in a bounded loop — gated by `settings.skynet_enabled` (default `False`); see [`docs/skynet.md`](./docs/skynet.md) and [`specs/skynet-swarm/`](./specs/skynet-swarm/)\n- **Extension surface (implemented, opt-in)** — `prismal.langgraph` re-export, `@prismal_node` decorator (security/OTel/audit/retry middleware), `PrismalStateGraphBuilder` fluent API, plugin discovery via `importlib.metadata` entry points, `LangChainRunnableAdapter`, and formal `Protocol`s for ports (checkpoint/audit/embeddings/tools) — see [`docs/extension.md`](./docs/extension.md) and [`specs/extension-surface/`](./specs/extension-surface/)\n- **MCP client with capability routing** — [Model Context Protocol](https://modelcontextprotocol.io) with auto-discovery and per-agent capability-based tool filtering (`config/mcp_servers.yaml`)\n- **Process isolation** — `SandboxExecutor` with docker/podman/nsjail/bwrap/firejail backends\n- **Human-in-the-Loop** — `hitl_gate()` with LangGraph `interrupt()` support\n- **Composable primitives** — `reflection_loop()` (generate → critique → refine) and `make_parallel_dispatcher()` (fan-out via `Send()`)\n- **Cron engine** — APScheduler + timezone-aware `DateTimeService` (single time source of truth)\n- **Long-term memory** — PII-sanitized cross-session store (SQLite + ChromaDB; optional MongoDB)\n- **Observability** — Langfuse traces, OpenTelemetry spans, structlog\n- **Deterministic intent routing** — regex-based `match_intent()` ahead of LLM supervision\n- **Tool provider injection (implemented)** — `ToolProviderPort` hexagonal port: the host composes MCP/Skills/stub providers and injects them (`set_tool_provider` or per-session via graph config); the agent core no longer imports `prismal.mcp`/`prismal.skills` — see [`docs/tool-providers.md`](./docs/tool-providers.md) and [`specs/tool-provider-injection/`](./specs/tool-provider-injection/)\n- **Runtime composition root (implemented)** — `build_runtime(settings, *, org_id=None)` composes every core port (tool provider, vector store, embeddings, checkpointer, audit) into a `RuntimeContext` in a single call, with `global`/`context` modes (`settings.runtime_mode`), per-`org_id` collection isolation, coordinated `aclose()`, and `build_test_runtime` fakes — the one composition contract `prismal-server`/`prismal-dashboard` build on; see [`docs/composition-root.md`](./docs/composition-root.md) and [`specs/composition-root/`](./specs/composition-root/)\n- **Config source injection (implemented)** — `ConfigSourcePort` hexagonal port: the core stops reading `.env`/`os.environ` and consumes an injected source (`EnvConfigSource` default keeps byte-for-byte parity; `MappingConfigSource`/`ChainedConfigSource` compose secrets managers; per-tenant via `build_settings(source)`); `set_config_source` invalidates the `get_settings` cache, an AST guard forbids new config `os.getenv` in the core — see [`docs/configuration.md`](./docs/configuration.md) and [`specs/config-source-injection/`](./specs/config-source-injection/)\n- **120-tool global cap** enforced by the official `CompositeToolProvider` (legacy constants kept in `tool_registry.py`)\n- **Graph visualization** — `to_mermaid()` / `visualize()` / `save_graph_image()` (from `prismal.langgraph`) render any compiled graph or `SubgraphDefinition`; `SubgraphDefinition.to_mermaid()` and `visualize_supervisor_graph()` are one-line shortcuts (see `examples/visualize_graphs.py`)\n\n---\n\n## Installation\n\n```bash\npip install prismal-ai\n# or with uv:\nuv pip install prismal-ai\n```\n\nThe distribution is named `prismal-ai`, but the import namespace is `prismal` (e.g. `from prismal.agents.graph import get_async_compiled_graph`).\n\n### Optional extras\n\n```bash\npip install \"prismal-ai[postgres]\"          # PostgreSQL checkpointing\npip install \"prismal-ai[mongodb]\"           # MongoDB long-term memory\npip install \"prismal-ai[ollama]\"            # Local LLMs via Ollama\npip install \"prismal-ai[local-embeddings]\"  # HuggingFace embeddings\npip install \"prismal-ai[ml]\"                # ML/AutoML pipeline\npip install \"prismal-ai[ml-dl]\"             # ML + PyTorch Lightning\npip install \"prismal-ai[finance]\"           # yfinance + pandas-ta\npip install \"prismal-ai[analytics]\"         # matplotlib + plotly\npip install \"prismal-ai[datetime]\"          # tzdata + NTP\npip install \"prismal-ai[maintenance]\"       # pip-audit\npip install \"prismal-ai[multimodal]\"         # Pillow + ffmpeg-python + imagehash (Phase F)\npip install \"prismal-ai[multimodal-local]\"   # faster-whisper (local STT)\npip install \"prismal-ai[multimodal-premium]\" # elevenlabs TTS\npip install \"prismal-ai[multimodal-embed]\"   # open-clip-torch (CLIP cross-modal embeddings)\npip install \"prismal-ai[lancedb]\"            # LanceDB embedded vector store (Phase Z)\npip install \"prismal-ai[sqlite-vec]\"         # sqlite-vec embedded vector store (Phase Z)\npip install \"prismal-ai[qdrant]\"             # Qdrant vector store, embedded or server (Phase Z)\npip install \"prismal-ai[pgvector]\"           # PostgreSQL + pgvector vector store (Phase Z)\npip install \"prismal-ai[all]\"                # Everything above\n```\n\n---\n\n## Quick Start\n\n```python\nfrom prismal.agents.graph import get_async_compiled_graph\nfrom prismal.agents.state import create_initial_state\nfrom prismal.core.config import get_settings\n\nasync def main():\n    settings = get_settings()\n    graph = await get_async_compiled_graph()   # async contexts MUST use this\n\n    state = create_initial_state(\n        session_id=\"my-session\",\n        user_message=\"Analyse the sales data in data/sales.csv\",\n    )\n\n    result = await graph.ainvoke(\n        state,\n        config={\"configurable\": {\"thread_id\": \"my-session\"}},\n    )\n    print(result[\"messages\"][-1].content)\n```\n\nA synchronous `get_compiled_graph()` entry point is also available for non-async callers.\n\n---\n\n## Advanced architectures\n\nThe package ships 19 composable architectures under `specs/advanced-architectures/` (Phases A/B/C, ≥82% coverage per module, 0 bandit issues). Every component follows a **callable-injection pattern** — business logic accepts `generate_fn`, `evaluate_fn`, `reward_fn`, `plan_fn`, `tool_executor`, … so tests run without LLM backends. Defaults wire `ProviderRegistry().get_llm()` lazily.\n\n### RAG engines (`prismal/rag/`)\n\n| Engine | Module | Purpose |\n|--------|--------|---------|\n| **HyDE** | `hyde.py` | Generates a hypothetical answer and searches by its embedding (recall boost on abstract queries) |\n| **RAG-Fusion** | `fusion.py` | N query variants + `reciprocal_rank_fusion()` (RRF, k=60) over parallel searches |\n| **Hybrid Search** | `hybrid.py` | BM25 (`rank-bm25`) + semantic linear fusion with configurable `alpha` |\n| **Self-RAG** | `self_rag.py` | LLM decides whether to retrieve (`RETRIEVE`/`NO_RETRIEVE`) and self-assesses support (`SUPPORTED`/`PARTIALLY_SUPPORTED`/`UNSUPPORTED`) + utility score |\n| **Parent-Child** | `hierarchical.py` | Indexes small child chunks (~100 tok) for precision but returns parent context (~500 tok) to the LLM |\n| **Multi-Vector** | `multi_vector.py` | Indexes each chunk plus a summary and N hypothetical questions per chunk |\n| **Adaptive RAG** | `adaptive.py` | Facade that classifies queries (`FACTUAL_SIMPLE` / `ABSTRACT` / `AMBIGUOUS` / `MULTI_HOP` / `TECHNICAL` / `CONVERSATIONAL`) and routes to the engine above |\n\n### Agent reasoning patterns (`prismal/agents/patterns/`)\n\n| Pattern | Module | Purpose |\n|---------|--------|---------|\n| **Tree of Thoughts** | `tree_of_thoughts.py` | Explores a tree of candidate thoughts with BFS / DFS / beam search |\n| **Debate** | `debate.py` | N-agent multi-round debate with moderator / majority-vote / weighted synthesis and Jaccard agreement score |\n| **Constitutional AI** | `constitutional.py` | Principle-driven self-critique + revision loop with audit log (3 default principles: `no_harmful_content`, `factual_accuracy`, `no_pii_exposure`) |\n| **LATS** | `lats.py` | Monte Carlo Tree Search (UCB1) over the action space — real backtracking when a branch fails |\n| **LLM-Compiler** | `llm_compiler.py` | Compiles a DAG of tasks, validates with Kahn topological sort, executes independent tasks in parallel waves |\n| **Mixture of Agents** | `mixture_of_agents.py` | Parallel proposers across multiple providers + aggregator synthesis layers |\n| **Swarm/Handoff** | `swarm.py` | Decentralised agent-to-agent handoff with `HandoffRecord` audit trail and allow-list validation |\n\n### Domain subgraph pipelines (`prismal/agents/subgraphs/`)\n\n| Pipeline | Directory | Flow |\n|----------|-----------|------|\n| **Customer Service** | `customer_service/` | classifier → faq_retrieval → escalation_gate → response \\| ticket_creator |\n| **Document Generation** | `document_generation/` | planner → researcher → writer → editor → formatter (markdown/plain/html) |\n| **Data ETL** | `data_etl/` | extractor → validator → (conditional gate) → transformer → loader → auditor |\n| **Code Review** | `code_review/` | linter → security_scanner → logic_reviewer → suggester → report_generator |\n| **Debate/Consensus** | `debate_consensus/` | proponent → opponent → moderator → consensus |\n\nEach subgraph exports both `build_\u003cname\u003e_subgraph()` (returns a `SubgraphDefinition`) and an idempotent `register_\u003cname\u003e()` mirroring the existing `register_ml_pipeline`. Wiring into the top-level supervisor is opt-in operational work — the primitives are ready to register.\n\n### MCP capability routing\n\n`config/mcp_servers.yaml` declares each server's `capabilities: list[str]`. `MCPClientManager.get_all_langchain_tools(capabilities=…)` and `get_tools_for_agent(agent, required_capabilities=…)` filter the tool pool per agent. Servers tagged `general` are always included; omitting `capabilities` from a YAML entry defaults to `[\"general\"]` for backward compatibility. The capability set is extended in Phase F to include `vision`, `audio`, and `video`.\n\nSee [`specs/advanced-architectures/SPEC.md`](./specs/advanced-architectures/SPEC.md) for the full interface contracts of Phases A/B/C/D/E.\n\n### Multimodal layer (Phase F — implemented, opt-in)\n\nThe multimodal expansion described in [`specs/multimodal-agents/`](./specs/multimodal-agents/) adds voice, image, and video to the existing text-only stack without modifying any existing agent. It is **opt-in**: gated by `settings.multimodal_enabled` (default `False`) and registered via `register_multimodal_pipeline(registry)` when the operator is ready.\n\n#### Provider wrappers (`prismal/providers/`)\n\n| Wrapper | Module | Backends |\n|---------|--------|----------|\n| **STT** | `stt.py` | OpenAI Whisper API, local (`openai-whisper` / `faster-whisper`) |\n| **TTS** | `tts.py` | `pyttsx3` (offline default), OpenAI, ElevenLabs — automatic cascade fallback |\n| **Vision LLM** | `vision.py` | Any LiteLLM model with vision (Claude, GPT-4o, Gemini) |\n| **Multimodal LLM** | `multimodal.py` | Gemini 2.x, GPT-4o, Sonnet 4.6 (native multimodal) |\n| **Cross-modal embeddings** | `cross_modal_embeddings.py` | CLIP / `open_clip_torch` (opt-in extra) |\n\n#### Modal agents (`prismal/agents/multimodal/`)\n\n| Agent | Module | Purpose |\n|-------|--------|---------|\n| **VisionAgent** | `vision_agent.py` | General-purpose image analysis: description, object detection, optional OCR |\n| **AudioAgent** | `audio_agent.py` | Voice-to-voice pipeline: STT → LLM reasoning → optional TTS |\n| **VideoAgent** | `video_agent.py` | FFmpeg frame extraction (via `SandboxExecutor`) + audio transcript + fusion summary |\n| **ModalityRouter** | `modality_router.py` | Heuristic classifier (MIME + regex) with optional LLM fallback |\n| **MultimodalFusion** | `multimodal_fusion.py` | Combines outputs from modal agents using `moa`, `moderator`, or `concat` strategies (reuses `mixture_of_agents.py`) |\n\n#### Multimodal subgraph (`prismal/agents/subgraphs/multimodal_pipeline/`)\n\n```\nrouter_node → [vision_node | audio_node | video_node | text passthrough] → fusion_node → output_formatter_node\n```\n\nExports `build_multimodal_subgraph()` (returns `SubgraphDefinition`) and an idempotent `register_multimodal_pipeline()` matching the existing `register_ml_pipeline` pattern.\n\n#### Multimodal RAG (`prismal/rag/`)\n\n`MultimodalRAGEngine` indexes text + image captions + audio/video transcripts and exposes `search(query, modalities=[...])` with metadata-based modality filtering. Without the `[multimodal-embed]` extra it falls back to textual captions; with it, vectors come from CLIP-style cross-modal embeddings. New loaders: `loaders/image_loader.py`, `loaders/audio_loader.py`, `loaders/video_loader.py`.\n\n#### Security (`prismal/security/`)\n\n`MediaValidator` enforces magic-byte verification + size/duration limits before any media reaches an agent. `InputSanitizer.sanitize_media()` strips EXIF; `AuditLogger.log_media()` records SHA-256 + modality (never content); `ActionInterceptor.check_media_op()` gates filesystem media operations; FFmpeg always runs inside `SandboxExecutor`.\n\nSee [`specs/multimodal-agents/SPEC.md`](./specs/multimodal-agents/SPEC.md) for the full interface contracts of Phase F.\n\n### Extension surface (Phase X — implemented, opt-in)\n\nThe extension surface (user guide: [`docs/extension.md`](./docs/extension.md); contracts: [`specs/extension-surface/`](./specs/extension-surface/)) exposes LangGraph as a first-class build target for users and third-party plugins, so you can write new patterns without forking prismal. All public symbols import from `prismal.agents.extension`. Five components:\n\n#### `prismal.langgraph` — official re-export\n\n```python\nfrom prismal.langgraph import StateGraph, START, END, Send, interrupt, add_messages, AgentState, VERSION\n\ngraph = StateGraph(AgentState)\ngraph.add_node(\"my_node\", my_node)\ngraph.add_edge(START, \"my_node\")\ngraph.add_edge(\"my_node\", END)\ncompiled = graph.compile()\n```\n\nImporting from `prismal.langgraph` (rather than `langgraph.*` directly) guarantees the LangGraph version prismal was tested against, exposed as `VERSION`.\n\n#### `@prismal_node` decorator\n\n```python\nfrom prismal.agents.extension import prismal_node\n\n@prismal_node(name=\"my_classifier\", capabilities=[\"general\"], security=\"standard\", audit=True)\nasync def my_classifier(state):\n    last = state[\"messages\"][-1].content\n    label = await classify(last)\n    return {\"metadata\": {\"my_classifier\": {\"label\": label}}}\n```\n\nWraps any async `(state) → state_update` with a middleware chain: `InputSanitizer` + `SecurePromptBuilder` + `ActionInterceptor` → OTel span → structured logger bind → retry/backoff → timeout → user function → audit log → error mapping. Side effect: registers the node's capabilities in `tool_registry.DEFAULT_CAPABILITY_MAP`.\n\n#### `PrismalStateGraphBuilder` — fluent API\n\n```python\nfrom prismal.agents.extension import PrismalStateGraphBuilder\n\nbuilder = PrismalStateGraphBuilder(\"my_pipeline\")\nbuilder.add_node(\"classify\", classify_fn)        # auto-wraps with @prismal_node if missing\nbuilder.add_node(\"respond\", respond_fn)\nbuilder.add_edge(\"classify\", \"respond\")\nbuilder.set_entry_point(\"classify\")\nsubgraph = builder.compile()                      # returns SubgraphDefinition\n```\n\n#### Plugin discovery via entry points\n\n```toml\n# prismal-x-healthcare/pyproject.toml\n[project.entry-points.\"prismal.subgraphs\"]\nhealthcare_triage = \"prismal_x_healthcare:register_healthcare_pipeline\"\n```\n\nAfter `pip install prismal-x-healthcare`, `discover_plugins()` auto-registers the subgraph. Allowlist/denylist via `settings.plugins_allowlist` / `plugins_denylist`. CLI: `python -m prismal.plugins list | info \u003cname\u003e | doctor`. Each plugin loads in isolation — individual failures do not abort startup.\n\n#### `LangChainRunnableAdapter` — bridge for existing LangChain code\n\n```python\nfrom prismal.agents.extension import LangChainRunnableAdapter\n\nadapter = LangChainRunnableAdapter(my_agent_executor)\nnode = adapter.as_node(name=\"legacy_research\", capabilities=[\"research\"])\n```\n\nAutomatically maps `state[\"messages\"]` ↔ the Runnable's input/output. Supports `Runnable`, `RunnableSequence`, `RunnableLambda`, `AgentExecutor`.\n\n#### Formal ports (hexagonal)\n\n`prismal/agents/extension/ports.py` declares `CheckpointPort`, `AuditPort`, `EmbeddingsPort`, `ToolPort`, `ToolProviderPort` as `Protocol`s. Existing implementations (`AsyncSqliteSaver`, `AuditLogger`, ChromaDB embeddings, `BaseTool`, the Phase Y tool providers) conform structurally; users substitute their own (Redis checkpointer, Splunk audit, custom tool source, etc.) without modifying the core.\n\nSee [`specs/extension-surface/SPEC.md`](./specs/extension-surface/SPEC.md) for the full interface contracts of Phase X.\n\n### Tool provider injection (Phase Y — implemented)\n\nTool resolution is a hexagonal port (user guide: [`docs/tool-providers.md`](./docs/tool-providers.md); contracts: [`specs/tool-provider-injection/`](./specs/tool-provider-injection/)): the agent core asks an injected `ToolProviderPort` for tools and never imports `prismal.mcp` / `prismal.skills` (enforced by an architecture test). The host composes the providers and injects them at startup:\n\n```python\nfrom prismal.agents.extension import build_default_tool_provider\nfrom prismal.agents.tool_registry import set_tool_provider\n\nasync def on_startup() -\u003e None:                       # FastAPI lifespan or equivalent\n    set_tool_provider(await build_default_tool_provider())   # MCP + Skills + stubs\n```\n\n- **Providers** (`prismal.agents.extension`): `McpToolProvider`, `SkillToolProvider`, `StubToolProvider`, `CompositeToolProvider` (merge with MCP→Skills→stubs priority, name dedupe, 60/120 tool caps, fixed-tool-agent exemption — exact parity with the historical registry) and `FakeToolProvider` for tests.\n- **Variante A (global)** — `set_tool_provider()` once per process; nodes keep calling `get_tools_for_agent(name)` unchanged.\n- **Variante B (multi-tenant)** — `get_async_compiled_graph(tool_provider=provider)` with `tool_provider_mode=\"context\"` binds a per-session provider; nodes resolve via `get_tools_for_agent_ctx(name, config)`. No shared global state.\n- **No provider?** The registry degrades to static stubs with a warning (`tool_provider_strict=True` raises `ToolProviderNotConfigured` instead).\n- **Legacy shims** — `init_mcp()` / `get_mcp_tools()` / `get_skill_tools()` still work, emit `DeprecationWarning`, and will be removed in the next minor.\n\nRunnable examples: [`examples/tool_provider_host.py`](./examples/tool_provider_host.py), [`examples/tool_provider_custom.py`](./examples/tool_provider_custom.py).\n\n---\n\n### Config source injection (Phase W — implemented)\n\nConfiguration is a hexagonal port (user guide: [`docs/configuration.md`](./docs/configuration.md); contracts: [`specs/config-source-injection/`](./specs/config-source-injection/)): the core stops *reading* `.env`/`os.environ` and instead *consumes* an injected `ConfigSourcePort` that *supplies* raw values. `Settings` keeps its schema and only validates. **Additive and opt-in** — with no source injected the default `EnvConfigSource` reproduces today's behaviour byte-for-byte, so the ~151 `get_settings()` call sites are untouched.\n\n```python\nfrom prismal.core.config_source import ChainedConfigSource, EnvConfigSource, set_config_source\n\n# Front a secrets manager, fall back to the environment (first-wins).\nset_config_source(ChainedConfigSource([VaultConfigSource(), EnvConfigSource()]))\n```\n\n- **Sources** (`prismal.core.config_source`): `EnvConfigSource` (the only core reader of `os.environ`/`.env`; folds the legacy `LIGHTAGENT_` mirror into its mapping, no global mutation), `MappingConfigSource`, `ChainedConfigSource` (first-wins, sub-error skipped), `FakeConfigSource` for tests.\n- **Global** — `set_config_source(source)` once per process; invalidates the `get_settings` cache so the next read rebuilds `Settings`.\n- **Per-tenant** — `build_settings(source)` is a pure constructor (no global state, `ContextVar`-isolated); composition-root threads it via `apply_org_overrides(*, source=...)`.\n- **Strict** — `config_source_strict=True` makes `build_settings` raise `ConfigSourceError` when no source is available instead of falling back.\n- **Guardrail** — an AST guard (`tests/unit/core/test_no_env_reads.py`) forbids new direct config `os.getenv`/`os.environ` reads in `prismal/**` (exempt: `EnvConfigSource`, the LiteLLM write-bridge).\n\nRunnable examples: [`examples/config_source_env.py`](./examples/config_source_env.py), [`examples/config_source_custom.py`](./examples/config_source_custom.py).\n\n---\n\n## Roadmap — features to build\n\nAlready implemented: extension surface (Phase X), tool provider injection (Phase Y), advanced architectures (Phase A/B/C), multimodal (Phase F), Kokoro (Phase K), Skynet (Phase S), the **vector store port (Phase Z)**, the **runtime composition root (Phase R)**, the **config source injection (Phase W)**, and the dependency remediation (18/18 alerts in a terminal state).\n\nWhat remains, **ordered from fast-and-necessary → complex-and-less-necessary**. Each feature has its SDD contract in [`specs/`](./specs/). Status: `spec ready` = ready to build (PLAN/ARCHITECTURE/SPEC/TASKS); `PRD seed` = PRD only, needs expansion before building.\n\n1. **Finish Tool Provider Injection (Phase Y)** — *fast · necessary · in progress* — [`specs/tool-provider-injection/`](./specs/tool-provider-injection/). The Y1–Y5 code has already landed; what's left is closing Y6–Y8 (settings/observability, docs/examples, parity tests) and marking the spec `IMPLEMENTED`.\n2. **Vector Store Port (Phase Z)** — *moderate · necessary · ✅ implemented* — [`specs/vector-store-port/`](./specs/vector-store-port/). Removes the ChromaDB lock-in behind a `VectorStorePort` with adapters (Chroma default + LanceDB, sqlite-vec, Qdrant, pgvector), selectable via `settings.vector_store_backend`. Reduces the security surface and opens up embedded backends. See [`docs/vector-stores.md`](./docs/vector-stores.md).\n3. **Runtime Composition Root (Phase R)** — *moderate · necessary · ✅ implemented* — [`specs/composition-root/`](./specs/composition-root/). `build_runtime()` composes and injects every port (tools, vector store, embeddings, checkpoint, audit) into a `RuntimeContext` in a single call, with `global`/`context` modes and per-`org_id` collection isolation; **unblocks `prismal-server` / `prismal-dashboard`**. See [`docs/composition-root.md`](./docs/composition-root.md).\n4. **Cost \u0026 Budget Governance** — *fast-to-moderate · useful · PRD seed* — [`specs/cost-budget-governance/`](./specs/cost-budget-governance/). Per-run/session/tenant budgets + cost/token/call circuit-breakers in `react_loop` and the expensive patterns (debate, ToT, LATS, MoA). A cheap insurance policy against runaway spend.\n5. **A2A / Agent Cards interop (Phase I)** — *complex · necessary (ecosystem) · spec ready* — [`specs/a2a-interop/`](./specs/a2a-interop/). Bidirectional agent-to-agent interop: expose prismal as an A2A agent (Agent Card at `/.well-known/agent-card.json`, JSON-RPC + SSE) and consume remote agents as nodes/tools. Complements MCP; closes the gap with MS Agent Framework / Google ADK.\n6. **Agent Identity \u0026 Access Governance** — *complex · necessary (enterprise) · PRD seed* — [`specs/agent-identity-governance/`](./specs/agent-identity-governance/). Per-agent identity (W3C DID), scoped credentials, OAuth-on-behalf, and a `PolicyEngine`. The trust foundation that A2A consumes; an enterprise production blocker.\n7. **Agent Evaluation \u0026 Reliability Harness** — *moderate-to-complex · useful (reliability) · PRD seed* — [`specs/agent-eval-harness/`](./specs/agent-eval-harness/). System-level evaluation of the graph (trajectories, tool usage, RAG groundedness), regression with a CI gate, and an adversarial suite. Closes the \"scaffold gap\".\n8. **Polish (no spec yet)** — *variable · less urgent* — first-party observability UI (or a deep LangSmith/Langfuse integration) and per-node type safety (Pydantic validation of node I/O; evolution of `AgentState`).\n\n### Framework or host? (where each feature lives)\n\nRule: **contract/logic → framework (`prismal/`); serving HTTP, authenticating, rendering, persisting config → host (`prismal-server` / `prismal-dashboard`).** That's why A2A and Identity are split across both.\n\n| # | Feature | Framework (`prismal/`) | Host (`prismal-server` / `dashboard`) |\n|---|---|---|---|\n| 1 | Tool Provider (Phase Y) | ✅ ports/providers (`agents/extension`) | composes and injects at startup |\n| 2 | Vector Store Port (Phase Z) | ✅ `rag/stores/` + `VectorStorePort` | picks the backend via config |\n| 3 | Composition Root (Phase R) | ✅ `prismal/composition/` · `build_runtime()` / `RuntimeContext` | calls it in the lifespan |\n| 3b | Config Source Injection (Phase W) | ✅ `core/config_source.py` · `ConfigSourcePort` / `build_settings(source)` | owns secrets/`.env`, injects per-tenant sources |\n| 4 | Cost \u0026 Budget Governance | ✅ guard in `react_loop` + patterns | per-tenant quotas |\n| 5 | A2A / Agent Cards (Phase I) | ✅ types · card · client · `A2AToolProvider` · handler | **HTTP endpoint (`/a2a`, `/.well-known/agent-card.json`) + auth** |\n| 6 | Agent Identity \u0026 Governance | ✅ `PolicyEngine` + identity port (`security/`) | **IdP/OAuth + credential vault + DID issuance/rotation** |\n| 7 | Agent Eval Harness | eval engine (module) | runs as a dev/CI tool (or a separate package) |\n| 8 | Polish | per-node type safety (`AgentState`) | observability UI |\n\nThe framework defines the ports and logic; the host composes and exposes them. Details in [`docs/competitive-analysis.md`](./docs/competitive-analysis.md).\n\nA full analysis and comparison with 2026 frameworks is in [`docs/competitive-analysis.md`](./docs/competitive-analysis.md).\n\n---\n\n## Development\n\nPython 3.13+ is required. `uv` is the recommended package manager.\n\n```bash\n# Install with dev tools\nuv pip install -e \".[dev]\"\n# or with dev + extras:\nuv pip install -e \".[dev,all]\"\n\n# Run the test suite (pytest-asyncio auto-mode, filterwarnings=\"error\")\nuv run pytest                                      # full suite\nuv run pytest tests/unit                           # one tier\nuv run pytest -m unit                              # by marker (unit|integration|security|slow|live_api)\nuv run pytest tests/unit/security/test_sanitizer.py::TestSanitizer::test_strip_controls  # single test\nuv run pytest -n auto                              # parallel (pytest-xdist)\nuv run pytest --cov=prismal --cov-report=term-missing   # coverage (fail_under = 80)\n\n# Lint + format (ruff, line-length=100, target py313)\nuv run ruff check .\nuv run ruff check --fix .\nuv run ruff format .\n\n# Strict type-check (mypy strict mode, namespace_packages=true)\nuv run mypy prismal\n\n# Security linting\nuv run bandit -r prismal -c pyproject.toml\n\n# Build the distribution\nuv run python -m build\n```\n\n`live_api` tests call real LLM APIs and require provider keys; skip them locally with `-m \"not live_api\"`. Integration tests under `tests/integration/` expect running services (sandbox backends, databases).\n\n---\n\n## Architecture\n\nThe core is a LangGraph `StateGraph[AgentState]` assembled in `prismal/agents/graph.py` following the **SUPERVISOR pattern**: a central `supervisor_node` routes each turn to one of 26 specialist agent nodes, each of which returns control to the supervisor; the supervisor routes to `END` when the task is complete. Checkpointing is handled by `AsyncSqliteSaver` (or PostgreSQL via the `[postgres]` extra).\n\n```\nprismal/                ← PEP 420 namespace package (NO __init__.py at root)\n├── agents/                ← LangGraph state machine + 26 agent nodes\n│   ├── graph.py           ← get_compiled_graph() / get_async_compiled_graph()\n│   ├── supervisor.py      ← Central router\n│   ├── state.py           ← AgentState (TypedDict; messages uses add_messages reducer)\n│   ├── intent_router.py   ← Deterministic regex routing\n│   ├── tool_registry.py   ← stable facade: delegates to the injected ToolProviderPort (Phase Y)\n│   ├── patterns/\n│   │   ├── reflection.py           ← reflection_loop()\n│   │   ├── parallel.py             ← make_parallel_dispatcher() via Send()\n│   │   ├── tree_of_thoughts.py     ← ToT with BFS/DFS/beam\n│   │   ├── debate.py               ← N-agent multi-round debate + Jaccard\n│   │   ├── constitutional.py       ← principle-driven self-revision + audit\n│   │   ├── lats.py                 ← MCTS with UCB1\n│   │   ├── llm_compiler.py         ← DAG compilation + Kahn validation + parallel waves\n│   │   ├── mixture_of_agents.py    ← multi-provider proposers + aggregator\n│   │   └── swarm.py                ← decentralised handoff with audit\n│   ├── multimodal/                  ← (Phase F) vision / audio / video agents + router + fusion\n│   │   ├── vision_agent.py\n│   │   ├── audio_agent.py\n│   │   ├── video_agent.py\n│   │   ├── modality_router.py\n│   │   └── multimodal_fusion.py\n│   └── subgraphs/\n│       ├── factory.py              ← SubgraphFactory\n│       ├── registry.py             ← SubgraphRegistry\n│       ├── gates.py                ← hitl_gate() with interrupt()\n│       ├── dev_pipeline/           ← PO → Architect → Developer → Tests → QA → Reviewer\n│       ├── ml_pipeline/            ← Ingester → EDA → Features → Trainer → Evaluator → Exporter\n│       ├── financial/              ← Collector → Technical → Fundamental → Risk → Report\n│       ├── customer_service/       ← classifier → faq_retrieval → gate → response | ticket\n│       ├── document_generation/    ← planner → researcher → writer → editor → formatter\n│       ├── data_etl/               ← extractor → validator → gate → transformer → loader → auditor\n│       ├── code_review/            ← linter → security_scanner → logic_reviewer → suggester → report\n│       ├── debate_consensus/       ← proponent → opponent → moderator → consensus\n│       ├── multimodal_pipeline/    ← (Phase F) router → vision|audio|video → fusion → output_formatter\n│       ├── analysis_orchestrator/\n│       ├── engineering_orchestrator/\n│       └── research_orchestrator/\n├── core/                  ← Pydantic Settings, logging, exceptions, DB, user model\n├── providers/             ← LiteLLM wrapper (ONLY location for provider-specific imports;\n│                             Phase F adds stt/tts/vision/multimodal/cross_modal_embeddings)\n├── memory/                ← Short-term history + long-term PII-sanitized store\n├── mcp/                   ← MCP client, adapter, connection manager, capability routing\n├── security/              ← 5-layer defense-in-depth (see below) + (Phase F) media_validator.py\n├── rag/                   ← 7 retrieval engines:\n│   ├── engine.py          ← standard RAGEngine\n│   ├── crag.py            ← CRAG pipeline\n│   ├── hyde.py            ← Hypothetical Document Embeddings\n│   ├── fusion.py          ← RAG-Fusion (RRF)\n│   ├── hybrid.py          ← BM25 + semantic hybrid search\n│   ├── self_rag.py        ← Self-RAG (conditional retrieval + self-assessment)\n│   ├── hierarchical.py    ← Parent-Child chunking\n│   ├── multi_vector.py    ← chunk + summary + N hypothetical questions\n│   ├── adaptive.py        ← facade routing by query type\n│   ├── federated.py       ← federated search\n│   ├── multimodal.py      ← (Phase F) MultimodalRAGEngine — text + image captions + audio/video transcripts\n│   ├── loaders/           ← (Phase F) document/image/audio/video loaders\n│   ├── stores/            ← (Phase Z) VectorStorePort adapters: chroma (default), lancedb, sqlite_vec, qdrant, pgvector\n│   ├── vector_store_factory.py ← (Phase Z) VectorStoreFactory + FakeVectorStore\n│   └── vector_store.py    ← (Phase Z) backward-compatible shim re-exporting ChromaVectorStore from stores/chroma.py\n├── skills/                ← available/ (source) · active/ (gitignored) · custom/ (gitignored)\n├── scheduler/             ← APScheduler CronExecutor, DateTimeService, Prefect flows\n├── monitoring/            ← Langfuse, OpenTelemetry, structlog\n├── data/                  ← DuckDB + Polars utilities\n├── sandbox/               ← SandboxExecutor process isolation\n├── utils/                 ← Shared utilities\n└── events/                ← Event bus\n```\n\n### Namespace package\n\n`prismal/` has **no `__init__.py`** — it is a PEP 420 implicit namespace package (renamed from `lightagent/` in v3.0.0). Both `prismal` and the sibling `lightagent` app package contribute modules into the same `prismal.*` namespace. Do not add `prismal/__init__.py`; it would break the sibling package.\n\n### Security stack (5 layers)\n\n| Layer | Component | Purpose |\n|-------|-----------|---------|\n| L1 | `InputSanitizer` | Strip control chars, normalize unicode, enforce `MAX_INPUT_LENGTH` |\n| L2 | `GuardrailsEngine` | Regex pattern matching + risk scoring |\n| L3 | `nemo_rails.py` | NVIDIA NeMo Guardrails integration |\n| L4 | `ActionInterceptor` | LangChain callback, pre-tool permission checks |\n| L5 | `AuditLogger` | Append-only JSONL audit log with xxhash chaining |\n| Support | `SecurePromptBuilder` | User-input isolation with canary tokens |\n| Support | `PermissionManager` | TTL-based SQLite permission grants |\n| Support | `filesystem_guard.py` | Path confinement via `resolve().is_relative_to()` |\n\n---\n\n## Critical rules\n\n1. **Never** concatenate user input into prompts — use `SecurePromptBuilder`. This applies to STT transcripts, OCR text, and image captions as well — they are user-controlled content.\n2. **Never** bypass `GuardrailsEngine` / `ActionInterceptor`.\n3. **Always** use `get_async_compiled_graph()` in async contexts (the sync variant wires a non-async SQLite saver).\n4. **Never** add provider-specific imports (`anthropic`, `openai`, `google.generativeai`, `ollama`, `whisper`, `pyttsx3`, `elevenlabs`, `open_clip_torch`, …) outside `prismal/providers/`.\n5. **Always** call `ActionInterceptor.check()` before tool calls that write files or execute code; call `ActionInterceptor.check_media_op()` before media filesystem operations (Phase F).\n6. **Always** validate incoming media with `MediaValidator.validate()` before passing to a multimodal agent (Phase F); FFmpeg always runs inside `SandboxExecutor`.\n7. **Never** add `__init__.py` to `prismal/` — it must remain a PEP 420 namespace package.\n\nSee [CLAUDE.md](./CLAUDE.md) for the full working guide (commands, testing notes, architectural context for contributors and AI assistants).\n\n---\n\n## Versioning\n\nThis package follows [Semantic Versioning](https://semver.org/).\nTag format for releases: `prismal/vMAJOR.MINOR.PATCH`\n\n```bash\ngit tag prismal/v3.1.4\ngit push --tags\n```\n\nSee [CHANGELOG.md](./CHANGELOG.md) for release history.\n\n---\n\n## Releasing (maintainers)\n\nRun only after the full suite, linters and type/security checks are green.\n\n```bash\n# 0) Verify on the release branch\ncd prismal \u0026\u0026 git switch main\n\n# 1) Quality gates (must all pass)\nuv pip install -e \".[dev,all]\"\nuv run pytest -m \"not live_api\"\nuv run ruff check . \u0026\u0026 uv run mypy prismal \u0026\u0026 uv run bandit -r prismal -c pyproject.toml\n\n# 2) Build + validate the prismal-ai distribution\nrm -rf dist/ \u0026\u0026 python -m build \u0026\u0026 twine check dist/*\ntwine upload --repository testpypi dist/*          # validate on TestPyPI first\n\n# 3) Push history and publish prismal-ai\ngit push origin main\ntwine upload dist/*                                 # publish to PyPI\ngit tag prismal/v3.1.4 \u0026\u0026 git push --tags         # tag format: prismal/vMAJOR.MINOR.PATCH\n\n# 4) Publish the deprecated compatibility bridge (lightagent-agents -\u003e prismal-ai)\ncd compat/lightagent-agents\nrm -rf dist/ \u0026\u0026 python -m build \u0026\u0026 twine check dist/*\ntwine upload dist/*                                 # publishes lightagent-agents 2.9.0\n```\n\nPost-release follow-ups: configure DNS/site for `prismal.dev`, coordinate the\nsibling `lightagent` app package (the namespace rename breaks the shared PEP 420\nnamespace), and regenerate the branded binary assets (PDF/PPTX/HTML).\n\n---\n\n## License\n\nMIT © Ernesto Crespo\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fprismal-ai%2Fprismal","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fprismal-ai%2Fprismal","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fprismal-ai%2Fprismal/lists"}