1import { themes as prismThemes } from 'prism-react-renderer'
2import type { Config } from '@docusaurus/types'
3import type * as Preset from '@docusaurus/preset-classic'
4
5import type * as OpenApiPlugin from 'docusaurus-plugin-openapi-docs'
6import type { Options as BlogOptions } from '@docusaurus/plugin-content-blog'
7import type { Options as PageOptions } from '@docusaurus/plugin-content-pages'
8
9import tailwindPlugin from './plugins/tailwind-config.cjs'
10import * as fs from 'fs'
11import * as path from 'path'
12
13// Load versions from versions.json if it exists (generated at build time)
14const versionsPath = path.join(__dirname, 'versions.json')
15const versions: string[] = fs.existsSync(versionsPath)
16 ? JSON.parse(fs.readFileSync(versionsPath, 'utf-8'))
17 : []
18
19// Build version configuration dynamically
20// Trunk docs (current) are "Next" at /docs/next
21// Highest version in versions.json (e.g., 1.11.x) is "Latest" at /docs (default)
22// Previous versions are at /docs/v1.10, etc.
23// Maintenance policy: latest + 1 previous minor versions are maintained
24const hasVersions = versions.length > 0
25
26// Extract major and minor version numbers from versions to determine ordering and maintenance status
27const getVersion = (v: string): { major: number; minor: number } => {
28 const match = v.match(/^(\d+)\.(\d+)/)
29 return match
30 ? { major: parseInt(match[1], 10), minor: parseInt(match[2], 10) }
31 : { major: 0, minor: 0 }
32}
33
34// Sort versions by semver (major desc, then minor desc)
35const sortedVersions = [...versions].sort((a, b) => {
36 const vA = getVersion(a)
37 const vB = getVersion(b)
38 if (vB.major !== vA.major) return vB.major - vA.major
39 return vB.minor - vA.minor
40})
41
42// Highest version is "latest", trunk (current) is "next"
43const latestVersion = sortedVersions[0] || null
44const latestVer = latestVersion ? getVersion(latestVersion) : { major: 0, minor: 0 }
45
46// Final release of the previous major stays maintained through the transition
47// (e.g. v1.11.x remains maintained after v2.0 ships as latest). sortedVersions is
48// major-desc, so the first entry below the latest major is that major's newest release.
49const prevMajorLatest = sortedVersions.find((v) => getVersion(v).major < latestVer.major) || null
50
51const docsVersionConfig = hasVersions
52 ? {
53 lastVersion: latestVersion!, // The stable release is the default
54 onlyIncludeVersions: [...versions, 'current'],
55 versions: {
56 current: {
57 label: 'Next',
58 path: 'next',
59 banner: 'unreleased' as const
60 },
61 ...Object.fromEntries(
62 versions.map((version) => {
63 const ver = getVersion(version)
64 const isLatest = version === latestVersion
65 // Maintained (no banner): the latest major's current + previous minor,
66 // plus the final release of the previous major (e.g. v1.11.x under v2.0).
67 const isMaintained =
68 ver.major > latestVer.major ||
69 (ver.major === latestVer.major && ver.minor >= latestVer.minor - 1) ||
70 version === prevMajorLatest
71
72 return [
73 version,
74 {
75 label: isLatest
76 ? `Latest (v${version.replace('.x', '')})`
77 : `v${version.replace('.x', '')}`,
78 path: isLatest ? '' : `v${version.replace('.x', '')}`,
79 banner: isMaintained ? ('none' as const) : ('unmaintained' as const)
80 }
81 ]
82 })
83 )
84 }
85 }
86 : {
87 // No versions generated - use current docs only (for local dev)
88 lastVersion: 'current',
89 versions: {
90 current: {
91 label: 'Latest',
92 path: '',
93 banner: 'none' as const
94 }
95 },
96 onlyIncludeVersions: ['current']
97 }
98
99const config: Config = {
100 title: 'Spice.ai OSS',
101 tagline:
102 'A portable SQL query and AI compute engine, written in Rust, for data-grounded apps and agents.',
103 favicon: 'img/favicon.ico',
104
105 // Set the production url of your site here
106 url: 'https://spiceai.org',
107 // Set the /<baseUrl>/ pathname under which your site is served
108 // For GitHub pages deployment, it is often '/<projectName>/'
109 baseUrl: '/',
110
111 trailingSlash: false,
112
113 // GitHub pages deployment config.
114 // If you aren't using GitHub pages, you don't need these.
115 organizationName: 'spiceai', // Usually your GitHub org/user name.
116 projectName: 'docs', // Usually your repo name.
117
118 onBrokenAnchors: 'throw',
119 onBrokenLinks: 'throw',
120
121 markdown: {
122 mermaid: true,
123 hooks: {
124 onBrokenMarkdownLinks: 'throw'
125 }
126 },
127
128 // Even if you don't use internationalization, you can use this field to set
129 // useful metadata like html lang. For example, if your site is Chinese, you
130 // may want to replace "en" with "zh-Hans".
131 i18n: {
132 defaultLocale: 'en',
133 locales: ['en']
134 },
135
136 future: {
137 v4: {
138 removeLegacyPostBuildHeadAttribute: true // required
139 },
140 faster: true
141 },
142
143 presets: [
144 [
145 'classic',
146 {
147 docs: {
148 routeBasePath: '/docs',
149 path: 'docs',
150 sidebarPath: 'sidebars.ts',
151 onInlineTags: 'throw',
152 docItemComponent: '@theme/ApiItem',
153 editUrl: ({ versionDocsDirPath, docPath }) => {
154 return `https://github.com/spiceai/docs/edit/trunk/website/${versionDocsDirPath}/${docPath}`
155 },
156 ...docsVersionConfig
157 },
158 blog: {
159 path: 'blog',
160 showLastUpdateAuthor: true,
161 showLastUpdateTime: true,
162 onInlineTags: 'throw',
163 onUntruncatedBlogPosts: 'ignore',
164 editUrl: ({ locale, blogDirPath, blogPath }) => {
165 return `https://github.com/spiceai/docs/edit/trunk/website/${blogDirPath}/${blogPath}`
166 },
167 remarkPlugins: [],
168 postsPerPage: 5,
169 feedOptions: {
170 type: 'all',
171 description:
172 'Keep up to date with upcoming Spice.ai OSS releases and articles by following our feed!',
173 copyright: `Copyright © 2021-2026 Spice AI, Inc.`,
174 xslt: true
175 },
176 blogTitle: 'Spice.ai OSS blog',
177 blogDescription: 'Read blog posts about Spice.ai OSS from the team and community',
178 blogSidebarCount: 'ALL',
179 blogSidebarTitle: 'All Posts'
180 } satisfies BlogOptions,
181 pages: {
182 remarkPlugins: [],
183 showLastUpdateAuthor: true,
184 showLastUpdateTime: true
185 } satisfies PageOptions,
186 theme: {
187 customCss: ['./src/css/custom.css', './src/css/openapi.css', './src/css/preflight.css']
188 },
189 gtag: {
190 trackingID: 'G-SST0X6NS37',
191 anonymizeIP: true
192 },
193 sitemap: {
194 lastmod: 'date',
195 changefreq: 'weekly',
196 priority: 0.5,
197 ignorePatterns: ['/tags/**'],
198 filename: 'sitemap.xml'
199 }
200 } satisfies Preset.Options
201 ]
202 ],
203 themes: ['docusaurus-theme-openapi-docs', '@docusaurus/theme-mermaid'],
204 themeConfig: {
205 // SEO metadata configuration
206 metadata: [
207 {
208 name: 'keywords',
209 content:
210 'spice, spice.ai, sql query engine, ai compute engine, data federation, data acceleration, rag, retrieval augmented generation, arrow flight, datafusion, duckdb, rust, llm, openai compatible, mcp server'
211 },
212 { name: 'author', content: 'Spice AI, Inc.' },
213 { name: 'robots', content: 'index, follow' },
214 { property: 'og:type', content: 'website' },
215 { property: 'og:site_name', content: 'Spice.ai OSS' },
216 { property: 'og:image:width', content: '1200' },
217 { property: 'og:image:height', content: '630' },
218 { property: 'og:image:alt', content: 'Spice.ai - SQL Query and AI Compute Engine' },
219 { name: 'twitter:card', content: 'summary_large_image' },
220 { name: 'twitter:site', content: '@spice_ai' },
221 { name: 'twitter:creator', content: '@spice_ai' }
222 ],
223 announcementBar: {
224 content: '<a href="/releases/v2.2.0">Spice.ai OSS v2.2.0</a> is now available! 🎉',
225 backgroundColor: 'var(--announcement-bar-bg)',
226 textColor: 'var(--announcement-bar-text)',
227 isCloseable: true
228 },
229 navbar: {
230 title: 'Spice.ai OSS',
231 logo: {
232 alt: 'Spice.ai OSS logo',
233 src: 'img/logo.svg'
234 },
235 hideOnScroll: true,
236 items: [
237 {
238 type: 'doc',
239 position: 'left',
240 docId: 'index',
241 label: 'Docs'
242 },
243 {
244 type: 'docSidebar',
245 position: 'left',
246 sidebarId: 'api',
247 label: 'API'
248 },
249 { to: 'releases', label: 'Releases', position: 'left' },
250 {
251 label: 'Blog',
252 href: 'https://spice.ai/blog',
253 position: 'left'
254 },
255 { to: 'cookbook', label: 'Cookbook', position: 'left' },
256 { to: 'docs/reference/sql', label: 'SQL Reference', position: 'left' },
257 // Version dropdown is only shown when versions exist
258 ...(hasVersions
259 ? [
260 {
261 type: 'docsVersionDropdown' as const,
262 position: 'right' as const,
263 dropdownActiveClassDisabled: true
264 }
265 ]
266 : []),
267 {
268 label: 'Try Spice Cloud',
269 href: 'https://spice.ai/login',
270 position: 'right'
271 },
272 {
273 label: 'X',
274 href: 'https://x.com/spice_ai',
275 position: 'right'
276 },
277 {
278 label: 'Slack',
279 href: 'https://spice.ai/slack',
280 position: 'right'
281 },
282 {
283 label: 'YouTube',
284 href: 'https://www.youtube.com/@spiceai',
285 position: 'right'
286 },
287 {
288 href: 'https://github.com/spiceai/spiceai',
289 position: 'right',
290 className: 'header-github-link',
291 'aria-label': 'GitHub repository'
292 }
293 ]
294 },
295 footer: {
296 style: 'dark',
297 links: [
298 {
299 title: 'Docs',
300 items: [
301 {
302 label: 'Getting Started',
303 to: '/docs/getting-started'
304 },
305 {
306 label: 'API',
307 to: '/docs/api'
308 },
309 {
310 label: 'CLI',
311 to: '/docs/cli'
312 },
313 {
314 label: 'SDKs',
315 to: '/docs/sdks'
316 }
317 ]
318 },
319 {
320 title: 'Community',
321 items: [
322 {
323 label: 'Reddit',
324 href: 'https://reddit.com/r/spiceai'
325 },
326 {
327 label: 'Slack',
328 href: 'https://spice.ai/slack'
329 },
330 {
331 label: 'X',
332 href: 'https://x.com/spice_ai'
333 },
334 {
335 label: 'YouTube',
336 href: 'https://www.youtube.com/@spiceai'
337 }
338 ]
339 },
340 {
341 title: 'More',
342 items: [
343 {
344 label: 'Blog',
345 href: 'https://spice.ai/blog'
346 },
347 {
348 label: 'GitHub',
349 href: 'https://github.com/spiceai/spiceai'
350 }
351 ]
352 }
353 ],
354 copyright: `Copyright © 2025 Spice AI, Inc.`
355 },
356 languageTabs: [
357 {
358 highlight: 'bash',
359 language: 'curl',
360 logoClass: 'curl'
361 }
362 ],
363 prism: {
364 theme: prismThemes.oneLight,
365 darkTheme: prismThemes.gruvboxMaterialDark,
366 additionalLanguages: ['bash', 'json', 'csharp']
367 },
368 algolia: {
369 appId: '0SP8I8JTL8',
370 apiKey: '72f66fe334ccd3c7db696a123d68735c',
371 indexName: 'spiceai',
372 contextualSearch: true
373 }
374 } satisfies Preset.ThemeConfig,
375
376 headTags: [
377 // Structured data for SEO (JSON-LD)
378 {
379 tagName: 'script',
380 attributes: {
381 type: 'application/ld+json'
382 },
383 innerHTML: JSON.stringify({
384 '@context': 'https://schema.org',
385 '@type': 'SoftwareApplication',
386 name: 'Spice.ai OSS',
387 applicationCategory: 'DeveloperApplication',
388 operatingSystem: 'Linux, macOS, Windows',
389 description:
390 'A portable SQL query and AI compute engine, written in Rust, for data-grounded apps and agents.',
391 url: 'https://spiceai.org',
392 downloadUrl: 'https://github.com/spiceai/spiceai/releases',
393 softwareVersion: '1.10.0',
394 author: {
395 '@type': 'Organization',
396 name: 'Spice AI, Inc.',
397 url: 'https://spice.ai'
398 },
399 license: 'https://github.com/spiceai/spiceai/blob/trunk/LICENSE',
400 programmingLanguage: 'Rust',
401 offers: {
402 '@type': 'Offer',
403 price: '0',
404 priceCurrency: 'USD'
405 }
406 })
407 },
408 {
409 tagName: 'script',
410 attributes: {
411 type: 'application/ld+json'
412 },
413 innerHTML: JSON.stringify({
414 '@context': 'https://schema.org',
415 '@type': 'Organization',
416 name: 'Spice AI, Inc.',
417 url: 'https://spice.ai',
418 logo: 'https://spiceai.org/img/logo.svg',
419 sameAs: [
420 'https://github.com/spiceai',
421 'https://x.com/spice_ai',
422 'https://www.youtube.com/@spiceai',
423 'https://www.linkedin.com/company/spiceai'
424 ]
425 })
426 },
427 {
428 tagName: 'link',
429 attributes: {
430 rel: 'preconnect',
431 href: 'https://fonts.googleapis.com'
432 }
433 },
434 {
435 tagName: 'link',
436 attributes: {
437 rel: 'preconnect',
438 href: 'https://fonts.gstatic.com',
439 crossorigin: 'true'
440 }
441 },
442 {
443 tagName: 'link',
444 attributes: {
445 href: 'https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;700&family=Roboto+Mono:ital,wght@0,100..700;1,100..700&display=swap',
446 rel: 'stylesheet'
447 }
448 },
449 {
450 tagName: 'link',
451 attributes: {
452 rel: 'icon',
453 type: 'image/png',
454 sizes: '32x32',
455 href: '/favicon-32x32.png'
456 }
457 },
458 {
459 tagName: 'link',
460 attributes: {
461 rel: 'icon',
462 type: 'image/png',
463 sizes: '16x16',
464 href: '/favicon-16x16.png'
465 }
466 },
467 {
468 tagName: 'script',
469 attributes: {
470 type: 'text/javascript',
471 id: 'hs-script-loader',
472 async: 'true',
473 defer: 'true',
474 src: '//js.hs-scripts.com/46107967.js'
475 }
476 },
477 // SEO: hreflang for language targeting
478 {
479 tagName: 'link',
480 attributes: {
481 rel: 'alternate',
482 hreflang: 'en',
483 href: 'https://spiceai.org'
484 }
485 },
486 {
487 tagName: 'link',
488 attributes: {
489 rel: 'alternate',
490 hreflang: 'x-default',
491 href: 'https://spiceai.org'
492 }
493 }
494 ],
495
496 plugins: [
497 tailwindPlugin,
498 [
499 '@docusaurus/plugin-content-blog',
500 {
501 id: 'releases',
502 path: 'releases',
503 routeBasePath: 'releases',
504 showLastUpdateAuthor: true,
505 showLastUpdateTime: true,
506 onInlineTags: 'throw',
507 onUntruncatedBlogPosts: 'ignore',
508 editUrl: ({ locale, blogDirPath, blogPath }) => {
509 return `https://github.com/spiceai/docs/edit/trunk/website/${blogDirPath}/${blogPath}`
510 },
511 remarkPlugins: [],
512 postsPerPage: 10,
513 feedOptions: {
514 type: 'all',
515 description: 'Keep up to date with Spice.ai OSS releases by following our feed!',
516 copyright: `Copyright © 2025-2026 Spice AI, Inc.`,
517 xslt: true
518 },
519 blogTitle: 'Spice.ai OSS Releases',
520 blogDescription: 'Spice.ai OSS release notes and announcements',
521 blogSidebarCount: 'ALL',
522 blogSidebarTitle: 'All Releases'
523 } satisfies BlogOptions
524 ],
525 [
526 'docusaurus-plugin-openapi-docs',
527 {
528 id: 'api',
529 docsPluginId: 'classic',
530 config: {
531 spice: {
532 proxy: 'http://localhost:8090',
533
534 specPath: 'public/openapi.json',
535 outputDir: 'docs/api/HTTP',
536 sidebarOptions: {
537 groupPathsBy: 'tag'
538 }
539 } satisfies OpenApiPlugin.Options
540 }
541 }
542 ],
543 [
544 '@docusaurus/plugin-client-redirects',
545 {
546 redirects: [
547 {
548 from: '/blog',
549 to: 'https://spice.ai/blog'
550 },
551 {
552 from: '/blog/announcing-1.0-stable',
553 to: 'https://spice.ai/blog/announcing-spice-ai-open-source-1-0-stable'
554 },
555 {
556 from: '/blog/amazon-s3-vectors-with-spice',
557 to: 'https://spice.ai/blog/getting-started-with-amazon-s3-vectors-and-spice'
558 },
559 {
560 from: '/blog/releases/v1.10-0',
561 to: '/releases/v1.10.0'
562 },
563 {
564 from: '/query-federation',
565 to: '/docs/features/query-federation'
566 },
567 // 2021 blog posts
568 {
569 from: '/blog/a-new-class-of-applications-that-learn-and-adapt',
570 to: 'https://spice.ai/blog/a-new-class-of-applications-that-learn-and-adapt'
571 },
572 {
573 from: '/blog/2021/a-new-class-of-applications-that-learn-and-adapt',
574 to: 'https://spice.ai/blog/a-new-class-of-applications-that-learn-and-adapt'
575 },
576 {
577 from: '/blog/ai-needs-ai-ready-data',
578 to: 'https://spice.ai/blog/ai-needs-ai-ready-data'
579 },
580 {
581 from: '/blog/2021/ai-needs-ai-ready-data',
582 to: 'https://spice.ai/blog/ai-needs-ai-ready-data'
583 },
584 {
585 from: '/blog/making-apps-that-learn-and-adapt',
586 to: 'https://spice.ai/blog/making-apps-that-learn-and-adapt'
587 },
588 {
589 from: '/blog/2021/making-apps-that-learn-and-adapt',
590 to: 'https://spice.ai/blog/making-apps-that-learn-and-adapt'
591 },
592 {
593 from: '/blog/q-learning-reward-is-all-you-need',
594 to: 'https://spice.ai/blog/q-learning-reward-is-all-you-need'
595 },
596 {
597 from: '/blog/spiceais-approach-to-time-series-ai',
598 to: 'https://spice.ai/blog/spiceais-approach-to-time-series-ai'
599 },
600 {
601 from: '/blog/2021/spiceais-approach-to-time-series-ai',
602 to: 'https://spice.ai/blog/spiceais-approach-to-time-series-ai'
603 },
604 {
605 from: '/blog/spicepods-from-zero-to-hero',
606 to: 'https://spice.ai/blog/spicepods-from-zero-to-hero'
607 },
608 {
609 from: '/blog/2021/spicepods-from-zero-to-hero',
610 to: 'https://spice.ai/blog/spicepods-from-zero-to-hero'
611 },
612 {
613 from: '/blog/teaching-apps-how-to-learn-with-spicepods',
614 to: 'https://spice.ai/blog/teaching-apps-how-to-learn-with-spicepods'
615 },
616 // 2022 blog posts
617 {
618 from: '/blog/adding-soft-actor-critic',
619 to: 'https://spice.ai/blog/adding-soft-actor-critic'
620 },
621 {
622 from: '/blog/building-on-apache-arrow-and-flight',
623 to: 'https://spice.ai/blog/building-on-apache-arrow-and-flight'
624 },
625 {
626 from: '/blog/what-data-informs-ai-driven-decision-making',
627 to: 'https://spice.ai/blog/what-data-informs-ai-driven-decision-making'
628 },
629 {
630 from: '/blog/2022/what-data-informs-ai-driven-decision-making',
631 to: 'https://spice.ai/blog/what-data-informs-ai-driven-decision-making'
632 },
633 // 2024 blog posts
634 {
635 from: '/blog/adding-spice',
636 to: 'https://spice.ai/blog/adding-spice-the-next-generation-of-spice-ai-oss'
637 },
638 {
639 from: '/blog/2024/adding-spice',
640 to: 'https://spice.ai/blog/adding-spice--the-next-generation-of-spice-ai-oss'
641 },
642 {
643 from: '/federated-queries',
644 to: '/docs/features/query-federation'
645 },
646 {
647 from: '/data-ingestion',
648 to: '/docs/features/data-ingestion'
649 },
650 {
651 from: '/data-acceleration',
652 to: '/docs/features/data-acceleration'
653 },
654 {
655 from: '/monitoring',
656 to: '/docs/features/observability'
657 }
658 ]
659 }
660 ],
661 [
662 '@signalwire/docusaurus-plugin-llms-txt',
663 {
664 siteTitle: 'Spice.ai OSS',
665 siteDescription:
666 'A portable SQL query and AI compute engine, written in Rust, for data-grounded apps and agents.',
667 depth: 2,
668 logLevel: 1,
669 content: {
670 includeBlog: false,
671 includePages: true,
672 includeDocs: true,
673 enableLlmsFullTxt: true,
674 enableMarkdownFiles: false,
675 excludeRoutes: ['/tags/**', '/search', '/api/HTTP/**']
676 },
677 optionalLinks: [
678 {
679 title: 'GitHub Repository',
680 url: 'https://github.com/spiceai/spiceai',
681 description: 'Spice.ai OSS source code and issue tracker'
682 },
683 {
684 title: 'Cookbook',
685 url: 'https://github.com/spiceai/cookbook',
686 description: 'Ready-to-use recipes and examples for Spice.ai'
687 },
688 {
689 title: 'Spice Cloud Platform',
690 url: 'https://spice.ai',
691 description: 'Managed cloud platform for Spice.ai'
692 }
693 ]
694 }
695 ]
696 ]
697}
698
699export default config
700
1import { themes as prismThemes } from 'prism-react-renderer'
2import type { Config } from '@docusaurus/types'
3import type * as Preset from '@docusaurus/preset-classic'
4
5import type * as OpenApiPlugin from 'docusaurus-plugin-openapi-docs'
6import type { Options as BlogOptions } from '@docusaurus/plugin-content-blog'
7import type { Options as PageOptions } from '@docusaurus/plugin-content-pages'
8
9import tailwindPlugin from './plugins/tailwind-config.cjs'
10import * as fs from 'fs'
11import * as path from 'path'
12
13// Load versions from versions.json if it exists (generated at build time)
14const versionsPath = path.join(__dirname, 'versions.json')
15const versions: string[] = fs.existsSync(versionsPath)
16 ? JSON.parse(fs.readFileSync(versionsPath, 'utf-8'))
17 : []
18
19// Build version configuration dynamically
20// Trunk docs (current) are "Next" at /docs/next
21// Highest version in versions.json (e.g., 1.11.x) is "Latest" at /docs (default)
22// Previous versions are at /docs/v1.10, etc.
23// Maintenance policy: latest + 1 previous minor versions are maintained
24const hasVersions = versions.length > 0
25
26// Extract major and minor version numbers from versions to determine ordering and maintenance status
27const getVersion = (v: string): { major: number; minor: number } => {
28 const match = v.match(/^(\d+)\.(\d+)/)
29 return match
30 ? { major: parseInt(match[1], 10), minor: parseInt(match[2], 10) }
31 : { major: 0, minor: 0 }
32}
33
34// Sort versions by semver (major desc, then minor desc)
35const sortedVersions = [...versions].sort((a, b) => {
36 const vA = getVersion(a)
37 const vB = getVersion(b)
38 if (vB.major !== vA.major) return vB.major - vA.major
39 return vB.minor - vA.minor
40})
41
42// Highest version is "latest", trunk (current) is "next"
43const latestVersion = sortedVersions[0] || null
44const latestVer = latestVersion ? getVersion(latestVersion) : { major: 0, minor: 0 }
45
46// Final release of the previous major stays maintained through the transition
47// (e.g. v1.11.x remains maintained after v2.0 ships as latest). sortedVersions is
48// major-desc, so the first entry below the latest major is that major's newest release.
49const prevMajorLatest = sortedVersions.find((v) => getVersion(v).major < latestVer.major) || null
50
51const docsVersionConfig = hasVersions
52 ? {
53 lastVersion: latestVersion!, // The stable release is the default
54 onlyIncludeVersions: [...versions, 'current'],
55 versions: {
56 current: {
57 label: 'Next',
58 path: 'next',
59 banner: 'unreleased' as const
60 },
61 ...Object.fromEntries(
62 versions.map((version) => {
63 const ver = getVersion(version)
64 const isLatest = version === latestVersion
65 // Maintained (no banner): the latest major's current + previous minor,
66 // plus the final release of the previous major (e.g. v1.11.x under v2.0).
67 const isMaintained =
68 ver.major > latestVer.major ||
69 (ver.major === latestVer.major && ver.minor >= latestVer.minor - 1) ||
70 version === prevMajorLatest
71
72 return [
73 version,
74 {
75 label: isLatest
76 ? `Latest (v${version.replace('.x', '')})`
77 : `v${version.replace('.x', '')}`,
78 path: isLatest ? '' : `v${version.replace('.x', '')}`,
79 banner: isMaintained ? ('none' as const) : ('unmaintained' as const)
80 }
81 ]
82 })
83 )
84 }
85 }
86 : {
87 // No versions generated - use current docs only (for local dev)
88 lastVersion: 'current',
89 versions: {
90 current: {
91 label: 'Latest',
92 path: '',
93 banner: 'none' as const
94 }
95 },
96 onlyIncludeVersions: ['current']
97 }
98
99const config: Config = {
100 title: 'Spice.ai OSS',
101 tagline:
102 'A portable SQL query and AI compute engine, written in Rust, for data-grounded apps and agents.',
103 favicon: 'img/favicon.ico',
104
105 // Set the production url of your site here
106 url: 'https://spiceai.org',
107 // Set the /<baseUrl>/ pathname under which your site is served
108 // For GitHub pages deployment, it is often '/<projectName>/'
109 baseUrl: '/',
110
111 trailingSlash: false,
112
113 // GitHub pages deployment config.
114 // If you aren't using GitHub pages, you don't need these.
115 organizationName: 'spiceai', // Usually your GitHub org/user name.
116 projectName: 'docs', // Usually your repo name.
117
118 onBrokenAnchors: 'throw',
119 onBrokenLinks: 'throw',
120
121 markdown: {
122 mermaid: true,
123 hooks: {
124 onBrokenMarkdownLinks: 'throw'
125 }
126 },
127
128 // Even if you don't use internationalization, you can use this field to set
129 // useful metadata like html lang. For example, if your site is Chinese, you
130 // may want to replace "en" with "zh-Hans".
131 i18n: {
132 defaultLocale: 'en',
133 locales: ['en']
134 },
135
136 future: {
137 v4: {
138 removeLegacyPostBuildHeadAttribute: true // required
139 },
140 faster: true
141 },
142
143 presets: [
144 [
145 'classic',
146 {
147 docs: {
148 routeBasePath: '/docs',
149 path: 'docs',
150 sidebarPath: 'sidebars.ts',
151 onInlineTags: 'throw',
152 docItemComponent: '@theme/ApiItem',
153 editUrl: ({ versionDocsDirPath, docPath }) => {
154 return `https://github.com/spiceai/docs/edit/trunk/website/${versionDocsDirPath}/${docPath}`
155 },
156 ...docsVersionConfig
157 },
158 blog: {
159 path: 'blog',
160 showLastUpdateAuthor: true,
161 showLastUpdateTime: true,
162 onInlineTags: 'throw',
163 onUntruncatedBlogPosts: 'ignore',
164 editUrl: ({ locale, blogDirPath, blogPath }) => {
165 return `https://github.com/spiceai/docs/edit/trunk/website/${blogDirPath}/${blogPath}`
166 },
167 remarkPlugins: [],
168 postsPerPage: 5,
169 feedOptions: {
170 type: 'all',
171 description:
172 'Keep up to date with upcoming Spice.ai OSS releases and articles by following our feed!',
173 copyright: `Copyright © 2021-2026 Spice AI, Inc.`,
174 xslt: true
175 },
176 blogTitle: 'Spice.ai OSS blog',
177 blogDescription: 'Read blog posts about Spice.ai OSS from the team and community',
178 blogSidebarCount: 'ALL',
179 blogSidebarTitle: 'All Posts'
180 } satisfies BlogOptions,
181 pages: {
182 remarkPlugins: [],
183 showLastUpdateAuthor: true,
184 showLastUpdateTime: true
185 } satisfies PageOptions,
186 theme: {
187 customCss: ['./src/css/custom.css', './src/css/openapi.css', './src/css/preflight.css']
188 },
189 gtag: {
190 trackingID: 'G-SST0X6NS37',
191 anonymizeIP: true
192 },
193 sitemap: {
194 lastmod: 'date',
195 changefreq: 'weekly',
196 priority: 0.5,
197 ignorePatterns: ['/tags/**'],
198 filename: 'sitemap.xml'
199 }
200 } satisfies Preset.Options
201 ]
202 ],
203 themes: ['docusaurus-theme-openapi-docs', '@docusaurus/theme-mermaid'],
204 themeConfig: {
205 // SEO metadata configuration
206 metadata: [
207 {
208 name: 'keywords',
209 content:
210 'spice, spice.ai, sql query engine, ai compute engine, data federation, data acceleration, rag, retrieval augmented generation, arrow flight, datafusion, duckdb, rust, llm, openai compatible, mcp server'
211 },
212 { name: 'author', content: 'Spice AI, Inc.' },
213 { name: 'robots', content: 'index, follow' },
214 { property: 'og:type', content: 'website' },
215 { property: 'og:site_name', content: 'Spice.ai OSS' },
216 { property: 'og:image:width', content: '1200' },
217 { property: 'og:image:height', content: '630' },
218 { property: 'og:image:alt', content: 'Spice.ai - SQL Query and AI Compute Engine' },
219 { name: 'twitter:card', content: 'summary_large_image' },
220 { name: 'twitter:site', content: '@spice_ai' },
221 { name: 'twitter:creator', content: '@spice_ai' }
222 ],
223 announcementBar: {
224 content: '<a href="/releases/v2.2.0">Spice.ai OSS v2.2.0</a> is now available! 🎉',
225 backgroundColor: 'var(--announcement-bar-bg)',
226 textColor: 'var(--announcement-bar-text)',
227 isCloseable: true
228 },
229 navbar: {
230 title: 'Spice.ai OSS',
231 logo: {
232 alt: 'Spice.ai OSS logo',
233 src: 'img/logo.svg'
234 },
235 hideOnScroll: true,
236 items: [
237 {
238 type: 'doc',
239 position: 'left',
240 docId: 'index',
241 label: 'Docs'
242 },
243 {
244 type: 'docSidebar',
245 position: 'left',
246 sidebarId: 'api',
247 label: 'API'
248 },
249 { to: 'releases', label: 'Releases', position: 'left' },
250 {
251 label: 'Blog',
252 href: 'https://spice.ai/blog',
253 position: 'left'
254 },
255 { to: 'cookbook', label: 'Cookbook', position: 'left' },
256 { to: 'docs/reference/sql', label: 'SQL Reference', position: 'left' },
257 // Version dropdown is only shown when versions exist
258 ...(hasVersions
259 ? [
260 {
261 type: 'docsVersionDropdown' as const,
262 position: 'right' as const,
263 dropdownActiveClassDisabled: true
264 }
265 ]
266 : []),
267 {
268 label: 'Try Spice Cloud',
269 href: 'https://spice.ai/login',
270 position: 'right'
271 },
272 {
273 label: 'X',
274 href: 'https://x.com/spice_ai',
275 position: 'right'
276 },
277 {
278 label: 'Slack',
279 href: 'https://spice.ai/slack',
280 position: 'right'
281 },
282 {
283 label: 'YouTube',
284 href: 'https://www.youtube.com/@spiceai',
285 position: 'right'
286 },
287 {
288 href: 'https://github.com/spiceai/spiceai',
289 position: 'right',
290 className: 'header-github-link',
291 'aria-label': 'GitHub repository'
292 }
293 ]
294 },
295 footer: {
296 style: 'dark',
297 links: [
298 {
299 title: 'Docs',
300 items: [
301 {
302 label: 'Getting Started',
303 to: '/docs/getting-started'
304 },
305 {
306 label: 'API',
307 to: '/docs/api'
308 },
309 {
310 label: 'CLI',
311 to: '/docs/cli'
312 },
313 {
314 label: 'SDKs',
315 to: '/docs/sdks'
316 }
317 ]
318 },
319 {
320 title: 'Community',
321 items: [
322 {
323 label: 'Reddit',
324 href: 'https://reddit.com/r/spiceai'
325 },
326 {
327 label: 'Slack',
328 href: 'https://spice.ai/slack'
329 },
330 {
331 label: 'X',
332 href: 'https://x.com/spice_ai'
333 },
334 {
335 label: 'YouTube',
336 href: 'https://www.youtube.com/@spiceai'
337 }
338 ]
339 },
340 {
341 title: 'More',
342 items: [
343 {
344 label: 'Blog',
345 href: 'https://spice.ai/blog'
346 },
347 {
348 label: 'GitHub',
349 href: 'https://github.com/spiceai/spiceai'
350 }
351 ]
352 }
353 ],
354 copyright: `Copyright © 2025 Spice AI, Inc.`
355 },
356 languageTabs: [
357 {
358 highlight: 'bash',
359 language: 'curl',
360 logoClass: 'curl'
361 }
362 ],
363 prism: {
364 theme: prismThemes.oneLight,
365 darkTheme: prismThemes.gruvboxMaterialDark,
366 additionalLanguages: ['bash', 'json', 'csharp']
367 },
368 algolia: {
369 appId: '0SP8I8JTL8',
370 apiKey: '72f66fe334ccd3c7db696a123d68735c',
371 indexName: 'spiceai',
372 contextualSearch: true
373 }
374 } satisfies Preset.ThemeConfig,
375
376 headTags: [
377 // Structured data for SEO (JSON-LD)
378 {
379 tagName: 'script',
380 attributes: {
381 type: 'application/ld+json'
382 },
383 innerHTML: JSON.stringify({
384 '@context': 'https://schema.org',
385 '@type': 'SoftwareApplication',
386 name: 'Spice.ai OSS',
387 applicationCategory: 'DeveloperApplication',
388 operatingSystem: 'Linux, macOS, Windows',
389 description:
390 'A portable SQL query and AI compute engine, written in Rust, for data-grounded apps and agents.',
391 url: 'https://spiceai.org',
392 downloadUrl: 'https://github.com/spiceai/spiceai/releases',
393 softwareVersion: '1.10.0',
394 author: {
395 '@type': 'Organization',
396 name: 'Spice AI, Inc.',
397 url: 'https://spice.ai'
398 },
399 license: 'https://github.com/spiceai/spiceai/blob/trunk/LICENSE',
400 programmingLanguage: 'Rust',
401 offers: {
402 '@type': 'Offer',
403 price: '0',
404 priceCurrency: 'USD'
405 }
406 })
407 },
408 {
409 tagName: 'script',
410 attributes: {
411 type: 'application/ld+json'
412 },
413 innerHTML: JSON.stringify({
414 '@context': 'https://schema.org',
415 '@type': 'Organization',
416 name: 'Spice AI, Inc.',
417 url: 'https://spice.ai',
418 logo: 'https://spiceai.org/img/logo.svg',
419 sameAs: [
420 'https://github.com/spiceai',
421 'https://x.com/spice_ai',
422 'https://www.youtube.com/@spiceai',
423 'https://www.linkedin.com/company/spiceai'
424 ]
425 })
426 },
427 {
428 tagName: 'link',
429 attributes: {
430 rel: 'preconnect',
431 href: 'https://fonts.googleapis.com'
432 }
433 },
434 {
435 tagName: 'link',
436 attributes: {
437 rel: 'preconnect',
438 href: 'https://fonts.gstatic.com',
439 crossorigin: 'true'
440 }
441 },
442 {
443 tagName: 'link',
444 attributes: {
445 href: 'https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;700&family=Roboto+Mono:ital,wght@0,100..700;1,100..700&display=swap',
446 rel: 'stylesheet'
447 }
448 },
449 {
450 tagName: 'link',
451 attributes: {
452 rel: 'icon',
453 type: 'image/png',
454 sizes: '32x32',
455 href: '/favicon-32x32.png'
456 }
457 },
458 {
459 tagName: 'link',
460 attributes: {
461 rel: 'icon',
462 type: 'image/png',
463 sizes: '16x16',
464 href: '/favicon-16x16.png'
465 }
466 },
467 {
468 tagName: 'script',
469 attributes: {
470 type: 'text/javascript',
471 id: 'hs-script-loader',
472 async: 'true',
473 defer: 'true',
474 src: '//js.hs-scripts.com/46107967.js'
475 }
476 },
477 // SEO: hreflang for language targeting
478 {
479 tagName: 'link',
480 attributes: {
481 rel: 'alternate',
482 hreflang: 'en',
483 href: 'https://spiceai.org'
484 }
485 },
486 {
487 tagName: 'link',
488 attributes: {
489 rel: 'alternate',
490 hreflang: 'x-default',
491 href: 'https://spiceai.org'
492 }
493 }
494 ],
495
496 plugins: [
497 tailwindPlugin,
498 [
499 '@docusaurus/plugin-content-blog',
500 {
501 id: 'releases',
502 path: 'releases',
503 routeBasePath: 'releases',
504 showLastUpdateAuthor: true,
505 showLastUpdateTime: true,
506 onInlineTags: 'throw',
507 onUntruncatedBlogPosts: 'ignore',
508 editUrl: ({ locale, blogDirPath, blogPath }) => {
509 return `https://github.com/spiceai/docs/edit/trunk/website/${blogDirPath}/${blogPath}`
510 },
511 remarkPlugins: [],
512 postsPerPage: 10,
513 feedOptions: {
514 type: 'all',
515 description: 'Keep up to date with Spice.ai OSS releases by following our feed!',
516 copyright: `Copyright © 2025-2026 Spice AI, Inc.`,
517 xslt: true
518 },
519 blogTitle: 'Spice.ai OSS Releases',
520 blogDescription: 'Spice.ai OSS release notes and announcements',
521 blogSidebarCount: 'ALL',
522 blogSidebarTitle: 'All Releases'
523 } satisfies BlogOptions
524 ],
525 [
526 'docusaurus-plugin-openapi-docs',
527 {
528 id: 'api',
529 docsPluginId: 'classic',
530 config: {
531 spice: {
532 proxy: 'http://localhost:8090',
533
534 specPath: 'public/openapi.json',
535 outputDir: 'docs/api/HTTP',
536 sidebarOptions: {
537 groupPathsBy: 'tag'
538 }
539 } satisfies OpenApiPlugin.Options
540 }
541 }
542 ],
543 [
544 '@docusaurus/plugin-client-redirects',
545 {
546 redirects: [
547 {
548 from: '/blog',
549 to: 'https://spice.ai/blog'
550 },
551 {
552 from: '/blog/announcing-1.0-stable',
553 to: 'https://spice.ai/blog/announcing-spice-ai-open-source-1-0-stable'
554 },
555 {
556 from: '/blog/amazon-s3-vectors-with-spice',
557 to: 'https://spice.ai/blog/getting-started-with-amazon-s3-vectors-and-spice'
558 },
559 {
560 from: '/blog/releases/v1.10-0',
561 to: '/releases/v1.10.0'
562 },
563 {
564 from: '/query-federation',
565 to: '/docs/features/query-federation'
566 },
567 // 2021 blog posts
568 {
569 from: '/blog/a-new-class-of-applications-that-learn-and-adapt',
570 to: 'https://spice.ai/blog/a-new-class-of-applications-that-learn-and-adapt'
571 },
572 {
573 from: '/blog/2021/a-new-class-of-applications-that-learn-and-adapt',
574 to: 'https://spice.ai/blog/a-new-class-of-applications-that-learn-and-adapt'
575 },
576 {
577 from: '/blog/ai-needs-ai-ready-data',
578 to: 'https://spice.ai/blog/ai-needs-ai-ready-data'
579 },
580 {
581 from: '/blog/2021/ai-needs-ai-ready-data',
582 to: 'https://spice.ai/blog/ai-needs-ai-ready-data'
583 },
584 {
585 from: '/blog/making-apps-that-learn-and-adapt',
586 to: 'https://spice.ai/blog/making-apps-that-learn-and-adapt'
587 },
588 {
589 from: '/blog/2021/making-apps-that-learn-and-adapt',
590 to: 'https://spice.ai/blog/making-apps-that-learn-and-adapt'
591 },
592 {
593 from: '/blog/q-learning-reward-is-all-you-need',
594 to: 'https://spice.ai/blog/q-learning-reward-is-all-you-need'
595 },
596 {
597 from: '/blog/spiceais-approach-to-time-series-ai',
598 to: 'https://spice.ai/blog/spiceais-approach-to-time-series-ai'
599 },
600 {
601 from: '/blog/2021/spiceais-approach-to-time-series-ai',
602 to: 'https://spice.ai/blog/spiceais-approach-to-time-series-ai'
603 },
604 {
605 from: '/blog/spicepods-from-zero-to-hero',
606 to: 'https://spice.ai/blog/spicepods-from-zero-to-hero'
607 },
608 {
609 from: '/blog/2021/spicepods-from-zero-to-hero',
610 to: 'https://spice.ai/blog/spicepods-from-zero-to-hero'
611 },
612 {
613 from: '/blog/teaching-apps-how-to-learn-with-spicepods',
614 to: 'https://spice.ai/blog/teaching-apps-how-to-learn-with-spicepods'
615 },
616 // 2022 blog posts
617 {
618 from: '/blog/adding-soft-actor-critic',
619 to: 'https://spice.ai/blog/adding-soft-actor-critic'
620 },
621 {
622 from: '/blog/building-on-apache-arrow-and-flight',
623 to: 'https://spice.ai/blog/building-on-apache-arrow-and-flight'
624 },
625 {
626 from: '/blog/what-data-informs-ai-driven-decision-making',
627 to: 'https://spice.ai/blog/what-data-informs-ai-driven-decision-making'
628 },
629 {
630 from: '/blog/2022/what-data-informs-ai-driven-decision-making',
631 to: 'https://spice.ai/blog/what-data-informs-ai-driven-decision-making'
632 },
633 // 2024 blog posts
634 {
635 from: '/blog/adding-spice',
636 to: 'https://spice.ai/blog/adding-spice-the-next-generation-of-spice-ai-oss'
637 },
638 {
639 from: '/blog/2024/adding-spice',
640 to: 'https://spice.ai/blog/adding-spice--the-next-generation-of-spice-ai-oss'
641 },
642 {
643 from: '/federated-queries',
644 to: '/docs/features/query-federation'
645 },
646 {
647 from: '/data-ingestion',
648 to: '/docs/features/data-ingestion'
649 },
650 {
651 from: '/data-acceleration',
652 to: '/docs/features/data-acceleration'
653 },
654 {
655 from: '/monitoring',
656 to: '/docs/features/observability'
657 }
658 ]
659 }
660 ],
661 [
662 '@signalwire/docusaurus-plugin-llms-txt',
663 {
664 siteTitle: 'Spice.ai OSS',
665 siteDescription:
666 'A portable SQL query and AI compute engine, written in Rust, for data-grounded apps and agents.',
667 depth: 2,
668 logLevel: 1,
669 content: {
670 includeBlog: false,
671 includePages: true,
672 includeDocs: true,
673 enableLlmsFullTxt: true,
674 enableMarkdownFiles: false,
675 excludeRoutes: ['/tags/**', '/search', '/api/HTTP/**']
676 },
677 optionalLinks: [
678 {
679 title: 'GitHub Repository',
680 url: 'https://github.com/spiceai/spiceai',
681 description: 'Spice.ai OSS source code and issue tracker'
682 },
683 {
684 title: 'Cookbook',
685 url: 'https://github.com/spiceai/cookbook',
686 description: 'Ready-to-use recipes and examples for Spice.ai'
687 },
688 {
689 title: 'Spice Cloud Platform',
690 url: 'https://spice.ai',
691 description: 'Managed cloud platform for Spice.ai'
692 }
693 ]
694 }
695 ]
696 ]
697}
698
699export default config
700
