OpenAI-compatible embeddings and chat on your key. Duplicate detection before post, and Filament reply drafts. Commentify never hosts models.
Pro can call an OpenAI-compatible embeddings and chat API that you configure. There is no hosted Commentify model, no token meter, and no COMMENTIFY_AI_KEY in our cloud. If the provider errors, both features fail open (no matches, no draft) rather than blocking the post.Perspective toxicity is a separate spam checker. See AI toxicity.
Before a top-level post, Livewire and the SDKs ask:POST /commentify/api/v1/{type}/{id}/duplicates with { "body": "…" }.A candidate matches only when all of these hold:
COMMENTIFY_AI is on, a key is set, and commentify_embeddings exists
After collapsing whitespace, the draft is at least duplicate_min_chars (default 12)
The stored comment is approved, top-level, and on the same commentable
Cosine similarity of the two vectors is ≥ duplicate_threshold (default 0.82)
Up to five hits, highest score first. Replies are not compared as questions. Short scraps (ok, thanks) are skipped.The UI is an info callout (not a red validation error) with permalinks and Post anyway. That button submits the held draft and skips the check once.Embeddings are stored as JSON. Matching is PHP cosine similarity. No pgvector.
On the Filament Moderation queue, Draft reply (when reply drafts are on) queues a job. The model may supply page context:
public function commentifyAiContext(): string{ return $this->body;}
The draft is a pending reply (is_approved = false) with metadata.drafted_by = ai. It stays off the public thread until the author or a moderator approves it.
GET ui reports features.duplicate_detection and features.reply_drafts. List payloads include thread.duplicate_detection. React and Vue intercept in the thread store (post() returns null when matches exist; store.postAnyway() posts the draft). Livewire uses postAnyway on the comments component.
Never commit COMMENTIFY_AI_KEY. Keep it in .env. Commentify does not log the key.
Last modified on November 1, 2026
{"docsConfig":{"theme":"mint","$schema":"https://mintlify.com/docs.json","name":"Commentify Pro","description":"Self-hosted comments for Laravel. JSON API, React and Vue SDKs, reviews and Q\u0026A, BYO-key AI, spam pipeline, Filament moderation, and the same Livewire UI.","colors":{"primary":"#EC3013","light":"#FF6A52","dark":"#EC3013"},"logo":{"light":"https://mintcdn.com/commentify/O63iJiVjr-DWhaIG/logo/commentify-pro-lockup.png?fit=max\u0026auto=format\u0026n=O63iJiVjr-DWhaIG\u0026q=85\u0026s=e6d287c1a094e92bf096365289389212","dark":"https://mintcdn.com/commentify/O63iJiVjr-DWhaIG/logo/commentify-pro-lockup-dark.png?fit=max\u0026auto=format\u0026n=O63iJiVjr-DWhaIG\u0026q=85\u0026s=4de7e002f975e1c3afa74795abf07702"},"favicon":"https://mintcdn.com/commentify/O63iJiVjr-DWhaIG/favicon.svg?fit=max\u0026auto=format\u0026n=O63iJiVjr-DWhaIG\u0026q=85\u0026s=e20039356d7a3ed3c5be095304bbf85c","navbar":{"links":[{"href":"mailto:hello@usamamuneer.me","label":"Support"},{"icon":"play","href":"https://commentify.pro","label":"Demo"}],"primary":{"type":"button","label":"Purchase","href":"https://checkout.anystack.sh/commentify-pro"}},"footer":{"socials":{"website":"https://commentify.pro","github":"https://github.com/usamamuneerchaudhary/commentify"}},"search":{"prompt":"Search Commentify docs…"},"seo":{"metatags":{"og:site_name":"Commentify Pro Docs","twitter:card":"summary_large_image","og:image":"https://docs.commentify.pro/images/github-social-card.png","twitter:image":"https://docs.commentify.pro/images/github-social-card.png","keywords":"Laravel comments package, Commentify Pro, Laravel Livewire comments, Laravel comment API, Disqus alternative Laravel, Filament comment moderation, Laravel React comments, Laravel Vue comments, self-hosted comments Laravel, import Disqus comments Laravel"},"indexing":"navigable"},"integrations":{"ga4":{"measurementId":"G-GRSVJTPRSB"}},"banner":{"content":"Commentify Pro adds the API, React, Vue, and moderation. [Try the demo](https://commentify.pro) or [buy a license](https://checkout.anystack.sh/commentify-pro).","dismissible":true},"contextual":{"options":["copy","view","chatgpt","claude","perplexity","mcp","cursor","vscode"],"display":"header"},"metadata":{"timestamp":true},"styling":{"latex":true},"navigation":{"pages":[]}},"deployment":{"deploymentMetadataValue":{"subdomain":"commentify","hasAudioNarration":false,"actualSubdomain":"commentify","customDomains":["docs.commentify.pro"],"gitSource":{"type":"github","owner":"usamamuneerchaudhary","repo":"docs-commentify-pro","deployBranch":"main","contentDirectory":"","isPrivate":true,"mountPath":"","label":""},"entitlements":{},"buildId":"6ae69088ee23c3f33b1cb17c","clientVersion":"0.0.4087","searchFilterCounts":[{"language":"default","tag":"Commentify","count":11},{"language":"default","tag":"Features","count":9},{"language":"default","tag":"Frontends","count":6},{"language":"default","tag":"Commentify Pro","count":4},{"language":"default","tag":"API","count":2},{"language":"default","tag":"Moderation","count":2},{"language":"default","tag":"Help","count":1}]},"assistantConfig":{"enableAskAiButton":false,"enableStarterQuestions":false,"enableContextualStarterQuestions":false,"enableAskAiSearchBar":true},"basePath":"","hasChatPermissions":false,"hasWebMcpPermissions":false,"toggles":[{"name":"kill-isr","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-mintlify-slack","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-integrations-settings","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"rbac-expansion","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-pr-grouping","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"custom-prefix","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"analytics-v7","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"user-flows-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"no-publish-flow","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-onboarding","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"llms-txt-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving-live","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"stripTemperatureMiddleware","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"support-platform-pylon-zendesk","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"instant-rollback","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications-server","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-fable","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fixed-ai-action-pricing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automation-resolve-merge-conflict","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"scoped-publishing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"mintql","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"page-level-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-version-history-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"billing-page-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"full-page-search","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workspace-projects","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"assistant-v5","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fork-pr-previews","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"revalidation-jwt","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-sso-scim","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"hobby-abuse-scan","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"dashboard-inbox","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-signup","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"turbopuffer-dual-write","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-enabled","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"approve-review-flow","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-metadata","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"sso-setup-deadline-banner","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"turbopuffer-serve","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"live-agents","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"comment-mode","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false}],"googleFontsInlined":false},"customCss":[{"filePath":"style.css","content":"#assistant-entry,#assistant-entry-mobile,.chat-assistant-floating-input,#chat-assistant-sheet{display:none!important}"}],"page":{"pagePath":"ai","slug":"/ai","pageMetadata":{"title":"Bring-your-own-key AI","description":"OpenAI-compatible embeddings and chat on your key. Duplicate detection before post, and Filament reply drafts. Commentify never hosts models.","href":"/ai"},"description":{"compiledSource":"\"use strict\";\nconst {jsx: _jsx} = arguments[0];\nconst {useMDXComponents: _provideComponents} = arguments[0];\nfunction _createMdxContent(props) {\n const _components = {\n p: \"p\",\n ..._provideComponents(),\n ...props.components\n };\n return _jsx(_components.p, {\n children: \"OpenAI-compatible embeddings and chat on your key. Duplicate detection before post, and Filament reply drafts. Commentify never hosts models.\"\n });\n}\nfunction MDXContent(props = {}) {\n const {wrapper: MDXLayout} = {\n ..._provideComponents(),\n ...props.components\n };\n return MDXLayout ? _jsx(MDXLayout, {\n ...props,\n children: _jsx(_createMdxContent, {\n ...props\n })\n }) : _createMdxContent(props);\n}\nreturn {\n default: MDXContent\n};\n","frontmatter":{},"scope":{}},"pageType":"default","mdx":{"mdxSource":{"compiledSource":"\"use strict\";const{Fragment:_Fragment,jsx:_jsx,jsxs:_jsxs}=arguments[0];const{useMDXComponents:_provideComponents}=arguments[0];function _createMdxContent(props){const _components={a:`a`,code:`code`,li:`li`,ol:`ol`,p:`p`,pre:`pre`,span:`span`,strong:`strong`,..._provideComponents(),...props.components},{CodeBlock,Heading,Warning}=_components;if(!CodeBlock)_missingMdxReference(`CodeBlock`,true);if(!Heading)_missingMdxReference(`Heading`,true);if(!Warning)_missingMdxReference(`Warning`,true);return _jsxs(_Fragment,{children:[_jsxs(_components.p,{children:[`Pro can call an `,_jsx(_components.strong,{children:`OpenAI-compatible`}),` embeddings and chat API that you configure. There is no hosted Commentify model, no token meter, and no `,_jsx(_components.code,{children:`COMMENTIFY_AI_KEY`}),` in our cloud. If the provider errors, both features `,_jsx(_components.strong,{children:`fail open`}),` (no matches, no draft) rather than blocking the post.`]}),`\n`,_jsxs(_components.p,{children:[`Perspective toxicity is a separate spam checker. See `,_jsx(_components.a,{href:`/toxicity`,children:`AI toxicity`}),`.`]}),`\n`,_jsx(Heading,{level:`2`,id:`enable-it`,children:`Enable it`}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`6`,language:`text`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`text`,children:_jsxs(_components.code,{language:`text`,numberOfLines:`6`,children:[_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`COMMENTIFY_AI=true`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`COMMENTIFY_AI_KEY=sk-...`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`# COMMENTIFY_AI_URL=https://api.openai.com/v1`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`# COMMENTIFY_AI_EMBEDDING_MODEL=text-embedding-3-small`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`# COMMENTIFY_AI_CHAT_MODEL=gpt-4o-mini`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{})})]})})}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`15`,language:`php`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`php`,children:_jsxs(_components.code,{language:`php`,numberOfLines:`15`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`'ai'`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:` =>`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` [`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` 'enabled'`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:` =>`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` (`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:`bool`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`) `}),_jsx(_components.span,{style:{color:`#8250DF`,\"--shiki-dark\":`#DCDCAA`},children:`env`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`(`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`'COMMENTIFY_AI'`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`, `}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:`false`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`),`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` 'base_url'`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:` =>`}),_jsx(_components.span,{style:{color:`#8250DF`,\"--shiki-dark\":`#DCDCAA`},children:` env`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`(`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`'COMMENTIFY_AI_URL'`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`, `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`'https://api.openai.com/v1'`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`),`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` 'api_key'`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:` =>`}),_jsx(_components.span,{style:{color:`#8250DF`,\"--shiki-dark\":`#DCDCAA`},children:` env`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`(`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`'COMMENTIFY_AI_KEY'`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`),`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` 'embedding_model'`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:` =>`}),_jsx(_components.span,{style:{color:`#8250DF`,\"--shiki-dark\":`#DCDCAA`},children:` env`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`(`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`'COMMENTIFY_AI_EMBEDDING_MODEL'`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`, `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`'text-embedding-3-small'`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`),`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` 'chat_model'`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:` =>`}),_jsx(_components.span,{style:{color:`#8250DF`,\"--shiki-dark\":`#DCDCAA`},children:` env`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`(`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`'COMMENTIFY_AI_CHAT_MODEL'`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`, `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`'gpt-4o-mini'`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`),`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` 'timeout'`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:` =>`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#B5CEA8`},children:` 8`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`,`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` 'chat_timeout'`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:` =>`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#B5CEA8`},children:` 30`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`,`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` 'duplicate_threshold'`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:` =>`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#B5CEA8`},children:` 0.82`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`,`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` 'duplicate_min_chars'`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:` =>`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#B5CEA8`},children:` 12`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`,`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` 'features'`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:` =>`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` [`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` 'duplicate_detection'`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:` =>`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` true`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`,`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` 'reply_drafts'`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:` =>`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` true`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`,`})]}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` ],`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`],`})}),`\n`]})})}),`\n`,_jsxs(_components.p,{children:[`Run migrations so `,_jsx(_components.code,{children:`commentify_embeddings`}),` exists. Approved comments are embedded when they are approved. Backfill older rows:`]}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`2`,language:`shellscript`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`shellscript`,children:_jsxs(_components.code,{language:`shellscript`,numberOfLines:`2`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#DCDCAA`},children:`php`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` artisan`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` commentify:embed-comments`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#DCDCAA`},children:`php`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` artisan`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` commentify:embed-comments`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` --id=48`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` --sync`})]}),`\n`]})})}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.code,{children:`--sync`}),` runs inline. Otherwise jobs go on your default queue. Process the queue or embeddings stay empty and duplicate checks return no matches.`]}),`\n`,_jsx(Heading,{level:`2`,id:`duplicate-detection`,children:`Duplicate detection`}),`\n`,_jsxs(_components.p,{children:[`Before a `,_jsx(_components.strong,{children:`top-level`}),` post, Livewire and the SDKs ask:`]}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.code,{children:`POST /commentify/api/v1/{type}/{id}/duplicates`}),` with `,_jsx(_components.code,{children:`{ \"body\": \"…\" }`}),`.`]}),`\n`,_jsxs(_components.p,{children:[`A candidate matches only when `,_jsx(_components.strong,{children:`all`}),` of these hold:`]}),`\n`,_jsxs(_components.ol,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`COMMENTIFY_AI`}),` is on, a key is set, and `,_jsx(_components.code,{children:`commentify_embeddings`}),` exists`]}),`\n`,_jsxs(_components.li,{children:[`After collapsing whitespace, the draft is at least `,_jsx(_components.code,{children:`duplicate_min_chars`}),` (default `,_jsx(_components.strong,{children:`12`}),`)`]}),`\n`,_jsxs(_components.li,{children:[`The stored comment is `,_jsx(_components.strong,{children:`approved`}),`, `,_jsx(_components.strong,{children:`top-level`}),`, and on the `,_jsx(_components.strong,{children:`same commentable`})]}),`\n`,_jsxs(_components.li,{children:[`Cosine similarity of the two vectors is `,_jsxs(_components.strong,{children:[`≥ `,_jsx(_components.code,{children:`duplicate_threshold`})]}),` (default `,_jsx(_components.strong,{children:`0.82`}),`)`]}),`\n`]}),`\n`,_jsxs(_components.p,{children:[`Up to five hits, highest score first. Replies are not compared as questions. Short scraps (`,_jsx(_components.code,{children:`ok`}),`, `,_jsx(_components.code,{children:`thanks`}),`) are skipped.`]}),`\n`,_jsxs(_components.p,{children:[`The UI is an `,_jsx(_components.strong,{children:`info`}),` callout (not a red validation error) with permalinks and `,_jsx(_components.strong,{children:`Post anyway`}),`. That button submits the held draft and skips the check once.`]}),`\n`,_jsx(_components.p,{children:`Embeddings are stored as JSON. Matching is PHP cosine similarity. No pgvector.`}),`\n`,_jsx(Heading,{level:`2`,id:`reply-drafts`,children:`Reply drafts`}),`\n`,_jsxs(_components.p,{children:[`On the Filament `,_jsx(_components.strong,{children:`Moderation queue`}),`, `,_jsx(_components.strong,{children:`Draft reply`}),` (when reply drafts are on) queues a job. The model may supply page context:`]}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`4`,language:`php`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`php`,children:_jsxs(_components.code,{language:`php`,numberOfLines:`4`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:`public`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:` function`}),_jsx(_components.span,{style:{color:`#8250DF`,\"--shiki-dark\":`#DCDCAA`},children:` commentifyAiContext`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`()`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:`:`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:` string`})]}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`{`})}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#C586C0`},children:` return`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` $this`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:`->`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`body`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`;`})]}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`}`})}),`\n`]})})}),`\n`,_jsxs(_components.p,{children:[`The draft is a `,_jsx(_components.strong,{children:`pending`}),` reply (`,_jsx(_components.code,{children:`is_approved = false`}),`) with `,_jsx(_components.code,{children:`metadata.drafted_by = ai`}),`. It stays off the public thread until the author or a moderator approves it.`]}),`\n`,_jsx(Heading,{level:`2`,id:`frontends`,children:`Frontends`}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.code,{children:`GET ui`}),` reports `,_jsx(_components.code,{children:`features.duplicate_detection`}),` and `,_jsx(_components.code,{children:`features.reply_drafts`}),`. List payloads include `,_jsx(_components.code,{children:`thread.duplicate_detection`}),`. React and Vue intercept in the thread store (`,_jsx(_components.code,{children:`post()`}),` returns `,_jsx(_components.code,{children:`null`}),` when matches exist; `,_jsx(_components.code,{children:`store.postAnyway()`}),` posts the draft). Livewire uses `,_jsx(_components.code,{children:`postAnyway`}),` on the comments component.`]}),`\n`,_jsx(Warning,{children:_jsxs(_components.p,{children:[`Never commit `,_jsx(_components.code,{children:`COMMENTIFY_AI_KEY`}),`. Keep it in `,_jsx(_components.code,{children:`.env`}),`. Commentify does not log the key.`]})})]})}function MDXContent(props={}){const{wrapper:MDXLayout}={..._provideComponents(),...props.components};return MDXLayout?_jsx(MDXLayout,{...props,children:_jsx(_createMdxContent,{...props})}):_createMdxContent(props)}return{default:MDXContent};function _missingMdxReference(id,component){throw new Error(`Expected `+(component?`component`:`object`)+\" `\"+id+\"` to be defined: you likely forgot to import, pass, or provide it.\")}","frontmatter":{},"scope":{"config":{},"pageMetadata":{"title":"Bring-your-own-key AI","description":"OpenAI-compatible embeddings and chat on your key. Duplicate detection before post, and Filament reply drafts. Commentify never hosts models.","href":"/ai"}}},"mdxSourceWithNoJs":null},"mdxExtracts":{"tableOfContents":[{"title":"Enable it","slug":"enable-it","depth":2,"children":[]},{"title":"Duplicate detection","slug":"duplicate-detection","depth":2,"children":[]},{"title":"Reply drafts","slug":"reply-drafts","depth":2,"children":[]},{"title":"Frontends","slug":"frontends","depth":2,"children":[]}],"codeExamples":{}},"jsLinks":[],"subdomain":"commentify","siteName":"Commentify Pro","title":"Bring-your-own-key AI - Commentify Pro","navMaps":{"firstHrefInVersion":{},"firstHrefInLanguage":{}},"lastModified":"2026-11-01T00:02:22.474Z","gitLastModified":"2026-11-01T00:02:15.000Z","originalFileLocation":"ai.mdx"},"headHtml":"\u003cscript data-mint-head>(function(){var d=document.documentElement,p=null;if(!p){try{p=localStorage.getItem(\"isDarkMode\")}catch(e){}if(p==='true')p='dark';else if(p==='false')p='light';if(p!=='light'\u0026\u0026p!=='dark'\u0026\u0026p!=='system')p=\"system\"}var r=p==='system'?(matchMedia(\"(prefers-color-scheme: dark)\").matches?'dark':'light'):p;d.classList.remove(r==='dark'?'light':'dark');d.classList.add(r);d.setAttribute(\"data-theme-preference\",p)})();\u003c/script>\u003cmeta data-mint-head name=\"description\" content=\"OpenAI-compatible embeddings and chat on your key. Duplicate detection before post, and Filament reply drafts. Commentify never hosts models.\">\u003cmeta data-mint-head name=\"keywords\" content=\"Laravel comments package, Commentify Pro, Laravel Livewire comments, Laravel comment API, Disqus alternative Laravel, Filament comment moderation, Laravel React comments, Laravel Vue comments, self-hosted comments Laravel, import Disqus comments Laravel\">\u003cmeta data-mint-head name=\"application-name\" content=\"Commentify Pro\">\u003cmeta data-mint-head name=\"generator\" content=\"Mintlify\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/commentify/EDJI4D5MmY9jtmId/_generated/favicon/android-chrome-192x192.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"apple-touch-icon\" type=\"image/png\" sizes=\"180x180\" href=\"/mintlify-assets/_mintlify/favicons/commentify/EDJI4D5MmY9jtmId/_generated/favicon/apple-touch-icon.png\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/commentify/EDJI4D5MmY9jtmId/_generated/favicon/favicon-16x16.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/commentify/EDJI4D5MmY9jtmId/_generated/favicon/favicon-32x32.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/commentify/EDJI4D5MmY9jtmId/_generated/favicon/favicon.ico\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/commentify/EDJI4D5MmY9jtmId/_generated/favicon-dark/android-chrome-192x192.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/commentify/EDJI4D5MmY9jtmId/_generated/favicon-dark/favicon-16x16.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/commentify/EDJI4D5MmY9jtmId/_generated/favicon-dark/favicon-32x32.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/commentify/EDJI4D5MmY9jtmId/_generated/favicon-dark/favicon.ico\" media=\"(prefers-color-scheme: dark)\">\u003cmeta data-mint-head property=\"og:title\" content=\"Bring-your-own-key AI - Commentify Pro\">\u003cmeta data-mint-head property=\"og:description\" content=\"OpenAI-compatible embeddings and chat on your key. Duplicate detection before post, and Filament reply drafts. Commentify never hosts models.\">\u003cmeta data-mint-head property=\"og:site_name\" content=\"Commentify Pro Docs\">\u003cmeta data-mint-head property=\"og:url\" content=\"https://docs.commentify.pro/ai\">\u003cmeta data-mint-head property=\"og:type\" content=\"website\">\u003cmeta data-mint-head property=\"og:image\" content=\"https://docs.commentify.pro/images/github-social-card.png\">\u003cmeta data-mint-head property=\"og:image:width\" content=\"1200\">\u003cmeta data-mint-head property=\"og:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"twitter:card\" content=\"summary_large_image\">\u003cmeta data-mint-head name=\"twitter:title\" content=\"Bring-your-own-key AI - Commentify Pro\">\u003cmeta data-mint-head name=\"twitter:description\" content=\"OpenAI-compatible embeddings and chat on your key. Duplicate detection before post, and Filament reply drafts. Commentify never hosts models.\">\u003cmeta data-mint-head name=\"twitter:image\" content=\"https://docs.commentify.pro/images/github-social-card.png\">\u003cmeta data-mint-head name=\"twitter:image:width\" content=\"1200\">\u003cmeta data-mint-head name=\"twitter:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"msapplication-config\" content=\"/mintlify-assets/_mintlify/favicons/commentify/EDJI4D5MmY9jtmId/_generated/favicon/browserconfig.xml\">\u003cmeta data-mint-head name=\"apple-mobile-web-app-title\" content=\"Commentify Pro\">\u003cmeta data-mint-head name=\"msapplication-TileColor\" content=\"#EC3013\">\u003clink data-mint-head rel=\"canonical\" href=\"https://docs.commentify.pro/ai\">\u003clink data-mint-head rel=\"alternate\" type=\"application/xml\" href=\"/sitemap.xml\">\u003clink data-mint-head rel=\"alternate\" type=\"text/markdown\" href=\"/ai.md\">\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@graph\":[{\"@type\":\"Organization\",\"@id\":\"https://docs.commentify.pro/#organization\",\"name\":\"Commentify Pro\",\"url\":\"https://docs.commentify.pro\",\"logo\":{\"@type\":\"ImageObject\",\"url\":\"https://mintcdn.com/commentify/O63iJiVjr-DWhaIG/logo/commentify-pro-lockup.png?fit=max\u0026auto=format\u0026n=O63iJiVjr-DWhaIG\u0026q=85\u0026s=e6d287c1a094e92bf096365289389212\"}},{\"@type\":\"WebSite\",\"@id\":\"https://docs.commentify.pro#website\",\"name\":\"Commentify Pro\",\"url\":\"https://docs.commentify.pro\",\"publisher\":{\"@id\":\"https://docs.commentify.pro/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https://docs.commentify.pro/ai#webpage\",\"url\":\"https://docs.commentify.pro/ai\",\"name\":\"Bring-your-own-key AI\",\"description\":\"OpenAI-compatible embeddings and chat on your key. Duplicate detection before post, and Filament reply drafts. Commentify never hosts models.\",\"dateModified\":\"2026-11-01T00:02:15.000Z\",\"isPartOf\":{\"@id\":\"https://docs.commentify.pro#website\"},\"breadcrumb\":{\"@id\":\"https://docs.commentify.pro/ai#breadcrumb\"}},{\"@type\":\"BreadcrumbList\",\"@id\":\"https://docs.commentify.pro/ai#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Features\",\"item\":\"https://docs.commentify.pro/modes\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Bring-your-own-key AI\",\"item\":\"https://docs.commentify.pro/ai\"}]},{\"@type\":[\"Article\",\"TechArticle\"],\"@id\":\"https://docs.commentify.pro/ai#article\",\"headline\":\"Bring-your-own-key AI\",\"name\":\"Bring-your-own-key AI\",\"description\":\"OpenAI-compatible embeddings and chat on your key. Duplicate detection before post, and Filament reply drafts. Commentify never hosts models.\",\"url\":\"https://docs.commentify.pro/ai\",\"mainEntityOfPage\":{\"@id\":\"https://docs.commentify.pro/ai#webpage\"},\"image\":\"https://docs.commentify.pro/images/github-social-card.png\",\"dateModified\":\"2026-11-01T00:02:15.000Z\",\"publisher\":{\"@id\":\"https://docs.commentify.pro/#organization\"},\"isPartOf\":{\"@id\":\"https://docs.commentify.pro#website\"}}]}\u003c/script>\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@type\":\"WebSite\",\"name\":\"Commentify Pro\",\"creator\":{\"@type\":\"Organization\",\"name\":\"Mintlify\",\"url\":\"https://mintlify.com\"}}\u003c/script>","siteName":"Commentify Pro","nav":{"global":null,"pages":[{"group":"Commentify","expanded":true,"pages":[{"title":"Commentify Pro","href":"/index"},{"title":"Install Commentify","href":"/installation"},{"title":"Make a model commentable","href":"/usage"},{"title":"Threads","href":"/threads"},{"title":"Configuration","href":"/configuration"},{"title":"Guest commenting","href":"/guests"},{"title":"Customization","href":"/customization"},{"title":"Authorization","href":"/authorization"},{"title":"Email notifications and subscriptions","href":"/notifications"},{"title":"Filament","href":"/filament"},{"title":"Upgrade Commentify","href":"/upgrading-core"}]},{"group":"Commentify Pro","tag":"Pro","pages":[{"title":"Install Commentify Pro","href":"/quickstart"},{"title":"Licensing","href":"/licensing"},{"title":"Upgrade to Commentify Pro","href":"/upgrading"},{"title":"Commentify Pro","href":"/readme"}]},{"group":"Frontends","tag":"Pro","pages":[{"title":"Livewire","href":"/livewire"},{"title":"How the JavaScript ships","href":"/javascript"},{"title":"React","href":"/react"},{"title":"Vue","href":"/vue"},{"title":"Hook reference","href":"/hooks"},{"title":"Bring your own markup","href":"/headless"}]},{"group":"API","tag":"Pro","pages":[{"title":"JSON API","href":"/api"},{"title":"Authentication","href":"/authentication"}]},{"group":"Moderation","tag":"Pro","pages":[{"title":"Spam pipeline","href":"/spam"},{"title":"Moderator webhooks","href":"/webhooks"}]},{"group":"Features","tag":"Pro","pages":[{"title":"Thread modes","description":"Per-commentable discussion, reviews, or Q\u0026A. Ratings and accepted answers follow the mode unless you override them.","href":"/modes"},{"title":"Bring-your-own-key AI","href":"/ai"},{"title":"Image uploads","description":"GIF, JPEG, PNG, and WebP via POST commentify/api/v1/media. Per-role size and rate limits, optional S3 bucket and thumbnails.","href":"/media"},{"title":"Realtime threads","href":"/realtime"},{"title":"Badges and pinned comments","href":"/badges"},{"title":"Reactions","href":"/reactions"},{"title":"AI toxicity scoring","href":"/toxicity"},{"title":"Import Disqus and WordPress","href":"/import"},{"title":"GDPR","href":"/gdpr"}]},{"group":"Help","pages":[{"title":"Troubleshooting","href":"/troubleshooting"}]}]},"basePath":"","title":"Bring-your-own-key AI - Commentify Pro","buildId":"dpl_FWZhTb2vab28gFGFH4ojka6qEznQ","deploymentId":"dpl_FWZhTb2vab28gFGFH4ojka6qEznQ","dynamics":["10e4515597"],"chunks":["BannerMarkdown-C79IUEr8.js","dist-DE3EVdvd.js","lib-DuNMoZ05.js","lib-B8HbPgcC.js","clsx-DK3K4Z1S.js","html-void-elements-mCSPHezU.js","katex-YMM-FMeD.js","marked.esm-CZWythHk.js"]}