{"id":"4cdcaf25-58fe-4c18-83ba-05e6e1226dcc","slug":"ai-kit-core","kind":"mcp","name":"ai-kit-core","summary":"ai-kit core — Laravel RAG toolkit: ingestion, chunking, extraction, vector retrieval (sqlite-vec + pgvector), and agents on the Laravel AI SDK.","owner":"solutionforest","repoUrl":"https://github.com/solutionforest/ai-kit-core","license":"MIT","licenseTier":"permissive","trust":"community","stars":3,"match":null,"fewStars":true,"format":"mcp","createdAt":"2026-07-21T17:16:32.000Z","addedAt":"2026-10-21T17:46:06.660Z","logoUrl":"https://avatars.githubusercontent.com/u/68211796?v=4","linkOnly":false,"collection":null,"skillPath":null,"categories":["databases","search","developer-tools"],"tags":[],"runtime":"binary","local":true,"cloudCapable":false,"cloudCapability":"unknown","cloudReadyOnPlatform":false,"needsApiKey":false,"reviewed":false,"syncedAt":"2026-10-26T03:37:08.553Z","sample":false,"url":"https://conduits.dev/tools/ai-kit-core","language":"PHP","transports":["stdio"],"install":{},"requirements":{"env":[],"upstreamMayCharge":false},"tools":null,"readme":"# ai-kit-core\n\n![PHP](https://img.shields.io/badge/PHP-8.3+-777BB4?logo=php&logoColor=white)\n![Laravel](https://img.shields.io/badge/Laravel-12%20|%2013-FF2D20?logo=laravel&logoColor=white)\n![Laravel AI SDK](https://img.shields.io/badge/laravel%2Fai-^0.9-FF2D20)\n![License](https://img.shields.io/badge/license-MIT-green)\n\n**The engine of the ai-kit suite** — document ingestion, chunking, extraction, vector retrieval, and RAG agents for Laravel, built on the official [Laravel AI SDK](https://laravel.com/docs/ai).\n\n`composer require` → `php artisan ai-kit:install` → a grounded, cited RAG assistant over your own documents — with **no external vector database required**.\n\n- **Two vector drivers** behind one contract: **SQLite** (zero-infra, default) and **pgvector** (production).\n- **Two RAG modes** via one config flag: **tool** (agentic) and **inject** (classic).\n- **Ingest** PDF, Word (.docx), Excel (.xlsx), HTML, Markdown, plain text, **and URLs**.\n- **Provider-agnostic** — OpenAI, Anthropic, OpenRouter, Gemini, Groq, Mistral, … (anything the AI SDK supports).\n- **Fully testable offline** — no API keys needed in CI.\n\n## Why ai-kit?\n\nBuilding a grounded, cited RAG chatbot over your own documents in Laravel means\nwiring together a dozen moving parts: parsing PDFs/Word/Excel, chunking text,\ngenerating embeddings, running a vector store, similarity search, prompt\norchestration, streaming, citations, conversation history, and cost tracking.\n\n**ai-kit does all of that for you** — on the official Laravel AI SDK, with no\nexternal vector database. `composer require` → `php artisan ai-kit:install` →\na working, source-cited assistant in under 30 minutes. Then expose it however\nyou like: a Livewire chat widget, an admin panel, a JSON API, a CLI, or an MCP\nserver for AI agents.\n\n## Architecture\n\n```mermaid\nflowchart TB\n    UI[\"Livewire UI<br/>(ai-kit-ui-livewire)\"]\n    CLI[\"Artisan CLI\"]\n    API[\"HTTP API\"]\n    MCP[\"MCP Server\"]\n\n    subgraph services[\"Service layer\"]\n        SVC[\"ChatService · CollectionService · DocumentService\"]\n    end\n\n    UI --> SVC\n    CLI --> SVC\n    API --> SVC\n    MCP --> SVC\n\n    SVC --> ING[\"Ingestion<br/>extract → chunk → embed\"]\n    SVC --> RET[\"Retriever\"]\n    SVC --> AGENT[\"RagAgent<br/>tool / inject mode\"]\n\n    ING --> VS[(\"Vector store<br/>sqlite-vec / pgvector\")]\n    RET --> VS\n    ING --> LLM[\"Laravel AI SDK<br/>(any provider)\"]\n    AGENT --> RET\n    AGENT --> LLM\n```\n\n**One core, four faces.** Every surface calls the same service layer, so RAG\nbehaviour is identical whether a user chats in the browser, you script the CLI,\nanother service hits the API, or an AI agent connects over MCP.\n\n## Requirements\n\n- PHP **8.3+**\n- Laravel **12 or 13**\n- `laravel/ai` **^0.9**\n\n## Install\n\n```bash\ncomposer require solution-forest/ai-kit-core\nphp artisan ai-kit:install\n```\n\nThe installer:\n1. Publishes `config/ai-kit.php` + migrations (including the AI SDK's conversation tables).\n2. Interactively asks for your **AI provider config** (chat + embeddings provider/model/key) and writes it to `.env`.\n3. Scaffolds `App\\Ai\\SupportAgent` (extends `RagAgent`).\n4. Runs migrations.\n5. Optionally seeds a demo knowledge base.\n\nThen set your keys (the installer adds the scaffold; fill in the value):\n\n```dotenv\nAI_KIT_CHAT_PROVIDER=openai\nAI_KIT_CHAT_MODEL=gpt-5\nAI_KIT_EMBED_PROVIDER=openai\nAI_KIT_EMBED_MODEL=text-embedding-3-small\nOPENAI_API_KEY=sk-...\n```\n\n## Quick start\n\n```php\nuse SolutionForest\\AiKit\\Models\\Collection;\nuse SolutionForest\\AiKit\\Ingestion\\IngestionService;\nuse SolutionForest\\AiKit\\Agents\\RagAgent;\n\n// 1. Create a knowledge base and ingest documents.\n$kb = Collection::create(['name' => 'Handbook']);\napp(IngestionService::class)->createFromPath($kb, storage_path('handbook.pdf'), sync: true);\napp(IngestionService::class)->createFromUrl($kb, 'https://example.com/faq', sync: true);\n\n// 2. Ask a question — grounded, cited, provider from config.\n$response = (new RagAgent('handbook'))->prompt('What is the refund window?');\necho $response->text;\n```\n\nOr from the CLI:\n\n```bash\nphp artisan ai-kit:ingest path/to/docs --collection=handbook   # file or directory\nphp artisan ai-kit:ingest-url https://example.com/faq --collection=handbook\nphp artisan ai-kit:demo                                         # seed 3 sample docs\n```\n\n## How ingestion works\n\n```\ncreateFromPath / createFromUrl\n        │\n        ▼\n  ExtractText ──▶ ChunkDocument ──▶ EmbedChunks ──▶ vector index\n (pdf/docx/xlsx/    (recursive,      (AI SDK          (sqlite-vec\n  html/md/txt)      sentence-aware)   embeddings)      or pgvector)\n```\n\n- **Synchronous** (`sync: true`) — runs inline; used by `ai-kit:demo` and CLI. No queue worker needed.\n- **Queued** (default) — dispatches a chained `ExtractText → ChunkDocument → EmbedChunks` batch; document status moves `pending → processing → indexed` (or `failed`).\n\nEvery chunk stores its raw embedding **and** the embedding model name, so re-indexing with a different model is detectable and you can switch drivers without re-embedding blindly.\n\n### Supported document types\n\n| Type | Extractor | Notes |\n|---|---|---|\n| `.txt` `.md` `.csv` | `PlainTextExtractor` | |\n| `.html` | `HtmlExtractor` | strips script/style/boilerplate |\n| `.pdf` | `PdfExtractor` | via `smalot/pdfparser` |\n| `.docx` | `WordExtractor` | zero-dependency (zipped XML) |\n| `.xlsx` | `ExcelExtractor` | zero-dependency; extracts text cells |\n| URL | fetch + `HtmlExtractor` | `createFromUrl` / `ai-kit:ingest-url` |\n\nLegacy binary `.doc` / `.xls` are **not** supported.\n\n## Two RAG modes\n\n`config('ai-kit.mode')`:\n\n- **`tool`** (default) — the agent gets a `SearchKnowledgeBase` tool and decides when/what to search (agentic RAG; supports multi-hop). Requires a model with reliable tool-calling.\n- **`inject`** — no tool; `ContextInjector` retrieves and prepends context to the prompt before the model runs (classic RAG). Grounding never depends on the model choosing to search — safer with small models.\n\nBoth consume the same `Retriever` contract.\n\n## Two vector drivers\n\n`config('ai-kit.retrieval.driver')`:\n\n- **`sqlite`** (default) — uses the sqlite-vec (`vec1`) extension when present, otherwise an **exact pure-PHP cosine scan** (correct everywhere, zero setup — ideal for demos and small/medium corpora). Optional extension path: `AI_KIT_SQLITE_VEC_PATH`.\n- **`pgvector`** — a `vector` column indexed with HNSW/IVFFlat, searched with the `<=>` cosine operator. Scales to millions of chunks.\n\n## Building agents\n\n`RagAgent` reads config and wires the configured chat provider/model into every `prompt()` / `stream()`:\n\n```php\nuse SolutionForest\\AiKit\\Agents\\RagAgent;\n\n(new RagAgent(collection: 'handbook'))\n    ->forTenant('acme')            // optional tenant scope\n    ->prompt('...');               // uses ai-kit.chat.provider + model\n```\n\nCustomise instructions by extending it (the installer scaffolds this):\n\n```php\nnamespace App\\Ai;\n\nuse SolutionForest\\AiKit\\Agents\\RagAgent;\n\nfinal class SupportAgent extends RagAgent\n{\n    public function instructions(): \\Stringable|string\n    {\n        return 'You are our support assistant. Always search first; answer only from the knowledge base; cite sources.';\n    }\n}\n```\n\nPoint `AI_KIT_AGENT=App\\Ai\\SupportAgent` at it. The default instructions are strict-grounded (search-first, refuse-if-unsupported, cite by document title).\n\n## Conversation persistence & token usage\n\nWhen a prompt runs **for an authenticated participant** (`->forUser($user)` / `->continue($id, $user)`), the AI SDK persists the exchange to `agent_conversations` / `agent_conversation_messages`, **including per-message token usage**. Aggregate it with `UsageStats`:\n\n```php\nuse SolutionForest\\AiKit\\Support\\UsageStats;\n\napp(UsageStats::class)->totals(30);            // ['prompt'=>, 'completion'=>, 'total'=>, 'cost'=>, 'messages'=>]\napp(UsageStats::class)->daily(14);             // per-day token series\napp(UsageStats::class)->conversationTokens($id);\n```\n\nCost is estimated from `config('ai-kit.pricing')` ($ per 1M tokens, per model) using the active chat model's rate.\n\n## Multi-tenancy\n\nEvery table has a nullable `tenant_id`. Scope models + retrieval:\n\n```php\nuse SolutionForest\\AiKit\\Facades\\AiKit;\n\nAiKit::withTenant('acme', fn () => (new RagAgent('handbook'))->prompt('...'));\n```\n\nEnable with `AI_KIT_TENANCY=true`.\n\n## Contracts (swap any layer)\n\n| Contract | Default | Bind your own via the container |\n|---|---|---|\n| `Retriever` / `VectorIndex` | `SqliteVecRetriever` / `PgVectorRetriever` | custom store |\n| `Embedder` | `AiSdkEmbedder` | e.g. a local embedder |\n| `Chunker` | `RecursiveTextChunker` | custom splitting |\n| `TextExtractor` | `ExtractorManager` | add extractors |\n\n## Configuration reference\n\n`config/ai-kit.php`: `mode`, `default_collection`, `agent` (class + instructions), `chat` (provider/model/fallback/streaming), `embeddings` (provider/model/dimensions/cache/timeout), `chunking` (size/overlap), `retrieval` (driver/top_k/min_score/sqlite/pgvector), `ingestion` (queue/embed_batch), `tenancy`, `database` (connection/prefix), `features` (dashboard/token_usage), `pricing`. Every value has an `env()` override.\n\n## Commands\n\n| Command | Purpose |\n|---|---|\n| `ai-kit:install` | Publish config/migrations, configure AI providers, scaffold agent |\n| `ai-kit:demo` | Seed a demo collection + 3 sample docs (sync) |\n| `ai-kit:ingest {path} --collection=` | Ingest a file or directory |\n| `ai-kit:ingest-url {url} --collection=` | Fetch + ingest a web page |\n\n## Headless surfaces (CLI · HTTP API · MCP)\n\nEverything the Filament panel does is reachable without Filament. All four\nsurfaces call the same service layer (`ChatService`, `CollectionService`,\n`DocumentService`), so behaviour is identical everywhere.\n\n### CLI\n\n| Command | Purpose |\n|---|---|\n| `ai-kit:collection {list\\|new\\|show\\|delete}` | Manage knowledge bases (`--json`) |\n| `ai-kit:document {list\\|chunks\\|reindex\\|delete}` | Manage documents (`--collection`, `--json`) |\n| `ai-kit:search {query} --collection=` | Raw retrieval, no LLM (`--json`) |\n| `ai-kit:chat {collection}` | Interactive grounded chat REPL |\n| `ai-kit:conversation {list\\|show}` | Conversation log + token usage (`--json`) |\n| `ai-kit:stats` | Documents/chunks/tokens/cost (`--json`) |\n\n### HTTP API (optional)\n\nEnable with `AI_KIT_API=true`. Locked behind auth by default\n(`config('ai-kit.api.middleware') = ['api','auth:sanctum']`); set your own guard,\nor `['api']` for public. Prefix: `AI_KIT_API_PREFIX` (default `api/ai-kit`).\n\n```\nPOST  /chat                          { message, collection?, conversation_id? } → answer + citations + usage\nPOST  /chat/stream                   SSE (token-by-token)\nPOST  /search                        { query, collection?, top? } → chunks\nGET   /collections                   list\nPOST  /collections                   { name, slug?, description? }\nGET   /collections/{slug}            + stats\nGET   /collections/{slug}/documents  list\nPOST  /collections/{slug}/documents  { url | text | file, title?, sync? } → ingest\nGET   /conversations                 list (+ token totals)\nGET   /conversations/{id}            thread + per-message tokens\nGET   /usage?days=30                 totals + daily series\n```\n\n### MCP server (optional)\n\nExpose ai-kit to MCP clients (Claude Desktop, IDE agents). Requires\n`composer require laravel/mcp`, then `AI_KIT_MCP=true`. Tools: `search_knowledge_base`,\n`ask_knowledge_base`, `list_collections`, `ingest_url`. Serve over stdio:\n\n```bash\nphp artisan mcp:start ai-kit\n```\n\nSet `AI_KIT_MCP_WEB=true` to also register an HTTP transport route\n(`config('ai-kit.mcp.web_route')`, guarded by `mcp.web_middleware`).\n\n### Examples\n\n**HTTP API** (Bearer auth by default):\n\n```bash\n# Ask a question\ncurl -X POST https://your-app.test/api/ai-kit/chat \\\n  -H \"Authorization: Bearer <token>\" -H \"Content-Type: application/json\" \\\n  -d '{\"message\":\"What is the refund window?\",\"collection\":\"handbook\"}'\n\n# Raw search\ncurl -X POST https://your-app.test/api/ai-kit/search \\\n  -H \"Authorization: Bearer <token>\" -H \"Content-Type: application/json\" \\\n  -d '{\"query\":\"refunds\",\"collection\":\"handbook\",\"top\":5}'\n\n# Ingest a URL\ncurl -X POST https://your-app.test/api/ai-kit/collections/handbook/documents \\\n  -H \"Authorization: Bearer <token>\" -H \"Content-Type: application/json\" \\\n  -d '{\"url\":\"https://example.com/faq\"}'\n```\n\n**MCP** — point a client (e.g. Claude Desktop) at the stdio server:\n\n```json\n{\n  \"mcpServers\": {\n    \"ai-kit\": {\n      \"command\": \"php\",\n      \"args\": [\"artisan\", \"mcp:start\", \"ai-kit\"],\n      \"cwd\": \"/path/to/your/laravel/app\"\n    }\n  }\n}\n```\n\n**Programmatic** (any surface, same result):\n\n```php\nuse SolutionForest\\AiKit\\Services\\ChatService;\n\n$result = app(ChatService::class)->ask('What is the refund window?', 'handbook');\n$result->answer;      // string\n$result->citations;   // [{document_title, chunk_index, score, content}, ...]\n$result->usage;       // {in, out, total}\n```\n\n## Testing\n\nThe suite runs **offline with no API keys** — LLM calls use the AI SDK's fakes, embeddings use a deterministic in-memory double.\n\n```bash\ncomposer install && vendor/bin/pest\n```\n\n## The ai-kit suite\n\n**One core, four faces** — the Livewire UI, CLI, HTTP API, and MCP server all\ncall the same service layer (`ChatService`, `CollectionService`,\n`DocumentService`), so behaviour is identical everywhere.\n\n| Package | Purpose |\n| --- | --- |\n| **solution-forest/ai-kit-core** (this) | Ingestion, retrieval, agents + CLI, HTTP API, MCP |\n| [solution-forest/ai-kit-ui-livewire](https://github.com/solutionforest/ai-kit-ui-livewire) | Livewire 4 chat component |\n| [solution-forest/ai-kit-filament](https://github.com/solutionforest/ai-kit-filament) | Filament v5 admin panel |\n\n`ai-kit-ui-livewire` and `ai-kit-filament` are optional; core is fully usable headless.\n\n## License\n\nMIT © [SolutionForest](https://www.solutionforest.com)\n"}